@patch-adams/core 1.5.19 → 1.5.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1922,6 +1922,101 @@ function generateLrsBridgeCode(options) {
1922
1922
  return hasValidMbox || hasValidAccount;
1923
1923
  }
1924
1924
 
1925
+ // ========================================================================
1926
+ // CROSS-FRAME ACTOR SHARING
1927
+ // PA-Patcher injects the bridge into ALL HTML files in a SCORM package.
1928
+ // Rise courses have multiple HTML files (index.html, scormcontent/index.html),
1929
+ // each getting their own bridge instance with its own LRS.actor.
1930
+ // The skin overlay (email gate) only runs in one frame and sets the actor there.
1931
+ // Without sharing, other bridge instances keep the Anonymous Learner actor.
1932
+ // Solution: persist actor to localStorage so all bridge instances can use it.
1933
+ // ========================================================================
1934
+ var ACTOR_STORAGE_KEY = 'pa_lrs_shared_actor';
1935
+
1936
+ function isAnonymousActor(actor) {
1937
+ if (!actor) return true;
1938
+ if (!actor.name) return true;
1939
+ var n = actor.name.toLowerCase();
1940
+ return n === 'anonymous learner' || n === 'unknown learner' || n === 'anonymous' || n === 'unknown';
1941
+ }
1942
+
1943
+ /**
1944
+ * Persist actor to localStorage for cross-frame sharing.
1945
+ * Only stores non-anonymous actors.
1946
+ */
1947
+ function persistActor(actor) {
1948
+ if (!actor || isAnonymousActor(actor)) return;
1949
+ try {
1950
+ localStorage.setItem(ACTOR_STORAGE_KEY, JSON.stringify(actor));
1951
+ log('Actor persisted to localStorage:', actor.name);
1952
+ } catch (e) { /* localStorage unavailable */ }
1953
+ }
1954
+
1955
+ /**
1956
+ * Load shared actor from localStorage.
1957
+ * Returns the stored actor or null.
1958
+ */
1959
+ function loadSharedActor() {
1960
+ try {
1961
+ var stored = localStorage.getItem(ACTOR_STORAGE_KEY);
1962
+ if (stored) {
1963
+ var actor = JSON.parse(stored);
1964
+ if (actor && !isAnonymousActor(actor)) {
1965
+ return actor;
1966
+ }
1967
+ }
1968
+ } catch (e) { /* localStorage unavailable or parse error */ }
1969
+ return null;
1970
+ }
1971
+
1972
+ /**
1973
+ * Get the best available actor for statement building.
1974
+ * Priority: LRS.actor (if non-anonymous) > localStorage shared actor > LRS.actor > extractActor()
1975
+ */
1976
+ function getActor() {
1977
+ // If current actor is non-anonymous, use it
1978
+ if (LRS.actor && !isAnonymousActor(LRS.actor)) {
1979
+ return LRS.actor;
1980
+ }
1981
+ // Check localStorage for actor set by another frame (e.g., skin overlay)
1982
+ var shared = loadSharedActor();
1983
+ if (shared) {
1984
+ // Update local actor so future calls are fast
1985
+ LRS.actor = shared;
1986
+ log('Actor loaded from cross-frame storage:', shared.name);
1987
+ return shared;
1988
+ }
1989
+ // Fallback to current actor or re-extract
1990
+ return LRS.actor || extractActor();
1991
+ }
1992
+
1993
+ /**
1994
+ * Set actor on the bridge with cross-frame persistence.
1995
+ * Called by skins and external code via window.pa_patcher.lrs.setActor(actor)
1996
+ */
1997
+ LRS.setActor = function(actor) {
1998
+ LRS.actor = actor;
1999
+ persistActor(actor);
2000
+ // Also try to propagate to other frames in the hierarchy
2001
+ try {
2002
+ var w = window;
2003
+ for (var i = 0; i < 10; i++) {
2004
+ try {
2005
+ if (w !== window && w.pa_patcher && w.pa_patcher.lrs) {
2006
+ w.pa_patcher.lrs.actor = actor;
2007
+ }
2008
+ } catch (e) { /* cross-origin */ }
2009
+ if (w === w.parent) break;
2010
+ w = w.parent;
2011
+ }
2012
+ } catch (e) {}
2013
+ if (window.console && window.console.info) {
2014
+ console.info('[PA-LRS] Actor set:', actor.name,
2015
+ actor.mbox ? '(' + actor.mbox + ')' : '',
2016
+ '\u2014 shared across frames');
2017
+ }
2018
+ };
2019
+
1925
2020
  /**
1926
2021
  * Async version that fetches Bravais session if needed
1927
2022
  * Also re-checks all sources in case they became available
@@ -1933,6 +2028,7 @@ function generateLrsBridgeCode(options) {
1933
2028
  // Try to enhance actor with email if missing
1934
2029
  enhanceActorWithEmail(actor, function(enhancedActor) {
1935
2030
  LRS.actor = enhancedActor;
2031
+ persistActor(enhancedActor);
1936
2032
  callback(enhancedActor);
1937
2033
  });
1938
2034
  }
@@ -2939,7 +3035,7 @@ function generateLrsBridgeCode(options) {
2939
3035
 
2940
3036
  var statement = {
2941
3037
  id: generateUUID(),
2942
- actor: LRS.actor || extractActor(),
3038
+ actor: getActor(),
2943
3039
  verb: typeof verb === 'string' ? (VERBS[verb] || { id: verb, display: { 'en-US': verb } }) : verb,
2944
3040
  object: courseObj,
2945
3041
  timestamp: new Date().toISOString()
@@ -3361,7 +3457,7 @@ function generateLrsBridgeCode(options) {
3361
3457
  function buildStatementXyleme(verb, activityObject, result, additionalContext) {
3362
3458
  var statement = {
3363
3459
  id: generateUUID(),
3364
- actor: LRS.actor || extractActor(),
3460
+ actor: getActor(),
3365
3461
  verb: typeof verb === 'string' ? (VERBS[verb] || { id: verb, display: { 'en-US': verb } }) : verb,
3366
3462
  object: activityObject,
3367
3463
  timestamp: new Date().toISOString()
@@ -5090,6 +5186,15 @@ function generateLrsBridgeCode(options) {
5090
5186
  // Extract actor (sync first for immediate use)
5091
5187
  extractActor();
5092
5188
 
5189
+ // Check localStorage for actor set by another frame (e.g., skin overlay in a different HTML file)
5190
+ if (isAnonymousActor(LRS.actor)) {
5191
+ var sharedActor = loadSharedActor();
5192
+ if (sharedActor) {
5193
+ LRS.actor = sharedActor;
5194
+ log('Loaded actor from cross-frame storage at init:', sharedActor.name);
5195
+ }
5196
+ }
5197
+
5093
5198
  log('Bridge initialized in mode:', LRS.mode);
5094
5199
  log('Actor:', LRS.actor);
5095
5200
  log('Course:', LRS.courseInfo);