scorm-again 3.1.0 → 3.1.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.
@@ -1 +1 @@
1
- {"version":3,"file":"cross-frame-lms.js","sources":["../src/CrossFrameLMS.ts"],"sourcesContent":["// src/CrossFrameLMS.ts\nimport { CrossFrameLMSOptions, MessageData, MessageResponse } from \"./types/CrossFrame\";\nimport { IBaseAPI } from \"./interfaces/IBaseAPI\";\n\n/**\n * Server-side SCORM adapter running in your LMS frame (lms.example.com).\n * Listens for postMessage from child (content) frames, invokes real API,\n * and posts back { messageId, result, error }.\n */\nexport default class CrossFrameLMS {\n private readonly _api: IBaseAPI;\n private readonly _origin: string;\n private readonly _rateLimit: number;\n private _requestTimes: number[] = [];\n private _destroyed = false;\n private readonly _boundOnMessage: (ev: MessageEvent) => void;\n\n /**\n * Strict allowlist of methods that can be invoked via cross-frame messages.\n * Only SCORM API methods and internal helpers are permitted.\n */\n private static readonly ALLOWED_METHODS = new Set([\n // SCORM 1.2 methods\n \"LMSInitialize\",\n \"LMSFinish\",\n \"LMSGetValue\",\n \"LMSSetValue\",\n \"LMSCommit\",\n \"LMSGetLastError\",\n \"LMSGetErrorString\",\n \"LMSGetDiagnostic\",\n // SCORM 2004 methods\n \"Initialize\",\n \"Terminate\",\n \"GetValue\",\n \"SetValue\",\n \"Commit\",\n \"GetLastError\",\n \"GetErrorString\",\n \"GetDiagnostic\",\n // Internal method for cache warming\n \"getFlattenedCMI\",\n ]);\n\n /**\n * Creates a new CrossFrameLMS instance.\n * @param api - The SCORM API instance to delegate calls to\n * @param targetOrigin - Origin to accept messages from. Default \"*\" accepts all origins.\n * @param options - Configuration options\n */\n constructor(api: IBaseAPI, targetOrigin: string = \"*\", options: CrossFrameLMSOptions = {}) {\n this._api = api;\n this._origin = targetOrigin;\n this._rateLimit = options.rateLimit ?? 100;\n\n // Warn about wildcard origin security implications\n if (targetOrigin === \"*\") {\n console.warn(\n \"CrossFrameLMS: Using wildcard origin ('*') allows any origin to send messages. \" +\n \"This is insecure for production use. \" +\n \"Specify an explicit origin (e.g., 'https://content.example.com') to restrict message sources.\",\n );\n }\n\n this._boundOnMessage = this._onMessage.bind(this);\n window.addEventListener(\"message\", this._boundOnMessage);\n }\n\n /**\n * Destroys this instance, removing event listeners and preventing further message processing.\n * Once destroyed, the instance cannot be reused.\n */\n destroy(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n window.removeEventListener(\"message\", this._boundOnMessage);\n this._requestTimes.length = 0;\n }\n\n /**\n * Checks if the rate limit has been exceeded.\n * Uses a sliding window of 1 second.\n * @returns true if rate limit exceeded, false otherwise\n */\n private _isRateLimited(): boolean {\n const now = Date.now();\n // Remove requests older than 1 second\n this._requestTimes = this._requestTimes.filter((t) => now - t < 1000);\n if (this._requestTimes.length >= this._rateLimit) {\n return true;\n }\n this._requestTimes.push(now);\n return false;\n }\n\n /**\n * Type guard to validate MessageData structure\n */\n private static _isValidMessageData(data: unknown): data is MessageData {\n if (typeof data !== \"object\" || data === null) return false;\n\n const msg = data as Partial<MessageData>;\n\n // Required fields\n if (typeof msg.messageId !== \"string\" || msg.messageId.length === 0) return false;\n if (typeof msg.method !== \"string\" || msg.method.length === 0) return false;\n\n // params must be an array if present (required for apply())\n if (msg.params !== undefined && !Array.isArray(msg.params)) return false;\n\n // isHeartbeat must be boolean if present\n if (msg.isHeartbeat !== undefined && typeof msg.isHeartbeat !== \"boolean\") return false;\n\n return true;\n }\n\n /**\n * Handles incoming postMessage events from child frames.\n */\n private _onMessage(ev: MessageEvent): void {\n // Ignore messages if destroyed\n if (this._destroyed) return;\n\n // Validate the message origin unless all origins are allowed\n if (this._origin !== \"*\" && ev.origin !== this._origin) {\n return;\n }\n\n // CF-LMS-02: Validate that ev.data has the expected MessageData structure\n if (!CrossFrameLMS._isValidMessageData(ev.data)) return;\n\n const msg = ev.data;\n if (!ev.source) return;\n\n // Validate that ev.source is a Window with postMessage capability\n // ev.source can be Window, MessagePort, ServiceWorker, or null\n // We only handle Window sources for iframe communication\n if (!(\"postMessage\" in ev.source)) return;\n\n const source = ev.source as Window;\n\n // Handle heartbeat separately (bypass rate limit and allowlist)\n if (msg.isHeartbeat) {\n const resp: MessageResponse = {\n messageId: msg.messageId,\n isHeartbeat: true,\n };\n source.postMessage(resp, this._origin);\n return;\n }\n\n // Check rate limit\n if (this._isRateLimited()) {\n // SCORM ERROR CODE USAGE: Using error code \"101\" for rate limiting\n //\n // While not a standard SCORM error code, \"101\" is used here to signal rate limiting\n // in cross-frame communication. Standard SCORM error codes are:\n // - SCORM 1.2: 0, 101, 201, 202, 203, 301, 401, 402, 403, 404, 405\n // - SCORM 2004: 0, 101, 102, 103, 201, 301, 351, 391, 401-408\n //\n // Using \"101\" (General Exception) is appropriate here because:\n // 1. It indicates a general error condition without exposing security details\n // 2. It's recognized by SCORM content as a non-zero error code\n // 3. It doesn't conflict with specific SCORM error semantics\n // 4. The actual error message \"Rate limit exceeded\" provides debug context\n //\n // The CrossFrameAPI detects this specific message to emit a \"rateLimited\" event,\n // allowing content to react appropriately (e.g., back off, show user message).\n const resp: MessageResponse = {\n messageId: msg.messageId,\n error: { message: \"Rate limit exceeded\", code: \"101\" },\n };\n source.postMessage(resp, this._origin);\n return;\n }\n\n // Check method allowlist\n if (!CrossFrameLMS.ALLOWED_METHODS.has(msg.method)) {\n const resp: MessageResponse = {\n messageId: msg.messageId,\n error: { message: `Method not allowed: ${msg.method}`, code: \"101\" },\n };\n source.postMessage(resp, this._origin);\n return;\n }\n\n this._process(msg, source);\n }\n\n /**\n * Processes a validated message by invoking the requested API method.\n */\n private _process(msg: MessageData, source: Window): void {\n const sendResponse = (result?: unknown, error?: { message: string; code?: string }) => {\n const resp: MessageResponse = { messageId: msg.messageId };\n if (result !== undefined) resp.result = result;\n if (error !== undefined) resp.error = error;\n source.postMessage(resp, this._origin);\n };\n\n try {\n const fn = (this._api as unknown as Record<string, unknown>)[msg.method];\n if (typeof fn !== \"function\") {\n sendResponse(undefined, { message: `Method ${msg.method} not found` });\n return;\n }\n\n // CF-LMS-01: Validate params is an array before apply()\n // This should never fail due to _isValidMessageData check, but defense in depth\n const params = Array.isArray(msg.params) ? msg.params : [];\n\n const result = fn.apply(this._api, params);\n\n if (result && typeof (result as Promise<unknown>).then === \"function\") {\n (result as Promise<unknown>)\n .then((r) => sendResponse(r))\n .catch((e: unknown) => {\n // ERROR CODE PRESERVATION: Extract error code from Error objects\n // SCORM API errors may include a numeric code property for specific error conditions.\n // We preserve this code to maintain proper error semantics across the cross-frame boundary.\n const message = e instanceof Error ? e.message : \"Unknown error\";\n const code =\n e && typeof e === \"object\" && \"code\" in e && typeof e.code === \"string\"\n ? e.code\n : undefined;\n const errorObj: { message: string; code?: string } = { message };\n if (code !== undefined) {\n errorObj.code = code;\n }\n sendResponse(undefined, errorObj);\n });\n } else {\n sendResponse(result);\n }\n } catch (e: unknown) {\n // ERROR CODE PRESERVATION: Extract error code from Error objects\n // SCORM API errors may include a numeric code property for specific error conditions.\n // We preserve this code to maintain proper error semantics across the cross-frame boundary.\n const message = e instanceof Error ? e.message : \"Unknown error\";\n const code =\n e && typeof e === \"object\" && \"code\" in e && typeof e.code === \"string\"\n ? e.code\n : undefined;\n const errorObj: { message: string; code?: string } = { message };\n if (code !== undefined) {\n errorObj.code = code;\n }\n sendResponse(undefined, errorObj);\n }\n }\n}\n"],"names":["_CrossFrameLMS","constructor","api","targetOrigin","arguments","length","undefined","options","__publicField","_api","_origin","_rateLimit","rateLimit","console","warn","_boundOnMessage","_onMessage","bind","window","addEventListener","destroy","_destroyed","removeEventListener","_requestTimes","_isRateLimited","now","Date","filter","t","push","_isValidMessageData","data","msg","messageId","method","params","Array","isArray","isHeartbeat","ev","origin","source","resp","postMessage","error","message","code","ALLOWED_METHODS","has","_process","sendResponse","result","fn","apply","then","r","catch","e","Error","errorObj","Set","CrossFrameLMS"],"mappings":"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"}
1
+ {"version":3,"file":"cross-frame-lms.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,2 +1,2 @@
1
- this.CrossFrameLMS=function(){"use strict";var e=Object.defineProperty,s=(s,t,i)=>((s,t,i)=>t in s?e(s,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):s[t]=i)(s,"symbol"!=typeof t?t+"":t,i);const t=class e{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"*",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};s(this,"_api"),s(this,"_origin"),s(this,"_rateLimit"),s(this,"_requestTimes",[]),s(this,"_destroyed",!1),s(this,"_boundOnMessage"),this._api=e,this._origin=t,this._rateLimit=i.rateLimit??100,"*"===t&&console.warn("CrossFrameLMS: Using wildcard origin ('*') allows any origin to send messages. This is insecure for production use. Specify an explicit origin (e.g., 'https://content.example.com') to restrict message sources."),this._boundOnMessage=this._onMessage.bind(this),window.addEventListener("message",this._boundOnMessage)}destroy(){this._destroyed||(this._destroyed=!0,window.removeEventListener("message",this._boundOnMessage),this._requestTimes.length=0)}_isRateLimited(){const e=Date.now();return this._requestTimes=this._requestTimes.filter(s=>1e3>e-s),this._requestTimes.length>=this._rateLimit||(this._requestTimes.push(e),!1)}static _isValidMessageData(e){if("object"!=typeof e||null===e)return!1;const s=e;return!("string"!=typeof s.messageId||0===s.messageId.length||"string"!=typeof s.method||0===s.method.length||void 0!==s.params&&!Array.isArray(s.params)||void 0!==s.isHeartbeat&&"boolean"!=typeof s.isHeartbeat)}_onMessage(s){if(this._destroyed)return;if("*"!==this._origin&&s.origin!==this._origin)return;if(!e._isValidMessageData(s.data))return;const t=s.data;if(!s.source)return;if(!("postMessage"in s.source))return;const i=s.source;t.isHeartbeat?i.postMessage({messageId:t.messageId,isHeartbeat:!0},this._origin):this._isRateLimited()?i.postMessage({messageId:t.messageId,error:{message:"Rate limit exceeded",code:"101"}},this._origin):e.ALLOWED_METHODS.has(t.method)?this._process(t,i):i.postMessage({messageId:t.messageId,error:{message:"Method not allowed: "+t.method,code:"101"}},this._origin)}_process(e,s){const t=(result,t)=>{const i={messageId:e.messageId};void 0!==result&&(i.result=result),void 0!==t&&(i.error=t),s.postMessage(i,this._origin)};try{const s=this._api[e.method];if("function"!=typeof s)return void t(void 0,{message:`Method ${e.method} not found`});const result=s.apply(this._api,Array.isArray(e.params)?e.params:[]);result&&"function"==typeof result.then?result.then(e=>t(e)).catch(e=>{const s=e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code?e.code:void 0,i={message:e instanceof Error?e.message:"Unknown error"};void 0!==s&&(i.code=s),t(void 0,i)}):t(result)}catch(e){const s=e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code?e.code:void 0,i={message:e instanceof Error?e.message:"Unknown error"};void 0!==s&&(i.code=s),t(void 0,i)}}};return s(t,"ALLOWED_METHODS",new Set(["LMSInitialize","LMSFinish","LMSGetValue","LMSSetValue","LMSCommit","LMSGetLastError","LMSGetErrorString","LMSGetDiagnostic","Initialize","Terminate","GetValue","SetValue","Commit","GetLastError","GetErrorString","GetDiagnostic","getFlattenedCMI"])),t}();
1
+ this.CrossFrameLMS=function(){"use strict";function e(e,t){for(var i=0;t.length>i;i++){var s=t[i];s.enumerable=s.enumerable||!1,s.configurable=!0,"value"in s&&(s.writable=!0),Object.defineProperty(e,s.key,s)}}function t(e,t){return null!=t&&"undefined"!=typeof Symbol&&t[Symbol.hasInstance]?!!t[Symbol.hasInstance](e):e instanceof t}function i(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e}var s=Object.defineProperty,n=function(e,t,n){return function(e,t,i){return t in e?s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i}(e,"symbol"!==(void 0===t?"undefined":i(t))?t+"":t,n)},o=function(){function s(e){var t,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"*",o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,s),n(this,"_api"),n(this,"_origin"),n(this,"_rateLimit"),n(this,"_requestTimes",[]),n(this,"_destroyed",!1),n(this,"_boundOnMessage"),this._api=e,this._origin=i,this._rateLimit=null!==(t=o.rateLimit)&&void 0!==t?t:100,"*"===i&&console.warn("CrossFrameLMS: Using wildcard origin ('*') allows any origin to send messages. This is insecure for production use. Specify an explicit origin (e.g., 'https://content.example.com') to restrict message sources."),this._boundOnMessage=this._onMessage.bind(this),window.addEventListener("message",this._boundOnMessage)}var o,r,a;return o=s,a=[{key:"_isValidMessageData",value:function(e){if("object"!==(void 0===e?"undefined":i(e))||null===e)return!1;var t=e;return!("string"!=typeof t.messageId||0===t.messageId.length||"string"!=typeof t.method||0===t.method.length||void 0!==t.params&&!Array.isArray(t.params)||void 0!==t.isHeartbeat&&"boolean"!=typeof t.isHeartbeat)}}],(r=[{key:"destroy",value:function(){this._destroyed||(this._destroyed=!0,window.removeEventListener("message",this._boundOnMessage),this._requestTimes.length=0)}},{key:"_isRateLimited",value:function(){var e=Date.now();return this._requestTimes=this._requestTimes.filter(function(t){return 1e3>e-t}),this._requestTimes.length>=this._rateLimit||(this._requestTimes.push(e),!1)}},{key:"_onMessage",value:function(e){if(!this._destroyed&&("*"===this._origin||e.origin===this._origin)&&s._isValidMessageData(e.data)){var t=e.data;if(e.source&&"postMessage"in e.source){var i=e.source;t.isHeartbeat?i.postMessage({messageId:t.messageId,isHeartbeat:!0},this._origin):this._isRateLimited()?i.postMessage({messageId:t.messageId,error:{message:"Rate limit exceeded",code:"101"}},this._origin):s.ALLOWED_METHODS.has(t.method)?this._process(t,i):i.postMessage({messageId:t.messageId,error:{message:"Method not allowed: ".concat(t.method),code:"101"}},this._origin)}}}},{key:"_process",value:function(e,s){var n=this,o=function(result,t){var i={messageId:e.messageId};void 0!==result&&(i.result=result),void 0!==t&&(i.error=t),s.postMessage(i,n._origin)};try{var r=this._api[e.method];if("function"!=typeof r)return void o(void 0,{message:"Method ".concat(e.method," not found")});var result=r.apply(this._api,Array.isArray(e.params)?e.params:[]);result&&"function"==typeof result.then?result.then(function(e){return o(e)}).catch(function(e){var s=t(e,Error)?e.message:"Unknown error",n=e&&"object"===(void 0===e?"undefined":i(e))&&"code"in e&&"string"==typeof e.code?e.code:void 0,r={message:s};void 0!==n&&(r.code=n),o(void 0,r)}):o(result)}catch(e){var a=t(e,Error)?e.message:"Unknown error",d=e&&"object"===(void 0===e?"undefined":i(e))&&"code"in e&&"string"==typeof e.code?e.code:void 0,u={message:a};void 0!==d&&(u.code=d),o(void 0,u)}}}])&&e(o.prototype,r),a&&e(o,a),s}();return n(o,"ALLOWED_METHODS",new Set(["LMSInitialize","LMSFinish","LMSGetValue","LMSSetValue","LMSCommit","LMSGetLastError","LMSGetErrorString","LMSGetDiagnostic","Initialize","Terminate","GetValue","SetValue","Commit","GetLastError","GetErrorString","GetDiagnostic","getFlattenedCMI"])),o}();
2
2
  //# sourceMappingURL=cross-frame-lms.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cross-frame-lms.min.js","sources":["../src/CrossFrameLMS.ts"],"sourcesContent":["// src/CrossFrameLMS.ts\nimport { CrossFrameLMSOptions, MessageData, MessageResponse } from \"./types/CrossFrame\";\nimport { IBaseAPI } from \"./interfaces/IBaseAPI\";\n\n/**\n * Server-side SCORM adapter running in your LMS frame (lms.example.com).\n * Listens for postMessage from child (content) frames, invokes real API,\n * and posts back { messageId, result, error }.\n */\nexport default class CrossFrameLMS {\n private readonly _api: IBaseAPI;\n private readonly _origin: string;\n private readonly _rateLimit: number;\n private _requestTimes: number[] = [];\n private _destroyed = false;\n private readonly _boundOnMessage: (ev: MessageEvent) => void;\n\n /**\n * Strict allowlist of methods that can be invoked via cross-frame messages.\n * Only SCORM API methods and internal helpers are permitted.\n */\n private static readonly ALLOWED_METHODS = new Set([\n // SCORM 1.2 methods\n \"LMSInitialize\",\n \"LMSFinish\",\n \"LMSGetValue\",\n \"LMSSetValue\",\n \"LMSCommit\",\n \"LMSGetLastError\",\n \"LMSGetErrorString\",\n \"LMSGetDiagnostic\",\n // SCORM 2004 methods\n \"Initialize\",\n \"Terminate\",\n \"GetValue\",\n \"SetValue\",\n \"Commit\",\n \"GetLastError\",\n \"GetErrorString\",\n \"GetDiagnostic\",\n // Internal method for cache warming\n \"getFlattenedCMI\",\n ]);\n\n /**\n * Creates a new CrossFrameLMS instance.\n * @param api - The SCORM API instance to delegate calls to\n * @param targetOrigin - Origin to accept messages from. Default \"*\" accepts all origins.\n * @param options - Configuration options\n */\n constructor(api: IBaseAPI, targetOrigin: string = \"*\", options: CrossFrameLMSOptions = {}) {\n this._api = api;\n this._origin = targetOrigin;\n this._rateLimit = options.rateLimit ?? 100;\n\n // Warn about wildcard origin security implications\n if (targetOrigin === \"*\") {\n console.warn(\n \"CrossFrameLMS: Using wildcard origin ('*') allows any origin to send messages. \" +\n \"This is insecure for production use. \" +\n \"Specify an explicit origin (e.g., 'https://content.example.com') to restrict message sources.\",\n );\n }\n\n this._boundOnMessage = this._onMessage.bind(this);\n window.addEventListener(\"message\", this._boundOnMessage);\n }\n\n /**\n * Destroys this instance, removing event listeners and preventing further message processing.\n * Once destroyed, the instance cannot be reused.\n */\n destroy(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n window.removeEventListener(\"message\", this._boundOnMessage);\n this._requestTimes.length = 0;\n }\n\n /**\n * Checks if the rate limit has been exceeded.\n * Uses a sliding window of 1 second.\n * @returns true if rate limit exceeded, false otherwise\n */\n private _isRateLimited(): boolean {\n const now = Date.now();\n // Remove requests older than 1 second\n this._requestTimes = this._requestTimes.filter((t) => now - t < 1000);\n if (this._requestTimes.length >= this._rateLimit) {\n return true;\n }\n this._requestTimes.push(now);\n return false;\n }\n\n /**\n * Type guard to validate MessageData structure\n */\n private static _isValidMessageData(data: unknown): data is MessageData {\n if (typeof data !== \"object\" || data === null) return false;\n\n const msg = data as Partial<MessageData>;\n\n // Required fields\n if (typeof msg.messageId !== \"string\" || msg.messageId.length === 0) return false;\n if (typeof msg.method !== \"string\" || msg.method.length === 0) return false;\n\n // params must be an array if present (required for apply())\n if (msg.params !== undefined && !Array.isArray(msg.params)) return false;\n\n // isHeartbeat must be boolean if present\n if (msg.isHeartbeat !== undefined && typeof msg.isHeartbeat !== \"boolean\") return false;\n\n return true;\n }\n\n /**\n * Handles incoming postMessage events from child frames.\n */\n private _onMessage(ev: MessageEvent): void {\n // Ignore messages if destroyed\n if (this._destroyed) return;\n\n // Validate the message origin unless all origins are allowed\n if (this._origin !== \"*\" && ev.origin !== this._origin) {\n return;\n }\n\n // CF-LMS-02: Validate that ev.data has the expected MessageData structure\n if (!CrossFrameLMS._isValidMessageData(ev.data)) return;\n\n const msg = ev.data;\n if (!ev.source) return;\n\n // Validate that ev.source is a Window with postMessage capability\n // ev.source can be Window, MessagePort, ServiceWorker, or null\n // We only handle Window sources for iframe communication\n if (!(\"postMessage\" in ev.source)) return;\n\n const source = ev.source as Window;\n\n // Handle heartbeat separately (bypass rate limit and allowlist)\n if (msg.isHeartbeat) {\n const resp: MessageResponse = {\n messageId: msg.messageId,\n isHeartbeat: true,\n };\n source.postMessage(resp, this._origin);\n return;\n }\n\n // Check rate limit\n if (this._isRateLimited()) {\n // SCORM ERROR CODE USAGE: Using error code \"101\" for rate limiting\n //\n // While not a standard SCORM error code, \"101\" is used here to signal rate limiting\n // in cross-frame communication. Standard SCORM error codes are:\n // - SCORM 1.2: 0, 101, 201, 202, 203, 301, 401, 402, 403, 404, 405\n // - SCORM 2004: 0, 101, 102, 103, 201, 301, 351, 391, 401-408\n //\n // Using \"101\" (General Exception) is appropriate here because:\n // 1. It indicates a general error condition without exposing security details\n // 2. It's recognized by SCORM content as a non-zero error code\n // 3. It doesn't conflict with specific SCORM error semantics\n // 4. The actual error message \"Rate limit exceeded\" provides debug context\n //\n // The CrossFrameAPI detects this specific message to emit a \"rateLimited\" event,\n // allowing content to react appropriately (e.g., back off, show user message).\n const resp: MessageResponse = {\n messageId: msg.messageId,\n error: { message: \"Rate limit exceeded\", code: \"101\" },\n };\n source.postMessage(resp, this._origin);\n return;\n }\n\n // Check method allowlist\n if (!CrossFrameLMS.ALLOWED_METHODS.has(msg.method)) {\n const resp: MessageResponse = {\n messageId: msg.messageId,\n error: { message: `Method not allowed: ${msg.method}`, code: \"101\" },\n };\n source.postMessage(resp, this._origin);\n return;\n }\n\n this._process(msg, source);\n }\n\n /**\n * Processes a validated message by invoking the requested API method.\n */\n private _process(msg: MessageData, source: Window): void {\n const sendResponse = (result?: unknown, error?: { message: string; code?: string }) => {\n const resp: MessageResponse = { messageId: msg.messageId };\n if (result !== undefined) resp.result = result;\n if (error !== undefined) resp.error = error;\n source.postMessage(resp, this._origin);\n };\n\n try {\n const fn = (this._api as unknown as Record<string, unknown>)[msg.method];\n if (typeof fn !== \"function\") {\n sendResponse(undefined, { message: `Method ${msg.method} not found` });\n return;\n }\n\n // CF-LMS-01: Validate params is an array before apply()\n // This should never fail due to _isValidMessageData check, but defense in depth\n const params = Array.isArray(msg.params) ? msg.params : [];\n\n const result = fn.apply(this._api, params);\n\n if (result && typeof (result as Promise<unknown>).then === \"function\") {\n (result as Promise<unknown>)\n .then((r) => sendResponse(r))\n .catch((e: unknown) => {\n // ERROR CODE PRESERVATION: Extract error code from Error objects\n // SCORM API errors may include a numeric code property for specific error conditions.\n // We preserve this code to maintain proper error semantics across the cross-frame boundary.\n const message = e instanceof Error ? e.message : \"Unknown error\";\n const code =\n e && typeof e === \"object\" && \"code\" in e && typeof e.code === \"string\"\n ? e.code\n : undefined;\n const errorObj: { message: string; code?: string } = { message };\n if (code !== undefined) {\n errorObj.code = code;\n }\n sendResponse(undefined, errorObj);\n });\n } else {\n sendResponse(result);\n }\n } catch (e: unknown) {\n // ERROR CODE PRESERVATION: Extract error code from Error objects\n // SCORM API errors may include a numeric code property for specific error conditions.\n // We preserve this code to maintain proper error semantics across the cross-frame boundary.\n const message = e instanceof Error ? e.message : \"Unknown error\";\n const code =\n e && typeof e === \"object\" && \"code\" in e && typeof e.code === \"string\"\n ? e.code\n : undefined;\n const errorObj: { message: string; code?: string } = { message };\n if (code !== undefined) {\n errorObj.code = code;\n }\n sendResponse(undefined, errorObj);\n }\n }\n}\n"],"names":["_CrossFrameLMS","constructor","api","targetOrigin","arguments","length","undefined","options","__publicField","this","_api","_origin","_rateLimit","rateLimit","console","warn","_boundOnMessage","_onMessage","bind","window","addEventListener","destroy","_destroyed","removeEventListener","_requestTimes","_isRateLimited","now","Date","filter","t","push","_isValidMessageData","data","msg","messageId","method","params","Array","isArray","isHeartbeat","ev","origin","source","postMessage","error","message","code","ALLOWED_METHODS","has","_process","sendResponse","result","resp","fn","apply","then","r","catch","e","errorObj","Error","Set"],"mappings":"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"}
1
+ {"version":3,"file":"cross-frame-lms.min.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -10996,12 +10996,6 @@ class TerminationHandler {
10996
10996
  }
10997
10997
  }
10998
10998
  } while (processedExit);
10999
- if (!hasSequencingRequest && !postConditionResult.sequencingRequest) {
11000
- const current = this.activityTree.currentActivity || currentActivity;
11001
- if (current.parent) {
11002
- this.activityTree.setCurrentActivityWithoutActivation(current.parent);
11003
- }
11004
- }
11005
10999
  return {
11006
11000
  terminationRequest: SequencingRequestType.EXIT,
11007
11001
  sequencingRequest: postConditionResult.sequencingRequest,
@@ -18917,18 +18911,12 @@ class Scorm12API extends BaseAPI {
18917
18911
  if (terminateCommit || includeTotalTime) {
18918
18912
  cmiExport.cmi.core.total_time = this.cmi.getCurrentTotalTime();
18919
18913
  }
18920
- const result = [];
18921
18914
  const flattened = flatten(cmiExport);
18922
18915
  switch (this.settings.dataCommitFormat) {
18923
18916
  case "flattened":
18924
18917
  return flattened;
18925
18918
  case "params":
18926
- for (const item in flattened) {
18927
- if ({}.hasOwnProperty.call(flattened, item)) {
18928
- result.push(`${item}=${flattened[item]}`);
18929
- }
18930
- }
18931
- return result;
18919
+ return Object.entries(flattened).map(([item, value]) => `${item}=${value}`);
18932
18920
  case "json":
18933
18921
  default:
18934
18922
  return cmiExport;
@@ -24502,19 +24490,13 @@ class Scorm2004DataSerializer {
24502
24490
  } else {
24503
24491
  delete cmiExport.cmi.total_time;
24504
24492
  }
24505
- const result = [];
24506
24493
  const flattened = flatten(cmiExport);
24507
24494
  const settings = this.context.getSettings();
24508
24495
  switch (settings.dataCommitFormat) {
24509
24496
  case "flattened":
24510
- return flatten(cmiExport);
24497
+ return flattened;
24511
24498
  case "params":
24512
- for (const item in flattened) {
24513
- if ({}.hasOwnProperty.call(flattened, item)) {
24514
- result.push(`${item}=${flattened[item]}`);
24515
- }
24516
- }
24517
- return result;
24499
+ return Object.entries(flattened).map(([item, value]) => `${item}=${value}`);
24518
24500
  case "json":
24519
24501
  default:
24520
24502
  return cmiExport;