@runappsai/sdk 0.7.8 → 0.7.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runappsai/sdk",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "JavaScript / TypeScript client for the RunApps AI Gateway",
5
5
  "license": "MIT",
6
6
  "author": "RunApps",
@@ -261,7 +261,10 @@ export class BrowserAuth {
261
261
  // user to click signIn. Without this guard, the backend's
262
262
  // auto-grant flow would re-issue a token on every page reload
263
263
  // (the cookie on the gateway domain is still valid).
264
- if (this.isSignedOut()) {
264
+ // In popup mode a signed-out user is simply asked to sign in again:
265
+ // the popup only ever opens on the user's own action, so there is no
266
+ // page to lose and no reason to refuse the call.
267
+ if (this.isSignedOut() && !(this.signInMode === "popup" && !this.inIframe())) {
265
268
  throw new Error("RunApps: signed out — call client.signIn() to authenticate");
266
269
  }
267
270
 
@@ -441,15 +444,42 @@ export class BrowserAuth {
441
444
  if (this.popupWait && now - this.popupOpenedAt < 3000) return this.popupWait;
442
445
  this.clearSignedOut();
443
446
  const key = storageKey(this.project);
444
- const win = window.open(this.grantUrlForThisPage(true), POPUP_NAME, "popup=yes,width=520,height=700");
447
+ const open = () => window.open(this.grantUrlForThisPage(true), POPUP_NAME, "popup=yes,width=520,height=700");
448
+ let win = open();
445
449
  if (!win) {
446
- // Popup blocked: the redirect is the only way left.
447
- this.signingIn = true;
448
- location.href = this.grantUrlForThisPage();
449
- return new Promise(() => {});
450
+ // Blocked — usually because no user gesture is active (a call made
451
+ // at load, or after a long await). Open it on the user's next click
452
+ // or key press instead; only a popup blocked on a real gesture falls
453
+ // back to the redirect.
454
+ if (this.popupWait) return this.popupWait;
455
+ this.popupWait = this.waitForUserGesture().then(() => {
456
+ this.popupWait = null;
457
+ win = open();
458
+ if (!win) {
459
+ this.signingIn = true;
460
+ location.href = this.grantUrlForThisPage();
461
+ return new Promise<void>(() => {});
462
+ }
463
+ return this.waitForPopupToken(key, Date.now());
464
+ });
465
+ return this.popupWait;
450
466
  }
451
- this.popupOpenedAt = now;
452
- if (this.popupWait) return this.popupWait;
467
+ if (this.popupWait) { this.popupOpenedAt = now; return this.popupWait; }
468
+ return this.waitForPopupToken(key, now);
469
+ }
470
+ /** Resolves on the next click, tap or key press anywhere in the page. */
471
+ private waitForUserGesture(): Promise<void> {
472
+ return new Promise((resolve) => {
473
+ const types = ["pointerdown", "keydown", "touchend"];
474
+ const handler = () => {
475
+ for (const t of types) window.removeEventListener(t, handler, true);
476
+ resolve();
477
+ };
478
+ for (const t of types) window.addEventListener(t, handler, true);
479
+ });
480
+ }
481
+ private waitForPopupToken(key: string, openedAt: number): Promise<void> {
482
+ this.popupOpenedAt = openedAt;
453
483
  // Under COOP the popup's handle is cut off as soon as it leaves this
454
484
  // origin (`win.closed` reads true), so a closed popup cannot be told
455
485
  // from one still signing in: the promise stays pending until a token