@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.cjs CHANGED
@@ -1932,6 +1932,101 @@ function generateLrsBridgeCode(options) {
1932
1932
  return hasValidMbox || hasValidAccount;
1933
1933
  }
1934
1934
 
1935
+ // ========================================================================
1936
+ // CROSS-FRAME ACTOR SHARING
1937
+ // PA-Patcher injects the bridge into ALL HTML files in a SCORM package.
1938
+ // Rise courses have multiple HTML files (index.html, scormcontent/index.html),
1939
+ // each getting their own bridge instance with its own LRS.actor.
1940
+ // The skin overlay (email gate) only runs in one frame and sets the actor there.
1941
+ // Without sharing, other bridge instances keep the Anonymous Learner actor.
1942
+ // Solution: persist actor to localStorage so all bridge instances can use it.
1943
+ // ========================================================================
1944
+ var ACTOR_STORAGE_KEY = 'pa_lrs_shared_actor';
1945
+
1946
+ function isAnonymousActor(actor) {
1947
+ if (!actor) return true;
1948
+ if (!actor.name) return true;
1949
+ var n = actor.name.toLowerCase();
1950
+ return n === 'anonymous learner' || n === 'unknown learner' || n === 'anonymous' || n === 'unknown';
1951
+ }
1952
+
1953
+ /**
1954
+ * Persist actor to localStorage for cross-frame sharing.
1955
+ * Only stores non-anonymous actors.
1956
+ */
1957
+ function persistActor(actor) {
1958
+ if (!actor || isAnonymousActor(actor)) return;
1959
+ try {
1960
+ localStorage.setItem(ACTOR_STORAGE_KEY, JSON.stringify(actor));
1961
+ log('Actor persisted to localStorage:', actor.name);
1962
+ } catch (e) { /* localStorage unavailable */ }
1963
+ }
1964
+
1965
+ /**
1966
+ * Load shared actor from localStorage.
1967
+ * Returns the stored actor or null.
1968
+ */
1969
+ function loadSharedActor() {
1970
+ try {
1971
+ var stored = localStorage.getItem(ACTOR_STORAGE_KEY);
1972
+ if (stored) {
1973
+ var actor = JSON.parse(stored);
1974
+ if (actor && !isAnonymousActor(actor)) {
1975
+ return actor;
1976
+ }
1977
+ }
1978
+ } catch (e) { /* localStorage unavailable or parse error */ }
1979
+ return null;
1980
+ }
1981
+
1982
+ /**
1983
+ * Get the best available actor for statement building.
1984
+ * Priority: LRS.actor (if non-anonymous) > localStorage shared actor > LRS.actor > extractActor()
1985
+ */
1986
+ function getActor() {
1987
+ // If current actor is non-anonymous, use it
1988
+ if (LRS.actor && !isAnonymousActor(LRS.actor)) {
1989
+ return LRS.actor;
1990
+ }
1991
+ // Check localStorage for actor set by another frame (e.g., skin overlay)
1992
+ var shared = loadSharedActor();
1993
+ if (shared) {
1994
+ // Update local actor so future calls are fast
1995
+ LRS.actor = shared;
1996
+ log('Actor loaded from cross-frame storage:', shared.name);
1997
+ return shared;
1998
+ }
1999
+ // Fallback to current actor or re-extract
2000
+ return LRS.actor || extractActor();
2001
+ }
2002
+
2003
+ /**
2004
+ * Set actor on the bridge with cross-frame persistence.
2005
+ * Called by skins and external code via window.pa_patcher.lrs.setActor(actor)
2006
+ */
2007
+ LRS.setActor = function(actor) {
2008
+ LRS.actor = actor;
2009
+ persistActor(actor);
2010
+ // Also try to propagate to other frames in the hierarchy
2011
+ try {
2012
+ var w = window;
2013
+ for (var i = 0; i < 10; i++) {
2014
+ try {
2015
+ if (w !== window && w.pa_patcher && w.pa_patcher.lrs) {
2016
+ w.pa_patcher.lrs.actor = actor;
2017
+ }
2018
+ } catch (e) { /* cross-origin */ }
2019
+ if (w === w.parent) break;
2020
+ w = w.parent;
2021
+ }
2022
+ } catch (e) {}
2023
+ if (window.console && window.console.info) {
2024
+ console.info('[PA-LRS] Actor set:', actor.name,
2025
+ actor.mbox ? '(' + actor.mbox + ')' : '',
2026
+ '\u2014 shared across frames');
2027
+ }
2028
+ };
2029
+
1935
2030
  /**
1936
2031
  * Async version that fetches Bravais session if needed
1937
2032
  * Also re-checks all sources in case they became available
@@ -1943,6 +2038,7 @@ function generateLrsBridgeCode(options) {
1943
2038
  // Try to enhance actor with email if missing
1944
2039
  enhanceActorWithEmail(actor, function(enhancedActor) {
1945
2040
  LRS.actor = enhancedActor;
2041
+ persistActor(enhancedActor);
1946
2042
  callback(enhancedActor);
1947
2043
  });
1948
2044
  }
@@ -2949,7 +3045,7 @@ function generateLrsBridgeCode(options) {
2949
3045
 
2950
3046
  var statement = {
2951
3047
  id: generateUUID(),
2952
- actor: LRS.actor || extractActor(),
3048
+ actor: getActor(),
2953
3049
  verb: typeof verb === 'string' ? (VERBS[verb] || { id: verb, display: { 'en-US': verb } }) : verb,
2954
3050
  object: courseObj,
2955
3051
  timestamp: new Date().toISOString()
@@ -3371,7 +3467,7 @@ function generateLrsBridgeCode(options) {
3371
3467
  function buildStatementXyleme(verb, activityObject, result, additionalContext) {
3372
3468
  var statement = {
3373
3469
  id: generateUUID(),
3374
- actor: LRS.actor || extractActor(),
3470
+ actor: getActor(),
3375
3471
  verb: typeof verb === 'string' ? (VERBS[verb] || { id: verb, display: { 'en-US': verb } }) : verb,
3376
3472
  object: activityObject,
3377
3473
  timestamp: new Date().toISOString()
@@ -5100,6 +5196,15 @@ function generateLrsBridgeCode(options) {
5100
5196
  // Extract actor (sync first for immediate use)
5101
5197
  extractActor();
5102
5198
 
5199
+ // Check localStorage for actor set by another frame (e.g., skin overlay in a different HTML file)
5200
+ if (isAnonymousActor(LRS.actor)) {
5201
+ var sharedActor = loadSharedActor();
5202
+ if (sharedActor) {
5203
+ LRS.actor = sharedActor;
5204
+ log('Loaded actor from cross-frame storage at init:', sharedActor.name);
5205
+ }
5206
+ }
5207
+
5103
5208
  log('Bridge initialized in mode:', LRS.mode);
5104
5209
  log('Actor:', LRS.actor);
5105
5210
  log('Course:', LRS.courseInfo);