hfviewer automation commited on
Commit
62e3bbe
·
1 Parent(s): e83a526

Sync private Space from hfviewer-719a11371473

Browse files
Files changed (2) hide show
  1. index.html +3 -3
  2. space-auth.js +93 -16
index.html CHANGED
@@ -183,7 +183,7 @@
183
  window.HFVIEWER_PRODUCT_CONTEXT = Object.freeze({
184
  product_surface: "hf_space",
185
  space_repo: "embedl/hfviewer",
186
- space_revision: "hfviewer-eae906457df8",
187
  });
188
  try {
189
  var initialUrl = new URL(window.location.href);
@@ -10630,7 +10630,7 @@ body.sx-viewer-open { overflow: hidden; }
10630
  window.hfviewerAnalyticsBridge?.capture?.(eventName, Object.assign({
10631
  product_surface: "hf_space",
10632
  space_repo: "embedl/hfviewer",
10633
- space_revision: "hfviewer-eae906457df8",
10634
  }, properties || {}));
10635
  } catch (error) { /* analytics must never break navigation */ }
10636
  }
@@ -10846,7 +10846,7 @@ body.sx-viewer-open { overflow: hidden; }
10846
  </script>
10847
 
10848
  <script src="space-shell.js?v=84289fb68096"></script>
10849
- <script type="module" src="space-auth.js?v=cd3690650167"></script>
10850
 
10851
  </body>
10852
  </html>
 
183
  window.HFVIEWER_PRODUCT_CONTEXT = Object.freeze({
184
  product_surface: "hf_space",
185
  space_repo: "embedl/hfviewer",
186
+ space_revision: "hfviewer-719a11371473",
187
  });
188
  try {
189
  var initialUrl = new URL(window.location.href);
 
10630
  window.hfviewerAnalyticsBridge?.capture?.(eventName, Object.assign({
10631
  product_surface: "hf_space",
10632
  space_repo: "embedl/hfviewer",
10633
+ space_revision: "hfviewer-719a11371473",
10634
  }, properties || {}));
10635
  } catch (error) { /* analytics must never break navigation */ }
10636
  }
 
10846
  </script>
10847
 
10848
  <script src="space-shell.js?v=84289fb68096"></script>
10849
+ <script type="module" src="space-auth.js?v=e89e39084329"></script>
10850
 
10851
  </body>
10852
  </html>
space-auth.js CHANGED
@@ -5,7 +5,14 @@ const DESTINATION_URL = "https://hfviewer.com/api/hf_space_oauth/destination";
5
  const POLL_INTERVAL_MS = 800;
6
  const FLOW_TIMEOUT_MS = 5 * 60 * 1000;
7
  const PREPARE_REFRESH_MS = 4 * 60 * 1000;
 
 
 
8
  const AUTH_CHANGED_EVENT = "hfviewer-space-auth-changed";
 
 
 
 
9
  // The Space shell intercepts canonical hfviewer.com links during capture and
10
  // treats every root-path URL as "return to the Space landing page", even when
11
  // its query opens an account view. Use the canonicalizing www host so these
@@ -19,10 +26,20 @@ let rendering = false;
19
  let activeFlow = null;
20
  let preparedFlow = null;
21
  let preparePromise = null;
 
 
22
  let accountDestinationLinks = [];
23
  let lastAnnouncedAuthIdentity = null;
24
  let nativeLoginPopover = null;
25
 
 
 
 
 
 
 
 
 
26
  function readStoredResult() {
27
  try {
28
  const value = localStorage.getItem(STORAGE_KEY);
@@ -79,6 +96,8 @@ function trackAuthEvent(eventName, properties = {}) {
79
  product_surface: "hf_space",
80
  space_repo: String(productContext.space_repo || "unknown"),
81
  space_revision: String(productContext.space_revision || "unknown"),
 
 
82
  auth_provider: "hugging_face",
83
  ...properties,
84
  });
@@ -470,23 +489,65 @@ function failLogin(error, failureStage) {
470
  component: "space_nav",
471
  failure_stage: failureStage,
472
  error_name: String(error?.name || "Error").slice(0, 80),
 
 
473
  });
474
  void prepareLogin();
475
  }
476
 
477
- async function postJson(url, body) {
478
- const response = await fetch(url, {
479
- method: "POST",
480
- mode: "cors",
481
- credentials: "omit",
482
- headers: { "content-type": "application/json" },
483
- body: JSON.stringify(body),
484
- });
485
- const payload = await response.json().catch(() => ({}));
486
- if (!response.ok && response.status !== 202) {
487
- throw new Error(payload?.error || `Request failed (${response.status})`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  }
489
- return payload;
490
  }
491
 
492
  function announceAuthState() {
@@ -569,6 +630,14 @@ function startLogin(flow, link) {
569
 
570
  async function prepareLogin() {
571
  if (oauthResult || activeFlow) return;
 
 
 
 
 
 
 
 
572
  if (
573
  preparedFlow &&
574
  Date.now() - preparedFlow.preparedAt < PREPARE_REFRESH_MS
@@ -581,10 +650,11 @@ async function prepareLogin() {
581
  startedAt: 0,
582
  loginUrl: "",
583
  };
584
- preparePromise = postJson(START_URL, {
585
- flow_id: flow.id,
586
- flow_secret: flow.secret,
587
- }).then((result) => {
 
588
  const loginUrl = new URL(String(result?.login_url || ""));
589
  if (
590
  loginUrl.origin !== "https://huggingface.co" ||
@@ -592,6 +662,9 @@ async function prepareLogin() {
592
  ) throw new Error("The OAuth login URL is invalid");
593
  flow.loginUrl = loginUrl.toString();
594
  preparedFlow = flow;
 
 
 
595
  if (!activeFlow) renderAuth();
596
  refreshNativeLoginPopover();
597
  }).catch((error) => {
@@ -600,7 +673,11 @@ async function prepareLogin() {
600
  component: "space_nav",
601
  failure_stage: "login_prepare",
602
  error_name: String(error?.name || "Error").slice(0, 80),
 
 
 
603
  });
 
604
  }).finally(() => {
605
  preparePromise = null;
606
  });
 
5
  const POLL_INTERVAL_MS = 800;
6
  const FLOW_TIMEOUT_MS = 5 * 60 * 1000;
7
  const PREPARE_REFRESH_MS = 4 * 60 * 1000;
8
+ const PREPARE_RETRY_DELAYS_MS = [0, 450, 1400];
9
+ const PREPARE_RECOVERY_DELAYS_MS = [2000, 8000, 30000, 60000];
10
+ const REQUEST_TIMEOUT_MS = 12 * 1000;
11
  const AUTH_CHANGED_EVENT = "hfviewer-space-auth-changed";
12
+ const OFFICIAL_SPACE_HOSTS = new Set([
13
+ "embedl-hfviewer.static.hf.space",
14
+ "hfvwr-hfviewer-private-qa.static.hf.space",
15
+ ]);
16
  // The Space shell intercepts canonical hfviewer.com links during capture and
17
  // treats every root-path URL as "return to the Space landing page", even when
18
  // its query opens an account view. Use the canonicalizing www host so these
 
26
  let activeFlow = null;
27
  let preparedFlow = null;
28
  let preparePromise = null;
29
+ let prepareFailureCount = 0;
30
+ let prepareRecoveryTimer = null;
31
  let accountDestinationLinks = [];
32
  let lastAnnouncedAuthIdentity = null;
33
  let nativeLoginPopover = null;
34
 
35
+ function currentSpaceDeployment() {
36
+ const host = String(window.location.hostname || "").trim().toLowerCase();
37
+ if (host === "embedl-hfviewer.static.hf.space") return "production";
38
+ if (host === "hfvwr-hfviewer-private-qa.static.hf.space") return "qa";
39
+ if (host.endsWith(".hf.space")) return "copy";
40
+ return "local";
41
+ }
42
+
43
  function readStoredResult() {
44
  try {
45
  const value = localStorage.getItem(STORAGE_KEY);
 
96
  product_surface: "hf_space",
97
  space_repo: String(productContext.space_repo || "unknown"),
98
  space_revision: String(productContext.space_revision || "unknown"),
99
+ space_host: String(window.location.hostname || "unknown").toLowerCase(),
100
+ space_deployment: currentSpaceDeployment(),
101
  auth_provider: "hugging_face",
102
  ...properties,
103
  });
 
489
  component: "space_nav",
490
  failure_stage: failureStage,
491
  error_name: String(error?.name || "Error").slice(0, 80),
492
+ error_message: String(error?.message || "Unknown error").slice(0, 240),
493
+ request_status: Number(error?.status || 0) || undefined,
494
  });
495
  void prepareLogin();
496
  }
497
 
498
+ function delay(milliseconds) {
499
+ return new Promise((resolve) => window.setTimeout(resolve, milliseconds));
500
+ }
501
+
502
+ function schedulePrepareRecovery() {
503
+ if (prepareRecoveryTimer || oauthResult || activeFlow) return;
504
+ const index = Math.min(prepareFailureCount, PREPARE_RECOVERY_DELAYS_MS.length - 1);
505
+ const recoveryDelay = PREPARE_RECOVERY_DELAYS_MS[index];
506
+ prepareFailureCount += 1;
507
+ prepareRecoveryTimer = window.setTimeout(() => {
508
+ prepareRecoveryTimer = null;
509
+ void prepareLogin();
510
+ }, recoveryDelay);
511
+ }
512
+
513
+ function retryableRequestError(error) {
514
+ return (
515
+ error?.name === "TypeError" ||
516
+ error?.name === "AbortError" ||
517
+ [408, 425, 429, 500, 502, 503, 504].includes(Number(error?.status || 0))
518
+ );
519
+ }
520
+
521
+ async function postJson(url, body, { retryDelays = [0] } = {}) {
522
+ let lastError = null;
523
+ for (let attempt = 0; attempt < retryDelays.length; attempt += 1) {
524
+ if (retryDelays[attempt] > 0) await delay(retryDelays[attempt]);
525
+ const controller = new AbortController();
526
+ const timer = window.setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
527
+ try {
528
+ const response = await fetch(url, {
529
+ method: "POST",
530
+ mode: "cors",
531
+ credentials: "omit",
532
+ headers: { "content-type": "application/json" },
533
+ body: JSON.stringify(body),
534
+ signal: controller.signal,
535
+ });
536
+ const payload = await response.json().catch(() => ({}));
537
+ if (!response.ok && response.status !== 202) {
538
+ const error = new Error(payload?.error || `Request failed (${response.status})`);
539
+ error.status = response.status;
540
+ throw error;
541
+ }
542
+ return payload;
543
+ } catch (error) {
544
+ lastError = error;
545
+ if (!retryableRequestError(error) || attempt === retryDelays.length - 1) throw error;
546
+ } finally {
547
+ window.clearTimeout(timer);
548
+ }
549
  }
550
+ throw lastError || new Error("Request failed");
551
  }
552
 
553
  function announceAuthState() {
 
630
 
631
  async function prepareLogin() {
632
  if (oauthResult || activeFlow) return;
633
+ // Authentication is intentionally available only in the two official
634
+ // deployments. A fork can copy our declared space_repo value, but it cannot
635
+ // claim an official browser origin and must never generate misleading auth
636
+ // failures or receive a trusted handoff.
637
+ if (
638
+ window.location.hostname.endsWith(".hf.space") &&
639
+ !OFFICIAL_SPACE_HOSTS.has(window.location.hostname.toLowerCase())
640
+ ) return;
641
  if (
642
  preparedFlow &&
643
  Date.now() - preparedFlow.preparedAt < PREPARE_REFRESH_MS
 
650
  startedAt: 0,
651
  loginUrl: "",
652
  };
653
+ preparePromise = postJson(
654
+ START_URL,
655
+ { flow_id: flow.id, flow_secret: flow.secret },
656
+ { retryDelays: PREPARE_RETRY_DELAYS_MS }
657
+ ).then((result) => {
658
  const loginUrl = new URL(String(result?.login_url || ""));
659
  if (
660
  loginUrl.origin !== "https://huggingface.co" ||
 
662
  ) throw new Error("The OAuth login URL is invalid");
663
  flow.loginUrl = loginUrl.toString();
664
  preparedFlow = flow;
665
+ prepareFailureCount = 0;
666
+ if (prepareRecoveryTimer) window.clearTimeout(prepareRecoveryTimer);
667
+ prepareRecoveryTimer = null;
668
  if (!activeFlow) renderAuth();
669
  refreshNativeLoginPopover();
670
  }).catch((error) => {
 
673
  component: "space_nav",
674
  failure_stage: "login_prepare",
675
  error_name: String(error?.name || "Error").slice(0, 80),
676
+ error_message: String(error?.message || "Unknown error").slice(0, 240),
677
+ request_status: Number(error?.status || 0) || undefined,
678
+ retry_count: PREPARE_RETRY_DELAYS_MS.length - 1,
679
  });
680
+ schedulePrepareRecovery();
681
  }).finally(() => {
682
  preparePromise = null;
683
  });