mm-math 0.0.4 → 0.0.6

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.
Files changed (2) hide show
  1. package/index.html +67 -10
  2. package/package.json +1 -1
package/index.html CHANGED
@@ -519,7 +519,69 @@ document.getElementById('fUser').addEventListener('keydown', e => {
519
519
  });
520
520
 
521
521
  // ── Login ─────────────────────────────────────────────────────────────────────
522
- const API_BASE = window.location.hostname === 'calc.moshelab.com' ? '' : 'https://calc.moshelab.com';
522
+ const _onCalc = window.location.hostname === 'calc.moshelab.com';
523
+ const BRIDGE_ORIGIN = 'https://calc.moshelab.com';
524
+
525
+ // Preload invisible auth-bridge iframe. The iframe is on calc.moshelab.com so its
526
+ // fetch to /api/login is same-origin — no CORS, no Cloudflare cross-origin checks.
527
+ let _bridge = null;
528
+ let _bridgeReadyResolve = null;
529
+ const _bridgeReady = new Promise(function(resolve) { _bridgeReadyResolve = resolve; });
530
+
531
+ if (!_onCalc) {
532
+ _bridge = document.createElement('iframe');
533
+ _bridge.src = BRIDGE_ORIGIN + '/login-frame.html';
534
+ _bridge.style.cssText = 'display:none;width:0;height:0;border:0;position:absolute;left:-9999px;top:-9999px;';
535
+ _bridge.setAttribute('aria-hidden', 'true');
536
+ document.body.appendChild(_bridge);
537
+ } else {
538
+ // On calc.moshelab.com, bridge isn't used — resolve immediately
539
+ _bridgeReadyResolve();
540
+ }
541
+
542
+ // Shared message handler — resolves the ready promise and dispatches login results
543
+ const _pendingLogins = {};
544
+ window.addEventListener('message', function(event) {
545
+ if (event.origin !== BRIDGE_ORIGIN) return;
546
+ const d = event.data;
547
+ if (!d) return;
548
+ if (d.type === 'bridge_ready') { _bridgeReadyResolve(); return; }
549
+ if (d.type === 'login_result' && d.nonce && _pendingLogins[d.nonce]) {
550
+ _pendingLogins[d.nonce](d);
551
+ delete _pendingLogins[d.nonce];
552
+ }
553
+ });
554
+
555
+ function _loginDirect(username, password) {
556
+ return fetch('/api/login', {
557
+ method: 'POST',
558
+ headers: { 'Content-Type': 'application/json' },
559
+ body: JSON.stringify({ username, password }),
560
+ }).then(function(res) {
561
+ return res.json().then(function(data) {
562
+ data.ok = res.ok; data.status = res.status; return data;
563
+ });
564
+ });
565
+ }
566
+
567
+ function _loginViaBridge(username, password) {
568
+ return _bridgeReady.then(function() {
569
+ return new Promise(function(resolve, reject) {
570
+ if (!_bridge || !_bridge.contentWindow) {
571
+ return reject(new Error('Auth bridge unavailable'));
572
+ }
573
+ const nonce = Math.random().toString(36).slice(2, 10);
574
+ const timer = setTimeout(function() {
575
+ delete _pendingLogins[nonce];
576
+ reject(new Error('Sign-in timed out. Try again.'));
577
+ }, 15000);
578
+
579
+ _pendingLogins[nonce] = function(d) { clearTimeout(timer); resolve(d); };
580
+ _bridge.contentWindow.postMessage({ type: 'login', username: username, password: password, nonce: nonce }, BRIDGE_ORIGIN);
581
+ });
582
+ });
583
+ }
584
+
523
585
  async function doLogin() {
524
586
  const username = document.getElementById('fUser').value.trim();
525
587
  const password = document.getElementById('fPass').value;
@@ -536,15 +598,10 @@ async function doLogin() {
536
598
  errEl.textContent = '';
537
599
 
538
600
  try {
539
- const res = await fetch(API_BASE + '/api/login', {
540
- method: 'POST',
541
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
542
- body: new URLSearchParams({ username, password }),
543
- });
544
- const data = await res.json();
601
+ const data = await (_onCalc ? _loginDirect(username, password) : _loginViaBridge(username, password));
545
602
 
546
- if (!res.ok || !data.success) {
547
- errEl.textContent = res.status === 429
603
+ if (!data.ok || !data.success) {
604
+ errEl.textContent = data.status === 429 || (data.error || '').includes('Too many')
548
605
  ? 'Too many attempts. Wait 60 seconds.'
549
606
  : 'Invalid username or password.';
550
607
  btn.disabled = false;
@@ -568,7 +625,7 @@ async function doLogin() {
568
625
  window.location.href = './browse.html';
569
626
  }
570
627
  } catch (err) {
571
- errEl.textContent = 'Error: ' + (err && err.message ? err.message : err);
628
+ errEl.textContent = err && err.message ? err.message : String(err);
572
629
  btn.disabled = false;
573
630
  btn.textContent = 'Sign In';
574
631
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm-math",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "STEM educational toolkit",
5
5
  "main": "index.html",
6
6
  "files": [