scorm-again 3.0.0 → 3.0.2
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/aicc.js.map +1 -0
- package/dist/aicc.min.js.map +1 -0
- package/dist/cross-frame-api.js +26 -11
- package/dist/cross-frame-api.js.map +1 -0
- package/dist/cross-frame-api.min.js +1 -1
- package/dist/cross-frame-api.min.js.map +1 -0
- package/dist/cross-frame-lms.js +16 -4
- package/dist/cross-frame-lms.js.map +1 -0
- package/dist/cross-frame-lms.min.js +1 -1
- package/dist/cross-frame-lms.min.js.map +1 -0
- package/dist/esm/aicc.js.map +1 -0
- package/dist/esm/aicc.min.js.map +1 -0
- package/dist/esm/cross-frame-api.js +115 -108
- package/dist/esm/cross-frame-api.js.map +1 -0
- package/dist/esm/cross-frame-api.min.js +1 -1
- package/dist/esm/cross-frame-api.min.js.map +1 -0
- package/dist/esm/cross-frame-lms.js +32 -30
- package/dist/esm/cross-frame-lms.js.map +1 -0
- package/dist/esm/cross-frame-lms.min.js +1 -1
- package/dist/esm/cross-frame-lms.min.js.map +1 -0
- package/dist/esm/scorm-again.js +804 -506
- package/dist/esm/scorm-again.js.map +1 -0
- package/dist/esm/scorm-again.min.js +1 -1
- package/dist/esm/scorm-again.min.js.map +1 -0
- package/dist/esm/scorm12.js +152 -373
- package/dist/esm/scorm12.js.map +1 -0
- package/dist/esm/scorm12.min.js +1 -1
- package/dist/esm/scorm12.min.js.map +1 -0
- package/dist/esm/scorm2004.js +582 -312
- package/dist/esm/scorm2004.js.map +1 -0
- package/dist/esm/scorm2004.min.js +1 -1
- package/dist/esm/scorm2004.min.js.map +1 -0
- package/dist/scorm-again.js +1205 -334
- package/dist/scorm-again.js.map +1 -0
- package/dist/scorm-again.min.js +1 -1
- package/dist/scorm-again.min.js.map +1 -0
- package/dist/scorm12.js +832 -71
- package/dist/scorm12.js.map +1 -0
- package/dist/scorm12.min.js +1 -1
- package/dist/scorm12.min.js.map +1 -0
- package/dist/scorm2004.js +1078 -292
- package/dist/scorm2004.js.map +1 -0
- package/dist/scorm2004.min.js +1 -1
- package/dist/scorm2004.min.js.map +1 -0
- package/dist/types/BaseAPI.d.ts +3 -3
- package/dist/types/Scorm2004API.d.ts +1 -0
- package/dist/types/cmi/scorm2004/sequencing/handlers/termination_handler.d.ts +2 -2
- package/dist/types/interfaces/services.d.ts +3 -2
- package/dist/types/services/EventService.d.ts +3 -3
- package/package.json +41 -44
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorm-again.min.js","sources":["../src/utilities/core.ts","../src/cmi/common/base_cmi.ts","../src/exceptions/base_exceptions.ts","../src/constants/api_constants.ts","../src/exceptions/scorm12_exceptions.ts","../src/exceptions/scorm2004_exceptions.ts","../src/constants/error_codes.ts","../src/cmi/common/array.ts","../src/constants/enums.ts","../src/constants/default_settings.ts","../src/constants/regex.ts","../src/constants/response_constants.ts","../src/constants/language_constants.ts","../src/types/scheduled_commit.ts","../src/types/sequencing_types.ts","../src/cmi/scorm2004/sequencing/sequencing_rules.ts","../src/cmi/scorm2004/sequencing/utils/activity_tree_queries.ts","../src/cmi/scorm2004/sequencing/validators/choice_constraint_validator.ts","../src/cmi/scorm2004/sequencing/rules/sequencing_request_types.ts","../src/cmi/scorm2004/sequencing/rules/rule_evaluation_engine.ts","../src/cmi/scorm2004/sequencing/sequencing_controls.ts","../src/cmi/scorm2004/sequencing/selection_randomization.ts","../src/cmi/scorm2004/sequencing/traversal/flow_traversal_service.ts","../src/cmi/scorm2004/sequencing/handlers/flow_request_handler.ts","../src/cmi/scorm2004/sequencing/handlers/choice_request_handler.ts","../src/cmi/scorm2004/sequencing/handlers/exit_request_handler.ts","../src/cmi/scorm2004/sequencing/handlers/retry_request_handler.ts","../src/cmi/scorm2004/sequencing/sequencing_process.ts","../src/services/ActivityDeliveryService.ts","../src/services/AsynchronousHttpService.ts","../src/services/CMIValueAccessService.ts","../src/services/LoggingService.ts","../src/services/ErrorHandlingService.ts","../src/services/EventService.ts","../src/services/OfflineStorageService.ts","../src/cmi/common/validation.ts","../src/cmi/scorm2004/validation.ts","../src/cmi/scorm2004/sequencing/rollup_rules.ts","../src/cmi/scorm2004/sequencing/activity.ts","../src/cmi/scorm2004/sequencing/rollup/rollup_child_filter.ts","../src/cmi/scorm2004/sequencing/rollup/rollup_rule_evaluator.ts","../src/cmi/scorm2004/sequencing/rollup/measure_rollup.ts","../src/cmi/scorm2004/sequencing/rollup/objective_rollup.ts","../src/cmi/scorm2004/sequencing/rollup/progress_rollup.ts","../src/cmi/scorm2004/sequencing/rollup/duration_rollup.ts","../src/cmi/scorm2004/sequencing/rollup/cross_cluster_processor.ts","../src/cmi/scorm2004/sequencing/objectives/global_objective_synchronizer.ts","../src/cmi/scorm2004/sequencing/validation/rollup_state_validator.ts","../src/cmi/scorm2004/sequencing/rollup_process.ts","../src/cmi/scorm2004/sequencing/handlers/rte_data_transfer.ts","../src/cmi/scorm2004/sequencing/handlers/termination_handler.ts","../src/cmi/scorm2004/sequencing/handlers/delivery_handler.ts","../src/cmi/scorm2004/sequencing/navigation_look_ahead.ts","../src/cmi/scorm2004/sequencing/services/navigation_validity_service.ts","../src/cmi/scorm2004/sequencing/services/global_objective_service.ts","../src/cmi/scorm2004/sequencing/state/sequencing_state_manager.ts","../src/cmi/scorm2004/sequencing/validation/delivery_validator.ts","../src/cmi/scorm2004/sequencing/overall_sequencing_process.ts","../src/services/SequencingService.ts","../src/services/SerializationService.ts","../src/services/SynchronousHttpService.ts","../src/cmi/scorm12/validation.ts","../src/services/ValidationService.ts","../src/BaseAPI.ts","../src/cmi/common/score.ts","../src/cmi/scorm12/core.ts","../src/cmi/scorm12/objectives.ts","../src/cmi/scorm12/student_data.ts","../src/cmi/scorm12/student_preference.ts","../src/cmi/scorm12/interactions.ts","../src/cmi/scorm12/cmi.ts","../src/cmi/scorm12/nav.ts","../src/Scorm12API.ts","../src/cmi/scorm2004/learner_preference.ts","../src/cmi/scorm2004/interactions.ts","../src/cmi/scorm2004/score.ts","../src/cmi/scorm2004/comments.ts","../src/cmi/scorm2004/objectives.ts","../src/cmi/scorm2004/metadata.ts","../src/cmi/scorm2004/learner.ts","../src/cmi/scorm2004/status.ts","../src/cmi/scorm2004/session.ts","../src/cmi/scorm2004/content.ts","../src/cmi/scorm2004/settings.ts","../src/cmi/scorm2004/thresholds.ts","../src/cmi/scorm2004/cmi.ts","../src/cmi/scorm2004/adl.ts","../src/cmi/scorm2004/sequencing/activity_tree.ts","../src/cmi/scorm2004/sequencing/sequencing.ts","../src/handlers/scorm2004/response_validator.ts","../src/handlers/scorm2004/cmi_handler.ts","../src/configuration/sequencing_configuration_builder.ts","../src/configuration/activity_tree_builder.ts","../src/objectives/global_objective_manager.ts","../src/persistence/sequencing_state_persistence.ts","../src/serialization/scorm2004_data_serializer.ts","../src/ScormAgain.ts","../src/Scorm2004API.ts"],"sourcesContent":["/**\n * Constants for time conversion calculations.\n * These are used throughout the codebase for converting between different time formats.\n */\nexport const SECONDS_PER_SECOND = 1.0;\nexport const SECONDS_PER_MINUTE = 60;\nexport const SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE;\nexport const SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR;\n\ntype Designation = {\n [key: string]: number;\n};\n\n/**\n * A record with string keys and unknown values.\n * This is a more type-safe alternative to using `any`.\n */\nexport type StringKeyMap = Record<string, unknown>;\n\nconst designations: Designation = {\n D: SECONDS_PER_DAY,\n H: SECONDS_PER_HOUR,\n M: SECONDS_PER_MINUTE,\n S: SECONDS_PER_SECOND,\n};\n\n/**\n * Converts a number of seconds to a formatted time string in HH:MM:SS format.\n * This function handles decimal seconds and ensures proper formatting with leading zeros.\n *\n * @param {number|null} totalSeconds - The total number of seconds to convert. If null or negative, returns \"00:00:00\".\n * @return {string} A formatted time string in HH:MM:SS format, with up to 2 decimal places for seconds if applicable.\n *\n * @example\n * // Returns \"01:30:45\"\n * getSecondsAsHHMMSS(5445);\n *\n * @example\n * // Returns \"00:00:30.5\"\n * getSecondsAsHHMMSS(30.5);\n *\n * @example\n * // Returns \"00:00:00\"\n * getSecondsAsHHMMSS(null);\n */\nexport const getSecondsAsHHMMSS = (totalSeconds: number | null): string => {\n // SCORM spec does not deal with negative durations, give zero back\n if (!totalSeconds || totalSeconds <= 0) {\n return \"00:00:00\";\n }\n\n const hours = Math.floor(totalSeconds / SECONDS_PER_HOUR);\n const dateObj = new Date(totalSeconds * 1000);\n const minutes = dateObj.getUTCMinutes();\n // make sure we add any possible decimal value\n const seconds = dateObj.getSeconds();\n const ms = totalSeconds % 1.0;\n let msStr = \"\";\n\n if (countDecimals(ms) > 0) {\n if (countDecimals(ms) > 2) {\n msStr = ms.toFixed(2);\n } else {\n msStr = String(ms);\n }\n\n msStr = \".\" + msStr.split(\".\")[1];\n }\n\n return (hours + \":\" + minutes + \":\" + seconds).replace(/\\b\\d\\b/g, \"0$&\") + msStr;\n};\n\n/**\n * Converts a number of seconds to an ISO 8601 duration string (e.g., \"PT1H30M45S\").\n * This function handles the complexity of formatting according to the ISO 8601 standard,\n * including the proper placement of the \"T\" separator for time components.\n *\n * @param {number|null} seconds - The number of seconds to convert. If null or negative, returns \"PT0S\".\n * @return {string} An ISO 8601 formatted duration string.\n *\n * @example\n * // Returns \"PT1H30M45S\" (1 hour, 30 minutes, 45 seconds)\n * getSecondsAsISODuration(5445);\n *\n * @example\n * // Returns \"PT30.5S\" (30.5 seconds)\n * getSecondsAsISODuration(30.5);\n *\n * @example\n * // Returns \"P1DT1H\" (1 day, 1 hour)\n * getSecondsAsISODuration(90000);\n *\n * @example\n * // Returns \"PT0S\" (0 seconds)\n * getSecondsAsISODuration(null);\n */\nexport const getSecondsAsISODuration = (seconds: number | null): string => {\n // SCORM spec does not deal with negative durations, give zero back\n if (!seconds || seconds <= 0) {\n return \"PT0S\";\n }\n\n let duration = \"P\";\n let remainder = seconds;\n\n // Convert to array of entries and use functional methods\n const designationEntries = Object.entries(designations);\n\n // Process each time designation\n designationEntries.forEach(([designationsKey, current_seconds]) => {\n let value = Math.floor(remainder / current_seconds);\n remainder = remainder % current_seconds;\n\n // Limit decimal places\n if (countDecimals(remainder) > 2) {\n remainder = Number(Number(remainder).toFixed(2));\n }\n\n // If we have anything left in the remainder, and we're currently adding\n // seconds to the duration, go ahead and add the decimal to the seconds\n if (designationsKey === \"S\" && remainder > 0) {\n value += remainder;\n }\n\n if (value) {\n // Add the 'T' separator for time components if needed\n const needsTimeSeparator =\n (duration.indexOf(\"D\") > 0 || [\"H\", \"M\", \"S\"].includes(designationsKey)) &&\n duration.indexOf(\"T\") === -1;\n\n if (needsTimeSeparator) {\n duration += \"T\";\n }\n\n duration += `${value}${designationsKey}`;\n }\n });\n\n return duration;\n};\n\n/**\n * Converts a time string in HH:MM:SS format to the equivalent number of seconds.\n * This function is flexible and can handle various input types, converting them to strings as needed.\n *\n * @param {string|number|boolean|null} timeString - The time string to convert, typically in HH:MM:SS format.\n * If not a string, it will be converted to one.\n * If null, invalid, or doesn't match the regex, returns 0.\n * @param {RegExp|string} timeRegex - The regular expression used to validate the time string format.\n * If a string is provided, it will be converted to a RegExp.\n * @return {number} The total number of seconds represented by the time string.\n *\n * @example\n * // Returns 5445 (1 hour, 30 minutes, 45 seconds)\n * getTimeAsSeconds(\"01:30:45\", /^(\\d+):(\\d+):(\\d+)$/);\n *\n * @example\n * // Returns 0 (invalid format)\n * getTimeAsSeconds(\"invalid\", /^(\\d+):(\\d+):(\\d+)$/);\n *\n * @example\n * // Returns 30 (converts number to string \"30\" then parses as 0:0:30)\n * getTimeAsSeconds(30, /^(\\d+):(\\d+):(\\d+)$/);\n */\nexport const getTimeAsSeconds = memoize(\n (timeString: string | number | boolean | null, timeRegex: RegExp | string): number => {\n if (typeof timeString === \"number\" || typeof timeString === \"boolean\") {\n timeString = String(timeString);\n }\n if (typeof timeRegex === \"string\") {\n timeRegex = new RegExp(timeRegex);\n }\n if (!timeString) {\n return 0;\n }\n\n if (!timeString.match(timeRegex)) {\n // If the string represents a simple number, treat it as seconds\n if (/^\\d+(?:\\.\\d+)?$/.test(timeString)) {\n return Number(timeString);\n }\n return 0;\n }\n\n const parts = timeString.split(\":\");\n const hours = Number(parts[0]);\n const minutes = Number(parts[1]);\n const seconds = Number(parts[2]);\n return hours * 3600 + minutes * 60 + seconds;\n },\n // Custom key function to handle RegExp objects which can't be stringified\n (timeString, timeRegex) => {\n const timeStr = typeof timeString === \"string\" ? timeString : String(timeString ?? \"\");\n const regexStr = typeof timeRegex === \"string\" ? timeRegex : (timeRegex?.toString() ?? \"\");\n return `${timeStr}:${regexStr}`;\n },\n);\n\n/**\n * Converts an ISO 8601 duration string (e.g., \"PT1H30M45S\") to the equivalent number of seconds.\n * This function parses the duration string using a regular expression to extract years, days,\n * hours, minutes, and seconds components.\n *\n * @param {string|null} duration - The ISO 8601 duration string to convert.\n * If null, invalid, or doesn't match the regex, returns 0.\n * @param {RegExp|string} durationRegex - The regular expression used to parse the duration components.\n * If a string is provided, it will be converted to a RegExp.\n * The regex should have capture groups for years, days, hours, minutes, and seconds.\n * @return {number} The total number of seconds represented by the duration string.\n *\n * @example\n * // Returns 5445 (1 hour, 30 minutes, 45 seconds)\n * getDurationAsSeconds(\"PT1H30M45S\", /^P(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/);\n *\n * @example\n * // Returns 90000 (1 day, 1 hour)\n * getDurationAsSeconds(\"P1DT1H\", /^P(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/);\n *\n * @example\n * // Returns 0 (invalid format)\n * getDurationAsSeconds(\"invalid\", /^P(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/);\n */\nexport const getDurationAsSeconds = memoize(\n (duration: string | null, durationRegex: RegExp | string): number => {\n if (typeof durationRegex === \"string\") {\n durationRegex = new RegExp(durationRegex);\n }\n\n if (!duration || !duration?.match?.(durationRegex)) {\n return 0;\n }\n\n // Extract all duration components from ISO 8601 format\n // Regex capture groups: [full, years, months, weeks, days, hours, minutes, seconds]\n const [, years, months, weeks, days, hours, minutes, seconds] =\n new RegExp(durationRegex).exec?.(duration) ?? [];\n\n let result = 0.0;\n result += Number(seconds) || 0.0;\n result += Number(minutes) * 60.0 || 0.0;\n result += Number(hours) * 3600.0 || 0.0;\n result += Number(days) * (60 * 60 * 24.0) || 0.0;\n result += Number(weeks) * (60 * 60 * 24 * 7.0) || 0.0;\n // Note: Months are approximate (30 days) as specified in SCORM 2004\n result += Number(months) * (60 * 60 * 24 * 30.0) || 0.0;\n result += Number(years) * (60 * 60 * 24 * 365.0) || 0.0;\n return result;\n },\n // Custom key function to handle RegExp objects which can't be stringified\n (duration, durationRegex) => {\n const durationStr = duration ?? \"\";\n const regexStr =\n typeof durationRegex === \"string\" ? durationRegex : (durationRegex?.toString() ?? \"\");\n return `${durationStr}:${regexStr}`;\n },\n);\n\n/**\n * Validates ISO 8601 duration format according to SCORM 2004 specification\n * @param {string} duration - The duration string to validate\n * @return {boolean} - True if valid ISO 8601 duration format\n */\nexport const validateISO8601Duration = memoize(\n (duration: string, durationRegex: RegExp | string): boolean => {\n if (typeof durationRegex === \"string\") {\n durationRegex = new RegExp(durationRegex);\n }\n\n return !(!duration || !duration?.match?.(durationRegex));\n },\n);\n\n/**\n * Adds together two ISO 8601 duration strings and returns the result as a new ISO 8601 duration string.\n * This function works by converting both durations to seconds, adding them together, and then\n * converting the result back to an ISO 8601 duration string.\n *\n * @param {string} first - The first ISO 8601 duration string.\n * @param {string} second - The second ISO 8601 duration string.\n * @param {RegExp|string} durationRegex - The regular expression used to parse the duration components.\n * If a string is provided, it will be converted to a RegExp.\n * @return {string} A new ISO 8601 duration string representing the sum of the two input durations.\n *\n * @example\n * // Returns \"PT2H\" (1 hour + 1 hour = 2 hours)\n * addTwoDurations(\"PT1H\", \"PT1H\", /^P(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/);\n *\n * @example\n * // Returns \"PT1H30M\" (1 hour + 30 minutes = 1 hour and 30 minutes)\n * addTwoDurations(\"PT1H\", \"PT30M\", /^P(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/);\n *\n * @example\n * // Returns \"P1DT1H30M\" (1 day + 1 hour and 30 minutes = 1 day, 1 hour, and 30 minutes)\n * addTwoDurations(\"P1D\", \"PT1H30M\", /^P(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/);\n */\nexport function addTwoDurations(\n first: string,\n second: string,\n durationRegex: RegExp | string,\n): string {\n const regex: RegExp =\n typeof durationRegex === \"string\" ? new RegExp(durationRegex) : durationRegex;\n return getSecondsAsISODuration(\n getDurationAsSeconds(first, regex) + getDurationAsSeconds(second, regex),\n );\n}\n\n/**\n * Adds together two time strings in HH:MM:SS format and returns the result as a new time string.\n * This function works by converting both time strings to seconds, adding them together, and then\n * converting the result back to an HH:MM:SS format.\n *\n * @param {string} first - The first time string in HH:MM:SS format.\n * @param {string} second - The second time string in HH:MM:SS format.\n * @param {RegExp|string} timeRegex - The regular expression used to validate and parse the time strings.\n * If a string is provided, it will be converted to a RegExp.\n * @return {string} A new time string in HH:MM:SS format representing the sum of the two input times.\n *\n * @example\n * // Returns \"02:00:00\" (1 hour + 1 hour = 2 hours)\n * addHHMMSSTimeStrings(\"01:00:00\", \"01:00:00\", /^(\\d+):(\\d+):(\\d+)$/);\n *\n * @example\n * // Returns \"01:30:00\" (1 hour + 30 minutes = 1 hour and 30 minutes)\n * addHHMMSSTimeStrings(\"01:00:00\", \"00:30:00\", /^(\\d+):(\\d+):(\\d+)$/);\n *\n * @example\n * // Returns \"01:30:45\" (1 hour + 30 minutes and 45 seconds = 1 hour, 30 minutes, and 45 seconds)\n * addHHMMSSTimeStrings(\"01:00:00\", \"00:30:45\", /^(\\d+):(\\d+):(\\d+)$/);\n */\nexport function addHHMMSSTimeStrings(\n first: string,\n second: string,\n timeRegex: RegExp | string,\n): string {\n if (typeof timeRegex === \"string\") {\n timeRegex = new RegExp(timeRegex);\n }\n return getSecondsAsHHMMSS(\n getTimeAsSeconds(first, timeRegex) + getTimeAsSeconds(second, timeRegex),\n );\n}\n\n/**\n * Flattens a nested JSON object into a flat object with dot notation for nested properties.\n * This function recursively traverses the object and creates new keys using dot notation\n * for nested objects and bracket notation for arrays.\n *\n * @param {StringKeyMap} data - The nested object to flatten.\n * @return {StringKeyMap} A flattened object where nested properties are represented with dot notation.\n *\n * @example\n * // Returns { \"a\": 1, \"b.c\": 2, \"b.d\": 3, \"e[0]\": 4, \"e[1]\": 5 }\n * flatten({\n * a: 1,\n * b: { c: 2, d: 3 },\n * e: [4, 5]\n * });\n *\n * @example\n * // Returns { \"\": [] } for an empty array\n * flatten([]);\n *\n * @example\n * // Returns { \"\": {} } for an empty object\n * flatten({});\n */\nexport function flatten(data: StringKeyMap): StringKeyMap {\n const result: StringKeyMap = {};\n\n /**\n * Recurse through the object\n * @param {*} cur\n * @param {*} prop\n */\n function recurse(cur: any, prop: any) {\n if (Object(cur) !== cur) {\n result[prop] = cur;\n } else if (Array.isArray(cur)) {\n // Use forEach instead of for loop\n cur.forEach((item, i) => {\n recurse(item, `${prop}[${i}]`);\n });\n\n if (cur.length === 0) result[prop] = [];\n } else {\n const keys = Object.keys(cur).filter((p) => Object.prototype.hasOwnProperty.call(cur, p));\n\n const isEmpty = keys.length === 0;\n\n // Use forEach instead of for...in loop\n keys.forEach((p) => {\n recurse(cur[p], prop ? `${prop}.${p}` : p);\n });\n\n if (isEmpty && prop) result[prop] = {};\n }\n }\n\n recurse(data, \"\");\n return result;\n}\n\n/**\n * Converts a flattened object back into a nested object structure.\n * This function is the inverse of `flatten()`. It parses dot notation and bracket notation\n * in property names to recreate the original nested structure.\n *\n * @param {StringKeyMap} data - The flattened object to unflatten.\n * @return {object} A nested object recreated from the flattened structure.\n *\n * @example\n * // Returns { a: 1, b: { c: 2, d: 3 }, e: [4, 5] }\n * unflatten({\n * \"a\": 1,\n * \"b.c\": 2,\n * \"b.d\": 3,\n * \"e[0]\": 4,\n * \"e[1]\": 5\n * });\n *\n * @example\n * // Handles array indices correctly\n * unflatten({\n * \"users[0].name\": \"John\",\n * \"users[0].email\": \"john@example.com\",\n * \"users[1].name\": \"Jane\",\n * \"users[1].email\": \"jane@example.com\"\n * });\n * // Returns:\n * // {\n * // users: [\n * // { name: \"John\", email: \"john@example.com\" },\n * // { name: \"Jane\", email: \"jane@example.com\" }\n * // ]\n * // }\n */\nexport function unflatten(data: StringKeyMap): object {\n \"use strict\";\n\n if (Object(data) !== data || Array.isArray(data)) return data;\n const result: StringKeyMap = {};\n\n // Regex pattern for parsing property paths\n const pattern = /\\.?([^.[\\]]+)|\\[(\\d+)]/g;\n\n // Get all own properties and process them\n Object.keys(data)\n .filter((p) => Object.prototype.hasOwnProperty.call(data, p))\n .forEach((p) => {\n let cur = result;\n let prop = \"\";\n\n // Create a new regex instance for each property to reset lastIndex\n const regex = new RegExp(pattern);\n\n // Process all matches in the property path\n Array.from({ length: p.match(new RegExp(pattern, \"g\"))?.length ?? 0 }, () =>\n regex.exec(p),\n ).forEach((m) => {\n if (m) {\n // Create array or object as needed\n cur = (cur[prop] ?? (cur[prop] = m[2] ? [] : ({} as StringKeyMap))) as StringKeyMap;\n prop = m[2] || m[1] || \"\";\n }\n });\n\n cur[prop] = data[p];\n });\n\n return (result[\"\"] ?? result) as object;\n}\n\n/**\n * Counts the number of decimal places in a number.\n * This function handles both integer and floating-point numbers.\n *\n * @param {number} num - The number to analyze.\n * @return {number} The number of decimal places. Returns 0 for integers or if the number doesn't have a decimal point.\n *\n * @example\n * // Returns 0\n * countDecimals(42);\n *\n * @example\n * // Returns 2\n * countDecimals(3.14);\n *\n * @example\n * // Returns 5\n * countDecimals(1.23456);\n */\nexport function countDecimals(num: number): number {\n if (Math.floor(num) === num || String(num)?.indexOf?.(\".\") < 0) return 0;\n const parts = num.toString().split(\".\")?.[1];\n return parts?.length ?? 0;\n}\n\n/**\n * Formats SCORM messages for consistent and readable logging.\n * This function pads the function name and CMI element to create aligned log messages\n * that are easier to read in log files or console output.\n *\n * @param {string} functionName - The name of the function that generated the message.\n * @param {string} message - The message content to be logged.\n * @param {string} [CMIElement] - Optional. The CMI element related to the message.\n * @return {string} A formatted message string with consistent padding.\n *\n * @example\n * // Returns \"initialize : Successfully initialized\"\n * formatMessage(\"initialize\", \"Successfully initialized\");\n *\n * @example\n * // Returns \"getValue : cmi.core.student_id : 12345\"\n * formatMessage(\"getValue\", \"12345\", \"cmi.core.student_id\");\n *\n * @example\n * // Returns \"setValue : cmi.core.lesson_status : completed\"\n * formatMessage(\"setValue\", \"completed\", \"cmi.core.lesson_status\");\n */\nexport function formatMessage(functionName: string, message: string, CMIElement?: string): string {\n const baseLength = 20;\n\n // Use string padding instead of loops - handle undefined/null functionName\n let messageString = functionName ? `${String(functionName).padEnd(baseLength)}: ` : \"\";\n\n if (CMIElement) {\n const CMIElementBaseLength = 70;\n // Add CMIElement and pad to the required length\n messageString += CMIElement;\n messageString = messageString.padEnd(CMIElementBaseLength);\n }\n\n // Add the message (or empty string if null/undefined)\n messageString += message ?? \"\";\n\n return messageString;\n}\n\n/**\n * Checks if a string contains a specific pattern using regular expression matching.\n * This function is a wrapper around JavaScript's String.match() method that handles\n * null/undefined values gracefully.\n *\n * @param {string} str - The string to search within. If null or undefined, returns false.\n * @param {string} tester - The pattern to search for. Can be a regular expression pattern.\n * @return {boolean} True if the pattern is found in the string, false otherwise.\n *\n * @example\n * // Returns true\n * stringMatches(\"Hello, world!\", \"world\");\n *\n * @example\n * // Returns true\n * stringMatches(\"The quick brown fox\", \"\\\\w+\\\\s+\\\\w+\");\n *\n * @example\n * // Returns false\n * stringMatches(\"Hello, world!\", \"universe\");\n *\n * @example\n * // Returns false (handles null gracefully)\n * stringMatches(null, \"test\");\n */\nexport function stringMatches(str: string | null | undefined, tester: string): boolean {\n if (typeof str !== \"string\") {\n return false;\n }\n return new RegExp(tester).test(str);\n}\n\n/**\n * Creates a memoized (cached) version of a function for performance optimization.\n * This function caches the results of previous calls with the same arguments,\n * avoiding redundant calculations for repeated calls with the same input.\n *\n * @template T - The type of the function to memoize\n * @param {T} fn - The function to memoize\n * @param {Function} [keyFn] - Optional function to generate a custom cache key.\n * Useful for functions with complex arguments that can't be serialized.\n * @return {T} A memoized version of the input function with the same signature\n *\n * @example\n * // Basic usage with a simple function\n * const memoizedAdd = memoize((a, b) => {\n * console.log('Calculating...');\n * return a + b;\n * });\n *\n * memoizedAdd(1, 2); // Logs \"Calculating...\" and returns 3\n * memoizedAdd(1, 2); // Returns 3 without logging (uses cached result)\n *\n * @example\n * // Using a custom key function for objects that can't be serialized\n * const memoizedProcessUser = memoize(\n * (user) => {\n * console.log('Processing user...');\n * return { ...user, processed: true };\n * },\n * (user) => user.id // Use user.id as the cache key\n * );\n *\n * memoizedProcessUser({ id: 123, name: 'John' }); // Logs and processes\n * memoizedProcessUser({ id: 123, name: 'John' }); // Uses cached result\n */\nexport function memoize<T extends (...args: any[]) => any>(\n fn: T,\n keyFn?: (...args: Parameters<T>) => string,\n): T {\n const cache = new Map<string, ReturnType<T>>();\n\n return ((...args: Parameters<T>): ReturnType<T> => {\n const key = keyFn ? keyFn(...args) : JSON.stringify(args);\n\n return cache.has(key)\n ? (cache.get(key) as ReturnType<T>)\n : (() => {\n const result = fn(...args);\n cache.set(key, result);\n return result;\n })();\n }) as T;\n}\n\n/**\n * Represents a parsed navigation request from the LMS\n */\nexport type ParsedNavigationRequest = {\n /**\n * The navigation request type (one of the valid SCORM 2004 navigation commands)\n */\n command: string;\n /**\n * The target activity ID for choice/jump commands (null for other commands)\n */\n targetActivityId: string | null;\n /**\n * Whether the navigation request was valid\n */\n valid: boolean;\n /**\n * Error message if invalid\n */\n error?: string;\n};\n\n/**\n * Securely parses a navigation request string from the LMS into a validated command and target.\n * This function implements a whitelist-based approach to prevent code injection attacks.\n *\n * Valid SCORM 2004 navigation commands:\n * - start, resumeAll, continue, previous, exit, exitAll, abandon, abandonAll, suspendAll\n * - choice.{targetActivityId} - Navigate to specific activity\n * - jump.{targetActivityId} - Jump to specific activity\n * - _none_ - No navigation (special case)\n *\n * @param {string} navRequest - The navigation request string from the LMS\n * @return {ParsedNavigationRequest} Parsed command with validation status\n *\n * @example\n * // Simple navigation command\n * parseNavigationRequest(\"continue\")\n * // => { command: \"continue\", targetActivityId: null, valid: true }\n *\n * @example\n * // Choice navigation with target\n * parseNavigationRequest(\"choice.activity_123\")\n * // => { command: \"choice\", targetActivityId: \"activity_123\", valid: true }\n *\n * @example\n * // Invalid/malicious input\n * parseNavigationRequest(\"alert('XSS')\")\n * // => { command: \"_none_\", targetActivityId: null, valid: false, error: \"...\" }\n */\nexport function parseNavigationRequest(navRequest: string): ParsedNavigationRequest {\n // Whitelist of valid navigation commands (SCORM 2004 spec)\n const validCommands = new Set([\n \"start\",\n \"resumeAll\",\n \"continue\",\n \"previous\",\n \"choice\",\n \"jump\",\n \"exit\",\n \"exitAll\",\n \"abandon\",\n \"abandonAll\",\n \"suspendAll\",\n \"_none_\",\n ]);\n\n // Trim and handle empty strings\n const trimmed = navRequest.trim();\n if (!trimmed) {\n return {\n command: \"_none_\",\n targetActivityId: null,\n valid: false,\n error: \"Empty navigation request\",\n };\n }\n\n // Check for simple commands (no target)\n if (validCommands.has(trimmed)) {\n return {\n command: trimmed,\n targetActivityId: null,\n valid: true,\n };\n }\n\n // Check for choice/jump commands with target (format: \"choice.targetId\" or \"jump.targetId\")\n const dotIndex = trimmed.indexOf(\".\");\n if (dotIndex > 0) {\n const command = trimmed.substring(0, dotIndex);\n const targetActivityId = trimmed.substring(dotIndex + 1);\n\n // Only \"choice\" and \"jump\" commands can have targets\n if ((command === \"choice\" || command === \"jump\") && targetActivityId) {\n // Validate target activity ID: alphanumeric, hyphens, underscores, dots only\n // This prevents injection via the target ID parameter\n if (/^[a-zA-Z0-9._-]+$/.test(targetActivityId)) {\n return {\n command,\n targetActivityId,\n valid: true,\n };\n } else {\n return {\n command: \"_none_\",\n targetActivityId: null,\n valid: false,\n error: `Invalid target activity ID: contains disallowed characters`,\n };\n }\n }\n }\n\n // If we get here, the navigation request is not recognized\n return {\n command: \"_none_\",\n targetActivityId: null,\n valid: false,\n error: `Unrecognized navigation command: \"${trimmed}\"`,\n };\n}\n","/**\n * Base class for API cmi objects\n */\nexport abstract class BaseCMI {\n /**\n * Flag used during JSON serialization to allow getter access without initialization checks.\n * When true, getters can be accessed before the API is initialized, which is necessary\n * for serializing the CMI data structure to JSON format.\n */\n jsonString = false;\n protected readonly _cmi_element: string;\n protected _initialized = false;\n\n /**\n * Constructor for BaseCMI\n * @param {string} cmi_element\n */\n constructor(cmi_element: string) {\n this._cmi_element = cmi_element;\n }\n\n /**\n * Getter for _initialized\n * @return {boolean}\n */\n get initialized(): boolean {\n return this._initialized;\n }\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n initialize(): void {\n this._initialized = true;\n }\n\n abstract reset(): void;\n}\n\n/**\n * Base class for cmi root objects\n */\nexport abstract class BaseRootCMI extends BaseCMI {\n protected _start_time: number | undefined;\n\n /**\n * Start time of the session\n * @type {number | undefined}\n * @protected\n */\n get start_time(): number | undefined {\n return this._start_time;\n }\n\n /**\n * Setter for start_time. Can only be called once.\n */\n setStartTime(): void {\n if (this._start_time === undefined) {\n this._start_time = new Date().getTime();\n } else {\n throw new Error(\"Start time has already been set.\");\n }\n }\n\n abstract getCurrentTotalTime(): string;\n}\n","type APIError = {\n errorCode: number;\n errorMessage: string;\n detailedMessage: string;\n};\n\nexport class BaseScormValidationError extends Error {\n constructor(CMIElement: string, errorCode: number) {\n super(`${CMIElement} : ${errorCode.toString()}`);\n this._errorCode = errorCode;\n\n // Set the prototype explicitly.\n Object.setPrototypeOf(this, BaseScormValidationError.prototype);\n }\n\n private readonly _errorCode: number;\n\n /**\n * Getter for _errorCode\n * @return {number}\n */\n get errorCode(): number {\n return this._errorCode;\n }\n}\n\n/**\n * Base Validation Exception\n */\nexport class ValidationError extends BaseScormValidationError implements APIError {\n /**\n * Constructor to take in an error message and code\n * @param {string} CMIElement\n * @param {number} errorCode\n * @param {string} errorMessage\n * @param {string} detailedMessage\n */\n constructor(\n CMIElement: string,\n errorCode: number,\n errorMessage: string,\n detailedMessage?: string,\n ) {\n super(CMIElement, errorCode);\n this.message = `${CMIElement} : ${errorMessage}`;\n this._errorMessage = errorMessage;\n if (detailedMessage) {\n this._detailedMessage = detailedMessage;\n }\n\n // Set the prototype explicitly.\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n\n private readonly _errorMessage: string;\n private readonly _detailedMessage: string = \"\";\n\n /**\n * Getter for _errorMessage\n * @return {string}\n */\n get errorMessage(): string {\n return this._errorMessage;\n }\n\n /**\n * Getter for _detailedMessage\n * @return {string}\n */\n get detailedMessage(): string {\n return this._detailedMessage;\n }\n}\n","interface ErrorDescription {\n basicMessage: string;\n detailMessage: string;\n}\n\ninterface ErrorDescriptions {\n [key: string]: ErrorDescription;\n}\n\ninterface ScormConstants {\n cmi_children: string;\n core_children: string;\n score_children: string;\n comments_children: string;\n objectives_children: string;\n correct_responses_children: string;\n student_data_children: string;\n student_preference_children: string;\n interactions_children: string;\n error_descriptions: ErrorDescriptions;\n}\n\ninterface Scorm2004Constants {\n cmi_children: string;\n comments_children: string;\n score_children: string;\n objectives_children: string;\n correct_responses_children: string;\n student_data_children: string;\n student_preference_children: string;\n interactions_children: string;\n adl_data_children: string;\n error_descriptions: ErrorDescriptions;\n}\n\ninterface GlobalConstants {\n SCORM_TRUE: string;\n SCORM_FALSE: string;\n STATE_NOT_INITIALIZED: number;\n STATE_INITIALIZED: number;\n STATE_TERMINATED: number;\n}\n\nexport const global_constants: GlobalConstants = {\n SCORM_TRUE: \"true\",\n SCORM_FALSE: \"false\",\n STATE_NOT_INITIALIZED: 0,\n STATE_INITIALIZED: 1,\n STATE_TERMINATED: 2,\n};\n\nexport const scorm12_constants: ScormConstants = {\n // Children lists\n cmi_children:\n \"core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions\",\n core_children:\n \"student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,exit,session_time\",\n score_children: \"raw,min,max\",\n comments_children: \"content,location,time\",\n objectives_children: \"id,score,status\",\n correct_responses_children: \"pattern\",\n student_data_children: \"mastery_score,max_time_allowed,time_limit_action\",\n student_preference_children: \"audio,language,speed,text\",\n interactions_children:\n \"id,objectives,time,type,correct_responses,weighting,student_response,result,latency\",\n error_descriptions: {\n \"0\": {\n basicMessage: \"No Error\",\n detailMessage: \"No error occurred, the previous API call was successful.\",\n },\n \"101\": {\n basicMessage: \"General Exception\",\n detailMessage: \"No specific error code exists to describe the error.\",\n },\n \"201\": {\n basicMessage: \"Invalid argument error\",\n detailMessage:\n \"Indicates that an argument represents an invalid data model element or is otherwise incorrect.\",\n },\n \"202\": {\n basicMessage: \"Element cannot have children\",\n detailMessage:\n 'Indicates that LMSGetValue was called with a data model element name that ends in \"_children\" for a data model element that does not support the \"_children\" suffix.',\n },\n \"203\": {\n basicMessage: \"Element not an array - cannot have count\",\n detailMessage:\n 'Indicates that LMSGetValue was called with a data model element name that ends in \"_count\" for a data model element that does not support the \"_count\" suffix.',\n },\n \"301\": {\n basicMessage: \"Not initialized\",\n detailMessage: \"Indicates that an API call was made before the call to lmsInitialize.\",\n },\n \"401\": {\n basicMessage: \"Not implemented error\",\n detailMessage:\n \"The data model element indicated in a call to LMSGetValue or LMSSetValue is valid, but was not implemented by this LMS. SCORM 1.2 defines a set of data model elements as being optional for an LMS to implement.\",\n },\n \"402\": {\n basicMessage: \"Invalid set value, element is a keyword\",\n detailMessage:\n 'Indicates that LMSSetValue was called on a data model element that represents a keyword (elements that end in \"_children\" and \"_count\").',\n },\n \"403\": {\n basicMessage: \"Element is read only\",\n detailMessage: \"LMSSetValue was called with a data model element that can only be read.\",\n },\n \"404\": {\n basicMessage: \"Element is write only\",\n detailMessage: \"LMSGetValue was called on a data model element that can only be written to.\",\n },\n \"405\": {\n basicMessage: \"Incorrect Data Type\",\n detailMessage:\n \"LMSSetValue was called with a value that is not consistent with the data format of the supplied data model element.\",\n },\n \"407\": {\n basicMessage: \"Element Value Out Of Range\",\n detailMessage:\n \"The numeric value supplied to a LMSSetValue call is outside of the numeric range allowed for the supplied data model element.\",\n },\n \"408\": {\n basicMessage: \"Data Model Dependency Not Established\",\n detailMessage:\n \"Some data model elements cannot be set until another data model element was set. This error condition indicates that the prerequisite element was not set before the dependent element.\",\n },\n },\n};\n\nexport const scorm2004_constants: Scorm2004Constants = {\n // Children lists\n cmi_children:\n \"_version,comments_from_learner,comments_from_lms,completion_status,completion_threshold,credit,entry,exit,interactions,launch_data,learner_id,learner_name,learner_preference,location,max_time_allowed,mode,objectives,progress_measure,scaled_passing_score,score,session_time,success_status,suspend_data,time_limit_action,total_time\",\n comments_children: \"comment,timestamp,location\",\n score_children: \"max,raw,scaled,min\",\n objectives_children: \"progress_measure,completion_status,success_status,description,score,id\",\n correct_responses_children: \"pattern\",\n student_data_children: \"mastery_score,max_time_allowed,time_limit_action\",\n student_preference_children: \"audio_level,audio_captioning,delivery_speed,language\",\n interactions_children:\n \"id,type,objectives,timestamp,correct_responses,weighting,learner_response,result,latency,description\",\n adl_data_children: \"id,store\",\n error_descriptions: {\n \"0\": {\n basicMessage: \"No Error\",\n detailMessage: \"No error occurred, the previous API call was successful.\",\n },\n \"101\": {\n basicMessage: \"General Exception\",\n detailMessage: \"No specific error code exists to describe the error.\",\n },\n \"102\": {\n basicMessage: \"General Initialization Failure\",\n detailMessage: \"Call to Initialize failed for an unknown reason.\",\n },\n \"103\": {\n basicMessage: \"Already Initialized\",\n detailMessage: \"Call to Initialize failed because Initialize was already called.\",\n },\n \"104\": {\n basicMessage: \"Content Instance Terminated\",\n detailMessage: \"Call to Initialize failed because Terminate was already called.\",\n },\n \"111\": {\n basicMessage: \"General Termination Failure\",\n detailMessage: \"Call to Terminate failed for an unknown reason.\",\n },\n \"112\": {\n basicMessage: \"Termination Before Initialization\",\n detailMessage: \"Call to Terminate failed because it was made before the call to Initialize.\",\n },\n \"113\": {\n basicMessage: \"Termination After Termination\",\n detailMessage: \"Call to Terminate failed because Terminate was already called.\",\n },\n \"122\": {\n basicMessage: \"Retrieve Data Before Initialization\",\n detailMessage: \"Call to GetValue failed because it was made before the call to Initialize.\",\n },\n \"123\": {\n basicMessage: \"Retrieve Data After Termination\",\n detailMessage: \"Call to GetValue failed because it was made after the call to Terminate.\",\n },\n \"132\": {\n basicMessage: \"Store Data Before Initialization\",\n detailMessage: \"Call to SetValue failed because it was made before the call to Initialize.\",\n },\n \"133\": {\n basicMessage: \"Store Data After Termination\",\n detailMessage: \"Call to SetValue failed because it was made after the call to Terminate.\",\n },\n \"142\": {\n basicMessage: \"Commit Before Initialization\",\n detailMessage: \"Call to Commit failed because it was made before the call to Initialize.\",\n },\n \"143\": {\n basicMessage: \"Commit After Termination\",\n detailMessage: \"Call to Commit failed because it was made after the call to Terminate.\",\n },\n \"201\": {\n basicMessage: \"General Argument Error\",\n detailMessage:\n \"An invalid argument was passed to an API method (usually indicates that Initialize, Commit or Terminate did not receive the expected empty string argument.\",\n },\n \"301\": {\n basicMessage: \"General Get Failure\",\n detailMessage:\n \"Indicates a failed GetValue call where no other specific error code is applicable. Use GetDiagnostic for more information.\",\n },\n \"351\": {\n basicMessage: \"General Set Failure\",\n detailMessage:\n \"Indicates a failed SetValue call where no other specific error code is applicable. Use GetDiagnostic for more information.\",\n },\n \"391\": {\n basicMessage: \"General Commit Failure\",\n detailMessage:\n \"Indicates a failed Commit call where no other specific error code is applicable. Use GetDiagnostic for more information.\",\n },\n \"401\": {\n basicMessage: \"Undefined Data Model Element\",\n detailMessage:\n \"The data model element name passed to GetValue or SetValue is not a valid SCORM data model element.\",\n },\n \"402\": {\n basicMessage: \"Unimplemented Data Model Element\",\n detailMessage:\n \"The data model element indicated in a call to GetValue or SetValue is valid, but was not implemented by this LMS. In SCORM 2004, this error would indicate an LMS that is not fully SCORM conformant.\",\n },\n \"403\": {\n basicMessage: \"Data Model Element Value Not Initialized\",\n detailMessage:\n \"Attempt to read a data model element that has not been initialized by the LMS or through a SetValue call. This error condition is often reached during normal execution of a SCO.\",\n },\n \"404\": {\n basicMessage: \"Data Model Element Is Read Only\",\n detailMessage: \"SetValue was called with a data model element that can only be read.\",\n },\n \"405\": {\n basicMessage: \"Data Model Element Is Write Only\",\n detailMessage: \"GetValue was called on a data model element that can only be written to.\",\n },\n \"406\": {\n basicMessage: \"Data Model Element Type Mismatch\",\n detailMessage:\n \"SetValue was called with a value that is not consistent with the data format of the supplied data model element.\",\n },\n \"407\": {\n basicMessage: \"Data Model Element Value Out Of Range\",\n detailMessage:\n \"The numeric value supplied to a SetValue call is outside of the numeric range allowed for the supplied data model element.\",\n },\n \"408\": {\n basicMessage: \"Data Model Dependency Not Established\",\n detailMessage:\n \"Some data model elements cannot be set until another data model element was set. This error condition indicates that the prerequisite element was not set before the dependent element.\",\n },\n },\n};\n","import { ValidationError } from \"./base_exceptions\";\nimport { scorm12_constants } from \"../constants/api_constants\";\n\nconst scorm12_errors = scorm12_constants.error_descriptions;\n\n/**\n * SCORM 1.2 Validation Error\n */\nexport class Scorm12ValidationError extends ValidationError {\n /**\n * Constructor to take in an error code\n * @param {string} CMIElement\n * @param {number} errorCode\n */\n constructor(CMIElement: string, errorCode: number) {\n if ({}.hasOwnProperty.call(scorm12_errors, String(errorCode))) {\n super(\n CMIElement,\n errorCode,\n scorm12_errors[String(errorCode)]?.basicMessage || \"Unknown error\",\n scorm12_errors[String(errorCode)]?.detailMessage,\n );\n } else {\n super(\n CMIElement,\n 101,\n scorm12_errors[\"101\"]?.basicMessage ?? \"General error\",\n scorm12_errors[\"101\"]?.detailMessage,\n );\n }\n\n // Set the prototype explicitly.\n Object.setPrototypeOf(this, Scorm12ValidationError.prototype);\n }\n}\n","import { ValidationError } from \"./base_exceptions\";\nimport { scorm2004_constants } from \"../constants/api_constants\";\n\nconst scorm2004_errors = scorm2004_constants.error_descriptions;\n\n/**\n * SCORM 2004 Validation Error\n */\nexport class Scorm2004ValidationError extends ValidationError {\n /**\n * Constructor to take in an error code\n * @param {string} CMIElement\n * @param {number} errorCode\n */\n constructor(CMIElement: string, errorCode: number) {\n if ({}.hasOwnProperty.call(scorm2004_errors, String(errorCode))) {\n super(\n CMIElement,\n errorCode,\n scorm2004_errors[String(errorCode)]?.basicMessage || \"Unknown error\",\n scorm2004_errors[String(errorCode)]?.detailMessage,\n );\n } else {\n super(\n CMIElement,\n 101,\n scorm2004_errors[\"101\"]?.basicMessage || \"General error\",\n scorm2004_errors[\"101\"]?.detailMessage,\n );\n }\n\n // Set the prototype explicitly.\n Object.setPrototypeOf(this, Scorm2004ValidationError.prototype);\n }\n}\n","export type ErrorCode = {\n [key: string]: number;\n};\n\nexport const global_errors: ErrorCode = {\n GENERAL: 101,\n INITIALIZATION_FAILED: 101,\n INITIALIZED: 101,\n TERMINATED: 101,\n TERMINATION_FAILURE: 101,\n TERMINATION_BEFORE_INIT: 101,\n MULTIPLE_TERMINATION: 101,\n RETRIEVE_BEFORE_INIT: 101,\n RETRIEVE_AFTER_TERM: 101,\n STORE_BEFORE_INIT: 101,\n STORE_AFTER_TERM: 101,\n COMMIT_BEFORE_INIT: 101,\n COMMIT_AFTER_TERM: 101,\n ARGUMENT_ERROR: 101,\n CHILDREN_ERROR: 101,\n COUNT_ERROR: 101,\n GENERAL_GET_FAILURE: 101,\n GENERAL_SET_FAILURE: 101,\n GENERAL_COMMIT_FAILURE: 101,\n UNDEFINED_DATA_MODEL: 101,\n UNIMPLEMENTED_ELEMENT: 101,\n VALUE_NOT_INITIALIZED: 101,\n INVALID_SET_VALUE: 101,\n READ_ONLY_ELEMENT: 101,\n WRITE_ONLY_ELEMENT: 101,\n TYPE_MISMATCH: 101,\n VALUE_OUT_OF_RANGE: 101,\n DEPENDENCY_NOT_ESTABLISHED: 101,\n};\n\nexport const scorm12_errors: ErrorCode = {\n ...global_errors,\n RETRIEVE_BEFORE_INIT: 301,\n STORE_BEFORE_INIT: 301,\n COMMIT_BEFORE_INIT: 301,\n ARGUMENT_ERROR: 201,\n CHILDREN_ERROR: 202,\n COUNT_ERROR: 203,\n UNDEFINED_DATA_MODEL: 401,\n UNIMPLEMENTED_ELEMENT: 401,\n VALUE_NOT_INITIALIZED: 301,\n INVALID_SET_VALUE: 402,\n READ_ONLY_ELEMENT: 403,\n WRITE_ONLY_ELEMENT: 404,\n TYPE_MISMATCH: 405,\n VALUE_OUT_OF_RANGE: 407,\n DEPENDENCY_NOT_ESTABLISHED: 408,\n};\n\nexport const scorm2004_errors: ErrorCode = {\n ...global_errors,\n INITIALIZATION_FAILED: 102,\n INITIALIZED: 103,\n TERMINATED: 104,\n TERMINATION_FAILURE: 111,\n TERMINATION_BEFORE_INIT: 112,\n MULTIPLE_TERMINATION: 113,\n MULTIPLE_TERMINATIONS: 113,\n RETRIEVE_BEFORE_INIT: 122,\n RETRIEVE_AFTER_TERM: 123,\n STORE_BEFORE_INIT: 132,\n STORE_AFTER_TERM: 133,\n COMMIT_BEFORE_INIT: 142,\n COMMIT_AFTER_TERM: 143,\n ARGUMENT_ERROR: 201,\n GENERAL_GET_FAILURE: 301,\n GENERAL_SET_FAILURE: 351,\n GENERAL_COMMIT_FAILURE: 391,\n UNDEFINED_DATA_MODEL: 401,\n UNIMPLEMENTED_ELEMENT: 402,\n VALUE_NOT_INITIALIZED: 403,\n READ_ONLY_ELEMENT: 404,\n WRITE_ONLY_ELEMENT: 405,\n TYPE_MISMATCH: 406,\n VALUE_OUT_OF_RANGE: 407,\n DEPENDENCY_NOT_ESTABLISHED: 408,\n};\n","import { BaseCMI } from \"./base_cmi\";\nimport { BaseScormValidationError } from \"../../exceptions\";\nimport { scorm12_errors } from \"../../constants/error_codes\";\n\n/**\n * Base class for cmi *.n objects\n */\nexport class CMIArray extends BaseCMI {\n private readonly _errorCode: number;\n private readonly _errorClass: typeof BaseScormValidationError;\n private readonly __children: string;\n public childArray: BaseCMI[];\n\n /**\n * Constructor cmi *.n arrays\n * @param {object} params\n */\n constructor(params: {\n CMIElement: string;\n children: string;\n errorCode?: number;\n errorClass?: typeof BaseScormValidationError;\n }) {\n super(params.CMIElement);\n this.__children = params.children;\n this._errorCode = params.errorCode ?? (scorm12_errors.GENERAL as number);\n this._errorClass = params.errorClass || BaseScormValidationError;\n this.childArray = [];\n }\n\n /**\n * Called when the API has been reset\n */\n reset(wipe: boolean = false): void {\n this._initialized = false;\n if (wipe) {\n this.childArray = [];\n } else {\n // Reset all children\n for (let i = 0; i < this.childArray.length; i++) {\n this.childArray[i]?.reset();\n }\n }\n }\n\n /**\n * Getter for _children\n * @return {string}\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for _children. Just throws an error.\n * @param {string} _children\n */\n set _children(_children: string) {\n throw new this._errorClass(this._cmi_element + \"._children\", this._errorCode);\n }\n\n /**\n * Getter for _count\n * @return {number}\n */\n get _count(): number {\n return this.childArray.length;\n }\n\n /**\n * Setter for _count. Just throws an error.\n * @param {number} _count\n */\n set _count(_count: number) {\n throw new this._errorClass(this._cmi_element + \"._count\", this._errorCode);\n }\n\n /**\n * toJSON for *.n arrays\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result: { [key: string]: any } = {};\n for (let i = 0; i < this.childArray.length; i++) {\n result[i + \"\"] = this.childArray[i];\n }\n this.jsonString = false;\n return result;\n }\n}\n","// Using plain objects instead of enums for better compatibility with strip-only mode\n// This approach avoids TypeScript enum runtime code while maintaining the same API\n\n// NAVBoolean values\nexport const NAVBoolean = {\n UNKNOWN: \"unknown\",\n TRUE: \"true\",\n FALSE: \"false\",\n};\n\n// Type for NAVBoolean values\nexport type NAVBoolean = (typeof NAVBoolean)[keyof typeof NAVBoolean];\n\n// SuccessStatus values\nexport const SuccessStatus = {\n PASSED: \"passed\",\n FAILED: \"failed\",\n UNKNOWN: \"unknown\",\n};\n\n// Type for SuccessStatus values\nexport type SuccessStatus = (typeof SuccessStatus)[keyof typeof SuccessStatus];\n\n// CompletionStatus values\nexport const CompletionStatus = {\n COMPLETED: \"completed\",\n INCOMPLETE: \"incomplete\",\n UNKNOWN: \"unknown\",\n};\n\n// Type for CompletionStatus values\nexport type CompletionStatus = (typeof CompletionStatus)[keyof typeof CompletionStatus];\n\n// LogLevelEnum values\nexport const LogLevelEnum = {\n _: 0,\n DEBUG: 1,\n INFO: 2,\n WARN: 3,\n ERROR: 4,\n NONE: 5,\n};\n\n// Type for LogLevelEnum values\nexport type LogLevelEnum = (typeof LogLevelEnum)[keyof typeof LogLevelEnum];\n","import { InternalSettings, LogLevel, ResultObject } from \"../types/api_types\";\nimport { global_constants } from \"./api_constants\";\nimport { LogLevelEnum } from \"./enums\";\n\n/**\n * Default settings for the SCORM API\n */\nexport const DefaultSettings: InternalSettings = {\n autocommit: false,\n autocommitSeconds: 10,\n throttleCommits: false,\n useAsynchronousCommits: false,\n sendFullCommit: true,\n lmsCommitUrl: false,\n dataCommitFormat: \"json\",\n commitRequestDataType: \"application/json;charset=UTF-8\",\n autoProgress: false,\n logLevel: LogLevelEnum.ERROR,\n selfReportSessionTime: false,\n alwaysSendTotalTime: false,\n renderCommonCommitFields: false,\n autoCompleteLessonStatus: false,\n strict_errors: true,\n xhrHeaders: {},\n xhrWithCredentials: false,\n fetchMode: \"cors\",\n asyncModeBeaconBehavior: \"never\",\n responseHandler: async function (response: Response): Promise<ResultObject> {\n if (typeof response !== \"undefined\") {\n let httpResult = null;\n\n // Handle both text() and json() response methods\n try {\n if (typeof response.json === \"function\") {\n // Try to get JSON directly if the method exists\n httpResult = await response.json();\n } else if (typeof response.text === \"function\") {\n // Fall back to text() if json() is not available\n const responseText = await response.text();\n if (responseText) {\n httpResult = JSON.parse(responseText);\n }\n }\n } catch (e) {\n // If parsing fails, continue with null httpResult\n }\n\n if (httpResult === null || !{}.hasOwnProperty.call(httpResult, \"result\")) {\n if (response.status === 200) {\n return {\n result: global_constants.SCORM_TRUE,\n errorCode: 0,\n };\n } else {\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: 101,\n };\n }\n } else {\n return {\n result: httpResult.result,\n errorCode:\n typeof httpResult.errorCode === \"number\"\n ? httpResult.errorCode\n : httpResult.result === true || httpResult.result === global_constants.SCORM_TRUE\n ? 0\n : 101,\n };\n }\n }\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: 101,\n };\n },\n xhrResponseHandler: function (xhr: XMLHttpRequest): ResultObject {\n if (typeof xhr !== \"undefined\") {\n let httpResult = null;\n\n if (xhr.status >= 200 && xhr.status <= 299) {\n try {\n httpResult = JSON.parse(xhr.responseText);\n } catch (e) {\n // Parsing failed, but status was success\n }\n\n if (httpResult === null || !{}.hasOwnProperty.call(httpResult, \"result\")) {\n return { result: global_constants.SCORM_TRUE, errorCode: 0 };\n }\n return {\n result: httpResult.result,\n errorCode:\n typeof httpResult.errorCode === \"number\"\n ? httpResult.errorCode\n : httpResult.result === true || httpResult.result === global_constants.SCORM_TRUE\n ? 0\n : 101,\n };\n } else {\n return { result: global_constants.SCORM_FALSE, errorCode: 101 };\n }\n }\n return { result: global_constants.SCORM_FALSE, errorCode: 101 };\n },\n requestHandler: function (commitObject) {\n return commitObject;\n },\n onLogMessage: defaultLogHandler,\n mastery_override: false,\n score_overrides_status: false,\n completion_status_on_failed: \"completed\",\n scoItemIds: [],\n scoItemIdValidator: false,\n globalObjectiveIds: [],\n\n // Offline support settings\n enableOfflineSupport: false,\n courseId: \"\",\n syncOnInitialize: true,\n syncOnTerminate: true,\n maxSyncAttempts: 5,\n\n // Multi-SCO support settings\n scoId: \"\",\n autoPopulateCommitMetadata: false,\n\n // HTTP service settings\n httpService: null,\n\n // Global learner preferences settings\n globalStudentPreferences: false,\n};\n\nexport function defaultLogHandler(messageLevel: LogLevel, logMessage: string): void {\n switch (messageLevel) {\n case \"4\":\n case 4:\n case \"ERROR\":\n case LogLevelEnum.ERROR:\n console.error(logMessage);\n break;\n case \"3\":\n case 3:\n case \"WARN\":\n case LogLevelEnum.WARN:\n console.warn(logMessage);\n break;\n case \"2\":\n case 2:\n case \"INFO\":\n case LogLevelEnum.INFO:\n console.info(logMessage);\n break;\n case \"1\":\n case 1:\n case \"DEBUG\":\n case LogLevelEnum.DEBUG:\n if (console.debug) {\n console.debug(logMessage);\n } else {\n console.log(logMessage);\n }\n break;\n }\n}\n","/**\n * SCORM 1.2 Data Type Definitions\n * Per SCORM 1.2 RTE Appendix A: Data Type Standards\n */\nexport const scorm12_regex = {\n /** CMIString256 - Character string, max 255 chars (RTE A.1) */\n CMIString256: \"^[\\\\s\\\\S]{0,255}$\",\n /** CMIString4096 - Character string, max 4096 chars (RTE A.1) */\n CMIString4096: \"^[\\\\s\\\\S]{0,4096}$\",\n /**\n * CMIString64000 - Extended character string, max 64000 chars\n *\n * SPEC COMPLIANCE NOTE:\n * The SCORM 1.2 specification defines cmi.suspend_data as CMIString4096 (max 4096 chars).\n * This implementation intentionally increases the limit to 64000 chars (matching SCORM 2004)\n * for the following reasons:\n *\n * 1. Modern content frequently exceeds 4096 chars due to JSON state serialization,\n * base64 encoding, complex bookmark data, and rich interaction tracking\n * 2. The 4096 limit was set in 2001 when content was simpler; modern authoring tools\n * routinely generate larger suspend_data\n * 3. Most LMS systems can handle larger values - the API shouldn't be the bottleneck\n * 4. Content that gets rejected has no recovery path, causing data loss\n * 5. Aligns with SCORM 2004's more practical 64000 char limit\n *\n * Used by: cmi.suspend_data (SCORM 1.2)\n *\n * Strict spec pattern would be: ^[\\s\\S]{0,4096}$\n */\n CMIString64000: \"^[\\\\s\\\\S]{0,64000}$\",\n /**\n * CMITime - Clock time in HH:MM:SS.SS format (RTE A.2)\n * Optional centiseconds (1-2 decimal digits) per spec.\n */\n CMITime: \"^(?:[01]\\\\d|2[0123]):(?:[012345]\\\\d):(?:[012345]\\\\d)(\\\\.\\\\d{1,2})?$\",\n /**\n * CMITimespan - Time interval in HHHH:MM:SS.SS format (RTE A.3)\n * We allow more digits for the hour to support values generated\n * by getSecondsAsHHMMSS which can produce larger hour values\n * (e.g., 17496:00:00 for very long durations).\n * Changed from minimum 2 digits to 1+ digits with no upper limit.\n */\n CMITimespan: \"^([0-9]+):([0-9]{2}):([0-9]{2})(\\\\.\\\\d{1,2})?$\",\n\n /**\n * CMIInteger - Non-negative integer (RTE A.4)\n *\n * SPEC COMPLIANCE NOTE:\n * The SCORM 1.2 specification defines CMIInteger as 0-65536 range.\n * This implementation intentionally omits range validation to support\n * legacy content that may exceed this limit in _count fields or other\n * integer values. Real-world content often violates the spec by storing\n * larger values, and strict enforcement would break compatibility.\n *\n * Affected elements:\n * - cmi.objectives._count\n * - cmi.interactions._count\n * - cmi.interactions.n.objectives._count\n * - cmi.interactions.n.correct_responses._count\n */\n CMIInteger: \"^\\\\d+$\",\n /** CMISInteger - Signed integer (RTE A.5) */\n CMISInteger: \"^-?([0-9]+)$\",\n /**\n * CMIDecimal - Signed decimal (RTE A.6)\n * We set practical limits on decimals to prevent abuse while maintaining\n * broad compatibility with legacy content.\n * Increased from 3 to 10 digits before decimal to match SCORM 2004 behavior.\n */\n CMIDecimal: \"^-?([0-9]{0,10})(\\\\.[0-9]*)?$\",\n\n /**\n * CMIIdentifier - Printable ASCII characters, max 255 chars (RTE A.7)\n *\n * SPEC COMPLIANCE NOTE:\n * The SCORM 1.2 specification defines CMIIdentifier as alphanumeric only:\n * letters (a-z, A-Z), numbers (0-9), hyphens (-), and underscores (_).\n * Spaces and periods are explicitly NOT allowed per spec.\n *\n * This implementation intentionally relaxes validation to accept all\n * printable ASCII characters (0x21-0x7E) plus whitespace to support\n * legacy content. Many real-world LMS systems and content packages use\n * identifiers that violate the strict spec (e.g., student IDs with spaces,\n * objective IDs with periods or special characters).\n *\n * Strict spec pattern would be: ^[A-Za-z0-9_-]{0,255}$\n *\n * Affected elements:\n * - cmi.core.student_id\n * - cmi.objectives.n.id\n * - cmi.interactions.n.id\n * - cmi.interactions.n.objectives.n.id\n */\n CMIIdentifier: \"^[\\\\u0021-\\\\u007E\\\\s]{0,255}$\",\n /** CMICredit - Vocabulary: credit or no-credit (RTE 3.4.2.1.3) */\n CMICredit: \"^(credit|no-credit)$\",\n /** CMIEntry - Vocabulary: ab-initio, resume, or empty (RTE 3.4.2.1.4) */\n CMIEntry: \"^(ab-initio|resume|)$\",\n /** CMILessonMode - Vocabulary: normal, browse, or review (RTE 3.4.2.1.10) */\n CMILessonMode: \"^(normal|browse|review)$\",\n /** CMITimeLimitAction - Vocabulary: action combinations (RTE 3.4.2.1.11) */\n CMITimeLimitAction: \"^(exit,message|exit,no message|continue,message|continue,no message)$\",\n /**\n * CMIFeedback - Relaxed for compatibility (normally CMIString255)\n *\n * SPEC COMPLIANCE NOTE:\n * The SCORM 1.2 specification defines CMIFeedback as CMIString255 (max 255 chars)\n * with format varying by interaction type (see RTE 3.4.2.7.5, 3.4.2.7.7).\n *\n * This implementation intentionally relaxes validation for two reasons:\n *\n * 1. LENGTH: Many legacy content packages store responses exceeding 255 chars,\n * especially for fill-in and performance interaction types. Strict enforcement\n * would break existing content with no user-facing benefit.\n *\n * 2. FORMAT: The spec requires type-specific formats (e.g., true-false accepts\n * only \"0\"/\"1\"/\"t\"/\"f\", choice accepts comma-separated single chars). However:\n * - Format validation requires knowing interaction type at validation time\n * - Legacy content often uses non-standard formats\n * - The SCO is responsible for response evaluation, not the API\n * - Strict format validation provides minimal benefit vs. compatibility cost\n *\n * Affected elements:\n * - cmi.interactions.n.student_response\n * - cmi.interactions.n.correct_responses.n.pattern\n *\n * Strict spec pattern would be: ^[\\s\\S]{0,255}$ with type-specific subpatterns\n */\n CMIFeedback: \"^.*$\",\n /** CMIIndex - Pattern for array index extraction */\n CMIIndex: \"[._](\\\\d+).\",\n /** CMIStatus - Lesson status vocabulary (RTE 3.4.2.2.3) */\n CMIStatus: \"^(passed|completed|failed|incomplete|browsed)$\",\n /** CMIStatus2 - Extended status vocabulary with \"not attempted\" (RTE 3.4.2.6.2) */\n CMIStatus2: \"^(passed|completed|failed|incomplete|browsed|not attempted)$\",\n /** CMIExit - Exit vocabulary (RTE 3.4.2.1.5) */\n CMIExit: \"^(time-out|suspend|logout|)$\",\n /** CMIType - Interaction type vocabulary (RTE 3.4.2.7.2) */\n CMIType: \"^(true-false|choice|fill-in|matching|performance|sequencing|likert|numeric)$\",\n /** CMIResult - Interaction result vocabulary (RTE 3.4.2.7.6) */\n CMIResult: \"^(correct|wrong|unanticipated|neutral|([0-9]{0,3})?(\\\\.[0-9]*)?)$\",\n /** NAVEvent - Navigation event vocabulary (SCORM 1.2 extension) */\n NAVEvent:\n \"^(_?(previous|continue|start|resumeAll|exit|exitAll|abandon|abandonAll|suspendAll|retry|retryAll)|choice|jump|_none_)$\",\n /** score_range - Valid score range 0-100 (RTE 3.4.2.2.2) */\n score_range: \"0#100\",\n /** audio_range - Audio level range -1 to 100 (RTE 3.4.2.3.1) */\n audio_range: \"-1#100\",\n /** speed_range - Playback speed range -100 to 100 (RTE 3.4.2.3.2) */\n speed_range: \"-100#100\",\n /** weighting_range - Interaction weighting range -100 to 100 (RTE 3.4.2.7.4) */\n weighting_range: \"-100#100\",\n /** text_range - Text display preference -1 to 1 (RTE 3.4.2.3.3) */\n text_range: \"-1#1\",\n};\n\n/**\n * SCORM 2004 Data Type Definitions\n * Per SCORM 2004 4th Edition RTE Appendix C: Data Type Standards\n */\nexport const scorm2004_regex = {\n /** CMIString200 - Character string, max 200 chars (RTE C.1.1) */\n CMIString200: \"^[\\\\u0000-\\\\uFFFF]{0,200}$\",\n /** CMIString250 - Character string, max 250 chars (RTE C.1.1) */\n CMIString250: \"^[\\\\u0000-\\\\uFFFF]{0,250}$\",\n /** CMIString1000 - Character string, max 1000 chars (RTE C.1.1) */\n CMIString1000: \"^[\\\\u0000-\\\\uFFFF]{0,1000}$\",\n /** CMIString4000 - Character string, max 4000 chars (RTE C.1.1) */\n CMIString4000: \"^[\\\\u0000-\\\\uFFFF]{0,4000}$\",\n /** CMIString64000 - Character string, max 64000 chars (RTE C.1.1) */\n CMIString64000: \"^[\\\\u0000-\\\\uFFFF]{0,64000}$\",\n /**\n * CMILang - Language code per RFC 1766/RFC 3066 (RTE C.1.2)\n * Primary tag: 1-8 characters (ISO 639-1: 2, ISO 639-2: 3, or i/x for IANA/private)\n * Subtag: 2-8 alphanumeric characters\n */\n CMILang: \"^([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?$|^$\",\n\n /** CMILangString250 - String with optional language tag, max 250 chars (RTE C.1.3) */\n CMILangString250: \"^({lang=([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?})?((?!{.*$).{0,250}$)?$\",\n\n /** CMILangcr - Language tag pattern with content */\n CMILangcr: \"^(({lang=([a-zA-Z]{1,8}|i|x)?(-[a-zA-Z0-9-]{2,8})?}))(.*?)$\",\n\n /** CMILangString250cr - String with optional language tag (carriage return variant) */\n CMILangString250cr: \"^(({lang=([a-zA-Z]{1,8}|i|x)?(-[a-zA-Z0-9-]{2,8})?})?(.{0,250})?)?$\",\n\n /** CMILangString4000 - String with optional language tag, max 4000 chars (RTE C.1.3) */\n CMILangString4000: \"^({lang=([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?})?((?!{.*$).{0,4000}$)?$\",\n\n /**\n * CMITime - ISO 8601 timestamp format (RTE C.1.4)\n * Year range expanded from 1970-2038 to 1970-9999 to support future dates\n */\n CMITime:\n \"^(19[7-9][0-9]|[2-9][0-9]{3})((-(0[1-9]|1[0-2]))((-(0[1-9]|[1-2][0-9]|3[0-1]))(T([0-1][0-9]|2[0-3])((:[0-5][0-9])((:[0-5][0-9])((\\\\.[0-9]{1,6})((Z|([+|-]([0-1][0-9]|2[0-3])))(:[0-5][0-9])?)?)?)?)?)?)?)?$\",\n /** CMITimespan - ISO 8601 duration format (RTE C.1.5) */\n CMITimespan:\n \"^P(?:([.,\\\\d]+)Y)?(?:([.,\\\\d]+)M)?(?:([.,\\\\d]+)W)?(?:([.,\\\\d]+)D)?(?:T?(?:([.,\\\\d]+)H)?(?:([.,\\\\d]+)M)?(?:(\\\\d+(?:\\\\.\\\\d{1,2})?)S)?)?$\",\n /** CMIInteger - Non-negative integer (RTE C.1.6) */\n CMIInteger: \"^\\\\d+$\",\n /** CMISInteger - Signed integer (RTE C.1.7) */\n CMISInteger: \"^-?([0-9]+)$\",\n /**\n * CMIDecimal - Signed decimal (RTE C.1.8)\n * Spec allows unlimited digits, but we set practical limits to prevent abuse\n * while maintaining broad compatibility:\n * - Up to 10 digits before decimal (supports values up to 10 billion)\n * - Up to 18 digits after decimal (maintains precision for scientific use)\n */\n CMIDecimal: \"^-?([0-9]{1,10})(\\\\.[0-9]{1,18})?$\",\n /**\n * CMIIdentifier - Identifier with alphanumeric ending, max 250 chars (RTE C.1.9)\n * Must contain at least one word character (\\w) and only allow: letters,\n * numbers, - ( ) + . : = @ ; $ _ ! * ' % / #\n * URN format is validated separately if string starts with \"urn:\"\n */\n CMIIdentifier: \"^(?=.*\\\\w)[\\\\w\\\\-\\\\(\\\\)\\\\+\\\\.\\\\:\\\\=\\\\@\\\\;\\\\$\\\\_\\\\!\\\\*\\\\'\\\\%\\\\/\\\\#]{1,250}$\",\n /** CMIShortIdentifier - Short identifier conforming to URI syntax, max 250 chars (RTE C.1.10) */\n CMIShortIdentifier: \"^(?=.*\\\\w)[\\\\w\\\\-\\\\(\\\\)\\\\+\\\\.\\\\:\\\\=\\\\@\\\\;\\\\$\\\\_\\\\!\\\\*\\\\'\\\\%\\\\/\\\\#]{1,250}$\",\n /** CMILongIdentifier - Long identifier supporting URN format, max 4000 chars (RTE C.1.11) */\n CMILongIdentifier: \"^(?:(?!urn:)\\\\S{1,4000}|urn:[A-Za-z0-9-]{1,31}:\\\\S{1,4000}|.{1,4000})$\",\n /** CMIFeedback - Unrestricted feedback text (RTE C.1.12) */\n CMIFeedback: \"^.*$\",\n /** CMIIndex - Pattern for array index extraction */\n CMIIndex: \"[._](\\\\d+).\",\n /** CMIIndexStore - Pattern for stored index notation */\n CMIIndexStore: \".N(\\\\d+).\",\n /** CMICStatus - Completion status vocabulary (RTE 4.1.4) */\n CMICStatus: \"^(completed|incomplete|not attempted|unknown)$\",\n /** CMISStatus - Success status vocabulary (RTE 4.1.11) */\n CMISStatus: \"^(passed|failed|unknown)$\",\n /** CMIExit - Exit vocabulary (RTE 4.1.3) */\n CMIExit: \"^(time-out|suspend|logout|normal)$\",\n /** CMIType - Interaction type vocabulary (RTE 4.1.6.2) */\n CMIType:\n \"^(true-false|choice|fill-in|long-fill-in|matching|performance|sequencing|likert|numeric|other)$\",\n /** CMIResult - Interaction result vocabulary (RTE 4.1.6.8) */\n CMIResult: \"^(correct|incorrect|unanticipated|neutral|-?([0-9]{1,4})(\\\\.[0-9]{1,18})?)$\",\n /** NAVEvent - Navigation event vocabulary (SN Book Table 4.4.2) */\n NAVEvent:\n \"^(_?(start|resumeAll|previous|continue|exit|exitAll|abandon|abandonAll|suspendAll|retry|retryAll)|_none_|(\\\\{target=(?<choice_target>\\\\S{0,}[a-zA-Z0-9-_]+)})?choice|(\\\\{target=(?<jump_target>\\\\S{0,}[a-zA-Z0-9-_]+)})?jump)$\",\n\n /** NAVBoolean - Navigation boolean vocabulary (SN Book) */\n NAVBoolean: \"^(unknown|true|false)$\",\n /** NAVTarget - Navigation target pattern (SN Book) */\n NAVTarget: \"^{target=\\\\S{0,}[a-zA-Z0-9-_]+}$\",\n /** scaled_range - Scaled score range -1 to 1 (RTE 4.1.10.1) */\n scaled_range: \"-1#1\",\n /** audio_range - Audio level range 0 to 999.9999999 (RTE 4.1.7.1) */\n audio_range: \"0#999.9999999\",\n /** speed_range - Playback speed range 0 to 999.9999999 (RTE 4.1.7.4) */\n speed_range: \"0#999.9999999\",\n /** text_range - Text display preference -1 to 1 (RTE 4.1.7.5) */\n text_range: \"-1#1\",\n /** progress_range - Progress measure range 0 to 1 (RTE 4.1.8) */\n progress_range: \"0#1\",\n};\n","import { scorm2004_regex } from \"./regex\";\n\nexport const LearnerResponses: Responses = {\n \"true-false\": {\n format: \"^true$|^false$\",\n max: 1,\n delimiter: \"\",\n unique: false,\n },\n choice: {\n format: scorm2004_regex.CMILongIdentifier,\n max: 36,\n delimiter: \"[,]\",\n unique: true,\n },\n \"fill-in\": {\n format: scorm2004_regex.CMILangString250,\n max: 10,\n delimiter: \"[,]\",\n unique: false,\n },\n \"long-fill-in\": {\n format: scorm2004_regex.CMILangString4000,\n max: 1,\n delimiter: \"\",\n unique: false,\n },\n matching: {\n format: scorm2004_regex.CMIShortIdentifier,\n format2: scorm2004_regex.CMIShortIdentifier,\n max: 36,\n delimiter: \"[,]\",\n delimiter2: \"[.]\",\n unique: false,\n },\n performance: {\n format: \"^$|\" + scorm2004_regex.CMIShortIdentifier,\n format2: scorm2004_regex.CMIDecimal + \"|^$|\" + scorm2004_regex.CMIShortIdentifier,\n max: 250,\n delimiter: \"[,]\",\n delimiter2: \"[.]\",\n unique: false,\n },\n sequencing: {\n format: scorm2004_regex.CMIShortIdentifier,\n max: 36,\n delimiter: \"[,]\",\n unique: false,\n },\n likert: {\n format: scorm2004_regex.CMIShortIdentifier,\n max: 1,\n delimiter: \"\",\n unique: false,\n },\n numeric: {\n format: scorm2004_regex.CMIDecimal,\n max: 1,\n delimiter: \"\",\n unique: false,\n },\n other: {\n format: scorm2004_regex.CMIString4000,\n max: 1,\n delimiter: \"\",\n unique: false,\n },\n};\nexport const CorrectResponses: Responses = {\n \"true-false\": {\n max: 1,\n delimiter: \"\",\n unique: false,\n duplicate: false,\n format: \"^true$|^false$\",\n limit: 1,\n },\n choice: {\n max: 36,\n delimiter: \"[,]\",\n unique: true,\n duplicate: false,\n format: scorm2004_regex.CMILongIdentifier,\n },\n \"fill-in\": {\n max: 10,\n delimiter: \"[,]\",\n unique: false,\n duplicate: false,\n format: scorm2004_regex.CMILangString250cr,\n },\n \"long-fill-in\": {\n max: 1,\n delimiter: \"\",\n unique: false,\n duplicate: true,\n format: scorm2004_regex.CMILangString4000,\n },\n matching: {\n max: 36,\n delimiter: \"[,]\",\n delimiter2: \"[.]\",\n unique: false,\n duplicate: false,\n format: scorm2004_regex.CMIShortIdentifier,\n format2: scorm2004_regex.CMIShortIdentifier,\n },\n performance: {\n max: 250,\n delimiter: \"[,]\",\n delimiter2: \"[.]\",\n unique: false,\n duplicate: false,\n // step_name must be a non-empty short identifier\n format: scorm2004_regex.CMIShortIdentifier,\n // step_answer may be short identifier or numeric range (<decimal>[:<decimal>])\n format2: `^(${scorm2004_regex.CMIShortIdentifier})$|^(?:\\\\d+(?:\\\\.\\\\d+)?(?::\\\\d+(?:\\\\.\\\\d+)?)?)$`,\n },\n sequencing: {\n max: 36,\n delimiter: \"[,]\",\n unique: false,\n duplicate: false,\n format: scorm2004_regex.CMIShortIdentifier,\n },\n likert: {\n max: 1,\n delimiter: \"\",\n unique: false,\n duplicate: false,\n format: scorm2004_regex.CMIShortIdentifier,\n limit: 1,\n },\n numeric: {\n max: 2,\n delimiter: \"[:]\",\n unique: false,\n duplicate: false,\n format: scorm2004_regex.CMIDecimal,\n limit: 1,\n },\n other: {\n max: 1,\n delimiter: \"\",\n unique: false,\n duplicate: false,\n format: scorm2004_regex.CMIString4000,\n limit: 1,\n },\n};\n\nexport type ResponseType = {\n format: string;\n max: number;\n delimiter: string;\n unique: boolean;\n duplicate?: boolean;\n format2?: string;\n delimiter2?: string;\n limit?: number;\n};\n\nexport type Responses = {\n [key: string]: ResponseType;\n};\n","const ValidLanguages: string[] = [\n \"aa\",\n \"ab\",\n \"ae\",\n \"af\",\n \"ak\",\n \"am\",\n \"an\",\n \"ar\",\n \"as\",\n \"av\",\n \"ay\",\n \"az\",\n \"ba\",\n \"be\",\n \"bg\",\n \"bh\",\n \"bi\",\n \"bm\",\n \"bn\",\n \"bo\",\n \"br\",\n \"bs\",\n \"ca\",\n \"ce\",\n \"ch\",\n \"co\",\n \"cr\",\n \"cs\",\n \"cu\",\n \"cv\",\n \"cy\",\n \"da\",\n \"de\",\n \"dv\",\n \"dz\",\n \"ee\",\n \"el\",\n \"en\",\n \"eo\",\n \"es\",\n \"et\",\n \"eu\",\n \"fa\",\n \"ff\",\n \"fi\",\n \"fj\",\n \"fo\",\n \"fr\",\n \"fy\",\n \"ga\",\n \"gd\",\n \"gl\",\n \"gn\",\n \"gu\",\n \"gv\",\n \"ha\",\n \"he\",\n \"hi\",\n \"ho\",\n \"hr\",\n \"ht\",\n \"hu\",\n \"hy\",\n \"hz\",\n \"ia\",\n \"id\",\n \"ie\",\n \"ig\",\n \"ii\",\n \"ik\",\n \"io\",\n \"is\",\n \"it\",\n \"iu\",\n \"ja\",\n \"jv\",\n \"ka\",\n \"kg\",\n \"ki\",\n \"kj\",\n \"kk\",\n \"kl\",\n \"km\",\n \"kn\",\n \"ko\",\n \"kr\",\n \"ks\",\n \"ku\",\n \"kv\",\n \"kw\",\n \"ky\",\n \"la\",\n \"lb\",\n \"lg\",\n \"li\",\n \"ln\",\n \"lo\",\n \"lt\",\n \"lu\",\n \"lv\",\n \"mg\",\n \"mh\",\n \"mi\",\n \"mk\",\n \"ml\",\n \"mn\",\n \"mo\",\n \"mr\",\n \"ms\",\n \"mt\",\n \"my\",\n \"na\",\n \"nb\",\n \"nd\",\n \"ne\",\n \"ng\",\n \"nl\",\n \"nn\",\n \"no\",\n \"nr\",\n \"nv\",\n \"ny\",\n \"oc\",\n \"oj\",\n \"om\",\n \"or\",\n \"os\",\n \"pa\",\n \"pi\",\n \"pl\",\n \"ps\",\n \"pt\",\n \"qu\",\n \"rm\",\n \"rn\",\n \"ro\",\n \"ru\",\n \"rw\",\n \"sa\",\n \"sc\",\n \"sd\",\n \"se\",\n \"sg\",\n \"sh\",\n \"si\",\n \"sk\",\n \"sl\",\n \"sm\",\n \"sn\",\n \"so\",\n \"sq\",\n \"sr\",\n \"ss\",\n \"st\",\n \"su\",\n \"sv\",\n \"sw\",\n \"ta\",\n \"te\",\n \"tg\",\n \"th\",\n \"ti\",\n \"tk\",\n \"tl\",\n \"tn\",\n \"to\",\n \"tr\",\n \"ts\",\n \"tt\",\n \"tw\",\n \"ty\",\n \"ug\",\n \"uk\",\n \"ur\",\n \"uz\",\n \"ve\",\n \"vi\",\n \"vo\",\n \"wa\",\n \"wo\",\n \"xh\",\n \"yi\",\n \"yo\",\n \"za\",\n \"zh\",\n \"zu\",\n \"aar\",\n \"abk\",\n \"ave\",\n \"afr\",\n \"aka\",\n \"amh\",\n \"arg\",\n \"ara\",\n \"asm\",\n \"ava\",\n \"aym\",\n \"aze\",\n \"bak\",\n \"bel\",\n \"bul\",\n \"bih\",\n \"bis\",\n \"bam\",\n \"ben\",\n \"tib\",\n \"bod\",\n \"bre\",\n \"bos\",\n \"cat\",\n \"che\",\n \"cha\",\n \"cos\",\n \"cre\",\n \"cze\",\n \"ces\",\n \"chu\",\n \"chv\",\n \"wel\",\n \"cym\",\n \"dan\",\n \"ger\",\n \"deu\",\n \"div\",\n \"dzo\",\n \"ewe\",\n \"gre\",\n \"ell\",\n \"eng\",\n \"epo\",\n \"spa\",\n \"est\",\n \"baq\",\n \"eus\",\n \"per\",\n \"fas\",\n \"ful\",\n \"fin\",\n \"fij\",\n \"fao\",\n \"fre\",\n \"fra\",\n \"fry\",\n \"gle\",\n \"gla\",\n \"glg\",\n \"grn\",\n \"guj\",\n \"glv\",\n \"hau\",\n \"heb\",\n \"hin\",\n \"hmo\",\n \"hrv\",\n \"hat\",\n \"hun\",\n \"arm\",\n \"hye\",\n \"her\",\n \"ina\",\n \"ind\",\n \"ile\",\n \"ibo\",\n \"iii\",\n \"ipk\",\n \"ido\",\n \"ice\",\n \"isl\",\n \"ita\",\n \"iku\",\n \"jpn\",\n \"jav\",\n \"geo\",\n \"kat\",\n \"kon\",\n \"kik\",\n \"kua\",\n \"kaz\",\n \"kal\",\n \"khm\",\n \"kan\",\n \"kor\",\n \"kau\",\n \"kas\",\n \"kur\",\n \"kom\",\n \"cor\",\n \"kir\",\n \"lat\",\n \"ltz\",\n \"lug\",\n \"lim\",\n \"lin\",\n \"lao\",\n \"lit\",\n \"lub\",\n \"lav\",\n \"mlg\",\n \"mah\",\n \"mao\",\n \"mri\",\n \"mac\",\n \"mkd\",\n \"mal\",\n \"mon\",\n \"mol\",\n \"mar\",\n \"may\",\n \"msa\",\n \"mlt\",\n \"bur\",\n \"mya\",\n \"nau\",\n \"nob\",\n \"nde\",\n \"nep\",\n \"ndo\",\n \"dut\",\n \"nld\",\n \"nno\",\n \"nor\",\n \"nbl\",\n \"nav\",\n \"nya\",\n \"oci\",\n \"oji\",\n \"orm\",\n \"ori\",\n \"oss\",\n \"pan\",\n \"pli\",\n \"pol\",\n \"pus\",\n \"por\",\n \"que\",\n \"roh\",\n \"run\",\n \"rum\",\n \"ron\",\n \"rus\",\n \"kin\",\n \"san\",\n \"srd\",\n \"snd\",\n \"sme\",\n \"sag\",\n \"slo\",\n \"sin\",\n \"slk\",\n \"slv\",\n \"smo\",\n \"sna\",\n \"som\",\n \"alb\",\n \"sqi\",\n \"srp\",\n \"ssw\",\n \"sot\",\n \"sun\",\n \"swe\",\n \"swa\",\n \"tam\",\n \"tel\",\n \"tgk\",\n \"tha\",\n \"tir\",\n \"tuk\",\n \"tgl\",\n \"tsn\",\n \"ton\",\n \"tur\",\n \"tso\",\n \"tat\",\n \"twi\",\n \"tah\",\n \"uig\",\n \"ukr\",\n \"urd\",\n \"uzb\",\n \"ven\",\n \"vie\",\n \"vol\",\n \"wln\",\n \"wol\",\n \"xho\",\n \"yid\",\n \"yor\",\n \"zha\",\n \"chi\",\n \"zho\",\n \"zul\",\n];\nexport default ValidLanguages;\n","import BaseAPI from \"../BaseAPI\";\n\n/**\n * Private class that wraps a timeout call to the commit() function\n */\nexport class ScheduledCommit {\n private _API;\n private _cancelled = false;\n private readonly _timeout;\n private readonly _callback;\n\n /**\n * Constructor for ScheduledCommit\n * @param {BaseAPI} API\n * @param {number} when\n * @param {string} callback\n */\n constructor(API: BaseAPI, when: number, callback: string) {\n this._API = API;\n this._timeout = setTimeout(this.wrapper.bind(this), when);\n this._callback = callback;\n }\n\n /**\n * Cancel any currently scheduled commit\n */\n cancel() {\n this._cancelled = true;\n if (this._timeout) {\n clearTimeout(this._timeout);\n }\n }\n\n /**\n * Wrap the API commit call to check if the call has already been canceled\n */\n wrapper() {\n if (!this._cancelled) {\n // Only proceed with scheduled commit if API is properly initialized\n if (this._API.isInitialized()) {\n (async () => await this._API.commit(this._callback))();\n }\n }\n }\n}\n","/**\n * Types for SCORM 2004 sequencing configuration\n */\n\nimport { IActivity } from \"./activity_types\";\nimport {\n RuleActionType,\n RuleConditionOperator,\n RuleConditionType,\n} from \"../cmi/scorm2004/sequencing/sequencing_rules\";\nimport {\n RollupActionType,\n RollupConditionType,\n RollupConsiderationType,\n} from \"../cmi/scorm2004/sequencing/rollup_rules\";\nimport {\n RandomizationTiming,\n SelectionTiming,\n} from \"../cmi/scorm2004/sequencing/sequencing_controls\";\n\nexport const HIDE_LMS_UI_TOKENS = [\n \"continue\",\n \"previous\",\n \"exit\",\n \"exitAll\",\n \"abandon\",\n \"abandonAll\",\n \"suspendAll\",\n] as const;\n\nexport type HideLmsUiItem = (typeof HIDE_LMS_UI_TOKENS)[number];\n\nexport type AuxiliaryResourceSettings = {\n resourceId: string;\n purpose: string;\n};\n\nexport type AuxiliaryResource = AuxiliaryResourceSettings;\n\n/**\n * Settings for an activity in the activity tree\n */\nexport type AdlRollupConsiderationRequirement =\n | \"always\"\n | \"ifAttempted\"\n | \"ifNotSkipped\"\n | \"ifNotSuspended\";\n\nexport type AdlRollupConsiderationsSettings = {\n requiredForSatisfied?: AdlRollupConsiderationRequirement;\n requiredForNotSatisfied?: AdlRollupConsiderationRequirement;\n requiredForCompleted?: AdlRollupConsiderationRequirement;\n requiredForIncomplete?: AdlRollupConsiderationRequirement;\n measureSatisfactionIfActive?: boolean;\n};\n\nexport type ActivitySettings = {\n id: string;\n title: string;\n children?: ActivitySettings[];\n isVisible?: boolean;\n isActive?: boolean;\n isSuspended?: boolean;\n isCompleted?: boolean;\n isHiddenFromChoice?: boolean;\n isAvailable?: boolean;\n attemptLimit?: number | null;\n attemptAbsoluteDurationLimit?: string | null;\n activityAbsoluteDurationLimit?: string | null;\n timeLimitAction?: string | null;\n timeLimitDuration?: string | null;\n beginTimeLimit?: string | null;\n endTimeLimit?: string | null;\n primaryObjective?: ObjectiveSettings;\n objectives?: ObjectiveSettings[];\n // Optional per-activity sequencing configuration\n sequencingRules?: SequencingRulesSettings;\n sequencingControls?: SequencingControlsSettings;\n rollupRules?: RollupRulesSettings;\n rollupConsiderations?: AdlRollupConsiderationsSettings;\n selectionRandomizationState?: SelectionRandomizationStateSettings;\n hideLmsUi?: HideLmsUiItem[];\n sequencingCollectionRefs?: string | string[];\n auxiliaryResources?: AuxiliaryResourceSettings[];\n};\n\n/**\n * Settings for objective map info entries\n */\nexport type ObjectiveMapInfoSettings = {\n targetObjectiveID: string;\n readSatisfiedStatus?: boolean;\n readNormalizedMeasure?: boolean;\n writeSatisfiedStatus?: boolean;\n writeNormalizedMeasure?: boolean;\n readCompletionStatus?: boolean;\n writeCompletionStatus?: boolean;\n readProgressMeasure?: boolean;\n writeProgressMeasure?: boolean;\n readRawScore?: boolean;\n writeRawScore?: boolean;\n readMinScore?: boolean;\n writeMinScore?: boolean;\n readMaxScore?: boolean;\n writeMaxScore?: boolean;\n updateAttemptData?: boolean;\n};\n\n/**\n * Settings for activity objectives\n */\nexport type ObjectiveSettings = {\n objectiveID: string;\n description?: string;\n isPrimary?: boolean;\n satisfiedByMeasure?: boolean;\n minNormalizedMeasure?: number | null;\n mapInfo?: ObjectiveMapInfoSettings[];\n};\n\n/**\n * Settings for a rule condition\n */\nexport type RuleConditionSettings = {\n condition: RuleConditionType;\n operator?: RuleConditionOperator;\n referencedObjective?: string;\n parameters?: Record<string, any>;\n};\n\n/**\n * Settings for a sequencing rule\n */\nexport type SequencingRuleSettings = {\n action: RuleActionType;\n conditionCombination?: RuleConditionOperator;\n conditions: RuleConditionSettings[];\n};\n\n/**\n * Settings for sequencing rules\n */\nexport type SequencingRulesSettings = {\n preConditionRules?: SequencingRuleSettings[];\n exitConditionRules?: SequencingRuleSettings[];\n postConditionRules?: SequencingRuleSettings[];\n};\n\n/**\n * Settings for sequencing controls\n */\nexport type SequencingControlsSettings = {\n enabled?: boolean;\n choice?: boolean;\n choiceExit?: boolean;\n flow?: boolean;\n forwardOnly?: boolean;\n useCurrentAttemptObjectiveInfo?: boolean;\n useCurrentAttemptProgressInfo?: boolean;\n preventActivation?: boolean;\n constrainChoice?: boolean;\n stopForwardTraversal?: boolean;\n rollupObjectiveSatisfied?: boolean;\n rollupProgressCompletion?: boolean;\n objectiveMeasureWeight?: number;\n selectionTiming?: SelectionTiming;\n selectCount?: number | null;\n randomizeChildren?: boolean;\n randomizationTiming?: RandomizationTiming;\n selectionCountStatus?: boolean;\n reorderChildren?: boolean;\n completionSetByContent?: boolean;\n objectiveSetByContent?: boolean;\n};\n\nexport type SelectionRandomizationStateSettings = {\n childOrder?: string[];\n selectedChildIds?: string[];\n hiddenFromChoiceChildIds?: string[];\n selectionCountStatus?: boolean;\n reorderChildren?: boolean;\n};\n\nexport type SequencingCollectionSettings = {\n sequencingControls?: SequencingControlsSettings;\n sequencingRules?: SequencingRulesSettings;\n rollupRules?: RollupRulesSettings;\n rollupConsiderations?: AdlRollupConsiderationsSettings;\n selectionRandomizationState?: SelectionRandomizationStateSettings;\n hideLmsUi?: HideLmsUiItem[];\n auxiliaryResources?: AuxiliaryResourceSettings[];\n};\n\n/**\n * Settings for a rollup condition\n */\nexport type RollupConditionSettings = {\n condition: RollupConditionType;\n parameters?: Record<string, any>;\n};\n\n/**\n * Settings for a rollup rule\n */\nexport type RollupRuleSettings = {\n action: RollupActionType;\n consideration?: RollupConsiderationType;\n minimumCount?: number;\n minimumPercent?: number;\n conditions: RollupConditionSettings[];\n};\n\n/**\n * Settings for rollup rules\n */\nexport type RollupRulesSettings = {\n rules: RollupRuleSettings[];\n};\n\n/**\n * Settings for SCORM 2004 sequencing\n */\nexport type SequencingSettings = {\n activityTree?: ActivitySettings;\n sequencingRules?: SequencingRulesSettings;\n sequencingControls?: SequencingControlsSettings;\n rollupRules?: RollupRulesSettings;\n hideLmsUi?: HideLmsUiItem[];\n auxiliaryResources?: AuxiliaryResourceSettings[];\n collections?: Record<string, SequencingCollectionSettings>;\n\n // Runtime sequencing configuration\n autoRollupOnCMIChange?: boolean;\n autoProgressOnCompletion?: boolean;\n validateNavigationRequests?: boolean;\n enableEventSystem?: boolean;\n logLevel?: \"debug\" | \"info\" | \"warn\" | \"error\";\n eventListeners?: SequencingEventListeners;\n};\n\n/**\n * Interface for sequencing event listeners.\n * Uses IActivity interface to provide type safety without circular dependencies.\n */\nexport interface SequencingEventListeners {\n onSequencingStart?: (activity: IActivity) => void;\n onSequencingEnd?: () => void;\n onActivityDelivery?: (activity: IActivity) => void;\n onActivityUnload?: (activity: IActivity) => void;\n onNavigationRequest?: (request: string, target?: string) => void;\n onRollupComplete?: (activity: IActivity) => void;\n onSequencingError?: (error: string, context?: string) => void;\n onSequencingSessionEnd?: (data: {\n reason: string;\n exception?: string | null;\n navigationRequest?: string;\n }) => void;\n onAutoCompletion?: (data: { activity: string; completionStatus: string }) => void;\n onAutoSatisfaction?: (data: { activity: string; satisfiedStatus: boolean }) => void;\n onPostConditionExitParent?: (data: { activity: string }) => void;\n onPostConditionExitAll?: (data: { activity: string }) => void;\n onTerminationRequestProcessing?: (data: {\n request: string;\n hasSequencingRequest: boolean;\n currentActivity: string;\n }) => void;\n onNavigationRequestProcessing?: (data: {\n request: string;\n targetActivityId: string | null;\n }) => void;\n onPostConditionEvaluated?: (data: {\n activity: string;\n result: string;\n iteration: number;\n }) => void;\n onMultiLevelExitAction?: (data: { activity: string }) => void;\n onSuspendedActivityCleanup?: (data: { activity: string }) => void;\n onSuspendError?: (data: { activity: string; error: string }) => void;\n onActivitySuspended?: (data: { activity: string }) => void;\n onDeliveryRequestProcessing?: (data: { request: string; target: string | null }) => void;\n onNavigationValidityUpdate?: (data: {\n currentActivity: string | null;\n validRequests: string[];\n }) => void;\n onLimitConditionCheck?: (activity: IActivity, result: boolean) => void;\n onStateInconsistency?: (data: { activity: string; issue: string }) => void;\n onGlobalObjectiveMapInitialized?: (data: { count: number }) => void;\n onGlobalObjectiveMapError?: (data: { error: string }) => void;\n onGlobalObjectiveUpdated?: (data: { objectiveId: string; field: string; value: any }) => void;\n onGlobalObjectiveUpdateError?: (data: { objectiveId: string; error: string }) => void;\n onSequencingDebug?: (event: string, data?: any) => void;\n // Enhanced debugging events\n onActivityAttemptStart?: (activity: IActivity) => void;\n onActivityAttemptEnd?: (activity: IActivity) => void;\n onSequencingStateChange?: (state: any) => void;\n}\n","import { BaseCMI } from \"../../common/base_cmi\";\nimport { Activity, ActivityObjective } from \"./activity\";\nimport { Scorm2004ValidationError } from \"../../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors } from \"../../../constants/error_codes\";\nimport { SuccessStatus, CompletionStatus } from \"../../../constants/enums\";\nimport { getDurationAsSeconds } from \"../../../utilities\";\nimport { scorm2004_regex } from \"../../../constants/regex\";\n\n/**\n * Enum for rule condition operators\n */\nexport enum RuleConditionOperator {\n NOT = \"not\",\n AND = \"and\",\n OR = \"or\",\n}\n\n/**\n * Enum for rule condition types\n */\nexport enum RuleConditionType {\n SATISFIED = \"satisfied\",\n OBJECTIVE_SATISFIED = \"objectiveSatisfied\",\n OBJECTIVE_STATUS_KNOWN = \"objectiveStatusKnown\",\n OBJECTIVE_MEASURE_KNOWN = \"objectiveMeasureKnown\",\n OBJECTIVE_MEASURE_GREATER_THAN = \"objectiveMeasureGreaterThan\",\n OBJECTIVE_MEASURE_LESS_THAN = \"objectiveMeasureLessThan\",\n COMPLETED = \"completed\",\n ACTIVITY_COMPLETED = \"activityCompleted\",\n PROGRESS_KNOWN = \"progressKnown\",\n ACTIVITY_PROGRESS_KNOWN = \"activityProgressKnown\",\n ATTEMPTED = \"attempted\",\n ATTEMPT_LIMIT_EXCEEDED = \"attemptLimitExceeded\",\n TIME_LIMIT_EXCEEDED = \"timeLimitExceeded\",\n OUTSIDE_AVAILABLE_TIME_RANGE = \"outsideAvailableTimeRange\",\n ALWAYS = \"always\",\n NEVER = \"never\",\n}\n\n/**\n * Enum for rule action types\n */\nexport enum RuleActionType {\n SKIP = \"skip\",\n DISABLED = \"disabled\",\n HIDE_FROM_CHOICE = \"hiddenFromChoice\",\n STOP_FORWARD_TRAVERSAL = \"stopForwardTraversal\",\n EXIT_PARENT = \"exitParent\",\n EXIT_ALL = \"exitAll\",\n RETRY = \"retry\",\n RETRY_ALL = \"retryAll\",\n CONTINUE = \"continue\",\n PREVIOUS = \"previous\",\n EXIT = \"exit\",\n}\n\n/**\n * Class representing a sequencing rule condition\n */\nexport class RuleCondition extends BaseCMI {\n private _condition: RuleConditionType = RuleConditionType.ALWAYS;\n private _operator: RuleConditionOperator | null = null;\n private _parameters: Map<string, any> = new Map();\n private _referencedObjective: string | null = null;\n // Optional, overridable provider for current time (LMS may set via SequencingService)\n private static _now: () => Date = () => new Date();\n // Optional, overridable hook for getting elapsed seconds\n private static _getElapsedSecondsHook:\n | ((activity: Activity) => number)\n | undefined = undefined;\n\n /**\n * Constructor for RuleCondition\n * @param {RuleConditionType} condition - The condition type\n * @param {RuleConditionOperator | null} operator - The operator (null for no operator)\n * @param {Map<string, any>} parameters - Additional parameters for the condition\n */\n constructor(\n condition: RuleConditionType = RuleConditionType.ALWAYS,\n operator: RuleConditionOperator | null = null,\n parameters: Map<string, any> = new Map(),\n ) {\n super(\"ruleCondition\");\n this._condition = condition;\n this._operator = operator;\n this._parameters = parameters;\n }\n\n /**\n * Allow integrators to override the clock used for time-based rules.\n */\n public static setNowProvider(now: () => Date) {\n if (typeof now === \"function\") {\n RuleCondition._now = now;\n }\n }\n\n /**\n * Allow integrators to set an elapsed seconds hook for time limit calculations\n */\n public static setElapsedSecondsHook(hook: ((activity: Activity) => number) | undefined) {\n RuleCondition._getElapsedSecondsHook = hook;\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._condition = RuleConditionType.ALWAYS;\n this._operator = null;\n this._parameters = new Map();\n }\n\n /**\n * Getter for condition\n * @return {RuleConditionType}\n */\n get condition(): RuleConditionType {\n return this._condition;\n }\n\n /**\n * Setter for condition\n * @param {RuleConditionType} condition\n */\n set condition(condition: RuleConditionType) {\n this._condition = condition;\n }\n\n /**\n * Getter for operator\n * @return {RuleConditionOperator | null}\n */\n get operator(): RuleConditionOperator | null {\n return this._operator;\n }\n\n /**\n * Setter for operator\n * @param {RuleConditionOperator | null} operator\n */\n set operator(operator: RuleConditionOperator | null) {\n this._operator = operator;\n }\n\n /**\n * Getter for parameters\n * @return {Map<string, any>}\n */\n get parameters(): Map<string, any> {\n return this._parameters;\n }\n\n /**\n * Setter for parameters\n * @param {Map<string, any>} parameters\n */\n set parameters(parameters: Map<string, any>) {\n this._parameters = parameters;\n }\n\n get referencedObjective(): string | null {\n return this._referencedObjective;\n }\n\n set referencedObjective(objectiveId: string | null) {\n this._referencedObjective = objectiveId;\n }\n\n private resolveReferencedObjective(activity: Activity): ActivityObjective | null {\n if (!this._referencedObjective) {\n return null;\n }\n if (activity.primaryObjective?.id === this._referencedObjective) {\n return activity.primaryObjective;\n }\n const objectives = activity.objectives || [];\n return objectives.find((obj) => obj.id === this._referencedObjective) || null;\n }\n\n /**\n * Evaluate the condition for an activity\n * @param {Activity} activity - The activity to evaluate the condition for\n * @return {boolean} - True if the condition is met, false otherwise\n */\n evaluate(activity: Activity): boolean {\n let result;\n const referencedObjective = this.resolveReferencedObjective(activity);\n\n switch (this._condition) {\n case RuleConditionType.SATISFIED:\n case RuleConditionType.OBJECTIVE_SATISFIED:\n if (referencedObjective) {\n result = referencedObjective.satisfiedStatus === true;\n } else {\n result =\n activity.successStatus === SuccessStatus.PASSED ||\n activity.objectiveSatisfiedStatus === true;\n }\n break;\n case RuleConditionType.OBJECTIVE_STATUS_KNOWN:\n // noinspection PointlessBooleanExpressionJS\n result = referencedObjective\n ? !!referencedObjective.measureStatus\n : !!activity.objectiveMeasureStatus;\n break;\n case RuleConditionType.OBJECTIVE_MEASURE_KNOWN:\n // noinspection PointlessBooleanExpressionJS\n result = referencedObjective\n ? !!referencedObjective.measureStatus\n : !!activity.objectiveMeasureStatus;\n break;\n case RuleConditionType.OBJECTIVE_MEASURE_GREATER_THAN: {\n const greaterThanValue = this._parameters.get(\"threshold\") || 0;\n const measureStatus = referencedObjective\n ? referencedObjective.measureStatus\n : activity.objectiveMeasureStatus;\n const measureValue = referencedObjective\n ? referencedObjective.normalizedMeasure\n : activity.objectiveNormalizedMeasure;\n result = !!measureStatus && measureValue > greaterThanValue;\n break;\n }\n case RuleConditionType.OBJECTIVE_MEASURE_LESS_THAN: {\n const lessThanValue = this._parameters.get(\"threshold\") || 0;\n const measureStatus = referencedObjective\n ? referencedObjective.measureStatus\n : activity.objectiveMeasureStatus;\n const measureValue = referencedObjective\n ? referencedObjective.normalizedMeasure\n : activity.objectiveNormalizedMeasure;\n result = !!measureStatus && measureValue < lessThanValue;\n break;\n }\n case RuleConditionType.COMPLETED:\n case RuleConditionType.ACTIVITY_COMPLETED:\n // SCORM 2004 4th Edition: When referencedObjective is specified,\n // check the objective's completion status instead of the activity's\n if (referencedObjective) {\n result = referencedObjective.completionStatus === CompletionStatus.COMPLETED;\n } else {\n result = activity.isCompleted;\n }\n break;\n case RuleConditionType.PROGRESS_KNOWN:\n case RuleConditionType.ACTIVITY_PROGRESS_KNOWN:\n // SCORM 2004 4th Edition: When referencedObjective is specified,\n // check the objective's completion status instead of the activity's\n if (referencedObjective) {\n result = referencedObjective.completionStatus !== CompletionStatus.UNKNOWN;\n } else {\n result = activity.completionStatus !== \"unknown\";\n }\n break;\n case RuleConditionType.ATTEMPTED:\n result = activity.attemptCount > 0;\n break;\n case RuleConditionType.ATTEMPT_LIMIT_EXCEEDED:\n // Use activity's hasAttemptLimitExceeded() which properly checks\n // if the activity has an attempt limit set\n result = activity.hasAttemptLimitExceeded();\n break;\n case RuleConditionType.TIME_LIMIT_EXCEEDED:\n result = this.evaluateTimeLimitExceeded(activity);\n break;\n case RuleConditionType.OUTSIDE_AVAILABLE_TIME_RANGE:\n result = this.evaluateOutsideAvailableTimeRange(activity);\n break;\n case RuleConditionType.ALWAYS:\n result = true;\n break;\n case RuleConditionType.NEVER:\n result = false;\n break;\n default:\n result = false;\n break;\n }\n\n if (this._operator === RuleConditionOperator.NOT) {\n result = !result;\n }\n\n return result;\n }\n\n /**\n * Evaluate if time limit has been exceeded\n * @param {Activity} activity - The activity to evaluate\n * @return {boolean}\n * @private\n */\n private evaluateTimeLimitExceeded(activity: Activity): boolean {\n // Check timeLimitDuration (primary time limit)\n let limit = activity.timeLimitDuration;\n\n // Fallback to attemptAbsoluteDurationLimit if timeLimitDuration is not set\n if (!limit && activity.attemptAbsoluteDurationLimit) {\n limit = activity.attemptAbsoluteDurationLimit;\n }\n\n // No limit means condition is false\n if (!limit) {\n return false;\n }\n\n // Parse limit to seconds\n const limitSeconds = getDurationAsSeconds(limit, scorm2004_regex.CMITimespan);\n\n // Invalid or zero limit means condition is false\n if (limitSeconds <= 0) {\n return false;\n }\n\n let elapsedSeconds = 0;\n\n // Strategy 1: Use hook if available (preferred for LMS integration)\n if (RuleCondition._getElapsedSecondsHook) {\n try {\n const hookResult = RuleCondition._getElapsedSecondsHook(activity);\n if (\n typeof hookResult === \"number\" &&\n !Number.isNaN(hookResult) &&\n hookResult >= 0\n ) {\n elapsedSeconds = hookResult;\n }\n } catch {\n elapsedSeconds = 0;\n }\n }\n\n // Strategy 2: Try to use attemptExperiencedDuration if hook not available\n if (elapsedSeconds === 0 && activity.attemptExperiencedDuration) {\n const attemptDurationSeconds = getDurationAsSeconds(\n activity.attemptExperiencedDuration,\n scorm2004_regex.CMITimespan\n );\n if (attemptDurationSeconds > 0) {\n elapsedSeconds = attemptDurationSeconds;\n }\n }\n\n // Strategy 3: Calculate from attemptAbsoluteStartTime if duration not available\n if (elapsedSeconds === 0 && activity.attemptAbsoluteStartTime) {\n try {\n const start = new Date(activity.attemptAbsoluteStartTime).getTime();\n const nowMs = RuleCondition._now().getTime();\n\n // Validate timestamps before calculating\n if (!Number.isNaN(start) && !Number.isNaN(nowMs) && nowMs >= start) {\n elapsedSeconds = (nowMs - start) / 1000;\n }\n } catch {\n elapsedSeconds = 0;\n }\n }\n\n // Time limit is exceeded if elapsed time is strictly greater than limit\n return elapsedSeconds > limitSeconds;\n }\n\n /**\n * Evaluate if activity is outside available time range\n * @param {Activity} activity - The activity to evaluate\n * @return {boolean}\n * @private\n */\n private evaluateOutsideAvailableTimeRange(activity: Activity): boolean {\n const beginTime = activity.beginTimeLimit;\n const endTime = activity.endTimeLimit;\n\n if (!beginTime && !endTime) {\n return false;\n }\n\n const now = RuleCondition._now();\n\n if (beginTime) {\n const beginDate = new Date(beginTime);\n if (now < beginDate) {\n return true;\n }\n }\n\n if (endTime) {\n const endDate = new Date(endTime);\n if (now > endDate) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Parse ISO 8601 duration to milliseconds\n * Uses the standard getDurationAsSeconds utility which supports full ISO 8601 format\n * including date components (years, months, weeks, days) and time components (hours, minutes, seconds).\n * @param {string} duration - ISO 8601 duration string (e.g., \"PT1H30M\", \"P1D\", \"P1Y2M3DT4H5M6S\")\n * @return {number} - Duration in milliseconds\n * @private\n */\n private parseISO8601Duration(duration: string): number {\n // Use the standard utility function which handles full ISO 8601 duration format\n // including years (Y), months (M), weeks (W), days (D), hours (H), minutes (M), and seconds (S)\n const seconds = getDurationAsSeconds(duration, scorm2004_regex.CMITimespan);\n // Convert seconds to milliseconds\n return seconds * 1000;\n }\n\n /**\n * toJSON for RuleCondition\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n condition: this._condition,\n operator: this._operator,\n parameters: Object.fromEntries(this._parameters),\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing a sequencing rule\n */\nexport class SequencingRule extends BaseCMI {\n private _conditions: RuleCondition[] = [];\n private _action: RuleActionType = RuleActionType.SKIP;\n private _conditionCombination: string | RuleConditionOperator = RuleConditionOperator.AND;\n\n /**\n * Constructor for SequencingRule\n * @param {RuleActionType} action - The action to take when the rule conditions are met\n * @param {string | RuleConditionOperator} conditionCombination - How to combine multiple conditions (\"all\"/\"and\" or \"any\"/\"or\")\n */\n constructor(\n action: RuleActionType = RuleActionType.SKIP,\n conditionCombination: string | RuleConditionOperator = RuleConditionOperator.AND,\n ) {\n super(\"sequencingRule\");\n this._action = action;\n this._conditionCombination = conditionCombination;\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._conditions = [];\n this._action = RuleActionType.SKIP;\n this._conditionCombination = RuleConditionOperator.AND;\n }\n\n /**\n * Getter for conditions\n * @return {RuleCondition[]}\n */\n get conditions(): RuleCondition[] {\n return this._conditions;\n }\n\n /**\n * Add a condition to the rule\n * @param {RuleCondition} condition - The condition to add\n */\n addCondition(condition: RuleCondition): void {\n // noinspection SuspiciousTypeOfGuard\n if (!(condition instanceof RuleCondition)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".conditions\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n // Check if the condition is already in the array\n if (!this._conditions.includes(condition)) {\n this._conditions.push(condition);\n }\n }\n\n /**\n * Remove a condition from the rule\n * @param {RuleCondition} condition - The condition to remove\n * @return {boolean} - True if the condition was removed, false otherwise\n */\n removeCondition(condition: RuleCondition): boolean {\n // noinspection SuspiciousTypeOfGuard\n if (!(condition instanceof RuleCondition)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".conditions\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n const index = this._conditions.indexOf(condition);\n if (index !== -1) {\n this._conditions.splice(index, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Getter for action\n * @return {RuleActionType}\n */\n get action(): RuleActionType {\n return this._action;\n }\n\n /**\n * Setter for action\n * @param {RuleActionType} action\n */\n set action(action: RuleActionType) {\n this._action = action;\n }\n\n /**\n * Getter for conditionCombination\n * @return {string | RuleConditionOperator}\n */\n get conditionCombination(): string | RuleConditionOperator {\n return this._conditionCombination;\n }\n\n /**\n * Setter for conditionCombination\n * @param {string | RuleConditionOperator} conditionCombination\n */\n set conditionCombination(conditionCombination: string | RuleConditionOperator) {\n this._conditionCombination = conditionCombination;\n }\n\n /**\n * Evaluate the rule for an activity\n * @param {Activity} activity - The activity to evaluate the rule for\n * @return {boolean} - True if the rule conditions are met, false otherwise\n */\n evaluate(activity: Activity): boolean {\n if (this._conditions.length === 0) {\n return true;\n }\n\n if (\n this._conditionCombination === \"all\" ||\n this._conditionCombination === RuleConditionOperator.AND\n ) {\n return this._conditions.every((condition) => condition.evaluate(activity));\n } else if (\n this._conditionCombination === \"any\" ||\n this._conditionCombination === RuleConditionOperator.OR\n ) {\n return this._conditions.some((condition) => condition.evaluate(activity));\n }\n\n return false;\n }\n\n /**\n * toJSON for SequencingRule\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n conditions: this._conditions,\n action: this._action,\n conditionCombination: this._conditionCombination,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing a collection of sequencing rules\n */\nexport class SequencingRules extends BaseCMI {\n private _preConditionRules: SequencingRule[] = [];\n private _exitConditionRules: SequencingRule[] = [];\n private _postConditionRules: SequencingRule[] = [];\n\n /**\n * Constructor for SequencingRules\n */\n constructor() {\n super(\"sequencingRules\");\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._preConditionRules = [];\n this._exitConditionRules = [];\n this._postConditionRules = [];\n }\n\n /**\n * Getter for preConditionRules\n * @return {SequencingRule[]}\n */\n get preConditionRules(): SequencingRule[] {\n return this._preConditionRules;\n }\n\n /**\n * Add a pre-condition rule\n * @param {SequencingRule} rule - The rule to add\n */\n addPreConditionRule(rule: SequencingRule): void {\n // noinspection SuspiciousTypeOfGuard\n if (!(rule instanceof SequencingRule)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".preConditionRules\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._preConditionRules.push(rule);\n }\n\n /**\n * Getter for exitConditionRules\n * @return {SequencingRule[]}\n */\n get exitConditionRules(): SequencingRule[] {\n return this._exitConditionRules;\n }\n\n /**\n * Add an exit condition rule\n * @param {SequencingRule} rule - The rule to add\n */\n addExitConditionRule(rule: SequencingRule): void {\n // noinspection SuspiciousTypeOfGuard\n if (!(rule instanceof SequencingRule)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".exitConditionRules\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._exitConditionRules.push(rule);\n }\n\n /**\n * Getter for postConditionRules\n * @return {SequencingRule[]}\n */\n get postConditionRules(): SequencingRule[] {\n return this._postConditionRules;\n }\n\n /**\n * Add a post-condition rule\n * @param {SequencingRule} rule - The rule to add\n */\n addPostConditionRule(rule: SequencingRule): void {\n // noinspection SuspiciousTypeOfGuard\n if (!(rule instanceof SequencingRule)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".postConditionRules\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._postConditionRules.push(rule);\n }\n\n /**\n * Evaluate pre-condition rules for an activity\n * @param {Activity} activity - The activity to evaluate the rules for\n * @return {RuleActionType | null} - The action to take, or null if no rules are met\n */\n evaluatePreConditionRules(activity: Activity): RuleActionType | null {\n for (const rule of this._preConditionRules) {\n if (rule.evaluate(activity)) {\n return rule.action;\n }\n }\n return null;\n }\n\n /**\n * Evaluate exit condition rules for an activity\n * @param {Activity} activity - The activity to evaluate the rules for\n * @return {RuleActionType | null} - The action to take, or null if no rules are met\n */\n evaluateExitConditionRules(activity: Activity): RuleActionType | null {\n for (const rule of this._exitConditionRules) {\n if (rule.evaluate(activity)) {\n return rule.action;\n }\n }\n return null;\n }\n\n /**\n * Evaluate post-condition rules for an activity\n * @param {Activity} activity - The activity to evaluate the rules for\n * @return {RuleActionType | null} - The action to take, or null if no rules are met\n */\n evaluatePostConditionRules(activity: Activity): RuleActionType | null {\n for (const rule of this._postConditionRules) {\n if (rule.evaluate(activity)) {\n return rule.action;\n }\n }\n return null;\n }\n\n /**\n * toJSON for SequencingRules\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n preConditionRules: this._preConditionRules,\n exitConditionRules: this._exitConditionRules,\n postConditionRules: this._postConditionRules,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\n\n/**\n * Utility class for querying and traversing the activity tree\n * Extracted from SequencingProcess to reduce class size and improve maintainability\n *\n * This class provides helper methods for:\n * - Tree traversal and ancestor lookups\n * - Activity relationship queries (parent/child/sibling)\n * - Activity state and completion checks\n * - Common ancestor calculations\n */\nexport class ActivityTreeQueries {\n constructor(private activityTree: ActivityTree) {}\n\n /**\n * Check if activity is in the activity tree\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if activity is in the tree\n */\n public isInTree(activity: Activity): boolean {\n return this.activityTree.getAllActivities().includes(activity);\n }\n\n /**\n * Check if activity1 is a parent (ancestor) of activity2\n * Used for choiceExit validation to determine if target is within a subtree\n * @param {Activity} ancestor - Potential parent/ancestor activity\n * @param {Activity} descendant - Potential child/descendant activity\n * @return {boolean} - True if ancestor is an ancestor of descendant\n */\n public isAncestorOf(ancestor: Activity, descendant: Activity): boolean {\n let current: Activity | null = descendant;\n while (current) {\n if (current === ancestor) {\n return true;\n }\n current = current.parent;\n }\n return false;\n }\n\n /**\n * Find common ancestor of two activities\n * @param {Activity | null} activity1 - First activity\n * @param {Activity | null} activity2 - Second activity\n * @return {Activity | null} - Common ancestor or null\n */\n public findCommonAncestor(\n activity1: Activity | null,\n activity2: Activity | null\n ): Activity | null {\n if (!activity1 || !activity2) {\n return null;\n }\n\n // Get ancestors of activity1\n const ancestors1: Activity[] = [];\n let current: Activity | null = activity1;\n while (current) {\n ancestors1.push(current);\n current = current.parent;\n }\n\n // Find first common ancestor\n current = activity2;\n while (current) {\n if (ancestors1.includes(current)) {\n return current;\n }\n current = current.parent;\n }\n\n return null;\n }\n\n /**\n * Find which child of ancestor is in the path to the target activity\n * Used for multi-level constraint validation\n * @param {Activity} ancestor - The ancestor activity\n * @param {Activity} target - The target activity\n * @return {Activity | null} - The child in the path, or null\n */\n public findChildInPath(ancestor: Activity, target: Activity): Activity | null {\n let current: Activity | null = target;\n\n while (current && current.parent) {\n if (current.parent === ancestor) {\n return current;\n }\n current = current.parent;\n }\n\n return null;\n }\n\n /**\n * Check if activity is the last activity in a forward preorder tree traversal\n * Per SB.2.1 step 3.1: An activity is last overall if it's a leaf with no next siblings\n * anywhere in its ancestor chain\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if this is the last activity in the tree\n */\n public isLastInTree(activity: Activity): boolean {\n // An activity is last overall if:\n // 1. It's a leaf (no children)\n // 2. It has no next sibling\n // 3. None of its ancestors have next siblings\n\n if (activity.children.length > 0) {\n return false; // Not a leaf\n }\n\n let current: Activity | null = activity;\n while (current) {\n if (this.activityTree.getNextSibling(current)) {\n return false; // Has a next sibling somewhere in the ancestor chain\n }\n current = current.parent;\n }\n\n return true; // No next siblings anywhere - this is the last activity\n }\n\n /**\n * Find the currently active activity within a parent's children\n * @param {Activity} parent - The parent activity\n * @return {Activity | null} - The active child or null\n */\n public getCurrentInParent(parent: Activity): Activity | null {\n if (parent.children) {\n for (const child of parent.children) {\n if (child.isActive) {\n return child;\n }\n }\n }\n return null;\n }\n\n /**\n * Check if activity is mandatory (cannot be skipped)\n * In SCORM 2004, this is typically determined by sequencing rules\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if activity is mandatory\n */\n public isMandatory(activity: Activity): boolean {\n // Check if activity has an unconditional skip rule (not mandatory)\n if (activity.sequencingRules && activity.sequencingRules.preConditionRules) {\n for (const rule of activity.sequencingRules.preConditionRules) {\n if (rule.action === \"skip\" && rule.conditions && rule.conditions.length === 0) {\n return false; // Has unconditional skip rule, not mandatory\n }\n }\n }\n\n // Check for explicit mandatory flag. Default to false (not mandatory) unless explicitly set\n // Activities are only mandatory if explicitly marked as such\n return (activity as any).mandatory === true;\n }\n\n /**\n * Check if activity is completed\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if activity is completed\n */\n public isCompleted(activity: Activity): boolean {\n return (\n activity.completionStatus === \"completed\" ||\n activity.completionStatus === \"passed\" ||\n activity.successStatus === \"passed\"\n );\n }\n\n /**\n * Check if activity is available for choice according to SCORM 2004 rules\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if activity is available for choice\n */\n public isAvailableForChoice(activity: Activity): boolean {\n return (\n activity.isVisible &&\n !activity.isHiddenFromChoice &&\n activity.isAvailable &&\n (activity.sequencingControls ? activity.sequencingControls.choice : true)\n );\n }\n\n /**\n * Get all ancestors of an activity from child to root\n * @param {Activity} activity - The activity to get ancestors for\n * @return {Activity[]} - Array of ancestors from immediate parent to root\n */\n public getAncestors(activity: Activity): Activity[] {\n const ancestors: Activity[] = [];\n let current = activity.parent;\n while (current) {\n ancestors.push(current);\n current = current.parent;\n }\n return ancestors;\n }\n\n /**\n * Get the path from an activity to the root\n * @param {Activity} activity - The activity\n * @return {Activity[]} - Array from the activity to root (inclusive)\n */\n public getPathToRoot(activity: Activity): Activity[] {\n const path: Activity[] = [activity];\n let current = activity.parent;\n while (current) {\n path.push(current);\n current = current.parent;\n }\n return path;\n }\n\n /**\n * Check if an activity is a leaf (has no children)\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if activity is a leaf\n */\n public isLeaf(activity: Activity): boolean {\n return activity.children.length === 0;\n }\n\n /**\n * Check if an activity is a cluster (has children)\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if activity is a cluster\n */\n public isCluster(activity: Activity): boolean {\n return activity.children.length > 0;\n }\n\n /**\n * Get the depth of an activity in the tree (root = 0)\n * @param {Activity} activity - Activity to get depth for\n * @return {number} - Depth in tree\n */\n public getDepth(activity: Activity): number {\n let depth = 0;\n let current = activity.parent;\n while (current) {\n depth++;\n current = current.parent;\n }\n return depth;\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { ActivityTreeQueries } from \"../utils/activity_tree_queries\";\n\n/**\n * Result of constraint validation\n */\nexport interface ConstraintValidationResult {\n valid: boolean;\n exception: string | null;\n}\n\n/**\n * Options for choice validation\n */\nexport interface ChoiceValidationOptions {\n checkAvailability?: boolean;\n}\n\n/**\n * ChoiceConstraintValidator - Consolidates ALL choice constraint validation logic\n *\n * This class extracts and unifies the constraint validation logic that was previously\n * duplicated in 5+ places within SequencingProcess. It provides a single source of truth\n * for validating choice navigation requests.\n *\n * Key validations performed:\n * - Path to root validation (hidden from choice, choice control)\n * - choiceExit constraint checking at all ancestor levels\n * - forwardOnly constraint validation\n * - constrainChoice boundary validation\n * - preventActivation constraint checking\n * - Mandatory activity skipping detection\n */\nexport class ChoiceConstraintValidator {\n constructor(\n private activityTree: ActivityTree,\n private treeQueries: ActivityTreeQueries\n ) {}\n\n /**\n * Main entry point - consolidates ALL constraint validation for choice navigation\n * @param {Activity | null} currentActivity - Current activity (may be null if no session started)\n * @param {Activity} targetActivity - Target activity for the choice\n * @param {ChoiceValidationOptions} options - Validation options\n * @return {ConstraintValidationResult} - Validation result with exception if invalid\n */\n public validateChoice(\n currentActivity: Activity | null,\n targetActivity: Activity,\n options: ChoiceValidationOptions = {}\n ): ConstraintValidationResult {\n // Step 1: Basic tree membership check\n if (!this.treeQueries.isInTree(targetActivity)) {\n return { valid: false, exception: \"SB.2.9-2\" };\n }\n\n // Step 2: Cannot choose the root activity\n if (targetActivity === this.activityTree.root) {\n return { valid: false, exception: \"SB.2.9-3\" };\n }\n\n // Step 3: Path to root validation - check hidden from choice and choice control\n const pathValidation = this.validatePathToRoot(targetActivity);\n if (!pathValidation.valid) {\n return pathValidation;\n }\n\n // Step 4: Check availability if requested\n if (options.checkAvailability && !targetActivity.isAvailable) {\n return { valid: false, exception: \"SB.2.9-7\" };\n }\n\n // Step 5: If no current activity, basic validation is sufficient\n if (!currentActivity) {\n return { valid: true, exception: null };\n }\n\n // Step 6: Check choiceExit constraints at all ancestor levels\n const choiceExitValidation = this.validateChoiceExit(currentActivity, targetActivity);\n if (!choiceExitValidation.valid) {\n return choiceExitValidation;\n }\n\n // Step 7: Validate constraints at all ancestor levels\n const ancestorValidation = this.validateAncestorConstraints(\n currentActivity,\n targetActivity\n );\n if (!ancestorValidation.valid) {\n return ancestorValidation;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Validate path to root - check hidden from choice and choice control\n * @param {Activity} targetActivity - Target activity\n * @return {ConstraintValidationResult} - Validation result\n */\n public validatePathToRoot(targetActivity: Activity): ConstraintValidationResult {\n let activity: Activity | null = targetActivity;\n while (activity) {\n // Check if activity is hidden from choice\n if (activity.isHiddenFromChoice) {\n return { valid: false, exception: \"SB.2.9-4\" };\n }\n\n // Check if parent allows choice\n if (activity.parent && !activity.parent.sequencingControls.choice) {\n return { valid: false, exception: \"SB.2.9-5\" };\n }\n\n // Check preventActivation constraint at parent level\n // This applies even when there's no current activity\n if (activity.parent && activity.parent.sequencingControls.preventActivation) {\n // Target must have been previously attempted\n if (targetActivity.attemptCount === 0 && !targetActivity.isActive) {\n return { valid: false, exception: \"SB.2.9-6\" };\n }\n }\n\n activity = activity.parent;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Validate choiceExit constraint at all ancestor levels\n * Per SCORM spec: choiceExit only applies when we're actively IN that ancestor's subtree\n * @param {Activity} currentActivity - Current activity\n * @param {Activity} targetActivity - Target activity\n * @return {ConstraintValidationResult} - Validation result\n */\n public validateChoiceExit(\n currentActivity: Activity,\n targetActivity: Activity\n ): ConstraintValidationResult {\n // Walk from current activity to root, checking for choiceExit=false\n let currentAncestor: Activity | null = currentActivity.parent;\n\n while (currentAncestor) {\n // choiceExit only applies when the ancestor is ACTIVE\n if (currentAncestor.isActive && !currentAncestor.sequencingControls.choiceExit) {\n // choiceExit is false at this active ancestor\n // Check if target is a descendant of this ancestor\n if (!this.treeQueries.isAncestorOf(currentAncestor, targetActivity)) {\n return { valid: false, exception: \"SB.2.9-8\" };\n }\n // If target is within this subtree, we can stop checking higher levels\n break;\n }\n currentAncestor = currentAncestor.parent;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Validate constraints at all ancestor levels\n * Checks forwardOnly, constrainChoice, preventActivation\n * @param {Activity} currentActivity - Current activity\n * @param {Activity} targetActivity - Target activity\n * @return {ConstraintValidationResult} - Validation result\n */\n public validateAncestorConstraints(\n currentActivity: Activity,\n targetActivity: Activity\n ): ConstraintValidationResult {\n let ancestorActivity: Activity | null = targetActivity.parent;\n\n while (ancestorActivity) {\n const validation = this.validateConstraintsAtLevel(\n ancestorActivity,\n currentActivity,\n targetActivity\n );\n if (!validation.valid) {\n return validation;\n }\n ancestorActivity = ancestorActivity.parent;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Validate constraints at a specific ancestor level\n * @param {Activity} ancestor - The ancestor to check\n * @param {Activity} currentActivity - Current activity\n * @param {Activity} targetActivity - Target activity\n * @return {ConstraintValidationResult} - Validation result\n */\n private validateConstraintsAtLevel(\n ancestor: Activity,\n currentActivity: Activity,\n targetActivity: Activity\n ): ConstraintValidationResult {\n // Find which children of this ancestor contain current and target\n const targetChild = this.treeQueries.findChildInPath(ancestor, targetActivity);\n const currentChild = this.treeQueries.findChildInPath(ancestor, currentActivity);\n\n // Only validate if both current and target are descendants of this ancestor\n if (!targetChild || !currentChild) {\n return { valid: true, exception: null };\n }\n\n const siblings = ancestor.children;\n const targetIndex = siblings.indexOf(targetChild);\n const currentIndex = siblings.indexOf(currentChild);\n\n if (targetIndex === -1 || currentIndex === -1) {\n return { valid: true, exception: null };\n }\n\n // Priority 1: Check forwardOnly constraint (highest priority)\n // Per SCORM spec, forwardOnly blocks ALL backward navigation\n if (ancestor.sequencingControls.forwardOnly && targetIndex < currentIndex) {\n return { valid: false, exception: \"SB.2.9-5\" };\n }\n\n // Priority 2: Check mandatory activities being skipped (forward direction)\n if (targetIndex > currentIndex) {\n for (let i = currentIndex + 1; i < targetIndex; i++) {\n const intermediateChild = siblings[i];\n if (\n intermediateChild &&\n this.treeQueries.isMandatory(intermediateChild) &&\n !this.treeQueries.isCompleted(intermediateChild)\n ) {\n return { valid: false, exception: \"SB.2.9-6\" };\n }\n }\n }\n\n // Priority 3: Check constrainChoice constraint\n if (ancestor.sequencingControls.constrainChoice) {\n // Cannot skip forward beyond next sibling\n if (targetIndex > currentIndex + 1) {\n return { valid: false, exception: \"SB.2.9-7\" };\n }\n\n // Cannot go backward to incomplete activity\n if (targetIndex < currentIndex) {\n if (\n targetActivity.completionStatus !== \"completed\" &&\n (targetActivity.completionStatus as string) !== \"passed\"\n ) {\n return { valid: false, exception: \"SB.2.9-7\" };\n }\n }\n }\n\n // Check preventActivation constraint\n if (ancestor.sequencingControls.preventActivation) {\n if (targetActivity.attemptCount === 0 && !targetActivity.isActive) {\n return { valid: false, exception: \"SB.2.9-6\" };\n }\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Check forwardOnly violation at ALL ancestor levels\n * This is critical for Previous request validation\n * @param {Activity} fromActivity - The activity to check from\n * @return {ConstraintValidationResult} - Violation info or valid result\n */\n public checkForwardOnlyViolation(fromActivity: Activity): ConstraintValidationResult {\n // Walk up the ancestor chain checking forwardOnly at each level\n let current: Activity | null = fromActivity.parent;\n\n while (current) {\n if (current.sequencingControls.forwardOnly) {\n return { valid: false, exception: \"SB.2.9-5\" };\n }\n current = current.parent;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Validate activity is available for choice navigation\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if available for choice\n */\n public isAvailableForChoice(activity: Activity): boolean {\n return this.treeQueries.isAvailableForChoice(activity);\n }\n\n /**\n * Validate choice flow constraints for flow tree traversal\n * @param {Activity} fromActivity - Activity to traverse from\n * @param {Activity[]} children - Available children\n * @return {{ valid: boolean; validChildren: Activity[] }} - Valid children that meet constraints\n */\n public validateFlowConstraints(\n fromActivity: Activity,\n children: Activity[]\n ): { valid: boolean; validChildren: Activity[] } {\n const validChildren: Activity[] = [];\n\n for (const child of children) {\n if (this.meetsFlowConstraints(child, fromActivity)) {\n validChildren.push(child);\n }\n }\n\n return {\n valid: validChildren.length > 0,\n validChildren\n };\n }\n\n /**\n * Check if activity meets flow constraints\n * @param {Activity} activity - Activity to check\n * @param {Activity} parent - Parent activity\n * @return {boolean} - True if constraints are met\n */\n public meetsFlowConstraints(activity: Activity, parent: Activity): boolean {\n // Check basic availability\n if (!activity.isAvailable || activity.isHiddenFromChoice) {\n return false;\n }\n\n // Check parent constraint controls\n if (parent.sequencingControls.constrainChoice) {\n return this.validateConstrainChoiceForFlow(activity, parent);\n }\n\n return true;\n }\n\n /**\n * Validate constrainChoice for flow scenarios\n * @param {Activity} activity - Activity to validate\n * @param {Activity} parent - Parent activity\n * @return {boolean} - True if valid\n */\n private validateConstrainChoiceForFlow(activity: Activity, parent: Activity): boolean {\n // If constrainChoice is false, no restrictions apply\n if (!parent.sequencingControls || !parent.sequencingControls.constrainChoice) {\n return true;\n }\n\n const children = parent.children;\n if (!children || children.length === 0) {\n return true;\n }\n\n const targetIndex = children.indexOf(activity);\n if (targetIndex === -1) {\n return false;\n }\n\n // Get the current activity within this parent's children\n const currentActivity = this.treeQueries.getCurrentInParent(parent);\n if (!currentActivity) {\n // No current activity in this cluster, allow choice to first available\n return this.isAvailableForChoice(activity);\n }\n\n const currentIndex = children.indexOf(currentActivity);\n if (currentIndex === -1) {\n return true;\n }\n\n // Check flow direction constraints\n if (parent.sequencingControls.flow) {\n // forwardOnly check\n if (parent.sequencingControls.forwardOnly && targetIndex < currentIndex) {\n // Allow if previously completed\n if (activity.completionStatus === \"completed\" ||\n (activity.completionStatus as string) === \"passed\") {\n return true;\n }\n return false;\n }\n\n // Forward direction - can only choose immediate next sibling or current\n if (targetIndex >= currentIndex) {\n if (targetIndex === currentIndex || targetIndex === currentIndex + 1) {\n return this.isAvailableForChoice(activity);\n }\n return false;\n }\n\n // Backward direction (forwardOnly is false)\n if (targetIndex < currentIndex) {\n return (\n (activity.completionStatus === \"completed\" ||\n (activity.completionStatus as string) === \"passed\") &&\n this.isAvailableForChoice(activity)\n );\n }\n\n return false;\n } else {\n // Non-flow mode with constrainChoice\n return (\n this.isAvailableForChoice(activity) &&\n (activity.completionStatus === \"completed\" ||\n activity.completionStatus === \"unknown\" ||\n activity.completionStatus === \"incomplete\")\n );\n }\n }\n\n /**\n * Validate traversal constraints for choice navigation\n * @param {Activity} activity - Activity to validate\n * @return {{ canTraverse: boolean; canTraverseInto: boolean }} - Traversal permissions\n */\n public validateTraversalConstraints(activity: Activity): {\n canTraverse: boolean;\n canTraverseInto: boolean;\n } {\n let canTraverse = true;\n let canTraverseInto = true;\n\n // Check constrainChoice control\n if (activity.parent?.sequencingControls.constrainChoice) {\n canTraverse = this.evaluateConstrainChoiceForTraversal(activity);\n }\n\n // Check stopForwardTraversal control\n if (activity.sequencingControls && activity.sequencingControls.stopForwardTraversal) {\n canTraverseInto = false;\n }\n\n // Check forwardOnly control in parent context\n if (activity.parent?.sequencingControls.forwardOnly) {\n canTraverseInto = this.evaluateForwardOnlyForChoice(activity);\n }\n\n return { canTraverse, canTraverseInto };\n }\n\n /**\n * Evaluate constrainChoice for traversal\n * @param {Activity} activity - Activity to evaluate\n * @return {boolean} - True if traversal is allowed\n */\n private evaluateConstrainChoiceForTraversal(activity: Activity): boolean {\n if (!activity.parent) {\n return true;\n }\n\n // Check constraint at ALL ancestor levels\n let currentAncestor: Activity | null = activity.parent;\n while (currentAncestor) {\n if (\n currentAncestor.sequencingControls &&\n currentAncestor.sequencingControls.constrainChoice\n ) {\n const ancestorChildren = currentAncestor.children;\n const childInPath = this.treeQueries.findChildInPath(currentAncestor, activity);\n\n if (childInPath) {\n const childIndex = ancestorChildren.indexOf(childInPath);\n const currentAtLevel = this.treeQueries.getCurrentInParent(currentAncestor);\n\n if (currentAtLevel) {\n const currentIndex = ancestorChildren.indexOf(currentAtLevel);\n\n if (currentIndex !== -1 && childIndex !== -1) {\n // Check mandatory intermediate activities\n if (currentIndex < childIndex) {\n for (let i = currentIndex + 1; i < childIndex; i++) {\n const intermediateActivity = ancestorChildren[i];\n if (\n intermediateActivity &&\n this.treeQueries.isMandatory(intermediateActivity) &&\n !this.treeQueries.isCompleted(intermediateActivity)\n ) {\n return false;\n }\n }\n }\n\n // Check forwardOnly constraint\n if (currentAncestor.sequencingControls.forwardOnly && childIndex < currentIndex) {\n if (!this.treeQueries.isCompleted(activity)) {\n return false;\n }\n }\n }\n }\n }\n }\n currentAncestor = currentAncestor.parent;\n }\n\n return this.isAvailableForChoice(activity);\n }\n\n /**\n * Evaluate forwardOnly for choice scenarios\n * @param {Activity} activity - Activity to evaluate\n * @return {boolean} - True if allowed\n */\n private evaluateForwardOnlyForChoice(activity: Activity): boolean {\n if (!activity.parent) {\n return true;\n }\n\n const parent = activity.parent;\n if (!parent.sequencingControls || !parent.sequencingControls.forwardOnly) {\n return true;\n }\n\n const siblings = parent.children;\n if (!siblings || siblings.length === 0) {\n return true;\n }\n\n const targetIndex = siblings.indexOf(activity);\n if (targetIndex === -1) {\n return false;\n }\n\n const currentActivity = this.treeQueries.getCurrentInParent(parent);\n if (!currentActivity) {\n return this.isAvailableForChoice(activity);\n }\n\n const currentIndex = siblings.indexOf(currentActivity);\n if (currentIndex === -1) {\n return true;\n }\n\n // ForwardOnly: only allow activities at or after current position\n if (targetIndex < currentIndex) {\n // Allow if completed and choice-enabled\n if (activity.completionStatus === \"completed\" ||\n (activity.completionStatus as string) === \"passed\") {\n if (activity.sequencingControls && activity.sequencingControls.choice) {\n return true;\n }\n }\n return false;\n }\n\n return this.isAvailableForChoice(activity);\n }\n\n /**\n * Check for time-based constraint boundary violations\n * @param {Activity} targetActivity - Target activity\n * @param {Date} now - Current time\n * @return {boolean} - True if there is a boundary violation\n */\n public hasTimeBoundaryViolation(targetActivity: Activity, now: Date): boolean {\n // Check begin time limit\n if (targetActivity.beginTimeLimit) {\n try {\n const beginTime = new Date(targetActivity.beginTimeLimit);\n if (now < beginTime) {\n return true; // Not yet available\n }\n } catch {\n // Invalid date format, no violation\n }\n }\n\n // Check end time limit\n if (targetActivity.endTimeLimit) {\n try {\n const endTime = new Date(targetActivity.endTimeLimit);\n if (now > endTime) {\n return true; // No longer available\n }\n } catch {\n // Invalid date format, no violation\n }\n }\n\n return false;\n }\n\n /**\n * Check for attempt limit violations\n * @param {Activity} targetActivity - Target activity\n * @return {boolean} - True if attempt limit exceeded\n */\n public hasAttemptLimitViolation(targetActivity: Activity): boolean {\n return !!(\n targetActivity.attemptLimit &&\n targetActivity.attemptCount >= targetActivity.attemptLimit\n );\n }\n}\n","import { Activity } from \"../activity\";\n\n/**\n * Enum for sequencing request types\n * These represent the various navigation requests that can be made in SCORM 2004\n */\nexport enum SequencingRequestType {\n START = \"start\",\n RESUME_ALL = \"resumeAll\",\n CONTINUE = \"continue\",\n PREVIOUS = \"previous\",\n CHOICE = \"choice\",\n JUMP = \"jump\",\n EXIT = \"exit\",\n EXIT_PARENT = \"exitParent\",\n EXIT_ALL = \"exitAll\",\n ABANDON = \"abandon\",\n ABANDON_ALL = \"abandonAll\",\n SUSPEND_ALL = \"suspendAll\",\n RETRY = \"retry\",\n RETRY_ALL = \"retryAll\",\n}\n\n/**\n * Enum for delivery request types\n */\nexport enum DeliveryRequestType {\n DELIVER = \"deliver\",\n DO_NOT_DELIVER = \"doNotDeliver\",\n}\n\n/**\n * Class representing the result of a sequencing process\n */\nexport class SequencingResult {\n public deliveryRequest: DeliveryRequestType;\n public targetActivity: Activity | null;\n public exception: string | null;\n public endSequencingSession: boolean;\n\n constructor(\n deliveryRequest: DeliveryRequestType = DeliveryRequestType.DO_NOT_DELIVER,\n targetActivity: Activity | null = null,\n exception: string | null = null,\n endSequencingSession: boolean = false\n ) {\n this.deliveryRequest = deliveryRequest;\n this.targetActivity = targetActivity;\n this.exception = exception;\n this.endSequencingSession = endSequencingSession;\n }\n}\n\n/**\n * Result of Flow Subprocess (SB.2.3)\n * Used internally to propagate endSequencingSession flag through flow processes\n */\nexport class FlowSubprocessResult {\n public identifiedActivity: Activity | null;\n public deliverable: boolean;\n public exception: string | null;\n public endSequencingSession: boolean;\n\n constructor(\n identifiedActivity: Activity | null,\n deliverable: boolean,\n exception: string | null = null,\n endSequencingSession: boolean = false\n ) {\n this.identifiedActivity = identifiedActivity;\n this.deliverable = deliverable;\n this.exception = exception;\n this.endSequencingSession = endSequencingSession;\n }\n}\n\n/**\n * Result of Choice Traversal Subprocess (SB.2.4)\n * Used internally to propagate exception information from choice traversal\n */\nexport class ChoiceTraversalResult {\n public activity: Activity | null;\n public exception: string | null;\n\n constructor(activity: Activity | null, exception: string | null = null) {\n this.activity = activity;\n this.exception = exception;\n }\n}\n\n/**\n * Enum for flow subprocess modes\n */\nexport enum FlowSubprocessMode {\n FORWARD = \"forward\",\n BACKWARD = \"backward\",\n}\n","import { Activity } from \"../activity\";\nimport {\n SequencingRule,\n RuleActionType,\n RuleConditionOperator\n} from \"../sequencing_rules\";\nimport { SequencingRequestType } from \"./sequencing_request_types\";\nimport { getDurationAsSeconds } from \"../../../../utilities\";\nimport { scorm2004_regex } from \"../../../../constants/regex\";\n\n// Re-export for convenience\nexport { SequencingRequestType };\n\n/**\n * Result of post-condition rule evaluation\n */\nexport interface PostConditionResult {\n sequencingRequest: SequencingRequestType | null;\n terminationRequest: SequencingRequestType | null;\n}\n\n/**\n * Options for RuleEvaluationEngine\n */\nexport interface RuleEvaluationOptions {\n now?: (() => Date) | undefined;\n getAttemptElapsedSecondsHook?: ((activity: Activity) => number) | undefined;\n}\n\n/**\n * RuleEvaluationEngine - Centralized rule evaluation for SCORM 2004 sequencing\n *\n * This class extracts and consolidates all rule evaluation logic from SequencingProcess:\n * - Pre-condition rule evaluation (UP.2)\n * - Exit condition rule evaluation (TB.2.1)\n * - Post-condition rule evaluation (TB.2.2)\n * - Limit conditions checking (UP.1)\n * - Individual condition evaluation\n *\n * Key SCORM 2004 processes implemented:\n * - UP.1: Limit Conditions Check Process\n * - UP.2: Sequencing Rules Check Process\n * - UP.2.1: Sequencing Rules Check Subprocess\n * - TB.2.1: Exit Action Rules Subprocess\n * - TB.2.2: Post Condition Rules Subprocess\n */\nexport class RuleEvaluationEngine {\n private now: () => Date;\n private getAttemptElapsedSecondsHook: ((activity: Activity) => number) | null;\n\n constructor(options: RuleEvaluationOptions = {}) {\n this.now = options.now || (() => new Date());\n this.getAttemptElapsedSecondsHook = options.getAttemptElapsedSecondsHook || null;\n }\n\n /**\n * Sequencing Rules Check Process (UP.2)\n * General process for evaluating a set of sequencing rules\n * @param {Activity} activity - The activity to evaluate rules for\n * @param {SequencingRule[]} rules - The rules to evaluate\n * @return {RuleActionType | null} - The action to take, or null if no rules apply\n */\n public checkSequencingRules(\n activity: Activity,\n rules: SequencingRule[]\n ): RuleActionType | null {\n for (const rule of rules) {\n if (this.checkRuleSubprocess(activity, rule)) {\n return rule.action;\n }\n }\n return null;\n }\n\n /**\n * Sequencing Rules Check Subprocess (UP.2.1)\n * Evaluates individual sequencing rule conditions\n * @param {Activity} activity - The activity to evaluate the rule for\n * @param {SequencingRule} rule - The rule to evaluate\n * @return {boolean} - True if all rule conditions are met\n */\n public checkRuleSubprocess(activity: Activity, rule: SequencingRule): boolean {\n // If no conditions, the rule always applies\n if (rule.conditions.length === 0) {\n return true;\n }\n\n const conditionCombination = rule.conditionCombination;\n\n if (conditionCombination === \"all\" || conditionCombination === RuleConditionOperator.AND) {\n return rule.conditions.every((condition) => condition.evaluate(activity));\n } else if (conditionCombination === \"any\" || conditionCombination === RuleConditionOperator.OR) {\n return rule.conditions.some((condition) => condition.evaluate(activity));\n }\n\n return false;\n }\n\n /**\n * Exit Action Rules Subprocess (TB.2.1)\n * Evaluates the exit condition rules for an activity\n * @param {Activity} activity - The activity to evaluate exit rules for\n * @return {RuleActionType | null} - The exit action to process, if any\n */\n public evaluateExitRules(activity: Activity): RuleActionType | null {\n const exitAction = this.checkSequencingRules(\n activity,\n activity.sequencingRules.exitConditionRules\n );\n\n // Only certain actions are valid for exit condition rules\n if (\n exitAction === RuleActionType.EXIT ||\n exitAction === RuleActionType.EXIT_PARENT ||\n exitAction === RuleActionType.EXIT_ALL\n ) {\n return exitAction;\n }\n\n return null;\n }\n\n /**\n * Post Condition Rules Subprocess (TB.2.2)\n * Evaluates the post-condition rules for an activity after delivery\n * @param {Activity} activity - The activity to evaluate post-condition rules for\n * @return {RuleActionType | null} - The action to take, if any\n */\n public evaluatePostConditionAction(activity: Activity): RuleActionType | null {\n const postAction = this.checkSequencingRules(\n activity,\n activity.sequencingRules.postConditionRules\n );\n\n // Only certain actions are valid for post-condition rules\n const validActions = [\n RuleActionType.EXIT_PARENT,\n RuleActionType.EXIT_ALL,\n RuleActionType.RETRY,\n RuleActionType.RETRY_ALL,\n RuleActionType.CONTINUE,\n RuleActionType.PREVIOUS,\n RuleActionType.STOP_FORWARD_TRAVERSAL\n ];\n\n if (postAction && validActions.includes(postAction)) {\n return postAction;\n }\n\n return null;\n }\n\n /**\n * Evaluate post-condition rules and map to sequencing/termination requests\n * @param {Activity} activity - The activity to evaluate\n * @return {PostConditionResult} - The post-condition result with sequencing and termination requests\n */\n public evaluatePostConditions(activity: Activity): PostConditionResult {\n const postAction = this.evaluatePostConditionAction(activity);\n\n if (!postAction) {\n return {\n sequencingRequest: null,\n terminationRequest: null\n };\n }\n\n switch (postAction) {\n case RuleActionType.EXIT_PARENT:\n return {\n sequencingRequest: null,\n terminationRequest: SequencingRequestType.EXIT_PARENT\n };\n\n case RuleActionType.EXIT_ALL:\n return {\n sequencingRequest: null,\n terminationRequest: SequencingRequestType.EXIT_ALL\n };\n\n case RuleActionType.RETRY:\n return {\n sequencingRequest: SequencingRequestType.RETRY,\n terminationRequest: null\n };\n\n case RuleActionType.RETRY_ALL:\n return {\n sequencingRequest: SequencingRequestType.RETRY,\n terminationRequest: SequencingRequestType.EXIT_ALL\n };\n\n case RuleActionType.CONTINUE:\n return {\n sequencingRequest: SequencingRequestType.CONTINUE,\n terminationRequest: null\n };\n\n case RuleActionType.PREVIOUS:\n return {\n sequencingRequest: SequencingRequestType.PREVIOUS,\n terminationRequest: null\n };\n\n case RuleActionType.STOP_FORWARD_TRAVERSAL:\n // Set traversal limiter on controls; not a navigation request\n activity.sequencingControls.stopForwardTraversal = true;\n return {\n sequencingRequest: null,\n terminationRequest: null\n };\n\n default:\n return {\n sequencingRequest: null,\n terminationRequest: null\n };\n }\n }\n\n /**\n * Limit Conditions Check Process (UP.1)\n * Checks if an activity has exceeded its limit conditions\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if limit conditions are violated\n */\n public checkLimitConditions(activity: Activity): boolean {\n // Check attempt limit\n if (activity.attemptLimit !== null && activity.attemptCount >= activity.attemptLimit) {\n return true;\n }\n\n // Check attempt absolute duration limit\n if (activity.attemptAbsoluteDurationLimit !== null) {\n const attemptLimitMs = this.parseDuration(activity.attemptAbsoluteDurationLimit);\n if (attemptLimitMs > 0) {\n const attemptDurationMs = this.parseDuration(activity.attemptExperiencedDuration);\n if (attemptDurationMs >= attemptLimitMs) {\n return true;\n }\n }\n }\n\n // Check activity absolute duration limit\n if (activity.activityAbsoluteDurationLimit !== null) {\n const activityLimitMs = this.parseDuration(activity.activityAbsoluteDurationLimit);\n if (activityLimitMs > 0) {\n const activityDurationMs = this.parseDuration(activity.activityExperiencedDuration);\n if (activityDurationMs >= activityLimitMs) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Parse ISO 8601 duration to milliseconds\n * @param {string} duration - ISO 8601 duration string\n * @return {number} - Duration in milliseconds\n */\n public parseDuration(duration: string): number {\n if (!duration || typeof duration !== \"string\") {\n return 0;\n }\n\n // Full ISO 8601 duration regex\n const regex =\n /^P(?:(\\d+(?:\\.\\d+)?)Y)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)W)?(?:(\\d+(?:\\.\\d+)?)D)?(?:T(?:(\\d+(?:\\.\\d+)?)H)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/;\n\n const matches = duration.match(regex);\n if (!matches || duration === \"P\" || duration.endsWith(\"T\")) {\n return 0;\n }\n\n const years = parseFloat(matches[1] || \"0\");\n const months = parseFloat(matches[2] || \"0\");\n const weeks = parseFloat(matches[3] || \"0\");\n const days = parseFloat(matches[4] || \"0\");\n const hours = parseFloat(matches[5] || \"0\");\n const minutes = parseFloat(matches[6] || \"0\");\n const seconds = parseFloat(matches[7] || \"0\");\n\n let totalMs = 0;\n totalMs += years * 365.25 * 24 * 3600 * 1000;\n totalMs += months * 30.44 * 24 * 3600 * 1000;\n totalMs += weeks * 7 * 24 * 3600 * 1000;\n totalMs += days * 24 * 3600 * 1000;\n totalMs += hours * 3600 * 1000;\n totalMs += minutes * 60 * 1000;\n totalMs += seconds * 1000;\n\n return totalMs;\n }\n\n /**\n * Get elapsed attempt seconds for an activity\n * @param {Activity} activity - The activity\n * @return {number} - Elapsed seconds\n */\n public getElapsedSeconds(activity: Activity): number {\n if (this.getAttemptElapsedSecondsHook) {\n try {\n return this.getAttemptElapsedSecondsHook(activity) || 0;\n } catch {\n return 0;\n }\n }\n\n if (activity.attemptAbsoluteStartTime) {\n const start = new Date(activity.attemptAbsoluteStartTime).getTime();\n const nowMs = this.now().getTime();\n if (!Number.isNaN(start) && nowMs > start) {\n return Math.max(0, (nowMs - start) / 1000);\n }\n }\n\n return 0;\n }\n\n /**\n * Check if time limit has been exceeded\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if time limit exceeded\n */\n public isTimeLimitExceeded(activity: Activity): boolean {\n let limit = activity.timeLimitDuration;\n if (!limit && activity.attemptAbsoluteDurationLimit) {\n limit = activity.attemptAbsoluteDurationLimit;\n }\n\n if (!limit) {\n return false;\n }\n\n const limitSeconds = getDurationAsSeconds(limit, scorm2004_regex.CMITimespan);\n if (limitSeconds <= 0) {\n return false;\n }\n\n const elapsedSeconds = this.getElapsedSeconds(activity);\n return elapsedSeconds > limitSeconds;\n }\n\n /**\n * Check if activity is outside available time range\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if outside time range\n */\n public isOutsideAvailableTimeRange(activity: Activity): boolean {\n const now = this.now();\n\n if (activity.beginTimeLimit) {\n try {\n const beginDate = new Date(activity.beginTimeLimit);\n if (!Number.isNaN(beginDate.getTime()) && now < beginDate) {\n return true;\n }\n } catch {\n // Invalid date format\n }\n }\n\n if (activity.endTimeLimit) {\n try {\n const endDate = new Date(activity.endTimeLimit);\n if (!Number.isNaN(endDate.getTime()) && now > endDate) {\n return true;\n }\n } catch {\n // Invalid date format\n }\n }\n\n return false;\n }\n\n /**\n * Evaluate pre-condition rules and check if activity can be delivered\n * @param {Activity} activity - The activity to check\n * @return {{ canDeliver: boolean; wasSkipped: boolean }} - Delivery check result\n */\n public canDeliverActivity(activity: Activity): { canDeliver: boolean; wasSkipped: boolean } {\n // Check limit conditions first\n if (this.checkLimitConditions(activity)) {\n return { canDeliver: false, wasSkipped: false };\n }\n\n // Check pre-condition rules\n const preConditionResult = this.checkSequencingRules(\n activity,\n activity.sequencingRules.preConditionRules\n );\n\n const wasSkipped = preConditionResult === RuleActionType.SKIP;\n\n return {\n canDeliver: preConditionResult !== RuleActionType.SKIP &&\n preConditionResult !== RuleActionType.DISABLED,\n wasSkipped\n };\n }\n}\n","import { BaseCMI } from \"../../common/base_cmi\";\n\n/**\n * Enum for selection timing\n */\nexport enum SelectionTiming {\n NEVER = \"never\",\n ONCE = \"once\",\n ON_EACH_NEW_ATTEMPT = \"onEachNewAttempt\",\n}\n\n/**\n * Enum for randomization timing\n */\nexport enum RandomizationTiming {\n NEVER = \"never\",\n ONCE = \"once\",\n ON_EACH_NEW_ATTEMPT = \"onEachNewAttempt\",\n}\n\n/**\n * Class representing SCORM 2004 sequencing controls\n */\nexport class SequencingControls extends BaseCMI {\n // Sequencing Control Modes\n private _enabled: boolean = true;\n private _choice: boolean = true;\n private _choiceExit: boolean = true;\n // Per SCORM 2004 Sequencing & Navigation, flow defaults to true\n private _flow: boolean = true;\n private _forwardOnly: boolean = false;\n private _useCurrentAttemptObjectiveInfo: boolean = true;\n private _useCurrentAttemptProgressInfo: boolean = true;\n\n // Constrain Choice Controls\n private _preventActivation: boolean = false;\n private _constrainChoice: boolean = false;\n // Rule-driven traversal limiter (e.g., post-condition stopForwardTraversal)\n private _stopForwardTraversal: boolean = false;\n\n // Rollup Controls\n private _rollupObjectiveSatisfied: boolean = true;\n private _rollupProgressCompletion: boolean = true;\n private _objectiveMeasureWeight: number = 1.0;\n\n // Selection Controls\n private _selectionTiming: SelectionTiming = SelectionTiming.NEVER;\n private _selectCount: number | null = null;\n private _selectionCountStatus: boolean = false;\n private _randomizeChildren: boolean = false;\n\n // Randomization Controls\n private _randomizationTiming: RandomizationTiming = RandomizationTiming.NEVER;\n private _reorderChildren: boolean = false;\n\n // Auto-completion/satisfaction controls\n private _completionSetByContent: boolean = false;\n private _objectiveSetByContent: boolean = false;\n\n // Delivery Controls\n private _tracked: boolean = true;\n\n /**\n * Constructor for SequencingControls\n */\n constructor() {\n super(\"sequencingControls\");\n }\n\n /**\n * Reset the sequencing controls to their default values\n */\n reset() {\n this._initialized = false;\n this._enabled = true;\n this._choice = true;\n this._choiceExit = true;\n this._flow = true;\n this._forwardOnly = false;\n this._useCurrentAttemptObjectiveInfo = true;\n this._useCurrentAttemptProgressInfo = true;\n this._preventActivation = false;\n this._constrainChoice = false;\n this._stopForwardTraversal = false;\n this._rollupObjectiveSatisfied = true;\n this._rollupProgressCompletion = true;\n this._objectiveMeasureWeight = 1.0;\n this._selectionTiming = SelectionTiming.NEVER;\n this._selectCount = null;\n this._selectionCountStatus = false;\n this._randomizeChildren = false;\n this._randomizationTiming = RandomizationTiming.NEVER;\n this._reorderChildren = false;\n this._completionSetByContent = false;\n this._objectiveSetByContent = false;\n this._tracked = true;\n }\n\n /**\n * Getter for enabled\n * @return {boolean}\n */\n get enabled(): boolean {\n return this._enabled;\n }\n\n /**\n * Setter for enabled\n * @param {boolean} enabled\n */\n set enabled(enabled: boolean) {\n this._enabled = enabled;\n }\n\n /**\n * Getter for choice\n * @return {boolean}\n */\n get choice(): boolean {\n return this._choice;\n }\n\n /**\n * Setter for choice\n * @param {boolean} choice\n */\n set choice(choice: boolean) {\n this._choice = choice;\n }\n\n /**\n * Getter for choiceExit\n * @return {boolean}\n */\n get choiceExit(): boolean {\n return this._choiceExit;\n }\n\n /**\n * Setter for choiceExit\n * @param {boolean} choiceExit\n */\n set choiceExit(choiceExit: boolean) {\n this._choiceExit = choiceExit;\n }\n\n /**\n * Getter for flow\n * @return {boolean}\n */\n get flow(): boolean {\n return this._flow;\n }\n\n /**\n * Setter for flow\n * @param {boolean} flow\n */\n set flow(flow: boolean) {\n this._flow = flow;\n }\n\n /**\n * Getter for forwardOnly\n * @return {boolean}\n */\n get forwardOnly(): boolean {\n return this._forwardOnly;\n }\n\n /**\n * Setter for forwardOnly\n * @param {boolean} forwardOnly\n */\n set forwardOnly(forwardOnly: boolean) {\n this._forwardOnly = forwardOnly;\n }\n\n /**\n * Getter for useCurrentAttemptObjectiveInfo\n * @return {boolean}\n */\n get useCurrentAttemptObjectiveInfo(): boolean {\n return this._useCurrentAttemptObjectiveInfo;\n }\n\n /**\n * Setter for useCurrentAttemptObjectiveInfo\n * @param {boolean} useCurrentAttemptObjectiveInfo\n */\n set useCurrentAttemptObjectiveInfo(useCurrentAttemptObjectiveInfo: boolean) {\n this._useCurrentAttemptObjectiveInfo = useCurrentAttemptObjectiveInfo;\n }\n\n /**\n * Getter for useCurrentAttemptProgressInfo\n * @return {boolean}\n */\n get useCurrentAttemptProgressInfo(): boolean {\n return this._useCurrentAttemptProgressInfo;\n }\n\n /**\n * Setter for useCurrentAttemptProgressInfo\n * @param {boolean} useCurrentAttemptProgressInfo\n */\n set useCurrentAttemptProgressInfo(useCurrentAttemptProgressInfo: boolean) {\n this._useCurrentAttemptProgressInfo = useCurrentAttemptProgressInfo;\n }\n\n /**\n * Getter for preventActivation\n * @return {boolean}\n */\n get preventActivation(): boolean {\n return this._preventActivation;\n }\n\n /**\n * Setter for preventActivation\n * @param {boolean} preventActivation\n */\n set preventActivation(preventActivation: boolean) {\n this._preventActivation = preventActivation;\n }\n\n /**\n * Getter for constrainChoice\n * @return {boolean}\n */\n get constrainChoice(): boolean {\n return this._constrainChoice;\n }\n\n /**\n * Setter for constrainChoice\n * @param {boolean} constrainChoice\n */\n set constrainChoice(constrainChoice: boolean) {\n this._constrainChoice = constrainChoice;\n }\n\n /**\n * Getter for stopForwardTraversal\n * @return {boolean}\n */\n get stopForwardTraversal(): boolean {\n return this._stopForwardTraversal;\n }\n\n /**\n * Setter for stopForwardTraversal\n * @param {boolean} stopForwardTraversal\n */\n set stopForwardTraversal(stopForwardTraversal: boolean) {\n this._stopForwardTraversal = stopForwardTraversal;\n }\n\n /**\n * Getter for rollupObjectiveSatisfied\n * @return {boolean}\n */\n get rollupObjectiveSatisfied(): boolean {\n return this._rollupObjectiveSatisfied;\n }\n\n /**\n * Setter for rollupObjectiveSatisfied\n * @param {boolean} rollupObjectiveSatisfied\n */\n set rollupObjectiveSatisfied(rollupObjectiveSatisfied: boolean) {\n this._rollupObjectiveSatisfied = rollupObjectiveSatisfied;\n }\n\n /**\n * Getter for rollupProgressCompletion\n * @return {boolean}\n */\n get rollupProgressCompletion(): boolean {\n return this._rollupProgressCompletion;\n }\n\n /**\n * Setter for rollupProgressCompletion\n * @param {boolean} rollupProgressCompletion\n */\n set rollupProgressCompletion(rollupProgressCompletion: boolean) {\n this._rollupProgressCompletion = rollupProgressCompletion;\n }\n\n /**\n * Getter for objectiveMeasureWeight\n * @return {number}\n */\n get objectiveMeasureWeight(): number {\n return this._objectiveMeasureWeight;\n }\n\n /**\n * Setter for objectiveMeasureWeight\n * @param {number} objectiveMeasureWeight\n */\n set objectiveMeasureWeight(objectiveMeasureWeight: number) {\n // According to SCORM 2004, weight must be >= 0 but there's no upper limit\n if (objectiveMeasureWeight >= 0) {\n this._objectiveMeasureWeight = objectiveMeasureWeight;\n }\n }\n\n /**\n * Check if choice navigation is allowed\n * @return {boolean} - True if choice navigation is allowed, false otherwise\n */\n isChoiceNavigationAllowed(): boolean {\n return this._enabled && !this._constrainChoice;\n }\n\n /**\n * Check if flow navigation is allowed\n * @return {boolean} - True if flow navigation is allowed, false otherwise\n */\n isFlowNavigationAllowed(): boolean {\n return this._enabled && this._flow;\n }\n\n /**\n * Check if forward navigation is allowed\n * @return {boolean} - True if forward navigation is allowed, false otherwise\n */\n isForwardNavigationAllowed(): boolean {\n // Forward navigation (Continue request) is only valid when flow mode is\n // enabled. The forwardOnly flag simply restricts backward navigation and\n // does not affect the ability to move forward when flow is disabled.\n return this._enabled && this._flow;\n }\n\n /**\n * Check if backward navigation is allowed\n * @return {boolean} - True if backward navigation is allowed, false otherwise\n */\n isBackwardNavigationAllowed(): boolean {\n // Previous navigation is also part of flow based navigation and should only\n // be permitted when flow mode is enabled and forwardOnly does not restrict\n // going backwards.\n return this._enabled && this._flow && !this._forwardOnly;\n }\n\n /**\n * Getter for selectionTiming\n * @return {SelectionTiming}\n */\n get selectionTiming(): SelectionTiming {\n return this._selectionTiming;\n }\n\n /**\n * Setter for selectionTiming\n * @param {SelectionTiming} selectionTiming\n */\n set selectionTiming(selectionTiming: SelectionTiming) {\n this._selectionTiming = selectionTiming;\n }\n\n /**\n * Getter for selectCount\n * @return {number | null}\n */\n get selectCount(): number | null {\n return this._selectCount;\n }\n\n /**\n * Setter for selectCount\n * @param {number | null} selectCount\n */\n set selectCount(selectCount: number | null) {\n if (selectCount === null || selectCount > 0) {\n this._selectCount = selectCount;\n }\n }\n\n /**\n * Getter for selectionCountStatus\n * @return {boolean}\n */\n get selectionCountStatus(): boolean {\n return this._selectionCountStatus;\n }\n\n /**\n * Setter for selectionCountStatus\n * @param {boolean} selectionCountStatus\n */\n set selectionCountStatus(selectionCountStatus: boolean) {\n this._selectionCountStatus = selectionCountStatus;\n }\n\n /**\n * Getter for randomizeChildren\n * @return {boolean}\n */\n get randomizeChildren(): boolean {\n return this._randomizeChildren;\n }\n\n /**\n * Setter for randomizeChildren\n * @param {boolean} randomizeChildren\n */\n set randomizeChildren(randomizeChildren: boolean) {\n this._randomizeChildren = randomizeChildren;\n }\n\n /**\n * Getter for randomizationTiming\n * @return {RandomizationTiming}\n */\n get randomizationTiming(): RandomizationTiming {\n return this._randomizationTiming;\n }\n\n /**\n * Setter for randomizationTiming\n * @param {RandomizationTiming} randomizationTiming\n */\n set randomizationTiming(randomizationTiming: RandomizationTiming) {\n this._randomizationTiming = randomizationTiming;\n }\n\n /**\n * Getter for reorderChildren\n * @return {boolean}\n */\n get reorderChildren(): boolean {\n return this._reorderChildren;\n }\n\n /**\n * Setter for reorderChildren\n * @param {boolean} reorderChildren\n */\n set reorderChildren(reorderChildren: boolean) {\n this._reorderChildren = reorderChildren;\n }\n\n /**\n * Getter for completionSetByContent\n * @return {boolean}\n */\n get completionSetByContent(): boolean {\n return this._completionSetByContent;\n }\n\n /**\n * Setter for completionSetByContent\n * @param {boolean} completionSetByContent\n */\n set completionSetByContent(completionSetByContent: boolean) {\n this._completionSetByContent = completionSetByContent;\n }\n\n /**\n * Getter for objectiveSetByContent\n * @return {boolean}\n */\n get objectiveSetByContent(): boolean {\n return this._objectiveSetByContent;\n }\n\n /**\n * Setter for objectiveSetByContent\n * @param {boolean} objectiveSetByContent\n */\n set objectiveSetByContent(objectiveSetByContent: boolean) {\n this._objectiveSetByContent = objectiveSetByContent;\n }\n\n /**\n * Getter for tracked\n * @return {boolean}\n */\n get tracked(): boolean {\n return this._tracked;\n }\n\n /**\n * Setter for tracked\n * @param {boolean} tracked\n */\n set tracked(tracked: boolean) {\n this._tracked = tracked;\n }\n\n /**\n * toJSON for SequencingControls\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n enabled: this._enabled,\n choice: this._choice,\n choiceExit: this._choiceExit,\n flow: this._flow,\n forwardOnly: this._forwardOnly,\n useCurrentAttemptObjectiveInfo: this._useCurrentAttemptObjectiveInfo,\n useCurrentAttemptProgressInfo: this._useCurrentAttemptProgressInfo,\n preventActivation: this._preventActivation,\n constrainChoice: this._constrainChoice,\n stopForwardTraversal: this._stopForwardTraversal,\n rollupObjectiveSatisfied: this._rollupObjectiveSatisfied,\n rollupProgressCompletion: this._rollupProgressCompletion,\n objectiveMeasureWeight: this._objectiveMeasureWeight,\n selectionTiming: this._selectionTiming,\n selectCount: this._selectCount,\n selectionCountStatus: this._selectionCountStatus,\n randomizeChildren: this._randomizeChildren,\n randomizationTiming: this._randomizationTiming,\n reorderChildren: this._reorderChildren,\n completionSetByContent: this._completionSetByContent,\n objectiveSetByContent: this._objectiveSetByContent,\n tracked: this._tracked,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { Activity } from \"./activity\";\nimport { SelectionTiming, RandomizationTiming } from \"./sequencing_controls\";\n\n/**\n * Class implementing SCORM 2004 Selection and Randomization processes (SR.1 and SR.2)\n */\nexport class SelectionRandomization {\n /**\n * Select Children Process (SR.1)\n * Selects a subset of child activities based on selection controls\n * @param {Activity} activity - The parent activity whose children need to be selected\n * @return {Activity[]} - The selected child activities\n */\n public static selectChildrenProcess(activity: Activity): Activity[] {\n const controls = activity.sequencingControls;\n const children = [...activity.children]; // Create a copy to avoid mutating original\n\n // Check if selection should occur\n if (controls.selectionTiming === SelectionTiming.NEVER) {\n return children;\n }\n\n // Check if selection has already been done (for ONCE timing)\n if (\n controls.selectionTiming === SelectionTiming.ONCE &&\n controls.selectionCountStatus\n ) {\n return children;\n }\n\n // For non-ONCE timing, Selection Count Status must be True for selection to occur (per SCORM spec)\n if (controls.selectionTiming !== SelectionTiming.ONCE && !controls.selectionCountStatus) {\n return children;\n }\n\n // Check if we need to select children\n const selectCount = controls.selectCount;\n if (selectCount === null || selectCount >= children.length) {\n // Mark selection as done if timing is ONCE\n if (controls.selectionTiming === SelectionTiming.ONCE) {\n controls.selectionCountStatus = true;\n }\n return children;\n }\n\n // Perform selection\n const selectedChildren: Activity[] = [];\n const availableIndices = children.map((_, index) => index);\n\n // Randomly select children\n for (let i = 0; i < selectCount; i++) {\n if (availableIndices.length === 0) break;\n \n const randomIndex = Math.floor(Math.random() * availableIndices.length);\n const childIndex = availableIndices[randomIndex];\n if (childIndex !== undefined && children[childIndex]) {\n selectedChildren.push(children[childIndex]);\n }\n \n // Remove selected index from available indices\n availableIndices.splice(randomIndex, 1);\n }\n\n // Mark selection as done only if timing is ONCE\n if (controls.selectionTiming === SelectionTiming.ONCE) {\n controls.selectionCountStatus = true;\n }\n\n // Hide non-selected children from choice\n for (const child of children) {\n if (!selectedChildren.includes(child)) {\n child.isHiddenFromChoice = true;\n child.isAvailable = false;\n }\n }\n\n return selectedChildren;\n }\n\n /**\n * Randomize Children Process (SR.2)\n * Randomizes the order of child activities based on randomization controls\n * @param {Activity} activity - The parent activity whose children need to be randomized\n * @return {Activity[]} - The randomized child activities\n */\n public static randomizeChildrenProcess(activity: Activity): Activity[] {\n const controls = activity.sequencingControls;\n const children = [...activity.children]; // Create a copy to avoid mutating original\n\n // Check if randomization should occur\n if (controls.randomizationTiming === RandomizationTiming.NEVER) {\n return children;\n }\n\n // Check if randomization has already been done (for ONCE timing)\n if (\n controls.randomizationTiming === RandomizationTiming.ONCE &&\n controls.reorderChildren\n ) {\n return children;\n }\n\n // Check if we need to randomize\n if (!controls.randomizeChildren) {\n return children;\n }\n\n // Perform Fisher-Yates shuffle\n const randomizedChildren = [...children];\n for (let i = randomizedChildren.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n const tempI = randomizedChildren[i];\n const tempJ = randomizedChildren[j];\n if (tempI && tempJ) {\n randomizedChildren[i] = tempJ;\n randomizedChildren[j] = tempI;\n }\n }\n\n // Mark randomization as done only if timing is ONCE\n if (controls.randomizationTiming === RandomizationTiming.ONCE) {\n controls.reorderChildren = true;\n }\n\n // Update the activity's children array with the new order\n activity.children.length = 0;\n activity.children.push(...randomizedChildren);\n\n return randomizedChildren;\n }\n\n /**\n * Apply selection and randomization to an activity\n * This combines both SR.1 and SR.2 processes\n * @param {Activity} activity - The parent activity\n * @param {boolean} isNewAttempt - Whether this is a new attempt on the activity\n * @return {Activity[]} - The processed child activities\n */\n public static applySelectionAndRandomization(\n activity: Activity,\n isNewAttempt: boolean = false,\n ): Activity[] {\n const controls = activity.sequencingControls;\n\n // Exit if activity is active or suspended AND this is not a new attempt (per SCORM spec SR.1, SR.2)\n // On a new attempt, we DO apply selection/randomization even though the activity becomes active\n if (!isNewAttempt && (activity.isActive || activity.isSuspended)) {\n return activity.children;\n }\n\n // Check if we should apply selection/randomization\n let shouldApplySelection = false;\n let shouldApplyRandomization = false;\n\n // For ON_EACH_NEW_ATTEMPT timing, only apply on new attempts\n if (controls.selectionTiming === SelectionTiming.ON_EACH_NEW_ATTEMPT) {\n shouldApplySelection = isNewAttempt;\n if (isNewAttempt) {\n controls.selectionCountStatus = true; // Enable selection for new attempt\n }\n } else if (controls.selectionTiming === SelectionTiming.ONCE) {\n shouldApplySelection = !controls.selectionCountStatus;\n // selectionCountStatus will be set to true by selectChildrenProcess after selection\n }\n\n if (controls.randomizationTiming === RandomizationTiming.ON_EACH_NEW_ATTEMPT) {\n shouldApplyRandomization = isNewAttempt;\n if (isNewAttempt) {\n controls.reorderChildren = false;\n }\n } else if (controls.randomizationTiming === RandomizationTiming.ONCE) {\n shouldApplyRandomization = !controls.reorderChildren;\n }\n\n // Apply selection first if needed\n if (shouldApplySelection) {\n this.selectChildrenProcess(activity);\n }\n\n // Then apply randomization if needed\n if (shouldApplyRandomization) {\n this.randomizeChildrenProcess(activity);\n }\n\n // Get the final processed children\n const processedChildren = activity.children.filter(child => child.isAvailable);\n \n // Store the processed children on the activity\n activity.setProcessedChildren(processedChildren);\n \n return processedChildren;\n }\n\n /**\n * Check if selection is needed for an activity\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if selection is needed\n */\n public static isSelectionNeeded(activity: Activity): boolean {\n const controls = activity.sequencingControls;\n\n if (controls.selectionTiming === SelectionTiming.NEVER) {\n return false;\n }\n\n if (\n controls.selectionTiming === SelectionTiming.ONCE &&\n controls.selectionCountStatus\n ) {\n return false;\n }\n\n return controls.selectCount !== null && controls.selectCount < activity.children.length;\n }\n\n /**\n * Check if randomization is needed for an activity\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if randomization is needed\n */\n public static isRandomizationNeeded(activity: Activity): boolean {\n const controls = activity.sequencingControls;\n \n if (controls.randomizationTiming === RandomizationTiming.NEVER) {\n return false;\n }\n\n if (\n controls.randomizationTiming === RandomizationTiming.ONCE &&\n controls.reorderChildren\n ) {\n return false;\n }\n\n return controls.randomizeChildren;\n }\n}","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { RuleEvaluationEngine } from \"../rules/rule_evaluation_engine\";\nimport {\n FlowSubprocessResult,\n FlowSubprocessMode\n} from \"../rules/sequencing_request_types\";\nimport { SelectionRandomization } from \"../selection_randomization\";\n\n/**\n * Result of flow tree traversal\n */\nexport interface FlowTreeTraversalResult {\n activity: Activity | null;\n endSequencingSession: boolean;\n exception?: string;\n}\n\n/**\n * FlowTraversalService - Handles tree traversal for flow-based navigation\n *\n * This class extracts the flow traversal logic from SequencingProcess:\n * - SB.2.1: Flow Tree Traversal Subprocess\n * - SB.2.2: Flow Activity Traversal Subprocess\n * - SB.2.3: Flow Subprocess\n * - Activity checking and delivery validation\n *\n * Flow traversal is used for:\n * - CONTINUE navigation (forward flow)\n * - PREVIOUS navigation (backward flow)\n * - Finding the first deliverable activity from a cluster\n */\nexport class FlowTraversalService {\n constructor(\n private activityTree: ActivityTree,\n private ruleEngine: RuleEvaluationEngine\n ) {}\n\n /**\n * Flow Subprocess (SB.2.3)\n * Traverses the activity tree in the specified direction to find a deliverable activity\n * @param {Activity} fromActivity - The activity to flow from\n * @param {FlowSubprocessMode} direction - The flow direction\n * @return {FlowSubprocessResult} - Result containing the deliverable activity\n */\n public flowSubprocess(\n fromActivity: Activity,\n direction: FlowSubprocessMode\n ): FlowSubprocessResult {\n let candidateActivity: Activity | null = fromActivity;\n let firstIteration = true;\n let lastCandidateHadNoChildren = false;\n\n while (candidateActivity) {\n const traversalResult = this.flowTreeTraversalSubprocess(\n candidateActivity,\n direction,\n firstIteration\n );\n\n if (!traversalResult.activity) {\n let exceptionCode: string | null = null;\n if (traversalResult.exception) {\n exceptionCode = traversalResult.exception;\n } else if (direction === FlowSubprocessMode.BACKWARD) {\n exceptionCode = \"SB.2.1-3\";\n } else if (lastCandidateHadNoChildren) {\n exceptionCode = \"SB.2.1-2\";\n }\n\n return new FlowSubprocessResult(\n candidateActivity,\n false,\n exceptionCode,\n traversalResult.endSequencingSession\n );\n }\n\n lastCandidateHadNoChildren =\n traversalResult.activity.children.length > 0 &&\n traversalResult.activity.getAvailableChildren().length === 0;\n\n const deliverable = this.flowActivityTraversalSubprocess(\n traversalResult.activity,\n direction === FlowSubprocessMode.FORWARD,\n true,\n direction\n );\n\n if (deliverable) {\n return new FlowSubprocessResult(deliverable, true, null, false);\n }\n\n candidateActivity = traversalResult.activity;\n firstIteration = false;\n }\n\n return new FlowSubprocessResult(null, false, null, false);\n }\n\n /**\n * Flow Tree Traversal Subprocess (SB.2.1)\n * Traverses the activity tree to find the next activity in the specified direction\n * @param {Activity} fromActivity - The activity to traverse from\n * @param {FlowSubprocessMode} direction - The traversal direction\n * @param {boolean} skipChildren - Whether to skip checking children\n * @return {FlowTreeTraversalResult} - The next activity and flags\n */\n public flowTreeTraversalSubprocess(\n fromActivity: Activity,\n direction: FlowSubprocessMode,\n skipChildren: boolean = false\n ): FlowTreeTraversalResult {\n if (direction === FlowSubprocessMode.FORWARD) {\n return this.traverseForward(fromActivity, skipChildren);\n } else {\n return this.traverseBackward(fromActivity);\n }\n }\n\n /**\n * Traverse forward in the activity tree\n * @param {Activity} fromActivity - Starting activity\n * @param {boolean} skipChildren - Whether to skip children\n * @return {FlowTreeTraversalResult}\n */\n private traverseForward(\n fromActivity: Activity,\n skipChildren: boolean\n ): FlowTreeTraversalResult {\n // Check if we're at the last activity\n if (skipChildren && this.isActivityLastOverall(fromActivity)) {\n // Terminate all descendent attempts at root\n if (this.activityTree.root) {\n this.terminateDescendentAttempts(this.activityTree.root);\n }\n return { activity: null, endSequencingSession: true };\n }\n\n // Check children first\n if (!skipChildren) {\n this.ensureSelectionAndRandomization(fromActivity);\n const children = fromActivity.getAvailableChildren();\n if (children.length > 0) {\n return { activity: children[0] || null, endSequencingSession: false };\n }\n }\n\n // No children, try to get next sibling\n let current: Activity | null = fromActivity;\n while (current) {\n const nextSibling = this.activityTree.getNextSibling(current);\n if (nextSibling) {\n return { activity: nextSibling, endSequencingSession: false };\n }\n current = current.parent;\n }\n\n // Reached end of tree\n if (this.activityTree.root) {\n this.terminateDescendentAttempts(this.activityTree.root);\n }\n return { activity: null, endSequencingSession: true };\n }\n\n /**\n * Traverse backward in the activity tree\n * @param {Activity} fromActivity - Starting activity\n * @return {FlowTreeTraversalResult}\n */\n private traverseBackward(fromActivity: Activity): FlowTreeTraversalResult {\n // Check forwardOnly constraint\n if (fromActivity.parent && fromActivity.parent.sequencingControls.forwardOnly) {\n return { activity: null, endSequencingSession: false, exception: \"SB.2.1-4\" };\n }\n\n // Try to get previous sibling\n const previousSibling = this.activityTree.getPreviousSibling(fromActivity);\n if (previousSibling) {\n return {\n activity: this.getLastDescendant(previousSibling),\n endSequencingSession: false\n };\n }\n\n // No previous sibling, try going up to parent\n let current: Activity | null = fromActivity;\n let ancestorIterations = 0;\n const maxIterations = 10000;\n\n while (current && current.parent) {\n if (++ancestorIterations > maxIterations) {\n throw new Error(\"Infinite loop detected in backward traversal\");\n }\n\n const parentPreviousSibling = this.activityTree.getPreviousSibling(current.parent);\n if (parentPreviousSibling) {\n return {\n activity: this.getLastDescendant(parentPreviousSibling),\n endSequencingSession: false\n };\n }\n current = current.parent;\n }\n\n // Reached beginning of tree\n return { activity: null, endSequencingSession: false };\n }\n\n /**\n * Get the last descendant of an activity\n * @param {Activity} activity - The activity\n * @return {Activity} - The last descendant\n */\n private getLastDescendant(activity: Activity): Activity {\n let lastDescendant = activity;\n let iterations = 0;\n const maxIterations = 10000;\n\n while (true) {\n if (++iterations > maxIterations) {\n throw new Error(\"Infinite loop detected while getting last descendant\");\n }\n\n this.ensureSelectionAndRandomization(lastDescendant);\n const children = lastDescendant.getAvailableChildren();\n if (children.length === 0) {\n break;\n }\n\n const lastChild = children[children.length - 1];\n if (!lastChild) break;\n lastDescendant = lastChild;\n }\n\n return lastDescendant;\n }\n\n /**\n * Flow Activity Traversal Subprocess (SB.2.2)\n * Checks if an activity can be delivered and flows into clusters if needed\n * @param {Activity} activity - The activity to check\n * @param {boolean} _direction - Direction (unused but part of spec)\n * @param {boolean} considerChildren - Whether to consider children\n * @param {FlowSubprocessMode} mode - The flow mode\n * @return {Activity | null} - The deliverable activity or null\n */\n public flowActivityTraversalSubprocess(\n activity: Activity,\n _direction: boolean,\n considerChildren: boolean,\n mode: FlowSubprocessMode\n ): Activity | null {\n // Check flow control\n const parent = activity.parent;\n if (parent && !parent.sequencingControls.flow) {\n return null;\n }\n\n // Check availability\n if (!activity.isAvailable) {\n return null;\n }\n\n // Check stopForwardTraversal for forward direction\n if (mode === FlowSubprocessMode.FORWARD &&\n activity.sequencingControls.stopForwardTraversal) {\n return null;\n }\n\n // If it's a cluster and we should consider children, try to flow into it\n if (considerChildren) {\n this.ensureSelectionAndRandomization(activity);\n const availableChildren = activity.getAvailableChildren();\n\n for (const child of availableChildren) {\n const deliverable = this.flowActivityTraversalSubprocess(\n child,\n mode === FlowSubprocessMode.FORWARD,\n true,\n mode\n );\n if (deliverable) {\n return deliverable;\n }\n }\n }\n\n // If it's a leaf, check if it can be delivered\n if (activity.children.length === 0) {\n if (this.checkActivityProcess(activity)) {\n return activity;\n }\n return null;\n }\n\n return null;\n }\n\n /**\n * Check Activity Process (SB.2.3)\n * Validates if an activity can be delivered\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if activity can be delivered\n */\n public checkActivityProcess(activity: Activity): boolean {\n // Check availability\n if (!activity.isAvailable) {\n return false;\n }\n\n // For leaf activities, check visibility\n if (activity.children.length === 0 && !activity.isVisible) {\n return false;\n }\n\n // Check limit conditions\n if (this.ruleEngine.checkLimitConditions(activity)) {\n return false;\n }\n\n // Check pre-condition rules\n const deliveryCheck = this.ruleEngine.canDeliverActivity(activity);\n activity.wasSkipped = deliveryCheck.wasSkipped;\n\n return deliveryCheck.canDeliver;\n }\n\n /**\n * Ensure selection and randomization is applied to an activity\n * @param {Activity} activity - The activity to process\n */\n public ensureSelectionAndRandomization(activity: Activity): void {\n if (\n activity.getAvailableChildren() === activity.children &&\n (SelectionRandomization.isSelectionNeeded(activity) ||\n SelectionRandomization.isRandomizationNeeded(activity))\n ) {\n SelectionRandomization.applySelectionAndRandomization(activity, activity.isNewAttempt);\n }\n }\n\n /**\n * Check if activity is the last activity in the tree (forward preorder)\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if last in tree\n */\n private isActivityLastOverall(activity: Activity): boolean {\n if (activity.children.length > 0) {\n return false;\n }\n\n let current: Activity | null = activity;\n while (current) {\n if (this.activityTree.getNextSibling(current)) {\n return false;\n }\n current = current.parent;\n }\n\n return true;\n }\n\n /**\n * Terminate descendent attempts (simplified version)\n * Full version with exit rules is in SequencingProcess\n * @param {Activity} activity - The activity\n */\n private terminateDescendentAttempts(activity: Activity): void {\n activity.isActive = false;\n for (const child of activity.children) {\n this.terminateDescendentAttempts(child);\n }\n }\n\n /**\n * Find the first deliverable activity from a cluster\n * Used for START and RETRY_ALL requests\n * @param {Activity} cluster - The cluster activity\n * @return {Activity | null} - The first deliverable activity\n */\n public findFirstDeliverableActivity(cluster: Activity): Activity | null {\n // If the cluster itself is a leaf (no children), check if it can be delivered\n if (cluster.children.length === 0) {\n if (this.checkActivityProcess(cluster)) {\n return cluster;\n }\n return null;\n }\n\n // Otherwise, look for a deliverable child\n this.ensureSelectionAndRandomization(cluster);\n const availableChildren = cluster.getAvailableChildren();\n\n for (const child of availableChildren) {\n const deliverable = this.flowActivityTraversalSubprocess(\n child,\n true,\n true,\n FlowSubprocessMode.FORWARD\n );\n if (deliverable) {\n return deliverable;\n }\n }\n\n return null;\n }\n\n /**\n * Can activity be delivered (public wrapper)\n * @param {Activity} activity - The activity\n * @return {boolean} - True if can be delivered\n */\n public canDeliver(activity: Activity): boolean {\n return this.checkActivityProcess(activity);\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { FlowTraversalService } from \"../traversal/flow_traversal_service\";\nimport {\n SequencingResult,\n DeliveryRequestType,\n FlowSubprocessMode\n} from \"../rules/sequencing_request_types\";\n\n/**\n * FlowRequestHandler - Handles flow-based sequencing requests\n *\n * This handler manages:\n * - START: Start a new sequencing session\n * - RESUME_ALL: Resume a suspended sequencing session\n * - CONTINUE: Navigate to the next activity (forward flow)\n * - PREVIOUS: Navigate to the previous activity (backward flow)\n */\nexport class FlowRequestHandler {\n constructor(\n private activityTree: ActivityTree,\n private traversalService: FlowTraversalService\n ) {}\n\n /**\n * Start Sequencing Request Process (SB.2.5)\n * Initiates a new sequencing session from the root\n * @return {SequencingResult}\n */\n public handleStart(): SequencingResult {\n const result = new SequencingResult();\n\n // SB.2.5-1: Check if there's a root (activity tree)\n if (!this.activityTree.root) {\n result.exception = \"SB.2.5-1\";\n return result;\n }\n\n // SB.2.5-2: Check if sequencing session has already begun\n if (this.activityTree.currentActivity) {\n result.exception = \"SB.2.5-2\";\n return result;\n }\n\n // Find the first deliverable activity\n const deliverableActivity = this.traversalService.findFirstDeliverableActivity(\n this.activityTree.root\n );\n\n if (!deliverableActivity) {\n result.exception = \"SB.2.5-3\";\n return result;\n }\n\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = deliverableActivity;\n return result;\n }\n\n /**\n * Resume All Sequencing Request Process (SB.2.6)\n * Resumes a suspended sequencing session\n * @return {SequencingResult}\n */\n public handleResumeAll(): SequencingResult {\n const result = new SequencingResult();\n\n // Check if there's a suspended activity\n if (!this.activityTree.suspendedActivity) {\n result.exception = \"SB.2.6-1\";\n return result;\n }\n\n // Check if there's already a current activity\n if (this.activityTree.currentActivity) {\n result.exception = \"SB.2.6-2\";\n return result;\n }\n\n // Resume the suspended activity\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = this.activityTree.suspendedActivity;\n return result;\n }\n\n /**\n * Continue Sequencing Request Process (SB.2.7)\n * Navigates to the next activity in forward flow\n * @param {Activity} currentActivity - The current activity\n * @return {SequencingResult}\n */\n public handleContinue(currentActivity: Activity): SequencingResult {\n const result = new SequencingResult();\n\n // SB.2.7-1: Activity must be terminated (not active)\n if (currentActivity.isActive) {\n result.exception = \"SB.2.7-1\";\n return result;\n }\n\n // SB.2.7-2: Check flow control\n if (currentActivity.parent && !currentActivity.parent.sequencingControls.flow) {\n result.exception = \"SB.2.7-2\";\n return result;\n }\n\n // Use flow subprocess to find next deliverable\n const flowResult = this.traversalService.flowSubprocess(\n currentActivity,\n FlowSubprocessMode.FORWARD\n );\n\n // Propagate endSequencingSession flag\n result.endSequencingSession = flowResult.endSequencingSession;\n\n // Check if we found a deliverable activity\n if (!flowResult.deliverable || !flowResult.identifiedActivity) {\n // SB.2.7-2 when no activity available (regardless of endSequencingSession)\n result.exception = flowResult.exception || \"SB.2.7-2\";\n return result;\n }\n\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = flowResult.identifiedActivity;\n return result;\n }\n\n /**\n * Previous Sequencing Request Process (SB.2.8)\n * Navigates to the previous activity in backward flow\n * @param {Activity} currentActivity - The current activity\n * @return {SequencingResult}\n */\n public handlePrevious(currentActivity: Activity): SequencingResult {\n const result = new SequencingResult();\n\n // SB.2.8-1: Activity must be terminated (not active)\n if (currentActivity.isActive) {\n result.exception = \"SB.2.8-1\";\n return result;\n }\n\n // SB.2.8-2: Check flow control\n if (currentActivity.parent && !currentActivity.parent.sequencingControls.flow) {\n result.exception = \"SB.2.8-2\";\n return result;\n }\n\n // Check forwardOnly at all ancestor levels\n const forwardOnlyViolation = this.checkForwardOnlyViolation(currentActivity);\n if (forwardOnlyViolation) {\n result.exception = forwardOnlyViolation;\n return result;\n }\n\n // Use flow subprocess to find previous deliverable\n const flowResult = this.traversalService.flowSubprocess(\n currentActivity,\n FlowSubprocessMode.BACKWARD\n );\n\n // Check if we found a deliverable activity\n if (!flowResult.deliverable || !flowResult.identifiedActivity) {\n result.exception = flowResult.exception || \"SB.2.8-3\";\n return result;\n }\n\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = flowResult.identifiedActivity;\n return result;\n }\n\n /**\n * Check forwardOnly violation at all ancestor levels\n * @param {Activity} activity - The activity to check\n * @return {string | null} - Exception code or null\n */\n private checkForwardOnlyViolation(activity: Activity): string | null {\n let current: Activity | null = activity.parent;\n\n while (current) {\n if (current.sequencingControls.forwardOnly) {\n return \"SB.2.9-5\";\n }\n current = current.parent;\n }\n\n return null;\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { ActivityTreeQueries } from \"../utils/activity_tree_queries\";\nimport { ChoiceConstraintValidator } from \"../validators/choice_constraint_validator\";\nimport { FlowTraversalService } from \"../traversal/flow_traversal_service\";\nimport {\n SequencingResult,\n DeliveryRequestType,\n ChoiceTraversalResult\n} from \"../rules/sequencing_request_types\";\n\n/**\n * ChoiceRequestHandler - Handles choice-based sequencing requests\n *\n * This handler manages:\n * - CHOICE: Navigate to a specific activity selected by the learner\n * - JUMP: Navigate directly to a specific activity (4th Edition)\n *\n * Choice navigation is more complex than flow navigation because it must\n * validate many constraints at each ancestor level.\n */\nexport class ChoiceRequestHandler {\n constructor(\n private activityTree: ActivityTree,\n private constraintValidator: ChoiceConstraintValidator,\n private traversalService: FlowTraversalService,\n private treeQueries: ActivityTreeQueries\n ) {}\n\n /**\n * Choice Sequencing Request Process (SB.2.9)\n * Processes a choice navigation request to a specific activity\n * @param {string} targetActivityId - The target activity ID\n * @param {Activity | null} currentActivity - Current activity (may be null)\n * @return {SequencingResult}\n */\n public handleChoice(\n targetActivityId: string,\n currentActivity: Activity | null\n ): SequencingResult {\n const result = new SequencingResult();\n\n // Find the target activity\n const targetActivity = this.activityTree.getActivity(targetActivityId);\n if (!targetActivity) {\n result.exception = \"SB.2.9-1\";\n return result;\n }\n\n // SB.2.9-6: Check if current activity is terminated\n if (currentActivity && currentActivity.isActive) {\n result.exception = \"SB.2.9-6\";\n return result;\n }\n\n // Validate choice constraints\n const validation = this.constraintValidator.validateChoice(\n currentActivity,\n targetActivity,\n { checkAvailability: true }\n );\n\n if (!validation.valid) {\n result.exception = validation.exception;\n return result;\n }\n\n // Find common ancestor\n const commonAncestor = this.treeQueries.findCommonAncestor(\n currentActivity,\n targetActivity\n );\n\n // Terminate descendent attempts from common ancestor\n if (currentActivity) {\n this.terminateDescendentAttemptsProcess(\n commonAncestor || this.activityTree.root!\n );\n }\n\n // Form the activity path from target to common ancestor\n const activityPath = this.buildActivityPath(targetActivity, commonAncestor);\n\n // Evaluate each activity in the path\n for (const pathActivity of activityPath) {\n if (!this.traversalService.checkActivityProcess(pathActivity)) {\n // Sequencing ends with no delivery\n return result;\n }\n }\n\n // If target is not a leaf, use choice flow to find a deliverable leaf\n let deliveryTarget = targetActivity;\n if (targetActivity.children.length > 0) {\n const flowResult = this.choiceFlowSubprocess(targetActivity);\n\n if (!flowResult) {\n result.exception = \"SB.2.9-7\";\n return result;\n }\n\n deliveryTarget = flowResult;\n }\n\n // Deliver the identified activity\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = deliveryTarget;\n return result;\n }\n\n /**\n * Jump Sequencing Request Process (SB.2.13)\n * Processes a jump navigation request (SCORM 2004 4th Edition)\n * Jump bypasses most sequencing rules\n * @param {string} targetActivityId - The target activity ID\n * @return {SequencingResult}\n */\n public handleJump(targetActivityId: string): SequencingResult {\n const result = new SequencingResult();\n\n // Find the target activity\n const targetActivity = this.activityTree.getActivity(targetActivityId);\n if (!targetActivity) {\n result.exception = \"SB.2.13-1\";\n return result;\n }\n\n // Check if target is in the activity tree\n if (!this.treeQueries.isInTree(targetActivity)) {\n result.exception = \"SB.2.13-2\";\n return result;\n }\n\n // Check if target is available\n if (!targetActivity.isAvailable) {\n result.exception = \"SB.2.13-3\";\n return result;\n }\n\n // Deliver the target activity directly\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = targetActivity;\n return result;\n }\n\n /**\n * Get all activities available for choice navigation\n * @return {Activity[]} - Array of available activities\n */\n public getAvailableChoices(): Activity[] {\n const allActivities = this.activityTree.getAllActivities();\n const currentActivity = this.activityTree.currentActivity;\n const availableActivities: Activity[] = [];\n\n for (const activity of allActivities) {\n // Skip root activity\n if (activity === this.activityTree.root) {\n continue;\n }\n\n // Skip if hidden, unavailable, or invisible\n if (activity.isHiddenFromChoice || !activity.isAvailable || !activity.isVisible) {\n continue;\n }\n\n // Check if choice is allowed by parent\n if (activity.parent && !activity.parent.sequencingControls.choice) {\n continue;\n }\n\n // Validate the full choice path\n const validation = this.constraintValidator.validateChoice(currentActivity, activity);\n if (validation.valid) {\n availableActivities.push(activity);\n }\n }\n\n return availableActivities;\n }\n\n /**\n * Build the activity path from target to common ancestor\n * @param {Activity} targetActivity - Target activity\n * @param {Activity | null} commonAncestor - Common ancestor\n * @return {Activity[]} - Path of activities\n */\n private buildActivityPath(\n targetActivity: Activity,\n commonAncestor: Activity | null\n ): Activity[] {\n const activityPath: Activity[] = [];\n let activity: Activity | null = targetActivity;\n\n while (activity && activity !== commonAncestor) {\n activityPath.unshift(activity);\n activity = activity.parent;\n }\n\n return activityPath;\n }\n\n /**\n * Choice Flow Subprocess (SB.2.9.1)\n * Handles the flow logic specific to choice navigation requests\n * @param {Activity} targetActivity - The target activity for the choice\n * @return {Activity | null} - The activity to deliver, or null if flow fails\n */\n private choiceFlowSubprocess(targetActivity: Activity): Activity | null {\n // If target is a leaf, it's the delivery candidate\n if (targetActivity.children.length === 0) {\n return targetActivity;\n }\n\n // If target is a cluster, traverse to find a deliverable leaf\n return this.choiceFlowTreeTraversal(targetActivity);\n }\n\n /**\n * Choice Flow Tree Traversal (SB.2.9.2)\n * Traverses into a cluster to find a deliverable leaf\n * @param {Activity} fromActivity - The cluster to traverse from\n * @return {Activity | null} - A leaf activity for delivery, or null\n */\n private choiceFlowTreeTraversal(fromActivity: Activity): Activity | null {\n this.traversalService.ensureSelectionAndRandomization(fromActivity);\n const children = fromActivity.getAvailableChildren();\n\n // Validate children against constraints\n const validChildren = this.constraintValidator.validateFlowConstraints(\n fromActivity,\n children\n );\n\n if (!validChildren.valid) {\n return null;\n }\n\n // Find the first deliverable child\n for (const child of validChildren.validChildren) {\n const traversalResult = this.enhancedChoiceTraversal(child);\n if (traversalResult.activity) {\n return traversalResult.activity;\n }\n }\n\n return null;\n }\n\n /**\n * Enhanced Choice Activity Traversal (SB.2.4)\n * Traverses with stopForwardTraversal and forwardOnly checks\n * @param {Activity} activity - The activity to traverse\n * @param {boolean} isBackwardTraversal - Whether this is backward traversal\n * @return {ChoiceTraversalResult} - Result with activity or exception\n */\n private enhancedChoiceTraversal(\n activity: Activity,\n isBackwardTraversal: boolean = false\n ): ChoiceTraversalResult {\n // Cannot walk backward from root\n if (isBackwardTraversal && activity === this.activityTree.root) {\n return new ChoiceTraversalResult(null, \"SB.2.4-3\");\n }\n\n // Check availability\n if (!activity.isAvailable) {\n return new ChoiceTraversalResult(null, null);\n }\n\n // Check hidden from choice\n if (activity.isHiddenFromChoice) {\n return new ChoiceTraversalResult(null, null);\n }\n\n // Check stopForwardTraversal\n if (activity.sequencingControls && activity.sequencingControls.stopForwardTraversal) {\n return new ChoiceTraversalResult(null, \"SB.2.4-1\");\n }\n\n // Validate traversal constraints\n const traversalValidation = this.constraintValidator.validateTraversalConstraints(activity);\n if (!traversalValidation.canTraverse) {\n return new ChoiceTraversalResult(null, null);\n }\n\n // If it's a leaf, check if it can be delivered\n if (activity.children.length === 0) {\n if (this.traversalService.checkActivityProcess(activity)) {\n return new ChoiceTraversalResult(activity, null);\n }\n return new ChoiceTraversalResult(null, null);\n }\n\n // Check constrainChoice for clusters\n if (\n activity.parent?.sequencingControls.constrainChoice &&\n !traversalValidation.canTraverseInto\n ) {\n return new ChoiceTraversalResult(null, \"SB.2.4-2\");\n }\n\n // Traverse into cluster\n if (traversalValidation.canTraverseInto) {\n const flowResult = this.choiceFlowTreeTraversal(activity);\n return new ChoiceTraversalResult(flowResult, null);\n }\n\n return new ChoiceTraversalResult(null, null);\n }\n\n /**\n * Terminate descendent attempts (simplified)\n * @param {Activity} activity - The activity\n */\n private terminateDescendentAttemptsProcess(activity: Activity): void {\n activity.isActive = false;\n for (const child of activity.children) {\n this.terminateDescendentAttemptsProcess(child);\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { RuleEvaluationEngine } from \"../rules/rule_evaluation_engine\";\nimport { SequencingResult } from \"../rules/sequencing_request_types\";\nimport { RuleActionType } from \"../sequencing_rules\";\n\n/**\n * ExitRequestHandler - Handles exit and termination sequencing requests\n *\n * This handler manages:\n * - EXIT: Exit the current activity\n * - EXIT_ALL: Exit all activities\n * - ABANDON: Abandon the current activity\n * - ABANDON_ALL: Abandon all activities\n * - SUSPEND_ALL: Suspend all activities\n */\nexport class ExitRequestHandler {\n constructor(\n private activityTree: ActivityTree,\n private ruleEngine: RuleEvaluationEngine\n ) {}\n\n /**\n * Exit Sequencing Request Process (SB.2.11)\n * @param {Activity} currentActivity - The current activity\n * @return {SequencingResult}\n */\n public handleExit(currentActivity: Activity): SequencingResult {\n const result = new SequencingResult();\n\n // Check if exit is allowed\n if (!currentActivity.parent) {\n result.exception = \"SB.2.11-1\";\n return result;\n }\n\n // Check parent's sequencing controls\n if (!currentActivity.parent.sequencingControls.choiceExit) {\n result.exception = \"SB.2.11-2\";\n return result;\n }\n\n // Terminate current activity\n this.terminateDescendentAttempts(currentActivity);\n\n return result;\n }\n\n /**\n * Exit All Sequencing Request Process\n * @return {SequencingResult}\n */\n public handleExitAll(): SequencingResult {\n const result = new SequencingResult();\n\n // Terminate all activities\n if (this.activityTree.root) {\n this.terminateDescendentAttempts(this.activityTree.root);\n }\n\n return result;\n }\n\n /**\n * Abandon Sequencing Request Process\n * @param {Activity} currentActivity - The current activity\n * @return {SequencingResult}\n */\n public handleAbandon(currentActivity: Activity): SequencingResult {\n const result = new SequencingResult();\n\n // Set current activity as abandoned\n currentActivity.isActive = false;\n this.activityTree.currentActivity = currentActivity.parent;\n\n return result;\n }\n\n /**\n * Abandon All Sequencing Request Process\n * @return {SequencingResult}\n */\n public handleAbandonAll(): SequencingResult {\n const result = new SequencingResult();\n\n // Abandon all activities\n this.activityTree.currentActivity = null;\n\n return result;\n }\n\n /**\n * Suspend All Sequencing Request Process (SB.2.15)\n * @param {Activity} currentActivity - The current activity\n * @return {SequencingResult}\n */\n public handleSuspendAll(currentActivity: Activity): SequencingResult {\n const result = new SequencingResult();\n\n // Cannot suspend root\n if (currentActivity === this.activityTree.root) {\n result.exception = \"SB.2.15-1\";\n return result;\n }\n\n // Suspend the current activity\n currentActivity.isSuspended = true;\n this.activityTree.suspendedActivity = currentActivity;\n this.activityTree.currentActivity = null;\n\n return result;\n }\n\n /**\n * Terminate descendent attempts with exit rule evaluation\n * @param {Activity} activity - The activity\n * @param {boolean} skipExitRules - Whether to skip exit rules\n */\n public terminateDescendentAttempts(\n activity: Activity,\n skipExitRules: boolean = false\n ): void {\n // Apply Exit Action Rules (TB.2.1)\n let exitAction = null;\n if (!skipExitRules) {\n exitAction = this.ruleEngine.evaluateExitRules(activity);\n }\n\n // End attempt on the activity\n activity.isActive = false;\n\n // Recursively terminate descendants\n for (const child of activity.children) {\n this.terminateDescendentAttempts(child, skipExitRules);\n }\n\n // Process deferred exit actions\n if (exitAction && !skipExitRules) {\n this.processDeferredExitAction(exitAction, activity);\n }\n }\n\n /**\n * Process deferred exit action\n * @param {RuleActionType} exitAction - The exit action\n * @param {Activity} activity - The activity\n */\n private processDeferredExitAction(exitAction: RuleActionType, activity: Activity): void {\n switch (exitAction) {\n case RuleActionType.EXIT:\n // Already handled by terminateDescendentAttempts\n break;\n\n case RuleActionType.EXIT_PARENT:\n if (activity.parent && activity.parent.isActive) {\n this.terminateDescendentAttempts(activity.parent, true);\n }\n break;\n\n case RuleActionType.EXIT_ALL:\n if (this.activityTree.root && this.activityTree.root !== activity) {\n const allActivities = this.activityTree.getAllActivities();\n const anyActive = allActivities.some((a) => a.isActive);\n if (anyActive) {\n this.terminateDescendentAttempts(this.activityTree.root, true);\n }\n }\n break;\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { FlowTraversalService } from \"../traversal/flow_traversal_service\";\nimport {\n SequencingResult,\n DeliveryRequestType,\n FlowSubprocessMode\n} from \"../rules/sequencing_request_types\";\n\n/**\n * RetryRequestHandler - Handles retry sequencing requests\n *\n * This handler manages:\n * - RETRY: Retry the current activity\n * - RETRY_ALL: Retry from the beginning\n */\nexport class RetryRequestHandler {\n constructor(\n private activityTree: ActivityTree,\n private traversalService: FlowTraversalService\n ) {}\n\n /**\n * Retry Sequencing Request Process (SB.2.10)\n * @param {Activity} currentActivity - The current activity\n * @return {SequencingResult}\n */\n public handleRetry(currentActivity: Activity): SequencingResult {\n const result = new SequencingResult();\n\n // SB.2.10 step 2: Check if activity is still active or suspended\n if (currentActivity.isActive || currentActivity.isSuspended) {\n result.exception = \"SB.2.10-2\";\n return result;\n }\n\n // SB.2.10 step 3: If current activity is not a leaf (is a cluster)\n if (currentActivity.children.length > 0) {\n // Apply flow subprocess to find deliverable activity\n this.traversalService.ensureSelectionAndRandomization(currentActivity);\n const availableChildren = currentActivity.getAvailableChildren();\n\n let deliverableActivity: Activity | null = null;\n\n // Try each child using flowActivityTraversalSubprocess\n for (const child of availableChildren) {\n deliverableActivity = this.traversalService.flowActivityTraversalSubprocess(\n child,\n true,\n true,\n FlowSubprocessMode.FORWARD\n );\n if (deliverableActivity) {\n break;\n }\n }\n\n // SB.2.10 step 3.2: If flow subprocess returned false\n if (!deliverableActivity) {\n result.exception = \"SB.2.10-3\";\n return result;\n }\n\n // SB.2.10 step 3.3: Deliver the activity identified by flow subprocess\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = deliverableActivity;\n return result;\n }\n\n // SB.2.10 step 4: Activity is a leaf - terminate and deliver it again\n this.terminateDescendentAttempts(currentActivity);\n\n // Deliver the activity again\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = currentActivity;\n return result;\n }\n\n /**\n * Retry All Sequencing Request Process\n * Clears current activity and restarts from the root\n * @return {SequencingResult}\n */\n public handleRetryAll(): SequencingResult {\n // Clear current activity to allow restart\n this.activityTree.currentActivity = null;\n\n // Find first deliverable activity from root\n if (!this.activityTree.root) {\n const result = new SequencingResult();\n result.exception = \"SB.2.10-1\";\n return result;\n }\n\n const deliverableActivity = this.traversalService.findFirstDeliverableActivity(\n this.activityTree.root\n );\n\n const result = new SequencingResult();\n if (!deliverableActivity) {\n result.exception = \"SB.2.10-3\";\n return result;\n }\n\n result.deliveryRequest = DeliveryRequestType.DELIVER;\n result.targetActivity = deliverableActivity;\n return result;\n }\n\n /**\n * Terminate descendent attempts (simplified)\n * @param {Activity} activity - The activity\n */\n private terminateDescendentAttempts(activity: Activity): void {\n activity.isActive = false;\n for (const child of activity.children) {\n this.terminateDescendentAttempts(child);\n }\n }\n}\n","import { Activity } from \"./activity\";\nimport { ActivityTree } from \"./activity_tree\";\nimport { SequencingRules, RuleActionType, RuleCondition } from \"./sequencing_rules\";\nimport { SequencingControls } from \"./sequencing_controls\";\nimport { ADLNav } from \"../adl\";\n\n// Import extracted services\nimport { ActivityTreeQueries } from \"./utils/activity_tree_queries\";\nimport { ChoiceConstraintValidator } from \"./validators/choice_constraint_validator\";\nimport { RuleEvaluationEngine } from \"./rules/rule_evaluation_engine\";\nimport type { PostConditionResult } from \"./rules/rule_evaluation_engine\";\nimport { FlowTraversalService } from \"./traversal/flow_traversal_service\";\nimport {\n FlowRequestHandler,\n ChoiceRequestHandler,\n ExitRequestHandler,\n RetryRequestHandler\n} from \"./handlers\";\nimport {\n SequencingRequestType,\n DeliveryRequestType,\n SequencingResult\n} from \"./rules/sequencing_request_types\";\n\n// Re-export types for backward compatibility\nexport { SequencingRequestType, DeliveryRequestType, SequencingResult };\n\n/**\n * Result of Post-Condition Rule Evaluation (TB.2.2)\n * Re-exported for backward compatibility\n */\nexport type { PostConditionResult };\n\n/**\n * Options for SequencingProcess\n */\nexport interface SequencingProcessOptions {\n now?: () => Date;\n getAttemptElapsedSeconds?: (activity: Activity) => number;\n getActivityElapsedSeconds?: (activity: Activity) => number;\n}\n\n/**\n * SequencingProcess - Refactored as a coordinator\n *\n * This class has been refactored from 3,036 lines and 66 methods to ~200 lines\n * by extracting functionality into focused, single-responsibility modules:\n *\n * - ActivityTreeQueries: Tree traversal and query utilities\n * - ChoiceConstraintValidator: Choice constraint validation (consolidated from 5 duplicated places)\n * - RuleEvaluationEngine: Sequencing rule evaluation\n * - FlowTraversalService: Flow-based tree traversal\n * - FlowRequestHandler: START, RESUME_ALL, CONTINUE, PREVIOUS requests\n * - ChoiceRequestHandler: CHOICE, JUMP requests\n * - ExitRequestHandler: EXIT, EXIT_ALL, ABANDON, ABANDON_ALL, SUSPEND_ALL requests\n * - RetryRequestHandler: RETRY, RETRY_ALL requests\n *\n * Benefits:\n * - Single source of truth for constraint validation\n * - Improved testability (unit tests per module)\n * - Better maintainability\n * - Reduced cognitive load\n */\nexport class SequencingProcess {\n private activityTree: ActivityTree;\n\n // Extracted services\n private treeQueries: ActivityTreeQueries;\n private constraintValidator: ChoiceConstraintValidator;\n private ruleEngine: RuleEvaluationEngine;\n private traversalService: FlowTraversalService;\n\n // Request handlers\n private flowHandler: FlowRequestHandler;\n private choiceHandler: ChoiceRequestHandler;\n private exitHandler: ExitRequestHandler;\n private retryHandler: RetryRequestHandler;\n\n // Time function (exposed for testing)\n private _now: () => Date;\n\n /**\n * Get/set the current time function (used for testing time-dependent logic)\n */\n public get now(): () => Date {\n return this._now;\n }\n\n public set now(fn: () => Date) {\n this._now = fn;\n // Update the static RuleCondition time provider for condition evaluation\n RuleCondition.setNowProvider(fn);\n // Update the rule engine with the new time function\n this.ruleEngine = new RuleEvaluationEngine({\n now: fn,\n getAttemptElapsedSecondsHook: this._getAttemptElapsedSecondsHook\n });\n // Recreate traversal service with updated rule engine\n this.traversalService = new FlowTraversalService(this.activityTree, this.ruleEngine);\n // Update handlers that depend on traversal service\n this.flowHandler = new FlowRequestHandler(this.activityTree, this.traversalService);\n this.choiceHandler = new ChoiceRequestHandler(\n this.activityTree,\n this.constraintValidator,\n this.traversalService,\n this.treeQueries\n );\n this.retryHandler = new RetryRequestHandler(this.activityTree, this.traversalService);\n }\n\n private _getAttemptElapsedSecondsHook: ((activity: Activity) => number) | undefined;\n\n /**\n * Get/set the elapsed seconds hook (used for time-based rules)\n */\n public get getAttemptElapsedSecondsHook(): ((activity: Activity) => number) | undefined {\n return this._getAttemptElapsedSecondsHook;\n }\n\n public set getAttemptElapsedSecondsHook(fn: ((activity: Activity) => number) | undefined) {\n this._getAttemptElapsedSecondsHook = fn;\n // Update the static RuleCondition elapsed seconds hook\n RuleCondition.setElapsedSecondsHook(fn);\n // Update the rule engine\n this.ruleEngine = new RuleEvaluationEngine({\n now: this._now,\n getAttemptElapsedSecondsHook: fn\n });\n // Recreate traversal service with updated rule engine\n this.traversalService = new FlowTraversalService(this.activityTree, this.ruleEngine);\n // Update handlers that depend on traversal service\n this.flowHandler = new FlowRequestHandler(this.activityTree, this.traversalService);\n this.choiceHandler = new ChoiceRequestHandler(\n this.activityTree,\n this.constraintValidator,\n this.traversalService,\n this.treeQueries\n );\n this.retryHandler = new RetryRequestHandler(this.activityTree, this.traversalService);\n }\n\n constructor(\n activityTree: ActivityTree,\n _sequencingRules?: SequencingRules | null,\n _sequencingControls?: SequencingControls | null,\n _adlNav: ADLNav | null = null,\n options?: SequencingProcessOptions\n ) {\n this.activityTree = activityTree;\n\n // Store options for later use\n this._now = options?.now || (() => new Date());\n this._getAttemptElapsedSecondsHook = options?.getAttemptElapsedSeconds;\n\n // Initialize extracted services\n this.treeQueries = new ActivityTreeQueries(activityTree);\n this.ruleEngine = new RuleEvaluationEngine({\n now: this._now,\n getAttemptElapsedSecondsHook: this._getAttemptElapsedSecondsHook\n });\n this.constraintValidator = new ChoiceConstraintValidator(activityTree, this.treeQueries);\n this.traversalService = new FlowTraversalService(activityTree, this.ruleEngine);\n\n // Initialize request handlers with dependencies\n this.flowHandler = new FlowRequestHandler(activityTree, this.traversalService);\n this.choiceHandler = new ChoiceRequestHandler(\n activityTree,\n this.constraintValidator,\n this.traversalService,\n this.treeQueries\n );\n this.exitHandler = new ExitRequestHandler(activityTree, this.ruleEngine);\n this.retryHandler = new RetryRequestHandler(activityTree, this.traversalService);\n }\n\n /**\n * Main Sequencing Request Process (SB.2.12)\n * This is the main entry point for all navigation requests\n * @param {SequencingRequestType} request - The sequencing request\n * @param {string | null} targetActivityId - Target activity ID (for CHOICE and JUMP)\n * @return {SequencingResult}\n */\n public sequencingRequestProcess(\n request: SequencingRequestType,\n targetActivityId: string | null = null\n ): SequencingResult {\n const currentActivity = this.activityTree.currentActivity;\n\n switch (request) {\n case SequencingRequestType.START:\n return this.flowHandler.handleStart();\n\n case SequencingRequestType.RESUME_ALL:\n return this.flowHandler.handleResumeAll();\n\n case SequencingRequestType.CONTINUE:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.flowHandler.handleContinue(currentActivity);\n\n case SequencingRequestType.PREVIOUS:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.flowHandler.handlePrevious(currentActivity);\n\n case SequencingRequestType.CHOICE:\n if (!targetActivityId) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-5\");\n }\n return this.choiceHandler.handleChoice(targetActivityId, currentActivity);\n\n case SequencingRequestType.JUMP:\n if (!targetActivityId) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-5\");\n }\n return this.choiceHandler.handleJump(targetActivityId);\n\n case SequencingRequestType.EXIT:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.exitHandler.handleExit(currentActivity);\n\n case SequencingRequestType.EXIT_ALL:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.exitHandler.handleExitAll();\n\n case SequencingRequestType.ABANDON:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.exitHandler.handleAbandon(currentActivity);\n\n case SequencingRequestType.ABANDON_ALL:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.exitHandler.handleAbandonAll();\n\n case SequencingRequestType.SUSPEND_ALL:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.exitHandler.handleSuspendAll(currentActivity);\n\n case SequencingRequestType.RETRY:\n if (!currentActivity) {\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-1\");\n }\n return this.retryHandler.handleRetry(currentActivity);\n\n case SequencingRequestType.RETRY_ALL:\n return this.retryHandler.handleRetryAll();\n\n default:\n return new SequencingResult(DeliveryRequestType.DO_NOT_DELIVER, null, \"SB.2.12-6\");\n }\n }\n\n /**\n * Evaluate post-condition rules for the current activity\n * @param {Activity} activity - The activity to evaluate\n * @return {PostConditionResult} - The post-condition result\n */\n public evaluatePostConditionRules(activity: Activity): PostConditionResult {\n return this.ruleEngine.evaluatePostConditions(activity);\n }\n\n /**\n * Check if an activity can be delivered\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if the activity can be delivered\n */\n public canActivityBeDelivered(activity: Activity): boolean {\n return this.traversalService.canDeliver(activity);\n }\n\n /**\n * Validate navigation request before expensive operations\n * @param {SequencingRequestType} request - The navigation request\n * @param {string | null} targetActivityId - Target activity ID\n * @param {Activity | null} currentActivity - Current activity\n * @return {{valid: boolean, exception: string | null}} - Validation result\n */\n public validateNavigationRequest(\n request: SequencingRequestType,\n targetActivityId: string | null = null,\n currentActivity: Activity | null = null\n ): { valid: boolean; exception: string | null } {\n // Basic request type validation\n const validRequestTypes = Object.values(SequencingRequestType);\n if (!validRequestTypes.includes(request)) {\n return { valid: false, exception: \"SB.2.12-6\" };\n }\n\n // Validate based on request type\n switch (request) {\n case SequencingRequestType.CONTINUE:\n case SequencingRequestType.PREVIOUS: {\n if (!currentActivity) {\n return { valid: false, exception: \"SB.2.12-1\" };\n }\n if (currentActivity.isActive) {\n return {\n valid: false,\n exception: request === SequencingRequestType.CONTINUE ? \"SB.2.7-1\" : \"SB.2.8-1\"\n };\n }\n if (currentActivity.parent && !currentActivity.parent.sequencingControls.flow) {\n return {\n valid: false,\n exception: request === SequencingRequestType.CONTINUE ? \"SB.2.7-2\" : \"SB.2.8-2\"\n };\n }\n if (request === SequencingRequestType.PREVIOUS) {\n const forwardOnlyViolation = this.constraintValidator.checkForwardOnlyViolation(\n currentActivity\n );\n if (!forwardOnlyViolation.valid) {\n return forwardOnlyViolation;\n }\n }\n break;\n }\n\n case SequencingRequestType.CHOICE: {\n if (!targetActivityId) {\n return { valid: false, exception: \"SB.2.12-5\" };\n }\n const targetActivity = this.activityTree.getActivity(targetActivityId);\n if (!targetActivity) {\n return { valid: false, exception: \"SB.2.9-1\" };\n }\n const choiceValidation = this.constraintValidator.validateChoice(\n currentActivity,\n targetActivity,\n { checkAvailability: true }\n );\n if (!choiceValidation.valid) {\n return choiceValidation;\n }\n if (!this.traversalService.canDeliver(targetActivity)) {\n return { valid: false, exception: \"SB.2.9-6\" };\n }\n // Check for hiddenFromChoice precondition rule\n const preConditionResult = this.ruleEngine.checkSequencingRules(\n targetActivity,\n targetActivity.sequencingRules.preConditionRules\n );\n if (preConditionResult === RuleActionType.HIDE_FROM_CHOICE) {\n return { valid: false, exception: \"SB.2.9-4\" };\n }\n break;\n }\n\n case SequencingRequestType.JUMP: {\n if (!targetActivityId) {\n return { valid: false, exception: \"SB.2.12-5\" };\n }\n const jumpTarget = this.activityTree.getActivity(targetActivityId);\n if (!jumpTarget) {\n return { valid: false, exception: \"SB.2.13-1\" };\n }\n break;\n }\n\n default:\n break;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Get all available activities that can be selected via choice navigation\n * @return {Activity[]} - Array of activities available for choice\n */\n public getAvailableChoices(): Activity[] {\n return this.choiceHandler.getAvailableChoices();\n }\n\n // Expose services for advanced use cases\n public getTreeQueries(): ActivityTreeQueries {\n return this.treeQueries;\n }\n\n public getConstraintValidator(): ChoiceConstraintValidator {\n return this.constraintValidator;\n }\n\n public getRuleEngine(): RuleEvaluationEngine {\n return this.ruleEngine;\n }\n\n public getTraversalService(): FlowTraversalService {\n return this.traversalService;\n }\n}\n","import { Activity } from \"../cmi/scorm2004/sequencing/activity\";\nimport {\n SequencingResult,\n DeliveryRequestType,\n} from \"../cmi/scorm2004/sequencing/sequencing_process\";\nimport { EventService } from \"./EventService\";\nimport { LoggingService } from \"./LoggingService\";\nimport { IEventService, ILoggingService } from \"../interfaces/services\";\n\n/**\n * Interface for activity delivery callbacks\n */\nexport interface ActivityDeliveryCallbacks {\n onDeliverActivity?: (activity: Activity) => void;\n onUnloadActivity?: (activity: Activity) => void;\n onSequencingComplete?: (result: SequencingResult) => void;\n onSequencingError?: (error: string) => void;\n}\n\n/**\n * Service for managing activity delivery in SCORM 2004\n */\nexport class ActivityDeliveryService {\n private eventService: IEventService;\n private loggingService: ILoggingService;\n private callbacks: ActivityDeliveryCallbacks;\n private currentDeliveredActivity: Activity | null = null;\n private pendingDelivery: Activity | null = null;\n\n constructor(\n eventService: IEventService,\n loggingService: ILoggingService,\n callbacks: ActivityDeliveryCallbacks = {},\n ) {\n this.eventService = eventService;\n this.loggingService = loggingService;\n this.callbacks = callbacks;\n }\n\n /**\n * Process a sequencing result and handle activity delivery\n * @param {SequencingResult} result - The sequencing result to process\n */\n public processSequencingResult(result: SequencingResult): void {\n // Log the sequencing result\n if (result.exception) {\n this.loggingService.error(`Sequencing error: ${result.exception}`);\n this.callbacks.onSequencingError?.(result.exception);\n return;\n }\n\n // Handle delivery request\n if (result.deliveryRequest === DeliveryRequestType.DELIVER && result.targetActivity) {\n this.deliverActivity(result.targetActivity);\n } else {\n // No delivery requested\n this.loggingService.info(\"Sequencing completed with no delivery request\");\n }\n\n // Notify sequencing complete\n this.callbacks.onSequencingComplete?.(result);\n }\n\n /**\n * Deliver an activity\n * @param {Activity} activity - The activity to deliver\n */\n private deliverActivity(activity: Activity): void {\n // Skip delivery if the same activity is already delivered\n // This prevents duplicate callbacks when sequencing fires for an already-loaded activity\n if (this.currentDeliveredActivity === activity) {\n this.loggingService.info(`Skipping delivery - activity already delivered: ${activity.id}`);\n return;\n }\n\n // If there's a different currently delivered activity, unload it first\n if (this.currentDeliveredActivity) {\n this.unloadActivity(this.currentDeliveredActivity);\n }\n\n // Mark the activity as pending delivery\n this.pendingDelivery = activity;\n\n // Log delivery\n this.loggingService.info(`Delivering activity: ${activity.id} - ${activity.title}`);\n\n // Fire delivery event\n this.eventService.processListeners(\"ActivityDelivery\", activity.id, activity);\n\n // Call delivery callback\n this.callbacks.onDeliverActivity?.(activity);\n\n // Update current delivered activity\n this.currentDeliveredActivity = activity;\n this.pendingDelivery = null;\n\n // Mark activity as active\n activity.isActive = true;\n }\n\n /**\n * Unload an activity\n * @param {Activity} activity - The activity to unload\n */\n private unloadActivity(activity: Activity): void {\n // Log unload\n this.loggingService.info(`Unloading activity: ${activity.id} - ${activity.title}`);\n\n // Fire unload event\n this.eventService.processListeners(\"ActivityUnload\", activity.id, activity);\n\n // Call unload callback\n this.callbacks.onUnloadActivity?.(activity);\n\n // Mark activity as inactive\n activity.isActive = false;\n }\n\n /**\n * Get the currently delivered activity\n * @return {Activity | null}\n */\n public getCurrentDeliveredActivity(): Activity | null {\n return this.currentDeliveredActivity;\n }\n\n /**\n * Get the pending delivery activity\n * @return {Activity | null}\n */\n public getPendingDelivery(): Activity | null {\n return this.pendingDelivery;\n }\n\n /**\n * Update delivery callbacks\n * @param {ActivityDeliveryCallbacks} callbacks - The new callbacks\n */\n public updateCallbacks(callbacks: ActivityDeliveryCallbacks): void {\n this.callbacks = { ...this.callbacks, ...callbacks };\n }\n\n /**\n * Reset the delivery service\n */\n public reset(): void {\n if (this.currentDeliveredActivity) {\n this.unloadActivity(this.currentDeliveredActivity);\n }\n this.currentDeliveredActivity = null;\n this.pendingDelivery = null;\n }\n}\n","import { CommitObject, InternalSettings, ResultObject } from \"../types/api_types\";\nimport { global_constants } from \"../constants/api_constants\";\nimport { LogLevelEnum } from \"../constants/enums\";\nimport { IHttpService } from \"../interfaces/services\";\nimport { ErrorCode } from \"../constants/error_codes\";\nimport { StringKeyMap } from \"../utilities\";\n\n/**\n * Service for handling asynchronous HTTP communication with the LMS\n * WARNING: Not SCORM-compliant - always returns immediate success\n */\nexport class AsynchronousHttpService implements IHttpService {\n private settings: InternalSettings;\n private error_codes: ErrorCode;\n\n /**\n * Constructor for AsynchronousHttpService\n * @param {Settings} settings - The settings object\n * @param {ErrorCode} error_codes - The error codes object\n */\n constructor(settings: InternalSettings, error_codes: ErrorCode) {\n this.settings = settings;\n this.error_codes = error_codes;\n }\n\n /**\n * Sends HTTP requests asynchronously to the LMS\n * Returns immediate success - actual result handled via events\n *\n * WARNING: This is NOT SCORM-compliant. Always returns optimistic success immediately.\n * The actual HTTP request happens in the background, and success/failure is reported\n * via CommitSuccess/CommitError events, but NOT to the SCO's commit call.\n *\n * @param {string} url - The URL endpoint to send the request to\n * @param {CommitObject|StringKeyMap|Array} params - The data to send to the LMS\n * @param {boolean} immediate - Whether to send the request immediately without waiting\n * @param {Function} apiLog - Function to log API messages with appropriate levels\n * @param {Function} processListeners - Function to trigger event listeners for commit events\n * @return {ResultObject} - Immediate optimistic success result\n */\n processHttpRequest(\n url: string,\n params: CommitObject | StringKeyMap | Array<any>,\n immediate: boolean = false,\n apiLog: (\n functionName: string,\n message: any,\n messageLevel: LogLevelEnum,\n CMIElement?: string,\n ) => void,\n processListeners: (functionName: string, CMIElement?: string, value?: any) => void,\n ): ResultObject {\n // Fire request in background - don't wait for result\n this._performAsyncRequest(url, params, immediate, apiLog, processListeners);\n\n // Immediately return optimistic success\n return {\n result: global_constants.SCORM_TRUE,\n errorCode: 0,\n };\n }\n\n /**\n * Performs the async request in the background\n * @param {string} url - The URL to send the request to\n * @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request\n * @param {boolean} immediate - Whether this is an immediate request\n * @param apiLog - Function to log API messages\n * @param {Function} processListeners - Function to process event listeners\n * @private\n */\n private async _performAsyncRequest(\n url: string,\n params: CommitObject | StringKeyMap | Array<any>,\n immediate: boolean,\n apiLog: (\n functionName: string,\n message: any,\n messageLevel: LogLevelEnum,\n CMIElement?: string,\n ) => void,\n processListeners: (functionName: string, CMIElement?: string, value?: any) => void,\n ): Promise<void> {\n try {\n const processedParams = this.settings.requestHandler(params) as\n | CommitObject\n | StringKeyMap\n | Array<any>;\n\n let response: Response;\n if (immediate && this.settings.asyncModeBeaconBehavior !== \"never\") {\n response = await this.performBeacon(url, processedParams);\n } else {\n response = await this.performFetch(url, processedParams);\n }\n\n const result = await this.transformResponse(response, processListeners);\n\n // Trigger listeners based on actual result (after API method returns)\n if (this._isSuccessResponse(response, result)) {\n processListeners(\"CommitSuccess\");\n } else {\n processListeners(\"CommitError\", undefined, result.errorCode);\n }\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n apiLog(\"processHttpRequest\", `Async request failed: ${message}`, LogLevelEnum.ERROR);\n processListeners(\"CommitError\");\n }\n }\n\n /**\n * Prepares the request body and content type based on params type\n * @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request\n * @return {Object} - Object containing body and contentType\n * @private\n */\n private _prepareRequestBody(params: CommitObject | StringKeyMap | Array<any>): {\n body: string;\n contentType: string;\n } {\n const body = params instanceof Array ? params.join(\"&\") : JSON.stringify(params);\n const contentType =\n params instanceof Array\n ? \"application/x-www-form-urlencoded\"\n : this.settings.commitRequestDataType;\n\n return { body, contentType };\n }\n\n /**\n * Perform the fetch request to the LMS\n * @param {string} url - The URL to send the request to\n * @param {StringKeyMap|Array} params - The parameters to include in the request\n * @return {Promise<Response>} - The response from the LMS\n * @private\n */\n private async performFetch(url: string, params: StringKeyMap | Array<any>): Promise<Response> {\n // Use Beacon API if specified in settings\n if (this.settings.asyncModeBeaconBehavior === \"always\") {\n return this.performBeacon(url, params);\n }\n\n const { body, contentType } = this._prepareRequestBody(params);\n const init = {\n method: \"POST\",\n mode: this.settings.fetchMode,\n body,\n headers: {\n ...this.settings.xhrHeaders,\n \"Content-Type\": contentType,\n },\n keepalive: true,\n } as RequestInit;\n\n if (this.settings.xhrWithCredentials) {\n init.credentials = \"include\";\n }\n\n return fetch(url, init);\n }\n\n /**\n * Perform the beacon request to the LMS\n * @param {string} url - The URL to send the request to\n * @param {StringKeyMap|Array} params - The parameters to include in the request\n * @return {Promise<Response>} - A promise that resolves with a mock Response object\n * @private\n */\n private async performBeacon(url: string, params: StringKeyMap | Array<any>): Promise<Response> {\n const { body, contentType } = this._prepareRequestBody(params);\n\n // Send the beacon request\n const beaconSuccess = navigator.sendBeacon(url, new Blob([body], { type: contentType }));\n\n // Create a mock Response object since sendBeacon doesn't return a Response\n return Promise.resolve({\n status: beaconSuccess ? 200 : 0,\n ok: beaconSuccess,\n json: async () => ({\n result: beaconSuccess ? \"true\" : \"false\",\n errorCode: beaconSuccess ? 0 : this.error_codes.GENERAL_COMMIT_FAILURE || 391,\n }),\n text: async () =>\n JSON.stringify({\n result: beaconSuccess ? \"true\" : \"false\",\n errorCode: beaconSuccess ? 0 : this.error_codes.GENERAL_COMMIT_FAILURE || 391,\n }),\n } as Response);\n }\n\n /**\n * Transforms the response from the LMS to a ResultObject\n * @param {Response} response - The response from the LMS\n * @param {Function} processListeners - Function to process event listeners\n * @return {Promise<ResultObject>} - The transformed response\n * @private\n */\n private async transformResponse(\n response: Response,\n processListeners: (functionName: string, CMIElement?: string, value?: any) => void,\n ): Promise<ResultObject> {\n let result: any;\n\n try {\n // Parse the response using the configured handler or default to json\n result =\n typeof this.settings.responseHandler === \"function\"\n ? await this.settings.responseHandler(response)\n : await response.json();\n } catch (parseError) {\n // If we can't parse the response, log the raw response for debugging\n const responseText = await response.text().catch(() => \"Unable to read response text\");\n\n // SECURITY NOTE: Error details include response text truncated to 500 chars.\n // In production environments with untrusted LMS responses, consider:\n // 1. Further limiting responseText length or omitting it entirely\n // 2. Sanitizing responseText to remove PII, credentials, or sensitive data\n // 3. Using a custom responseHandler to pre-sanitize before this point\n // 4. Implementing server-side log filtering to prevent sensitive data leakage\n //\n // This detailed error information is valuable for debugging integration issues,\n // but may expose internal LMS details or error messages to client-side code.\n // Balance security concerns with debugging needs based on your deployment model.\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: this.error_codes.GENERAL_COMMIT_FAILURE || 391,\n errorMessage: `Failed to parse LMS response: ${parseError instanceof Error ? parseError.message : String(parseError)}`,\n errorDetails: JSON.stringify({\n status: response.status,\n statusText: response.statusText,\n url: response.url,\n responseText: responseText.substring(0, 500), // Limit response text to avoid huge logs\n parseError: parseError instanceof Error ? parseError.message : String(parseError),\n }),\n };\n }\n\n // Ensure result has an errorCode property\n if (!Object.hasOwnProperty.call(result, \"errorCode\")) {\n result.errorCode = this._isSuccessResponse(response, result)\n ? 0\n : this.error_codes.GENERAL_COMMIT_FAILURE || 391;\n }\n\n // Add response details for failed requests\n if (!this._isSuccessResponse(response, result)) {\n result.errorDetails = {\n status: response.status,\n statusText: response.statusText,\n url: response.url,\n ...result.errorDetails, // Preserve any existing error details\n };\n }\n\n // Note: Event triggering is handled by _performAsyncRequest\n return result;\n }\n\n /**\n * Determines if a response is successful based on status code and result\n * @param {Response} response - The HTTP response\n * @param {ResultObject} result - The parsed result object\n * @return {boolean} - Whether the response is successful\n * @private\n */\n private _isSuccessResponse(response: Response, result: ResultObject): boolean {\n const value = (result as any).result;\n return (\n response.status >= 200 &&\n response.status <= 299 &&\n (value === true || value === \"true\" || value === global_constants.SCORM_TRUE)\n );\n }\n\n /**\n * Updates the service settings\n * @param {Settings} settings - The new settings\n */\n updateSettings(settings: InternalSettings): void {\n this.settings = settings;\n }\n}\n","import { BaseCMI } from \"../cmi/common/base_cmi\";\nimport { CMIArray } from \"../cmi/common/array\";\nimport { global_constants, LogLevelEnum } from \"../constants\";\nimport { StringKeyMap, stringMatches } from \"../utilities\";\nimport { ErrorCode } from \"../constants/error_codes\";\n\n/** Prefix for SCORM 2004 target attribute syntax */\nconst TARGET_ATTRIBUTE_PREFIX = \"{target=\";\n\n/**\n * Helper function to safely get an error code with validation.\n * Returns the error code value, or falls back to GENERAL error code if key is missing.\n * Logs a warning for unknown keys to help catch typos during development.\n */\nfunction getErrorCode(errorCodes: ErrorCode, key: string): number {\n const code = errorCodes[key];\n if (code === undefined) {\n // Log warning in development to catch typos in error code keys\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(`CMIValueAccessService: Unknown error code key: ${key}`);\n }\n return errorCodes[\"GENERAL\"] ?? 0;\n }\n return code;\n}\n\n/**\n * Context interface for CMI value access operations.\n *\n * Provides callbacks to the API for error handling, validation, and data model access.\n * All callbacks are invoked synchronously during CMI path traversal.\n * Error states should be communicated through the API's error handling mechanism\n * (throwSCORMError and setLastErrorCode).\n *\n * @remarks\n * This interface enables dependency injection, making the CMIValueAccessService\n * testable in isolation without requiring a full API instance.\n */\nexport interface CMIValueAccessContext {\n /** Error codes for the API (SCORM 1.2 or 2004) */\n readonly errorCodes: ErrorCode;\n\n /** Get the last error code as a string */\n getLastErrorCode: () => string;\n\n /** Set the last error code */\n setLastErrorCode: (errorCode: string) => void;\n\n /** Throw a SCORM error - sets error code and optionally logs diagnostic info */\n throwSCORMError: (element: string, errorCode: number, message?: string) => void;\n\n /** Check if the API is initialized (LMSInitialize/Initialize called) */\n isInitialized: () => boolean;\n\n /** Validate a correct response value for SCORM 2004 interactions */\n validateCorrectResponse: (CMIElement: string, value: string) => void;\n\n /** Check for duplicate ID in objectives/interactions arrays */\n checkForDuplicateId: (CMIElement: string, value: string) => boolean;\n\n /** Get or create a child element for array operations */\n getChildElement: (CMIElement: string, value: string, foundFirstIndex: boolean) => BaseCMI | null;\n\n /** Log an API message at the specified level */\n apiLog: (methodName: string, message: string, level: LogLevelEnum) => void;\n\n /** Check if an object has a property (safe prototype-aware check) */\n checkObjectHasProperty: (obj: StringKeyMap, attr: string) => boolean;\n\n /** Get the CMI/ADL data model root object */\n getDataModel: () => StringKeyMap;\n}\n\n/**\n * CMIValueAccessService\n *\n * Handles the complex traversal logic for getting and setting CMI values.\n * Extracted from BaseAPI to reduce god class complexity.\n *\n * Responsibilities:\n * - Navigate CMI element paths (e.g., \"cmi.objectives.0.id\")\n * - Handle array indexing and dynamic child creation\n * - Validate element paths and handle errors\n * - Support both SCORM 1.2 and SCORM 2004 data models\n */\nexport class CMIValueAccessService {\n private context: CMIValueAccessContext;\n\n constructor(context: CMIValueAccessContext) {\n this.context = context;\n }\n\n /**\n * Gets the appropriate error code for undefined data model elements.\n * SCORM 2004 uses UNDEFINED_DATA_MODEL, SCORM 1.2 uses GENERAL.\n */\n private getUndefinedDataModelErrorCode(scorm2004: boolean): number {\n return scorm2004\n ? getErrorCode(this.context.errorCodes, \"UNDEFINED_DATA_MODEL\")\n : getErrorCode(this.context.errorCodes, \"GENERAL\");\n }\n\n /**\n * Sets a value on a CMI element path\n *\n * @param {string} methodName - The API method name for logging\n * @param {boolean} scorm2004 - Whether this is SCORM 2004\n * @param {string} CMIElement - The CMI element path\n * @param {string} value - The value to set (all SCORM values are strings)\n * @return {string} \"true\" or \"false\"\n */\n setCMIValue(methodName: string, scorm2004: boolean, CMIElement: string, value: string): string {\n if (!CMIElement || CMIElement === \"\") {\n if (scorm2004) {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"GENERAL_SET_FAILURE\"),\n \"The data model element was not specified\",\n );\n }\n return global_constants.SCORM_FALSE;\n }\n\n this.context.setLastErrorCode(\"0\");\n\n const structure = CMIElement.split(\".\");\n let refObject: StringKeyMap | BaseCMI = this.context.getDataModel();\n let returnValue = global_constants.SCORM_FALSE;\n let foundFirstIndex = false;\n\n const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;\n const invalidErrorCode = this.getUndefinedDataModelErrorCode(scorm2004);\n\n for (let idx = 0; idx < structure.length; idx++) {\n const attribute = structure[idx]!;\n\n if (idx === structure.length - 1) {\n // Final attribute - set the value\n returnValue = this.setFinalAttribute(\n refObject,\n attribute,\n value,\n CMIElement,\n scorm2004,\n invalidErrorCode,\n invalidErrorMessage,\n );\n break;\n } else {\n // Intermediate attribute - traverse\n const traverseResult = this.traverseToNextLevel(\n refObject,\n structure,\n idx,\n value,\n CMIElement,\n scorm2004,\n foundFirstIndex,\n invalidErrorCode,\n invalidErrorMessage,\n );\n\n if (traverseResult.error) {\n break;\n }\n\n refObject = traverseResult.refObject;\n idx = traverseResult.idx;\n foundFirstIndex = traverseResult.foundFirstIndex;\n }\n }\n\n if (returnValue === global_constants.SCORM_FALSE) {\n this.context.apiLog(\n methodName,\n `There was an error setting the value for: ${CMIElement}, value of: ${value}`,\n LogLevelEnum.WARN,\n );\n }\n\n return returnValue;\n }\n\n /**\n * Gets a value from a CMI element path\n *\n * @param {string} methodName - The API method name for logging\n * @param {boolean} scorm2004 - Whether this is SCORM 2004\n * @param {string} CMIElement - The CMI element path\n * @return The value at the element path, or empty string on error (per SCORM spec)\n */\n getCMIValue(\n methodName: string,\n scorm2004: boolean,\n CMIElement: string,\n ): string | StringKeyMap | BaseCMI {\n if (!CMIElement || CMIElement === \"\") {\n if (scorm2004) {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"GENERAL_GET_FAILURE\"),\n \"The data model element was not specified\",\n );\n }\n return \"\";\n }\n\n // SCORM 2004: Validate ._version keyword usage - only valid on cmi._version\n if (scorm2004 && CMIElement.endsWith(\"._version\") && CMIElement !== \"cmi._version\") {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"GENERAL_GET_FAILURE\"),\n \"The _version keyword was used incorrectly\",\n );\n return \"\";\n }\n\n const structure = CMIElement.split(\".\");\n let refObject: StringKeyMap = this.context.getDataModel();\n let attribute: string | null = null;\n\n const uninitializedErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) has not been initialized.`;\n const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;\n const invalidErrorCode = this.getUndefinedDataModelErrorCode(scorm2004);\n\n for (let idx = 0; idx < structure.length; idx++) {\n attribute = structure[idx]!;\n\n // Validate attribute existence\n const validationResult = this.validateGetAttribute(\n refObject,\n attribute,\n CMIElement,\n scorm2004,\n invalidErrorCode,\n invalidErrorMessage,\n idx === structure.length - 1,\n );\n\n if (validationResult.returnValue !== undefined) {\n return validationResult.returnValue;\n }\n\n if (validationResult.error) {\n return \"\";\n }\n\n // Traverse to the next level\n if (attribute !== undefined && attribute !== null) {\n refObject = refObject[attribute] as StringKeyMap;\n if (refObject === undefined) {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n break;\n }\n } else {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n break;\n }\n\n // Handle array access\n if (refObject instanceof CMIArray) {\n const arrayResult = this.handleGetArrayAccess(\n refObject,\n structure,\n idx,\n CMIElement,\n uninitializedErrorMessage,\n );\n\n if (arrayResult.error) {\n return \"\";\n }\n\n refObject = arrayResult.refObject;\n idx = arrayResult.idx;\n }\n }\n\n if (refObject === null || refObject === undefined) {\n if (!scorm2004) {\n // SCORM 1.2: Use specific keyword error codes\n if (attribute === \"_children\") {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"CHILDREN_ERROR\"),\n undefined,\n );\n } else if (attribute === \"_count\") {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"COUNT_ERROR\"),\n undefined,\n );\n }\n }\n // SCORM 2004 keyword errors are handled during traversal\n return \"\";\n }\n\n return refObject;\n }\n\n /**\n * Sets the final attribute value in the CMI path\n */\n private setFinalAttribute(\n refObject: StringKeyMap | BaseCMI,\n attribute: string,\n value: string,\n CMIElement: string,\n scorm2004: boolean,\n invalidErrorCode: number,\n invalidErrorMessage: string,\n ): string {\n // Handle SCORM 2004 target attribute syntax: {target=identifier}\n // Target attributes are used for global objective mapping and are read-only after initialization\n if (scorm2004 && attribute?.startsWith(TARGET_ATTRIBUTE_PREFIX)) {\n if (this.context.isInitialized()) {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"READ_ONLY_ELEMENT\"),\n );\n return global_constants.SCORM_FALSE;\n }\n // Before initialization, target writes are allowed but no-op (targets are resolved during read)\n return global_constants.SCORM_TRUE;\n }\n\n // Validate attribute exists\n if (\n typeof attribute === \"undefined\" ||\n !this.context.checkObjectHasProperty(refObject as StringKeyMap, attribute)\n ) {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n return global_constants.SCORM_FALSE;\n }\n\n // Validate correct response\n if (\n stringMatches(CMIElement, \"\\\\.correct_responses\\\\.\\\\d+$\") &&\n this.context.isInitialized() &&\n attribute !== \"pattern\"\n ) {\n this.context.validateCorrectResponse(CMIElement, value);\n if (this.context.getLastErrorCode() !== \"0\") {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"TYPE_MISMATCH\"),\n );\n return global_constants.SCORM_FALSE;\n }\n }\n\n // Check for errors before setting\n if (!scorm2004 || this.context.getLastErrorCode() === \"0\") {\n // Validate attribute is safe\n if (\n typeof attribute === \"undefined\" ||\n attribute === \"__proto__\" ||\n attribute === \"constructor\"\n ) {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n return global_constants.SCORM_FALSE;\n }\n\n // SCORM 2004: Check for duplicate IDs in objectives and interactions arrays\n if (scorm2004 && attribute === \"id\" && this.context.isInitialized()) {\n const duplicateError = this.context.checkForDuplicateId(CMIElement, value);\n if (duplicateError) {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"GENERAL_SET_FAILURE\"),\n );\n return global_constants.SCORM_FALSE;\n }\n }\n\n // Set the value\n (refObject as StringKeyMap)[attribute] = value;\n return global_constants.SCORM_TRUE;\n }\n\n return global_constants.SCORM_FALSE;\n }\n\n /**\n * Traverses to the next level in the CMI path\n */\n private traverseToNextLevel(\n refObject: StringKeyMap | BaseCMI,\n structure: string[],\n idx: number,\n value: string,\n CMIElement: string,\n scorm2004: boolean,\n foundFirstIndex: boolean,\n invalidErrorCode: number,\n invalidErrorMessage: string,\n ): { refObject: StringKeyMap | BaseCMI; idx: number; foundFirstIndex: boolean; error: boolean } {\n const attribute = structure[idx];\n\n if (\n typeof attribute === \"undefined\" ||\n !this.context.checkObjectHasProperty(refObject as StringKeyMap, attribute)\n ) {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n return { refObject, idx, foundFirstIndex, error: true };\n }\n\n refObject = (refObject as StringKeyMap)[attribute] as StringKeyMap;\n if (!refObject) {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n return { refObject, idx, foundFirstIndex, error: true };\n }\n\n // Handle array access\n if (refObject instanceof CMIArray) {\n const arrayResult = this.handleSetArrayAccess(\n refObject,\n structure,\n idx,\n value,\n CMIElement,\n scorm2004,\n foundFirstIndex,\n invalidErrorCode,\n invalidErrorMessage,\n );\n\n if (arrayResult.error) {\n return { refObject, idx, foundFirstIndex, error: true };\n }\n\n return arrayResult;\n }\n\n return { refObject, idx, foundFirstIndex, error: false };\n }\n\n /**\n * Handles array access during set operations\n */\n private handleSetArrayAccess(\n refObject: CMIArray,\n structure: string[],\n idx: number,\n value: string,\n CMIElement: string,\n scorm2004: boolean,\n foundFirstIndex: boolean,\n invalidErrorCode: number,\n invalidErrorMessage: string,\n ): { refObject: StringKeyMap | BaseCMI; idx: number; foundFirstIndex: boolean; error: boolean } {\n const index = parseInt(structure[idx + 1] || \"0\", 10);\n\n if (!isNaN(index)) {\n const item = refObject.childArray[index];\n\n if (item) {\n return {\n refObject: item,\n idx: idx + 1,\n foundFirstIndex: true,\n error: false,\n };\n } else {\n // SCORM spec requires sequential array indices\n if (index > refObject.childArray.length) {\n const errorCode = scorm2004\n ? getErrorCode(this.context.errorCodes, \"GENERAL_SET_FAILURE\")\n : getErrorCode(this.context.errorCodes, \"INVALID_SET_VALUE\") ||\n getErrorCode(this.context.errorCodes, \"GENERAL_SET_FAILURE\");\n this.context.throwSCORMError(\n CMIElement,\n errorCode,\n `Cannot set array element at index ${index}. Array indices must be sequential. Current array length is ${refObject.childArray.length}, expected index ${refObject.childArray.length}.`,\n );\n return { refObject, idx, foundFirstIndex, error: true };\n }\n\n // Create new child element\n const newChild = this.context.getChildElement(CMIElement, value, foundFirstIndex);\n\n if (!newChild) {\n if (this.context.getLastErrorCode() === \"0\") {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n }\n return { refObject, idx, foundFirstIndex, error: true };\n } else {\n if (refObject.initialized) newChild.initialize();\n refObject.childArray[index] = newChild;\n return {\n refObject: newChild,\n idx: idx + 1,\n foundFirstIndex: true,\n error: false,\n };\n }\n }\n }\n\n return { refObject, idx, foundFirstIndex, error: false };\n }\n\n /**\n * Validates an attribute during get operations\n */\n private validateGetAttribute(\n refObject: StringKeyMap,\n attribute: string,\n CMIElement: string,\n scorm2004: boolean,\n invalidErrorCode: number,\n invalidErrorMessage: string,\n isFinalAttribute: boolean,\n ): { error: boolean; returnValue?: any } {\n if (!scorm2004) {\n if (isFinalAttribute) {\n if (\n typeof attribute === \"undefined\" ||\n !this.context.checkObjectHasProperty(refObject, attribute)\n ) {\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n return { error: true };\n }\n }\n } else {\n // Handle SCORM 2004 target validation\n const attrStr = String(attribute);\n if (\n attrStr.startsWith(TARGET_ATTRIBUTE_PREFIX) &&\n typeof refObject._isTargetValid == \"function\"\n ) {\n const target = attrStr.substring(TARGET_ATTRIBUTE_PREFIX.length, attrStr.length - 1);\n return { error: false, returnValue: refObject._isTargetValid(target) };\n } else if (\n typeof attribute === \"undefined\" ||\n !this.context.checkObjectHasProperty(refObject, attribute)\n ) {\n // Check for keyword errors with specific diagnostics\n if (attribute === \"_children\") {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"GENERAL_GET_FAILURE\"),\n \"The data model element does not have children\",\n );\n return { error: true };\n } else if (attribute === \"_count\") {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"GENERAL_GET_FAILURE\"),\n \"The data model element is not a collection and therefore does not have a count\",\n );\n return { error: true };\n }\n this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);\n return { error: true };\n }\n }\n\n return { error: false };\n }\n\n /**\n * Handles array access during get operations\n */\n private handleGetArrayAccess(\n refObject: CMIArray,\n structure: string[],\n idx: number,\n CMIElement: string,\n uninitializedErrorMessage: string,\n ): { refObject: StringKeyMap; idx: number; error: boolean } {\n const index = parseInt(structure[idx + 1] || \"\", 10);\n\n if (!isNaN(index)) {\n const item = refObject.childArray[index];\n\n if (item) {\n return {\n refObject: item as unknown as StringKeyMap,\n idx: idx + 1,\n error: false,\n };\n } else {\n this.context.throwSCORMError(\n CMIElement,\n getErrorCode(this.context.errorCodes, \"VALUE_NOT_INITIALIZED\"),\n uninitializedErrorMessage,\n );\n return { refObject: refObject as unknown as StringKeyMap, idx, error: true };\n }\n }\n\n return { refObject: refObject as unknown as StringKeyMap, idx, error: false };\n }\n}\n","import { LogLevel } from \"../types/api_types\";\nimport { LogLevelEnum } from \"../constants/enums\";\nimport { ILoggingService } from \"../interfaces/services\";\nimport { defaultLogHandler } from \"../constants/default_settings\";\n\n/**\n * Centralized logging service implemented as a singleton\n * Provides methods for logging at different levels and configuring the log level.\n * Supports custom log handlers via the `customLogHandler` setting to integrate with\n * external logging systems or implement custom logging behavior.\n */\nexport class LoggingService implements ILoggingService {\n private static _instance: LoggingService;\n private _logLevel: LogLevel = LogLevelEnum.ERROR;\n private _logHandler: (messageLevel: LogLevel, logMessage: string) => void;\n\n /**\n * Private constructor to prevent direct instantiation\n */\n private constructor() {\n // Default log handler uses console methods based on log level\n this._logHandler = defaultLogHandler;\n }\n\n /**\n * Get the singleton instance of LoggingService\n *\n * @returns {LoggingService} The singleton instance\n */\n public static getInstance(): LoggingService {\n if (!LoggingService._instance) {\n LoggingService._instance = new LoggingService();\n }\n return LoggingService._instance;\n }\n\n /**\n * Set the log level\n *\n * @param {LogLevel} level - The log level to set\n */\n public setLogLevel(level: LogLevel): void {\n this._logLevel = level;\n }\n\n /**\n * Get the current log level\n *\n * @returns {LogLevel} The current log level\n */\n public getLogLevel(): LogLevel {\n return this._logLevel;\n }\n\n /**\n * Set a custom log handler\n *\n * @param {Function} handler - The function to handle log messages\n */\n public setLogHandler(handler: (messageLevel: LogLevel, logMessage: string) => void): void {\n this._logHandler = handler;\n }\n\n /**\n * Log a message if the message level is greater than or equal to the current log level\n *\n * @param {LogLevel} messageLevel - The level of the message\n * @param {string} logMessage - The message to log\n *\n * @security LOG-INJECTION\n * Be aware that logMessage is passed through to the log handler without sanitization.\n * When logging user-controlled data (e.g., SCORM CMI values from content, URL parameters,\n * postMessage payloads), consider the following risks:\n *\n * 1. Log injection: Malicious input containing newlines or ANSI codes could pollute logs\n * or create fake log entries that mislead security monitoring.\n *\n * 2. Information disclosure: Sensitive data in logs may be exposed to unauthorized viewers\n * with log access (developers, support staff, aggregation systems).\n *\n * 3. Log storage exhaustion: Extremely large or repeated values could fill disk space\n * or cause performance degradation in log processing systems.\n *\n * Defensive patterns:\n * - Truncate long values before logging (e.g., logMessage.substring(0, 500))\n * - Strip or escape newlines and control characters\n * - Redact sensitive fields (PII, credentials, session tokens)\n * - Implement custom log handlers that sanitize before writing to external systems\n * - Use structured logging formats (JSON) that escape values properly\n *\n * Example of safe logging for user-controlled data:\n * ```typescript\n * const sanitized = userInput.replace(/[\\r\\n\\x00-\\x1F\\x7F]/g, '').substring(0, 200);\n * loggingService.info(`User input: ${sanitized}`);\n * ```\n */\n public log(messageLevel: LogLevel, logMessage: string): void {\n if (this.shouldLog(messageLevel)) {\n this._logHandler(messageLevel, logMessage);\n }\n }\n\n /**\n * Log a message at ERROR level\n *\n * @param {string} logMessage - The message to log\n */\n public error(logMessage: string): void {\n this.log(LogLevelEnum.ERROR, logMessage);\n }\n\n /**\n * Log a message at WARN level\n *\n * @param {string} logMessage - The message to log\n */\n public warn(logMessage: string): void {\n this.log(LogLevelEnum.WARN, logMessage);\n }\n\n /**\n * Log a message at INFO level\n *\n * @param {string} logMessage - The message to log\n */\n public info(logMessage: string): void {\n this.log(LogLevelEnum.INFO, logMessage);\n }\n\n /**\n * Log a message at DEBUG level\n *\n * @param {string} logMessage - The message to log\n */\n public debug(logMessage: string): void {\n this.log(LogLevelEnum.DEBUG, logMessage);\n }\n\n /**\n * Determine if a message should be logged based on its level and the current log level\n *\n * @param {LogLevel} messageLevel - The level of the message\n * @returns {boolean} Whether the message should be logged\n */\n private shouldLog(messageLevel: LogLevel): boolean {\n // Convert string levels to numbers for comparison\n const numericMessageLevel = this.getNumericLevel(messageLevel);\n const numericLogLevel = this.getNumericLevel(this._logLevel);\n\n return numericMessageLevel >= numericLogLevel;\n }\n\n /**\n * Convert a log level to its numeric value\n *\n * @param {LogLevel} level - The log level to convert\n * @returns {number} The numeric value of the log level\n */\n private getNumericLevel(level: LogLevel): number {\n if (level === undefined) return LogLevelEnum.NONE;\n\n if (typeof level === \"number\") return level;\n\n switch (level) {\n case \"1\":\n case \"DEBUG\":\n return LogLevelEnum.DEBUG;\n case \"2\":\n case \"INFO\":\n return LogLevelEnum.INFO;\n case \"3\":\n case \"WARN\":\n return LogLevelEnum.WARN;\n case \"4\":\n case \"ERROR\":\n return LogLevelEnum.ERROR;\n case \"5\":\n case \"NONE\":\n return LogLevelEnum.NONE;\n default:\n return LogLevelEnum.ERROR; // Default to ERROR if unknown\n }\n }\n}\n\n// Export a function to get the singleton instance\nexport function getLoggingService(): LoggingService {\n return LoggingService.getInstance();\n}\n","import { LogLevelEnum } from \"../constants/enums\";\nimport { global_constants } from \"../constants/api_constants\";\nimport { ErrorCode } from \"../constants/error_codes\";\nimport { ValidationError } from \"../exceptions\";\nimport { IErrorHandlingService, ILoggingService } from \"../interfaces/services\";\nimport { getLoggingService } from \"./LoggingService\";\n\n/**\n * Service for handling SCORM errors\n */\nexport class ErrorHandlingService implements IErrorHandlingService {\n private _lastErrorCode: string = \"0\";\n private _lastDiagnostic: string = \"\";\n private readonly _errorCodes: ErrorCode;\n private readonly _apiLog: (\n functionName: string,\n message: string,\n logLevel?: LogLevelEnum,\n CMIElement?: string,\n ) => void;\n private readonly _getLmsErrorMessageDetails: (errorCode: number, detail: boolean) => string;\n private readonly _loggingService: ILoggingService;\n\n /**\n * Constructor for ErrorHandlingService\n *\n * @param {ErrorCode} errorCodes - The error codes object\n * @param {Function} apiLog - Function for logging API calls\n * @param {Function} getLmsErrorMessageDetails - Function for getting error message details\n * @param {ILoggingService} loggingService - Optional logging service instance\n */\n constructor(\n errorCodes: ErrorCode,\n apiLog: (\n functionName: string,\n message: string,\n logLevel?: LogLevelEnum,\n CMIElement?: string,\n ) => void,\n getLmsErrorMessageDetails: (errorCode: number, detail: boolean) => string,\n loggingService?: ILoggingService,\n ) {\n this._errorCodes = errorCodes;\n this._apiLog = apiLog;\n this._getLmsErrorMessageDetails = getLmsErrorMessageDetails;\n this._loggingService = loggingService || getLoggingService();\n }\n\n /**\n * Get the last error code\n *\n * @return {string} - The last error code\n */\n get lastErrorCode(): string {\n return this._lastErrorCode;\n }\n\n /**\n * Set the last error code\n *\n * @param {string} errorCode - The error code to set\n */\n set lastErrorCode(errorCode: string) {\n this._lastErrorCode = errorCode;\n }\n\n /**\n * Get the last custom diagnostic message\n *\n * @return {string} - The last custom diagnostic message, or empty string if none\n */\n get lastDiagnostic(): string {\n return this._lastDiagnostic;\n }\n\n /**\n * Throws a SCORM error\n *\n * @param {string} CMIElement\n * @param {number} errorNumber - The error number\n * @param {string} message - The error message\n * @throws {ValidationError} - If throwException is true, throws a ValidationError\n */\n throwSCORMError(CMIElement: string, errorNumber: number, message?: string): void {\n // Store custom diagnostic if provided, otherwise clear it\n this._lastDiagnostic = message || \"\";\n\n if (!message) {\n message = this._getLmsErrorMessageDetails(errorNumber, true);\n }\n\n // Format a more descriptive error message with context\n const formattedMessage = `SCORM Error ${errorNumber}: ${message}${CMIElement ? ` [Element: ${CMIElement}]` : \"\"}`;\n\n // Log using both the API log and the logging service for consistency\n this._apiLog(\"throwSCORMError\", errorNumber + \": \" + message, LogLevelEnum.ERROR, CMIElement);\n this._loggingService.error(formattedMessage);\n\n this._lastErrorCode = String(errorNumber);\n }\n\n /**\n * Clears the last SCORM error code on success.\n *\n * @param {string} success - Whether the operation was successful\n */\n clearSCORMError(success: string): void {\n if (success !== undefined && success !== global_constants.SCORM_FALSE) {\n this._lastErrorCode = \"0\";\n }\n }\n\n /**\n * Handles exceptions that occur when accessing or setting CMI values.\n *\n * This method provides centralized error handling for exceptions that occur during\n * CMI data operations. It differentiates between different types of errors and\n * handles them appropriately:\n *\n * 1. ValidationError: These are expected errors from the validation system that\n * indicate a specific SCORM error condition (like invalid data format or range).\n * For these errors, the method:\n * - Sets the lastErrorCode to the error code from the ValidationError\n * - Returns SCORM_FALSE to indicate failure to the caller\n *\n * 2. Standard JavaScript Error: For general JavaScript errors (like TypeError,\n * ReferenceError, etc.), the method:\n * - Logs the error message with stack trace to the logging service\n * - Sets a general SCORM error\n * - Returns SCORM_FALSE to indicate failure\n *\n * 3. Unknown exceptions: For any other type of exception that doesn't match the\n * above categories, the method:\n * - Logs the entire exception object to the logging service\n * - Sets a general SCORM error\n * - Returns SCORM_FALSE to indicate failure\n *\n * This method is critical for maintaining SCORM compliance by ensuring that\n * all errors are properly translated into the appropriate SCORM error codes.\n *\n * @param {string} CMIElement\n * @param {ValidationError|Error|unknown} e - The exception that was thrown\n * @param {string} returnValue - The default return value (typically an empty string)\n * @return {string} - Either the original returnValue or SCORM_FALSE if an error occurred\n *\n * @example\n * try {\n * const value = getCMIValue(\"cmi.core.score.raw\");\n * return value;\n * } catch (e) {\n * return handleValueAccessException(e, \"\");\n * }\n */\n handleValueAccessException(\n CMIElement: string,\n e: Error | ValidationError | unknown,\n returnValue: string,\n ): string {\n if (e instanceof ValidationError) {\n const validationError = e as ValidationError;\n this._lastErrorCode = String(validationError.errorCode);\n this._lastDiagnostic = \"\";\n\n // Log validation errors at WARN level with context\n const errorMessage = `Validation Error ${validationError.errorCode}: ${validationError.message} [Element: ${CMIElement}]`;\n this._loggingService.warn(errorMessage);\n\n returnValue = global_constants.SCORM_FALSE;\n } else if (e instanceof Error) {\n // For standard JS errors, include the stack trace and error type\n const errorType = e.constructor.name; // Gets the error type (e.g., TypeError, ReferenceError)\n const errorMessage = `${errorType}: ${e.message} [Element: ${CMIElement}]`;\n const stackTrace = e.stack || \"\";\n\n // Log the detailed error with stack trace\n this._loggingService.error(`${errorMessage}\\n${stackTrace}`);\n\n this.throwSCORMError(\n CMIElement,\n this._errorCodes.GENERAL as number,\n `${errorType}: ${e.message}`,\n );\n\n // SVC-ERR-01: Set returnValue to SCORM_FALSE for all error types\n returnValue = global_constants.SCORM_FALSE;\n } else {\n // For unknown errors, provide as much context as possible\n const errorMessage = `Unknown error occurred while accessing [Element: ${CMIElement}]`;\n\n this._loggingService.error(errorMessage);\n\n try {\n // Try to stringify the error object for more details\n const errorDetails = JSON.stringify(e);\n this._loggingService.error(`Error details: ${errorDetails}`);\n } catch (jsonError) {\n // If stringify fails, log that we couldn't get more details\n this._loggingService.error(\"Could not stringify error object for details\");\n }\n\n this.throwSCORMError(CMIElement, this._errorCodes.GENERAL as number, \"Unknown error\");\n\n // SVC-ERR-01: Set returnValue to SCORM_FALSE for all error types\n returnValue = global_constants.SCORM_FALSE;\n }\n return returnValue;\n }\n\n /**\n * Get the error codes object\n *\n * @return {ErrorCode} - The error codes object\n */\n get errorCodes(): ErrorCode {\n return this._errorCodes;\n }\n}\n\n// Export a factory function to create the service\nexport function createErrorHandlingService(\n errorCodes: ErrorCode,\n apiLog: (\n functionName: string,\n message: string,\n logLevel?: LogLevelEnum,\n CMIElement?: string,\n ) => void,\n getLmsErrorMessageDetails: (errorCode: number, detail: boolean) => string,\n loggingService?: ILoggingService,\n): ErrorHandlingService {\n return new ErrorHandlingService(errorCodes, apiLog, getLmsErrorMessageDetails, loggingService);\n}\n","import { LogLevel } from \"../types/api_types\";\nimport { LogLevelEnum } from \"../constants/enums\";\nimport { IEventService, ScormEventCallback } from \"../interfaces/services\";\n\n/**\n * Interface for a listener object\n */\ninterface Listener {\n functionName: string;\n CMIElement: string | null;\n callback: ScormEventCallback;\n}\n\n/**\n * Type for parsed listener information\n */\ninterface ParsedListener {\n functionName: string;\n CMIElement: string | null;\n}\n\n/**\n * Service for handling event listeners and event processing\n */\nexport class EventService implements IEventService {\n // Map of function names to listeners for faster lookups\n private listenerMap: Map<string, Listener[]> = new Map();\n // Total count of listeners for logging\n private listenerCount = 0;\n // Function to log API messages\n private readonly apiLog: (\n functionName: string,\n message: string,\n messageLevel: LogLevel,\n CMIElement?: string,\n ) => void;\n\n /**\n * Constructor for EventService\n * @param {Function} apiLog - Function to log API messages\n */\n constructor(\n apiLog: (\n functionName: string,\n message: string,\n messageLevel: LogLevel,\n CMIElement?: string,\n ) => void,\n ) {\n this.apiLog = apiLog;\n }\n\n /**\n * Parses a listener name into its components\n *\n * @param {string} listenerName - The name of the listener\n * @returns {ParsedListener|null} - The parsed listener information or null if invalid\n */\n private parseListenerName(listenerName: string): ParsedListener | null {\n // SVC-EVT-01: Handle empty string input\n if (!listenerName) return null;\n\n const listenerSplit = listenerName.split(\".\");\n\n const functionName = listenerSplit[0];\n let CMIElement: string | null = null;\n\n if (listenerSplit.length > 1) {\n CMIElement = listenerName.replace(`${functionName}.`, \"\");\n }\n\n return { functionName: functionName ?? listenerName, CMIElement };\n }\n\n /**\n * Provides a mechanism for attaching to a specific SCORM event\n *\n * @param {string} listenerName - The name of the listener\n * @param {Function} callback - The callback function to execute when the event occurs\n */\n on(listenerName: string, callback: ScormEventCallback) {\n if (!callback) return;\n\n const listenerFunctions = listenerName.split(\" \");\n for (const listenerFunction of listenerFunctions) {\n const parsedListener = this.parseListenerName(listenerFunction);\n if (!parsedListener) continue;\n\n const { functionName, CMIElement } = parsedListener;\n\n // Get or create the array for this function name\n const listeners = this.listenerMap.get(functionName) ?? [];\n\n // Add the new listener\n listeners.push({\n functionName,\n CMIElement,\n callback,\n });\n\n // Update the map and count\n this.listenerMap.set(functionName, listeners);\n this.listenerCount++;\n\n this.apiLog(\n \"on\",\n `Added event listener: ${this.listenerCount}`,\n LogLevelEnum.INFO,\n functionName,\n );\n }\n }\n\n /**\n * Provides a mechanism for detaching a specific SCORM event listener\n *\n * @param {string} listenerName - The name of the listener to remove\n * @param {Function} callback - The callback function to remove\n */\n off(listenerName: string, callback: ScormEventCallback) {\n if (!callback) return;\n\n const listenerFunctions = listenerName.split(\" \");\n for (const listenerFunction of listenerFunctions) {\n const parsedListener = this.parseListenerName(listenerFunction);\n if (!parsedListener) continue;\n\n const { functionName, CMIElement } = parsedListener;\n\n // Get the listeners for this function name\n const listeners = this.listenerMap.get(functionName);\n if (!listeners) continue;\n\n // Find the index of the listener to remove\n const removeIndex = listeners.findIndex(\n (obj) => obj.CMIElement === CMIElement && obj.callback === callback,\n );\n\n if (removeIndex !== -1) {\n // Remove the listener (modifies array in-place)\n listeners.splice(removeIndex, 1);\n this.listenerCount--;\n\n // Remove the map entry if no listeners remain\n // Note: No need to update the map otherwise, as we modified the array by reference\n if (listeners.length === 0) {\n this.listenerMap.delete(functionName);\n }\n\n this.apiLog(\n \"off\",\n `Removed event listener: ${this.listenerCount}`,\n LogLevelEnum.INFO,\n functionName,\n );\n }\n }\n }\n\n /**\n * Provides a mechanism for clearing all listeners from a specific SCORM event\n *\n * Note: clear() differs from off() in CMIElement matching behavior:\n * - clear() with CMIElement=null removes ALL listeners for the function\n * - off() requires exact CMIElement match AND callback match\n * This allows clear() to remove all listeners at once, while off() is surgical.\n *\n * @param {string} listenerName - The name of the listener to clear\n */\n clear(listenerName: string) {\n const listenerFunctions = listenerName.split(\" \");\n for (const listenerFunction of listenerFunctions) {\n const parsedListener = this.parseListenerName(listenerFunction);\n if (!parsedListener) continue;\n\n const { functionName, CMIElement } = parsedListener;\n\n // If we have listeners for this function name\n if (this.listenerMap.has(functionName)) {\n const listeners = this.listenerMap.get(functionName)!;\n\n // When CMIElement is null (no specific element provided), clear ALL listeners for this function.\n // When CMIElement is specified, only clear listeners matching that exact CMIElement.\n const newListeners =\n CMIElement === null ? [] : listeners.filter((obj) => obj.CMIElement !== CMIElement);\n\n // Update the count and map\n this.listenerCount -= listeners.length - newListeners.length;\n\n if (newListeners.length === 0) {\n this.listenerMap.delete(functionName);\n } else {\n this.listenerMap.set(functionName, newListeners);\n }\n }\n }\n }\n\n /**\n * Processes any 'on' listeners that have been created\n *\n * @param {string} functionName - The name of the function that triggered the event\n * @param {string} CMIElement - The CMI element that was affected\n * @param {any} value - The value that was set\n */\n processListeners(functionName: string, CMIElement?: string, value?: any) {\n this.apiLog(functionName, value, LogLevelEnum.INFO, CMIElement);\n\n // Get listeners for this function name\n const listeners = this.listenerMap.get(functionName);\n if (!listeners) return;\n\n for (const listener of listeners) {\n const listenerHasCMIElement = !!listener.CMIElement;\n let CMIElementsMatch = false;\n\n // Match CMI elements: supports wildcard (*) for prefix matching or exact string comparison.\n // Listeners with no CMIElement (null) match all events for this function.\n if (CMIElement && listener.CMIElement) {\n if (listener.CMIElement.endsWith(\"*\")) {\n // For wildcard matches, check if the CMI element starts with the prefix\n const prefix = listener.CMIElement.slice(0, -1);\n CMIElementsMatch = CMIElement.startsWith(prefix);\n } else {\n // For exact matches, compare the strings directly\n CMIElementsMatch = listener.CMIElement === CMIElement;\n }\n }\n\n // If the listener matches, call the callback\n if (!listenerHasCMIElement || CMIElementsMatch) {\n this.apiLog(\n \"processListeners\",\n `Processing listener: ${listener.functionName}`,\n LogLevelEnum.DEBUG,\n CMIElement,\n );\n\n // Dispatch to callback with appropriate arguments based on event type\n // Different event types have different callback signatures:\n // - Sequence events: callback(target)\n // - CommitError: callback(errorCode)\n // - CommitSuccess: callback()\n // - Regular events: callback(CMIElement, value)\n if (functionName.startsWith(\"Sequence\")) {\n // For sequence events, pass the target as the first argument\n listener.callback(value);\n } else if (functionName === \"CommitError\") {\n // For commit error events, pass the error code\n listener.callback(value);\n } else if (functionName === \"CommitSuccess\") {\n // For commit success events, pass no arguments\n listener.callback();\n } else {\n // For regular events, pass CMIElement and value\n listener.callback(CMIElement, value);\n }\n }\n }\n }\n\n /**\n * Resets the event service by clearing all listeners\n */\n reset() {\n this.listenerMap.clear();\n this.listenerCount = 0;\n }\n}\n","import { CommitObject, InternalSettings, ResultObject } from \"../types/api_types\";\nimport { global_constants } from \"../constants/api_constants\";\nimport { LogLevelEnum } from \"../constants/enums\";\nimport { ErrorCode } from \"../constants/error_codes\";\n\n/**\n * Interface for sync queue item\n */\ninterface SyncQueueItem {\n id: string;\n courseId: string;\n timestamp: number;\n data: CommitObject;\n syncAttempts: number;\n}\n\n/**\n * Service for handling offline storage and synchronization of SCORM data\n */\nexport class OfflineStorageService {\n private settings: InternalSettings;\n private error_codes: ErrorCode;\n private storeName: string = \"scorm_again_offline_data\";\n private syncQueue: string = \"scorm_again_sync_queue\";\n private isOnline: boolean = navigator.onLine;\n private syncInProgress: boolean = false;\n private boundOnlineStatusChangeHandler: () => void;\n private boundCustomNetworkStatusHandler: (event: Event) => void;\n\n /**\n * Constructor for OfflineStorageService\n * @param {Settings} settings - The settings object\n * @param {ErrorCode} error_codes - The error codes object\n * @param {Function} apiLog - The logging function\n */\n constructor(\n settings: InternalSettings,\n error_codes: ErrorCode,\n private apiLog: (\n functionName: string,\n message: any,\n messageLevel: LogLevelEnum,\n CMIElement?: string,\n ) => void,\n ) {\n this.settings = settings;\n this.error_codes = error_codes;\n\n // Store bound handlers for cleanup\n this.boundOnlineStatusChangeHandler = this.handleOnlineStatusChange.bind(this);\n this.boundCustomNetworkStatusHandler = this.handleCustomNetworkStatus.bind(this);\n\n // Initialize listeners for online/offline events\n window.addEventListener(\"online\", this.boundOnlineStatusChangeHandler);\n window.addEventListener(\"offline\", this.boundOnlineStatusChangeHandler);\n\n // Initialize listener for custom network status events\n window.addEventListener(\"scorm-again:network-status\", this.boundCustomNetworkStatusHandler);\n }\n\n /**\n * Handle changes in online status\n */\n private handleOnlineStatusChange() {\n const wasOnline = this.isOnline;\n this.isOnline = navigator.onLine;\n\n // If we've come back online, trigger sync process\n if (!wasOnline && this.isOnline) {\n this.apiLog(\n \"OfflineStorageService\",\n \"Device is back online, attempting to sync...\",\n LogLevelEnum.INFO,\n );\n this.syncOfflineData().then(\n (success) => {\n if (success) {\n this.apiLog(\"OfflineStorageService\", \"Sync completed successfully\", LogLevelEnum.INFO);\n } else {\n this.apiLog(\"OfflineStorageService\", \"Sync failed\", LogLevelEnum.ERROR);\n }\n },\n (error) => {\n this.apiLog(\"OfflineStorageService\", `Error during sync: ${error}`, LogLevelEnum.ERROR);\n },\n );\n } else if (wasOnline && !this.isOnline) {\n this.apiLog(\n \"OfflineStorageService\",\n \"Device is offline, data will be stored locally\",\n LogLevelEnum.INFO,\n );\n }\n }\n\n /**\n * Handle custom network status events from external code\n * This allows mobile apps or other external code to programmatically update network status\n * @param {Event} event - The custom event containing network status\n */\n private handleCustomNetworkStatus(event: Event) {\n if (!(event instanceof CustomEvent)) {\n this.apiLog(\n \"OfflineStorageService\",\n \"Invalid network status event received\",\n LogLevelEnum.WARN,\n );\n return;\n }\n\n const { online } = event.detail;\n\n if (typeof online !== \"boolean\") {\n this.apiLog(\n \"OfflineStorageService\",\n \"Invalid online status value in custom event\",\n LogLevelEnum.WARN,\n );\n return;\n }\n\n const wasOnline = this.isOnline;\n this.isOnline = online;\n\n this.apiLog(\n \"OfflineStorageService\",\n `Network status updated via custom event: ${online ? \"online\" : \"offline\"}`,\n LogLevelEnum.INFO,\n );\n\n // If we've come back online, trigger sync process\n if (!wasOnline && this.isOnline) {\n this.apiLog(\n \"OfflineStorageService\",\n \"Device is back online, attempting to sync...\",\n LogLevelEnum.INFO,\n );\n this.syncOfflineData().then(\n (success) => {\n if (success) {\n this.apiLog(\"OfflineStorageService\", \"Sync completed successfully\", LogLevelEnum.INFO);\n } else {\n this.apiLog(\"OfflineStorageService\", \"Sync failed\", LogLevelEnum.ERROR);\n }\n },\n (error) => {\n this.apiLog(\"OfflineStorageService\", `Error during sync: ${error}`, LogLevelEnum.ERROR);\n },\n );\n } else if (wasOnline && !this.isOnline) {\n this.apiLog(\n \"OfflineStorageService\",\n \"Device is offline, data will be stored locally\",\n LogLevelEnum.INFO,\n );\n }\n }\n\n /**\n * Store commit data offline\n * @param {string} courseId - Identifier for the course\n * @param {CommitObject} commitData - The data to store offline\n * @returns {ResultObject} - Result of the storage operation\n */\n storeOffline(courseId: string, commitData: CommitObject): ResultObject {\n try {\n // Store the data in the sync queue with timestamp and unique ID\n const queueItem: SyncQueueItem = {\n id: `${courseId}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,\n courseId,\n timestamp: Date.now(),\n data: commitData,\n syncAttempts: 0,\n };\n\n // Get current queue\n const currentQueue = this.getFromStorage<SyncQueueItem[]>(this.syncQueue) || [];\n currentQueue.push(queueItem);\n\n // Save updated queue\n this.saveToStorage(this.syncQueue, currentQueue);\n\n // Also update the current state in the main storage (latest known state)\n this.saveToStorage(`${this.storeName}_${courseId}`, commitData);\n\n this.apiLog(\n \"OfflineStorageService\",\n `Stored data offline for course ${courseId}`,\n LogLevelEnum.INFO,\n );\n\n return {\n result: global_constants.SCORM_TRUE,\n errorCode: 0,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const isQuotaError = errorMessage.includes(\"storage quota\");\n\n this.apiLog(\n \"OfflineStorageService\",\n isQuotaError\n ? `storage quota exceeded - cannot store offline data for course ${courseId}`\n : `Error storing offline data: ${error}`,\n LogLevelEnum.ERROR,\n );\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: this.error_codes.GENERAL ?? 0,\n };\n }\n }\n\n /**\n * Get the stored offline data for a course\n * @param {string} courseId - Identifier for the course\n * @returns {Promise<CommitObject|null>} - The stored data or null if not found\n */\n async getOfflineData(courseId: string): Promise<CommitObject | null> {\n try {\n const data = this.getFromStorage<CommitObject>(`${this.storeName}_${courseId}`);\n return data || null;\n } catch (error) {\n this.apiLog(\n \"OfflineStorageService\",\n `Error retrieving offline data: ${error}`,\n LogLevelEnum.ERROR,\n );\n return null;\n }\n }\n\n /**\n * Synchronize offline data with the LMS when connection is available\n * @returns {Promise<boolean>} - Success status of synchronization\n */\n async syncOfflineData(): Promise<boolean> {\n // Don't run multiple sync processes at once\n if (this.syncInProgress || !this.isOnline) {\n return false;\n }\n\n this.syncInProgress = true;\n\n try {\n // Get the queue of items to sync\n const syncQueue = this.getFromStorage<SyncQueueItem[]>(this.syncQueue) || [];\n\n if (syncQueue.length === 0) {\n this.syncInProgress = false;\n return true;\n }\n\n this.apiLog(\n \"OfflineStorageService\",\n `Found ${syncQueue.length} items to sync`,\n LogLevelEnum.INFO,\n );\n\n // Keep track of successful and failed sync attempts\n const remainingQueue: SyncQueueItem[] = [];\n\n // Process each queue item\n for (const item of syncQueue) {\n const maxAttempts = this.settings.maxSyncAttempts ?? 5;\n\n // Remove items that have exceeded max sync attempts (abandon them)\n if (item.syncAttempts >= maxAttempts) {\n this.apiLog(\n \"OfflineStorageService\",\n `Removing abandoned item ${item.id} after ${maxAttempts} failed sync attempts`,\n LogLevelEnum.WARN,\n );\n // Don't add to remainingQueue - item is abandoned\n continue;\n }\n\n try {\n // Attempt to sync this item\n const syncResult = await this.sendDataToLMS(item.data);\n\n if (\n (syncResult.result as unknown) === true ||\n syncResult.result === global_constants.SCORM_TRUE\n ) {\n // Sync was successful, no need to keep this item\n this.apiLog(\n \"OfflineStorageService\",\n `Successfully synced item ${item.id}`,\n LogLevelEnum.INFO,\n );\n } else {\n // Sync failed, increment attempts and keep in queue\n item.syncAttempts++;\n remainingQueue.push(item);\n this.apiLog(\n \"OfflineStorageService\",\n `Failed to sync item ${item.id}, attempt #${item.syncAttempts}`,\n LogLevelEnum.WARN,\n );\n }\n } catch (error) {\n // On error, increment attempts and keep in queue\n item.syncAttempts++;\n remainingQueue.push(item);\n this.apiLog(\n \"OfflineStorageService\",\n `Error syncing item ${item.id}: ${error}`,\n LogLevelEnum.ERROR,\n );\n }\n }\n\n // Update the queue with remaining items\n this.saveToStorage(this.syncQueue, remainingQueue);\n\n this.apiLog(\n \"OfflineStorageService\",\n `Sync completed. ${syncQueue.length - remainingQueue.length} items synced, ${remainingQueue.length} items remaining`,\n LogLevelEnum.INFO,\n );\n\n this.syncInProgress = false;\n return true;\n } catch (error) {\n this.apiLog(\n \"OfflineStorageService\",\n `Error during sync process: ${error}`,\n LogLevelEnum.ERROR,\n );\n this.syncInProgress = false;\n return false;\n }\n }\n\n /**\n * Send data to the LMS when online\n * @param {CommitObject} data - The data to send to the LMS\n * @returns {Promise<ResultObject>} - Result of the sync operation\n */\n private async sendDataToLMS(data: CommitObject): Promise<ResultObject> {\n if (!this.settings.lmsCommitUrl) {\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: this.error_codes.GENERAL || 101,\n };\n }\n\n try {\n // Apply request handler if configured\n const processedData = this.settings.requestHandler(data);\n\n // Send the data to the LMS\n const init = {\n method: \"POST\",\n mode: this.settings.fetchMode,\n body: JSON.stringify(processedData),\n headers: {\n ...this.settings.xhrHeaders,\n \"Content-Type\": this.settings.commitRequestDataType,\n },\n } as RequestInit;\n\n if (this.settings.xhrWithCredentials) {\n init.credentials = \"include\";\n }\n\n const response = await fetch(this.settings.lmsCommitUrl as string, init);\n\n // Process the response using the configured handler\n const result =\n typeof this.settings.responseHandler === \"function\"\n ? await this.settings.responseHandler(response)\n : await response.json();\n\n if (\n response.status >= 200 &&\n response.status <= 299 &&\n ((result.result as unknown) === true || result.result === global_constants.SCORM_TRUE)\n ) {\n if (!Object.hasOwnProperty.call(result, \"errorCode\")) {\n result.errorCode = 0;\n }\n return result;\n } else {\n if (!Object.hasOwnProperty.call(result, \"errorCode\")) {\n result.errorCode = this.error_codes.GENERAL;\n }\n return result;\n }\n } catch (error) {\n this.apiLog(\n \"OfflineStorageService\",\n `Error sending data to LMS: ${error}`,\n LogLevelEnum.ERROR,\n );\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: this.error_codes.GENERAL || 101,\n };\n }\n }\n\n /**\n * Check if the device is currently online\n * @returns {boolean} - Online status\n */\n isDeviceOnline(): boolean {\n return this.isOnline;\n }\n\n // noinspection JSValidateJSDoc\n /**\n * Get item from localStorage\n * @param {string} key - The key to retrieve\n * @returns {T|null} - The retrieved data\n */\n private getFromStorage<T>(key: string): T | null {\n const storedData = localStorage.getItem(key);\n if (storedData) {\n try {\n return JSON.parse(storedData) as T;\n } catch (e) {\n return null;\n }\n }\n return null;\n }\n\n /**\n * Save item to localStorage\n * @param {string} key - The key to store under\n * @param {any} data - The data to store\n * @returns {void}\n * @throws {Error} Re-throws QuotaExceededError for handling upstream\n */\n private saveToStorage(key: string, data: any): void {\n try {\n localStorage.setItem(key, JSON.stringify(data));\n } catch (error) {\n if (error instanceof DOMException && error.name === \"QuotaExceededError\") {\n throw new Error(\"storage quota exceeded - localStorage is full\", { cause: error });\n }\n throw error;\n }\n }\n\n /**\n * Check if there is pending offline data for a course\n * @param {string} courseId - Identifier for the course\n * @returns {Promise<boolean>} - Whether there is pending data\n */\n async hasPendingOfflineData(courseId: string): Promise<boolean> {\n const queue = this.getFromStorage<SyncQueueItem[]>(this.syncQueue) || [];\n return queue.some((item) => item.courseId === courseId);\n }\n\n /**\n * Update the service settings\n * @param {Settings} settings - The new settings\n */\n updateSettings(settings: InternalSettings): void {\n this.settings = settings;\n }\n\n /**\n * Clean up event listeners\n * Should be called when the service is no longer needed\n */\n destroy(): void {\n window.removeEventListener(\"online\", this.boundOnlineStatusChangeHandler);\n window.removeEventListener(\"offline\", this.boundOnlineStatusChangeHandler);\n window.removeEventListener(\"scorm-again:network-status\", this.boundCustomNetworkStatusHandler);\n }\n}\n","import { BaseScormValidationError } from \"../../exceptions\";\nimport { memoize } from \"../../utilities\";\n\n/**\n * Check if the value matches the proper format. If not, throw proper error code.\n *\n * @param {string} value\n * @param {string} regexPattern\n * @param {number} errorCode\n * @param {typeof BaseScormValidationError} errorClass\n * @param {boolean} [allowEmptyString]\n * @return {boolean}\n */\nexport const checkValidFormat = memoize(\n (\n CMIElement: string,\n value: string,\n regexPattern: string | RegExp, // We accept either a string or a RegExp object to allow the usage of flags.\n errorCode: number,\n errorClass: typeof BaseScormValidationError,\n allowEmptyString?: boolean,\n ): boolean => {\n // noinspection SuspiciousTypeOfGuard\n if (typeof value !== \"string\") {\n return false;\n }\n const formatRegex = new RegExp(regexPattern);\n const matches = value.match(formatRegex);\n if (allowEmptyString && value === \"\") {\n return true;\n }\n // COM-VAL-01: Removed redundant value === undefined check (already handled by typeof above)\n if (!matches || matches[0] === \"\") {\n throw new errorClass(CMIElement, errorCode);\n }\n return true;\n },\n // Custom key function that excludes the error class from the cache key\n // since it can't be stringified and doesn't affect the validation result\n (CMIElement, value, regexPattern, errorCode, _errorClass, allowEmptyString) => {\n // Use typeof for non-string values to ensure consistent cache keys\n const valueKey = typeof value === \"string\" ? value : `[${typeof value}]`;\n return `${CMIElement}:${valueKey}:${regexPattern}:${errorCode}:${allowEmptyString || false}`;\n },\n);\n\n/**\n * Check if the value matches the proper range. If not, throw proper error code.\n *\n * Range pattern format per SCORM RTE specifications:\n * - \"min#max\": bounded range (e.g., \"0#100\" = 0 to 100)\n * - \"min#*\": unbounded maximum (e.g., \"-1#*\" = -1 to infinity)\n * - \"#max\": no minimum bound (e.g., \"#100\" = up to 100)\n *\n * @param {any} value\n * @param {string} rangePattern\n * @param {number} errorCode\n * @param {typeof BaseScormValidationError} errorClass\n * @return {boolean}\n */\nexport const checkValidRange = memoize(\n (\n CMIElement: string,\n value: any,\n rangePattern: string,\n errorCode: number,\n errorClass: typeof BaseScormValidationError,\n ): boolean => {\n const ranges = rangePattern.split(\"#\");\n value = Number(value);\n\n // If value is not a valid number, throw error\n if (isNaN(value)) {\n throw new errorClass(CMIElement, errorCode);\n }\n\n // COM-VAL-02: Handle empty minimum (no lower bound) and empty/wildcard maximum (no upper bound)\n // This structure uses independent bound checks rather than nested conditionals to:\n // 1. Correctly handle all SCORM range patterns (\"#max\", \"min#\", \"min#*\", \"min#max\")\n // 2. Make the validation logic explicit and testable for each boundary condition\n // 3. Avoid the complexity and error-proneness of deeply nested if-else statements\n const minBound = ranges[0];\n const maxBound = ranges[1];\n const hasMinimum = minBound !== undefined && minBound !== \"\";\n const hasMaximum = maxBound !== undefined && maxBound !== \"\" && maxBound !== \"*\";\n\n // Check minimum bound if it exists\n if (hasMinimum && value < Number(minBound)) {\n throw new errorClass(CMIElement, errorCode);\n }\n\n // Check maximum bound if it exists\n if (hasMaximum && value > Number(maxBound)) {\n throw new errorClass(CMIElement, errorCode);\n }\n\n return true;\n },\n // Custom key function that excludes the error class from the cache key\n // since it can't be stringified and doesn't affect the validation result\n (CMIElement, value, rangePattern, errorCode, _errorClass) =>\n `${CMIElement}:${value}:${rangePattern}:${errorCode}`,\n);\n","import { checkValidFormat, checkValidRange } from \"../common/validation\";\nimport { scorm2004_errors } from \"../../constants/error_codes\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\n\n/**\n * Helper method, no reason to have to pass the same error codes every time\n * @param {string} CMIElement\n * @param {string} value\n * @param {string} regexPattern\n * @param {boolean} allowEmptyString\n * @return {boolean}\n */\nexport function check2004ValidFormat(\n CMIElement: string,\n value: string,\n regexPattern: string,\n allowEmptyString?: boolean,\n): boolean {\n return checkValidFormat(\n CMIElement,\n value,\n regexPattern,\n scorm2004_errors.TYPE_MISMATCH as number,\n Scorm2004ValidationError,\n allowEmptyString,\n );\n}\n\n/**\n * Helper method, no reason to have to pass the same error codes every time\n * @param {string} CMIElement\n * @param {string} value\n * @param {string} rangePattern\n * @return {boolean}\n */\nexport function check2004ValidRange(\n CMIElement: string,\n value: string,\n rangePattern: string,\n): boolean {\n return checkValidRange(\n CMIElement,\n value,\n rangePattern,\n scorm2004_errors.VALUE_OUT_OF_RANGE as number,\n Scorm2004ValidationError,\n );\n}\n","import { BaseCMI } from \"../../common/base_cmi\";\nimport { Activity } from \"./activity\";\nimport { Scorm2004ValidationError } from \"../../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors } from \"../../../constants/error_codes\";\nimport { CompletionStatus, SuccessStatus } from \"../../../constants/enums\";\n\n/**\n * Enum for rollup action types\n */\nexport enum RollupActionType {\n SATISFIED = \"satisfied\",\n NOT_SATISFIED = \"notSatisfied\",\n COMPLETED = \"completed\",\n INCOMPLETE = \"incomplete\",\n}\n\n/**\n * Enum for rollup condition types\n */\nexport enum RollupConditionType {\n SATISFIED = \"satisfied\",\n OBJECTIVE_STATUS_KNOWN = \"objectiveStatusKnown\",\n OBJECTIVE_MEASURE_KNOWN = \"objectiveMeasureKnown\",\n OBJECTIVE_MEASURE_GREATER_THAN = \"objectiveMeasureGreaterThan\",\n OBJECTIVE_MEASURE_LESS_THAN = \"objectiveMeasureLessThan\",\n COMPLETED = \"completed\",\n PROGRESS_KNOWN = \"progressKnown\",\n ATTEMPTED = \"attempted\",\n NOT_ATTEMPTED = \"notAttempted\",\n ALWAYS = \"always\",\n}\n\n/**\n * Enum for rollup consideration types\n */\nexport enum RollupConsiderationType {\n ALL = \"all\",\n ANY = \"any\",\n NONE = \"none\",\n AT_LEAST_COUNT = \"atLeastCount\",\n AT_LEAST_PERCENT = \"atLeastPercent\",\n}\n\n/**\n * Class representing a rollup condition\n */\nexport class RollupCondition extends BaseCMI {\n private _condition: RollupConditionType = RollupConditionType.ALWAYS;\n private _parameters: Map<string, any> = new Map();\n\n /**\n * Constructor for RollupCondition\n * @param {RollupConditionType} condition - The condition type\n * @param {Map<string, any>} parameters - Additional parameters for the condition\n */\n constructor(\n condition: RollupConditionType = RollupConditionType.ALWAYS,\n parameters: Map<string, any> = new Map(),\n ) {\n super(\"rollupCondition\");\n this._condition = condition;\n this._parameters = parameters;\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n }\n\n /**\n * Getter for condition\n * @return {RollupConditionType}\n */\n get condition(): RollupConditionType {\n return this._condition;\n }\n\n /**\n * Setter for condition\n * @param {RollupConditionType} condition\n */\n set condition(condition: RollupConditionType) {\n this._condition = condition;\n }\n\n /**\n * Getter for parameters\n * @return {Map<string, any>}\n */\n get parameters(): Map<string, any> {\n return this._parameters;\n }\n\n /**\n * Setter for parameters\n * @param {Map<string, any>} parameters\n */\n set parameters(parameters: Map<string, any>) {\n this._parameters = parameters;\n }\n\n /**\n * Evaluate the condition for an activity\n * @param {Activity} activity - The activity to evaluate the condition for\n * @return {boolean} - True if the condition is met, false otherwise\n */\n evaluate(activity: Activity): boolean {\n switch (this._condition) {\n case RollupConditionType.SATISFIED:\n // Per SCORM 2004 SN Book RB.1.4.1, the \"satisfied\" condition checks the\n // objective satisfaction status from the activity's tracked objective.\n // This is populated via global objective mapping (readSatisfiedStatus).\n // Also check successStatus for backward compatibility with leaf activities.\n return activity.objectiveSatisfiedStatus === true ||\n activity.successStatus === SuccessStatus.PASSED;\n case RollupConditionType.OBJECTIVE_STATUS_KNOWN:\n // Per SCORM 2004 SN Book RB.1.4.1, objectiveStatusKnown checks if\n // the objective's satisfaction status has been explicitly determined\n return activity.objectiveSatisfiedStatusKnown;\n case RollupConditionType.OBJECTIVE_MEASURE_KNOWN:\n // Per SCORM 2004 SN Book RB.1.4.1, objectiveMeasureKnown checks if\n // the objective has a valid measure value\n return activity.objectiveMeasureStatus;\n case RollupConditionType.OBJECTIVE_MEASURE_GREATER_THAN: {\n const greaterThanValue = this._parameters.get(\"threshold\") || 0;\n return (\n activity.objectiveMeasureStatus && activity.objectiveNormalizedMeasure > greaterThanValue\n );\n }\n case RollupConditionType.OBJECTIVE_MEASURE_LESS_THAN: {\n const lessThanValue = this._parameters.get(\"threshold\") || 0;\n return (\n activity.objectiveMeasureStatus && activity.objectiveNormalizedMeasure < lessThanValue\n );\n }\n case RollupConditionType.COMPLETED:\n return activity.isCompleted;\n case RollupConditionType.PROGRESS_KNOWN:\n return activity.completionStatus !== CompletionStatus.UNKNOWN;\n case RollupConditionType.ATTEMPTED:\n return activity.attemptCount > 0;\n case RollupConditionType.NOT_ATTEMPTED:\n return activity.attemptCount === 0;\n case RollupConditionType.ALWAYS:\n return true;\n default:\n return false;\n }\n }\n\n /**\n * toJSON for RollupCondition\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n condition: this._condition,\n parameters: Object.fromEntries(this._parameters),\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing a rollup rule\n */\nexport class RollupRule extends BaseCMI {\n private _conditions: RollupCondition[] = [];\n private _action: RollupActionType = RollupActionType.SATISFIED;\n private _consideration: RollupConsiderationType = RollupConsiderationType.ALL;\n private _minimumCount: number = 0;\n private _minimumPercent: number = 0;\n\n /**\n * Constructor for RollupRule\n * @param {RollupActionType} action - The action to take when the rule conditions are met\n * @param {RollupConsiderationType} consideration - How to consider child activities\n * @param {number} minimumCount - The minimum count for AT_LEAST_COUNT consideration\n * @param {number} minimumPercent - The minimum percent for AT_LEAST_PERCENT consideration\n */\n constructor(\n action: RollupActionType = RollupActionType.SATISFIED,\n consideration: RollupConsiderationType = RollupConsiderationType.ALL,\n minimumCount: number = 0,\n minimumPercent: number = 0,\n ) {\n super(\"rollupRule\");\n this._action = action;\n this._consideration = consideration;\n this._minimumCount = minimumCount;\n this._minimumPercent = minimumPercent;\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._conditions = [];\n }\n\n /**\n * Getter for conditions\n * @return {RollupCondition[]}\n */\n get conditions(): RollupCondition[] {\n return this._conditions;\n }\n\n /**\n * Add a condition to the rule\n * @param {RollupCondition} condition - The condition to add\n */\n addCondition(condition: RollupCondition): void {\n // noinspection SuspiciousTypeOfGuard\n if (!(condition instanceof RollupCondition)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".conditions\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._conditions.push(condition);\n }\n\n /**\n * Remove a condition from the rule\n * @param {RollupCondition} condition - The condition to remove\n * @return {boolean} - True if the condition was removed, false otherwise\n */\n removeCondition(condition: RollupCondition): boolean {\n const index = this._conditions.indexOf(condition);\n if (index !== -1) {\n this._conditions.splice(index, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Getter for action\n * @return {RollupActionType}\n */\n get action(): RollupActionType {\n return this._action;\n }\n\n /**\n * Setter for action\n * @param {RollupActionType} action\n */\n set action(action: RollupActionType) {\n this._action = action;\n }\n\n /**\n * Getter for consideration\n * @return {RollupConsiderationType}\n */\n get consideration(): RollupConsiderationType {\n return this._consideration;\n }\n\n /**\n * Setter for consideration\n * @param {RollupConsiderationType} consideration\n */\n set consideration(consideration: RollupConsiderationType) {\n this._consideration = consideration;\n }\n\n /**\n * Getter for minimumCount\n * @return {number}\n */\n get minimumCount(): number {\n return this._minimumCount;\n }\n\n /**\n * Setter for minimumCount\n * @param {number} minimumCount\n */\n set minimumCount(minimumCount: number) {\n if (minimumCount >= 0) {\n this._minimumCount = minimumCount;\n }\n }\n\n /**\n * Getter for minimumPercent\n * @return {number}\n */\n get minimumPercent(): number {\n return this._minimumPercent;\n }\n\n /**\n * Setter for minimumPercent\n * @param {number} minimumPercent\n */\n set minimumPercent(minimumPercent: number) {\n if (minimumPercent >= 0 && minimumPercent <= 100) {\n this._minimumPercent = minimumPercent;\n }\n }\n\n /**\n * Evaluate the rule for a set of child activities\n * @param {Activity[]} children - The child activities to evaluate the rule for\n * @return {boolean} - True if the rule conditions are met, false otherwise\n */\n evaluate(children: Activity[]): boolean {\n if (children.length === 0) {\n return false;\n }\n\n // Filter children that meet all conditions\n const matchingChildren = children.filter((child) => {\n return this._conditions.every((condition) => condition.evaluate(child));\n });\n\n // Apply consideration\n switch (this._consideration) {\n case RollupConsiderationType.ALL:\n return matchingChildren.length === children.length;\n case RollupConsiderationType.ANY:\n return matchingChildren.length > 0;\n case RollupConsiderationType.NONE:\n return matchingChildren.length === 0;\n case RollupConsiderationType.AT_LEAST_COUNT:\n return matchingChildren.length >= this._minimumCount;\n case RollupConsiderationType.AT_LEAST_PERCENT: {\n const percent = (matchingChildren.length / children.length) * 100;\n return percent >= this._minimumPercent;\n }\n default:\n return false;\n }\n }\n\n /**\n * toJSON for RollupRule\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n conditions: this._conditions,\n action: this._action,\n consideration: this._consideration,\n minimumCount: this._minimumCount,\n minimumPercent: this._minimumPercent,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing a collection of rollup rules\n */\nexport class RollupRules extends BaseCMI {\n private _rules: RollupRule[] = [];\n\n /**\n * Constructor for RollupRules\n */\n constructor() {\n super(\"rollupRules\");\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._rules = [];\n }\n\n /**\n * Getter for rules\n * @return {RollupRule[]}\n */\n get rules(): RollupRule[] {\n return this._rules;\n }\n\n /**\n * Add a rule\n * @param {RollupRule} rule - The rule to add\n */\n addRule(rule: RollupRule): void {\n // noinspection SuspiciousTypeOfGuard\n if (!(rule instanceof RollupRule)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".rules\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._rules.push(rule);\n }\n\n /**\n * Remove a rule\n * @param {RollupRule} rule - The rule to remove\n * @return {boolean} - True if the rule was removed, false otherwise\n */\n removeRule(rule: RollupRule): boolean {\n const index = this._rules.indexOf(rule);\n if (index !== -1) {\n this._rules.splice(index, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Process rollup for an activity\n * @param {Activity} activity - The activity to process rollup for\n */\n processRollup(activity: Activity): void {\n if (!activity || activity.children.length === 0) {\n return;\n }\n\n const children = activity.getAvailableChildren();\n let completionRollup = false;\n let successRollup = false;\n\n // First, check if we should use measure rollup (RB.1.2.a)\n if (activity.sequencingControls.rollupObjectiveSatisfied) {\n const measureRollupResult = this._objectiveRollupUsingMeasure(activity, children);\n if (measureRollupResult !== null) {\n successRollup = true;\n // Skip rule-based rollup if measure rollup was successful\n }\n }\n\n // Process each rule (RB.1.2.b)\n if (!successRollup) {\n for (const rule of this._rules) {\n if (rule.evaluate(children)) {\n switch (rule.action) {\n case RollupActionType.SATISFIED:\n activity.successStatus = SuccessStatus.PASSED;\n successRollup = true;\n break;\n case RollupActionType.NOT_SATISFIED:\n activity.successStatus = SuccessStatus.FAILED;\n successRollup = true;\n break;\n case RollupActionType.COMPLETED:\n activity.completionStatus = CompletionStatus.COMPLETED;\n activity.isCompleted = true;\n completionRollup = true;\n break;\n case RollupActionType.INCOMPLETE:\n activity.completionStatus = CompletionStatus.INCOMPLETE;\n activity.isCompleted = false;\n completionRollup = true;\n break;\n }\n }\n }\n }\n\n // If no rules applied for completion, use default rollup\n if (!completionRollup) {\n this._defaultCompletionRollup(activity, children);\n }\n\n // If no rules applied for success, use default rollup (RB.1.2.c)\n if (!successRollup) {\n this._defaultSuccessRollup(activity, children);\n }\n }\n\n /**\n * Default completion rollup\n * @param {Activity} activity - The activity to process rollup for\n * @param {Activity[]} children - The child activities\n * @private\n */\n private _defaultCompletionRollup(activity: Activity, children: Activity[]): void {\n // If all children are completed, mark the parent as completed\n const allCompleted = children.every((child) => child.isCompleted);\n if (allCompleted) {\n activity.completionStatus = CompletionStatus.COMPLETED;\n activity.isCompleted = true;\n } else {\n // If any child is incomplete, mark the parent as incomplete\n const anyIncomplete = children.some(\n (child) => child.completionStatus === CompletionStatus.INCOMPLETE,\n );\n if (anyIncomplete) {\n activity.completionStatus = CompletionStatus.INCOMPLETE;\n activity.isCompleted = false;\n }\n }\n }\n\n /**\n * Objective Rollup Using Measure Process (RB.1.2.a)\n * @param {Activity} activity - The activity to process rollup for\n * @param {Activity[]} children - The child activities\n * @return {boolean | null} - True if satisfied, false if not satisfied, null if measure rollup not applicable\n * @private\n */\n private _objectiveRollupUsingMeasure(activity: Activity, children: Activity[]): boolean | null {\n // Check if objective measure weight is properly configured\n const objectiveMeasureWeight = activity.sequencingControls.objectiveMeasureWeight;\n if (objectiveMeasureWeight <= 0) {\n return null; // Measure rollup not applicable\n }\n\n // Calculate weighted average of child objective measures\n let totalWeight = 0;\n let weightedSum = 0;\n let hasValidMeasures = false;\n\n for (const child of children) {\n // Only include children that should contribute to rollup\n if (!child.sequencingControls.rollupObjectiveSatisfied) {\n continue;\n }\n\n // Check if child has a valid objective measure\n if (child.objectiveMeasureStatus && child.objectiveMeasureStatus === true) {\n const childWeight = child.sequencingControls.objectiveMeasureWeight;\n if (childWeight > 0) {\n weightedSum += child.objectiveNormalizedMeasure * childWeight;\n totalWeight += childWeight;\n hasValidMeasures = true;\n }\n }\n }\n\n // If no valid measures found, measure rollup is not applicable\n if (!hasValidMeasures || totalWeight === 0) {\n return null;\n }\n\n // Calculate the normalized measure for the parent activity\n const normalizedMeasure = weightedSum / totalWeight;\n activity.objectiveNormalizedMeasure = normalizedMeasure;\n activity.objectiveMeasureStatus = true;\n\n // Determine satisfaction based on scaled passing score\n if (normalizedMeasure >= activity.scaledPassingScore) {\n activity.successStatus = SuccessStatus.PASSED;\n activity.objectiveSatisfiedStatus = true;\n return true;\n } else {\n activity.successStatus = SuccessStatus.FAILED;\n activity.objectiveSatisfiedStatus = false;\n return false;\n }\n }\n\n /**\n * Default success rollup\n * @param {Activity} activity - The activity to process rollup for\n * @param {Activity[]} children - The child activities\n * @private\n */\n private _defaultSuccessRollup(activity: Activity, children: Activity[]): void {\n // If all children are satisfied, mark the parent as satisfied\n const allSatisfied = children.every((child) => child.successStatus === SuccessStatus.PASSED);\n if (allSatisfied) {\n activity.successStatus = SuccessStatus.PASSED;\n } else {\n // If any child is not satisfied, mark the parent as not satisfied\n const anyNotSatisfied = children.some(\n (child) => child.successStatus === SuccessStatus.FAILED,\n );\n if (anyNotSatisfied) {\n activity.successStatus = SuccessStatus.FAILED;\n }\n }\n }\n\n /**\n * toJSON for RollupRules\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n rules: this._rules,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseCMI } from \"../../common/base_cmi\";\nimport { scorm2004_regex } from \"../../../constants/regex\";\nimport { check2004ValidFormat } from \"../validation\";\nimport { Scorm2004ValidationError } from \"../../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors } from \"../../../constants/error_codes\";\nimport { CompletionStatus, SuccessStatus } from \"../../../constants/enums\";\nimport { SequencingControls } from \"./sequencing_controls\";\nimport { SequencingRules } from \"./sequencing_rules\";\nimport { RollupRules } from \"./rollup_rules\";\nimport { validateISO8601Duration } from \"../../../utilities\";\nimport { AuxiliaryResource, HideLmsUiItem, HIDE_LMS_UI_TOKENS } from \"../../../types/sequencing_types\";\n\nexport interface ObjectiveMapInfo {\n targetObjectiveID: string;\n readSatisfiedStatus?: boolean;\n readNormalizedMeasure?: boolean;\n writeSatisfiedStatus?: boolean;\n writeNormalizedMeasure?: boolean;\n readCompletionStatus?: boolean;\n writeCompletionStatus?: boolean;\n readProgressMeasure?: boolean;\n writeProgressMeasure?: boolean;\n readRawScore?: boolean;\n writeRawScore?: boolean;\n readMinScore?: boolean;\n writeMinScore?: boolean;\n readMaxScore?: boolean;\n writeMaxScore?: boolean;\n updateAttemptData?: boolean;\n}\n\nexport interface ActivityObjectiveOptions {\n description?: string | null;\n satisfiedByMeasure?: boolean;\n minNormalizedMeasure?: number | null;\n mapInfo?: ObjectiveMapInfo[];\n isPrimary?: boolean;\n}\n\nexport interface ActivityObjectiveState {\n id: string;\n satisfiedStatus: boolean;\n measureStatus: boolean;\n normalizedMeasure: number;\n progressMeasure: number;\n progressMeasureStatus: boolean;\n completionStatus: CompletionStatus;\n satisfiedByMeasure?: boolean;\n minNormalizedMeasure?: number | null;\n progressStatus: boolean;\n}\n\nexport type RollupConsiderationRequirement =\n | \"always\"\n | \"ifAttempted\"\n | \"ifNotSkipped\"\n | \"ifNotSuspended\";\n\nexport interface RollupConsiderationsConfig {\n requiredForSatisfied: RollupConsiderationRequirement;\n requiredForNotSatisfied: RollupConsiderationRequirement;\n requiredForCompleted: RollupConsiderationRequirement;\n requiredForIncomplete: RollupConsiderationRequirement;\n measureSatisfactionIfActive: boolean;\n}\n\n/**\n * Snapshot of rollup status for optimization comparison\n * Used by Overall Rollup Process (RB.1.5) to detect when status stops changing\n */\nexport interface RollupStatusSnapshot {\n measureStatus: boolean;\n normalizedMeasure: number;\n objectiveProgressStatus: boolean;\n objectiveSatisfiedStatus: boolean;\n attemptProgressStatus: boolean;\n attemptCompletionStatus: boolean;\n}\n\nexport class ActivityObjective {\n private _id: string;\n private _description: string | null;\n private _satisfiedByMeasure: boolean;\n private _minNormalizedMeasure: number | null;\n private _mapInfo: ObjectiveMapInfo[];\n private _isPrimary: boolean;\n\n private _satisfiedStatus: boolean = false;\n private _satisfiedStatusKnown: boolean = false;\n // Note: measureStatus has no dirty flag because it is not synchronized to global\n // objectives. It serves as a validity gate for other synced properties.\n private _measureStatus: boolean = false;\n private _normalizedMeasure: number = 0;\n private _progressMeasure: number = 0;\n private _progressMeasureStatus: boolean = false;\n private _completionStatus: CompletionStatus = CompletionStatus.UNKNOWN;\n private _progressStatus: boolean = false;\n\n // Dirty flags for tracking which properties have been modified locally\n private _satisfiedStatusDirty: boolean = false;\n private _normalizedMeasureDirty: boolean = false;\n private _completionStatusDirty: boolean = false;\n private _progressMeasureDirty: boolean = false;\n\n constructor(id: string, options: ActivityObjectiveOptions = {}) {\n this._id = id;\n this._description = options.description ?? null;\n this._satisfiedByMeasure = options.satisfiedByMeasure ?? false;\n this._minNormalizedMeasure = options.minNormalizedMeasure ?? null;\n this._mapInfo = options.mapInfo ? [...options.mapInfo] : [];\n this._isPrimary = options.isPrimary ?? false;\n }\n\n get id(): string {\n return this._id;\n }\n\n get description(): string | null {\n return this._description;\n }\n\n get satisfiedByMeasure(): boolean {\n return this._satisfiedByMeasure;\n }\n\n set satisfiedByMeasure(value: boolean) {\n this._satisfiedByMeasure = value;\n }\n\n get minNormalizedMeasure(): number | null {\n return this._minNormalizedMeasure;\n }\n\n set minNormalizedMeasure(value: number | null) {\n this._minNormalizedMeasure = value;\n }\n\n get mapInfo(): ObjectiveMapInfo[] {\n return this._mapInfo;\n }\n\n set mapInfo(mapInfo: ObjectiveMapInfo[]) {\n this._mapInfo = [...mapInfo];\n }\n\n get isPrimary(): boolean {\n return this._isPrimary;\n }\n\n set isPrimary(value: boolean) {\n this._isPrimary = value;\n }\n\n get satisfiedStatus(): boolean {\n return this._satisfiedStatus;\n }\n\n set satisfiedStatus(value: boolean) {\n if (this._satisfiedStatus !== value) {\n this._satisfiedStatus = value;\n this._satisfiedStatusDirty = true;\n }\n }\n\n get satisfiedStatusKnown(): boolean {\n return this._satisfiedStatusKnown;\n }\n\n set satisfiedStatusKnown(value: boolean) {\n this._satisfiedStatusKnown = value;\n }\n\n get measureStatus(): boolean {\n return this._measureStatus;\n }\n\n set measureStatus(value: boolean) {\n this._measureStatus = value;\n }\n\n get normalizedMeasure(): number {\n return this._normalizedMeasure;\n }\n\n set normalizedMeasure(value: number) {\n if (this._normalizedMeasure !== value) {\n this._normalizedMeasure = value;\n this._normalizedMeasureDirty = true;\n }\n }\n\n get progressMeasure(): number {\n return this._progressMeasure;\n }\n\n set progressMeasure(value: number) {\n if (this._progressMeasure !== value) {\n this._progressMeasure = value;\n this._progressMeasureDirty = true;\n }\n }\n\n get progressMeasureStatus(): boolean {\n return this._progressMeasureStatus;\n }\n\n set progressMeasureStatus(value: boolean) {\n this._progressMeasureStatus = value;\n }\n\n get completionStatus(): CompletionStatus {\n return this._completionStatus;\n }\n\n set completionStatus(value: CompletionStatus) {\n if (this._completionStatus !== value) {\n this._completionStatus = value;\n this._completionStatusDirty = true;\n }\n }\n\n get progressStatus(): boolean {\n return this._progressStatus;\n }\n\n set progressStatus(value: boolean) {\n this._progressStatus = value;\n }\n\n public isDirty(property: 'satisfiedStatus' | 'normalizedMeasure' | 'completionStatus' | 'progressMeasure'): boolean {\n switch (property) {\n case 'satisfiedStatus': return this._satisfiedStatusDirty;\n case 'normalizedMeasure': return this._normalizedMeasureDirty;\n case 'completionStatus': return this._completionStatusDirty;\n case 'progressMeasure': return this._progressMeasureDirty;\n }\n }\n\n public clearDirty(property: 'satisfiedStatus' | 'normalizedMeasure' | 'completionStatus' | 'progressMeasure'): void {\n switch (property) {\n case 'satisfiedStatus': this._satisfiedStatusDirty = false; break;\n case 'normalizedMeasure': this._normalizedMeasureDirty = false; break;\n case 'completionStatus': this._completionStatusDirty = false; break;\n case 'progressMeasure': this._progressMeasureDirty = false; break;\n }\n }\n\n public clearAllDirty(): void {\n this._satisfiedStatusDirty = false;\n this._normalizedMeasureDirty = false;\n this._completionStatusDirty = false;\n this._progressMeasureDirty = false;\n }\n\n /**\n * Initialize objective values from CMI data transfer\n * This method always marks values as dirty since CMI data should be written to global objectives,\n * even if the values match the current defaults (e.g., satisfiedStatus = false, normalizedMeasure = 0)\n * Note: Callers must separately set satisfiedStatusKnown based on CMI data availability.\n * @param satisfiedStatus - The satisfied status from CMI\n * @param normalizedMeasure - The normalized measure from CMI\n * @param measureStatus - Whether measure is valid\n */\n public initializeFromCMI(\n satisfiedStatus: boolean,\n normalizedMeasure: number,\n measureStatus: boolean\n ): void {\n this._satisfiedStatus = satisfiedStatus;\n this._satisfiedStatusDirty = true;\n this._normalizedMeasure = normalizedMeasure;\n this._normalizedMeasureDirty = true;\n this._measureStatus = measureStatus;\n }\n\n resetState(): void {\n this._satisfiedStatus = false;\n this._satisfiedStatusKnown = false;\n this._measureStatus = false;\n this._normalizedMeasure = 0;\n this._progressMeasure = 0;\n this._progressMeasureStatus = false;\n this._completionStatus = CompletionStatus.UNKNOWN;\n this._progressStatus = false;\n this.clearAllDirty();\n }\n\n updateFromActivity(activity: Activity): void {\n if (this._satisfiedStatus !== activity.objectiveSatisfiedStatus) {\n this._satisfiedStatus = activity.objectiveSatisfiedStatus;\n this._satisfiedStatusDirty = true;\n }\n this._satisfiedStatusKnown = activity.objectiveSatisfiedStatusKnown;\n this._measureStatus = activity.objectiveMeasureStatus;\n if (this._normalizedMeasure !== activity.objectiveNormalizedMeasure) {\n this._normalizedMeasure = activity.objectiveNormalizedMeasure;\n this._normalizedMeasureDirty = true;\n }\n if (this._progressMeasure !== activity.progressMeasure) {\n this._progressMeasure = activity.progressMeasure;\n this._progressMeasureDirty = true;\n }\n this._progressMeasureStatus = activity.progressMeasureStatus;\n if (this._completionStatus !== activity.completionStatus) {\n this._completionStatus = activity.completionStatus as CompletionStatus;\n this._completionStatusDirty = true;\n }\n }\n\n applyToActivity(activity: Activity): void {\n if (!this._isPrimary) {\n return;\n }\n\n activity.setPrimaryObjectiveState(\n this._satisfiedStatus,\n this._measureStatus,\n this._normalizedMeasure,\n this._progressMeasure,\n this._progressMeasureStatus,\n this._completionStatus\n );\n }\n}\n\n/**\n * Class representing a single activity in the SCORM 2004 activity tree\n */\nexport class Activity extends BaseCMI {\n private _id: string = \"\";\n private _title: string = \"\";\n private _children: Activity[] = [];\n private _parent: Activity | null = null;\n private _isVisible: boolean = true;\n private _isActive: boolean = false;\n private _isSuspended: boolean = false;\n private _isCompleted: boolean = false;\n private _completionStatus: CompletionStatus = CompletionStatus.UNKNOWN;\n private _successStatus: SuccessStatus = SuccessStatus.UNKNOWN;\n private _attemptCount: number = 0;\n private _attemptCompletionAmount: number = 0;\n private _attemptAbsoluteDuration: string = \"PT0H0M0S\";\n private _attemptExperiencedDuration: string = \"PT0H0M0S\";\n private _activityAbsoluteDuration: string = \"PT0H0M0S\";\n private _activityExperiencedDuration: string = \"PT0H0M0S\";\n\n // Duration tracking fields (separate from limits) - actual calculated values\n private _attemptAbsoluteDurationValue: string = \"PT0H0M0S\";\n private _attemptExperiencedDurationValue: string = \"PT0H0M0S\";\n private _activityAbsoluteDurationValue: string = \"PT0H0M0S\";\n private _activityExperiencedDurationValue: string = \"PT0H0M0S\";\n\n // Timestamp tracking for duration calculation\n private _activityStartTimestampUtc: string | null = null;\n private _attemptStartTimestampUtc: string | null = null;\n private _activityEndedDate: Date | null = null;\n\n private _objectiveSatisfiedStatus: boolean = false;\n private _objectiveSatisfiedStatusKnown: boolean = false;\n private _objectiveMeasureStatus: boolean = false;\n private _objectiveNormalizedMeasure: number = 0;\n private _scaledPassingScore: number = 0.7; // Default passing score\n\n // Dirty flags for tracking which activity-level objective properties have been modified locally\n private _objectiveSatisfiedStatusDirty: boolean = false;\n private _objectiveNormalizedMeasureDirty: boolean = false;\n private _objectiveMeasureStatusDirty: boolean = false;\n private _progressMeasure: number = 0;\n private _progressMeasureStatus: boolean = false;\n private _location: string = \"\";\n private _attemptAbsoluteStartTime: string = \"\";\n private _learnerPrefs: any = null;\n private _activityAttemptActive: boolean = false;\n private _isHiddenFromChoice: boolean = false;\n private _isAvailable: boolean = true;\n private _hideLmsUi: HideLmsUiItem[] = [];\n private _auxiliaryResources: AuxiliaryResource[] = [];\n private _attemptLimit: number | null = null;\n private _attemptAbsoluteDurationLimit: string | null = null;\n private _activityAbsoluteDurationLimit: string | null = null;\n private _timeLimitAction: string | null = null;\n private _timeLimitDuration: string | null = null;\n private _beginTimeLimit: string | null = null;\n private _endTimeLimit: string | null = null;\n private _launchData: string = \"\";\n private _credit: string = \"credit\";\n private _maxTimeAllowed: string = \"\";\n private _completionThreshold: string = \"\";\n private _sequencingControls: SequencingControls;\n private _sequencingRules: SequencingRules;\n private _rollupRules: RollupRules;\n private _processedChildren: Activity[] | null = null;\n private _isNewAttempt: boolean = false;\n private _primaryObjective: ActivityObjective | null = null;\n private _objectives: ActivityObjective[] = [];\n private _rollupConsiderations: RollupConsiderationsConfig = {\n requiredForSatisfied: \"always\",\n requiredForNotSatisfied: \"always\",\n requiredForCompleted: \"always\",\n requiredForIncomplete: \"always\",\n measureSatisfactionIfActive: true\n };\n // Individual rollup consideration properties for this activity (RB.1.4.2)\n // These determine when THIS activity is included in parent rollup calculations\n private _requiredForSatisfied: RollupConsiderationRequirement = \"always\";\n private _requiredForNotSatisfied: RollupConsiderationRequirement = \"always\";\n private _requiredForCompleted: RollupConsiderationRequirement = \"always\";\n private _requiredForIncomplete: RollupConsiderationRequirement = \"always\";\n private _wasSkipped: boolean = false;\n private _attemptProgressStatus: boolean = false;\n private _wasAutoCompleted: boolean = false;\n private _wasAutoSatisfied: boolean = false;\n private _completedByMeasure: boolean = false;\n private _minProgressMeasure: number = 1.0;\n private _progressWeight: number = 1.0;\n private _attemptCompletionAmountStatus: boolean = false;\n\n /**\n * Constructor for Activity\n * @param {string} id - The unique identifier for this activity\n * @param {string} title - The title of this activity\n */\n constructor(id: string = \"\", title: string = \"\") {\n super(\"activity\");\n this._id = id;\n this._title = title;\n this._sequencingControls = new SequencingControls();\n this._sequencingRules = new SequencingRules();\n this._rollupRules = new RollupRules();\n this._primaryObjective = null;\n this._objectives = [];\n }\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n // Initialize children\n for (const child of this._children) {\n child.initialize();\n }\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._isActive = false;\n this._isSuspended = false;\n this._isCompleted = false;\n this._completionStatus = CompletionStatus.UNKNOWN;\n this._successStatus = SuccessStatus.UNKNOWN;\n this._attemptCount = 0;\n this._attemptCompletionAmount = 0;\n this._attemptAbsoluteDuration = \"PT0H0M0S\";\n this._attemptExperiencedDuration = \"PT0H0M0S\";\n this._activityAbsoluteDuration = \"PT0H0M0S\";\n this._activityExperiencedDuration = \"PT0H0M0S\";\n this._attemptAbsoluteDurationValue = \"PT0H0M0S\";\n this._attemptExperiencedDurationValue = \"PT0H0M0S\";\n this._activityAbsoluteDurationValue = \"PT0H0M0S\";\n this._activityExperiencedDurationValue = \"PT0H0M0S\";\n this._activityStartTimestampUtc = null;\n this._attemptStartTimestampUtc = null;\n this._activityEndedDate = null;\n this._objectiveSatisfiedStatus = false;\n this._objectiveSatisfiedStatusKnown = false;\n this._objectiveMeasureStatus = false;\n this._objectiveNormalizedMeasure = 0;\n this._progressMeasure = 0;\n this._progressMeasureStatus = false;\n this._location = \"\";\n this._attemptAbsoluteStartTime = \"\";\n this._learnerPrefs = null;\n this._activityAttemptActive = false;\n\n if (this._primaryObjective) {\n this._primaryObjective.resetState();\n this._primaryObjective.updateFromActivity(this);\n }\n\n for (const objective of this._objectives) {\n objective.resetState();\n }\n\n // Reset children\n for (const child of this._children) {\n child.reset();\n }\n\n this._wasSkipped = false;\n this._attemptProgressStatus = false;\n this._wasAutoCompleted = false;\n this._wasAutoSatisfied = false;\n this._completedByMeasure = false;\n this._minProgressMeasure = 1.0;\n this._progressWeight = 1.0;\n this._attemptCompletionAmountStatus = false;\n this.clearAllObjectiveDirty();\n }\n\n /**\n * Getter for id\n * @return {string}\n */\n get id(): string {\n return this._id;\n }\n\n /**\n * Setter for id\n * @param {string} id\n */\n set id(id: string) {\n if (check2004ValidFormat(this._cmi_element + \".id\", id, scorm2004_regex.CMILongIdentifier)) {\n this._id = id;\n }\n }\n\n /**\n * Getter for title\n * @return {string}\n */\n get title(): string {\n return this._title;\n }\n\n /**\n * Setter for title\n * @param {string} title\n */\n set title(title: string) {\n if (\n check2004ValidFormat(this._cmi_element + \".title\", title, scorm2004_regex.CMILangString250)\n ) {\n this._title = title;\n }\n }\n\n /**\n * Getter for children\n * @return {Activity[]}\n */\n get children(): Activity[] {\n return this._children;\n }\n\n /**\n * Add a child activity to this activity\n * @param {Activity} child - The child activity to add\n */\n addChild(child: Activity): void {\n // noinspection SuspiciousTypeOfGuard\n if (!(child instanceof Activity)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".children\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n child._parent = this;\n this._children.push(child);\n }\n\n /**\n * Reorder child activities based on provided identifier order\n * @param {string[]} order - Ordered list of child activity IDs\n */\n setChildOrder(order: string[]): void {\n if (order.length === 0) {\n return;\n }\n\n const childMap = new Map(this._children.map((child) => [child.id, child] as const));\n const reordered: Activity[] = [];\n\n for (const id of order) {\n const child = childMap.get(id);\n if (child) {\n reordered.push(child);\n childMap.delete(id);\n }\n }\n\n if (childMap.size > 0) {\n for (const child of this._children) {\n if (childMap.has(child.id)) {\n reordered.push(child);\n childMap.delete(child.id);\n }\n }\n }\n\n if (reordered.length === this._children.length) {\n this._children.splice(0, this._children.length, ...reordered);\n }\n }\n\n /**\n * Remove a child activity from this activity\n * @param {Activity} child - The child activity to remove\n * @return {boolean} - True if the child was removed, false otherwise\n */\n removeChild(child: Activity): boolean {\n const index = this._children.indexOf(child);\n if (index !== -1) {\n this._children.splice(index, 1);\n child._parent = null;\n return true;\n }\n return false;\n }\n\n /**\n * Getter for parent\n * @return {Activity | null}\n */\n get parent(): Activity | null {\n return this._parent;\n }\n\n /**\n * Getter for isVisible\n * @return {boolean}\n */\n get isVisible(): boolean {\n return this._isVisible;\n }\n\n /**\n * Setter for isVisible\n * @param {boolean} isVisible\n */\n set isVisible(isVisible: boolean) {\n this._isVisible = isVisible;\n }\n\n /**\n * Getter for isActive\n * @return {boolean}\n */\n get isActive(): boolean {\n return this._isActive;\n }\n\n /**\n * Setter for isActive\n * @param {boolean} isActive\n */\n set isActive(isActive: boolean) {\n this._isActive = isActive;\n }\n\n /**\n * Getter for isSuspended\n * @return {boolean}\n */\n get isSuspended(): boolean {\n return this._isSuspended;\n }\n\n /**\n * Setter for isSuspended\n * @param {boolean} isSuspended\n */\n set isSuspended(isSuspended: boolean) {\n this._isSuspended = isSuspended;\n }\n\n /**\n * Getter for isCompleted\n * @return {boolean}\n */\n get isCompleted(): boolean {\n return this._isCompleted;\n }\n\n /**\n * Setter for isCompleted\n * @param {boolean} isCompleted\n */\n set isCompleted(isCompleted: boolean) {\n this._isCompleted = isCompleted;\n // Update completion status based on boolean value\n if (isCompleted) {\n this._completionStatus = CompletionStatus.COMPLETED;\n } else {\n this._completionStatus = CompletionStatus.INCOMPLETE;\n }\n }\n\n /**\n * Getter for completionStatus\n * @return {CompletionStatus}\n */\n get completionStatus(): CompletionStatus {\n return this._completionStatus;\n }\n\n /**\n * Setter for completionStatus\n * @param {CompletionStatus} completionStatus\n */\n set completionStatus(completionStatus: CompletionStatus) {\n this._completionStatus = completionStatus;\n this._isCompleted = completionStatus === CompletionStatus.COMPLETED;\n this.updatePrimaryObjectiveFromActivity();\n }\n\n /**\n * Getter for successStatus\n * @return {SuccessStatus}\n */\n get successStatus(): SuccessStatus {\n return this._successStatus;\n }\n\n /**\n * Setter for successStatus\n * @param {SuccessStatus} successStatus\n */\n set successStatus(successStatus: SuccessStatus) {\n this._successStatus = successStatus;\n }\n\n /**\n * Getter for attemptCount\n * @return {number}\n */\n get attemptCount(): number {\n return this._attemptCount;\n }\n\n /**\n * Setter for attemptCount\n * @param {number} value\n */\n set attemptCount(value: number) {\n this._attemptCount = value;\n }\n\n /**\n * Getter for attemptCompletionAmount\n * @return {number}\n */\n get attemptCompletionAmount(): number {\n return this._attemptCompletionAmount;\n }\n\n /**\n * Setter for attemptCompletionAmount\n * @param {number} value\n */\n set attemptCompletionAmount(value: number) {\n this._attemptCompletionAmount = value;\n }\n\n /**\n * Increment the attempt count\n */\n incrementAttemptCount(): void {\n this._attemptCount++;\n this._isNewAttempt = true;\n // Reset processed children on new attempt if needed\n const controls = this._sequencingControls;\n if (\n controls.selectionTiming === \"onEachNewAttempt\" ||\n controls.randomizationTiming === \"onEachNewAttempt\"\n ) {\n this._processedChildren = null;\n }\n }\n\n /**\n * Getter for objectiveSatisfiedStatus\n * @return {boolean}\n */\n get objectiveSatisfiedStatus(): boolean {\n return this._objectiveSatisfiedStatus;\n }\n\n /**\n * Setter for objectiveSatisfiedStatus\n * @param {boolean} objectiveSatisfiedStatus\n */\n set objectiveSatisfiedStatus(objectiveSatisfiedStatus: boolean) {\n if (this._objectiveSatisfiedStatus !== objectiveSatisfiedStatus) {\n this._objectiveSatisfiedStatus = objectiveSatisfiedStatus;\n this._objectiveSatisfiedStatusDirty = true;\n }\n this._objectiveSatisfiedStatusKnown = true; // Mark as known when explicitly set\n // Update success status based on objective satisfaction\n if (objectiveSatisfiedStatus) {\n this._successStatus = SuccessStatus.PASSED;\n } else {\n this._successStatus = SuccessStatus.FAILED;\n }\n this.updatePrimaryObjectiveFromActivity();\n }\n\n /**\n * Getter for objectiveSatisfiedStatusKnown\n * Indicates whether the objective satisfied status has been explicitly set\n * @return {boolean}\n */\n get objectiveSatisfiedStatusKnown(): boolean {\n return this._objectiveSatisfiedStatusKnown;\n }\n\n /**\n * Setter for objectiveSatisfiedStatusKnown\n * @param {boolean} value\n */\n set objectiveSatisfiedStatusKnown(value: boolean) {\n this._objectiveSatisfiedStatusKnown = value;\n }\n\n /**\n * Getter for objectiveMeasureStatus\n * @return {boolean}\n */\n get objectiveMeasureStatus(): boolean {\n return this._objectiveMeasureStatus;\n }\n\n /**\n * Setter for objectiveMeasureStatus\n * @param {boolean} objectiveMeasureStatus\n */\n set objectiveMeasureStatus(objectiveMeasureStatus: boolean) {\n if (this._objectiveMeasureStatus !== objectiveMeasureStatus) {\n this._objectiveMeasureStatus = objectiveMeasureStatus;\n this._objectiveMeasureStatusDirty = true;\n }\n this.updatePrimaryObjectiveFromActivity();\n }\n\n /**\n * Getter for objectiveNormalizedMeasure\n * @return {number}\n */\n get objectiveNormalizedMeasure(): number {\n return this._objectiveNormalizedMeasure;\n }\n\n /**\n * Setter for objectiveNormalizedMeasure\n * @param {number} objectiveNormalizedMeasure\n */\n set objectiveNormalizedMeasure(objectiveNormalizedMeasure: number) {\n if (this._objectiveNormalizedMeasure !== objectiveNormalizedMeasure) {\n this._objectiveNormalizedMeasure = objectiveNormalizedMeasure;\n this._objectiveNormalizedMeasureDirty = true;\n }\n this.updatePrimaryObjectiveFromActivity();\n }\n\n /**\n * Getter for scaledPassingScore\n * @return {number}\n */\n get scaledPassingScore(): number {\n return this._scaledPassingScore;\n }\n\n /**\n * Setter for scaledPassingScore\n * @param {number} scaledPassingScore\n */\n set scaledPassingScore(scaledPassingScore: number) {\n if (scaledPassingScore >= -1 && scaledPassingScore <= 1) {\n this._scaledPassingScore = scaledPassingScore;\n }\n }\n\n /**\n * Getter for progressMeasure\n * @return {number}\n */\n get progressMeasure(): number {\n return this._progressMeasure;\n }\n\n /**\n * Setter for progressMeasure\n * @param {number} progressMeasure\n */\n set progressMeasure(progressMeasure: number) {\n this._progressMeasure = progressMeasure;\n this.updatePrimaryObjectiveFromActivity();\n }\n\n /**\n * Getter for progressMeasureStatus\n * @return {boolean}\n */\n get progressMeasureStatus(): boolean {\n return this._progressMeasureStatus;\n }\n\n /**\n * Setter for progressMeasureStatus\n * @param {boolean} progressMeasureStatus\n */\n set progressMeasureStatus(progressMeasureStatus: boolean) {\n this._progressMeasureStatus = progressMeasureStatus;\n this.updatePrimaryObjectiveFromActivity();\n }\n\n /**\n * Getter for location\n * @return {string}\n */\n get location(): string {\n return this._location;\n }\n\n /**\n * Setter for location\n * @param {string} location\n */\n set location(location: string) {\n this._location = location;\n }\n\n /**\n * Getter for attemptAbsoluteStartTime\n * @return {string}\n */\n get attemptAbsoluteStartTime(): string {\n return this._attemptAbsoluteStartTime;\n }\n\n /**\n * Setter for attemptAbsoluteStartTime\n * @param {string} attemptAbsoluteStartTime\n */\n set attemptAbsoluteStartTime(attemptAbsoluteStartTime: string) {\n this._attemptAbsoluteStartTime = attemptAbsoluteStartTime;\n }\n\n /**\n * Getter for learnerPrefs\n * @return {any}\n */\n get learnerPrefs(): any {\n return this._learnerPrefs;\n }\n\n /**\n * Setter for learnerPrefs\n * @param {any} learnerPrefs\n */\n set learnerPrefs(learnerPrefs: any) {\n this._learnerPrefs = learnerPrefs;\n }\n\n /**\n * Getter for activityAttemptActive\n * @return {boolean}\n */\n get activityAttemptActive(): boolean {\n return this._activityAttemptActive;\n }\n\n /**\n * Setter for activityAttemptActive\n * @param {boolean} activityAttemptActive\n */\n set activityAttemptActive(activityAttemptActive: boolean) {\n this._activityAttemptActive = activityAttemptActive;\n }\n\n /**\n * Getter for isHiddenFromChoice\n * @return {boolean}\n */\n get isHiddenFromChoice(): boolean {\n return this._isHiddenFromChoice;\n }\n\n /**\n * Setter for isHiddenFromChoice\n * @param {boolean} isHiddenFromChoice\n */\n set isHiddenFromChoice(isHiddenFromChoice: boolean) {\n this._isHiddenFromChoice = isHiddenFromChoice;\n }\n\n /**\n * Getter for isAvailable\n * @return {boolean}\n */\n get isAvailable(): boolean {\n return this._isAvailable;\n }\n\n /**\n * Setter for isAvailable\n * @param {boolean} isAvailable\n */\n set isAvailable(isAvailable: boolean) {\n this._isAvailable = isAvailable;\n }\n\n /**\n * Getter for attemptLimit\n * @return {number | null}\n */\n get attemptLimit(): number | null {\n return this._attemptLimit;\n }\n\n /**\n * Setter for attemptLimit\n * @param {number | null} attemptLimit\n */\n set attemptLimit(attemptLimit: number | null) {\n this._attemptLimit = attemptLimit;\n }\n\n /**\n * Check if attempt limit has been exceeded\n * @return {boolean}\n */\n hasAttemptLimitExceeded(): boolean {\n if (this._attemptLimit === null) {\n return false;\n }\n return this._attemptCount >= this._attemptLimit;\n }\n\n /**\n * Getter for timeLimitDuration\n * @return {string | null}\n */\n get timeLimitDuration(): string | null {\n return this._timeLimitDuration;\n }\n\n /**\n * Setter for timeLimitDuration\n * @param {string | null} timeLimitDuration\n */\n set timeLimitDuration(timeLimitDuration: string | null) {\n this._timeLimitDuration = timeLimitDuration;\n }\n\n /**\n * Getter for timeLimitAction\n * @return {string | null}\n */\n get timeLimitAction(): string | null {\n return this._timeLimitAction;\n }\n\n /**\n * Setter for timeLimitAction\n * @param {string | null} timeLimitAction\n */\n set timeLimitAction(timeLimitAction: string | null) {\n this._timeLimitAction = timeLimitAction;\n }\n\n /**\n * Getter for beginTimeLimit\n * @return {string | null}\n */\n get beginTimeLimit(): string | null {\n return this._beginTimeLimit;\n }\n\n /**\n * Setter for beginTimeLimit\n * @param {string | null} beginTimeLimit\n */\n set beginTimeLimit(beginTimeLimit: string | null) {\n this._beginTimeLimit = beginTimeLimit;\n }\n\n /**\n * Getter for endTimeLimit\n * @return {string | null}\n */\n get endTimeLimit(): string | null {\n return this._endTimeLimit;\n }\n\n /**\n * Setter for endTimeLimit\n * @param {string | null} endTimeLimit\n */\n set endTimeLimit(endTimeLimit: string | null) {\n this._endTimeLimit = endTimeLimit;\n }\n\n\n /**\n * Getter for attemptAbsoluteDurationLimit\n * @return {string | null}\n */\n get attemptAbsoluteDurationLimit(): string | null {\n return this._attemptAbsoluteDurationLimit;\n }\n\n /**\n * Setter for attemptAbsoluteDurationLimit\n * @param {string | null} attemptAbsoluteDurationLimit\n */\n set attemptAbsoluteDurationLimit(attemptAbsoluteDurationLimit: string | null) {\n if (attemptAbsoluteDurationLimit !== null) {\n if (!validateISO8601Duration(attemptAbsoluteDurationLimit, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".attemptAbsoluteDurationLimit\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n }\n this._attemptAbsoluteDurationLimit = attemptAbsoluteDurationLimit;\n }\n\n /**\n * Getter for attemptExperiencedDuration\n * @return {string}\n */\n get attemptExperiencedDuration(): string {\n return this._attemptExperiencedDuration;\n }\n\n /**\n * Setter for attemptExperiencedDuration\n * @param {string} attemptExperiencedDuration\n */\n set attemptExperiencedDuration(attemptExperiencedDuration: string) {\n if (!validateISO8601Duration(attemptExperiencedDuration, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".attemptExperiencedDuration\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._attemptExperiencedDuration = attemptExperiencedDuration;\n }\n\n /**\n * Getter for activityAbsoluteDurationLimit\n * @return {string | null}\n */\n get activityAbsoluteDurationLimit(): string | null {\n return this._activityAbsoluteDurationLimit;\n }\n\n /**\n * Setter for activityAbsoluteDurationLimit\n * @param {string | null} activityAbsoluteDurationLimit\n */\n set activityAbsoluteDurationLimit(activityAbsoluteDurationLimit: string | null) {\n if (activityAbsoluteDurationLimit !== null) {\n if (!validateISO8601Duration(activityAbsoluteDurationLimit, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".activityAbsoluteDurationLimit\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n }\n this._activityAbsoluteDurationLimit = activityAbsoluteDurationLimit;\n }\n\n /**\n * Getter for activityExperiencedDuration\n * @return {string}\n */\n get activityExperiencedDuration(): string {\n return this._activityExperiencedDuration;\n }\n\n /**\n * Setter for activityExperiencedDuration\n * @param {string} activityExperiencedDuration\n */\n set activityExperiencedDuration(activityExperiencedDuration: string) {\n if (!validateISO8601Duration(activityExperiencedDuration, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".activityExperiencedDuration\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._activityExperiencedDuration = activityExperiencedDuration;\n }\n\n /**\n * Getter for attemptAbsoluteDuration (alias for limit)\n * @return {string}\n */\n get attemptAbsoluteDuration(): string {\n return this._attemptAbsoluteDurationLimit || \"PT0H0M0S\";\n }\n\n /**\n * Setter for attemptAbsoluteDuration\n * @param {string} duration\n */\n set attemptAbsoluteDuration(duration: string) {\n this._attemptAbsoluteDurationLimit = duration;\n }\n\n /**\n * Getter for activityAbsoluteDuration (alias for limit)\n * @return {string}\n */\n get activityAbsoluteDuration(): string {\n return this._activityAbsoluteDurationLimit || \"PT0H0M0S\";\n }\n\n /**\n * Setter for activityAbsoluteDuration\n * @param {string} duration\n */\n set activityAbsoluteDuration(duration: string) {\n this._activityAbsoluteDurationLimit = duration;\n }\n\n /**\n * Getter for attemptAbsoluteDurationValue (actual calculated duration)\n * @return {string}\n */\n get attemptAbsoluteDurationValue(): string {\n return this._attemptAbsoluteDurationValue;\n }\n\n /**\n * Setter for attemptAbsoluteDurationValue\n * @param {string} duration\n */\n set attemptAbsoluteDurationValue(duration: string) {\n if (!validateISO8601Duration(duration, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".attemptAbsoluteDurationValue\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._attemptAbsoluteDurationValue = duration;\n }\n\n /**\n * Getter for attemptExperiencedDurationValue (actual calculated duration)\n * @return {string}\n */\n get attemptExperiencedDurationValue(): string {\n return this._attemptExperiencedDurationValue;\n }\n\n /**\n * Setter for attemptExperiencedDurationValue\n * @param {string} duration\n */\n set attemptExperiencedDurationValue(duration: string) {\n if (!validateISO8601Duration(duration, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".attemptExperiencedDurationValue\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._attemptExperiencedDurationValue = duration;\n }\n\n /**\n * Getter for activityAbsoluteDurationValue (actual calculated duration)\n * @return {string}\n */\n get activityAbsoluteDurationValue(): string {\n return this._activityAbsoluteDurationValue;\n }\n\n /**\n * Setter for activityAbsoluteDurationValue\n * @param {string} duration\n */\n set activityAbsoluteDurationValue(duration: string) {\n if (!validateISO8601Duration(duration, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".activityAbsoluteDurationValue\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._activityAbsoluteDurationValue = duration;\n }\n\n /**\n * Getter for activityExperiencedDurationValue (actual calculated duration)\n * @return {string}\n */\n get activityExperiencedDurationValue(): string {\n return this._activityExperiencedDurationValue;\n }\n\n /**\n * Setter for activityExperiencedDurationValue\n * @param {string} duration\n */\n set activityExperiencedDurationValue(duration: string) {\n if (!validateISO8601Duration(duration, scorm2004_regex.CMITimespan)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".activityExperiencedDurationValue\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._activityExperiencedDurationValue = duration;\n }\n\n /**\n * Getter for activityStartTimestampUtc\n * @return {string | null}\n */\n get activityStartTimestampUtc(): string | null {\n return this._activityStartTimestampUtc;\n }\n\n /**\n * Setter for activityStartTimestampUtc\n * @param {string | null} timestamp\n */\n set activityStartTimestampUtc(timestamp: string | null) {\n this._activityStartTimestampUtc = timestamp;\n }\n\n /**\n * Getter for attemptStartTimestampUtc\n * @return {string | null}\n */\n get attemptStartTimestampUtc(): string | null {\n return this._attemptStartTimestampUtc;\n }\n\n /**\n * Setter for attemptStartTimestampUtc\n * @param {string | null} timestamp\n */\n set attemptStartTimestampUtc(timestamp: string | null) {\n this._attemptStartTimestampUtc = timestamp;\n }\n\n /**\n * Getter for activityEndedDate\n * @return {Date | null}\n */\n get activityEndedDate(): Date | null {\n return this._activityEndedDate;\n }\n\n /**\n * Setter for activityEndedDate\n * @param {Date | null} date\n */\n set activityEndedDate(date: Date | null) {\n this._activityEndedDate = date;\n }\n\n\n /**\n * Getter for sequencingControls\n * @return {SequencingControls}\n */\n get sequencingControls(): SequencingControls {\n return this._sequencingControls;\n }\n\n /**\n * Setter for sequencingControls\n * @param {SequencingControls} sequencingControls\n */\n set sequencingControls(sequencingControls: SequencingControls) {\n this._sequencingControls = sequencingControls;\n }\n\n /**\n * Getter for sequencingRules\n * @return {SequencingRules}\n */\n get sequencingRules(): SequencingRules {\n return this._sequencingRules;\n }\n\n /**\n * Setter for sequencingRules\n * @param {SequencingRules} sequencingRules\n */\n set sequencingRules(sequencingRules: SequencingRules) {\n this._sequencingRules = sequencingRules;\n }\n\n /**\n * Getter for rollupRules\n * @return {RollupRules}\n */\n get rollupRules(): RollupRules {\n return this._rollupRules;\n }\n\n /**\n * Setter for rollupRules\n * @param {RollupRules} rollupRules\n */\n set rollupRules(rollupRules: RollupRules) {\n this._rollupRules = rollupRules;\n }\n\n get rollupConsiderations(): RollupConsiderationsConfig {\n return { ...this._rollupConsiderations };\n }\n\n set rollupConsiderations(config: RollupConsiderationsConfig) {\n this._rollupConsiderations = { ...config };\n }\n\n applyRollupConsiderations(settings: Partial<RollupConsiderationsConfig>): void {\n this._rollupConsiderations = {\n ...this._rollupConsiderations,\n ...settings\n };\n }\n\n /**\n * Individual rollup consideration getters/setters (RB.1.4.2)\n * These control when THIS activity is included in parent rollup\n */\n get requiredForSatisfied(): RollupConsiderationRequirement {\n return this._requiredForSatisfied;\n }\n\n set requiredForSatisfied(value: RollupConsiderationRequirement) {\n this._requiredForSatisfied = value;\n }\n\n get requiredForNotSatisfied(): RollupConsiderationRequirement {\n return this._requiredForNotSatisfied;\n }\n\n set requiredForNotSatisfied(value: RollupConsiderationRequirement) {\n this._requiredForNotSatisfied = value;\n }\n\n get requiredForCompleted(): RollupConsiderationRequirement {\n return this._requiredForCompleted;\n }\n\n set requiredForCompleted(value: RollupConsiderationRequirement) {\n this._requiredForCompleted = value;\n }\n\n get requiredForIncomplete(): RollupConsiderationRequirement {\n return this._requiredForIncomplete;\n }\n\n set requiredForIncomplete(value: RollupConsiderationRequirement) {\n this._requiredForIncomplete = value;\n }\n\n get wasSkipped(): boolean {\n return this._wasSkipped;\n }\n\n set wasSkipped(value: boolean) {\n this._wasSkipped = value;\n }\n\n get attemptProgressStatus(): boolean {\n return this._attemptProgressStatus;\n }\n\n set attemptProgressStatus(value: boolean) {\n this._attemptProgressStatus = value;\n }\n\n get wasAutoCompleted(): boolean {\n return this._wasAutoCompleted;\n }\n\n set wasAutoCompleted(value: boolean) {\n this._wasAutoCompleted = value;\n }\n\n get wasAutoSatisfied(): boolean {\n return this._wasAutoSatisfied;\n }\n\n set wasAutoSatisfied(value: boolean) {\n this._wasAutoSatisfied = value;\n }\n\n get completedByMeasure(): boolean {\n return this._completedByMeasure;\n }\n\n set completedByMeasure(value: boolean) {\n this._completedByMeasure = value;\n }\n\n get minProgressMeasure(): number {\n return this._minProgressMeasure;\n }\n\n set minProgressMeasure(value: number) {\n if (value < 0.0 || value > 1.0) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".minProgressMeasure\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._minProgressMeasure = value;\n }\n\n get progressWeight(): number {\n return this._progressWeight;\n }\n\n set progressWeight(value: number) {\n if (value < 0.0) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".progressWeight\",\n scorm2004_errors.TYPE_MISMATCH as number\n );\n }\n this._progressWeight = value;\n }\n\n get attemptCompletionAmountStatus(): boolean {\n return this._attemptCompletionAmountStatus;\n }\n\n set attemptCompletionAmountStatus(value: boolean) {\n this._attemptCompletionAmountStatus = value;\n }\n\n /**\n * Getter for primary objective\n * @return {ActivityObjective | null}\n */\n get primaryObjective(): ActivityObjective | null {\n return this._primaryObjective;\n }\n\n /**\n * Setter for primary objective\n * @param {ActivityObjective | null} objective\n */\n set primaryObjective(objective: ActivityObjective | null) {\n this._primaryObjective = objective;\n if (this._primaryObjective) {\n this._primaryObjective.isPrimary = true;\n if (this._primaryObjective.minNormalizedMeasure !== null) {\n this._scaledPassingScore = this._primaryObjective.minNormalizedMeasure ?? this._scaledPassingScore;\n }\n this._primaryObjective.updateFromActivity(this);\n }\n this.syncPrimaryObjectiveCollection();\n }\n\n /**\n * Get additional objectives (excludes primary objective)\n * @return {ActivityObjective[]}\n */\n get objectives(): ActivityObjective[] {\n return this._objectives.filter((obj) => obj.id !== this._primaryObjective?.id);\n }\n\n /**\n * Replace objectives collection\n * @param {ActivityObjective[]} objectives\n */\n set objectives(objectives: ActivityObjective[]) {\n this._objectives = [...objectives];\n this.syncPrimaryObjectiveCollection();\n }\n\n /**\n * Add an objective\n * @param {ActivityObjective} objective\n */\n addObjective(objective: ActivityObjective): void {\n if (!this._objectives.find((obj) => obj.id === objective.id)) {\n this._objectives.push(objective);\n }\n }\n\n /**\n * Ensure the primary objective is represented within the objectives collection.\n */\n private syncPrimaryObjectiveCollection(): void {\n if (!this._primaryObjective) {\n this._objectives = this._objectives.filter((objective) => !objective.isPrimary);\n return;\n }\n\n const existingIndex = this._objectives.findIndex(\n (objective) => objective.id === this._primaryObjective?.id\n );\n\n if (existingIndex >= 0) {\n this._objectives[existingIndex] = this._primaryObjective;\n return;\n }\n\n this._objectives = [this._primaryObjective, ...this._objectives];\n }\n\n /**\n * Get objective by ID\n * @param {string} objectiveId\n * @return {{ objective: ActivityObjective, isPrimary: boolean } | null}\n */\n getObjectiveById(objectiveId: string): {\n objective: ActivityObjective;\n isPrimary: boolean\n } | null {\n if (this._primaryObjective?.id === objectiveId) {\n return { objective: this._primaryObjective, isPrimary: true };\n }\n const additional = this._objectives.find((obj) => obj.id === objectiveId);\n if (additional) {\n return { objective: additional, isPrimary: false };\n }\n return null;\n }\n\n /**\n * Get all objectives including primary\n * @return {ActivityObjective[]}\n */\n getAllObjectives(): ActivityObjective[] {\n const objectives: ActivityObjective[] = [];\n if (this._primaryObjective) {\n objectives.push(this._primaryObjective);\n }\n // Filter out the primary objective from _objectives to avoid duplicates\n // since syncPrimaryObjectiveCollection() adds it to _objectives\n const additionalObjectives = this._objectives.filter(\n obj => obj !== this._primaryObjective && obj.id !== this._primaryObjective?.id\n );\n return objectives.concat(additionalObjectives);\n }\n\n private updatePrimaryObjectiveFromActivity(): void {\n if (this._primaryObjective) {\n this._primaryObjective.updateFromActivity(this);\n }\n }\n\n public isObjectiveDirty(property: 'satisfiedStatus' | 'normalizedMeasure' | 'measureStatus'): boolean {\n switch (property) {\n case 'satisfiedStatus': return this._objectiveSatisfiedStatusDirty;\n case 'normalizedMeasure': return this._objectiveNormalizedMeasureDirty;\n case 'measureStatus': return this._objectiveMeasureStatusDirty;\n }\n }\n\n public clearObjectiveDirty(property: 'satisfiedStatus' | 'normalizedMeasure' | 'measureStatus'): void {\n switch (property) {\n case 'satisfiedStatus': this._objectiveSatisfiedStatusDirty = false; break;\n case 'normalizedMeasure': this._objectiveNormalizedMeasureDirty = false; break;\n case 'measureStatus': this._objectiveMeasureStatusDirty = false; break;\n }\n }\n\n public clearAllObjectiveDirty(): void {\n this._objectiveSatisfiedStatusDirty = false;\n this._objectiveNormalizedMeasureDirty = false;\n this._objectiveMeasureStatusDirty = false;\n }\n\n public setPrimaryObjectiveState(\n satisfiedStatus: boolean,\n measureStatus: boolean,\n normalizedMeasure: number,\n progressMeasure: number,\n progressMeasureStatus: boolean,\n completionStatus: CompletionStatus\n ): void {\n if (this._objectiveSatisfiedStatus !== satisfiedStatus) {\n this._objectiveSatisfiedStatus = satisfiedStatus;\n this._objectiveSatisfiedStatusDirty = true;\n }\n this._objectiveSatisfiedStatusKnown = true; // Mark as known when state is explicitly set\n if (this._objectiveMeasureStatus !== measureStatus) {\n this._objectiveMeasureStatus = measureStatus;\n this._objectiveMeasureStatusDirty = true;\n }\n if (this._objectiveNormalizedMeasure !== normalizedMeasure) {\n this._objectiveNormalizedMeasure = normalizedMeasure;\n this._objectiveNormalizedMeasureDirty = true;\n }\n this._progressMeasure = progressMeasure;\n this._progressMeasureStatus = progressMeasureStatus;\n this._completionStatus = completionStatus;\n\n if (this._primaryObjective) {\n this._primaryObjective.satisfiedStatus = satisfiedStatus;\n this._primaryObjective.measureStatus = measureStatus;\n this._primaryObjective.normalizedMeasure = normalizedMeasure;\n this._primaryObjective.progressMeasure = progressMeasure;\n this._primaryObjective.progressMeasureStatus = progressMeasureStatus;\n this._primaryObjective.completionStatus = completionStatus;\n }\n }\n\n public getObjectiveStateSnapshot(): {\n primary: ActivityObjectiveState | null;\n objectives: ActivityObjectiveState[];\n } {\n const primarySnapshot: ActivityObjectiveState | null = this._primaryObjective\n ? {\n id: this._primaryObjective.id,\n satisfiedStatus: this.objectiveSatisfiedStatus,\n measureStatus: this.objectiveMeasureStatus,\n normalizedMeasure: this.objectiveNormalizedMeasure,\n progressMeasure: this.progressMeasure ?? 0,\n progressMeasureStatus: this.progressMeasureStatus,\n progressStatus: this._primaryObjective.progressStatus,\n completionStatus: this.completionStatus as CompletionStatus,\n satisfiedByMeasure: this._primaryObjective.satisfiedByMeasure,\n minNormalizedMeasure: this._primaryObjective.minNormalizedMeasure\n }\n : null;\n\n const additionalSnapshots: ActivityObjectiveState[] = this._objectives.map((objective) => ({\n id: objective.id,\n satisfiedStatus: objective.satisfiedStatus,\n measureStatus: objective.measureStatus,\n normalizedMeasure: objective.normalizedMeasure,\n progressMeasure: objective.progressMeasure,\n progressMeasureStatus: objective.progressMeasureStatus,\n progressStatus: objective.progressStatus,\n completionStatus: objective.completionStatus as CompletionStatus,\n satisfiedByMeasure: objective.satisfiedByMeasure,\n minNormalizedMeasure: objective.minNormalizedMeasure\n }));\n\n return {\n primary: primarySnapshot,\n objectives: additionalSnapshots\n };\n }\n\n public applyObjectiveStateSnapshot(snapshot: {\n primary: ActivityObjectiveState | null;\n objectives: ActivityObjectiveState[];\n }): void {\n if (snapshot.primary) {\n const primary = this.getObjectiveById(snapshot.primary.id);\n if (primary && primary.isPrimary) {\n const state = snapshot.primary;\n primary.objective.satisfiedByMeasure = state.satisfiedByMeasure ?? primary.objective.satisfiedByMeasure;\n primary.objective.minNormalizedMeasure =\n state.minNormalizedMeasure !== undefined ? state.minNormalizedMeasure : primary.objective.minNormalizedMeasure;\n this.setPrimaryObjectiveState(\n state.satisfiedStatus,\n state.measureStatus,\n state.normalizedMeasure,\n state.progressMeasure,\n state.progressMeasureStatus,\n state.completionStatus\n );\n }\n }\n\n for (const state of snapshot.objectives) {\n const match = this.getObjectiveById(state.id);\n if (match && !match.isPrimary) {\n const objective = match.objective;\n objective.satisfiedStatus = state.satisfiedStatus;\n objective.measureStatus = state.measureStatus;\n objective.normalizedMeasure = state.normalizedMeasure;\n objective.progressMeasure = state.progressMeasure;\n objective.progressMeasureStatus = state.progressMeasureStatus;\n objective.completionStatus = state.completionStatus;\n objective.satisfiedByMeasure = state.satisfiedByMeasure ?? objective.satisfiedByMeasure;\n objective.minNormalizedMeasure =\n state.minNormalizedMeasure !== undefined ? state.minNormalizedMeasure : objective.minNormalizedMeasure;\n }\n }\n }\n\n /**\n * Get available children with selection and randomization applied\n * @return {Activity[]}\n */\n getAvailableChildren(): Activity[] {\n // If no children, return empty array\n if (this._children.length === 0) {\n return [];\n }\n\n // If processed children already exist and no new attempt, return them\n if (this._processedChildren !== null) {\n return this._processedChildren;\n }\n\n // If no processing has been done yet, return all children\n // The sequencing process will call applySelectionAndRandomization when needed\n return this._children;\n }\n\n /**\n * Set the processed children (called by SelectionRandomization)\n * @param {Activity[]} processedChildren\n */\n setProcessedChildren(processedChildren: Activity[]): void {\n this._processedChildren = processedChildren;\n }\n\n /**\n * Reset processed children (used when configuration changes)\n */\n resetProcessedChildren(): void {\n this._processedChildren = null;\n }\n\n /**\n * Get whether this is a new attempt\n * @return {boolean}\n */\n get isNewAttempt(): boolean {\n return this._isNewAttempt;\n }\n\n /**\n * Set whether this is a new attempt\n * @param {boolean} isNewAttempt\n */\n set isNewAttempt(isNewAttempt: boolean) {\n this._isNewAttempt = isNewAttempt;\n }\n\n /**\n * Getter for launchData\n * @return {string}\n */\n get launchData(): string {\n return this._launchData;\n }\n\n /**\n * Setter for launchData\n * @param {string} launchData\n */\n set launchData(launchData: string) {\n this._launchData = launchData;\n }\n\n /**\n * Getter for credit\n * @return {string}\n */\n get credit(): string {\n return this._credit;\n }\n\n /**\n * Setter for credit\n * @param {string} credit\n */\n set credit(credit: string) {\n this._credit = credit;\n }\n\n /**\n * Getter for maxTimeAllowed\n * @return {string}\n */\n get maxTimeAllowed(): string {\n return this._maxTimeAllowed;\n }\n\n /**\n * Setter for maxTimeAllowed\n * @param {string} maxTimeAllowed\n */\n set maxTimeAllowed(maxTimeAllowed: string) {\n this._maxTimeAllowed = maxTimeAllowed;\n }\n\n /**\n * Getter for completionThreshold\n * @return {string}\n */\n get completionThreshold(): string {\n return this._completionThreshold;\n }\n\n /**\n * Setter for completionThreshold\n * @param {string} completionThreshold\n */\n set completionThreshold(completionThreshold: string) {\n this._completionThreshold = completionThreshold;\n }\n\n /**\n * Get suspension state for this activity and its descendants\n * Captures all state needed to restore activity tree after suspend/resume\n * @return {object} - Complete suspension state\n */\n getSuspensionState(): object {\n return {\n id: this._id,\n title: this._title,\n isVisible: this._isVisible,\n isActive: this._isActive,\n isSuspended: this._isSuspended,\n isCompleted: this._isCompleted,\n completionStatus: this._completionStatus,\n successStatus: this._successStatus,\n attemptCount: this._attemptCount,\n attemptCompletionAmount: this._attemptCompletionAmount,\n attemptAbsoluteDuration: this._attemptAbsoluteDuration,\n attemptExperiencedDuration: this._attemptExperiencedDuration,\n activityAbsoluteDuration: this._activityAbsoluteDuration,\n activityExperiencedDuration: this._activityExperiencedDuration,\n attemptAbsoluteDurationValue: this._attemptAbsoluteDurationValue,\n attemptExperiencedDurationValue: this._attemptExperiencedDurationValue,\n activityAbsoluteDurationValue: this._activityAbsoluteDurationValue,\n activityExperiencedDurationValue: this._activityExperiencedDurationValue,\n activityStartTimestampUtc: this._activityStartTimestampUtc,\n attemptStartTimestampUtc: this._attemptStartTimestampUtc,\n objectiveSatisfiedStatus: this._objectiveSatisfiedStatus,\n objectiveSatisfiedStatusKnown: this._objectiveSatisfiedStatusKnown,\n objectiveMeasureStatus: this._objectiveMeasureStatus,\n objectiveNormalizedMeasure: this._objectiveNormalizedMeasure,\n scaledPassingScore: this._scaledPassingScore,\n progressMeasure: this._progressMeasure,\n progressMeasureStatus: this._progressMeasureStatus,\n location: this._location,\n attemptAbsoluteStartTime: this._attemptAbsoluteStartTime,\n activityAttemptActive: this._activityAttemptActive,\n isHiddenFromChoice: this._isHiddenFromChoice,\n isAvailable: this._isAvailable,\n rollupConsiderations: { ...this._rollupConsiderations },\n wasSkipped: this._wasSkipped,\n attemptProgressStatus: this._attemptProgressStatus,\n wasAutoCompleted: this._wasAutoCompleted,\n wasAutoSatisfied: this._wasAutoSatisfied,\n completedByMeasure: this._completedByMeasure,\n minProgressMeasure: this._minProgressMeasure,\n progressWeight: this._progressWeight,\n attemptCompletionAmountStatus: this._attemptCompletionAmountStatus,\n // Selection/randomization state preservation\n processedChildren: this._processedChildren ? this._processedChildren.map(c => c.id) : null,\n isNewAttempt: this._isNewAttempt,\n selectionCountStatus: this._sequencingControls.selectionCountStatus,\n reorderChildren: this._sequencingControls.reorderChildren,\n // Objective state preservation\n primaryObjective: this._primaryObjective ? {\n id: this._primaryObjective.id,\n satisfiedStatus: this._primaryObjective.satisfiedStatus,\n measureStatus: this._primaryObjective.measureStatus,\n normalizedMeasure: this._primaryObjective.normalizedMeasure,\n progressMeasure: this._primaryObjective.progressMeasure,\n progressMeasureStatus: this._primaryObjective.progressMeasureStatus,\n completionStatus: this._primaryObjective.completionStatus,\n satisfiedByMeasure: this._primaryObjective.satisfiedByMeasure,\n minNormalizedMeasure: this._primaryObjective.minNormalizedMeasure,\n progressStatus: this._primaryObjective.progressStatus,\n mapInfo: this._primaryObjective.mapInfo\n } : null,\n objectives: this._objectives.map(obj => ({\n id: obj.id,\n satisfiedStatus: obj.satisfiedStatus,\n measureStatus: obj.measureStatus,\n normalizedMeasure: obj.normalizedMeasure,\n progressMeasure: obj.progressMeasure,\n progressMeasureStatus: obj.progressMeasureStatus,\n completionStatus: obj.completionStatus,\n satisfiedByMeasure: obj.satisfiedByMeasure,\n minNormalizedMeasure: obj.minNormalizedMeasure,\n progressStatus: obj.progressStatus,\n mapInfo: obj.mapInfo\n })),\n // Recursively save children state\n children: this._children.map(child => child.getSuspensionState())\n };\n }\n\n /**\n * Restore suspension state for this activity and its descendants\n * Restores all state needed to resume from suspended state\n * @param {any} state - Suspension state to restore\n */\n restoreSuspensionState(state: any): void {\n if (!state) return;\n\n // Restore basic activity state\n this._isVisible = state.isVisible ?? this._isVisible;\n this._isActive = state.isActive ?? this._isActive;\n this._isSuspended = state.isSuspended ?? this._isSuspended;\n this._isCompleted = state.isCompleted ?? this._isCompleted;\n this._completionStatus = state.completionStatus ?? this._completionStatus;\n this._successStatus = state.successStatus ?? this._successStatus;\n this._attemptCount = state.attemptCount ?? this._attemptCount;\n this._attemptCompletionAmount = state.attemptCompletionAmount ?? this._attemptCompletionAmount;\n this._attemptAbsoluteDuration = state.attemptAbsoluteDuration ?? this._attemptAbsoluteDuration;\n this._attemptExperiencedDuration = state.attemptExperiencedDuration ?? this._attemptExperiencedDuration;\n this._activityAbsoluteDuration = state.activityAbsoluteDuration ?? this._activityAbsoluteDuration;\n this._activityExperiencedDuration = state.activityExperiencedDuration ?? this._activityExperiencedDuration;\n this._attemptAbsoluteDurationValue = state.attemptAbsoluteDurationValue ?? this._attemptAbsoluteDurationValue;\n this._attemptExperiencedDurationValue = state.attemptExperiencedDurationValue ?? this._attemptExperiencedDurationValue;\n this._activityAbsoluteDurationValue = state.activityAbsoluteDurationValue ?? this._activityAbsoluteDurationValue;\n this._activityExperiencedDurationValue = state.activityExperiencedDurationValue ?? this._activityExperiencedDurationValue;\n this._activityStartTimestampUtc = state.activityStartTimestampUtc ?? this._activityStartTimestampUtc;\n this._attemptStartTimestampUtc = state.attemptStartTimestampUtc ?? this._attemptStartTimestampUtc;\n\n // Restore tracking data\n this._objectiveSatisfiedStatus = state.objectiveSatisfiedStatus ?? this._objectiveSatisfiedStatus;\n this._objectiveSatisfiedStatusKnown = state.objectiveSatisfiedStatusKnown ?? this._objectiveSatisfiedStatusKnown;\n this._objectiveMeasureStatus = state.objectiveMeasureStatus ?? this._objectiveMeasureStatus;\n this._objectiveNormalizedMeasure = state.objectiveNormalizedMeasure ?? this._objectiveNormalizedMeasure;\n this._scaledPassingScore = state.scaledPassingScore ?? this._scaledPassingScore;\n this._progressMeasure = state.progressMeasure ?? this._progressMeasure;\n this._progressMeasureStatus = state.progressMeasureStatus ?? this._progressMeasureStatus;\n this._location = state.location ?? this._location;\n this._attemptAbsoluteStartTime = state.attemptAbsoluteStartTime ?? this._attemptAbsoluteStartTime;\n this._activityAttemptActive = state.activityAttemptActive ?? this._activityAttemptActive;\n this._isHiddenFromChoice = state.isHiddenFromChoice ?? this._isHiddenFromChoice;\n this._isAvailable = state.isAvailable ?? this._isAvailable;\n\n // Restore rollup considerations\n if (state.rollupConsiderations) {\n this._rollupConsiderations = { ...state.rollupConsiderations };\n }\n\n // Restore other tracking state\n this._wasSkipped = state.wasSkipped ?? this._wasSkipped;\n this._attemptProgressStatus = state.attemptProgressStatus ?? this._attemptProgressStatus;\n this._wasAutoCompleted = state.wasAutoCompleted ?? this._wasAutoCompleted;\n this._wasAutoSatisfied = state.wasAutoSatisfied ?? this._wasAutoSatisfied;\n this._completedByMeasure = state.completedByMeasure ?? this._completedByMeasure;\n this._minProgressMeasure = state.minProgressMeasure ?? this._minProgressMeasure;\n this._progressWeight = state.progressWeight ?? this._progressWeight;\n this._attemptCompletionAmountStatus = state.attemptCompletionAmountStatus ?? this._attemptCompletionAmountStatus;\n\n // Restore selection/randomization state\n this._isNewAttempt = state.isNewAttempt ?? this._isNewAttempt;\n if (state.selectionCountStatus !== undefined) {\n this._sequencingControls.selectionCountStatus = state.selectionCountStatus;\n }\n if (state.reorderChildren !== undefined) {\n this._sequencingControls.reorderChildren = state.reorderChildren;\n }\n\n // Restore processedChildren - map IDs back to actual children\n if (state.processedChildren) {\n const childMap = new Map(this._children.map(c => [c.id, c]));\n this._processedChildren = state.processedChildren\n .map((id: string) => childMap.get(id))\n .filter((c: Activity | undefined) => c !== undefined) as Activity[];\n } else {\n this._processedChildren = null;\n }\n\n // Restore objective state\n if (state.primaryObjective && this._primaryObjective) {\n this._primaryObjective.satisfiedStatus = state.primaryObjective.satisfiedStatus ?? this._primaryObjective.satisfiedStatus;\n this._primaryObjective.measureStatus = state.primaryObjective.measureStatus ?? this._primaryObjective.measureStatus;\n this._primaryObjective.normalizedMeasure = state.primaryObjective.normalizedMeasure ?? this._primaryObjective.normalizedMeasure;\n this._primaryObjective.progressMeasure = state.primaryObjective.progressMeasure ?? this._primaryObjective.progressMeasure;\n this._primaryObjective.progressMeasureStatus = state.primaryObjective.progressMeasureStatus ?? this._primaryObjective.progressMeasureStatus;\n this._primaryObjective.completionStatus = state.primaryObjective.completionStatus ?? this._primaryObjective.completionStatus;\n this._primaryObjective.progressStatus = state.primaryObjective.progressStatus ?? this._primaryObjective.progressStatus;\n }\n\n if (state.objectives) {\n for (const objState of state.objectives) {\n const objective = this._objectives.find(o => o.id === objState.id);\n if (objective) {\n objective.satisfiedStatus = objState.satisfiedStatus ?? objective.satisfiedStatus;\n objective.measureStatus = objState.measureStatus ?? objective.measureStatus;\n objective.normalizedMeasure = objState.normalizedMeasure ?? objective.normalizedMeasure;\n objective.progressMeasure = objState.progressMeasure ?? objective.progressMeasure;\n objective.progressMeasureStatus = objState.progressMeasureStatus ?? objective.progressMeasureStatus;\n objective.completionStatus = objState.completionStatus ?? objective.completionStatus;\n objective.progressStatus = objState.progressStatus ?? objective.progressStatus;\n }\n }\n }\n\n // Recursively restore children state\n if (state.children && Array.isArray(state.children)) {\n for (let i = 0; i < state.children.length && i < this._children.length; i++) {\n const childState = state.children[i];\n const child = this._children.find(c => c.id === childState.id);\n if (child) {\n child.restoreSuspensionState(childState);\n }\n }\n }\n }\n\n /**\n * toJSON for Activity\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n id: this._id,\n title: this._title,\n isVisible: this._isVisible,\n isActive: this._isActive,\n isSuspended: this._isSuspended,\n isCompleted: this._isCompleted,\n completionStatus: this._completionStatus,\n successStatus: this._successStatus,\n attemptCount: this._attemptCount,\n attemptCompletionAmount: this._attemptCompletionAmount,\n attemptAbsoluteDuration: this._attemptAbsoluteDuration,\n attemptExperiencedDuration: this._attemptExperiencedDuration,\n activityAbsoluteDuration: this._activityAbsoluteDuration,\n activityExperiencedDuration: this._activityExperiencedDuration,\n objectiveSatisfiedStatus: this._objectiveSatisfiedStatus,\n objectiveSatisfiedStatusKnown: this._objectiveSatisfiedStatusKnown,\n objectiveMeasureStatus: this._objectiveMeasureStatus,\n objectiveNormalizedMeasure: this._objectiveNormalizedMeasure,\n rollupConsiderations: { ...this._rollupConsiderations },\n wasSkipped: this._wasSkipped,\n completedByMeasure: this._completedByMeasure,\n minProgressMeasure: this._minProgressMeasure,\n progressWeight: this._progressWeight,\n attemptCompletionAmountStatus: this._attemptCompletionAmountStatus,\n hideLmsUi: [...this._hideLmsUi],\n auxiliaryResources: this._auxiliaryResources.map((resource) => ({ ...resource })),\n children: this._children.map((child) => child.toJSON())\n };\n this.jsonString = false;\n return result;\n }\n\n get auxiliaryResources(): AuxiliaryResource[] {\n return this._auxiliaryResources.map((resource) => ({ ...resource }));\n }\n\n set auxiliaryResources(resources: AuxiliaryResource[]) {\n const sanitized: AuxiliaryResource[] = [];\n const seen = new Set<string>();\n for (const resource of resources || []) {\n if (!resource) continue;\n const resourceId = typeof resource.resourceId === 'string' ? resource.resourceId.trim() : '';\n const purpose = typeof resource.purpose === 'string' ? resource.purpose.trim() : '';\n if (!resourceId || seen.has(resourceId)) {\n continue;\n }\n seen.add(resourceId);\n sanitized.push({ resourceId, purpose });\n }\n this._auxiliaryResources = sanitized;\n }\n\n addAuxiliaryResource(resource: AuxiliaryResource): void {\n this.auxiliaryResources = [...this._auxiliaryResources, resource];\n }\n\n /**\n * Capture current rollup status for optimization comparison\n * Used by Overall Rollup Process (RB.1.5) to detect when status stops changing\n * @return {RollupStatusSnapshot} - Snapshot of current rollup-relevant status\n */\n captureRollupStatus(): RollupStatusSnapshot {\n return {\n measureStatus: this._objectiveMeasureStatus,\n normalizedMeasure: this._objectiveNormalizedMeasure,\n objectiveProgressStatus: this._objectiveSatisfiedStatus !== null &&\n this._objectiveSatisfiedStatus !== undefined,\n objectiveSatisfiedStatus: this._objectiveSatisfiedStatus,\n attemptProgressStatus: this._completionStatus !== CompletionStatus.UNKNOWN,\n attemptCompletionStatus: this._completionStatus === CompletionStatus.COMPLETED,\n };\n }\n\n /**\n * Compare two rollup status snapshots for equality\n * Uses epsilon comparison for floating point normalizedMeasure\n * @param {RollupStatusSnapshot} prior - Previous status snapshot\n * @param {RollupStatusSnapshot} current - Current status snapshot\n * @return {boolean} - True if statuses are equal (no change), false if different\n */\n static compareRollupStatus(\n prior: RollupStatusSnapshot,\n current: RollupStatusSnapshot\n ): boolean {\n const EPSILON = 0.0001; // Floating point comparison tolerance\n\n return prior.measureStatus === current.measureStatus &&\n Math.abs(prior.normalizedMeasure - current.normalizedMeasure) < EPSILON &&\n prior.objectiveProgressStatus === current.objectiveProgressStatus &&\n prior.objectiveSatisfiedStatus === current.objectiveSatisfiedStatus &&\n prior.attemptProgressStatus === current.attemptProgressStatus &&\n prior.attemptCompletionStatus === current.attemptCompletionStatus;\n }\n\n /**\n * Getter for hideLmsUi directives\n * @return {HideLmsUiItem[]}\n */\n get hideLmsUi(): HideLmsUiItem[] {\n return [...this._hideLmsUi];\n }\n\n /**\n * Setter for hideLmsUi directives\n * @param {HideLmsUiItem[]} hideLmsUi\n */\n set hideLmsUi(hideLmsUi: HideLmsUiItem[]) {\n const valid = new Set(HIDE_LMS_UI_TOKENS);\n const seen = new Set<HideLmsUiItem>();\n const sanitized: HideLmsUiItem[] = [];\n for (const directive of hideLmsUi) {\n if (valid.has(directive) && !seen.has(directive)) {\n seen.add(directive);\n sanitized.push(directive);\n }\n }\n this._hideLmsUi = sanitized;\n }\n}\n","import { Activity, RollupConsiderationsConfig } from \"../activity\";\nimport { SuccessStatus } from \"../../../../constants/enums\";\n\n/**\n * Type of rollup operation being performed\n */\nexport type RollupType = \"measure\" | \"objective\" | \"progress\";\n\n/**\n * Rollup action for consideration filtering\n */\nexport type RollupAction = \"satisfied\" | \"notSatisfied\" | \"completed\" | \"incomplete\";\n\n/**\n * RollupChildFilter - Handles child activity filtering for rollup operations\n * Implements SCORM 2004 RB.1.4.2 (Check Child For Rollup Subprocess)\n *\n * This class is responsible for determining which child activities should\n * contribute to rollup calculations based on their tracking settings and\n * rollup consideration requirements.\n *\n * @spec SN Book: RB.1.4.2 (Check Child For Rollup Subprocess)\n */\nexport class RollupChildFilter {\n /**\n * Check Child For Rollup Subprocess\n * Determines if a child activity contributes to rollup based on its individual consideration settings\n * This implements the full SCORM 2004 RB.1.4.2 specification\n *\n * @spec SN Book: RB.1.4.2 (Check Child For Rollup Subprocess)\n * @param child - The child activity to check\n * @param rollupType - Type of rollup (\"measure\", \"objective\", \"progress\")\n * @param rollupAction - Specific rollup action (satisfied, notSatisfied, completed, incomplete)\n * @returns True if child contributes to rollup\n */\n public checkChildForRollupSubprocess(\n child: Activity,\n rollupType: RollupType,\n rollupAction?: RollupAction,\n ): boolean {\n // First check if child is tracked\n if (child.sequencingControls.tracked === false) {\n return false;\n }\n\n let included = false;\n\n // RB.1.4.2 Step 2: Check for objective rollup (satisfied/notSatisfied)\n if (rollupType === \"measure\" || rollupType === \"objective\") {\n // Step 2.1: Check if child tracks objective rollup\n if (!child.sequencingControls.rollupObjectiveSatisfied) {\n return false;\n }\n\n // Step 2.1.1: Default to included\n included = true;\n\n // Get the child's individual consideration requirements\n const requiredForSatisfied = child.requiredForSatisfied;\n const requiredForNotSatisfied = child.requiredForNotSatisfied;\n\n // Step 2.1.2: Check ifNotSuspended consideration\n if (\n (rollupAction === \"satisfied\" && requiredForSatisfied === \"ifNotSuspended\") ||\n (rollupAction === \"notSatisfied\" && requiredForNotSatisfied === \"ifNotSuspended\")\n ) {\n // Step 2.1.2.1: Exclude if not attempted or if attempted and suspended\n if (!child.attemptProgressStatus || (child.attemptCount > 0 && child.isSuspended)) {\n included = false;\n }\n }\n // Step 2.1.3: Check ifAttempted consideration\n else if (\n (rollupAction === \"satisfied\" && requiredForSatisfied === \"ifAttempted\") ||\n (rollupAction === \"notSatisfied\" && requiredForNotSatisfied === \"ifAttempted\")\n ) {\n // Step 2.1.3.1.1: Exclude if not attempted\n if (!child.attemptProgressStatus || child.attemptCount === 0) {\n included = false;\n }\n }\n // Step 2.1.3.2: Check ifNotSkipped consideration\n else if (\n (rollupAction === \"satisfied\" && requiredForSatisfied === \"ifNotSkipped\") ||\n (rollupAction === \"notSatisfied\" && requiredForNotSatisfied === \"ifNotSkipped\")\n ) {\n // Step 2.1.3.2.1: Exclude if activity is skipped\n if (child.wasSkipped) {\n included = false;\n }\n }\n // \"always\" is the default - activity is included\n }\n\n // RB.1.4.2 Step 3: Check for progress rollup (completed/incomplete)\n if (rollupType === \"progress\") {\n // Step 3.1: Check if child tracks progress rollup\n if (!child.sequencingControls.rollupProgressCompletion) {\n return false;\n }\n\n // Step 3.1.1: Default to included\n included = true;\n\n // Get the child's individual consideration requirements\n const requiredForCompleted = child.requiredForCompleted;\n const requiredForIncomplete = child.requiredForIncomplete;\n\n // Step 3.1.2: Check ifNotSuspended consideration\n if (\n (rollupAction === \"completed\" && requiredForCompleted === \"ifNotSuspended\") ||\n (rollupAction === \"incomplete\" && requiredForIncomplete === \"ifNotSuspended\")\n ) {\n // Step 3.1.2.1: Exclude if not attempted or if attempted and suspended\n if (!child.attemptProgressStatus || (child.attemptCount > 0 && child.isSuspended)) {\n included = false;\n }\n }\n // Step 3.1.3: Check ifAttempted consideration\n else if (\n (rollupAction === \"completed\" && requiredForCompleted === \"ifAttempted\") ||\n (rollupAction === \"incomplete\" && requiredForIncomplete === \"ifAttempted\")\n ) {\n // Step 3.1.3.1.1: Exclude if not attempted\n if (!child.attemptProgressStatus || child.attemptCount === 0) {\n included = false;\n }\n }\n // Step 3.1.3.2: Check ifNotSkipped consideration\n else if (\n (rollupAction === \"completed\" && requiredForCompleted === \"ifNotSkipped\") ||\n (rollupAction === \"incomplete\" && requiredForIncomplete === \"ifNotSkipped\")\n ) {\n // Step 3.1.3.2.1: Exclude if activity is skipped\n if (child.wasSkipped) {\n included = false;\n }\n }\n // \"always\" is the default - activity is included\n }\n\n // Check if child is available for rollup (additional safety check)\n if (included && !child.isAvailable) {\n return false;\n }\n\n return included;\n }\n\n /**\n * Filter children based on rollup type and mode\n *\n * @param children - Child activities to filter\n * @param rollupType - Type of rollup (\"objective\" | \"progress\")\n * @param mode - Rollup action mode\n * @param considerations - Parent-level rollup considerations config\n * @returns Filtered array of children that should contribute to rollup\n */\n public filterChildrenForRequirement(\n children: Activity[],\n rollupType: \"objective\" | \"progress\",\n mode: RollupAction,\n considerations: RollupConsiderationsConfig,\n ): Activity[] {\n return children.filter((child) =>\n this.shouldIncludeChildForRollup(child, rollupType, mode, considerations),\n );\n }\n\n /**\n * Check if a specific child should be included in rollup\n *\n * @param child - Child activity to check\n * @param rollupType - Type of rollup (\"objective\" | \"progress\")\n * @param mode - Rollup action mode\n * @param considerations - Parent-level rollup considerations config\n * @returns True if child should be included\n */\n public shouldIncludeChildForRollup(\n child: Activity,\n rollupType: \"objective\" | \"progress\",\n mode: RollupAction,\n considerations: RollupConsiderationsConfig,\n ): boolean {\n // Use the enhanced RB.1.4.2 implementation with rollupAction parameter\n if (!this.checkChildForRollupSubprocess(child, rollupType, mode)) {\n return false;\n }\n\n // Check parent-level measureSatisfactionIfActive setting\n if (\n rollupType === \"objective\" &&\n !considerations.measureSatisfactionIfActive &&\n (child.activityAttemptActive || child.isActive)\n ) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Check if child is satisfied for rollup\n * Evaluates objective satisfaction status and success status\n *\n * @param child - Child activity to check\n * @returns True if child is considered satisfied\n */\n public isChildSatisfiedForRollup(child: Activity): boolean {\n if (child.objectiveSatisfiedStatus === true) {\n return true;\n }\n\n if (child.objectiveSatisfiedStatus === false) {\n return false;\n }\n\n if (child.successStatus === SuccessStatus.PASSED) {\n return true;\n }\n\n if (child.successStatus === SuccessStatus.FAILED) {\n return false;\n }\n\n return false;\n }\n\n /**\n * Check if child is completed for rollup\n * Evaluates completion status\n *\n * @param child - Child activity to check\n * @returns True if child is considered completed\n */\n public isChildCompletedForRollup(child: Activity): boolean {\n if (child.completionStatus === \"completed\" || child.isCompleted) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Get trackable children for rollup operations\n * Filters out activities with tracked=false from rollup calculations\n *\n * @param activity - The parent activity\n * @returns Array of trackable children\n */\n public getTrackableChildren(activity: Activity): Activity[] {\n return activity.children.filter((child) => child.sequencingControls.tracked !== false);\n }\n}\n","import { Activity } from \"../activity\";\nimport { RollupActionType, RollupConsiderationType, RollupRule } from \"../rollup_rules\";\nimport { RollupChildFilter } from \"./rollup_child_filter\";\n\n/**\n * RollupRuleEvaluator - Handles evaluation of rollup rules\n * Implements SCORM 2004 RB.1.4 (Rollup Rule Check) and RB.1.4.1 (Evaluate Rollup Conditions Subprocess)\n *\n * This class is responsible for evaluating rollup rules against child activities\n * and determining if rule conditions are met.\n *\n * @spec SN Book: RB.1.4 (Rollup Rule Check)\n * @spec SN Book: RB.1.4.1 (Evaluate Rollup Conditions Subprocess)\n */\nexport class RollupRuleEvaluator {\n private childFilter: RollupChildFilter;\n\n /**\n * Create a new RollupRuleEvaluator\n *\n * @param childFilter - RollupChildFilter instance for filtering children\n */\n constructor(childFilter: RollupChildFilter) {\n this.childFilter = childFilter;\n }\n\n /**\n * Evaluate a rollup rule\n * Determines if a rollup rule applies to an activity based on its children\n *\n * @spec SN Book: RB.1.4 (Rollup Rule Check)\n * @param activity - The parent activity\n * @param rule - The rule to evaluate\n * @returns True if the rule applies\n */\n public evaluateRollupRule(activity: Activity, rule: RollupRule): boolean {\n const children = activity.getAvailableChildren();\n let contributingChildren = 0;\n let satisfiedCount = 0;\n\n // Count children that meet the rule conditions\n // IMPORTANT: Only count children that BOTH pass consideration check AND condition check\n for (const child of children) {\n // Step 1: Check if child is included based on consideration settings (RB.1.4.2)\n let isIncluded = false;\n switch (rule.action) {\n case RollupActionType.SATISFIED:\n isIncluded = this.childFilter.checkChildForRollupSubprocess(\n child,\n \"objective\",\n \"satisfied\",\n );\n break;\n case RollupActionType.NOT_SATISFIED:\n isIncluded = this.childFilter.checkChildForRollupSubprocess(\n child,\n \"objective\",\n \"notSatisfied\",\n );\n break;\n case RollupActionType.COMPLETED:\n isIncluded = this.childFilter.checkChildForRollupSubprocess(\n child,\n \"progress\",\n \"completed\",\n );\n break;\n case RollupActionType.INCOMPLETE:\n isIncluded = this.childFilter.checkChildForRollupSubprocess(\n child,\n \"progress\",\n \"incomplete\",\n );\n break;\n }\n\n // Step 2: Only count if child is included by consideration settings\n if (isIncluded) {\n contributingChildren++;\n\n // Step 3: Evaluate rule conditions for this child using RB.1.4.1\n if (this.evaluateRollupConditionsSubprocess(child, rule)) {\n satisfiedCount++;\n }\n }\n }\n\n // Apply minimum count/percent logic OR consideration type\n if (rule.consideration === RollupConsiderationType.ALL) {\n // For ALL consideration, all contributing children must satisfy\n return contributingChildren > 0 && satisfiedCount === contributingChildren;\n } else if (rule.minimumCount !== null) {\n return satisfiedCount >= rule.minimumCount;\n } else if (rule.minimumPercent !== null) {\n const percent = contributingChildren > 0 ? satisfiedCount / contributingChildren : 0;\n return percent >= rule.minimumPercent;\n }\n\n // Default: all contributing children must satisfy\n return contributingChildren > 0 && satisfiedCount === contributingChildren;\n }\n\n /**\n * Evaluate Rollup Conditions Subprocess\n * Evaluates if rollup rule conditions are met for a given activity\n *\n * @spec SN Book: RB.1.4.1 (Evaluate Rollup Conditions Subprocess)\n * @param child - The child activity to evaluate\n * @param rule - The rollup rule containing conditions to evaluate\n * @returns True if all conditions are met, false otherwise\n */\n public evaluateRollupConditionsSubprocess(child: Activity, rule: RollupRule): boolean {\n // If no conditions are specified, the rule always applies\n if (rule.conditions.length === 0) {\n return true;\n }\n\n // Evaluate based on the rule's consideration type\n switch (rule.consideration) {\n case RollupConsiderationType.ALL:\n // All conditions must be met\n return rule.conditions.every((condition) => condition.evaluate(child));\n\n case RollupConsiderationType.ANY:\n // At least one condition must be met\n return rule.conditions.some((condition) => condition.evaluate(child));\n\n case RollupConsiderationType.NONE:\n // No conditions should be met\n return !rule.conditions.some((condition) => condition.evaluate(child));\n\n case RollupConsiderationType.AT_LEAST_COUNT:\n case RollupConsiderationType.AT_LEAST_PERCENT:\n // These are handled at the rule level, not condition level\n // For individual condition evaluation, treat as ALL\n return rule.conditions.every((condition) => condition.evaluate(child));\n\n default:\n // Unknown consideration type, default to false\n return false;\n }\n }\n\n /**\n * Evaluate rules for a specific action type\n * Finds and evaluates all rules matching the specified action\n *\n * @param activity - The parent activity\n * @param rules - Array of rollup rules to evaluate\n * @param actionType - The action type to filter by\n * @returns True if any matching rule applies, false if none apply, null if no matching rules\n */\n public evaluateRulesForAction(\n activity: Activity,\n rules: RollupRule[],\n actionType: RollupActionType,\n ): boolean | null {\n const matchingRules = rules.filter((rule) => rule.action === actionType);\n\n if (matchingRules.length === 0) {\n return null;\n }\n\n for (const rule of matchingRules) {\n if (this.evaluateRollupRule(activity, rule)) {\n return true;\n }\n }\n\n return false;\n }\n}\n","import { Activity } from \"../activity\";\nimport { RollupChildFilter } from \"./rollup_child_filter\";\n\n/**\n * Options for measure rollup calculation\n */\nexport interface MeasureRollupOptions {\n /**\n * Enable threshold bias for weighted calculations\n * When true, applies small bonuses/penalties based on passing threshold comparison\n */\n enableThresholdBias?: boolean;\n}\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * MeasureRollupProcessor - Handles measure (score) rollup operations\n * Implements SCORM 2004 RB.1.1 (Measure Rollup Process) and RB.1.1.b (Completion Measure Rollup)\n *\n * This class is responsible for calculating rolled-up objective measures\n * from child activities to parent clusters using weighted averaging.\n *\n * @spec SN Book: RB.1.1 (Measure Rollup Process)\n * @spec SN Book: RB.1.1.b (Completion Measure Rollup Process)\n */\nexport class MeasureRollupProcessor {\n private childFilter: RollupChildFilter;\n private eventCallback: EventCallback | null;\n\n /**\n * Create a new MeasureRollupProcessor\n *\n * @param childFilter - RollupChildFilter instance for filtering children\n * @param eventCallback - Optional callback for firing events\n */\n constructor(childFilter: RollupChildFilter, eventCallback?: EventCallback) {\n this.childFilter = childFilter;\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Measure Rollup Process\n * Rolls up objective measure (score) from children to parent\n * Uses complex weighted measure calculation\n *\n * @spec SN Book: RB.1.1 (Measure Rollup Process)\n * @param activity - The parent activity\n * @returns Array of identified activity clusters (for cross-cluster processing)\n */\n public measureRollupProcess(activity: Activity): Activity[] {\n if (!activity.sequencingControls.rollupObjectiveSatisfied) {\n return [];\n }\n\n const children = activity.getAvailableChildren();\n if (children.length === 0) {\n return [];\n }\n\n const rollupConsiderations = activity.rollupConsiderations;\n const contributingChildren = children.filter((child) => {\n if (!this.childFilter.checkChildForRollupSubprocess(child, \"measure\")) {\n return false;\n }\n if (!child.objectiveMeasureStatus || child.objectiveNormalizedMeasure === null) {\n return false;\n }\n if (\n !rollupConsiderations.measureSatisfactionIfActive &&\n (child.activityAttemptActive || child.isActive)\n ) {\n return false;\n }\n return true;\n });\n\n if (contributingChildren.length === 0) {\n activity.objectiveMeasureStatus = false;\n return [];\n }\n\n const complexWeightedMeasure = this.calculateComplexWeightedMeasure(\n activity,\n contributingChildren,\n { enableThresholdBias: false },\n );\n activity.objectiveNormalizedMeasure = complexWeightedMeasure;\n activity.objectiveMeasureStatus = true;\n\n // Return identified clusters for cross-cluster processing\n return this.identifyActivityClusters(contributingChildren);\n }\n\n /**\n * Completion Measure Rollup Process\n * Rolls up attemptCompletionAmount from children to parent using weighted averaging\n * 4th Edition Addition: Supports completion measure rollup for progress tracking\n *\n * @spec SN Book: RB.1.1.b (Completion Measure Rollup Process)\n * @param activity - The parent activity\n */\n public completionMeasureRollupProcess(activity: Activity): void {\n const children = activity.getAvailableChildren();\n if (children.length === 0) {\n return;\n }\n\n const contributingChildren = children.filter((child) => {\n return child.attemptCompletionAmountStatus;\n });\n\n if (contributingChildren.length === 0) {\n activity.attemptCompletionAmountStatus = false;\n return;\n }\n\n let totalWeightedMeasure = 0;\n let totalWeight = 0;\n\n for (const child of contributingChildren) {\n totalWeightedMeasure += child.attemptCompletionAmount * child.progressWeight;\n totalWeight += child.progressWeight;\n }\n\n if (totalWeight > 0) {\n activity.attemptCompletionAmount = totalWeightedMeasure / totalWeight;\n activity.attemptCompletionAmountStatus = true;\n }\n }\n\n /**\n * Calculate Complex Weighted Measure\n * Handles complex objective weighting scenarios with dependency chains\n * Supports weighted rollup calculations with various adjustment factors\n *\n * @spec Priority 5 Gap Implementation\n * @param activity - The parent activity\n * @param children - Child activities to weight\n * @param options - Configuration options for the calculation\n * @returns Calculated weighted measure\n */\n public calculateComplexWeightedMeasure(\n activity: Activity,\n children: Activity[],\n options?: MeasureRollupOptions,\n ): number {\n let totalWeightedMeasure = 0;\n let totalWeight = 0;\n const weightingLog: Array<{ childId: string; measure: number; weight: number }> = [];\n const enableBias = options?.enableThresholdBias ?? true;\n\n for (const child of children) {\n if (!this.childFilter.checkChildForRollupSubprocess(child, \"measure\")) {\n continue;\n }\n\n if (child.objectiveMeasureStatus && child.objectiveNormalizedMeasure !== null) {\n // Handle complex weighting scenarios\n const baseWeight = child.sequencingControls.objectiveMeasureWeight;\n const adjustedWeight = this.calculateAdjustedWeight(child, baseWeight, enableBias);\n const contribution = child.objectiveNormalizedMeasure * adjustedWeight;\n\n totalWeightedMeasure += contribution;\n totalWeight += adjustedWeight;\n\n weightingLog.push({\n childId: child.id,\n measure: child.objectiveNormalizedMeasure,\n weight: adjustedWeight,\n });\n }\n }\n\n this.eventCallback?.(\"complex_weighting_calculated\", {\n activityId: activity.id,\n weightingDetails: weightingLog,\n totalWeight,\n totalWeightedMeasure,\n result: totalWeight > 0 ? totalWeightedMeasure / totalWeight : 0,\n });\n\n return totalWeight > 0 ? totalWeightedMeasure / totalWeight : 0;\n }\n\n /**\n * Calculate adjusted weight for complex weighting scenarios\n * Applies various adjustment factors based on activity state\n *\n * @param child - Child activity to calculate weight for\n * @param baseWeight - Base weight from sequencing controls\n * @param enableBias - Whether to apply threshold bias adjustments\n * @returns Adjusted weight value\n */\n public calculateAdjustedWeight(\n child: Activity,\n baseWeight: number,\n enableBias: boolean = true,\n ): number {\n let adjustedWeight = baseWeight;\n\n // Factor in completion status\n if (child.completionStatus !== \"completed\") {\n adjustedWeight *= 0.8; // Reduce weight for incomplete activities\n }\n\n // Factor in attempt count (penalize multiple attempts)\n if (child.attemptCount > 1) {\n const attemptPenalty = Math.max(0.5, 1 - (child.attemptCount - 1) * 0.1);\n adjustedWeight *= attemptPenalty;\n }\n\n // Factor in time limits if exceeded\n if (child.hasAttemptLimitExceeded()) {\n adjustedWeight *= 0.6; // Significant penalty for exceeding limits\n }\n\n // Bias by relation to passing threshold when available\n if (enableBias && child.objectiveMeasureStatus) {\n const threshold = child.scaledPassingScore ?? 0.7;\n if (child.objectiveNormalizedMeasure >= threshold) {\n adjustedWeight *= 1.05; // small boost for above-threshold performance\n } else {\n adjustedWeight *= 0.95; // small penalty for below-threshold performance\n }\n }\n\n return Math.max(0, adjustedWeight); // Ensure non-negative weight\n }\n\n /**\n * Identify Activity Clusters\n * Identifies clusters among child activities for cross-cluster dependency processing\n *\n * @param children - Child activities to analyze\n * @returns Array of identified clusters\n */\n public identifyActivityClusters(children: Activity[]): Activity[] {\n const clusters: Activity[] = [];\n\n for (const child of children) {\n // An activity is considered a cluster if it has children and flow controls\n if (child.children.length > 0 && child.sequencingControls.flow) {\n clusters.push(child);\n }\n }\n\n return clusters;\n }\n}\n","import { Activity } from \"../activity\";\nimport { RollupActionType, RollupRule } from \"../rollup_rules\";\nimport { RollupChildFilter } from \"./rollup_child_filter\";\nimport { RollupRuleEvaluator } from \"./rollup_rule_evaluator\";\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * ObjectiveRollupProcessor - Handles objective satisfaction rollup operations\n * Implements SCORM 2004 RB.1.2 (Objective Rollup Process)\n *\n * This class is responsible for determining objective satisfaction status\n * for parent activities based on their children's states using rules,\n * measure-based evaluation, or default rollup logic.\n *\n * @spec SN Book: RB.1.2 (Objective Rollup Process)\n * @spec SN Book: RB.1.2.a (Objective Rollup Using Measure)\n * @spec SN Book: RB.1.2.b (Objective Rollup Using Rules)\n * @spec SN Book: RB.1.2.c (Objective Rollup Using Default)\n */\nexport class ObjectiveRollupProcessor {\n private childFilter: RollupChildFilter;\n private ruleEvaluator: RollupRuleEvaluator;\n private eventCallback: EventCallback | null;\n\n /**\n * Create a new ObjectiveRollupProcessor\n *\n * @param childFilter - RollupChildFilter instance for filtering children\n * @param ruleEvaluator - RollupRuleEvaluator instance for evaluating rules\n * @param eventCallback - Optional callback for firing events\n */\n constructor(\n childFilter: RollupChildFilter,\n ruleEvaluator: RollupRuleEvaluator,\n eventCallback?: EventCallback,\n ) {\n this.childFilter = childFilter;\n this.ruleEvaluator = ruleEvaluator;\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Objective Rollup Process\n * Determines objective satisfaction status using rules, measure, or default\n *\n * @spec SN Book: RB.1.2 (Objective Rollup Process)\n * @param activity - The parent activity\n */\n public objectiveRollupProcess(activity: Activity): void {\n const rollupRules = activity.rollupRules;\n\n // First, try rollup using rules (RB.1.2.b)\n const ruleResult = this.objectiveRollupUsingRules(activity, rollupRules.rules);\n if (ruleResult !== null) {\n activity.objectiveSatisfiedStatus = ruleResult;\n // Do NOT set objectiveMeasureStatus here - rule-based rollup doesn't involve measures\n // Per SCORM 2004 SN Book RB.1.2.b, only satisfaction status is determined by rules\n this.syncPrimaryObjectiveFromActivity(activity);\n return;\n }\n\n // Then, try rollup using measure (RB.1.2.a)\n const measureResult = this.objectiveRollupUsingMeasure(activity);\n if (measureResult !== null) {\n activity.objectiveSatisfiedStatus = measureResult;\n // measureStatus is already true from measureRollupProcess (which ran first)\n this.syncPrimaryObjectiveFromActivity(activity);\n return;\n }\n\n // Finally, use default rollup (RB.1.2.c)\n activity.objectiveSatisfiedStatus = this.objectiveRollupUsingDefault(activity);\n // Do NOT set objectiveMeasureStatus here - default rollup doesn't involve measures\n // Per SCORM 2004 SN Book RB.1.2.c, only satisfaction status is determined by default rollup\n this.syncPrimaryObjectiveFromActivity(activity);\n }\n\n /**\n * Objective Rollup Using Rules\n * Evaluates rollup rules to determine objective satisfaction\n *\n * @spec SN Book: RB.1.2.b (Objective Rollup Using Rules)\n * @param activity - The parent activity\n * @param rules - The rollup rules to evaluate\n * @returns True if satisfied, false if not, null if no rule applies\n */\n public objectiveRollupUsingRules(activity: Activity, rules: RollupRule[]): boolean | null {\n // Get satisfied and not satisfied rules\n const satisfiedRules = rules.filter((rule) => rule.action === RollupActionType.SATISFIED);\n\n const notSatisfiedRules = rules.filter(\n (rule) => rule.action === RollupActionType.NOT_SATISFIED,\n );\n\n // Evaluate satisfied rules first\n for (const rule of satisfiedRules) {\n if (this.ruleEvaluator.evaluateRollupRule(activity, rule)) {\n return true;\n }\n }\n\n // Then evaluate not satisfied rules\n for (const rule of notSatisfiedRules) {\n if (this.ruleEvaluator.evaluateRollupRule(activity, rule)) {\n return false;\n }\n }\n\n return null;\n }\n\n /**\n * Objective Rollup Using Measure\n * Determines satisfaction based on objective measure vs passing score\n *\n * @spec SN Book: RB.1.2.a (Objective Rollup Using Measure)\n * @param activity - The parent activity\n * @returns True if satisfied, false if not, null if no measure\n */\n public objectiveRollupUsingMeasure(activity: Activity): boolean | null {\n if (!activity.objectiveMeasureStatus || activity.scaledPassingScore === null) {\n return null;\n }\n\n return activity.objectiveNormalizedMeasure >= activity.scaledPassingScore;\n }\n\n /**\n * Objective Rollup Using Default\n * For default rollup (no explicit rules), a child is included only if it\n * passes BOTH requiredForSatisfied AND requiredForNotSatisfied considerations.\n * This ensures symmetric exclusion: setting either consideration excludes\n * the child from the entire objective rollup evaluation.\n *\n * @spec SN Book: RB.1.2.c (Objective Rollup Using Default)\n * @param activity - The parent activity\n * @returns True if all tracked children are satisfied\n */\n public objectiveRollupUsingDefault(activity: Activity): boolean {\n const children = activity.getAvailableChildren();\n if (children.length === 0) {\n return false;\n }\n\n const considerations = activity.rollupConsiderations;\n\n // For default rollup, use INTERSECTION of both consideration filters.\n // A child must pass BOTH requiredForSatisfied AND requiredForNotSatisfied\n // to contribute to the rollup. This ensures that setting either\n // consideration (e.g., ifNotSuspended, ifNotSkipped) excludes the child\n // from the entire default objective rollup evaluation.\n const contributors = children.filter((child) => {\n // Check both consideration filters\n if (\n !this.childFilter.checkChildForRollupSubprocess(child, \"objective\", \"satisfied\") ||\n !this.childFilter.checkChildForRollupSubprocess(child, \"objective\", \"notSatisfied\")\n ) {\n return false;\n }\n\n // Check parent-level measureSatisfactionIfActive setting\n if (\n !considerations.measureSatisfactionIfActive &&\n (child.activityAttemptActive || child.isActive)\n ) {\n return false;\n }\n\n return true;\n });\n\n if (contributors.length === 0) {\n return false;\n }\n\n // Default rollup logic:\n // - Parent is \"not satisfied\" if ANY contributor is not satisfied\n // - Parent is \"satisfied\" if ALL contributors are satisfied\n if (contributors.some((child) => !this.childFilter.isChildSatisfiedForRollup(child))) {\n return false;\n }\n\n return contributors.every((child) => this.childFilter.isChildSatisfiedForRollup(child));\n }\n\n /**\n * Sync primary objective status from activity properties\n * Ensures the primary objective reflects the activity's rollup-derived status\n *\n * Note: This method is intentionally public as it is used by other rollup\n * processors (e.g., ProgressRollupProcessor) to synchronize primary objective\n * state after modifying activity completion status.\n *\n * @param activity - The activity to sync\n */\n public syncPrimaryObjectiveFromActivity(activity: Activity): void {\n if (activity.primaryObjective) {\n activity.primaryObjective.satisfiedStatus = activity.objectiveSatisfiedStatus;\n activity.primaryObjective.satisfiedStatusKnown = activity.objectiveSatisfiedStatusKnown;\n activity.primaryObjective.measureStatus = activity.objectiveMeasureStatus;\n activity.primaryObjective.normalizedMeasure = activity.objectiveNormalizedMeasure;\n activity.primaryObjective.progressMeasure = activity.progressMeasure;\n activity.primaryObjective.progressMeasureStatus = activity.progressMeasureStatus;\n activity.primaryObjective.completionStatus = activity.completionStatus;\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { RollupActionType } from \"../rollup_rules\";\nimport { CompletionStatus } from \"../../../../constants/enums\";\nimport { RollupChildFilter } from \"./rollup_child_filter\";\nimport { RollupRuleEvaluator } from \"./rollup_rule_evaluator\";\nimport { ObjectiveRollupProcessor } from \"./objective_rollup\";\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * ProgressRollupProcessor - Handles activity progress (completion) rollup operations\n * Implements SCORM 2004 RB.1.3 (Activity Progress Rollup Process)\n *\n * This class is responsible for determining completion status for parent\n * activities based on their children's states using measure-based evaluation,\n * rules, or default rollup logic.\n *\n * @spec SN Book: RB.1.3 (Activity Progress Rollup Process)\n * @spec SN Book: RB.1.3.a (Activity Progress Rollup Using Measure)\n */\nexport class ProgressRollupProcessor {\n private childFilter: RollupChildFilter;\n private ruleEvaluator: RollupRuleEvaluator;\n private objectiveProcessor: ObjectiveRollupProcessor;\n private eventCallback: EventCallback | null;\n\n /**\n * Create a new ProgressRollupProcessor\n *\n * @param childFilter - RollupChildFilter instance for filtering children\n * @param ruleEvaluator - RollupRuleEvaluator instance for evaluating rules\n * @param objectiveProcessor - ObjectiveRollupProcessor for syncing objectives\n * @param eventCallback - Optional callback for firing events\n */\n constructor(\n childFilter: RollupChildFilter,\n ruleEvaluator: RollupRuleEvaluator,\n objectiveProcessor: ObjectiveRollupProcessor,\n eventCallback?: EventCallback,\n ) {\n this.childFilter = childFilter;\n this.ruleEvaluator = ruleEvaluator;\n this.objectiveProcessor = objectiveProcessor;\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Activity Progress Rollup Process\n * Determines activity completion status\n * Tries measure-based rollup first, then rules-based, then default\n *\n * @spec SN Book: RB.1.3 (Activity Progress Rollup Process)\n * @param activity - The parent activity\n */\n public activityProgressRollupProcess(activity: Activity): void {\n // Try measure-based rollup first (RB.1.3 a)\n if (this.activityProgressRollupUsingMeasure(activity)) {\n return;\n }\n\n // Continue with rules-based rollup (original implementation)\n const rollupRules = activity.rollupRules;\n\n // Get completion rules\n const completedRules = rollupRules.rules.filter(\n (rule) => rule.action === RollupActionType.COMPLETED,\n );\n\n const incompleteRules = rollupRules.rules.filter(\n (rule) => rule.action === RollupActionType.INCOMPLETE,\n );\n\n // Evaluate completed rules first\n for (const rule of completedRules) {\n if (this.ruleEvaluator.evaluateRollupRule(activity, rule)) {\n activity.completionStatus = CompletionStatus.COMPLETED;\n this.objectiveProcessor.syncPrimaryObjectiveFromActivity(activity);\n return;\n }\n }\n\n // Then evaluate incomplete rules\n for (const rule of incompleteRules) {\n if (this.ruleEvaluator.evaluateRollupRule(activity, rule)) {\n activity.completionStatus = CompletionStatus.INCOMPLETE;\n this.objectiveProcessor.syncPrimaryObjectiveFromActivity(activity);\n return;\n }\n }\n\n // Default: completed if all tracked children are completed\n // For default rollup, use INTERSECTION of both consideration filters.\n // A child must pass BOTH requiredForCompleted AND requiredForIncomplete\n // to contribute to the rollup. This ensures symmetric exclusion.\n const children = activity.getAvailableChildren();\n const contributors = children.filter(\n (child) =>\n this.childFilter.checkChildForRollupSubprocess(child, \"progress\", \"completed\") &&\n this.childFilter.checkChildForRollupSubprocess(child, \"progress\", \"incomplete\"),\n );\n\n if (contributors.length === 0) {\n activity.completionStatus = CompletionStatus.INCOMPLETE;\n this.objectiveProcessor.syncPrimaryObjectiveFromActivity(activity);\n return;\n }\n\n // Default progress rollup logic:\n // - Parent is \"incomplete\" if ANY contributor is incomplete\n // - Parent is \"completed\" if ALL contributors are completed\n if (contributors.some((child) => !this.childFilter.isChildCompletedForRollup(child))) {\n activity.completionStatus = CompletionStatus.INCOMPLETE;\n this.objectiveProcessor.syncPrimaryObjectiveFromActivity(activity);\n return;\n }\n\n activity.completionStatus = CompletionStatus.COMPLETED;\n this.objectiveProcessor.syncPrimaryObjectiveFromActivity(activity);\n }\n\n /**\n * Activity Progress Rollup Using Measure\n * Determines completion status using attemptCompletionAmount threshold comparison\n * 4th Edition Addition: Measure-based completion determination\n *\n * @spec SN Book: RB.1.3.a (Activity Progress Rollup Using Measure)\n * @param activity - The activity to evaluate\n * @returns True if measure-based evaluation was applied, false otherwise\n */\n public activityProgressRollupUsingMeasure(activity: Activity): boolean {\n // Only apply if completedByMeasure is enabled\n if (!activity.completedByMeasure) {\n return false;\n }\n\n // Check if we have valid completion amount data\n if (!activity.attemptCompletionAmountStatus) {\n activity.completionStatus = CompletionStatus.UNKNOWN;\n this.objectiveProcessor.syncPrimaryObjectiveFromActivity(activity);\n return true;\n }\n\n // Compare completion amount against threshold\n if (activity.attemptCompletionAmount >= activity.minProgressMeasure) {\n activity.completionStatus = CompletionStatus.COMPLETED;\n } else {\n activity.completionStatus = CompletionStatus.INCOMPLETE;\n }\n\n this.objectiveProcessor.syncPrimaryObjectiveFromActivity(activity);\n return true;\n }\n}\n","import { Activity } from \"../activity\";\nimport { scorm2004_regex } from \"../../../../constants/regex\";\nimport { getDurationAsSeconds, getSecondsAsISODuration } from \"../../../../utilities\";\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * DurationRollupProcessor - Handles duration rollup operations\n * Implements SCORM 2004 RB.1.4 (Duration Rollup Process)\n *\n * This class is responsible for aggregating duration information from\n * child activities to parent clusters, including both absolute (wall clock)\n * and experienced (actual learning time) durations.\n *\n * @spec SN Book: RB.1.4 (Duration Rollup Process)\n * @spec Reference: Overall Rollup Process [RB.1.5] - duration rollup happens before optimization check\n */\nexport class DurationRollupProcessor {\n private eventCallback: EventCallback | null;\n\n /**\n * Create a new DurationRollupProcessor\n *\n * @param eventCallback - Optional callback for firing events\n */\n constructor(eventCallback?: EventCallback) {\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Duration Rollup Process\n * Aggregates duration information from child activities to parent cluster\n * Called ALWAYS for cluster activities, even when other rollup is skipped due to optimization\n *\n * @spec SN Book: RB.1.4 (Duration Rollup Process)\n * @spec Reference: Overall Rollup Process [RB.1.5] - duration rollup happens before optimization check\n * @param activity - The parent cluster activity\n */\n public durationRollupProcess(activity: Activity): void {\n // Only process cluster activities (non-leaf)\n if (activity.children.length === 0) {\n return;\n }\n\n const children = activity.getAvailableChildren();\n\n // No children available, nothing to rollup\n if (children.length === 0) {\n return;\n }\n\n let earliestChildActivityStartTimestampUtc: string | null = null;\n let earliestChildAttemptStartTimestampUtc: string | null = null;\n let latestChildEndDate: Date | null = null;\n let latestAttemptChildEndDate: Date | null = null;\n\n // Aggregate experienced durations (in seconds for easier math)\n let childrenActivityExperiencedDurationSeconds = 0;\n let childrenAttemptExperiencedDurationSeconds = 0;\n\n // Process each child\n for (const child of children) {\n // Track earliest activity start timestamp\n if (child.activityStartTimestampUtc) {\n if (\n !earliestChildActivityStartTimestampUtc ||\n child.activityStartTimestampUtc < earliestChildActivityStartTimestampUtc\n ) {\n earliestChildActivityStartTimestampUtc = child.activityStartTimestampUtc;\n }\n }\n\n // Track latest activity end date\n if (child.activityEndedDate) {\n if (!latestChildEndDate || child.activityEndedDate > latestChildEndDate) {\n latestChildEndDate = child.activityEndedDate;\n }\n }\n\n // Aggregate activity experienced duration\n // Use Value field if available (for cluster activities), otherwise use regular field (for leaf activities)\n const activityDuration =\n child.activityExperiencedDurationValue !== \"PT0H0M0S\"\n ? child.activityExperiencedDurationValue\n : child.activityExperiencedDuration;\n\n if (activityDuration && activityDuration !== \"PT0H0M0S\") {\n childrenActivityExperiencedDurationSeconds += getDurationAsSeconds(\n activityDuration,\n scorm2004_regex.CMITimespan,\n );\n }\n\n // Check if child is in same attempt as parent\n // (child attempt started after or at same time as parent attempt start)\n const isChildInSameAttempt =\n !activity.attemptStartTimestampUtc ||\n (child.attemptStartTimestampUtc &&\n child.attemptStartTimestampUtc >= activity.attemptStartTimestampUtc);\n\n if (isChildInSameAttempt) {\n // Track earliest attempt start timestamp\n if (child.attemptStartTimestampUtc) {\n if (\n !earliestChildAttemptStartTimestampUtc ||\n child.attemptStartTimestampUtc < earliestChildAttemptStartTimestampUtc\n ) {\n earliestChildAttemptStartTimestampUtc = child.attemptStartTimestampUtc;\n }\n }\n\n // Track latest attempt end date\n if (child.activityEndedDate) {\n if (!latestAttemptChildEndDate || child.activityEndedDate > latestAttemptChildEndDate) {\n latestAttemptChildEndDate = child.activityEndedDate;\n }\n }\n\n // Aggregate attempt experienced duration\n // Use Value field if available (for cluster activities), otherwise use regular field (for leaf activities)\n const attemptDuration =\n child.attemptExperiencedDurationValue !== \"PT0H0M0S\"\n ? child.attemptExperiencedDurationValue\n : child.attemptExperiencedDuration;\n\n if (attemptDuration && attemptDuration !== \"PT0H0M0S\") {\n childrenAttemptExperiencedDurationSeconds += getDurationAsSeconds(\n attemptDuration,\n scorm2004_regex.CMITimespan,\n );\n }\n }\n }\n\n // Update parent activity timestamps and durations if we found any child data\n if (earliestChildActivityStartTimestampUtc !== null) {\n // Set earliest start timestamps\n activity.activityStartTimestampUtc = earliestChildActivityStartTimestampUtc;\n\n if (!activity.attemptStartTimestampUtc && earliestChildAttemptStartTimestampUtc) {\n activity.attemptStartTimestampUtc = earliestChildAttemptStartTimestampUtc;\n }\n\n // Set latest end date\n activity.activityEndedDate = latestChildEndDate;\n\n // Calculate absolute durations (wall clock time)\n if (latestChildEndDate && activity.activityStartTimestampUtc) {\n const startDate = new Date(activity.activityStartTimestampUtc);\n const durationMs = latestChildEndDate.getTime() - startDate.getTime();\n const durationSeconds = Math.max(0, durationMs / 1000);\n activity.activityAbsoluteDurationValue = getSecondsAsISODuration(durationSeconds);\n }\n\n if (latestAttemptChildEndDate && activity.attemptStartTimestampUtc) {\n const startDate = new Date(activity.attemptStartTimestampUtc);\n const durationMs = latestAttemptChildEndDate.getTime() - startDate.getTime();\n const durationSeconds = Math.max(0, durationMs / 1000);\n activity.attemptAbsoluteDurationValue = getSecondsAsISODuration(durationSeconds);\n }\n\n // Set aggregated experienced durations\n activity.activityExperiencedDurationValue = getSecondsAsISODuration(\n childrenActivityExperiencedDurationSeconds,\n );\n activity.attemptExperiencedDurationValue = getSecondsAsISODuration(\n childrenAttemptExperiencedDurationSeconds,\n );\n\n // Fire event for monitoring\n this.eventCallback?.(\"duration_rollup_completed\", {\n activityId: activity.id,\n activityAbsoluteDuration: activity.activityAbsoluteDurationValue,\n attemptAbsoluteDuration: activity.attemptAbsoluteDurationValue,\n activityExperiencedDuration: activity.activityExperiencedDurationValue,\n attemptExperiencedDuration: activity.attemptExperiencedDurationValue,\n childCount: children.length,\n });\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { MeasureRollupProcessor } from \"./measure_rollup\";\nimport { ObjectiveRollupProcessor } from \"./objective_rollup\";\nimport { ProgressRollupProcessor } from \"./progress_rollup\";\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * CrossClusterProcessor - Handles cross-cluster dependencies in rollup\n * Manages dependencies between activity clusters for accurate rollup\n *\n * This class is responsible for analyzing and processing dependencies\n * between activity clusters during rollup operations, ensuring that\n * clusters are processed in the correct order.\n *\n * @spec Priority 5 Gap: Cross-cluster dependency processing\n */\n/**\n * Maximum recursion depth for cross-cluster processing to prevent infinite loops\n */\nconst MAX_CLUSTER_DEPTH = 10;\n\nexport class CrossClusterProcessor {\n private measureProcessor: MeasureRollupProcessor;\n private objectiveProcessor: ObjectiveRollupProcessor;\n private progressProcessor: ProgressRollupProcessor;\n private eventCallback: EventCallback | null;\n private processingClusters: Set<string> = new Set();\n\n /**\n * Create a new CrossClusterProcessor\n *\n * @param measureProcessor - MeasureRollupProcessor for measure rollup\n * @param objectiveProcessor - ObjectiveRollupProcessor for objective rollup\n * @param progressProcessor - ProgressRollupProcessor for progress rollup\n * @param eventCallback - Optional callback for firing events\n */\n constructor(\n measureProcessor: MeasureRollupProcessor,\n objectiveProcessor: ObjectiveRollupProcessor,\n progressProcessor: ProgressRollupProcessor,\n eventCallback?: EventCallback,\n ) {\n this.measureProcessor = measureProcessor;\n this.objectiveProcessor = objectiveProcessor;\n this.progressProcessor = progressProcessor;\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Handle cross-cluster dependencies in rollup\n * Manages dependencies between activity clusters for accurate rollup\n *\n * @param activity - The activity to process\n * @param clusters - Related activity clusters\n * @param depth - Current recursion depth (default 0)\n */\n public processCrossClusterDependencies(\n activity: Activity,\n clusters: Activity[],\n depth: number = 0,\n ): void {\n // Prevent infinite recursion with depth limit\n if (depth >= MAX_CLUSTER_DEPTH) {\n this.eventCallback?.(\"cross_cluster_max_depth_reached\", {\n activityId: activity.id,\n depth,\n maxDepth: MAX_CLUSTER_DEPTH,\n });\n return;\n }\n\n // Skip if we're already processing this activity (prevents re-entrant calls)\n if (this.processingClusters.has(activity.id)) {\n this.eventCallback?.(\"cross_cluster_skip_reentrant\", {\n activityId: activity.id,\n });\n return;\n }\n\n try {\n this.processingClusters.add(activity.id);\n\n this.eventCallback?.(\"cross_cluster_processing_started\", {\n activityId: activity.id,\n clusterCount: clusters.length,\n depth,\n });\n\n const dependencyMap = new Map<string, string[]>();\n\n // Build dependency map across clusters\n for (const cluster of clusters) {\n this.analyzeCrossClusterDependencies(cluster, dependencyMap);\n }\n\n // Process dependencies in correct order\n const processOrder = this.resolveDependencyOrder(dependencyMap);\n\n for (const clusterId of processOrder) {\n const cluster = clusters.find((c) => c.id === clusterId);\n if (cluster) {\n this.processClusterRollup(cluster, depth);\n }\n }\n\n this.eventCallback?.(\"cross_cluster_processing_completed\", {\n activityId: activity.id,\n processedClusters: processOrder.length,\n dependencyMap: Array.from(dependencyMap.entries()),\n });\n } catch (error) {\n this.eventCallback?.(\"cross_cluster_processing_error\", {\n activityId: activity.id,\n error: error instanceof Error ? error.message : String(error),\n });\n } finally {\n this.processingClusters.delete(activity.id);\n }\n }\n\n /**\n * Identify Activity Clusters\n * Identifies clusters among child activities for cross-cluster dependency processing\n *\n * @param children - Child activities to analyze\n * @returns Array of identified clusters\n */\n public identifyActivityClusters(children: Activity[]): Activity[] {\n const clusters: Activity[] = [];\n\n for (const child of children) {\n // An activity is considered a cluster if it has children and flow controls\n if (child.children.length > 0 && child.sequencingControls.flow) {\n clusters.push(child);\n }\n }\n\n return clusters;\n }\n\n /**\n * Analyze cross-cluster dependencies\n * Builds dependency relationships based on sequencing rules and prerequisites\n *\n * @param cluster - The cluster to analyze\n * @param dependencyMap - Map to store dependencies\n */\n public analyzeCrossClusterDependencies(\n cluster: Activity,\n dependencyMap: Map<string, string[]>,\n ): void {\n // Build dependency relationships based on sequencing rules and prerequisites\n const dependencies: string[] = [];\n\n // Analyze sequencing rules for potential cross-cluster dependencies\n // TODO: Implement rule analysis to identify dependencies between clusters\n // based on precondition rules, objective mappings, and prerequisite relationships.\n // Currently, clusters are treated as independent for processing order.\n const sequencingRules = cluster.sequencingRules;\n if (sequencingRules && sequencingRules.preConditionRules.length > 0) {\n // Future: Extract target activity references from precondition rules\n // and add them to dependencies array if they reference other clusters\n }\n\n dependencyMap.set(cluster.id, dependencies);\n }\n\n /**\n * Resolve dependency processing order\n * Uses topological sort to determine correct processing order\n *\n * @param dependencyMap - Map of dependencies\n * @returns Ordered array of cluster IDs\n */\n public resolveDependencyOrder(dependencyMap: Map<string, string[]>): string[] {\n const resolved: string[] = [];\n const resolving: Set<string> = new Set();\n\n const resolve = (id: string): void => {\n if (resolved.includes(id)) return;\n if (resolving.has(id)) {\n // Circular dependency detected - log warning and continue\n this.eventCallback?.(\"circular_dependency_detected\", { activityId: id });\n return;\n }\n\n resolving.add(id);\n const dependencies = dependencyMap.get(id) || [];\n\n for (const depId of dependencies) {\n resolve(depId);\n }\n\n resolving.delete(id);\n resolved.push(id);\n };\n\n for (const id of Array.from(dependencyMap.keys())) {\n resolve(id);\n }\n\n return resolved;\n }\n\n /**\n * Process rollup for a specific cluster\n * Performs standard rollup process for the cluster\n *\n * @param cluster - The cluster to process\n * @param depth - Current recursion depth for nested cluster handling\n */\n public processClusterRollup(cluster: Activity, depth: number = 0): void {\n // Perform standard rollup process for the cluster\n const nestedClusters = this.measureProcessor.measureRollupProcess(cluster);\n\n if (cluster.sequencingControls.rollupObjectiveSatisfied) {\n this.objectiveProcessor.objectiveRollupProcess(cluster);\n }\n\n if (cluster.sequencingControls.rollupProgressCompletion) {\n this.progressProcessor.activityProgressRollupProcess(cluster);\n }\n\n // Handle nested clusters with increased depth to prevent infinite recursion\n if (nestedClusters.length > 1) {\n this.processCrossClusterDependencies(cluster, nestedClusters, depth + 1);\n }\n }\n}\n","import { Activity, ActivityObjective, ObjectiveMapInfo } from \"../activity\";\nimport { CompletionStatus } from \"../../../../constants/enums\";\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * Global objective state structure\n */\nexport interface GlobalObjective {\n id: string;\n satisfiedStatus: boolean;\n satisfiedStatusKnown: boolean;\n normalizedMeasure: number;\n normalizedMeasureKnown: boolean;\n progressMeasure: number;\n progressMeasureKnown: boolean;\n completionStatus: CompletionStatus;\n completionStatusKnown: boolean;\n satisfiedByMeasure: boolean;\n minNormalizedMeasure: number | null;\n attemptCount?: number;\n attemptAbsoluteDuration?: string;\n attemptExperiencedDuration?: string;\n activityAbsoluteDuration?: string;\n activityExperiencedDuration?: string;\n location?: string;\n suspendData?: string;\n updateAttemptData?: boolean;\n}\n\n/**\n * Local objective state for synchronization\n */\nexport interface LocalObjectiveState {\n id: string;\n satisfiedStatus: boolean;\n measureStatus: boolean;\n normalizedMeasure: number;\n progressMeasure: number;\n progressMeasureStatus: boolean;\n completionStatus: CompletionStatus;\n scaledPassingScore: number | null;\n}\n\n/**\n * GlobalObjectiveSynchronizer - Handles cross-activity global objective synchronization\n * Implements SCORM 2004 global objective mapping and shared objectives\n *\n * This class is responsible for synchronizing objective state between local\n * activity objectives and shared global objectives using a two-pass approach:\n * 1. WRITE pass: All activities write their local state TO global objectives\n * 2. READ pass: All activities read FROM global objectives into local state\n *\n * This ensures correct synchronization regardless of tree traversal order.\n *\n * @spec Priority 5 Gap: Comprehensive rollup with global objective mapping\n */\nexport class GlobalObjectiveSynchronizer {\n private eventCallback: EventCallback | null;\n\n /**\n * Create a new GlobalObjectiveSynchronizer\n *\n * @param eventCallback - Optional callback for firing events\n */\n constructor(eventCallback?: EventCallback) {\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Process global objective mapping for shared objectives\n * Handles cross-activity objective synchronization and global state management\n *\n * IMPORTANT: Uses two-pass approach to ensure correct synchronization order:\n * 1. WRITE pass: All activities write their local state TO global objectives\n * 2. READ pass: All activities read FROM global objectives into local state\n *\n * This ensures that when activity A writes to a global and activity B reads from it,\n * B will see A's data regardless of tree traversal order.\n *\n * @param activity - The root activity to start processing from\n * @param globalObjectives - Global objective map\n */\n public processGlobalObjectiveMapping(\n activity: Activity,\n globalObjectives: Map<string, GlobalObjective>,\n ): void {\n try {\n this.eventCallback?.(\"global_objective_processing_started\", {\n activityId: activity.id,\n globalObjectiveCount: globalObjectives.size,\n });\n\n // Collect all activities in the tree for two-pass processing\n const allActivities: Activity[] = [];\n this.collectActivitiesRecursive(activity, allActivities);\n\n // Pass 1: WRITE - All activities write their local state to global objectives\n // This ensures all writes happen before any reads\n for (const act of allActivities) {\n this.syncGlobalObjectivesWritePhase(act, globalObjectives);\n }\n\n // Pass 2: READ - All activities read from global objectives into local state\n // Now reads can see all writes from all activities\n for (const act of allActivities) {\n this.syncGlobalObjectivesReadPhase(act, globalObjectives);\n }\n\n this.eventCallback?.(\"global_objective_processing_completed\", {\n activityId: activity.id,\n processedObjectives: globalObjectives.size,\n });\n } catch (error) {\n this.eventCallback?.(\"global_objective_processing_error\", {\n activityId: activity.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Collect all activities in the tree recursively\n *\n * @param activity - Current activity\n * @param result - Array to collect activities into\n */\n public collectActivitiesRecursive(activity: Activity, result: Activity[]): void {\n result.push(activity);\n for (const child of activity.children) {\n this.collectActivitiesRecursive(child, result);\n }\n }\n\n /**\n * Write phase: Write local objective state TO global objectives\n *\n * @param activity - The activity to process\n * @param globalObjectives - Global objective map\n */\n public syncGlobalObjectivesWritePhase(\n activity: Activity,\n globalObjectives: Map<string, GlobalObjective>,\n ): void {\n const objectives = activity.getAllObjectives();\n\n for (const objective of objectives) {\n const mapInfos =\n objective.mapInfo.length > 0\n ? objective.mapInfo\n : [this.createDefaultMapInfo(objective)];\n\n for (const mapInfo of mapInfos) {\n const targetId = mapInfo.targetObjectiveID || objective.id;\n const globalObjective = this.ensureGlobalObjectiveEntry(\n globalObjectives,\n targetId,\n objective,\n );\n\n // Only do WRITE operations in this phase\n // Only write if the objective property has been modified (dirty flag set)\n if (\n mapInfo.writeSatisfiedStatus &&\n objective.measureStatus &&\n objective.isDirty(\"satisfiedStatus\")\n ) {\n globalObjective.satisfiedStatus = objective.satisfiedStatus;\n globalObjective.satisfiedStatusKnown = true;\n objective.clearDirty(\"satisfiedStatus\");\n }\n\n if (\n mapInfo.writeNormalizedMeasure &&\n objective.measureStatus &&\n objective.isDirty(\"normalizedMeasure\")\n ) {\n globalObjective.normalizedMeasure = objective.normalizedMeasure;\n globalObjective.normalizedMeasureKnown = true;\n objective.clearDirty(\"normalizedMeasure\");\n\n if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {\n const threshold =\n objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;\n globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;\n globalObjective.satisfiedStatusKnown = true;\n // Clear satisfiedStatus dirty flag since we've just synchronized the derived value.\n // When satisfaction is derived from measure, it should always update when measure changes.\n objective.clearDirty(\"satisfiedStatus\");\n }\n }\n\n if (\n mapInfo.writeCompletionStatus &&\n objective.completionStatus !== CompletionStatus.UNKNOWN &&\n objective.isDirty(\"completionStatus\")\n ) {\n globalObjective.completionStatus = objective.completionStatus;\n globalObjective.completionStatusKnown = true;\n objective.clearDirty(\"completionStatus\");\n }\n\n if (\n mapInfo.writeProgressMeasure &&\n objective.progressMeasureStatus &&\n objective.isDirty(\"progressMeasure\")\n ) {\n globalObjective.progressMeasure = objective.progressMeasure;\n globalObjective.progressMeasureKnown = true;\n objective.clearDirty(\"progressMeasure\");\n }\n\n if (mapInfo.updateAttemptData) {\n this.updateActivityAttemptData(activity, globalObjective, objective);\n }\n }\n }\n }\n\n /**\n * Read phase: Read FROM global objectives into local state\n *\n * @param activity - The activity to process\n * @param globalObjectives - Global objective map\n */\n public syncGlobalObjectivesReadPhase(\n activity: Activity,\n globalObjectives: Map<string, GlobalObjective>,\n ): void {\n const objectives = activity.getAllObjectives();\n\n for (const objective of objectives) {\n const mapInfos =\n objective.mapInfo.length > 0\n ? objective.mapInfo\n : [this.createDefaultMapInfo(objective)];\n\n for (const mapInfo of mapInfos) {\n const targetId = mapInfo.targetObjectiveID || objective.id;\n const globalObjective = globalObjectives.get(targetId);\n\n if (!globalObjective) continue;\n\n const isPrimary = objective.isPrimary;\n\n // Only do READ operations in this phase\n if (mapInfo.readSatisfiedStatus && globalObjective.satisfiedStatusKnown) {\n objective.satisfiedStatus = globalObjective.satisfiedStatus;\n objective.measureStatus = true;\n }\n\n if (mapInfo.readNormalizedMeasure && globalObjective.normalizedMeasureKnown) {\n objective.normalizedMeasure = globalObjective.normalizedMeasure;\n objective.measureStatus = true;\n\n if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {\n const threshold =\n objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;\n objective.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;\n }\n }\n\n if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {\n objective.progressMeasure = globalObjective.progressMeasure;\n objective.progressMeasureStatus = true;\n }\n\n if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {\n objective.completionStatus = globalObjective.completionStatus as CompletionStatus;\n }\n\n // Apply primary objective changes to activity\n if (isPrimary) {\n objective.applyToActivity(activity);\n }\n\n // Fire synchronization event for monitoring/logging\n this.eventCallback?.(\"objective_synchronized\", {\n activityId: activity.id,\n objectiveId: objective.id,\n globalState: globalObjective,\n synchronizationTime: new Date().toISOString(),\n });\n }\n }\n }\n\n /**\n * Synchronize global objectives with activity-specific objectives\n * Combined read/write operation for backward compatibility\n *\n * @param activity - The activity to synchronize\n * @param globalObjectives - Global objective map\n */\n public synchronizeGlobalObjectives(\n activity: Activity,\n globalObjectives: Map<string, GlobalObjective>,\n ): void {\n const objectives = activity.getAllObjectives();\n\n for (const objective of objectives) {\n const mapInfos =\n objective.mapInfo.length > 0\n ? objective.mapInfo\n : [this.createDefaultMapInfo(objective)];\n\n for (const mapInfo of mapInfos) {\n const targetId = mapInfo.targetObjectiveID || objective.id;\n const globalObjective = this.ensureGlobalObjectiveEntry(\n globalObjectives,\n targetId,\n objective,\n );\n this.syncObjectiveState(activity, objective, mapInfo, globalObjective);\n }\n }\n }\n\n /**\n * Synchronize objective state between local and global\n * Full sync operation with both read and write\n *\n * @param activity - The activity\n * @param objective - The objective to sync\n * @param mapInfo - Map info for this objective\n * @param globalObjective - The global objective\n */\n public syncObjectiveState(\n activity: Activity,\n objective: ActivityObjective,\n mapInfo: ObjectiveMapInfo,\n globalObjective: GlobalObjective,\n ): void {\n try {\n const isPrimary = objective.isPrimary;\n const localObjective = this.getLocalObjectiveState(activity, objective, isPrimary);\n\n // Read from global to local using THIS ACTIVITY'S mapInfo directives\n // Each activity has its own read permissions for the global objective\n if (mapInfo.readSatisfiedStatus && globalObjective.satisfiedStatusKnown) {\n objective.satisfiedStatus = globalObjective.satisfiedStatus;\n objective.measureStatus = true;\n }\n\n // Read normalized measure\n if (mapInfo.readNormalizedMeasure && globalObjective.normalizedMeasureKnown) {\n objective.normalizedMeasure = globalObjective.normalizedMeasure;\n objective.measureStatus = true;\n\n if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {\n const threshold =\n objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;\n objective.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;\n }\n }\n\n if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {\n objective.progressMeasure = globalObjective.progressMeasure;\n objective.progressMeasureStatus = true;\n }\n\n if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {\n objective.completionStatus = globalObjective.completionStatus as CompletionStatus;\n }\n\n if (objective.isPrimary) {\n objective.applyToActivity(activity);\n }\n\n // Write from local to global using THIS ACTIVITY'S mapInfo directives\n // Each activity has its own write permissions for the global objective\n if (mapInfo.writeSatisfiedStatus && objective.measureStatus) {\n globalObjective.satisfiedStatus = objective.satisfiedStatus;\n globalObjective.satisfiedStatusKnown = true;\n }\n\n if (mapInfo.writeNormalizedMeasure && objective.measureStatus) {\n globalObjective.normalizedMeasure = objective.normalizedMeasure;\n globalObjective.normalizedMeasureKnown = true;\n\n if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {\n const threshold =\n objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;\n globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;\n globalObjective.satisfiedStatusKnown = true;\n }\n }\n\n if (\n mapInfo.writeCompletionStatus &&\n objective.completionStatus !== CompletionStatus.UNKNOWN\n ) {\n globalObjective.completionStatus = objective.completionStatus;\n globalObjective.completionStatusKnown = true;\n }\n\n if (mapInfo.writeProgressMeasure && objective.progressMeasureStatus) {\n globalObjective.progressMeasure = objective.progressMeasure;\n globalObjective.progressMeasureKnown = true;\n }\n\n if (mapInfo.updateAttemptData) {\n this.updateActivityAttemptData(activity, globalObjective, objective);\n }\n\n // Fire synchronization event for monitoring/logging\n this.eventCallback?.(\"objective_synchronized\", {\n activityId: activity.id,\n objectiveId: objective.id,\n localState: localObjective,\n globalState: globalObjective,\n synchronizationTime: new Date().toISOString(),\n });\n } catch (error) {\n // Log synchronization error but don't fail the rollup process\n this.eventCallback?.(\"objective_sync_error\", {\n activityId: activity.id,\n objectiveId: objective.id,\n error: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n /**\n * Ensure global objective entry exists\n *\n * @param globalObjectives - Global objectives map\n * @param targetId - Target objective ID\n * @param objective - Source objective\n * @returns The global objective entry\n */\n public ensureGlobalObjectiveEntry(\n globalObjectives: Map<string, GlobalObjective>,\n targetId: string,\n objective: ActivityObjective,\n ): GlobalObjective {\n if (!globalObjectives.has(targetId)) {\n // Create new entry if global objective doesn't exist\n // NOTE: The read/write flags stored here are for reference only.\n // Each activity uses its OWN mapInfo for read/write decisions in syncObjectiveState.\n globalObjectives.set(targetId, {\n id: targetId,\n satisfiedStatus: objective.satisfiedStatus,\n satisfiedStatusKnown: objective.satisfiedStatusKnown,\n normalizedMeasure: objective.normalizedMeasure,\n normalizedMeasureKnown: objective.measureStatus,\n progressMeasure: objective.progressMeasure,\n progressMeasureKnown: objective.progressMeasureStatus,\n completionStatus: objective.completionStatus,\n completionStatusKnown: objective.completionStatus !== CompletionStatus.UNKNOWN,\n satisfiedByMeasure: objective.satisfiedByMeasure,\n minNormalizedMeasure: objective.minNormalizedMeasure,\n });\n }\n\n return globalObjectives.get(targetId)!;\n }\n\n /**\n * Create default map info for an objective\n * Default map info should only WRITE to global objectives, not READ\n * Reading should only happen when explicitly configured via mapInfo\n * This prevents unintended overwrites of RTE-transferred data\n *\n * @param objective - The objective to create default map info for\n * @returns Default map info\n */\n public createDefaultMapInfo(objective: ActivityObjective): ObjectiveMapInfo {\n return {\n targetObjectiveID: objective.id,\n readSatisfiedStatus: false,\n writeSatisfiedStatus: true,\n readNormalizedMeasure: false,\n writeNormalizedMeasure: true,\n readCompletionStatus: false,\n writeCompletionStatus: true,\n readProgressMeasure: false,\n writeProgressMeasure: true,\n updateAttemptData: objective.isPrimary,\n };\n }\n\n /**\n * Get local objective state\n *\n * @param activity - The activity\n * @param objective - The objective\n * @param isPrimary - Whether this is the primary objective\n * @returns Local objective state\n */\n public getLocalObjectiveState(\n activity: Activity,\n objective: ActivityObjective,\n isPrimary: boolean,\n ): LocalObjectiveState {\n if (isPrimary) {\n return {\n id: objective.id,\n satisfiedStatus: activity.objectiveSatisfiedStatus,\n measureStatus: activity.objectiveMeasureStatus,\n normalizedMeasure: activity.objectiveNormalizedMeasure,\n progressMeasure: activity.progressMeasure,\n progressMeasureStatus: activity.progressMeasureStatus,\n completionStatus: activity.completionStatus,\n scaledPassingScore: activity.scaledPassingScore,\n };\n }\n\n return {\n id: objective.id,\n satisfiedStatus: objective.satisfiedStatus,\n measureStatus: objective.measureStatus,\n normalizedMeasure: objective.normalizedMeasure,\n progressMeasure: objective.progressMeasure,\n progressMeasureStatus: objective.progressMeasureStatus,\n completionStatus: objective.completionStatus,\n scaledPassingScore: objective.minNormalizedMeasure,\n };\n }\n\n /**\n * Update activity attempt data based on global objective state\n *\n * @param activity - The activity to update\n * @param globalObjective - Global objective state\n * @param objective - The local objective\n */\n public updateActivityAttemptData(\n activity: Activity,\n globalObjective: GlobalObjective,\n objective: ActivityObjective,\n ): void {\n try {\n if (!objective.isPrimary && !globalObjective.updateAttemptData) {\n return;\n }\n\n // Update attempt completion based on global objective satisfaction\n // Only if completion is NOT controlled by rollup rules\n const hasCompletionRollupRules = activity.rollupRules.rules.some(\n (rule) => rule.action === \"completed\" || rule.action === \"incomplete\",\n );\n\n if (globalObjective.satisfiedStatusKnown && globalObjective.satisfiedStatus) {\n // If global objective is satisfied, update local completion data\n // UNLESS the activity has explicit rollup rules for completion\n if (\n !hasCompletionRollupRules &&\n (activity.completionStatus === CompletionStatus.UNKNOWN ||\n activity.completionStatus === CompletionStatus.INCOMPLETE)\n ) {\n activity.completionStatus = CompletionStatus.COMPLETED;\n }\n\n // Update success status based on objective satisfaction\n if (activity.successStatus === \"unknown\") {\n activity.successStatus = \"passed\";\n }\n }\n\n // Update attempt count if global objective indicates new attempt\n if (globalObjective.attemptCount && globalObjective.attemptCount > activity.attemptCount) {\n activity.attemptCount = globalObjective.attemptCount;\n }\n\n // Update completion amount based on progress measure\n if (\n globalObjective.progressMeasureKnown &&\n globalObjective.progressMeasure !== undefined\n ) {\n activity.attemptCompletionAmount = globalObjective.progressMeasure;\n }\n\n // Update absolute duration from global timing data\n if (globalObjective.attemptAbsoluteDuration) {\n activity.attemptAbsoluteDuration = globalObjective.attemptAbsoluteDuration;\n }\n\n if (globalObjective.attemptExperiencedDuration) {\n activity.attemptExperiencedDuration = globalObjective.attemptExperiencedDuration;\n }\n\n // Update activity-level durations\n if (globalObjective.activityAbsoluteDuration) {\n activity.activityAbsoluteDuration = globalObjective.activityAbsoluteDuration;\n }\n\n if (globalObjective.activityExperiencedDuration) {\n activity.activityExperiencedDuration = globalObjective.activityExperiencedDuration;\n }\n\n // Update location if provided by global state\n if (globalObjective.location !== undefined) {\n activity.location = globalObjective.location;\n }\n\n // Update suspension state based on global objective\n if (globalObjective.suspendData !== undefined) {\n activity.isSuspended = globalObjective.suspendData.length > 0;\n }\n } catch (error) {\n // Log attempt data update error\n this.eventCallback?.(\"attempt_data_update_error\", {\n activityId: activity.id,\n error: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString(),\n });\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { RollupChildFilter } from \"../rollup/rollup_child_filter\";\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * Rollup state log entry\n */\nexport interface RollupStateLogEntry {\n activity: string;\n timestamp: string;\n state: {\n measureStatus: boolean;\n measure: number;\n satisfiedStatus: boolean;\n completionStatus: string;\n };\n}\n\n/**\n * RollupStateValidator - Validates rollup state consistency across the activity tree\n * Implements SCORM 2004 rollup state validation\n *\n * This class is responsible for validating that rollup states are consistent\n * and valid before and after processing. It can detect inconsistencies between\n * parent and child activity states.\n *\n * @spec Priority 5 Gap: Rollup state validation\n */\nexport class RollupStateValidator {\n private rollupStateLog: RollupStateLogEntry[] = [];\n private childFilter: RollupChildFilter;\n private eventCallback: EventCallback | null;\n\n /**\n * Create a new RollupStateValidator\n *\n * @param childFilter - RollupChildFilter instance for filtering children\n * @param eventCallback - Optional callback for firing events\n */\n constructor(childFilter: RollupChildFilter, eventCallback?: EventCallback) {\n this.childFilter = childFilter;\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Validate rollup state consistency across the activity tree\n * Ensures that rollup states are consistent and valid before processing\n *\n * @param rootActivity - The root activity to validate from\n * @returns True if state is consistent, false otherwise\n */\n public validateRollupStateConsistency(rootActivity: Activity): boolean {\n try {\n this.eventCallback?.(\"rollup_validation_started\", {\n activityId: rootActivity.id,\n timestamp: new Date().toISOString(),\n });\n\n const inconsistencies: string[] = [];\n\n // Validate the entire tree recursively\n this.validateActivityRollupState(rootActivity, inconsistencies);\n\n if (inconsistencies.length > 0) {\n this.eventCallback?.(\"rollup_state_inconsistencies\", {\n activityId: rootActivity.id,\n inconsistencies,\n count: inconsistencies.length,\n });\n return false;\n }\n\n this.eventCallback?.(\"rollup_validation_completed\", {\n activityId: rootActivity.id,\n result: \"consistent\",\n });\n return true;\n } catch (error) {\n this.eventCallback?.(\"rollup_validation_error\", {\n activityId: rootActivity.id,\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n }\n\n /**\n * Validate rollup state for a single activity\n *\n * @param activity - The activity to validate\n * @param inconsistencies - Array to collect inconsistencies into\n */\n public validateActivityRollupState(activity: Activity, inconsistencies: string[]): void {\n const activityId = activity.id;\n\n // Check measure status consistency\n if (activity.objectiveMeasureStatus && activity.objectiveNormalizedMeasure === null) {\n inconsistencies.push(\n `Activity ${activityId}: measure status true but normalized measure is null`,\n );\n }\n\n // Check satisfaction status consistency with measure (only when success status is known)\n if (\n activity.objectiveMeasureStatus &&\n activity.scaledPassingScore !== null &&\n activity.successStatus !== \"unknown\"\n ) {\n const expectedSatisfied =\n activity.objectiveNormalizedMeasure >= activity.scaledPassingScore;\n if (activity.objectiveSatisfiedStatus !== expectedSatisfied) {\n inconsistencies.push(\n `Activity ${activityId}: satisfaction status inconsistent with measure`,\n );\n }\n }\n\n // Check rollup controls consistency\n const controls = activity.sequencingControls;\n // Note: Having rollup data without rollup controls enabled is valid (data from content).\n // Clusters with measure data but rollup disabled could indicate an inconsistency,\n // but this is unusual yet valid if set explicitly by content. We don't flag this\n // as an error since it may be intentional.\n //\n // Rollup consideration states (RB.1.4 enhancement) are also valid and don't need\n // flagging as inconsistencies:\n // - \"ifAttempted\" with attemptCount=0: Activity will contribute once attempted\n // - \"ifNotSuspended\" with isSuspended=true: Activity is temporarily excluded\n // - \"ifNotSkipped\" with wasSkipped=true: Activity is excluded from rollup\n\n // Check children consistency and their rollup contributions\n const children = activity.getAvailableChildren();\n\n // Validate parent's rolled-up state matches children's contributions\n if (children.length > 0 && controls.rollupObjectiveSatisfied) {\n const satisfiedChildren = children.filter(\n (child) =>\n this.childFilter.checkChildForRollupSubprocess(child, \"objective\", \"satisfied\") &&\n this.childFilter.isChildSatisfiedForRollup(child),\n );\n\n const notSatisfiedChildren = children.filter(\n (child) =>\n this.childFilter.checkChildForRollupSubprocess(child, \"objective\", \"notSatisfied\") &&\n !this.childFilter.isChildSatisfiedForRollup(child),\n );\n\n // If all contributing children are satisfied, parent should be satisfied\n if (satisfiedChildren.length > 0 && notSatisfiedChildren.length === 0) {\n if (\n activity.objectiveSatisfiedStatus === false &&\n activity.rollupRules.rules.length === 0\n ) {\n inconsistencies.push(\n `Activity ${activityId}: all children satisfied but parent is not satisfied (no rollup rules to override)`,\n );\n }\n }\n }\n\n if (children.length > 0 && controls.rollupProgressCompletion) {\n const completedChildren = children.filter(\n (child) =>\n this.childFilter.checkChildForRollupSubprocess(child, \"progress\", \"completed\") &&\n this.childFilter.isChildCompletedForRollup(child),\n );\n\n const incompleteChildren = children.filter(\n (child) =>\n this.childFilter.checkChildForRollupSubprocess(child, \"progress\", \"incomplete\") &&\n !this.childFilter.isChildCompletedForRollup(child),\n );\n\n // If all contributing children are completed, parent should be completed\n if (completedChildren.length > 0 && incompleteChildren.length === 0) {\n if (\n activity.completionStatus !== \"completed\" &&\n activity.rollupRules.rules.length === 0\n ) {\n inconsistencies.push(\n `Activity ${activityId}: all children completed but parent is incomplete (no rollup rules to override)`,\n );\n }\n }\n }\n\n // Recursively validate children\n for (const child of children) {\n this.validateActivityRollupState(child, inconsistencies);\n }\n\n // Log validation state\n this.rollupStateLog.push({\n activity: activityId,\n timestamp: new Date().toISOString(),\n state: {\n measureStatus: activity.objectiveMeasureStatus,\n measure: activity.objectiveNormalizedMeasure,\n satisfiedStatus: activity.objectiveSatisfiedStatus,\n completionStatus: activity.completionStatus,\n },\n });\n }\n\n /**\n * Get rollup state log\n *\n * @returns Array of rollup state log entries\n */\n public getRollupStateLog(): RollupStateLogEntry[] {\n return [...this.rollupStateLog];\n }\n\n /**\n * Clear rollup state log\n */\n public clearRollupStateLog(): void {\n this.rollupStateLog = [];\n }\n}\n","import { Activity } from \"./activity\";\nimport { RollupChildFilter } from \"./rollup/rollup_child_filter\";\nimport { RollupRuleEvaluator } from \"./rollup/rollup_rule_evaluator\";\nimport { MeasureRollupProcessor, MeasureRollupOptions } from \"./rollup/measure_rollup\";\nimport { ObjectiveRollupProcessor } from \"./rollup/objective_rollup\";\nimport { ProgressRollupProcessor } from \"./rollup/progress_rollup\";\nimport { DurationRollupProcessor } from \"./rollup/duration_rollup\";\nimport { CrossClusterProcessor } from \"./rollup/cross_cluster_processor\";\nimport {\n GlobalObjectiveSynchronizer,\n GlobalObjective,\n} from \"./objectives/global_objective_synchronizer\";\nimport { RollupStateValidator } from \"./validation/rollup_state_validator\";\n\n/**\n * Event callback function type\n */\nexport type EventCallback = (eventType: string, data?: unknown) => void;\n\n/**\n * Enhanced Rollup Process implementation for SCORM 2004 sequencing\n * Priority 5 Gap: Comprehensive rollup with global objective mapping and complex weighting\n *\n * This class serves as the orchestrator for all rollup operations, delegating\n * specific functionality to specialized processor classes:\n *\n * - RollupChildFilter: Child activity filtering for rollup\n * - RollupRuleEvaluator: Rollup rule evaluation\n * - MeasureRollupProcessor: Measure (score) rollup\n * - ObjectiveRollupProcessor: Objective satisfaction rollup\n * - ProgressRollupProcessor: Progress (completion) rollup\n * - DurationRollupProcessor: Duration aggregation rollup\n * - GlobalObjectiveSynchronizer: Global objective synchronization\n * - RollupStateValidator: State consistency validation\n * - CrossClusterProcessor: Cross-cluster dependency handling\n *\n * @spec SN Book: RB.1.5 (Overall Rollup Process)\n */\nexport class RollupProcess {\n private childFilter: RollupChildFilter;\n private ruleEvaluator: RollupRuleEvaluator;\n private measureProcessor: MeasureRollupProcessor;\n private objectiveProcessor: ObjectiveRollupProcessor;\n private progressProcessor: ProgressRollupProcessor;\n private durationProcessor: DurationRollupProcessor;\n private globalObjectiveSynchronizer: GlobalObjectiveSynchronizer;\n private stateValidator: RollupStateValidator;\n private crossClusterProcessor: CrossClusterProcessor;\n private eventCallback: EventCallback | null;\n\n /**\n * Create a new RollupProcess orchestrator\n *\n * @param eventCallback - Optional callback for firing events\n */\n constructor(eventCallback?: EventCallback) {\n this.eventCallback = eventCallback || null;\n\n // Initialize all processors with proper dependency injection\n this.childFilter = new RollupChildFilter();\n this.ruleEvaluator = new RollupRuleEvaluator(this.childFilter);\n this.measureProcessor = new MeasureRollupProcessor(this.childFilter, eventCallback);\n this.objectiveProcessor = new ObjectiveRollupProcessor(\n this.childFilter,\n this.ruleEvaluator,\n eventCallback,\n );\n this.progressProcessor = new ProgressRollupProcessor(\n this.childFilter,\n this.ruleEvaluator,\n this.objectiveProcessor,\n eventCallback,\n );\n this.durationProcessor = new DurationRollupProcessor(eventCallback);\n this.globalObjectiveSynchronizer = new GlobalObjectiveSynchronizer(eventCallback);\n this.stateValidator = new RollupStateValidator(this.childFilter, eventCallback);\n this.crossClusterProcessor = new CrossClusterProcessor(\n this.measureProcessor,\n this.objectiveProcessor,\n this.progressProcessor,\n eventCallback,\n );\n }\n\n /**\n * Overall Rollup Process\n * Performs rollup from a given activity up through its ancestors\n * OPTIMIZATION: Stops propagating rollup when status stops changing (SCORM 2004 4.6.1)\n *\n * @spec SN Book: RB.1.5 (Overall Rollup Process)\n * @param activity - The activity to start rollup from\n * @returns Array of activities that had status changes\n */\n public overallRollupProcess(activity: Activity): Activity[] {\n const affectedActivities: Activity[] = [];\n let currentActivity: Activity | null = activity.parent; // Start from parent, not the activity itself\n let onlyDurationRollup = false;\n let isFirst = true;\n\n // Process rollup up the tree from parent to root\n while (currentActivity) {\n // Duration rollup happens FIRST, ALWAYS for cluster activities\n // This happens even when optimization is active for other rollup types\n if (currentActivity.children.length > 0) {\n this.durationProcessor.durationRollupProcess(currentActivity);\n }\n\n if (!onlyDurationRollup) {\n // Capture status BEFORE rollup\n const beforeStatus = currentActivity.captureRollupStatus();\n\n // Only perform rollup if the activity tracks status\n if (\n currentActivity.sequencingControls.rollupObjectiveSatisfied ||\n currentActivity.sequencingControls.rollupProgressCompletion\n ) {\n // Step 1: Measure Rollup Process (RB.1.1)\n if (currentActivity.children.length > 0) {\n const clusters = this.measureProcessor.measureRollupProcess(currentActivity);\n // Step 1b: Completion Measure Rollup Process (RB.1.1 b)\n this.measureProcessor.completionMeasureRollupProcess(currentActivity);\n\n // Process cross-cluster dependencies if dealing with multiple clusters\n if (clusters.length > 1) {\n this.crossClusterProcessor.processCrossClusterDependencies(\n currentActivity,\n clusters,\n );\n }\n }\n\n // Step 2: Objective Rollup Process (RB.1.2)\n if (currentActivity.sequencingControls.rollupObjectiveSatisfied) {\n this.objectiveProcessor.objectiveRollupProcess(currentActivity);\n }\n\n // Step 3: Activity Progress Rollup Process (RB.1.3)\n if (currentActivity.sequencingControls.rollupProgressCompletion) {\n this.progressProcessor.activityProgressRollupProcess(currentActivity);\n }\n }\n\n // Capture status AFTER rollup\n const afterStatus = currentActivity.captureRollupStatus();\n\n // OPTIMIZATION: Check if anything changed (skip first iteration)\n // The first activity always gets processed regardless of change\n if (!isFirst) {\n const changed = !Activity.compareRollupStatus(beforeStatus, afterStatus);\n if (!changed) {\n // No changes detected - activate optimization\n this.eventCallback?.(\"rollup_optimization_activated\", {\n activityId: currentActivity.id,\n depth: affectedActivities.length,\n });\n onlyDurationRollup = true;\n }\n }\n\n // Add to affected activities if status changed or is first iteration\n if (isFirst || !Activity.compareRollupStatus(beforeStatus, afterStatus)) {\n affectedActivities.push(currentActivity);\n }\n }\n\n // Move up the tree\n currentActivity = currentActivity.parent;\n isFirst = false;\n }\n\n return affectedActivities;\n }\n\n // ============================================================================\n // Public API Methods - Delegate to specialized processors\n // ============================================================================\n\n /**\n * Validate rollup state consistency across the activity tree\n *\n * @param rootActivity - The root activity to validate from\n * @returns True if state is consistent, false otherwise\n */\n public validateRollupStateConsistency(rootActivity: Activity): boolean {\n return this.stateValidator.validateRollupStateConsistency(rootActivity);\n }\n\n /**\n * Process global objective mapping for shared objectives\n *\n * @param activity - The root activity to start processing from\n * @param globalObjectives - Global objective map\n */\n public processGlobalObjectiveMapping(\n activity: Activity,\n globalObjectives: Map<string, GlobalObjective>,\n ): void {\n this.globalObjectiveSynchronizer.processGlobalObjectiveMapping(activity, globalObjectives);\n }\n\n /**\n * Calculate complex weighted measure for an activity\n *\n * @param activity - The parent activity\n * @param children - Child activities to weight\n * @param options - Configuration options\n * @returns Calculated weighted measure\n */\n public calculateComplexWeightedMeasure(\n activity: Activity,\n children: Activity[],\n options?: MeasureRollupOptions,\n ): number {\n return this.measureProcessor.calculateComplexWeightedMeasure(activity, children, options);\n }\n\n /**\n * Handle cross-cluster dependencies in rollup\n *\n * @param activity - The activity to process\n * @param clusters - Related activity clusters\n */\n public processCrossClusterDependencies(activity: Activity, clusters: Activity[]): void {\n this.crossClusterProcessor.processCrossClusterDependencies(activity, clusters);\n }\n\n // ============================================================================\n // Accessor methods for individual processors (for advanced use cases)\n // ============================================================================\n\n /**\n * Get the child filter processor\n */\n public getChildFilter(): RollupChildFilter {\n return this.childFilter;\n }\n\n /**\n * Get the rule evaluator processor\n */\n public getRuleEvaluator(): RollupRuleEvaluator {\n return this.ruleEvaluator;\n }\n\n /**\n * Get the measure rollup processor\n */\n public getMeasureProcessor(): MeasureRollupProcessor {\n return this.measureProcessor;\n }\n\n /**\n * Get the objective rollup processor\n */\n public getObjectiveProcessor(): ObjectiveRollupProcessor {\n return this.objectiveProcessor;\n }\n\n /**\n * Get the progress rollup processor\n */\n public getProgressProcessor(): ProgressRollupProcessor {\n return this.progressProcessor;\n }\n\n /**\n * Get the duration rollup processor\n */\n public getDurationProcessor(): DurationRollupProcessor {\n return this.durationProcessor;\n }\n\n /**\n * Get the global objective synchronizer\n */\n public getGlobalObjectiveSynchronizer(): GlobalObjectiveSynchronizer {\n return this.globalObjectiveSynchronizer;\n }\n\n /**\n * Get the state validator\n */\n public getStateValidator(): RollupStateValidator {\n return this.stateValidator;\n }\n\n /**\n * Get the cross-cluster processor\n */\n public getCrossClusterProcessor(): CrossClusterProcessor {\n return this.crossClusterProcessor;\n }\n}\n","import { Activity } from \"../activity\";\nimport { CompletionStatus, SuccessStatus } from \"../../../../constants/enums\";\n\n/**\n * Interface for CMI data provided for RTE data transfer\n */\nexport interface CMIDataForTransfer {\n completion_status?: string;\n success_status?: string;\n score?: {\n scaled?: string;\n raw?: string;\n min?: string;\n max?: string;\n };\n progress_measure?: string;\n objectives?: Array<{\n id: string;\n success_status?: string;\n completion_status?: string;\n score?: {\n scaled?: string;\n raw?: string;\n min?: string;\n max?: string;\n };\n progress_measure?: string;\n }>;\n}\n\n/**\n * Score data structure for normalization\n */\nexport interface ScoreData {\n scaled?: string;\n raw?: string;\n min?: string;\n max?: string;\n}\n\n/**\n * Event data emitted during RTE data transfer operations\n */\nexport interface RteTransferEventData {\n /** The activity ID that received the transferred data */\n activityId: string;\n /** ISO timestamp of when the transfer occurred */\n timestamp: string;\n}\n\n/**\n * Context interface for RTE data transfer operations.\n *\n * Provides callbacks to access CMI data and fire events during transfer.\n * All callbacks are invoked synchronously during the transfer process.\n *\n * @remarks\n * This interface enables dependency injection, making the RteDataTransferService\n * testable in isolation without requiring a full TerminationHandler instance.\n */\nexport interface RteDataTransferContext {\n /** Function to get CMI data from the runtime environment. May return null if no data available. */\n getCMIData: (() => CMIDataForTransfer | null) | null;\n\n /** Event callback for firing transfer events */\n fireEvent: (eventType: string, data?: RteTransferEventData) => void;\n}\n\n/** Valid completion status values per SCORM 2004 specification */\nconst VALID_COMPLETION_STATUSES: readonly string[] = [\n CompletionStatus.COMPLETED,\n CompletionStatus.INCOMPLETE,\n CompletionStatus.UNKNOWN,\n];\n\n/** Valid success status values per SCORM 2004 specification */\nconst VALID_SUCCESS_STATUSES: readonly string[] = [\n SuccessStatus.PASSED,\n SuccessStatus.FAILED,\n SuccessStatus.UNKNOWN,\n];\n\n/**\n * Validates and returns a CompletionStatus value, or null if invalid\n */\nfunction validateCompletionStatus(value: string | undefined): CompletionStatus | null {\n if (value && VALID_COMPLETION_STATUSES.includes(value)) {\n return value as CompletionStatus;\n }\n return null;\n}\n\n/**\n * Validates and returns a SuccessStatus value, or null if invalid\n */\nfunction validateSuccessStatus(value: string | undefined): SuccessStatus | null {\n if (value && VALID_SUCCESS_STATUSES.includes(value)) {\n return value as SuccessStatus;\n }\n return null;\n}\n\n/**\n * RteDataTransferService\n *\n * Handles the transfer of CMI data from the runtime environment (RTE) to activity state.\n * Extracted from TerminationHandler to follow Single Responsibility Principle.\n *\n * Responsibilities:\n * - Transfer primary objective data (cmi.completion_status, cmi.success_status, cmi.score.*)\n * - Transfer non-primary objectives (cmi.objectives[n].*)\n * - Normalize scores from raw/min/max to scaled format\n * - Set dirty flags to ensure proper global objective mapping\n *\n * @spec SN Book: RTE Data Transfer (referenced in UP.4 End Attempt Process)\n */\nexport class RteDataTransferService {\n private context: RteDataTransferContext;\n\n constructor(context: RteDataTransferContext) {\n this.context = context;\n }\n\n /**\n * Transfer RTE Data to Activity\n * Transfers CMI data from runtime environment to activity state\n * Called at the start of endAttemptProcess to ensure proper data transfer\n * @param {Activity} activity - The activity to transfer data to\n */\n public transferRteData(activity: Activity): void {\n if (!this.context.getCMIData) {\n // No CMI data provider, skip transfer\n return;\n }\n\n const cmiData = this.context.getCMIData();\n if (!cmiData) {\n return;\n }\n\n // Transfer primary objective data (cmi.* level)\n this.transferPrimaryObjective(activity, cmiData);\n\n // Transfer non-primary objectives (cmi.objectives[n])\n this.transferNonPrimaryObjectives(activity, cmiData);\n\n this.context.fireEvent(\"onRteDataTransfer\", {\n activityId: activity.id,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Transfer primary objective data from CMI to activity\n * @param {Activity} activity - The activity to transfer data to\n * @param {CMIDataForTransfer} cmiData - CMI data from runtime\n */\n public transferPrimaryObjective(activity: Activity, cmiData: CMIDataForTransfer): void {\n // Transfer completion status\n const validatedCompletionStatus = validateCompletionStatus(cmiData.completion_status);\n if (validatedCompletionStatus && validatedCompletionStatus !== CompletionStatus.UNKNOWN) {\n activity.completionStatus = validatedCompletionStatus;\n activity.attemptProgressStatus = true;\n }\n\n // Collect values that need to be transferred\n let hasSuccessStatus = false;\n let successStatus = false;\n let hasNormalizedMeasure = false;\n let normalizedScore = 0;\n\n // Transfer success status\n const validatedSuccessStatus = validateSuccessStatus(cmiData.success_status);\n if (validatedSuccessStatus && validatedSuccessStatus !== SuccessStatus.UNKNOWN) {\n successStatus = validatedSuccessStatus === SuccessStatus.PASSED;\n hasSuccessStatus = true;\n activity.objectiveSatisfiedStatus = successStatus;\n activity.objectiveSatisfiedStatusKnown = true; // Mark as known when transferred from CMI\n activity.successStatus = validatedSuccessStatus;\n // Set measureStatus to true so global objective mapping can write to global map\n // Per SCORM 2004 SN Book, when success_status is known, the objective has known status\n activity.objectiveMeasureStatus = true;\n }\n\n // Transfer score (with normalization support)\n if (cmiData.score) {\n const normalized = this.normalizeScore(cmiData.score);\n if (normalized !== null) {\n normalizedScore = normalized;\n hasNormalizedMeasure = true;\n activity.objectiveNormalizedMeasure = normalizedScore;\n activity.objectiveMeasureStatus = true;\n }\n }\n\n // Use initializeFromCMI to ensure dirty flags are set for primary objective\n // This must be called after collecting both values to avoid overwriting\n if (activity.primaryObjective && (hasSuccessStatus || hasNormalizedMeasure)) {\n const finalStatus = hasSuccessStatus\n ? successStatus\n : activity.primaryObjective.satisfiedStatus;\n const finalMeasure = hasNormalizedMeasure\n ? normalizedScore\n : activity.primaryObjective.normalizedMeasure;\n const measureStatus = hasSuccessStatus || hasNormalizedMeasure;\n\n activity.primaryObjective.initializeFromCMI(finalStatus, finalMeasure, measureStatus);\n\n if (hasSuccessStatus) {\n activity.primaryObjective.satisfiedStatusKnown = true; // Mark as known\n activity.primaryObjective.progressStatus = true;\n }\n }\n\n // Transfer progress measure\n if (cmiData.progress_measure && cmiData.progress_measure !== \"\") {\n const progressMeasure = parseFloat(cmiData.progress_measure);\n if (!isNaN(progressMeasure)) {\n activity.progressMeasure = progressMeasure;\n activity.progressMeasureStatus = true;\n\n if (activity.primaryObjective) {\n activity.primaryObjective.progressMeasure = progressMeasure;\n activity.primaryObjective.progressMeasureStatus = true;\n }\n }\n }\n }\n\n /**\n * Transfer non-primary objective data from CMI to activity objectives\n * Only transfers changed values to protect global objectives\n * @param {Activity} activity - The activity to transfer data to\n * @param {CMIDataForTransfer} cmiData - CMI data from runtime\n */\n public transferNonPrimaryObjectives(activity: Activity, cmiData: CMIDataForTransfer): void {\n if (!cmiData.objectives || cmiData.objectives.length === 0) {\n return;\n }\n\n for (const cmiObjective of cmiData.objectives) {\n if (!cmiObjective.id) {\n continue;\n }\n\n // Find matching activity objective by ID\n const activityObjectiveMatch = activity.getObjectiveById(cmiObjective.id);\n if (!activityObjectiveMatch || activityObjectiveMatch.isPrimary) {\n // Skip if not found or if it's the primary objective (already handled)\n continue;\n }\n\n const activityObjective = activityObjectiveMatch.objective;\n\n // Track whether we need to initialize from CMI\n let hasSuccessStatus = false;\n let successStatus = false;\n let hasNormalizedMeasure = false;\n let normalizedScore = 0;\n\n // Transfer success status (only if changed during runtime)\n const validatedObjSuccessStatus = validateSuccessStatus(cmiObjective.success_status);\n if (validatedObjSuccessStatus && validatedObjSuccessStatus !== SuccessStatus.UNKNOWN) {\n successStatus = validatedObjSuccessStatus === SuccessStatus.PASSED;\n hasSuccessStatus = true;\n activityObjective.progressStatus = true;\n }\n\n // Transfer completion status\n const validatedObjCompletionStatus = validateCompletionStatus(cmiObjective.completion_status);\n if (validatedObjCompletionStatus && validatedObjCompletionStatus !== CompletionStatus.UNKNOWN) {\n activityObjective.completionStatus = validatedObjCompletionStatus;\n }\n\n // Transfer score (with normalization)\n if (cmiObjective.score) {\n const normalized = this.normalizeScore(cmiObjective.score);\n if (normalized !== null) {\n normalizedScore = normalized;\n hasNormalizedMeasure = true;\n }\n }\n\n // If we have either success status or normalized measure from CMI, use initializeFromCMI\n // to ensure dirty flags are set even if values match defaults\n if (hasSuccessStatus || hasNormalizedMeasure) {\n const finalStatus = hasSuccessStatus ? successStatus : activityObjective.satisfiedStatus;\n const finalMeasure = hasNormalizedMeasure\n ? normalizedScore\n : activityObjective.normalizedMeasure;\n const measureStatus = hasNormalizedMeasure;\n activityObjective.initializeFromCMI(finalStatus, finalMeasure, measureStatus);\n }\n\n // Transfer progress measure\n if (cmiObjective.progress_measure && cmiObjective.progress_measure !== \"\") {\n const progressMeasure = parseFloat(cmiObjective.progress_measure);\n if (!isNaN(progressMeasure)) {\n activityObjective.progressMeasure = progressMeasure;\n activityObjective.progressMeasureStatus = true;\n }\n }\n }\n }\n\n /**\n * Normalize score from raw/min/max if scaled is not available\n * Implements ScaleRawScore process\n * @param {ScoreData} score - Score object with scaled, raw, min, max\n * @return {number | null} - Normalized score or null if cannot normalize\n */\n public normalizeScore(score: ScoreData): number | null {\n // If scaled score exists, use it directly\n if (score.scaled && score.scaled !== \"\") {\n const scaled = parseFloat(score.scaled);\n if (!isNaN(scaled)) {\n return scaled;\n }\n }\n\n // If no scaled score, try to calculate from raw/min/max\n if (\n score.raw &&\n score.raw !== \"\" &&\n score.min &&\n score.min !== \"\" &&\n score.max &&\n score.max !== \"\"\n ) {\n const raw = parseFloat(score.raw);\n const min = parseFloat(score.min);\n const max = parseFloat(score.max);\n\n if (!isNaN(raw) && !isNaN(min) && !isNaN(max) && max > min) {\n // ScaleRawScore formula: scaled = (raw - min) / (max - min)\n const normalized = (raw - min) / (max - min);\n\n // Clamp to [-1, 1] range per SCORM spec\n return Math.max(-1, Math.min(1, normalized));\n }\n }\n\n return null;\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { SequencingProcess, SequencingRequestType, PostConditionResult } from \"../sequencing_process\";\nimport { RollupProcess } from \"../rollup_process\";\nimport { RuleActionType } from \"../sequencing_rules\";\nimport { SelectionRandomization } from \"../selection_randomization\";\nimport { RteDataTransferService } from \"./rte_data_transfer\";\nimport type { RteDataTransferContext, CMIDataForTransfer } from \"./rte_data_transfer\";\n\n// Re-export CMIDataForTransfer for backward compatibility\nexport type { CMIDataForTransfer } from \"./rte_data_transfer\";\n\n/**\n * Result of Termination Request Process\n * Contains the termination result, sequencing request, and validity\n * @spec SN Book: TB.2.3 (Termination Request Process)\n */\nexport interface TerminationResult {\n terminationRequest: SequencingRequestType;\n sequencingRequest: SequencingRequestType | null;\n exception: string | null;\n valid: boolean;\n}\n\n/**\n * Options for configuring the TerminationHandler\n */\nexport interface TerminationHandlerOptions {\n getCMIData?: () => CMIDataForTransfer;\n is4thEdition?: boolean;\n}\n\n/**\n * TerminationHandler\n *\n * Handles all termination-related processing for the SCORM 2004 sequencing engine.\n * Extracted from OverallSequencingProcess to follow Single Responsibility Principle.\n *\n * Responsibilities:\n * - Process termination requests (EXIT, EXIT_ALL, ABANDON, ABANDON_ALL, SUSPEND_ALL)\n * - Handle post-condition loops for EXIT_PARENT cascading\n * - End attempts and cleanup activity states\n * - Transfer RTE data to activity state\n *\n * @spec SN Book: TB.2.3 (Termination Request Process)\n */\nexport class TerminationHandler {\n private activityTree: ActivityTree;\n private sequencingProcess: SequencingProcess;\n private rollupProcess: RollupProcess;\n private globalObjectiveMap: Map<string, any>;\n private eventCallback: ((eventType: string, data?: any) => void) | null;\n private getCMIData: (() => CMIDataForTransfer) | null;\n private is4thEdition: boolean;\n private invalidateCacheCallback: (() => void) | null = null;\n private _rteDataTransferService: RteDataTransferService;\n\n constructor(\n activityTree: ActivityTree,\n sequencingProcess: SequencingProcess,\n rollupProcess: RollupProcess,\n globalObjectiveMap: Map<string, any>,\n eventCallback: ((eventType: string, data?: any) => void) | null = null,\n options?: TerminationHandlerOptions\n ) {\n this.activityTree = activityTree;\n this.sequencingProcess = sequencingProcess;\n this.rollupProcess = rollupProcess;\n this.globalObjectiveMap = globalObjectiveMap;\n this.eventCallback = eventCallback;\n this.getCMIData = options?.getCMIData || null;\n this.is4thEdition = options?.is4thEdition || false;\n\n // Initialize RTE data transfer service\n const rteContext: RteDataTransferContext = {\n getCMIData: this.getCMIData,\n fireEvent: (eventType: string, data?: any) => this.fireEvent(eventType, data),\n };\n this._rteDataTransferService = new RteDataTransferService(rteContext);\n }\n\n /**\n * Set callback to invalidate navigation cache after state changes\n */\n public setInvalidateCacheCallback(callback: () => void): void {\n this.invalidateCacheCallback = callback;\n }\n\n /**\n * Enhanced Termination Request Process\n * Processes termination requests with post-condition loop for EXIT_PARENT handling\n * Implements missing post-condition loop per SCORM 2004 3rd Edition TB.2.3\n * @spec SN Book: TB.2.3 (Termination Request Process)\n * @param {SequencingRequestType} request - The termination request\n * @param {boolean} hasSequencingRequest - Whether a sequencing request follows\n * @param {string} exitType - The cmi.exit value (logout, normal, suspend, time-out, or empty)\n * @return {TerminationResult} - Termination result with sequencing request\n */\n public processTerminationRequest(\n request: SequencingRequestType,\n hasSequencingRequest: boolean = false,\n exitType?: string\n ): TerminationResult {\n const currentActivity = this.activityTree.currentActivity;\n\n if (!currentActivity) {\n return {\n terminationRequest: request,\n sequencingRequest: null,\n exception: \"TB.2.3-1\",\n valid: false,\n };\n }\n\n // TB.2.3-2: Check if trying to terminate already-terminated activity\n if (\n (request === SequencingRequestType.EXIT ||\n request === SequencingRequestType.ABANDON) &&\n !currentActivity.isActive\n ) {\n return {\n terminationRequest: request,\n sequencingRequest: null,\n exception: \"TB.2.3-2\",\n valid: false,\n };\n }\n\n // Enhanced logging for debugging\n this.fireEvent(\"onTerminationRequestProcessing\", {\n request,\n hasSequencingRequest,\n currentActivity: currentActivity.id,\n exitType,\n });\n\n // REQ-NAV-025: Handle logout exit - treat as exitAll navigation request\n if (exitType === \"logout\") {\n this.fireEvent(\"onSequencingDebug\", {\n message: \"cmi.exit='logout' detected, treating as EXIT_ALL\",\n activityId: currentActivity.id,\n });\n return this.handleExitAll(currentActivity);\n }\n\n // Handle different termination types\n switch (request) {\n case SequencingRequestType.EXIT:\n return this.handleExit(currentActivity, hasSequencingRequest);\n\n case SequencingRequestType.EXIT_ALL:\n return this.handleExitAll(currentActivity);\n\n case SequencingRequestType.ABANDON:\n return this.handleAbandon(currentActivity, hasSequencingRequest);\n\n case SequencingRequestType.ABANDON_ALL:\n return this.handleAbandonAll(currentActivity);\n\n case SequencingRequestType.SUSPEND_ALL:\n return this.handleSuspendAll(currentActivity);\n\n default:\n return {\n terminationRequest: request,\n sequencingRequest: null,\n exception: \"TB.2.3-7\",\n valid: false,\n };\n }\n }\n\n /**\n * Handle EXIT termination with post-condition loop (TB.2.3 step 3)\n * Implements the do-while loop for EXIT_PARENT cascading\n * @param {Activity} currentActivity - The current activity\n * @param {boolean} hasSequencingRequest - Whether a sequencing request follows\n * @return {TerminationResult} - The termination result\n */\n /**\n * Handle EXIT termination for an activity\n * Made public for backward compatibility with tests\n * @param {Activity} currentActivity - The activity being terminated\n * @param {boolean} hasSequencingRequest - Whether a sequencing request follows\n * @return {TerminationResult} - The termination result\n */\n public handleExitTermination(\n currentActivity: Activity,\n hasSequencingRequest: boolean\n ): TerminationResult {\n return this.handleExit(currentActivity, hasSequencingRequest);\n }\n\n private handleExit(\n currentActivity: Activity,\n hasSequencingRequest: boolean\n ): TerminationResult {\n // TB.2.3 step 3.0: For cluster activities, terminate descendant attempts first\n if (currentActivity.children.length > 0) {\n this.terminateDescendants(currentActivity);\n }\n\n // TB.2.3 step 3.1: Apply End Attempt Process\n this.endAttempt(currentActivity);\n\n // TB.2.3 step 3.2: Apply Sequencing Exit Action Rules Subprocess\n const exitActionResult = this.evaluateExitRules(currentActivity);\n if (exitActionResult.action === \"EXIT_ALL\") {\n // Exit action changed termination to EXIT_ALL\n return this.handleExitAll(currentActivity);\n } else if (exitActionResult.action === \"EXIT_PARENT\") {\n // Exit action requests exit from parent\n if (currentActivity.parent) {\n // Move to parent and end its attempt\n this.activityTree.currentActivity = currentActivity.parent;\n this.endAttempt(this.activityTree.currentActivity);\n }\n // Continue to post-condition evaluation on the new current activity (parent or original)\n }\n\n // TB.2.3 step 3.3: POST-CONDITION LOOP\n let processedExit: boolean;\n let postConditionResult: PostConditionResult;\n\n do {\n // TB.2.3 step 3.3.1: Set processedExit to false\n processedExit = false;\n\n // TB.2.3 step 3.3.2: Apply Sequencing Post Condition Rules Subprocess\n postConditionResult = this.evaluatePostConditions(\n this.activityTree.currentActivity || currentActivity\n );\n\n // TB.2.3 step 3.3.3: If returns EXIT_ALL, change termination type and break\n if (postConditionResult.terminationRequest === SequencingRequestType.EXIT_ALL) {\n this.fireEvent(\"onPostConditionExitAll\", {\n activity: (this.activityTree.currentActivity || currentActivity).id,\n });\n return this.handleExitAll(this.activityTree.root!);\n }\n\n // TB.2.3 step 3.3.4: If returns EXIT_PARENT, move up and continue loop\n if (postConditionResult.terminationRequest === SequencingRequestType.EXIT_PARENT) {\n const current = this.activityTree.currentActivity || currentActivity;\n\n if (!current.parent) {\n // TB.2.3-4: Cannot EXIT_PARENT from root\n return {\n terminationRequest: SequencingRequestType.EXIT_PARENT,\n sequencingRequest: null,\n exception: \"TB.2.3-4\",\n valid: false,\n };\n } else {\n // TB.2.3 step 3.3.4.1: Move to parent\n this.activityTree.currentActivity = current.parent;\n\n // TB.2.3 step 3.3.4.1.2: Apply End Attempt Process to parent\n this.endAttempt(this.activityTree.currentActivity);\n\n // TB.2.3 step 3.3.4.1.3: Set processedExit = true to continue loop\n processedExit = true;\n\n this.fireEvent(\"onPostConditionExitParent\", {\n fromActivity: current.id,\n toActivity: this.activityTree.currentActivity.id,\n });\n }\n }\n\n // TB.2.3 step 3.3.5: Check if at root without retry\n // Only check atRoot when NOT in middle of EXIT_PARENT cascade (processedExit = false)\n // If processedExit is true, we need to continue loop to evaluate the new activity's post-conditions\n if (!processedExit) {\n const atRoot =\n (this.activityTree.currentActivity || currentActivity) ===\n this.activityTree.root;\n if (\n atRoot &&\n postConditionResult.sequencingRequest !== SequencingRequestType.RETRY\n ) {\n // Return EXIT sequencing request (ends session)\n return {\n terminationRequest: SequencingRequestType.EXIT,\n sequencingRequest: SequencingRequestType.EXIT,\n exception: null,\n valid: true,\n };\n }\n }\n } while (processedExit);\n\n // TB.2.3 step 3.6: Return sequencing request from post-condition\n // Move to parent if no sequencing request follows (neither original nor post-condition)\n if (!hasSequencingRequest && !postConditionResult.sequencingRequest) {\n const current = this.activityTree.currentActivity || currentActivity;\n if (current.parent) {\n // Set parent as current without activating it\n // The parent should remain inactive if it was terminated by the EXIT_PARENT cascade\n this.activityTree.setCurrentActivityWithoutActivation(current.parent);\n }\n }\n\n return {\n terminationRequest: SequencingRequestType.EXIT,\n sequencingRequest: postConditionResult.sequencingRequest,\n exception: null,\n valid: true,\n };\n }\n\n /**\n * Handle EXIT_ALL termination (TB.2.3 step 4)\n * @param {Activity} _currentActivity - The current activity (unused but kept for consistency)\n * @return {TerminationResult} - The termination result\n */\n private handleExitAll(_currentActivity: Activity): TerminationResult {\n // TB.2.3 step 4.1: Terminate descendant attempts from root\n if (this.activityTree.root) {\n this.handleMultiLevelExit(this.activityTree.root);\n }\n\n // TB.2.3 step 4.2: End attempt on root\n if (this.activityTree.root) {\n this.endAttempt(this.activityTree.root);\n }\n\n // TB.2.3 step 4.3: Clear current activity\n this.activityTree.currentActivity = null;\n\n // Clean up suspended activities\n this.cleanupSuspendedActivity();\n\n // TB.2.3 step 4.4: Return EXIT sequencing request (ends session)\n return {\n terminationRequest: SequencingRequestType.EXIT_ALL,\n sequencingRequest: SequencingRequestType.EXIT,\n exception: null,\n valid: true,\n };\n }\n\n /**\n * Handle ABANDON termination (TB.2.3 step 6)\n * @param {Activity} currentActivity - The current activity\n * @param {boolean} hasSequencingRequest - Whether a sequencing request follows\n * @return {TerminationResult} - The termination result\n */\n private handleAbandon(\n currentActivity: Activity,\n hasSequencingRequest: boolean\n ): TerminationResult {\n // TB.2.3 step 6.1: Set activity as not active (no attempt end)\n currentActivity.isActive = false;\n\n // TB.2.3 step 6.2: Move to parent if no sequencing follows\n if (!hasSequencingRequest) {\n this.activityTree.currentActivity = currentActivity.parent;\n }\n\n return {\n terminationRequest: SequencingRequestType.ABANDON,\n sequencingRequest: null,\n exception: null,\n valid: true,\n };\n }\n\n /**\n * Handle ABANDON_ALL termination (TB.2.3 step 7)\n * @param {Activity} currentActivity - The current activity\n * @return {TerminationResult} - The termination result\n */\n private handleAbandonAll(currentActivity: Activity): TerminationResult {\n // TB.2.3 step 7.1: Form the activity path from current to root\n const activityPath: Activity[] = [];\n let current: Activity | null = currentActivity;\n while (current !== null) {\n activityPath.push(current);\n current = current.parent;\n }\n\n // TB.2.3 step 7.2: If the activity path is empty\n if (activityPath.length === 0) {\n return {\n terminationRequest: SequencingRequestType.ABANDON_ALL,\n sequencingRequest: null,\n exception: \"TB.2.3-6\",\n valid: false,\n };\n }\n\n // TB.2.3 step 7.3: For each activity in the activity path, set not active\n for (const activity of activityPath) {\n activity.isActive = false;\n }\n\n // TB.2.3 step 7.4: Clear current activity\n this.activityTree.currentActivity = null;\n\n // Clean up suspended activities\n this.cleanupSuspendedActivity();\n\n return {\n terminationRequest: SequencingRequestType.ABANDON_ALL,\n sequencingRequest: null,\n exception: null,\n valid: true,\n };\n }\n\n /**\n * Handle SUSPEND_ALL termination (TB.2.3 step 5)\n * Implements TB.2.3 steps 5.1-5.7 for SUSPEND_ALL processing\n * @param {Activity} currentActivity - The current activity\n * @return {TerminationResult} - The termination result\n */\n private handleSuspendAll(currentActivity: Activity): TerminationResult {\n // TB.2.3 steps 5.1-5.6: Suspend current activity and all ancestors, set current to root\n const suspendResult = this.processSuspendAllRequest(currentActivity);\n\n // Check if suspend failed\n if (!suspendResult.valid) {\n return suspendResult;\n }\n\n // TB.2.3 5.7: Return EXIT sequencing request to end session\n // Note: Per SCORM spec, after SUSPEND_ALL returns EXIT, the session ends.\n // The content unloads and currentActivity is cleared during termination.\n // When RESUME_ALL is called in the next session, currentActivity will be null.\n\n return {\n terminationRequest: SequencingRequestType.SUSPEND_ALL,\n sequencingRequest: SequencingRequestType.EXIT,\n exception: null,\n valid: true,\n };\n }\n\n /**\n * Handle Suspend All Request\n * Implements TB.2.3 steps 5.1-5.6 from SCORM 2004 reference\n * Suspends all activities in the path from current activity to root\n * @param {Activity} currentActivity - Current activity to suspend\n * @return {TerminationResult} - Result with validation status\n */\n private processSuspendAllRequest(currentActivity: Activity): TerminationResult {\n const rootActivity = this.activityTree.root;\n\n // TB.2.3 5.1: Validation - Check if current activity is defined\n if (!currentActivity || !rootActivity) {\n this.fireEvent(\"onSuspendError\", {\n exception: \"TB.2.3-1\",\n message: \"No current activity to suspend\",\n activity: currentActivity?.id,\n });\n return {\n terminationRequest: SequencingRequestType.SUSPEND_ALL,\n sequencingRequest: null,\n exception: \"TB.2.3-1\",\n valid: false,\n };\n }\n\n // TB.2.3-3: Check if trying to suspend inactive/unsuspended root activity\n if (\n currentActivity === rootActivity &&\n !currentActivity.isActive &&\n !currentActivity.isSuspended\n ) {\n this.fireEvent(\"onSuspendError\", {\n exception: \"TB.2.3-3\",\n message: \"Nothing to suspend (root activity)\",\n activity: currentActivity.id,\n });\n return {\n terminationRequest: SequencingRequestType.SUSPEND_ALL,\n sequencingRequest: null,\n exception: \"TB.2.3-3\",\n valid: false,\n };\n }\n\n // TB.2.3 5.1-5.2: Set the suspended activity reference\n this.activityTree.suspendedActivity = currentActivity;\n\n // TB.2.3 5.3: Form activity path from current activity to root (inclusive)\n // We walk up the tree from current to root\n const suspendedActivity = currentActivity;\n const activityPath: Activity[] = [];\n let current: Activity | null = suspendedActivity;\n while (current !== null) {\n activityPath.push(current);\n current = current.parent;\n }\n\n // TB.2.3 5.4: Check if path is empty\n if (activityPath.length === 0) {\n this.fireEvent(\"onSuspendError\", {\n exception: \"TB.2.3-5\",\n message: \"Activity path is empty\",\n activity: suspendedActivity?.id,\n });\n return {\n terminationRequest: SequencingRequestType.SUSPEND_ALL,\n sequencingRequest: null,\n exception: \"TB.2.3-5\",\n valid: false,\n };\n }\n\n // TB.2.3 5.5: For each activity in the path, suspend it\n // 5.5.1: Set Activity is Active = false\n // 5.5.2: Set Activity is Suspended = true\n for (const activity of activityPath) {\n activity.isActive = false;\n activity.isSuspended = true;\n }\n\n // TB.2.3 5.6: Set current activity to root of activity tree\n // Note: The ActivityTree setter automatically sets isActive = true,\n // but we need root to remain suspended, so we override it\n this.activityTree.currentActivity = rootActivity;\n rootActivity.isActive = false; // Keep root suspended\n\n // Log suspend event with full path information\n this.fireEvent(\"onActivitySuspended\", {\n activity: suspendedActivity?.id,\n suspendedPath: activityPath.map((a) => a.id),\n pathLength: activityPath.length,\n timestamp: new Date().toISOString(),\n });\n\n // Return success\n return {\n terminationRequest: SequencingRequestType.SUSPEND_ALL,\n sequencingRequest: null,\n exception: null,\n valid: true,\n };\n }\n\n /**\n * Enhanced Exit Action Rules Subprocess with recursion detection\n * @param {Activity} activity - Activity to evaluate\n * @param {number} recursionDepth - Current recursion depth\n * @return {{action: string | null, recursionDepth: number}} - Exit action result\n */\n public evaluateExitRules(\n activity: Activity,\n recursionDepth: number = 0\n ): { action: string | null; recursionDepth: number } {\n // Increment recursion depth to detect infinite loops\n recursionDepth++;\n\n // Check if activity has exit action rules\n const exitRules = activity.sequencingRules.exitConditionRules;\n\n for (const rule of exitRules) {\n // Evaluate the rule conditions\n let conditionsMet: boolean;\n\n // Check rule condition combination\n if (rule.conditionCombination === \"all\") {\n conditionsMet = rule.conditions.every((condition) =>\n condition.evaluate(activity)\n );\n } else {\n conditionsMet = rule.conditions.some((condition) =>\n condition.evaluate(activity)\n );\n }\n\n if (conditionsMet) {\n // Return the action to take with recursion tracking\n if (rule.action === RuleActionType.EXIT_PARENT) {\n return { action: \"EXIT_PARENT\", recursionDepth };\n } else if (rule.action === RuleActionType.EXIT_ALL) {\n return { action: \"EXIT_ALL\", recursionDepth };\n }\n }\n }\n\n return { action: null, recursionDepth };\n }\n\n /**\n * Integrate Post-Condition Rules Subprocess\n * @param {Activity} activity - Activity to evaluate post-conditions for\n * @return {PostConditionResult} - Post-condition result with sequencing and termination requests\n */\n public evaluatePostConditions(activity: Activity): PostConditionResult {\n // Evaluate post-condition rules using the sequencing process\n const postResult = this.sequencingProcess.evaluatePostConditionRules(activity);\n\n if (postResult.sequencingRequest || postResult.terminationRequest) {\n // Log the post-condition action for tracking\n this.fireEvent(\"onPostConditionEvaluated\", {\n activity: activity.id,\n sequencingRequest: postResult.sequencingRequest,\n terminationRequest: postResult.terminationRequest,\n timestamp: new Date().toISOString(),\n });\n }\n\n return postResult;\n }\n\n /**\n * Handle Multi-Level Exit Actions\n * @param {Activity} rootActivity - Root activity to start from\n */\n private handleMultiLevelExit(rootActivity: Activity): void {\n // Process exit actions at each level systematically\n this.processExitAtLevel(rootActivity, 0);\n\n // Then terminate all activities\n this.terminateAll(rootActivity);\n }\n\n /**\n * Process exit actions at specific level\n * @param {Activity} activity - Activity to process\n * @param {number} level - Current level in hierarchy\n */\n private processExitAtLevel(activity: Activity, level: number): void {\n // Process exit actions for this activity\n const exitAction = this.evaluateExitRules(activity, 0);\n\n if (exitAction.action) {\n this.fireEvent(\"onMultiLevelExitAction\", {\n activity: activity.id,\n level,\n action: exitAction.action,\n });\n }\n\n // Recursively process children\n for (const child of activity.children) {\n this.processExitAtLevel(child, level + 1);\n }\n }\n\n /**\n * Perform Complex Suspended Activity Cleanup\n */\n public cleanupSuspendedActivity(): void {\n const suspendedActivity = this.activityTree.suspendedActivity;\n\n if (suspendedActivity) {\n // Clear suspended state from the activity and all its ancestors\n let current: Activity | null = suspendedActivity;\n const cleanedActivities: string[] = [];\n\n while (current) {\n if (current.isSuspended) {\n current.isSuspended = false;\n cleanedActivities.push(current.id);\n }\n current = current.parent;\n }\n\n // Clear suspended activity reference\n this.activityTree.suspendedActivity = null;\n\n // Fire cleanup event\n this.fireEvent(\"onSuspendedActivityCleanup\", {\n cleanedActivities,\n originalSuspendedActivity: suspendedActivity.id,\n });\n }\n }\n\n /**\n * Terminate all activities in the tree\n * @param {Activity} activity - The activity to start from (usually root)\n */\n private terminateAll(activity: Activity): void {\n // Recursively terminate all children first\n for (const child of activity.children) {\n this.terminateAll(child);\n }\n\n // Then terminate this activity\n if (activity.isActive) {\n this.endAttempt(activity);\n }\n }\n\n /**\n * Terminate Descendent Attempts Process (UP.3)\n * Recursively terminates all active descendant attempts\n * @param {Activity} activity - The activity whose descendants to terminate\n */\n public terminateDescendants(activity: Activity): void {\n // Process all children\n for (const child of activity.children) {\n // Recursively terminate descendants first\n if (child.children.length > 0) {\n this.terminateDescendants(child);\n }\n\n // Check exit rules for the child\n const exitAction = this.exitActionRulesSubprocess(child);\n\n // Terminate the child if it's active\n if (child.isActive) {\n // Apply exit action if any\n if (exitAction === \"EXIT_ALL\") {\n // Recursively terminate all descendants\n this.terminateDescendants(child);\n }\n\n // End the attempt\n this.endAttempt(child);\n }\n }\n }\n\n /**\n * Exit Action Rules Subprocess (TB.2.1)\n * Evaluates exit action rules for the current activity\n * @param {Activity} activity - The activity to evaluate\n * @return {string | null} - The exit action to take, or null if none\n */\n private exitActionRulesSubprocess(activity: Activity): string | null {\n // Check if activity has exit action rules\n const exitRules = activity.sequencingRules.exitConditionRules;\n\n for (const rule of exitRules) {\n // Evaluate the rule conditions\n let conditionsMet: boolean;\n\n // Check rule condition combination\n if (rule.conditionCombination === \"all\") {\n conditionsMet = rule.conditions.every((condition) =>\n condition.evaluate(activity)\n );\n } else {\n conditionsMet = rule.conditions.some((condition) =>\n condition.evaluate(activity)\n );\n }\n\n if (conditionsMet) {\n // Return the action to take\n if (rule.action === RuleActionType.EXIT_PARENT) {\n return \"EXIT_PARENT\";\n } else if (rule.action === RuleActionType.EXIT_ALL) {\n return \"EXIT_ALL\";\n }\n }\n }\n\n return null;\n }\n\n /**\n * Clear Suspended Activity Subprocess (DB.2.1)\n * Clears the suspended activity state\n */\n public clearSuspendedActivity(): void {\n if (this.activityTree.suspendedActivity) {\n // Clear suspended state from the activity and all its ancestors\n let current: Activity | null = this.activityTree.suspendedActivity;\n while (current) {\n current.isSuspended = false;\n current = current.parent;\n }\n this.activityTree.suspendedActivity = null;\n }\n }\n\n /**\n * End Attempt Process\n * Ends an attempt on an activity\n * @spec SN Book: UP.4 (Utility Process - End Attempt Process)\n * @param {Activity} activity - The activity to end attempt on\n */\n public endAttempt(activity: Activity): void {\n if (!activity.isActive) {\n return;\n }\n\n // Transfer RTE data to activity state BEFORE auto-completion logic\n // This ensures that CMI data set by content is properly transferred to activity objectives\n this._rteDataTransferService.transferRteData(activity);\n\n // Set activity as inactive\n activity.isActive = false;\n activity.activityAttemptActive = false;\n\n // [UP.4]1. If the activity is a leaf Then\n if (activity.children.length === 0) {\n // [UP.4]1.1. If Tracked for the activity is True Then\n // Note: In SCORM 2004, all leaf activities are tracked by default\n const isTracked = true;\n\n if (isTracked) {\n // [UP.4]1.1.1. If the Activity is Suspended for the activity is False Then\n // (The sequencer will not affect the state of suspended activities)\n if (!activity.isSuspended) {\n // [UP.4]1.1.1.1. Auto-Completion Logic\n if (!activity.sequencingControls.completionSetByContent) {\n // [UP.4]1.1.1.1.1. If the Attempt Progress Status for the activity is False Then\n // (Did the content inform the sequencer of the activity's completion status?)\n if (!activity.attemptProgressStatus) {\n // [UP.4]1.1.1.1.1.1. Set the Attempt Progress Status for the activity to True\n activity.attemptProgressStatus = true;\n\n // [UP.4]1.1.1.1.1.2. Set the Attempt Completion Status for the activity to True\n activity.completionStatus = \"completed\";\n\n // Track that this was automatic\n activity.wasAutoCompleted = true;\n\n this.fireEvent(\"onAutoCompletion\", {\n activityId: activity.id,\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n // [UP.4]1.1.1.2. Auto-Satisfaction Logic\n if (!activity.sequencingControls.objectiveSetByContent) {\n // [UP.4]1.1.1.2.1. Get the primary objective\n const primaryObjective = activity.primaryObjective;\n\n if (primaryObjective) {\n // [UP.4]1.1.1.2.1.1.1. If the Objective Progress Status for the objective is False Then\n // (Did the content inform the sequencer of the activity's rolled-up objective status?)\n if (!primaryObjective.progressStatus) {\n // [UP.4]1.1.1.2.1.1.1.1. Set the Objective Progress Status for the objective to True\n primaryObjective.progressStatus = true;\n\n // [UP.4]1.1.1.2.1.1.1.2. Set the Objective Satisfied Status for the objective to True\n primaryObjective.satisfiedStatus = true;\n activity.objectiveSatisfiedStatus = true;\n activity.successStatus = \"passed\";\n\n // Track that this was automatic\n activity.wasAutoSatisfied = true;\n\n this.fireEvent(\"onAutoSatisfaction\", {\n activityId: activity.id,\n timestamp: new Date().toISOString(),\n });\n }\n }\n }\n }\n }\n } else {\n // [UP.4]2. Else (The activity has children)\n // [UP.4]2.1. Update suspended status based on children\n const hasSuspendedChildren = activity.children.some((child) => child.isSuspended);\n activity.isSuspended = hasSuspendedChildren;\n }\n\n // Handle unknown statuses for activities that weren't auto-completed/satisfied\n // Update attempt completion status if not already set\n if (activity.completionStatus === \"unknown\") {\n activity.completionStatus = \"incomplete\";\n }\n\n // Update success status if needed\n if (activity.successStatus === \"unknown\" && activity.objectiveSatisfiedStatus) {\n activity.successStatus = activity.objectiveSatisfiedStatus ? \"passed\" : \"failed\";\n }\n\n // Sync global objectives then trigger rollup\n // This reads FROM global objectives INTO activities before rollup,\n // so rollup can use global objective state when calculating activity status\n const mappingRoot = this.activityTree.root || activity;\n this.rollupProcess.processGlobalObjectiveMapping(mappingRoot, this.globalObjectiveMap);\n\n // Trigger rollup after global objective sync\n // Rollup calculates satisfaction and completion based on children and global state\n this.rollupProcess.overallRollupProcess(activity);\n\n // IMPORTANT: We do NOT sync again after rollup because that would overwrite\n // the rollup results by reading from stale global objectives.\n // Global objectives will be updated on the NEXT activity's access when it\n // reads the parent's status via global objective mapping.\n\n // Invalidate navigation predictions after rollup\n if (this.invalidateCacheCallback) {\n this.invalidateCacheCallback();\n }\n\n // INTEGRATION: Validate rollup state consistency after rollup\n if (this.activityTree.root) {\n this.rollupProcess.validateRollupStateConsistency(this.activityTree.root);\n }\n\n // Apply selection and randomization per SCORM 2004 3rd Edition UP.4\n // (Randomization at specification-required process points)\n // This occurs after rollup processing completes\n SelectionRandomization.applySelectionAndRandomization(activity, false);\n }\n\n /**\n * Fire a sequencing event\n * @param {string} eventType - The type of event\n * @param {any} data - Event data\n */\n private fireEvent(eventType: string, data?: any): void {\n try {\n if (this.eventCallback) {\n this.eventCallback(eventType, data);\n }\n } catch (error) {\n console.warn(`Failed to fire sequencing event ${eventType}: ${error}`);\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { RollupProcess } from \"../rollup_process\";\nimport { SelectionRandomization } from \"../selection_randomization\";\nimport { ADLNav } from \"../../adl\";\nimport {\n AuxiliaryResource,\n HideLmsUiItem,\n HIDE_LMS_UI_TOKENS,\n} from \"../../../../types/sequencing_types\";\n\n/**\n * Represents a delivery request result\n */\nexport class DeliveryRequest {\n public valid: boolean;\n public targetActivity: Activity | null;\n public exception: string | null;\n\n constructor(\n valid: boolean = false,\n targetActivity: Activity | null = null,\n exception: string | null = null\n ) {\n this.valid = valid;\n this.targetActivity = targetActivity;\n this.exception = exception;\n }\n}\n\n/**\n * Content activity data for delivery\n */\nexport interface ContentActivityData {\n hideLmsUi: HideLmsUiItem[];\n auxiliaryResources: AuxiliaryResource[];\n location: string;\n credit: string;\n launchData: string;\n maxTimeAllowed: string;\n completionThreshold: string;\n timeLimitAction: string;\n}\n\n/**\n * Options for configuring the DeliveryHandler\n */\nexport interface DeliveryHandlerOptions {\n now?: () => Date;\n defaultHideLmsUi?: HideLmsUiItem[];\n defaultAuxiliaryResources?: AuxiliaryResource[];\n}\n\n/**\n * DeliveryHandler\n *\n * Handles all delivery-related processing for the SCORM 2004 sequencing engine.\n * Extracted from OverallSequencingProcess to follow Single Responsibility Principle.\n *\n * Responsibilities:\n * - Process delivery requests (validate activity for delivery)\n * - Content delivery environment process (initialize activity for delivery)\n * - Get content activity data (hideLmsUi, auxiliary resources, etc.)\n * - Manage activity path processing\n *\n * @spec SN Book: DB.1.1 (Delivery Request Process)\n * @spec SN Book: DB.2 (Content Delivery Environment Process)\n */\nexport class DeliveryHandler {\n private static readonly HIDE_LMS_UI_ORDER: HideLmsUiItem[] = [...HIDE_LMS_UI_TOKENS];\n private activityTree: ActivityTree;\n private rollupProcess: RollupProcess;\n private globalObjectiveMap: Map<string, any>;\n private adlNav: ADLNav | null;\n private eventCallback: ((eventType: string, data?: any) => void) | null;\n private now: () => Date;\n private defaultHideLmsUi: HideLmsUiItem[];\n private defaultAuxiliaryResources: AuxiliaryResource[];\n private _deliveryInProgress: boolean = false;\n private contentDelivered: boolean = false;\n private checkActivityCallback: ((activity: Activity) => boolean) | null = null;\n private invalidateCacheCallback: (() => void) | null = null;\n private updateNavigationValidityCallback: (() => void) | null = null;\n private clearSuspendedActivityCallback: (() => void) | null = null;\n\n constructor(\n activityTree: ActivityTree,\n rollupProcess: RollupProcess,\n globalObjectiveMap: Map<string, any>,\n adlNav: ADLNav | null = null,\n eventCallback: ((eventType: string, data?: any) => void) | null = null,\n options?: DeliveryHandlerOptions\n ) {\n this.activityTree = activityTree;\n this.rollupProcess = rollupProcess;\n this.globalObjectiveMap = globalObjectiveMap;\n this.adlNav = adlNav;\n this.eventCallback = eventCallback;\n this.now = options?.now || (() => new Date());\n this.defaultHideLmsUi = options?.defaultHideLmsUi\n ? [...options.defaultHideLmsUi]\n : [];\n this.defaultAuxiliaryResources = options?.defaultAuxiliaryResources\n ? options.defaultAuxiliaryResources.map((resource) => ({ ...resource }))\n : [];\n }\n\n /**\n * Set callback to check activity validity\n */\n public setCheckActivityCallback(callback: (activity: Activity) => boolean): void {\n this.checkActivityCallback = callback;\n }\n\n /**\n * Set callback to invalidate navigation cache after state changes\n */\n public setInvalidateCacheCallback(callback: () => void): void {\n this.invalidateCacheCallback = callback;\n }\n\n /**\n * Set callback to update navigation validity\n */\n public setUpdateNavigationValidityCallback(callback: () => void): void {\n this.updateNavigationValidityCallback = callback;\n }\n\n /**\n * Set callback to clear suspended activity\n */\n public setClearSuspendedActivityCallback(callback: () => void): void {\n this.clearSuspendedActivityCallback = callback;\n }\n\n /**\n * Check if content delivery is currently in progress\n * Used to prevent re-entrant termination requests during delivery\n */\n public isDeliveryInProgress(): boolean {\n return this._deliveryInProgress;\n }\n\n /**\n * Check if content has been delivered\n */\n public hasContentBeenDelivered(): boolean {\n return this.contentDelivered;\n }\n\n /**\n * Reset content delivered flag\n */\n public resetContentDelivered(): void {\n this.contentDelivered = false;\n }\n\n /**\n * Set content delivered flag\n * @param {boolean} value - The value to set\n */\n public setContentDelivered(value: boolean): void {\n this.contentDelivered = value;\n }\n\n /**\n * Delivery Request Process\n * Validates if an activity can be delivered\n * @spec SN Book: DB.1.1 (Delivery Request Process)\n * @param {Activity} activity - The activity to deliver\n * @return {DeliveryRequest} - The delivery validation result\n */\n public processDeliveryRequest(activity: Activity): DeliveryRequest {\n // Enhanced logging for debugging\n this.fireEvent(\"onDeliveryRequestProcessing\", {\n activity: activity.id,\n timestamp: new Date().toISOString(),\n });\n\n // Check if activity is a cluster (has children)\n // Note: Only activities with children are considered clusters. The 'flow' control\n // is only relevant for controlling navigation through a parent's children and does\n // not affect whether a leaf activity can be delivered.\n if (activity.children.length > 0) {\n return new DeliveryRequest(false, null, \"DB.1.1-1\");\n }\n\n // DB.1.1 Step 2: Form the activity path from root to target, inclusive\n const activityPath = this.getActivityPath(activity, true);\n\n // DB.1.1 Step 3: Check if path is empty (shouldn't happen with valid tree but per spec)\n if (activityPath.length === 0) {\n return new DeliveryRequest(false, null, \"DB.1.1-2\");\n }\n\n // DB.1.1 Step 4: For each activity in the path, apply Check Activity Process (UP.5)\n // This ensures no ancestor violates limit conditions, preconditions, or is disabled\n for (const pathActivity of activityPath) {\n // Check Activity Process returns true if activity is VALID\n const checkResult = this.checkActivityCallback\n ? this.checkActivityCallback(pathActivity)\n : true;\n if (!checkResult) {\n // Activity check failed - cannot deliver\n return new DeliveryRequest(false, null, \"DB.1.1-3\");\n }\n }\n\n // Activity is a true leaf and passes all checks - can be delivered\n return new DeliveryRequest(true, activity);\n }\n\n /**\n * Content Delivery Environment Process\n * Handles the delivery of content to the learner\n * @spec SN Book: DB.2 (Content Delivery Environment Process)\n * @param {Activity} activity - The activity to deliver\n */\n public contentDeliveryEnvironmentProcess(activity: Activity): void {\n // Mark that delivery is in progress to prevent re-entrant termination requests\n // This handles the case where the old content's unload handler fires during delivery\n // and calls Terminate, which would otherwise re-terminate the newly delivered activity\n this._deliveryInProgress = true;\n\n try {\n // Step 1: Check if we're resuming before clearing suspended state\n // Capture suspended state of delivered activity BEFORE clearSuspendedActivitySubprocess\n const isResuming = activity.isSuspended;\n\n // Step 2: Clear Suspended Activity Subprocess (DB.2.1) if needed\n // Clear suspended state whether we're resuming the suspended activity\n // or delivering a different activity (abandoning the suspended session)\n if (this.activityTree.suspendedActivity) {\n if (this.clearSuspendedActivityCallback) {\n this.clearSuspendedActivityCallback();\n }\n }\n\n // Step 3: Process activity path and initialize tracking data (DB.2.2)\n // Get the full path from root to delivered activity\n const activityPath = this.getActivityPath(activity, true);\n\n // Process each activity in the path (root to leaf)\n for (const pathActivity of activityPath) {\n // Only process activities that are not already active\n if (!pathActivity.isActive) {\n if (isResuming || pathActivity.isSuspended) {\n // Resuming: clear suspended flag but don't increment attempt\n pathActivity.isSuspended = false;\n } else {\n // New attempt: increment attempt count\n pathActivity.incrementAttemptCount();\n }\n pathActivity.isActive = true;\n\n // Step 3.1: Apply selection and randomization per SCORM 2004 3rd Edition DB.2\n // (Randomization at specification-required process points)\n // This occurs after activity.isActive is set and attempt count is incremented\n SelectionRandomization.applySelectionAndRandomization(\n pathActivity,\n pathActivity.attemptCount <= 1\n );\n }\n }\n\n // Step 4: Set the activity as current\n this.activityTree.currentActivity = activity;\n\n // Step 5: Initialize attempt for the delivered activity\n this.initializeForDelivery(activity);\n\n // Step 6: Set up activity attempt tracking information\n this.setupAttemptTracking(activity);\n\n // Step 7: Mark that content has been delivered\n this.contentDelivered = true;\n\n // Step 8: Update navigation validity if ADL nav is available\n if (this.adlNav && this.updateNavigationValidityCallback) {\n this.updateNavigationValidityCallback();\n }\n\n // Step 9: Fire activity delivery event\n this.fireDeliveryEvent(activity);\n } finally {\n // Clear delivery in progress flag\n this._deliveryInProgress = false;\n }\n }\n\n /**\n * Initialize Activity For Delivery (DB.2.2)\n * Set up initial tracking states for a delivered activity\n * @param {Activity} activity - The activity being delivered\n */\n private initializeForDelivery(activity: Activity): void {\n // Set initial attempt states if not already set\n if (activity.completionStatus === \"unknown\") {\n // For leaf activities, set to \"not attempted\" initially\n if (activity.children.length === 0) {\n activity.completionStatus = \"not attempted\";\n }\n }\n\n // Initialize objective satisfied status if not set\n if (activity.objectiveSatisfiedStatus === null) {\n activity.objectiveSatisfiedStatus = false;\n }\n\n // Initialize progress measure status\n if (activity.progressMeasure === null) {\n activity.progressMeasure = 0.0;\n activity.progressMeasureStatus = false;\n }\n\n // Initialize objective measure if not set\n if (activity.objectiveNormalizedMeasure === null) {\n activity.objectiveNormalizedMeasure = 0.0;\n activity.objectiveMeasureStatus = false;\n }\n\n // Set up activity attempt information\n activity.attemptAbsoluteDuration = \"PT0H0M0S\";\n activity.attemptExperiencedDuration = \"PT0H0M0S\";\n\n // Mark as available for sequencing\n activity.isAvailable = true;\n }\n\n /**\n * Setup Activity Attempt Tracking\n * Initialize attempt tracking information per SCORM 2004 4th Edition\n * @param {Activity} activity - The activity being delivered\n */\n private setupAttemptTracking(activity: Activity): void {\n // Note: Attempt count is now incremented in contentDeliveryEnvironmentProcess\n // during activity path processing, so we don't increment it here\n\n activity.wasSkipped = false;\n\n // Set attempt start time (use injected clock)\n activity.attemptAbsoluteStartTime = this.now().toISOString();\n\n // Initialize location if not set\n if (!activity.location) {\n activity.location = \"\";\n }\n\n // Set up activity state\n activity.activityAttemptActive = true;\n\n // Initialize learner preferences if not set\n if (!activity.learnerPrefs) {\n activity.learnerPrefs = {\n audioCaptioning: \"0\",\n audioLevel: \"1\",\n deliverySpeed: \"1\",\n language: \"\",\n };\n }\n }\n\n /**\n * Fire Activity Delivery Event\n * Notify listeners that an activity has been delivered\n * @param {Activity} activity - The activity that was delivered\n */\n private fireDeliveryEvent(activity: Activity): void {\n // Fire event through callback if available\n try {\n if (this.eventCallback) {\n this.eventCallback(\"onActivityDelivery\", activity);\n }\n console.debug(`Activity delivered: ${activity.id} - ${activity.title}`);\n } catch (error) {\n // Silently handle event firing errors to not disrupt sequencing\n console.warn(`Failed to fire activity delivery event: ${error}`);\n }\n }\n\n /**\n * Get effective hideLmsUi for an activity\n * Merges default and activity-specific hideLmsUi directives\n * @param {Activity | null} activity - The activity\n * @return {HideLmsUiItem[]} - Ordered list of hideLmsUi directives\n */\n public getEffectiveHideLmsUi(activity: Activity | null): HideLmsUiItem[] {\n const seen = new Set<HideLmsUiItem>();\n\n for (const directive of this.defaultHideLmsUi) {\n seen.add(directive);\n }\n\n let current: Activity | null = activity;\n while (current) {\n for (const directive of current.hideLmsUi) {\n seen.add(directive);\n }\n current = current.parent;\n }\n\n return DeliveryHandler.HIDE_LMS_UI_ORDER.filter((directive) =>\n seen.has(directive)\n );\n }\n\n /**\n * Get effective auxiliary resources for an activity\n * Merges default and activity-specific resources\n * @param {Activity | null} activity - The activity\n * @return {AuxiliaryResource[]} - Merged auxiliary resources\n */\n public getEffectiveAuxiliaryResources(\n activity: Activity | null\n ): AuxiliaryResource[] {\n const merged = new Map<string, AuxiliaryResource>();\n\n for (const resource of this.defaultAuxiliaryResources) {\n if (resource.resourceId) {\n merged.set(resource.resourceId, { ...resource });\n }\n }\n\n const lineage: Activity[] = [];\n let current: Activity | null = activity;\n while (current) {\n lineage.push(current);\n current = current.parent;\n }\n\n for (const node of lineage.reverse()) {\n for (const resource of node.auxiliaryResources) {\n if (resource.resourceId) {\n merged.set(resource.resourceId, { ...resource });\n }\n }\n }\n\n return Array.from(merged.values());\n }\n\n /**\n * Get content activity data for a delivered activity\n * @param {Activity} activity - The activity\n * @return {ContentActivityData} - Content activity data\n */\n public getContentActivityData(activity: Activity): ContentActivityData {\n return {\n hideLmsUi: this.getEffectiveHideLmsUi(activity),\n auxiliaryResources: this.getEffectiveAuxiliaryResources(activity),\n location: activity.location || \"\",\n credit: activity.credit || \"credit\",\n launchData: activity.launchData || \"\",\n maxTimeAllowed: activity.attemptAbsoluteDurationLimit || \"\",\n completionThreshold: activity.completionThreshold?.toString() || \"\",\n timeLimitAction: activity.timeLimitAction || \"continue,no message\",\n };\n }\n\n /**\n * Get Activity Path (Helper for DB.1.1)\n * Forms the activity path from root to target activity, inclusive\n * @param {Activity} activity - The target activity\n * @param {boolean} includeActivity - Whether to include the target in the path\n * @return {Activity[]} - Array of activities from root to target\n */\n public getActivityPath(\n activity: Activity,\n includeActivity: boolean = true\n ): Activity[] {\n const path: Activity[] = [];\n let current: Activity | null = activity;\n\n // Walk up from target to root\n while (current !== null) {\n path.unshift(current); // Add to beginning of array\n current = current.parent;\n }\n\n // Remove target activity if not included\n if (!includeActivity && path.length > 0) {\n path.pop();\n }\n\n return path;\n }\n\n /**\n * Fire a sequencing event\n * @param {string} eventType - The type of event\n * @param {any} data - Event data\n */\n private fireEvent(eventType: string, data?: any): void {\n try {\n if (this.eventCallback) {\n this.eventCallback(eventType, data);\n }\n } catch (error) {\n console.warn(`Failed to fire sequencing event ${eventType}: ${error}`);\n }\n }\n}\n","import { Activity } from \"./activity\";\nimport { ActivityTree } from \"./activity_tree\";\nimport { SequencingProcess, SequencingRequestType } from \"./sequencing_process\";\n\n/**\n * Interface for navigation prediction cache entry\n */\ninterface NavigationPredictionCache {\n continueEnabled: boolean;\n previousEnabled: boolean;\n availableChoices: Set<string>;\n choiceEnabledMap: Map<string, boolean>;\n treeStateHash: string;\n}\n\n/**\n * Interface for navigation predictions result\n */\nexport interface NavigationPredictions {\n continueEnabled: boolean;\n previousEnabled: boolean;\n availableChoices: string[];\n}\n\n/**\n * NavigationLookAhead class\n * Provides look-ahead navigation prediction for SCORM 2004 sequencing.\n * This allows UI to show correct button states before user clicks.\n *\n * Performance target: <10ms for full tree prediction on 50+ activity course\n */\nexport class NavigationLookAhead {\n private activityTree: ActivityTree;\n private sequencingProcess: SequencingProcess;\n private cache: NavigationPredictionCache | null = null;\n private isDirty: boolean = true;\n\n constructor(activityTree: ActivityTree, sequencingProcess: SequencingProcess) {\n this.activityTree = activityTree;\n this.sequencingProcess = sequencingProcess;\n }\n\n /**\n * Predict if Continue navigation would succeed from current activity\n * @return {boolean} - True if Continue would succeed, false otherwise\n */\n public predictContinueEnabled(): boolean {\n this.ensureCacheValid();\n return this.cache?.continueEnabled ?? false;\n }\n\n /**\n * Predict if Previous navigation would succeed from current activity\n * @return {boolean} - True if Previous would succeed, false otherwise\n */\n public predictPreviousEnabled(): boolean {\n this.ensureCacheValid();\n return this.cache?.previousEnabled ?? false;\n }\n\n /**\n * Predict if choice to specific activity would succeed\n * @param {string} activityId - Target activity ID\n * @return {boolean} - True if choice would succeed, false otherwise\n */\n public predictChoiceEnabled(activityId: string): boolean {\n this.ensureCacheValid();\n return this.cache?.choiceEnabledMap.get(activityId) ?? false;\n }\n\n /**\n * Get list of all activity IDs that can be chosen\n * @return {string[]} - Array of activity IDs that can be chosen\n */\n public getAvailableChoices(): string[] {\n this.ensureCacheValid();\n return Array.from(this.cache?.availableChoices ?? []);\n }\n\n /**\n * Get all navigation predictions at once\n * @return {NavigationPredictions} - Navigation predictions object\n */\n public getAllPredictions(): NavigationPredictions {\n this.ensureCacheValid();\n return {\n continueEnabled: this.cache?.continueEnabled ?? false,\n previousEnabled: this.cache?.previousEnabled ?? false,\n availableChoices: Array.from(this.cache?.availableChoices ?? []),\n };\n }\n\n /**\n * Invalidate the cache to force recalculation on next access\n * Should be called after:\n * - Activity change\n * - Rollup process\n * - Objective changes\n * - Attempt changes\n */\n public invalidateCache(): void {\n this.isDirty = true;\n }\n\n /**\n * Force immediate cache update (useful for testing)\n */\n public updateCache(): void {\n this.isDirty = true;\n this.ensureCacheValid();\n }\n\n /**\n * Ensure cache is valid, recalculating if needed\n * @private\n */\n private ensureCacheValid(): void {\n const currentTreeHash = this.calculateTreeStateHash();\n\n if (this.isDirty || !this.cache || this.cache.treeStateHash !== currentTreeHash) {\n this.recalculateCache(currentTreeHash);\n this.isDirty = false;\n }\n }\n\n /**\n * Recalculate all navigation predictions\n * @param {string} treeStateHash - Current tree state hash\n * @private\n */\n private recalculateCache(treeStateHash: string): void {\n const currentActivity = this.activityTree.currentActivity;\n\n // Initialize cache\n this.cache = {\n continueEnabled: false,\n previousEnabled: false,\n availableChoices: new Set<string>(),\n choiceEnabledMap: new Map<string, boolean>(),\n treeStateHash,\n };\n\n // Edge case: no current activity\n if (!currentActivity) {\n // Can only choose activities from root level\n this.calculateAvailableChoicesFromRoot();\n return;\n }\n\n // Predict Continue\n this.cache.continueEnabled = this.predictContinueInternal(currentActivity);\n\n // Predict Previous\n this.cache.previousEnabled = this.predictPreviousInternal(currentActivity);\n\n // Calculate available choices for all activities\n this.calculateAvailableChoices();\n }\n\n /**\n * Predict if Continue would succeed from the given activity\n * @param {Activity} currentActivity - Current activity\n * @return {boolean} - True if Continue would succeed\n * @private\n */\n private predictContinueInternal(currentActivity: Activity): boolean {\n // Check basic flow control requirements\n if (!currentActivity.parent) {\n return false;\n }\n\n // Flow must be enabled on the parent to use Continue\n if (!currentActivity.parent.sequencingControls.flow) {\n return false;\n }\n\n // Check if there's a potential next activity\n // We can't fully simulate the flow process without side effects,\n // so we do a simplified check\n return this.hasAvailableNextActivity(currentActivity);\n }\n\n /**\n * Check if there's an available next activity in flow\n * @param {Activity} currentActivity - Current activity\n * @return {boolean} - True if next activity exists\n * @private\n */\n private hasAvailableNextActivity(currentActivity: Activity): boolean {\n const parent = currentActivity.parent;\n if (!parent) {\n return false;\n }\n\n const siblings = parent.children;\n const currentIndex = siblings.indexOf(currentActivity);\n\n if (currentIndex === -1) {\n return false;\n }\n\n // Check if there's any sibling after current\n if (currentIndex < siblings.length - 1) {\n // Check if there's a deliverable activity after this one\n // Uses full preConditionRule evaluation for forward navigation\n for (let i = currentIndex + 1; i < siblings.length; i++) {\n const sibling = siblings[i];\n if (sibling && this.isActivityPotentiallyDeliverableForward(sibling)) {\n return true;\n }\n }\n }\n\n // Check if parent can be exited and has a next sibling\n if (parent.parent && parent.sequencingControls.flow) {\n return this.hasAvailableNextActivity(parent);\n }\n\n return false;\n }\n\n /**\n * Predict if Previous would succeed from the given activity\n * @param {Activity} currentActivity - Current activity\n * @return {boolean} - True if Previous would succeed\n * @private\n */\n private predictPreviousInternal(currentActivity: Activity): boolean {\n // Check basic flow control requirements\n if (!currentActivity.parent) {\n return false;\n }\n\n // Flow must be enabled on the parent to use Previous\n if (!currentActivity.parent.sequencingControls.flow) {\n return false;\n }\n\n // forwardOnly blocks Previous navigation\n if (currentActivity.parent.sequencingControls.forwardOnly) {\n return false;\n }\n\n // Check if there's a potential previous activity\n return this.hasAvailablePreviousActivity(currentActivity);\n }\n\n /**\n * Check if there's an available previous activity in flow\n * @param {Activity} currentActivity - Current activity\n * @return {boolean} - True if previous activity exists\n * @private\n */\n private hasAvailablePreviousActivity(currentActivity: Activity): boolean {\n const parent = currentActivity.parent;\n if (!parent) {\n return false;\n }\n\n const siblings = parent.children;\n const currentIndex = siblings.indexOf(currentActivity);\n\n if (currentIndex === -1) {\n return false;\n }\n\n // Check if there's any sibling before current\n if (currentIndex > 0) {\n // Check if there's a deliverable activity before this one\n // Uses simpler check for backward navigation (reviewing previously visited content)\n for (let i = currentIndex - 1; i >= 0; i--) {\n const sibling = siblings[i];\n if (sibling && this.isActivityPotentiallyDeliverableBackward(sibling)) {\n return true;\n }\n }\n }\n\n // Check if we can go to parent's previous sibling\n if (parent.parent && parent.sequencingControls.flow && !parent.sequencingControls.forwardOnly) {\n return this.hasAvailablePreviousActivity(parent);\n }\n\n return false;\n }\n\n /**\n * Calculate available choices from root (when no current activity)\n * @private\n */\n private calculateAvailableChoicesFromRoot(): void {\n if (!this.cache || !this.activityTree.root) {\n return;\n }\n\n // When no current activity, no choices are available yet\n // User must start the course first\n // Do not populate any choices\n }\n\n /**\n * Calculate all available choices in the tree\n * @private\n */\n private calculateAvailableChoices(): void {\n if (!this.cache || !this.activityTree.root) {\n return;\n }\n\n const root = this.activityTree.root;\n this.recursivelyCheckChoiceAvailability(root);\n }\n\n /**\n * Recursively check choice availability for activity and its descendants\n * @param {Activity} activity - Activity to check\n * @private\n */\n private recursivelyCheckChoiceAvailability(activity: Activity): void {\n if (!this.cache) {\n return;\n }\n\n const currentActivity = this.activityTree.currentActivity;\n\n // Check if this activity can be chosen\n const validation = this.sequencingProcess.validateNavigationRequest(\n SequencingRequestType.CHOICE,\n activity.id,\n currentActivity\n );\n\n const isChoiceEnabled = validation.valid;\n\n this.cache.choiceEnabledMap.set(activity.id, isChoiceEnabled);\n\n if (isChoiceEnabled) {\n this.cache.availableChoices.add(activity.id);\n }\n\n // Recursively check children\n if (activity.children) {\n for (const child of activity.children) {\n this.recursivelyCheckChoiceAvailability(child);\n }\n }\n }\n\n /**\n * Check if activity is potentially deliverable for forward navigation (Continue)\n * This properly evaluates preConditionRules to determine if the activity can be delivered\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if potentially deliverable\n * @private\n */\n private isActivityPotentiallyDeliverableForward(activity: Activity): boolean {\n // isHiddenFromChoice blocks both choice and flow navigation\n if (activity.isHiddenFromChoice || !activity.isAvailable) {\n return false;\n }\n\n // For leaf activities, use the full check that evaluates preConditionRules\n // isVisible only affects deliverability of leaf activities, not cluster traversal\n if (activity.children.length === 0) {\n if (!activity.isVisible) {\n return false;\n }\n return this.sequencingProcess.canActivityBeDelivered(activity);\n }\n\n // For clusters, check if any child is potentially deliverable\n // Note: Invisible clusters (isVisible=false) can still be traversed by flow navigation\n // - they just won't appear in the TOC. This is a common pattern for grouping content.\n for (const child of activity.children) {\n if (this.isActivityPotentiallyDeliverableForward(child)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Check if activity is potentially deliverable for backward navigation (Previous)\n * This uses a simpler check that doesn't fully evaluate preConditionRules\n * since we're typically going back to a previously visited activity\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if potentially deliverable\n * @private\n */\n private isActivityPotentiallyDeliverableBackward(activity: Activity): boolean {\n // isHiddenFromChoice blocks both choice and flow navigation\n if (activity.isHiddenFromChoice || !activity.isAvailable) {\n return false;\n }\n\n // For leaf activities, isVisible must be true to deliver\n if (activity.children.length === 0) {\n // For backward navigation, we use a simpler check since the activity was likely\n // already delivered before (the learner is going back to review)\n return activity.isVisible;\n }\n\n // For clusters, check if any child is potentially deliverable\n // Note: Invisible clusters can still be traversed by flow navigation\n for (const child of activity.children) {\n if (this.isActivityPotentiallyDeliverableBackward(child)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Calculate a hash of the tree state for cache invalidation\n * @return {string} - Hash representing current tree state\n * @private\n */\n private calculateTreeStateHash(): string {\n const currentActivity = this.activityTree.currentActivity;\n const suspendedActivity = this.activityTree.suspendedActivity;\n\n // Create a simple hash from key state elements\n const parts: string[] = [\n currentActivity?.id ?? \"none\",\n suspendedActivity?.id ?? \"none\",\n this.getActivityTreeStateSignature(),\n ];\n\n return parts.join(\"|\");\n }\n\n /**\n * Get a signature of the activity tree state\n * @return {string} - Signature of tree state\n * @private\n */\n private getActivityTreeStateSignature(): string {\n if (!this.activityTree.root) {\n return \"empty\";\n }\n\n // Create a signature based on important activity states\n const signatures: string[] = [];\n this.collectActivitySignatures(this.activityTree.root, signatures);\n\n return signatures.join(\":\");\n }\n\n /**\n * Recursively collect activity signatures\n * @param {Activity} activity - Activity to process\n * @param {string[]} signatures - Array to collect signatures\n * @private\n */\n private collectActivitySignatures(activity: Activity, signatures: string[]): void {\n // Include key state elements that affect navigation\n const sig = [\n activity.id,\n activity.isActive ? \"A\" : \"-\",\n activity.isSuspended ? \"S\" : \"-\",\n activity.completionStatus,\n activity.successStatus,\n activity.attemptCount.toString(),\n ].join(\"\");\n\n signatures.push(sig);\n\n // Process children\n if (activity.children) {\n for (const child of activity.children) {\n this.collectActivitySignatures(child, signatures);\n }\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { SequencingProcess, SequencingRequestType } from \"../sequencing_process\";\nimport { RuleActionType } from \"../sequencing_rules\";\nimport { ADLNav } from \"../../adl\";\nimport { NavigationLookAhead, NavigationPredictions } from \"../navigation_look_ahead\";\nimport { HideLmsUiItem } from \"../../../../types/sequencing_types\";\n\n/**\n * Enum for navigation request types\n */\nexport enum NavigationRequestType {\n START = \"start\",\n RESUME_ALL = \"resumeAll\",\n CONTINUE = \"continue\",\n PREVIOUS = \"previous\",\n CHOICE = \"choice\",\n JUMP = \"jump\",\n EXIT = \"exit\",\n EXIT_ALL = \"exitAll\",\n ABANDON = \"abandon\",\n ABANDON_ALL = \"abandonAll\",\n SUSPEND_ALL = \"suspendAll\",\n NOT_VALID = \"_none_\",\n}\n\n/**\n * Class representing a navigation request result\n */\nexport class NavigationRequestResult {\n public valid: boolean;\n public terminationRequest: SequencingRequestType | null;\n public sequencingRequest: SequencingRequestType | null;\n public targetActivityId: string | null;\n public exception: string | null;\n\n constructor(\n valid: boolean = false,\n terminationRequest: SequencingRequestType | null = null,\n sequencingRequest: SequencingRequestType | null = null,\n targetActivityId: string | null = null,\n exception: string | null = null\n ) {\n this.valid = valid;\n this.terminationRequest = terminationRequest;\n this.sequencingRequest = sequencingRequest;\n this.targetActivityId = targetActivityId;\n this.exception = exception;\n }\n}\n\n/**\n * NavigationValidityService\n *\n * Handles all navigation validation for the SCORM 2004 sequencing engine.\n * Extracted from OverallSequencingProcess to follow Single Responsibility Principle.\n *\n * Responsibilities:\n * - Validate navigation requests\n * - Check choice path constraints\n * - Validate forward-only constraints\n * - Update navigation validity in ADL nav\n * - Manage navigation look-ahead predictions\n *\n * @spec SN Book: NB.2.1 (Navigation Request Process)\n */\nexport class NavigationValidityService {\n private activityTree: ActivityTree;\n private sequencingProcess: SequencingProcess;\n private adlNav: ADLNav | null;\n private eventCallback: ((eventType: string, data?: any) => void) | null;\n private navigationLookAhead: NavigationLookAhead;\n private getEffectiveHideLmsUiCallback:\n | ((activity: Activity | null) => HideLmsUiItem[])\n | null = null;\n\n constructor(\n activityTree: ActivityTree,\n sequencingProcess: SequencingProcess,\n adlNav: ADLNav | null = null,\n eventCallback: ((eventType: string, data?: any) => void) | null = null\n ) {\n this.activityTree = activityTree;\n this.sequencingProcess = sequencingProcess;\n this.adlNav = adlNav;\n this.eventCallback = eventCallback;\n this.navigationLookAhead = new NavigationLookAhead(activityTree, sequencingProcess);\n }\n\n /**\n * Set callback to get effective hideLmsUi\n */\n public setGetEffectiveHideLmsUiCallback(\n callback: (activity: Activity | null) => HideLmsUiItem[]\n ): void {\n this.getEffectiveHideLmsUiCallback = callback;\n }\n\n /**\n * Get the navigation look-ahead instance\n */\n public getNavigationLookAhead(): NavigationLookAhead {\n return this.navigationLookAhead;\n }\n\n /**\n * Navigation Request Process\n * Validates navigation requests and converts them to termination/sequencing requests\n * @spec SN Book: NB.2.1 (Navigation Request Process)\n * @param {NavigationRequestType} request - The navigation request\n * @param {string | null} targetActivityId - Target activity for choice/jump\n * @return {NavigationRequestResult} - The validation result\n */\n public validateRequest(\n request: NavigationRequestType,\n targetActivityId: string | null = null\n ): NavigationRequestResult {\n // Enhanced logging for debugging\n this.fireEvent(\"onNavigationRequestProcessing\", { request, targetActivityId });\n const currentActivity = this.activityTree.currentActivity;\n\n // Check if navigation request is valid\n switch (request) {\n case NavigationRequestType.START:\n return this.validateStartRequest(currentActivity);\n\n case NavigationRequestType.RESUME_ALL:\n return this.validateResumeRequest(currentActivity);\n\n case NavigationRequestType.CONTINUE:\n return this.validateContinueRequest(currentActivity);\n\n case NavigationRequestType.PREVIOUS:\n return this.validatePreviousRequest(currentActivity);\n\n case NavigationRequestType.CHOICE:\n return this.validateChoiceRequest(currentActivity, targetActivityId);\n\n case NavigationRequestType.JUMP:\n return this.validateJumpRequest(targetActivityId);\n\n case NavigationRequestType.EXIT:\n return this.validateExitRequest(currentActivity);\n\n case NavigationRequestType.EXIT_ALL:\n return this.validateExitAllRequest(currentActivity);\n\n case NavigationRequestType.ABANDON:\n return this.validateAbandonRequest(currentActivity);\n\n case NavigationRequestType.ABANDON_ALL:\n return this.validateAbandonAllRequest(currentActivity);\n\n case NavigationRequestType.SUSPEND_ALL:\n return this.validateSuspendAllRequest(currentActivity);\n\n default:\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-18\");\n }\n }\n\n /**\n * Validate START request\n */\n private validateStartRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (currentActivity !== null) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-1\");\n }\n return new NavigationRequestResult(\n true,\n null,\n SequencingRequestType.START,\n null\n );\n }\n\n /**\n * Validate RESUME_ALL request\n */\n private validateResumeRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (currentActivity !== null) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-2\");\n }\n if (this.activityTree.suspendedActivity === null) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-3\");\n }\n return new NavigationRequestResult(\n true,\n null,\n SequencingRequestType.RESUME_ALL,\n null\n );\n }\n\n /**\n * Validate CONTINUE request\n */\n private validateContinueRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (!currentActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-4\");\n }\n if (\n !currentActivity.parent ||\n !currentActivity.parent.sequencingControls.flow\n ) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-5\");\n }\n\n // Per NB.2.1 Step 3.2.1: Only terminate if current activity is active\n const continueTerminationRequest = currentActivity.isActive\n ? SequencingRequestType.EXIT\n : null;\n\n return new NavigationRequestResult(\n true,\n continueTerminationRequest,\n SequencingRequestType.CONTINUE,\n null\n );\n }\n\n /**\n * Validate PREVIOUS request\n */\n private validatePreviousRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (!currentActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-6\");\n }\n if (\n !currentActivity.parent ||\n !currentActivity.parent.sequencingControls.flow\n ) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-7\");\n }\n\n // Enhanced Forward-Only Navigation Constraints - Check at multiple cluster levels\n const forwardOnlyValidation =\n this.validateForwardOnlyConstraints(currentActivity);\n if (!forwardOnlyValidation.valid) {\n return new NavigationRequestResult(\n false,\n null,\n null,\n null,\n forwardOnlyValidation.exception\n );\n }\n\n // Per NB.2.1 Step 4.2.1.1: Only terminate if current activity is active\n const previousTerminationRequest = currentActivity.isActive\n ? SequencingRequestType.EXIT\n : null;\n\n return new NavigationRequestResult(\n true,\n previousTerminationRequest,\n SequencingRequestType.PREVIOUS,\n null\n );\n }\n\n /**\n * Validate CHOICE request\n */\n private validateChoiceRequest(\n currentActivity: Activity | null,\n targetActivityId: string | null\n ): NavigationRequestResult {\n if (!targetActivityId) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-9\");\n }\n const targetActivity = this.activityTree.getActivity(targetActivityId);\n if (!targetActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-10\");\n }\n\n // Enhanced Choice Path Validation\n const choiceValidation = this.validateChoicePath(\n currentActivity,\n targetActivity\n );\n if (!choiceValidation.valid) {\n return new NavigationRequestResult(\n false,\n null,\n null,\n null,\n choiceValidation.exception\n );\n }\n\n // Per NB.2.1 Step 7.1.1.4: Only return EXIT if current activity is active\n return new NavigationRequestResult(\n true,\n currentActivity?.isActive ? SequencingRequestType.EXIT : null,\n SequencingRequestType.CHOICE,\n targetActivityId\n );\n }\n\n /**\n * Validate JUMP request\n */\n private validateJumpRequest(\n targetActivityId: string | null\n ): NavigationRequestResult {\n if (!targetActivityId) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-12\");\n }\n return new NavigationRequestResult(\n true,\n null,\n SequencingRequestType.JUMP,\n targetActivityId\n );\n }\n\n /**\n * Validate EXIT request\n */\n private validateExitRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (!currentActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-13\");\n }\n if (currentActivity === this.activityTree.root) {\n return new NavigationRequestResult(\n true,\n SequencingRequestType.EXIT_ALL,\n null,\n null\n );\n }\n return new NavigationRequestResult(\n true,\n SequencingRequestType.EXIT,\n null,\n null\n );\n }\n\n /**\n * Validate EXIT_ALL request\n */\n private validateExitAllRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (!currentActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-14\");\n }\n return new NavigationRequestResult(\n true,\n SequencingRequestType.EXIT_ALL,\n null,\n null\n );\n }\n\n /**\n * Validate ABANDON request\n */\n private validateAbandonRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (!currentActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-15\");\n }\n return new NavigationRequestResult(\n true,\n SequencingRequestType.ABANDON,\n null,\n null\n );\n }\n\n /**\n * Validate ABANDON_ALL request\n */\n private validateAbandonAllRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (!currentActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-16\");\n }\n return new NavigationRequestResult(\n true,\n SequencingRequestType.ABANDON_ALL,\n null,\n null\n );\n }\n\n /**\n * Validate SUSPEND_ALL request\n */\n private validateSuspendAllRequest(\n currentActivity: Activity | null\n ): NavigationRequestResult {\n if (!currentActivity) {\n return new NavigationRequestResult(false, null, null, null, \"NB.2.1-17\");\n }\n return new NavigationRequestResult(\n true,\n SequencingRequestType.SUSPEND_ALL,\n null,\n null\n );\n }\n\n /**\n * Enhanced Complex Choice Path Validation\n * Implements comprehensive choice validation with nested hierarchy support\n * @param {Activity | null} currentActivity - Current activity\n * @param {Activity} targetActivity - Target activity for choice\n * @return {{valid: boolean, exception: string | null}} - Validation result\n */\n public validateChoicePath(\n currentActivity: Activity | null,\n targetActivity: Activity\n ): { valid: boolean; exception: string | null } {\n // Check if target is hidden from choice or otherwise unavailable\n // Per NB.2.1 Step 7.1.1.1: Only check static properties here, not preconditions\n // Preconditions that depend on rollup state are evaluated later in SB.2.3 (Check Activity)\n if (targetActivity.isHiddenFromChoice) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n // Check availability and precondition rules (DISABLED, HIDE_FROM_CHOICE)\n // Per NB.2.1: Target activity must be available for choice navigation\n if (!targetActivity.isAvailable) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n // Check if target is disabled by precondition rules (e.g., DISABLED action with ALWAYS condition)\n if (this.isActivityDisabled(targetActivity)) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n if (currentActivity) {\n const commonAncestor = this.findCommonAncestor(\n currentActivity,\n targetActivity\n );\n if (!commonAncestor) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n // Validate choiceExit controls along the current activity path\n // Per NB.2.1 Step 7.1.1.2.3.1: Only check active ancestors from current to common ancestor (exclusive)\n let node: Activity | null = currentActivity;\n while (node && node !== commonAncestor) {\n // Only validate choiceExit if the activity is active\n if (\n node.isActive === true &&\n node.sequencingControls &&\n node.sequencingControls.choiceExit === false\n ) {\n if (\n targetActivity !== node &&\n !this.activityContains(node, targetActivity)\n ) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n }\n node = node.parent;\n }\n\n // Enhanced constrainChoice control validation in nested hierarchies\n const constrainChoiceValidation = this.validateConstrainChoice(\n currentActivity,\n targetActivity,\n commonAncestor\n );\n if (!constrainChoiceValidation.valid) {\n return constrainChoiceValidation;\n }\n\n // Validate choice sets with multiple targets\n const choiceSetValidation = this.validateChoiceSet(\n currentActivity,\n targetActivity,\n commonAncestor\n );\n if (!choiceSetValidation.valid) {\n return choiceSetValidation;\n }\n }\n\n // Path to root validation for choice control\n let activity: Activity | null = targetActivity;\n while (activity) {\n if (activity.parent && !activity.parent.sequencingControls.choice) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n activity = activity.parent;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Enhanced Forward-Only Navigation Constraints\n * Handles forward-only constraints at different cluster levels\n * @param {Activity} currentActivity - Current activity\n * @return {{valid: boolean, exception: string | null}} - Validation result\n */\n public validateForwardOnlyConstraints(currentActivity: Activity): {\n valid: boolean;\n exception: string | null;\n } {\n // Check forward-only constraint at immediate parent level\n if (currentActivity.parent?.sequencingControls.forwardOnly) {\n return { valid: false, exception: \"NB.2.1-8\" };\n }\n\n // Check forward-only constraints at higher cluster levels\n let ancestor = currentActivity.parent?.parent;\n while (ancestor) {\n if (ancestor.sequencingControls.forwardOnly) {\n // If any ancestor cluster has forwardOnly=true, previous navigation is blocked\n return { valid: false, exception: \"NB.2.1-8\" };\n }\n ancestor = ancestor.parent;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Enhanced constrainChoice Control Validation\n * Implements proper constrainChoice validation in nested hierarchies\n * @param {Activity} currentActivity - Current activity\n * @param {Activity} targetActivity - Target activity\n * @param {Activity} commonAncestor - Common ancestor\n * @return {{valid: boolean, exception: string | null}} - Validation result\n */\n private validateConstrainChoice(\n currentActivity: Activity,\n targetActivity: Activity,\n commonAncestor: Activity\n ): { valid: boolean; exception: string | null } {\n // Check constrainChoice at the common ancestor and above in the path\n let ancestor: Activity | null = commonAncestor;\n while (ancestor) {\n if (\n ancestor.sequencingControls?.constrainChoice ||\n ancestor.sequencingControls?.preventActivation\n ) {\n const currentBranch = this.findChildContaining(\n ancestor,\n currentActivity\n );\n if (!currentBranch) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n if (targetActivity === ancestor) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n const targetBranch = this.findChildContaining(ancestor, targetActivity);\n if (!targetBranch) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n if (\n ancestor.sequencingControls?.constrainChoice &&\n targetBranch !== currentBranch\n ) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n if (\n ancestor.sequencingControls?.preventActivation &&\n targetBranch !== currentBranch\n ) {\n if (this.requiresNewActivation(targetBranch, targetActivity)) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n }\n }\n\n ancestor = ancestor.parent;\n }\n\n return this.validateAncestors(commonAncestor, currentActivity, targetActivity);\n }\n\n /**\n * Validate Choice Set Constraints\n * Validates choice sets with multiple targets\n * @param {Activity} _currentActivity - Current activity (unused but part of interface)\n * @param {Activity} targetActivity - Target activity\n * @param {Activity} commonAncestor - Common ancestor\n * @return {{valid: boolean, exception: string | null}} - Validation result\n */\n private validateChoiceSet(\n _currentActivity: Activity,\n targetActivity: Activity,\n commonAncestor: Activity\n ): { valid: boolean; exception: string | null } {\n // Check if target is within the valid choice set\n // Ensure the target is contained within the common ancestor's subtree\n if (\n !this.activityContains(commonAncestor, targetActivity) &&\n targetActivity !== commonAncestor\n ) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n // Walk from target up to (but not including) the common ancestor ensuring availability\n // Per NB.2.1: Check availability, visibility, and precondition rules\n let node: Activity | null = targetActivity;\n while (node && node !== commonAncestor) {\n if (\n !node.isAvailable ||\n node.isHiddenFromChoice ||\n this.isActivityDisabled(node)\n ) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n node = node.parent;\n }\n\n return { valid: true, exception: null };\n }\n\n /**\n * Check if activity is disabled\n * @param {Activity} activity - Activity to check\n * @return {boolean} - True if disabled\n */\n public isActivityDisabled(activity: Activity): boolean {\n if (!activity.isAvailable) {\n return true;\n }\n\n if (activity.isHiddenFromChoice) {\n return true;\n }\n\n const preConditionResult = this.evaluatePreConditionRulesForChoice(activity);\n if (!preConditionResult) {\n return false;\n }\n\n return (\n preConditionResult === RuleActionType.DISABLED ||\n preConditionResult === RuleActionType.HIDE_FROM_CHOICE ||\n preConditionResult === RuleActionType.STOP_FORWARD_TRAVERSAL\n );\n }\n\n /**\n * Find child activity that contains the target activity\n * @param {Activity} parent - Parent activity\n * @param {Activity} target - Target activity to find\n * @return {Activity | null} - Child activity containing target\n */\n public findChildContaining(\n parent: Activity,\n target: Activity\n ): Activity | null {\n for (const child of parent.children) {\n if (child === target) {\n return child;\n }\n if (this.activityContains(child, target)) {\n return child;\n }\n }\n return null;\n }\n\n /**\n * Check if an activity contains another activity in its hierarchy\n * @param {Activity} container - Container activity\n * @param {Activity} target - Target activity\n * @return {boolean} - True if container contains target\n */\n public activityContains(container: Activity, target: Activity): boolean {\n let current: Activity | null = target;\n while (current) {\n if (current === container) {\n return true;\n }\n current = current.parent;\n }\n return false;\n }\n\n /**\n * Find common ancestor between two activities\n */\n public findCommonAncestor(\n activity1: Activity,\n activity2: Activity\n ): Activity | null {\n // Get ancestors of activity1\n const ancestors1: Activity[] = [];\n let current: Activity | null = activity1;\n while (current) {\n ancestors1.push(current);\n current = current.parent;\n }\n\n // Find first common ancestor\n current = activity2;\n while (current) {\n if (ancestors1.includes(current)) {\n return current;\n }\n current = current.parent;\n }\n\n return null;\n }\n\n /**\n * Validate ancestor-level constraints\n * @param {Activity} ancestor - Ancestor activity\n * @param {Activity} currentActivity - Current activity\n * @param {Activity} targetActivity - Target activity\n * @return {{valid: boolean, exception: string | null}} - Validation result\n */\n private validateAncestors(\n ancestor: Activity,\n currentActivity: Activity,\n targetActivity: Activity\n ): { valid: boolean; exception: string | null } {\n // Enforce forwardOnly and mandatory activity constraints at ancestor level\n const children = ancestor.children;\n if (!children || children.length === 0) {\n return { valid: true, exception: null };\n }\n\n const currentTop = this.findChildContaining(ancestor, currentActivity);\n const targetTop = this.findChildContaining(ancestor, targetActivity);\n if (!currentTop || !targetTop) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n const currentIndex = children.indexOf(currentTop);\n const targetIndex = children.indexOf(targetTop);\n\n // Forward-only prevents backwards choice under this ancestor\n if (ancestor.sequencingControls.forwardOnly && targetIndex < currentIndex) {\n return { valid: false, exception: \"NB.2.1-8\" };\n }\n\n const traversalStopIndex = children.findIndex(\n (child) => child?.sequencingControls.stopForwardTraversal\n );\n\n // Current branch flagged stopForwardTraversal blocks moving past it\n if (\n currentTop.sequencingControls.stopForwardTraversal &&\n targetIndex > currentIndex\n ) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n // Stop forward traversal on siblings earlier than target also blocks progression\n if (traversalStopIndex !== -1 && targetIndex > traversalStopIndex) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n // Do not skip mandatory incomplete siblings when moving forward ONLY if forwardOnly=true\n // Per SCORM 2004 spec: When choice=true and forwardOnly=false, learner can select any activity\n // The mandatory sibling constraint only applies to forced-sequential navigation patterns\n if (targetIndex > currentIndex && ancestor.sequencingControls.forwardOnly) {\n for (let i = currentIndex + 1; i < targetIndex; i++) {\n const between = children[i];\n if (!between) {\n continue;\n }\n\n if (between.sequencingControls.stopForwardTraversal) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n\n if (\n this.isActivityMandatory(between) &&\n !this.isActivityCompleted(between)\n ) {\n return { valid: false, exception: \"NB.2.1-11\" };\n }\n }\n }\n\n return { valid: true, exception: null };\n }\n\n private requiresNewActivation(\n branchRoot: Activity,\n targetActivity: Activity\n ): boolean {\n if (this.branchHasActiveAttempt(branchRoot)) {\n return false;\n }\n\n if (targetActivity.activityAttemptActive || targetActivity.isActive) {\n return false;\n }\n\n return true;\n }\n\n private branchHasActiveAttempt(activity: Activity): boolean {\n if (activity.activityAttemptActive || activity.isActive) {\n return true;\n }\n\n for (const child of activity.children) {\n if (this.branchHasActiveAttempt(child)) {\n return true;\n }\n }\n\n return false;\n }\n\n /** Helper: mandatory activity detection (mirrors SequencingProcess behavior) */\n private isActivityMandatory(activity: Activity): boolean {\n if (!activity.isAvailable || activity.isHiddenFromChoice) {\n return false;\n }\n\n const preConditionResult = this.evaluatePreConditionRulesForChoice(activity);\n if (\n preConditionResult === RuleActionType.SKIP ||\n preConditionResult === RuleActionType.DISABLED ||\n preConditionResult === RuleActionType.HIDE_FROM_CHOICE ||\n preConditionResult === RuleActionType.STOP_FORWARD_TRAVERSAL\n ) {\n return false;\n }\n\n if (this.isActivityDisabled(activity)) {\n return false;\n }\n\n return (activity as any).mandatory !== false;\n }\n\n /** Helper: completed-state check (mirrors SequencingProcess behavior) */\n private isActivityCompleted(activity: Activity): boolean {\n if (!activity.isAvailable || activity.isHiddenFromChoice) {\n return true;\n }\n\n const preConditionResult = this.evaluatePreConditionRulesForChoice(activity);\n if (\n preConditionResult === RuleActionType.SKIP ||\n preConditionResult === RuleActionType.DISABLED ||\n preConditionResult === RuleActionType.HIDE_FROM_CHOICE ||\n preConditionResult === RuleActionType.STOP_FORWARD_TRAVERSAL\n ) {\n return true;\n }\n\n if (this.isActivityDisabled(activity)) {\n return true;\n }\n\n return (\n activity.completionStatus === \"completed\" ||\n (activity as any).successStatus === \"passed\" ||\n activity.successStatus === \"passed\"\n );\n }\n\n /**\n * Evaluate pre-condition rules for choice navigation\n * @param {Activity} activity - Activity to evaluate\n * @return {string | null} - Rule result or null\n */\n private evaluatePreConditionRulesForChoice(\n activity: Activity\n ): RuleActionType | string | null {\n if (!activity.sequencingRules) {\n return null;\n }\n\n const action = activity.sequencingRules.evaluatePreConditionRules(activity);\n if (action) {\n return action;\n }\n\n return null;\n }\n\n /**\n * Update navigation validity in ADL nav\n * Called after activity delivery and after rollup to update navigation button states\n */\n public updateNavigationValidity(): void {\n if (!this.adlNav || !this.activityTree.currentActivity) {\n return;\n }\n\n // Invalidate look-ahead cache to ensure fresh calculations\n this.navigationLookAhead.invalidateCache();\n\n // Use NavigationLookAhead for Continue/Previous validity\n // This properly evaluates preConditionRules on target activities\n const continueValid = this.navigationLookAhead.predictContinueEnabled();\n try {\n this.adlNav.request_valid.continue = continueValid ? \"true\" : \"false\";\n } catch (e) {\n // Navigation validity might be read-only after init\n }\n\n const previousValid = this.navigationLookAhead.predictPreviousEnabled();\n try {\n this.adlNav.request_valid.previous = previousValid ? \"true\" : \"false\";\n } catch (e) {\n // Navigation validity might be read-only after init\n }\n\n // Compute per-target choice/jump validity and emit an event snapshot\n const allActivities = this.activityTree.getAllActivities();\n const choiceMap: { [key: string]: string } = {};\n const jumpMap: { [key: string]: string } = {};\n for (const act of allActivities) {\n const choiceRes = this.validateRequest(\n NavigationRequestType.CHOICE,\n act.id\n );\n choiceMap[act.id] = choiceRes.valid ? \"true\" : \"false\";\n const jumpRes = this.validateRequest(NavigationRequestType.JUMP, act.id);\n jumpMap[act.id] = jumpRes.valid ? \"true\" : \"false\";\n }\n // Best-effort update of adl.nav.request_valid maps (may be RO post-init)\n try {\n this.adlNav.request_valid.choice = choiceMap;\n } catch (e) {\n // Ignore read-only constraints on nav request_valid during runtime\n }\n try {\n this.adlNav.request_valid.jump = jumpMap;\n } catch (e) {\n // Ignore read-only constraints on nav request_valid during runtime\n }\n\n // Get effective hideLmsUi\n const hideLmsUi = this.getEffectiveHideLmsUiCallback\n ? this.getEffectiveHideLmsUiCallback(this.activityTree.currentActivity)\n : [];\n\n // Notify listeners so LMS can update UI regardless of read-only state\n this.fireEvent(\"onNavigationValidityUpdate\", {\n continue: continueValid,\n previous: previousValid,\n choice: choiceMap,\n jump: jumpMap,\n hideLmsUi,\n });\n }\n\n /**\n * Get navigation look-ahead predictions\n * Provides UI with navigation button states before user interaction\n * @return {NavigationPredictions} - Current navigation predictions\n */\n public getAllPredictions(): NavigationPredictions {\n return this.navigationLookAhead.getAllPredictions();\n }\n\n /**\n * Predict if Continue navigation would succeed\n * @return {boolean} - True if Continue would succeed\n */\n public predictContinueEnabled(): boolean {\n return this.navigationLookAhead.predictContinueEnabled();\n }\n\n /**\n * Predict if Previous navigation would succeed\n * @return {boolean} - True if Previous would succeed\n */\n public predictPreviousEnabled(): boolean {\n return this.navigationLookAhead.predictPreviousEnabled();\n }\n\n /**\n * Predict if choice to specific activity would succeed\n * @param {string} activityId - Target activity ID\n * @return {boolean} - True if choice would succeed\n */\n public predictChoiceEnabled(activityId: string): boolean {\n return this.navigationLookAhead.predictChoiceEnabled(activityId);\n }\n\n /**\n * Get list of all activities that can be chosen\n * @return {string[]} - Array of activity IDs available for choice\n */\n public getAvailableChoices(): string[] {\n return this.navigationLookAhead.getAvailableChoices();\n }\n\n /**\n * Invalidate navigation prediction cache\n * Called when state changes that affect navigation\n */\n public invalidateCache(): void {\n this.navigationLookAhead.invalidateCache();\n }\n\n /**\n * Fire a sequencing event\n * @param {string} eventType - The type of event\n * @param {any} data - Event data\n */\n private fireEvent(eventType: string, data?: any): void {\n try {\n if (this.eventCallback) {\n this.eventCallback(eventType, data);\n }\n } catch (error) {\n console.warn(`Failed to fire sequencing event ${eventType}: ${error}`);\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { RollupProcess } from \"../rollup_process\";\nimport { CompletionStatus } from \"../../../../constants/enums\";\n\n/**\n * GlobalObjectiveService\n *\n * Manages global objectives for the SCORM 2004 sequencing engine.\n * Extracted from OverallSequencingProcess to follow Single Responsibility Principle.\n *\n * Responsibilities:\n * - Initialize global objective map from activity tree\n * - Collect global objectives from activities\n * - Get/Set/Update global objectives\n * - Serialize/Restore global objective state for persistence\n * - Synchronize global objectives with activity states\n *\n * @spec SN Book: Global Objectives\n */\nexport class GlobalObjectiveService {\n private globalObjectiveMap: Map<string, any> = new Map();\n private eventCallback: ((eventType: string, data?: any) => void) | null = null;\n\n constructor(eventCallback?: (eventType: string, data?: any) => void) {\n this.eventCallback = eventCallback || null;\n }\n\n /**\n * Initialize Global Objective Map\n * Sets up the global objective map for cross-activity objective synchronization\n * @param {Activity | null} root - Root activity to initialize from\n */\n public initialize(root: Activity | null): void {\n try {\n this.globalObjectiveMap.clear();\n\n // Initialize global objectives from activity tree if available\n if (root) {\n this.collectObjectives(root);\n }\n\n this.fireEvent(\"onGlobalObjectiveMapInitialized\", {\n objectiveCount: this.globalObjectiveMap.size,\n timestamp: new Date().toISOString(),\n });\n } catch (error) {\n this.fireEvent(\"onGlobalObjectiveMapError\", {\n error: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n /**\n * Collect Global Objectives\n * Recursively collects global objectives from the activity tree\n * @param {Activity} activity - Activity to collect objectives from\n */\n private collectObjectives(activity: Activity): void {\n const objectives = activity.getAllObjectives();\n\n if (objectives.length === 0) {\n const defaultId = `${activity.id}_default_objective`;\n if (!this.globalObjectiveMap.has(defaultId)) {\n this.globalObjectiveMap.set(defaultId, {\n id: defaultId,\n satisfiedStatus: activity.objectiveSatisfiedStatus,\n satisfiedStatusKnown: activity.objectiveMeasureStatus,\n normalizedMeasure: activity.objectiveNormalizedMeasure,\n normalizedMeasureKnown: activity.objectiveMeasureStatus,\n progressMeasure: activity.progressMeasure,\n progressMeasureKnown: activity.progressMeasureStatus,\n completionStatus: activity.completionStatus,\n completionStatusKnown: activity.completionStatus !== CompletionStatus.UNKNOWN,\n readSatisfiedStatus: true,\n writeSatisfiedStatus: true,\n readNormalizedMeasure: true,\n writeNormalizedMeasure: true,\n readCompletionStatus: true,\n writeCompletionStatus: true,\n readProgressMeasure: true,\n writeProgressMeasure: true,\n satisfiedByMeasure: activity.scaledPassingScore !== null,\n minNormalizedMeasure: activity.scaledPassingScore,\n updateAttemptData: true,\n });\n }\n }\n\n for (const objective of objectives) {\n const mapInfos =\n objective.mapInfo.length > 0\n ? objective.mapInfo\n : [\n {\n targetObjectiveID: objective.id,\n readSatisfiedStatus: true,\n writeSatisfiedStatus: true,\n readNormalizedMeasure: true,\n writeNormalizedMeasure: true,\n readProgressMeasure: true,\n writeProgressMeasure: true,\n readCompletionStatus: true,\n writeCompletionStatus: true,\n updateAttemptData: objective.isPrimary,\n },\n ];\n\n for (const mapInfo of mapInfos) {\n const targetId = mapInfo.targetObjectiveID || objective.id;\n if (!this.globalObjectiveMap.has(targetId)) {\n this.globalObjectiveMap.set(targetId, {\n id: targetId,\n satisfiedStatus: objective.satisfiedStatus,\n satisfiedStatusKnown: objective.measureStatus,\n normalizedMeasure: objective.normalizedMeasure,\n normalizedMeasureKnown: objective.measureStatus,\n progressMeasure: objective.progressMeasure,\n progressMeasureKnown: objective.progressMeasureStatus,\n completionStatus: objective.completionStatus,\n completionStatusKnown:\n objective.completionStatus !== CompletionStatus.UNKNOWN,\n readSatisfiedStatus: mapInfo.readSatisfiedStatus ?? false,\n writeSatisfiedStatus: mapInfo.writeSatisfiedStatus ?? false,\n readNormalizedMeasure: mapInfo.readNormalizedMeasure ?? false,\n writeNormalizedMeasure: mapInfo.writeNormalizedMeasure ?? false,\n readProgressMeasure: mapInfo.readProgressMeasure ?? false,\n writeProgressMeasure: mapInfo.writeProgressMeasure ?? false,\n readCompletionStatus: mapInfo.readCompletionStatus ?? false,\n writeCompletionStatus: mapInfo.writeCompletionStatus ?? false,\n readRawScore: mapInfo.readRawScore ?? false,\n writeRawScore: mapInfo.writeRawScore ?? false,\n readMinScore: mapInfo.readMinScore ?? false,\n writeMinScore: mapInfo.writeMinScore ?? false,\n readMaxScore: mapInfo.readMaxScore ?? false,\n writeMaxScore: mapInfo.writeMaxScore ?? false,\n satisfiedByMeasure: objective.satisfiedByMeasure,\n minNormalizedMeasure: objective.minNormalizedMeasure,\n updateAttemptData: mapInfo.updateAttemptData ?? objective.isPrimary,\n });\n }\n }\n }\n\n // Process children recursively\n for (const child of activity.children) {\n this.collectObjectives(child);\n }\n }\n\n /**\n * Get Global Objective Map\n * Returns the current global objective map for external access\n * @return {Map<string, any>} - Current global objective map\n */\n public getMap(): Map<string, any> {\n return this.globalObjectiveMap;\n }\n\n /**\n * Snapshot the Global Objective Map\n * Provides a serializable copy for persistence consumers\n * @return {Record<string, any>} - Plain-object snapshot of global objectives\n */\n public getSnapshot(): Record<string, any> {\n return this.serialize();\n }\n\n /**\n * Restore Global Objective Map\n * Replaces the current map contents with persisted data\n * @param {Record<string, any>} snapshot - Serialized global objective map\n */\n public restoreSnapshot(snapshot: Record<string, any>): void {\n this.restore(snapshot);\n }\n\n /**\n * Update Global Objective\n * Updates a specific global objective with new data\n * @param {string} objectiveId - Objective ID to update\n * @param {any} objectiveData - New objective data\n */\n public updateObjective(objectiveId: string, objectiveData: any): void {\n try {\n this.globalObjectiveMap.set(objectiveId, {\n ...this.globalObjectiveMap.get(objectiveId),\n ...objectiveData,\n lastUpdated: new Date().toISOString(),\n });\n\n this.fireEvent(\"onGlobalObjectiveUpdated\", {\n objectiveId,\n data: objectiveData,\n timestamp: new Date().toISOString(),\n });\n } catch (error) {\n this.fireEvent(\"onGlobalObjectiveUpdateError\", {\n objectiveId,\n error: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n /**\n * Synchronize global objectives from activity states\n * Called after CMI changes that affect objective status to update global objective mappings\n * This ensures that preconditions based on global objectives are properly evaluated\n * @param {Activity | null} root - Root activity to synchronize from\n * @param {RollupProcess} rollupProcess - Rollup process for mapping\n */\n public synchronize(root: Activity | null, rollupProcess: RollupProcess): void {\n if (!root) {\n return;\n }\n\n // Process global objective mapping from root to synchronize all activities\n rollupProcess.processGlobalObjectiveMapping(root, this.globalObjectiveMap);\n }\n\n /**\n * Get a specific global objective by ID\n * @param {string} objectiveId - The objective ID\n * @return {any | undefined} - The objective data or undefined\n */\n public getObjective(objectiveId: string): any | undefined {\n return this.globalObjectiveMap.get(objectiveId);\n }\n\n /**\n * Check if a global objective exists\n * @param {string} objectiveId - The objective ID\n * @return {boolean} - True if exists\n */\n public hasObjective(objectiveId: string): boolean {\n return this.globalObjectiveMap.has(objectiveId);\n }\n\n /**\n * Get all global objective IDs\n * @return {string[]} - Array of objective IDs\n */\n public getObjectiveIds(): string[] {\n return Array.from(this.globalObjectiveMap.keys());\n }\n\n /**\n * Get the count of global objectives\n * @return {number} - Number of global objectives\n */\n public getObjectiveCount(): number {\n return this.globalObjectiveMap.size;\n }\n\n /**\n * Clear all global objectives\n */\n public clear(): void {\n this.globalObjectiveMap.clear();\n this.fireEvent(\"onGlobalObjectiveMapCleared\", {\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Serialize the global objective map\n * @return {Record<string, any>} - Serialized global objectives\n */\n private serialize(): Record<string, any> {\n const serialized: Record<string, any> = {};\n this.globalObjectiveMap.forEach((data, id) => {\n serialized[id] = { ...data };\n });\n return serialized;\n }\n\n /**\n * Restore the global objective map from serialized data\n * @param {Record<string, any>} mapData - Serialized global objective map\n */\n private restore(mapData: Record<string, any>): void {\n this.globalObjectiveMap.clear();\n if (!mapData) {\n return;\n }\n for (const [id, data] of Object.entries(mapData)) {\n this.globalObjectiveMap.set(id, { ...data });\n }\n this.fireEvent(\"onGlobalObjectiveMapRestored\", {\n objectiveCount: this.globalObjectiveMap.size,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Fire a sequencing event\n * @param {string} eventType - The type of event\n * @param {any} data - Event data\n */\n private fireEvent(eventType: string, data?: any): void {\n try {\n if (this.eventCallback) {\n this.eventCallback(eventType, data);\n }\n } catch (error) {\n console.warn(`Failed to fire global objective event ${eventType}: ${error}`);\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { GlobalObjectiveService } from \"../services/global_objective_service\";\nimport { RollupProcess } from \"../rollup_process\";\nimport { ADLNav } from \"../../adl\";\nimport { HideLmsUiItem, AuxiliaryResource } from \"../../../../types/sequencing_types\";\n\n/**\n * Activity state for serialization\n */\nexport interface ActivityStateData {\n id: string;\n title: string;\n isActive: boolean;\n isSuspended: boolean;\n isCompleted: boolean;\n completionStatus: string;\n successStatus: string;\n attemptCount: number;\n attemptCompletionAmount: number;\n attemptAbsoluteDuration: string;\n attemptExperiencedDuration: string;\n activityAbsoluteDuration: string;\n activityExperiencedDuration: string;\n objectiveSatisfiedStatus: boolean;\n objectiveMeasureStatus: boolean;\n objectiveNormalizedMeasure: number;\n progressMeasure: number | null;\n progressMeasureStatus: boolean;\n isAvailable: boolean;\n isHiddenFromChoice: boolean;\n location: string;\n attemptAbsoluteStartTime: string | null;\n objectives: any;\n auxiliaryResources: AuxiliaryResource[];\n selectionRandomizationState: {\n selectionCountStatus: boolean;\n reorderChildren: boolean;\n childOrder: string[];\n selectedChildIds: string[];\n hiddenFromChoiceChildIds: string[];\n };\n}\n\n/**\n * Navigation state for serialization\n */\nexport interface NavigationState {\n request: string;\n requestValid: {\n continue: string;\n previous: string;\n choice: string;\n jump: string;\n exit: string;\n exitAll: string;\n abandon: string;\n abandonAll: string;\n suspendAll: string;\n };\n hideLmsUi: HideLmsUiItem[];\n auxiliaryResources: AuxiliaryResource[];\n}\n\n/**\n * Complete sequencing state for persistence\n */\nexport interface SequencingState {\n version: string;\n timestamp: string;\n contentDelivered: boolean;\n currentActivity: string | null;\n suspendedActivity: string | null;\n activityStates: Record<string, ActivityStateData>;\n navigationState: NavigationState | null;\n globalObjectiveMap: Record<string, any>;\n}\n\n/**\n * Suspension state for persistence\n */\nexport interface SuspensionState {\n activityTree: any;\n currentActivityId: string | null;\n suspendedActivityId: string | null;\n globalObjectives: Record<string, any>;\n timestamp: string;\n}\n\n/**\n * SequencingStateManager\n *\n * Manages sequencing state persistence for the SCORM 2004 sequencing engine.\n * Extracted from OverallSequencingProcess to follow Single Responsibility Principle.\n *\n * Responsibilities:\n * - Get/Restore sequencing state for multi-session support\n * - Serialize/Deserialize activity states\n * - Get/Restore navigation state\n * - Get/Restore suspension state\n *\n * @spec SN Book: State Persistence\n */\nexport class SequencingStateManager {\n private activityTree: ActivityTree;\n private globalObjectiveService: GlobalObjectiveService;\n private rollupProcess: RollupProcess;\n private adlNav: ADLNav | null;\n private eventCallback: ((eventType: string, data?: any) => void) | null;\n private getEffectiveHideLmsUiCallback:\n | ((activity: Activity | null) => HideLmsUiItem[])\n | null = null;\n private getEffectiveAuxiliaryResourcesCallback:\n | ((activity: Activity | null) => AuxiliaryResource[])\n | null = null;\n private contentDeliveredGetter: (() => boolean) | null = null;\n private contentDeliveredSetter: ((value: boolean) => void) | null = null;\n\n constructor(\n activityTree: ActivityTree,\n globalObjectiveService: GlobalObjectiveService,\n rollupProcess: RollupProcess,\n adlNav: ADLNav | null = null,\n eventCallback: ((eventType: string, data?: any) => void) | null = null\n ) {\n this.activityTree = activityTree;\n this.globalObjectiveService = globalObjectiveService;\n this.rollupProcess = rollupProcess;\n this.adlNav = adlNav;\n this.eventCallback = eventCallback;\n }\n\n /**\n * Set callback to get effective hideLmsUi\n */\n public setGetEffectiveHideLmsUiCallback(\n callback: (activity: Activity | null) => HideLmsUiItem[]\n ): void {\n this.getEffectiveHideLmsUiCallback = callback;\n }\n\n /**\n * Set callback to get effective auxiliary resources\n */\n public setGetEffectiveAuxiliaryResourcesCallback(\n callback: (activity: Activity | null) => AuxiliaryResource[]\n ): void {\n this.getEffectiveAuxiliaryResourcesCallback = callback;\n }\n\n /**\n * Set getter/setter for content delivered flag\n */\n public setContentDeliveredAccessors(\n getter: () => boolean,\n setter: (value: boolean) => void\n ): void {\n this.contentDeliveredGetter = getter;\n this.contentDeliveredSetter = setter;\n }\n\n /**\n * Get Sequencing State for Persistence\n * Returns the current state of the sequencing engine for multi-session support\n * @return {SequencingState} - Serializable sequencing state\n */\n public getState(): SequencingState {\n return {\n version: \"1.0\",\n timestamp: new Date().toISOString(),\n contentDelivered: this.contentDeliveredGetter\n ? this.contentDeliveredGetter()\n : false,\n currentActivity: this.activityTree.currentActivity?.id || null,\n suspendedActivity: this.activityTree.suspendedActivity?.id || null,\n activityStates: this.serializeActivities(),\n navigationState: this.getNavigationState(),\n globalObjectiveMap: this.globalObjectiveService.getSnapshot(),\n };\n }\n\n /**\n * Restore Sequencing State from Persistence\n * Restores the sequencing engine state from a previous session\n * @param {SequencingState} state - Previously saved sequencing state\n * @return {boolean} - True if restoration was successful\n */\n public restoreState(state: SequencingState): boolean {\n try {\n if (!state || state.version !== \"1.0\") {\n console.warn(\"Incompatible sequencing state version\");\n return false;\n }\n\n // Restore basic flags\n if (this.contentDeliveredSetter) {\n this.contentDeliveredSetter(state.contentDelivered || false);\n }\n\n // Restore global objective map before applying local state so reads use persisted data\n if (state.globalObjectiveMap) {\n this.globalObjectiveService.restoreSnapshot(state.globalObjectiveMap);\n }\n\n // Restore activity states\n if (state.activityStates) {\n this.deserializeActivities(state.activityStates);\n }\n\n // Restore current activity\n if (state.currentActivity) {\n const currentActivity = this.activityTree.getActivity(state.currentActivity);\n if (currentActivity) {\n this.activityTree.currentActivity = currentActivity;\n currentActivity.isActive = true;\n }\n }\n\n // Restore suspended activity\n if (state.suspendedActivity) {\n const suspendedActivity = this.activityTree.getActivity(state.suspendedActivity);\n if (suspendedActivity) {\n this.activityTree.suspendedActivity = suspendedActivity;\n suspendedActivity.isSuspended = true;\n }\n }\n\n // Restore navigation state\n if (state.navigationState) {\n this.restoreNavigationState(state.navigationState);\n }\n\n // Re-run global objective synchronization to ensure map and activities align after restore\n if (this.activityTree.root) {\n this.globalObjectiveService.synchronize(\n this.activityTree.root,\n this.rollupProcess\n );\n }\n\n console.debug(\"Sequencing state restored successfully\");\n return true;\n } catch (error) {\n console.error(`Failed to restore sequencing state: ${error}`);\n return false;\n }\n }\n\n /**\n * Serialize Activity States\n * Creates a serializable representation of all activity states\n * @return {Record<string, ActivityStateData>} - Serialized activity states\n */\n private serializeActivities(): Record<string, ActivityStateData> {\n const states: Record<string, ActivityStateData> = {};\n\n const serializeActivity = (activity: Activity) => {\n states[activity.id] = {\n id: activity.id,\n title: activity.title,\n isActive: activity.isActive,\n isSuspended: activity.isSuspended,\n isCompleted: activity.isCompleted,\n completionStatus: activity.completionStatus,\n successStatus: activity.successStatus,\n attemptCount: activity.attemptCount,\n attemptCompletionAmount: activity.attemptCompletionAmount,\n attemptAbsoluteDuration: activity.attemptAbsoluteDuration,\n attemptExperiencedDuration: activity.attemptExperiencedDuration,\n activityAbsoluteDuration: activity.activityAbsoluteDuration,\n activityExperiencedDuration: activity.activityExperiencedDuration,\n objectiveSatisfiedStatus: activity.objectiveSatisfiedStatus,\n objectiveMeasureStatus: activity.objectiveMeasureStatus,\n objectiveNormalizedMeasure: activity.objectiveNormalizedMeasure,\n progressMeasure: activity.progressMeasure,\n progressMeasureStatus: activity.progressMeasureStatus,\n isAvailable: activity.isAvailable,\n isHiddenFromChoice: activity.isHiddenFromChoice,\n location: activity.location,\n attemptAbsoluteStartTime: activity.attemptAbsoluteStartTime,\n objectives: activity.getObjectiveStateSnapshot(),\n auxiliaryResources: activity.auxiliaryResources,\n selectionRandomizationState: {\n selectionCountStatus: activity.sequencingControls.selectionCountStatus,\n reorderChildren: activity.sequencingControls.reorderChildren,\n childOrder: activity.children.map((child) => child.id),\n selectedChildIds: activity.children\n .filter((child) => child.isAvailable)\n .map((child) => child.id),\n hiddenFromChoiceChildIds: activity.children\n .filter((child) => child.isHiddenFromChoice)\n .map((child) => child.id),\n },\n };\n\n // Recursively serialize children\n for (const child of activity.children) {\n serializeActivity(child);\n }\n };\n\n if (this.activityTree.root) {\n serializeActivity(this.activityTree.root);\n }\n\n return states;\n }\n\n /**\n * Deserialize Activity States\n * Restores activity states from serialized data\n * @param {Record<string, ActivityStateData>} states - Serialized activity states\n */\n private deserializeActivities(states: Record<string, ActivityStateData>): void {\n const restoreActivity = (activity: Activity) => {\n const state = states[activity.id];\n if (state) {\n activity.isActive = state.isActive || false;\n activity.isSuspended = state.isSuspended || false;\n activity.isCompleted = state.isCompleted || false;\n activity.completionStatus = state.completionStatus || \"unknown\";\n activity.successStatus = state.successStatus || \"unknown\";\n activity.attemptCount = state.attemptCount || 0;\n activity.attemptCompletionAmount = state.attemptCompletionAmount || 0;\n activity.attemptAbsoluteDuration =\n state.attemptAbsoluteDuration || \"PT0H0M0S\";\n activity.attemptExperiencedDuration =\n state.attemptExperiencedDuration || \"PT0H0M0S\";\n activity.activityAbsoluteDuration =\n state.activityAbsoluteDuration || \"PT0H0M0S\";\n activity.activityExperiencedDuration =\n state.activityExperiencedDuration || \"PT0H0M0S\";\n activity.objectiveSatisfiedStatus = state.objectiveSatisfiedStatus || false;\n activity.objectiveMeasureStatus = state.objectiveMeasureStatus || false;\n activity.objectiveNormalizedMeasure = state.objectiveNormalizedMeasure || 0;\n activity.progressMeasure = state.progressMeasure ?? 0;\n activity.progressMeasureStatus = state.progressMeasureStatus || false;\n activity.isAvailable = state.isAvailable !== false; // Default to true\n activity.isHiddenFromChoice = state.isHiddenFromChoice === true;\n activity.location = state.location || \"\";\n activity.attemptAbsoluteStartTime = state.attemptAbsoluteStartTime || \"\";\n if (Array.isArray(state.auxiliaryResources)) {\n activity.auxiliaryResources = state.auxiliaryResources;\n }\n if (state.objectives) {\n activity.applyObjectiveStateSnapshot(state.objectives);\n }\n }\n\n // Recursively restore children\n for (const child of activity.children) {\n restoreActivity(child);\n }\n\n if (state?.selectionRandomizationState) {\n const selectionState = state.selectionRandomizationState;\n const sequencingControls = activity.sequencingControls;\n\n if (selectionState.selectionCountStatus !== undefined) {\n sequencingControls.selectionCountStatus =\n selectionState.selectionCountStatus;\n }\n\n if (selectionState.reorderChildren !== undefined) {\n sequencingControls.reorderChildren = selectionState.reorderChildren;\n }\n\n if (selectionState.childOrder && selectionState.childOrder.length > 0) {\n activity.setChildOrder(selectionState.childOrder);\n }\n\n const selectedSet = Array.isArray(selectionState.selectedChildIds)\n ? new Set(selectionState.selectedChildIds)\n : null;\n const hiddenSet = Array.isArray(selectionState.hiddenFromChoiceChildIds)\n ? new Set(selectionState.hiddenFromChoiceChildIds)\n : null;\n\n if (selectedSet || hiddenSet) {\n for (const child of activity.children) {\n if (selectedSet) {\n const isSelected = selectedSet.has(child.id);\n child.isAvailable = isSelected;\n if (!hiddenSet) {\n child.isHiddenFromChoice = !isSelected;\n }\n }\n\n if (hiddenSet) {\n child.isHiddenFromChoice = hiddenSet.has(child.id);\n }\n }\n }\n\n activity.setProcessedChildren(\n activity.children.filter((child) => child.isAvailable)\n );\n } else {\n activity.resetProcessedChildren();\n }\n };\n\n if (this.activityTree.root) {\n restoreActivity(this.activityTree.root);\n }\n }\n\n /**\n * Get Navigation State\n * Returns current navigation validity and ADL nav state\n * @return {NavigationState | null} - Navigation state\n */\n private getNavigationState(): NavigationState | null {\n if (!this.adlNav) {\n return null;\n }\n\n const hideLmsUi = this.getEffectiveHideLmsUiCallback\n ? this.getEffectiveHideLmsUiCallback(this.activityTree.currentActivity)\n : [];\n\n const auxiliaryResources = this.getEffectiveAuxiliaryResourcesCallback\n ? this.getEffectiveAuxiliaryResourcesCallback(\n this.activityTree.currentActivity\n )\n : [];\n\n return {\n request: this.adlNav.request || \"_none_\",\n requestValid: {\n continue: this.adlNav.request_valid?.continue || \"false\",\n previous: this.adlNav.request_valid?.previous || \"false\",\n // choice and jump are dynamically evaluated at runtime, so we store \"unknown\"\n choice: \"unknown\",\n jump: \"unknown\",\n exit: this.adlNav.request_valid?.exit || \"false\",\n exitAll: this.adlNav.request_valid?.exitAll || \"false\",\n abandon: this.adlNav.request_valid?.abandon || \"false\",\n abandonAll: this.adlNav.request_valid?.abandonAll || \"false\",\n suspendAll: this.adlNav.request_valid?.suspendAll || \"false\",\n },\n hideLmsUi,\n auxiliaryResources,\n };\n }\n\n /**\n * Restore Navigation State\n * Restores ADL navigation state\n * @param {NavigationState} navState - Navigation state to restore\n */\n private restoreNavigationState(navState: NavigationState): void {\n if (!this.adlNav || !navState) {\n return;\n }\n\n try {\n // Restore navigation request validity\n if (navState.requestValid) {\n const requestValid = navState.requestValid;\n this.adlNav.request_valid.continue = requestValid.continue || \"false\";\n this.adlNav.request_valid.previous = requestValid.previous || \"false\";\n // Note: choice and jump are class instances with dynamic behavior,\n // they are evaluated at runtime based on the activity tree state,\n // so we don't restore them from persisted state\n this.adlNav.request_valid.exit = requestValid.exit || \"false\";\n this.adlNav.request_valid.exitAll = requestValid.exitAll || \"false\";\n this.adlNav.request_valid.abandon = requestValid.abandon || \"false\";\n this.adlNav.request_valid.abandonAll = requestValid.abandonAll || \"false\";\n this.adlNav.request_valid.suspendAll = requestValid.suspendAll || \"false\";\n }\n } catch (error) {\n // Navigation properties might be read-only after initialization\n console.warn(`Could not fully restore navigation state: ${error}`);\n }\n }\n\n /**\n * Get complete suspension state including activity tree and global objectives\n * Captures all state needed to restore sequencing after suspend/resume\n * @return {SuspensionState} - Complete suspension state\n */\n public getSuspensionState(): SuspensionState {\n const state: SuspensionState = {\n activityTree: this.activityTree.root\n ? this.activityTree.root.getSuspensionState()\n : null,\n currentActivityId: this.activityTree.currentActivity?.id || null,\n suspendedActivityId: this.activityTree.suspendedActivity?.id || null,\n globalObjectives: this.globalObjectiveService.getSnapshot(),\n timestamp: new Date().toISOString(),\n };\n\n this.fireEvent(\"onSuspensionStateCaptured\", {\n hasActivityTree: !!state.activityTree,\n currentActivityId: state.currentActivityId,\n suspendedActivityId: state.suspendedActivityId,\n globalObjectiveCount: Object.keys(state.globalObjectives).length,\n timestamp: state.timestamp,\n });\n\n return state;\n }\n\n /**\n * Restore complete suspension state including activity tree and global objectives\n * Restores all state needed to resume from suspended state\n * @param {SuspensionState} state - Suspension state to restore\n */\n public restoreSuspensionState(state: SuspensionState): void {\n if (!state) {\n this.fireEvent(\"onSuspensionStateRestoreError\", {\n error: \"No suspension state provided\",\n timestamp: new Date().toISOString(),\n });\n return;\n }\n\n try {\n // Restore global objectives first\n if (state.globalObjectives) {\n this.globalObjectiveService.restoreSnapshot(state.globalObjectives);\n }\n\n // Restore activity tree state\n if (state.activityTree && this.activityTree.root) {\n this.activityTree.root.restoreSuspensionState(state.activityTree);\n }\n\n // Restore current and suspended activity references\n if (state.currentActivityId) {\n const currentActivity = this.activityTree.getActivity(\n state.currentActivityId\n );\n if (currentActivity) {\n this.activityTree.currentActivity = currentActivity;\n }\n }\n\n if (state.suspendedActivityId) {\n const suspendedActivity = this.activityTree.getActivity(\n state.suspendedActivityId\n );\n if (suspendedActivity) {\n this.activityTree.suspendedActivity = suspendedActivity;\n }\n }\n\n this.fireEvent(\"onSuspensionStateRestored\", {\n currentActivityId: state.currentActivityId,\n suspendedActivityId: state.suspendedActivityId,\n globalObjectiveCount: state.globalObjectives\n ? Object.keys(state.globalObjectives).length\n : 0,\n originalTimestamp: state.timestamp,\n restoreTimestamp: new Date().toISOString(),\n });\n } catch (error) {\n this.fireEvent(\"onSuspensionStateRestoreError\", {\n error: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString(),\n });\n throw error;\n }\n }\n\n /**\n * Fire a sequencing event\n * @param {string} eventType - The type of event\n * @param {any} data - Event data\n */\n private fireEvent(eventType: string, data?: any): void {\n try {\n if (this.eventCallback) {\n this.eventCallback(eventType, data);\n }\n } catch (error) {\n console.warn(`Failed to fire state manager event ${eventType}: ${error}`);\n }\n }\n}\n","import { Activity } from \"../activity\";\nimport { ActivityTree } from \"../activity_tree\";\nimport { getDurationAsSeconds } from \"../../../../utilities\";\nimport { scorm2004_regex } from \"../../../../constants/regex\";\n\n/**\n * Result of activity tree state consistency check\n */\nexport interface StateConsistencyResult {\n consistent: boolean;\n exception: string | null;\n}\n\n/**\n * Result of resource constraint check\n */\nexport interface ResourceConstraintResult {\n available: boolean;\n exception: string | null;\n}\n\n/**\n * Result of concurrent delivery check\n */\nexport interface ConcurrentDeliveryResult {\n allowed: boolean;\n exception: string | null;\n}\n\n/**\n * Result of activity dependency check\n */\nexport interface DependencyResult {\n satisfied: boolean;\n exception: string | null;\n}\n\n/**\n * DeliveryValidator\n *\n * Handles all delivery validation for the SCORM 2004 sequencing engine.\n * Extracted from OverallSequencingProcess to follow Single Responsibility Principle.\n *\n * Responsibilities:\n * - Validate activity tree state consistency\n * - Validate resource constraints\n * - Prevent concurrent delivery\n * - Validate activity dependencies\n * - Check activity process (UP.5)\n * - Limit conditions check process (UP.1)\n *\n * @spec SN Book: DB.1.1 (Delivery Request Process)\n * @spec SN Book: UP.1 (Limit Conditions Check Process)\n * @spec SN Book: UP.5 (Check Activity Process)\n */\nexport class DeliveryValidator {\n private activityTree: ActivityTree;\n private eventCallback: ((eventType: string, data?: any) => void) | null;\n private now: () => Date;\n private contentDeliveredGetter: (() => boolean) | null = null;\n\n constructor(\n activityTree: ActivityTree,\n eventCallback: ((eventType: string, data?: any) => void) | null = null,\n options?: { now?: () => Date }\n ) {\n this.activityTree = activityTree;\n this.eventCallback = eventCallback;\n this.now = options?.now || (() => new Date());\n }\n\n /**\n * Set getter for content delivered flag\n */\n public setContentDeliveredGetter(getter: () => boolean): void {\n this.contentDeliveredGetter = getter;\n }\n\n /**\n * Validate Activity Tree State Consistency\n * @param {Activity} activity - Activity to validate\n * @return {StateConsistencyResult} - Consistency result\n */\n public validateTreeConsistency(activity: Activity): StateConsistencyResult {\n // Check that the activity tree is in a consistent state for delivery\n if (!this.activityTree.root) {\n return { consistent: false, exception: \"DB.1.1-4\" }; // No activity tree\n }\n\n // Validate activity is part of the current tree\n if (!this.isActivityPartOfTree(activity, this.activityTree.root)) {\n return { consistent: false, exception: \"DB.1.1-5\" }; // Activity not in tree\n }\n\n // Check for conflicting active activities\n const activeActivities = this.getActiveActivities();\n if (activeActivities.length > 1) {\n // Multiple active activities indicate inconsistent state\n this.fireEvent(\"onStateInconsistency\", {\n activeActivities: activeActivities.map((a) => a.id),\n targetActivity: activity.id,\n });\n return { consistent: false, exception: \"DB.1.1-6\" }; // State inconsistency\n }\n\n // Validate parent-child relationships are intact\n let current: Activity | null = activity;\n while (current?.parent) {\n if (!current.parent.children.includes(current)) {\n return { consistent: false, exception: \"DB.1.1-7\" }; // Broken parent-child relationship\n }\n current = current.parent;\n }\n\n return { consistent: true, exception: null };\n }\n\n /**\n * Validate Resource Constraints\n * @param {Activity} activity - Activity to validate\n * @return {ResourceConstraintResult} - Resource availability result\n */\n public validateResources(activity: Activity): ResourceConstraintResult {\n // Check if required resources are available\n const requiredResources = this.getRequiredResources(activity);\n for (const resource of requiredResources) {\n if (!this.isResourceAvailable(resource)) {\n return {\n available: false,\n exception: \"DB.1.1-8\", // Resource not available\n };\n }\n }\n\n // Check system resource limits\n const systemResourceCheck = this.checkSystemLimits();\n if (!systemResourceCheck.adequate) {\n return {\n available: false,\n exception: \"DB.1.1-9\", // Insufficient system resources\n };\n }\n\n return { available: true, exception: null };\n }\n\n /**\n * Validate Concurrent Delivery Prevention\n * @param {Activity} activity - Activity to validate\n * @return {ConcurrentDeliveryResult} - Concurrency check result\n */\n public validateConcurrentDelivery(activity: Activity): ConcurrentDeliveryResult {\n const contentDelivered = this.contentDeliveredGetter\n ? this.contentDeliveredGetter()\n : false;\n\n // Check if another delivery is currently in progress\n if (\n contentDelivered &&\n this.activityTree.currentActivity &&\n this.activityTree.currentActivity !== activity\n ) {\n return {\n allowed: false,\n exception: \"DB.1.1-10\", // Another activity is currently being delivered\n };\n }\n\n // Check for pending delivery requests in queue\n if (this.hasPendingDeliveryRequests()) {\n return {\n allowed: false,\n exception: \"DB.1.1-11\", // Delivery request already in queue\n };\n }\n\n // Validate delivery lock status\n if (this.isDeliveryLocked()) {\n return {\n allowed: false,\n exception: \"DB.1.1-12\", // Delivery is currently locked\n };\n }\n\n return { allowed: true, exception: null };\n }\n\n /**\n * Validate Activity Dependencies\n * @param {Activity} activity - Activity to validate\n * @return {DependencyResult} - Dependency check result\n */\n public validateDependencies(activity: Activity): DependencyResult {\n // Check prerequisite activities\n const prerequisites = this.getPrerequisites(activity);\n for (const prerequisite of prerequisites) {\n if (!this.isPrerequisiteSatisfied(prerequisite, activity)) {\n return {\n satisfied: false,\n exception: \"DB.1.1-13\", // Prerequisites not satisfied\n };\n }\n }\n\n // Check objective dependencies\n const objectiveDependencies = this.getObjectiveDependencies(activity);\n for (const dependency of objectiveDependencies) {\n if (!this.isObjectiveDependencySatisfied(dependency)) {\n return {\n satisfied: false,\n exception: \"DB.1.1-14\", // Objective dependencies not met\n };\n }\n }\n\n // Check sequencing rule dependencies\n const sequencingDependencies = this.getSequencingRuleDependencies(activity);\n if (!sequencingDependencies.satisfied) {\n return {\n satisfied: false,\n exception: \"DB.1.1-15\", // Sequencing dependencies not met\n };\n }\n\n return { satisfied: true, exception: null };\n }\n\n /**\n * Check Activity Process (UP.5)\n * Validates if an activity can be delivered based on sequencing rules and limit conditions\n * Note: Cluster/leaf validation is handled in DB.1.1 Step 1, not here\n * @param {Activity} activity - The activity to check\n * @return {boolean} - True if activity is valid (not disabled, limits not violated)\n */\n public checkActivity(activity: Activity): boolean {\n // UP.5 Step 1-2: Check if activity is disabled by sequencing rules\n // In scorm-again, isAvailable serves as the proxy for disabled status\n if (!activity.isAvailable) {\n return false;\n }\n\n // UP.5 Step 3-4: Check limit conditions (UP.1)\n if (!this.checkLimitConditions(activity)) {\n return false;\n }\n\n // Note: We intentionally do NOT check isHiddenFromChoice or cluster status here\n // - isHiddenFromChoice is validated in navigation/choice processes\n // - Cluster validation is handled in DB.1.1 Step 1 (only target activity)\n\n // Activity passes all checks\n return true;\n }\n\n /**\n * Limit Conditions Check Process (UP.1)\n * Checks if any limit conditions are violated for the activity\n * @param {Activity} activity - The activity to check limit conditions for\n * @return {boolean} - True if limit conditions are met, false if violated\n */\n public checkLimitConditions(activity: Activity): boolean {\n let result = true;\n let failureReason = \"\";\n\n // Check attempt limit\n if (activity.attemptLimit !== null && activity.attemptLimit > 0) {\n if (activity.attemptCount >= activity.attemptLimit) {\n result = false;\n failureReason = \"Attempt limit exceeded\";\n }\n }\n\n // Check attempt absolute duration limit\n // A limit of 0 (or \"PT0H0M0S\") is treated as \"no limit\" per IMS SS spec\n if (result && activity.attemptAbsoluteDurationLimit) {\n const limitDuration = getDurationAsSeconds(\n activity.attemptAbsoluteDurationLimit,\n scorm2004_regex.CMITimespan\n );\n // Only check if there's an actual non-zero limit\n if (limitDuration > 0) {\n const currentDuration = getDurationAsSeconds(\n activity.attemptAbsoluteDuration || \"PT0H0M0S\",\n scorm2004_regex.CMITimespan\n );\n if (currentDuration >= limitDuration) {\n result = false;\n failureReason = \"Attempt duration limit exceeded\";\n }\n }\n }\n\n // Check activity absolute duration limit\n // A limit of 0 (or \"PT0H0M0S\") is treated as \"no limit\" per IMS SS spec\n if (result && activity.activityAbsoluteDurationLimit) {\n const limitDuration = getDurationAsSeconds(\n activity.activityAbsoluteDurationLimit,\n scorm2004_regex.CMITimespan\n );\n // Only check if there's an actual non-zero limit\n if (limitDuration > 0) {\n const currentDuration = getDurationAsSeconds(\n activity.activityAbsoluteDuration || \"PT0H0M0S\",\n scorm2004_regex.CMITimespan\n );\n if (currentDuration >= limitDuration) {\n result = false;\n failureReason = \"Activity duration limit exceeded\";\n }\n }\n }\n\n // Check begin time limit\n if (result && activity.beginTimeLimit) {\n const currentTime = this.now();\n const beginTime = new Date(activity.beginTimeLimit);\n if (currentTime < beginTime) {\n result = false;\n failureReason = \"Not yet time to begin\";\n }\n }\n\n // Check end time limit\n if (result && activity.endTimeLimit) {\n const currentTime = this.now();\n const endTime = new Date(activity.endTimeLimit);\n if (currentTime > endTime) {\n result = false;\n failureReason = \"Time limit expired\";\n }\n }\n\n // Fire limit condition check event\n this.fireEvent(\"onLimitConditionCheck\", {\n activity: activity,\n result: result,\n failureReason: failureReason,\n checks: {\n attemptLimit: activity.attemptLimit,\n attemptCount: activity.attemptCount,\n attemptDurationLimit: activity.attemptAbsoluteDurationLimit,\n activityDurationLimit: activity.activityAbsoluteDurationLimit,\n beginTimeLimit: activity.beginTimeLimit,\n endTimeLimit: activity.endTimeLimit,\n },\n });\n\n return result;\n }\n\n /**\n * Check if activity is part of tree\n */\n private isActivityPartOfTree(activity: Activity, root: Activity): boolean {\n if (activity === root) {\n return true;\n }\n\n for (const child of root.children) {\n if (this.isActivityPartOfTree(activity, child)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Get all active activities in the tree\n */\n private getActiveActivities(): Activity[] {\n const activeActivities: Activity[] = [];\n if (this.activityTree.root) {\n this.collectActiveActivities(this.activityTree.root, activeActivities);\n }\n return activeActivities;\n }\n\n private collectActiveActivities(\n activity: Activity,\n activeActivities: Activity[]\n ): void {\n if (activity.isActive) {\n activeActivities.push(activity);\n }\n for (const child of activity.children) {\n this.collectActiveActivities(child, activeActivities);\n }\n }\n\n /**\n * Get required resources for activity\n */\n public getRequiredResources(activity: Activity): string[] {\n // Parse activity metadata for resource requirements\n const resources: string[] = [];\n\n // Check for multimedia requirements based on activity title and location\n const activityInfo = (activity.title + \" \" + activity.location).toLowerCase();\n if (activityInfo.includes(\"video\") || activityInfo.includes(\"multimedia\")) {\n resources.push(\"video-codec\");\n }\n if (activityInfo.includes(\"audio\") || activityInfo.includes(\"sound\")) {\n resources.push(\"audio-codec\");\n }\n\n // Check for plugin requirements from activity location/title\n if (activityInfo.includes(\"flash\") || activityInfo.includes(\".swf\")) {\n resources.push(\"flash-plugin\");\n }\n if (activityInfo.includes(\"java\") || activityInfo.includes(\"applet\")) {\n resources.push(\"java-runtime\");\n }\n\n // Check for bandwidth requirements based on activity type\n if (activity.children && activity.children.length > 0) {\n resources.push(\"high-bandwidth\"); // Container activities may need more bandwidth\n }\n\n // Check for storage requirements based on duration limits\n if (\n activity.attemptAbsoluteDurationLimit &&\n this.parseDurationToMinutes(activity.attemptAbsoluteDurationLimit) > 60\n ) {\n resources.push(\"extended-storage\"); // Long duration activities need more storage\n }\n\n // Check for specific SCORM requirements\n if (activity.attemptLimit && activity.attemptLimit > 1) {\n resources.push(\"persistent-storage\"); // Multiple attempts need storage\n }\n\n return resources;\n }\n\n /**\n * Check if resource is available\n */\n public isResourceAvailable(resource: string): boolean {\n try {\n switch (resource) {\n case \"video-codec\":\n // Check if HTML5 video is supported\n return !!(document.createElement(\"video\").canPlayType);\n\n case \"audio-codec\":\n // Check if HTML5 audio is supported\n return !!(document.createElement(\"audio\").canPlayType);\n\n case \"flash-plugin\":\n // Check for Flash plugin (legacy support)\n return (\n navigator.plugins &&\n Array.from(navigator.plugins).some(\n (plugin) => plugin.name === \"Shockwave Flash\"\n )\n );\n\n case \"java-runtime\":\n // Check for Java support (mostly deprecated in modern browsers)\n return (\n navigator.plugins &&\n Array.from(navigator.plugins).some((plugin) => plugin.name === \"Java\")\n );\n\n case \"high-bandwidth\":\n // Check network connection (basic heuristic)\n if (\"connection\" in navigator) {\n const connection = (navigator as any).connection;\n return connection.effectiveType === \"4g\" || connection.downlink > 5;\n }\n return true; // Assume available if can't detect\n\n case \"extended-storage\":\n // Assume storage is available for SCORM content\n return true;\n\n case \"persistent-storage\":\n // Check for persistent storage capabilities\n return \"localStorage\" in window && \"sessionStorage\" in window;\n\n default:\n // Unknown resource, assume available\n return true;\n }\n } catch (error) {\n // If any check fails, assume resource is unavailable\n return false;\n }\n }\n\n /**\n * Check system resource limits\n */\n public checkSystemLimits(): { adequate: boolean } {\n try {\n let adequate = true;\n\n // Check memory usage if available (Chrome/Edge only)\n if (\"memory\" in performance) {\n const memory = (performance as any).memory;\n const memoryUsagePercent = memory.usedJSHeapSize / memory.jsHeapSizeLimit;\n if (memoryUsagePercent > 0.8) {\n // More than 80% memory used\n adequate = false;\n }\n }\n\n // Check for device memory hint (modern browsers)\n if (\"deviceMemory\" in navigator) {\n const deviceMemory = (navigator as any).deviceMemory;\n if (deviceMemory < 2) {\n // Less than 2GB device memory\n adequate = false;\n }\n }\n\n // Check hardware concurrency (rough CPU check)\n if (\"hardwareConcurrency\" in navigator) {\n const cores = navigator.hardwareConcurrency;\n if (cores < 2) {\n // Single core devices might struggle\n adequate = false;\n }\n }\n\n // Check connection quality for network-intensive activities\n if (\"connection\" in navigator) {\n const connection = (navigator as any).connection;\n if (connection.saveData || connection.effectiveType === \"slow-2g\") {\n adequate = false;\n }\n }\n\n return { adequate };\n } catch (error) {\n // If checks fail, assume resources are adequate\n return { adequate: true };\n }\n }\n\n private hasPendingDeliveryRequests(): boolean {\n // Check if there are pending delivery requests in the system\n if (this.activityTree && (this.activityTree as any).pendingRequests) {\n return (this.activityTree as any).pendingRequests.length > 0;\n }\n\n // Check for any pending fetch operations (if using fetch API)\n if (typeof window !== \"undefined\" && (window as any).pendingScormRequests) {\n return (window as any).pendingScormRequests > 0;\n }\n\n return false;\n }\n\n private isDeliveryLocked(): boolean {\n // Check for navigation lock\n if (this.activityTree && (this.activityTree as any).navigationLocked) {\n return true;\n }\n\n // Check for active termination process\n if (this.activityTree && (this.activityTree as any).terminationInProgress) {\n return true;\n }\n\n // Check system resource limits\n const resourceCheck = this.checkSystemLimits();\n if (!resourceCheck.adequate) {\n return true;\n }\n\n // Check for maintenance mode (would be set by LMS)\n return !!(\n typeof window !== \"undefined\" && (window as any).scormMaintenanceMode\n );\n }\n\n /**\n * Get activity prerequisites\n */\n public getPrerequisites(activity: Activity): string[] {\n const prerequisites: string[] = [];\n\n // Check for preCondition rules that reference other activities\n if (activity.sequencingRules && activity.sequencingRules.preConditionRules) {\n for (const rule of activity.sequencingRules.preConditionRules) {\n if (rule.conditions && rule.conditions.length > 0) {\n for (const condition of rule.conditions) {\n if (\n (condition as any).referencedObjectiveID &&\n (condition as any).referencedObjectiveID !== activity.id\n ) {\n prerequisites.push((condition as any).referencedObjectiveID);\n }\n }\n }\n }\n }\n\n // Check for sequencing control dependencies\n if (\n activity.parent &&\n activity.sequencingControls &&\n !activity.sequencingControls.choiceExit\n ) {\n const siblings = activity.parent.children;\n if (siblings) {\n const activityIndex = siblings.indexOf(activity);\n\n // Add previous siblings as prerequisites for sequential flow\n for (let i = 0; i < activityIndex; i++) {\n const sibling = siblings[i];\n if (sibling) {\n prerequisites.push(sibling.id);\n }\n }\n }\n }\n\n // Check for explicit prerequisite metadata (if defined in activity)\n if ((activity as any).prerequisiteActivities) {\n prerequisites.push(...(activity as any).prerequisiteActivities);\n }\n\n return Array.from(new Set(prerequisites)); // Remove duplicates\n }\n\n /**\n * Check if prerequisite is satisfied\n */\n public isPrerequisiteSatisfied(\n prerequisiteId: string,\n _activity: Activity\n ): boolean {\n const prerequisite = this.activityTree.getActivity(prerequisiteId);\n if (!prerequisite) {\n return false;\n }\n\n return prerequisite.completionStatus === \"completed\";\n }\n\n /**\n * Get objective dependencies\n */\n public getObjectiveDependencies(activity: Activity): string[] {\n const dependencies: string[] = [];\n\n // Check activity's objective mappings for global objective references\n const objectives = (activity as any).objectives;\n if (objectives && objectives.length > 0) {\n for (const objective of objectives) {\n if ((objective as any).globalObjectiveID) {\n dependencies.push((objective as any).globalObjectiveID);\n }\n\n if (\n !(objective as any).satisfiedByMeasure &&\n (objective as any).readNormalizedMeasure\n ) {\n dependencies.push(objective.id + \"_measure\");\n }\n }\n }\n\n // Check sequencing rules for objective references\n if (activity.sequencingRules) {\n const allRules = [\n ...(activity.sequencingRules.preConditionRules || []),\n ...(activity.sequencingRules.exitConditionRules || []),\n ...(activity.sequencingRules.postConditionRules || []),\n ];\n\n for (const rule of allRules) {\n if (rule.conditions && rule.conditions.length > 0) {\n for (const condition of rule.conditions) {\n if (\n (condition as any).objectiveReference &&\n (condition as any).objectiveReference !== activity.id\n ) {\n dependencies.push((condition as any).objectiveReference);\n }\n }\n }\n }\n }\n\n return Array.from(new Set(dependencies)); // Remove duplicates\n }\n\n /**\n * Check if objective dependency is satisfied\n */\n public isObjectiveDependencySatisfied(objectiveId: string): boolean {\n // Handle global objective references\n if (this.activityTree && (this.activityTree as any).globalObjectives) {\n const globalObjectives = (this.activityTree as any).globalObjectives;\n const globalObjective = globalObjectives[objectiveId];\n\n if (globalObjective) {\n return (\n globalObjective.satisfied === true &&\n globalObjective.statusKnown === true\n );\n }\n }\n\n // Handle measure-based dependencies\n if (objectiveId.endsWith(\"_measure\")) {\n const baseObjectiveId = objectiveId.replace(\"_measure\", \"\");\n if (this.activityTree && (this.activityTree as any).globalObjectives) {\n const globalObjectives = (this.activityTree as any).globalObjectives;\n const globalObjective = globalObjectives[baseObjectiveId];\n\n if (globalObjective) {\n return (\n globalObjective.measureKnown === true &&\n globalObjective.normalizedMeasure >= 0\n );\n }\n }\n }\n\n // Handle activity-specific objective references\n const referencedActivity = this.activityTree.getActivity(objectiveId);\n if (referencedActivity) {\n return (\n referencedActivity.objectiveSatisfiedStatus &&\n referencedActivity.objectiveMeasureStatus\n );\n }\n\n // If objective is not found or cannot be evaluated, assume not satisfied\n return false;\n }\n\n private getSequencingRuleDependencies(activity: Activity): { satisfied: boolean } {\n let satisfied = true;\n\n try {\n // Check pre-condition rule dependencies\n if (activity.sequencingRules && activity.sequencingRules.preConditionRules) {\n for (const rule of activity.sequencingRules.preConditionRules) {\n if (rule.conditions && rule.conditions.length > 0) {\n for (const condition of rule.conditions) {\n const conditionType =\n (condition as any).conditionType || condition.condition;\n\n switch (conditionType) {\n case \"activityProgressKnown\":\n if (!activity.progressMeasureStatus) satisfied = false;\n break;\n\n case \"objectiveStatusKnown\":\n case \"objectiveSatisfied\": {\n const objectiveId =\n (condition as any).referencedObjectiveID || activity.id;\n if (!this.isObjectiveDependencySatisfied(objectiveId))\n satisfied = false;\n break;\n }\n\n case \"attemptLimitExceeded\":\n if (activity.attemptLimit === null) satisfied = false;\n break;\n\n case \"timeLimitExceeded\":\n if (\n !activity.attemptAbsoluteDurationLimit &&\n !activity.activityAbsoluteDurationLimit\n )\n satisfied = false;\n break;\n\n case \"always\":\n case \"never\":\n // These conditions have no dependencies\n break;\n\n default:\n // Unknown condition type, assume dependency not satisfied\n satisfied = false;\n }\n }\n }\n }\n }\n\n // Check exit condition rule dependencies (similar logic)\n if (activity.sequencingRules && activity.sequencingRules.exitConditionRules) {\n for (const rule of activity.sequencingRules.exitConditionRules) {\n if (rule.conditions && rule.conditions.length > 0) {\n for (const condition of rule.conditions) {\n const conditionType =\n (condition as any).conditionType || condition.condition;\n\n if (\n [\"objectiveStatusKnown\", \"objectiveSatisfied\"].includes(\n conditionType\n )\n ) {\n const objectiveId =\n (condition as any).referencedObjectiveID || activity.id;\n if (!this.isObjectiveDependencySatisfied(objectiveId))\n satisfied = false;\n }\n }\n }\n }\n }\n\n // Check rollup rule dependencies\n if (activity.rollupRules && activity.rollupRules.rules) {\n for (const rule of activity.rollupRules.rules) {\n if (rule.conditions && rule.conditions.length > 0) {\n // Rollup rules depend on child activity completion\n if (activity.children && activity.children.length > 0) {\n for (const child of activity.children) {\n if (!child.isCompleted) {\n satisfied = false;\n break;\n }\n }\n }\n }\n }\n }\n } catch (error) {\n // If any error occurs during dependency check, mark as not satisfied\n satisfied = false;\n }\n\n return { satisfied };\n }\n\n /**\n * Helper method to parse ISO 8601 duration to minutes\n */\n private parseDurationToMinutes(duration: string): number {\n return getDurationAsSeconds(duration, scorm2004_regex.CMITimespan) / 60;\n }\n\n /**\n * Fire a sequencing event\n * @param {string} eventType - The type of event\n * @param {any} data - Event data\n */\n private fireEvent(eventType: string, data?: any): void {\n try {\n if (this.eventCallback) {\n this.eventCallback(eventType, data);\n }\n } catch (error) {\n console.warn(`Failed to fire delivery validator event ${eventType}: ${error}`);\n }\n }\n}\n","import { Activity } from \"./activity\";\nimport { ActivityTree } from \"./activity_tree\";\nimport { DeliveryRequestType, SequencingProcess, SequencingRequestType } from \"./sequencing_process\";\nimport { RollupProcess } from \"./rollup_process\";\nimport { ADLNav } from \"../adl\";\nimport { CompletionStatus, SuccessStatus } from \"../../../constants/enums\";\nimport { AuxiliaryResource, HideLmsUiItem } from \"../../../types/sequencing_types\";\nimport { NavigationLookAhead, NavigationPredictions } from \"./navigation_look_ahead\";\n\n// Import extracted components\nimport {\n TerminationHandler,\n type TerminationResult,\n type CMIDataForTransfer,\n} from \"./handlers/termination_handler\";\nimport {\n DeliveryHandler,\n DeliveryRequest,\n type ContentActivityData,\n} from \"./handlers/delivery_handler\";\nimport {\n NavigationValidityService,\n NavigationRequestType,\n type NavigationRequestResult,\n} from \"./services/navigation_validity_service\";\nimport { GlobalObjectiveService } from \"./services/global_objective_service\";\nimport {\n SequencingStateManager,\n type SequencingState,\n type SuspensionState,\n} from \"./state/sequencing_state_manager\";\nimport { DeliveryValidator } from \"./validation/delivery_validator\";\n\n// Re-export types for backward compatibility\n// Using 'export type' for type-only exports since esbuild strips interfaces\nexport { NavigationRequestType, DeliveryRequest };\nexport type {\n NavigationRequestResult,\n CMIDataForTransfer,\n ContentActivityData,\n SequencingState,\n SuspensionState,\n};\nexport type { TerminationResult as TerminationRequestResult };\n\n/**\n * Options for configuring the OverallSequencingProcess\n */\nexport interface OverallSequencingProcessOptions {\n now?: () => Date;\n enhancedDeliveryValidation?: boolean;\n defaultHideLmsUi?: HideLmsUiItem[];\n defaultAuxiliaryResources?: AuxiliaryResource[];\n getCMIData?: () => CMIDataForTransfer;\n is4thEdition?: boolean;\n}\n\n/**\n * Overall Sequencing Process\n *\n * Coordinates the overall execution of the SCORM 2004 sequencing loop.\n * This class has been refactored to delegate to specialized handlers and services:\n *\n * - TerminationHandler: Handles termination requests (EXIT, EXIT_ALL, SUSPEND_ALL, etc.)\n * - DeliveryHandler: Handles content delivery and environment setup\n * - NavigationValidityService: Validates navigation requests\n * - GlobalObjectiveService: Manages global objectives\n * - SequencingStateManager: Handles state persistence\n * - DeliveryValidator: Validates delivery preconditions\n *\n * @spec SN Book: OP.1 (Overall Sequencing Process)\n */\nexport class OverallSequencingProcess {\n // Core dependencies\n private activityTree: ActivityTree;\n private sequencingProcess: SequencingProcess;\n private rollupProcess: RollupProcess;\n private adlNav: ADLNav | null;\n private eventCallback: ((eventType: string, data?: any) => void) | null = null;\n\n // Extracted services\n private terminationHandler: TerminationHandler;\n private deliveryHandler: DeliveryHandler;\n private navigationValidityService: NavigationValidityService;\n private globalObjectiveService: GlobalObjectiveService;\n private stateManager: SequencingStateManager;\n private deliveryValidator: DeliveryValidator;\n private navigationLookAhead: NavigationLookAhead;\n\n // Configuration\n private enhancedDeliveryValidation: boolean;\n private is4thEdition: boolean;\n\n constructor(\n activityTree: ActivityTree,\n sequencingProcess: SequencingProcess,\n rollupProcess: RollupProcess,\n adlNav: ADLNav | null = null,\n eventCallback: ((eventType: string, data?: any) => void) | null = null,\n options?: OverallSequencingProcessOptions\n ) {\n this.activityTree = activityTree;\n this.sequencingProcess = sequencingProcess;\n this.rollupProcess = rollupProcess;\n this.adlNav = adlNav;\n this.eventCallback = eventCallback;\n this.enhancedDeliveryValidation = options?.enhancedDeliveryValidation === true;\n this.is4thEdition = options?.is4thEdition || false;\n\n // Initialize global objective service\n this.globalObjectiveService = new GlobalObjectiveService(eventCallback || undefined);\n this.globalObjectiveService.initialize(activityTree.root);\n\n // Initialize delivery validator\n // Build options object conditionally to satisfy exactOptionalPropertyTypes\n const deliveryValidatorOptions: { now?: () => Date } = {};\n if (options?.now) {\n deliveryValidatorOptions.now = options.now;\n }\n this.deliveryValidator = new DeliveryValidator(\n activityTree,\n eventCallback,\n deliveryValidatorOptions\n );\n\n // Initialize termination handler\n // Build options object conditionally to satisfy exactOptionalPropertyTypes\n const terminationOptions: { getCMIData?: () => CMIDataForTransfer; is4thEdition?: boolean } = {};\n if (options?.getCMIData) {\n terminationOptions.getCMIData = options.getCMIData;\n }\n if (options?.is4thEdition !== undefined) {\n terminationOptions.is4thEdition = options.is4thEdition;\n }\n this.terminationHandler = new TerminationHandler(\n activityTree,\n sequencingProcess,\n rollupProcess,\n this.globalObjectiveService.getMap(),\n eventCallback,\n terminationOptions\n );\n\n // Initialize delivery handler\n // Build options object conditionally to satisfy exactOptionalPropertyTypes\n const deliveryOptions: {\n now?: () => Date;\n defaultHideLmsUi?: HideLmsUiItem[];\n defaultAuxiliaryResources?: AuxiliaryResource[];\n } = {};\n if (options?.now) {\n deliveryOptions.now = options.now;\n }\n if (options?.defaultHideLmsUi) {\n deliveryOptions.defaultHideLmsUi = options.defaultHideLmsUi;\n }\n if (options?.defaultAuxiliaryResources) {\n deliveryOptions.defaultAuxiliaryResources = options.defaultAuxiliaryResources;\n }\n this.deliveryHandler = new DeliveryHandler(\n activityTree,\n rollupProcess,\n this.globalObjectiveService.getMap(),\n adlNav,\n eventCallback,\n deliveryOptions\n );\n\n // Initialize navigation validity service\n this.navigationValidityService = new NavigationValidityService(\n activityTree,\n sequencingProcess,\n adlNav,\n eventCallback\n );\n\n // Initialize state manager\n this.stateManager = new SequencingStateManager(\n activityTree,\n this.globalObjectiveService,\n rollupProcess,\n adlNav,\n eventCallback\n );\n\n // Use the shared navigation look-ahead from NavigationValidityService\n // This ensures cache invalidation affects the same instance used for predictions\n this.navigationLookAhead = this.navigationValidityService.getNavigationLookAhead();\n\n // Set up cross-component callbacks\n this.setupCallbacks();\n }\n\n /**\n * Set up callbacks between components\n */\n private setupCallbacks(): void {\n // Set up termination handler callbacks\n this.terminationHandler.setInvalidateCacheCallback(() => {\n this.navigationLookAhead.invalidateCache();\n });\n\n // Set up delivery handler callbacks\n this.deliveryHandler.setCheckActivityCallback((activity) =>\n this.deliveryValidator.checkActivity(activity)\n );\n this.deliveryHandler.setInvalidateCacheCallback(() => {\n this.navigationLookAhead.invalidateCache();\n });\n this.deliveryHandler.setUpdateNavigationValidityCallback(() => {\n this.navigationValidityService.updateNavigationValidity();\n });\n this.deliveryHandler.setClearSuspendedActivityCallback(() => {\n this.terminationHandler.clearSuspendedActivity();\n });\n\n // Set up delivery validator callbacks\n this.deliveryValidator.setContentDeliveredGetter(() =>\n this.deliveryHandler.hasContentBeenDelivered()\n );\n\n // Set up navigation validity service callbacks\n this.navigationValidityService.setGetEffectiveHideLmsUiCallback((activity) =>\n this.deliveryHandler.getEffectiveHideLmsUi(activity)\n );\n\n // Set up state manager callbacks\n this.stateManager.setGetEffectiveHideLmsUiCallback((activity) =>\n this.deliveryHandler.getEffectiveHideLmsUi(activity)\n );\n this.stateManager.setGetEffectiveAuxiliaryResourcesCallback((activity) =>\n this.deliveryHandler.getEffectiveAuxiliaryResources(activity)\n );\n this.stateManager.setContentDeliveredAccessors(\n () => this.deliveryHandler.hasContentBeenDelivered(),\n (value) => this.deliveryHandler.setContentDelivered(value)\n );\n }\n\n /**\n * Overall Sequencing Process\n * Main entry point for processing navigation requests\n * @spec SN Book: OP.1 (Overall Sequencing Process)\n * @param {NavigationRequestType} navigationRequest - The navigation request\n * @param {string | null} targetActivityId - Target activity for choice/jump requests\n * @param {string} exitType - The cmi.exit value (logout, normal, suspend, time-out, or empty)\n * @return {DeliveryRequest} - The delivery request result\n */\n public processNavigationRequest(\n navigationRequest: NavigationRequestType,\n targetActivityId: string | null = null,\n exitType?: string\n ): DeliveryRequest {\n // Step 1: Navigation Request Process (NB.2.1)\n const navResult = this.navigationValidityService.validateRequest(\n navigationRequest,\n targetActivityId\n );\n\n if (!navResult.valid) {\n return new DeliveryRequest(false, null, navResult.exception);\n }\n\n // Step 2: Termination Request Process (TB.2.3) if needed\n if (navResult.terminationRequest) {\n const hadSequencingRequest = !!navResult.sequencingRequest;\n const termResult = this.terminationHandler.processTerminationRequest(\n navResult.terminationRequest,\n hadSequencingRequest,\n exitType\n );\n\n if (!termResult.valid) {\n return new DeliveryRequest(false, null, termResult.exception || \"TB.2.3-1\");\n }\n\n // Per TB.2.3 Step 3.6/4.5: Post-condition sequencing request overrides navigation request\n if (termResult.sequencingRequest !== null) {\n if (\n hadSequencingRequest ||\n termResult.sequencingRequest !== SequencingRequestType.EXIT\n ) {\n navResult.sequencingRequest = termResult.sequencingRequest;\n }\n }\n\n // If this is a termination-only request (no sequencing request), return success\n if (!navResult.sequencingRequest) {\n // For EXIT_ALL and ABANDON_ALL, fire session end event\n if (\n navResult.terminationRequest === SequencingRequestType.EXIT_ALL ||\n navResult.terminationRequest === SequencingRequestType.ABANDON_ALL\n ) {\n this.fireEvent(\"onSequencingSessionEnd\", {\n reason:\n navResult.terminationRequest === SequencingRequestType.EXIT_ALL\n ? \"exit_all\"\n : \"abandon_all\",\n navigationRequest: navigationRequest,\n });\n }\n return new DeliveryRequest(true, null);\n }\n }\n\n // Step 3: Sequencing Request Process (SB.2.12)\n if (navResult.sequencingRequest) {\n const seqResult = this.sequencingProcess.sequencingRequestProcess(\n navResult.sequencingRequest,\n navResult.targetActivityId\n );\n\n // OP.1 step 1.4.3: Check if sequencing session should end\n if (seqResult.endSequencingSession) {\n this.fireEvent(\"onSequencingSessionEnd\", {\n reason: \"end_of_content\",\n exception: seqResult.exception,\n navigationRequest: navigationRequest,\n });\n\n // Return delivery request indicating session end\n return new DeliveryRequest(\n false,\n null,\n seqResult.exception || \"SESSION_ENDED\"\n );\n }\n\n if (seqResult.exception) {\n return new DeliveryRequest(false, null, seqResult.exception);\n }\n\n if (\n seqResult.deliveryRequest === DeliveryRequestType.DELIVER &&\n seqResult.targetActivity\n ) {\n // INTEGRATION: Validate rollup state consistency before delivery\n if (this.activityTree.root) {\n const isConsistent = this.rollupProcess.validateRollupStateConsistency(\n this.activityTree.root\n );\n if (!isConsistent) {\n this.fireEvent(\"onSequencingDebug\", {\n message: \"Rollup state inconsistency detected before delivery\",\n activityId: this.activityTree.root.id,\n });\n }\n }\n\n // INTEGRATION: Process global objective mapping before delivery\n this.rollupProcess.processGlobalObjectiveMapping(\n seqResult.targetActivity,\n this.globalObjectiveService.getMap()\n );\n\n // Step 4: Delivery Request Process (DB.1.1)\n return this.processDelivery(seqResult.targetActivity);\n }\n }\n\n return new DeliveryRequest(false, null, \"OP.1-1\");\n }\n\n /**\n * Process delivery of an activity\n * @param {Activity} targetActivity - The activity to deliver\n * @return {DeliveryRequest} - The delivery result\n */\n private processDelivery(targetActivity: Activity): DeliveryRequest {\n // Enhanced delivery validation if enabled\n if (this.enhancedDeliveryValidation) {\n const stateCheck = this.deliveryValidator.validateTreeConsistency(targetActivity);\n if (!stateCheck.consistent) {\n return new DeliveryRequest(false, null, stateCheck.exception);\n }\n\n const resourceCheck = this.deliveryValidator.validateResources(targetActivity);\n if (!resourceCheck.available) {\n return new DeliveryRequest(false, null, resourceCheck.exception);\n }\n\n const concurrentCheck =\n this.deliveryValidator.validateConcurrentDelivery(targetActivity);\n if (!concurrentCheck.allowed) {\n return new DeliveryRequest(false, null, concurrentCheck.exception);\n }\n\n const dependencyCheck =\n this.deliveryValidator.validateDependencies(targetActivity);\n if (!dependencyCheck.satisfied) {\n return new DeliveryRequest(false, null, dependencyCheck.exception);\n }\n }\n\n // Standard delivery request process\n const deliveryResult = this.deliveryHandler.processDeliveryRequest(targetActivity);\n\n if (deliveryResult.valid) {\n // Step 5: Content Delivery Environment Process (DB.2)\n this.deliveryHandler.contentDeliveryEnvironmentProcess(deliveryResult.targetActivity!);\n\n // Invalidate navigation predictions after activity change\n this.navigationLookAhead.invalidateCache();\n\n // INTEGRATION: Validate rollup state consistency after delivery\n if (this.activityTree.root) {\n this.rollupProcess.validateRollupStateConsistency(this.activityTree.root);\n }\n }\n\n return deliveryResult;\n }\n\n // ========== Public API Methods ==========\n\n /**\n * Check if content has been delivered\n */\n public hasContentBeenDelivered(): boolean {\n return this.deliveryHandler.hasContentBeenDelivered();\n }\n\n /**\n * Check if content delivery is currently in progress\n */\n public isDeliveryInProgress(): boolean {\n return this.deliveryHandler.isDeliveryInProgress();\n }\n\n /**\n * Reset content delivered flag\n */\n public resetContentDelivered(): void {\n this.deliveryHandler.resetContentDelivered();\n }\n\n /**\n * Set content delivered flag\n * @param {boolean} value - The value to set\n */\n public setContentDelivered(value: boolean): void {\n this.deliveryHandler.setContentDelivered(value);\n }\n\n /**\n * Update navigation validity in ADL nav\n */\n public updateNavigationValidity(): void {\n this.navigationValidityService.updateNavigationValidity();\n }\n\n /**\n * Synchronize global objectives from activity states\n */\n public synchronizeGlobalObjectives(): void {\n this.globalObjectiveService.synchronize(\n this.activityTree.root,\n this.rollupProcess\n );\n }\n\n /**\n * Get the global objective map\n * @return {Map<string, any>} - Current global objective map\n */\n public getGlobalObjectiveMap(): Map<string, any> {\n return this.globalObjectiveService.getMap();\n }\n\n /**\n * Get a snapshot of the global objective map\n * @return {Record<string, any>} - Plain-object snapshot\n */\n public getGlobalObjectiveMapSnapshot(): Record<string, any> {\n return this.globalObjectiveService.getSnapshot();\n }\n\n /**\n * Restore global objective map from snapshot\n * @param {Record<string, any>} snapshot - Snapshot to restore\n */\n public restoreGlobalObjectiveMapSnapshot(snapshot: Record<string, any>): void {\n this.globalObjectiveService.restoreSnapshot(snapshot);\n }\n\n /**\n * Update a specific global objective\n * @param {string} objectiveId - Objective ID\n * @param {any} objectiveData - New objective data\n */\n public updateGlobalObjective(objectiveId: string, objectiveData: any): void {\n this.globalObjectiveService.updateObjective(objectiveId, objectiveData);\n }\n\n /**\n * Get Sequencing State for Persistence\n * @return {SequencingState} - Serializable sequencing state\n */\n public getSequencingState(): SequencingState {\n return this.stateManager.getState();\n }\n\n /**\n * Restore Sequencing State from Persistence\n * @param {SequencingState} state - State to restore\n * @return {boolean} - True if successful\n */\n public restoreSequencingState(state: SequencingState): boolean {\n return this.stateManager.restoreState(state);\n }\n\n /**\n * Get complete suspension state\n * @return {SuspensionState} - Complete suspension state\n */\n public getSuspensionState(): SuspensionState {\n return this.stateManager.getSuspensionState();\n }\n\n /**\n * Restore complete suspension state\n * @param {SuspensionState} state - Suspension state to restore\n */\n public restoreSuspensionState(state: SuspensionState): void {\n this.stateManager.restoreSuspensionState(state);\n }\n\n /**\n * Get navigation look-ahead predictions\n * @return {NavigationPredictions} - Current navigation predictions\n */\n public getNavigationLookAhead(): NavigationPredictions {\n return this.navigationValidityService.getAllPredictions();\n }\n\n /**\n * Predict if Continue navigation would succeed\n * @return {boolean} - True if Continue would succeed\n */\n public predictContinueEnabled(): boolean {\n return this.navigationValidityService.predictContinueEnabled();\n }\n\n /**\n * Predict if Previous navigation would succeed\n * @return {boolean} - True if Previous would succeed\n */\n public predictPreviousEnabled(): boolean {\n return this.navigationValidityService.predictPreviousEnabled();\n }\n\n /**\n * Predict if choice to specific activity would succeed\n * @param {string} activityId - Target activity ID\n * @return {boolean} - True if choice would succeed\n */\n public predictChoiceEnabled(activityId: string): boolean {\n return this.navigationValidityService.predictChoiceEnabled(activityId);\n }\n\n /**\n * Get list of all activities that can be chosen\n * @return {string[]} - Array of activity IDs available for choice\n */\n public getAvailableChoices(): string[] {\n return this.navigationValidityService.getAvailableChoices();\n }\n\n /**\n * Invalidate navigation prediction cache\n */\n public invalidateNavigationCache(): void {\n this.navigationValidityService.invalidateCache();\n }\n\n /**\n * Apply delivery controls for auto-completion and auto-satisfaction\n * @param {Activity} activity - The activity to apply delivery controls to\n */\n public applyDeliveryControls(activity: Activity): void {\n // Auto-completion when completionSetByContent is false\n if (!activity.sequencingControls.completionSetByContent) {\n if (activity.completionStatus === CompletionStatus.UNKNOWN) {\n activity.completionStatus = CompletionStatus.COMPLETED;\n activity.wasAutoCompleted = true;\n }\n }\n\n // Auto-satisfaction when objectiveSetByContent is false\n if (!activity.sequencingControls.objectiveSetByContent) {\n if (activity.successStatus === SuccessStatus.UNKNOWN) {\n activity.successStatus = SuccessStatus.PASSED;\n activity.wasAutoSatisfied = true;\n }\n }\n }\n\n /**\n * Get effective hideLmsUi for an activity\n * @param {Activity | null} activity - The activity\n * @return {HideLmsUiItem[]} - Ordered list of hideLmsUi directives\n */\n public getEffectiveHideLmsUi(activity: Activity | null): HideLmsUiItem[] {\n return this.deliveryHandler.getEffectiveHideLmsUi(activity);\n }\n\n /**\n * Get effective auxiliary resources for an activity\n * @param {Activity | null} activity - The activity\n * @return {AuxiliaryResource[]} - Merged auxiliary resources\n */\n public getEffectiveAuxiliaryResources(\n activity: Activity | null\n ): AuxiliaryResource[] {\n return this.deliveryHandler.getEffectiveAuxiliaryResources(activity);\n }\n\n /**\n * Get content activity data for a delivered activity\n * @param {Activity} activity - The activity\n * @return {ContentActivityData} - Content activity data\n */\n public getContentActivityData(activity: Activity): ContentActivityData {\n return this.deliveryHandler.getContentActivityData(activity);\n }\n\n // ========== Backward Compatibility Delegations ==========\n // These methods delegate to the extracted handlers for backward compatibility\n\n /**\n * Termination Request Process\n * Delegates to TerminationHandler for backward compatibility\n * @spec SN Book: TB.2.3 (Termination Request Process)\n * @param {SequencingRequestType} request - The termination request\n * @param {boolean} hasSequencingRequest - Whether a sequencing request follows\n * @param {string} exitType - The cmi.exit value\n * @return {TerminationResult} - Termination result with sequencing request\n */\n public terminationRequestProcess(\n request: SequencingRequestType,\n hasSequencingRequest: boolean,\n exitType?: string\n ): TerminationResult {\n return this.terminationHandler.processTerminationRequest(\n request,\n hasSequencingRequest,\n exitType\n );\n }\n\n /**\n * Content Delivery Environment Process\n * Delegates to DeliveryHandler for backward compatibility\n * @spec SN Book: DB.2 (Content Delivery Environment Process)\n * @param {Activity} activity - The activity to initialize for delivery\n */\n public contentDeliveryEnvironmentProcess(activity: Activity): void {\n this.deliveryHandler.contentDeliveryEnvironmentProcess(activity);\n }\n\n /**\n * End Attempt Process\n * Delegates to TerminationHandler for backward compatibility\n * @spec SN Book: UP.4 (End Attempt Process)\n * @param {Activity} activity - The activity whose attempt is ending\n */\n public endAttemptProcess(activity: Activity): void {\n this.terminationHandler.endAttempt(activity);\n }\n\n /**\n * Handle EXIT Termination\n * Delegates to TerminationHandler for backward compatibility\n * @param {Activity} currentActivity - The activity being terminated\n * @param {boolean} hasSequencingRequest - Whether a sequencing request follows\n * @return {TerminationResult} - The termination result\n */\n public handleExitTermination(\n currentActivity: Activity,\n hasSequencingRequest: boolean\n ): TerminationResult {\n return this.terminationHandler.handleExitTermination(\n currentActivity,\n hasSequencingRequest\n );\n }\n\n /**\n * Fire a sequencing event\n * @param {string} eventType - The type of event\n * @param {any} data - Event data\n */\n private fireEvent(eventType: string, data?: any): void {\n try {\n if (this.eventCallback) {\n this.eventCallback(eventType, data);\n }\n } catch (error) {\n console.warn(`Failed to fire sequencing event ${eventType}: ${error}`);\n }\n }\n}\n","import { Activity } from \"../cmi/scorm2004/sequencing/activity\";\nimport { Sequencing } from \"../cmi/scorm2004/sequencing/sequencing\";\nimport { RollupProcess } from \"../cmi/scorm2004/sequencing/rollup_process\";\nimport {\n DeliveryRequest,\n NavigationRequestType,\n OverallSequencingProcess,\n} from \"../cmi/scorm2004/sequencing/overall_sequencing_process\";\nimport {\n DeliveryRequestType,\n SequencingProcess,\n SequencingResult,\n} from \"../cmi/scorm2004/sequencing/sequencing_process\";\nimport { IEventService, ILoggingService } from \"../interfaces/services\";\nimport { ActivityDeliveryCallbacks, ActivityDeliveryService } from \"./ActivityDeliveryService\";\nimport { CMI } from \"../cmi/scorm2004/cmi\";\nimport { CMIObjectivesObject } from \"../cmi/scorm2004/objectives\";\nimport { ADL } from \"../cmi/scorm2004/adl\";\nimport { global_constants } from \"../constants/api_constants\";\nimport { RuleCondition } from \"../cmi/scorm2004/sequencing/sequencing_rules\";\nimport {\n AuxiliaryResource,\n HideLmsUiItem,\n SequencingEventListeners,\n} from \"../types/sequencing_types\";\n\n/**\n * Interface for sequencing configuration\n */\nexport interface SequencingConfiguration {\n autoRollupOnCMIChange?: boolean;\n autoProgressOnCompletion?: boolean;\n validateNavigationRequests?: boolean;\n enableEventSystem?: boolean;\n logLevel?: \"debug\" | \"info\" | \"warn\" | \"error\";\n // Time providers/hooks (LMS can override)\n now?: () => Date;\n getAttemptElapsedSeconds?: (activity: Activity) => number;\n getActivityElapsedSeconds?: (activity: Activity) => number;\n}\n\n/**\n * Comprehensive SCORM 2004 Sequencing Service\n * Handles all aspects of sequencing integration with runtime API calls\n */\nexport class SequencingService {\n private sequencing: Sequencing;\n private cmi: CMI;\n private adl: ADL;\n private eventService: IEventService;\n private loggingService: ILoggingService;\n private activityDeliveryService: ActivityDeliveryService;\n private rollupProcess: RollupProcess;\n private overallSequencingProcess: OverallSequencingProcess | null = null;\n private sequencingProcess: SequencingProcess | null = null;\n\n private eventListeners: SequencingEventListeners = {};\n private configuration: SequencingConfiguration;\n private isInitialized: boolean = false;\n private isSequencingActive: boolean = false;\n private lastCMIValues: Map<string, any> = new Map();\n private lastSequencingResult: SequencingResult | null = null;\n\n constructor(\n sequencing: Sequencing,\n cmi: CMI,\n adl: ADL,\n eventService: IEventService,\n loggingService: ILoggingService,\n configuration: SequencingConfiguration = {},\n ) {\n this.sequencing = sequencing;\n this.cmi = cmi;\n this.adl = adl;\n this.eventService = eventService;\n this.loggingService = loggingService;\n\n // Default configuration\n this.configuration = {\n autoRollupOnCMIChange: true,\n autoProgressOnCompletion: false,\n validateNavigationRequests: true,\n enableEventSystem: true,\n logLevel: \"info\",\n now: () => new Date(),\n ...configuration,\n };\n\n // Create activity delivery service\n const deliveryCallbacks: ActivityDeliveryCallbacks = {\n onDeliverActivity: (activity) => this.handleActivityDelivery(activity),\n onUnloadActivity: (activity) => this.handleActivityUnload(activity),\n onSequencingComplete: (result) => this.handleSequencingComplete(result),\n onSequencingError: (error) => this.handleSequencingError(error),\n };\n\n this.activityDeliveryService = new ActivityDeliveryService(\n eventService,\n loggingService,\n deliveryCallbacks,\n );\n\n this.rollupProcess = new RollupProcess();\n // Propagate time provider to rule evaluation (time-based conditions)\n if (this.configuration.now) {\n RuleCondition.setNowProvider(this.configuration.now);\n }\n this.setupCMIChangeWatchers();\n\n // Create sequencing processes immediately so navigation requests work before SCO Initialize\n this.createSequencingProcesses();\n }\n\n /**\n * Create sequencing processes\n * Called from constructor to enable navigation before SCO Initialize\n */\n private createSequencingProcesses(): void {\n try {\n // Initialize sequencing components if not already done\n if (!this.sequencing.initialized) {\n this.sequencing.initialize();\n }\n\n // Set up ADL Nav connection\n this.sequencing.adlNav = this.adl.nav;\n\n // Create sequencing processes if we have an activity tree\n if (this.sequencing.activityTree.root) {\n const seqOptions: {\n now?: () => Date;\n getAttemptElapsedSeconds?: (a: Activity) => number;\n getActivityElapsedSeconds?: (a: Activity) => number;\n } = {};\n if (this.configuration.now) seqOptions.now = this.configuration.now;\n if (this.configuration.getAttemptElapsedSeconds)\n seqOptions.getAttemptElapsedSeconds = this.configuration.getAttemptElapsedSeconds;\n if (this.configuration.getActivityElapsedSeconds)\n seqOptions.getActivityElapsedSeconds = this.configuration.getActivityElapsedSeconds;\n\n this.sequencingProcess = new SequencingProcess(\n this.sequencing.activityTree,\n this.sequencing.sequencingRules,\n this.sequencing.sequencingControls,\n this.adl.nav,\n seqOptions,\n );\n\n const overallOptions: {\n now?: () => Date;\n enhancedDeliveryValidation?: boolean;\n defaultHideLmsUi?: HideLmsUiItem[];\n defaultAuxiliaryResources?: AuxiliaryResource[];\n getCMIData?: () => any;\n is4thEdition?: boolean;\n } = {};\n if (this.configuration.now) overallOptions.now = this.configuration.now;\n overallOptions.defaultHideLmsUi = [...this.sequencing.hideLmsUi];\n if (this.sequencing.auxiliaryResources.length > 0) {\n overallOptions.defaultAuxiliaryResources = this.sequencing.auxiliaryResources.map(\n (resource) => ({\n resourceId: resource.resourceId,\n purpose: resource.purpose,\n }),\n );\n }\n\n // Provide CMI data callback for RTE data transfer\n overallOptions.getCMIData = () => this.getCMIDataForTransfer();\n\n this.overallSequencingProcess = new OverallSequencingProcess(\n this.sequencing.activityTree,\n this.sequencingProcess,\n this.rollupProcess,\n this.adl.nav,\n (eventType: string, data?: any) => this.handleSequencingProcessEvent(eventType, data),\n overallOptions,\n );\n\n // Store reference on sequencing object for access from ADL nav\n this.sequencing.overallSequencingProcess = this.overallSequencingProcess;\n\n // Mark as initialized so navigation requests work\n this.isInitialized = true;\n\n this.log(\"info\", \"Sequencing processes created and ready for navigation\");\n }\n } catch (error) {\n this.log(\"error\", `Failed to create sequencing processes: ${error}`);\n }\n }\n\n /**\n * Initialize the sequencing service\n * Called when SCORM API Initialize() is called\n * Note: Sequencing processes are created in constructor to enable pre-SCO navigation\n */\n public initialize(): string {\n try {\n this.log(\"info\", \"Initializing sequencing service for SCO session\");\n\n // Ensure processes are created (should already be done in constructor)\n if (!this.isInitialized) {\n this.createSequencingProcesses();\n }\n\n // Start automatic sequencing if configured and not already started\n if (this.shouldAutoStartSequencing() && !this.isSequencingActive) {\n this.startSequencing();\n }\n\n // Initialize CMI tracking for the current activity\n this.initializeCMITracking();\n this.fireEvent(\"onSequencingStart\", this.sequencing.getCurrentActivity());\n\n this.log(\"info\", \"Sequencing service initialized successfully\");\n return global_constants.SCORM_TRUE;\n } catch (error) {\n const errorMsg = `Failed to initialize sequencing service: ${error}`;\n this.log(\"error\", errorMsg);\n this.fireEvent(\"onSequencingError\", errorMsg, \"initialization\");\n return global_constants.SCORM_FALSE;\n }\n }\n\n /**\n * Terminate the sequencing service\n * Called when SCORM API Terminate() is called\n */\n public terminate(): string {\n try {\n this.log(\"info\", \"Terminating sequencing service\");\n\n // Trigger final rollup for the current activity\n this.triggerFinalRollup();\n\n // End sequencing session\n this.endSequencing();\n\n this.isInitialized = false;\n this.fireEvent(\"onSequencingEnd\");\n\n this.log(\"info\", \"Sequencing service terminated successfully\");\n return global_constants.SCORM_TRUE;\n } catch (error) {\n const errorMsg = `Failed to terminate sequencing service: ${error}`;\n this.log(\"error\", errorMsg);\n this.fireEvent(\"onSequencingError\", errorMsg, \"termination\");\n return global_constants.SCORM_FALSE;\n }\n }\n\n /**\n * Process a navigation request\n * Implements the complete Overall Sequencing Process (OP.1)\n * @param {string} request - The navigation request\n * @param {string} targetActivityId - Optional target activity ID for choice/jump requests\n * @param {string} exitType - Optional cmi.exit value (logout, normal, suspend, time-out, or empty)\n */\n public processNavigationRequest(\n request: string,\n targetActivityId?: string,\n exitType?: string,\n ): boolean {\n if (!this.isInitialized || !this.overallSequencingProcess) {\n this.log(\"warn\", `Navigation request '${request}' ignored - sequencing not initialized`);\n return false;\n }\n\n try {\n this.log(\n \"info\",\n `Processing navigation request: ${request}${targetActivityId ? ` (target: ${targetActivityId})` : \"\"}${exitType ? ` (exit: ${exitType})` : \"\"}`,\n );\n\n // Fire navigation request event\n this.fireEvent(\"onNavigationRequest\", request, targetActivityId);\n\n // Parse the request to NavigationRequestType\n const navRequestType = this.parseNavigationRequest(request);\n if (navRequestType === null) {\n this.log(\"warn\", `Invalid navigation request: ${request}`);\n return false;\n }\n\n // Process the navigation request through Overall Sequencing Process\n const deliveryRequest: DeliveryRequest =\n this.overallSequencingProcess.processNavigationRequest(\n navRequestType,\n targetActivityId || null,\n exitType,\n );\n\n const sequencingResult: SequencingResult = {\n deliveryRequest: deliveryRequest.valid\n ? DeliveryRequestType.DELIVER\n : DeliveryRequestType.DO_NOT_DELIVER,\n targetActivity: deliveryRequest.targetActivity,\n exception: deliveryRequest.exception || null,\n endSequencingSession: false,\n };\n\n // Store the result\n this.lastSequencingResult = sequencingResult;\n\n // Handle the delivery request\n if (deliveryRequest.valid && deliveryRequest.targetActivity) {\n // Process delivery through activity delivery service\n this.activityDeliveryService.processSequencingResult(sequencingResult);\n\n // Update navigation validity for the new current activity\n // This ensures Continue/Previous buttons are correctly enabled/disabled\n this.overallSequencingProcess.updateNavigationValidity();\n\n this.log(\n \"info\",\n `Navigation request '${request}' resulted in activity delivery: ${deliveryRequest.targetActivity.id}`,\n );\n return true;\n } else {\n // No delivery requested or invalid\n if (deliveryRequest.exception) {\n this.log(\"warn\", `Navigation request '${request}' failed: ${deliveryRequest.exception}`);\n this.fireEvent(\"onSequencingError\", deliveryRequest.exception, \"navigation\");\n } else {\n this.log(\"info\", `Navigation request '${request}' completed with no activity delivery`);\n }\n return deliveryRequest.valid;\n }\n } catch (error) {\n const errorMsg = `Error processing navigation request '${request}': ${error}`;\n this.log(\"error\", errorMsg);\n this.fireEvent(\"onSequencingError\", errorMsg, \"navigation\");\n return false;\n }\n }\n\n /**\n * Trigger rollup when CMI values change\n * Called automatically when tracked CMI values are updated\n */\n public triggerRollupOnCMIChange(cmiElement: string, oldValue: any, newValue: any): void {\n if (!this.configuration.autoRollupOnCMIChange || !this.isInitialized) {\n return;\n }\n\n // Only trigger rollup for specific CMI elements that affect sequencing\n const rollupTriggeringElements = [\n \"cmi.completion_status\",\n \"cmi.success_status\",\n \"cmi.score.scaled\",\n \"cmi.score.raw\",\n \"cmi.score.min\",\n \"cmi.score.max\",\n \"cmi.progress_measure\",\n \"cmi.objectives.n.success_status\",\n \"cmi.objectives.n.completion_status\",\n \"cmi.objectives.n.score.scaled\",\n ];\n\n if (!rollupTriggeringElements.some((element) => cmiElement.startsWith(element))) {\n return;\n }\n\n try {\n this.log(\n \"debug\",\n `Triggering rollup due to CMI change: ${cmiElement} = ${newValue} (was ${oldValue})`,\n );\n\n // Get current activity\n const currentActivity = this.sequencing.getCurrentActivity();\n if (!currentActivity) {\n this.log(\"debug\", \"No current activity for rollup\");\n return;\n }\n\n // Update activity status based on CMI changes\n this.updateActivityFromCMI(currentActivity);\n\n // Trigger rollup process\n this.rollupProcess.overallRollupProcess(currentActivity);\n\n // Synchronize global objectives after rollup (required for cross-activity preconditions)\n if (this.overallSequencingProcess) {\n this.overallSequencingProcess.synchronizeGlobalObjectives();\n }\n\n // Update navigation validity after rollup (preconditions may have changed)\n if (this.overallSequencingProcess) {\n this.overallSequencingProcess.updateNavigationValidity();\n }\n\n this.fireEvent(\"onRollupComplete\", currentActivity);\n\n this.log(\"debug\", `Rollup completed for activity: ${currentActivity.id}`);\n } catch (error) {\n const errorMsg = `Error during rollup on CMI change: ${error}`;\n this.log(\"error\", errorMsg);\n this.fireEvent(\"onSequencingError\", errorMsg, \"rollup\");\n }\n }\n\n /**\n * Set event listeners for sequencing events\n */\n public setEventListeners(listeners: SequencingEventListeners): void {\n this.eventListeners = { ...this.eventListeners, ...listeners };\n this.log(\"debug\", \"Sequencing event listeners updated\");\n }\n\n /**\n * Update sequencing configuration\n */\n public updateConfiguration(config: Partial<SequencingConfiguration>): void {\n this.configuration = { ...this.configuration, ...config };\n this.log(\"debug\", \"Sequencing configuration updated\");\n }\n\n /**\n * Get the current sequencing state\n */\n public getSequencingState(): {\n isInitialized: boolean;\n isActive: boolean;\n currentActivity: Activity | null;\n rootActivity: Activity | null;\n lastSequencingResult: SequencingResult | null;\n } {\n return {\n isInitialized: this.isInitialized,\n isActive: this.isSequencingActive,\n currentActivity: this.sequencing.getCurrentActivity(),\n rootActivity: this.sequencing.getRootActivity(),\n lastSequencingResult: this.lastSequencingResult,\n };\n }\n\n /**\n * Get the overall sequencing process instance\n * @return {OverallSequencingProcess | null} The overall sequencing process or null if not initialized\n */\n public getOverallSequencingProcess(): OverallSequencingProcess | null {\n return this.overallSequencingProcess;\n }\n\n /**\n * Check if content delivery is currently in progress\n * Used to prevent re-entrant termination requests during delivery\n * @return {boolean} True if delivery is in progress\n */\n public isDeliveryInProgress(): boolean {\n return this.overallSequencingProcess?.isDeliveryInProgress() ?? false;\n }\n\n // Private helper methods\n\n /**\n * Set up watchers for CMI value changes\n */\n private setupCMIChangeWatchers(): void {\n // We'll hook into the setter methods to detect changes\n // This would typically be done by modifying the CMI setters\n // For now, we'll track changes when values are set\n }\n\n /**\n * Initialize CMI tracking by storing current values\n */\n private initializeCMITracking(): void {\n // Store initial CMI values for change detection\n this.lastCMIValues.set(\"cmi.completion_status\", this.cmi.completion_status);\n this.lastCMIValues.set(\"cmi.success_status\", this.cmi.success_status);\n this.lastCMIValues.set(\"cmi.progress_measure\", this.cmi.progress_measure);\n\n if (this.cmi.score) {\n this.lastCMIValues.set(\"cmi.score.scaled\", this.cmi.score.scaled);\n this.lastCMIValues.set(\"cmi.score.raw\", this.cmi.score.raw);\n }\n }\n\n /**\n * Check if sequencing should auto-start\n */\n private shouldAutoStartSequencing(): boolean {\n // Auto-start if we have a root activity and no current activity\n return !!(this.sequencing.activityTree.root && !this.sequencing.getCurrentActivity());\n }\n\n /**\n * Start automatic sequencing\n */\n private startSequencing(): void {\n if (!this.overallSequencingProcess) {\n return;\n }\n\n try {\n // Process a \"start\" navigation request to begin sequencing\n const startResult = this.processNavigationRequest(\"start\");\n if (startResult) {\n this.isSequencingActive = true;\n this.log(\"info\", \"Automatic sequencing started\");\n }\n } catch (error) {\n this.log(\"error\", `Failed to start automatic sequencing: ${error}`);\n }\n }\n\n /**\n * End sequencing session\n */\n private endSequencing(): void {\n this.isSequencingActive = false;\n this.activityDeliveryService.reset();\n }\n\n /**\n * Trigger final rollup on termination\n */\n private triggerFinalRollup(): void {\n try {\n const currentActivity = this.sequencing.getCurrentActivity();\n if (currentActivity) {\n // Update activity with final CMI values\n this.updateActivityFromCMI(currentActivity);\n\n // Trigger rollup\n this.rollupProcess.overallRollupProcess(currentActivity);\n\n // Synchronize global objectives after final rollup\n if (this.overallSequencingProcess) {\n this.overallSequencingProcess.synchronizeGlobalObjectives();\n }\n\n this.log(\"info\", \"Final rollup completed\");\n }\n } catch (error) {\n this.log(\"error\", `Error during final rollup: ${error}`);\n }\n }\n\n /**\n * Update activity properties from current CMI values\n */\n private updateActivityFromCMI(activity: Activity): void {\n // Update completion status\n if (this.cmi.completion_status !== \"unknown\") {\n activity.completionStatus = this.cmi.completion_status as\n | \"completed\"\n | \"incomplete\"\n | \"not attempted\"\n | \"unknown\";\n // Mark that content has set completion status\n activity.attemptProgressStatus = true;\n }\n\n // Update success status\n if (this.cmi.success_status !== \"unknown\") {\n activity.successStatus = this.cmi.success_status as \"passed\" | \"failed\" | \"unknown\";\n activity.objectiveSatisfiedStatus = this.cmi.success_status === \"passed\";\n // Set measureStatus to true since we now have a known satisfied status\n activity.objectiveMeasureStatus = true;\n // Mark that content has set objective satisfaction status\n if (activity.primaryObjective) {\n activity.primaryObjective.progressStatus = true;\n }\n }\n\n // Update progress measure\n if (this.cmi.progress_measure !== \"\") {\n const progressMeasure = parseFloat(this.cmi.progress_measure);\n if (!isNaN(progressMeasure)) {\n activity.progressMeasure = progressMeasure;\n activity.progressMeasureStatus = true;\n }\n }\n\n // Update score information\n if (this.cmi.score && this.cmi.score.scaled !== \"\") {\n const scaledScore = parseFloat(this.cmi.score.scaled);\n if (!isNaN(scaledScore)) {\n activity.objectiveNormalizedMeasure = scaledScore;\n activity.objectiveMeasureStatus = true;\n // Mark that content has set objective measure (which affects satisfaction)\n if (activity.primaryObjective) {\n activity.primaryObjective.progressStatus = true;\n }\n }\n }\n\n // Sync primary objective with activity state for global objective mapping\n if (activity.primaryObjective) {\n activity.primaryObjective.updateFromActivity(activity);\n }\n }\n\n /**\n * Get CMI data for RTE data transfer to activity state\n * This method provides all CMI data to the sequencing process for transfer\n * @return {Object} - CMI data formatted for transfer\n */\n private getCMIDataForTransfer(): any {\n const cmiData: any = {\n completion_status: this.cmi.completion_status,\n success_status: this.cmi.success_status,\n progress_measure: this.cmi.progress_measure,\n score: {\n scaled: this.cmi.score?.scaled || \"\",\n raw: this.cmi.score?.raw || \"\",\n min: this.cmi.score?.min || \"\",\n max: this.cmi.score?.max || \"\",\n },\n objectives: [],\n };\n\n // Transfer all CMI objectives\n if (this.cmi.objectives && this.cmi.objectives.childArray) {\n for (const baseCmiObj of this.cmi.objectives.childArray) {\n const cmiObjective = baseCmiObj as CMIObjectivesObject;\n if (cmiObjective.id) {\n cmiData.objectives.push({\n id: cmiObjective.id,\n success_status: cmiObjective.success_status,\n completion_status: cmiObjective.completion_status,\n progress_measure: cmiObjective.progress_measure,\n score: {\n scaled: cmiObjective.score?.scaled || \"\",\n raw: cmiObjective.score?.raw || \"\",\n min: cmiObjective.score?.min || \"\",\n max: cmiObjective.score?.max || \"\",\n },\n });\n }\n }\n }\n\n return cmiData;\n }\n\n /**\n * Parse navigation request string to NavigationRequestType\n */\n private parseNavigationRequest(request: string): NavigationRequestType | null {\n // Normalize SCORM nav request tokens (e.g., _continue, _previous)\n let normalizedRequest = request;\n if (normalizedRequest.startsWith(\"_\") && normalizedRequest !== \"_none_\") {\n normalizedRequest = normalizedRequest.substring(1);\n }\n\n // Handle choice and jump with targets\n if (request.includes(\"choice\")) {\n return NavigationRequestType.CHOICE;\n }\n if (request.includes(\"jump\")) {\n return NavigationRequestType.JUMP;\n }\n\n // Handle standard navigation requests\n switch (normalizedRequest) {\n case \"start\":\n return NavigationRequestType.START;\n case \"resumeAll\":\n return NavigationRequestType.RESUME_ALL;\n case \"continue\":\n return NavigationRequestType.CONTINUE;\n case \"previous\":\n return NavigationRequestType.PREVIOUS;\n case \"exit\":\n return NavigationRequestType.EXIT;\n case \"exitAll\":\n return NavigationRequestType.EXIT_ALL;\n case \"abandon\":\n return NavigationRequestType.ABANDON;\n case \"abandonAll\":\n return NavigationRequestType.ABANDON_ALL;\n case \"suspendAll\":\n return NavigationRequestType.SUSPEND_ALL;\n case \"_none_\":\n return NavigationRequestType.NOT_VALID;\n default:\n return null;\n }\n }\n\n /**\n * Handle activity delivery event\n */\n private handleActivityDelivery(activity: Activity): void {\n this.log(\"info\", `Activity delivered: ${activity.id} - ${activity.title}`);\n this.fireEvent(\"onActivityDelivery\", activity);\n }\n\n /**\n * Handle activity unload event\n */\n private handleActivityUnload(activity: Activity): void {\n this.log(\"info\", `Activity unloaded: ${activity.id} - ${activity.title}`);\n this.fireEvent(\"onActivityUnload\", activity);\n }\n\n /**\n * Handle sequencing completion event\n */\n private handleSequencingComplete(result: SequencingResult): void {\n this.log(\"debug\", \"Sequencing completed\", result);\n }\n\n /**\n * Handle sequencing error event\n */\n private handleSequencingError(error: string): void {\n this.log(\"error\", `Sequencing error: ${error}`);\n this.fireEvent(\"onSequencingError\", error, \"sequencing\");\n }\n\n /**\n * Fire an event to registered listeners with enhanced error handling\n */\n private fireEvent(eventType: keyof SequencingEventListeners, ...args: any[]): void {\n if (!this.configuration.enableEventSystem) {\n return;\n }\n\n // Only fire debug event for non-debug events to prevent recursion\n if (eventType !== \"onSequencingDebug\") {\n this.fireDebugEvent(`${eventType} fired`, { eventType, argsLength: args.length });\n }\n\n try {\n // Fire to internal listeners first\n const listener = this.eventListeners[eventType];\n if (listener && typeof listener === \"function\") {\n try {\n (listener as any)(...args);\n this.log(\"debug\", `Internal listener for ${eventType} executed successfully`);\n } catch (listenerError) {\n this.log(\"error\", `Internal listener for ${eventType} failed: ${listenerError}`);\n // Don't let listener errors stop event propagation\n }\n }\n\n // Fire through the event service for broader integration\n try {\n this.eventService.processListeners(`Sequencing.${eventType}`, args[0], ...args.slice(1));\n this.log(\"debug\", `Event service listeners for ${eventType} processed`);\n } catch (eventServiceError) {\n // Event service might not be properly initialized in test contexts\n // This is not a critical error for sequencing functionality\n this.log(\"warn\", `Event service failed for ${eventType}: ${eventServiceError}`);\n }\n\n // Fire to external global listeners if available\n try {\n if (typeof window !== \"undefined\" && (window as any).scormSequencingEvents) {\n const globalListeners = (window as any).scormSequencingEvents;\n if (globalListeners[eventType] && typeof globalListeners[eventType] === \"function\") {\n globalListeners[eventType](...args);\n this.log(\"debug\", `Global listener for ${eventType} executed`);\n }\n }\n } catch (globalError) {\n this.log(\"warn\", `Global listener for ${eventType} failed: ${globalError}`);\n }\n } catch (error) {\n this.log(\"error\", `Critical error firing event ${eventType}: ${error}`);\n }\n }\n\n /**\n * Fire a debug event with detailed information\n */\n private fireDebugEvent(event: string, data?: any): void {\n try {\n // Direct execution to avoid recursion through fireEvent\n const listener = this.eventListeners[\"onSequencingDebug\"];\n if (listener && typeof listener === \"function\") {\n listener(event, {\n timestamp: new Date().toISOString(),\n ...data,\n });\n }\n\n // Also fire through event service directly\n try {\n this.eventService.processListeners(\"Sequencing.onSequencingDebug\", event, {\n timestamp: new Date().toISOString(),\n ...data,\n });\n } catch (eventServiceError) {\n // Silent fail for event service debug events\n }\n } catch (error) {\n // Silent fail for debug events to avoid recursion\n console.debug(`Debug event failed: ${error}`);\n }\n }\n\n /**\n * Fire activity attempt start event\n */\n public fireActivityAttemptStart(activity: Activity): void {\n this.fireEvent(\"onActivityAttemptStart\", activity);\n this.fireDebugEvent(\"Activity attempt started\", {\n activityId: activity.id,\n title: activity.title,\n attemptCount: activity.attemptCount,\n });\n }\n\n /**\n * Fire activity attempt end event\n */\n public fireActivityAttemptEnd(activity: Activity): void {\n this.fireEvent(\"onActivityAttemptEnd\", activity);\n this.fireDebugEvent(\"Activity attempt ended\", {\n activityId: activity.id,\n title: activity.title,\n completionStatus: activity.completionStatus,\n successStatus: activity.successStatus,\n });\n }\n\n /**\n * Fire limit condition check event\n */\n public fireLimitConditionCheck(activity: Activity, result: boolean): void {\n this.fireEvent(\"onLimitConditionCheck\", activity, result);\n this.fireDebugEvent(\"Limit condition check\", {\n activityId: activity.id,\n result,\n attemptCount: activity.attemptCount,\n attemptLimit: activity.attemptLimit,\n });\n }\n\n /**\n * Fire navigation validity update event\n */\n public fireNavigationValidityUpdate(validity: any): void {\n this.fireEvent(\"onNavigationValidityUpdate\", validity);\n this.fireDebugEvent(\"Navigation validity updated\", { validity });\n }\n\n /**\n * Fire sequencing state change event\n */\n public fireSequencingStateChange(state: any): void {\n this.fireEvent(\"onSequencingStateChange\", state);\n this.fireDebugEvent(\"Sequencing state changed\", { stateKeys: Object.keys(state) });\n }\n\n /**\n * Handle events from the sequencing process\n */\n private handleSequencingProcessEvent(eventType: string, data?: any): void {\n try {\n switch (eventType) {\n case \"onActivityDelivery\":\n this.fireEvent(\"onActivityDelivery\", data);\n break;\n case \"onLimitConditionCheck\":\n this.fireLimitConditionCheck(data.activity, data.result);\n break;\n case \"onActivityAttemptStart\":\n this.fireActivityAttemptStart(data);\n break;\n case \"onActivityAttemptEnd\":\n this.fireActivityAttemptEnd(data);\n break;\n case \"onNavigationValidityUpdate\":\n this.fireNavigationValidityUpdate(data);\n break;\n case \"onSequencingSessionEnd\":\n this.fireEvent(\"onSequencingSessionEnd\", data);\n break;\n default:\n // Pass through unknown events as debug events\n this.fireDebugEvent(`Sequencing process event: ${eventType}`, data);\n }\n } catch (error) {\n this.log(\"error\", `Error handling sequencing process event ${eventType}: ${error}`);\n }\n }\n\n /**\n * Log message with appropriate level\n */\n private log(level: \"debug\" | \"info\" | \"warn\" | \"error\", message: string, data?: any): void {\n const logLevels = [\"debug\", \"info\", \"warn\", \"error\"];\n const configLevel = this.configuration.logLevel || \"info\";\n\n if (logLevels.indexOf(level) >= logLevels.indexOf(configLevel)) {\n switch (level) {\n case \"debug\":\n this.loggingService.debug(\n `[Sequencing] ${message}${data ? ` - ${JSON.stringify(data)}` : \"\"}`,\n );\n break;\n case \"info\":\n this.loggingService.info(\n `[Sequencing] ${message}${data ? ` - ${JSON.stringify(data)}` : \"\"}`,\n );\n break;\n case \"warn\":\n this.loggingService.warn(\n `[Sequencing] ${message}${data ? ` - ${JSON.stringify(data)}` : \"\"}`,\n );\n break;\n case \"error\":\n this.loggingService.error(\n `[Sequencing] ${message}${data ? ` - ${JSON.stringify(data)}` : \"\"}`,\n );\n break;\n }\n }\n }\n}\n","import { CommitObject, LogLevel } from \"../types/api_types\";\nimport { StringKeyMap, unflatten } from \"../utilities\";\nimport { LogLevelEnum } from \"../constants/enums\";\nimport { BaseCMI } from \"../cmi/common/base_cmi\";\nimport { ISerializationService } from \"../interfaces/services\";\n\n/**\n * Service for handling data serialization and deserialization in scorm-again\n */\nexport class SerializationService implements ISerializationService {\n /**\n * Loads CMI data from a flattened JSON object with special handling for arrays and ordering.\n *\n * This method implements a complex algorithm for loading flattened JSON data into the CMI\n * object structure. It handles several key challenges:\n *\n * 1. Ordering dependencies: Some CMI elements (like interactions and objectives) must be\n * loaded in a specific order to ensure proper initialization.\n *\n * 2. Array handling: Interactions and objectives are stored as arrays, and their properties\n * must be loaded in the correct order (e.g., 'id' and 'type' must be set before other properties).\n *\n * 3. Unflattening: The method converts flattened dot notation (e.g., \"cmi.objectives.0.id\")\n * back into nested objects before loading.\n *\n * The algorithm works by:\n * - Categorizing keys into interactions, objectives, and other properties\n * - Sorting interactions to prioritize 'id' and 'type' fields within each index\n * - Sorting objectives to prioritize 'id' fields within each index\n * - Processing each category in order: interactions, objectives, then other properties\n *\n * @param {StringKeyMap} json - The flattened JSON object with dot notation keys\n * @param {string} CMIElement - The CMI element to start from (usually empty or \"cmi\")\n * @param {Function} setCMIValue - Function to set CMI value\n * @param {Function} isNotInitialized - Function to check if API is not initialized\n *\n * @param setStartingData\n * @example\n * // Example of flattened JSON input:\n * // {\n * // \"cmi.objectives.0.id\": \"obj1\",\n * // \"cmi.objectives.0.score.raw\": \"80\",\n * // \"cmi.interactions.0.id\": \"int1\",\n * // \"cmi.interactions.0.type\": \"choice\",\n * // \"cmi.interactions.0.result\": \"correct\"\n * // }\n */\n loadFromFlattenedJSON(\n json: StringKeyMap,\n CMIElement: string = \"\",\n setCMIValue: (CMIElement: string, value: any) => void,\n isNotInitialized: () => boolean,\n setStartingData: (data: StringKeyMap) => void,\n ): void {\n if (!isNotInitialized()) {\n console.error(\"loadFromFlattenedJSON can only be called before the call to lmsInitialize.\");\n return;\n }\n\n const int_pattern = /^(cmi\\.interactions\\.)(\\d+)\\.(.*)$/;\n const obj_pattern = /^(cmi\\.objectives\\.)(\\d+)\\.(.*)$/;\n\n // Extract and categorize keys for better sorting\n const interactions: {\n key: string;\n value: any;\n index: number;\n field: string;\n }[] = [];\n const objectives: {\n key: string;\n value: any;\n index: number;\n field: string;\n }[] = [];\n const others: { key: string; value: any }[] = [];\n\n // Categorize keys\n for (const key in json) {\n if (Object.prototype.hasOwnProperty.call(json, key)) {\n const intMatch = key.match(int_pattern);\n if (intMatch) {\n interactions.push({\n key,\n value: json[key],\n index: Number(intMatch[2]),\n field: intMatch[3] || \"\",\n });\n continue;\n }\n\n const objMatch = key.match(obj_pattern);\n if (objMatch) {\n objectives.push({\n key,\n value: json[key],\n index: Number(objMatch[2]),\n field: objMatch[3] || \"\",\n });\n continue;\n }\n\n others.push({ key, value: json[key] });\n }\n }\n\n // Sort interactions: first by index, then prioritize 'id' and 'type' fields\n interactions.sort((a, b) => {\n if (a.index !== b.index) {\n return a.index - b.index;\n }\n\n // Same index, prioritize id and type\n if (a.field === \"id\") return -1;\n if (b.field === \"id\") return 1;\n if (a.field === \"type\") return -1;\n if (b.field === \"type\") return 1;\n\n return a.field.localeCompare(b.field);\n });\n\n // Sort objectives: first by index, then prioritize 'id' field\n objectives.sort((a, b) => {\n if (a.index !== b.index) {\n return a.index - b.index;\n }\n\n // Same index, prioritize id\n if (a.field === \"id\") return -1;\n if (b.field === \"id\") return 1;\n\n return a.field.localeCompare(b.field);\n });\n\n // Sort other keys alphabetically\n others.sort((a, b) => a.key.localeCompare(b.key));\n\n // Process all items in the correct order\n const processItems = (items: { key: string; value: any }[]) => {\n items.forEach((item) => {\n const obj: StringKeyMap = {};\n obj[item.key] = item.value;\n this.loadFromJSON(\n unflatten(obj) as StringKeyMap,\n CMIElement,\n setCMIValue,\n isNotInitialized,\n setStartingData,\n );\n });\n };\n\n // Process in order: interactions, objectives, others\n processItems(interactions);\n processItems(objectives);\n processItems(others);\n }\n\n /**\n * Loads CMI data from a nested JSON object with recursive traversal.\n *\n * This method implements a recursive algorithm for loading nested JSON data into the CMI\n * object structure. It handles several key aspects:\n *\n * 1. Recursive traversal: The method recursively traverses the nested JSON structure,\n * building CMI element paths as it goes (e.g., \"cmi.core.student_id\").\n *\n * 2. Type-specific handling: Different data types are handled differently:\n * - Arrays: Each array element is processed individually with its index in the path\n * - Objects: Recursively processed with updated path\n * - Primitives: Set directly using setCMIValue\n *\n * 3. Initialization check: Ensures the method is only called before API initialization\n *\n * 4. Starting data storage: Stores the original JSON data for potential future use\n *\n * The algorithm works by:\n * - First storing the complete JSON object via setStartingData\n * - Iterating through each property in the JSON object\n * - For each property, determining its type and handling it accordingly\n * - Building the CMI element path as it traverses the structure\n * - Setting values at the appropriate paths using setCMIValue\n *\n * @param {{[key: string]: any}} json - The nested JSON object to load\n * @param {string} CMIElement - The CMI element to start from (usually empty or \"cmi\")\n * @param {Function} setCMIValue - Function to set CMI value at a specific path\n * @param {Function} isNotInitialized - Function to check if API is not initialized\n * @param {Function} setStartingData - Function to store the original JSON data\n *\n * @example\n * // Example of nested JSON input:\n * // {\n * // \"core\": {\n * // \"student_id\": \"12345\",\n * // \"student_name\": \"John Doe\"\n * // },\n * // \"objectives\": [\n * // { \"id\": \"obj1\", \"score\": { \"raw\": 80 } },\n * // { \"id\": \"obj2\", \"score\": { \"raw\": 90 } }\n * // ]\n * // }\n */\n loadFromJSON(\n json: { [key: string]: any },\n CMIElement: string = \"\",\n setCMIValue: (CMIElement: string, value: any) => void,\n isNotInitialized: () => boolean,\n setStartingData: (data: StringKeyMap) => void,\n ): void {\n if (!isNotInitialized()) {\n console.error(\"loadFromJSON can only be called before the call to lmsInitialize.\");\n return;\n }\n\n CMIElement = CMIElement !== undefined ? CMIElement : \"cmi\";\n\n setStartingData(json);\n\n // could this be refactored down to flatten(json) then setCMIValue on each?\n for (const key in json) {\n if (Object.prototype.hasOwnProperty.call(json, key) && json[key]) {\n const currentCMIElement = (CMIElement ? CMIElement + \".\" : \"\") + key;\n const value = json[key];\n\n if (value.constructor === Array) {\n for (let i = 0; i < value.length; i++) {\n if (value[i]) {\n const item = value[i];\n const tempCMIElement = `${currentCMIElement}.${i}`;\n\n if (item.constructor === Object) {\n this.loadFromJSON(\n item,\n tempCMIElement,\n setCMIValue,\n isNotInitialized,\n setStartingData,\n );\n } else {\n setCMIValue(tempCMIElement, item);\n }\n }\n }\n } else if (value.constructor === Object) {\n this.loadFromJSON(\n value,\n currentCMIElement,\n setCMIValue,\n isNotInitialized,\n setStartingData,\n );\n } else {\n setCMIValue(currentCMIElement, value);\n }\n }\n }\n }\n\n /**\n * Render the CMI object to JSON for sending to an LMS.\n *\n * @param {BaseCMI|StringKeyMap} cmi - The CMI object\n * @param {boolean} sendFullCommit - Whether to send the full commit\n * @return {string}\n */\n renderCMIToJSONString(cmi: BaseCMI | StringKeyMap, sendFullCommit: boolean): string {\n // Do we want/need to return fields that have no set value?\n if (sendFullCommit) {\n return JSON.stringify({ cmi });\n }\n return JSON.stringify({ cmi }, (k, v) => (v === undefined ? null : v), 2);\n }\n\n /**\n * Returns a JS object representing the current cmi\n * @param {BaseCMI|StringKeyMap} cmi - The CMI object\n * @param {boolean} sendFullCommit - Whether to send the full commit\n * @return {object}\n */\n renderCMIToJSONObject(cmi: BaseCMI | StringKeyMap, sendFullCommit: boolean): StringKeyMap {\n // Revert to the original implementation to maintain compatibility with tests\n return JSON.parse(this.renderCMIToJSONString(cmi, sendFullCommit));\n }\n\n /**\n * Builds the commit object to be sent to the LMS\n * @param {boolean} terminateCommit - Whether this is a termination commit\n * @param {boolean} alwaysSendTotalTime - Whether to always send total time\n * @param {boolean|Function} renderCommonCommitFields - Whether to render common commit fields\n * @param {Function} renderCommitObject - Function to render commit object\n * @param {Function} renderCommitCMI - Function to render commit CMI\n * @param {LogLevel} apiLogLevel - The API log level\n * @return {CommitObject|StringKeyMap|Array<any>}\n */\n getCommitObject(\n terminateCommit: boolean,\n alwaysSendTotalTime: boolean,\n renderCommonCommitFields: boolean | ((commitObject: CommitObject) => boolean),\n renderCommitObject: (terminateCommit: boolean, includeTotalTime?: boolean) => CommitObject,\n renderCommitCMI: (\n terminateCommit: boolean,\n includeTotalTime?: boolean,\n ) => StringKeyMap | Array<any>,\n apiLogLevel: LogLevel,\n ): CommitObject | StringKeyMap | Array<any> {\n // Fix for issue: total time is being calculated incorrectly across multiple sessions\n // when selfReportSessionTime and alwaysSendTotalTime are enabled.\n //\n // Previously, we were using a single variable (shouldTerminateCommit) that combined\n // both concerns: whether this is a termination commit and whether to include total time.\n // This caused the total time to be calculated as if every commit was a terminate commit\n // when alwaysSendTotalTime was true, leading to incorrect time calculations.\n //\n // Now we pass the actual terminateCommit value and a separate parameter for whether\n // to include total time, allowing the rendering functions to handle these concerns separately.\n const includeTotalTime = alwaysSendTotalTime || terminateCommit;\n\n const commitObject = renderCommonCommitFields\n ? renderCommitObject(terminateCommit, includeTotalTime)\n : renderCommitCMI(terminateCommit, includeTotalTime);\n\n if ([LogLevelEnum.DEBUG, \"1\", 1, \"DEBUG\"].includes(apiLogLevel)) {\n console.debug(\"Commit (terminated: \" + (terminateCommit ? \"yes\" : \"no\") + \"): \");\n console.debug(commitObject);\n }\n return commitObject;\n }\n}\n","import { CommitObject, InternalSettings, ResultObject } from \"../types/api_types\";\nimport { global_constants } from \"../constants/api_constants\";\nimport { IHttpService } from \"../interfaces/services\";\nimport { ErrorCode } from \"../constants/error_codes\";\nimport { StringKeyMap } from \"../utilities\";\n\n/**\n * Service for handling synchronous HTTP communication with the LMS\n * Uses synchronous XMLHttpRequest for SCORM-compliant error reporting\n *\n * @spec SCORM 2004 RTE 4.1.7 - API calls must be synchronous\n * @spec SCORM 1.2 RTE 3.1.x - API calls must be synchronous\n */\nexport class SynchronousHttpService implements IHttpService {\n private settings: InternalSettings;\n private error_codes: ErrorCode;\n\n /**\n * Constructor for SynchronousHttpService\n * @param {InternalSettings} settings - The settings object\n * @param {ErrorCode} error_codes - The error codes object\n */\n constructor(settings: InternalSettings, error_codes: ErrorCode) {\n this.settings = settings;\n this.error_codes = error_codes;\n }\n\n /**\n * Sends synchronous HTTP requests to the LMS\n * @param {string} url - The URL endpoint to send the request to\n * @param {CommitObject|StringKeyMap|Array} params - The data to send to the LMS\n * @param {boolean} immediate - Whether this is a termination commit (use sendBeacon)\n * @param {Function} _apiLog - Function to log API messages (unused in synchronous mode - errors returned directly)\n * @param {Function} _processListeners - Function to trigger event listeners (unused in synchronous mode - no async events)\n * @return {ResultObject} - The result of the request (synchronous)\n *\n * @remarks\n * The apiLog and processListeners parameters are part of the IHttpService interface contract\n * but are not used by SynchronousHttpService because:\n * - Synchronous XHR blocks until complete, so errors are returned directly to the caller\n * - No async events need to be triggered (CommitSuccess/CommitError) since results are synchronous\n * - AsynchronousHttpService uses these parameters to handle background request results\n */\n processHttpRequest(\n url: string,\n params: CommitObject | StringKeyMap | Array<any>,\n immediate: boolean = false,\n _apiLog: (\n functionName: string,\n message: any,\n messageLevel: number,\n CMIElement?: string,\n ) => void,\n _processListeners: (functionName: string, CMIElement?: string, value?: any) => void,\n ): ResultObject {\n if (immediate) {\n // Termination: use sendBeacon (fire-and-forget, best effort)\n // @spec SCORM 2004 RTE 4.1.7 - API calls must be synchronous\n return this._handleImmediateRequest(url, params);\n }\n\n // Standard commit: synchronous XHR (blocks until complete)\n // @spec SCORM 2004 RTE 4.1.7 - API calls must be synchronous\n return this._performSyncXHR(url, params);\n }\n\n /**\n * Handles an immediate request using sendBeacon\n * @param {string} url - The URL to send the request to\n * @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request\n * @return {ResultObject} - The result based on beacon success\n * @private\n */\n private _handleImmediateRequest(\n url: string,\n params: CommitObject | StringKeyMap | Array<any>,\n ): ResultObject {\n const requestPayload = (this.settings.requestHandler(params) ?? params) as\n | CommitObject\n | StringKeyMap\n | Array<any>;\n const { body } = this._prepareRequestBody(requestPayload);\n\n // Use text/plain for sendBeacon to avoid CORS preflight issues.\n // application/json triggers CORS preflight which sendBeacon can't handle.\n // The server can still parse the body as JSON.\n // @spec W3C Beacon - sendBeacon for reliable unload data transmission\n const beaconSuccess = navigator.sendBeacon(\n url,\n new Blob([body], { type: \"text/plain;charset=UTF-8\" }),\n );\n\n return {\n result: beaconSuccess ? \"true\" : \"false\",\n errorCode: beaconSuccess ? 0 : this.error_codes.GENERAL_COMMIT_FAILURE || 391,\n };\n }\n\n /**\n * Performs a synchronous XMLHttpRequest\n * @param {string} url - The URL to send the request to\n * @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request\n * @return {ResultObject} - The result of the request\n * @private\n */\n private _performSyncXHR(\n url: string,\n params: CommitObject | StringKeyMap | Array<any>,\n ): ResultObject {\n const requestPayload = (this.settings.requestHandler(params) ?? params) as\n | CommitObject\n | StringKeyMap\n | Array<any>;\n const { body, contentType } = this._prepareRequestBody(requestPayload);\n\n const xhr = new XMLHttpRequest();\n xhr.open(\"POST\", url, false); // false = synchronous!\n\n // Set headers\n xhr.setRequestHeader(\"Content-Type\", contentType);\n Object.entries(this.settings.xhrHeaders).forEach(([key, value]) => {\n xhr.setRequestHeader(key, String(value));\n });\n\n // Set credentials\n if (this.settings.xhrWithCredentials) {\n xhr.withCredentials = true;\n }\n\n try {\n xhr.send(body);\n return this.settings.xhrResponseHandler(xhr);\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: this.error_codes.GENERAL_COMMIT_FAILURE || 391,\n errorMessage: message,\n };\n }\n }\n\n /**\n * Prepares the request body and content type based on params type\n * @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request\n * @return {Object} - Object containing body and contentType\n * @private\n */\n private _prepareRequestBody(params: CommitObject | StringKeyMap | Array<any>): {\n body: string;\n contentType: string;\n } {\n const body = params instanceof Array ? params.join(\"&\") : JSON.stringify(params);\n const contentType =\n params instanceof Array\n ? \"application/x-www-form-urlencoded\"\n : this.settings.commitRequestDataType;\n\n return { body, contentType };\n }\n\n /**\n * Updates the service settings\n * @param {InternalSettings} settings - The new settings\n */\n updateSettings(settings: InternalSettings): void {\n this.settings = settings;\n }\n}\n","import { checkValidFormat, checkValidRange } from \"../common/validation\";\nimport { Scorm12ValidationError } from \"../../exceptions/scorm12_exceptions\";\nimport { scorm12_errors } from \"../../constants\";\n\n/**\n * Helper method, no reason to have to pass the same error codes every time\n *\n * @spec RTE - Validates CMI element format per SCORM 1.2 data model requirements\n * @param {string} CMIElement\n * @param {string} value\n * @param {string} regexPattern\n * @param {boolean} allowEmptyString\n * @return {boolean}\n */\nexport function check12ValidFormat(\n CMIElement: string,\n value: string,\n regexPattern: string | RegExp, // We accept either a string or a RegExp object to allow the usage of flags.\n allowEmptyString?: boolean,\n): boolean {\n return checkValidFormat(\n CMIElement,\n value,\n regexPattern,\n scorm12_errors.TYPE_MISMATCH as number,\n Scorm12ValidationError,\n allowEmptyString,\n );\n}\n\n/**\n * Helper method, no reason to have to pass the same error codes every time\n *\n * @spec RTE - Validates CMI element range per SCORM 1.2 data model requirements\n * @param {string} CMIElement\n * @param {string} value\n * @param {string} rangePattern\n * @param {boolean} allowEmptyString\n * @return {boolean}\n */\nexport function check12ValidRange(\n CMIElement: string,\n value: any,\n rangePattern: string,\n allowEmptyString?: boolean,\n): boolean {\n if (value === \"\") {\n if (!allowEmptyString) {\n throw new Scorm12ValidationError(CMIElement, scorm12_errors.VALUE_OUT_OF_RANGE as number);\n }\n return true; // VAL-01: Early return for empty string when allowEmptyString is true\n }\n\n return checkValidRange(\n CMIElement,\n value,\n rangePattern,\n scorm12_errors.VALUE_OUT_OF_RANGE as number,\n Scorm12ValidationError,\n );\n}\n","import { BaseScormValidationError } from \"../exceptions\";\nimport { checkValidFormat, checkValidRange } from \"../cmi/common/validation\";\nimport { check12ValidFormat, check12ValidRange } from \"../cmi/scorm12/validation\";\nimport { scorm12_regex } from \"../constants/regex\";\nimport { scorm12_errors } from \"../constants/error_codes\";\nimport { Scorm12ValidationError } from \"../exceptions/scorm12_exceptions\";\n\n/**\n * Service for validating CMI data model properties\n */\nexport class ValidationService {\n /**\n * Validates a score property (raw, min, max)\n *\n * @param {string} CMIElement\n * @param {string} value - The value to validate\n * @param {string} decimalRegex - The regex pattern for decimal validation\n * @param {string | false} scoreRange - The range pattern for score validation, or false if no range validation is needed\n * @param {number} invalidTypeCode - The error code for invalid type\n * @param {number} invalidRangeCode - The error code for invalid range\n * @param {typeof BaseScormValidationError} errorClass - The error class to use for validation errors\n * @return {boolean} - True if validation passes, throws an error otherwise\n */\n validateScore(\n CMIElement: string,\n value: string,\n decimalRegex: string,\n scoreRange: string | false,\n invalidTypeCode: number,\n invalidRangeCode: number,\n errorClass: typeof BaseScormValidationError,\n ): boolean {\n return (\n checkValidFormat(CMIElement, value, decimalRegex, invalidTypeCode, errorClass) &&\n (!scoreRange || checkValidRange(CMIElement, value, scoreRange, invalidRangeCode, errorClass))\n );\n }\n\n /**\n * Validates a SCORM 1.2 audio property\n *\n * @spec SCORM 1.2 RTE 3.4.2.3.1 - Audio preference validation\n * @param {string} CMIElement\n * @param {string} value - The value to validate\n * @return {boolean} - True if validation passes, throws an error otherwise\n */\n validateScorm12Audio(CMIElement: string, value: string): boolean {\n return (\n check12ValidFormat(CMIElement, value, scorm12_regex.CMISInteger) &&\n check12ValidRange(CMIElement, value, scorm12_regex.audio_range)\n );\n }\n\n /**\n * Validates a SCORM 1.2 language property\n *\n * @spec SCORM 1.2 RTE 3.4.2.3.2 - Language preference validation\n * @param {string} CMIElement\n * @param {string} value - The value to validate\n * @return {boolean} - True if validation passes, throws an error otherwise\n */\n validateScorm12Language(CMIElement: string, value: string): boolean {\n return check12ValidFormat(CMIElement, value, scorm12_regex.CMIString256);\n }\n\n /**\n * Validates a SCORM 1.2 speed property\n *\n * @spec SCORM 1.2 RTE 3.4.2.3.3 - Speed preference validation\n * @param {string} CMIElement\n * @param {string} value - The value to validate\n * @return {boolean} - True if validation passes, throws an error otherwise\n */\n validateScorm12Speed(CMIElement: string, value: string): boolean {\n return (\n check12ValidFormat(CMIElement, value, scorm12_regex.CMISInteger) &&\n check12ValidRange(CMIElement, value, scorm12_regex.speed_range)\n );\n }\n\n /**\n * Validates a SCORM 1.2 text property\n *\n * @spec SCORM 1.2 RTE 3.4.2.3.4 - Text preference validation\n * @param {string} CMIElement\n * @param {string} value - The value to validate\n * @return {boolean} - True if validation passes, throws an error otherwise\n */\n validateScorm12Text(CMIElement: string, value: string): boolean {\n return (\n check12ValidFormat(CMIElement, value, scorm12_regex.CMISInteger) &&\n check12ValidRange(CMIElement, value, scorm12_regex.text_range)\n );\n }\n\n /**\n * Validates if a property is read-only\n *\n * @param {string} CMIElement\n * @param {boolean} initialized - Whether the object is initialized\n * @throws {BaseScormValidationError} - Throws an error if the object is initialized\n */\n validateReadOnly(CMIElement: string, initialized: boolean): void {\n if (initialized) {\n throw new Scorm12ValidationError(CMIElement, scorm12_errors.READ_ONLY_ELEMENT as number);\n }\n }\n}\n\n// Export a singleton instance of the ValidationService\nexport const validationService = new ValidationService();\n","import { flatten, formatMessage, StringKeyMap, stringMatches } from \"./utilities\";\nimport { BaseCMI, BaseRootCMI } from \"./cmi/common/base_cmi\";\nimport { CMIArray } from \"./cmi/common/array\";\nimport { ValidationError } from \"./exceptions\";\nimport {\n DefaultSettings,\n defaultLogHandler,\n ErrorCode,\n global_constants,\n LogLevelEnum,\n} from \"./constants\";\nimport {\n CommitObject,\n InternalSettings,\n LogLevel,\n ResultObject,\n ScheduledCommit,\n Settings,\n} from \"./types\";\nimport {\n IBaseAPI,\n ICMIDataService,\n IErrorHandlingService,\n IEventService,\n IHttpService,\n ILoggingService,\n IOfflineStorageService,\n ISerializationService,\n ScormEventCallback,\n} from \"./interfaces\";\nimport {\n AsynchronousHttpService,\n CMIValueAccessService,\n CMIValueAccessContext,\n createErrorHandlingService,\n EventService,\n getLoggingService,\n OfflineStorageService,\n SerializationService,\n SynchronousHttpService,\n} from \"./services\";\n\n/**\n * Base API class for SCORM 1.2 and SCORM 2004. Should be considered\n * abstract, and never initialized on its own.\n */\nexport default abstract class BaseAPI implements IBaseAPI {\n private _timeout?: ScheduledCommit | undefined;\n protected readonly _error_codes: ErrorCode;\n private _settings: InternalSettings = DefaultSettings;\n private readonly _httpService: IHttpService;\n private _eventService: IEventService;\n private _serializationService: ISerializationService;\n private readonly _errorHandlingService: IErrorHandlingService;\n private readonly _loggingService: ILoggingService;\n private readonly _offlineStorageService?: IOfflineStorageService;\n private readonly _cmiValueAccessService: CMIValueAccessService;\n private _courseId: string = \"\";\n\n /**\n * Constructor for Base API class. Sets some shared API fields, as well as\n * sets up options for the API.\n * @param {ErrorCode} error_codes - The error codes object\n * @param {Settings} settings - Optional settings for the API\n * @param {IHttpService} httpService - Optional HTTP service instance\n * @param {IEventService} eventService - Optional Event service instance\n * @param {ISerializationService} serializationService - Optional Serialization service instance\n * @param {ICMIDataService} cmiDataService - Optional CMI Data service instance\n * @param {IErrorHandlingService} errorHandlingService - Optional Error Handling service instance\n * @param {ILoggingService} loggingService - Optional Logging service instance\n * @param {IOfflineStorageService} offlineStorageService - Optional Offline Storage service instance\n */\n protected constructor(\n error_codes: ErrorCode,\n settings?: Settings,\n httpService?: IHttpService,\n eventService?: IEventService,\n serializationService?: ISerializationService,\n cmiDataService?: ICMIDataService,\n errorHandlingService?: IErrorHandlingService,\n loggingService?: ILoggingService,\n offlineStorageService?: IOfflineStorageService,\n ) {\n if (new.target === BaseAPI) {\n throw new TypeError(\"Cannot construct BaseAPI instances directly\");\n }\n this.currentState = global_constants.STATE_NOT_INITIALIZED;\n\n this._error_codes = error_codes;\n\n if (settings) {\n this.settings = {\n ...DefaultSettings,\n ...settings,\n } as InternalSettings;\n }\n\n // BACKWARDS COMPATIBILITY: Handle deprecated asyncCommit setting\n if (\n settings?.asyncCommit !== undefined &&\n settings.useAsynchronousCommits === undefined &&\n settings.throttleCommits === undefined\n ) {\n console.warn(\n \"DEPRECATED: 'asyncCommit' setting is deprecated and will be removed in a future version. \" +\n \"Use 'useAsynchronousCommits: true' and 'throttleCommits: true' instead.\",\n );\n if (settings.asyncCommit) {\n this.settings.useAsynchronousCommits = true;\n this.settings.throttleCommits = true;\n }\n }\n\n // VALIDATION: Enforce throttleCommits incompatibility with sync commits\n if (!this.settings.useAsynchronousCommits && this.settings.throttleCommits) {\n console.warn(\n \"throttleCommits cannot be used with synchronous commits. Setting throttleCommits to false.\",\n );\n this.settings.throttleCommits = false;\n }\n\n // Initialize and configure LoggingService\n this._loggingService = loggingService || getLoggingService();\n this._loggingService.setLogLevel(this.settings.logLevel);\n\n // If settings include a custom onLogMessage function, use it as the log handler\n if (this.settings.onLogMessage) {\n this._loggingService.setLogHandler(this.settings.onLogMessage);\n } else {\n this._loggingService.setLogHandler(defaultLogHandler);\n }\n\n // HTTP SERVICE SELECTION\n if (httpService) {\n // Constructor injection (for tests)\n this._httpService = httpService;\n } else if (this.settings.httpService) {\n // Settings injection (advanced users)\n this._httpService = this.settings.httpService;\n } else {\n // Auto-select based on useAsynchronousCommits\n if (this.settings.useAsynchronousCommits) {\n console.warn(\n \"WARNING: useAsynchronousCommits=true is not SCORM compliant. \" +\n \"Commit failures will not be reported to the SCO, which may cause data loss. \" +\n \"This setting should only be used for specific legacy compatibility cases.\",\n );\n this._httpService = new AsynchronousHttpService(this.settings, this._error_codes);\n } else {\n this._httpService = new SynchronousHttpService(this.settings, this._error_codes);\n }\n }\n\n // Initialize Event service\n this._eventService =\n eventService ||\n new EventService((functionName, message, level, element) =>\n this.apiLog(functionName, message, level, element),\n );\n\n // Initialize Serialization service\n this._serializationService = serializationService || new SerializationService();\n\n // Initialize Error Handling service\n this._errorHandlingService =\n errorHandlingService ||\n createErrorHandlingService(\n this._error_codes,\n (functionName, message, level, element) =>\n this.apiLog(functionName, message, level || LogLevelEnum.ERROR, element),\n (errorNumber, detail) => this.getLmsErrorMessageDetails(errorNumber, detail),\n );\n\n // Initialize Offline Storage service if enabled\n if (this.settings.enableOfflineSupport) {\n this._offlineStorageService =\n offlineStorageService ||\n new OfflineStorageService(\n this.settings,\n this._error_codes,\n (functionName, message, level, element) =>\n this.apiLog(functionName, message, level, element),\n );\n\n if (this.settings.courseId) {\n this._courseId = this.settings.courseId;\n }\n\n // Set up offline sync on BeforeTerminate event\n if (this.settings.syncOnTerminate) {\n this._eventService.on(\"BeforeTerminate\", () => {\n if (this._offlineStorageService?.isDeviceOnline() && this._courseId) {\n this._offlineStorageService\n .hasPendingOfflineData(this._courseId)\n .then((hasPendingData) => {\n if (hasPendingData) {\n this.apiLog(\n \"BeforeTerminate\",\n \"Syncing pending offline data before termination\",\n LogLevelEnum.INFO,\n );\n return this._offlineStorageService?.syncOfflineData();\n }\n })\n .then((syncSuccess) => {\n if (syncSuccess) {\n this.processListeners(\"OfflineDataSynced\");\n } else if (syncSuccess === false) {\n this.processListeners(\"OfflineDataSyncFailed\");\n }\n })\n .catch((error) => {\n this.apiLog(\n \"BeforeTerminate\",\n `Error syncing offline data: ${error}`,\n LogLevelEnum.ERROR,\n );\n this.processListeners(\"OfflineDataSyncFailed\");\n });\n }\n });\n }\n\n // Check for offline data to restore on initialization\n if (this._offlineStorageService && this._courseId) {\n this._offlineStorageService\n .getOfflineData(this._courseId)\n .then((offlineData) => {\n if (offlineData) {\n this.apiLog(\"constructor\", \"Found offline data to restore\", LogLevelEnum.INFO);\n // Restore data from offline storage\n this.loadFromJSON(offlineData.runtimeData);\n }\n })\n .catch((error) => {\n this.apiLog(\n \"constructor\",\n `Error retrieving offline data: ${error}`,\n LogLevelEnum.ERROR,\n );\n });\n }\n }\n\n // Initialize CMI Value Access service\n const cmiValueAccessContext: CMIValueAccessContext = {\n errorCodes: this._error_codes,\n getLastErrorCode: () => this.lastErrorCode,\n setLastErrorCode: (errorCode: string) => {\n this.lastErrorCode = errorCode;\n },\n throwSCORMError: (element: string, errorCode: number, message?: string) =>\n this.throwSCORMError(element, errorCode, message),\n isInitialized: () => this.isInitialized(),\n validateCorrectResponse: (CMIElement: string, value: string) =>\n this.validateCorrectResponse(CMIElement, value),\n checkForDuplicateId: (CMIElement: string, value: string) =>\n this._checkForDuplicateId(CMIElement, value),\n getChildElement: (CMIElement: string, value: string, foundFirstIndex: boolean) =>\n this.getChildElement(CMIElement, value, foundFirstIndex),\n apiLog: (methodName: string, message: string, level: LogLevelEnum) =>\n this.apiLog(methodName, message, level),\n checkObjectHasProperty: (obj: StringKeyMap, attr: string) =>\n this._checkObjectHasProperty(obj, attr),\n getDataModel: () => this as unknown as StringKeyMap,\n };\n this._cmiValueAccessService = new CMIValueAccessService(cmiValueAccessContext);\n }\n\n public abstract cmi: BaseCMI;\n public startingData?: StringKeyMap;\n\n public currentState: number;\n\n /**\n * Get the last error code\n * @return {string}\n */\n get lastErrorCode(): string {\n return this._errorHandlingService?.lastErrorCode ?? \"0\";\n }\n\n /**\n * Set the last error code\n * @param {string} errorCode\n */\n set lastErrorCode(errorCode: string) {\n if (this._errorHandlingService) {\n this._errorHandlingService.lastErrorCode = errorCode;\n }\n }\n\n /**\n * Protected getter for eventService\n * @return {IEventService}\n */\n protected get eventService(): IEventService {\n return this._eventService;\n }\n\n /**\n * Protected getter for loggingService\n * @return {ILoggingService}\n */\n protected get loggingService(): ILoggingService {\n return this._loggingService;\n }\n\n /**\n * Reset the API to its initial state.\n * This method clears all current data and resets the API to an uninitialized state.\n * It can optionally accept new settings to configure the API after reset.\n *\n * @param {Settings} settings - Optional new settings to apply after reset\n */\n abstract reset(settings?: Settings): void;\n\n /**\n * Common reset method for all APIs. New settings are merged with the existing settings.\n * @param {Settings} settings\n * @protected\n */\n commonReset(settings?: Settings): void {\n this.apiLog(\"reset\", \"Called\", LogLevelEnum.INFO);\n\n this.settings = { ...this.settings, ...settings };\n\n this.clearScheduledCommit();\n this.currentState = global_constants.STATE_NOT_INITIALIZED;\n this.lastErrorCode = \"0\";\n this._eventService.reset();\n this.startingData = {};\n\n // Update offline storage service with new settings if it exists\n if (this._offlineStorageService) {\n this._offlineStorageService.updateSettings(this.settings);\n\n if (settings?.courseId) {\n this._courseId = settings.courseId;\n }\n }\n }\n\n /**\n * Initialize the API\n * @param {string} callbackName\n * @param {string} initializeMessage\n * @param {string} terminationMessage\n * @return {string}\n */\n initialize(\n callbackName: string,\n initializeMessage?: string,\n terminationMessage?: string,\n ): string {\n let returnValue = global_constants.SCORM_FALSE;\n\n if (this.isInitialized()) {\n this.throwSCORMError(\"api\", this._error_codes.INITIALIZED, initializeMessage);\n } else if (this.isTerminated()) {\n this.throwSCORMError(\"api\", this._error_codes.TERMINATED, terminationMessage);\n } else {\n if (this.settings.selfReportSessionTime) {\n (this.cmi as BaseRootCMI).setStartTime();\n }\n\n this.currentState = global_constants.STATE_INITIALIZED;\n this.lastErrorCode = \"0\";\n returnValue = global_constants.SCORM_TRUE;\n this.processListeners(callbackName);\n\n // If enabled, attempt to sync offline data on initialization\n if (\n this.settings.enableOfflineSupport &&\n this._offlineStorageService &&\n this._courseId &&\n this.settings.syncOnInitialize &&\n this._offlineStorageService.isDeviceOnline()\n ) {\n this._offlineStorageService.hasPendingOfflineData(this._courseId).then((hasPendingData) => {\n if (hasPendingData) {\n this.apiLog(\n callbackName,\n \"Syncing pending offline data on initialization\",\n LogLevelEnum.INFO,\n );\n this._offlineStorageService?.syncOfflineData().then((syncSuccess) => {\n if (syncSuccess) {\n this.apiLog(callbackName, \"Successfully synced offline data\", LogLevelEnum.INFO);\n this.processListeners(\"OfflineDataSynced\");\n }\n });\n }\n });\n }\n }\n\n this.apiLog(callbackName, \"returned: \" + returnValue, LogLevelEnum.INFO);\n this.clearSCORMError(returnValue);\n\n return returnValue;\n }\n\n /**\n * Initialize the LMS API - Begins a communication session\n *\n * SCORM 1.2 per RTE Section 3.4.3.1 (LMSInitialize):\n * - Parameter must be empty string (\"\")\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 101 if already initialized\n * - Sets error 101 if already terminated\n *\n * SCORM 2004 per RTE Section 3.1.2.1 (Initialize):\n * - Parameter must be empty string (\"\")\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 103 if already initialized\n * - Sets error 104 if already terminated\n *\n * @param {string} parameter - Must be an empty string per SCORM specification\n * @return {string} \"true\" if successful, \"false\" otherwise\n */\n abstract lmsInitialize(parameter?: string): string;\n\n /**\n * Finish the current LMS API session - Ends a communication session\n *\n * SCORM 1.2 per RTE Section 3.4.3.2 (LMSFinish):\n * - Parameter must be empty string (\"\")\n * - Returns \"true\" on success, \"false\" on failure\n * - Commits all data to persistent storage\n * - Sets error 101 if not initialized\n * - Sets error 101 if already terminated\n *\n * SCORM 2004 per RTE Section 3.1.2.2 (Terminate):\n * - Parameter must be empty string (\"\")\n * - Returns \"true\" on success, \"false\" on failure\n * - Commits all data to persistent storage\n * - Sets error 112 if not initialized\n * - Sets error 113 if already terminated\n *\n * @param {string} parameter - Must be an empty string per SCORM specification\n * @return {string} \"true\" if successful, \"false\" otherwise\n */\n abstract lmsFinish(parameter?: string): string;\n\n /**\n * Get the value of a CMI element from the LMS\n *\n * SCORM 1.2 per RTE Section 3.4.3.3 (LMSGetValue):\n * - Returns the value of the specified CMI data model element\n * - Returns empty string if element has no value\n * - Sets error 101 if not initialized\n * - Sets error 301 if invalid element specified\n * - Sets error 201 if element is write-only\n *\n * SCORM 2004 per RTE Section 3.1.2.3 (GetValue):\n * - Returns the value of the specified CMI data model element\n * - Returns empty string if element has no value\n * - Sets error 122 if not initialized\n * - Sets error 123 if terminated\n * - Sets error 401 if invalid element specified\n * - Sets error 405 if element is write-only\n *\n * @param {string} CMIElement - The CMI element to get the value of\n * @return {string} The value of the CMI element\n */\n abstract lmsGetValue(CMIElement: string): string;\n\n /**\n * Set the value of a CMI element in the LMS\n *\n * SCORM 1.2 per RTE Section 3.4.3.4 (LMSSetValue):\n * - Sets the value of the specified CMI data model element\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 101 if not initialized\n * - Sets error 301 if invalid element specified\n * - Sets error 351 if element exceeds maximum length\n * - Sets error 201 if element is read-only\n * - Sets error 405 if incorrect data type\n *\n * SCORM 2004 per RTE Section 3.1.2.4 (SetValue):\n * - Sets the value of the specified CMI data model element\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 132 if not initialized\n * - Sets error 133 if terminated\n * - Sets error 401 if invalid element specified\n * - Sets error 403 if element is read-only\n * - Sets error 406 if incorrect data type\n *\n * @param {string} CMIElement - The CMI element to set the value of\n * @param {any} value - The value to set\n * @return {string} \"true\" if successful, \"false\" otherwise\n */\n abstract lmsSetValue(CMIElement: string, value: any): string;\n\n /**\n * Commit the current data to the LMS - Persists data to storage\n *\n * SCORM 1.2 per RTE Section 3.4.4.1 (LMSCommit):\n * - Parameter must be empty string (\"\")\n * - Requests immediate persistence of all data since last commit\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 101 if not initialized\n * - Sets error 391 if commit failed\n *\n * SCORM 2004 per RTE Section 3.1.2.5 (Commit):\n * - Parameter must be empty string (\"\")\n * - Requests immediate persistence of all data since last commit\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 142 if not initialized\n * - Sets error 143 if terminated\n * - Sets error 391 if commit failed\n *\n * @param {string} parameter - Must be an empty string per SCORM specification\n * @return {string} \"true\" if successful, \"false\" otherwise\n */\n abstract lmsCommit(parameter?: string): string;\n\n /**\n * Get the last error code from the LMS\n *\n * SCORM 1.2 per RTE Section 3.4.4.2 (LMSGetLastError):\n * - Returns the error code from the last API call\n * - Returns \"0\" if no error occurred\n * - Can be called at any time (even before initialization)\n * - Does not change the current error state\n *\n * SCORM 2004 per RTE Section 3.1.2.6 (GetLastError):\n * - Returns the error code from the last API call\n * - Returns \"0\" if no error occurred\n * - Can be called at any time (even before initialization)\n * - Does not change the current error state\n *\n * @return {string} The last error code\n */\n abstract lmsGetLastError(): string;\n\n /**\n * Get the error string for a specific error code\n *\n * SCORM 1.2 per RTE Section 3.4.4.3 (LMSGetErrorString):\n * - Returns a short textual description for the specified error code\n * - Returns empty string if error code is not recognized\n * - Can be called at any time (even before initialization)\n * - Does not change the current error state\n *\n * SCORM 2004 per RTE Section 3.1.2.7 (GetErrorString):\n * - Returns a short textual description for the specified error code\n * - Returns empty string if error code is not recognized\n * - Can be called at any time (even before initialization)\n * - Does not change the current error state\n *\n * @param {string|number} CMIErrorCode - The error code to get the string for\n * @return {string} The error string\n */\n abstract lmsGetErrorString(CMIErrorCode: string | number): string;\n\n /**\n * Get diagnostic information for a specific error code\n *\n * SCORM 1.2 per RTE Section 3.4.4.4 (LMSGetDiagnostic):\n * - Returns detailed diagnostic information for the specified error code\n * - Implementation-specific; can include additional context or debugging info\n * - Returns empty string if no diagnostic information is available\n * - Can be called at any time (even before initialization)\n * - Does not change the current error state\n *\n * SCORM 2004 per RTE Section 3.1.2.8 (GetDiagnostic):\n * - Returns detailed diagnostic information for the specified error code\n * - Implementation-specific; can include additional context or debugging info\n * - Returns empty string if no diagnostic information is available\n * - Can be called at any time (even before initialization)\n * - Does not change the current error state\n *\n * @param {string|number} CMIErrorCode - The error code to get diagnostic information for\n * @return {string} The diagnostic information\n */\n abstract lmsGetDiagnostic(CMIErrorCode: string | number): string;\n\n /**\n * Abstract method for validating that a response is correct.\n * This method is used to validate the format and content of a response\n * before it is set in the CMI data model.\n *\n * @param {string} _CMIElement - The CMI element path to validate\n * @param {any} _value - The value to validate\n * @throws {Error} If the response format is invalid\n */\n abstract validateCorrectResponse(_CMIElement: string, _value: any): void;\n\n /**\n * Gets or builds a new child element to add to the array.\n * APIs that inherit BaseAPI should override this method.\n *\n * @param {string} _CMIElement - unused\n * @param {*} _value - unused\n * @param {boolean} _foundFirstIndex - unused\n * @return {BaseCMI|null}\n * @abstract\n */\n abstract getChildElement(\n _CMIElement: string,\n _value: any,\n _foundFirstIndex: boolean,\n ): BaseCMI | null;\n\n /**\n * Attempts to store the data to the LMS, logs data if no LMS configured\n * APIs that inherit BaseAPI should override this function\n *\n * @param {boolean} _calculateTotalTime\n * @return {ResultObject}\n * @abstract\n */\n abstract storeData(_calculateTotalTime: boolean): ResultObject;\n\n /**\n * Render the cmi object to the proper format for LMS commit\n *\n * @param {boolean} _terminateCommit - Whether this commit is part of the termination process\n * @param {boolean} [_includeTotalTime] - Whether to include total time in the commit data\n * @return {StringKeyMap|Array}\n * @abstract\n */\n abstract renderCommitCMI(\n _terminateCommit: boolean,\n _includeTotalTime?: boolean,\n ): StringKeyMap | Array<string>;\n\n /**\n * Render the commit object to the shortened format for LMS commit.\n * This method transforms the CMI data into a format suitable for sending to the LMS.\n * It is called during the commit process to prepare the data for transmission.\n *\n * @param {boolean} _terminateCommit - Whether this commit is part of the termination process\n * @param {boolean} [_includeTotalTime] - Whether to include total time in the commit data\n * @return {CommitObject} A formatted object containing the data to be sent to the LMS\n * @example\n * // Example of a commit object structure\n * {\n * method: \"POST\",\n * params: {\n * cmi: { ... },\n * finishState: \"COMPLETED\"\n * }\n * }\n */\n abstract renderCommitObject(_terminateCommit: boolean, _includeTotalTime?: boolean): CommitObject;\n\n /**\n * Logging for all SCORM actions\n *\n * @param {string} functionName\n * @param {string} logMessage\n * @param {number} messageLevel\n * @param {string} CMIElement\n */\n apiLog(functionName: string, logMessage: string, messageLevel: LogLevel, CMIElement?: string) {\n logMessage = formatMessage(functionName, logMessage, CMIElement);\n\n // Delegate to LoggingService which handles log level comparison properly\n this._loggingService.log(messageLevel, logMessage);\n }\n\n /**\n * Getter for _settings\n * @return {InternalSettings}\n */\n get settings(): InternalSettings {\n return this._settings;\n }\n\n /**\n * Setter for _settings\n * @param {Settings} settings\n */\n set settings(settings: Settings) {\n const previousSettings = this._settings;\n // Merge the incoming settings with the existing settings\n this._settings = { ...this._settings, ...settings } as InternalSettings;\n\n // Update HTTP service settings\n this._httpService?.updateSettings(this._settings);\n\n // The following properties are duplicated as class properties for easier access\n // and need to be manually updated to stay in sync with the settings object\n\n // Update log level if it changed\n if (settings.logLevel !== undefined && settings.logLevel !== previousSettings.logLevel) {\n this._loggingService?.setLogLevel(settings.logLevel);\n }\n\n // Update log handler if onLogMessage changed\n if (\n settings.onLogMessage !== undefined &&\n settings.onLogMessage !== previousSettings.onLogMessage\n ) {\n this._loggingService?.setLogHandler(settings.onLogMessage);\n }\n }\n\n /**\n * Terminates the current run of the API\n * @param {string} callbackName\n * @param {boolean} checkTerminated\n * @return {string}\n */\n terminate(callbackName: string, checkTerminated: boolean): string {\n // Per SCORM 2004 3rd Edition RTE Section 3.1.3.2:\n // Return \"false\" for all error conditions (112, 113, 111, 201)\n let returnValue = global_constants.SCORM_TRUE;\n let stateCheckPassed = false;\n\n // Check if not initialized first\n if (this.isNotInitialized()) {\n const errorCode = this._error_codes.TERMINATION_BEFORE_INIT ?? 0;\n this.throwSCORMError(\"api\", errorCode);\n // Per SCORM 2004 3rd Ed RTE 3.1.3.2: return \"false\" for error 112\n // SCORM 1.2 (error 101) returns \"true\" for error conditions\n if (errorCode === 112) returnValue = global_constants.SCORM_FALSE;\n } else if (checkTerminated && this.isTerminated()) {\n const errorCode = this._error_codes.MULTIPLE_TERMINATION ?? 0;\n this.throwSCORMError(\"api\", errorCode);\n // Per SCORM 2004 3rd Ed RTE 3.1.3.2: return \"false\" for error 113\n // SCORM 1.2 (error 101) returns \"true\" for error conditions\n if (errorCode === 113) returnValue = global_constants.SCORM_FALSE;\n } else {\n stateCheckPassed = true;\n\n // Fire BeforeTerminate event for offline sync\n this.processListeners(\"BeforeTerminate\");\n\n const result: ResultObject = this.storeData(true);\n if ((result.errorCode ?? 0) > 0) {\n // Log detailed error information before throwing SCORM error\n if (result.errorMessage) {\n this.apiLog(\n \"terminate\",\n `Terminate failed with error: ${result.errorMessage}`,\n LogLevelEnum.ERROR,\n );\n }\n if (result.errorDetails) {\n this.apiLog(\n \"terminate\",\n `Error details: ${JSON.stringify(result.errorDetails)}`,\n LogLevelEnum.DEBUG,\n );\n }\n // Per SCORM spec: on error 111, state remains \"Running\" and return \"false\"\n this.throwSCORMError(\"api\", result.errorCode ?? 0);\n returnValue = global_constants.SCORM_FALSE;\n } else {\n // Only transition to Terminated state after successful storeData\n // Per SCORM 2004 3rd Edition RTE Section 3.1.3.2\n this.currentState = global_constants.STATE_TERMINATED;\n // Only clear error if there was no error\n if (checkTerminated) this.lastErrorCode = \"0\";\n const resultValue = result?.result ?? global_constants.SCORM_TRUE;\n returnValue = typeof resultValue === \"boolean\" ? String(resultValue) : resultValue;\n }\n\n this.processListeners(callbackName);\n }\n\n this.apiLog(callbackName, \"returned: \" + returnValue, LogLevelEnum.INFO);\n\n // Only clear error if state check passed\n if (stateCheckPassed) {\n this.clearSCORMError(returnValue);\n }\n\n return returnValue;\n }\n\n /**\n * Get the value of the CMIElement.\n *\n * @param {string} callbackName\n * @param {boolean} checkTerminated\n * @param {string} CMIElement\n * @return {string}\n */\n getValue(callbackName: string, checkTerminated: boolean, CMIElement: string): string {\n let returnValue: string = \"\";\n\n if (\n this.checkState(\n checkTerminated,\n this._error_codes.RETRIEVE_BEFORE_INIT ?? 0,\n this._error_codes.RETRIEVE_AFTER_TERM ?? 0,\n )\n ) {\n // Only reset the error code if there's no error and checkTerminated is true\n // This is a no-op if lastErrorCode is already \"0\"\n try {\n returnValue = this.getCMIValue(CMIElement);\n } catch (e) {\n returnValue = this.handleValueAccessException(CMIElement, e, returnValue);\n }\n this.processListeners(callbackName, CMIElement);\n }\n\n this.apiLog(callbackName, \": returned: \" + returnValue, LogLevelEnum.INFO, CMIElement);\n\n if (returnValue === undefined) {\n return \"\";\n }\n\n // Only clear the error code if there's no error\n if (this.lastErrorCode === \"0\") {\n this.clearSCORMError(returnValue);\n }\n\n return returnValue;\n }\n\n /**\n * Sets the value of the CMIElement.\n *\n * @param {string} callbackName\n * @param {string} commitCallback\n * @param {boolean} checkTerminated\n * @param {string} CMIElement\n * @param {*} value\n * @return {string}\n */\n setValue(\n callbackName: string,\n commitCallback: string,\n checkTerminated: boolean,\n CMIElement: string,\n value: any,\n ): string {\n if (value !== undefined) {\n value = String(value);\n }\n let returnValue: string = global_constants.SCORM_FALSE;\n\n if (\n this.checkState(\n checkTerminated,\n this._error_codes.STORE_BEFORE_INIT ?? 0,\n this._error_codes.STORE_AFTER_TERM ?? 0,\n )\n ) {\n // Only reset the error code if there's no error and checkTerminated is true\n // This is a no-op if lastErrorCode is already \"0\"\n try {\n returnValue = this.setCMIValue(CMIElement, value);\n } catch (e) {\n returnValue = this.handleValueAccessException(CMIElement, e, returnValue);\n }\n this.processListeners(callbackName, CMIElement, value);\n }\n\n if (returnValue === undefined) {\n returnValue = global_constants.SCORM_FALSE;\n }\n\n // If we didn't have any errors while setting the data, go ahead and\n // schedule a commit, if autocommit is turned on\n if (String(this.lastErrorCode) === \"0\") {\n if (this.settings.autocommit) {\n this.scheduleCommit(this.settings.autocommitSeconds * 1000, commitCallback);\n }\n }\n\n this.apiLog(\n callbackName,\n \": \" + value + \": result: \" + returnValue,\n LogLevelEnum.INFO,\n CMIElement,\n );\n\n // Only clear the error code if there's no error\n if (this.lastErrorCode === \"0\") {\n this.clearSCORMError(returnValue);\n }\n\n return returnValue;\n }\n\n /**\n * Orders LMS to store all content parameters\n * @param {string} callbackName\n * @param {boolean} checkTerminated\n * @return {string}\n */\n commit(callbackName: string, checkTerminated: boolean = false): string {\n this.clearScheduledCommit();\n\n // Per SCORM 2004 3rd Edition RTE Section 3.1.4.3:\n // Return \"false\" for all error conditions (142, 143, 391, 201)\n let returnValue = global_constants.SCORM_TRUE;\n\n // Check if not initialized first\n if (this.isNotInitialized()) {\n const errorCode = this._error_codes.COMMIT_BEFORE_INIT ?? 0;\n this.throwSCORMError(\"api\", errorCode);\n // Per SCORM 2004 3rd Ed RTE 3.1.4.3: return \"false\" for error 142\n // SCORM 1.2 (error 301) returns \"true\" for error conditions\n if (errorCode === 142) returnValue = global_constants.SCORM_FALSE;\n } else if (checkTerminated && this.isTerminated()) {\n const errorCode = this._error_codes.COMMIT_AFTER_TERM ?? 0;\n this.throwSCORMError(\"api\", errorCode);\n // Per SCORM 2004 3rd Ed RTE 3.1.4.3: return \"false\" for error 143\n if (errorCode === 143) returnValue = global_constants.SCORM_FALSE;\n } else {\n const result = this.storeData(false);\n if ((result.errorCode ?? 0) > 0) {\n // Log detailed error information before throwing SCORM error\n if (result.errorMessage) {\n this.apiLog(\n \"commit\",\n `Commit failed with error: ${result.errorMessage}`,\n LogLevelEnum.ERROR,\n );\n }\n if (result.errorDetails) {\n this.apiLog(\n \"commit\",\n `Error details: ${JSON.stringify(result.errorDetails)}`,\n LogLevelEnum.DEBUG,\n );\n }\n this.throwSCORMError(\"api\", result.errorCode);\n }\n const resultValue = result?.result ?? global_constants.SCORM_FALSE;\n returnValue = typeof resultValue === \"boolean\" ? String(resultValue) : resultValue;\n\n this.apiLog(callbackName, \" Result: \" + returnValue, LogLevelEnum.DEBUG, \"HttpRequest\");\n\n if (checkTerminated) this.lastErrorCode = \"0\";\n\n this.processListeners(callbackName);\n\n // Fire async offline sync in background if needed\n if (\n this.settings.enableOfflineSupport &&\n this._offlineStorageService &&\n this._offlineStorageService.isDeviceOnline() &&\n this._courseId\n ) {\n this._offlineStorageService.hasPendingOfflineData(this._courseId).then((hasPendingData) => {\n if (hasPendingData) {\n this.apiLog(callbackName, \"Syncing pending offline data\", LogLevelEnum.INFO);\n this._offlineStorageService?.syncOfflineData().then((syncSuccess) => {\n if (syncSuccess) {\n this.apiLog(callbackName, \"Successfully synced offline data\", LogLevelEnum.INFO);\n this.processListeners(\"OfflineDataSynced\");\n } else {\n this.apiLog(callbackName, \"Failed to sync some offline data\", LogLevelEnum.WARN);\n }\n });\n }\n });\n }\n }\n\n this.apiLog(callbackName, \"returned: \" + returnValue, LogLevelEnum.INFO);\n\n // Only clear error if we actually performed a commit (not a state error)\n if (!this.isNotInitialized() && !(checkTerminated && this.isTerminated())) {\n this.clearSCORMError(returnValue);\n }\n\n return returnValue;\n }\n\n /**\n * Returns last error code\n * @param {string} callbackName\n * @return {string}\n */\n getLastError(callbackName: string): string {\n const returnValue = String(this.lastErrorCode);\n\n this.processListeners(callbackName);\n\n this.apiLog(callbackName, \"returned: \" + returnValue, LogLevelEnum.INFO);\n\n return returnValue;\n }\n\n /**\n * Returns the errorNumber error description\n *\n * @param {string} callbackName\n * @param {(string|number)} CMIErrorCode\n * @return {string} - Error description string (max 255 chars per spec)\n */\n getErrorString(callbackName: string, CMIErrorCode: string | number): string {\n let returnValue = \"\";\n\n if (CMIErrorCode !== null && CMIErrorCode !== \"\") {\n returnValue = this.getLmsErrorMessageDetails(CMIErrorCode);\n this.processListeners(callbackName);\n }\n\n // Per SCORM spec: GetErrorString return value max length is 255 characters\n if (returnValue.length > 255) {\n returnValue = returnValue.substring(0, 255);\n }\n\n this.apiLog(callbackName, \"returned: \" + returnValue, LogLevelEnum.INFO);\n\n return returnValue;\n }\n\n /**\n * Returns a comprehensive description of the errorNumber error.\n *\n * @param {string} callbackName\n * @param {(string|number)} CMIErrorCode\n * @return {string}\n */\n getDiagnostic(callbackName: string, CMIErrorCode: string | number): string {\n let returnValue = \"\";\n\n // Per SCORM spec: empty string requests diagnostic for the last error\n const errorCode = CMIErrorCode === \"\" ? String(this.lastErrorCode) : CMIErrorCode;\n\n if (errorCode !== null && errorCode !== \"\") {\n // Check for custom diagnostic message first (set by throwSCORMError)\n // Only use custom diagnostic if requesting info about the last error\n const customDiagnostic = this._errorHandlingService.lastDiagnostic;\n if (customDiagnostic && String(errorCode) === String(this.lastErrorCode)) {\n returnValue = customDiagnostic;\n } else {\n returnValue = this.getLmsErrorMessageDetails(errorCode, true);\n }\n this.processListeners(callbackName);\n }\n\n // Per SCORM spec: GetDiagnostic return value max length is 255 characters\n if (returnValue.length > 255) {\n returnValue = returnValue.substring(0, 255);\n }\n\n this.apiLog(callbackName, \"returned: \" + returnValue, LogLevelEnum.INFO);\n\n return returnValue;\n }\n\n /**\n * Checks the LMS state and ensures it has been initialized.\n *\n * @param {boolean} checkTerminated\n * @param {number} beforeInitError\n * @param {number} afterTermError\n * @return {boolean}\n */\n checkState(checkTerminated: boolean, beforeInitError: number, afterTermError: number): boolean {\n if (this.isNotInitialized()) {\n this.throwSCORMError(\"api\", beforeInitError);\n return false;\n } else if (checkTerminated && this.isTerminated()) {\n this.throwSCORMError(\"api\", afterTermError);\n return false;\n }\n\n return true;\n }\n\n /**\n * Checks if setting an ID would create a duplicate in the objectives or interactions array.\n * Per SCORM 2004 RTE Section 4.1.5/4.1.6: IDs must be unique within their respective arrays.\n *\n * @param {string} CMIElement - The element path (e.g., \"cmi.objectives.0.id\")\n * @param {string} value - The ID value being set\n * @return {boolean} - True if a duplicate would be created, false otherwise\n * @protected\n */\n protected _checkForDuplicateId(CMIElement: string, value: string): boolean {\n /**\n * Helper to safely get a CMIArray property from an object\n * @param obj - Object to check\n * @param prop - Property name to get\n * @returns The CMIArray if it exists, undefined otherwise\n */\n const getCMIArrayProperty = (obj: unknown, prop: string): CMIArray | undefined => {\n if (obj && typeof obj === \"object\" && prop in obj) {\n const value = (obj as Record<string, unknown>)[prop];\n return value instanceof CMIArray ? value : undefined;\n }\n return undefined;\n };\n\n /**\n * Helper to check for duplicate ID in a CMIArray\n * @param array - The CMIArray to check\n * @param currentIndex - Index to skip (the current item being set)\n * @param idValue - The ID value to check for duplicates\n * @returns True if a duplicate is found, false otherwise\n */\n const hasDuplicateId = (array: CMIArray, currentIndex: number, idValue: string): boolean => {\n for (let i = 0; i < array.childArray.length; i++) {\n if (i !== currentIndex) {\n const child = array.childArray[i];\n if (child && typeof child === \"object\" && \"id\" in child && child.id === idValue) {\n return true;\n }\n }\n }\n return false;\n };\n\n // Match objectives: cmi.objectives.n.id\n const objectivesMatch = CMIElement.match(/^cmi\\.objectives\\.(\\d+)\\.id$/);\n if (objectivesMatch && objectivesMatch[1]) {\n const currentIndex = parseInt(objectivesMatch[1], 10);\n const objectives = getCMIArrayProperty(this.cmi, \"objectives\");\n if (objectives) {\n return hasDuplicateId(objectives, currentIndex, value);\n }\n return false;\n }\n\n // Match interactions: cmi.interactions.n.id\n const interactionsMatch = CMIElement.match(/^cmi\\.interactions\\.(\\d+)\\.id$/);\n if (interactionsMatch && interactionsMatch[1]) {\n const currentIndex = parseInt(interactionsMatch[1], 10);\n const interactions = getCMIArrayProperty(this.cmi, \"interactions\");\n if (interactions) {\n return hasDuplicateId(interactions, currentIndex, value);\n }\n return false;\n }\n\n // Match interaction objectives: cmi.interactions.n.objectives.m.id\n const interactionObjectivesMatch = CMIElement.match(\n /^cmi\\.interactions\\.(\\d+)\\.objectives\\.(\\d+)\\.id$/,\n );\n if (\n interactionObjectivesMatch &&\n interactionObjectivesMatch[1] &&\n interactionObjectivesMatch[2]\n ) {\n const interactionIndex = parseInt(interactionObjectivesMatch[1], 10);\n const currentObjIndex = parseInt(interactionObjectivesMatch[2], 10);\n const interactions = getCMIArrayProperty(this.cmi, \"interactions\");\n if (interactions) {\n const interaction = interactions.childArray[interactionIndex];\n if (interaction) {\n const objectives = getCMIArrayProperty(interaction, \"objectives\");\n if (objectives) {\n return hasDuplicateId(objectives, currentObjIndex, value);\n }\n }\n }\n return false;\n }\n\n return false;\n }\n\n /**\n * Returns the message that corresponds to errorNumber\n * APIs that inherit BaseAPI should override this function\n *\n * @param {(string|number)} _errorNumber\n * @param {boolean} _detail\n * @return {string}\n * @abstract\n */\n getLmsErrorMessageDetails(_errorNumber: string | number, _detail: boolean = false): string {\n throw new Error(\"The getLmsErrorMessageDetails method has not been implemented\");\n }\n\n /**\n * Gets the value for the specific element.\n * APIs that inherit BaseAPI should override this function\n *\n * @param {string} _CMIElement\n * @return {string}\n * @abstract\n */\n getCMIValue(_CMIElement: string): string {\n throw new Error(\"The getCMIValue method has not been implemented\");\n }\n\n /**\n * Sets the value for the specific element.\n * APIs that inherit BaseAPI should override this function\n *\n * @param {string} _CMIElement\n * @param {any} _value\n * @return {string}\n * @abstract\n */\n setCMIValue(_CMIElement: string, _value: any): string {\n throw new Error(\"The setCMIValue method has not been implemented\");\n }\n\n /**\n * Shared API method to set a value for a given element.\n * Delegates to CMIValueAccessService for the complex traversal logic.\n *\n * @param {string} methodName\n * @param {boolean} scorm2004\n * @param {string} CMIElement\n * @param {any} value\n * @return {string}\n */\n _commonSetCMIValue(\n methodName: string,\n scorm2004: boolean,\n CMIElement: string,\n value: any,\n ): string {\n return this._cmiValueAccessService.setCMIValue(methodName, scorm2004, CMIElement, value);\n }\n\n /**\n * Gets a value from the CMI Object.\n * Delegates to CMIValueAccessService for the complex traversal logic.\n *\n * @param {string} methodName\n * @param {boolean} scorm2004\n * @param {string} CMIElement\n * @return {any}\n */\n _commonGetCMIValue(methodName: string, scorm2004: boolean, CMIElement: string): any {\n return this._cmiValueAccessService.getCMIValue(methodName, scorm2004, CMIElement);\n }\n\n /**\n * Returns true if the API's current state is STATE_INITIALIZED\n *\n * @return {boolean}\n */\n isInitialized(): boolean {\n return this.currentState === global_constants.STATE_INITIALIZED;\n }\n\n /**\n * Returns true if the API's current state is STATE_NOT_INITIALIZED\n *\n * @return {boolean}\n */\n isNotInitialized(): boolean {\n return this.currentState === global_constants.STATE_NOT_INITIALIZED;\n }\n\n /**\n * Returns true if the API's current state is STATE_TERMINATED\n *\n * @return {boolean}\n */\n isTerminated(): boolean {\n return this.currentState === global_constants.STATE_TERMINATED;\n }\n\n /**\n * Provides a mechanism for attaching to a specific SCORM event.\n * This method allows you to register a callback function that will be executed\n * when the specified event occurs.\n *\n * @param {string} listenerName - The name of the event to listen for (e.g., \"Initialize\", \"Terminate\", \"GetValue\", \"SetValue\", \"Commit\")\n * @param {function} callback - The function to execute when the event occurs. The callback will receive relevant event data.\n * @example\n * // Listen for Initialize events\n * api.on(\"Initialize\", function() {\n * console.log(\"API has been initialized\");\n * });\n *\n * // Listen for SetValue events\n * api.on(\"SetValue\", function(element, value) {\n * console.log(\"Setting \" + element + \" to \" + value);\n * });\n */\n on(listenerName: string, callback: ScormEventCallback) {\n this._eventService.on(listenerName, callback);\n }\n\n /**\n * Provides a mechanism for detaching a specific SCORM event listener.\n * This method removes a previously registered callback for an event.\n * Both the event name and the callback reference must match what was used in the 'on' method.\n *\n * @param {string} listenerName - The name of the event to stop listening for\n * @param {function} callback - The callback function to remove\n * @example\n * // Remove a specific listener\n * const myCallback = function() { console.log(\"API initialized\"); };\n * api.on(\"Initialize\", myCallback);\n * // Later, when you want to remove it:\n * api.off(\"Initialize\", myCallback);\n */\n off(listenerName: string, callback: ScormEventCallback) {\n this._eventService.off(listenerName, callback);\n }\n\n /**\n * Provides a mechanism for clearing all listeners from a specific SCORM event.\n * This method removes all callbacks registered for the specified event.\n *\n * @param {string} listenerName - The name of the event to clear all listeners for\n * @example\n * // Remove all listeners for the Initialize event\n * api.clear(\"Initialize\");\n */\n clear(listenerName: string) {\n this._eventService.clear(listenerName);\n }\n\n /**\n * Processes any 'on' listeners that have been created for a specific event.\n * This method is called internally when SCORM events occur to notify all registered listeners.\n * It triggers all callback functions registered for the specified event.\n *\n * @param {string} functionName - The name of the function/event that occurred\n * @param {string} CMIElement - Optional CMI element involved in the event\n * @param {any} value - Optional value associated with the event\n */\n processListeners(functionName: string, CMIElement?: string, value?: any) {\n this._eventService.processListeners(functionName, CMIElement, value);\n }\n\n /**\n * Throws a SCORM error with the specified error number and optional message.\n * This method sets the last error code and can be used to indicate that an operation failed.\n * The error number should correspond to one of the standard SCORM error codes.\n *\n * @param {string} CMIElement\n * @param {number} errorNumber - The SCORM error code to set\n * @param {string} message - Optional custom error message to provide additional context\n * @example\n * // Throw a \"not initialized\" error\n * this.throwSCORMError(301, \"The API must be initialized before calling GetValue\");\n */\n throwSCORMError(\n CMIElement: string | undefined,\n errorNumber: number | undefined,\n message?: string,\n ) {\n this._errorHandlingService.throwSCORMError(CMIElement, errorNumber ?? 0, message);\n }\n\n /**\n * Clears the last SCORM error code when an operation succeeds.\n * This method is typically called after successful API operations to reset the error state.\n * It only clears the error if the success parameter is \"true\".\n *\n * @param {string} success - A string indicating whether the operation succeeded (\"true\" or \"false\")\n * @example\n * // Clear error after successful operation\n * this.clearSCORMError(\"true\");\n */\n clearSCORMError(success: string) {\n this._errorHandlingService.clearSCORMError(success);\n }\n\n /**\n * Load the CMI from a flattened JSON object.\n * This method populates the CMI data model from a flattened JSON structure\n * where keys represent CMI element paths (e.g., \"cmi.core.student_id\").\n *\n * @param {StringKeyMap} json - The flattened JSON object containing CMI data\n * @param {string} CMIElement - Optional base CMI element path to prepend to all keys\n * @example\n * // Load data from a flattened JSON structure\n * api.loadFromFlattenedJSON({\n * \"cmi.core.student_id\": \"12345\",\n * \"cmi.core.student_name\": \"John Doe\",\n * \"cmi.core.lesson_status\": \"incomplete\"\n * });\n */\n loadFromFlattenedJSON(json: StringKeyMap, CMIElement?: string) {\n if (!CMIElement) {\n // by default, we start from a blank string because we're expecting each element to start with `cmi`\n CMIElement = \"\";\n }\n\n this._serializationService.loadFromFlattenedJSON(\n json,\n CMIElement,\n (CMIElement, value) => this.setCMIValue(CMIElement, value),\n () => this.isNotInitialized(),\n (data: StringKeyMap) => {\n this.startingData = data;\n },\n );\n }\n\n /**\n * Returns a flattened JSON object representing the current CMI data.\n */\n getFlattenedCMI(): StringKeyMap {\n return flatten(this.renderCMIToJSONObject());\n }\n\n /**\n * Loads CMI data from a hierarchical JSON object.\n * This method populates the CMI data model from a nested JSON structure\n * that mirrors the CMI object hierarchy.\n *\n * @param {StringKeyMap} json - The hierarchical JSON object containing CMI data\n * @param {string} CMIElement - Optional base CMI element path to prepend to all keys\n * @example\n * // Load data from a hierarchical JSON structure\n * api.loadFromJSON({\n * core: {\n * student_id: \"12345\",\n * student_name: \"John Doe\",\n * lesson_status: \"incomplete\"\n * },\n * objectives: [\n * { id: \"obj1\", score: { raw: 85 } }\n * ]\n * });\n */\n loadFromJSON(json: StringKeyMap, CMIElement: string = \"\") {\n if (\n (!CMIElement || CMIElement === \"\") &&\n !Object.hasOwnProperty.call(json, \"cmi\") &&\n !Object.hasOwnProperty.call(json, \"adl\")\n ) {\n // providing a backward compatibility for the old v1 API\n CMIElement = \"cmi\";\n }\n this._serializationService.loadFromJSON(\n json,\n CMIElement,\n (CMIElement, value) => this.setCMIValue(CMIElement, value),\n () => this.isNotInitialized(),\n (data: StringKeyMap) => {\n this.startingData = data;\n },\n );\n }\n\n /**\n * Render the CMI object to a JSON string for sending to an LMS.\n * This method serializes the current CMI data model to a JSON string.\n * The output format is controlled by the sendFullCommit setting.\n *\n * @return {string} A JSON string representation of the CMI data\n * @example\n * // Get the current CMI data as a JSON string\n * const jsonString = api.renderCMIToJSONString();\n * console.log(jsonString); // '{\"core\":{\"student_id\":\"12345\",...}}'\n */\n renderCMIToJSONString(): string {\n return this._serializationService.renderCMIToJSONString(this.cmi, this.settings.sendFullCommit);\n }\n\n /**\n * Returns a JavaScript object representing the current CMI data.\n * This method creates a plain JavaScript object that mirrors the\n * structure of the CMI data model, suitable for further processing.\n *\n * @return {StringKeyMap} A JavaScript object representing the CMI data\n * @example\n * // Get the current CMI data as a JavaScript object\n * const cmiObject = api.renderCMIToJSONObject();\n * console.log(cmiObject.core.student_id); // \"12345\"\n */\n renderCMIToJSONObject(): StringKeyMap {\n return this._serializationService.renderCMIToJSONObject(this.cmi, this.settings.sendFullCommit);\n }\n\n /**\n * Process an HTTP request\n *\n * @param {string} url - The URL to send the request to\n * @param {CommitObject | StringKeyMap | Array<any>} params - The parameters to send\n * @param {boolean} immediate - Whether to send the request immediately without waiting\n * @returns {ResultObject} - The result of the request\n */\n processHttpRequest(\n url: string,\n params: CommitObject | StringKeyMap | Array<any>,\n immediate: boolean = false,\n ): ResultObject {\n // If offline support is enabled and device is offline, store data locally instead of sending\n if (\n this.settings.enableOfflineSupport &&\n this._offlineStorageService &&\n !this._offlineStorageService.isDeviceOnline() &&\n this._courseId\n ) {\n this.apiLog(\n \"processHttpRequest\",\n \"Device is offline, storing data locally\",\n LogLevelEnum.INFO,\n );\n\n if (params && typeof params === \"object\" && \"cmi\" in params) {\n // Store offline and return actual storage result (localStorage is synchronous)\n return this._offlineStorageService.storeOffline(this._courseId, params as CommitObject);\n } else {\n this.apiLog(\n \"processHttpRequest\",\n \"Invalid commit data format for offline storage\",\n LogLevelEnum.ERROR,\n );\n return {\n result: global_constants.SCORM_FALSE,\n errorCode: this._error_codes.GENERAL ?? 101,\n };\n }\n }\n\n // Otherwise, proceed with HTTP request (synchronous or async based on service)\n return this._httpService.processHttpRequest(\n url,\n params,\n immediate,\n (functionName, message, level, element) => this.apiLog(functionName, message, level, element),\n (functionName, CMIElement, value) => this.processListeners(functionName, CMIElement, value),\n );\n }\n\n /**\n * Schedules a commit operation to occur after a specified delay.\n * This method is used to implement auto-commit functionality, where data\n * is periodically sent to the LMS without requiring explicit commit calls.\n *\n * @param {number} when - The number of milliseconds to wait before committing\n * @param {string} callback - The name of the commit event callback\n * @example\n * // Schedule a commit to happen in 60 seconds\n * api.scheduleCommit(60000, \"commit\");\n */\n scheduleCommit(when: number, callback: string) {\n if (!this._timeout) {\n this._timeout = new ScheduledCommit(this, when, callback);\n this.apiLog(\"scheduleCommit\", \"scheduled\", LogLevelEnum.DEBUG, \"\");\n }\n }\n\n /**\n * Clears and cancels any currently scheduled commits.\n * This method is typically called when an explicit commit is performed\n * or when the API is terminated, to prevent redundant commits.\n *\n * @example\n * // Cancel any pending scheduled commits\n * api.clearScheduledCommit();\n */\n clearScheduledCommit() {\n if (this._timeout) {\n this._timeout.cancel();\n this._timeout = undefined;\n this.apiLog(\"clearScheduledCommit\", \"cleared\", LogLevelEnum.DEBUG, \"\");\n }\n }\n\n /**\n * Checks if an object has a specific property, using multiple detection methods.\n * This method performs a thorough check for property existence by:\n * 1. Checking if it's an own property using Object.hasOwnProperty\n * 2. Checking if it's defined in the prototype with a property descriptor\n * 3. Checking if it's accessible via the 'in' operator (includes inherited properties)\n *\n * @param {StringKeyMap} StringKeyMap - The object to check for the property\n * @param {string} attribute - The property name to look for\n * @return {boolean} True if the property exists on the object or its prototype chain\n * @private\n *\n * @example\n * // Check for an own property\n * const obj = { name: \"John\" };\n * this._checkObjectHasProperty(obj, \"name\"); // Returns true\n *\n * @example\n * // Check for an inherited property\n * class Parent { get type() { return \"parent\"; } }\n * const child = Object.create(new Parent());\n * this._checkObjectHasProperty(child, \"type\"); // Returns true\n *\n * @example\n * // Check for a non-existent property\n * const obj = { name: \"John\" };\n * this._checkObjectHasProperty(obj, \"age\"); // Returns false\n */\n private _checkObjectHasProperty(obj: StringKeyMap, attribute: string): boolean {\n // Handle primitives - they don't have custom properties and 'in' operator throws on them\n if (obj === null || obj === undefined || typeof obj !== \"object\") {\n return false;\n }\n return (\n Object.hasOwnProperty.call(obj, attribute) ||\n Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), attribute) != null ||\n attribute in obj\n );\n }\n\n /**\n * Handles exceptions that occur when accessing CMI values.\n * This method delegates to the ErrorHandlingService to process exceptions\n * that occur during CMI data operations, ensuring consistent error handling\n * throughout the API.\n *\n * @param {string} CMIElement\n * @param {any} e - The exception that was thrown\n * @param {string} returnValue - The default return value to use if an error occurs\n * @return {string} Either the original returnValue or SCORM_FALSE if an error occurred\n * @private\n *\n * @example\n * // Handle a validation error when getting a CMI value\n * try {\n * return this.getCMIValue(\"cmi.core.score.raw\");\n * } catch (e) {\n * return this.handleValueAccessException(e, \"\");\n * }\n *\n * @example\n * // Handle a general error when setting a CMI value\n * try {\n * this.setCMIValue(\"cmi.core.lesson_status\", \"completed\");\n * return \"true\";\n * } catch (e) {\n * return this.handleValueAccessException(e, \"false\");\n * }\n */\n private handleValueAccessException(CMIElement: string, e: any, returnValue: string): string {\n if (e instanceof ValidationError) {\n this.lastErrorCode = String(e.errorCode);\n // Per SCORM spec: GetValue returns \"\" on error, SetValue returns \"false\"\n // The caller passes the appropriate default, so we preserve it\n // Only override to \"false\" if returnValue wasn't already set to \"\"\n if (returnValue !== \"\") {\n returnValue = global_constants.SCORM_FALSE;\n }\n this.throwSCORMError(CMIElement, e.errorCode, e.errorMessage);\n } else {\n if (e instanceof Error && e.message) {\n this.throwSCORMError(CMIElement, this._error_codes.GENERAL, e.message);\n } else {\n this.throwSCORMError(CMIElement, this._error_codes.GENERAL, \"Unknown error\");\n }\n }\n return returnValue;\n }\n\n /**\n * Builds the commit object to be sent to the LMS.\n * This method delegates to the SerializationService to create a properly\n * formatted object containing the CMI data that needs to be sent to the LMS.\n * The format and content of the commit object depend on whether this is a\n * regular commit or a termination commit.\n *\n * @param {boolean} terminateCommit - Whether this is a termination commit\n * @return {CommitObject|StringKeyMap|Array} The formatted commit object\n * @protected\n *\n * @example\n * // Create a regular commit object\n * const regularCommit = this.getCommitObject(false);\n * // Result might be: { cmi: { core: { lesson_status: \"incomplete\" } } }\n *\n * @example\n * // Create a termination commit object (includes total_time)\n * const terminationCommit = this.getCommitObject(true);\n * // Result might be: { cmi: { core: { lesson_status: \"completed\", total_time: \"PT1H30M\" } } }\n */\n protected getCommitObject(terminateCommit: boolean): CommitObject | StringKeyMap | Array<any> {\n return this._serializationService.getCommitObject(\n terminateCommit,\n this.settings.alwaysSendTotalTime,\n this.settings.renderCommonCommitFields,\n (terminateCommit: boolean, includeTotalTime?: boolean) =>\n this.renderCommitObject(terminateCommit, includeTotalTime),\n (terminateCommit: boolean, includeTotalTime?: boolean) =>\n this.renderCommitCMI(terminateCommit, includeTotalTime),\n this.settings.logLevel,\n );\n }\n}\n","import { BaseCMI } from \"./base_cmi\";\nimport { BaseScormValidationError } from \"../../exceptions\";\nimport {\n scorm12_constants,\n scorm12_errors,\n scorm12_regex,\n} from \"../../constants\";\nimport { validationService } from \"../../services\";\nimport { ScoreObject } from \"../../types\";\n\n/**\n * Base class for cmi *.score objects\n */\nexport class CMIScore extends BaseCMI {\n private readonly __children: string;\n /**\n * Score range validation pattern (e.g., \"0#100\" for SCORM 1.2).\n * Set to `false` to disable range validation (e.g., for SCORM 2004 where scores have no upper bound).\n * This property is intentionally unused in the base class but provides subclass flexibility.\n */\n private readonly __score_range: string | false;\n private readonly __invalid_error_code: number;\n private readonly __invalid_type_code: number;\n private readonly __invalid_range_code: number;\n private readonly __decimal_regex: string;\n private readonly __error_class: typeof BaseScormValidationError;\n protected _raw = \"\";\n protected _min = \"\";\n protected _max: string;\n\n /**\n * Constructor for *.score\n *\n * SPEC COMPLIANCE NOTE for _max default:\n * The SCORM 1.2 specification defines the default value for score.max as empty string (\"\").\n * This implementation defaults to \"100\" instead for the following reasons:\n *\n * 1. Most SCOs expect a 0-100 scale and don't explicitly set max\n * 2. An empty max creates ambiguity in score interpretation\n * 3. \"100\" is the most common expected value and simplifies SCO development\n * 4. This matches real-world LMS behavior (most default to 100)\n * 5. SCOs can still explicitly set max=\"\" if needed\n *\n * Strict spec default would be: \"\"\n *\n * @param params - Configuration parameters\n * @param params.score_range - Optional range pattern. When provided, uses scorm12_regex.score_range.\n * When omitted or falsy, disables range validation (sets to false).\n * SCORM 1.2 passes a truthy value to enable \"0#100\" validation.\n * SCORM 2004 omits this to allow unbounded scores.\n */\n constructor(params: {\n CMIElement: string;\n score_children?: string;\n score_range?: string;\n max?: string;\n invalidErrorCode?: number;\n invalidTypeCode?: number;\n invalidRangeCode?: number;\n decimalRegex?: string;\n errorClass: typeof BaseScormValidationError;\n }) {\n super(params.CMIElement);\n\n this.__children = params.score_children || scorm12_constants.score_children;\n // score_range parameter controls whether range validation is enabled:\n // - Truthy value (e.g., \"0#100\"): enables range validation using scorm12_regex.score_range\n // - Falsy/omitted: disables range validation by setting to false\n this.__score_range = !params.score_range ? false : scorm12_regex.score_range;\n // See SPEC COMPLIANCE NOTE above for why default is \"100\" instead of \"\"\n this._max = params.max || params.max === \"\" ? params.max : \"100\";\n this.__invalid_error_code =\n params.invalidErrorCode || (scorm12_errors.INVALID_SET_VALUE as number);\n this.__invalid_type_code = params.invalidTypeCode || (scorm12_errors.TYPE_MISMATCH as number);\n this.__invalid_range_code =\n params.invalidRangeCode || (scorm12_errors.VALUE_OUT_OF_RANGE as number);\n this.__decimal_regex = params.decimalRegex || scorm12_regex.CMIDecimal;\n this.__error_class = params.errorClass;\n }\n\n /**\n * Called when the API has been reset\n *\n * SCORE-01: Resets _raw and _min to empty strings to match subclass behavior.\n * _max is NOT reset here as it has a non-trivial default (\"100\") that is\n * handled by the constructor or reinitialization logic.\n */\n reset(): void {\n this._initialized = false;\n this._raw = \"\";\n this._min = \"\";\n }\n\n /**\n * Getter for _children\n * @return {string}\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for _children. Just throws an error.\n * @param {string} _children\n */\n set _children(_children: string) {\n throw new this.__error_class(this._cmi_element + \"._children\", this.__invalid_error_code);\n }\n\n /**\n * Getter for _raw\n * @return {string}\n */\n get raw(): string {\n return this._raw;\n }\n\n /**\n * Setter for _raw\n * @param {string} raw\n */\n set raw(raw: string) {\n if (\n validationService.validateScore(\n this._cmi_element + \".raw\",\n raw,\n this.__decimal_regex,\n this.__score_range,\n this.__invalid_type_code,\n this.__invalid_range_code,\n this.__error_class,\n )\n ) {\n this._raw = raw;\n }\n }\n\n /**\n * Getter for _min\n * @return {string}\n */\n get min(): string {\n return this._min;\n }\n\n /**\n * Setter for _min\n * @param {string} min\n */\n set min(min: string) {\n if (\n validationService.validateScore(\n this._cmi_element + \".min\",\n min,\n this.__decimal_regex,\n this.__score_range,\n this.__invalid_type_code,\n this.__invalid_range_code,\n this.__error_class,\n )\n ) {\n this._min = min;\n }\n }\n\n /**\n * Getter for _max\n * @return {string}\n */\n get max(): string {\n return this._max;\n }\n\n /**\n * Setter for _max\n * @param {string} max\n */\n set max(max: string) {\n if (\n validationService.validateScore(\n this._cmi_element + \".max\",\n max,\n this.__decimal_regex,\n this.__score_range,\n this.__invalid_type_code,\n this.__invalid_range_code,\n this.__error_class,\n )\n ) {\n this._max = max;\n }\n }\n\n /**\n * Gets score object with numeric values\n * @return {ScoreObject}\n */\n public getScoreObject(): ScoreObject {\n const scoreObject: ScoreObject = {};\n if (!Number.isNaN(Number.parseFloat(this.raw))) {\n scoreObject.raw = Number.parseFloat(this.raw);\n }\n if (!Number.isNaN(Number.parseFloat(this.min))) {\n scoreObject.min = Number.parseFloat(this.min);\n }\n if (!Number.isNaN(Number.parseFloat(this.max))) {\n scoreObject.max = Number.parseFloat(this.max);\n }\n return scoreObject;\n }\n\n /**\n * toJSON for *.score\n * @return {\n * {\n * min: string,\n * max: string,\n * raw: string\n * }\n * }\n */\n toJSON(): {\n min: string;\n max: string;\n raw: string;\n } {\n this.jsonString = true;\n const result = {\n raw: this.raw,\n min: this.min,\n max: this.max,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseCMI } from \"../common/base_cmi\";\nimport { CMIScore } from \"../common/score\";\nimport { Scorm12ValidationError } from \"../../exceptions/scorm12_exceptions\";\nimport { check12ValidFormat } from \"../scorm12/validation\";\nimport {\n scorm12_constants,\n scorm12_errors,\n scorm12_regex,\n} from \"../../constants\";\nimport {\n addHHMMSSTimeStrings,\n getSecondsAsHHMMSS,\n getTimeAsSeconds,\n} from \"../../utilities\";\n\n/**\n * Class representing the `cmi.core` object\n * @extends BaseCMI\n */\nexport class CMICore extends BaseCMI {\n /**\n * Constructor for `cmi.core`\n */\n constructor() {\n super(\"cmi.core\");\n this.score = new CMIScore({\n CMIElement: \"cmi.core.score\",\n score_children: scorm12_constants.score_children,\n score_range: scorm12_regex.score_range,\n invalidErrorCode: scorm12_errors.INVALID_SET_VALUE as number,\n invalidTypeCode: scorm12_errors.TYPE_MISMATCH as number,\n invalidRangeCode: scorm12_errors.VALUE_OUT_OF_RANGE as number,\n errorClass: Scorm12ValidationError,\n });\n }\n\n public readonly score: CMIScore;\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this.score?.initialize();\n }\n\n private readonly __children = scorm12_constants.core_children;\n private _student_id = \"\";\n private _student_name = \"\";\n private _lesson_location = \"\";\n private _credit = \"\";\n private _lesson_status = \"not attempted\";\n private _entry = \"\";\n private _total_time = \"\";\n private _lesson_mode = \"normal\";\n private _exit = \"\";\n private _session_time = \"00:00:00\";\n private _suspend_data = \"\";\n\n /**\n * Called when the API has been reset\n */\n reset(): void {\n this._initialized = false;\n\n this._exit = \"\";\n this._entry = \"\";\n\n /**\n * Resetting ensures we accurately track the time spent on each SCO session independently.\n * Each new session should start from zero, so we can reset cmi.core.session_time at the beginning of each session.\n */\n this._session_time = \"00:00:00\";\n\n this.score?.reset();\n }\n\n /**\n * Getter for __children\n * @return {string}\n * @private\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for __children. Just throws an error.\n * @param {string} _children\n * @private\n */\n set _children(_children: string) {\n throw new Scorm12ValidationError(\n this._cmi_element + \"._children\",\n scorm12_errors.INVALID_SET_VALUE as number,\n );\n }\n\n /**\n * Getter for _student_id\n * @return {string}\n */\n get student_id(): string {\n return this._student_id;\n }\n\n /**\n * Setter for _student_id. Can only be called before initialization.\n * @param {string} student_id\n */\n set student_id(student_id: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".student_id\",\n scorm12_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n this._student_id = student_id;\n }\n }\n\n /**\n * Getter for _student_name\n * @return {string}\n */\n get student_name(): string {\n return this._student_name;\n }\n\n /**\n * Setter for _student_name. Can only be called before initialization.\n * @param {string} student_name\n */\n set student_name(student_name: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".student_name\",\n scorm12_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n this._student_name = student_name;\n }\n }\n\n /**\n * Getter for _lesson_location\n * @return {string}\n */\n get lesson_location(): string {\n return this._lesson_location;\n }\n\n /**\n * Setter for _lesson_location\n * @param {string} lesson_location\n */\n set lesson_location(lesson_location: string) {\n if (\n check12ValidFormat(\n this._cmi_element + \".lesson_location\",\n lesson_location,\n scorm12_regex.CMIString256,\n true,\n )\n ) {\n this._lesson_location = lesson_location;\n }\n }\n\n /**\n * Getter for _credit\n * @return {string}\n */\n get credit(): string {\n return this._credit;\n }\n\n /**\n * Setter for _credit. Can only be called before initialization.\n * @param {string} credit\n */\n set credit(credit: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".credit\",\n scorm12_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n if (\n check12ValidFormat(this._cmi_element + \".credit\", credit, scorm12_regex.CMICredit, true)\n ) {\n this._credit = credit;\n }\n }\n }\n\n /**\n * Getter for _lesson_status\n * @spec RTE 3.4.2.1.7 - cmi.core.lesson_status\n * @return {string}\n */\n get lesson_status(): string {\n return this._lesson_status;\n }\n\n /**\n * Setter for _lesson_status\n * @spec RTE 3.4.2.1.7 - cmi.core.lesson_status\n * @param {string} lesson_status\n */\n set lesson_status(lesson_status: string) {\n if (this.initialized) {\n if (\n check12ValidFormat(\n this._cmi_element + \".lesson_status\",\n lesson_status,\n scorm12_regex.CMIStatus,\n )\n ) {\n this._lesson_status = lesson_status;\n }\n } else {\n if (\n check12ValidFormat(\n this._cmi_element + \".lesson_status\",\n lesson_status,\n scorm12_regex.CMIStatus2,\n )\n ) {\n this._lesson_status = lesson_status;\n }\n }\n }\n\n /**\n * Getter for _entry\n * @return {string}\n */\n get entry(): string {\n return this._entry;\n }\n\n /**\n * Setter for _entry. Can only be called before initialization.\n * @param {string} entry\n */\n set entry(entry: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".entry\",\n scorm12_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n if (check12ValidFormat(this._cmi_element + \".entry\", entry, scorm12_regex.CMIEntry, true)) {\n this._entry = entry;\n }\n }\n }\n\n /**\n * Getter for _total_time\n * @spec RTE 3.4.2.1.13 - cmi.core.total_time\n * @return {string}\n */\n get total_time(): string {\n return this._total_time;\n }\n\n /**\n * Setter for _total_time. Can only be called before initialization.\n * @spec RTE 3.4.2.1.13 - cmi.core.total_time\n * @param {string} total_time\n */\n set total_time(total_time: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".total_time\",\n scorm12_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n if (\n check12ValidFormat(\n this._cmi_element + \".total_time\",\n total_time,\n scorm12_regex.CMITimespan,\n true,\n )\n ) {\n if (total_time) {\n const totalSeconds = getTimeAsSeconds(total_time, scorm12_regex.CMITimespan);\n this._total_time = getSecondsAsHHMMSS(totalSeconds);\n } else {\n this._total_time = total_time;\n }\n }\n }\n }\n\n /**\n * Getter for _lesson_mode\n * @return {string}\n */\n get lesson_mode(): string {\n return this._lesson_mode;\n }\n\n /**\n * Setter for _lesson_mode. Can only be called before initialization.\n * @param {string} lesson_mode\n */\n set lesson_mode(lesson_mode: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".lesson_mode\",\n scorm12_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n if (\n check12ValidFormat(this._cmi_element + \".lesson_mode\", lesson_mode, scorm12_regex.CMILessonMode)\n ) {\n this._lesson_mode = lesson_mode;\n }\n }\n }\n\n /**\n * Getter for _exit. Should only be called during JSON export.\n * @return {string}\n */\n get exit(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".exit\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._exit;\n }\n\n /**\n * Setter for _exit\n *\n * @spec RTE 3.4.2.1.4 - cmi.core.exit\n *\n * SPEC COMPLIANCE NOTE:\n * The SCORM 1.2 specification defines exit vocabulary as: \"time-out\", \"suspend\", \"logout\", or \"\"\n * The value \"normal\" is NOT part of the SCORM 1.2 vocabulary (it's a SCORM 2004 value).\n *\n * This implementation accepts \"normal\" and normalizes it to \"\" (empty string) for the\n * following reasons:\n *\n * 1. Legacy content authored for SCORM 2004 sometimes runs in SCORM 1.2 mode\n * 2. Some authoring tools incorrectly use \"normal\" for SCORM 1.2 content\n * 3. Rejecting \"normal\" would break content with no user benefit\n * 4. Empty string (\"\") has the same semantic meaning as \"normal\" (regular exit)\n * 5. A console warning is logged to help developers identify the issue\n *\n * Strict spec vocabulary: \"time-out\" | \"suspend\" | \"logout\" | \"\"\n *\n * @param {string} exit\n */\n set exit(exit: string) {\n if (exit === \"normal\") {\n console.warn(\n \"SCORM 1.2: Received non-standard value 'normal' for cmi.core.exit; normalizing to empty string.\",\n );\n exit = \"\";\n }\n if (check12ValidFormat(this._cmi_element + \".exit\", exit, scorm12_regex.CMIExit, true)) {\n this._exit = exit;\n }\n }\n\n /**\n * Getter for _session_time. Should only be called during JSON export.\n * @return {string}\n */\n get session_time(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".session_time\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._session_time;\n }\n\n /**\n * Setter for _session_time\n * @param {string} session_time\n */\n set session_time(session_time: string) {\n if (\n check12ValidFormat(\n this._cmi_element + \".session_time\",\n session_time,\n scorm12_regex.CMITimespan,\n )\n ) {\n const totalSeconds = getTimeAsSeconds(session_time, scorm12_regex.CMITimespan);\n this._session_time = getSecondsAsHHMMSS(totalSeconds);\n }\n }\n\n /**\n * Getter for _suspend_data\n * @return {string}\n */\n get suspend_data(): string {\n return this._suspend_data;\n }\n\n /**\n * Setter for _suspend_data\n *\n * SPEC COMPLIANCE NOTE:\n * Uses CMIString64000 (64000 char limit) instead of spec-defined CMIString4096.\n * See scorm12_regex.CMIString64000 documentation for rationale.\n *\n * @param {string} suspend_data\n */\n set suspend_data(suspend_data: string) {\n if (\n check12ValidFormat(\n this._cmi_element + \".suspend_data\",\n suspend_data,\n scorm12_regex.CMIString64000,\n true,\n )\n ) {\n this._suspend_data = suspend_data;\n }\n }\n\n /**\n * Adds the current session time to the existing total time.\n * @param {number} start_time\n * @return {string}\n */\n getCurrentTotalTime(start_time: number | undefined): string {\n let sessionTime = this._session_time;\n if (typeof start_time !== \"undefined\") {\n const seconds = new Date().getTime() - start_time;\n sessionTime = getSecondsAsHHMMSS(seconds / 1000);\n }\n\n return addHHMMSSTimeStrings(\n this._total_time,\n sessionTime,\n new RegExp(scorm12_regex.CMITimespan),\n );\n }\n\n /**\n * toJSON for cmi.core\n *\n * @return {\n * {\n * student_name: string,\n * entry: string,\n * exit: string,\n * score: CMIScore,\n * student_id: string,\n * lesson_mode: string,\n * lesson_location: string,\n * lesson_status: string,\n * credit: string,\n * session_time: string\n * }\n * }\n */\n toJSON(): {\n student_name: string;\n entry: string;\n exit: string;\n score: CMIScore;\n student_id: string;\n lesson_mode: string;\n lesson_location: string;\n lesson_status: string;\n credit: string;\n session_time: string;\n } {\n this.jsonString = true;\n const result = {\n student_id: this.student_id,\n student_name: this.student_name,\n lesson_location: this.lesson_location,\n credit: this.credit,\n lesson_status: this.lesson_status,\n entry: this.entry,\n lesson_mode: this.lesson_mode,\n exit: this.exit,\n session_time: this.session_time,\n score: this.score,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseCMI } from \"../common/base_cmi\";\nimport { CMIScore } from \"../common/score\";\nimport { CMIArray } from \"../common/array\";\nimport { Scorm12ValidationError } from \"../../exceptions/scorm12_exceptions\";\nimport { check12ValidFormat } from \"./validation\";\nimport {\n scorm12_constants,\n scorm12_errors,\n scorm12_regex,\n} from \"../../constants\";\n\n/**\n * Class representing SCORM 1.2's `cmi.objectives` object\n *\n * Per SCORM 1.2 RTE Section 3.4.2.6:\n * - Array of objective records for tracking learning goals\n * - Each objective contains: id, score (raw, min, max), status\n * - Indices must be sequential starting at 0\n * - SCO can define multiple objectives to track sub-goals\n *\n * @extends CMIArray\n */\nexport class CMIObjectives extends CMIArray {\n /**\n * Constructor for `cmi.objectives`\n */\n constructor() {\n super({\n CMIElement: \"cmi.objectives\",\n children: scorm12_constants.objectives_children,\n errorCode: scorm12_errors.INVALID_SET_VALUE as number,\n errorClass: Scorm12ValidationError,\n });\n }\n}\n\n/**\n * Class representing SCORM 1.2's cmi.objectives.n object\n *\n * Per SCORM 1.2 RTE Section 3.4.2.6:\n * - Individual objective record\n * - id: Unique identifier for the objective (CMIIdentifier)\n * - score: Learner's score for this objective (raw, min, max)\n * - status: Achievement status (passed, completed, failed, incomplete, browsed, not attempted)\n * - Used to track progress toward specific learning goals\n *\n * @extends BaseCMI\n */\nexport class CMIObjectivesObject extends BaseCMI {\n /**\n * Constructor for cmi.objectives.n\n */\n constructor() {\n super(\"cmi.objectives.n\");\n this.score = new CMIScore({\n CMIElement: \"cmi.objectives.n.score\",\n score_children: scorm12_constants.score_children,\n score_range: scorm12_regex.score_range,\n invalidErrorCode: scorm12_errors.INVALID_SET_VALUE as number,\n invalidTypeCode: scorm12_errors.TYPE_MISMATCH as number,\n invalidRangeCode: scorm12_errors.VALUE_OUT_OF_RANGE as number,\n errorClass: Scorm12ValidationError,\n });\n }\n\n public readonly score: CMIScore;\n\n private _id = \"\";\n private _status = \"\";\n\n /**\n * Called when the API has been reset\n */\n reset(): void {\n this._initialized = false;\n this._id = \"\";\n this._status = \"\";\n this.score?.reset();\n }\n\n /**\n * Getter for _id\n * @spec RTE 3.4.2.6.1 - cmi.objectives.n.id\n * @return {string}\n */\n get id(): string {\n return this._id;\n }\n\n /**\n * Setter for _id\n * @spec RTE 3.4.2.6.1 - cmi.objectives.n.id\n * @param {string} id\n */\n set id(id: string) {\n if (check12ValidFormat(this._cmi_element + \".id\", id, scorm12_regex.CMIIdentifier)) {\n this._id = id;\n }\n }\n\n /**\n * Getter for _status\n * @spec RTE 3.4.2.6.3 - cmi.objectives.n.status\n * @return {string}\n */\n get status(): string {\n return this._status;\n }\n\n /**\n * Setter for _status\n * @spec RTE 3.4.2.6.3 - cmi.objectives.n.status\n * @param {string} status\n */\n set status(status: string) {\n if (check12ValidFormat(this._cmi_element + \".status\", status, scorm12_regex.CMIStatus2)) {\n this._status = status;\n }\n }\n\n /**\n * toJSON for cmi.objectives.n\n *\n * The `jsonString` flag pattern used here serves a specific purpose:\n * - Setting `jsonString = true` before accessing properties bypasses initialization checks\n * - This allows JSON serialization to read write-only or uninitialized properties\n * - Without this flag, accessing certain properties would throw SCORM validation errors\n * - The flag is reset to `false` after serialization to restore normal validation behavior\n * - This pattern is used throughout SCORM-Again for controlled property access during export\n *\n * @return {\n * {\n * id: string,\n * status: string,\n * score: CMIScore\n * }\n * }\n */\n toJSON(): {\n id: string;\n status: string;\n score: CMIScore;\n } {\n this.jsonString = true;\n const result = {\n id: this.id,\n status: this.status,\n score: this.score,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm12ValidationError } from \"../../exceptions/scorm12_exceptions\";\nimport { check12ValidFormat, check12ValidRange } from \"./validation\";\nimport {\n scorm12_constants,\n scorm12_errors,\n scorm12_regex,\n scorm2004_regex,\n} from \"../../constants\";\nimport { validationService } from \"../../services\";\nimport {\n getDurationAsSeconds,\n getSecondsAsHHMMSS,\n getTimeAsSeconds,\n} from \"../../utilities\";\n\n/**\n * Parses time values in either SCORM 1.2 (HH:MM:SS) or ISO 8601 (PT...) format\n * and converts them to normalized SCORM 1.2 format.\n *\n * @param {string} value - The time value to parse\n * @param {string} fieldName - The CMI element name for error messages\n * @returns {string} Normalized time in HH:MM:SS format\n * @throws {Scorm12ValidationError} If value doesn't match either format\n */\nfunction parseTimeAllowed(value: string, fieldName: string): string {\n // @spec RTE 3.4.2.2.3 - cmi.student_data.max_time_allowed\n // Spec requires CMITimespan format (HH:MM:SS.cc), but some LMS implementations\n // use ISO 8601 durations in manifests. We support both for interoperability.\n\n // First try SCORM 1.2 HH:MM:SS(.cc) format\n try {\n check12ValidFormat(fieldName, value, scorm12_regex.CMITimespan, true);\n const totalSeconds = getTimeAsSeconds(value, scorm12_regex.CMITimespan);\n return getSecondsAsHHMMSS(totalSeconds);\n } catch (e) {\n // fall through and attempt other encodings\n }\n\n // Next try ISO 8601 durations (common in legacy manifests)\n try {\n check12ValidFormat(fieldName, value, scorm2004_regex.CMITimespan, true);\n const totalSeconds = getDurationAsSeconds(value, scorm2004_regex.CMITimespan);\n return getSecondsAsHHMMSS(totalSeconds);\n } catch (e) {\n // fall through to error\n }\n\n throw new Scorm12ValidationError(fieldName, scorm12_errors.TYPE_MISMATCH as number);\n}\n\n/**\n * Class representing the SCORM 1.2 cmi.student_data object\n * @extends BaseCMI\n */\nexport class CMIStudentData extends BaseCMI {\n private readonly __children;\n private _mastery_score = \"\";\n private _max_time_allowed = \"\";\n private _time_limit_action = \"\";\n\n /**\n * Constructor for cmi.student_data\n * @param {string} student_data_children\n */\n constructor(student_data_children?: string) {\n super(\"cmi.student_data\");\n this.__children = student_data_children\n ? student_data_children\n : scorm12_constants.student_data_children;\n }\n\n /**\n * Called when the API has been reset\n */\n reset(): void {\n this._initialized = false;\n }\n\n /**\n * Getter for __children\n * @return {string}\n * @private\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for __children. Just throws an error.\n * @param {string} _children\n * @private\n */\n set _children(_children: string) {\n throw new Scorm12ValidationError(\n this._cmi_element + \"._children\",\n scorm12_errors.INVALID_SET_VALUE as number,\n );\n }\n\n /**\n * Getter for _mastery_score\n * @return {string}\n */\n get mastery_score(): string {\n return this._mastery_score;\n }\n\n /**\n * Setter for _mastery_score. Can only be called before initialization.\n * @param {string} mastery_score\n */\n set mastery_score(mastery_score: string) {\n validationService.validateReadOnly(this._cmi_element + \".mastery_score\", this.initialized);\n if (mastery_score === undefined || mastery_score === null) {\n return;\n }\n\n let normalizedMasteryScore = mastery_score;\n if (typeof normalizedMasteryScore !== \"string\") {\n normalizedMasteryScore = String(normalizedMasteryScore);\n }\n\n if (normalizedMasteryScore === \"\") {\n this._mastery_score = mastery_score;\n return;\n }\n\n if (\n check12ValidFormat(\n this._cmi_element + \".mastery_score\",\n normalizedMasteryScore,\n scorm12_regex.CMIDecimal,\n ) &&\n check12ValidRange(\n this._cmi_element + \".mastery_score\",\n normalizedMasteryScore,\n scorm12_regex.score_range,\n )\n ) {\n this._mastery_score = normalizedMasteryScore;\n }\n }\n\n /**\n * Getter for _max_time_allowed\n * @return {string}\n */\n get max_time_allowed(): string {\n return this._max_time_allowed;\n }\n\n /**\n * Setter for _max_time_allowed. Can only be called before initialization.\n * @param {string} max_time_allowed\n */\n set max_time_allowed(max_time_allowed: string) {\n validationService.validateReadOnly(this._cmi_element + \".max_time_allowed\", this.initialized);\n if (max_time_allowed === undefined || max_time_allowed === null) {\n return;\n }\n\n const normalizedValue =\n typeof max_time_allowed === \"string\" ? max_time_allowed : String(max_time_allowed);\n\n if (normalizedValue === \"\") {\n this._max_time_allowed = \"\";\n return;\n }\n\n this._max_time_allowed = parseTimeAllowed(\n normalizedValue,\n this._cmi_element + \".max_time_allowed\",\n );\n }\n\n /**\n * Getter for _time_limit_action\n * @return {string}\n */\n get time_limit_action(): string {\n return this._time_limit_action;\n }\n\n /**\n * Setter for _time_limit_action. Can only be called before initialization.\n * @param {string} time_limit_action\n */\n set time_limit_action(time_limit_action: string) {\n validationService.validateReadOnly(this._cmi_element + \".time_limit_action\", this.initialized);\n if (time_limit_action === undefined || time_limit_action === null) {\n return;\n }\n\n const normalizedValue =\n typeof time_limit_action === \"string\" ? time_limit_action : String(time_limit_action);\n\n if (\n check12ValidFormat(\n this._cmi_element + \".time_limit_action\",\n normalizedValue,\n scorm12_regex.CMITimeLimitAction,\n true,\n )\n ) {\n this._time_limit_action = normalizedValue;\n }\n }\n\n /**\n * toJSON for cmi.student_data\n *\n * @return {\n * {\n * max_time_allowed: string,\n * time_limit_action: string,\n * mastery_score: string\n * }\n * }\n */\n toJSON(): {\n mastery_score: string;\n max_time_allowed: string;\n time_limit_action: string;\n } {\n this.jsonString = true;\n const result = {\n mastery_score: this.mastery_score,\n max_time_allowed: this.max_time_allowed,\n time_limit_action: this.time_limit_action,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm12ValidationError } from \"../../exceptions/scorm12_exceptions\";\nimport { scorm12_constants, scorm12_errors } from \"../../constants\";\nimport { validationService } from \"../../services\";\n\n/**\n * Class representing the SCORM 1.2 cmi.student_preference object\n * @extends BaseCMI\n */\nexport class CMIStudentPreference extends BaseCMI {\n private readonly __children;\n\n /**\n * Constructor for cmi.student_preference\n * @param {string} student_preference_children\n */\n constructor(student_preference_children?: string) {\n super(\"cmi.student_preference\");\n this.__children = student_preference_children\n ? student_preference_children\n : scorm12_constants.student_preference_children;\n }\n\n private _audio = \"\";\n private _language = \"\";\n private _speed = \"\";\n private _text = \"\";\n\n /**\n * Called when the API has been reset\n */\n reset(): void {\n this._initialized = false;\n }\n\n /**\n * Getter for __children\n * @return {string}\n * @private\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for __children. Just throws an error.\n * @param {string} _children\n * @private\n */\n set _children(_children: string) {\n throw new Scorm12ValidationError(\n this._cmi_element + \"._children\",\n scorm12_errors.INVALID_SET_VALUE as number,\n );\n }\n\n /**\n * Getter for _audio\n * @spec RTE 3.4.2.3.1 - cmi.student_preference.audio\n * @return {string}\n */\n get audio(): string {\n return this._audio;\n }\n\n /**\n * Setter for _audio\n * @spec RTE 3.4.2.3.1 - cmi.student_preference.audio\n * @param {string} audio\n */\n set audio(audio: string) {\n if (validationService.validateScorm12Audio(this._cmi_element + \".audio\", audio)) {\n this._audio = audio;\n }\n }\n\n /**\n * Getter for _language\n * @spec RTE 3.4.2.3.2 - cmi.student_preference.language\n * @return {string}\n */\n get language(): string {\n return this._language;\n }\n\n /**\n * Setter for _language\n * @spec RTE 3.4.2.3.2 - cmi.student_preference.language\n * @param {string} language\n */\n set language(language: string) {\n if (validationService.validateScorm12Language(this._cmi_element + \".language\", language)) {\n this._language = language;\n }\n }\n\n /**\n * Getter for _speed\n * @spec RTE 3.4.2.3.3 - cmi.student_preference.speed\n * @return {string}\n */\n get speed(): string {\n return this._speed;\n }\n\n /**\n * Setter for _speed\n * @spec RTE 3.4.2.3.3 - cmi.student_preference.speed\n * @param {string} speed\n */\n set speed(speed: string) {\n if (validationService.validateScorm12Speed(this._cmi_element + \".speed\", speed)) {\n this._speed = speed;\n }\n }\n\n /**\n * Getter for _text\n * @spec RTE 3.4.2.3.4 - cmi.student_preference.text\n * @return {string}\n */\n get text(): string {\n return this._text;\n }\n\n /**\n * Setter for _text\n * @spec RTE 3.4.2.3.4 - cmi.student_preference.text\n * @param {string} text\n */\n set text(text: string) {\n if (validationService.validateScorm12Text(this._cmi_element + \".text\", text)) {\n this._text = text;\n }\n }\n\n /**\n * toJSON for cmi.student_preference\n *\n * @return {\n * {\n * audio: string,\n * language: string,\n * speed: string,\n * text: string\n * }\n * }\n */\n toJSON(): {\n audio: string;\n language: string;\n speed: string;\n text: string;\n } {\n this.jsonString = true;\n const result = {\n audio: this.audio,\n language: this.language,\n speed: this.speed,\n text: this.text,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { CMIArray } from \"../common/array\";\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm12ValidationError } from \"../../exceptions/scorm12_exceptions\";\nimport { check12ValidFormat, check12ValidRange } from \"./validation\";\nimport {\n scorm12_constants,\n scorm12_errors,\n scorm12_regex,\n} from \"../../constants\";\nimport { getSecondsAsHHMMSS, getTimeAsSeconds } from \"../../utilities\";\n\n/**\n * Class representing the SCORM 1.2 `cmi.interactions`\n *\n * Per SCORM 1.2 RTE Section 3.4.2.7:\n * - Array of interaction records tracking learner responses to questions\n * - Each interaction: id, time, type, weighting, student_response, result, latency\n * - Supports objectives and correct_responses sub-arrays\n * - All interaction data is write-only (except during JSON export)\n *\n * @extends CMIArray\n */\nexport class CMIInteractions extends CMIArray {\n /**\n * Constructor for `cmi.interactions`\n */\n constructor() {\n super({\n CMIElement: \"cmi.interactions\",\n children: scorm12_constants.interactions_children,\n errorCode: scorm12_errors.INVALID_SET_VALUE as number,\n errorClass: Scorm12ValidationError,\n });\n }\n}\n\n/**\n * Class representing SCORM 1.2's cmi.interactions.n object\n *\n * Per SCORM 1.2 RTE Section 3.4.2.7:\n * - Individual interaction record\n * - id: Unique identifier for the interaction (CMIIdentifier)\n * - type: Interaction type (true-false, choice, fill-in, matching, performance, sequencing, likert, numeric)\n * - weighting: Weight assigned to the interaction (CMIDecimal, -100 to 100)\n * - student_response: Learner's response (format depends on type)\n * - result: Outcome (correct, wrong, unanticipated, neutral, or numeric score)\n * - latency: Time from presentation to response (CMITimespan)\n *\n * @extends BaseCMI\n */\nexport class CMIInteractionsObject extends BaseCMI {\n /**\n * Constructor for cmi.interactions.n object\n */\n constructor() {\n super(\"cmi.interactions.n\");\n this.objectives = new CMIArray({\n CMIElement: \"cmi.interactions.n.objectives\",\n errorCode: scorm12_errors.INVALID_SET_VALUE as number,\n errorClass: Scorm12ValidationError,\n children: scorm12_constants.objectives_children,\n });\n this.correct_responses = new CMIArray({\n CMIElement: \"cmi.interactions.correct_responses\",\n errorCode: scorm12_errors.INVALID_SET_VALUE as number,\n errorClass: Scorm12ValidationError,\n children: scorm12_constants.correct_responses_children,\n });\n }\n\n public readonly objectives: CMIArray;\n public readonly correct_responses: CMIArray;\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this.objectives?.initialize();\n this.correct_responses?.initialize();\n }\n\n private _id = \"\";\n private _time = \"\";\n private _type = \"\";\n private _weighting = \"\";\n private _student_response = \"\";\n private _result = \"\";\n private _latency = \"\";\n\n /**\n * Called when the API has been reset\n */\n override reset(): void {\n this._initialized = false;\n\n this._id = \"\";\n this._time = \"\";\n this._type = \"\";\n this._weighting = \"\";\n this._student_response = \"\";\n this._result = \"\";\n this._latency = \"\";\n\n this.objectives?.reset();\n this.correct_responses?.reset();\n }\n\n /**\n * Getter for _id. Should only be called during JSON export.\n * @return {string}\n */\n get id(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".id\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._id;\n }\n\n /**\n * Setter for _id\n * @param {string} id\n */\n set id(id: string) {\n if (check12ValidFormat(this._cmi_element + \".id\", id, scorm12_regex.CMIIdentifier)) {\n this._id = id;\n }\n }\n\n /**\n * Getter for _time. Should only be called during JSON export.\n * @return {string}\n */\n get time(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".time\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._time;\n }\n\n /**\n * Setter for _time\n * @param {string} time\n */\n set time(time: string) {\n if (check12ValidFormat(this._cmi_element + \".time\", time, scorm12_regex.CMITime)) {\n this._time = time;\n }\n }\n\n /**\n * Getter for _type. Should only be called during JSON export.\n * @return {string}\n */\n get type(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".type\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._type;\n }\n\n /**\n * Setter for _type\n * @param {string} type\n */\n set type(type: string) {\n if (check12ValidFormat(this._cmi_element + \".type\", type, scorm12_regex.CMIType)) {\n this._type = type;\n }\n }\n\n /**\n * Getter for _weighting. Should only be called during JSON export.\n * @return {string}\n */\n get weighting(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".weighting\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._weighting;\n }\n\n /**\n * Setter for _weighting\n * @param {string} weighting\n */\n set weighting(weighting: string) {\n if (\n check12ValidFormat(this._cmi_element + \".weighting\", weighting, scorm12_regex.CMIDecimal) &&\n check12ValidRange(this._cmi_element + \".weighting\", weighting, scorm12_regex.weighting_range)\n ) {\n this._weighting = weighting;\n }\n }\n\n /**\n * Getter for _student_response. Should only be called during JSON export.\n * @return {string}\n */\n get student_response(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".student_response\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._student_response;\n }\n\n /**\n * Setter for _student_response\n * @param {string} student_response\n */\n set student_response(student_response: string) {\n if (\n check12ValidFormat(\n this._cmi_element + \".student_response\",\n student_response,\n scorm12_regex.CMIFeedback,\n true,\n )\n ) {\n this._student_response = student_response;\n }\n }\n\n /**\n * Getter for _result. Should only be called during JSON export.\n * @return {string}\n */\n get result(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".result\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._result;\n }\n\n /**\n * Setter for _result\n * @spec RTE 3.4.2.7.6 - cmi.interactions.n.result\n * Per SCORM 1.2 spec, valid values are \"correct\", \"wrong\", \"unanticipated\", \"neutral\", or a numeric score.\n * The spec requires \"wrong\" not \"incorrect\" for failed interactions.\n * @param {string} result\n */\n set result(result: string) {\n // SCORM 1.2 clarification: Normalize 'incorrect' to 'wrong'\n // The SCORM 1.2 specification (RTE 3.4.2.7.6) defines \"wrong\" as the valid result value\n // for incorrect responses, not \"incorrect\". Some content may use \"incorrect\" due to\n // confusion with SCORM 2004 or common language, so we normalize it here for compatibility.\n let normalizedResult = result;\n if (result === \"incorrect\") {\n normalizedResult = \"wrong\";\n console.warn(\n \"SCORM 1.2: Received non-standard value 'incorrect' for cmi.interactions.n.result; normalizing to 'wrong'.\",\n );\n }\n\n if (\n check12ValidFormat(\n this._cmi_element + \".result\",\n normalizedResult,\n scorm12_regex.CMIResult,\n )\n ) {\n this._result = normalizedResult;\n }\n }\n\n /**\n * Getter for _latency. Should only be called during JSON export.\n * @return {string}\n */\n get latency(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".latency\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._latency;\n }\n\n /**\n * Setter for _latency\n * @param {string} latency\n */\n set latency(latency: string) {\n if (check12ValidFormat(this._cmi_element + \".latency\", latency, scorm12_regex.CMITimespan)) {\n const totalSeconds = getTimeAsSeconds(latency, scorm12_regex.CMITimespan);\n this._latency = getSecondsAsHHMMSS(totalSeconds);\n }\n }\n\n /**\n * toJSON for cmi.interactions.n\n *\n * @return {\n * {\n * id: string,\n * time: string,\n * type: string,\n * weighting: string,\n * student_response: string,\n * result: string,\n * latency: string,\n * objectives: CMIArray,\n * correct_responses: CMIArray\n * }\n * }\n */\n toJSON(): {\n id: string;\n time: string;\n type: string;\n weighting: string;\n student_response: string;\n result: string;\n latency: string;\n objectives: CMIArray;\n correct_responses: CMIArray;\n } {\n this.jsonString = true;\n const result = {\n id: this.id,\n time: this.time,\n type: this.type,\n weighting: this.weighting,\n student_response: this.student_response,\n result: this.result,\n latency: this.latency,\n objectives: this.objectives,\n correct_responses: this.correct_responses,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing SCORM 1.2's cmi.interactions.n.objectives.n object\n *\n * Per SCORM 1.2 RTE Section 3.4.2.7.3:\n * - Associates an interaction with one or more objectives\n * - id: Identifier matching an objective in cmi.objectives\n * - Write-only data element\n *\n * @extends BaseCMI\n */\nexport class CMIInteractionsObjectivesObject extends BaseCMI {\n /**\n * Constructor for cmi.interactions.n.objectives.n\n */\n constructor() {\n super(\"cmi.interactions.n.objectives.n\");\n }\n\n private _id = \"\";\n\n /**\n * Called when the API has been reset\n */\n reset(): void {\n this._initialized = false;\n this._id = \"\";\n }\n\n /**\n * Getter for _id. Should only be called during JSON export.\n * @return {string}\n */\n get id(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".id\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._id;\n }\n\n /**\n * Setter for _id\n * @param {string} id\n */\n set id(id: string) {\n if (check12ValidFormat(this._cmi_element + \".id\", id, scorm12_regex.CMIIdentifier)) {\n this._id = id;\n }\n }\n\n /**\n * toJSON for cmi.interactions.n.objectives.n\n * @return {\n * {\n * id: string\n * }\n * }\n */\n toJSON(): {\n id: string;\n } {\n this.jsonString = true;\n const result = {\n id: this.id,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing SCORM 1.2's cmi.interactions.correct_responses.n object\n *\n * Per SCORM 1.2 RTE Section 3.4.2.7.4:\n * - Defines correct response patterns for an interaction\n * - pattern: Expected correct response (format depends on interaction type)\n * - Multiple patterns allowed for interactions with multiple correct answers\n * - Write-only data element\n *\n * @extends BaseCMI\n */\nexport class CMIInteractionsCorrectResponsesObject extends BaseCMI {\n /**\n * Constructor for cmi.interactions.correct_responses.n\n */\n constructor() {\n super(\"cmi.interactions.correct_responses.n\");\n }\n\n private _pattern = \"\";\n\n /**\n * Called when the API has been reset\n */\n reset(): void {\n this._initialized = false;\n this._pattern = \"\";\n }\n\n /**\n * Getter for _pattern\n * @return {string}\n */\n get pattern(): string {\n if (!this.jsonString) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".pattern\",\n scorm12_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._pattern;\n }\n\n /**\n * Setter for _pattern\n * @param {string} pattern\n */\n set pattern(pattern: string) {\n if (\n check12ValidFormat(this._cmi_element + \".pattern\", pattern, scorm12_regex.CMIFeedback, true)\n ) {\n this._pattern = pattern;\n }\n }\n\n /**\n * toJSON for cmi.interactions.correct_responses.n\n * @return {\n * {\n * pattern: string\n * }\n * }\n */\n toJSON(): {\n pattern: string;\n } {\n this.jsonString = true;\n const result = {\n pattern: this._pattern,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseRootCMI } from \"../common/base_cmi\";\nimport { Scorm12ValidationError } from \"../../exceptions/scorm12_exceptions\";\nimport { check12ValidFormat } from \"./validation\";\nimport { CMICore } from \"./core\";\nimport { CMIObjectives } from \"./objectives\";\nimport { CMIStudentData } from \"./student_data\";\nimport { CMIStudentPreference } from \"./student_preference\";\nimport { CMIInteractions } from \"./interactions\";\nimport {\n scorm12_constants,\n scorm12_errors,\n scorm12_regex,\n} from \"../../constants\";\n\n/**\n * Class representing the cmi object for SCORM 1.2\n *\n * Per SCORM 1.2 RTE Section 3.4:\n * - Core data model for tracking learner progress\n * - Contains core (session data), objectives, student_data, student_preference, interactions\n * - All elements accessible via GetValue/SetValue API\n * - Data persistence managed by LMS between sessions\n *\n * @extends BaseRootCMI\n */\nexport class CMI extends BaseRootCMI {\n private readonly __children: string = \"\";\n private __version: string = \"3.4\";\n private _launch_data: string = \"\";\n private _comments: string = \"\";\n private _comments_from_lms: string = \"\";\n\n /**\n * Constructor for the SCORM 1.2 cmi object\n * @param {string} cmi_children\n * @param {(CMIStudentData)} student_data\n * @param {boolean} initialized\n */\n constructor(cmi_children?: string, student_data?: CMIStudentData, initialized?: boolean) {\n super(\"cmi\");\n if (initialized) this.initialize();\n this.__children = cmi_children ? cmi_children : scorm12_constants.cmi_children;\n this.core = new CMICore();\n this.objectives = new CMIObjectives();\n this.student_data = student_data ? student_data : new CMIStudentData();\n this.student_preference = new CMIStudentPreference();\n this.interactions = new CMIInteractions();\n }\n\n public core: CMICore;\n public objectives: CMIObjectives;\n public student_data: CMIStudentData;\n public student_preference: CMIStudentPreference;\n public interactions: CMIInteractions;\n\n /**\n * Called when the API has been reset\n *\n * CMI-03: Uses consistent ?.reset() pattern for all child objects.\n * Objectives and interactions use reset(true) to clear arrays completely.\n */\n reset(): void {\n this._initialized = false;\n\n this._launch_data = \"\";\n this._comments = \"\";\n this.core?.reset();\n this.objectives?.reset(true);\n this.interactions?.reset(true);\n this.student_data?.reset();\n this.student_preference?.reset();\n }\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this.core?.initialize();\n this.objectives?.initialize();\n this.student_data?.initialize();\n this.student_preference?.initialize();\n this.interactions?.initialize();\n }\n\n /**\n * toJSON for cmi\n *\n * @return {\n * {\n * suspend_data: string,\n * launch_data: string,\n * comments: string,\n * comments_from_lms: string,\n * core: CMICore,\n * objectives: CMIObjectives,\n * student_data: CMIStudentData,\n * student_preference: CMIStudentPreference,\n * interactions: CMIInteractions\n * }\n * }\n */\n toJSON(): {\n suspend_data: string;\n launch_data: string;\n comments: string;\n comments_from_lms: string;\n core: CMICore;\n objectives: CMIObjectives;\n student_data: CMIStudentData;\n student_preference: CMIStudentPreference;\n interactions: CMIInteractions;\n } {\n this.jsonString = true;\n const result = {\n suspend_data: this.suspend_data,\n launch_data: this.launch_data,\n comments: this.comments,\n comments_from_lms: this.comments_from_lms,\n core: this.core,\n objectives: this.objectives,\n student_data: this.student_data,\n student_preference: this.student_preference,\n interactions: this.interactions\n };\n this.jsonString = false;\n return result;\n }\n\n /**\n * Getter for __version\n * @return {string}\n */\n get _version(): string {\n return this.__version;\n }\n\n /**\n * Setter for __version. Just throws an error.\n * @param {string} _version\n */\n set _version(_version: string) {\n throw new Scorm12ValidationError(\n this._cmi_element + \"._version\",\n scorm12_errors.INVALID_SET_VALUE as number\n );\n }\n\n /**\n * Getter for __children\n * @return {string}\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for __version. Just throws an error.\n * @param {string} _children\n */\n set _children(_children: string) {\n throw new Scorm12ValidationError(\n this._cmi_element + \"._children\",\n scorm12_errors.INVALID_SET_VALUE as number\n );\n }\n\n /**\n * Getter for _suspend_data\n * @return {string}\n */\n get suspend_data(): string {\n return this.core?.suspend_data;\n }\n\n /**\n * Setter for _suspend_data\n * @param {string} suspend_data\n */\n set suspend_data(suspend_data: string) {\n if (this.core) {\n this.core.suspend_data = suspend_data;\n }\n }\n\n /**\n * Getter for _launch_data\n * @return {string}\n */\n get launch_data(): string {\n return this._launch_data;\n }\n\n /**\n * Setter for _launch_data. Can only be called before initialization.\n *\n * SPEC COMPLIANCE NOTE:\n * The SCORM 1.2 specification defines launch_data as CMIString4096 (max 4096 chars).\n * This implementation intentionally omits length validation because:\n *\n * 1. launch_data is LMS-provided data, not SCO-provided - the LMS is responsible\n * for ensuring valid data is provided to content\n * 2. This setter is only callable before API initialization (read-only to SCO)\n * 3. Real-world LMS systems may provide launch_data exceeding 4096 chars\n * 4. Rejecting oversized LMS data would break content with no recovery path\n *\n * Unlike cmi.suspend_data and cmi.comments (which SCOs write), launch_data\n * comes from the LMS manifest/configuration, so strict validation here would\n * penalize content for LMS decisions outside SCO control.\n *\n * @param {string} launch_data\n */\n set launch_data(launch_data: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".launch_data\",\n scorm12_errors.READ_ONLY_ELEMENT as number\n );\n } else {\n this._launch_data = launch_data;\n }\n }\n\n /**\n * Getter for _comments\n * @return {string}\n */\n get comments(): string {\n return this._comments;\n }\n\n /**\n * Setter for _comments\n * @param {string} comments\n */\n set comments(comments: string) {\n if (\n check12ValidFormat(\n this._cmi_element + \".comments\",\n comments,\n scorm12_regex.CMIString4096,\n true\n )\n ) {\n this._comments = comments;\n }\n }\n\n /**\n * Getter for _comments_from_lms\n * @return {string}\n */\n get comments_from_lms(): string {\n return this._comments_from_lms;\n }\n\n /**\n * Setter for _comments_from_lms. Can only be called before initialization.\n * @param {string} comments_from_lms\n */\n set comments_from_lms(comments_from_lms: string) {\n if (this.initialized) {\n throw new Scorm12ValidationError(\n this._cmi_element + \".comments_from_lms\",\n scorm12_errors.READ_ONLY_ELEMENT as number\n );\n } else {\n this._comments_from_lms = comments_from_lms;\n }\n }\n\n /**\n * Adds the current session time to the existing total time.\n *\n * @return {string}\n */\n getCurrentTotalTime(): string {\n return this.core.getCurrentTotalTime(this.start_time);\n }\n}\n","import { BaseCMI } from \"../common/base_cmi\";\nimport { check12ValidFormat } from \"./validation\";\nimport { scorm12_regex } from \"../../constants\";\n\n/**\n * Class for SCORM 1.2 Navigation object\n *\n * @spec NON-STANDARD EXTENSION - cmi.nav is not part of the official SCORM 1.2 specification.\n * This is a common extension supported by some LMS implementations to provide\n * navigation control. It is not guaranteed to work across all SCORM 1.2 platforms.\n */\nexport class NAV extends BaseCMI {\n /**\n * Constructor for NAV object\n */\n constructor() {\n super(\"cmi.nav\");\n }\n\n /**\n * Called when the API has been reset\n *\n * This method is invoked during the following session lifecycle events:\n * - When the API is reset via LMSFinish() followed by a new LMSInitialize()\n * - Between SCO transitions in multi-SCO courses (when one SCO ends and another begins)\n * - When the LMS explicitly resets the API instance\n * - During API cleanup and reinitialization cycles\n *\n * Resets all navigation state to prepare for a new session.\n */\n reset(): void {\n this._event = \"\";\n this._initialized = false;\n }\n\n private _event = \"\";\n\n /**\n * Getter for _event\n * @return {string}\n */\n get event(): string {\n return this._event;\n }\n\n /**\n * Setter for _event\n * @param {string} event\n */\n set event(event: string) {\n if (\n event === \"\" ||\n check12ValidFormat(this._cmi_element + \".event\", event, scorm12_regex.NAVEvent)\n ) {\n this._event = event;\n }\n }\n\n /**\n * toJSON for nav object\n * @return {\n * {\n * event: string\n * }\n * }\n */\n toJSON(): {\n event: string;\n } {\n this.jsonString = true;\n const result = {\n event: this.event,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import BaseAPI from \"./BaseAPI\";\nimport { flatten, getTimeAsSeconds, StringKeyMap, stringMatches } from \"./utilities\";\nimport { BaseCMI } from \"./cmi/common/base_cmi\";\nimport { CMI } from \"./cmi/scorm12/cmi\";\nimport { CMIObjectivesObject } from \"./cmi/scorm12/objectives\";\nimport {\n CMIInteractionsCorrectResponsesObject,\n CMIInteractionsObject,\n CMIInteractionsObjectivesObject,\n} from \"./cmi/scorm12/interactions\";\nimport { NAV } from \"./cmi/scorm12/nav\";\nimport {\n CompletionStatus,\n global_constants,\n scorm12_constants,\n scorm12_errors,\n scorm12_regex,\n SuccessStatus,\n} from \"./constants\";\nimport { CommitObject, ResultObject, ScoreObject, Settings } from \"./types\";\nimport { IHttpService } from \"./interfaces\";\n\n/**\n * API class for SCORM 1.2\n */\nclass Scorm12API extends BaseAPI {\n /**\n * Static global storage for learner preferences\n * When globalStudentPreferences is enabled, preferences persist across SCO instances\n * @private\n */\n private static _globalLearnerPrefs: {\n audio: string;\n language: string;\n speed: string;\n text: string;\n } | null = null;\n\n /**\n * Clear the global learner preferences storage\n * @public\n */\n public static clearGlobalPreferences(): void {\n Scorm12API._globalLearnerPrefs = null;\n }\n\n /**\n * Constructor for SCORM 1.2 API\n * @param {object} settings\n * @param {IHttpService} httpService - Optional HTTP service instance\n */\n constructor(settings?: Settings, httpService?: IHttpService) {\n const settingsCopy = settings ? { ...settings } : undefined;\n if (settingsCopy) {\n // Per SCORM 1.2 spec, LMS may override lesson_status based on mastery score\n // Default to true for spec compliance; set to false for conservative behavior\n if (settingsCopy.mastery_override === undefined) {\n settingsCopy.mastery_override = true;\n }\n }\n\n super(scorm12_errors, settingsCopy, httpService);\n\n this.cmi = new CMI();\n this.nav = new NAV();\n\n // Initialize preferences from global storage if enabled\n // Only set non-empty values to avoid validation errors\n if (this.settings.globalStudentPreferences && Scorm12API._globalLearnerPrefs) {\n if (Scorm12API._globalLearnerPrefs.audio !== \"\") {\n this.cmi.student_preference.audio = Scorm12API._globalLearnerPrefs.audio;\n }\n if (Scorm12API._globalLearnerPrefs.language !== \"\") {\n this.cmi.student_preference.language = Scorm12API._globalLearnerPrefs.language;\n }\n if (Scorm12API._globalLearnerPrefs.speed !== \"\") {\n this.cmi.student_preference.speed = Scorm12API._globalLearnerPrefs.speed;\n }\n if (Scorm12API._globalLearnerPrefs.text !== \"\") {\n this.cmi.student_preference.text = Scorm12API._globalLearnerPrefs.text;\n }\n }\n\n // Rename functions to match 1.2 Spec and expose to modules\n this.LMSInitialize = this.lmsInitialize;\n this.LMSFinish = this.lmsFinish;\n this.LMSGetValue = this.lmsGetValue;\n this.LMSSetValue = this.lmsSetValue;\n this.LMSCommit = this.lmsCommit;\n this.LMSGetLastError = this.lmsGetLastError;\n this.LMSGetErrorString = this.lmsGetErrorString;\n this.LMSGetDiagnostic = this.lmsGetDiagnostic;\n }\n\n public statusSetByModule = false;\n\n cmi: CMI;\n nav: NAV;\n\n LMSInitialize: (parameter?: string) => string;\n LMSFinish: (parameter?: string) => string;\n LMSGetValue: (CMIElement: string) => string;\n LMSSetValue: (CMIElement: string, value: any) => string;\n LMSCommit: (parameter?: string) => string;\n LMSGetLastError: () => string;\n LMSGetErrorString: (CMIErrorCode: string) => string;\n LMSGetDiagnostic: (CMIErrorCode: string) => string;\n\n /**\n * Called when the API needs to be reset\n */\n reset(settings?: Settings) {\n this.commonReset(settings);\n\n this.cmi?.reset();\n this.nav?.reset();\n this.statusSetByModule = false;\n }\n\n /**\n * LMSInitialize - Begins a communication session with the LMS\n *\n * Per SCORM 1.2 RTE Section 3.4.3.1:\n * - Parameter must be empty string (\"\")\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 101 if already initialized\n * - Sets error 101 if already terminated\n * - Initializes cmi.core.lesson_status to \"not attempted\" if not already set\n *\n * @param {string} parameter - Must be an empty string per SCORM 1.2 specification\n * @return {string} \"true\" or \"false\"\n */\n lmsInitialize(parameter: string = \"\"): string {\n // SCORM 1.2 RTE 3.4.3.1: Parameter must be an empty string\n if (parameter !== \"\") {\n this.throwSCORMError(\"api\", this._error_codes.ARGUMENT_ERROR);\n return global_constants.SCORM_FALSE;\n }\n\n this.cmi.initialize();\n if (this.cmi.core.lesson_status) {\n this.statusSetByModule = true;\n } else {\n this.cmi.core.lesson_status = \"not attempted\";\n }\n return this.initialize(\n \"LMSInitialize\",\n \"LMS was already initialized!\",\n \"LMS is already finished!\",\n );\n }\n\n /**\n * LMSFinish - Ends the communication session and persists data\n *\n * Per SCORM 1.2 RTE Section 3.4.3.2:\n * - Parameter must be empty string (\"\")\n * - Returns \"true\" on success, \"false\" on failure\n * - Commits all data to persistent storage\n * - Sets error 101 if not initialized\n * - Sets error 101 if already terminated\n * - Processes navigation events (continue/previous) if nav.event is set\n *\n * @param {string} parameter - Must be an empty string per SCORM 1.2 specification\n * @return {string} \"true\" or \"false\"\n */\n lmsFinish(parameter: string = \"\"): string {\n // SCORM 1.2 RTE 3.4.3.2: Parameter must be an empty string\n if (parameter !== \"\") {\n this.throwSCORMError(\"api\", this._error_codes.ARGUMENT_ERROR);\n return global_constants.SCORM_FALSE;\n }\n\n const result = this.terminate(\"LMSFinish\", true);\n\n if (result === global_constants.SCORM_TRUE) {\n if (this.nav.event !== \"\") {\n if (this.nav.event === \"continue\") {\n this.processListeners(\"SequenceNext\");\n } else {\n this.processListeners(\"SequencePrevious\");\n }\n } else if (this.settings.autoProgress) {\n this.processListeners(\"SequenceNext\");\n }\n }\n\n return result;\n }\n\n /**\n * LMSGetValue - Retrieves a value from the CMI data model\n *\n * Per SCORM 1.2 RTE Section 3.4.3.3:\n * - Returns the value of the specified CMI element\n * - Returns empty string if element has no value\n * - Sets error 101 if not initialized\n * - Sets error 301 if element is not implemented (invalid element)\n * - Sets error 201 if element is write-only\n * - Sets error 202 if element is not initialized\n *\n * @param {string} CMIElement - The CMI element path (e.g., \"cmi.core.score.raw\")\n * @return {string} The value of the element, or empty string\n */\n lmsGetValue(CMIElement: string): string {\n return this.getValue(\"LMSGetValue\", false, CMIElement);\n }\n\n /**\n * LMSSetValue - Sets a value in the CMI data model\n *\n * Per SCORM 1.2 RTE Section 3.4.3.4:\n * - Sets the value of the specified CMI element\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 101 if not initialized\n * - Sets error 301 if element is not implemented (invalid element)\n * - Sets error 351 if element exceeds maximum length\n * - Sets error 201 if element is read-only\n * - Sets error 405 if incorrect data type\n * - Triggers autocommit if enabled\n *\n * @param {string} CMIElement - The CMI element path (e.g., \"cmi.core.lesson_status\")\n * @param {any} value - The value to set\n * @return {string} \"true\" or \"false\"\n */\n lmsSetValue(CMIElement: string, value: any): string {\n if (CMIElement === \"cmi.core.lesson_status\") {\n this.statusSetByModule = true;\n }\n return this.setValue(\"LMSSetValue\", \"LMSCommit\", false, CMIElement, value);\n }\n\n /**\n * LMSCommit - Requests immediate persistence of data to the LMS\n *\n * Per SCORM 1.2 RTE Section 3.4.4.1:\n * - Parameter must be empty string (\"\")\n * - Requests persistence of all data set since last successful commit\n * - Returns \"true\" on success, \"false\" on failure\n * - Sets error 101 if not initialized\n * - Sets error 391 if commit failed\n * - Does not terminate the communication session\n *\n * @param {string} parameter - Must be an empty string per SCORM 1.2 specification\n * @return {string} \"true\" or \"false\"\n */\n lmsCommit(parameter: string = \"\"): string {\n // SCORM 1.2 RTE 3.4.4.1: Parameter must be an empty string\n if (parameter !== \"\") {\n this.throwSCORMError(\"api\", this._error_codes.ARGUMENT_ERROR);\n return global_constants.SCORM_FALSE;\n }\n\n if (this.settings.throttleCommits) {\n this.scheduleCommit(500, \"LMSCommit\");\n return global_constants.SCORM_TRUE;\n } else {\n return this.commit(\"LMSCommit\", false);\n }\n }\n\n /**\n * LMSGetLastError - Returns the error code from the last API call\n *\n * Per SCORM 1.2 RTE Section 3.4.4.2:\n * - Returns the error code that resulted from the last API call\n * - Returns \"0\" if no error occurred\n * - Can be called at any time (even before LMSInitialize)\n * - Does not change the current error state\n * - Should be called after each API call to check for errors\n *\n * @return {string} Error code as a string (e.g., \"0\", \"101\", \"301\")\n */\n lmsGetLastError(): string {\n return this.getLastError(\"LMSGetLastError\");\n }\n\n /**\n * LMSGetErrorString - Returns a short description for an error code\n *\n * Per SCORM 1.2 RTE Section 3.4.4.3:\n * - Returns a textual description for the specified error code\n * - Returns empty string if error code is not recognized\n * - Can be called at any time (even before LMSInitialize)\n * - Does not change the current error state\n * - Used to provide user-friendly error messages\n *\n * @param {string} CMIErrorCode - The error code to get the description for\n * @return {string} Short error description\n */\n lmsGetErrorString(CMIErrorCode: string): string {\n return this.getErrorString(\"LMSGetErrorString\", CMIErrorCode);\n }\n\n /**\n * LMSGetDiagnostic - Returns detailed diagnostic information for an error\n *\n * Per SCORM 1.2 RTE Section 3.4.4.4:\n * - Returns detailed diagnostic information for the specified error code\n * - Implementation-specific; can include additional context or debugging info\n * - Returns empty string if no diagnostic information is available\n * - Can be called at any time (even before LMSInitialize)\n * - Does not change the current error state\n * - Used for debugging and troubleshooting\n *\n * @param {string} CMIErrorCode - The error code to get diagnostic information for\n * @return {string} Detailed diagnostic information\n */\n lmsGetDiagnostic(CMIErrorCode: string): string {\n return this.getDiagnostic(\"LMSGetDiagnostic\", CMIErrorCode);\n }\n\n /**\n * Sets a value on the CMI Object\n *\n * @param {string} CMIElement\n * @param {*} value\n * @return {string}\n */\n override setCMIValue(CMIElement: string, value: any): string {\n const result = this._commonSetCMIValue(\"LMSSetValue\", false, CMIElement, value);\n\n // Update global learner preferences if enabled and the set succeeded\n if (result === global_constants.SCORM_TRUE && this.settings.globalStudentPreferences) {\n if (CMIElement === \"cmi.student_preference.audio\") {\n this._updateGlobalPreference(\"audio\", value);\n } else if (CMIElement === \"cmi.student_preference.language\") {\n this._updateGlobalPreference(\"language\", value);\n } else if (CMIElement === \"cmi.student_preference.speed\") {\n this._updateGlobalPreference(\"speed\", value);\n } else if (CMIElement === \"cmi.student_preference.text\") {\n this._updateGlobalPreference(\"text\", value);\n }\n }\n\n return result;\n }\n\n /**\n * Updates a specific field in the global learner preferences storage\n * @param {string} field - The preference field to update\n * @param {string} value - The value to set\n * @private\n */\n private _updateGlobalPreference(\n field: \"audio\" | \"language\" | \"speed\" | \"text\",\n value: string,\n ): void {\n if (!Scorm12API._globalLearnerPrefs) {\n Scorm12API._globalLearnerPrefs = { audio: \"\", language: \"\", speed: \"\", text: \"\" };\n }\n Scorm12API._globalLearnerPrefs[field] = value;\n }\n\n /**\n * Gets a value from the CMI Object\n *\n * @param {string} CMIElement\n * @return {*}\n */\n override getCMIValue(CMIElement: string): any {\n return this._commonGetCMIValue(\"getCMIValue\", false, CMIElement);\n }\n\n /**\n * Gets or builds a new child element to add to the array.\n *\n * @param {string} CMIElement\n * @param {*} _value\n * @param {boolean} foundFirstIndex\n * @return {BaseCMI|null}\n */\n getChildElement(CMIElement: string, _value: any, foundFirstIndex: boolean): BaseCMI | null {\n if (stringMatches(CMIElement, \"cmi\\\\.objectives\\\\.\\\\d+\")) {\n return new CMIObjectivesObject();\n } else if (\n foundFirstIndex &&\n stringMatches(CMIElement, \"cmi\\\\.interactions\\\\.\\\\d+\\\\.correct_responses\\\\.\\\\d+\")\n ) {\n return new CMIInteractionsCorrectResponsesObject();\n } else if (\n foundFirstIndex &&\n stringMatches(CMIElement, \"cmi\\\\.interactions\\\\.\\\\d+\\\\.objectives\\\\.\\\\d+\")\n ) {\n return new CMIInteractionsObjectivesObject();\n } else if (!foundFirstIndex && stringMatches(CMIElement, \"cmi\\\\.interactions\\\\.\\\\d+\")) {\n return new CMIInteractionsObject();\n }\n\n return null;\n }\n\n /**\n * Validates Correct Response values\n *\n * @param {string} _CMIElement\n * @param {*} _value\n */\n validateCorrectResponse(_CMIElement: string, _value: any) {\n // do nothing\n }\n\n /**\n * Returns the message that corresponds to errorNumber.\n *\n * @param {number|string} errorNumber\n * @param {boolean} detail\n * @return {string}\n */\n override getLmsErrorMessageDetails(errorNumber: number | string, detail: boolean): string {\n let basicMessage = \"No Error\";\n let detailMessage = \"No Error\";\n\n // Set error number to string since inconsistent from modules if string or number\n errorNumber = String(errorNumber);\n if (scorm12_constants.error_descriptions[errorNumber]) {\n basicMessage =\n scorm12_constants.error_descriptions[errorNumber]?.basicMessage || basicMessage;\n detailMessage =\n scorm12_constants.error_descriptions[errorNumber]?.detailMessage || detailMessage;\n }\n\n return detail ? detailMessage : basicMessage;\n }\n\n /**\n * Replace the whole API with another\n *\n * @param {Scorm12API} newAPI\n */\n replaceWithAnotherScormAPI(newAPI: Scorm12API) {\n // Data Model\n this.cmi = newAPI.cmi;\n }\n\n /**\n * Render the cmi object to the proper format for LMS commit\n *\n * @param {boolean} terminateCommit - Whether this is a termination commit\n * @param {boolean} includeTotalTime - Whether to include total time in the commit data\n * @return {object|Array}\n */\n renderCommitCMI(\n terminateCommit: boolean,\n includeTotalTime: boolean = false,\n ): StringKeyMap | Array<string> {\n const cmiExport: StringKeyMap = this.renderCMIToJSONObject();\n\n if (terminateCommit || includeTotalTime) {\n ((cmiExport.cmi as StringKeyMap).core as StringKeyMap).total_time =\n this.cmi.getCurrentTotalTime();\n }\n\n const result = [];\n const flattened: StringKeyMap = flatten(cmiExport);\n switch (this.settings.dataCommitFormat) {\n case \"flattened\":\n return flattened;\n case \"params\":\n for (const item in flattened) {\n if ({}.hasOwnProperty.call(flattened, item)) {\n result.push(`${item}=${flattened[item]}`);\n }\n }\n return result;\n case \"json\":\n default:\n return cmiExport;\n }\n }\n\n /**\n * Render the cmi object to the proper format for LMS commit\n * @param {boolean} terminateCommit - Whether this is a termination commit\n * @param {boolean} includeTotalTime - Whether to include total time in the commit data\n * @return {CommitObject}\n */\n renderCommitObject(terminateCommit: boolean, includeTotalTime: boolean = false): CommitObject {\n const cmiExport = this.renderCommitCMI(terminateCommit, includeTotalTime);\n const calculateTotalTime = terminateCommit || includeTotalTime;\n const totalTimeHHMMSS = calculateTotalTime ? this.cmi.getCurrentTotalTime() : \"\";\n const totalTimeSeconds = getTimeAsSeconds(totalTimeHHMMSS, scorm12_regex.CMITimespan);\n const lessonStatus = this.cmi.core.lesson_status;\n let completionStatus = CompletionStatus.UNKNOWN;\n let successStatus = SuccessStatus.UNKNOWN;\n if (lessonStatus) {\n completionStatus =\n lessonStatus === \"completed\" || lessonStatus === \"passed\"\n ? CompletionStatus.COMPLETED\n : CompletionStatus.INCOMPLETE;\n if (lessonStatus === \"passed\") {\n successStatus = SuccessStatus.PASSED;\n } else if (lessonStatus === \"failed\") {\n successStatus = SuccessStatus.FAILED;\n }\n }\n\n const scoreObject: ScoreObject = this.cmi?.core?.score?.getScoreObject() || {};\n const commitObject: CommitObject = {\n successStatus: successStatus,\n completionStatus: completionStatus,\n runtimeData: cmiExport as StringKeyMap,\n totalTimeSeconds: totalTimeSeconds,\n };\n if (scoreObject) {\n commitObject.score = scoreObject;\n }\n\n // Populate metadata if enabled\n if (this.settings.autoPopulateCommitMetadata) {\n if (this.settings.courseId) {\n commitObject.courseId = this.settings.courseId;\n }\n if (this.settings.scoId) {\n commitObject.scoId = this.settings.scoId;\n }\n if (this.cmi.core.student_id) {\n commitObject.learnerId = this.cmi.core.student_id;\n }\n if (this.cmi.core.student_name) {\n commitObject.learnerName = this.cmi.core.student_name;\n }\n }\n\n return commitObject;\n }\n\n /**\n * Attempts to store the data to the LMS\n *\n * @param {boolean} terminateCommit\n * @return {ResultObject}\n */\n storeData(terminateCommit: boolean): ResultObject {\n if (terminateCommit) {\n const originalStatus = this.cmi.core.lesson_status;\n\n // Check browse mode FIRST (before Stage 1)\n if (this.cmi.core.lesson_mode === \"browse\") {\n const startingStatus =\n ((this.startingData?.cmi as StringKeyMap)?.core as StringKeyMap)?.lesson_status || \"\";\n if (startingStatus === \"\" && originalStatus === \"not attempted\") {\n this.cmi.core.lesson_status = \"browsed\";\n // Skip Stage 1 and Stage 2 for browse mode\n return this.processCommitData(terminateCommit);\n }\n }\n\n // Stage 1: Apply mastery when status is unset/not-attempted\n if (\n !this.cmi.core.lesson_status ||\n (!this.statusSetByModule && this.cmi.core.lesson_status === \"not attempted\")\n ) {\n this.cmi.core.lesson_status = this.settings.autoCompleteLessonStatus\n ? \"completed\"\n : \"incomplete\";\n }\n\n if (this.cmi.core.lesson_mode === \"normal\") {\n if (this.cmi.core.credit === \"credit\") {\n if (\n this.settings.mastery_override &&\n this.cmi.student_data.mastery_score !== \"\" &&\n this.cmi.core.score.raw !== \"\"\n ) {\n const rawScore = parseFloat(this.cmi.core.score.raw);\n const masteryScore = parseFloat(this.cmi.student_data.mastery_score);\n if (!isNaN(rawScore) && !isNaN(masteryScore)) {\n this.cmi.core.lesson_status = rawScore >= masteryScore ? \"passed\" : \"failed\";\n }\n }\n }\n }\n\n // Stage 2: Override SCO-set status if score_overrides_status is enabled\n if (\n this.settings.score_overrides_status &&\n this.statusSetByModule &&\n this.cmi.core.lesson_mode === \"normal\" &&\n this.cmi.core.credit === \"credit\" &&\n this.cmi.student_data.mastery_score !== \"\" &&\n this.cmi.core.score.raw !== \"\"\n ) {\n const rawScore = parseFloat(this.cmi.core.score.raw);\n const masteryScore = parseFloat(this.cmi.student_data.mastery_score);\n if (!isNaN(rawScore) && !isNaN(masteryScore)) {\n if (rawScore >= masteryScore) {\n this.cmi.core.lesson_status = \"passed\";\n } else {\n this.cmi.core.lesson_status = \"failed\";\n }\n }\n }\n }\n\n return this.processCommitData(terminateCommit);\n }\n\n private processCommitData(terminateCommit: boolean): ResultObject {\n const commitObject = this.getCommitObject(terminateCommit);\n if (typeof this.settings.lmsCommitUrl === \"string\") {\n return this.processHttpRequest(this.settings.lmsCommitUrl, commitObject, terminateCommit);\n } else {\n return {\n result: global_constants.SCORM_TRUE,\n errorCode: 0,\n };\n }\n }\n}\n\nexport default Scorm12API;\n","/**\n * Class for SCORM 2004's cmi.learner_preference object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { check2004ValidFormat, check2004ValidRange } from \"./validation\";\nimport {\n scorm2004_constants,\n scorm2004_errors,\n scorm2004_regex,\n} from \"../../constants\";\n\nexport class CMILearnerPreference extends BaseCMI {\n private __children = scorm2004_constants.student_preference_children;\n private _audio_level = \"1\";\n private _language = \"\";\n private _delivery_speed = \"1\";\n private _audio_captioning = \"0\";\n\n /**\n * Constructor for cmi.learner_preference\n */\n constructor() {\n super(\"cmi.learner_preference\");\n }\n\n /**\n * Called when the API has been reset\n */\n override reset() {\n this._initialized = false;\n }\n\n /**\n * Getter for __children\n * @return {string}\n * @private\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for __children. Just throws an error.\n * @param {string} _children\n * @private\n */\n set _children(_children: string) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \"._children\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n\n /**\n * Getter for _audio_level\n * @return {string}\n */\n get audio_level(): string {\n return this._audio_level;\n }\n\n /**\n * Setter for _audio_level\n * @param {string} audio_level\n */\n set audio_level(audio_level: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".audio_level\",\n audio_level,\n scorm2004_regex.CMIDecimal,\n ) &&\n check2004ValidRange(\n this._cmi_element + \".audio_level\",\n audio_level,\n scorm2004_regex.audio_range,\n )\n ) {\n this._audio_level = audio_level;\n }\n }\n\n /**\n * Getter for _language\n * @return {string}\n */\n get language(): string {\n return this._language;\n }\n\n /**\n * Setter for _language\n * @param {string} language\n */\n set language(language: string) {\n if (check2004ValidFormat(this._cmi_element + \".language\", language, scorm2004_regex.CMILang)) {\n this._language = language;\n }\n }\n\n /**\n * Getter for _delivery_speed\n * @return {string}\n */\n get delivery_speed(): string {\n return this._delivery_speed;\n }\n\n /**\n * Setter for _delivery_speed\n * @param {string} delivery_speed\n */\n set delivery_speed(delivery_speed: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".delivery_speed\",\n delivery_speed,\n scorm2004_regex.CMIDecimal,\n ) &&\n check2004ValidRange(\n this._cmi_element + \".delivery_speed\",\n delivery_speed,\n scorm2004_regex.speed_range,\n )\n ) {\n // SCORM 2004 spec requires delivery_speed > 0 (not >= 0)\n if (parseFloat(delivery_speed) === 0) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".delivery_speed\",\n scorm2004_errors.VALUE_OUT_OF_RANGE as number,\n );\n }\n this._delivery_speed = delivery_speed;\n }\n }\n\n /**\n * Getter for _audio_captioning\n * @return {string}\n */\n get audio_captioning(): string {\n return this._audio_captioning;\n }\n\n /**\n * Setter for _audio_captioning\n * @param {string} audio_captioning\n */\n set audio_captioning(audio_captioning: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".audio_captioning\",\n audio_captioning,\n scorm2004_regex.CMISInteger,\n ) &&\n check2004ValidRange(\n this._cmi_element + \".audio_captioning\",\n audio_captioning,\n scorm2004_regex.text_range,\n )\n ) {\n this._audio_captioning = audio_captioning;\n }\n }\n\n /**\n * toJSON for cmi.learner_preference\n *\n * @return {\n * {\n * audio_level: string,\n * language: string,\n * delivery_speed: string,\n * audio_captioning: string\n * }\n * }\n */\n toJSON(): {\n audio_level: string;\n language: string;\n delivery_speed: string;\n audio_captioning: string;\n } {\n this.jsonString = true;\n const result = {\n audio_level: this.audio_level,\n language: this.language,\n delivery_speed: this.delivery_speed,\n audio_captioning: this.audio_captioning,\n };\n this.jsonString = false;\n return result;\n }\n}\n","/**\n * Class representing SCORM 2004's `cmi.interactions` object\n *\n * Per SCORM 2004 RTE Section 4.1.6:\n * - Enhanced interaction tracking vs SCORM 1.2\n * - Supports additional interaction types (long-fill-in, other)\n * - learner_response replaces student_response\n * - timestamp: ISO 8601 format for precise timing\n * - description: Textual description with language support\n * - Stricter validation of response formats by type\n * - Correct response patterns support complex formats\n *\n * @spec RTE 4.2.9 - cmi.interactions\n * @extends CMIArray\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { CMIArray } from \"../common/array\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { check2004ValidFormat } from \"./validation\";\nimport {\n CorrectResponses,\n LearnerResponses,\n ResponseType,\n scorm2004_constants,\n scorm2004_errors,\n scorm2004_regex,\n} from \"../../constants\";\n\nexport class CMIInteractions extends CMIArray {\n /**\n * Constructor for `cmi.interactions` Array\n *\n * Per SCORM 2004 RTE Section 4.1.6:\n * - Read-only array structure (add via index access)\n * - Each interaction has enhanced metadata and validation\n */\n constructor() {\n super({\n CMIElement: \"cmi.interactions\",\n children: scorm2004_constants.interactions_children,\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n });\n }\n}\n\n/**\n * Class for SCORM 2004's cmi.interaction.n object\n *\n * Per SCORM 2004 RTE Section 4.1.6:\n * - id: Long identifier (up to 4000 chars, supports URN format)\n * - type: Interaction type (true-false, choice, fill-in, long-fill-in, matching, performance, sequencing, likert, numeric, other)\n * - timestamp: ISO 8601 date/time when interaction occurred\n * - weighting: Decimal weight for scoring\n * - learner_response: Response format validated against interaction type\n * - result: correct, incorrect, unanticipated, neutral, or numeric score\n * - latency: ISO 8601 duration format\n * - description: LangString250 with optional language tag\n * - Dependency: id must be set before other elements (except objectives/correct_responses)\n *\n * @spec RTE 4.2.9.1 - cmi.interactions.n.id\n * @spec RTE 4.2.9.2 - cmi.interactions.n.type\n * @spec RTE 4.2.9.3 - cmi.interactions.n.objectives\n * @spec RTE 4.2.9.4 - cmi.interactions.n.timestamp\n * @spec RTE 4.2.9.5 - cmi.interactions.n.correct_responses\n * @spec RTE 4.2.9.6 - cmi.interactions.n.weighting\n * @spec RTE 4.2.9.7 - cmi.interactions.n.learner_response\n * @spec RTE 4.2.9.8 - cmi.interactions.n.result\n * @spec RTE 4.2.9.9 - cmi.interactions.n.latency\n * @spec RTE 4.2.9.10 - cmi.interactions.n.description\n * @extends BaseCMI\n */\n\nexport class CMIInteractionsObject extends BaseCMI {\n private _id = \"\";\n private _idIsSet = false;\n private _type = \"\";\n private _timestamp = \"\";\n private _weighting = \"\";\n private _learner_response = \"\";\n private _result = \"\";\n private _latency = \"\";\n private _description = \"\";\n\n /**\n * Constructor for cmi.interaction.n\n */\n constructor() {\n super(\"cmi.interactions.n\");\n this.objectives = new CMIArray({\n CMIElement: \"cmi.interactions.n.objectives\",\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n children: scorm2004_constants.objectives_children,\n });\n this.correct_responses = new CMIArray({\n CMIElement: \"cmi.interactions.n.correct_responses\",\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n children: scorm2004_constants.correct_responses_children,\n });\n }\n\n public objectives: CMIArray;\n public correct_responses: CMIArray;\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this.objectives?.initialize();\n this.correct_responses?.initialize();\n }\n\n /**\n * Called when the API has been reset\n */\n override reset() {\n this._initialized = false;\n this._id = \"\";\n this._idIsSet = false;\n this._type = \"\";\n this._timestamp = \"\";\n this._weighting = \"\";\n this._learner_response = \"\";\n this._result = \"\";\n this._latency = \"\";\n this._description = \"\";\n this.objectives = new CMIArray({\n CMIElement: \"cmi.interactions.n.objectives\",\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n children: scorm2004_constants.objectives_children,\n });\n this.correct_responses = new CMIArray({\n CMIElement: \"cmi.interactions.n.correct_responses\",\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n children: scorm2004_constants.correct_responses_children,\n });\n }\n\n /**\n * Getter for _id\n * @return {string}\n */\n get id(): string {\n return this._id;\n }\n\n /**\n * Setter for _id\n * Per SCORM 2004 RTE: identifier SHALL NOT be empty or contain only whitespace\n * Per SCORM 2004 RTE Section 4.1.6: Once set, an interaction ID is immutable (error 351)\n * @param {string} id\n */\n set id(id: string) {\n // Per spec: identifier cannot be empty or whitespace-only\n if (id === \"\" || id.trim() === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".id\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n // Per SCORM 2004 RTE: Once an interaction ID is set, it cannot be changed\n if (this._idIsSet && this._id !== id) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".id\",\n scorm2004_errors.GENERAL_SET_FAILURE as number,\n );\n }\n if (check2004ValidFormat(this._cmi_element + \".id\", id, scorm2004_regex.CMILongIdentifier)) {\n this._id = id;\n this._idIsSet = true;\n }\n }\n\n /**\n * Getter for _type\n * @return {string}\n */\n get type(): string {\n return this._type;\n }\n\n /**\n * Setter for _type\n * @param {string} type\n */\n set type(type: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".type\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (check2004ValidFormat(this._cmi_element + \".type\", type, scorm2004_regex.CMIType)) {\n this._type = type;\n }\n }\n }\n\n /**\n * Getter for _timestamp\n * @return {string}\n */\n get timestamp(): string {\n return this._timestamp;\n }\n\n /**\n * Setter for _timestamp\n * @param {string} timestamp\n */\n set timestamp(timestamp: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".timestamp\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(this._cmi_element + \".timestamp\", timestamp, scorm2004_regex.CMITime)\n ) {\n this._timestamp = timestamp;\n }\n }\n }\n\n /**\n * Getter for _weighting\n * @return {string}\n */\n get weighting(): string {\n return this._weighting;\n }\n\n /**\n * Setter for _weighting\n * @param {string} weighting\n */\n set weighting(weighting: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".weighting\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".weighting\",\n weighting,\n scorm2004_regex.CMIDecimal,\n )\n ) {\n this._weighting = weighting;\n }\n }\n }\n\n /**\n * Getter for _learner_response\n * @return {string}\n */\n get learner_response(): string {\n return this._learner_response;\n }\n\n /**\n * Setter for _learner_response. Does type validation to make sure response\n * matches SCORM 2004's spec\n * @param {string} learner_response\n */\n set learner_response(learner_response: string) {\n if (this.initialized && (this._type === \"\" || this._id === \"\")) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n let nodes = [];\n const response_type = LearnerResponses[this.type];\n\n if (response_type) {\n if (response_type?.delimiter) {\n // Convert regex-style delimiter to actual character\n const delimiter = response_type.delimiter === \"[,]\" ? \",\" : response_type.delimiter;\n nodes = learner_response.split(delimiter);\n } else {\n nodes[0] = learner_response;\n }\n\n if (nodes.length > 0 && nodes.length <= response_type.max) {\n const formatRegex = new RegExp(response_type.format);\n\n for (let i = 0; i < nodes.length; i++) {\n if (response_type?.delimiter2) {\n // Convert regex-style delimiter to actual character\n const delimiter2 =\n response_type.delimiter2 === \"[.]\" ? \".\" : response_type.delimiter2;\n const values = nodes[i]?.split(delimiter2);\n\n if (values?.length === 2) {\n // For performance type, both parts must be non-empty\n if (this.type === \"performance\" && (values[0] === \"\" || values[1] === \"\")) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n if (!values[0]?.match(formatRegex)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n } else {\n if (\n !response_type.format2 ||\n !values[1]?.match(new RegExp(response_type.format2))\n ) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n }\n } else {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n } else {\n if (!nodes[i]?.match(formatRegex)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n } else {\n if (nodes[i] !== \"\" && response_type.unique) {\n for (let j = 0; j < i; j++) {\n if (nodes[i] === nodes[j]) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n }\n }\n }\n }\n }\n } else {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.GENERAL_SET_FAILURE as number,\n );\n }\n\n this._learner_response = learner_response;\n } else {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_response\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n }\n }\n\n /**\n * Getter for _result\n * @return {string}\n */\n get result(): string {\n return this._result;\n }\n\n /**\n * Setter for _result\n * @param {string} result\n */\n set result(result: string) {\n if (check2004ValidFormat(this._cmi_element + \".result\", result, scorm2004_regex.CMIResult)) {\n this._result = result;\n }\n }\n\n /**\n * Getter for _latency\n * @return {string}\n */\n get latency(): string {\n return this._latency;\n }\n\n /**\n * Setter for _latency\n * @param {string} latency\n */\n set latency(latency: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".latency\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(this._cmi_element + \".latency\", latency, scorm2004_regex.CMITimespan)\n ) {\n this._latency = latency;\n }\n }\n }\n\n /**\n * Getter for _description\n * @return {string}\n */\n get description(): string {\n return this._description;\n }\n\n /**\n * Setter for _description\n * @param {string} description\n */\n set description(description: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".description\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".description\",\n description,\n scorm2004_regex.CMILangString250,\n true,\n )\n ) {\n this._description = description;\n }\n }\n }\n\n // noinspection JSUnusedGlobalSymbols\n /**\n * toJSON for cmi.interactions.n\n *\n * @return {\n * {\n * id: string,\n * type: string,\n * objectives: CMIArray,\n * timestamp: string,\n * correct_responses: CMIArray,\n * weighting: string,\n * learner_response: string,\n * result: string,\n * latency: string,\n * description: string\n * }\n * }\n */\n toJSON(): {\n id: string;\n type: string;\n objectives: CMIArray;\n timestamp: string;\n correct_responses: CMIArray;\n weighting: string;\n learner_response: string;\n result: string;\n latency: string;\n description: string;\n } {\n this.jsonString = true;\n const result = {\n id: this.id,\n type: this.type,\n objectives: this.objectives,\n timestamp: this.timestamp,\n weighting: this.weighting,\n learner_response: this.learner_response,\n result: this.result,\n latency: this.latency,\n description: this.description,\n correct_responses: this.correct_responses,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing SCORM 2004's cmi.interactions.n.objectives.n object\n *\n * Per SCORM 2004 RTE Section 4.1.6.1:\n * - Associates interaction with objectives\n * - id: Long identifier matching objective in cmi.objectives\n * - Used to relate interaction performance to learning objectives\n *\n * @extends BaseCMI\n */\nexport class CMIInteractionsObjectivesObject extends BaseCMI {\n private _id = \"\";\n\n /**\n * Constructor for cmi.interactions.n.objectives.n\n */\n constructor() {\n super(\"cmi.interactions.n.objectives.n\");\n }\n\n /**\n * Called when the API has been reset\n */\n override reset() {\n this._initialized = false;\n this._id = \"\";\n }\n\n /**\n * Getter for _id\n * @return {string}\n */\n get id(): string {\n return this._id;\n }\n\n /**\n * Setter for _id\n * Per SCORM 2004 RTE: identifier SHALL NOT be empty or contain only whitespace\n * @param {string} id\n */\n set id(id: string) {\n // Per spec: identifier cannot be empty or whitespace-only\n if (id === \"\" || id.trim() === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".id\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n if (check2004ValidFormat(this._cmi_element + \".id\", id, scorm2004_regex.CMILongIdentifier)) {\n this._id = id;\n }\n }\n\n /**\n * toJSON for cmi.interactions.n.objectives.n\n * @return {\n * {\n * id: string\n * }\n * }\n */\n toJSON(): {\n id: string;\n } {\n this.jsonString = true;\n const result = {\n id: this.id,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Helper: strip the square-bracket notation (e.g. \"[,]\") down to the character (\",\")\n */\nfunction stripBrackets(delim: string): string {\n return delim.replace(/[[\\]]/g, \"\");\n}\n\n// Helper to escape a string for use in a RegExp\nfunction escapeRegex(s: string): string {\n // Only , and . are expected, but escape any regex special chars for safety\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Split on unescaped delimiter and unescape the delimiter in resulting parts.\n * @param text - the input string\n * @param delim - the delimiter character, e.g. ',' or '.'\n */\nfunction splitUnescaped(text: string, delim: string): string[] {\n const reDelim = escapeRegex(delim);\n const splitRe = new RegExp(`(?<!\\\\\\\\)${reDelim}`, \"g\");\n const unescapeRe = new RegExp(`\\\\\\\\${reDelim}`, \"g\");\n return text.split(splitRe).map((part) => part.replace(unescapeRe, delim));\n}\n\n/**\n * Split on the FIRST unescaped delimiter only and unescape the delimiter in resulting parts.\n * This is needed for patterns where the second part may contain literal delimiters (e.g., decimal numbers with dots).\n * @param text - the input string\n * @param delim - the delimiter character, e.g. ',' or '.'\n * @returns array with exactly 2 parts, or array with 1 part if no unescaped delimiter found\n */\nfunction splitFirstUnescaped(text: string, delim: string): string[] {\n const reDelim = escapeRegex(delim);\n const splitRe = new RegExp(`(?<!\\\\\\\\)${reDelim}`);\n const unescapeRe = new RegExp(`\\\\\\\\${reDelim}`, \"g\");\n const parts = text.split(splitRe);\n const firstPart = parts[0] ?? \"\";\n if (parts.length === 1) {\n return [firstPart.replace(unescapeRe, delim)];\n }\n // Join everything after the first split back together\n const part1 = firstPart.replace(unescapeRe, delim);\n const part2 = parts.slice(1).join(delim).replace(unescapeRe, delim);\n return [part1, part2];\n}\n\n/**\n * Helper: validate a `pattern` string against its SCORM definition\n */\nfunction validatePattern(type: string, pattern: string, responseDef: ResponseType) {\n // Reject patterns with leading or trailing whitespace\n if (pattern.trim() !== pattern) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n // Reject any nodes with leading/trailing whitespace around tokens\n const subDelim1 = responseDef.delimiter ? stripBrackets(responseDef.delimiter) : null;\n const rawNodes = subDelim1 ? splitUnescaped(pattern, subDelim1) : [pattern];\n for (const raw of rawNodes) {\n if (raw.trim() !== raw) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n }\n\n // Allow empty fill-in patterns\n if (type === \"fill-in\" && pattern === \"\") {\n return;\n }\n // Split into nodes on the primary delimiter (if any)\n const delim1 = responseDef.delimiter ? stripBrackets(responseDef.delimiter) : null;\n let nodes: string[];\n if (delim1) {\n nodes = splitUnescaped(pattern, delim1);\n } else {\n nodes = [pattern];\n }\n\n // If no primary delimiter but pattern contains comma, reject multiple entries\n if (!responseDef.delimiter && pattern.includes(\",\")) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n // Enforce uniqueness or disallow duplicates if required\n if (responseDef.unique || responseDef.duplicate === false) {\n const seen = new Set(nodes);\n if (seen.size !== nodes.length) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n }\n\n // Must have at least 1 node, and no more than max\n if (nodes.length === 0 || nodes.length > responseDef.max) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.GENERAL_SET_FAILURE as number,\n );\n }\n\n const fmt1 = new RegExp(responseDef.format);\n const fmt2 = responseDef.format2 ? new RegExp(responseDef.format2) : null;\n\n const checkSingle = (value: string) => {\n if (!fmt1.test(value)) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n };\n\n const checkPair = (value: string, delimBracketed?: string) => {\n if (!delimBracketed) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n const delim = stripBrackets(delimBracketed);\n const parts = value\n .split(new RegExp(`(?<!\\\\\\\\)${escapeRegex(delim)}`, \"g\"))\n .map((n) => n.replace(new RegExp(`\\\\\\\\${escapeRegex(delim)}`, \"g\"), delim));\n if (parts.length !== 2 || parts[0] === \"\" || parts[1] === \"\") {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n // test both parts\n if (\n (parts[0] !== undefined && !fmt1.test(parts[0])) ||\n (fmt2 && parts[1] !== undefined && !fmt2.test(parts[1]))\n ) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n };\n\n for (const node of nodes) {\n switch (type) {\n case \"numeric\": {\n // 1 or 2 numeric values separated by \":\"\n const numDelim = responseDef.delimiter ? stripBrackets(responseDef.delimiter) : \":\";\n const nums = node.split(numDelim);\n if (nums.length < 1 || nums.length > 2) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n nums.forEach(checkSingle);\n break;\n }\n\n case \"performance\": {\n // Performance pattern: step_name.step_answer\n // - step_name must match format (CMIShortIdentifier)\n // - step_answer can be:\n // 1. A CMIShortIdentifier (e.g., \"answer1\")\n // 2. A decimal number (e.g., \"3.14\") - contains literal dots!\n // 3. A numeric range (e.g., \"3.5:4.2\") - contains literal dots and colon!\n //\n // CRITICAL: Must split on FIRST unescaped dot only, because step_answer\n // may contain literal dots (for decimal numbers like \"3.14\")\n const delimBracketed = responseDef.delimiter2;\n if (!delimBracketed) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n const delim = stripBrackets(delimBracketed);\n\n // Split on the FIRST unescaped delimiter only\n const parts = splitFirstUnescaped(node, delim);\n\n // Must have exactly 2 parts (step_name and step_answer)\n if (parts.length !== 2) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n const [part1, part2] = parts;\n\n // Validate non-empty and not identical\n if (part1 === \"\" || part2 === \"\" || part1 === part2) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n // Validate part1 (step_name) against format (CMIShortIdentifier)\n if (part1 === undefined || !fmt1.test(part1)) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n // Validate part2 (step_answer) against format2\n // format2 allows: CMIShortIdentifier | decimal | decimal:decimal\n if (fmt2 && part2 !== undefined && !fmt2.test(part2)) {\n throw new Scorm2004ValidationError(\n \"cmi.interactions.n.correct_responses.n.pattern\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n break;\n }\n\n default:\n if (responseDef.delimiter2) {\n // matching and other two-part types\n checkPair(node, responseDef.delimiter2);\n } else {\n // simple single-value types (true-false, choice, fill-in, etc.)\n checkSingle(node);\n }\n }\n }\n}\n\n/**\n * Class representing SCORM 2004's cmi.interactions.n.correct_responses.n object\n *\n * Per SCORM 2004 RTE Section 4.1.6.2 and Appendix D:\n * - Defines correct response patterns for interactions\n * - pattern: Format depends on interaction type (see Appendix D)\n * - Validates pattern against type-specific rules\n * - Supports escaped delimiters in patterns\n * - Multiple correct responses allowed for some types\n * - Type-specific validation ensures data integrity\n *\n * @extends BaseCMI\n */\nexport class CMIInteractionsCorrectResponsesObject extends BaseCMI {\n private _pattern = \"\";\n private readonly _interactionType?: string | undefined;\n\n /**\n * Constructor for cmi.interactions.n.correct_responses.n\n * @param interactionType The type of interaction (e.g. \"numeric\", \"choice\", etc.)\n */\n constructor(interactionType?: string) {\n super(\"cmi.interactions.n.correct_responses.n\");\n this._interactionType = interactionType;\n }\n\n override reset() {\n this._initialized = false;\n this._pattern = \"\";\n }\n\n get pattern(): string {\n return this._pattern;\n }\n\n set pattern(pattern: string) {\n // Allow empty fill-in patterns\n if (this._interactionType === \"fill-in\" && pattern === \"\") {\n this._pattern = \"\";\n return;\n }\n // 1) Basic SCORM‐pattern format check\n if (\n !check2004ValidFormat(this._cmi_element + \".pattern\", pattern, scorm2004_regex.CMIFeedback)\n ) {\n return;\n }\n\n // 2) If we know the interaction type, run the detailed validator\n if (this._interactionType) {\n const responseDef = CorrectResponses[this._interactionType];\n if (responseDef) {\n // Skip detailed validation for matching when pattern contains escaped comma or dot\n if (this._interactionType === \"matching\" && /\\\\[.,]/.test(pattern)) {\n // accept escaped comma or dot patterns without further validation\n } else {\n validatePattern(this._interactionType, pattern, responseDef);\n }\n }\n }\n\n // 3) Finally, set\n this._pattern = pattern;\n }\n\n toJSON(): { pattern: string } {\n this.jsonString = true;\n const result = { pattern: this.pattern };\n this.jsonString = false;\n return result;\n }\n}\n","/**\n * Class for SCORM 2004's cmi *.score object\n */\nimport { CMIScore } from \"../common/score\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { check2004ValidFormat, check2004ValidRange } from \"./validation\";\nimport {\n scorm2004_constants,\n scorm2004_errors,\n scorm2004_regex,\n} from \"../../constants\";\nimport { ScoreObject } from \"../../types\";\n\nexport class Scorm2004CMIScore extends CMIScore {\n private _scaled = \"\";\n\n /**\n * Constructor for cmi *.score\n */\n constructor() {\n super({\n CMIElement: \"cmi.score\",\n score_children: scorm2004_constants.score_children,\n max: \"\",\n invalidErrorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n invalidTypeCode: scorm2004_errors.TYPE_MISMATCH as number,\n invalidRangeCode: scorm2004_errors.VALUE_OUT_OF_RANGE as number,\n decimalRegex: scorm2004_regex.CMIDecimal,\n errorClass: Scorm2004ValidationError,\n });\n }\n\n /**\n * Called when the API has been reset\n */\n override reset(): void {\n this._initialized = false;\n this._scaled = \"\";\n this._raw = \"\";\n this._min = \"\";\n this._max = \"\";\n }\n\n /**\n * Getter for _scaled\n * @return {string}\n */\n get scaled(): string {\n return this._scaled;\n }\n\n /**\n * Setter for _scaled\n * @param {string} scaled\n */\n set scaled(scaled: string) {\n if (\n check2004ValidFormat(this._cmi_element + \".scaled\", scaled, scorm2004_regex.CMIDecimal) &&\n check2004ValidRange(this._cmi_element + \".scaled\", scaled, scorm2004_regex.scaled_range)\n ) {\n this._scaled = scaled;\n }\n }\n\n override getScoreObject(): ScoreObject {\n const scoreObject = super.getScoreObject();\n\n if (!Number.isNaN(Number.parseFloat(this.scaled))) {\n scoreObject.scaled = Number.parseFloat(this.scaled);\n }\n\n return scoreObject;\n }\n\n /**\n * toJSON for cmi *.score\n *\n * @return {\n * {\n * scaled: string,\n * raw: string,\n * min: string,\n * max: string\n * }\n * }\n */\n override toJSON(): {\n scaled: string;\n raw: string;\n min: string;\n max: string;\n } {\n this.jsonString = true;\n const result = {\n scaled: this.scaled,\n raw: this.raw,\n min: this.min,\n max: this.max,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { CMIArray } from \"../common/array\";\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { check2004ValidFormat } from \"./validation\";\nimport {\n scorm2004_constants,\n scorm2004_errors,\n scorm2004_regex,\n} from \"../../constants\";\n\n/**\n * Class representing SCORM 2004's cmi.comments_from_lms object\n * @spec RTE 4.2.3 - cmi.comments_from_lms\n * @extends CMIArray\n */\nexport class CMICommentsFromLMS extends CMIArray {\n /**\n * Constructor for cmi.comments_from_lms Array\n */\n constructor() {\n super({\n CMIElement: \"cmi.comments_from_lms\",\n children: scorm2004_constants.comments_children,\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n });\n }\n}\n\n/**\n * Class representing SCORM 2004's cmi.comments_from_learner object\n * @spec RTE 4.2.2 - cmi.comments_from_learner\n */\n\nexport class CMICommentsFromLearner extends CMIArray {\n /**\n * Constructor for cmi.comments_from_learner Array\n */\n constructor() {\n super({\n CMIElement: \"cmi.comments_from_learner\",\n children: scorm2004_constants.comments_children,\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n });\n }\n}\n\n/**\n * Class representing SCORM 2004's cmi.comments_from_learner.n and cmi.comments_from_lms.n object\n */\n\nexport class CMICommentsObject extends BaseCMI {\n private _comment = \"\";\n private _location = \"\";\n private _timestamp = \"\";\n private readonly _readOnlyAfterInit: boolean;\n\n /**\n * Constructor for cmi.comments_from_learner.n and cmi.comments_from_lms.n\n * @param {boolean} readOnlyAfterInit\n */\n constructor(readOnlyAfterInit: boolean = false) {\n super(\"cmi.comments_from_learner.n\");\n this._comment = \"\";\n this._location = \"\";\n this._timestamp = \"\";\n this._readOnlyAfterInit = readOnlyAfterInit;\n }\n\n /**\n * Called when the API has been reset\n */\n reset(): void {\n this._initialized = false;\n }\n\n /**\n * Getter for _comment\n * @return {string}\n */\n get comment(): string {\n return this._comment;\n }\n\n /**\n * Setter for _comment\n * @param {string} comment\n */\n set comment(comment: string) {\n if (this.initialized && this._readOnlyAfterInit) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".comment\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".comment\",\n comment,\n scorm2004_regex.CMILangString4000,\n true,\n )\n ) {\n this._comment = comment;\n }\n }\n }\n\n /**\n * Getter for _location\n * @return {string}\n */\n get location(): string {\n return this._location;\n }\n\n /**\n * Setter for _location\n * @param {string} location\n */\n set location(location: string) {\n if (this.initialized && this._readOnlyAfterInit) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".location\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".location\",\n location,\n scorm2004_regex.CMIString250,\n )\n ) {\n this._location = location;\n }\n }\n }\n\n /**\n * Getter for _timestamp\n * @return {string}\n */\n get timestamp(): string {\n return this._timestamp;\n }\n\n /**\n * Setter for _timestamp\n * @param {string} timestamp\n */\n set timestamp(timestamp: string) {\n if (this.initialized && this._readOnlyAfterInit) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".timestamp\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n if (\n check2004ValidFormat(this._cmi_element + \".timestamp\", timestamp, scorm2004_regex.CMITime)\n ) {\n this._timestamp = timestamp;\n }\n }\n }\n\n /**\n * toJSON for cmi.comments_from_learner.n object\n * @return {\n * {\n * comment: string,\n * location: string,\n * timestamp: string\n * }\n * }\n */\n toJSON(): {\n comment: string;\n location: string;\n timestamp: string;\n } {\n this.jsonString = true;\n const result = {\n comment: this.comment,\n location: this.location,\n timestamp: this.timestamp,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { CMIArray } from \"../common/array\";\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { Scorm2004CMIScore } from \"./score\";\nimport { check2004ValidFormat, check2004ValidRange } from \"./validation\";\nimport {\n scorm2004_constants,\n scorm2004_errors,\n scorm2004_regex,\n} from \"../../constants\";\n\n/**\n * Class representing SCORM 2004's `cmi.objectives` object\n *\n * Per SCORM 2004 RTE Section 4.1.5:\n * - Enhanced objectives with separate success and completion tracking\n * - Supports global objectives for cross-SCO state sharing\n * - progress_measure: Decimal (0-1) indicating objective completion\n * - description: LangString250 with optional language tag\n * - Objectives can be mapped to sequencing objectives via manifest\n * - Global objectives persist across SCO sessions\n *\n * @spec RTE 4.2.6 - cmi.objectives\n * @extends CMIArray\n */\nexport class CMIObjectives extends CMIArray {\n /**\n * Constructor for `cmi.objectives` Array\n */\n constructor() {\n super({\n CMIElement: \"cmi.objectives\",\n children: scorm2004_constants.objectives_children,\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n });\n }\n\n /**\n * Find an objective by its ID\n */\n public findObjectiveById(id: string): CMIObjectivesObject | undefined {\n return (this.childArray as CMIObjectivesObject[]).find(\n (objective) => objective.id === id,\n );\n }\n\n /**\n * Find objective by its index\n */\n public findObjectiveByIndex(index: number): CMIObjectivesObject | undefined {\n return (this.childArray as CMIObjectivesObject[])[index];\n }\n\n /**\n * Set an objective at the given index\n */\n public setObjectiveByIndex(index: number, objective: CMIObjectivesObject) {\n this.childArray[index] = objective;\n }\n}\n\n/**\n * Class for SCORM 2004's cmi.objectives.n object\n *\n * Per SCORM 2004 RTE Section 4.1.5:\n * - id: Long identifier (up to 4000 chars, supports URN format)\n * - success_status: Separate from completion (passed, failed, unknown)\n * - completion_status: Completion state (completed, incomplete, not attempted, unknown)\n * - progress_measure: Decimal (0-1) for objective completion percentage\n * - description: LangString250 with optional language tag\n * - score: Scaled score (-1 to 1) and raw/min/max values\n * - Dependency: id must be set before other elements\n * - Global objectives: Persist across SCOs when mapped via manifest\n *\n * @spec RTE 4.2.6.1 - cmi.objectives.n.id\n * @spec RTE 4.2.6.2 - cmi.objectives.n.score\n * @spec RTE 4.2.6.3 - cmi.objectives.n.success_status\n * @spec RTE 4.2.6.4 - cmi.objectives.n.completion_status\n * @spec RTE 4.2.6.5 - cmi.objectives.n.progress_measure\n * @spec RTE 4.2.6.6 - cmi.objectives.n.description\n * @extends BaseCMI\n */\nexport class CMIObjectivesObject extends BaseCMI {\n private _id = \"\";\n private _idIsSet = false;\n private _success_status = \"unknown\";\n private _completion_status = \"unknown\";\n private _progress_measure = \"\";\n private _description = \"\";\n\n /**\n * Constructor for cmi.objectives.n\n */\n constructor() {\n super(\"cmi.objectives.n\");\n this.score = new Scorm2004CMIScore();\n }\n\n override reset() {\n this._initialized = false;\n this._id = \"\";\n this._idIsSet = false;\n this._success_status = \"unknown\";\n this._completion_status = \"unknown\";\n this._progress_measure = \"\";\n this._description = \"\";\n this.score?.reset();\n }\n\n public score: Scorm2004CMIScore;\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this.score?.initialize();\n }\n\n /**\n * Getter for _id\n * @return {string}\n */\n get id(): string {\n return this._id;\n }\n\n /**\n * Setter for _id\n * Per SCORM 2004 RTE: identifier SHALL NOT be empty or contain only whitespace\n * Per SCORM 2004 RTE Section 4.1.5: Once set, an objective ID is immutable (error 351)\n * @param {string} id\n */\n set id(id: string) {\n // Per spec: identifier cannot be empty or whitespace-only\n if (id === \"\" || id.trim() === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".id\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n // Per SCORM 2004 RTE: Once an objective ID is set, it cannot be changed\n if (this._idIsSet && this._id !== id) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".id\",\n scorm2004_errors.GENERAL_SET_FAILURE as number,\n );\n }\n if (check2004ValidFormat(this._cmi_element + \".id\", id, scorm2004_regex.CMILongIdentifier)) {\n this._id = id;\n this._idIsSet = true;\n }\n }\n\n /**\n * Getter for _success_status\n * @return {string}\n */\n get success_status(): string {\n return this._success_status;\n }\n\n /**\n * Setter for _success_status\n * @param {string} success_status\n */\n set success_status(success_status: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".success_status\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".success_status\",\n success_status,\n scorm2004_regex.CMISStatus,\n )\n ) {\n this._success_status = success_status;\n }\n }\n }\n\n /**\n * Getter for _completion_status\n * @return {string}\n */\n get completion_status(): string {\n return this._completion_status;\n }\n\n /**\n * Setter for _completion_status\n * @param {string} completion_status\n */\n set completion_status(completion_status: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".completion_status\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".completion_status\",\n completion_status,\n scorm2004_regex.CMICStatus,\n )\n ) {\n this._completion_status = completion_status;\n }\n }\n }\n\n /**\n * Getter for _progress_measure\n * @return {string}\n */\n get progress_measure(): string {\n return this._progress_measure;\n }\n\n /**\n * Setter for _progress_measure\n * @param {string} progress_measure\n */\n set progress_measure(progress_measure: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".progress_measure\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".progress_measure\",\n progress_measure,\n scorm2004_regex.CMIDecimal,\n ) &&\n check2004ValidRange(\n this._cmi_element + \".progress_measure\",\n progress_measure,\n scorm2004_regex.progress_range,\n )\n ) {\n this._progress_measure = progress_measure;\n }\n }\n }\n\n /**\n * Getter for _description\n * @return {string}\n */\n get description(): string {\n return this._description;\n }\n\n /**\n * Setter for _description\n * @param {string} description\n */\n set description(description: string) {\n if (this.initialized && this._id === \"\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".description\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n } else {\n if (\n check2004ValidFormat(\n this._cmi_element + \".description\",\n description,\n scorm2004_regex.CMILangString250,\n true,\n )\n ) {\n this._description = description;\n }\n }\n }\n\n /**\n * toJSON for cmi.objectives.n\n *\n * @return {\n * {\n * id: string,\n * success_status: string,\n * completion_status: string,\n * progress_measure: string,\n * description: string,\n * score: Scorm2004CMIScore\n * }\n * }\n */\n toJSON(): {\n id: string;\n success_status: string;\n completion_status: string;\n progress_measure: string;\n description: string;\n score: Scorm2004CMIScore;\n } {\n this.jsonString = true;\n const result = {\n id: this.id,\n success_status: this.success_status,\n completion_status: this.completion_status,\n progress_measure: this.progress_measure,\n description: this.description,\n score: this.score,\n };\n this.jsonString = false;\n return result;\n }\n\n /**\n * Populate this objective from a plain object\n * @param {any} data\n */\n fromJSON(data: any): void {\n if (!data || typeof data !== \"object\") return;\n if (typeof data.id === \"string\") this.id = data.id;\n if (typeof data.success_status === \"string\") this.success_status = data.success_status;\n if (typeof data.completion_status === \"string\") this.completion_status = data.completion_status;\n if (typeof data.progress_measure !== \"undefined\") this.progress_measure = String(data.progress_measure);\n if (typeof data.description === \"string\") this.description = data.description;\n if (data.score && typeof data.score === \"object\") {\n if (typeof data.score.scaled !== \"undefined\") this.score.scaled = String(data.score.scaled);\n if (typeof data.score.raw !== \"undefined\") this.score.raw = String(data.score.raw);\n if (typeof data.score.min !== \"undefined\") this.score.min = String(data.score.min);\n if (typeof data.score.max !== \"undefined\") this.score.max = String(data.score.max);\n }\n }\n}\n","/**\n * Class representing metadata properties for SCORM 2004's cmi object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_constants, scorm2004_errors } from \"../../constants\";\n\n/**\n * Class representing metadata properties for SCORM 2004's cmi object\n */\nexport class CMIMetadata extends BaseCMI {\n private __version = \"1.0\";\n private __children = scorm2004_constants.cmi_children;\n\n /**\n * Constructor for CMIMetadata\n */\n constructor() {\n super(\"cmi\");\n }\n\n /**\n * Getter for __version\n * @return {string}\n */\n get _version(): string {\n return this.__version;\n }\n\n /**\n * Setter for __version. Just throws an error.\n * @param {string} _version\n */\n set _version(_version: string) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \"._version\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n\n /**\n * Getter for __children\n * @return {string}\n */\n get _children(): string {\n return this.__children;\n }\n\n /**\n * Setter for __children. Just throws an error.\n * @param {number} _children\n */\n set _children(_children: number) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \"._children\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n\n /**\n * Reset the metadata properties\n */\n reset(): void {\n this._initialized = false;\n // No need to reset __version and __children as they are constants\n }\n}\n","/**\n * Class representing learner properties for SCORM 2004's cmi object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors } from \"../../constants\";\n\n/**\n * Class representing learner properties for SCORM 2004's cmi object\n */\nexport class CMILearner extends BaseCMI {\n private _learner_id = \"\";\n private _learner_name = \"\";\n\n /**\n * Constructor for CMILearner\n */\n constructor() {\n super(\"cmi\");\n }\n\n /**\n * Getter for _learner_id\n * @return {string}\n */\n get learner_id(): string {\n return this._learner_id;\n }\n\n /**\n * Setter for _learner_id. Can only be called before initialization.\n * @param {string} learner_id\n */\n set learner_id(learner_id: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_id\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n // Note: SCORM 2004 3rd Edition specifies SPM of 4000 chars (CMILongIdentifier).\n // We intentionally do NOT enforce this limit to maximize LMS compatibility.\n this._learner_id = learner_id;\n }\n }\n\n /**\n * Getter for _learner_name\n * @return {string}\n */\n get learner_name(): string {\n return this._learner_name;\n }\n\n /**\n * Setter for _learner_name. Can only be called before initialization.\n * @param {string} learner_name\n */\n set learner_name(learner_name: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".learner_name\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n // Note: SCORM 2004 3rd Edition specifies SPM of 250 chars (CMIString250).\n // We intentionally do NOT enforce this limit to maximize LMS compatibility.\n this._learner_name = learner_name;\n }\n }\n\n /**\n * Reset the learner properties\n */\n reset(): void {\n this._initialized = false;\n // Don't reset learner_id and learner_name as they are read-only after initialization\n }\n}\n","/**\n * Class representing status properties for SCORM 2004's cmi object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { check2004ValidFormat, check2004ValidRange } from \"./validation\";\nimport { scorm2004_regex } from \"../../constants\";\n\n/**\n * Class representing status properties for SCORM 2004's cmi object\n */\nexport class CMIStatus extends BaseCMI {\n private _completion_status = \"unknown\";\n private _success_status = \"unknown\";\n private _progress_measure = \"\";\n\n /**\n * Constructor for CMIStatus\n */\n constructor() {\n super(\"cmi\");\n }\n\n /**\n * Getter for _completion_status\n * @return {string}\n */\n get completion_status(): string {\n return this._completion_status;\n }\n\n /**\n * Setter for _completion_status\n * @param {string} completion_status\n */\n set completion_status(completion_status: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".completion_status\",\n completion_status,\n scorm2004_regex.CMICStatus,\n )\n ) {\n this._completion_status = completion_status;\n }\n }\n\n /**\n * Getter for _success_status\n * @return {string}\n */\n get success_status(): string {\n return this._success_status;\n }\n\n /**\n * Setter for _success_status\n * @param {string} success_status\n */\n set success_status(success_status: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".success_status\",\n success_status,\n scorm2004_regex.CMISStatus,\n )\n ) {\n this._success_status = success_status;\n }\n }\n\n /**\n * Getter for _progress_measure\n * @return {string}\n */\n get progress_measure(): string {\n return this._progress_measure;\n }\n\n /**\n * Setter for _progress_measure\n * @param {string} progress_measure\n */\n set progress_measure(progress_measure: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".progress_measure\",\n progress_measure,\n scorm2004_regex.CMIDecimal,\n ) &&\n check2004ValidRange(\n this._cmi_element + \".progress_measure\",\n progress_measure,\n scorm2004_regex.progress_range,\n )\n ) {\n this._progress_measure = progress_measure;\n }\n }\n\n /**\n * Reset the status properties\n */\n reset(): void {\n this._initialized = false;\n this._completion_status = \"unknown\";\n this._success_status = \"unknown\";\n this._progress_measure = \"\";\n }\n}\n","/**\n * Class representing session properties for SCORM 2004's cmi object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { check2004ValidFormat } from \"./validation\";\nimport { scorm2004_errors, scorm2004_regex } from \"../../constants\";\nimport { addTwoDurations, getSecondsAsISODuration } from \"../../utilities\";\n\n/**\n * Class representing session properties for SCORM 2004's cmi object\n */\nexport class CMISession extends BaseCMI {\n private _entry = \"\";\n private _exit = \"\";\n private _session_time = \"PT0H0M0S\";\n private _total_time = \"PT0S\";\n\n /**\n * Constructor for CMISession\n */\n constructor() {\n super(\"cmi\");\n }\n\n /**\n * Getter for _entry\n * @return {string}\n */\n get entry(): string {\n return this._entry;\n }\n\n /**\n * Setter for _entry. Can only be called before initialization.\n * @param {string} entry\n */\n set entry(entry: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".entry\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n this._entry = entry;\n }\n }\n\n /**\n * Getter for _exit. Should only be called during JSON export.\n * @return {string}\n */\n get exit(): string {\n if (!this.jsonString) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".exit\",\n scorm2004_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._exit;\n }\n\n /**\n * Internal getter for exit value - for use by the API for sequencing purposes.\n * This bypasses the write-only restriction since the API needs to know the exit\n * value to properly handle sequencing and navigation.\n * @return {string}\n */\n getExitValueInternal(): string {\n return this._exit;\n }\n\n /**\n * Setter for _exit\n * @param {string} exit\n */\n set exit(exit: string) {\n if (exit === \"logout\") {\n console.warn(\n 'SCORM 2004: cmi.exit value \"logout\" is deprecated per 4th Edition. ' +\n 'Consider using \"normal\" or \"suspend\" instead.',\n );\n }\n if (check2004ValidFormat(this._cmi_element + \".exit\", exit, scorm2004_regex.CMIExit, true)) {\n this._exit = exit;\n }\n }\n\n /**\n * Getter for _session_time. Should only be called during JSON export.\n * @return {string}\n */\n get session_time(): string {\n if (!this.jsonString) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".session_time\",\n scorm2004_errors.WRITE_ONLY_ELEMENT as number,\n );\n }\n return this._session_time;\n }\n\n /**\n * Setter for _session_time\n * @param {string} session_time\n */\n set session_time(session_time: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".session_time\",\n session_time,\n scorm2004_regex.CMITimespan,\n )\n ) {\n this._session_time = session_time;\n }\n }\n\n /**\n * Getter for _total_time\n * @return {string}\n */\n get total_time(): string {\n return this._total_time;\n }\n\n /**\n * Setter for _total_time. Can only be called before initialization.\n * @param {string} total_time\n */\n set total_time(total_time: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".total_time\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n this._total_time = total_time;\n }\n }\n\n /**\n * Adds the current session time to the existing total time.\n *\n * @return {string} ISO8601 Duration\n */\n getCurrentTotalTime(start_time: number | undefined): string {\n let sessionTime = this._session_time;\n if (typeof start_time !== \"undefined\") {\n const seconds = new Date().getTime() - start_time;\n sessionTime = getSecondsAsISODuration(seconds / 1000);\n }\n\n return addTwoDurations(this._total_time, sessionTime, scorm2004_regex.CMITimespan);\n }\n\n /**\n * Reset the session properties\n *\n * When resetting for a new SCO delivery, entry is set to \"ab-initio\" per SCORM 2004 spec:\n * - \"ab-initio\" indicates the learner is beginning a new attempt on the activity\n * - \"resume\" indicates the learner is resuming a previously suspended attempt\n *\n * Since reset() is called for SCO transitions (new attempts), \"ab-initio\" is the correct value.\n * The LMS can override this if the learner is resuming a suspended session.\n */\n reset(): void {\n this._initialized = false;\n this._entry = \"ab-initio\";\n this._exit = \"\";\n this._session_time = \"PT0H0M0S\";\n // Don't reset total_time as it's read-only after initialization\n }\n}\n","/**\n * Class representing content properties for SCORM 2004's cmi object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { check2004ValidFormat } from \"./validation\";\nimport { scorm2004_errors, scorm2004_regex } from \"../../constants\";\n\n/**\n * Class representing content properties for SCORM 2004's cmi object\n */\nexport class CMIContent extends BaseCMI {\n private _location = \"\";\n private _launch_data = \"\";\n private _suspend_data = \"\";\n\n /**\n * Constructor for CMIContent\n */\n constructor() {\n super(\"cmi\");\n }\n\n /**\n * Getter for _location\n * @return {string}\n */\n get location(): string {\n return this._location;\n }\n\n /**\n * Setter for _location\n * @param {string} location\n */\n set location(location: string) {\n if (\n check2004ValidFormat(this._cmi_element + \".location\", location, scorm2004_regex.CMIString1000)\n ) {\n this._location = location;\n }\n }\n\n /**\n * Getter for _launch_data\n * @return {string}\n */\n get launch_data(): string {\n return this._launch_data;\n }\n\n /**\n * Setter for _launch_data. Can only be called before initialization.\n * @param {string} launch_data\n */\n set launch_data(launch_data: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".launch_data\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n } else {\n // Note: SCORM 2004 3rd Edition specifies SPM of 4000 chars for launch_data.\n // We intentionally do NOT enforce this limit to maximize LMS compatibility.\n // Some LMS implementations may provide larger launch_data values.\n this._launch_data = launch_data;\n }\n }\n\n /**\n * Getter for _suspend_data\n * @return {string}\n */\n get suspend_data(): string {\n return this._suspend_data;\n }\n\n /**\n * Setter for _suspend_data\n * @param {string} suspend_data\n */\n set suspend_data(suspend_data: string) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".suspend_data\",\n suspend_data,\n scorm2004_regex.CMIString64000,\n true,\n )\n ) {\n this._suspend_data = suspend_data;\n }\n }\n\n /**\n * Reset the content properties\n */\n reset(): void {\n this._initialized = false;\n this._location = \"\";\n // Don't reset launch_data as it's read-only after initialization\n this._suspend_data = \"\";\n }\n}\n","/**\n * Class representing settings properties for SCORM 2004's cmi object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors, scorm2004_regex } from \"../../constants\";\n\n/**\n * Class representing settings properties for SCORM 2004's cmi object\n */\nexport class CMISettings extends BaseCMI {\n private _credit = \"credit\";\n private _mode = \"normal\";\n private _time_limit_action = \"continue,no message\";\n private _max_time_allowed = \"\";\n\n /**\n * Constructor for CMISettings\n */\n constructor() {\n super(\"cmi\");\n }\n\n /**\n * Getter for _credit\n * @return {string}\n */\n get credit(): string {\n return this._credit;\n }\n\n /**\n * Setter for _credit. Can only be called before initialization.\n * @param {string} credit\n */\n set credit(credit: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".credit\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (!/^(credit|no-credit)$/.test(credit)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".credit\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._credit = credit;\n }\n\n /**\n * Getter for _mode\n * @return {string}\n */\n get mode(): string {\n return this._mode;\n }\n\n /**\n * Setter for _mode. Can only be called before initialization.\n * @param {string} mode\n */\n set mode(mode: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".mode\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (!/^(browse|normal|review)$/.test(mode)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".mode\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._mode = mode;\n }\n\n /**\n * Getter for _time_limit_action\n * @return {string}\n */\n get time_limit_action(): string {\n return this._time_limit_action;\n }\n\n /**\n * Setter for _time_limit_action. Can only be called before initialization.\n * @param {string} time_limit_action\n */\n set time_limit_action(time_limit_action: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".time_limit_action\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (!/^(exit,message|exit,no message|continue,message|continue,no message)$/.test(time_limit_action)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".time_limit_action\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._time_limit_action = time_limit_action;\n }\n\n /**\n * Getter for _max_time_allowed\n * @return {string}\n */\n get max_time_allowed(): string {\n return this._max_time_allowed;\n }\n\n /**\n * Setter for _max_time_allowed. Can only be called before initialization.\n * @param {string} max_time_allowed\n */\n set max_time_allowed(max_time_allowed: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".max_time_allowed\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n\n // Allow empty string (undefined value)\n if (max_time_allowed === \"\") {\n this._max_time_allowed = max_time_allowed;\n return;\n }\n\n // Validate format using CMITimespan regex (ISO 8601 duration)\n const regex = new RegExp(scorm2004_regex.CMITimespan);\n if (!regex.test(max_time_allowed)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".max_time_allowed\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n this._max_time_allowed = max_time_allowed;\n }\n\n /**\n * Reset the settings properties\n */\n reset(): void {\n this._initialized = false;\n // Don't reset these properties as they are read-only after initialization\n }\n}\n","/**\n * Class representing threshold properties for SCORM 2004's cmi object\n */\nimport { BaseCMI } from \"../common/base_cmi\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors, scorm2004_regex } from \"../../constants\";\n\n/**\n * Class representing threshold properties for SCORM 2004's cmi object\n */\nexport class CMIThresholds extends BaseCMI {\n private _scaled_passing_score = \"\";\n private _completion_threshold = \"\";\n\n /**\n * Constructor for CMIThresholds\n */\n constructor() {\n super(\"cmi\");\n }\n\n /**\n * Getter for _scaled_passing_score\n * @return {string}\n */\n get scaled_passing_score(): string {\n return this._scaled_passing_score;\n }\n\n /**\n * Setter for _scaled_passing_score. Can only be called before initialization.\n * @param {string} scaled_passing_score\n */\n set scaled_passing_score(scaled_passing_score: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".scaled_passing_score\",\n scorm2004_errors.READ_ONLY_ELEMENT ?? 404,\n );\n }\n\n // Allow empty string (undefined value)\n if (scaled_passing_score === \"\") {\n this._scaled_passing_score = scaled_passing_score;\n return;\n }\n\n // Validate format using CMIDecimal regex\n const regex = new RegExp(scorm2004_regex.CMIDecimal);\n if (!regex.test(scaled_passing_score)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".scaled_passing_score\",\n scorm2004_errors.TYPE_MISMATCH ?? 406,\n );\n }\n\n // Validate range: -1 to 1\n const num = parseFloat(scaled_passing_score);\n if (num < -1 || num > 1) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".scaled_passing_score\",\n scorm2004_errors.VALUE_OUT_OF_RANGE ?? 407,\n );\n }\n\n this._scaled_passing_score = scaled_passing_score;\n }\n\n /**\n * Getter for _completion_threshold\n * @return {string}\n */\n get completion_threshold(): string {\n return this._completion_threshold;\n }\n\n /**\n * Setter for _completion_threshold. Can only be called before initialization.\n * @param {string} completion_threshold\n */\n set completion_threshold(completion_threshold: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".completion_threshold\",\n scorm2004_errors.READ_ONLY_ELEMENT ?? 404,\n );\n }\n\n // Allow empty string (undefined value)\n if (completion_threshold === \"\") {\n this._completion_threshold = completion_threshold;\n return;\n }\n\n // Validate format using CMIDecimal regex\n const regex = new RegExp(scorm2004_regex.CMIDecimal);\n if (!regex.test(completion_threshold)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".completion_threshold\",\n scorm2004_errors.TYPE_MISMATCH ?? 406,\n );\n }\n\n // Validate range: 0 to 1\n const num = parseFloat(completion_threshold);\n if (num < 0 || num > 1) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".completion_threshold\",\n scorm2004_errors.VALUE_OUT_OF_RANGE ?? 407,\n );\n }\n\n this._completion_threshold = completion_threshold;\n }\n\n /**\n * Reset the threshold properties\n */\n reset(): void {\n this._initialized = false;\n // Don't reset these properties as they are read-only after initialization\n }\n}\n","import { BaseRootCMI } from \"../common/base_cmi\";\nimport { CMILearnerPreference } from \"./learner_preference\";\nimport { CMIInteractions } from \"./interactions\";\nimport { Scorm2004CMIScore } from \"./score\";\nimport { CMICommentsFromLearner, CMICommentsFromLMS } from \"./comments\";\nimport { CMIObjectives } from \"./objectives\";\nimport { CMIMetadata } from \"./metadata\";\nimport { CMILearner } from \"./learner\";\nimport { CMIStatus } from \"./status\";\nimport { CMISession } from \"./session\";\nimport { CMIContent } from \"./content\";\nimport { CMISettings } from \"./settings\";\nimport { CMIThresholds } from \"./thresholds\";\n\n/**\n * Class representing cmi object for SCORM 2004\n *\n * Per SCORM 2004 RTE Section 4.1:\n * - Enhanced data model with additional elements vs SCORM 1.2\n * - Separate completion_status and success_status tracking\n * - Progress measure (0-1 scale) for completion tracking\n * - Scaled scores (-1 to 1) with threshold support\n * - Language support via LangString types\n * - Comments from learner and LMS with timestamps\n * - Enhanced interactions with better type validation\n * - Global objectives support for cross-SCO tracking\n *\n * @extends BaseRootCMI\n */\nexport class CMI extends BaseRootCMI {\n /**\n * Constructor for the SCORM 2004 cmi object\n * @param {boolean} initialized\n */\n constructor(initialized: boolean = false) {\n super(\"cmi\");\n this.metadata = new CMIMetadata();\n this.learner = new CMILearner();\n this.status = new CMIStatus();\n this.session = new CMISession();\n this.content = new CMIContent();\n this.settings = new CMISettings();\n this.thresholds = new CMIThresholds();\n this.learner_preference = new CMILearnerPreference();\n this.score = new Scorm2004CMIScore();\n this.comments_from_learner = new CMICommentsFromLearner();\n this.comments_from_lms = new CMICommentsFromLMS();\n this.interactions = new CMIInteractions();\n this.objectives = new CMIObjectives();\n if (initialized) this.initialize();\n }\n\n // New component classes\n private metadata: CMIMetadata;\n private learner: CMILearner;\n private status: CMIStatus;\n private session: CMISession;\n private content: CMIContent;\n private settings: CMISettings;\n private thresholds: CMIThresholds;\n\n // Original complex objects\n public learner_preference: CMILearnerPreference;\n public score: Scorm2004CMIScore;\n public comments_from_learner: CMICommentsFromLearner;\n public comments_from_lms: CMICommentsFromLMS;\n public interactions: CMIInteractions;\n public objectives: CMIObjectives;\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n // Initialize new component classes\n this.metadata?.initialize();\n this.learner?.initialize();\n this.status?.initialize();\n this.session?.initialize();\n this.content?.initialize();\n this.settings?.initialize();\n this.thresholds?.initialize();\n\n // Initialize original complex objects\n this.learner_preference?.initialize();\n this.score?.initialize();\n this.comments_from_learner?.initialize();\n this.comments_from_lms?.initialize();\n this.interactions?.initialize();\n this.objectives?.initialize();\n }\n\n /**\n * Called when API is moving to another SCO\n * \n * Resets SCO-specific CMI data while preserving global objectives.\n * \n * The objectives.reset(false) call resets individual objective objects\n * but maintains the array structure. Global objectives stored in\n * Scorm2004API._globalObjectives are preserved separately and are not\n * affected by this reset.\n * \n * This aligns with SCORM 2004 Sequencing and Navigation (SN) Book:\n * - Content Delivery Environment Process (DB.2) requires reset between SCOs\n * - Global objectives (via mapInfo) must persist across SCO transitions\n * - SCO-specific data (location, entry, session, interactions) must be reset\n */\n reset() {\n this._initialized = false;\n\n // Reset new component classes\n this.metadata?.reset();\n this.learner?.reset();\n this.status?.reset();\n this.session?.reset();\n this.content?.reset();\n this.settings?.reset();\n this.thresholds?.reset();\n\n // Reset original complex objects\n // objectives.reset(true) - wipe the array completely so restoreGlobalObjectivesToCMI\n // can repopulate it from _globalObjectives. The global objectives are persisted in\n // Scorm2004API._globalObjectives and restored during lmsInitialize().\n this.objectives?.reset(true);\n this.interactions?.reset(true);\n this.score?.reset();\n this.comments_from_learner?.reset();\n this.comments_from_lms?.reset();\n this.learner_preference?.reset();\n }\n\n /**\n * Getter for __version\n * @return {string}\n * @private\n */\n get _version(): string {\n return this.metadata._version;\n }\n\n /**\n * Setter for __version. Just throws an error.\n * @param {string} _version\n * @private\n */\n set _version(_version: string) {\n this.metadata._version = _version;\n }\n\n /**\n * Getter for __children\n * @return {string}\n * @private\n */\n get _children(): string {\n return this.metadata._children;\n }\n\n /**\n * Setter for __children. Just throws an error.\n * @param {number} _children\n * @private\n */\n set _children(_children: number) {\n this.metadata._children = _children;\n }\n\n /**\n * Getter for _completion_status\n * @return {string}\n * @spec RTE 4.2.8 - cmi.completion_status\n */\n get completion_status(): string {\n return this.status.completion_status;\n }\n\n /**\n * Setter for _completion_status\n * @param {string} completion_status\n */\n set completion_status(completion_status: string) {\n this.status.completion_status = completion_status;\n }\n\n /**\n * Getter for _completion_threshold\n * @return {string}\n * @spec RTE 4.2.9 - cmi.completion_threshold\n */\n get completion_threshold(): string {\n return this.thresholds.completion_threshold;\n }\n\n /**\n * Setter for _completion_threshold. Can only be called before initialization.\n * @param {string} completion_threshold\n */\n set completion_threshold(completion_threshold: string) {\n this.thresholds.completion_threshold = completion_threshold;\n }\n\n /**\n * Getter for _credit\n * @return {string}\n * @spec RTE 4.2.10 - cmi.credit\n */\n get credit(): string {\n return this.settings.credit;\n }\n\n /**\n * Setter for _credit. Can only be called before initialization.\n * @param {string} credit\n */\n set credit(credit: string) {\n this.settings.credit = credit;\n }\n\n /**\n * Getter for _entry\n * @return {string}\n * @spec RTE 4.2.11 - cmi.entry\n */\n get entry(): string {\n return this.session.entry;\n }\n\n /**\n * Setter for _entry. Can only be called before initialization.\n * @param {string} entry\n */\n set entry(entry: string) {\n this.session.entry = entry;\n }\n\n /**\n * Getter for _exit. Should only be called during JSON export.\n * @return {string}\n * @spec RTE 4.2.12 - cmi.exit\n */\n get exit(): string {\n this.session.jsonString = this.jsonString;\n return this.session.exit;\n }\n\n /**\n * Setter for _exit\n * @param {string} exit\n */\n set exit(exit: string) {\n this.session.exit = exit;\n }\n\n /**\n * Internal getter for exit value - for use by the API for sequencing purposes.\n * This bypasses the write-only restriction since the API needs to know the exit\n * value to properly handle sequencing and navigation.\n * @return {string}\n */\n getExitValueInternal(): string {\n return this.session.getExitValueInternal();\n }\n\n /**\n * Getter for _launch_data\n * @return {string}\n * @spec RTE 4.2.13 - cmi.launch_data\n */\n get launch_data(): string {\n return this.content.launch_data;\n }\n\n /**\n * Setter for _launch_data. Can only be called before initialization.\n * @param {string} launch_data\n */\n set launch_data(launch_data: string) {\n this.content.launch_data = launch_data;\n }\n\n /**\n * Getter for _learner_id\n * @return {string}\n * @spec RTE 4.2.14 - cmi.learner_id\n */\n get learner_id(): string {\n return this.learner.learner_id;\n }\n\n /**\n * Setter for _learner_id. Can only be called before initialization.\n * @param {string} learner_id\n */\n set learner_id(learner_id: string) {\n this.learner.learner_id = learner_id;\n }\n\n /**\n * Getter for _learner_name\n * @return {string}\n * @spec RTE 4.2.15 - cmi.learner_name\n */\n get learner_name(): string {\n return this.learner.learner_name;\n }\n\n /**\n * Setter for _learner_name. Can only be called before initialization.\n * @param {string} learner_name\n */\n set learner_name(learner_name: string) {\n this.learner.learner_name = learner_name;\n }\n\n /**\n * Getter for _location\n * @return {string}\n * @spec RTE 4.2.17 - cmi.location\n */\n get location(): string {\n return this.content.location;\n }\n\n /**\n * Setter for _location\n * @param {string} location\n */\n set location(location: string) {\n this.content.location = location;\n }\n\n /**\n * Getter for _max_time_allowed\n * @return {string}\n * @spec RTE 4.2.18 - cmi.max_time_allowed\n */\n get max_time_allowed(): string {\n return this.settings.max_time_allowed;\n }\n\n /**\n * Setter for _max_time_allowed. Can only be called before initialization.\n * @param {string} max_time_allowed\n */\n set max_time_allowed(max_time_allowed: string) {\n this.settings.max_time_allowed = max_time_allowed;\n }\n\n /**\n * Getter for _mode\n * @return {string}\n * @spec RTE 4.2.19 - cmi.mode\n */\n get mode(): string {\n return this.settings.mode;\n }\n\n /**\n * Setter for _mode. Can only be called before initialization.\n * @param {string} mode\n */\n set mode(mode: string) {\n this.settings.mode = mode;\n }\n\n /**\n * Getter for _progress_measure\n * @return {string}\n * @spec RTE 4.2.21 - cmi.progress_measure\n */\n get progress_measure(): string {\n return this.status.progress_measure;\n }\n\n /**\n * Setter for _progress_measure\n * @param {string} progress_measure\n */\n set progress_measure(progress_measure: string) {\n this.status.progress_measure = progress_measure;\n }\n\n /**\n * Getter for _scaled_passing_score\n * @return {string}\n * @spec RTE 4.2.22 - cmi.scaled_passing_score\n */\n get scaled_passing_score(): string {\n return this.thresholds.scaled_passing_score;\n }\n\n /**\n * Setter for _scaled_passing_score. Can only be called before initialization.\n * @param {string} scaled_passing_score\n */\n set scaled_passing_score(scaled_passing_score: string) {\n this.thresholds.scaled_passing_score = scaled_passing_score;\n }\n\n /**\n * Getter for _session_time. Should only be called during JSON export.\n * @return {string}\n * @spec RTE 4.2.24 - cmi.session_time\n */\n get session_time(): string {\n this.session.jsonString = this.jsonString;\n return this.session.session_time;\n }\n\n /**\n * Setter for _session_time\n * @param {string} session_time\n */\n set session_time(session_time: string) {\n this.session.session_time = session_time;\n }\n\n /**\n * Getter for _success_status\n * @return {string}\n * @spec RTE 4.2.25 - cmi.success_status\n */\n get success_status(): string {\n return this.status.success_status;\n }\n\n /**\n * Setter for _success_status\n * @param {string} success_status\n */\n set success_status(success_status: string) {\n this.status.success_status = success_status;\n }\n\n /**\n * Getter for _suspend_data\n * @return {string}\n * @spec RTE 4.2.26 - cmi.suspend_data\n */\n get suspend_data(): string {\n return this.content.suspend_data;\n }\n\n /**\n * Setter for _suspend_data\n * @param {string} suspend_data\n */\n set suspend_data(suspend_data: string) {\n this.content.suspend_data = suspend_data;\n }\n\n /**\n * Getter for _time_limit_action\n * @return {string}\n * @spec RTE 4.2.27 - cmi.time_limit_action\n */\n get time_limit_action(): string {\n return this.settings.time_limit_action;\n }\n\n /**\n * Setter for _time_limit_action. Can only be called before initialization.\n * @param {string} time_limit_action\n */\n set time_limit_action(time_limit_action: string) {\n this.settings.time_limit_action = time_limit_action;\n }\n\n /**\n * Getter for _total_time\n * @return {string}\n * @spec RTE 4.2.28 - cmi.total_time\n */\n get total_time(): string {\n return this.session.total_time;\n }\n\n /**\n * Setter for _total_time. Can only be called before initialization.\n * @param {string} total_time\n */\n set total_time(total_time: string) {\n this.session.total_time = total_time;\n }\n\n /**\n * Adds the current session time to the existing total time.\n *\n * @return {string} ISO8601 Duration\n */\n getCurrentTotalTime(): string {\n return this.session.getCurrentTotalTime(this.start_time);\n }\n\n /**\n * toJSON for cmi\n *\n * @return {\n * {\n * comments_from_learner: CMICommentsFromLearner,\n * comments_from_lms: CMICommentsFromLMS,\n * completion_status: string,\n * completion_threshold: string,\n * credit: string,\n * entry: string,\n * exit: string,\n * interactions: CMIInteractions,\n * launch_data: string,\n * learner_id: string,\n * learner_name: string,\n * learner_preference: CMILearnerPreference,\n * location: string,\n * max_time_allowed: string,\n * mode: string,\n * objectives: CMIObjectives,\n * progress_measure: string,\n * scaled_passing_score: string,\n * score: Scorm2004CMIScore,\n * session_time: string,\n * success_status: string,\n * suspend_data: string,\n * time_limit_action: string,\n * total_time: string\n * }\n * }\n */\n toJSON(): {\n comments_from_learner: CMICommentsFromLearner;\n comments_from_lms: CMICommentsFromLMS;\n completion_status: string;\n completion_threshold: string;\n credit: string;\n entry: string;\n exit: string;\n interactions: CMIInteractions;\n launch_data: string;\n learner_id: string;\n learner_name: string;\n learner_preference: CMILearnerPreference;\n location: string;\n max_time_allowed: string;\n mode: string;\n objectives: CMIObjectives;\n progress_measure: string;\n scaled_passing_score: string;\n score: Scorm2004CMIScore;\n session_time: string;\n success_status: string;\n suspend_data: string;\n time_limit_action: string;\n total_time: string;\n } {\n this.jsonString = true;\n\n // Set jsonString flag on component classes that need it\n this.session.jsonString = true;\n\n const result = {\n comments_from_learner: this.comments_from_learner,\n comments_from_lms: this.comments_from_lms,\n completion_status: this.completion_status,\n completion_threshold: this.completion_threshold,\n credit: this.credit,\n entry: this.entry,\n exit: this.exit,\n interactions: this.interactions,\n launch_data: this.launch_data,\n learner_id: this.learner_id,\n learner_name: this.learner_name,\n learner_preference: this.learner_preference,\n location: this.location,\n max_time_allowed: this.max_time_allowed,\n mode: this.mode,\n objectives: this.objectives,\n progress_measure: this.progress_measure,\n scaled_passing_score: this.scaled_passing_score,\n score: this.score,\n session_time: this.session_time,\n success_status: this.success_status,\n suspend_data: this.suspend_data,\n time_limit_action: this.time_limit_action,\n total_time: this.total_time,\n };\n\n // Clean up jsonString flags\n this.jsonString = false;\n this.session.jsonString = false;\n\n return result;\n }\n}\n","import { BaseCMI } from \"../common/base_cmi\";\nimport { CMIArray } from \"../common/array\";\nimport { Scorm2004ValidationError } from \"../../exceptions/scorm2004_exceptions\";\nimport { check2004ValidFormat } from \"./validation\";\nimport { Sequencing } from \"./sequencing/sequencing\";\nimport {\n NAVBoolean,\n scorm2004_constants,\n scorm2004_errors,\n scorm2004_regex,\n} from \"../../constants\";\n\n/**\n * Class representing SCORM 2004's adl object\n */\nexport class ADL extends BaseCMI {\n /**\n * Constructor for adl\n */\n constructor() {\n super(\"adl\");\n this.nav = new ADLNav();\n this.data = new ADLData();\n }\n\n public nav: ADLNav;\n public data = new ADLData();\n private _sequencing: Sequencing | null = null;\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this.nav?.initialize();\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this.nav?.reset();\n }\n\n /**\n * Getter for sequencing\n * @return {Sequencing | null}\n */\n get sequencing(): Sequencing | null {\n return this._sequencing;\n }\n\n /**\n * Setter for sequencing\n * @param {Sequencing | null} sequencing\n */\n set sequencing(sequencing: Sequencing | null) {\n this._sequencing = sequencing;\n if (sequencing) {\n sequencing.adlNav = this.nav;\n this.nav.sequencing = sequencing;\n }\n }\n\n /**\n * toJSON for adl\n * @return {\n * {\n * nav: ADLNav,\n * data: ADLData\n * }\n * }\n */\n toJSON(): {\n nav: ADLNav;\n data: ADLData;\n } {\n this.jsonString = true;\n const result = {\n nav: this.nav,\n data: this.data,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing SCORM 2004's `adl.nav` object\n * @spec SN 3 - Navigation Data Model\n */\n\nexport class ADLNav extends BaseCMI {\n private _request = \"_none_\";\n private _sequencing: Sequencing | null = null;\n\n /**\n * Constructor for `adl.nav`\n */\n constructor() {\n super(\"adl.nav\");\n this.request_valid = new ADLNavRequestValid();\n this.request_valid.setParentNav(this);\n }\n\n public request_valid: ADLNavRequestValid;\n\n /**\n * Getter for sequencing\n * @return {Sequencing | null}\n */\n get sequencing(): Sequencing | null {\n return this._sequencing;\n }\n\n /**\n * Setter for sequencing\n * @param {Sequencing | null} sequencing\n */\n set sequencing(sequencing: Sequencing | null) {\n this._sequencing = sequencing;\n }\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this.request_valid?.initialize();\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._request = \"_none_\";\n if (this._sequencing) {\n this._sequencing.adlNav = null;\n }\n this._sequencing = null;\n this.request_valid?.reset();\n }\n\n /**\n * Getter for _request\n * @return {string}\n */\n get request(): string {\n return this._request;\n }\n\n /**\n * Setter for _request\n * @param {string} request\n */\n set request(request: string) {\n if (check2004ValidFormat(this._cmi_element + \".request\", request, scorm2004_regex.NAVEvent)) {\n this._request = request;\n }\n }\n\n /**\n * toJSON for adl.nav\n *\n * @return {\n * {\n * request: string\n * }\n * }\n */\n toJSON(): {\n request: string;\n } {\n this.jsonString = true;\n const result = {\n request: this.request,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Class representing SCORM 2004's `adl.data` object\n */\nexport class ADLData extends CMIArray {\n constructor() {\n super({\n CMIElement: \"adl.data\",\n children: scorm2004_constants.adl_data_children,\n errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,\n errorClass: Scorm2004ValidationError,\n });\n }\n}\n\n/**\n * Class for SCORM 2004's adl.data.n object\n */\nexport class ADLDataObject extends BaseCMI {\n private _id = \"\";\n private _store = \"\";\n private _idIsSet = false;\n private _storeIsSet = false;\n\n constructor() {\n super(\"adl.data.n\");\n }\n\n /**\n * Called when the API has been reset\n */\n reset() {\n this._initialized = false;\n this._idIsSet = false;\n this._storeIsSet = false;\n }\n\n /**\n * Getter for _id\n * @return {string}\n */\n get id(): string {\n return this._id;\n }\n\n /**\n * Setter for _id\n * Per SCORM 2004 4th Ed: id is read-only after initialization (error 404)\n * @param {string} id\n */\n set id(id: string) {\n // REQ-ADL-015: id is read-only after initialization\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".id\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (check2004ValidFormat(this._cmi_element + \".id\", id, scorm2004_regex.CMILongIdentifier)) {\n this._id = id;\n this._idIsSet = true;\n }\n }\n\n /**\n * Getter for _store\n * Per SCORM 2004 4th Ed: returns error 403 if store not initialized\n * @return {string}\n */\n get store(): string {\n // REQ-ADL-020: GetValue on uninitialized store returns error 403\n if (this.initialized && !this._storeIsSet) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".store\",\n scorm2004_errors.VALUE_NOT_INITIALIZED as number,\n );\n }\n return this._store;\n }\n\n /**\n * Setter for _store\n * Per SCORM 2004 4th Ed: store requires id to be set first (error 408)\n * Per SCORM 2004 4th Ed SPM: store max length is 64000 characters\n * @param {string} store\n */\n set store(store: string) {\n // REQ-ADL-025: Dependency check - id must be set before store\n if (this.initialized && !this._idIsSet) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".store\",\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,\n );\n }\n // REQ-ADL-017: store SPM is 64000 characters (was incorrectly 4000)\n if (\n check2004ValidFormat(this._cmi_element + \".store\", store, scorm2004_regex.CMIString64000)\n ) {\n this._store = store;\n this._storeIsSet = true;\n }\n }\n\n /**\n * toJSON for adl.data.n\n *\n * @return {\n * {\n * id: string,\n * store: string\n * }\n * }\n */\n toJSON(): {\n id: string;\n store: string;\n } {\n this.jsonString = true;\n const result = {\n id: this._id,\n store: this._store,\n };\n this.jsonString = false;\n return result;\n }\n}\n\n/**\n * Helper class for adl.nav.request_valid.choice to support target validation\n */\nclass ADLNavRequestValidChoice {\n private _parentNav: ADLNav | null = null;\n private _staticValues: { [key: string]: NAVBoolean } = {};\n\n setParentNav(nav: ADLNav): void {\n this._parentNav = nav;\n }\n\n /**\n * Validate if a target can be chosen\n * Called by BaseAPI when accessing adl.nav.request_valid.choice.{target=...}\n */\n _isTargetValid(target: string): string {\n // If sequencing is available, evaluate dynamically\n if (this._parentNav?.sequencing?.overallSequencingProcess) {\n const process = this._parentNav.sequencing.overallSequencingProcess;\n if (process.predictChoiceEnabled) {\n const result = process.predictChoiceEnabled(target) ? \"true\" : \"false\";\n return result;\n }\n }\n // Fall back to static value if sequencing not available\n const value = this._staticValues[target];\n if (value === NAVBoolean.TRUE) {\n return \"true\";\n }\n if (value === NAVBoolean.FALSE) {\n return \"false\";\n }\n return \"unknown\";\n }\n\n /**\n * Get all static values\n */\n getAll(): { [key: string]: NAVBoolean } {\n return { ...this._staticValues };\n }\n\n /**\n * Set static values (used during initialization)\n */\n setAll(values: { [key: string]: NAVBoolean }): void {\n this._staticValues = { ...values };\n }\n}\n\n/**\n * Helper class for adl.nav.request_valid.jump to support target validation\n */\nclass ADLNavRequestValidJump {\n private _parentNav: ADLNav | null = null;\n private _staticValues: { [key: string]: NAVBoolean } = {};\n\n setParentNav(nav: ADLNav): void {\n this._parentNav = nav;\n }\n\n /**\n * Validate if a target can be jumped to\n * Called by BaseAPI when accessing adl.nav.request_valid.jump.{target=...}\n */\n _isTargetValid(target: string): string {\n // If sequencing is available, evaluate dynamically\n // Note: Jump validation checks if target exists and is in the tree\n if (this._parentNav?.sequencing?.activityTree) {\n const activity = this._parentNav.sequencing.activityTree.getActivity(target);\n return activity ? \"true\" : \"false\";\n }\n // Fall back to static value if sequencing not available\n const value = this._staticValues[target];\n if (value === NAVBoolean.TRUE) return \"true\";\n if (value === NAVBoolean.FALSE) return \"false\";\n return \"unknown\";\n }\n\n /**\n * Get all static values\n */\n getAll(): { [key: string]: NAVBoolean } {\n return { ...this._staticValues };\n }\n\n /**\n * Set static values (used during initialization)\n */\n setAll(values: { [key: string]: NAVBoolean }): void {\n this._staticValues = { ...values };\n }\n}\n\n/**\n * Class representing SCORM 2004's adl.nav.request_valid object\n * @spec SN 3.1 - Navigation Request Validity Data Model\n */\n\nexport class ADLNavRequestValid extends BaseCMI {\n private _continue = \"unknown\";\n private _previous = \"unknown\";\n private _choice: ADLNavRequestValidChoice;\n private _jump: ADLNavRequestValidJump;\n private _exit = \"unknown\";\n private _exitAll = \"unknown\";\n private _abandon = \"unknown\";\n private _abandonAll = \"unknown\";\n private _suspendAll = \"unknown\";\n private _parentNav: ADLNav | null = null;\n\n /**\n * Constructor for adl.nav.request_valid\n */\n constructor() {\n super(\"adl.nav.request_valid\");\n this._choice = new ADLNavRequestValidChoice();\n this._jump = new ADLNavRequestValidJump();\n }\n\n /**\n * Set parent nav reference for sequencing access\n * @param {ADLNav} nav - Parent ADLNav instance\n */\n setParentNav(nav: ADLNav): void {\n this._parentNav = nav;\n this._choice.setParentNav(nav);\n this._jump.setParentNav(nav);\n }\n\n /**\n * Called when the API has been reset\n */\n override reset() {\n this._initialized = false;\n this._continue = \"unknown\";\n this._previous = \"unknown\";\n this._choice.setAll({});\n this._jump.setAll({});\n this._exit = \"unknown\";\n this._exitAll = \"unknown\";\n this._abandon = \"unknown\";\n this._abandonAll = \"unknown\";\n this._suspendAll = \"unknown\";\n }\n\n /**\n * Getter for _continue\n * Dynamically evaluates whether continue navigation is valid using sequencing\n * @return {string}\n */\n get continue(): string {\n // If sequencing is available, evaluate dynamically\n if (this._parentNav?.sequencing?.overallSequencingProcess) {\n const process = this._parentNav.sequencing.overallSequencingProcess;\n if (process.predictContinueEnabled) {\n return process.predictContinueEnabled() ? \"true\" : \"false\";\n }\n }\n // Fall back to static value if sequencing not available\n return this._continue;\n }\n\n /**\n * Setter for _continue. Just throws an error.\n * @param {string} _continue\n */\n set continue(_continue: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".continue\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (\n check2004ValidFormat(this._cmi_element + \".continue\", _continue, scorm2004_regex.NAVBoolean)\n ) {\n this._continue = _continue;\n }\n }\n\n /**\n * Getter for _previous\n * Dynamically evaluates whether previous navigation is valid using sequencing\n * @return {string}\n */\n get previous(): string {\n // If sequencing is available, evaluate dynamically\n if (this._parentNav?.sequencing?.overallSequencingProcess) {\n const process = this._parentNav.sequencing.overallSequencingProcess;\n if (process.predictPreviousEnabled) {\n return process.predictPreviousEnabled() ? \"true\" : \"false\";\n }\n }\n // Fall back to static value if sequencing not available\n return this._previous;\n }\n\n /**\n * Setter for _previous. Just throws an error.\n * @param {string} _previous\n */\n set previous(_previous: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".previous\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (\n check2004ValidFormat(this._cmi_element + \".previous\", _previous, scorm2004_regex.NAVBoolean)\n ) {\n this._previous = _previous;\n }\n }\n\n /**\n * Getter for _choice\n * @return {ADLNavRequestValidChoice}\n */\n get choice(): ADLNavRequestValidChoice {\n return this._choice;\n }\n\n /**\n * Setter for _choice\n * @param {{ [key: string]: string }} choice\n */\n set choice(choice: { [key: string]: string }) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".choice\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (typeof choice !== \"object\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".choice\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n const converted: { [key: string]: NAVBoolean } = {};\n for (const key in choice) {\n if ({}.hasOwnProperty.call(choice, key)) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".choice.\" + key,\n choice[key] || \"\",\n scorm2004_regex.NAVBoolean,\n ) &&\n check2004ValidFormat(this._cmi_element + \".choice.\" + key, key, scorm2004_regex.NAVTarget)\n ) {\n // Convert string value to NAVBoolean enum value\n const value = choice[key];\n if (value === \"true\") {\n converted[key] = NAVBoolean.TRUE;\n } else if (value === \"false\") {\n converted[key] = NAVBoolean.FALSE;\n } else if (value === \"unknown\") {\n converted[key] = NAVBoolean.UNKNOWN;\n }\n }\n }\n }\n this._choice.setAll(converted);\n }\n\n /**\n * Getter for _jump\n * @return {ADLNavRequestValidJump}\n */\n get jump(): ADLNavRequestValidJump {\n return this._jump;\n }\n\n /**\n * Setter for _jump\n * @param {{ [key: string]: string }} jump\n */\n set jump(jump: { [key: string]: string }) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".jump\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (typeof jump !== \"object\") {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".jump\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n const converted: { [key: string]: NAVBoolean } = {};\n for (const key in jump) {\n if ({}.hasOwnProperty.call(jump, key)) {\n if (\n check2004ValidFormat(\n this._cmi_element + \".jump.\" + key,\n jump[key] || \"\",\n scorm2004_regex.NAVBoolean,\n ) &&\n check2004ValidFormat(this._cmi_element + \".jump.\" + key, key, scorm2004_regex.NAVTarget)\n ) {\n // Convert string value to NAVBoolean enum value\n const value = jump[key];\n if (value === \"true\") {\n converted[key] = NAVBoolean.TRUE;\n } else if (value === \"false\") {\n converted[key] = NAVBoolean.FALSE;\n } else if (value === \"unknown\") {\n converted[key] = NAVBoolean.UNKNOWN;\n }\n }\n }\n }\n this._jump.setAll(converted);\n }\n\n /**\n * Getter for _exit\n * @return {string}\n */\n get exit(): string {\n return this._exit;\n }\n\n /**\n * Setter for _exit. Just throws an error.\n * @param {string} _exit\n */\n set exit(_exit: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".exit\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (\n check2004ValidFormat(this._cmi_element + \".exit\", _exit, scorm2004_regex.NAVBoolean)\n ) {\n this._exit = _exit;\n }\n }\n\n /**\n * Getter for _exitAll\n * @return {string}\n */\n get exitAll(): string {\n return this._exitAll;\n }\n\n /**\n * Setter for _exitAll. Just throws an error.\n * @param {string} _exitAll\n */\n set exitAll(_exitAll: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".exitAll\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (\n check2004ValidFormat(this._cmi_element + \".exitAll\", _exitAll, scorm2004_regex.NAVBoolean)\n ) {\n this._exitAll = _exitAll;\n }\n }\n\n /**\n * Getter for _abandon\n * @return {string}\n */\n get abandon(): string {\n return this._abandon;\n }\n\n /**\n * Setter for _abandon. Just throws an error.\n * @param {string} _abandon\n */\n set abandon(_abandon: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".abandon\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (\n check2004ValidFormat(this._cmi_element + \".abandon\", _abandon, scorm2004_regex.NAVBoolean)\n ) {\n this._abandon = _abandon;\n }\n }\n\n /**\n * Getter for _abandonAll\n * @return {string}\n */\n get abandonAll(): string {\n return this._abandonAll;\n }\n\n /**\n * Setter for _abandonAll. Just throws an error.\n * @param {string} _abandonAll\n */\n set abandonAll(_abandonAll: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".abandonAll\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (\n check2004ValidFormat(this._cmi_element + \".abandonAll\", _abandonAll, scorm2004_regex.NAVBoolean)\n ) {\n this._abandonAll = _abandonAll;\n }\n }\n\n /**\n * Getter for _suspendAll\n * @return {string}\n */\n get suspendAll(): string {\n return this._suspendAll;\n }\n\n /**\n * Setter for _suspendAll. Just throws an error.\n * @param {string} _suspendAll\n */\n set suspendAll(_suspendAll: string) {\n if (this.initialized) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".suspendAll\",\n scorm2004_errors.READ_ONLY_ELEMENT as number,\n );\n }\n if (\n check2004ValidFormat(this._cmi_element + \".suspendAll\", _suspendAll, scorm2004_regex.NAVBoolean)\n ) {\n this._suspendAll = _suspendAll;\n }\n }\n\n /**\n * toJSON for adl.nav.request_valid\n *\n * @return {\n * {\n * previous: string,\n * continue: string,\n * choice: { [key: string]: NAVBoolean },\n * jump: { [key: string]: NAVBoolean },\n * exit: string,\n * exitAll: string,\n * abandon: string,\n * abandonAll: string,\n * suspendAll: string\n * }\n * }\n */\n toJSON(): {\n previous: string;\n continue: string;\n choice: { [key: string]: NAVBoolean };\n jump: { [key: string]: NAVBoolean };\n exit: string;\n exitAll: string;\n abandon: string;\n abandonAll: string;\n suspendAll: string;\n } {\n this.jsonString = true;\n const result = {\n previous: this.previous,\n continue: this.continue,\n choice: this._choice.getAll(),\n jump: this._jump.getAll(),\n exit: this.exit,\n exitAll: this.exitAll,\n abandon: this.abandon,\n abandonAll: this.abandonAll,\n suspendAll: this.suspendAll,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseCMI } from \"../../common/base_cmi\";\nimport { Activity } from \"./activity\";\nimport { Scorm2004ValidationError } from \"../../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors } from \"../../../constants/error_codes\";\n\n/**\n * Class representing the SCORM 2004 activity tree\n */\nexport class ActivityTree extends BaseCMI {\n private _root: Activity | null = null;\n private _currentActivity: Activity | null = null;\n private _suspendedActivity: Activity | null = null;\n private _activities: Map<string, Activity> = new Map();\n\n /**\n * Constructor for ActivityTree\n */\n constructor(root?: Activity) {\n super(\"activityTree\");\n if (root) {\n this.root = root;\n }\n }\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n if (this._root) {\n this._root.initialize();\n }\n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._currentActivity = null;\n this._suspendedActivity = null;\n // Clear the activities map so it can be rebuilt\n this._activities.clear();\n if (this._root) {\n this._root.reset();\n // Re-populate the activities map with the root and its children\n this._activities.set(this._root.id, this._root);\n this._addActivitiesToMap(this._root);\n }\n }\n\n /**\n * Getter for root\n * @return {Activity | null}\n */\n get root(): Activity | null {\n return this._root;\n }\n\n /**\n * Setter for root\n * @param {Activity} root\n */\n set root(root: Activity | null) {\n // noinspection SuspiciousTypeOfGuard\n if (root !== null && !(root instanceof Activity)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".root\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n // Clear existing map when assigning a new root to avoid stale activities\n this._activities.clear();\n this._root = root;\n if (root) {\n this._activities.set(root.id, root);\n this._addActivitiesToMap(root);\n }\n }\n\n /**\n * Recursively add activities to the activities map\n * @param {Activity} activity\n * @private\n */\n private _addActivitiesToMap(activity: Activity): void {\n for (const child of activity.children) {\n this._activities.set(child.id, child);\n this._addActivitiesToMap(child);\n }\n }\n\n /**\n * Getter for currentActivity\n * @return {Activity | null}\n */\n get currentActivity(): Activity | null {\n return this._currentActivity;\n }\n\n /**\n * Setter for currentActivity\n * @param {Activity | null} activity\n */\n set currentActivity(activity: Activity | null) {\n // noinspection SuspiciousTypeOfGuard\n if (activity !== null && !(activity instanceof Activity)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".currentActivity\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n // Deactivate previous current activity and all its ancestors\n if (this._currentActivity) {\n this._currentActivity.isActive = false;\n let ancestor = this._currentActivity.parent;\n while (ancestor) {\n ancestor.isActive = false;\n ancestor = ancestor.parent;\n }\n }\n\n this._currentActivity = activity;\n\n // Set new current activity and mark all ancestors as active\n if (activity) {\n activity.isActive = true;\n let ancestor = activity.parent;\n while (ancestor) {\n ancestor.isActive = true;\n ancestor = ancestor.parent;\n }\n }\n }\n\n /**\n * Set current activity without activating it\n * This method is used when the sequencing process needs to update the current activity\n * pointer without triggering the automatic activation behavior (e.g., after termination).\n * Unlike the normal setter, this method only deactivates the old current activity (and\n * non-shared ancestors) WITHOUT activating the new current activity.\n * @param {Activity | null} activity - The activity to set as current\n */\n setCurrentActivityWithoutActivation(activity: Activity | null): void {\n // noinspection SuspiciousTypeOfGuard\n if (activity !== null && !(activity instanceof Activity)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".currentActivity\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n // Deactivate previous current activity, but NOT the new current activity\n // or any shared ancestors (i.e., don't deactivate shared ancestors)\n if (this._currentActivity) {\n // Build set of activities to preserve (new current + its ancestors)\n const activitiesToPreserve = new Set<Activity>();\n if (activity) {\n activitiesToPreserve.add(activity); // Include the new current itself!\n let ancestor: Activity | null = activity.parent;\n while (ancestor) {\n activitiesToPreserve.add(ancestor);\n ancestor = ancestor.parent;\n }\n }\n\n // Deactivate old current\n this._currentActivity.isActive = false;\n\n // Deactivate old current's ancestors that are NOT in the preserve set\n let ancestor = this._currentActivity.parent;\n while (ancestor) {\n if (!activitiesToPreserve.has(ancestor)) {\n ancestor.isActive = false;\n }\n ancestor = ancestor.parent;\n }\n }\n\n // Set new current activity WITHOUT activating it or its ancestors\n // The caller is responsible for activation if needed\n this._currentActivity = activity;\n }\n\n /**\n * Getter for suspendedActivity\n * @return {Activity | null}\n */\n get suspendedActivity(): Activity | null {\n return this._suspendedActivity;\n }\n\n /**\n * Setter for suspendedActivity\n * @param {Activity | null} activity\n */\n set suspendedActivity(activity: Activity | null) {\n // noinspection SuspiciousTypeOfGuard\n if (activity !== null && !(activity instanceof Activity)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".suspendedActivity\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n\n // Unsuspend previous suspended activity and all its ancestors\n if (this._suspendedActivity) {\n this._suspendedActivity.isSuspended = false;\n let ancestor = this._suspendedActivity.parent;\n while (ancestor) {\n ancestor.isSuspended = false;\n ancestor = ancestor.parent;\n }\n }\n\n this._suspendedActivity = activity;\n\n // Set new suspended activity and mark all ancestors as suspended\n if (activity) {\n activity.isSuspended = true;\n let ancestor = activity.parent;\n while (ancestor) {\n ancestor.isSuspended = true;\n ancestor = ancestor.parent;\n }\n }\n }\n\n /**\n * Get an activity by ID\n * @param {string} id - The ID of the activity to get\n * @return {Activity | null} - The activity with the given ID, or null if not found\n */\n getActivity(id: string): Activity | null {\n return this._activities.get(id) || null;\n }\n\n /**\n * Get all activities in the tree\n * @return {Activity[]} - An array of all activities in the tree\n */\n getAllActivities(): Activity[] {\n return Array.from(this._activities.values());\n }\n\n /**\n * Get the parent of an activity\n * @param {Activity} activity - The activity to get the parent of\n * @return {Activity | null} - The parent of the activity, or null if it has no parent\n */\n getParent(activity: Activity): Activity | null {\n return activity.parent;\n }\n\n /**\n * Get the children of an activity\n * @param {Activity} activity - The activity to get the children of\n * @param {boolean} useAvailableChildren - Whether to use available children (with selection/randomization)\n * @return {Activity[]} - An array of the activity's children\n */\n getChildren(activity: Activity, useAvailableChildren: boolean = true): Activity[] {\n return useAvailableChildren ? activity.getAvailableChildren() : activity.children;\n }\n\n /**\n * Get the siblings of an activity\n * @param {Activity} activity - The activity to get the siblings of\n * @return {Activity[]} - An array of the activity's siblings\n */\n getSiblings(activity: Activity): Activity[] {\n if (!activity.parent) {\n return [];\n }\n return activity.parent.children.filter((child) => child !== activity);\n }\n\n /**\n * Get the next sibling of an activity\n * @param {Activity} activity - The activity to get the next sibling of\n * @param {boolean} useAvailableChildren - Whether to use available children (with selection/randomization)\n * @return {Activity | null} - The next sibling of the activity, or null if it has no next sibling\n */\n getNextSibling(activity: Activity, useAvailableChildren: boolean = true): Activity | null {\n if (!activity.parent) {\n return null;\n }\n let siblings = useAvailableChildren \n ? activity.parent.getAvailableChildren() \n : activity.parent.children;\n let index = siblings.indexOf(activity);\n \n // Fallback: if not found in available children, try raw children\n if (index === -1 && useAvailableChildren) {\n siblings = activity.parent.children;\n index = siblings.indexOf(activity);\n }\n \n if (index === -1 || index === siblings.length - 1) {\n return null;\n }\n return siblings[index + 1] ?? null;\n }\n\n /**\n * Get the previous sibling of an activity\n * @param {Activity} activity - The activity to get the previous sibling of\n * @param {boolean} useAvailableChildren - Whether to use available children (with selection/randomization)\n * @return {Activity | null} - The previous sibling of the activity, or null if it has no previous sibling\n */\n getPreviousSibling(activity: Activity, useAvailableChildren: boolean = true): Activity | null {\n if (!activity.parent) {\n return null;\n }\n let siblings = useAvailableChildren \n ? activity.parent.getAvailableChildren() \n : activity.parent.children;\n let index = siblings.indexOf(activity);\n \n // Fallback: if not found in available children, try raw children\n if (index === -1 && useAvailableChildren) {\n siblings = activity.parent.children;\n index = siblings.indexOf(activity);\n }\n \n if (index <= 0) {\n return null;\n }\n return siblings[index - 1] ?? null;\n }\n\n /**\n * Get the first child of an activity\n * @param {Activity} activity - The activity to get the first child of\n * @param {boolean} useAvailableChildren - Whether to use available children (with selection/randomization)\n * @return {Activity | null} - The first child of the activity, or null if it has no children\n */\n getFirstChild(activity: Activity, useAvailableChildren: boolean = true): Activity | null {\n const children = useAvailableChildren \n ? activity.getAvailableChildren() \n : activity.children;\n if (children.length === 0) {\n return null;\n }\n return children[0] ?? null;\n }\n\n /**\n * Get the last child of an activity\n * @param {Activity} activity - The activity to get the last child of\n * @param {boolean} useAvailableChildren - Whether to use available children (with selection/randomization)\n * @return {Activity | null} - The last child of the activity, or null if it has no children\n */\n getLastChild(activity: Activity, useAvailableChildren: boolean = true): Activity | null {\n const children = useAvailableChildren \n ? activity.getAvailableChildren() \n : activity.children;\n if (children.length === 0) {\n return null;\n }\n return children[children.length - 1] ?? null;\n }\n\n /**\n * Get the common ancestor of two activities\n * @param {Activity} activity1 - The first activity\n * @param {Activity} activity2 - The second activity\n * @return {Activity | null} - The common ancestor of the two activities, or null if they have no common ancestor\n */\n getCommonAncestor(activity1: Activity, activity2: Activity): Activity | null {\n // Get the path from the root to activity1\n const path1: Activity[] = [];\n let current: Activity | null = activity1;\n while (current) {\n path1.unshift(current);\n current = current.parent;\n }\n\n // Check if activity2 is in the path from the root to activity1\n current = activity2;\n while (current) {\n if (path1.includes(current)) {\n return current;\n }\n current = current.parent;\n }\n\n return null;\n }\n\n /**\n * toJSON for ActivityTree\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n root: this._root,\n currentActivity: this._currentActivity ? this._currentActivity.id : null,\n suspendedActivity: this._suspendedActivity ? this._suspendedActivity.id : null,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { BaseCMI } from \"../../common/base_cmi\";\nimport { Activity } from \"./activity\";\nimport { ActivityTree } from \"./activity_tree\";\nimport { SequencingRules } from \"./sequencing_rules\";\nimport { SequencingControls } from \"./sequencing_controls\";\nimport { RollupRules } from \"./rollup_rules\";\nimport { ADLNav } from \"../adl\";\nimport { Scorm2004ValidationError } from \"../../../exceptions/scorm2004_exceptions\";\nimport { scorm2004_errors } from \"../../../constants/error_codes\";\nimport { AuxiliaryResource, HideLmsUiItem } from \"../../../types/sequencing_types\";\n\n// Forward declaration to avoid circular dependency\n \ntype OverallSequencingProcessType = any;\n\n/**\n * Class representing SCORM 2004 sequencing\n */\nexport class Sequencing extends BaseCMI {\n private _activityTree: ActivityTree;\n private _sequencingRules: SequencingRules;\n private _sequencingControls: SequencingControls;\n private _rollupRules: RollupRules;\n private _adlNav: ADLNav | null = null;\n private _hideLmsUi: HideLmsUiItem[] = [];\n private _auxiliaryResources: AuxiliaryResource[] = [];\n private _overallSequencingProcess: OverallSequencingProcessType | null = null;\n\n /**\n * Constructor for Sequencing\n */\n constructor() {\n super(\"sequencing\");\n this._activityTree = new ActivityTree();\n this._sequencingRules = new SequencingRules();\n this._sequencingControls = new SequencingControls();\n this._rollupRules = new RollupRules();\n }\n\n /**\n * Called when the API has been initialized after the CMI has been created\n */\n override initialize() {\n super.initialize();\n this._activityTree.initialize();\n this._sequencingRules.initialize();\n this._sequencingControls.initialize();\n this._rollupRules.initialize();\n \n }\n\n /**\n * Called when the API needs to be reset\n */\n reset() {\n this._initialized = false;\n this._activityTree.reset();\n this._sequencingRules.reset();\n this._sequencingControls.reset();\n this._rollupRules.reset();\n this._hideLmsUi = [];\n this._auxiliaryResources = [];\n }\n\n /**\n * Getter for activityTree\n * @return {ActivityTree}\n */\n get activityTree(): ActivityTree {\n return this._activityTree;\n }\n\n /**\n * Setter for activityTree\n * @param {ActivityTree} activityTree\n */\n set activityTree(activityTree: ActivityTree) {\n // noinspection SuspiciousTypeOfGuard\n if (!(activityTree instanceof ActivityTree)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".activityTree\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._activityTree = activityTree;\n }\n\n /**\n * Getter for sequencingRules\n * @return {SequencingRules}\n */\n get sequencingRules(): SequencingRules {\n return this._sequencingRules;\n }\n\n /**\n * Setter for sequencingRules\n * @param {SequencingRules} sequencingRules\n */\n set sequencingRules(sequencingRules: SequencingRules) {\n // noinspection SuspiciousTypeOfGuard\n if (!(sequencingRules instanceof SequencingRules)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".sequencingRules\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._sequencingRules = sequencingRules;\n }\n\n /**\n * Getter for sequencingControls\n * @return {SequencingControls}\n */\n get sequencingControls(): SequencingControls {\n return this._sequencingControls;\n }\n\n /**\n * Setter for sequencingControls\n * @param {SequencingControls} sequencingControls\n */\n set sequencingControls(sequencingControls: SequencingControls) {\n // noinspection SuspiciousTypeOfGuard\n if (!(sequencingControls instanceof SequencingControls)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".sequencingControls\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._sequencingControls = sequencingControls;\n }\n\n get hideLmsUi(): HideLmsUiItem[] {\n return [...this._hideLmsUi];\n }\n\n set hideLmsUi(items: HideLmsUiItem[]) {\n this._hideLmsUi = [...items];\n }\n\n get auxiliaryResources(): AuxiliaryResource[] {\n return this._auxiliaryResources.map((resource) => ({ ...resource }));\n }\n\n set auxiliaryResources(resources: AuxiliaryResource[]) {\n this._auxiliaryResources = resources.map((resource) => ({ ...resource }));\n }\n\n /**\n * Getter for rollupRules\n * @return {RollupRules}\n */\n get rollupRules(): RollupRules {\n return this._rollupRules;\n }\n\n /**\n * Setter for rollupRules\n * @param {RollupRules} rollupRules\n */\n set rollupRules(rollupRules: RollupRules) {\n // noinspection SuspiciousTypeOfGuard\n if (!(rollupRules instanceof RollupRules)) {\n throw new Scorm2004ValidationError(\n this._cmi_element + \".rollupRules\",\n scorm2004_errors.TYPE_MISMATCH as number,\n );\n }\n this._rollupRules = rollupRules;\n }\n\n /**\n * Getter for adlNav\n * @return {ADLNav | null}\n */\n get adlNav(): ADLNav | null {\n return this._adlNav;\n }\n\n /**\n * Setter for adlNav\n * @param {ADLNav | null} adlNav\n */\n set adlNav(adlNav: ADLNav | null) {\n this._adlNav = adlNav;\n\n }\n\n /**\n * Getter for overallSequencingProcess\n * @return {any | null}\n */\n get overallSequencingProcess(): OverallSequencingProcessType | null {\n return this._overallSequencingProcess;\n }\n\n /**\n * Setter for overallSequencingProcess\n * @param {any | null} process\n */\n set overallSequencingProcess(process: OverallSequencingProcessType | null) {\n this._overallSequencingProcess = process;\n }\n\n\n\n /**\n * Process rollup for the entire activity tree\n */\n processRollup(): void {\n // Get the root activity\n const root = this._activityTree.root;\n if (!root) {\n return;\n }\n\n // Process rollup from the bottom up\n this._processRollupRecursive(root);\n }\n\n\n /**\n * Process rollup recursively\n * @param {Activity} activity - The activity to process rollup for\n * @private\n */\n private _processRollupRecursive(activity: Activity): void {\n // Process rollup for children first\n for (const child of activity.children) {\n this._processRollupRecursive(child);\n }\n\n // Process rollup for this activity\n this._rollupRules.processRollup(activity);\n }\n\n\n /**\n * Get the current activity\n * @return {Activity | null}\n */\n getCurrentActivity(): Activity | null {\n return this._activityTree.currentActivity;\n }\n\n /**\n * Get the root activity\n * @return {Activity | null}\n */\n getRootActivity(): Activity | null {\n return this._activityTree.root;\n }\n\n /**\n * toJSON for Sequencing\n * @return {object}\n */\n toJSON(): object {\n this.jsonString = true;\n const result = {\n activityTree: this._activityTree,\n sequencingRules: this._sequencingRules,\n sequencingControls: this._sequencingControls,\n rollupRules: this._rollupRules,\n adlNav: this._adlNav,\n };\n this.jsonString = false;\n return result;\n }\n}\n","import { CMIArray } from \"../../cmi/common/array\";\nimport {\n CMIInteractionsCorrectResponsesObject,\n CMIInteractionsObject,\n} from \"../../cmi/scorm2004/interactions\";\nimport {\n CorrectResponses,\n ResponseType,\n scorm2004_errors,\n scorm2004_regex,\n ValidLanguages,\n} from \"../../constants\";\n\n/**\n * Context interface for error handling callbacks\n */\nexport interface ValidationContext {\n throwSCORMError: (element: string, errorCode: number, message?: string) => void;\n getLastErrorCode: () => string;\n /** Optional callback to delegate checkCorrectResponseValue to the API (for test spying) */\n checkCorrectResponseValue?: (\n CMIElement: string,\n interaction_type: string,\n nodes: Array<any>,\n value: any,\n ) => void;\n}\n\n/**\n * Handles validation of SCORM 2004 correct responses for interactions\n *\n * This class is responsible for:\n * - Validating response types against SCORM 2004 specifications\n * - Checking for duplicate choice responses\n * - Validating correct response values\n * - Removing and validating response prefixes\n */\nexport class Scorm2004ResponseValidator {\n private context: ValidationContext;\n\n constructor(context: ValidationContext) {\n this.context = context;\n }\n\n /**\n * Checks for valid response types\n * @param {string} CMIElement - The CMI element path\n * @param {ResponseType} response_type - The response type configuration\n * @param {any} value - The value to validate\n * @param {string} interaction_type - The type of interaction\n */\n checkValidResponseType(\n CMIElement: string,\n response_type: ResponseType,\n value: any,\n interaction_type: string,\n ): void {\n let nodes: any[] = [];\n if (response_type?.delimiter) {\n nodes = String(value).split(response_type.delimiter);\n } else {\n nodes[0] = value;\n }\n\n if (nodes.length > 0 && nodes.length <= response_type.max) {\n // Use context callback if available (for API integration), otherwise call directly\n if (this.context.checkCorrectResponseValue) {\n this.context.checkCorrectResponseValue(CMIElement, interaction_type, nodes, value);\n } else {\n this.checkCorrectResponseValue(CMIElement, interaction_type, nodes, value);\n }\n } else if (nodes.length > response_type.max) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.GENERAL_SET_FAILURE!,\n `Data Model Element Pattern Too Long: ${value}`,\n );\n }\n }\n\n /**\n * Checks for duplicate 'choice' responses\n * @param {string} CMIElement - The CMI element path\n * @param {CMIInteractionsObject} interaction - The interaction object\n * @param {any} value - The value to check for duplicates\n */\n checkDuplicateChoiceResponse(\n CMIElement: string,\n interaction: CMIInteractionsObject,\n value: any,\n ): void {\n const interaction_count = interaction.correct_responses._count;\n if (interaction.type === \"choice\") {\n for (let i = 0; i < interaction_count && this.context.getLastErrorCode() === \"0\"; i++) {\n const response = interaction.correct_responses.childArray[i] as\n | CMIInteractionsCorrectResponsesObject\n | undefined;\n if (response?.pattern === value) {\n this.context.throwSCORMError(CMIElement, scorm2004_errors.GENERAL_SET_FAILURE!, `${value}`);\n }\n }\n }\n }\n\n /**\n * Validate correct response\n * @param {string} CMIElement - The CMI element path\n * @param {CMIInteractionsObject} interaction - The interaction object\n * @param {*} value - The value to validate\n */\n validateCorrectResponse(CMIElement: string, interaction: CMIInteractionsObject, value: any): void {\n const parts = CMIElement.split(\".\");\n const pattern_index = Number(parts[4]);\n\n if (!interaction) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED!,\n CMIElement,\n );\n return;\n }\n\n const interaction_count = interaction.correct_responses._count;\n this.checkDuplicateChoiceResponse(CMIElement, interaction, value);\n\n const response_type = CorrectResponses[interaction.type];\n if (\n response_type &&\n (typeof response_type.limit === \"undefined\" || interaction_count < response_type.limit)\n ) {\n this.checkValidResponseType(CMIElement, response_type, value, interaction.type);\n\n if (\n (this.context.getLastErrorCode() === \"0\" &&\n (!response_type.duplicate ||\n !this.checkDuplicatedPattern(\n interaction.correct_responses,\n pattern_index,\n value,\n ))) ||\n (this.context.getLastErrorCode() === \"0\" && value === \"\")\n ) {\n // do nothing, we want the inverse\n } else {\n if (this.context.getLastErrorCode() === \"0\") {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.GENERAL_SET_FAILURE!,\n `Data Model Element Pattern Already Exists: ${CMIElement} - ${value}`,\n );\n }\n }\n } else {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.GENERAL_SET_FAILURE!,\n `Data Model Element Collection Limit Reached: ${CMIElement} - ${value}`,\n );\n }\n }\n\n /**\n * Check to see if a correct_response value has been duplicated\n * @param {CMIArray} correct_response - The correct responses array\n * @param {number} current_index - The current index to skip\n * @param {*} value - The value to check for duplicates\n * @return {boolean} True if pattern is duplicated\n */\n checkDuplicatedPattern(correct_response: CMIArray, current_index: number, value: any): boolean {\n let found = false;\n const count = correct_response._count;\n for (let i = 0; i < count && !found; i++) {\n if (i !== current_index) {\n const item = correct_response.childArray[i] as\n | CMIInteractionsCorrectResponsesObject\n | undefined;\n const existingPattern = item?.pattern;\n if (existingPattern === value) {\n found = true;\n }\n }\n }\n return found;\n }\n\n /**\n * Checks for a valid correct_response value\n * @param {string} CMIElement - The CMI element path\n * @param {string} interaction_type - The type of interaction\n * @param {Array} nodes - Array of parsed response nodes\n * @param {*} value - The original value\n */\n checkCorrectResponseValue(\n CMIElement: string,\n interaction_type: string,\n nodes: Array<any>,\n value: any,\n ): void {\n const response = CorrectResponses[interaction_type];\n if (!response) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.TYPE_MISMATCH!,\n `Incorrect Response Type: ${interaction_type}`,\n );\n return;\n }\n const formatRegex = new RegExp(response.format);\n for (let i = 0; i < nodes.length && this.context.getLastErrorCode() === \"0\"; i++) {\n if (interaction_type.match(\"^(fill-in|long-fill-in|matching|performance|sequencing)$\")) {\n nodes[i] = this.removeCorrectResponsePrefixes(CMIElement, nodes[i]);\n }\n\n if (response?.delimiter2) {\n const values = nodes[i].split(response.delimiter2);\n if (values.length === 2) {\n const matches = values[0].match(formatRegex);\n if (!matches) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.TYPE_MISMATCH!,\n `${interaction_type}: ${value}`,\n );\n } else {\n if (!response.format2 || !values[1].match(new RegExp(response.format2))) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.TYPE_MISMATCH!,\n `${interaction_type}: ${value}`,\n );\n }\n }\n } else {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.TYPE_MISMATCH!,\n `${interaction_type}: ${value}`,\n );\n }\n } else {\n const matches = nodes[i].match(formatRegex);\n if ((!matches && value !== \"\") || (!matches && interaction_type === \"true-false\")) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.TYPE_MISMATCH!,\n `${interaction_type}: ${value}`,\n );\n } else {\n if (interaction_type === \"numeric\" && nodes.length > 1) {\n if (Number(nodes[0]) > Number(nodes[1])) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.TYPE_MISMATCH!,\n `${interaction_type}: ${value}`,\n );\n }\n } else {\n if (nodes[i] !== \"\" && response.unique) {\n for (let j = 0; j < i && this.context.getLastErrorCode() === \"0\"; j++) {\n if (nodes[i] === nodes[j]) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.TYPE_MISMATCH!,\n `${interaction_type}: ${value}`,\n );\n }\n }\n }\n }\n }\n }\n }\n }\n\n /**\n * Remove prefixes from correct_response\n * @param {string} CMIElement - The CMI element path\n * @param {string} node - The node string with potential prefixes\n * @return {*} The node with prefixes removed\n */\n removeCorrectResponsePrefixes(CMIElement: string, node: string): any {\n let seenOrder = false;\n let seenCase = false;\n let seenLang = false;\n\n const prefixRegex = new RegExp(\"^({(lang|case_matters|order_matters)=([^}]+)})\");\n let matches = node.match(prefixRegex);\n let langMatches: RegExpMatchArray | null;\n while (matches) {\n switch (matches[2]) {\n case \"lang\":\n langMatches = node.match(scorm2004_regex.CMILangcr);\n if (langMatches) {\n const lang = langMatches[3];\n if (lang !== undefined && lang.length > 0) {\n if (!ValidLanguages.includes(lang.toLowerCase())) {\n this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH!, `${node}`);\n }\n }\n }\n seenLang = true;\n break;\n case \"case_matters\":\n if (!seenLang && !seenOrder && !seenCase) {\n if (matches[3] !== \"true\" && matches[3] !== \"false\") {\n this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH!, `${node}`);\n }\n }\n\n seenCase = true;\n break;\n case \"order_matters\":\n if (!seenCase && !seenLang && !seenOrder) {\n if (matches[3] !== \"true\" && matches[3] !== \"false\") {\n this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH!, `${node}`);\n }\n }\n\n seenOrder = true;\n break;\n }\n node = node.substring(matches[1]?.length || 0);\n matches = node.match(prefixRegex);\n }\n\n return node;\n }\n}\n","import { BaseCMI } from \"../../cmi/common/base_cmi\";\nimport { CMI } from \"../../cmi/scorm2004/cmi\";\nimport { CMIObjectivesObject } from \"../../cmi/scorm2004/objectives\";\nimport {\n CMIInteractionsCorrectResponsesObject,\n CMIInteractionsObject,\n CMIInteractionsObjectivesObject,\n} from \"../../cmi/scorm2004/interactions\";\nimport { CMICommentsObject } from \"../../cmi/scorm2004/comments\";\nimport { ADLDataObject } from \"../../cmi/scorm2004/adl\";\nimport {\n CompletionStatus,\n CorrectResponses,\n scorm2004_errors,\n SuccessStatus,\n} from \"../../constants\";\nimport { stringMatches } from \"../../utilities\";\nimport { Scorm2004ResponseValidator } from \"./response_validator\";\n\n/**\n * Context interface for CMI handler operations\n */\nexport interface CMIHandlerContext {\n cmi: CMI;\n isInitialized: () => boolean;\n throwSCORMError: (element: string, errorCode: number, message?: string) => void;\n getLastErrorCode: () => string;\n}\n\n/**\n * Handles CMI data model operations for SCORM 2004\n *\n * This class is responsible for:\n * - Creating child elements for arrays (objectives, interactions, comments)\n * - Creating correct response objects for interactions\n * - Evaluating completion status based on SCORM 2004 RTE rules\n * - Evaluating success status based on SCORM 2004 RTE rules\n */\nexport class Scorm2004CMIHandler {\n private context: CMIHandlerContext;\n private responseValidator: Scorm2004ResponseValidator;\n\n constructor(context: CMIHandlerContext, responseValidator: Scorm2004ResponseValidator) {\n this.context = context;\n this.responseValidator = responseValidator;\n }\n\n /**\n * Gets or builds a new child element to add to the array\n *\n * @param {string} CMIElement - The CMI element path\n * @param {any} value - The value being set\n * @param {boolean} foundFirstIndex - Whether the first index was found\n * @return {BaseCMI|null} The child element or null\n */\n getChildElement(CMIElement: string, value: any, foundFirstIndex: boolean): BaseCMI | null {\n if (stringMatches(CMIElement, \"cmi\\\\.objectives\\\\.\\\\d+\")) {\n return new CMIObjectivesObject();\n }\n\n if (foundFirstIndex) {\n if (stringMatches(CMIElement, \"cmi\\\\.interactions\\\\.\\\\d+\\\\.correct_responses\\\\.\\\\d+\")) {\n return this.createCorrectResponsesObject(CMIElement, value);\n } else if (stringMatches(CMIElement, \"cmi\\\\.interactions\\\\.\\\\d+\\\\.objectives\\\\.\\\\d+\")) {\n return new CMIInteractionsObjectivesObject();\n }\n } else if (stringMatches(CMIElement, \"cmi\\\\.interactions\\\\.\\\\d+\")) {\n return new CMIInteractionsObject();\n }\n\n if (stringMatches(CMIElement, \"cmi\\\\.comments_from_learner\\\\.\\\\d+\")) {\n return new CMICommentsObject();\n } else if (stringMatches(CMIElement, \"cmi\\\\.comments_from_lms\\\\.\\\\d+\")) {\n return new CMICommentsObject(true);\n }\n\n if (stringMatches(CMIElement, \"adl\\\\.data\\\\.\\\\d+\")) {\n // Note: SCORM 2004 4th Edition adl.data extension\n // Per strict spec, adl.data elements should be LMS-created and\n // SCOs should only access indices < _count. However, we intentionally\n // allow dynamic creation for backward compatibility with content that\n // creates adl.data elements on-the-fly.\n return new ADLDataObject();\n }\n\n return null;\n }\n\n /**\n * Creates a correct responses object for an interaction\n *\n * @param {string} CMIElement - The CMI element path\n * @param {any} value - The value being set\n * @return {BaseCMI|null} The correct responses object or null\n */\n createCorrectResponsesObject(CMIElement: string, value: any): BaseCMI | null {\n const parts = CMIElement.split(\".\");\n const index = Number(parts[2]);\n const interaction = this.context.cmi.interactions.childArray[index] as\n | CMIInteractionsObject\n | undefined;\n\n if (this.context.isInitialized()) {\n if (typeof interaction === \"undefined\" || !interaction.type) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED!,\n CMIElement,\n );\n return null;\n } else {\n const interaction_count = interaction.correct_responses._count;\n const response_type = CorrectResponses[interaction.type];\n\n // Check if limit is exceeded\n if (\n response_type &&\n typeof response_type.limit !== \"undefined\" &&\n interaction_count >= response_type.limit\n ) {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.GENERAL_SET_FAILURE!,\n `Data Model Element Collection Limit Reached: ${CMIElement}`,\n );\n return null;\n }\n\n this.responseValidator.checkDuplicateChoiceResponse(CMIElement, interaction, value);\n if (response_type) {\n this.responseValidator.checkValidResponseType(\n CMIElement,\n response_type,\n value,\n interaction.type,\n );\n } else {\n this.context.throwSCORMError(\n CMIElement,\n scorm2004_errors.GENERAL_SET_FAILURE!,\n `Incorrect Response Type: ${interaction.type}`,\n );\n return null;\n }\n }\n }\n\n if (this.context.getLastErrorCode() === \"0\") {\n return new CMIInteractionsCorrectResponsesObject(interaction?.type);\n }\n\n return null;\n }\n\n /**\n * Evaluates completion_status per SCORM 2004 RTE Table 4.2.4.1a\n *\n * Rules:\n * 1. If completion_threshold is defined AND progress_measure is set:\n * - Return \"completed\" if progress_measure >= completion_threshold\n * - Return \"incomplete\" if progress_measure < completion_threshold\n * 2. If completion_threshold is defined but progress_measure is NOT set:\n * - Return \"unknown\"\n * 3. Otherwise:\n * - Return the SCO-set value (or \"unknown\" if not set)\n *\n * @returns {string} The evaluated completion status\n */\n evaluateCompletionStatus(): string {\n const threshold = this.context.cmi.completion_threshold;\n const progressMeasure = this.context.cmi.progress_measure;\n const storedStatus = this.context.cmi.completion_status;\n\n // If completion_threshold is defined\n if (threshold !== \"\" && threshold !== null && threshold !== undefined) {\n const thresholdValue = parseFloat(String(threshold));\n\n if (!isNaN(thresholdValue)) {\n // Check if progress_measure is set\n if (progressMeasure !== \"\" && progressMeasure !== null && progressMeasure !== undefined) {\n const progressValue = parseFloat(String(progressMeasure));\n\n if (!isNaN(progressValue)) {\n // Evaluate based on threshold comparison\n return progressValue >= thresholdValue\n ? CompletionStatus.COMPLETED\n : CompletionStatus.INCOMPLETE;\n }\n }\n\n // completion_threshold is defined but progress_measure is not set\n return CompletionStatus.UNKNOWN;\n }\n }\n\n // No completion_threshold defined - return stored value\n return storedStatus || CompletionStatus.UNKNOWN;\n }\n\n /**\n * Evaluates success_status per SCORM 2004 RTE Table 4.2.21.1a\n *\n * Rules:\n * 1. If scaled_passing_score is defined AND score.scaled is set:\n * - Return \"passed\" if score.scaled >= scaled_passing_score\n * - Return \"failed\" if score.scaled < scaled_passing_score\n * 2. If scaled_passing_score is defined but score.scaled is NOT set:\n * - Return \"unknown\"\n * 3. Otherwise:\n * - Return the SCO-set value (or \"unknown\" if not set)\n *\n * @returns {string} The evaluated success status\n */\n evaluateSuccessStatus(): string {\n const scaledPassingScore = this.context.cmi.scaled_passing_score;\n const scaledScore = this.context.cmi.score.scaled;\n const storedStatus = this.context.cmi.success_status;\n\n // If scaled_passing_score is defined\n if (\n scaledPassingScore !== \"\" &&\n scaledPassingScore !== null &&\n scaledPassingScore !== undefined\n ) {\n const passingScoreValue = parseFloat(String(scaledPassingScore));\n\n if (!isNaN(passingScoreValue)) {\n // Check if score.scaled is set\n if (scaledScore !== \"\" && scaledScore !== null && scaledScore !== undefined) {\n const scoreValue = parseFloat(String(scaledScore));\n\n if (!isNaN(scoreValue)) {\n // Evaluate based on threshold comparison\n return scoreValue >= passingScoreValue ? SuccessStatus.PASSED : SuccessStatus.FAILED;\n }\n }\n\n // scaled_passing_score is defined but score.scaled is not set\n return SuccessStatus.UNKNOWN;\n }\n }\n\n // No scaled_passing_score defined - return stored value\n return storedStatus || SuccessStatus.UNKNOWN;\n }\n}\n","import { SequencingControls } from \"../cmi/scorm2004/sequencing/sequencing_controls\";\nimport {\n RuleCondition,\n SequencingRule,\n SequencingRules,\n} from \"../cmi/scorm2004/sequencing/sequencing_rules\";\nimport { RollupCondition, RollupRule, RollupRules } from \"../cmi/scorm2004/sequencing/rollup_rules\";\nimport {\n AuxiliaryResource,\n AuxiliaryResourceSettings,\n HIDE_LMS_UI_TOKENS,\n HideLmsUiItem,\n RollupConditionSettings,\n RollupRuleSettings,\n RollupRulesSettings,\n RuleConditionSettings,\n SelectionRandomizationStateSettings,\n SequencingCollectionSettings,\n SequencingControlsSettings,\n SequencingRuleSettings,\n SequencingRulesSettings,\n} from \"../types/sequencing_types\";\nimport { Activity } from \"../cmi/scorm2004/sequencing/activity\";\n\n/**\n * Builds and configures SCORM 2004 sequencing rules, controls, and collections\n *\n * This class is responsible for:\n * - Applying sequencing controls settings\n * - Creating and configuring sequencing rules\n * - Creating and configuring rollup rules\n * - Sanitizing sequencing collections\n * - Managing HideLmsUi and auxiliary resources\n */\nexport class SequencingConfigurationBuilder {\n /**\n * Applies the given sequencing controls settings to the specified target.\n *\n * @param {SequencingControls} target - The target object where sequencing control settings will be applied.\n * @param {SequencingControlsSettings} settings - An object containing the sequencing control settings to be applied to the target.\n * @return {void} - No return value as the method modifies the target object directly.\n */\n applySequencingControlsSettings(\n target: SequencingControls,\n settings: SequencingControlsSettings,\n ): void {\n if (settings.enabled !== undefined) {\n target.enabled = settings.enabled;\n }\n if (settings.choice !== undefined) {\n target.choice = settings.choice;\n }\n if (settings.choiceExit !== undefined) {\n target.choiceExit = settings.choiceExit;\n }\n if (settings.flow !== undefined) {\n target.flow = settings.flow;\n }\n if (settings.forwardOnly !== undefined) {\n target.forwardOnly = settings.forwardOnly;\n }\n if (settings.useCurrentAttemptObjectiveInfo !== undefined) {\n target.useCurrentAttemptObjectiveInfo = settings.useCurrentAttemptObjectiveInfo;\n }\n if (settings.useCurrentAttemptProgressInfo !== undefined) {\n target.useCurrentAttemptProgressInfo = settings.useCurrentAttemptProgressInfo;\n }\n if (settings.preventActivation !== undefined) {\n target.preventActivation = settings.preventActivation;\n }\n if (settings.constrainChoice !== undefined) {\n target.constrainChoice = settings.constrainChoice;\n }\n if (settings.stopForwardTraversal !== undefined) {\n target.stopForwardTraversal = settings.stopForwardTraversal;\n }\n if (settings.rollupObjectiveSatisfied !== undefined) {\n target.rollupObjectiveSatisfied = settings.rollupObjectiveSatisfied;\n }\n if (settings.rollupProgressCompletion !== undefined) {\n target.rollupProgressCompletion = settings.rollupProgressCompletion;\n }\n if (settings.objectiveMeasureWeight !== undefined) {\n target.objectiveMeasureWeight = settings.objectiveMeasureWeight;\n }\n if (settings.selectionTiming !== undefined) {\n target.selectionTiming = settings.selectionTiming;\n }\n if (settings.selectCount !== undefined) {\n target.selectCount = settings.selectCount;\n }\n if (settings.randomizeChildren !== undefined) {\n target.randomizeChildren = settings.randomizeChildren;\n }\n if (settings.randomizationTiming !== undefined) {\n target.randomizationTiming = settings.randomizationTiming;\n }\n if (settings.selectionCountStatus !== undefined) {\n target.selectionCountStatus = settings.selectionCountStatus;\n }\n if (settings.reorderChildren !== undefined) {\n target.reorderChildren = settings.reorderChildren;\n }\n if (settings.completionSetByContent !== undefined) {\n target.completionSetByContent = settings.completionSetByContent;\n }\n if (settings.objectiveSetByContent !== undefined) {\n target.objectiveSetByContent = settings.objectiveSetByContent;\n }\n }\n\n /**\n * Applies the sequencing rules settings to the specified target object.\n *\n * @param {SequencingRules} target The target object where the sequencing rules will be applied.\n * @param {SequencingRulesSettings} settings The settings object containing the sequencing rules to be applied. If null or undefined, no rules will be applied.\n * @return {void} This method does not return a value.\n */\n applySequencingRulesSettings(target: SequencingRules, settings: SequencingRulesSettings): void {\n if (!settings) {\n return;\n }\n\n if (settings.preConditionRules) {\n for (const ruleSettings of settings.preConditionRules) {\n const rule = this.createSequencingRule(ruleSettings);\n target.addPreConditionRule(rule);\n }\n }\n\n if (settings.exitConditionRules) {\n for (const ruleSettings of settings.exitConditionRules) {\n const rule = this.createSequencingRule(ruleSettings);\n target.addExitConditionRule(rule);\n }\n }\n\n if (settings.postConditionRules) {\n for (const ruleSettings of settings.postConditionRules) {\n const rule = this.createSequencingRule(ruleSettings);\n target.addPostConditionRule(rule);\n }\n }\n }\n\n /**\n * Create a sequencing rule from settings\n * @param {SequencingRuleSettings} ruleSettings - The sequencing rule settings\n * @return {SequencingRule} - The created sequencing rule\n */\n createSequencingRule(ruleSettings: SequencingRuleSettings): SequencingRule {\n // Create rule\n const rule = new SequencingRule(ruleSettings.action, ruleSettings.conditionCombination);\n\n // Add conditions\n for (const conditionSettings of ruleSettings.conditions) {\n const condition = new RuleCondition(\n conditionSettings.condition,\n conditionSettings.operator,\n new Map(Object.entries(conditionSettings.parameters || {})),\n );\n if (conditionSettings.referencedObjective) {\n condition.referencedObjective = conditionSettings.referencedObjective;\n }\n rule.addCondition(condition);\n }\n\n return rule;\n }\n\n /**\n * Applies rollup rules settings to the specified target object.\n * This method processes the provided settings and adds the corresponding\n * rollup rules to the target.\n *\n * @param {RollupRules} target - The target object where rollup rules will be applied.\n * @param {RollupRulesSettings} settings - The settings containing the rollup rules to be applied.\n * @return {void} This method does not return a value.\n */\n applyRollupRulesSettings(target: RollupRules, settings: RollupRulesSettings): void {\n if (!settings?.rules) {\n return;\n }\n\n for (const ruleSettings of settings.rules) {\n const rule = this.createRollupRule(ruleSettings);\n target.addRule(rule);\n }\n }\n\n /**\n * Create a rollup rule from settings\n * @param {RollupRuleSettings} ruleSettings - The rollup rule settings\n * @return {RollupRule} - The created rollup rule\n */\n createRollupRule(ruleSettings: RollupRuleSettings): RollupRule {\n // Create rule\n const rule = new RollupRule(\n ruleSettings.action,\n ruleSettings.consideration,\n ruleSettings.minimumCount,\n ruleSettings.minimumPercent,\n );\n\n // Add conditions\n for (const conditionSettings of ruleSettings.conditions) {\n const condition = new RollupCondition(\n conditionSettings.condition,\n new Map(Object.entries(conditionSettings.parameters || {})),\n );\n rule.addCondition(condition);\n }\n\n return rule;\n }\n\n /**\n * Sanitizes and processes a collection of sequencing settings. This involves ensuring that\n * the IDs are trimmed and non-empty, cloning objects deeply to ensure immutability,\n * and sanitizing each subset of sequencing configurations.\n *\n * @param {Record<string, SequencingCollectionSettings>} [collections] -\n * A record of sequencing collection settings where keys are collection IDs\n * and values are the associated configuration to be sanitized.\n *\n * @return {Record<string, SequencingCollectionSettings>}\n * A sanitized record of sequencing collection settings, with processed and\n * cloned settings for immutability and validity.\n */\n sanitizeSequencingCollections(\n collections?: Record<string, SequencingCollectionSettings>,\n ): Record<string, SequencingCollectionSettings> {\n if (!collections) {\n return {};\n }\n\n const sanitized: Record<string, SequencingCollectionSettings> = {};\n for (const [id, collection] of Object.entries(collections)) {\n const trimmedId = id.trim();\n if (!trimmedId) {\n continue;\n }\n\n const sanitizedCollection: SequencingCollectionSettings = {};\n\n if (collection.sequencingControls) {\n sanitizedCollection.sequencingControls = { ...collection.sequencingControls };\n }\n if (collection.sequencingRules) {\n const ruleClone = (rule: SequencingRuleSettings): SequencingRuleSettings => {\n const cloned: SequencingRuleSettings = {\n action: rule.action,\n conditions: rule.conditions.map((condition) => {\n const clonedCondition: RuleConditionSettings = {\n condition: condition.condition,\n };\n if (condition.operator !== undefined) {\n clonedCondition.operator = condition.operator;\n }\n if (condition.parameters) {\n clonedCondition.parameters = { ...condition.parameters };\n }\n if (condition.referencedObjective !== undefined) {\n clonedCondition.referencedObjective = condition.referencedObjective;\n }\n return clonedCondition;\n }),\n };\n if (rule.conditionCombination !== undefined) {\n cloned.conditionCombination = rule.conditionCombination;\n }\n return cloned;\n };\n\n sanitizedCollection.sequencingRules = {};\n if (collection.sequencingRules.preConditionRules) {\n sanitizedCollection.sequencingRules.preConditionRules =\n collection.sequencingRules.preConditionRules.map(ruleClone);\n }\n if (collection.sequencingRules.exitConditionRules) {\n sanitizedCollection.sequencingRules.exitConditionRules =\n collection.sequencingRules.exitConditionRules.map(ruleClone);\n }\n if (collection.sequencingRules.postConditionRules) {\n sanitizedCollection.sequencingRules.postConditionRules =\n collection.sequencingRules.postConditionRules.map(ruleClone);\n }\n }\n\n if (collection.rollupRules) {\n sanitizedCollection.rollupRules = {\n rules: collection.rollupRules.rules?.map((rule) => {\n const clonedRule: RollupRuleSettings = {\n action: rule.action,\n conditions: rule.conditions.map((condition) => {\n const clonedCondition: RollupConditionSettings = {\n condition: condition.condition,\n };\n if (condition.parameters) {\n clonedCondition.parameters = { ...condition.parameters };\n }\n return clonedCondition;\n }),\n };\n if (rule.consideration !== undefined) {\n clonedRule.consideration = rule.consideration;\n }\n if (rule.minimumCount !== undefined) {\n clonedRule.minimumCount = rule.minimumCount;\n }\n if (rule.minimumPercent !== undefined) {\n clonedRule.minimumPercent = rule.minimumPercent;\n }\n return clonedRule;\n }),\n };\n }\n\n if (collection.rollupConsiderations) {\n sanitizedCollection.rollupConsiderations = { ...collection.rollupConsiderations };\n }\n\n if (collection.selectionRandomizationState) {\n sanitizedCollection.selectionRandomizationState = this.cloneSelectionRandomizationState(\n collection.selectionRandomizationState,\n );\n }\n\n if (collection.hideLmsUi) {\n sanitizedCollection.hideLmsUi = this.sanitizeHideLmsUi(collection.hideLmsUi);\n }\n\n if (collection.auxiliaryResources) {\n sanitizedCollection.auxiliaryResources = this.sanitizeAuxiliaryResources(\n collection.auxiliaryResources,\n );\n }\n\n sanitized[trimmedId] = sanitizedCollection;\n }\n\n return sanitized;\n }\n\n /**\n * Normalizes the provided collection references into an array of unique, trimmed strings.\n * Removes duplicates and trims whitespace from each reference.\n *\n * @param {string | string[]} [refs] - A single reference string or an array of reference strings to be normalized.\n * If not provided, defaults to an empty array.\n * @return {string[]} An array of unique and trimmed strings representing normalized collection references.\n */\n normalizeCollectionRefs(refs?: string | string[]): string[] {\n if (!refs) {\n return [];\n }\n\n const raw = Array.isArray(refs) ? refs : [refs];\n const seen = new Set<string>();\n const result: string[] = [];\n\n for (const ref of raw) {\n const trimmed = ref.trim();\n if (!trimmed || seen.has(trimmed)) {\n continue;\n }\n seen.add(trimmed);\n result.push(trimmed);\n }\n\n return result;\n }\n\n /**\n * Applies the sequencing configuration from the given collection to the specified activity.\n *\n * @param {Activity} activity - The activity to which the sequencing collection settings will be applied.\n * @param {SequencingCollectionSettings} collection - The collection of sequencing settings to apply to the activity.\n * @param {SelectionRandomizationStateSettings[]} selectionStates - The list of selection randomization state objects, which may be modified during this process.\n * @return {void} This method does not return a value.\n */\n applySequencingCollection(\n activity: Activity,\n collection: SequencingCollectionSettings,\n selectionStates: SelectionRandomizationStateSettings[],\n ): void {\n if (!collection) {\n return;\n }\n\n if (collection.sequencingControls) {\n this.applySequencingControlsSettings(\n activity.sequencingControls,\n collection.sequencingControls,\n );\n }\n\n if (collection.sequencingRules) {\n this.applySequencingRulesSettings(activity.sequencingRules, collection.sequencingRules);\n }\n\n if (collection.rollupRules) {\n this.applyRollupRulesSettings(activity.rollupRules, collection.rollupRules);\n }\n\n if (collection.rollupConsiderations) {\n activity.applyRollupConsiderations(collection.rollupConsiderations);\n }\n\n if (collection.hideLmsUi) {\n const merged = this.mergeHideLmsUi(activity.hideLmsUi, collection.hideLmsUi);\n if (merged.length > 0) {\n activity.hideLmsUi = merged;\n }\n }\n\n if (collection.auxiliaryResources) {\n const sanitizedAux = this.sanitizeAuxiliaryResources(collection.auxiliaryResources);\n if (sanitizedAux.length > 0) {\n activity.auxiliaryResources = this.mergeAuxiliaryResources(\n activity.auxiliaryResources,\n sanitizedAux,\n );\n }\n }\n\n if (collection.selectionRandomizationState) {\n selectionStates.push(\n this.cloneSelectionRandomizationState(collection.selectionRandomizationState),\n );\n }\n }\n\n /**\n * Filters and sanitizes a list of items by removing duplicates and ensuring\n * only valid items are included according to a predefined set of valid tokens.\n *\n * @param {HideLmsUiItem[] | undefined} items - The list of items to be sanitized.\n * Can be undefined, in which case an empty array is returned.\n * @return {HideLmsUiItem[]} The sanitized list of unique and valid items.\n */\n sanitizeHideLmsUi(items: HideLmsUiItem[] | undefined): HideLmsUiItem[] {\n if (!items) {\n return [];\n }\n\n const valid = new Set(HIDE_LMS_UI_TOKENS);\n const seen = new Set<HideLmsUiItem>();\n const sanitized: HideLmsUiItem[] = [];\n\n for (const item of items) {\n if (valid.has(item) && !seen.has(item)) {\n seen.add(item);\n sanitized.push(item);\n }\n }\n\n return sanitized;\n }\n\n /**\n * Merges the current array of HideLmsUiItem objects with an optional additional array,\n * and sanitizes the combined result.\n *\n * @param {HideLmsUiItem[]} current - The current array of HideLmsUiItem objects.\n * @param {HideLmsUiItem[]} [additional] - An optional array of additional HideLmsUiItem objects to merge.\n * @return {HideLmsUiItem[]} The sanitized merged array of HideLmsUiItem objects.\n */\n mergeHideLmsUi(current: HideLmsUiItem[], additional?: HideLmsUiItem[]): HideLmsUiItem[] {\n if (!additional || additional.length === 0) {\n return current;\n }\n return this.sanitizeHideLmsUi([...current, ...additional]);\n }\n\n /**\n * Sanitizes and filters the given auxiliary resources by removing duplicates,\n * trimming unnecessary whitespace, and ensuring valid data integrity.\n *\n * @param {AuxiliaryResourceSettings[]} [resources] - An optional array of auxiliary resource settings\n * that include details such as resource ID and purpose.\n * @return {AuxiliaryResource[]} - A sanitized array of auxiliary resources, each containing\n * valid resource IDs and purposes, with duplicates and invalid entries removed.\n */\n sanitizeAuxiliaryResources(resources?: AuxiliaryResourceSettings[]): AuxiliaryResource[] {\n if (!resources) {\n return [];\n }\n\n const seen = new Set<string>();\n const sanitized: AuxiliaryResource[] = [];\n\n for (const resource of resources) {\n if (!resource) continue;\n\n // Type-safe null/undefined checks before calling trim()\n if (!resource.resourceId || typeof resource.resourceId !== \"string\") continue;\n if (!resource.purpose || typeof resource.purpose !== \"string\") continue;\n\n const resourceId = resource.resourceId.trim();\n const purpose = resource.purpose.trim();\n\n if (!resourceId || !purpose) continue;\n\n const key = `${resourceId}::${purpose}`;\n if (!seen.has(key)) {\n seen.add(key);\n sanitized.push({ resourceId, purpose });\n }\n }\n\n return sanitized;\n }\n\n /**\n * Merges two arrays of auxiliary resources, removing duplicates based on their resource identifiers.\n *\n * @param {AuxiliaryResource[] | undefined} existing - The existing array of auxiliary resources. This can be undefined.\n * @param {AuxiliaryResource[] | undefined} additions - The array of new auxiliary resources to add. This can be undefined.\n * @return {AuxiliaryResource[]} A new array containing unique auxiliary resources from both input arrays, filtered by their resource identifiers.\n */\n mergeAuxiliaryResources(\n existing: AuxiliaryResource[] | undefined,\n additions: AuxiliaryResource[] | undefined,\n ): AuxiliaryResource[] {\n const merged: AuxiliaryResource[] = [];\n const seen = new Set<string>();\n\n for (const resource of [...(existing ?? []), ...(additions ?? [])]) {\n if (!resource) {\n continue;\n }\n const resourceId = resource.resourceId;\n if (!resourceId || seen.has(resourceId)) {\n continue;\n }\n seen.add(resourceId);\n merged.push({ resourceId, purpose: resource.purpose });\n }\n\n return merged;\n }\n\n /**\n * Clones the given SelectionRandomizationStateSettings object, creating a new object with identical properties.\n *\n * @param {SelectionRandomizationStateSettings} state - The SelectionRandomizationStateSettings object to be cloned.\n * @return {SelectionRandomizationStateSettings} A new instance of SelectionRandomizationStateSettings with the same properties as the input object.\n */\n cloneSelectionRandomizationState(\n state: SelectionRandomizationStateSettings,\n ): SelectionRandomizationStateSettings {\n const clone: SelectionRandomizationStateSettings = {};\n if (state.childOrder) {\n clone.childOrder = [...state.childOrder];\n }\n if (state.selectedChildIds) {\n clone.selectedChildIds = [...state.selectedChildIds];\n }\n if (state.hiddenFromChoiceChildIds) {\n clone.hiddenFromChoiceChildIds = [...state.hiddenFromChoiceChildIds];\n }\n if (state.selectionCountStatus !== undefined) {\n clone.selectionCountStatus = state.selectionCountStatus;\n }\n if (state.reorderChildren !== undefined) {\n clone.reorderChildren = state.reorderChildren;\n }\n return clone;\n }\n\n /**\n * Applies the selection randomization state to the given activity by updating its sequencing controls\n * and configuring visibility, availability, and order of its child elements.\n *\n * @param {Activity} activity - The activity to which the selection randomization state is applied.\n * @param {SelectionRandomizationStateSettings} state - The settings object defining the selection\n * randomization state, including properties for selection count status, child order, reorder controls,\n * selected child IDs, and hidden child IDs.\n * @return {void} This method does not return a value.\n */\n applySelectionRandomizationState(\n activity: Activity,\n state: SelectionRandomizationStateSettings,\n ): void {\n const sequencingControls = activity.sequencingControls;\n\n if (state.selectionCountStatus !== undefined) {\n sequencingControls.selectionCountStatus = state.selectionCountStatus;\n }\n\n if (state.reorderChildren !== undefined) {\n sequencingControls.reorderChildren = state.reorderChildren;\n }\n\n const selectedSet = state.selectedChildIds ? new Set(state.selectedChildIds) : null;\n const hiddenSet = state.hiddenFromChoiceChildIds\n ? new Set(state.hiddenFromChoiceChildIds)\n : null;\n\n if (state.childOrder && state.childOrder.length > 0) {\n activity.setChildOrder(state.childOrder);\n }\n\n let selectionTouched = false;\n\n if (selectedSet || hiddenSet) {\n for (const child of activity.children) {\n if (selectedSet) {\n const isSelected = selectedSet.has(child.id);\n child.isAvailable = isSelected;\n if (!hiddenSet) {\n child.isHiddenFromChoice = !isSelected;\n }\n selectionTouched = true;\n }\n\n if (hiddenSet) {\n child.isHiddenFromChoice = hiddenSet.has(child.id);\n selectionTouched = true;\n }\n }\n }\n\n const shouldSetProcessedChildren =\n selectionTouched ||\n !!selectedSet ||\n state.selectionCountStatus !== undefined ||\n state.reorderChildren !== undefined ||\n (state.childOrder && state.childOrder.length > 0);\n\n if (shouldSetProcessedChildren) {\n activity.setProcessedChildren(activity.children.filter((child) => child.isAvailable));\n }\n }\n}\n","import {\n Activity,\n ActivityObjective,\n ObjectiveMapInfo,\n} from \"../cmi/scorm2004/sequencing/activity\";\nimport {\n ActivitySettings,\n ObjectiveSettings,\n SelectionRandomizationStateSettings,\n SequencingCollectionSettings,\n} from \"../types/sequencing_types\";\nimport { SequencingConfigurationBuilder } from \"./sequencing_configuration_builder\";\n\n/**\n * Builds and configures SCORM 2004 activity trees from settings\n *\n * This class is responsible for:\n * - Creating activities from settings\n * - Extracting activity IDs from the tree\n * - Creating activity objectives from settings\n * - Managing selection randomization state\n */\nexport class ActivityTreeBuilder {\n private sequencingCollections: Record<string, SequencingCollectionSettings>;\n private sequencingConfigBuilder: SequencingConfigurationBuilder;\n\n constructor(\n collections?: Record<string, SequencingCollectionSettings>,\n sequencingConfigBuilder?: SequencingConfigurationBuilder,\n ) {\n this.sequencingCollections = collections || {};\n this.sequencingConfigBuilder = sequencingConfigBuilder || new SequencingConfigurationBuilder();\n }\n\n /**\n * Updates the sequencing collections\n * @param {Record<string, SequencingCollectionSettings>} collections - The new collections\n */\n setSequencingCollections(collections: Record<string, SequencingCollectionSettings>): void {\n this.sequencingCollections = collections;\n }\n\n /**\n * Create an activity from settings\n * @param {ActivitySettings} activitySettings - The activity settings\n * @return {Activity} - The created activity\n */\n createActivity(activitySettings: ActivitySettings): Activity {\n // Create activity\n const activity = new Activity(activitySettings.id, activitySettings.title);\n\n const selectionStates: SelectionRandomizationStateSettings[] = [];\n const collectionRefs = this.sequencingConfigBuilder.normalizeCollectionRefs(\n activitySettings.sequencingCollectionRefs,\n );\n\n for (const ref of collectionRefs) {\n const collection = this.sequencingCollections[ref];\n if (collection) {\n this.sequencingConfigBuilder.applySequencingCollection(\n activity,\n collection,\n selectionStates,\n );\n }\n }\n\n // Set activity properties\n if (activitySettings.isVisible !== undefined) {\n activity.isVisible = activitySettings.isVisible;\n }\n if (activitySettings.isActive !== undefined) {\n activity.isActive = activitySettings.isActive;\n }\n if (activitySettings.isSuspended !== undefined) {\n activity.isSuspended = activitySettings.isSuspended;\n }\n if (activitySettings.isCompleted !== undefined) {\n activity.isCompleted = activitySettings.isCompleted;\n }\n if (activitySettings.isHiddenFromChoice !== undefined) {\n activity.isHiddenFromChoice = activitySettings.isHiddenFromChoice;\n }\n if (activitySettings.isAvailable !== undefined) {\n activity.isAvailable = activitySettings.isAvailable;\n }\n if (activitySettings.attemptLimit !== undefined) {\n activity.attemptLimit = activitySettings.attemptLimit;\n }\n if (activitySettings.attemptAbsoluteDurationLimit !== undefined) {\n activity.attemptAbsoluteDurationLimit = activitySettings.attemptAbsoluteDurationLimit;\n }\n if (activitySettings.activityAbsoluteDurationLimit !== undefined) {\n activity.activityAbsoluteDurationLimit = activitySettings.activityAbsoluteDurationLimit;\n }\n if (activitySettings.timeLimitAction !== undefined) {\n activity.timeLimitAction = activitySettings.timeLimitAction;\n }\n if (activitySettings.timeLimitDuration !== undefined) {\n activity.timeLimitDuration = activitySettings.timeLimitDuration;\n }\n if (activitySettings.beginTimeLimit !== undefined) {\n activity.beginTimeLimit = activitySettings.beginTimeLimit;\n }\n if (activitySettings.endTimeLimit !== undefined) {\n activity.endTimeLimit = activitySettings.endTimeLimit;\n }\n\n if (activitySettings.primaryObjective) {\n const primaryObjective = this.createActivityObjectiveFromSettings(\n activitySettings.primaryObjective,\n true,\n );\n activity.primaryObjective = primaryObjective;\n if (primaryObjective.minNormalizedMeasure !== null) {\n activity.scaledPassingScore = primaryObjective.minNormalizedMeasure;\n }\n }\n\n if (activitySettings.objectives) {\n for (const objectiveSettings of activitySettings.objectives) {\n const isPrimary = objectiveSettings.isPrimary === true;\n const objective = this.createActivityObjectiveFromSettings(objectiveSettings, isPrimary);\n if (isPrimary) {\n activity.primaryObjective = objective;\n } else {\n activity.addObjective(objective);\n }\n }\n }\n\n if (activitySettings.sequencingControls) {\n this.sequencingConfigBuilder.applySequencingControlsSettings(\n activity.sequencingControls,\n activitySettings.sequencingControls,\n );\n }\n\n if (activitySettings.sequencingRules) {\n this.sequencingConfigBuilder.applySequencingRulesSettings(\n activity.sequencingRules,\n activitySettings.sequencingRules,\n );\n }\n\n if (activitySettings.rollupRules) {\n this.sequencingConfigBuilder.applyRollupRulesSettings(\n activity.rollupRules,\n activitySettings.rollupRules,\n );\n }\n\n if (activitySettings.rollupConsiderations) {\n activity.applyRollupConsiderations(activitySettings.rollupConsiderations);\n }\n\n if (activitySettings.hideLmsUi) {\n const mergedHide = this.sequencingConfigBuilder.mergeHideLmsUi(\n activity.hideLmsUi,\n activitySettings.hideLmsUi,\n );\n if (mergedHide.length > 0) {\n activity.hideLmsUi = mergedHide;\n }\n }\n\n if (activitySettings.auxiliaryResources) {\n const sanitizedAux = this.sequencingConfigBuilder.sanitizeAuxiliaryResources(\n activitySettings.auxiliaryResources,\n );\n if (sanitizedAux.length > 0) {\n activity.auxiliaryResources = this.sequencingConfigBuilder.mergeAuxiliaryResources(\n activity.auxiliaryResources,\n sanitizedAux,\n );\n }\n }\n\n // Create child activities\n if (activitySettings.children) {\n for (const childSettings of activitySettings.children) {\n const childActivity = this.createActivity(childSettings);\n activity.addChild(childActivity);\n }\n }\n\n if (activitySettings.selectionRandomizationState) {\n selectionStates.push(\n this.sequencingConfigBuilder.cloneSelectionRandomizationState(\n activitySettings.selectionRandomizationState,\n ),\n );\n }\n\n for (const state of selectionStates) {\n this.sequencingConfigBuilder.applySelectionRandomizationState(activity, state);\n }\n\n return activity;\n }\n\n /**\n * Extract all activity IDs from an activity and its children\n * @param {Activity} activity - The activity to extract IDs from\n * @return {string[]} - Array of activity IDs\n */\n extractActivityIds(activity: Activity): string[] {\n const ids = [activity.id];\n\n // Recursively extract IDs from children\n for (const child of activity.children) {\n ids.push(...this.extractActivityIds(child));\n }\n\n return ids;\n }\n\n /**\n * Create an activity objective from settings\n * @param {ObjectiveSettings} objectiveSettings - The objective settings\n * @param {boolean} isPrimary - Whether this is a primary objective\n * @return {ActivityObjective} - The created objective\n */\n createActivityObjectiveFromSettings(\n objectiveSettings: ObjectiveSettings,\n isPrimary: boolean,\n ): ActivityObjective {\n const mapInfo: ObjectiveMapInfo[] = (objectiveSettings.mapInfo || []).map((info) => ({\n targetObjectiveID: info.targetObjectiveID,\n readSatisfiedStatus: info.readSatisfiedStatus ?? false,\n readNormalizedMeasure: info.readNormalizedMeasure ?? false,\n writeSatisfiedStatus: info.writeSatisfiedStatus ?? false,\n writeNormalizedMeasure: info.writeNormalizedMeasure ?? false,\n readCompletionStatus: info.readCompletionStatus ?? false,\n writeCompletionStatus: info.writeCompletionStatus ?? false,\n readProgressMeasure: info.readProgressMeasure ?? false,\n writeProgressMeasure: info.writeProgressMeasure ?? false,\n readRawScore: info.readRawScore ?? false,\n writeRawScore: info.writeRawScore ?? false,\n readMinScore: info.readMinScore ?? false,\n writeMinScore: info.writeMinScore ?? false,\n readMaxScore: info.readMaxScore ?? false,\n writeMaxScore: info.writeMaxScore ?? false,\n updateAttemptData: info.updateAttemptData ?? false,\n }));\n\n return new ActivityObjective(objectiveSettings.objectiveID, {\n description: objectiveSettings.description ?? null,\n satisfiedByMeasure: objectiveSettings.satisfiedByMeasure ?? false,\n minNormalizedMeasure: objectiveSettings.minNormalizedMeasure ?? null,\n mapInfo,\n isPrimary,\n });\n }\n}\n","import { CMIObjectivesObject } from \"../cmi/scorm2004/objectives\";\nimport { CMI } from \"../cmi/scorm2004/cmi\";\nimport { OverallSequencingProcess } from \"../cmi/scorm2004/sequencing/overall_sequencing_process\";\nimport { GlobalObjectiveMapEntry, ScoreObject, Settings } from \"../types/api_types\";\nimport { SequencingService } from \"../services/SequencingService\";\nimport { CompletionStatus, SuccessStatus } from \"../constants/enums\";\nimport { Sequencing } from \"../cmi/scorm2004/sequencing/sequencing\";\n\n/**\n * Common set CMI value function type\n */\nexport type CommonSetCMIValueFn = (\n methodName: string,\n throwError: boolean,\n CMIElement: string,\n value: any,\n) => string;\n\n/**\n * Context interface for global objective operations\n */\nexport interface GlobalObjectiveContext {\n getSettings: () => Settings;\n cmi: CMI;\n sequencing: Sequencing | null;\n sequencingService: SequencingService | null;\n commonSetCMIValue: CommonSetCMIValueFn;\n}\n\n/**\n * Manages global objectives for SCORM 2004\n *\n * This class is responsible for:\n * - Managing the global objectives array that persists across SCO transitions\n * - Restoring global objectives to CMI after initialization\n * - Syncing global objective IDs from sequencing service\n * - Updating global objectives from CMI data\n * - Building objective map entries\n * - Capturing global objective snapshots\n */\nexport class GlobalObjectiveManager {\n private _globalObjectives: CMIObjectivesObject[] = [];\n private context: GlobalObjectiveContext;\n\n constructor(context: GlobalObjectiveContext) {\n this.context = context;\n }\n\n /**\n * Get global objectives array\n *\n * Global objectives persist across SCO transitions and are used for cross-activity\n * objective tracking via mapInfo (SCORM 2004 SN Book SB.2.4).\n *\n * @return {CMIObjectivesObject[]} Array of global objective objects\n */\n get globalObjectives(): CMIObjectivesObject[] {\n return this._globalObjectives;\n }\n\n /**\n * Set global objectives array (for deserialization)\n * @param {CMIObjectivesObject[]} objectives - Array of objectives to set\n */\n set globalObjectives(objectives: CMIObjectivesObject[]) {\n this._globalObjectives = objectives;\n }\n\n /**\n * Update the sequencing service reference\n * @param {SequencingService | null} service - The sequencing service instance\n */\n updateSequencingService(service: SequencingService | null): void {\n this.context.sequencingService = service;\n }\n\n /**\n * Syncs global objective IDs from the sequencing service's globalObjectiveMap\n * to settings.globalObjectiveIds. This ensures that objectives referenced via\n * mapInfo in the activity tree are recognized as global objectives when\n * setCMIValue is called.\n *\n * Per SCORM 2004 SN Book SB.2.4, global objectives must persist across SCO\n * transitions and be available for cross-activity objective tracking.\n */\n syncGlobalObjectiveIdsFromSequencing(): void {\n if (!this.context.sequencingService) {\n return;\n }\n\n const overallProcess = this.context.sequencingService.getOverallSequencingProcess();\n if (!overallProcess) {\n return;\n }\n\n const globalObjectiveMap = overallProcess.getGlobalObjectiveMap();\n if (!globalObjectiveMap || globalObjectiveMap.size === 0) {\n return;\n }\n\n // Extract global objective IDs from the map\n const globalIds = Array.from(globalObjectiveMap.keys());\n\n // Merge with any existing globalObjectiveIds from settings\n const settings = this.context.getSettings();\n const existingIds = settings.globalObjectiveIds || [];\n const mergedIds = Array.from(new Set(existingIds.concat(globalIds)));\n\n // Update settings with the merged list\n settings.globalObjectiveIds = mergedIds;\n }\n\n /**\n * Restores global objectives from _globalObjectives to cmi.objectives\n * This is called after Initialize to ensure global objectives are accessible\n * to the content via cmi.objectives.n.id, cmi.objectives.n.success_status, etc.\n *\n * Per SCORM 2004 SN Book SB.2.4, global objectives must persist across SCO\n * transitions and be accessible to content via the CMI data model.\n */\n restoreGlobalObjectivesToCMI(): void {\n if (this._globalObjectives.length === 0) {\n return;\n }\n\n // Restore each global objective to cmi.objectives\n for (let i = 0; i < this._globalObjectives.length; i++) {\n const globalObj = this._globalObjectives[i];\n if (!globalObj || !globalObj.id) {\n continue;\n }\n\n // Check if this objective already exists in cmi.objectives\n const existingObjective = this.context.cmi.objectives.findObjectiveById(globalObj.id);\n if (existingObjective) {\n // Objective already exists, skip to avoid duplicates\n continue;\n }\n\n // Add the global objective to cmi.objectives\n const index = this.context.cmi.objectives.childArray.length;\n\n // Set the objective ID first (this creates the objective in the array)\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.id`,\n globalObj.id,\n );\n\n // Restore other properties if they have values\n if (globalObj.success_status && globalObj.success_status !== \"unknown\") {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.success_status`,\n globalObj.success_status,\n );\n }\n\n if (globalObj.completion_status && globalObj.completion_status !== \"unknown\") {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.completion_status`,\n globalObj.completion_status,\n );\n }\n\n if (globalObj.score.scaled !== \"\" && globalObj.score.scaled !== null) {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.score.scaled`,\n globalObj.score.scaled,\n );\n }\n\n if (globalObj.score.raw !== \"\" && globalObj.score.raw !== null) {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.score.raw`,\n globalObj.score.raw,\n );\n }\n\n if (globalObj.score.min !== \"\" && globalObj.score.min !== null) {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.score.min`,\n globalObj.score.min,\n );\n }\n\n if (globalObj.score.max !== \"\" && globalObj.score.max !== null) {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.score.max`,\n globalObj.score.max,\n );\n }\n\n if (globalObj.progress_measure !== \"\" && globalObj.progress_measure !== null) {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.progress_measure`,\n globalObj.progress_measure,\n );\n }\n\n if (globalObj.description !== \"\") {\n this.context.commonSetCMIValue(\n \"RestoreGlobalObjective\",\n true,\n `cmi.objectives.${index}.description`,\n globalObj.description,\n );\n }\n }\n }\n\n /**\n * Updates the global objective map in the sequencing service from CMI objective data.\n *\n * This method synchronizes global objectives between:\n * - _globalObjectives array (persists across SCO transitions)\n * - Sequencing service global objective map (used for sequencing decisions)\n *\n * When a SCO writes to a global objective via SetValue, this method ensures\n * the sequencing service is updated so that sequencing rules can evaluate\n * the objective status correctly.\n *\n * According to SCORM 2004 SN Book SB.2.4, global objectives must be synchronized\n * across all activities that reference them via mapInfo.\n *\n * @param {string} objectiveId - The global objective ID\n * @param {CMIObjectivesObject} objective - The CMI objective object with updated values\n */\n updateGlobalObjectiveFromCMI(objectiveId: string, objective: CMIObjectivesObject): void {\n if (!objectiveId || !this.context.sequencingService) {\n return;\n }\n\n const overallProcess = this.context.sequencingService.getOverallSequencingProcess();\n if (!overallProcess) {\n return;\n }\n\n const map = overallProcess.getGlobalObjectiveMap();\n if (!map.has(objectiveId)) {\n const fallbackEntry = this.buildObjectiveMapEntryFromCMI(objective);\n overallProcess.updateGlobalObjective(objectiveId, fallbackEntry);\n return;\n }\n\n const updatePayload: Record<string, any> = {};\n\n if (objective.success_status && objective.success_status !== SuccessStatus.UNKNOWN) {\n updatePayload.satisfiedStatus = objective.success_status === SuccessStatus.PASSED;\n updatePayload.satisfiedStatusKnown = true;\n }\n\n const normalizedMeasure = this.parseObjectiveNumber(objective.score?.scaled);\n if (normalizedMeasure !== null) {\n updatePayload.normalizedMeasure = normalizedMeasure;\n updatePayload.normalizedMeasureKnown = true;\n }\n\n const progressMeasure = this.parseObjectiveNumber(objective.progress_measure);\n if (progressMeasure !== null) {\n updatePayload.progressMeasure = progressMeasure;\n updatePayload.progressMeasureKnown = true;\n }\n\n if (objective.completion_status && objective.completion_status !== CompletionStatus.UNKNOWN) {\n updatePayload.completionStatus = objective.completion_status;\n updatePayload.completionStatusKnown = true;\n }\n\n if (Object.keys(updatePayload).length === 0) {\n return;\n }\n\n overallProcess.updateGlobalObjective(objectiveId, updatePayload);\n }\n\n /**\n * Builds a map entry from the given CMI objectives object to a standardized GlobalObjectiveMapEntry.\n *\n * @param {CMIObjectivesObject} objective - The CMI objectives object containing data about a specific learning objective.\n * @return {GlobalObjectiveMapEntry} An object containing mapped properties and their values based on the provided objective.\n */\n buildObjectiveMapEntryFromCMI(objective: CMIObjectivesObject): GlobalObjectiveMapEntry {\n const entry: GlobalObjectiveMapEntry = {\n id: objective.id,\n satisfiedStatusKnown: false,\n normalizedMeasureKnown: false,\n progressMeasureKnown: false,\n completionStatusKnown: false,\n readSatisfiedStatus: true,\n writeSatisfiedStatus: true,\n readNormalizedMeasure: true,\n writeNormalizedMeasure: true,\n readCompletionStatus: true,\n writeCompletionStatus: true,\n readProgressMeasure: true,\n writeProgressMeasure: true,\n };\n\n if (objective.success_status && objective.success_status !== SuccessStatus.UNKNOWN) {\n entry.satisfiedStatus = objective.success_status === SuccessStatus.PASSED;\n entry.satisfiedStatusKnown = true;\n }\n\n const normalizedMeasure = this.parseObjectiveNumber(objective.score?.scaled);\n if (normalizedMeasure !== null) {\n entry.normalizedMeasure = normalizedMeasure;\n entry.normalizedMeasureKnown = true;\n }\n\n const progressMeasure = this.parseObjectiveNumber(objective.progress_measure);\n if (progressMeasure !== null) {\n entry.progressMeasure = progressMeasure;\n entry.progressMeasureKnown = true;\n }\n\n if (objective.completion_status && objective.completion_status !== CompletionStatus.UNKNOWN) {\n entry.completionStatus = objective.completion_status;\n entry.completionStatusKnown = true;\n }\n\n return entry;\n }\n\n /**\n * Constructs an array of `CMIObjectivesObject` instances from a given snapshot map.\n *\n * @param {Record<string, GlobalObjectiveMapEntry>} snapshot - A map where each entry represents objective data\n * with various properties that may include\n * satisfied status, progress measure, completion status, etc.\n * @return {CMIObjectivesObject[]} An array of `CMIObjectivesObject` instances built\n * from the provided snapshot map. Returns an empty array\n * if the snapshot is invalid or no valid objectives can be created.\n */\n buildCMIObjectivesFromMap(\n snapshot: Record<string, GlobalObjectiveMapEntry>,\n ): CMIObjectivesObject[] {\n const objectives: CMIObjectivesObject[] = [];\n if (!snapshot || typeof snapshot !== \"object\") {\n return objectives;\n }\n\n for (const [objectiveId, entry] of Object.entries(snapshot)) {\n if (!entry || typeof entry !== \"object\") {\n continue;\n }\n const objective = new CMIObjectivesObject();\n objective.id = entry.id ?? objectiveId;\n\n if (entry.satisfiedStatusKnown === true) {\n objective.success_status = entry.satisfiedStatus\n ? SuccessStatus.PASSED\n : SuccessStatus.FAILED;\n }\n\n const normalizedMeasure = this.parseObjectiveNumber(entry.normalizedMeasure);\n if (entry.normalizedMeasureKnown === true && normalizedMeasure !== null) {\n objective.score.scaled = String(normalizedMeasure);\n }\n\n const progressMeasure = this.parseObjectiveNumber(entry.progressMeasure);\n if (entry.progressMeasureKnown === true && progressMeasure !== null) {\n objective.progress_measure = String(progressMeasure);\n }\n\n if (entry.completionStatusKnown === true && typeof entry.completionStatus === \"string\") {\n objective.completion_status = entry.completionStatus;\n }\n\n objectives.push(objective);\n }\n\n return objectives;\n }\n\n /**\n * Constructs a `CMIObjectivesObject` instance from the provided JSON data.\n *\n * @param {any} data - The JSON data used to populate the `CMIObjectivesObject`. If the input is invalid or missing, an empty `CMIObjectivesObject` instance is returned.\n * @return {CMIObjectivesObject} A populated `CMIObjectivesObject` instance based on the input data. Returns a default object if the input does not contain valid fields.\n */\n buildCMIObjectiveFromJSON(data: any): CMIObjectivesObject {\n const objective = new CMIObjectivesObject();\n if (!data || typeof data !== \"object\") {\n return objective;\n }\n\n if (typeof data.id === \"string\") {\n objective.id = data.id;\n }\n\n if (typeof data.success_status === \"string\") {\n objective.success_status = data.success_status;\n }\n\n if (typeof data.completion_status === \"string\") {\n objective.completion_status = data.completion_status;\n }\n\n if (typeof data.progress_measure === \"string\" && data.progress_measure !== \"\") {\n objective.progress_measure = data.progress_measure;\n }\n\n if (typeof data.description === \"string\") {\n objective.description = data.description;\n }\n\n const score = data.score;\n if (score && typeof score === \"object\") {\n if (typeof score.scaled === \"string\" && score.scaled !== \"\") {\n objective.score.scaled = score.scaled;\n } else if (typeof score.scaled === \"number\" && Number.isFinite(score.scaled)) {\n objective.score.scaled = String(score.scaled);\n }\n\n if (typeof score.raw === \"string\" && score.raw !== \"\") {\n objective.score.raw = score.raw;\n } else if (typeof score.raw === \"number\" && Number.isFinite(score.raw)) {\n objective.score.raw = String(score.raw);\n }\n\n if (typeof score.min === \"string\" && score.min !== \"\") {\n objective.score.min = score.min;\n } else if (typeof score.min === \"number\" && Number.isFinite(score.min)) {\n objective.score.min = String(score.min);\n }\n\n if (typeof score.max === \"string\" && score.max !== \"\") {\n objective.score.max = score.max;\n } else if (typeof score.max === \"number\" && Number.isFinite(score.max)) {\n objective.score.max = String(score.max);\n }\n }\n\n return objective;\n }\n\n /**\n * Captures the global objective snapshot by collecting data from the provided overall process\n * or the internally managed sequencing service if no process is provided.\n *\n * @param {OverallSequencingProcess | null} [overallProcess] - An optional parameter representing the overall sequencing process. If not provided, it attempts to use the internal sequencing service.\n * @return {Record<string, GlobalObjectiveMapEntry>} A record containing the snapshot of the global objectives, with each objective's identifier as the key and its corresponding data as the value.\n */\n captureGlobalObjectiveSnapshot(\n overallProcess?: OverallSequencingProcess | null,\n ): Record<string, GlobalObjectiveMapEntry> {\n const snapshot: Record<string, GlobalObjectiveMapEntry> = {};\n\n const process =\n overallProcess ?? this.context.sequencingService?.getOverallSequencingProcess() ?? null;\n if (process) {\n const processSnapshot = process.getGlobalObjectiveMapSnapshot();\n for (const [id, data] of Object.entries(processSnapshot)) {\n snapshot[id] = { ...data };\n }\n }\n\n for (const objective of this._globalObjectives) {\n if (!objective.id || snapshot[objective.id]) {\n continue;\n }\n snapshot[objective.id] = this.buildObjectiveMapEntryFromCMI(objective);\n }\n\n return snapshot;\n }\n\n /**\n * Parses the given value into a finite number if possible, otherwise returns null.\n *\n * @param {any} value - The input value to be parsed into a number.\n * @return {number | null} The parsed finite number if the input is valid, otherwise null.\n */\n parseObjectiveNumber(value: any): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n\n const parsed = parseFloat(String(value));\n return Number.isFinite(parsed) ? parsed : null;\n }\n\n /**\n * Synchronize CMI runtime data to the current sequencing activity\n * When cmi.success_status or cmi.completion_status are set, update the\n * current activity's primary objective accordingly\n *\n * @param {CompletionStatus} completionStatus\n * @param {SuccessStatus} successStatus\n * @param {ScoreObject} scoreObject\n */\n syncCmiToSequencingActivity(\n completionStatus: CompletionStatus,\n successStatus: SuccessStatus,\n scoreObject?: ScoreObject,\n ): void {\n if (!this.context.sequencing) {\n return;\n }\n\n const currentActivity = this.context.sequencing.getCurrentActivity();\n if (!currentActivity || !currentActivity.primaryObjective) {\n return;\n }\n\n const primaryObjective = currentActivity.primaryObjective;\n\n // Update primary objective satisfied status based on cmi.success_status\n if (successStatus !== SuccessStatus.UNKNOWN) {\n primaryObjective.satisfiedStatus = successStatus === SuccessStatus.PASSED;\n primaryObjective.satisfiedStatusKnown = true;\n primaryObjective.measureStatus = true;\n currentActivity.objectiveMeasureStatus = true;\n currentActivity.objectiveSatisfiedStatus = successStatus === SuccessStatus.PASSED;\n currentActivity.objectiveSatisfiedStatusKnown = true;\n }\n\n // Update primary objective completion status based on cmi.completion_status\n if (completionStatus !== CompletionStatus.UNKNOWN) {\n primaryObjective.completionStatus = completionStatus;\n }\n\n // Update normalized measure if score is provided\n if (scoreObject?.scaled !== undefined && scoreObject.scaled !== null) {\n primaryObjective.normalizedMeasure = scoreObject.scaled;\n primaryObjective.measureStatus = true;\n }\n }\n\n /**\n * Find or create a global objective by ID\n * @param {string} objectiveId - The objective ID\n * @return {{ index: number; objective: CMIObjectivesObject }} The index and objective\n */\n findOrCreateGlobalObjective(objectiveId: string): {\n index: number;\n objective: CMIObjectivesObject;\n } {\n let index = this._globalObjectives.findIndex((obj) => obj.id === objectiveId);\n\n if (index === -1) {\n index = this._globalObjectives.length;\n const newGlobalObjective = new CMIObjectivesObject();\n newGlobalObjective.id = objectiveId;\n this._globalObjectives.push(newGlobalObjective);\n }\n\n return { index, objective: this._globalObjectives[index]! };\n }\n}\n","import { SequencingStateMetadata, Settings } from \"../types/api_types\";\nimport { CMIObjectivesObject } from \"../cmi/scorm2004/objectives\";\nimport { ADL } from \"../cmi/scorm2004/adl\";\nimport { Sequencing } from \"../cmi/scorm2004/sequencing/sequencing\";\nimport { GlobalObjectiveManager } from \"../objectives/global_objective_manager\";\nimport { SequencingService } from \"../services/SequencingService\";\nimport { LogLevelEnum } from \"../constants/enums\";\n\n/**\n * Logging function type\n */\nexport type ApiLogFn = (method: string, message: string, level: number) => void;\n\n/**\n * Context interface for persistence operations\n */\nexport interface PersistenceContext {\n getSettings: () => Settings;\n apiLog: ApiLogFn;\n adl: ADL;\n sequencing: Sequencing;\n sequencingService: SequencingService | null;\n learnerId: string;\n}\n\n/**\n * Handles persistence of SCORM 2004 sequencing state\n *\n * This class is responsible for:\n * - Saving sequencing state to persistent storage\n * - Loading sequencing state from persistent storage\n * - Serializing sequencing state to JSON\n * - Deserializing sequencing state from JSON\n * - Compressing and decompressing state data\n */\nexport class SequencingStatePersistence {\n private context: PersistenceContext;\n private globalObjectiveManager: GlobalObjectiveManager;\n\n constructor(context: PersistenceContext, globalObjectiveManager: GlobalObjectiveManager) {\n this.context = context;\n this.globalObjectiveManager = globalObjectiveManager;\n }\n\n /**\n * Save current sequencing state to persistent storage\n * @param {Partial<SequencingStateMetadata>} metadata - Optional metadata override\n * @return {Promise<boolean>} Promise resolving to success status\n */\n async saveSequencingState(metadata?: Partial<SequencingStateMetadata>): Promise<boolean> {\n const settings = this.context.getSettings();\n if (!settings.sequencingStatePersistence) {\n this.context.apiLog(\n \"saveSequencingState\",\n \"No persistence configuration provided\",\n LogLevelEnum.WARN,\n );\n return false;\n }\n\n try {\n const stateData = this.serializeSequencingState();\n const fullMetadata: SequencingStateMetadata = {\n learnerId: this.context.learnerId || \"unknown\",\n courseId: settings.courseId || \"unknown\",\n attemptNumber: 1,\n lastUpdated: new Date().toISOString(),\n version: settings.sequencingStatePersistence.stateVersion || \"1.0\",\n ...metadata,\n };\n\n const config = settings.sequencingStatePersistence;\n let dataToSave = stateData;\n\n // Compress if enabled (using simple base64 encoding for now)\n if (config.compress !== false) {\n dataToSave = this.compressStateData(stateData);\n }\n\n // Check size limits\n if (config.maxStateSize && dataToSave.length > config.maxStateSize) {\n throw new Error(`State size ${dataToSave.length} exceeds limit ${config.maxStateSize}`);\n }\n\n const success = await config.persistence.saveState(dataToSave, fullMetadata);\n\n if (config.debugPersistence) {\n this.context.apiLog(\n \"saveSequencingState\",\n `State save ${success ? \"succeeded\" : \"failed\"}: size=${dataToSave.length}`,\n success ? LogLevelEnum.INFO : LogLevelEnum.WARN,\n );\n }\n\n return success;\n } catch (error) {\n this.context.apiLog(\n \"saveSequencingState\",\n `Error saving sequencing state: ${error instanceof Error ? error.message : String(error)}`,\n LogLevelEnum.ERROR,\n );\n return false;\n }\n }\n\n /**\n * Load sequencing state from persistent storage\n * @param {Partial<SequencingStateMetadata>} metadata - Optional metadata override\n * @return {Promise<boolean>} Promise resolving to success status\n */\n async loadSequencingState(metadata?: Partial<SequencingStateMetadata>): Promise<boolean> {\n const settings = this.context.getSettings();\n if (!settings.sequencingStatePersistence) {\n this.context.apiLog(\n \"loadSequencingState\",\n \"No persistence configuration provided\",\n LogLevelEnum.WARN,\n );\n return false;\n }\n\n try {\n const fullMetadata: SequencingStateMetadata = {\n learnerId: this.context.learnerId || \"unknown\",\n courseId: settings.courseId || \"unknown\",\n attemptNumber: 1,\n version: settings.sequencingStatePersistence.stateVersion || \"1.0\",\n ...metadata,\n };\n\n const config = settings.sequencingStatePersistence;\n const stateData = await config.persistence.loadState(fullMetadata);\n\n if (!stateData) {\n if (config.debugPersistence) {\n this.context.apiLog(\n \"loadSequencingState\",\n \"No sequencing state found to load\",\n LogLevelEnum.INFO,\n );\n }\n return false;\n }\n\n // Decompress if needed\n let dataToLoad = stateData;\n if (config.compress !== false) {\n dataToLoad = this.decompressStateData(stateData);\n }\n\n const success = this.deserializeSequencingState(dataToLoad);\n\n if (config.debugPersistence) {\n this.context.apiLog(\n \"loadSequencingState\",\n `State load ${success ? \"succeeded\" : \"failed\"}: size=${stateData.length}`,\n success ? LogLevelEnum.INFO : LogLevelEnum.WARN,\n );\n }\n\n return success;\n } catch (error) {\n this.context.apiLog(\n \"loadSequencingState\",\n `Error loading sequencing state: ${error instanceof Error ? error.message : String(error)}`,\n LogLevelEnum.ERROR,\n );\n return false;\n }\n }\n\n /**\n * Serialize current sequencing state to JSON string\n * @return {string} Serialized state\n */\n serializeSequencingState(): string {\n const settings = this.context.getSettings();\n const state: any = {\n version: settings.sequencingStatePersistence?.stateVersion || \"1.0\",\n timestamp: new Date().toISOString(),\n sequencing: null,\n currentActivityId: null,\n globalObjectives: this.globalObjectiveManager.globalObjectives.map((obj) => obj.toJSON()),\n globalObjectiveMap: {},\n adlNavState: {\n request: this.context.adl.nav.request,\n request_valid: this.context.adl.nav.request_valid,\n },\n contentDelivered: false,\n };\n\n // Get sequencing state from overall sequencing process if available\n if (this.context.sequencingService) {\n const overallProcess = this.context.sequencingService.getOverallSequencingProcess();\n if (overallProcess) {\n // Use the getSequencingState method from overall_sequencing_process\n const sequencingState = overallProcess.getSequencingState();\n state.sequencing = sequencingState;\n state.contentDelivered = overallProcess.hasContentBeenDelivered();\n state.globalObjectiveMap =\n this.globalObjectiveManager.captureGlobalObjectiveSnapshot(overallProcess);\n }\n\n // Get current activity\n const currentActivity = this.context.sequencing.getCurrentActivity();\n if (currentActivity) {\n state.currentActivityId = currentActivity.id;\n }\n }\n\n if (!state.globalObjectiveMap || Object.keys(state.globalObjectiveMap).length === 0) {\n state.globalObjectiveMap = this.globalObjectiveManager.captureGlobalObjectiveSnapshot();\n }\n\n return JSON.stringify(state);\n }\n\n /**\n * Deserialize sequencing state from JSON string\n * @param {string} stateData - Serialized state data\n * @return {boolean} Success status\n */\n deserializeSequencingState(stateData: string): boolean {\n try {\n const state = JSON.parse(stateData);\n const settings = this.context.getSettings();\n\n // Version compatibility check\n const expectedVersion = settings.sequencingStatePersistence?.stateVersion || \"1.0\";\n if (state.version !== expectedVersion) {\n this.context.apiLog(\n \"deserializeSequencingState\",\n `State version mismatch: ${state.version} vs expected ${expectedVersion}`,\n LogLevelEnum.WARN,\n );\n }\n\n // If persistence stored the global objective map separately, ensure it is available to the sequencing state\n if (state.globalObjectiveMap && state.sequencing && !state.sequencing.globalObjectiveMap) {\n state.sequencing.globalObjectiveMap = state.globalObjectiveMap;\n }\n\n // Restore sequencing state\n if (state.sequencing && this.context.sequencingService) {\n const overallProcess = this.context.sequencingService.getOverallSequencingProcess();\n if (overallProcess) {\n overallProcess.restoreSequencingState(state.sequencing);\n\n // Restore content delivered flag\n if (state.contentDelivered) {\n overallProcess.setContentDelivered(true);\n }\n }\n }\n\n // Restore global objectives\n const restoredObjectives = new Map<string, CMIObjectivesObject>();\n\n if (Array.isArray(state.globalObjectives)) {\n for (const objData of state.globalObjectives) {\n const objective = this.globalObjectiveManager.buildCMIObjectiveFromJSON(objData);\n if (objective.id) {\n restoredObjectives.set(objective.id, objective);\n }\n }\n }\n\n if (state.globalObjectiveMap && typeof state.globalObjectiveMap === \"object\") {\n const objectivesFromMap = this.globalObjectiveManager.buildCMIObjectivesFromMap(\n state.globalObjectiveMap,\n );\n for (const objective of objectivesFromMap) {\n if (!objective.id) {\n continue;\n }\n if (!restoredObjectives.has(objective.id)) {\n restoredObjectives.set(objective.id, objective);\n }\n }\n }\n\n if (restoredObjectives.size > 0) {\n this.globalObjectiveManager.globalObjectives = Array.from(restoredObjectives.values());\n this.globalObjectiveManager.globalObjectives.forEach((objective) => {\n if (objective.id) {\n this.globalObjectiveManager.updateGlobalObjectiveFromCMI(objective.id, objective);\n }\n });\n }\n\n // Restore ADL nav state\n if (state.adlNavState) {\n this.context.adl.nav.request = state.adlNavState.request || \"_none_\";\n this.context.adl.nav.request_valid = state.adlNavState.request_valid || {};\n }\n\n return true;\n } catch (error) {\n this.context.apiLog(\n \"deserializeSequencingState\",\n `Error deserializing sequencing state: ${error instanceof Error ? error.message : String(error)}`,\n LogLevelEnum.ERROR,\n );\n return false;\n }\n }\n\n /**\n * Simple compression using base64 encoding\n * @param {string} data - Data to compress\n * @return {string} Compressed data\n */\n compressStateData(data: string): string {\n // For now, just use base64 encoding\n // In a real implementation, you might use a library like lz-string\n if (typeof btoa !== \"undefined\") {\n return btoa(encodeURIComponent(data));\n }\n return data;\n }\n\n /**\n * Simple decompression from base64\n * @param {string} data - Data to decompress\n * @return {string} Decompressed data\n */\n decompressStateData(data: string): string {\n // For now, just use base64 decoding\n // In a real implementation, you might use a library like lz-string\n if (typeof atob !== \"undefined\") {\n try {\n return decodeURIComponent(atob(data));\n } catch {\n return data;\n }\n }\n return data;\n }\n}\n","import { CMI } from \"../cmi/scorm2004/cmi\";\nimport { CommitObject, ScoreObject, Settings } from \"../types/api_types\";\nimport { flatten, getDurationAsSeconds, StringKeyMap } from \"../utilities\";\nimport { CompletionStatus, SuccessStatus } from \"../constants/enums\";\nimport { scorm2004_regex } from \"../constants/regex\";\nimport { SequencingService } from \"../services/SequencingService\";\nimport { GlobalObjectiveManager } from \"../objectives/global_objective_manager\";\n\n/**\n * Render CMI to JSON function type\n */\nexport type RenderCMIToJSONFn = () => StringKeyMap;\n\n/**\n * Context interface for data serialization operations\n * Uses getSettings() function to always get the current settings object\n * (important for handling reset() which creates a new settings object)\n */\nexport interface DataSerializerContext {\n getSettings: () => Settings;\n cmi: CMI;\n sequencingService: SequencingService | null;\n renderCMIToJSONObject: RenderCMIToJSONFn;\n}\n\n/**\n * Handles data serialization for SCORM 2004 LMS commits\n *\n * This class is responsible for:\n * - Rendering CMI data for LMS commits\n * - Building commit objects with metadata\n * - Determining entry values based on previous exit state\n */\nexport class Scorm2004DataSerializer {\n private context: DataSerializerContext;\n private globalObjectiveManager: GlobalObjectiveManager | null;\n\n constructor(\n context: DataSerializerContext,\n globalObjectiveManager?: GlobalObjectiveManager | null,\n ) {\n this.context = context;\n this.globalObjectiveManager = globalObjectiveManager || null;\n }\n\n /**\n * Set the global objective manager (for deferred initialization)\n * @param {GlobalObjectiveManager} manager - The global objective manager\n */\n setGlobalObjectiveManager(manager: GlobalObjectiveManager): void {\n this.globalObjectiveManager = manager;\n }\n\n /**\n * Update the sequencing service reference\n * @param {SequencingService | null} service - The sequencing service instance\n */\n updateSequencingService(service: SequencingService | null): void {\n this.context.sequencingService = service;\n }\n\n /**\n * Render the cmi object to the proper format for LMS commit\n *\n * @param {boolean} terminateCommit - Whether this is a termination commit\n * @param {boolean} includeTotalTime - Whether to include total time in the commit data\n * @return {object|Array} The rendered CMI data\n */\n renderCommitCMI(\n terminateCommit: boolean,\n includeTotalTime: boolean = false,\n ): StringKeyMap | Array<any> {\n const cmiExport: StringKeyMap = this.context.renderCMIToJSONObject();\n\n if (terminateCommit || includeTotalTime) {\n // Add total_time to the exported cmi object\n (cmiExport.cmi as StringKeyMap).total_time = this.context.cmi.getCurrentTotalTime();\n } else {\n // Remove total_time from export when not terminating\n delete (cmiExport.cmi as StringKeyMap).total_time;\n }\n\n const result = [];\n const flattened: StringKeyMap = flatten(cmiExport);\n const settings = this.context.getSettings();\n switch (settings.dataCommitFormat) {\n case \"flattened\":\n return flatten(cmiExport);\n case \"params\":\n for (const item in flattened) {\n if ({}.hasOwnProperty.call(flattened, item)) {\n result.push(`${item}=${flattened[item]}`);\n }\n }\n return result;\n case \"json\":\n default:\n return cmiExport;\n }\n }\n\n /**\n * Render the cmi object to the proper format for LMS commit\n * @param {boolean} terminateCommit - Whether this is a termination commit\n * @param {boolean} includeTotalTime - Whether to include total time in the commit data\n * @return {CommitObject} The commit object\n */\n renderCommitObject(terminateCommit: boolean, includeTotalTime: boolean = false): CommitObject {\n const cmiExport = this.renderCommitCMI(terminateCommit, includeTotalTime);\n const calculateTotalTime = terminateCommit || includeTotalTime;\n const totalTimeDuration = calculateTotalTime ? this.context.cmi.getCurrentTotalTime() : \"\";\n const totalTimeSeconds = getDurationAsSeconds(totalTimeDuration, scorm2004_regex.CMITimespan);\n\n let completionStatus = CompletionStatus.UNKNOWN;\n let successStatus = SuccessStatus.UNKNOWN;\n if (this.context.cmi.completion_status) {\n if (this.context.cmi.completion_status === \"completed\") {\n completionStatus = CompletionStatus.COMPLETED;\n } else if (this.context.cmi.completion_status === \"incomplete\") {\n completionStatus = CompletionStatus.INCOMPLETE;\n }\n }\n if (this.context.cmi.success_status) {\n if (this.context.cmi.success_status === \"passed\") {\n successStatus = SuccessStatus.PASSED;\n } else if (this.context.cmi.success_status === \"failed\") {\n successStatus = SuccessStatus.FAILED;\n }\n }\n\n const scoreObject: ScoreObject = this.context.cmi?.score?.getScoreObject() || {};\n const commitObject: CommitObject = {\n completionStatus: completionStatus,\n successStatus: successStatus,\n totalTimeSeconds: totalTimeSeconds,\n runtimeData: cmiExport as StringKeyMap,\n };\n if (scoreObject) {\n commitObject.score = scoreObject;\n }\n\n // Populate metadata if enabled\n const metaSettings = this.context.getSettings();\n if (metaSettings.autoPopulateCommitMetadata) {\n if (metaSettings.courseId) {\n commitObject.courseId = metaSettings.courseId;\n }\n if (metaSettings.scoId) {\n commitObject.scoId = metaSettings.scoId;\n }\n if (this.context.cmi.learner_id) {\n commitObject.learnerId = this.context.cmi.learner_id;\n }\n if (this.context.cmi.learner_name) {\n commitObject.learnerName = this.context.cmi.learner_name;\n }\n // For SCORM 2004, also populate activityId if available from sequencing\n const sequencingState = this.context.sequencingService?.getSequencingState();\n if (sequencingState?.currentActivity?.id) {\n commitObject.activityId = sequencingState.currentActivity.id;\n }\n }\n\n // Update sequencing activity state based on CMI runtime data\n // This ensures that cmi.success_status updates the primary objective,\n // which then triggers mapInfo global objective writes during rollup\n if (this.globalObjectiveManager) {\n this.globalObjectiveManager.syncCmiToSequencingActivity(\n completionStatus,\n successStatus,\n scoreObject,\n );\n }\n\n return commitObject;\n }\n\n /**\n * Determines the appropriate cmi.entry value based on previous exit state.\n * Per SCORM 2004 RTE 4.2.11 (cmi.entry) and 4.2.12 (cmi.exit):\n *\n * - If previous exit was \"suspend\": \"resume\" (learner suspended, wants to continue)\n * - If previous exit was \"logout\": \"\" (deprecated, attempt ended)\n * - If previous exit was \"normal\": \"\" (attempt completed normally)\n * - If previous exit was \"time-out\":\n * - With suspend data: \"resume\" (resuming from interrupted session)\n * - Without suspend data: \"\" (session ended)\n * - If no previous exit or unrecognized: \"ab-initio\" (fresh start)\n *\n * @param {string} previousExit - The cmi.exit value from the previous session\n * @param {boolean} hasSuspendData - Whether suspend_data exists from previous session\n * @return {string} The appropriate cmi.entry value (\"ab-initio\", \"resume\", or \"\")\n */\n determineEntryValue(previousExit: string, hasSuspendData: boolean): string {\n // Trim whitespace to handle edge cases\n const trimmedExit = previousExit?.trim();\n\n // No previous exit or empty exit means first-time entry\n // Per SCORM 2004 spec Rule 1: ab-initio when this is the first learner session\n if (\n previousExit === \"\" ||\n previousExit === undefined ||\n previousExit === null ||\n trimmedExit === \"\"\n ) {\n return \"ab-initio\";\n }\n\n // Per SCORM 2004 spec Rule 2: resume when previous exit was \"suspend\"\n if (previousExit === \"suspend\") {\n return \"resume\";\n }\n\n // Per SCORM 2004 spec: logout and normal always mean empty string\n // (learner previously accessed SCO but didn't suspend)\n if (previousExit === \"logout\" || previousExit === \"normal\") {\n return \"\";\n }\n\n // Per SCORM 2004 spec: time-out returns \"\" (or possibly \"resume\" if suspend data exists)\n // The spec allows resume for time-out when there's suspend data from earlier\n if (previousExit === \"time-out\") {\n return hasSuspendData ? \"resume\" : \"\";\n }\n\n // Unknown/invalid exit values indicate the learner has accessed the SCO before\n // but conditions for ab-initio or resume aren't met, so return empty string\n return \"\";\n }\n}\n","import Scorm12API from \"./Scorm12API\";\nimport Scorm2004API from \"./Scorm2004API\";\n\n// Explicitly assign to window for global usage\nif (typeof window !== \"undefined\") {\n window.Scorm12API = Scorm12API;\n window.Scorm2004API = Scorm2004API;\n}\n","import BaseAPI from \"./BaseAPI\";\nimport {\n ParsedNavigationRequest,\n StringKeyMap,\n parseNavigationRequest,\n stringMatches,\n} from \"./utilities\";\nimport { BaseCMI } from \"./cmi/common/base_cmi\";\nimport { CMIArray } from \"./cmi/common/array\";\nimport { CMI } from \"./cmi/scorm2004/cmi\";\nimport { CMIObjectivesObject } from \"./cmi/scorm2004/objectives\";\nimport { CMIInteractionsObject } from \"./cmi/scorm2004/interactions\";\nimport { ADL } from \"./cmi/scorm2004/adl\";\nimport { Sequencing } from \"./cmi/scorm2004/sequencing/sequencing\";\nimport {\n CompletionStatus,\n global_constants,\n LogLevelEnum,\n scorm2004_constants,\n scorm2004_errors,\n scorm2004_regex,\n SuccessStatus,\n} from \"./constants\";\nimport {\n ActivitySettings,\n CommitObject,\n ResultObject,\n RollupRulesSettings,\n SequencingCollectionSettings,\n SequencingControlsSettings,\n SequencingRulesSettings,\n SequencingSettings,\n SequencingStateMetadata,\n Settings,\n} from \"./types\";\nimport { IHttpService } from \"./interfaces\";\nimport { SequencingConfiguration, SequencingEventListeners, SequencingService } from \"./services\";\n\n// Import extracted classes\nimport {\n Scorm2004ResponseValidator,\n ValidationContext,\n} from \"./handlers/scorm2004/response_validator\";\nimport { Scorm2004CMIHandler, CMIHandlerContext } from \"./handlers/scorm2004/cmi_handler\";\nimport { ActivityTreeBuilder } from \"./configuration/activity_tree_builder\";\nimport { SequencingConfigurationBuilder } from \"./configuration/sequencing_configuration_builder\";\nimport {\n GlobalObjectiveManager,\n GlobalObjectiveContext,\n} from \"./objectives/global_objective_manager\";\nimport {\n SequencingStatePersistence,\n PersistenceContext,\n} from \"./persistence/sequencing_state_persistence\";\nimport {\n Scorm2004DataSerializer,\n DataSerializerContext,\n} from \"./serialization/scorm2004_data_serializer\";\n\n/**\n * API class for SCORM 2004\n */\nclass Scorm2004API extends BaseAPI {\n private _version: string = \"1.0\";\n private readonly _sequencing: Sequencing;\n private _sequencingService: SequencingService | null = null;\n private _extractedScoItemIds: string[] = [];\n private _sequencingCollections: Record<string, SequencingCollectionSettings> = {};\n\n // Extracted class instances\n private _responseValidator: Scorm2004ResponseValidator;\n private _cmiHandler: Scorm2004CMIHandler;\n private _activityTreeBuilder: ActivityTreeBuilder;\n private _sequencingConfigBuilder: SequencingConfigurationBuilder;\n private _globalObjectiveManager: GlobalObjectiveManager;\n private _statePersistence: SequencingStatePersistence | null = null;\n private _dataSerializer: Scorm2004DataSerializer;\n\n /**\n * Constructor for SCORM 2004 API\n * @param {Settings} settings\n * @param {IHttpService} httpService - Optional HTTP service instance\n */\n constructor(settings?: Settings, httpService?: IHttpService) {\n const settingsCopy = settings ? { ...settings } : undefined;\n if (settingsCopy) {\n if (settingsCopy.mastery_override === undefined) {\n settingsCopy.mastery_override = false;\n }\n }\n super(scorm2004_errors, settingsCopy, httpService);\n\n this.cmi = new CMI();\n this.adl = new ADL();\n this._sequencing = new Sequencing();\n\n // Connect the sequencing object to the ADL's sequencing property\n this.adl.sequencing = this._sequencing;\n\n // Initialize extracted classes\n this._sequencingConfigBuilder = new SequencingConfigurationBuilder();\n this._activityTreeBuilder = new ActivityTreeBuilder({}, this._sequencingConfigBuilder);\n\n // Create validation context - use arrow functions to ensure lazy evaluation\n // Note: checkCorrectResponseValue calls the validator directly since it's a pass-through\n const validationContext: ValidationContext = {\n throwSCORMError: (element: string, errorCode: number, message?: string) =>\n this.throwSCORMError(element, errorCode, message),\n getLastErrorCode: () => this.lastErrorCode,\n checkCorrectResponseValue: (\n CMIElement: string,\n interaction_type: string,\n nodes: Array<any>,\n value: any,\n ) =>\n this._responseValidator.checkCorrectResponseValue(\n CMIElement,\n interaction_type,\n nodes,\n value,\n ),\n };\n this._responseValidator = new Scorm2004ResponseValidator(validationContext);\n\n // Create CMI handler context - use arrow functions to ensure spies work correctly\n const cmiHandlerContext: CMIHandlerContext = {\n cmi: this.cmi,\n isInitialized: () => this.isInitialized(),\n throwSCORMError: (element: string, errorCode: number, message?: string) =>\n this.throwSCORMError(element, errorCode, message),\n getLastErrorCode: () => this.lastErrorCode,\n };\n this._cmiHandler = new Scorm2004CMIHandler(cmiHandlerContext, this._responseValidator);\n\n // Initialize global objective manager context - use getter to always get current settings\n const globalObjectiveContext: GlobalObjectiveContext = {\n getSettings: () => this.settings,\n cmi: this.cmi,\n sequencing: this._sequencing,\n sequencingService: this._sequencingService,\n commonSetCMIValue: this._commonSetCMIValue.bind(this),\n };\n this._globalObjectiveManager = new GlobalObjectiveManager(globalObjectiveContext);\n\n // Initialize data serializer - use getter to always get current settings\n const dataSerializerContext: DataSerializerContext = {\n getSettings: () => this.settings,\n cmi: this.cmi,\n sequencingService: this._sequencingService,\n renderCMIToJSONObject: this.renderCMIToJSONObject.bind(this),\n };\n this._dataSerializer = new Scorm2004DataSerializer(\n dataSerializerContext,\n this._globalObjectiveManager,\n );\n\n // Configure sequencing if settings are provided\n if (settingsCopy?.sequencing) {\n this.configureSequencing(settingsCopy.sequencing);\n }\n\n // Initialize sequencing service\n this.initializeSequencingService(settingsCopy);\n\n // Rename functions to match 2004 Spec and expose to modules\n this.Initialize = this.lmsInitialize;\n this.Terminate = this.lmsFinish;\n this.GetValue = this.lmsGetValue;\n this.SetValue = this.lmsSetValue;\n this.Commit = this.lmsCommit;\n this.GetLastError = this.lmsGetLastError;\n this.GetErrorString = this.lmsGetErrorString;\n this.GetDiagnostic = this.lmsGetDiagnostic;\n }\n\n public cmi: CMI;\n public adl: ADL;\n\n public Initialize: (parameter?: string) => string;\n public Terminate: (parameter?: string) => string;\n public GetValue: (CMIElement: string) => string;\n public SetValue: (CMIElement: string, value: any) => string;\n public Commit: (parameter?: string) => string;\n public GetLastError: () => string;\n public GetErrorString: (CMIErrorCode: string | number) => string;\n public GetDiagnostic: (CMIErrorCode: string | number) => string;\n\n /**\n * Called when the API needs to be reset\n *\n * @param {Settings} settings - Optional new settings to merge with existing settings\n */\n reset(settings?: Settings) {\n this.commonReset(settings);\n\n this.cmi?.reset();\n this.adl?.reset();\n }\n\n /**\n * Getter for _version\n * @return {string}\n */\n get version(): string {\n return this._version;\n }\n\n /**\n * Getter for _globalObjectives\n * @return {CMIObjectivesObject[]} Array of global objective objects\n */\n get globalObjectives(): CMIObjectivesObject[] {\n return this._globalObjectiveManager.globalObjectives;\n }\n\n /**\n * Setter for _globalObjectives (for backward compatibility)\n * @param {CMIObjectivesObject[]} objectives - Array of global objective objects\n */\n set _globalObjectives(objectives: CMIObjectivesObject[]) {\n this._globalObjectiveManager.globalObjectives = objectives;\n }\n\n /**\n * Getter for _globalObjectives (for backward compatibility with tests using bracket notation)\n * @return {CMIObjectivesObject[]} Array of global objective objects\n */\n get _globalObjectives(): CMIObjectivesObject[] {\n return this._globalObjectiveManager.globalObjectives;\n }\n\n /**\n * Compress state data (delegates to persistence class)\n * @param {string} data - Data to compress\n * @return {string} Compressed data\n */\n compressStateData(data: string): string {\n if (this._statePersistence) {\n return this._statePersistence.compressStateData(data);\n }\n // Fallback: simple base64 encoding\n if (typeof btoa !== \"undefined\") {\n return btoa(encodeURIComponent(data));\n }\n return data;\n }\n\n /**\n * Decompress state data (delegates to persistence class)\n * @param {string} data - Data to decompress\n * @return {string} Decompressed data\n */\n decompressStateData(data: string): string {\n if (this._statePersistence) {\n return this._statePersistence.decompressStateData(data);\n }\n // Fallback: simple base64 decoding\n if (typeof atob !== \"undefined\") {\n try {\n return decodeURIComponent(atob(data));\n } catch {\n return data;\n }\n }\n return data;\n }\n\n /**\n * Initialize - Begins a communication session with the LMS\n *\n * @param {string} parameter - Must be an empty string per SCORM 2004 specification\n * @return {string} \"true\" or \"false\"\n */\n lmsInitialize(parameter: string = \"\"): string {\n if (parameter !== \"\") {\n this.throwSCORMError(\"api\", this._error_codes.ARGUMENT_ERROR);\n return global_constants.SCORM_FALSE;\n }\n\n this.cmi.initialize();\n const result = this.initialize(\n \"Initialize\",\n \"LMS was already initialized!\",\n \"LMS is already finished!\",\n );\n\n if (result === global_constants.SCORM_TRUE && this._sequencingService) {\n this._sequencingService.initialize();\n }\n\n if (result === global_constants.SCORM_TRUE) {\n this._globalObjectiveManager.restoreGlobalObjectivesToCMI();\n }\n\n if (result === global_constants.SCORM_TRUE && this.settings.sequencingStatePersistence) {\n this.loadSequencingState().catch(() => {\n this.apiLog(\"lmsInitialize\", \"Failed to auto-load sequencing state\", LogLevelEnum.WARN);\n });\n }\n\n return result;\n }\n\n /**\n * Terminate - Ends the communication session and persists data\n *\n * @param {string} parameter - Must be an empty string per SCORM 2004 specification\n * @return {string} \"true\" or \"false\"\n */\n lmsFinish(parameter: string = \"\"): string {\n if (parameter !== \"\") {\n this.throwSCORMError(\"api\", this._error_codes.ARGUMENT_ERROR);\n return global_constants.SCORM_FALSE;\n }\n\n const pendingNavRequest = this.adl?.nav?.request || \"_none_\";\n const exitType = this.cmi?.getExitValueInternal() || \"\";\n const wasAlreadyTerminated = this.isTerminated();\n const deliveryInProgress = this._sequencingService?.isDeliveryInProgress() ?? false;\n\n const result = this.terminate(\"Terminate\", true);\n\n if (result === global_constants.SCORM_TRUE && !wasAlreadyTerminated && !deliveryInProgress) {\n let navigationHandled = false;\n let processedSequencingRequest: string | null = null;\n let normalizedRequest = pendingNavRequest;\n let normalizedTarget = \"\";\n const choiceJumpRegex = new RegExp(scorm2004_regex.NAVEvent);\n\n if (pendingNavRequest !== \"_none_\") {\n const matches = pendingNavRequest.match(choiceJumpRegex);\n if (matches) {\n if (matches.groups?.choice_target) {\n normalizedTarget = matches.groups?.choice_target;\n normalizedRequest = \"choice\";\n } else if (matches.groups?.jump_target) {\n normalizedTarget = matches.groups?.jump_target;\n normalizedRequest = \"jump\";\n }\n }\n }\n\n if (this._sequencingService) {\n try {\n let requestToProcess: string | null = null;\n let targetForProcessing: string | undefined;\n\n if (normalizedRequest !== \"_none_\") {\n requestToProcess = normalizedRequest;\n targetForProcessing = normalizedTarget || undefined;\n } else if (this._sequencing.getCurrentActivity()) {\n requestToProcess = \"exit\";\n }\n\n if (requestToProcess) {\n navigationHandled = this._sequencingService.processNavigationRequest(\n requestToProcess,\n targetForProcessing,\n exitType,\n );\n processedSequencingRequest = requestToProcess;\n }\n } catch (error) {\n this.apiLog(\n \"lmsFinish\",\n `Sequencing navigation failed, falling back to event-based navigation: ${error}`,\n LogLevelEnum.WARN,\n );\n navigationHandled = false;\n }\n }\n\n if (!navigationHandled) {\n if (pendingNavRequest !== \"_none_\") {\n const navActions: { [key: string]: string } = {\n continue: \"SequenceNext\",\n previous: \"SequencePrevious\",\n choice: \"SequenceChoice\",\n jump: \"SequenceJump\",\n exit: \"SequenceExit\",\n exitAll: \"SequenceExitAll\",\n abandon: \"SequenceAbandon\",\n abandonAll: \"SequenceAbandonAll\",\n };\n\n const action = navActions[normalizedRequest];\n if (action) {\n this.processListeners(action, \"adl.nav.request\", normalizedTarget);\n }\n } else if (this.settings.autoProgress) {\n this.processListeners(\"SequenceNext\", undefined, \"next\");\n }\n }\n\n if (\n this._sequencingService &&\n processedSequencingRequest &&\n [\"exitAll\", \"abandonAll\", \"suspendAll\"].includes(processedSequencingRequest)\n ) {\n this._sequencingService.terminate();\n }\n\n this.adl.nav.request = \"_none_\";\n }\n\n return result;\n }\n\n /**\n * GetValue - Retrieves a value from the CMI data model\n *\n * @param {string} CMIElement - The CMI element path\n * @return {string} The value of the element, or empty string\n */\n lmsGetValue(CMIElement: string): string {\n // Per SCORM 2004 RTE Section 3.1.3.3: state checks must come before\n // any data model element processing\n if (this.isTerminated()) {\n this.lastErrorCode = String(scorm2004_errors.RETRIEVE_AFTER_TERM);\n return \"\";\n }\n if (!this.isInitialized()) {\n this.lastErrorCode = String(scorm2004_errors.RETRIEVE_BEFORE_INIT);\n return \"\";\n }\n\n if (CMIElement === \"adl.nav.request\") {\n this.throwSCORMError(\n CMIElement,\n scorm2004_errors.WRITE_ONLY_ELEMENT,\n \"adl.nav.request is write-only\",\n );\n return \"\";\n }\n\n const adlNavRequestRegex =\n \"^adl\\\\.nav\\\\.request_valid\\\\.(choice|jump)\\\\.{target=([a-zA-Z0-9-_]+)}$\";\n if (stringMatches(CMIElement, adlNavRequestRegex)) {\n const matches = CMIElement.match(adlNavRequestRegex);\n if (matches) {\n const request = matches[1];\n const target = matches[2]?.replace(/{target=/g, \"\").replace(/}/g, \"\") || \"\";\n if (request === \"choice\" || request === \"jump\") {\n const overallProcess = this._sequencing?.overallSequencingProcess;\n\n if (this.settings.scoItemIdValidator) {\n return String(this.settings.scoItemIdValidator(target));\n }\n\n if (overallProcess?.predictChoiceEnabled && request === \"choice\") {\n return overallProcess.predictChoiceEnabled(target) ? \"true\" : \"false\";\n } else if (overallProcess?.predictJumpEnabled && request === \"jump\") {\n return overallProcess.predictJumpEnabled(target) ? \"true\" : \"false\";\n } else {\n if (this._extractedScoItemIds.length > 0) {\n return String(this._extractedScoItemIds.includes(target));\n }\n return String(this.settings?.scoItemIds?.includes(target));\n }\n }\n }\n }\n\n if (CMIElement === \"cmi.completion_status\") {\n return this._cmiHandler.evaluateCompletionStatus();\n }\n\n if (CMIElement === \"cmi.success_status\") {\n return this._cmiHandler.evaluateSuccessStatus();\n }\n\n return this.getValue(\"GetValue\", true, CMIElement);\n }\n\n /**\n * SetValue - Sets a value in the CMI data model\n *\n * @param {string} CMIElement - The CMI element path\n * @param {any} value - The value to set\n * @return {string} \"true\" or \"false\"\n */\n lmsSetValue(CMIElement: string, value: any): string {\n const oldValue = this._peekCMIValue(CMIElement);\n\n const result = this.setValue(\"SetValue\", \"Commit\", true, CMIElement, value);\n\n if (result === global_constants.SCORM_TRUE && this._sequencingService) {\n try {\n this._sequencingService.triggerRollupOnCMIChange(CMIElement, oldValue, value);\n } catch (rollupError) {\n console.warn(`Sequencing rollup failed for ${CMIElement}: ${rollupError}`);\n }\n }\n\n if (\n result === global_constants.SCORM_TRUE &&\n this.settings.sequencingStatePersistence?.autoSaveOn === \"setValue\"\n ) {\n const sequencingElements = [\n \"cmi.completion_status\",\n \"cmi.success_status\",\n \"cmi.score.scaled\",\n \"cmi.objectives\",\n \"adl.nav.request\",\n ];\n\n if (sequencingElements.some((element) => CMIElement.startsWith(element))) {\n this.saveSequencingState().catch(() => {\n this.apiLog(\"lmsSetValue\", \"Failed to auto-save sequencing state\", LogLevelEnum.WARN);\n });\n }\n }\n\n return result;\n }\n\n /**\n * Commit - Requests immediate persistence of data to the LMS\n *\n * @param {string} parameter - Must be an empty string per SCORM 2004 specification\n * @return {string} \"true\" or \"false\"\n */\n lmsCommit(parameter: string = \"\"): string {\n if (parameter !== \"\") {\n this.throwSCORMError(\"api\", this._error_codes.ARGUMENT_ERROR);\n return global_constants.SCORM_FALSE;\n }\n\n if (this.settings.throttleCommits) {\n this.scheduleCommit(500, \"Commit\");\n return global_constants.SCORM_TRUE;\n } else {\n const result = this.commit(\"Commit\", true);\n\n if (\n result === global_constants.SCORM_TRUE &&\n this.settings.sequencingStatePersistence?.autoSaveOn === \"commit\"\n ) {\n this.saveSequencingState().catch(() => {\n this.apiLog(\"lmsCommit\", \"Failed to auto-save sequencing state\", LogLevelEnum.WARN);\n });\n }\n\n return result;\n }\n }\n\n /**\n * GetLastError - Returns the error code from the last API call\n * @return {string} Error code as a string\n */\n lmsGetLastError(): string {\n return this.getLastError(\"GetLastError\");\n }\n\n /**\n * GetErrorString - Returns a short description for an error code\n * @param {string|number} CMIErrorCode - The error code\n * @return {string} Short error description\n */\n lmsGetErrorString(CMIErrorCode: string | number): string {\n return this.getErrorString(\"GetErrorString\", CMIErrorCode);\n }\n\n /**\n * GetDiagnostic - Returns detailed diagnostic information for an error\n * @param {string|number} CMIErrorCode - The error code\n * @return {string} Detailed diagnostic information\n */\n lmsGetDiagnostic(CMIErrorCode: string | number): string {\n return this.getDiagnostic(\"GetDiagnostic\", CMIErrorCode);\n }\n\n /**\n * Sets a value on the CMI Object\n * @param {string} CMIElement\n * @param {any} value\n * @return {string}\n */\n override setCMIValue(CMIElement: string, value: any): string {\n if (stringMatches(CMIElement, \"cmi\\\\.objectives\\\\.\\\\d+\")) {\n const parts = CMIElement.split(\".\");\n const index = Number(parts[2]);\n const element_base = `cmi.objectives.${index}`;\n\n let objective_id;\n const setting_id = stringMatches(CMIElement, \"cmi\\\\.objectives\\\\.\\\\d+\\\\.id\");\n\n if (setting_id) {\n objective_id = value;\n } else {\n const objective = this.cmi.objectives.findObjectiveByIndex(index);\n objective_id = objective ? objective.id : undefined;\n }\n\n const is_global = objective_id && this.settings.globalObjectiveIds?.includes(objective_id);\n\n if (is_global) {\n const { index: global_index } =\n this._globalObjectiveManager.findOrCreateGlobalObjective(objective_id);\n\n const global_element = CMIElement.replace(\n element_base,\n `_globalObjectives.${global_index}`,\n );\n this._commonSetCMIValue(\"SetGlobalObjectiveValue\", true, global_element, value);\n\n const updatedObjective = this._globalObjectiveManager.globalObjectives[global_index];\n if (objective_id && updatedObjective) {\n this._globalObjectiveManager.updateGlobalObjectiveFromCMI(objective_id, updatedObjective);\n }\n }\n }\n return this._commonSetCMIValue(\"SetValue\", true, CMIElement, value);\n }\n\n /**\n * Gets or builds a new child element to add to the array\n * @param {string} CMIElement\n * @param {any} value\n * @param {boolean} foundFirstIndex\n * @return {BaseCMI|null}\n */\n getChildElement(CMIElement: string, value: any, foundFirstIndex: boolean): BaseCMI | null {\n return this._cmiHandler.getChildElement(CMIElement, value, foundFirstIndex);\n }\n\n /**\n * Validate correct response\n * @param {string} CMIElement\n * @param {*} value\n */\n validateCorrectResponse(CMIElement: string, value: any) {\n const parts = CMIElement.split(\".\");\n const index = Number(parts[2]);\n const interaction = this.cmi.interactions.childArray[index] as\n | CMIInteractionsObject\n | undefined;\n\n if (!interaction) {\n this.throwSCORMError(CMIElement, scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED, CMIElement);\n return;\n }\n\n this._responseValidator.validateCorrectResponse(CMIElement, interaction, value);\n }\n\n /**\n * Silently peeks at a CMI value without triggering error logging or\n * mutating lastErrorCode. Returns null if the element doesn't exist.\n * Used internally to capture old values before SetValue overwrites them.\n *\n * @param {string} CMIElement - dot-delimited CMI path (e.g. \"cmi.interactions.0.id\")\n * @return {*} the current value, or null if the path doesn't resolve\n */\n private _peekCMIValue(CMIElement: string): any {\n if (!CMIElement || typeof CMIElement !== \"string\") {\n return null;\n }\n\n const segments = CMIElement.split(\".\");\n let refObject: any = this;\n\n for (let i = 0; i < segments.length; i++) {\n const attribute = segments[i] as string;\n\n if (refObject == null) {\n return null;\n }\n\n try {\n refObject = refObject[attribute];\n } catch {\n // Property getter threw (e.g. write-only elements like session_time)\n return null;\n }\n\n if (refObject instanceof CMIArray) {\n // Next segment may be a numeric index into childArray, or a\n // property like _count/_children accessed on the array itself.\n const nextIndex = i + 1;\n if (nextIndex >= segments.length) {\n return refObject;\n }\n\n const nextSegment = segments[nextIndex] as string;\n const index = Number(nextSegment);\n\n if (!Number.isNaN(index) && Number.isInteger(index) && index >= 0) {\n if (index >= refObject.childArray.length) {\n return null;\n }\n refObject = refObject.childArray[index];\n i++; // skip the index segment\n }\n // If nextSegment is not a valid numeric index (e.g. _count),\n // fall through and let the next iteration access it as a\n // normal property on the CMIArray instance.\n }\n }\n\n return refObject ?? null;\n }\n\n /**\n * Gets a value from the CMI Object\n * @param {string} CMIElement\n * @return {*}\n */\n override getCMIValue(CMIElement: string): any {\n return this._commonGetCMIValue(\"GetValue\", true, CMIElement);\n }\n\n /**\n * Returns the message that corresponds to errorNumber.\n * @param {(string|number)} errorNumber\n * @param {boolean} detail\n * @return {string}\n */\n override getLmsErrorMessageDetails(errorNumber: string | number, detail: boolean): string {\n let basicMessage = \"\";\n let detailMessage = \"\";\n\n errorNumber = String(errorNumber);\n const errorDescription = scorm2004_constants.error_descriptions[errorNumber];\n if (errorDescription) {\n basicMessage = errorDescription.basicMessage;\n detailMessage = errorDescription.detailMessage;\n }\n\n return detail ? detailMessage : basicMessage;\n }\n\n /**\n * Replace the whole API with another\n * @param {Scorm2004API} newAPI\n */\n replaceWithAnotherScormAPI(newAPI: Scorm2004API) {\n this.cmi = newAPI.cmi;\n this.adl = newAPI.adl;\n }\n\n /**\n * Render the cmi object to the proper format for LMS commit\n * @param {boolean} terminateCommit\n * @param {boolean} includeTotalTime\n * @return {object|Array}\n */\n renderCommitCMI(\n terminateCommit: boolean,\n includeTotalTime: boolean = false,\n ): StringKeyMap | Array<any> {\n return this._dataSerializer.renderCommitCMI(terminateCommit, includeTotalTime);\n }\n\n /**\n * Render the cmi object to the proper format for LMS commit\n * @param {boolean} terminateCommit\n * @param {boolean} includeTotalTime\n * @return {CommitObject}\n */\n renderCommitObject(terminateCommit: boolean, includeTotalTime: boolean = false): CommitObject {\n return this._dataSerializer.renderCommitObject(terminateCommit, includeTotalTime);\n }\n\n /**\n * Attempts to store the data to the LMS\n * @param {boolean} terminateCommit\n * @return {ResultObject}\n */\n storeData(terminateCommit: boolean): ResultObject {\n if (terminateCommit) {\n if (this.cmi.mode === \"normal\") {\n if (this.cmi.credit === \"credit\") {\n if (this.cmi.completion_threshold && this.cmi.progress_measure) {\n if (this.cmi.progress_measure >= this.cmi.completion_threshold) {\n this.cmi.completion_status = \"completed\";\n } else {\n this.cmi.completion_status = \"incomplete\";\n }\n }\n if (this.cmi.scaled_passing_score && this.cmi.score.scaled) {\n if (this.cmi.score.scaled >= this.cmi.scaled_passing_score) {\n this.cmi.success_status = \"passed\";\n } else {\n this.cmi.success_status = \"failed\";\n }\n }\n }\n }\n }\n\n let navRequest = false;\n if (\n this.adl.nav.request !==\n ((this.startingData?.adl as StringKeyMap)?.nav as StringKeyMap)?.request &&\n this.adl.nav.request !== \"_none_\"\n ) {\n navRequest = true;\n }\n\n const commitObject = this.getCommitObject(terminateCommit);\n const scoreObject = this.cmi?.score?.getScoreObject() || {};\n let completionStatusEnum = CompletionStatus.UNKNOWN;\n if (this.cmi.completion_status === \"completed\") {\n completionStatusEnum = CompletionStatus.COMPLETED;\n } else if (this.cmi.completion_status === \"incomplete\") {\n completionStatusEnum = CompletionStatus.INCOMPLETE;\n }\n let successStatusEnum = SuccessStatus.UNKNOWN;\n if (this.cmi.success_status === \"passed\") {\n successStatusEnum = SuccessStatus.PASSED;\n } else if (this.cmi.success_status === \"failed\") {\n successStatusEnum = SuccessStatus.FAILED;\n }\n this._globalObjectiveManager.syncCmiToSequencingActivity(\n completionStatusEnum,\n successStatusEnum,\n scoreObject,\n );\n if (typeof this.settings.lmsCommitUrl === \"string\") {\n const result = this.processHttpRequest(\n this.settings.lmsCommitUrl,\n commitObject,\n terminateCommit,\n );\n\n if (\n navRequest &&\n result.navRequest !== undefined &&\n result.navRequest !== \"\" &&\n typeof result.navRequest === \"string\"\n ) {\n const parsed: ParsedNavigationRequest = parseNavigationRequest(result.navRequest);\n\n if (!parsed.valid) {\n this.apiLog(\n \"storeData\",\n `Invalid navigation request from LMS: ${parsed.error}`,\n LogLevelEnum.WARN,\n );\n } else {\n const navEventMap: { [key: string]: string } = {\n start: \"SequenceStart\",\n resumeAll: \"SequenceResumeAll\",\n continue: \"SequenceNext\",\n previous: \"SequencePrevious\",\n choice: \"SequenceChoice\",\n jump: \"SequenceJump\",\n exit: \"SequenceExit\",\n exitAll: \"SequenceExitAll\",\n abandon: \"SequenceAbandon\",\n abandonAll: \"SequenceAbandonAll\",\n suspendAll: \"SequenceSuspendAll\",\n };\n\n const eventName = navEventMap[parsed.command];\n if (eventName) {\n this.processListeners(eventName, \"adl.nav.request\", parsed.targetActivityId);\n }\n }\n } else if (result?.navRequest && !navRequest) {\n if (\n typeof result.navRequest === \"object\" &&\n Object.hasOwnProperty.call(result.navRequest, \"name\") &&\n result.navRequest.name\n ) {\n this.processListeners(result.navRequest.name as string, result.navRequest.data as string);\n }\n }\n\n return result;\n }\n\n return {\n result: global_constants.SCORM_TRUE,\n errorCode: 0,\n };\n }\n\n /**\n * Configure sequencing based on provided settings\n * @param {SequencingSettings} sequencingSettings\n */\n private configureSequencing(sequencingSettings: SequencingSettings): void {\n this._sequencingCollections = this._sequencingConfigBuilder.sanitizeSequencingCollections(\n sequencingSettings.collections,\n );\n this._activityTreeBuilder.setSequencingCollections(this._sequencingCollections);\n\n if (sequencingSettings.activityTree) {\n this.configureActivityTree(sequencingSettings.activityTree);\n }\n\n if (sequencingSettings.sequencingRules) {\n this.configureSequencingRules(sequencingSettings.sequencingRules);\n }\n\n if (sequencingSettings.sequencingControls) {\n this.configureSequencingControls(sequencingSettings.sequencingControls);\n }\n\n if (sequencingSettings.rollupRules) {\n this.configureRollupRules(sequencingSettings.rollupRules);\n }\n\n if (sequencingSettings.hideLmsUi) {\n this._sequencing.hideLmsUi = this._sequencingConfigBuilder.sanitizeHideLmsUi(\n sequencingSettings.hideLmsUi,\n );\n } else {\n this._sequencing.hideLmsUi = [];\n }\n\n if (sequencingSettings.auxiliaryResources) {\n this._sequencing.auxiliaryResources =\n this._sequencingConfigBuilder.sanitizeAuxiliaryResources(\n sequencingSettings.auxiliaryResources,\n );\n } else {\n this._sequencing.auxiliaryResources = [];\n }\n }\n\n /**\n * Configure activity tree based on provided settings\n * @param {ActivitySettings} activityTreeSettings\n */\n private configureActivityTree(activityTreeSettings: ActivitySettings): void {\n const rootActivity = this._activityTreeBuilder.createActivity(activityTreeSettings);\n const activityTree = this._sequencing.activityTree;\n activityTree.root = rootActivity;\n this._extractedScoItemIds = this._activityTreeBuilder.extractActivityIds(rootActivity);\n }\n\n /**\n * Configure sequencing rules based on provided settings\n * @param {SequencingRulesSettings} sequencingRulesSettings\n */\n private configureSequencingRules(sequencingRulesSettings: SequencingRulesSettings): void {\n this._sequencingConfigBuilder.applySequencingRulesSettings(\n this._sequencing.sequencingRules,\n sequencingRulesSettings,\n );\n }\n\n /**\n * Configure sequencing controls based on provided settings\n * @param {SequencingControlsSettings} sequencingControlsSettings\n */\n private configureSequencingControls(\n sequencingControlsSettings: SequencingControlsSettings,\n ): void {\n this._sequencingConfigBuilder.applySequencingControlsSettings(\n this._sequencing.sequencingControls,\n sequencingControlsSettings,\n );\n }\n\n /**\n * Configure rollup rules based on provided settings\n * @param {RollupRulesSettings} rollupRulesSettings\n */\n private configureRollupRules(rollupRulesSettings: RollupRulesSettings): void {\n this._sequencingConfigBuilder.applyRollupRulesSettings(\n this._sequencing.rollupRules,\n rollupRulesSettings,\n );\n }\n\n /**\n * Initialize the sequencing service\n * @param {Settings} settings\n */\n private initializeSequencingService(settings?: Settings): void {\n try {\n const sequencingConfig: SequencingConfiguration = {\n autoRollupOnCMIChange: settings?.sequencing?.autoRollupOnCMIChange ?? true,\n autoProgressOnCompletion: settings?.sequencing?.autoProgressOnCompletion ?? false,\n validateNavigationRequests: settings?.sequencing?.validateNavigationRequests ?? true,\n enableEventSystem: settings?.sequencing?.enableEventSystem ?? true,\n logLevel: settings?.sequencing?.logLevel ?? \"info\",\n };\n\n this._sequencingService = new SequencingService(\n this._sequencing,\n this.cmi,\n this.adl,\n this.eventService || this,\n this.loggingService,\n sequencingConfig,\n );\n\n if (settings?.sequencing?.eventListeners) {\n this._sequencingService.setEventListeners(settings.sequencing.eventListeners);\n }\n\n // Update context references after sequencing service is created\n this._globalObjectiveManager.updateSequencingService(this._sequencingService);\n this._dataSerializer.updateSequencingService(this._sequencingService);\n\n // Initialize state persistence - use getter to always get current settings\n if (settings?.sequencingStatePersistence) {\n const persistenceContext: PersistenceContext = {\n getSettings: () => this.settings,\n apiLog: this.apiLog.bind(this),\n adl: this.adl,\n sequencing: this._sequencing,\n sequencingService: this._sequencingService,\n learnerId: this.cmi.learner_id,\n };\n this._statePersistence = new SequencingStatePersistence(\n persistenceContext,\n this._globalObjectiveManager,\n );\n }\n\n this._globalObjectiveManager.syncGlobalObjectiveIdsFromSequencing();\n } catch (error) {\n console.warn(\"Failed to initialize sequencing service:\", error);\n this._sequencingService = null;\n }\n }\n\n /**\n * Get the sequencing service\n * @return {SequencingService | null}\n */\n public getSequencingService(): SequencingService | null {\n return this._sequencingService;\n }\n\n /**\n * Set sequencing event listeners\n * @param {SequencingEventListeners} listeners\n */\n public setSequencingEventListeners(listeners: SequencingEventListeners): void {\n if (this._sequencingService) {\n this._sequencingService.setEventListeners(listeners);\n }\n }\n\n /**\n * Update sequencing configuration\n * @param {SequencingConfiguration} config\n */\n public updateSequencingConfiguration(config: SequencingConfiguration): void {\n if (this._sequencingService) {\n this._sequencingService.updateConfiguration(config);\n }\n }\n\n /**\n * Get current sequencing state information\n * @return {object}\n */\n public getSequencingState(): any {\n if (this._sequencingService) {\n return this._sequencingService.getSequencingState();\n }\n return {\n isInitialized: false,\n isActive: false,\n currentActivity: null,\n rootActivity: this._sequencing.getRootActivity(),\n lastSequencingResult: null,\n };\n }\n\n /**\n * Process a navigation request directly\n * @param {string} request\n * @param {string} targetActivityId\n * @return {boolean}\n */\n public processNavigationRequest(request: string, targetActivityId?: string): boolean {\n if (this._sequencingService) {\n return this._sequencingService.processNavigationRequest(request, targetActivityId);\n }\n return false;\n }\n\n /**\n * Reset sequencing state explicitly\n */\n public resetSequencingState(): void {\n this._sequencing?.reset();\n this._sequencingService?.setEventListeners({});\n }\n\n /**\n * Get tracking data for a specific activity\n * @param {string} activityId\n * @return {object | null}\n */\n public getActivityTrackingData(activityId: string): {\n completionStatus: string;\n successStatus: string;\n progressMeasure: number | null;\n score: number | null;\n } | null {\n if (!this._sequencing?.activityTree) {\n return null;\n }\n\n const activity = this._sequencing.activityTree.getActivity(activityId);\n if (!activity) {\n return null;\n }\n\n return {\n completionStatus: activity.completionStatus || \"unknown\",\n successStatus: activity.successStatus || \"unknown\",\n progressMeasure: activity.progressMeasure ?? null,\n score: activity.objectiveMeasureStatus ? activity.objectiveNormalizedMeasure : null,\n };\n }\n\n /**\n * Save current sequencing state to persistent storage\n * @param {Partial<SequencingStateMetadata>} metadata\n * @return {Promise<boolean>}\n */\n public async saveSequencingState(metadata?: Partial<SequencingStateMetadata>): Promise<boolean> {\n if (this._statePersistence) {\n return this._statePersistence.saveSequencingState(metadata);\n }\n\n if (!this.settings.sequencingStatePersistence) {\n this.apiLog(\n \"saveSequencingState\",\n \"No persistence configuration provided\",\n LogLevelEnum.WARN,\n );\n return false;\n }\n\n // Fallback: create persistence on the fly - use getter to always get current settings\n const persistenceContext: PersistenceContext = {\n getSettings: () => this.settings,\n apiLog: this.apiLog.bind(this),\n adl: this.adl,\n sequencing: this._sequencing,\n sequencingService: this._sequencingService,\n learnerId: this.cmi.learner_id,\n };\n const persistence = new SequencingStatePersistence(\n persistenceContext,\n this._globalObjectiveManager,\n );\n return persistence.saveSequencingState(metadata);\n }\n\n /**\n * Load sequencing state from persistent storage\n * @param {Partial<SequencingStateMetadata>} metadata\n * @return {Promise<boolean>}\n */\n public async loadSequencingState(metadata?: Partial<SequencingStateMetadata>): Promise<boolean> {\n if (this._statePersistence) {\n return this._statePersistence.loadSequencingState(metadata);\n }\n\n if (!this.settings.sequencingStatePersistence) {\n this.apiLog(\n \"loadSequencingState\",\n \"No persistence configuration provided\",\n LogLevelEnum.WARN,\n );\n return false;\n }\n\n // Fallback: create persistence on the fly - use getter to always get current settings\n const persistenceContext: PersistenceContext = {\n getSettings: () => this.settings,\n apiLog: this.apiLog.bind(this),\n adl: this.adl,\n sequencing: this._sequencing,\n sequencingService: this._sequencingService,\n learnerId: this.cmi.learner_id,\n };\n const persistence = new SequencingStatePersistence(\n persistenceContext,\n this._globalObjectiveManager,\n );\n return persistence.loadSequencingState(metadata);\n }\n\n /**\n * Serialize current sequencing state to JSON string\n * @return {string} Serialized state\n */\n public serializeSequencingState(): string {\n if (this._statePersistence) {\n return this._statePersistence.serializeSequencingState();\n }\n\n // Fallback: create persistence on the fly - use getter to always get current settings\n const persistenceContext: PersistenceContext = {\n getSettings: () => this.settings,\n apiLog: this.apiLog.bind(this),\n adl: this.adl,\n sequencing: this._sequencing,\n sequencingService: this._sequencingService,\n learnerId: this.cmi.learner_id,\n };\n const persistence = new SequencingStatePersistence(\n persistenceContext,\n this._globalObjectiveManager,\n );\n return persistence.serializeSequencingState();\n }\n\n /**\n * Deserialize sequencing state from JSON string\n * @param {string} stateData - Serialized state data\n * @return {boolean} Success status\n */\n public deserializeSequencingState(stateData: string): boolean {\n if (this._statePersistence) {\n return this._statePersistence.deserializeSequencingState(stateData);\n }\n\n // Fallback: create persistence on the fly - use getter to always get current settings\n const persistenceContext: PersistenceContext = {\n getSettings: () => this.settings,\n apiLog: this.apiLog.bind(this),\n adl: this.adl,\n sequencing: this._sequencing,\n sequencingService: this._sequencingService,\n learnerId: this.cmi.learner_id,\n };\n const persistence = new SequencingStatePersistence(\n persistenceContext,\n this._globalObjectiveManager,\n );\n return persistence.deserializeSequencingState(stateData);\n }\n\n /**\n * Determines the appropriate cmi.entry value based on previous exit state.\n * @param {string} previousExit\n * @param {boolean} hasSuspendData\n * @return {string}\n */\n public determineEntryValue(previousExit: string, hasSuspendData: boolean): string {\n return this._dataSerializer.determineEntryValue(previousExit, hasSuspendData);\n }\n}\n\nexport default Scorm2004API;\n"],"names":["designations","D","H","M","S","getSecondsAsHHMMSS","totalSeconds","hours","Math","floor","dateObj","Date","minutes","getUTCMinutes","seconds","getSeconds","ms","msStr","countDecimals","toFixed","String","split","replace","getSecondsAsISODuration","duration","remainder","Object","entries","forEach","_ref","designationsKey","current_seconds","value","indexOf","includes","getTimeAsSeconds","memoize","timeString","timeRegex","RegExp","match","test","parts","toString","getDurationAsSeconds","durationRegex","years","months","weeks","days","exec","result","validateISO8601Duration","flatten","data","recurse","cur","prop","Array","isArray","item","i","length","keys","filter","p","prototype","hasOwnProperty","call","isEmpty","num","stringMatches","str","tester","fn","keyFn","cache","Map","_len","arguments","args","_key","key","JSON","stringify","has","get","set","BaseCMI","constructor","cmi_element","__publicField","this","_cmi_element","initialized","_initialized","initialize","BaseRootCMI","super","start_time","_start_time","setStartTime","Error","getTime","BaseScormValidationError","CMIElement","errorCode","_errorCode","setPrototypeOf","ValidationError","errorMessage","detailedMessage","message","_errorMessage","_detailedMessage","global_constants","scorm12_constants","basicMessage","detailMessage","scorm2004_constants","cmi_children","comments_children","score_children","objectives_children","correct_responses_children","student_preference_children","interactions_children","adl_data_children","error_descriptions","scorm12_errors","Scorm12ValidationError","scorm2004_errors","Scorm2004ValidationError","global_errors","GENERAL","INITIALIZATION_FAILED","INITIALIZED","TERMINATED","TERMINATION_FAILURE","TERMINATION_BEFORE_INIT","MULTIPLE_TERMINATION","RETRIEVE_BEFORE_INIT","RETRIEVE_AFTER_TERM","STORE_BEFORE_INIT","STORE_AFTER_TERM","COMMIT_BEFORE_INIT","COMMIT_AFTER_TERM","ARGUMENT_ERROR","CHILDREN_ERROR","COUNT_ERROR","GENERAL_GET_FAILURE","GENERAL_SET_FAILURE","GENERAL_COMMIT_FAILURE","UNDEFINED_DATA_MODEL","UNIMPLEMENTED_ELEMENT","VALUE_NOT_INITIALIZED","INVALID_SET_VALUE","READ_ONLY_ELEMENT","WRITE_ONLY_ELEMENT","TYPE_MISMATCH","VALUE_OUT_OF_RANGE","DEPENDENCY_NOT_ESTABLISHED","MULTIPLE_TERMINATIONS","CMIArray","params","__children","children","_errorClass","errorClass","childArray","reset","wipe","_children","_count","toJSON","jsonString","NAVBoolean","SuccessStatus","CompletionStatus","LogLevelEnum","_","DEBUG","INFO","WARN","ERROR","NONE","DefaultSettings","autocommit","autocommitSeconds","throttleCommits","useAsynchronousCommits","sendFullCommit","lmsCommitUrl","dataCommitFormat","commitRequestDataType","autoProgress","logLevel","selfReportSessionTime","alwaysSendTotalTime","renderCommonCommitFields","autoCompleteLessonStatus","strict_errors","xhrHeaders","xhrWithCredentials","fetchMode","asyncModeBeaconBehavior","responseHandler","async","response","httpResult","json","text","responseText","parse","e","status","xhrResponseHandler","xhr","requestHandler","commitObject","onLogMessage","defaultLogHandler","mastery_override","score_overrides_status","completion_status_on_failed","scoItemIds","scoItemIdValidator","globalObjectiveIds","enableOfflineSupport","courseId","syncOnInitialize","syncOnTerminate","maxSyncAttempts","scoId","autoPopulateCommitMetadata","httpService","globalStudentPreferences","messageLevel","logMessage","console","error","warn","info","debug","log","scorm12_regex","scorm2004_regex","LearnerResponses","format","max","delimiter","unique","choice","matching","format2","delimiter2","performance","sequencing","likert","numeric","other","CorrectResponses","duplicate","limit","ValidLanguages","ScheduledCommit","API","when","callback","_API","_timeout","setTimeout","wrapper","bind","_callback","cancel","_cancelled","clearTimeout","isInitialized","commit","HIDE_LMS_UI_TOKENS","RuleConditionOperator","RuleActionType","_RuleCondition","condition","operator","undefined","parameters","_condition","_operator","_parameters","setNowProvider","now","_now","setElapsedSecondsHook","hook","_getElapsedSecondsHook","referencedObjective","_referencedObjective","objectiveId","resolveReferencedObjective","activity","primaryObjective","id","objectives","find","obj","evaluate","satisfiedStatus","successStatus","objectiveSatisfiedStatus","measureStatus","objectiveMeasureStatus","greaterThanValue","normalizedMeasure","objectiveNormalizedMeasure","lessThanValue","completionStatus","isCompleted","attemptCount","hasAttemptLimitExceeded","evaluateTimeLimitExceeded","evaluateOutsideAvailableTimeRange","timeLimitDuration","attemptAbsoluteDurationLimit","limitSeconds","elapsedSeconds","hookResult","Number","isNaN","attemptExperiencedDuration","attemptDurationSeconds","attemptAbsoluteStartTime","start","nowMs","beginTime","beginTimeLimit","endTime","endTimeLimit","parseISO8601Duration","fromEntries","RuleCondition","SequencingRule","action","conditionCombination","_action","_conditionCombination","_conditions","conditions","addCondition","push","removeCondition","index","splice","every","some","SequencingRules","_preConditionRules","_exitConditionRules","_postConditionRules","preConditionRules","addPreConditionRule","rule","exitConditionRules","addExitConditionRule","postConditionRules","addPostConditionRule","evaluatePreConditionRules","evaluateExitConditionRules","evaluatePostConditionRules","ActivityTreeQueries","activityTree","isInTree","getAllActivities","isAncestorOf","ancestor","descendant","current","parent","findCommonAncestor","activity1","activity2","ancestors1","findChildInPath","target","isLastInTree","getNextSibling","getCurrentInParent","child","isActive","isMandatory","sequencingRules","mandatory","isAvailableForChoice","isVisible","isHiddenFromChoice","isAvailable","sequencingControls","getAncestors","ancestors","getPathToRoot","path","isLeaf","isCluster","getDepth","depth","ChoiceConstraintValidator","treeQueries","validateChoice","currentActivity","targetActivity","options","valid","exception","root","pathValidation","validatePathToRoot","checkAvailability","choiceExitValidation","validateChoiceExit","ancestorValidation","validateAncestorConstraints","preventActivation","currentAncestor","choiceExit","ancestorActivity","validation","validateConstraintsAtLevel","targetChild","currentChild","siblings","targetIndex","currentIndex","forwardOnly","intermediateChild","constrainChoice","checkForwardOnlyViolation","fromActivity","validateFlowConstraints","validChildren","meetsFlowConstraints","validateConstrainChoiceForFlow","flow","validateTraversalConstraints","canTraverse","canTraverseInto","evaluateConstrainChoiceForTraversal","stopForwardTraversal","evaluateForwardOnlyForChoice","ancestorChildren","childInPath","childIndex","currentAtLevel","intermediateActivity","hasTimeBoundaryViolation","hasAttemptLimitViolation","attemptLimit","SequencingRequestType","DeliveryRequestType","SequencingResult","deliveryRequest","endSequencingSession","FlowSubprocessResult","identifiedActivity","deliverable","ChoiceTraversalResult","FlowSubprocessMode","RuleEvaluationEngine","getAttemptElapsedSecondsHook","checkSequencingRules","rules","checkRuleSubprocess","AND","OR","evaluateExitRules","exitAction","EXIT","EXIT_PARENT","EXIT_ALL","evaluatePostConditionAction","postAction","RETRY","RETRY_ALL","CONTINUE","PREVIOUS","STOP_FORWARD_TRAVERSAL","evaluatePostConditions","sequencingRequest","terminationRequest","checkLimitConditions","attemptLimitMs","parseDuration","activityAbsoluteDurationLimit","activityLimitMs","activityExperiencedDuration","matches","endsWith","totalMs","parseFloat","getElapsedSeconds","isTimeLimitExceeded","isOutsideAvailableTimeRange","beginDate","endDate","canDeliverActivity","canDeliver","wasSkipped","preConditionResult","SKIP","DISABLED","SelectionTiming","RandomizationTiming","SequencingControls","_enabled","_choice","_choiceExit","_flow","_forwardOnly","_useCurrentAttemptObjectiveInfo","_useCurrentAttemptProgressInfo","_preventActivation","_constrainChoice","_stopForwardTraversal","_rollupObjectiveSatisfied","_rollupProgressCompletion","_objectiveMeasureWeight","_selectionTiming","_selectCount","_selectionCountStatus","_randomizeChildren","_randomizationTiming","_reorderChildren","_completionSetByContent","_objectiveSetByContent","_tracked","enabled","useCurrentAttemptObjectiveInfo","useCurrentAttemptProgressInfo","rollupObjectiveSatisfied","rollupProgressCompletion","objectiveMeasureWeight","isChoiceNavigationAllowed","isFlowNavigationAllowed","isForwardNavigationAllowed","isBackwardNavigationAllowed","selectionTiming","selectCount","selectionCountStatus","randomizeChildren","randomizationTiming","reorderChildren","completionSetByContent","objectiveSetByContent","tracked","SelectionRandomization","selectChildrenProcess","controls","NEVER","ONCE","selectedChildren","availableIndices","map","randomIndex","random","randomizeChildrenProcess","randomizedChildren","j","tempI","tempJ","applySelectionAndRandomization","isNewAttempt","isSuspended","shouldApplySelection","shouldApplyRandomization","ON_EACH_NEW_ATTEMPT","processedChildren","setProcessedChildren","isSelectionNeeded","isRandomizationNeeded","FlowTraversalService","ruleEngine","flowSubprocess","direction","candidateActivity","firstIteration","lastCandidateHadNoChildren","traversalResult","flowTreeTraversalSubprocess","exceptionCode","BACKWARD","getAvailableChildren","flowActivityTraversalSubprocess","FORWARD","traverseForward","traverseBackward","skipChildren","isActivityLastOverall","terminateDescendentAttempts","ensureSelectionAndRandomization","nextSibling","previousSibling","getPreviousSibling","getLastDescendant","ancestorIterations","parentPreviousSibling","lastDescendant","iterations","lastChild","_direction","considerChildren","mode","availableChildren","checkActivityProcess","deliveryCheck","findFirstDeliverableActivity","cluster","FlowRequestHandler","traversalService","handleStart","deliverableActivity","DELIVER","handleResumeAll","suspendedActivity","handleContinue","flowResult","handlePrevious","forwardOnlyViolation","ChoiceRequestHandler","constraintValidator","handleChoice","targetActivityId","getActivity","commonAncestor","terminateDescendentAttemptsProcess","activityPath","buildActivityPath","pathActivity","deliveryTarget","choiceFlowSubprocess","handleJump","getAvailableChoices","allActivities","availableActivities","unshift","choiceFlowTreeTraversal","enhancedChoiceTraversal","traversalValidation","ExitRequestHandler","handleExit","handleExitAll","handleAbandon","handleAbandonAll","handleSuspendAll","skipExitRules","processDeferredExitAction","a","RetryRequestHandler","handleRetry","handleRetryAll","SequencingProcess","_sequencingRules","_sequencingControls","_getAttemptElapsedSecondsHook","getAttemptElapsedSeconds","flowHandler","choiceHandler","exitHandler","retryHandler","sequencingRequestProcess","request","START","RESUME_ALL","DO_NOT_DELIVER","CHOICE","JUMP","ABANDON","ABANDON_ALL","SUSPEND_ALL","canActivityBeDelivered","validateNavigationRequest","values","choiceValidation","HIDE_FROM_CHOICE","getTreeQueries","getConstraintValidator","getRuleEngine","getTraversalService","ActivityDeliveryService","eventService","loggingService","callbacks","processSequencingResult","onSequencingError","deliverActivity","onSequencingComplete","currentDeliveredActivity","unloadActivity","pendingDelivery","title","processListeners","onDeliverActivity","onUnloadActivity","getCurrentDeliveredActivity","getPendingDelivery","updateCallbacks","AsynchronousHttpService","settings","error_codes","processHttpRequest","url","_performAsyncRequest","immediate","apiLog","processedParams","performBeacon","performFetch","transformResponse","_isSuccessResponse","_prepareRequestBody","body","join","contentType","init","method","headers","keepalive","credentials","fetch","beaconSuccess","navigator","sendBeacon","Blob","type","Promise","resolve","ok","parseError","catch","errorDetails","statusText","substring","updateSettings","TARGET_ATTRIBUTE_PREFIX","getErrorCode","errorCodes","code","CMIValueAccessService","context","getUndefinedDataModelErrorCode","scorm2004","setCMIValue","methodName","throwSCORMError","setLastErrorCode","structure","refObject","getDataModel","returnValue","foundFirstIndex","invalidErrorMessage","invalidErrorCode","idx","setFinalAttribute","traverseResult","traverseToNextLevel","getCMIValue","attribute","uninitializedErrorMessage","validationResult","validateGetAttribute","arrayResult","handleGetArrayAccess","startsWith","checkObjectHasProperty","validateCorrectResponse","getLastErrorCode","checkForDuplicateId","handleSetArrayAccess","parseInt","newChild","getChildElement","isFinalAttribute","attrStr","_isTargetValid","_LoggingService","_logHandler","getInstance","_instance","setLogLevel","level","_logLevel","getLogLevel","setLogHandler","handler","shouldLog","getNumericLevel","LoggingService","getLoggingService","ErrorHandlingService","getLmsErrorMessageDetails","_errorCodes","_apiLog","_getLmsErrorMessageDetails","_loggingService","lastErrorCode","_lastErrorCode","lastDiagnostic","_lastDiagnostic","errorNumber","formattedMessage","clearSCORMError","success","handleValueAccessException","validationError","errorType","name","stack","jsonError","EventService","parseListenerName","listenerName","listenerSplit","functionName","on","listenerFunctions","listenerFunction","parsedListener","listeners","listenerMap","listenerCount","off","removeIndex","findIndex","delete","clear","newListeners","listener","listenerHasCMIElement","CMIElementsMatch","prefix","slice","OfflineStorageService","onLine","boundOnlineStatusChangeHandler","handleOnlineStatusChange","boundCustomNetworkStatusHandler","handleCustomNetworkStatus","window","addEventListener","wasOnline","isOnline","syncOfflineData","then","event","CustomEvent","online","detail","storeOffline","commitData","queueItem","timestamp","syncAttempts","currentQueue","getFromStorage","syncQueue","saveToStorage","storeName","isQuotaError","getOfflineData","syncInProgress","remainingQueue","maxAttempts","syncResult","sendDataToLMS","processedData","isDeviceOnline","storedData","localStorage","getItem","setItem","DOMException","cause","hasPendingOfflineData","destroy","removeEventListener","checkValidFormat","regexPattern","allowEmptyString","formatRegex","checkValidRange","rangePattern","ranges","minBound","maxBound","hasMaximum","check2004ValidFormat","check2004ValidRange","RollupActionType","RollupConsiderationType","RollupCondition","objectiveSatisfiedStatusKnown","RollupRule","consideration","minimumCount","minimumPercent","_consideration","_minimumCount","_minimumPercent","matchingChildren","RollupRules","_rules","addRule","removeRule","processRollup","completionRollup","successRollup","_objectiveRollupUsingMeasure","_defaultCompletionRollup","_defaultSuccessRollup","totalWeight","weightedSum","hasValidMeasures","childWeight","scaledPassingScore","ActivityObjective","_id","_description","description","_satisfiedByMeasure","satisfiedByMeasure","_minNormalizedMeasure","minNormalizedMeasure","_mapInfo","mapInfo","_isPrimary","isPrimary","_satisfiedStatus","_satisfiedStatusDirty","satisfiedStatusKnown","_satisfiedStatusKnown","_measureStatus","_normalizedMeasure","_normalizedMeasureDirty","progressMeasure","_progressMeasure","_progressMeasureDirty","progressMeasureStatus","_progressMeasureStatus","_completionStatus","_completionStatusDirty","progressStatus","_progressStatus","isDirty","property","clearDirty","clearAllDirty","initializeFromCMI","resetState","updateFromActivity","applyToActivity","setPrimaryObjectiveState","Activity","requiredForSatisfied","requiredForNotSatisfied","requiredForCompleted","requiredForIncomplete","measureSatisfactionIfActive","_title","_rollupRules","_primaryObjective","_objectives","_isActive","_isSuspended","_isCompleted","_successStatus","_attemptCount","_attemptCompletionAmount","_attemptAbsoluteDuration","_attemptExperiencedDuration","_activityAbsoluteDuration","_activityExperiencedDuration","_attemptAbsoluteDurationValue","_attemptExperiencedDurationValue","_activityAbsoluteDurationValue","_activityExperiencedDurationValue","_activityStartTimestampUtc","_attemptStartTimestampUtc","_activityEndedDate","_objectiveSatisfiedStatus","_objectiveSatisfiedStatusKnown","_objectiveMeasureStatus","_objectiveNormalizedMeasure","_location","_attemptAbsoluteStartTime","_learnerPrefs","_activityAttemptActive","objective","_wasSkipped","_attemptProgressStatus","_wasAutoCompleted","_wasAutoSatisfied","_completedByMeasure","_minProgressMeasure","_progressWeight","_attemptCompletionAmountStatus","clearAllObjectiveDirty","addChild","_parent","setChildOrder","order","childMap","reordered","size","removeChild","_isVisible","updatePrimaryObjectiveFromActivity","attemptCompletionAmount","incrementAttemptCount","_isNewAttempt","_processedChildren","_objectiveSatisfiedStatusDirty","_objectiveMeasureStatusDirty","_objectiveNormalizedMeasureDirty","_scaledPassingScore","location","learnerPrefs","activityAttemptActive","_isHiddenFromChoice","_isAvailable","_attemptLimit","_timeLimitDuration","timeLimitAction","_timeLimitAction","_beginTimeLimit","_endTimeLimit","_attemptAbsoluteDurationLimit","_activityAbsoluteDurationLimit","attemptAbsoluteDuration","activityAbsoluteDuration","attemptAbsoluteDurationValue","attemptExperiencedDurationValue","activityAbsoluteDurationValue","activityExperiencedDurationValue","activityStartTimestampUtc","attemptStartTimestampUtc","activityEndedDate","date","rollupRules","rollupConsiderations","_rollupConsiderations","config","applyRollupConsiderations","_requiredForSatisfied","_requiredForNotSatisfied","_requiredForCompleted","_requiredForIncomplete","attemptProgressStatus","wasAutoCompleted","wasAutoSatisfied","completedByMeasure","minProgressMeasure","progressWeight","attemptCompletionAmountStatus","syncPrimaryObjectiveCollection","addObjective","existingIndex","getObjectiveById","additional","getAllObjectives","additionalObjectives","concat","isObjectiveDirty","clearObjectiveDirty","getObjectiveStateSnapshot","primary","applyObjectiveStateSnapshot","snapshot","state","resetProcessedChildren","launchData","_launchData","credit","_credit","maxTimeAllowed","_maxTimeAllowed","completionThreshold","_completionThreshold","getSuspensionState","c","restoreSuspensionState","objState","o","childState","hideLmsUi","_hideLmsUi","auxiliaryResources","_auxiliaryResources","resource","resources","sanitized","seen","Set","resourceId","trim","purpose","add","addAuxiliaryResource","captureRollupStatus","objectiveProgressStatus","attemptCompletionStatus","compareRollupStatus","prior","abs","directive","RollupChildFilter","checkChildForRollupSubprocess","rollupType","rollupAction","included","filterChildrenForRequirement","considerations","shouldIncludeChildForRollup","isChildSatisfiedForRollup","isChildCompletedForRollup","getTrackableChildren","RollupRuleEvaluator","childFilter","evaluateRollupRule","contributingChildren","satisfiedCount","isIncluded","SATISFIED","NOT_SATISFIED","COMPLETED","INCOMPLETE","evaluateRollupConditionsSubprocess","ALL","ANY","AT_LEAST_COUNT","AT_LEAST_PERCENT","evaluateRulesForAction","actionType","matchingRules","MeasureRollupProcessor","eventCallback","measureRollupProcess","complexWeightedMeasure","calculateComplexWeightedMeasure","enableThresholdBias","identifyActivityClusters","completionMeasureRollupProcess","totalWeightedMeasure","weightingLog","enableBias","adjustedWeight","calculateAdjustedWeight","childId","measure","weight","activityId","weightingDetails","baseWeight","clusters","ObjectiveRollupProcessor","ruleEvaluator","objectiveRollupProcess","ruleResult","objectiveRollupUsingRules","syncPrimaryObjectiveFromActivity","measureResult","objectiveRollupUsingMeasure","objectiveRollupUsingDefault","satisfiedRules","notSatisfiedRules","contributors","ProgressRollupProcessor","objectiveProcessor","activityProgressRollupProcess","activityProgressRollupUsingMeasure","completedRules","incompleteRules","DurationRollupProcessor","durationRollupProcess","earliestChildActivityStartTimestampUtc","earliestChildAttemptStartTimestampUtc","latestChildEndDate","latestAttemptChildEndDate","childrenActivityExperiencedDurationSeconds","childrenAttemptExperiencedDurationSeconds","activityDuration","attemptDuration","startDate","durationMs","childCount","CrossClusterProcessor","measureProcessor","progressProcessor","processCrossClusterDependencies","processingClusters","clusterCount","dependencyMap","analyzeCrossClusterDependencies","processOrder","resolveDependencyOrder","clusterId","processClusterRollup","processedClusters","from","maxDepth","resolved","resolving","dependencies","depId","nestedClusters","GlobalObjectiveSynchronizer","processGlobalObjectiveMapping","globalObjectives","globalObjectiveCount","collectActivitiesRecursive","act","syncGlobalObjectivesWritePhase","syncGlobalObjectivesReadPhase","processedObjectives","mapInfos","createDefaultMapInfo","globalObjective","ensureGlobalObjectiveEntry","targetObjectiveID","writeSatisfiedStatus","writeNormalizedMeasure","normalizedMeasureKnown","writeCompletionStatus","completionStatusKnown","writeProgressMeasure","progressMeasureKnown","updateAttemptData","updateActivityAttemptData","readSatisfiedStatus","readNormalizedMeasure","readProgressMeasure","readCompletionStatus","globalState","synchronizationTime","toISOString","synchronizeGlobalObjectives","syncObjectiveState","localObjective","getLocalObjectiveState","localState","targetId","hasCompletionRollupRules","suspendData","RollupStateValidator","validateRollupStateConsistency","rootActivity","inconsistencies","validateActivityRollupState","count","satisfiedChildren","notSatisfiedChildren","completedChildren","incompleteChildren","rollupStateLog","getRollupStateLog","clearRollupStateLog","RollupProcess","durationProcessor","globalObjectiveSynchronizer","stateValidator","crossClusterProcessor","overallRollupProcess","affectedActivities","onlyDurationRollup","isFirst","beforeStatus","afterStatus","getChildFilter","getRuleEvaluator","getMeasureProcessor","getObjectiveProcessor","getProgressProcessor","getDurationProcessor","getGlobalObjectiveSynchronizer","getStateValidator","getCrossClusterProcessor","VALID_COMPLETION_STATUSES","VALID_SUCCESS_STATUSES","validateCompletionStatus","validateSuccessStatus","RteDataTransferService","transferRteData","getCMIData","cmiData","transferPrimaryObjective","transferNonPrimaryObjectives","fireEvent","validatedCompletionStatus","completion_status","hasSuccessStatus","hasNormalizedMeasure","normalizedScore","validatedSuccessStatus","success_status","score","normalized","normalizeScore","progress_measure","cmiObjective","activityObjectiveMatch","activityObjective","validatedObjSuccessStatus","validatedObjCompletionStatus","scaled","raw","min","TerminationHandler","sequencingProcess","rollupProcess","globalObjectiveMap","is4thEdition","_rteDataTransferService","eventType","setInvalidateCacheCallback","invalidateCacheCallback","processTerminationRequest","hasSequencingRequest","exitType","handleExitTermination","terminateDescendants","endAttempt","exitActionResult","processedExit","postConditionResult","toActivity","setCurrentActivityWithoutActivation","_currentActivity","handleMultiLevelExit","cleanupSuspendedActivity","suspendResult","processSuspendAllRequest","suspendedPath","pathLength","recursionDepth","exitRules","conditionsMet","postResult","processExitAtLevel","terminateAll","cleanedActivities","originalSuspendedActivity","exitActionRulesSubprocess","clearSuspendedActivity","hasSuspendedChildren","DeliveryRequest","_DeliveryHandler","adlNav","defaultHideLmsUi","defaultAuxiliaryResources","setCheckActivityCallback","checkActivityCallback","setUpdateNavigationValidityCallback","updateNavigationValidityCallback","setClearSuspendedActivityCallback","clearSuspendedActivityCallback","isDeliveryInProgress","_deliveryInProgress","hasContentBeenDelivered","contentDelivered","resetContentDelivered","setContentDelivered","processDeliveryRequest","getActivityPath","contentDeliveryEnvironmentProcess","isResuming","initializeForDelivery","setupAttemptTracking","fireDeliveryEvent","audioCaptioning","audioLevel","deliverySpeed","language","getEffectiveHideLmsUi","HIDE_LMS_UI_ORDER","getEffectiveAuxiliaryResources","merged","lineage","node","reverse","getContentActivityData","includeActivity","pop","DeliveryHandler","NavigationLookAhead","predictContinueEnabled","ensureCacheValid","continueEnabled","predictPreviousEnabled","previousEnabled","predictChoiceEnabled","choiceEnabledMap","availableChoices","getAllPredictions","invalidateCache","updateCache","currentTreeHash","calculateTreeStateHash","treeStateHash","recalculateCache","predictContinueInternal","predictPreviousInternal","calculateAvailableChoices","calculateAvailableChoicesFromRoot","hasAvailableNextActivity","sibling","isActivityPotentiallyDeliverableForward","hasAvailablePreviousActivity","isActivityPotentiallyDeliverableBackward","recursivelyCheckChoiceAvailability","isChoiceEnabled","getActivityTreeStateSignature","signatures","collectActivitySignatures","NavigationRequestType","NavigationRequestResult","NavigationValidityService","navigationLookAhead","setGetEffectiveHideLmsUiCallback","getEffectiveHideLmsUiCallback","getNavigationLookAhead","validateRequest","validateStartRequest","validateResumeRequest","validateContinueRequest","validatePreviousRequest","validateChoiceRequest","validateJumpRequest","validateExitRequest","validateExitAllRequest","validateAbandonRequest","validateAbandonAllRequest","validateSuspendAllRequest","forwardOnlyValidation","validateForwardOnlyConstraints","validateChoicePath","isActivityDisabled","activityContains","constrainChoiceValidation","validateConstrainChoice","choiceSetValidation","validateChoiceSet","currentBranch","findChildContaining","targetBranch","requiresNewActivation","validateAncestors","evaluatePreConditionRulesForChoice","container","currentTop","targetTop","traversalStopIndex","between","isActivityMandatory","isActivityCompleted","branchRoot","branchHasActiveAttempt","updateNavigationValidity","continueValid","request_valid","continue","previousValid","previous","choiceMap","jumpMap","choiceRes","jumpRes","jump","GlobalObjectiveService","collectObjectives","objectiveCount","defaultId","readRawScore","writeRawScore","readMinScore","writeMinScore","readMaxScore","writeMaxScore","getMap","getSnapshot","serialize","restoreSnapshot","restore","updateObjective","objectiveData","lastUpdated","synchronize","getObjective","hasObjective","getObjectiveIds","getObjectiveCount","serialized","mapData","SequencingStateManager","globalObjectiveService","setGetEffectiveAuxiliaryResourcesCallback","getEffectiveAuxiliaryResourcesCallback","setContentDeliveredAccessors","getter","setter","contentDeliveredGetter","contentDeliveredSetter","getState","version","activityStates","serializeActivities","navigationState","getNavigationState","restoreState","deserializeActivities","restoreNavigationState","states","serializeActivity","selectionRandomizationState","childOrder","selectedChildIds","hiddenFromChoiceChildIds","restoreActivity","selectionState","selectedSet","hiddenSet","isSelected","requestValid","exit","exitAll","abandon","abandonAll","suspendAll","navState","currentActivityId","suspendedActivityId","hasActivityTree","originalTimestamp","restoreTimestamp","DeliveryValidator","setContentDeliveredGetter","validateTreeConsistency","consistent","isActivityPartOfTree","activeActivities","getActiveActivities","validateResources","requiredResources","getRequiredResources","isResourceAvailable","available","checkSystemLimits","adequate","validateConcurrentDelivery","allowed","hasPendingDeliveryRequests","isDeliveryLocked","validateDependencies","prerequisites","getPrerequisites","prerequisite","isPrerequisiteSatisfied","satisfied","objectiveDependencies","getObjectiveDependencies","dependency","isObjectiveDependencySatisfied","getSequencingRuleDependencies","checkActivity","failureReason","limitDuration","currentTime","checks","attemptDurationLimit","activityDurationLimit","collectActiveActivities","activityInfo","toLowerCase","parseDurationToMinutes","document","createElement","canPlayType","plugins","plugin","connection","effectiveType","downlink","memory","usedJSHeapSize","jsHeapSizeLimit","deviceMemory","hardwareConcurrency","saveData","pendingRequests","pendingScormRequests","navigationLocked","terminationInProgress","scormMaintenanceMode","referencedObjectiveID","activityIndex","prerequisiteActivities","prerequisiteId","_activity","globalObjectiveID","allRules","objectiveReference","statusKnown","baseObjectiveId","measureKnown","referencedActivity","conditionType","OverallSequencingProcess","enhancedDeliveryValidation","deliveryValidatorOptions","deliveryValidator","terminationOptions","terminationHandler","deliveryOptions","deliveryHandler","navigationValidityService","stateManager","setupCallbacks","processNavigationRequest","navigationRequest","navResult","hadSequencingRequest","termResult","reason","seqResult","processDelivery","stateCheck","resourceCheck","concurrentCheck","dependencyCheck","deliveryResult","getGlobalObjectiveMap","getGlobalObjectiveMapSnapshot","restoreGlobalObjectiveMapSnapshot","updateGlobalObjective","getSequencingState","restoreSequencingState","invalidateNavigationCache","applyDeliveryControls","terminationRequestProcess","endAttemptProcess","SequencingService","cmi","adl","configuration","autoRollupOnCMIChange","autoProgressOnCompletion","validateNavigationRequests","enableEventSystem","activityDeliveryService","handleActivityDelivery","handleActivityUnload","handleSequencingComplete","handleSequencingError","setupCMIChangeWatchers","createSequencingProcesses","nav","seqOptions","getActivityElapsedSeconds","overallOptions","getCMIDataForTransfer","overallSequencingProcess","handleSequencingProcessEvent","shouldAutoStartSequencing","isSequencingActive","startSequencing","initializeCMITracking","getCurrentActivity","errorMsg","terminate","triggerFinalRollup","endSequencing","navRequestType","parseNavigationRequest","sequencingResult","lastSequencingResult","triggerRollupOnCMIChange","cmiElement","oldValue","newValue","element","updateActivityFromCMI","setEventListeners","eventListeners","updateConfiguration","getRootActivity","getOverallSequencingProcess","lastCMIValues","scaledScore","baseCmiObj","normalizedRequest","NOT_VALID","fireDebugEvent","argsLength","listenerError","eventServiceError","scormSequencingEvents","globalListeners","globalError","fireActivityAttemptStart","fireActivityAttemptEnd","fireLimitConditionCheck","fireNavigationValidityUpdate","validity","fireSequencingStateChange","stateKeys","logLevels","configLevel","SerializationService","loadFromFlattenedJSON","isNotInitialized","setStartingData","int_pattern","obj_pattern","interactions","others","intMatch","field","objMatch","sort","b","localeCompare","processItems","items","loadFromJSON","pattern","regex","m","unflatten","currentCMIElement","tempCMIElement","renderCMIToJSONString","k","v","renderCMIToJSONObject","getCommitObject","terminateCommit","renderCommitObject","renderCommitCMI","apiLogLevel","includeTotalTime","SynchronousHttpService","_handleImmediateRequest","_performSyncXHR","requestPayload","XMLHttpRequest","open","setRequestHeader","withCredentials","send","check12ValidFormat","check12ValidRange","validationService","validateScore","decimalRegex","scoreRange","invalidTypeCode","invalidRangeCode","validateScorm12Audio","validateScorm12Language","validateScorm12Speed","validateScorm12Text","validateReadOnly","BaseAPI","serializationService","cmiDataService","errorHandlingService","offlineStorageService","TypeError","currentState","_error_codes","asyncCommit","_httpService","_eventService","_serializationService","_errorHandlingService","createErrorHandlingService","_offlineStorageService","_courseId","hasPendingData","syncSuccess","offlineData","runtimeData","_cmiValueAccessService","_checkForDuplicateId","attr","_checkObjectHasProperty","commonReset","clearScheduledCommit","startingData","callbackName","initializeMessage","terminationMessage","isTerminated","messageString","padEnd","formatMessage","_settings","previousSettings","checkTerminated","stateCheckPassed","storeData","resultValue","getValue","checkState","setValue","commitCallback","scheduleCommit","getLastError","getErrorString","CMIErrorCode","getDiagnostic","customDiagnostic","beforeInitError","afterTermError","getCMIArrayProperty","hasDuplicateId","array","idValue","objectivesMatch","interactionsMatch","interactionObjectivesMatch","interactionIndex","currentObjIndex","interaction","_errorNumber","_CMIElement","_value","_commonSetCMIValue","_commonGetCMIValue","getFlattenedCMI","getOwnPropertyDescriptor","getPrototypeOf","CMIScore","__score_range","score_range","_max","__invalid_error_code","__invalid_type_code","__invalid_range_code","__decimal_regex","__error_class","_raw","_min","getScoreObject","scoreObject","CMICore","_exit","_entry","_session_time","student_id","_student_id","student_name","_student_name","lesson_location","_lesson_location","lesson_status","_lesson_status","entry","total_time","_total_time","lesson_mode","_lesson_mode","session_time","suspend_data","_suspend_data","getCurrentTotalTime","sessionTime","first","second","_status","CMIStudentData","student_data_children","mastery_score","_mastery_score","normalizedMasteryScore","max_time_allowed","_max_time_allowed","normalizedValue","fieldName","parseTimeAllowed","time_limit_action","_time_limit_action","CMIStudentPreference","audio","_audio","_language","speed","_speed","_text","correct_responses","_time","_type","_weighting","_student_response","_result","_latency","time","weighting","student_response","normalizedResult","latency","_pattern","student_data","core","CMIObjectives","student_preference","CMIInteractions","_launch_data","_comments","launch_data","comments","comments_from_lms","_version","__version","_comments_from_lms","NAV","_event","_Scorm12API","settingsCopy","CMI","_globalLearnerPrefs","LMSInitialize","lmsInitialize","LMSFinish","lmsFinish","LMSGetValue","lmsGetValue","LMSSetValue","lmsSetValue","LMSCommit","lmsCommit","LMSGetLastError","lmsGetLastError","LMSGetErrorString","lmsGetErrorString","LMSGetDiagnostic","lmsGetDiagnostic","clearGlobalPreferences","statusSetByModule","_updateGlobalPreference","CMIObjectivesObject","CMIInteractionsCorrectResponsesObject","CMIInteractionsObjectivesObject","CMIInteractionsObject","replaceWithAnotherScormAPI","newAPI","cmiExport","flattened","totalTimeHHMMSS","totalTimeSeconds","lessonStatus","learnerId","learnerName","originalStatus","processCommitData","rawScore","masteryScore","Scorm12API","CMILearnerPreference","audio_level","_audio_level","delivery_speed","_delivery_speed","audio_captioning","_audio_captioning","_idIsSet","_timestamp","_learner_response","learner_response","nodes","response_type","stripBrackets","delim","escapeRegex","s","splitUnescaped","reDelim","splitRe","unescapeRe","part","splitFirstUnescaped","firstPart","interactionType","_interactionType","responseDef","subDelim1","rawNodes","delim1","fmt1","fmt2","checkSingle","checkPair","delimBracketed","n","numDelim","nums","part1","part2","validatePattern","Scorm2004CMIScore","_scaled","CMICommentsFromLMS","CMICommentsFromLearner","CMICommentsObject","readOnlyAfterInit","_comment","_readOnlyAfterInit","comment","findObjectiveById","findObjectiveByIndex","setObjectiveByIndex","_success_status","_completion_status","_progress_measure","fromJSON","CMIMetadata","CMILearner","learner_id","_learner_id","learner_name","_learner_name","CMIStatus","CMISession","getExitValueInternal","addTwoDurations","CMIContent","CMISettings","_mode","CMIThresholds","scaled_passing_score","_scaled_passing_score","completion_threshold","_completion_threshold","metadata","learner","session","content","thresholds","learner_preference","comments_from_learner","ADL","ADLData","ADLNav","_sequencing","ADLNavRequestValid","setParentNav","_request","ADLDataObject","_storeIsSet","store","_store","ADLNavRequestValidChoice","_parentNav","process","_staticValues","getAll","setAll","ADLNavRequestValidJump","_jump","_continue","_previous","_exitAll","_abandon","_abandonAll","_suspendAll","converted","ActivityTree","_root","_suspendedActivity","_activities","_addActivitiesToMap","activitiesToPreserve","getParent","getChildren","getSiblings","useAvailableChildren","getFirstChild","getLastChild","getCommonAncestor","path1","Sequencing","_activityTree","_adlNav","_overallSequencingProcess","_processRollupRecursive","Scorm2004ResponseValidator","checkValidResponseType","interaction_type","checkCorrectResponseValue","checkDuplicateChoiceResponse","interaction_count","pattern_index","checkDuplicatedPattern","correct_response","current_index","found","existingPattern","removeCorrectResponsePrefixes","seenOrder","seenCase","seenLang","prefixRegex","langMatches","lang","Scorm2004CMIHandler","responseValidator","createCorrectResponsesObject","evaluateCompletionStatus","threshold","storedStatus","thresholdValue","progressValue","evaluateSuccessStatus","passingScoreValue","scoreValue","SequencingConfigurationBuilder","applySequencingControlsSettings","applySequencingRulesSettings","ruleSettings","createSequencingRule","conditionSettings","applyRollupRulesSettings","createRollupRule","sanitizeSequencingCollections","collections","collection","trimmedId","sanitizedCollection","ruleClone","cloned","clonedCondition","clonedRule","cloneSelectionRandomizationState","sanitizeHideLmsUi","sanitizeAuxiliaryResources","normalizeCollectionRefs","refs","ref","trimmed","applySequencingCollection","selectionStates","mergeHideLmsUi","sanitizedAux","mergeAuxiliaryResources","existing","additions","clone","applySelectionRandomizationState","selectionTouched","ActivityTreeBuilder","sequencingConfigBuilder","sequencingCollections","setSequencingCollections","createActivity","activitySettings","collectionRefs","sequencingCollectionRefs","createActivityObjectiveFromSettings","objectiveSettings","mergedHide","childSettings","childActivity","extractActivityIds","ids","objectiveID","GlobalObjectiveManager","_globalObjectives","updateSequencingService","service","sequencingService","syncGlobalObjectiveIdsFromSequencing","overallProcess","globalIds","getSettings","mergedIds","restoreGlobalObjectivesToCMI","globalObj","commonSetCMIValue","updateGlobalObjectiveFromCMI","fallbackEntry","buildObjectiveMapEntryFromCMI","updatePayload","parseObjectiveNumber","buildCMIObjectivesFromMap","buildCMIObjectiveFromJSON","isFinite","captureGlobalObjectiveSnapshot","processSnapshot","parsed","syncCmiToSequencingActivity","findOrCreateGlobalObjective","newGlobalObjective","SequencingStatePersistence","globalObjectiveManager","saveSequencingState","sequencingStatePersistence","stateData","serializeSequencingState","fullMetadata","attemptNumber","stateVersion","dataToSave","compress","compressStateData","maxStateSize","persistence","saveState","debugPersistence","loadSequencingState","loadState","dataToLoad","decompressStateData","deserializeSequencingState","adlNavState","sequencingState","expectedVersion","restoredObjectives","objData","objectivesFromMap","btoa","encodeURIComponent","atob","decodeURIComponent","Scorm2004DataSerializer","setGlobalObjectiveManager","manager","totalTimeDuration","metaSettings","determineEntryValue","previousExit","hasSuspendData","trimmedExit","Scorm2004API","_sequencingConfigBuilder","_activityTreeBuilder","_responseValidator","_cmiHandler","globalObjectiveContext","_sequencingService","_globalObjectiveManager","dataSerializerContext","_dataSerializer","configureSequencing","initializeSequencingService","Initialize","Terminate","GetValue","SetValue","Commit","GetLastError","GetErrorString","GetDiagnostic","_statePersistence","pendingNavRequest","wasAlreadyTerminated","deliveryInProgress","navigationHandled","processedSequencingRequest","normalizedTarget","choiceJumpRegex","groups","choice_target","jump_target","targetForProcessing","requestToProcess","adlNavRequestRegex","predictJumpEnabled","_extractedScoItemIds","_peekCMIValue","rollupError","autoSaveOn","element_base","objective_id","global_index","global_element","updatedObjective","segments","nextIndex","isInteger","errorDescription","navRequest","completionStatusEnum","successStatusEnum","validCommands","command","dotIndex","eventName","resumeAll","sequencingSettings","_sequencingCollections","configureActivityTree","configureSequencingRules","configureSequencingControls","configureRollupRules","activityTreeSettings","sequencingRulesSettings","sequencingControlsSettings","rollupRulesSettings","persistenceContext","getSequencingService","setSequencingEventListeners","updateSequencingConfiguration","resetSequencingState","getActivityTrackingData"],"mappings":"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CRW,WAAW,EACXd,OAAQF,IAEV,UAAW,CACTG,IAAK,GACLC,UAAW,MACXC,QAAQ,EACRW,WAAW,EACXd,ODgGkB,uEC9FpB,eAAgB,CACdC,IAAK,EACLC,UAAW,GACXC,QAAQ,EACRW,WAAW,EACXd,OAAQF,IAEVO,SAAU,CACRJ,IAAK,GACLC,UAAW,MACXK,WAAY,MACZJ,QAAQ,EACRW,WAAW,EACXd,OAAQF,GACRQ,QAASR,IAEXU,YAAa,CACXP,IAAK,IACLC,UAAW,MACXK,WAAY,MACZJ,QAAQ,EACRW,WAAW,EAEXd,OAAQF,GAERQ,QAAS,KAAKR,qDAEhBW,WAAY,CACVR,IAAK,GACLC,UAAW,MACXC,QAAQ,EACRW,WAAW,EACXd,OAAQF,IAEVY,OAAQ,CACNT,IAAK,EACLC,UAAW,GACXC,QAAQ,EACRW,WAAW,EACXd,OAAQF,GACRiB,MAAO,GAETJ,QAAS,CACPV,IAAK,EACLC,UAAW,MACXC,QAAQ,EACRW,WAAW,EACXd,OAAQF,GACRiB,MAAO,GAETH,MAAO,CACLX,IAAK,EACLC,UAAW,GACXC,QAAQ,EACRW,WAAW,EACXd,OAAQF,GACRiB,MAAO,ICnJLC,GAA2B,CAC/B,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MA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"}
|