@authon/js 0.3.4 → 0.4.0

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
@@ -129,6 +129,7 @@ var ModalRenderer = class {
129
129
  shadowRoot = null;
130
130
  hostElement = null;
131
131
  containerElement = null;
132
+ containerId = null;
132
133
  mode;
133
134
  theme;
134
135
  branding;
@@ -171,9 +172,20 @@ var ModalRenderer = class {
171
172
  this.onPasskeyClick = options.onPasskeyClick || (() => {
172
173
  });
173
174
  if (options.mode === "embedded" && options.containerId) {
174
- this.containerElement = document.getElementById(options.containerId);
175
+ this.containerId = options.containerId;
175
176
  }
176
177
  }
178
+ resolveContainerElement() {
179
+ if (this.mode !== "embedded" || !this.containerId) return null;
180
+ const next = document.getElementById(this.containerId);
181
+ if (this.containerElement !== next) {
182
+ this.hostElement?.remove();
183
+ this.hostElement = null;
184
+ this.shadowRoot = null;
185
+ }
186
+ this.containerElement = next;
187
+ return next;
188
+ }
177
189
  setProviders(providers) {
178
190
  this.enabledProviders = providers;
179
191
  }
@@ -181,6 +193,11 @@ var ModalRenderer = class {
181
193
  this.branding = { ...DEFAULT_BRANDING, ...branding };
182
194
  }
183
195
  open(view = "signIn") {
196
+ this.resolveContainerElement();
197
+ if (this.hostElement && !this.hostElement.isConnected) {
198
+ this.hostElement = null;
199
+ this.shadowRoot = null;
200
+ }
184
201
  if (this.shadowRoot && this.hostElement) {
185
202
  this.hideOverlay();
186
203
  this.switchView(view);
@@ -210,8 +227,9 @@ var ModalRenderer = class {
210
227
  this.hostElement = null;
211
228
  this.shadowRoot = null;
212
229
  }
213
- if (this.containerElement) {
214
- this.containerElement.innerHTML = "";
230
+ const liveContainer = this.resolveContainerElement();
231
+ if (liveContainer) {
232
+ liveContainer.replaceChildren();
215
233
  }
216
234
  this.currentOverlay = "none";
217
235
  }
@@ -377,8 +395,14 @@ var ModalRenderer = class {
377
395
  this.hostElement = host;
378
396
  if (this.mode === "popup") {
379
397
  document.body.appendChild(host);
380
- } else if (this.containerElement) {
381
- this.containerElement.appendChild(host);
398
+ } else {
399
+ const container = this.resolveContainerElement();
400
+ if (!container) {
401
+ this.hostElement = null;
402
+ throw new Error(`Authon container "#${this.containerId}" not found`);
403
+ }
404
+ container.replaceChildren();
405
+ container.appendChild(host);
382
406
  }
383
407
  this.shadowRoot = host.attachShadow({ mode: "open" });
384
408
  this.shadowRoot.innerHTML = this.buildShell(view);
@@ -422,12 +446,10 @@ var ModalRenderer = class {
422
446
  </button>`;
423
447
  }).join("") : "";
424
448
  const divider = showProviders && b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
425
- const captchaContainer = this.captchaSiteKey ? '<div id="turnstile-container" style="display:flex;justify-content:center;margin:4px 0"></div>' : "";
426
449
  const emailForm = b.showEmailPassword !== false ? `<form class="email-form" id="email-form">
427
450
  <input type="email" placeholder="Email address" name="email" required class="input" autocomplete="email" />
428
451
  <input type="password" placeholder="Password" name="password" required class="input" autocomplete="${isSignUp ? "new-password" : "current-password"}" />
429
452
  ${isSignUp ? '<p class="password-hint">Must contain uppercase, lowercase, and a number (min 8 chars)</p>' : ""}
430
- ${captchaContainer}
431
453
  <button type="submit" class="submit-btn">${isSignUp ? "Sign up" : "Sign in"}</button>
432
454
  </form>` : "";
433
455
  const hasMethodAbove = showProviders && this.enabledProviders.length > 0 || b.showEmailPassword !== false;
@@ -485,28 +507,13 @@ var ModalRenderer = class {
485
507
  `;
486
508
  }
487
509
  renderTurnstile() {
488
- if (!this.captchaSiteKey || !this.shadowRoot) return;
489
- const anchor = this.shadowRoot.getElementById("turnstile-container");
490
- if (!anchor) return;
510
+ if (!this.captchaSiteKey) return;
491
511
  const w = window;
492
- const positionWrapper = () => {
493
- if (!this.turnstileWrapper || !anchor.isConnected) return;
494
- const rect = anchor.getBoundingClientRect();
495
- Object.assign(this.turnstileWrapper.style, {
496
- position: "fixed",
497
- top: `${rect.top}px`,
498
- left: `${rect.left}px`,
499
- width: `${rect.width}px`,
500
- zIndex: "2147483647",
501
- display: "flex",
502
- justifyContent: "center"
503
- });
504
- };
505
512
  const tryRender = () => {
506
- if (!w.turnstile || !anchor.isConnected) return;
513
+ if (!w.turnstile) return;
507
514
  this.turnstileWrapper = document.createElement("div");
515
+ this.turnstileWrapper.style.cssText = "position:fixed;bottom:10px;right:10px;z-index:2147483647;";
508
516
  document.body.appendChild(this.turnstileWrapper);
509
- positionWrapper();
510
517
  this.turnstileWidgetId = w.turnstile.render(this.turnstileWrapper, {
511
518
  sitekey: this.captchaSiteKey,
512
519
  callback: (token) => {
@@ -519,10 +526,8 @@ var ModalRenderer = class {
519
526
  this.turnstileToken = "";
520
527
  },
521
528
  theme: this.isDark() ? "dark" : "light",
522
- size: "flexible"
529
+ appearance: "interaction-only"
523
530
  });
524
- window.addEventListener("scroll", positionWrapper, { passive: true });
525
- window.addEventListener("resize", positionWrapper, { passive: true });
526
531
  };
527
532
  if (w.turnstile) {
528
533
  tryRender();
@@ -1155,9 +1160,42 @@ var SessionManager = class {
1155
1160
  refreshTimer = null;
1156
1161
  apiUrl;
1157
1162
  publishableKey;
1163
+ storageKey;
1158
1164
  constructor(publishableKey, apiUrl) {
1159
1165
  this.publishableKey = publishableKey;
1160
1166
  this.apiUrl = apiUrl;
1167
+ this.storageKey = `authon_session_${publishableKey.slice(0, 16)}`;
1168
+ this.restoreFromStorage();
1169
+ }
1170
+ restoreFromStorage() {
1171
+ if (typeof window === "undefined") return;
1172
+ try {
1173
+ const stored = localStorage.getItem(this.storageKey);
1174
+ if (!stored) return;
1175
+ const data = JSON.parse(stored);
1176
+ if (data.accessToken && data.refreshToken && data.user) {
1177
+ this.accessToken = data.accessToken;
1178
+ this.refreshToken = data.refreshToken;
1179
+ this.user = data.user;
1180
+ this.scheduleRefresh(5);
1181
+ }
1182
+ } catch {
1183
+ }
1184
+ }
1185
+ persistToStorage() {
1186
+ if (typeof window === "undefined") return;
1187
+ try {
1188
+ if (this.accessToken && this.refreshToken && this.user) {
1189
+ localStorage.setItem(this.storageKey, JSON.stringify({
1190
+ accessToken: this.accessToken,
1191
+ refreshToken: this.refreshToken,
1192
+ user: this.user
1193
+ }));
1194
+ } else {
1195
+ localStorage.removeItem(this.storageKey);
1196
+ }
1197
+ } catch {
1198
+ }
1161
1199
  }
1162
1200
  getToken() {
1163
1201
  return this.accessToken;
@@ -1169,6 +1207,7 @@ var SessionManager = class {
1169
1207
  this.accessToken = tokens.accessToken;
1170
1208
  this.refreshToken = tokens.refreshToken;
1171
1209
  this.user = tokens.user;
1210
+ this.persistToStorage();
1172
1211
  if (tokens.expiresIn && tokens.expiresIn > 0) {
1173
1212
  this.scheduleRefresh(tokens.expiresIn);
1174
1213
  }
@@ -1180,6 +1219,7 @@ var SessionManager = class {
1180
1219
  this.accessToken = null;
1181
1220
  this.refreshToken = null;
1182
1221
  this.user = null;
1222
+ this.persistToStorage();
1183
1223
  if (this.refreshTimer) {
1184
1224
  clearTimeout(this.refreshTimer);
1185
1225
  this.refreshTimer = null;
@@ -1975,6 +2015,18 @@ var Authon = class {
1975
2015
  await this.apiPostAuth(`/v1/auth/organizations/${orgId}/leave`, void 0, token);
1976
2016
  }
1977
2017
  };
2018
+ /** Testing utilities — only available when initialized with a pk_test_ key */
2019
+ get testing() {
2020
+ if (!this.publishableKey.startsWith("pk_test_")) return void 0;
2021
+ return {
2022
+ signIn: async (params) => {
2023
+ const res = await this.apiPost("/v1/auth/testing/token", params);
2024
+ this.session.setSession(res);
2025
+ this.emit("signedIn", res.user);
2026
+ return res.user;
2027
+ }
2028
+ };
2029
+ }
1978
2030
  destroy() {
1979
2031
  this.modal?.close();
1980
2032
  this.session.destroy();