@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.d.cts CHANGED
@@ -110,6 +110,13 @@ declare class Authon {
110
110
  updateMemberRole: (orgId: string, memberId: string, role: string) => Promise<OrganizationMember>;
111
111
  leave: (orgId: string) => Promise<void>;
112
112
  };
113
+ /** Testing utilities — only available when initialized with a pk_test_ key */
114
+ get testing(): {
115
+ signIn(params: {
116
+ email: string;
117
+ nickname?: string;
118
+ }): Promise<AuthonUser>;
119
+ } | undefined;
113
120
  destroy(): void;
114
121
  private loadTurnstileScript;
115
122
  private emit;
package/dist/index.d.ts CHANGED
@@ -110,6 +110,13 @@ declare class Authon {
110
110
  updateMemberRole: (orgId: string, memberId: string, role: string) => Promise<OrganizationMember>;
111
111
  leave: (orgId: string) => Promise<void>;
112
112
  };
113
+ /** Testing utilities — only available when initialized with a pk_test_ key */
114
+ get testing(): {
115
+ signIn(params: {
116
+ email: string;
117
+ nickname?: string;
118
+ }): Promise<AuthonUser>;
119
+ } | undefined;
113
120
  destroy(): void;
114
121
  private loadTurnstileScript;
115
122
  private emit;
package/dist/index.js CHANGED
@@ -100,6 +100,7 @@ var ModalRenderer = class {
100
100
  shadowRoot = null;
101
101
  hostElement = null;
102
102
  containerElement = null;
103
+ containerId = null;
103
104
  mode;
104
105
  theme;
105
106
  branding;
@@ -142,9 +143,20 @@ var ModalRenderer = class {
142
143
  this.onPasskeyClick = options.onPasskeyClick || (() => {
143
144
  });
144
145
  if (options.mode === "embedded" && options.containerId) {
145
- this.containerElement = document.getElementById(options.containerId);
146
+ this.containerId = options.containerId;
146
147
  }
147
148
  }
149
+ resolveContainerElement() {
150
+ if (this.mode !== "embedded" || !this.containerId) return null;
151
+ const next = document.getElementById(this.containerId);
152
+ if (this.containerElement !== next) {
153
+ this.hostElement?.remove();
154
+ this.hostElement = null;
155
+ this.shadowRoot = null;
156
+ }
157
+ this.containerElement = next;
158
+ return next;
159
+ }
148
160
  setProviders(providers) {
149
161
  this.enabledProviders = providers;
150
162
  }
@@ -152,6 +164,11 @@ var ModalRenderer = class {
152
164
  this.branding = { ...DEFAULT_BRANDING, ...branding };
153
165
  }
154
166
  open(view = "signIn") {
167
+ this.resolveContainerElement();
168
+ if (this.hostElement && !this.hostElement.isConnected) {
169
+ this.hostElement = null;
170
+ this.shadowRoot = null;
171
+ }
155
172
  if (this.shadowRoot && this.hostElement) {
156
173
  this.hideOverlay();
157
174
  this.switchView(view);
@@ -181,8 +198,9 @@ var ModalRenderer = class {
181
198
  this.hostElement = null;
182
199
  this.shadowRoot = null;
183
200
  }
184
- if (this.containerElement) {
185
- this.containerElement.innerHTML = "";
201
+ const liveContainer = this.resolveContainerElement();
202
+ if (liveContainer) {
203
+ liveContainer.replaceChildren();
186
204
  }
187
205
  this.currentOverlay = "none";
188
206
  }
@@ -348,8 +366,14 @@ var ModalRenderer = class {
348
366
  this.hostElement = host;
349
367
  if (this.mode === "popup") {
350
368
  document.body.appendChild(host);
351
- } else if (this.containerElement) {
352
- this.containerElement.appendChild(host);
369
+ } else {
370
+ const container = this.resolveContainerElement();
371
+ if (!container) {
372
+ this.hostElement = null;
373
+ throw new Error(`Authon container "#${this.containerId}" not found`);
374
+ }
375
+ container.replaceChildren();
376
+ container.appendChild(host);
353
377
  }
354
378
  this.shadowRoot = host.attachShadow({ mode: "open" });
355
379
  this.shadowRoot.innerHTML = this.buildShell(view);
@@ -393,12 +417,10 @@ var ModalRenderer = class {
393
417
  </button>`;
394
418
  }).join("") : "";
395
419
  const divider = showProviders && b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
396
- const captchaContainer = this.captchaSiteKey ? '<div id="turnstile-container" style="display:flex;justify-content:center;margin:4px 0"></div>' : "";
397
420
  const emailForm = b.showEmailPassword !== false ? `<form class="email-form" id="email-form">
398
421
  <input type="email" placeholder="Email address" name="email" required class="input" autocomplete="email" />
399
422
  <input type="password" placeholder="Password" name="password" required class="input" autocomplete="${isSignUp ? "new-password" : "current-password"}" />
400
423
  ${isSignUp ? '<p class="password-hint">Must contain uppercase, lowercase, and a number (min 8 chars)</p>' : ""}
401
- ${captchaContainer}
402
424
  <button type="submit" class="submit-btn">${isSignUp ? "Sign up" : "Sign in"}</button>
403
425
  </form>` : "";
404
426
  const hasMethodAbove = showProviders && this.enabledProviders.length > 0 || b.showEmailPassword !== false;
@@ -456,28 +478,13 @@ var ModalRenderer = class {
456
478
  `;
457
479
  }
458
480
  renderTurnstile() {
459
- if (!this.captchaSiteKey || !this.shadowRoot) return;
460
- const anchor = this.shadowRoot.getElementById("turnstile-container");
461
- if (!anchor) return;
481
+ if (!this.captchaSiteKey) return;
462
482
  const w = window;
463
- const positionWrapper = () => {
464
- if (!this.turnstileWrapper || !anchor.isConnected) return;
465
- const rect = anchor.getBoundingClientRect();
466
- Object.assign(this.turnstileWrapper.style, {
467
- position: "fixed",
468
- top: `${rect.top}px`,
469
- left: `${rect.left}px`,
470
- width: `${rect.width}px`,
471
- zIndex: "2147483647",
472
- display: "flex",
473
- justifyContent: "center"
474
- });
475
- };
476
483
  const tryRender = () => {
477
- if (!w.turnstile || !anchor.isConnected) return;
484
+ if (!w.turnstile) return;
478
485
  this.turnstileWrapper = document.createElement("div");
486
+ this.turnstileWrapper.style.cssText = "position:fixed;bottom:10px;right:10px;z-index:2147483647;";
479
487
  document.body.appendChild(this.turnstileWrapper);
480
- positionWrapper();
481
488
  this.turnstileWidgetId = w.turnstile.render(this.turnstileWrapper, {
482
489
  sitekey: this.captchaSiteKey,
483
490
  callback: (token) => {
@@ -490,10 +497,8 @@ var ModalRenderer = class {
490
497
  this.turnstileToken = "";
491
498
  },
492
499
  theme: this.isDark() ? "dark" : "light",
493
- size: "flexible"
500
+ appearance: "interaction-only"
494
501
  });
495
- window.addEventListener("scroll", positionWrapper, { passive: true });
496
- window.addEventListener("resize", positionWrapper, { passive: true });
497
502
  };
498
503
  if (w.turnstile) {
499
504
  tryRender();
@@ -1126,9 +1131,42 @@ var SessionManager = class {
1126
1131
  refreshTimer = null;
1127
1132
  apiUrl;
1128
1133
  publishableKey;
1134
+ storageKey;
1129
1135
  constructor(publishableKey, apiUrl) {
1130
1136
  this.publishableKey = publishableKey;
1131
1137
  this.apiUrl = apiUrl;
1138
+ this.storageKey = `authon_session_${publishableKey.slice(0, 16)}`;
1139
+ this.restoreFromStorage();
1140
+ }
1141
+ restoreFromStorage() {
1142
+ if (typeof window === "undefined") return;
1143
+ try {
1144
+ const stored = localStorage.getItem(this.storageKey);
1145
+ if (!stored) return;
1146
+ const data = JSON.parse(stored);
1147
+ if (data.accessToken && data.refreshToken && data.user) {
1148
+ this.accessToken = data.accessToken;
1149
+ this.refreshToken = data.refreshToken;
1150
+ this.user = data.user;
1151
+ this.scheduleRefresh(5);
1152
+ }
1153
+ } catch {
1154
+ }
1155
+ }
1156
+ persistToStorage() {
1157
+ if (typeof window === "undefined") return;
1158
+ try {
1159
+ if (this.accessToken && this.refreshToken && this.user) {
1160
+ localStorage.setItem(this.storageKey, JSON.stringify({
1161
+ accessToken: this.accessToken,
1162
+ refreshToken: this.refreshToken,
1163
+ user: this.user
1164
+ }));
1165
+ } else {
1166
+ localStorage.removeItem(this.storageKey);
1167
+ }
1168
+ } catch {
1169
+ }
1132
1170
  }
1133
1171
  getToken() {
1134
1172
  return this.accessToken;
@@ -1140,6 +1178,7 @@ var SessionManager = class {
1140
1178
  this.accessToken = tokens.accessToken;
1141
1179
  this.refreshToken = tokens.refreshToken;
1142
1180
  this.user = tokens.user;
1181
+ this.persistToStorage();
1143
1182
  if (tokens.expiresIn && tokens.expiresIn > 0) {
1144
1183
  this.scheduleRefresh(tokens.expiresIn);
1145
1184
  }
@@ -1151,6 +1190,7 @@ var SessionManager = class {
1151
1190
  this.accessToken = null;
1152
1191
  this.refreshToken = null;
1153
1192
  this.user = null;
1193
+ this.persistToStorage();
1154
1194
  if (this.refreshTimer) {
1155
1195
  clearTimeout(this.refreshTimer);
1156
1196
  this.refreshTimer = null;
@@ -1946,6 +1986,18 @@ var Authon = class {
1946
1986
  await this.apiPostAuth(`/v1/auth/organizations/${orgId}/leave`, void 0, token);
1947
1987
  }
1948
1988
  };
1989
+ /** Testing utilities — only available when initialized with a pk_test_ key */
1990
+ get testing() {
1991
+ if (!this.publishableKey.startsWith("pk_test_")) return void 0;
1992
+ return {
1993
+ signIn: async (params) => {
1994
+ const res = await this.apiPost("/v1/auth/testing/token", params);
1995
+ this.session.setSession(res);
1996
+ this.emit("signedIn", res.user);
1997
+ return res.user;
1998
+ }
1999
+ };
2000
+ }
1949
2001
  destroy() {
1950
2002
  this.modal?.close();
1951
2003
  this.session.destroy();