@calimero-network/mero-react 2.4.0 → 2.6.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,313 @@ 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
+ /** Members whose migrate aborted (migration-check failed or apply errored). */
2356
+ failed: rollup?.failed ?? 0,
2357
+ loading,
2358
+ error,
2359
+ refetch
2360
+ };
2361
+ }
2362
+ function useAppVersion(contextId, expected) {
2363
+ const { mero } = useMero();
2364
+ const [appVersion, setAppVersion] = react.useState(void 0);
2365
+ const [loading, setLoading] = react.useState(false);
2366
+ const [error, setError] = react.useState(null);
2367
+ const mountedRef = useMountedRef();
2368
+ const reqRef = react.useRef(0);
2369
+ const refetch = react.useCallback(async () => {
2370
+ if (!mero || !contextId) {
2371
+ if (mountedRef.current) {
2372
+ setAppVersion(void 0);
2373
+ setError(null);
2374
+ setLoading(false);
2375
+ }
2376
+ return;
2377
+ }
2378
+ const seq = ++reqRef.current;
2379
+ if (mountedRef.current) {
2380
+ setLoading(true);
2381
+ setError(null);
2382
+ }
2383
+ try {
2384
+ const context = await mero.admin.getContext(contextId);
2385
+ if (mountedRef.current && seq === reqRef.current) {
2386
+ setAppVersion(context.applicationVersion);
2387
+ }
2388
+ } catch (err) {
2389
+ const errorValue = toError(err);
2390
+ if (mountedRef.current && seq === reqRef.current) {
2391
+ setError(errorValue);
2392
+ }
2393
+ } finally {
2394
+ if (mountedRef.current && seq === reqRef.current) {
2395
+ setLoading(false);
2396
+ }
2397
+ }
2398
+ }, [contextId, mero, mountedRef]);
2399
+ react.useEffect(() => {
2400
+ setAppVersion(void 0);
2401
+ setError(null);
2402
+ }, [contextId]);
2403
+ react.useEffect(() => {
2404
+ void refetch();
2405
+ }, [refetch]);
2406
+ react.useEffect(() => {
2407
+ if (!mero?.events || !contextId) return;
2408
+ const off = mero.events.onAppVersionChanged((event) => {
2409
+ if (event.contextId !== contextId) return;
2410
+ if (mountedRef.current && event.toVersion) {
2411
+ reqRef.current += 1;
2412
+ setAppVersion(event.toVersion);
2413
+ }
2414
+ });
2415
+ return off;
2416
+ }, [mero, contextId, mountedRef]);
2417
+ const isStale = appVersion != null && expected != null && appVersion !== expected;
2418
+ return { appVersion, expected, isStale, loading, error, refetch };
2419
+ }
2420
+ function useLatestVersion(registryUrl, packageName, currentVersion) {
2421
+ const { mero } = useMero();
2422
+ const [versions, setVersions] = react.useState([]);
2423
+ const [loading, setLoading] = react.useState(false);
2424
+ const [error, setError] = react.useState(null);
2425
+ const mountedRef = useMountedRef();
2426
+ const reqRef = react.useRef(0);
2427
+ const refetch = react.useCallback(async () => {
2428
+ if (!mero || !registryUrl || !packageName) {
2429
+ reqRef.current += 1;
2430
+ if (mountedRef.current) {
2431
+ setVersions([]);
2432
+ setError(null);
2433
+ setLoading(false);
2434
+ }
2435
+ return;
2436
+ }
2437
+ const seq = ++reqRef.current;
2438
+ if (mountedRef.current) {
2439
+ setLoading(true);
2440
+ setError(null);
2441
+ }
2442
+ try {
2443
+ const result = await mero.admin.getRegistryVersions(registryUrl, packageName);
2444
+ if (mountedRef.current && seq === reqRef.current) {
2445
+ setVersions(result);
2446
+ }
2447
+ } catch (err) {
2448
+ const errorValue = toError(err);
2449
+ if (mountedRef.current && seq === reqRef.current) {
2450
+ setError(errorValue);
2451
+ }
2452
+ } finally {
2453
+ if (mountedRef.current && seq === reqRef.current) {
2454
+ setLoading(false);
2455
+ }
2456
+ }
2457
+ }, [mero, registryUrl, packageName, mountedRef]);
2458
+ react.useEffect(() => {
2459
+ setVersions([]);
2460
+ setError(null);
2461
+ }, [registryUrl, packageName]);
2462
+ react.useEffect(() => {
2463
+ void refetch();
2464
+ }, [refetch]);
2465
+ const latestVersion = versions[0] ?? null;
2466
+ const updateAvailable = latestVersion != null && currentVersion != null && meroJs.compareSemver(latestVersion, currentVersion) > 0;
2467
+ return { versions, latestVersion, currentVersion, updateAvailable, loading, error, refetch };
2468
+ }
2469
+ function useMyAuthoredMigration(contextId) {
2470
+ const { mero } = useMero();
2471
+ const [summary, setSummary] = react.useState(null);
2472
+ const [pendingCount, setPendingCount] = react.useState(0);
2473
+ const mountedRef = useMountedRef();
2474
+ const { loading, error, run } = useAsyncMutation();
2475
+ const reqRef = react.useRef(0);
2476
+ const refresh = react.useCallback(async () => {
2477
+ if (!mero || !contextId) {
2478
+ if (mountedRef.current) setPendingCount(0);
2479
+ return;
2480
+ }
2481
+ const seq = ++reqRef.current;
2482
+ try {
2483
+ const count = await mero.rpc.countMyPending(contextId);
2484
+ if (mountedRef.current && seq === reqRef.current) setPendingCount(count);
2485
+ } catch {
2486
+ }
2487
+ }, [mero, contextId, mountedRef]);
2488
+ react.useEffect(() => {
2489
+ void refresh();
2490
+ }, [refresh]);
2491
+ const authorize = react.useCallback(async () => {
2492
+ if (!mero || !contextId) return null;
2493
+ const result = await run(() => mero.rpc.migrateMyEntries(contextId));
2494
+ if (result && mountedRef.current) {
2495
+ reqRef.current += 1;
2496
+ setSummary(result);
2497
+ setPendingCount(result.remaining);
2498
+ }
2499
+ return result;
2500
+ }, [mero, contextId, run, mountedRef]);
2501
+ return {
2502
+ summary,
2503
+ pendingCount,
2504
+ pending: pendingCount > 0,
2505
+ authorize,
2506
+ loading,
2507
+ error,
2508
+ refresh
2509
+ };
2510
+ }
2511
+ function MigrationPendingBanner({
2512
+ contextId,
2513
+ className,
2514
+ children,
2515
+ onMigrated
2516
+ }) {
2517
+ const { pending, pendingCount, authorize, loading } = useMyAuthoredMigration(contextId);
2518
+ if (!pending) return null;
2519
+ const handleClick = async () => {
2520
+ const result = await authorize();
2521
+ if (result && result.remaining === 0) {
2522
+ onMigrated?.();
2523
+ }
2524
+ };
2525
+ const message = children ?? `You have ${pendingCount} ${pendingCount === 1 ? "entry" : "entries"} to re-sign after a recent upgrade.`;
2526
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-testid": "migration-pending-banner", role: "status", children: [
2527
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: message }),
2528
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: handleClick, disabled: loading, children: loading ? "Migrating\u2026" : "Migrate my data" })
2529
+ ] });
2530
+ }
2531
+ function MigrationAdminPanel({ namespaceId, pollIntervalMs, className }) {
2532
+ const { status, rollup, members, membersPendingSignature, failed, error } = useMigrationStatus(
2533
+ namespaceId,
2534
+ { pollIntervalMs }
2535
+ );
2536
+ if (error && !status) {
2537
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-testid": "migration-admin-panel", children: [
2538
+ "Failed to load migration status: ",
2539
+ error.message
2540
+ ] });
2541
+ }
2542
+ if (!status || !rollup) {
2543
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, "data-testid": "migration-admin-panel", children: "Loading migration status\u2026" });
2544
+ }
2545
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-testid": "migration-admin-panel", children: [
2546
+ error && // A poll failed after data was already loaded — keep showing the last
2547
+ // good rollup but flag that it may be stale.
2548
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "migration-status-error", role: "alert", children: [
2549
+ "Could not refresh migration status: ",
2550
+ error.message
2551
+ ] }),
2552
+ /* @__PURE__ */ jsxRuntime.jsxs("dl", { children: [
2553
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2554
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Target version" }),
2555
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: status.targetVersion })
2556
+ ] }),
2557
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2558
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Migrated" }),
2559
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.migrated })
2560
+ ] }),
2561
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2562
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "In progress" }),
2563
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.inProgress })
2564
+ ] }),
2565
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2566
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Unknown" }),
2567
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.unknown })
2568
+ ] }),
2569
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2570
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Failed" }),
2571
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": "migration-failed-count", children: failed })
2572
+ ] }),
2573
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2574
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Total" }),
2575
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.total })
2576
+ ] }),
2577
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2578
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "All migrated" }),
2579
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: rollup.allMigrated ? "yes" : "no" })
2580
+ ] }),
2581
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2582
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Members pending signature" }),
2583
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": "members-pending-signature", children: membersPendingSignature })
2584
+ ] })
2585
+ ] }),
2586
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { children: [
2587
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
2588
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "Peer" }),
2589
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "State" }),
2590
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "Schema" }),
2591
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "Pending" }),
2592
+ /* @__PURE__ */ jsxRuntime.jsx("th", { children: "Reason" })
2593
+ ] }) }),
2594
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: members.map((m) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { "data-testid": "migration-member-row", children: [
2595
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.peer }),
2596
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.state }),
2597
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.report?.schemaVersion ?? "\u2014" }),
2598
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.report?.authoredRemaining ?? "\u2014" }),
2599
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: m.report?.migrationFailed ?? "\u2014" })
2600
+ ] }, m.peer)) })
2601
+ ] })
2602
+ ] });
2603
+ }
2297
2604
 
2298
2605
  Object.defineProperty(exports, "CAPABILITIES", {
2299
2606
  enumerable: true,
@@ -2319,6 +2626,8 @@ exports.LoginModal = LoginModal;
2319
2626
  exports.MERO_CSS_VARS = MERO_CSS_VARS;
2320
2627
  exports.MeroContext = MeroContext;
2321
2628
  exports.MeroProvider = MeroProvider;
2629
+ exports.MigrationAdminPanel = MigrationAdminPanel;
2630
+ exports.MigrationPendingBanner = MigrationPendingBanner;
2322
2631
  exports.clearAllStorage = clearAllStorage;
2323
2632
  exports.clearApplicationId = clearApplicationId;
2324
2633
  exports.clearContextId = clearContextId;
@@ -2338,6 +2647,7 @@ exports.setContextIdentity = setContextIdentity;
2338
2647
  exports.setNodeUrl = setNodeUrl;
2339
2648
  exports.themeToCssVars = themeToCssVars;
2340
2649
  exports.useAddGroupMembers = useAddGroupMembers;
2650
+ exports.useAppVersion = useAppVersion;
2341
2651
  exports.useApplicationContexts = useApplicationContexts;
2342
2652
  exports.useContextDiscovery = useContextDiscovery;
2343
2653
  exports.useContextGroup = useContextGroup;
@@ -2363,8 +2673,11 @@ exports.useJoinContext = useJoinContext;
2363
2673
  exports.useJoinGroup = useJoinGroup;
2364
2674
  exports.useJoinNamespace = useJoinNamespace;
2365
2675
  exports.useJoinSubgroupInheritance = useJoinSubgroupInheritance;
2676
+ exports.useLatestVersion = useLatestVersion;
2366
2677
  exports.useMemberMetadata = useMemberMetadata;
2367
2678
  exports.useMero = useMero;
2679
+ exports.useMigrationStatus = useMigrationStatus;
2680
+ exports.useMyAuthoredMigration = useMyAuthoredMigration;
2368
2681
  exports.useNamespace = useNamespace;
2369
2682
  exports.useNamespaceGroups = useNamespaceGroups;
2370
2683
  exports.useNamespaceIdentity = useNamespaceIdentity;