astrogators-shared-ui 0.7.0 → 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`, {
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, n.refresh_token), 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,97 +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) => {
420
- 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()));
421
- }, [r, F]), G = n((e) => {
422
- if (_(e), H(e), e && r) {
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) {
423
427
  let t = m.find((t) => "ally_code" in t && t.ally_code === e);
424
- t && "id" in t && I(t.id);
425
- } else e && !r && B(e);
428
+ t && "id" in t && N(t.id);
429
+ } else e && !r && W(e);
426
430
  }, [
427
431
  r,
428
432
  m,
429
- I
430
- ]), K = n(async (e) => {
433
+ N
434
+ ]), F = n(async (e) => {
431
435
  if (!/^\d{9}$/.test(e)) throw Error("Ally code must be exactly 9 digits");
432
436
  if (r) {
433
- let t = await N.post("/api/v1/users/me/ally-codes", { ally_code: e });
434
- h((e) => [t, ...e]), m.length === 0 && G(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);
435
439
  } else {
436
- let t = await N.get(`/api/v1/player-data/player/${e}`);
440
+ let t = await R.get(`/api/v1/player-data/player/${e}`);
437
441
  if (!t) throw Error("Invalid ally code - player not found");
438
- R({
442
+ te({
439
443
  ally_code: e,
440
444
  player_name: t.data?.name ?? null,
441
445
  last_used_at: (/* @__PURE__ */ new Date()).toISOString()
442
- }), h(L()), m.length === 0 && G(e);
446
+ }), h(H()), m.length === 0 && P(e);
443
447
  }
444
448
  }, [
445
449
  r,
446
450
  m.length,
447
- G
448
- ]), q = n(async (e) => {
449
- 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 && G(null));
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));
450
454
  }, [
451
455
  r,
452
456
  g,
453
- G
454
- ]), J = n(() => {
457
+ P
458
+ ]), L = n(() => {
455
459
  x({
456
460
  show: !1,
457
461
  localStorageCodes: []
458
462
  });
459
- }, []), Y = n(async () => {
463
+ }, []), B = n(async () => {
460
464
  if (!r) return;
461
- let e = L();
465
+ let e = H();
462
466
  for (let t of e) try {
463
- await K(t.ally_code);
467
+ await F(t.ally_code);
464
468
  } catch (e) {
465
469
  console.error(`Failed to migrate ally code ${t.ally_code}:`, e);
466
470
  }
467
- U(), J();
471
+ re(), L();
468
472
  }, [
469
473
  r,
470
- K,
471
- J
474
+ F,
475
+ L
472
476
  ]);
473
477
  i(() => {
474
- F();
475
- }, [F]);
476
- let ee = {
478
+ M();
479
+ }, [M]);
480
+ let V = {
477
481
  user: r,
478
482
  isAuthenticated: !!r,
479
483
  isLoading: c,
@@ -483,30 +487,30 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
483
487
  refreshUser: E,
484
488
  forgotPassword: D,
485
489
  resetPassword: O,
486
- resendVerification: M,
490
+ resendVerification: j,
487
491
  authEnabled: u,
488
492
  isLoadingFeatures: f,
489
493
  allyCodes: m,
490
494
  selectedAllyCode: g,
491
495
  isLoadingAllyCodes: v,
492
- fetchAllyCodes: F,
493
- addAllyCode: K,
494
- removeAllyCode: q,
495
- selectAllyCode: G,
496
- updateAllyCodeLastUsed: I,
496
+ fetchAllyCodes: M,
497
+ addAllyCode: F,
498
+ removeAllyCode: I,
499
+ selectAllyCode: P,
500
+ updateAllyCodeLastUsed: N,
497
501
  migrationPrompt: b,
498
- dismissMigrationPrompt: J,
499
- migrateLocalStorageCodes: Y
502
+ dismissMigrationPrompt: L,
503
+ migrateLocalStorageCodes: B
500
504
  };
501
- return /* @__PURE__ */ s(W.Provider, {
502
- value: ee,
505
+ return /* @__PURE__ */ s(K.Provider, {
506
+ value: V,
503
507
  children: e
504
508
  });
505
- }, K = () => {
506
- let e = r(W);
509
+ }, q = () => {
510
+ let e = r(K);
507
511
  if (e === void 0) throw Error("useAuth must be used within an AuthProvider");
508
512
  return e;
509
- }, 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 = {
510
514
  allyCodeDropdown: "_allyCodeDropdown_1m0tw_1",
511
515
  select: "_select_1m0tw_8",
512
516
  manageButton: "_manageButton_1m0tw_12",
@@ -518,10 +522,10 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
518
522
  codesList: "_codesList_1m0tw_67",
519
523
  codeItem: "_codeItem_1m0tw_71",
520
524
  doneButton: "_doneButton_1m0tw_88"
521
- }, ee = ({ onAllyCodeSelected: e, className: t = "" }) => {
522
- 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) => ({
523
527
  value: e.ally_code,
524
- 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)
525
529
  })), T = (t) => {
526
530
  let n = t.target.value || null;
527
531
  i(n), e?.(n);
@@ -591,7 +595,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
591
595
  className: Y.codesList,
592
596
  children: n.map((e) => /* @__PURE__ */ c("div", {
593
597
  className: Y.codeItem,
594
- 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, {
595
599
  variant: "ghost",
596
600
  size: "sm",
597
601
  onClick: () => E(e.ally_code),
@@ -628,7 +632,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
628
632
  "padding-md": "_padding-md_b4471_36",
629
633
  "padding-lg": "_padding-lg_b4471_40",
630
634
  hoverable: "_hoverable_b4471_45"
631
- }, 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 }) => {
632
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;
633
637
  return /* @__PURE__ */ c("div", {
634
638
  className: `${X.card} ${n ? X.chamfered : ""} ${X[t]} ${X[`padding-${i}`]} ${m} ${a ? X.hoverable : ""} ${d}`,
@@ -667,7 +671,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
667
671
  warning: "_warning_rk7t5_47",
668
672
  error: "_error_rk7t5_52",
669
673
  info: "_info_rk7t5_57"
670
- }, 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", {
671
675
  className: `${Z.badge} ${Z[t]} ${Z[n]} ${r}`,
672
676
  children: e
673
677
  }), Q = {
@@ -683,7 +687,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
683
687
  title: "_title_1b3yt_53",
684
688
  closeButton: "_closeButton_1b3yt_60",
685
689
  content: "_content_1b3yt_78"
686
- }, 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", () => {
687
691
  document.body.style.overflow = "unset";
688
692
  }), [e]), i(() => {
689
693
  let n = (n) => {
@@ -725,7 +729,7 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
725
729
  dot: "_dot_1uhnh_34",
726
730
  bounce: "_bounce_1uhnh_1",
727
731
  visuallyHidden: "_visuallyHidden_1uhnh_91"
728
- }, 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", {
729
733
  className: `${$.dots} ${$[e]} ${n}`,
730
734
  children: [
731
735
  /* @__PURE__ */ s("span", { className: $.dot }),
@@ -741,4 +745,4 @@ var x = "astrogators_access_token", S = "astrogators_refresh_token", C = () => l
741
745
  })
742
746
  });
743
747
  //#endregion
744
- 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>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astrogators-shared-ui",
3
- "version": "0.7.0",
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",