@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/index.cjs CHANGED
@@ -1724,10 +1724,28 @@ var CloudStorageAdapter = class {
1724
1724
  this.cachedToken = null;
1725
1725
  /** Single-flight: at most one popup at a time. Subsequent requests await this. */
1726
1726
  this.pendingAuth = null;
1727
+ /** Whether we've already fired onAuthChanged for the current token. */
1728
+ this.lastNotifiedTokenId = null;
1727
1729
  this.workspace = opts.workspace;
1728
1730
  this.apiUrl = (opts.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
1729
1731
  this.autoSignIn = opts.autoSignIn !== false;
1732
+ this.onAuthChanged = opts.onAuthChanged;
1730
1733
  this.cachedToken = this.readStoredToken();
1734
+ if (this.cachedToken && !this.isExpired(this.cachedToken)) {
1735
+ this.fireAuthChanged(this.cachedToken);
1736
+ }
1737
+ }
1738
+ /**
1739
+ * Open the sign-in popup right now (or no-op if a valid token is
1740
+ * cached). Vizu calls this on enable() so the user signs in BEFORE
1741
+ * trying to leave a comment — no surprise modal mid-comment.
1742
+ *
1743
+ * Throws auth_canceled if the user closes the popup. Caller can
1744
+ * decide whether to disable the surface or leave it in a read-only
1745
+ * state.
1746
+ */
1747
+ async preflightAuth() {
1748
+ await this.resolveToken();
1731
1749
  }
1732
1750
  /* ─── StorageAdapter v2 ───────────────────────────────────────────── */
1733
1751
  async load(_namespace) {
@@ -1916,6 +1934,7 @@ var CloudStorageAdapter = class {
1916
1934
  const fresh = this.readStoredToken();
1917
1935
  if (fresh) {
1918
1936
  this.cachedToken = fresh;
1937
+ this.fireAuthChanged(fresh);
1919
1938
  return fresh;
1920
1939
  }
1921
1940
  if (!this.autoSignIn) {
@@ -1924,6 +1943,7 @@ var CloudStorageAdapter = class {
1924
1943
  if (!this.pendingAuth) {
1925
1944
  this.pendingAuth = this.openSignInPopup().then((t) => {
1926
1945
  this.writeStoredToken(t);
1946
+ this.fireAuthChanged(t);
1927
1947
  return t;
1928
1948
  }).finally(() => {
1929
1949
  this.pendingAuth = null;
@@ -1931,6 +1951,22 @@ var CloudStorageAdapter = class {
1931
1951
  }
1932
1952
  return this.pendingAuth;
1933
1953
  }
1954
+ /**
1955
+ * Notify the host that the authenticated identity changed. De-duped
1956
+ * via the token string so multiple resolveToken() hits with the same
1957
+ * cached token don't re-call setUser on every fetch.
1958
+ */
1959
+ fireAuthChanged(token) {
1960
+ if (this.lastNotifiedTokenId === token.token) return;
1961
+ this.lastNotifiedTokenId = token.token;
1962
+ try {
1963
+ this.onAuthChanged?.({ userId: token.userId, user: token.user });
1964
+ } catch (err) {
1965
+ if (typeof console !== "undefined") {
1966
+ console.warn("[vizu/cloud] onAuthChanged callback threw", err);
1967
+ }
1968
+ }
1969
+ }
1934
1970
  openSignInPopup() {
1935
1971
  return new Promise((resolve, reject) => {
1936
1972
  if (typeof window === "undefined") {
@@ -1963,7 +1999,13 @@ var CloudStorageAdapter = class {
1963
1999
  if (typeof data.token !== "string" || typeof data.expiresAt !== "string" || typeof data.userId !== "string") return;
1964
2000
  resolved = true;
1965
2001
  cleanup();
1966
- resolve({ token: data.token, expiresAt: data.expiresAt, userId: data.userId });
2002
+ const userPayload = data.user;
2003
+ const user = userPayload && typeof userPayload.name === "string" ? {
2004
+ id: typeof userPayload.id === "string" ? userPayload.id : data.userId,
2005
+ name: userPayload.name,
2006
+ avatarUrl: typeof userPayload.avatarUrl === "string" ? userPayload.avatarUrl : void 0
2007
+ } : null;
2008
+ resolve({ token: data.token, expiresAt: data.expiresAt, userId: data.userId, user });
1967
2009
  };
1968
2010
  const onPoll = window.setInterval(() => {
1969
2011
  if (popup.closed) {
@@ -4064,7 +4106,7 @@ var DEFAULTS = {
4064
4106
  namespace: "default",
4065
4107
  accent: "#FF6647"
4066
4108
  };
4067
- function resolveStorage(storage, cloud, defaultMode) {
4109
+ function resolveStorage(storage, cloud, defaultMode, onAuthChanged) {
4068
4110
  if (cloud) {
4069
4111
  if (storage && typeof console !== "undefined") {
4070
4112
  console.warn("[vizu] Both `cloud` and `storage` set; `cloud` wins. Drop `storage` to silence this.");
@@ -4072,7 +4114,8 @@ function resolveStorage(storage, cloud, defaultMode) {
4072
4114
  return new CloudStorageAdapter({
4073
4115
  workspace: cloud.workspace,
4074
4116
  apiUrl: cloud.apiUrl,
4075
- autoSignIn: cloud.autoSignIn
4117
+ autoSignIn: cloud.autoSignIn,
4118
+ onAuthChanged
4076
4119
  });
4077
4120
  }
4078
4121
  if (!storage) return defaultMode === "local" ? new LocalStorageAdapter() : new InMemoryStorageAdapter();
@@ -4178,7 +4221,14 @@ var Vizu = class {
4178
4221
  this.opts.namespace = options.namespace ?? DEFAULTS.namespace;
4179
4222
  this.opts.accent = options.accent ?? DEFAULTS.accent;
4180
4223
  this.parsedShortcut = parseShortcut(this.opts.shortcut);
4181
- this.storage = resolveStorage(options.storage, options.cloud, _defaultStorage);
4224
+ this.storage = resolveStorage(
4225
+ options.storage,
4226
+ options.cloud,
4227
+ _defaultStorage,
4228
+ (info) => {
4229
+ if (info.user) this.setUser(info.user);
4230
+ }
4231
+ );
4182
4232
  this.user = options.user ?? null;
4183
4233
  if (options.actions) this.actions = [...options.actions];
4184
4234
  if (options.onCommentAdded) this.bus.on("comment:added", (p) => options.onCommentAdded(p.comment));
@@ -4224,6 +4274,14 @@ var Vizu = class {
4224
4274
  this.enabled = true;
4225
4275
  this.bus.emit("enabled", {});
4226
4276
  this.deferred(() => this.mount());
4277
+ if (this.storage instanceof CloudStorageAdapter) {
4278
+ void this.storage.preflightAuth().catch((err) => {
4279
+ if (err?.code === "auth_canceled" || err?.code === "popup_blocked") return;
4280
+ if (typeof console !== "undefined") {
4281
+ console.warn("[vizu] preflight auth failed:", err);
4282
+ }
4283
+ });
4284
+ }
4227
4285
  }
4228
4286
  disable() {
4229
4287
  if (!this.enabled) return;