@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`, `
|
|
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
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
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`
|
|
1377
|
-
|
|
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 = '
|
|
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
|
-
/**
|
|
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) {
|