astrogators-shared-ui 0.7.0-rc.1 → 0.8.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.js CHANGED
@@ -200,61 +200,65 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
200
200
  w(e), D(t);
201
201
  }, A = () => {
202
202
  T(), O();
203
- }, j = () => !!C(), M = class {
203
+ }, ee = () => !!C(), j = "", M, N = null, P = (e) => {
204
+ j = e.baseURL, M = e.onUnauthorized;
205
+ }, F = () => N || (N = (async () => {
206
+ let e = E();
207
+ if (!e) throw Error("No refresh token available");
208
+ let t = await fetch(`${j}/api/v1/auth/refresh`, {
209
+ method: "POST",
210
+ headers: { "Content-Type": "application/json" },
211
+ body: JSON.stringify({ refresh_token: e })
212
+ });
213
+ if (!t.ok) throw A(), M?.(), Error("Token refresh failed");
214
+ let n = await t.json();
215
+ return k(n.access_token, n.refresh_token), n.access_token;
216
+ })().finally(() => {
217
+ N = null;
218
+ }), N), I = async (e, t = {}) => {
219
+ let n = C(), r = { ...t.headers };
220
+ n && (r.Authorization = `Bearer ${n}`);
221
+ let i = await fetch(e, {
222
+ ...t,
223
+ headers: r
224
+ });
225
+ if (i.status !== 401 || !n) return i;
226
+ try {
227
+ return r.Authorization = `Bearer ${await F()}`, await fetch(e, {
228
+ ...t,
229
+ headers: r
230
+ });
231
+ } catch {
232
+ return i;
233
+ }
234
+ }, L = class {
204
235
  baseURL;
205
- onUnauthorized;
206
- isRefreshing = !1;
207
- refreshPromise = null;
208
236
  constructor(e) {
209
- this.baseURL = e.baseURL, this.onUnauthorized = e.onUnauthorized;
210
- }
211
- async refreshAccessToken() {
212
- let e = E();
213
- if (!e) throw Error("No refresh token available");
214
- let t = await fetch(`${this.baseURL}/api/v1/auth/refresh-token`, {
215
- method: "POST",
216
- headers: { "Content-Type": "application/json" },
217
- body: JSON.stringify({ refresh_token: e })
237
+ this.baseURL = e.baseURL, P({
238
+ baseURL: e.baseURL,
239
+ onUnauthorized: e.onUnauthorized
218
240
  });
219
- if (!t.ok) throw A(), this.onUnauthorized?.(), Error("Token refresh failed");
220
- let n = await t.json();
221
- return k(n.access_token, e), n.access_token;
222
241
  }
223
242
  async request(e, t = {}) {
224
- let n = C(), r = {
243
+ let n = {
225
244
  "Content-Type": "application/json",
226
245
  ...t.headers
227
- };
228
- n && (r.Authorization = `Bearer ${n}`);
229
- let i = `${this.baseURL}${e}`, a = await fetch(i, {
246
+ }, r = await I(`${this.baseURL}${e}`, {
230
247
  ...t,
231
- headers: r
248
+ headers: n
232
249
  });
233
- if (a.status === 401 && n) {
234
- this.isRefreshing || (this.isRefreshing = !0, this.refreshPromise = this.refreshAccessToken().finally(() => {
235
- this.isRefreshing = !1, this.refreshPromise = null;
236
- }));
237
- try {
238
- r.Authorization = `Bearer ${await this.refreshPromise}`, a = await fetch(i, {
239
- ...t,
240
- headers: r
241
- });
242
- } catch {
243
- throw Error("Authentication failed");
244
- }
245
- }
246
- if (!a.ok) {
250
+ if (!r.ok) {
247
251
  let e = {
248
- message: a.statusText,
249
- status: a.status
252
+ message: r.statusText,
253
+ status: r.status
250
254
  };
251
255
  try {
252
- let t = await a.json();
253
- a.status === 422 && Array.isArray(t.detail) ? e.message = t.detail.map((e) => e.msg || "Validation error").join(", ") : typeof t.detail == "string" ? e.message = t.detail : t.message && (e.message = t.message), e.detail = t.detail;
256
+ let t = await r.json();
257
+ r.status === 422 && Array.isArray(t.detail) ? e.message = t.detail.map((e) => e.msg || "Validation error").join(", ") : typeof t.detail == "string" ? e.message = t.detail : t.message && (e.message = t.message), e.detail = t.detail;
254
258
  } catch {}
255
259
  throw e;
256
260
  }
257
- return a.status === 204 ? {} : a.json();
261
+ return r.status === 204 ? {} : r.json();
258
262
  }
259
263
  async get(e, t) {
260
264
  return this.request(e, {
@@ -289,55 +293,55 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
289
293
  method: "DELETE"
290
294
  });
291
295
  }
292
- }, N, P = (e) => {
293
- N = new M(e);
294
- }, F = "astrogators_ally_codes", I = "astrogators_selected_ally_code", L = () => {
296
+ }, R, z = (e) => {
297
+ R = new L(e);
298
+ }, B = "astrogators_ally_codes", V = "astrogators_selected_ally_code", H = () => {
295
299
  try {
296
- let e = localStorage.getItem(F);
300
+ let e = localStorage.getItem(B);
297
301
  return e ? JSON.parse(e) : [];
298
302
  } catch (e) {
299
303
  return console.error("Failed to read ally codes from localStorage:", e), [];
300
304
  }
301
- }, R = (e) => {
305
+ }, te = (e) => {
302
306
  try {
303
- let t = L();
307
+ let t = H();
304
308
  if (t.some((t) => t.ally_code === e.ally_code)) return;
305
- t.push(e), localStorage.setItem(F, JSON.stringify(t));
309
+ t.push(e), localStorage.setItem(B, JSON.stringify(t));
306
310
  } catch (e) {
307
311
  console.error("Failed to save ally code to localStorage:", e);
308
312
  }
309
- }, z = (e) => {
313
+ }, U = (e) => {
310
314
  try {
311
- let t = L().filter((t) => t.ally_code !== e);
312
- localStorage.setItem(F, JSON.stringify(t));
315
+ let t = H().filter((t) => t.ally_code !== e);
316
+ localStorage.setItem(B, JSON.stringify(t));
313
317
  } catch (e) {
314
318
  console.error("Failed to remove ally code from localStorage:", e);
315
319
  }
316
- }, B = (e) => {
320
+ }, W = (e) => {
317
321
  try {
318
- let t = L().map((t) => t.ally_code === e ? {
322
+ let t = H().map((t) => t.ally_code === e ? {
319
323
  ...t,
320
324
  last_used_at: (/* @__PURE__ */ new Date()).toISOString()
321
325
  } : t);
322
- localStorage.setItem(F, JSON.stringify(t));
326
+ localStorage.setItem(B, JSON.stringify(t));
323
327
  } catch (e) {
324
328
  console.error("Failed to update ally code last used:", e);
325
329
  }
326
- }, V = () => localStorage.getItem(I), H = (e) => {
327
- e ? localStorage.setItem(I, e) : localStorage.removeItem(I);
328
- }, U = () => {
329
- localStorage.removeItem(F), localStorage.removeItem(I);
330
- }, W = t(void 0), G = ({ children: e, apiBaseUrl: t }) => {
330
+ }, ne = () => localStorage.getItem(V), G = (e) => {
331
+ e ? localStorage.setItem(V, e) : localStorage.removeItem(V);
332
+ }, re = () => {
333
+ localStorage.removeItem(B), localStorage.removeItem(V);
334
+ }, K = t(void 0), ie = ({ children: e, apiBaseUrl: t }) => {
331
335
  let [r, o] = a(null), [c, l] = a(!0), [u, d] = a(!0), [f, p] = a(!0), [m, h] = a([]), [g, _] = a(null), [v, y] = a(!1), [b, x] = a({
332
336
  show: !1,
333
337
  localStorageCodes: []
334
338
  });
335
339
  i(() => {
336
- P({ baseURL: t });
340
+ z({ baseURL: t });
337
341
  }, [t]), i(() => {
338
342
  (async () => {
339
343
  try {
340
- d((await N.get("/api/v1/config/features")).auth_enabled);
344
+ d((await R.get("/api/v1/config/features")).auth_enabled);
341
345
  } catch (e) {
342
346
  console.error("Failed to fetch features:", e), d(!0);
343
347
  } finally {
@@ -346,12 +350,12 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
346
350
  })();
347
351
  }, [t]);
348
352
  let S = n(async () => {
349
- if (!j()) {
353
+ if (!ee()) {
350
354
  o(null), l(!1);
351
355
  return;
352
356
  }
353
357
  try {
354
- o(await N.get("/api/v1/users/me"));
358
+ o(await R.get("/api/v1/users/me"));
355
359
  } catch (e) {
356
360
  console.error("Failed to fetch user:", e), o(null), A();
357
361
  } finally {
@@ -363,14 +367,14 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
363
367
  }, [S]);
364
368
  let C = async (e) => {
365
369
  try {
366
- let t = await N.post("/api/v1/auth/login", e);
367
- k(t.access_token, t.refresh_token), o(await N.get("/api/v1/users/me"));
370
+ let t = await R.post("/api/v1/auth/login", e);
371
+ k(t.access_token, t.refresh_token), o(await R.get("/api/v1/users/me"));
368
372
  } catch (e) {
369
373
  throw console.error("Login failed:", e), e;
370
374
  }
371
375
  }, w = async (e) => {
372
376
  try {
373
- await N.post("/api/v1/auth/register", e);
377
+ await R.post("/api/v1/auth/register", e);
374
378
  } catch (e) {
375
379
  throw console.error("Registration failed:", e), e;
376
380
  }
@@ -383,85 +387,97 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
383
387
  await S();
384
388
  }, D = async (e) => {
385
389
  try {
386
- return (await N.post("/api/v1/auth/forgot-password", e)).message;
390
+ return (await R.post("/api/v1/auth/forgot-password", e)).message;
387
391
  } catch (e) {
388
392
  throw console.error("Forgot password failed:", e), e;
389
393
  }
390
394
  }, O = async (e) => {
391
395
  try {
392
- return (await N.post("/api/v1/auth/reset-password", e)).message;
396
+ return (await R.post("/api/v1/auth/reset-password", e)).message;
393
397
  } catch (e) {
394
398
  throw console.error("Reset password failed:", e), e;
395
399
  }
396
- }, M = async (e) => {
400
+ }, j = async (e) => {
397
401
  try {
398
- return (await N.post("/api/v1/auth/resend-verification", e)).message;
402
+ return (await R.post("/api/v1/auth/resend-verification", e)).message;
399
403
  } catch (e) {
400
404
  throw console.error("Resend verification failed:", e), e;
401
405
  }
402
- }, F = n(async () => {
406
+ }, M = n(async () => {
403
407
  y(!0);
404
408
  try {
405
409
  if (r) {
406
- h((await N.get("/api/v1/users/me/ally-codes")).ally_codes);
407
- let e = L();
410
+ h((await R.get("/api/v1/users/me/ally-codes")).ally_codes);
411
+ let e = H();
408
412
  e.length > 0 && x({
409
413
  show: !0,
410
414
  localStorageCodes: e.map((e) => e.ally_code)
411
415
  });
412
- } else h(L());
413
- _(V());
416
+ } else h(H());
417
+ _(ne());
414
418
  } catch (e) {
415
419
  console.error("Failed to fetch ally codes:", e), h([]);
416
420
  } finally {
417
421
  y(!1);
418
422
  }
419
- }, [r]), I = n(async (e) => {
423
+ }, [r]), N = n(async (e) => {
424
+ r && typeof e == "number" ? (await R.put(`/api/v1/users/me/ally-codes/${e}/use`, {}), await M()) : !r && typeof e == "string" && (W(e), h(H()));
425
+ }, [r, M]), P = n((e) => {
426
+ if (_(e), G(e), e && r) {
427
+ let t = m.find((t) => "ally_code" in t && t.ally_code === e);
428
+ t && "id" in t && N(t.id);
429
+ } else e && !r && W(e);
430
+ }, [
431
+ r,
432
+ m,
433
+ N
434
+ ]), F = n(async (e) => {
420
435
  if (!/^\d{9}$/.test(e)) throw Error("Ally code must be exactly 9 digits");
421
436
  if (r) {
422
- let t = await N.post("/api/v1/users/me/ally-codes", { ally_code: e });
423
- h((e) => [t, ...e]), m.length === 0 && K(e);
437
+ let t = await R.post("/api/v1/users/me/ally-codes", { ally_code: e });
438
+ h((e) => [t, ...e]), m.length === 0 && P(e);
424
439
  } else {
425
- let t = await N.get(`/api/v1/player-data/player/${e}`);
440
+ let t = await R.get(`/api/v1/player-data/player/${e}`);
426
441
  if (!t) throw Error("Invalid ally code - player not found");
427
- R({
442
+ te({
428
443
  ally_code: e,
429
- player_name: t?.data?.name || null,
444
+ player_name: t.data?.name ?? null,
430
445
  last_used_at: (/* @__PURE__ */ new Date()).toISOString()
431
- }), h(L()), m.length === 0 && K(e);
446
+ }), h(H()), m.length === 0 && P(e);
432
447
  }
433
- }, [r, m.length]), G = n(async (e) => {
434
- r && typeof e == "number" ? (await N.delete(`/api/v1/users/me/ally-codes/${e}`), h((t) => t.filter((t) => "id" in t && t.id !== e))) : !r && typeof e == "string" && (z(e), h(L()), g === e && K(null));
435
- }, [r, g]), K = n((e) => {
436
- if (_(e), H(e), e && r) {
437
- let t = m.find((t) => "ally_code" in t && t.ally_code === e);
438
- t && "id" in t && q(t.id);
439
- } else e && !r && B(e);
440
- }, [r, m]), q = n(async (e) => {
441
- r && typeof e == "number" ? (await N.put(`/api/v1/users/me/ally-codes/${e}/use`, {}), await F()) : !r && typeof e == "string" && (B(e), h(L()));
442
- }, [r, F]), J = n(() => {
448
+ }, [
449
+ r,
450
+ m.length,
451
+ P
452
+ ]), I = n(async (e) => {
453
+ r && typeof e == "number" ? (await R.delete(`/api/v1/users/me/ally-codes/${e}`), h((t) => t.filter((t) => "id" in t && t.id !== e))) : !r && typeof e == "string" && (U(e), h(H()), g === e && P(null));
454
+ }, [
455
+ r,
456
+ g,
457
+ P
458
+ ]), L = n(() => {
443
459
  x({
444
460
  show: !1,
445
461
  localStorageCodes: []
446
462
  });
447
- }, []), Y = n(async () => {
463
+ }, []), B = n(async () => {
448
464
  if (!r) return;
449
- let e = L();
465
+ let e = H();
450
466
  for (let t of e) try {
451
- await I(t.ally_code);
467
+ await F(t.ally_code);
452
468
  } catch (e) {
453
469
  console.error(`Failed to migrate ally code ${t.ally_code}:`, e);
454
470
  }
455
- U(), J();
471
+ re(), L();
456
472
  }, [
457
473
  r,
458
- I,
459
- J
474
+ F,
475
+ L
460
476
  ]);
461
477
  i(() => {
462
- F();
463
- }, [F]);
464
- let ee = {
478
+ M();
479
+ }, [M]);
480
+ let V = {
465
481
  user: r,
466
482
  isAuthenticated: !!r,
467
483
  isLoading: c,
@@ -471,30 +487,30 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
471
487
  refreshUser: E,
472
488
  forgotPassword: D,
473
489
  resetPassword: O,
474
- resendVerification: M,
490
+ resendVerification: j,
475
491
  authEnabled: u,
476
492
  isLoadingFeatures: f,
477
493
  allyCodes: m,
478
494
  selectedAllyCode: g,
479
495
  isLoadingAllyCodes: v,
480
- fetchAllyCodes: F,
481
- addAllyCode: I,
482
- removeAllyCode: G,
483
- selectAllyCode: K,
484
- updateAllyCodeLastUsed: q,
496
+ fetchAllyCodes: M,
497
+ addAllyCode: F,
498
+ removeAllyCode: I,
499
+ selectAllyCode: P,
500
+ updateAllyCodeLastUsed: N,
485
501
  migrationPrompt: b,
486
- dismissMigrationPrompt: J,
487
- migrateLocalStorageCodes: Y
502
+ dismissMigrationPrompt: L,
503
+ migrateLocalStorageCodes: B
488
504
  };
489
- return /* @__PURE__ */ s(W.Provider, {
490
- value: ee,
505
+ return /* @__PURE__ */ s(K.Provider, {
506
+ value: V,
491
507
  children: e
492
508
  });
493
- }, K = () => {
494
- let e = r(W);
509
+ }, q = () => {
510
+ let e = r(K);
495
511
  if (e === void 0) throw Error("useAuth must be used within an AuthProvider");
496
512
  return e;
497
- }, q = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e.slice(6, 9)}`, J = (e) => e.replace(/-/g, ""), Y = {
513
+ }, J = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e.slice(6, 9)}`, ae = (e) => e.replace(/-/g, ""), Y = {
498
514
  allyCodeDropdown: "_allyCodeDropdown_1m0tw_1",
499
515
  select: "_select_1m0tw_8",
500
516
  manageButton: "_manageButton_1m0tw_12",
@@ -506,10 +522,10 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
506
522
  codesList: "_codesList_1m0tw_67",
507
523
  codeItem: "_codeItem_1m0tw_71",
508
524
  doneButton: "_doneButton_1m0tw_88"
509
- }, ee = ({ onAllyCodeSelected: e, className: t = "" }) => {
510
- let { allyCodes: n, selectedAllyCode: r, selectAllyCode: i, removeAllyCode: o, addAllyCode: l, isLoadingAllyCodes: u } = K(), [d, f] = a(!1), [p, m] = a(""), [h, _] = a(""), [y, x] = a(""), [S, C] = a(!1), w = n.map((e) => ({
525
+ }, oe = ({ onAllyCodeSelected: e, className: t = "" }) => {
526
+ let { allyCodes: n, selectedAllyCode: r, selectAllyCode: i, removeAllyCode: o, addAllyCode: l, isLoadingAllyCodes: u } = q(), [d, f] = a(!1), [p, m] = a(""), [h, _] = a(""), [y, x] = a(""), [S, C] = a(!1), w = n.map((e) => ({
511
527
  value: e.ally_code,
512
- label: e.player_name ? `${e.player_name} (${q(e.ally_code)})` : q(e.ally_code)
528
+ label: e.player_name ? `${e.player_name} (${J(e.ally_code)})` : J(e.ally_code)
513
529
  })), T = (t) => {
514
530
  let n = t.target.value || null;
515
531
  i(n), e?.(n);
@@ -579,7 +595,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
579
595
  className: Y.codesList,
580
596
  children: n.map((e) => /* @__PURE__ */ c("div", {
581
597
  className: Y.codeItem,
582
- children: [/* @__PURE__ */ s("span", { children: e.player_name ? `${e.player_name} (${q(e.ally_code)})` : q(e.ally_code) }), /* @__PURE__ */ s(g, {
598
+ children: [/* @__PURE__ */ s("span", { children: e.player_name ? `${e.player_name} (${J(e.ally_code)})` : J(e.ally_code) }), /* @__PURE__ */ s(g, {
583
599
  variant: "ghost",
584
600
  size: "sm",
585
601
  onClick: () => E(e.ally_code),
@@ -616,7 +632,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
616
632
  "padding-md": "_padding-md_b4471_36",
617
633
  "padding-lg": "_padding-lg_b4471_40",
618
634
  hoverable: "_hoverable_b4471_45"
619
- }, te = ({ children: e, variant: t = "default", chamfered: n = !1, chamferSize: r = "md", padding: i = "md", hoverable: a = !1, showDiagonalBorders: l = !1, diagonalBorderColor: u, className: d = "", onClick: f, style: p }) => {
635
+ }, se = ({ children: e, variant: t = "default", chamfered: n = !1, chamferSize: r = "md", padding: i = "md", hoverable: a = !1, showDiagonalBorders: l = !1, diagonalBorderColor: u, className: d = "", onClick: f, style: p }) => {
620
636
  let m = n ? r === "asymmetric" ? "chamfered-box-asymmetric" : r === "sm" ? "chamfered-box-sm" : r === "lg" ? "chamfered-box-lg" : "chamfered-box" : "", h = u ? { color: u } : void 0;
621
637
  return /* @__PURE__ */ c("div", {
622
638
  className: `${X.card} ${n ? X.chamfered : ""} ${X[t]} ${X[`padding-${i}`]} ${m} ${a ? X.hoverable : ""} ${d}`,
@@ -638,7 +654,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
638
654
  style: h
639
655
  }),
640
656
  /* @__PURE__ */ s("div", {
641
- className: `chamfered-diagonal-border ${r === "asymmetric" ? "chamfered-diagonal-br-large" : "chamfered-diagonal-br"}`,
657
+ className: "chamfered-diagonal-border chamfered-diagonal-br",
642
658
  style: h
643
659
  })
644
660
  ] }), e]
@@ -655,7 +671,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
655
671
  warning: "_warning_rk7t5_47",
656
672
  error: "_error_rk7t5_52",
657
673
  info: "_info_rk7t5_57"
658
- }, ne = ({ children: e, variant: t = "default", size: n = "md", className: r = "" }) => /* @__PURE__ */ s("span", {
674
+ }, ce = ({ children: e, variant: t = "default", size: n = "md", className: r = "" }) => /* @__PURE__ */ s("span", {
659
675
  className: `${Z.badge} ${Z[t]} ${Z[n]} ${r}`,
660
676
  children: e
661
677
  }), Q = {
@@ -671,7 +687,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
671
687
  title: "_title_1b3yt_53",
672
688
  closeButton: "_closeButton_1b3yt_60",
673
689
  content: "_content_1b3yt_78"
674
- }, re = ({ isOpen: e, onClose: t, title: n, children: r, size: a = "md", closeOnOverlayClick: o = !0, className: l = "" }) => (i(() => (e ? document.body.style.overflow = "hidden" : document.body.style.overflow = "unset", () => {
690
+ }, le = ({ isOpen: e, onClose: t, title: n, children: r, size: a = "md", closeOnOverlayClick: o = !0, className: l = "" }) => (i(() => (e ? document.body.style.overflow = "hidden" : document.body.style.overflow = "unset", () => {
675
691
  document.body.style.overflow = "unset";
676
692
  }), [e]), i(() => {
677
693
  let n = (n) => {
@@ -713,7 +729,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
713
729
  dot: "_dot_1uhnh_34",
714
730
  bounce: "_bounce_1uhnh_1",
715
731
  visuallyHidden: "_visuallyHidden_1uhnh_91"
716
- }, ie = ({ size: e = "md", variant: t = "spinner", className: n = "" }) => t === "dots" ? /* @__PURE__ */ c("div", {
732
+ }, ue = ({ size: e = "md", variant: t = "spinner", className: n = "" }) => t === "dots" ? /* @__PURE__ */ c("div", {
717
733
  className: `${$.dots} ${$[e]} ${n}`,
718
734
  children: [
719
735
  /* @__PURE__ */ s("span", { className: $.dot }),
@@ -729,4 +745,4 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
729
745
  })
730
746
  });
731
747
  //#endregion
732
- export { ee as AllyCodeDropdown, M as ApiClient, G as AuthProvider, ne as Badge, g as Button, te as Card, u as Container, m as Footer, v as Input, ie as Loader, re as Modal, b as Select, f as TopBar, N as apiClient, U as clearAllyCodes, A as clearTokens, q as formatAllyCode, C as getAccessToken, L as getAllyCodesFromStorage, E as getRefreshToken, V as getSelectedAllyCode, P as initializeApiClient, j as isAuthenticated, T as removeAccessToken, z as removeAllyCodeFromStorage, O as removeRefreshToken, R as saveAllyCodeToStorage, w as setAccessToken, D as setRefreshToken, H as setSelectedAllyCode, k as setTokens, J as unformatAllyCode, B as updateAllyCodeLastUsed, K as useAuth };
748
+ export { oe as AllyCodeDropdown, L as ApiClient, ie as AuthProvider, ce as Badge, g as Button, se as Card, u as Container, m as Footer, v as Input, ue as Loader, le as Modal, b as Select, f as TopBar, R as apiClient, I as authedFetch, re as clearAllyCodes, A as clearTokens, P as configureAuthRefresh, J as formatAllyCode, C as getAccessToken, H as getAllyCodesFromStorage, E as getRefreshToken, ne as getSelectedAllyCode, z as initializeApiClient, ee as isAuthenticated, F as refreshAccessToken, T as removeAccessToken, U as removeAllyCodeFromStorage, O as removeRefreshToken, te as saveAllyCodeToStorage, w as setAccessToken, D as setRefreshToken, G as setSelectedAllyCode, k as setTokens, ae as unformatAllyCode, W as updateAllyCodeLastUsed, q as useAuth };
@@ -10,6 +10,8 @@ export { AuthProvider, useAuth } from './contexts/AuthContext';
10
10
  export type { AuthContextValue, AuthProviderProps } from './contexts/AuthContext';
11
11
  export { ApiClient, apiClient, initializeApiClient } from './services/api';
12
12
  export type { ApiClientConfig } from './services/api';
13
+ export { authedFetch, refreshAccessToken, configureAuthRefresh, } from './services/tokenRefresh';
14
+ export type { AuthRefreshConfig } from './services/tokenRefresh';
13
15
  export { getAccessToken, setAccessToken, removeAccessToken, getRefreshToken, setRefreshToken, removeRefreshToken, setTokens, clearTokens, isAuthenticated, } from './services/auth';
14
16
  export { getAllyCodesFromStorage, saveAllyCodeToStorage, removeAllyCodeFromStorage, updateAllyCodeLastUsed, getSelectedAllyCode, setSelectedAllyCode, clearAllyCodes, } from './services/allyCodeStorage';
15
17
  export type { StoredAllyCode } from './services/allyCodeStorage';
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * API Client
3
- * HTTP client with JWT token injection and automatic refresh
3
+ * HTTP client with JWT token injection and automatic refresh.
4
+ *
5
+ * Token injection + refresh-and-retry are delegated to the shared
6
+ * `authedFetch` primitive (services/tokenRefresh) so there is ONE refresh
7
+ * implementation across the app, and any service-specific client (with its
8
+ * own base URL) can reuse it. This class is the convenience layer on top:
9
+ * base-URL joining, JSON encoding, and structured error parsing.
4
10
  */
5
11
  export interface ApiClientConfig {
6
12
  baseURL: string;
@@ -8,11 +14,7 @@ export interface ApiClientConfig {
8
14
  }
9
15
  export declare class ApiClient {
10
16
  private baseURL;
11
- private onUnauthorized?;
12
- private isRefreshing;
13
- private refreshPromise;
14
17
  constructor(config: ApiClientConfig);
15
- private refreshAccessToken;
16
18
  request<T>(endpoint: string, options?: RequestInit): Promise<T>;
17
19
  get<T>(endpoint: string, options?: RequestInit): Promise<T>;
18
20
  post<T>(endpoint: string, data?: unknown, options?: RequestInit): Promise<T>;
@@ -0,0 +1,25 @@
1
+ export interface AuthRefreshConfig {
2
+ baseURL: string;
3
+ onUnauthorized?: () => void;
4
+ }
5
+ /** Point the refresh primitive at the auth service. Idempotent. */
6
+ export declare const configureAuthRefresh: (config: AuthRefreshConfig) => void;
7
+ /**
8
+ * Exchange the stored refresh token for a fresh access token. Concurrent
9
+ * callers share one in-flight request. On failure the tokens are cleared and
10
+ * `onUnauthorized` fires, then the error propagates.
11
+ */
12
+ export declare const refreshAccessToken: () => Promise<string>;
13
+ /**
14
+ * `fetch` that injects the current bearer token and, on a 401 from an
15
+ * authenticated request, refreshes the token once and retries.
16
+ *
17
+ * Returns the final Response — it does NOT throw on non-2xx, so callers keep
18
+ * full control over status handling (mirrors the raw `fetch` contract). When a
19
+ * refresh is needed but fails (e.g. the refresh token is also expired), the
20
+ * original 401 Response is returned so the caller can surface a re-login state.
21
+ *
22
+ * Works against ANY resource URL; only the refresh hop uses the configured
23
+ * auth base URL.
24
+ */
25
+ export declare const authedFetch: (input: string, init?: RequestInit) => Promise<Response>;
@@ -37,7 +37,9 @@ export interface RefreshTokenRequest {
37
37
  }
38
38
  export interface RefreshTokenResponse {
39
39
  access_token: string;
40
+ refresh_token: string;
40
41
  token_type: string;
42
+ expires_in: number;
41
43
  }
42
44
  export interface ForgotPasswordRequest {
43
45
  email: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astrogators-shared-ui",
3
- "version": "0.7.0-rc.1",
3
+ "version": "0.8.0",
4
4
  "description": "Shared UI components and utilities for Astrogator's Table applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -51,8 +51,5 @@
51
51
  "vite": "^8.0.9",
52
52
  "vite-plugin-dts": "^4.5.4"
53
53
  },
54
- "dependencies": {
55
- "@fontsource-variable/inter-tight": "^5.2.7",
56
- "@fontsource/michroma": "^5.2.8"
57
- }
54
+ "dependencies": {}
58
55
  }