@calimero-network/mero-react 5.1.0 → 6.0.1

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
@@ -328,6 +328,7 @@ function MeroProvider({
328
328
  const [contextId, setContextIdState] = react.useState(() => getContextId());
329
329
  const [contextIdentity, setContextIdentityState] = react.useState(() => getContextIdentity());
330
330
  const meroRef = react.useRef(null);
331
+ const recoveringRef = react.useRef(false);
331
332
  const callbackRef = react.useRef(void 0);
332
333
  if (callbackRef.current === void 0 && isBrowser) {
333
334
  callbackRef.current = meroJs.parseAuthCallback(window.location.href);
@@ -351,12 +352,18 @@ function MeroProvider({
351
352
  async (instance) => {
352
353
  const accessToken = tokenStore.getTokens()?.access_token;
353
354
  if (!accessToken) return false;
354
- try {
355
- const { valid } = await instance.auth.validateToken(accessToken);
356
- return valid;
357
- } catch {
358
- return false;
359
- }
355
+ const validate = async (token) => {
356
+ try {
357
+ const { valid } = await instance.auth.validateToken(token);
358
+ return valid;
359
+ } catch {
360
+ return false;
361
+ }
362
+ };
363
+ if (await validate(accessToken)) return true;
364
+ const rotated = tokenStore.getTokens()?.access_token;
365
+ if (!rotated || rotated === accessToken) return false;
366
+ return validate(rotated);
360
367
  },
361
368
  [tokenStore]
362
369
  );
@@ -483,9 +490,23 @@ function MeroProvider({
483
490
  const onError = (err) => {
484
491
  if (!active) return;
485
492
  setIsOnline(false);
486
- if (err.message.includes("401")) {
487
- logout();
488
- }
493
+ if (!err.message.includes("401")) return;
494
+ if (recoveringRef.current) return;
495
+ recoveringRef.current = true;
496
+ void (async () => {
497
+ try {
498
+ const instance = meroRef.current;
499
+ if (!instance) return;
500
+ if (await checkAuth(instance)) {
501
+ if (active) await instance.events.connect().catch(() => {
502
+ });
503
+ return;
504
+ }
505
+ if (active) logout();
506
+ } finally {
507
+ recoveringRef.current = false;
508
+ }
509
+ })();
489
510
  };
490
511
  sse.on("connect", onConnect);
491
512
  sse.on("error", onError);
@@ -497,7 +518,7 @@ function MeroProvider({
497
518
  sse.off("connect", onConnect);
498
519
  sse.off("error", onError);
499
520
  };
500
- }, [isAuthenticated, mero]);
521
+ }, [isAuthenticated, mero, checkAuth, logout]);
501
522
  const contextValue = react.useMemo(
502
523
  () => ({
503
524
  mero,
@@ -1990,6 +2011,15 @@ function useNamespace(namespaceId) {
1990
2011
  );
1991
2012
  return { namespace: data, loading, error, refetch };
1992
2013
  }
2014
+ function useNodeIdentity() {
2015
+ const { mero } = useMero();
2016
+ const { data, loading, error, refetch } = useAsyncResource(
2017
+ mero ? () => mero.admin.getNodeIdentity() : null,
2018
+ null,
2019
+ [mero]
2020
+ );
2021
+ return { identity: data, loading, error, refetch };
2022
+ }
1993
2023
  function useNamespaceIdentity(namespaceId) {
1994
2024
  const { mero } = useMero();
1995
2025
  const { data, loading, error, refetch } = useAsyncResource(
@@ -2359,6 +2389,35 @@ function useDetachContextFromGroup() {
2359
2389
  );
2360
2390
  return { detachContextFromGroup, loading, error };
2361
2391
  }
2392
+ var DEFAULT_MIGRATION_POLL_INTERVAL_MS = 5e3;
2393
+ function useMigrationEvents(namespaceId, onEvent) {
2394
+ const { mero } = useMero();
2395
+ const mountedRef = useMountedRef();
2396
+ const [live, setLive] = react.useState(false);
2397
+ const handlerRef = react.useRef(onEvent);
2398
+ handlerRef.current = onEvent;
2399
+ react.useEffect(() => {
2400
+ if (!mero || !namespaceId) return;
2401
+ const sse = mero.events;
2402
+ const unsubscribe = sse.onMigrationEvent((event) => handlerRef.current(event));
2403
+ let cancelled = false;
2404
+ void (async () => {
2405
+ try {
2406
+ await sse.connect();
2407
+ await sse.subscribe({ groupIds: [namespaceId] });
2408
+ if (!cancelled && mountedRef.current) setLive(true);
2409
+ } catch {
2410
+ if (!cancelled && mountedRef.current) setLive(false);
2411
+ }
2412
+ })();
2413
+ return () => {
2414
+ cancelled = true;
2415
+ if (mountedRef.current) setLive(false);
2416
+ unsubscribe();
2417
+ };
2418
+ }, [mero, namespaceId, mountedRef]);
2419
+ return live;
2420
+ }
2362
2421
  function useMigrationStatus(namespaceId, options) {
2363
2422
  const { mero } = useMero();
2364
2423
  const [status, setStatus] = react.useState(null);
@@ -2403,7 +2462,8 @@ function useMigrationStatus(namespaceId, options) {
2403
2462
  react.useEffect(() => {
2404
2463
  void refetch();
2405
2464
  }, [refetch]);
2406
- const pollIntervalMs = options?.pollIntervalMs;
2465
+ const sseLive = useMigrationEvents(namespaceId, () => void refetch());
2466
+ const pollIntervalMs = options?.pollIntervalMs ?? (sseLive ? void 0 : DEFAULT_MIGRATION_POLL_INTERVAL_MS);
2407
2467
  react.useEffect(() => {
2408
2468
  if (!pollIntervalMs || !mero || !namespaceId) return;
2409
2469
  const handle = setInterval(() => void refetch(), pollIntervalMs);
@@ -2416,6 +2476,12 @@ function useMigrationStatus(namespaceId, options) {
2416
2476
  rollup,
2417
2477
  members,
2418
2478
  allMigrated: rollup?.allMigrated ?? false,
2479
+ /**
2480
+ * When this node watched the fleet converge, or `null`. Durable, unlike
2481
+ * `allMigrated`, which is recomputed from in-TTL heartbeats and lapses back
2482
+ * to false when a member simply goes quiet.
2483
+ */
2484
+ fleetCompletedAt: status?.fleetCompletedAt ?? null,
2419
2485
  membersPendingSignature: rollup?.membersPendingSignature ?? 0,
2420
2486
  /** Members whose migrate aborted (migration-check failed or apply errored). */
2421
2487
  failed: rollup?.failed ?? 0,
@@ -2535,7 +2601,6 @@ var EMPTY_GROUP_APP_VERSION = {
2535
2601
  version: null,
2536
2602
  applicationId: null,
2537
2603
  pendingApply: false,
2538
- upgradePolicy: null,
2539
2604
  activeUpgrade: null
2540
2605
  };
2541
2606
  function isZeroAppKey(appKey) {
@@ -2567,17 +2632,14 @@ function useGroupAppVersion(groupId) {
2567
2632
  const namespace = namespaces.find((entry) => entry.namespaceId === groupId);
2568
2633
  let appKey;
2569
2634
  let applicationId;
2570
- let upgradePolicy;
2571
2635
  let activeUpgrade = null;
2572
2636
  if (namespace) {
2573
2637
  appKey = namespace.appKey;
2574
2638
  applicationId = namespace.targetApplicationId;
2575
- upgradePolicy = namespace.upgradePolicy ?? null;
2576
2639
  } else {
2577
2640
  const info = await mero.admin.getGroupInfo(groupId);
2578
2641
  appKey = info.appKey;
2579
2642
  applicationId = info.targetApplicationId;
2580
- upgradePolicy = info.upgradePolicy ?? null;
2581
2643
  activeUpgrade = info.activeUpgrade ?? null;
2582
2644
  }
2583
2645
  let version = null;
@@ -2591,7 +2653,7 @@ function useGroupAppVersion(groupId) {
2591
2653
  }
2592
2654
  }
2593
2655
  if (mountedRef.current && seq === reqRef.current) {
2594
- setData({ version, applicationId, pendingApply, upgradePolicy, activeUpgrade });
2656
+ setData({ version, applicationId, pendingApply, activeUpgrade });
2595
2657
  }
2596
2658
  } catch (err) {
2597
2659
  const errorValue = toError(err);
@@ -2802,6 +2864,7 @@ exports.AppMode = AppMode;
2802
2864
  exports.CalimeroLogo = CalimeroLogo;
2803
2865
  exports.ConnectButton = ConnectButton;
2804
2866
  exports.ConnectionType = ConnectionType;
2867
+ exports.DEFAULT_MIGRATION_POLL_INTERVAL_MS = DEFAULT_MIGRATION_POLL_INTERVAL_MS;
2805
2868
  exports.LoginModal = LoginModal;
2806
2869
  exports.MERO_CSS_VARS = MERO_CSS_VARS;
2807
2870
  exports.MeroContext = MeroContext;
@@ -2862,6 +2925,7 @@ exports.useJoinSubgroupInheritance = useJoinSubgroupInheritance;
2862
2925
  exports.useLatestVersion = useLatestVersion;
2863
2926
  exports.useMemberMetadata = useMemberMetadata;
2864
2927
  exports.useMero = useMero;
2928
+ exports.useMigrationEvents = useMigrationEvents;
2865
2929
  exports.useMigrationStatus = useMigrationStatus;
2866
2930
  exports.useMyAuthoredMigration = useMyAuthoredMigration;
2867
2931
  exports.useNamespace = useNamespace;
@@ -2869,6 +2933,7 @@ exports.useNamespaceGroups = useNamespaceGroups;
2869
2933
  exports.useNamespaceIdentity = useNamespaceIdentity;
2870
2934
  exports.useNamespaces = useNamespaces;
2871
2935
  exports.useNamespacesForApplication = useNamespacesForApplication;
2936
+ exports.useNodeIdentity = useNodeIdentity;
2872
2937
  exports.useRemoveGroupMembers = useRemoveGroupMembers;
2873
2938
  exports.useReparentGroup = useReparentGroup;
2874
2939
  exports.useResyncContext = useResyncContext;