@unhingged/vizu-core 0.1.0 → 0.1.1

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/dist/auto.js CHANGED
@@ -342,10 +342,28 @@ var CloudStorageAdapter = class {
342
342
  this.cachedToken = null;
343
343
  /** Single-flight: at most one popup at a time. Subsequent requests await this. */
344
344
  this.pendingAuth = null;
345
+ /** Whether we've already fired onAuthChanged for the current token. */
346
+ this.lastNotifiedTokenId = null;
345
347
  this.workspace = opts.workspace;
346
348
  this.apiUrl = (opts.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
347
349
  this.autoSignIn = opts.autoSignIn !== false;
350
+ this.onAuthChanged = opts.onAuthChanged;
348
351
  this.cachedToken = this.readStoredToken();
352
+ if (this.cachedToken && !this.isExpired(this.cachedToken)) {
353
+ this.fireAuthChanged(this.cachedToken);
354
+ }
355
+ }
356
+ /**
357
+ * Open the sign-in popup right now (or no-op if a valid token is
358
+ * cached). Vizu calls this on enable() so the user signs in BEFORE
359
+ * trying to leave a comment — no surprise modal mid-comment.
360
+ *
361
+ * Throws auth_canceled if the user closes the popup. Caller can
362
+ * decide whether to disable the surface or leave it in a read-only
363
+ * state.
364
+ */
365
+ async preflightAuth() {
366
+ await this.resolveToken();
349
367
  }
350
368
  /* ─── StorageAdapter v2 ───────────────────────────────────────────── */
351
369
  async load(_namespace) {
@@ -534,6 +552,7 @@ var CloudStorageAdapter = class {
534
552
  const fresh = this.readStoredToken();
535
553
  if (fresh) {
536
554
  this.cachedToken = fresh;
555
+ this.fireAuthChanged(fresh);
537
556
  return fresh;
538
557
  }
539
558
  if (!this.autoSignIn) {
@@ -542,6 +561,7 @@ var CloudStorageAdapter = class {
542
561
  if (!this.pendingAuth) {
543
562
  this.pendingAuth = this.openSignInPopup().then((t) => {
544
563
  this.writeStoredToken(t);
564
+ this.fireAuthChanged(t);
545
565
  return t;
546
566
  }).finally(() => {
547
567
  this.pendingAuth = null;
@@ -549,6 +569,22 @@ var CloudStorageAdapter = class {
549
569
  }
550
570
  return this.pendingAuth;
551
571
  }
572
+ /**
573
+ * Notify the host that the authenticated identity changed. De-duped
574
+ * via the token string so multiple resolveToken() hits with the same
575
+ * cached token don't re-call setUser on every fetch.
576
+ */
577
+ fireAuthChanged(token) {
578
+ if (this.lastNotifiedTokenId === token.token) return;
579
+ this.lastNotifiedTokenId = token.token;
580
+ try {
581
+ this.onAuthChanged?.({ userId: token.userId, user: token.user });
582
+ } catch (err) {
583
+ if (typeof console !== "undefined") {
584
+ console.warn("[vizu/cloud] onAuthChanged callback threw", err);
585
+ }
586
+ }
587
+ }
552
588
  openSignInPopup() {
553
589
  return new Promise((resolve, reject) => {
554
590
  if (typeof window === "undefined") {
@@ -581,7 +617,13 @@ var CloudStorageAdapter = class {
581
617
  if (typeof data.token !== "string" || typeof data.expiresAt !== "string" || typeof data.userId !== "string") return;
582
618
  resolved = true;
583
619
  cleanup();
584
- resolve({ token: data.token, expiresAt: data.expiresAt, userId: data.userId });
620
+ const userPayload = data.user;
621
+ const user = userPayload && typeof userPayload.name === "string" ? {
622
+ id: typeof userPayload.id === "string" ? userPayload.id : data.userId,
623
+ name: userPayload.name,
624
+ avatarUrl: typeof userPayload.avatarUrl === "string" ? userPayload.avatarUrl : void 0
625
+ } : null;
626
+ resolve({ token: data.token, expiresAt: data.expiresAt, userId: data.userId, user });
585
627
  };
586
628
  const onPoll = window.setInterval(() => {
587
629
  if (popup.closed) {
@@ -2676,7 +2718,7 @@ var DEFAULTS = {
2676
2718
  namespace: "default",
2677
2719
  accent: "#FF6647"
2678
2720
  };
2679
- function resolveStorage(storage, cloud, defaultMode) {
2721
+ function resolveStorage(storage, cloud, defaultMode, onAuthChanged) {
2680
2722
  if (cloud) {
2681
2723
  if (storage && typeof console !== "undefined") {
2682
2724
  console.warn("[vizu] Both `cloud` and `storage` set; `cloud` wins. Drop `storage` to silence this.");
@@ -2684,7 +2726,8 @@ function resolveStorage(storage, cloud, defaultMode) {
2684
2726
  return new CloudStorageAdapter({
2685
2727
  workspace: cloud.workspace,
2686
2728
  apiUrl: cloud.apiUrl,
2687
- autoSignIn: cloud.autoSignIn
2729
+ autoSignIn: cloud.autoSignIn,
2730
+ onAuthChanged
2688
2731
  });
2689
2732
  }
2690
2733
  if (!storage) return defaultMode === "local" ? new LocalStorageAdapter() : new InMemoryStorageAdapter();
@@ -2790,7 +2833,14 @@ var Vizu = class {
2790
2833
  this.opts.namespace = options.namespace ?? DEFAULTS.namespace;
2791
2834
  this.opts.accent = options.accent ?? DEFAULTS.accent;
2792
2835
  this.parsedShortcut = parseShortcut(this.opts.shortcut);
2793
- this.storage = resolveStorage(options.storage, options.cloud, _defaultStorage);
2836
+ this.storage = resolveStorage(
2837
+ options.storage,
2838
+ options.cloud,
2839
+ _defaultStorage,
2840
+ (info) => {
2841
+ if (info.user) this.setUser(info.user);
2842
+ }
2843
+ );
2794
2844
  this.user = options.user ?? null;
2795
2845
  if (options.actions) this.actions = [...options.actions];
2796
2846
  if (options.onCommentAdded) this.bus.on("comment:added", (p) => options.onCommentAdded(p.comment));
@@ -2836,6 +2886,14 @@ var Vizu = class {
2836
2886
  this.enabled = true;
2837
2887
  this.bus.emit("enabled", {});
2838
2888
  this.deferred(() => this.mount());
2889
+ if (this.storage instanceof CloudStorageAdapter) {
2890
+ void this.storage.preflightAuth().catch((err) => {
2891
+ if (err?.code === "auth_canceled" || err?.code === "popup_blocked") return;
2892
+ if (typeof console !== "undefined") {
2893
+ console.warn("[vizu] preflight auth failed:", err);
2894
+ }
2895
+ });
2896
+ }
2839
2897
  }
2840
2898
  disable() {
2841
2899
  if (!this.enabled) return;