@calimero-network/mero-react 2.4.0 → 2.5.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.cjs CHANGED
@@ -2294,6 +2294,256 @@ function useDetachContextFromGroup() {
2294
2294
  );
2295
2295
  return { detachContextFromGroup, loading, error };
2296
2296
  }
2297
+ function useMigrationStatus(namespaceId, options) {
2298
+ const { mero } = useMero();
2299
+ const [status, setStatus] = react.useState(null);
2300
+ const [loading, setLoading] = react.useState(false);
2301
+ const [error, setError] = react.useState(null);
2302
+ const mountedRef = useMountedRef();
2303
+ const reqRef = react.useRef(0);
2304
+ const refetch = react.useCallback(async () => {
2305
+ if (!mero || !namespaceId) {
2306
+ if (mountedRef.current) {
2307
+ setStatus(null);
2308
+ setError(null);
2309
+ setLoading(false);
2310
+ }
2311
+ return;
2312
+ }
2313
+ const seq = ++reqRef.current;
2314
+ if (mountedRef.current) {
2315
+ setLoading(true);
2316
+ setError(null);
2317
+ }
2318
+ try {
2319
+ const result = await mero.admin.getMigrationStatus(namespaceId);
2320
+ if (mountedRef.current && seq === reqRef.current) {
2321
+ setStatus(result);
2322
+ }
2323
+ } catch (err) {
2324
+ const errorValue = toError(err);
2325
+ if (mountedRef.current && seq === reqRef.current) {
2326
+ setError(errorValue);
2327
+ }
2328
+ } finally {
2329
+ if (mountedRef.current && seq === reqRef.current) {
2330
+ setLoading(false);
2331
+ }
2332
+ }
2333
+ }, [namespaceId, mero, mountedRef]);
2334
+ react.useEffect(() => {
2335
+ setStatus(null);
2336
+ setError(null);
2337
+ }, [namespaceId]);
2338
+ react.useEffect(() => {
2339
+ void refetch();
2340
+ }, [refetch]);
2341
+ const pollIntervalMs = options?.pollIntervalMs;
2342
+ react.useEffect(() => {
2343
+ if (!pollIntervalMs || !mero || !namespaceId) return;
2344
+ const handle = setInterval(() => void refetch(), pollIntervalMs);
2345
+ return () => clearInterval(handle);
2346
+ }, [pollIntervalMs, mero, namespaceId, refetch]);
2347
+ const rollup = status?.rollup ?? null;
2348
+ const members = status?.members ?? [];
2349
+ return {
2350
+ status,
2351
+ rollup,
2352
+ members,
2353
+ allMigrated: rollup?.allMigrated ?? false,
2354
+ membersPendingSignature: rollup?.membersPendingSignature ?? 0,
2355
+ loading,
2356
+ error,
2357
+ refetch
2358
+ };
2359
+ }
2360
+ function useAppVersion(contextId, expected) {
2361
+ const { mero } = useMero();
2362
+ const [appVersion, setAppVersion] = react.useState(void 0);
2363
+ const [loading, setLoading] = react.useState(false);
2364
+ const [error, setError] = react.useState(null);
2365
+ const mountedRef = useMountedRef();
2366
+ const reqRef = react.useRef(0);
2367
+ const refetch = react.useCallback(async () => {
2368
+ if (!mero || !contextId) {
2369
+ if (mountedRef.current) {
2370
+ setAppVersion(void 0);
2371
+ setError(null);
2372
+ setLoading(false);
2373
+ }
2374
+ return;
2375
+ }
2376
+ const seq = ++reqRef.current;
2377
+ if (mountedRef.current) {
2378
+ setLoading(true);
2379
+ setError(null);
2380
+ }
2381
+ try {
2382
+ const context = await mero.admin.getContext(contextId);
2383
+ if (mountedRef.current && seq === reqRef.current) {
2384
+ setAppVersion(context.applicationVersion);
2385
+ }
2386
+ } catch (err) {
2387
+ const errorValue = toError(err);
2388
+ if (mountedRef.current && seq === reqRef.current) {
2389
+ setError(errorValue);
2390
+ }
2391
+ } finally {
2392
+ if (mountedRef.current && seq === reqRef.current) {
2393
+ setLoading(false);
2394
+ }
2395
+ }
2396
+ }, [contextId, mero, mountedRef]);
2397
+ react.useEffect(() => {
2398
+ setAppVersion(void 0);
2399
+ setError(null);
2400
+ }, [contextId]);
2401
+ react.useEffect(() => {
2402
+ void refetch();
2403
+ }, [refetch]);
2404
+ react.useEffect(() => {
2405
+ if (!mero?.events || !contextId) return;
2406
+ const off = mero.events.onAppVersionChanged((event) => {
2407
+ if (event.contextId !== contextId) return;
2408
+ if (mountedRef.current && event.toVersion) {
2409
+ reqRef.current += 1;
2410
+ setAppVersion(event.toVersion);
2411
+ }
2412
+ });
2413
+ return off;
2414
+ }, [mero, contextId, mountedRef]);
2415
+ const isStale = appVersion != null && expected != null && appVersion !== expected;
2416
+ return { appVersion, expected, isStale, loading, error, refetch };
2417
+ }
2418
+ function useMyAuthoredMigration(contextId) {
2419
+ const { mero } = useMero();
2420
+ const [summary, setSummary] = react.useState(null);
2421
+ const [pendingCount, setPendingCount] = react.useState(0);
2422
+ const mountedRef = useMountedRef();
2423
+ const { loading, error, run } = useAsyncMutation();
2424
+ const reqRef = react.useRef(0);
2425
+ const refresh = react.useCallback(async () => {
2426
+ if (!mero || !contextId) {
2427
+ if (mountedRef.current) setPendingCount(0);
2428
+ return;
2429
+ }
2430
+ const seq = ++reqRef.current;
2431
+ try {
2432
+ const count = await mero.rpc.countMyPending(contextId);
2433
+ if (mountedRef.current && seq === reqRef.current) setPendingCount(count);
2434
+ } catch {
2435
+ }
2436
+ }, [mero, contextId, mountedRef]);
2437
+ react.useEffect(() => {
2438
+ void refresh();
2439
+ }, [refresh]);
2440
+ const authorize = react.useCallback(async () => {
2441
+ if (!mero || !contextId) return null;
2442
+ const result = await run(() => mero.rpc.migrateMyEntries(contextId));
2443
+ if (result && mountedRef.current) {
2444
+ reqRef.current += 1;
2445
+ setSummary(result);
2446
+ setPendingCount(result.remaining);
2447
+ }
2448
+ return result;
2449
+ }, [mero, contextId, run, mountedRef]);
2450
+ return {
2451
+ summary,
2452
+ pendingCount,
2453
+ pending: pendingCount > 0,
2454
+ authorize,
2455
+ loading,
2456
+ error,
2457
+ refresh
2458
+ };
2459
+ }
2460
+ function MigrationPendingBanner({
2461
+ contextId,
2462
+ className,
2463
+ children,
2464
+ onMigrated
2465
+ }) {
2466
+ const { pending, pendingCount, authorize, loading } = useMyAuthoredMigration(contextId);
2467
+ if (!pending) return null;
2468
+ const handleClick = async () => {
2469
+ const result = await authorize();
2470
+ if (result && result.remaining === 0) {
2471
+ onMigrated?.();
2472
+ }
2473
+ };
2474
+ const message = children ?? `You have ${pendingCount} ${pendingCount === 1 ? "entry" : "entries"} to re-sign after a recent upgrade.`;
2475
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-testid": "migration-pending-banner", role: "status", children: [
2476
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: message }),
2477
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: handleClick, disabled: loading, children: loading ? "Migrating\u2026" : "Migrate my data" })
2478
+ ] });
2479
+ }
2480
+ function MigrationAdminPanel({ namespaceId, pollIntervalMs, className }) {
2481
+ const { status, rollup, members, membersPendingSignature, error } = useMigrationStatus(
2482
+ namespaceId,
2483
+ { pollIntervalMs }
2484
+ );
2485
+ if (error && !status) {
2486
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-testid": "migration-admin-panel", children: [
2487
+ "Failed to load migration status: ",
2488
+ error.message
2489
+ ] });
2490
+ }
2491
+ if (!status || !rollup) {
2492
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, "data-testid": "migration-admin-panel", children: "Loading migration status\u2026" });
2493
+ }
2494
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-testid": "migration-admin-panel", children: [
2495
+ error && // A poll failed after data was already loaded — keep showing the last
2496
+ // good rollup but flag that it may be stale.
2497
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "migration-status-error", role: "alert", children: [
2498
+ "Could not refresh migration status: ",
2499
+ error.message
2500
+ ] }),
2501
+ /* @__PURE__ */ jsxRuntime.jsxs("dl", { children: [
2502
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2503
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Target version" }),
2504
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: status.targetVersion })
2505
+ ] }),
2506
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2507
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Migrated" }),
2508
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.migrated })
2509
+ ] }),
2510
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2511
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "In progress" }),
2512
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.inProgress })
2513
+ ] }),
2514
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2515
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Unknown" }),
2516
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.unknown })
2517
+ ] }),
2518
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2519
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Total" }),
2520
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.total })
2521
+ ] }),
2522
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2523
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "All migrated" }),
2524
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.allMigrated ? "yes" : "no" })
2525
+ ] }),
2526
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2527
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Members pending signature" }),
2528
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": "members-pending-signature", children: membersPendingSignature })
2529
+ ] })
2530
+ ] }),
2531
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { children: [
2532
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
2533
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "Peer" }),
2534
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "State" }),
2535
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "Schema" }),
2536
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "Pending" })
2537
+ ] }) }),
2538
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: members.map((m) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { "data-testid": "migration-member-row", children: [
2539
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.peer }),
2540
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.state }),
2541
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.report?.schemaVersion ?? "\u2014" }),
2542
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.report?.authoredRemaining ?? "\u2014" })
2543
+ ] }, m.peer)) })
2544
+ ] })
2545
+ ] });
2546
+ }
2297
2547
 
2298
2548
  Object.defineProperty(exports, "CAPABILITIES", {
2299
2549
  enumerable: true,
@@ -2319,6 +2569,8 @@ exports.LoginModal = LoginModal;
2319
2569
  exports.MERO_CSS_VARS = MERO_CSS_VARS;
2320
2570
  exports.MeroContext = MeroContext;
2321
2571
  exports.MeroProvider = MeroProvider;
2572
+ exports.MigrationAdminPanel = MigrationAdminPanel;
2573
+ exports.MigrationPendingBanner = MigrationPendingBanner;
2322
2574
  exports.clearAllStorage = clearAllStorage;
2323
2575
  exports.clearApplicationId = clearApplicationId;
2324
2576
  exports.clearContextId = clearContextId;
@@ -2338,6 +2590,7 @@ exports.setContextIdentity = setContextIdentity;
2338
2590
  exports.setNodeUrl = setNodeUrl;
2339
2591
  exports.themeToCssVars = themeToCssVars;
2340
2592
  exports.useAddGroupMembers = useAddGroupMembers;
2593
+ exports.useAppVersion = useAppVersion;
2341
2594
  exports.useApplicationContexts = useApplicationContexts;
2342
2595
  exports.useContextDiscovery = useContextDiscovery;
2343
2596
  exports.useContextGroup = useContextGroup;
@@ -2365,6 +2618,8 @@ exports.useJoinNamespace = useJoinNamespace;
2365
2618
  exports.useJoinSubgroupInheritance = useJoinSubgroupInheritance;
2366
2619
  exports.useMemberMetadata = useMemberMetadata;
2367
2620
  exports.useMero = useMero;
2621
+ exports.useMigrationStatus = useMigrationStatus;
2622
+ exports.useMyAuthoredMigration = useMyAuthoredMigration;
2368
2623
  exports.useNamespace = useNamespace;
2369
2624
  exports.useNamespaceGroups = useNamespaceGroups;
2370
2625
  exports.useNamespaceIdentity = useNamespaceIdentity;