@authon/js 0.1.12 → 0.1.13

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.mts CHANGED
@@ -44,6 +44,7 @@ declare class Authon {
44
44
  private startOAuthFlow;
45
45
  private apiGet;
46
46
  private apiPost;
47
+ private parseApiError;
47
48
  }
48
49
 
49
50
  interface ProviderButtonConfig {
package/dist/index.d.ts CHANGED
@@ -44,6 +44,7 @@ declare class Authon {
44
44
  private startOAuthFlow;
45
45
  private apiGet;
46
46
  private apiPost;
47
+ private parseApiError;
47
48
  }
48
49
 
49
50
  interface ProviderButtonConfig {
package/dist/index.js CHANGED
@@ -245,20 +245,22 @@ var ModalRenderer = class {
245
245
  const subtitle = isSignUp ? "Already have an account?" : "Don't have an account?";
246
246
  const subtitleLink = isSignUp ? "Sign in" : "Sign up";
247
247
  const dark = this.isDark();
248
- const providerButtons = this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
248
+ const showProviders = !isSignUp;
249
+ const providerButtons = showProviders ? this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
249
250
  const config = getProviderButtonConfig(p);
250
251
  const isWhiteBg = config.bgColor === "#ffffff";
251
252
  const btnBg = dark && isWhiteBg ? "#f8fafc" : config.bgColor;
252
253
  const btnBorder = isWhiteBg ? dark ? "#475569" : "#e5e7eb" : config.bgColor;
253
254
  return `<button class="provider-btn" data-provider="${p}" style="background:${btnBg};color:${config.textColor};border:1px solid ${btnBorder}">
254
- <span class="provider-icon">${config.iconSvg}</span>
255
- <span>${config.label}</span>
256
- </button>`;
257
- }).join("");
258
- const divider = b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
255
+ <span class="provider-icon">${config.iconSvg}</span>
256
+ <span>${config.label}</span>
257
+ </button>`;
258
+ }).join("") : "";
259
+ const divider = showProviders && b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
259
260
  const emailForm = b.showEmailPassword !== false ? `<form class="email-form" id="email-form">
260
261
  <input type="email" placeholder="Email address" name="email" required class="input" autocomplete="email" />
261
262
  <input type="password" placeholder="Password" name="password" required class="input" autocomplete="${isSignUp ? "new-password" : "current-password"}" />
263
+ ${isSignUp ? '<p class="password-hint">Must contain uppercase, lowercase, and a number (min 8 chars)</p>' : ""}
262
264
  <button type="submit" class="submit-btn">${isSignUp ? "Sign up" : "Sign in"}</button>
263
265
  </form>` : "";
264
266
  const footer = b.termsUrl || b.privacyUrl ? `<div class="footer">
@@ -266,11 +268,17 @@ var ModalRenderer = class {
266
268
  ${b.termsUrl && b.privacyUrl ? " \xB7 " : ""}
267
269
  ${b.privacyUrl ? `<a href="${b.privacyUrl}" target="_blank">Privacy Policy</a>` : ""}
268
270
  </div>` : "";
271
+ const titleHtml = isSignUp ? `<div class="title-row">
272
+ <button class="back-btn" id="back-btn" type="button" aria-label="Back to sign in">
273
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>
274
+ </button>
275
+ <h2 class="title">${title}</h2>
276
+ </div>` : `<h2 class="title">${title}</h2>`;
269
277
  return `
270
278
  ${b.logoDataUrl ? `<img src="${b.logoDataUrl}" alt="Logo" class="logo" />` : ""}
271
- <h2 class="title">${title}</h2>
279
+ ${titleHtml}
272
280
  ${b.brandName ? `<p class="brand-name">${b.brandName}</p>` : ""}
273
- <div class="providers">${providerButtons}</div>
281
+ ${showProviders ? `<div class="providers">${providerButtons}</div>` : ""}
274
282
  ${divider}
275
283
  ${emailForm}
276
284
  <p class="switch-view">${subtitle} <a href="#" id="switch-link">${subtitleLink}</a></p>
@@ -330,6 +338,16 @@ var ModalRenderer = class {
330
338
  transition: opacity 0.14s ease, transform 0.14s ease;
331
339
  }
332
340
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
341
+ .title-row { display: flex; align-items: center; position: relative; margin-bottom: 8px; }
342
+ .title-row .title { flex: 1; margin-bottom: 0; }
343
+ .back-btn {
344
+ position: absolute; left: 0; top: 50%; transform: translateY(-50%);
345
+ background: none; border: none; color: var(--authon-muted);
346
+ cursor: pointer; padding: 4px; border-radius: 6px; display: flex; align-items: center; justify-content: center;
347
+ transition: color 0.15s, background 0.15s;
348
+ }
349
+ .back-btn:hover { color: var(--authon-text); background: var(--authon-divider); }
350
+ .password-hint { font-size: 11px; color: var(--authon-dim); margin: -4px 0 2px; }
333
351
  .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; color: var(--authon-text); }
334
352
  .brand-name { text-align: center; font-size: 14px; color: var(--authon-muted); margin-bottom: 24px; }
335
353
  .providers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
@@ -473,6 +491,12 @@ var ModalRenderer = class {
473
491
  );
474
492
  });
475
493
  }
494
+ const backBtn = this.shadowRoot.getElementById("back-btn");
495
+ if (backBtn) {
496
+ backBtn.addEventListener("click", () => {
497
+ this.open("signIn");
498
+ });
499
+ }
476
500
  const switchLink = this.shadowRoot.getElementById("switch-link");
477
501
  if (switchLink) {
478
502
  switchLink.addEventListener("click", (e) => {
@@ -674,8 +698,7 @@ var Authon = class {
674
698
  const promise = isSignUp ? this.signUpWithEmail(email, password) : this.signInWithEmail(email, password);
675
699
  promise.then(() => this.modal?.close()).catch((err) => {
676
700
  const msg = err instanceof Error ? err.message : String(err);
677
- const friendlyMsg = msg.includes("401") ? "Invalid email or password" : msg.includes("409") ? "Email already in use" : msg.includes("400") ? "Please check your input" : msg || "Authentication failed";
678
- this.modal?.showError(friendlyMsg);
701
+ this.modal?.showError(msg || "Authentication failed");
679
702
  this.emit("error", err instanceof Error ? err : new Error(msg));
680
703
  });
681
704
  },
@@ -795,7 +818,7 @@ var Authon = class {
795
818
  headers: { "x-api-key": this.publishableKey },
796
819
  credentials: "include"
797
820
  });
798
- if (!res.ok) throw new Error(`API ${path}: ${res.status}`);
821
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
799
822
  return res.json();
800
823
  }
801
824
  async apiPost(path, body) {
@@ -808,9 +831,22 @@ var Authon = class {
808
831
  credentials: "include",
809
832
  body: body ? JSON.stringify(body) : void 0
810
833
  });
811
- if (!res.ok) throw new Error(`API ${path}: ${res.status}`);
834
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
812
835
  return res.json();
813
836
  }
837
+ async parseApiError(res, path) {
838
+ try {
839
+ const body = await res.json();
840
+ if (Array.isArray(body.message) && body.message.length > 0) {
841
+ return body.message[0];
842
+ }
843
+ if (typeof body.message === "string" && body.message !== "Bad Request") {
844
+ return body.message;
845
+ }
846
+ } catch {
847
+ }
848
+ return `API ${path}: ${res.status}`;
849
+ }
814
850
  };
815
851
  // Annotate the CommonJS export names for ESM import in node:
816
852
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -218,20 +218,22 @@ var ModalRenderer = class {
218
218
  const subtitle = isSignUp ? "Already have an account?" : "Don't have an account?";
219
219
  const subtitleLink = isSignUp ? "Sign in" : "Sign up";
220
220
  const dark = this.isDark();
221
- const providerButtons = this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
221
+ const showProviders = !isSignUp;
222
+ const providerButtons = showProviders ? this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
222
223
  const config = getProviderButtonConfig(p);
223
224
  const isWhiteBg = config.bgColor === "#ffffff";
224
225
  const btnBg = dark && isWhiteBg ? "#f8fafc" : config.bgColor;
225
226
  const btnBorder = isWhiteBg ? dark ? "#475569" : "#e5e7eb" : config.bgColor;
226
227
  return `<button class="provider-btn" data-provider="${p}" style="background:${btnBg};color:${config.textColor};border:1px solid ${btnBorder}">
227
- <span class="provider-icon">${config.iconSvg}</span>
228
- <span>${config.label}</span>
229
- </button>`;
230
- }).join("");
231
- const divider = b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
228
+ <span class="provider-icon">${config.iconSvg}</span>
229
+ <span>${config.label}</span>
230
+ </button>`;
231
+ }).join("") : "";
232
+ const divider = showProviders && b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
232
233
  const emailForm = b.showEmailPassword !== false ? `<form class="email-form" id="email-form">
233
234
  <input type="email" placeholder="Email address" name="email" required class="input" autocomplete="email" />
234
235
  <input type="password" placeholder="Password" name="password" required class="input" autocomplete="${isSignUp ? "new-password" : "current-password"}" />
236
+ ${isSignUp ? '<p class="password-hint">Must contain uppercase, lowercase, and a number (min 8 chars)</p>' : ""}
235
237
  <button type="submit" class="submit-btn">${isSignUp ? "Sign up" : "Sign in"}</button>
236
238
  </form>` : "";
237
239
  const footer = b.termsUrl || b.privacyUrl ? `<div class="footer">
@@ -239,11 +241,17 @@ var ModalRenderer = class {
239
241
  ${b.termsUrl && b.privacyUrl ? " \xB7 " : ""}
240
242
  ${b.privacyUrl ? `<a href="${b.privacyUrl}" target="_blank">Privacy Policy</a>` : ""}
241
243
  </div>` : "";
244
+ const titleHtml = isSignUp ? `<div class="title-row">
245
+ <button class="back-btn" id="back-btn" type="button" aria-label="Back to sign in">
246
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>
247
+ </button>
248
+ <h2 class="title">${title}</h2>
249
+ </div>` : `<h2 class="title">${title}</h2>`;
242
250
  return `
243
251
  ${b.logoDataUrl ? `<img src="${b.logoDataUrl}" alt="Logo" class="logo" />` : ""}
244
- <h2 class="title">${title}</h2>
252
+ ${titleHtml}
245
253
  ${b.brandName ? `<p class="brand-name">${b.brandName}</p>` : ""}
246
- <div class="providers">${providerButtons}</div>
254
+ ${showProviders ? `<div class="providers">${providerButtons}</div>` : ""}
247
255
  ${divider}
248
256
  ${emailForm}
249
257
  <p class="switch-view">${subtitle} <a href="#" id="switch-link">${subtitleLink}</a></p>
@@ -303,6 +311,16 @@ var ModalRenderer = class {
303
311
  transition: opacity 0.14s ease, transform 0.14s ease;
304
312
  }
305
313
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
314
+ .title-row { display: flex; align-items: center; position: relative; margin-bottom: 8px; }
315
+ .title-row .title { flex: 1; margin-bottom: 0; }
316
+ .back-btn {
317
+ position: absolute; left: 0; top: 50%; transform: translateY(-50%);
318
+ background: none; border: none; color: var(--authon-muted);
319
+ cursor: pointer; padding: 4px; border-radius: 6px; display: flex; align-items: center; justify-content: center;
320
+ transition: color 0.15s, background 0.15s;
321
+ }
322
+ .back-btn:hover { color: var(--authon-text); background: var(--authon-divider); }
323
+ .password-hint { font-size: 11px; color: var(--authon-dim); margin: -4px 0 2px; }
306
324
  .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; color: var(--authon-text); }
307
325
  .brand-name { text-align: center; font-size: 14px; color: var(--authon-muted); margin-bottom: 24px; }
308
326
  .providers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
@@ -446,6 +464,12 @@ var ModalRenderer = class {
446
464
  );
447
465
  });
448
466
  }
467
+ const backBtn = this.shadowRoot.getElementById("back-btn");
468
+ if (backBtn) {
469
+ backBtn.addEventListener("click", () => {
470
+ this.open("signIn");
471
+ });
472
+ }
449
473
  const switchLink = this.shadowRoot.getElementById("switch-link");
450
474
  if (switchLink) {
451
475
  switchLink.addEventListener("click", (e) => {
@@ -647,8 +671,7 @@ var Authon = class {
647
671
  const promise = isSignUp ? this.signUpWithEmail(email, password) : this.signInWithEmail(email, password);
648
672
  promise.then(() => this.modal?.close()).catch((err) => {
649
673
  const msg = err instanceof Error ? err.message : String(err);
650
- const friendlyMsg = msg.includes("401") ? "Invalid email or password" : msg.includes("409") ? "Email already in use" : msg.includes("400") ? "Please check your input" : msg || "Authentication failed";
651
- this.modal?.showError(friendlyMsg);
674
+ this.modal?.showError(msg || "Authentication failed");
652
675
  this.emit("error", err instanceof Error ? err : new Error(msg));
653
676
  });
654
677
  },
@@ -768,7 +791,7 @@ var Authon = class {
768
791
  headers: { "x-api-key": this.publishableKey },
769
792
  credentials: "include"
770
793
  });
771
- if (!res.ok) throw new Error(`API ${path}: ${res.status}`);
794
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
772
795
  return res.json();
773
796
  }
774
797
  async apiPost(path, body) {
@@ -781,9 +804,22 @@ var Authon = class {
781
804
  credentials: "include",
782
805
  body: body ? JSON.stringify(body) : void 0
783
806
  });
784
- if (!res.ok) throw new Error(`API ${path}: ${res.status}`);
807
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
785
808
  return res.json();
786
809
  }
810
+ async parseApiError(res, path) {
811
+ try {
812
+ const body = await res.json();
813
+ if (Array.isArray(body.message) && body.message.length > 0) {
814
+ return body.message[0];
815
+ }
816
+ if (typeof body.message === "string" && body.message !== "Bad Request") {
817
+ return body.message;
818
+ }
819
+ } catch {
820
+ }
821
+ return `API ${path}: ${res.status}`;
822
+ }
787
823
  };
788
824
  export {
789
825
  Authon,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authon/js",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Authon core SDK — ShadowDOM login modal for any app",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",