scorm-again 3.0.5 → 3.1.1
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/README.md +77 -1
- package/dist/cross-frame-api.js +579 -320
- package/dist/cross-frame-api.js.map +1 -1
- package/dist/cross-frame-api.min.js +1 -1
- package/dist/cross-frame-api.min.js.map +1 -1
- package/dist/cross-frame-lms.js +226 -163
- package/dist/cross-frame-lms.js.map +1 -1
- package/dist/cross-frame-lms.min.js +1 -1
- package/dist/cross-frame-lms.min.js.map +1 -1
- package/dist/esm/cross-frame-api.js.map +1 -1
- package/dist/esm/scorm-again.js +391 -86
- package/dist/esm/scorm-again.js.map +1 -1
- package/dist/esm/scorm-again.min.js +1 -1
- package/dist/esm/scorm-again.min.js.map +1 -1
- package/dist/esm/scorm12.js +323 -76
- package/dist/esm/scorm12.js.map +1 -1
- package/dist/esm/scorm12.min.js +1 -1
- package/dist/esm/scorm12.min.js.map +1 -1
- package/dist/esm/scorm2004.js +373 -72
- package/dist/esm/scorm2004.js.map +1 -1
- package/dist/esm/scorm2004.min.js +1 -1
- package/dist/esm/scorm2004.min.js.map +1 -1
- package/dist/scorm-again.js +32522 -19996
- package/dist/scorm-again.js.map +1 -1
- package/dist/scorm-again.min.js +1 -1
- package/dist/scorm-again.min.js.map +1 -1
- package/dist/scorm12.js +9130 -5031
- package/dist/scorm12.js.map +1 -1
- package/dist/scorm12.min.js +1 -1
- package/dist/scorm12.min.js.map +1 -1
- package/dist/scorm2004.js +30040 -18419
- package/dist/scorm2004.js.map +1 -1
- package/dist/scorm2004.min.js +1 -1
- package/dist/scorm2004.min.js.map +1 -1
- package/dist/types/BaseAPI.d.ts +15 -5
- package/dist/types/Scorm12API.d.ts +2 -2
- package/dist/types/Scorm2004API.d.ts +3 -2
- package/dist/types/cmi/common/score.d.ts +2 -0
- package/dist/types/interfaces/services.d.ts +8 -4
- package/dist/types/services/AsynchronousHttpService.d.ts +3 -2
- package/dist/types/services/EventService.d.ts +2 -2
- package/dist/types/services/OfflineStorageService.d.ts +6 -1
- package/dist/types/services/SynchronousHttpService.d.ts +2 -2
- package/dist/types/services/ValidationService.d.ts +1 -1
- package/dist/types/types/api_types.d.ts +22 -2
- package/dist/types/utilities/index.d.ts +1 -0
- package/dist/types/utilities/url.d.ts +2 -0
- package/package.json +27 -27
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross-frame-api.js","sources":["../src/constants/error_codes.ts","../src/CrossFrameAPI.ts"],"sourcesContent":["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","// src/CrossFrameAPI.ts\nimport {\n CrossFrameAPIOptions,\n CrossFrameEvent,\n CrossFrameEventCallback,\n MessageData,\n MessageResponse,\n} from \"./types/CrossFrame\";\nimport { global_errors } from \"./constants/error_codes\";\n\n/**\n * Pending request tracking with timestamp for cache merge protection\n */\ninterface PendingRequest {\n resolve: (v: unknown) => void;\n reject: (e: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n /**\n * Timestamp when the request was sent, used for cache merge protection.\n * Ensures local modifications made after the request was sent aren't overwritten\n * by stale server responses that arrive later.\n */\n requestTime: number;\n method: string; // CF-API-02: Track method name for better error reporting\n}\n\n/**\n * Client-side SCORM facade running in your content frame.\n * Returns cached values synchronously, then fires off an async\n * postMessage to the LMS frame to refresh cache and error state.\n */\nexport default class CrossFrameAPI {\n private _cache = new Map<string, string>();\n private _cacheTimestamps = new Map<string, number>();\n private _lastError = \"0\";\n private _pending = new Map<string, PendingRequest>();\n private _counter = 0;\n private readonly _origin: string;\n private readonly _targetWindow: Window;\n private readonly _timeout: number;\n private readonly _heartbeatInterval: number;\n private readonly _heartbeatTimeout: number;\n\n private _destroyed = false;\n private _connected = true;\n private _lastHeartbeatResponse = Date.now();\n private _heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n private _eventListeners = new Map<string, Set<CrossFrameEventCallback>>();\n private readonly _boundOnMessage: (ev: MessageEvent) => void;\n\n /**\n * Type guard to validate MessageResponse structure\n */\n private static _isValidMessageResponse(data: unknown): data is MessageResponse {\n if (typeof data !== \"object\" || data === null) return false;\n\n const resp = data as Partial<MessageResponse>;\n\n // messageId is required\n if (typeof resp.messageId !== \"string\" || resp.messageId.length === 0) return false;\n\n // error must have correct shape if present\n if (resp.error !== undefined) {\n if (typeof resp.error !== \"object\" || resp.error === null) return false;\n const err = resp.error as Record<string, unknown>;\n if (typeof err.message !== \"string\") return false;\n if (err.code !== undefined && typeof err.code !== \"string\") return false;\n }\n\n // isHeartbeat must be boolean if present\n if (resp.isHeartbeat !== undefined && typeof resp.isHeartbeat !== \"boolean\") return false;\n\n return true;\n }\n\n /**\n * Validates that args is an array and sanitizes it for safe use\n */\n private static _validateArgs(args: unknown[]): args is unknown[] {\n if (!Array.isArray(args)) return false;\n // Additional validation could check for specific types based on method\n return true;\n }\n\n private _handler: ProxyHandler<CrossFrameAPI> = {\n get: (target, prop, receiver) => {\n // If it's an existing property/method, return it\n if (typeof prop !== \"string\" || prop in target) {\n const v = Reflect.get(target, prop, receiver);\n return typeof v === \"function\" ? v.bind(target) : v;\n }\n\n // Otherwise treat prop as a SCORM call\n const methodName = prop;\n const isGet = methodName.endsWith(\"GetValue\");\n const isSet = methodName.startsWith(\"LMSSet\") || methodName.endsWith(\"SetValue\");\n const isInit = methodName === \"Initialize\" || methodName === \"LMSInitialize\";\n const isFinish = methodName === \"Terminate\" || methodName === \"LMSFinish\";\n const isCommit = methodName === \"Commit\" || methodName === \"LMSCommit\";\n const isErrorString = methodName === \"GetErrorString\" || methodName === \"LMSGetErrorString\";\n const isDiagnostic = methodName === \"GetDiagnostic\" || methodName === \"LMSGetDiagnostic\";\n\n return (...args: unknown[]): string => {\n // CF-API-03: Validate args is an array (TypeScript guarantees this, but runtime safety)\n if (!CrossFrameAPI._validateArgs(args)) {\n console.error(`CrossFrameAPI: Invalid arguments for ${methodName}`);\n return \"\";\n }\n\n // Synchronous cache update for setter calls\n if (isSet && args.length >= 2) {\n const key = args[0] as string;\n target._cache.set(key, String(args[1]));\n target._cacheTimestamps.set(key, Date.now());\n target._lastError = \"0\";\n }\n\n // Capture request time for cache merge protection\n const requestTime = Date.now();\n\n // Fire off async postMessage to refresh cache and error\n target\n ._post(methodName, args)\n .then((res) => {\n if (isGet && args.length >= 1) {\n const key = args[0] as string;\n // Only update if not locally modified after request was sent\n const localModTime = target._cacheTimestamps.get(key) ?? 0;\n if (localModTime < requestTime) {\n target._cache.set(key, String(res));\n target._cacheTimestamps.delete(key);\n }\n target._lastError = \"0\";\n }\n if (isErrorString && args.length >= 1) {\n const code = String(args[0]);\n target._cache.set(`error_${code}`, String(res));\n }\n if (isDiagnostic && args.length >= 1) {\n const code = String(args[0]);\n target._cache.set(`diag_${code}`, String(res));\n }\n if (methodName === \"GetLastError\" || methodName === \"LMSGetLastError\") {\n target._lastError = String(res);\n }\n })\n .catch((err) => target._capture(methodName, err));\n\n // Return synchronously\n if (isGet && args.length >= 1) {\n return target._cache.get(args[0] as string) ?? \"\";\n }\n if (isErrorString && args.length >= 1) {\n const code = String(args[0]);\n return target._cache.get(`error_${code}`) ?? \"\";\n }\n if (isDiagnostic && args.length >= 1) {\n const code = String(args[0]);\n return target._cache.get(`diag_${code}`) ?? \"\";\n }\n if (isInit || isFinish || isCommit || isSet) {\n // Immediately return \"true\"\n const result = \"true\";\n // Then warm cache with timestamp protection:\n target\n ._post(\"getFlattenedCMI\", [])\n .then((all: unknown) => {\n if (all && typeof all === \"object\") {\n const entries = Object.entries(all as Record<string, string>);\n entries.forEach(([key, val]) => {\n // Only update if not locally modified after request was sent\n const localModTime = target._cacheTimestamps.get(key) ?? 0;\n if (localModTime < requestTime) {\n target._cache.set(key, val);\n target._cacheTimestamps.delete(key);\n }\n });\n }\n // reset error\n target._lastError = \"0\";\n })\n .catch((err) => target._capture(\"getFlattenedCMI\", err));\n return result;\n }\n if (methodName === \"GetLastError\" || methodName === \"LMSGetLastError\") {\n return target._lastError;\n }\n return \"\";\n };\n },\n };\n\n /**\n * Creates a new CrossFrameAPI instance.\n * @param targetOrigin - Origin to send messages to. Default \"*\" sends to any origin.\n * @param targetWindow - Window to send messages to. Default is window.parent.\n * @param options - Configuration options\n */\n constructor(\n targetOrigin: string = \"*\",\n targetWindow: Window = window.parent,\n options: CrossFrameAPIOptions = {},\n ) {\n this._origin = targetOrigin;\n this._targetWindow = targetWindow;\n this._timeout = options.timeout ?? 5000;\n this._heartbeatInterval = options.heartbeatInterval ?? 30000;\n this._heartbeatTimeout = options.heartbeatTimeout ?? 60000;\n\n // Warn about wildcard origin security implications\n if (targetOrigin === \"*\") {\n console.warn(\n \"CrossFrameAPI: Using wildcard origin ('*') allows any origin to receive messages. \" +\n \"This is insecure for production use. \" +\n \"Specify an explicit origin (e.g., 'https://lms.example.com') to restrict message recipients.\",\n );\n }\n\n this._boundOnMessage = this._onMessage.bind(this);\n window.addEventListener(\"message\", this._boundOnMessage);\n this._startHeartbeat();\n\n return new Proxy(this, this._handler);\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 if (this._heartbeatTimer) {\n clearInterval(this._heartbeatTimer);\n this._heartbeatTimer = undefined;\n }\n // Reject all pending requests\n for (const pending of Array.from(this._pending.values())) {\n clearTimeout(pending.timer);\n pending.reject(new Error(\"CrossFrameAPI destroyed\"));\n }\n this._pending.clear();\n this._cache.clear();\n this._cacheTimestamps.clear();\n this._eventListeners.clear();\n }\n\n /**\n * Subscribes to a CrossFrame event.\n * @param event - Event type to listen for\n * @param callback - Function to call when event occurs\n */\n on(event: string, callback: CrossFrameEventCallback): void {\n if (!this._eventListeners.has(event)) {\n this._eventListeners.set(event, new Set());\n }\n this._eventListeners.get(event)?.add(callback);\n }\n\n /**\n * Unsubscribes from a CrossFrame event.\n * @param event - Event type to stop listening for\n * @param callback - Function to remove\n */\n off(event: string, callback: CrossFrameEventCallback): void {\n this._eventListeners.get(event)?.delete(callback);\n }\n\n /**\n * Returns whether the connection to the LMS frame is currently active.\n */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Emits an event to all registered listeners.\n */\n private _emit(event: CrossFrameEvent): void {\n this._eventListeners.get(event.type)?.forEach((cb) => cb(event));\n }\n\n /**\n * Starts the heartbeat mechanism for connection detection.\n */\n private _startHeartbeat(): void {\n // CF-API-01: Clear any existing heartbeat timer before starting a new one\n if (this._heartbeatTimer) {\n clearInterval(this._heartbeatTimer);\n }\n\n this._heartbeatTimer = setInterval(() => {\n if (this._destroyed) return;\n\n // Check if we've missed heartbeats\n const timeSinceLastResponse = Date.now() - this._lastHeartbeatResponse;\n if (timeSinceLastResponse > this._heartbeatTimeout && this._connected) {\n this._connected = false;\n this._emit({ type: \"connectionLost\" });\n }\n\n // Send heartbeat ping\n this._sendHeartbeat();\n }, this._heartbeatInterval);\n }\n\n /**\n * Sends a heartbeat ping to the LMS frame.\n */\n private _sendHeartbeat(): void {\n const messageId = `hb-${Date.now()}-${this._counter++}`;\n const msg: MessageData = {\n messageId,\n method: \"__heartbeat__\",\n params: [],\n isHeartbeat: true,\n };\n this._targetWindow.postMessage(msg, this._origin);\n }\n\n /**\n * Send a message to the LMS frame and return a promise for its response.\n */\n private _post(method: string, params: unknown[]): Promise<unknown> {\n if (this._destroyed) {\n return Promise.reject(new Error(\"CrossFrameAPI destroyed\"));\n }\n\n const messageId = `cfapi-${Date.now()}-${this._counter++}`;\n const requestTime = Date.now();\n\n // Deep-clean params of non-cloneables (e.g. functions)\n const safeParams = params.map((p) => {\n if (typeof p === \"function\") {\n // DEFENSIVE CODING: Warn about non-serializable function parameters.\n // This console.warn is intentionally kept in production builds because:\n // 1. Functions in SCORM API params indicate a programming error by content authors\n // 2. The warning helps diagnose integration issues during content development\n // 3. It alerts developers to potential data loss (function becomes undefined)\n // 4. The frequency should be very low in production (only on misconfigured content)\n //\n // Alternative: Could be gated by a debug flag if production console noise is a concern\n console.warn(\"Dropping function param when posting SCORM call:\", method);\n return undefined;\n }\n return p;\n });\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this._pending.has(messageId)) {\n this._pending.delete(messageId);\n reject(new Error(`Timeout calling ${method}`));\n }\n }, this._timeout);\n\n // CF-API-02: Store method name in pending request for better error reporting\n this._pending.set(messageId, { resolve, reject, timer, requestTime, method });\n const msg: MessageData = { messageId, method, params: safeParams };\n this._targetWindow.postMessage(msg, this._origin);\n });\n }\n\n /**\n * Handle incoming postMessage responses from the LMS frame.\n */\n private _onMessage(ev: MessageEvent): void {\n if (this._destroyed) return;\n\n // Validate the message origin and source unless all origins are allowed\n if (this._origin !== \"*\" && ev.origin !== this._origin) {\n return;\n }\n if (ev.source && ev.source !== this._targetWindow) {\n return;\n }\n\n // CF-API-04: Validate that ev.data has the expected MessageResponse structure\n if (!CrossFrameAPI._isValidMessageResponse(ev.data)) return;\n\n const data = ev.data;\n\n // Handle heartbeat response\n if (data.isHeartbeat) {\n this._lastHeartbeatResponse = Date.now();\n if (!this._connected) {\n this._connected = true;\n this._emit({ type: \"connectionRestored\" });\n }\n return;\n }\n\n // Handle regular response\n const pending = this._pending.get(data.messageId);\n if (!pending) return;\n\n clearTimeout(pending.timer);\n this._pending.delete(data.messageId);\n\n if (data.error) {\n // Check if rate limited and emit event\n // CF-API-02: Use the actual method name from the pending request\n if (data.error.message === \"Rate limit exceeded\") {\n this._emit({ type: \"rateLimited\", method: pending.method });\n }\n pending.reject(data.error);\n } else {\n pending.resolve(data.result);\n }\n }\n\n /**\n * Capture and cache SCORM errors.\n */\n private _capture(method: string, err: unknown): void {\n let errorMessage = \"Unknown error\";\n\n if (err instanceof Error) {\n errorMessage = err.message;\n } else if (typeof err === \"object\" && err !== null && \"message\" in err) {\n errorMessage = String((err as { message: unknown }).message);\n }\n\n console.error(`CrossFrameAPI ${method} error:`, err);\n // Match SCORM error codes (3 digits) from error messages\n const match = /(?:error code|code)?\\s*(\\d{3})\\b/i.exec(errorMessage);\n const code = match?.[1] ?? String(global_errors.GENERAL);\n this._lastError = code;\n this._cache.set(`error_${code}`, errorMessage);\n }\n}\n"],"names":["global_errors","GENERAL","CrossFrameAPI","constructor","targetOrigin","targetWindow","arguments","length","undefined","window","parent","options","__publicField","Map","Date","now","get","target","prop","receiver","v","Reflect","bind","methodName","isGet","endsWith","isSet","startsWith","isInit","isFinish","isCommit","isErrorString","isDiagnostic","_len","args","Array","_key","_validateArgs","console","error","key","_cache","set","String","_cacheTimestamps","_lastError","requestTime","_post","then","res","localModTime","delete","code","catch","err","_capture","result","all","entries","Object","forEach","_ref","val","_origin","_targetWindow","_timeout","timeout","_heartbeatInterval","heartbeatInterval","_heartbeatTimeout","heartbeatTimeout","warn","_boundOnMessage","_onMessage","addEventListener","_startHeartbeat","Proxy","_handler","_isValidMessageResponse","data","resp","messageId","message","isHeartbeat","isArray","destroy","_destroyed","removeEventListener","_heartbeatTimer","clearInterval","pending","from","_pending","values","clearTimeout","timer","reject","Error","clear","_eventListeners","on","event","callback","has","Set","add","off","connected","_connected","_emit","type","cb","setInterval","timeSinceLastResponse","_lastHeartbeatResponse","_sendHeartbeat","_counter","msg","method","params","postMessage","Promise","safeParams","map","p","resolve","setTimeout","ev","origin","source","errorMessage","match","exec"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cross-frame-api.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
this.CrossFrameAPI=function(){"use strict";var e=Object.defineProperty,t=(t,r,s)=>((t,r,s)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[r]=s)(t,"symbol"!=typeof r?r+"":r,s);class r{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"*",s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:window.parent,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t(this,"_cache",new Map),t(this,"_cacheTimestamps",new Map),t(this,"_lastError","0"),t(this,"_pending",new Map),t(this,"_counter",0),t(this,"_origin"),t(this,"_targetWindow"),t(this,"_timeout"),t(this,"_heartbeatInterval"),t(this,"_heartbeatTimeout"),t(this,"_destroyed",!1),t(this,"_connected",!0),t(this,"_lastHeartbeatResponse",Date.now()),t(this,"_heartbeatTimer"),t(this,"_eventListeners",new Map),t(this,"_boundOnMessage"),t(this,"_handler",{get:(e,t,s)=>{if("string"!=typeof t||t in e){const r=Reflect.get(e,t,s);return"function"==typeof r?r.bind(e):r}const i=t,n=i.endsWith("GetValue"),a=i.startsWith("LMSSet")||i.endsWith("SetValue"),o="Initialize"===i||"LMSInitialize"===i,h="Terminate"===i||"LMSFinish"===i,c="Commit"===i||"LMSCommit"===i,_="GetErrorString"===i||"LMSGetErrorString"===i,d="GetDiagnostic"===i||"LMSGetDiagnostic"===i;return function(){for(var t=arguments.length,s=Array(t),g=0;t>g;g++)s[g]=arguments[g];if(!r._validateArgs(s))return console.error("CrossFrameAPI: Invalid arguments for "+i),"";if(a&&s.length>=2){const t=s[0];e._cache.set(t,s[1]+""),e._cacheTimestamps.set(t,Date.now()),e._lastError="0"}const l=Date.now();if(e._post(i,s).then(t=>{if(n&&s.length>=1){const r=s[0],i=e._cacheTimestamps.get(r)??0;l>i&&(e._cache.set(r,t+""),e._cacheTimestamps.delete(r)),e._lastError="0"}_&&s.length>=1&&e._cache.set("error_"+s[0],t+""),d&&s.length>=1&&e._cache.set("diag_"+s[0],t+""),"GetLastError"!==i&&"LMSGetLastError"!==i||(e._lastError=t+"")}).catch(t=>e._capture(i,t)),n&&s.length>=1)return e._cache.get(s[0])??"";if(_&&s.length>=1)return e._cache.get("error_"+s[0])??"";if(d&&s.length>=1)return e._cache.get("diag_"+s[0])??"";if(o||h||c||a){const result="true";return e._post("getFlattenedCMI",[]).then(t=>{t&&"object"==typeof t&&Object.entries(t).forEach(t=>{let[r,s]=t;const i=e._cacheTimestamps.get(r)??0;l>i&&(e._cache.set(r,s),e._cacheTimestamps.delete(r))}),e._lastError="0"}).catch(t=>e._capture("getFlattenedCMI",t)),result}return"GetLastError"===i||"LMSGetLastError"===i?e._lastError:""}}}),this._origin=e,this._targetWindow=s,this._timeout=i.timeout??5e3,this._heartbeatInterval=i.heartbeatInterval??3e4,this._heartbeatTimeout=i.heartbeatTimeout??6e4,"*"===e&&console.warn("CrossFrameAPI: Using wildcard origin ('*') allows any origin to receive messages. This is insecure for production use. Specify an explicit origin (e.g., 'https://lms.example.com') to restrict message recipients."),this._boundOnMessage=this._onMessage.bind(this),window.addEventListener("message",this._boundOnMessage),this._startHeartbeat(),new Proxy(this,this._handler)}static _isValidMessageResponse(e){if("object"!=typeof e||null===e)return!1;if("string"!=typeof e.messageId||0===e.messageId.length)return!1;if(void 0!==e.error){if("object"!=typeof e.error||null===e.error)return!1;const e=e.error;if("string"!=typeof e.message)return!1;if(void 0!==e.code&&"string"!=typeof e.code)return!1}return void 0===e.isHeartbeat||"boolean"==typeof e.isHeartbeat}static _validateArgs(e){return!!Array.isArray(e)}destroy(){if(!this._destroyed){this._destroyed=!0,window.removeEventListener("message",this._boundOnMessage),this._heartbeatTimer&&(clearInterval(this._heartbeatTimer),this._heartbeatTimer=void 0);for(const e of Array.from(this._pending.values()))clearTimeout(e.timer),e.reject(Error("CrossFrameAPI destroyed"));this._pending.clear(),this._cache.clear(),this._cacheTimestamps.clear(),this._eventListeners.clear()}}on(e,t){this._eventListeners.has(e)||this._eventListeners.set(e,new Set),this._eventListeners.get(e)?.add(t)}off(e,t){this._eventListeners.get(e)?.delete(t)}get connected(){return this._connected}_emit(e){this._eventListeners.get(e.type)?.forEach(t=>t(e))}_startHeartbeat(){this._heartbeatTimer&&clearInterval(this._heartbeatTimer),this._heartbeatTimer=setInterval(()=>{this._destroyed||(Date.now()-this._lastHeartbeatResponse>this._heartbeatTimeout&&this._connected&&(this._connected=!1,this._emit({type:"connectionLost"})),this._sendHeartbeat())},this._heartbeatInterval)}_sendHeartbeat(){const e=`hb-${Date.now()}-${this._counter++}`;this._targetWindow.postMessage({messageId:e,method:"__heartbeat__",params:[],isHeartbeat:!0},this._origin)}_post(e,t){if(this._destroyed)return Promise.reject(Error("CrossFrameAPI destroyed"));const r=`cfapi-${Date.now()}-${this._counter++}`,s=Date.now(),i=t.map(t=>{if("function"!=typeof t)return t;console.warn("Dropping function param when posting SCORM call:",e)});return new Promise((t,n)=>{const a=setTimeout(()=>{this._pending.has(r)&&(this._pending.delete(r),n(Error("Timeout calling "+e)))},this._timeout);this._pending.set(r,{resolve:t,reject:n,timer:a,requestTime:s,method:e}),this._targetWindow.postMessage({messageId:r,method:e,params:i},this._origin)})}_onMessage(e){if(this._destroyed)return;if("*"!==this._origin&&e.origin!==this._origin)return;if(e.source&&e.source!==this._targetWindow)return;if(!r._isValidMessageResponse(e.data))return;const t=e.data;if(t.isHeartbeat)return this._lastHeartbeatResponse=Date.now(),void(this._connected||(this._connected=!0,this._emit({type:"connectionRestored"})));const s=this._pending.get(t.messageId);s&&(clearTimeout(s.timer),this._pending.delete(t.messageId),t.error?("Rate limit exceeded"===t.error.message&&this._emit({type:"rateLimited",method:s.method}),s.reject(t.error)):s.resolve(t.result))}_capture(e,t){let r="Unknown error";t instanceof Error?r=t.message:"object"==typeof t&&null!==t&&"message"in t&&(r=t.message+""),console.error(`CrossFrameAPI ${e} error:`,t);const s=/(?:error code|code)?\s*(\d{3})\b/i.exec(r),i=s?.[1]??"101";this._lastError=i,this._cache.set("error_"+i,r)}}return r}();
|
|
1
|
+
this.CrossFrameAPI=function(){"use strict";function e(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function t(t){for(var r=1;arguments.length>r;r++){var n=null!=arguments[r]?arguments[r]:{},i=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(i=i.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),i.forEach(function(r){e(t,r,n[r])})}return t}function r(e,t){return t=null!=t?t:{},Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):function(e){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t.push.apply(t,r)}return t}(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}),e}var n={GENERAL:101,INITIALIZATION_FAILED:101,INITIALIZED:101,TERMINATED:101,TERMINATION_FAILURE:101,TERMINATION_BEFORE_INIT:101,MULTIPLE_TERMINATION:101,RETRIEVE_BEFORE_INIT:101,RETRIEVE_AFTER_TERM:101,STORE_BEFORE_INIT:101,STORE_AFTER_TERM:101,COMMIT_BEFORE_INIT:101,COMMIT_AFTER_TERM:101,ARGUMENT_ERROR:101,CHILDREN_ERROR:101,COUNT_ERROR:101,GENERAL_GET_FAILURE:101,GENERAL_SET_FAILURE:101,GENERAL_COMMIT_FAILURE:101,UNDEFINED_DATA_MODEL:101,UNIMPLEMENTED_ELEMENT:101,VALUE_NOT_INITIALIZED:101,INVALID_SET_VALUE:101,READ_ONLY_ELEMENT:101,WRITE_ONLY_ELEMENT:101,TYPE_MISMATCH:101,VALUE_OUT_OF_RANGE:101,DEPENDENCY_NOT_ESTABLISHED:101};function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);t>r;r++)n[r]=e[r];return n}function o(e,t){for(var r=0;t.length>r;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function a(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e}r(t({},n),{RETRIEVE_BEFORE_INIT:301,STORE_BEFORE_INIT:301,COMMIT_BEFORE_INIT:301,ARGUMENT_ERROR:201,CHILDREN_ERROR:202,COUNT_ERROR:203,UNDEFINED_DATA_MODEL:401,UNIMPLEMENTED_ELEMENT:401,VALUE_NOT_INITIALIZED:301,INVALID_SET_VALUE:402,READ_ONLY_ELEMENT:403,WRITE_ONLY_ELEMENT:404,TYPE_MISMATCH:405,VALUE_OUT_OF_RANGE:405,DEPENDENCY_NOT_ESTABLISHED:408}),r(t({},n),{INITIALIZATION_FAILED:102,INITIALIZED:103,TERMINATED:104,TERMINATION_FAILURE:111,TERMINATION_BEFORE_INIT:112,MULTIPLE_TERMINATION:113,MULTIPLE_TERMINATIONS:113,RETRIEVE_BEFORE_INIT:122,RETRIEVE_AFTER_TERM:123,STORE_BEFORE_INIT:132,STORE_AFTER_TERM:133,COMMIT_BEFORE_INIT:142,COMMIT_AFTER_TERM:143,ARGUMENT_ERROR:201,GENERAL_GET_FAILURE:301,GENERAL_SET_FAILURE:351,GENERAL_COMMIT_FAILURE:391,UNDEFINED_DATA_MODEL:401,UNIMPLEMENTED_ELEMENT:402,VALUE_NOT_INITIALIZED:403,READ_ONLY_ELEMENT:404,WRITE_ONLY_ELEMENT:405,TYPE_MISMATCH:406,VALUE_OUT_OF_RANGE:407,DEPENDENCY_NOT_ESTABLISHED:408});var s=Object.defineProperty,c=function(e,t,r){return function(e,t,r){return t in e?s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r}(e,"symbol"!==(void 0===t?"undefined":a(t))?t+"":t,r)},_=function(){function e(){var t,r,n,o=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"*",s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:window.parent,_=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),c(this,"_cache",new Map),c(this,"_cacheTimestamps",new Map),c(this,"_lastError","0"),c(this,"_pending",new Map),c(this,"_counter",0),c(this,"_origin"),c(this,"_targetWindow"),c(this,"_timeout"),c(this,"_heartbeatInterval"),c(this,"_heartbeatTimeout"),c(this,"_destroyed",!1),c(this,"_connected",!0),c(this,"_lastHeartbeatResponse",Date.now()),c(this,"_heartbeatTimer"),c(this,"_eventListeners",new Map),c(this,"_boundOnMessage"),c(this,"_handler",{get:function(t,r,n){if("string"!=typeof r||r in t){var o=Reflect.get(t,r,n);return"function"==typeof o?o.bind(t):o}var s=r,c=s.endsWith("GetValue"),_=s.startsWith("LMSSet")||s.endsWith("SetValue"),E="Initialize"===s||"LMSInitialize"===s,l="Terminate"===s||"LMSFinish"===s,u="Commit"===s||"LMSCommit"===s,h="GetErrorString"===s||"LMSGetErrorString"===s,d="GetDiagnostic"===s||"LMSGetDiagnostic"===s;return function(){for(var r=arguments.length,n=Array(r),o=0;r>o;o++)n[o]=arguments[o];if(!e._validateArgs(n))return console.error("CrossFrameAPI: Invalid arguments for ".concat(s)),"";if(_&&n.length>=2){var T=n[0];t._cache.set(T,n[1]+""),t._cacheTimestamps.set(T,Date.now()),t._lastError="0"}var I,f,m,g=Date.now();return t._post(s,n).then(function(e){if(c&&n.length>=1){var r,i=n[0],o=null!==(r=t._cacheTimestamps.get(i))&&void 0!==r?r:0;g>o&&(t._cache.set(i,e+""),t._cacheTimestamps.delete(i)),t._lastError="0"}h&&n.length>=1&&t._cache.set("error_".concat(n[0]+""),e+""),d&&n.length>=1&&t._cache.set("diag_".concat(n[0]+""),e+""),"GetLastError"!==s&&"LMSGetLastError"!==s||(t._lastError=e+"")}).catch(function(e){return t._capture(s,e)}),c&&n.length>=1?null!==(I=t._cache.get(n[0]))&&void 0!==I?I:"":h&&n.length>=1?null!==(f=t._cache.get("error_".concat(n[0]+"")))&&void 0!==f?f:"":d&&n.length>=1?null!==(m=t._cache.get("diag_".concat(n[0]+"")))&&void 0!==m?m:"":E||l||u||_?(t._post("getFlattenedCMI",[]).then(function(e){e&&"object"===(void 0===e?"undefined":a(e))&&Object.entries(e).forEach(function(e){var r,n,o,a=(o=2,function(e){if(Array.isArray(e))return e}(n=e)||function(e,t){var r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,i,o=[],a=!0,s=!1;try{for(r=r.call(e);!(a=(n=r.next()).done)&&(o.push(n.value),!t||o.length!==t);a=!0);}catch(e){s=!0,i=e}finally{try{a||null==r.return||r.return()}finally{if(s)throw i}}return o}}(n,o)||function(e,t){if(e){if("string"==typeof e)return i(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(r):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?i(e,t):void 0}}(n,o)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),s=a[0],c=a[1],_=null!==(r=t._cacheTimestamps.get(s))&&void 0!==r?r:0;g>_&&(t._cache.set(s,c),t._cacheTimestamps.delete(s))}),t._lastError="0"}).catch(function(e){return t._capture("getFlattenedCMI",e)}),"true"):"GetLastError"===s||"LMSGetLastError"===s?t._lastError:""}}}),this._origin=o,this._targetWindow=s,this._timeout=null!==(t=_.timeout)&&void 0!==t?t:5e3,this._heartbeatInterval=null!==(r=_.heartbeatInterval)&&void 0!==r?r:3e4,this._heartbeatTimeout=null!==(n=_.heartbeatTimeout)&&void 0!==n?n:6e4,"*"===o&&console.warn("CrossFrameAPI: Using wildcard origin ('*') allows any origin to receive messages. This is insecure for production use. Specify an explicit origin (e.g., 'https://lms.example.com') to restrict message recipients."),this._boundOnMessage=this._onMessage.bind(this),window.addEventListener("message",this._boundOnMessage),this._startHeartbeat(),new Proxy(this,this._handler)}var t,r,s;return t=e,s=[{key:"_isValidMessageResponse",value:function(e){if("object"!==(void 0===e?"undefined":a(e))||null===e)return!1;var t=e;if("string"!=typeof t.messageId||0===t.messageId.length)return!1;if(void 0!==t.error){if("object"!==a(t.error)||null===t.error)return!1;var r=t.error;if("string"!=typeof r.message)return!1;if(void 0!==r.code&&"string"!=typeof r.code)return!1}return void 0===t.isHeartbeat||"boolean"==typeof t.isHeartbeat}},{key:"_validateArgs",value:function(e){return!!Array.isArray(e)}}],(r=[{key:"destroy",value:function(){if(!this._destroyed){this._destroyed=!0,window.removeEventListener("message",this._boundOnMessage),this._heartbeatTimer&&(clearInterval(this._heartbeatTimer),this._heartbeatTimer=void 0);var e=!0,t=!1,r=void 0;try{for(var n,i=Array.from(this._pending.values())[Symbol.iterator]();!(e=(n=i.next()).done);e=!0){var o=n.value;clearTimeout(o.timer),o.reject(Error("CrossFrameAPI destroyed"))}}catch(e){t=!0,r=e}finally{try{e||null==i.return||i.return()}finally{if(t)throw r}}this._pending.clear(),this._cache.clear(),this._cacheTimestamps.clear(),this._eventListeners.clear()}}},{key:"on",value:function(e,t){var r;this._eventListeners.has(e)||this._eventListeners.set(e,new Set),null===(r=this._eventListeners.get(e))||void 0===r||r.add(t)}},{key:"off",value:function(e,t){var r;null===(r=this._eventListeners.get(e))||void 0===r||r.delete(t)}},{key:"connected",get:function(){return this._connected}},{key:"_emit",value:function(e){var t;null===(t=this._eventListeners.get(e.type))||void 0===t||t.forEach(function(t){return t(e)})}},{key:"_startHeartbeat",value:function(){var e=this;this._heartbeatTimer&&clearInterval(this._heartbeatTimer),this._heartbeatTimer=setInterval(function(){e._destroyed||(Date.now()-e._lastHeartbeatResponse>e._heartbeatTimeout&&e._connected&&(e._connected=!1,e._emit({type:"connectionLost"})),e._sendHeartbeat())},this._heartbeatInterval)}},{key:"_sendHeartbeat",value:function(){var e="hb-".concat(Date.now(),"-").concat(this._counter++);this._targetWindow.postMessage({messageId:e,method:"__heartbeat__",params:[],isHeartbeat:!0},this._origin)}},{key:"_post",value:function(e,t){var r=this;if(this._destroyed)return Promise.reject(Error("CrossFrameAPI destroyed"));var n="cfapi-".concat(Date.now(),"-").concat(this._counter++),i=Date.now(),o=t.map(function(t){if("function"!=typeof t)return t;console.warn("Dropping function param when posting SCORM call:",e)});return new Promise(function(t,a){var s=setTimeout(function(){r._pending.has(n)&&(r._pending.delete(n),a(Error("Timeout calling ".concat(e))))},r._timeout);r._pending.set(n,{resolve:t,reject:a,timer:s,requestTime:i,method:e}),r._targetWindow.postMessage({messageId:n,method:e,params:o},r._origin)})}},{key:"_onMessage",value:function(t){if(!this._destroyed&&("*"===this._origin||t.origin===this._origin)&&(!t.source||t.source===this._targetWindow)&&e._isValidMessageResponse(t.data)){var r=t.data;if(r.isHeartbeat)return this._lastHeartbeatResponse=Date.now(),void(this._connected||(this._connected=!0,this._emit({type:"connectionRestored"})));var n=this._pending.get(r.messageId);n&&(clearTimeout(n.timer),this._pending.delete(r.messageId),r.error?("Rate limit exceeded"===r.error.message&&this._emit({type:"rateLimited",method:n.method}),n.reject(r.error)):n.resolve(r.result))}}},{key:"_capture",value:function(e,t){var r,i,o,s="Unknown error";i=t,(null!=(o=Error)&&"undefined"!=typeof Symbol&&o[Symbol.hasInstance]?o[Symbol.hasInstance](i):i instanceof o)?s=t.message:"object"===(void 0===t?"undefined":a(t))&&null!==t&&"message"in t&&(s=t.message+""),console.error("CrossFrameAPI ".concat(e," error:"),t);var c=/(?:error code|code)?\s*(\d{3})\b/i.exec(s),_=null!==(r=null==c?void 0:c[1])&&void 0!==r?r:n.GENERAL+"";this._lastError=_,this._cache.set("error_".concat(_),s)}}])&&o(t.prototype,r),s&&o(t,s),e}();return _}();
|
|
2
2
|
//# sourceMappingURL=cross-frame-api.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross-frame-api.min.js","sources":["../src/CrossFrameAPI.ts"],"sourcesContent":["// src/CrossFrameAPI.ts\nimport {\n CrossFrameAPIOptions,\n CrossFrameEvent,\n CrossFrameEventCallback,\n MessageData,\n MessageResponse,\n} from \"./types/CrossFrame\";\nimport { global_errors } from \"./constants/error_codes\";\n\n/**\n * Pending request tracking with timestamp for cache merge protection\n */\ninterface PendingRequest {\n resolve: (v: unknown) => void;\n reject: (e: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n /**\n * Timestamp when the request was sent, used for cache merge protection.\n * Ensures local modifications made after the request was sent aren't overwritten\n * by stale server responses that arrive later.\n */\n requestTime: number;\n method: string; // CF-API-02: Track method name for better error reporting\n}\n\n/**\n * Client-side SCORM facade running in your content frame.\n * Returns cached values synchronously, then fires off an async\n * postMessage to the LMS frame to refresh cache and error state.\n */\nexport default class CrossFrameAPI {\n private _cache = new Map<string, string>();\n private _cacheTimestamps = new Map<string, number>();\n private _lastError = \"0\";\n private _pending = new Map<string, PendingRequest>();\n private _counter = 0;\n private readonly _origin: string;\n private readonly _targetWindow: Window;\n private readonly _timeout: number;\n private readonly _heartbeatInterval: number;\n private readonly _heartbeatTimeout: number;\n\n private _destroyed = false;\n private _connected = true;\n private _lastHeartbeatResponse = Date.now();\n private _heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n private _eventListeners = new Map<string, Set<CrossFrameEventCallback>>();\n private readonly _boundOnMessage: (ev: MessageEvent) => void;\n\n /**\n * Type guard to validate MessageResponse structure\n */\n private static _isValidMessageResponse(data: unknown): data is MessageResponse {\n if (typeof data !== \"object\" || data === null) return false;\n\n const resp = data as Partial<MessageResponse>;\n\n // messageId is required\n if (typeof resp.messageId !== \"string\" || resp.messageId.length === 0) return false;\n\n // error must have correct shape if present\n if (resp.error !== undefined) {\n if (typeof resp.error !== \"object\" || resp.error === null) return false;\n const err = resp.error as Record<string, unknown>;\n if (typeof err.message !== \"string\") return false;\n if (err.code !== undefined && typeof err.code !== \"string\") return false;\n }\n\n // isHeartbeat must be boolean if present\n if (resp.isHeartbeat !== undefined && typeof resp.isHeartbeat !== \"boolean\") return false;\n\n return true;\n }\n\n /**\n * Validates that args is an array and sanitizes it for safe use\n */\n private static _validateArgs(args: unknown[]): args is unknown[] {\n if (!Array.isArray(args)) return false;\n // Additional validation could check for specific types based on method\n return true;\n }\n\n private _handler: ProxyHandler<CrossFrameAPI> = {\n get: (target, prop, receiver) => {\n // If it's an existing property/method, return it\n if (typeof prop !== \"string\" || prop in target) {\n const v = Reflect.get(target, prop, receiver);\n return typeof v === \"function\" ? v.bind(target) : v;\n }\n\n // Otherwise treat prop as a SCORM call\n const methodName = prop;\n const isGet = methodName.endsWith(\"GetValue\");\n const isSet = methodName.startsWith(\"LMSSet\") || methodName.endsWith(\"SetValue\");\n const isInit = methodName === \"Initialize\" || methodName === \"LMSInitialize\";\n const isFinish = methodName === \"Terminate\" || methodName === \"LMSFinish\";\n const isCommit = methodName === \"Commit\" || methodName === \"LMSCommit\";\n const isErrorString = methodName === \"GetErrorString\" || methodName === \"LMSGetErrorString\";\n const isDiagnostic = methodName === \"GetDiagnostic\" || methodName === \"LMSGetDiagnostic\";\n\n return (...args: unknown[]): string => {\n // CF-API-03: Validate args is an array (TypeScript guarantees this, but runtime safety)\n if (!CrossFrameAPI._validateArgs(args)) {\n console.error(`CrossFrameAPI: Invalid arguments for ${methodName}`);\n return \"\";\n }\n\n // Synchronous cache update for setter calls\n if (isSet && args.length >= 2) {\n const key = args[0] as string;\n target._cache.set(key, String(args[1]));\n target._cacheTimestamps.set(key, Date.now());\n target._lastError = \"0\";\n }\n\n // Capture request time for cache merge protection\n const requestTime = Date.now();\n\n // Fire off async postMessage to refresh cache and error\n target\n ._post(methodName, args)\n .then((res) => {\n if (isGet && args.length >= 1) {\n const key = args[0] as string;\n // Only update if not locally modified after request was sent\n const localModTime = target._cacheTimestamps.get(key) ?? 0;\n if (localModTime < requestTime) {\n target._cache.set(key, String(res));\n target._cacheTimestamps.delete(key);\n }\n target._lastError = \"0\";\n }\n if (isErrorString && args.length >= 1) {\n const code = String(args[0]);\n target._cache.set(`error_${code}`, String(res));\n }\n if (isDiagnostic && args.length >= 1) {\n const code = String(args[0]);\n target._cache.set(`diag_${code}`, String(res));\n }\n if (methodName === \"GetLastError\" || methodName === \"LMSGetLastError\") {\n target._lastError = String(res);\n }\n })\n .catch((err) => target._capture(methodName, err));\n\n // Return synchronously\n if (isGet && args.length >= 1) {\n return target._cache.get(args[0] as string) ?? \"\";\n }\n if (isErrorString && args.length >= 1) {\n const code = String(args[0]);\n return target._cache.get(`error_${code}`) ?? \"\";\n }\n if (isDiagnostic && args.length >= 1) {\n const code = String(args[0]);\n return target._cache.get(`diag_${code}`) ?? \"\";\n }\n if (isInit || isFinish || isCommit || isSet) {\n // Immediately return \"true\"\n const result = \"true\";\n // Then warm cache with timestamp protection:\n target\n ._post(\"getFlattenedCMI\", [])\n .then((all: unknown) => {\n if (all && typeof all === \"object\") {\n const entries = Object.entries(all as Record<string, string>);\n entries.forEach(([key, val]) => {\n // Only update if not locally modified after request was sent\n const localModTime = target._cacheTimestamps.get(key) ?? 0;\n if (localModTime < requestTime) {\n target._cache.set(key, val);\n target._cacheTimestamps.delete(key);\n }\n });\n }\n // reset error\n target._lastError = \"0\";\n })\n .catch((err) => target._capture(\"getFlattenedCMI\", err));\n return result;\n }\n if (methodName === \"GetLastError\" || methodName === \"LMSGetLastError\") {\n return target._lastError;\n }\n return \"\";\n };\n },\n };\n\n /**\n * Creates a new CrossFrameAPI instance.\n * @param targetOrigin - Origin to send messages to. Default \"*\" sends to any origin.\n * @param targetWindow - Window to send messages to. Default is window.parent.\n * @param options - Configuration options\n */\n constructor(\n targetOrigin: string = \"*\",\n targetWindow: Window = window.parent,\n options: CrossFrameAPIOptions = {},\n ) {\n this._origin = targetOrigin;\n this._targetWindow = targetWindow;\n this._timeout = options.timeout ?? 5000;\n this._heartbeatInterval = options.heartbeatInterval ?? 30000;\n this._heartbeatTimeout = options.heartbeatTimeout ?? 60000;\n\n // Warn about wildcard origin security implications\n if (targetOrigin === \"*\") {\n console.warn(\n \"CrossFrameAPI: Using wildcard origin ('*') allows any origin to receive messages. \" +\n \"This is insecure for production use. \" +\n \"Specify an explicit origin (e.g., 'https://lms.example.com') to restrict message recipients.\",\n );\n }\n\n this._boundOnMessage = this._onMessage.bind(this);\n window.addEventListener(\"message\", this._boundOnMessage);\n this._startHeartbeat();\n\n return new Proxy(this, this._handler);\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 if (this._heartbeatTimer) {\n clearInterval(this._heartbeatTimer);\n this._heartbeatTimer = undefined;\n }\n // Reject all pending requests\n for (const pending of Array.from(this._pending.values())) {\n clearTimeout(pending.timer);\n pending.reject(new Error(\"CrossFrameAPI destroyed\"));\n }\n this._pending.clear();\n this._cache.clear();\n this._cacheTimestamps.clear();\n this._eventListeners.clear();\n }\n\n /**\n * Subscribes to a CrossFrame event.\n * @param event - Event type to listen for\n * @param callback - Function to call when event occurs\n */\n on(event: string, callback: CrossFrameEventCallback): void {\n if (!this._eventListeners.has(event)) {\n this._eventListeners.set(event, new Set());\n }\n this._eventListeners.get(event)?.add(callback);\n }\n\n /**\n * Unsubscribes from a CrossFrame event.\n * @param event - Event type to stop listening for\n * @param callback - Function to remove\n */\n off(event: string, callback: CrossFrameEventCallback): void {\n this._eventListeners.get(event)?.delete(callback);\n }\n\n /**\n * Returns whether the connection to the LMS frame is currently active.\n */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Emits an event to all registered listeners.\n */\n private _emit(event: CrossFrameEvent): void {\n this._eventListeners.get(event.type)?.forEach((cb) => cb(event));\n }\n\n /**\n * Starts the heartbeat mechanism for connection detection.\n */\n private _startHeartbeat(): void {\n // CF-API-01: Clear any existing heartbeat timer before starting a new one\n if (this._heartbeatTimer) {\n clearInterval(this._heartbeatTimer);\n }\n\n this._heartbeatTimer = setInterval(() => {\n if (this._destroyed) return;\n\n // Check if we've missed heartbeats\n const timeSinceLastResponse = Date.now() - this._lastHeartbeatResponse;\n if (timeSinceLastResponse > this._heartbeatTimeout && this._connected) {\n this._connected = false;\n this._emit({ type: \"connectionLost\" });\n }\n\n // Send heartbeat ping\n this._sendHeartbeat();\n }, this._heartbeatInterval);\n }\n\n /**\n * Sends a heartbeat ping to the LMS frame.\n */\n private _sendHeartbeat(): void {\n const messageId = `hb-${Date.now()}-${this._counter++}`;\n const msg: MessageData = {\n messageId,\n method: \"__heartbeat__\",\n params: [],\n isHeartbeat: true,\n };\n this._targetWindow.postMessage(msg, this._origin);\n }\n\n /**\n * Send a message to the LMS frame and return a promise for its response.\n */\n private _post(method: string, params: unknown[]): Promise<unknown> {\n if (this._destroyed) {\n return Promise.reject(new Error(\"CrossFrameAPI destroyed\"));\n }\n\n const messageId = `cfapi-${Date.now()}-${this._counter++}`;\n const requestTime = Date.now();\n\n // Deep-clean params of non-cloneables (e.g. functions)\n const safeParams = params.map((p) => {\n if (typeof p === \"function\") {\n // DEFENSIVE CODING: Warn about non-serializable function parameters.\n // This console.warn is intentionally kept in production builds because:\n // 1. Functions in SCORM API params indicate a programming error by content authors\n // 2. The warning helps diagnose integration issues during content development\n // 3. It alerts developers to potential data loss (function becomes undefined)\n // 4. The frequency should be very low in production (only on misconfigured content)\n //\n // Alternative: Could be gated by a debug flag if production console noise is a concern\n console.warn(\"Dropping function param when posting SCORM call:\", method);\n return undefined;\n }\n return p;\n });\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this._pending.has(messageId)) {\n this._pending.delete(messageId);\n reject(new Error(`Timeout calling ${method}`));\n }\n }, this._timeout);\n\n // CF-API-02: Store method name in pending request for better error reporting\n this._pending.set(messageId, { resolve, reject, timer, requestTime, method });\n const msg: MessageData = { messageId, method, params: safeParams };\n this._targetWindow.postMessage(msg, this._origin);\n });\n }\n\n /**\n * Handle incoming postMessage responses from the LMS frame.\n */\n private _onMessage(ev: MessageEvent): void {\n if (this._destroyed) return;\n\n // Validate the message origin and source unless all origins are allowed\n if (this._origin !== \"*\" && ev.origin !== this._origin) {\n return;\n }\n if (ev.source && ev.source !== this._targetWindow) {\n return;\n }\n\n // CF-API-04: Validate that ev.data has the expected MessageResponse structure\n if (!CrossFrameAPI._isValidMessageResponse(ev.data)) return;\n\n const data = ev.data;\n\n // Handle heartbeat response\n if (data.isHeartbeat) {\n this._lastHeartbeatResponse = Date.now();\n if (!this._connected) {\n this._connected = true;\n this._emit({ type: \"connectionRestored\" });\n }\n return;\n }\n\n // Handle regular response\n const pending = this._pending.get(data.messageId);\n if (!pending) return;\n\n clearTimeout(pending.timer);\n this._pending.delete(data.messageId);\n\n if (data.error) {\n // Check if rate limited and emit event\n // CF-API-02: Use the actual method name from the pending request\n if (data.error.message === \"Rate limit exceeded\") {\n this._emit({ type: \"rateLimited\", method: pending.method });\n }\n pending.reject(data.error);\n } else {\n pending.resolve(data.result);\n }\n }\n\n /**\n * Capture and cache SCORM errors.\n */\n private _capture(method: string, err: unknown): void {\n let errorMessage = \"Unknown error\";\n\n if (err instanceof Error) {\n errorMessage = err.message;\n } else if (typeof err === \"object\" && err !== null && \"message\" in err) {\n errorMessage = String((err as { message: unknown }).message);\n }\n\n console.error(`CrossFrameAPI ${method} error:`, err);\n // Match SCORM error codes (3 digits) from error messages\n const match = /(?:error code|code)?\\s*(\\d{3})\\b/i.exec(errorMessage);\n const code = match?.[1] ?? String(global_errors.GENERAL);\n this._lastError = code;\n this._cache.set(`error_${code}`, errorMessage);\n }\n}\n"],"names":["CrossFrameAPI","constructor","targetOrigin","targetWindow","arguments","length","undefined","window","parent","options","__publicField","this","Map","Date","now","get","target","prop","receiver","v","Reflect","bind","methodName","isGet","endsWith","isSet","startsWith","isInit","isFinish","isCommit","isErrorString","isDiagnostic","_len","args","Array","_key","_validateArgs","console","error","key","_cache","set","String","_cacheTimestamps","_lastError","requestTime","_post","then","res","localModTime","delete","catch","err","_capture","result","all","Object","entries","forEach","_ref","val","_origin","_targetWindow","_timeout","timeout","_heartbeatInterval","heartbeatInterval","_heartbeatTimeout","heartbeatTimeout","warn","_boundOnMessage","_onMessage","addEventListener","_startHeartbeat","Proxy","_handler","_isValidMessageResponse","data","messageId","message","code","isHeartbeat","isArray","destroy","_destroyed","removeEventListener","_heartbeatTimer","clearInterval","pending","from","_pending","values","clearTimeout","timer","reject","Error","clear","_eventListeners","on","event","callback","has","Set","add","off","connected","_connected","_emit","type","cb","setInterval","_lastHeartbeatResponse","_sendHeartbeat","_counter","postMessage","method","params","Promise","safeParams","map","p","resolve","setTimeout","ev","origin","source","errorMessage","match","exec"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cross-frame-api.min.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/dist/cross-frame-lms.js
CHANGED
|
@@ -1,182 +1,245 @@
|
|
|
1
1
|
this.CrossFrameLMS = (function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
function _class_call_check(instance, Constructor) {
|
|
5
|
+
if (!(instance instanceof Constructor)) {
|
|
6
|
+
throw new TypeError("Cannot call a class as a function");
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function _defineProperties(target, props) {
|
|
10
|
+
for(var i = 0; i < props.length; i++){
|
|
11
|
+
var descriptor = props[i];
|
|
12
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
13
|
+
descriptor.configurable = true;
|
|
14
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
15
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function _create_class(Constructor, protoProps, staticProps) {
|
|
19
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
20
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
21
|
+
return Constructor;
|
|
22
|
+
}
|
|
23
|
+
function _instanceof(left, right) {
|
|
24
|
+
"@swc/helpers - instanceof";
|
|
25
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
26
|
+
return !!right[Symbol.hasInstance](left);
|
|
27
|
+
} else {
|
|
28
|
+
return left instanceof right;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function _type_of(obj) {
|
|
32
|
+
"@swc/helpers - typeof";
|
|
33
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
34
|
+
}
|
|
4
35
|
var __defProp = Object.defineProperty;
|
|
5
|
-
var __defNormalProp = (obj, key, value)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
var __defNormalProp = function __defNormalProp(obj, key, value) {
|
|
37
|
+
return key in obj ? __defProp(obj, key, {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: value
|
|
42
|
+
}) : obj[key] = value;
|
|
43
|
+
};
|
|
44
|
+
var __publicField = function __publicField(obj, key, value) {
|
|
45
|
+
return __defNormalProp(obj, (typeof key === "undefined" ? "undefined" : _type_of(key)) !== "symbol" ? key + "" : key, value);
|
|
46
|
+
};
|
|
47
|
+
var _CrossFrameLMS = /*#__PURE__*/ function() {
|
|
48
|
+
function _CrossFrameLMS(api) {
|
|
49
|
+
var targetOrigin = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "*", options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
|
|
50
|
+
_class_call_check(this, _CrossFrameLMS);
|
|
51
|
+
var _options_rateLimit;
|
|
52
|
+
__publicField(this, "_api");
|
|
53
|
+
__publicField(this, "_origin");
|
|
54
|
+
__publicField(this, "_rateLimit");
|
|
55
|
+
__publicField(this, "_requestTimes", []);
|
|
56
|
+
__publicField(this, "_destroyed", false);
|
|
57
|
+
__publicField(this, "_boundOnMessage");
|
|
58
|
+
this._api = api;
|
|
59
|
+
this._origin = targetOrigin;
|
|
60
|
+
this._rateLimit = (_options_rateLimit = options.rateLimit) !== null && _options_rateLimit !== void 0 ? _options_rateLimit : 100;
|
|
61
|
+
if (targetOrigin === "*") {
|
|
62
|
+
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.");
|
|
63
|
+
}
|
|
64
|
+
this._boundOnMessage = this._onMessage.bind(this);
|
|
65
|
+
window.addEventListener("message", this._boundOnMessage);
|
|
33
66
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
67
|
+
_create_class(_CrossFrameLMS, [
|
|
68
|
+
{
|
|
69
|
+
/**
|
|
38
70
|
* Destroys this instance, removing event listeners and preventing further message processing.
|
|
39
71
|
* Once destroyed, the instance cannot be reused.
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
*/ key: "destroy",
|
|
73
|
+
value: function destroy() {
|
|
74
|
+
if (this._destroyed) return;
|
|
75
|
+
this._destroyed = true;
|
|
76
|
+
window.removeEventListener("message", this._boundOnMessage);
|
|
77
|
+
this._requestTimes.length = 0;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
/**
|
|
48
82
|
* Checks if the rate limit has been exceeded.
|
|
49
83
|
* Uses a sliding window of 1 second.
|
|
50
84
|
* @returns true if rate limit exceeded, false otherwise
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const msg = data;
|
|
67
|
-
if (typeof msg.messageId !== "string" || msg.messageId.length === 0) return false;
|
|
68
|
-
if (typeof msg.method !== "string" || msg.method.length === 0) return false;
|
|
69
|
-
if (msg.params !== void 0 && !Array.isArray(msg.params)) return false;
|
|
70
|
-
if (msg.isHeartbeat !== void 0 && typeof msg.isHeartbeat !== "boolean") return false;
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
85
|
+
*/ key: "_isRateLimited",
|
|
86
|
+
value: function _isRateLimited() {
|
|
87
|
+
var now = Date.now();
|
|
88
|
+
this._requestTimes = this._requestTimes.filter(function(t) {
|
|
89
|
+
return now - t < 1e3;
|
|
90
|
+
});
|
|
91
|
+
if (this._requestTimes.length >= this._rateLimit) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
this._requestTimes.push(now);
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
/**
|
|
74
100
|
* Handles incoming postMessage events from child frames.
|
|
75
|
-
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
*/ key: "_onMessage",
|
|
102
|
+
value: function _onMessage(ev) {
|
|
103
|
+
if (this._destroyed) return;
|
|
104
|
+
if (this._origin !== "*" && ev.origin !== this._origin) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (!_CrossFrameLMS._isValidMessageData(ev.data)) return;
|
|
108
|
+
var msg = ev.data;
|
|
109
|
+
if (!ev.source) return;
|
|
110
|
+
if (!("postMessage" in ev.source)) return;
|
|
111
|
+
var source = ev.source;
|
|
112
|
+
if (msg.isHeartbeat) {
|
|
113
|
+
var resp = {
|
|
114
|
+
messageId: msg.messageId,
|
|
115
|
+
isHeartbeat: true
|
|
116
|
+
};
|
|
117
|
+
source.postMessage(resp, this._origin);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (this._isRateLimited()) {
|
|
121
|
+
var resp1 = {
|
|
122
|
+
messageId: msg.messageId,
|
|
123
|
+
error: {
|
|
124
|
+
message: "Rate limit exceeded",
|
|
125
|
+
code: "101"
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
source.postMessage(resp1, this._origin);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!_CrossFrameLMS.ALLOWED_METHODS.has(msg.method)) {
|
|
132
|
+
var resp2 = {
|
|
133
|
+
messageId: msg.messageId,
|
|
134
|
+
error: {
|
|
135
|
+
message: "Method not allowed: ".concat(msg.method),
|
|
136
|
+
code: "101"
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
source.postMessage(resp2, this._origin);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this._process(msg, source);
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
/**
|
|
147
|
+
* Processes a validated message by invoking the requested API method.
|
|
148
|
+
*/ key: "_process",
|
|
149
|
+
value: function _process(msg, source) {
|
|
150
|
+
var _this = this;
|
|
151
|
+
var sendResponse = function sendResponse(result, error) {
|
|
152
|
+
var resp = {
|
|
153
|
+
messageId: msg.messageId
|
|
154
|
+
};
|
|
155
|
+
if (result !== void 0) resp.result = result;
|
|
156
|
+
if (error !== void 0) resp.error = error;
|
|
157
|
+
source.postMessage(resp, _this._origin);
|
|
158
|
+
};
|
|
159
|
+
try {
|
|
160
|
+
var fn = this._api[msg.method];
|
|
161
|
+
if (typeof fn !== "function") {
|
|
162
|
+
sendResponse(void 0, {
|
|
163
|
+
message: "Method ".concat(msg.method, " not found")
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
var params = Array.isArray(msg.params) ? msg.params : [];
|
|
168
|
+
var result = fn.apply(this._api, params);
|
|
169
|
+
if (result && typeof result.then === "function") {
|
|
170
|
+
result.then(function(r) {
|
|
171
|
+
return sendResponse(r);
|
|
172
|
+
}).catch(function(e) {
|
|
173
|
+
var message = _instanceof(e, Error) ? e.message : "Unknown error";
|
|
174
|
+
var code = e && (typeof e === "undefined" ? "undefined" : _type_of(e)) === "object" && "code" in e && typeof e.code === "string" ? e.code : void 0;
|
|
175
|
+
var errorObj = {
|
|
176
|
+
message: message
|
|
177
|
+
};
|
|
178
|
+
if (code !== void 0) {
|
|
179
|
+
errorObj.code = code;
|
|
180
|
+
}
|
|
181
|
+
sendResponse(void 0, errorObj);
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
sendResponse(result);
|
|
185
|
+
}
|
|
186
|
+
} catch (e) {
|
|
187
|
+
var message = _instanceof(e, Error) ? e.message : "Unknown error";
|
|
188
|
+
var code = e && (typeof e === "undefined" ? "undefined" : _type_of(e)) === "object" && "code" in e && typeof e.code === "string" ? e.code : void 0;
|
|
189
|
+
var errorObj = {
|
|
190
|
+
message: message
|
|
191
|
+
};
|
|
192
|
+
if (code !== void 0) {
|
|
193
|
+
errorObj.code = code;
|
|
194
|
+
}
|
|
195
|
+
sendResponse(void 0, errorObj);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
100
198
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
199
|
+
], [
|
|
200
|
+
{
|
|
201
|
+
key: "_isValidMessageData",
|
|
202
|
+
value: /**
|
|
203
|
+
* Type guard to validate MessageData structure
|
|
204
|
+
*/ function _isValidMessageData(data) {
|
|
205
|
+
if ((typeof data === "undefined" ? "undefined" : _type_of(data)) !== "object" || data === null) return false;
|
|
206
|
+
var msg = data;
|
|
207
|
+
if (typeof msg.messageId !== "string" || msg.messageId.length === 0) return false;
|
|
208
|
+
if (typeof msg.method !== "string" || msg.method.length === 0) return false;
|
|
209
|
+
if (msg.params !== void 0 && !Array.isArray(msg.params)) return false;
|
|
210
|
+
if (msg.isHeartbeat !== void 0 && typeof msg.isHeartbeat !== "boolean") return false;
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
111
213
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
this._process(msg, source);
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Processes a validated message by invoking the requested API method.
|
|
120
|
-
*/
|
|
121
|
-
_process(msg, source) {
|
|
122
|
-
const sendResponse = (result, error) => {
|
|
123
|
-
const resp = {
|
|
124
|
-
messageId: msg.messageId
|
|
125
|
-
};
|
|
126
|
-
if (result !== void 0) resp.result = result;
|
|
127
|
-
if (error !== void 0) resp.error = error;
|
|
128
|
-
source.postMessage(resp, this._origin);
|
|
129
|
-
};
|
|
130
|
-
try {
|
|
131
|
-
const fn = this._api[msg.method];
|
|
132
|
-
if (typeof fn !== "function") {
|
|
133
|
-
sendResponse(void 0, {
|
|
134
|
-
message: `Method ${msg.method} not found`
|
|
135
|
-
});
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const params = Array.isArray(msg.params) ? msg.params : [];
|
|
139
|
-
const result = fn.apply(this._api, params);
|
|
140
|
-
if (result && typeof result.then === "function") {
|
|
141
|
-
result.then(r => sendResponse(r)).catch(e => {
|
|
142
|
-
const message = e instanceof Error ? e.message : "Unknown error";
|
|
143
|
-
const code = e && typeof e === "object" && "code" in e && typeof e.code === "string" ? e.code : void 0;
|
|
144
|
-
const errorObj = {
|
|
145
|
-
message
|
|
146
|
-
};
|
|
147
|
-
if (code !== void 0) {
|
|
148
|
-
errorObj.code = code;
|
|
149
|
-
}
|
|
150
|
-
sendResponse(void 0, errorObj);
|
|
151
|
-
});
|
|
152
|
-
} else {
|
|
153
|
-
sendResponse(result);
|
|
154
|
-
}
|
|
155
|
-
} catch (e) {
|
|
156
|
-
const message = e instanceof Error ? e.message : "Unknown error";
|
|
157
|
-
const code = e && typeof e === "object" && "code" in e && typeof e.code === "string" ? e.code : void 0;
|
|
158
|
-
const errorObj = {
|
|
159
|
-
message
|
|
160
|
-
};
|
|
161
|
-
if (code !== void 0) {
|
|
162
|
-
errorObj.code = code;
|
|
163
|
-
}
|
|
164
|
-
sendResponse(void 0, errorObj);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
};
|
|
214
|
+
]);
|
|
215
|
+
return _CrossFrameLMS;
|
|
216
|
+
}();
|
|
168
217
|
/**
|
|
169
218
|
* Strict allowlist of methods that can be invoked via cross-frame messages.
|
|
170
219
|
* Only SCORM API methods and internal helpers are permitted.
|
|
171
|
-
*/
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
220
|
+
*/ __publicField(_CrossFrameLMS, "ALLOWED_METHODS", /* @__PURE__ */ new Set([
|
|
221
|
+
// SCORM 1.2 methods
|
|
222
|
+
"LMSInitialize",
|
|
223
|
+
"LMSFinish",
|
|
224
|
+
"LMSGetValue",
|
|
225
|
+
"LMSSetValue",
|
|
226
|
+
"LMSCommit",
|
|
227
|
+
"LMSGetLastError",
|
|
228
|
+
"LMSGetErrorString",
|
|
229
|
+
"LMSGetDiagnostic",
|
|
230
|
+
// SCORM 2004 methods
|
|
231
|
+
"Initialize",
|
|
232
|
+
"Terminate",
|
|
233
|
+
"GetValue",
|
|
234
|
+
"SetValue",
|
|
235
|
+
"Commit",
|
|
236
|
+
"GetLastError",
|
|
237
|
+
"GetErrorString",
|
|
238
|
+
"GetDiagnostic",
|
|
239
|
+
// Internal method for cache warming
|
|
240
|
+
"getFlattenedCMI"
|
|
241
|
+
]));
|
|
242
|
+
var CrossFrameLMS = _CrossFrameLMS;
|
|
180
243
|
|
|
181
244
|
return CrossFrameLMS;
|
|
182
245
|
|