@marsaude/devtools-shell 0.1.2 → 0.1.4

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.
@@ -641,7 +641,7 @@ const DEVTOOLS_CONFIG = new InjectionToken('DEVTOOLS_CONFIG');
641
641
  /**
642
642
  * Plain localStorage wrapper owned by the package. DevTools-internal keys are
643
643
  * prefixed `DEVTOOLS_`; the *active app session* keys (`JWT_TOKEN`,
644
- * `JWT_REFRESH_TOKEN`, `APP_USER`) are written deliberately so the host patient
644
+ * `JWT_REFRESH_TOKEN`, `EGRET_USER`) are written deliberately so the host patient
645
645
  * app consumes them — that is the only intentional touch-point with the host.
646
646
  */
647
647
  class DevtoolsStorage {
@@ -1352,32 +1352,30 @@ const RECAPTCHA_SCRIPT_ID = 'dc-recaptcha-v2-invisible';
1352
1352
  * the `grecaptcha` API. The host only passes `recaptchaSiteKey`; the package
1353
1353
  * injects the script itself so nothing depends on the host having it loaded.
1354
1354
  */
1355
+ const RECAPTCHA_ONLOAD_CB = '__dcRecaptchaOnload';
1355
1356
  function ensureRecaptchaScript() {
1356
1357
  const w = window;
1357
1358
  if (w.grecaptcha?.render)
1358
1359
  return Promise.resolve(w.grecaptcha);
1359
1360
  return new Promise((resolve, reject) => {
1360
- const onReady = () => {
1361
- if (w.grecaptcha?.render)
1362
- resolve(w.grecaptcha);
1363
- else
1364
- reject(new Error('grecaptcha unavailable after script load'));
1365
- };
1361
+ // The script tag's own `load` event fires before grecaptcha.render is ready.
1362
+ // The library's own `onload=` callback (passed in the URL) only fires once
1363
+ // the API is fully initialized — that's the reliable readiness signal.
1364
+ w[RECAPTCHA_ONLOAD_CB] = () => resolve(w.grecaptcha);
1366
1365
  const existing = document.getElementById(RECAPTCHA_SCRIPT_ID);
1367
1366
  if (existing) {
1368
1367
  if (w.grecaptcha?.render)
1369
1368
  return resolve(w.grecaptcha);
1370
- existing.addEventListener('load', onReady, { once: true });
1371
1369
  existing.addEventListener('error', () => reject(new Error('reCAPTCHA script failed to load')), { once: true });
1372
- return;
1370
+ return; // the pending onload callback above will resolve us
1373
1371
  }
1374
1372
  const script = document.createElement('script');
1375
1373
  script.id = RECAPTCHA_SCRIPT_ID;
1376
- // `render=explicit` so we control the (invisible) widget creation ourselves.
1377
- script.src = 'https://www.google.com/recaptcha/api.js?render=explicit';
1374
+ // `onload=` => library calls us when ready; `render=explicit` => we create
1375
+ // the (invisible) widget ourselves.
1376
+ script.src = `https://www.google.com/recaptcha/api.js?onload=${RECAPTCHA_ONLOAD_CB}&render=explicit`;
1378
1377
  script.async = true;
1379
1378
  script.defer = true;
1380
- script.onload = onReady;
1381
1379
  script.onerror = () => reject(new Error('reCAPTCHA script failed to load'));
1382
1380
  document.head.appendChild(script);
1383
1381
  });
@@ -1564,10 +1562,11 @@ const devtoolsHttpInterceptor = (req, next) => {
1564
1562
  const SESSIONS_KEY = 'DEVTOOLS_SESSIONS';
1565
1563
  const ACTIVE_KEY = 'DEVTOOLS_ACTIVE';
1566
1564
  // The active app session — read by the HOST patient app. This is the ONE
1567
- // intentional touch-point with the host.
1565
+ // intentional touch-point with the host, so these keys MUST match the host's
1566
+ // JwtAuthService exactly and must never change.
1568
1567
  const JWT_TOKEN = 'JWT_TOKEN';
1569
1568
  const JWT_REFRESH_TOKEN = 'JWT_REFRESH_TOKEN';
1570
- const APP_USER = 'APP_USER';
1569
+ const APP_USER = 'EGRET_USER';
1571
1570
  /**
1572
1571
  * Manages the *active app session* that the host patient app consumes, letting
1573
1572
  * the QA switch it between the fixed admin (the DevTools identity) and any saved
@@ -1603,7 +1602,11 @@ class DevtoolsSessionService {
1603
1602
  this.storage.set(SESSIONS_KEY, next);
1604
1603
  return full;
1605
1604
  }
1606
- /** Write a stored session into the host's active-session keys. */
1605
+ /**
1606
+ * Write a stored session into the host's active-session keys and reload so the
1607
+ * host app re-reads it (it only reads these keys at bootstrap). Covers both a
1608
+ * fresh login and switching between users.
1609
+ */
1607
1610
  switchTo(id) {
1608
1611
  const session = id === 'admin' ? this.admin() : this._sessions().find((s) => s.id === id) ?? null;
1609
1612
  if (!session)
@@ -1613,6 +1616,12 @@ class DevtoolsSessionService {
1613
1616
  this.storage.set(APP_USER, session.profile ?? null);
1614
1617
  this.activeId.set(id);
1615
1618
  this.storage.set(ACTIVE_KEY, id);
1619
+ try {
1620
+ window.location.reload();
1621
+ }
1622
+ catch {
1623
+ /* non-browser env — ignore */
1624
+ }
1616
1625
  return session;
1617
1626
  }
1618
1627
  removeSession(id) {