@calo-design/cli 0.9.4 → 0.9.6

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.
Files changed (2) hide show
  1. package/bin/mirror-push.js +40 -4
  2. package/package.json +1 -1
@@ -412,7 +412,8 @@ function injectNativeIntent(stagedAppDir, mirrorChromeDir, slug) {
412
412
  // live) — the launcher's JS isn't running, so this bundle performs the
413
413
  // channel switch itself: point expo-updates at the other channel, download,
414
414
  // reload. ("mirror" is the launcher's own channel — open/mirror = the feed.)
415
- import { Linking, Platform } from "react-native";
415
+ import { AppState, Platform } from "react-native";
416
+ import * as ExpoLinking from "expo-linking";
416
417
  import * as Updates from "expo-updates";
417
418
 
418
419
  const PROTOTYPE = ${JSON.stringify(slug)};
@@ -472,11 +473,46 @@ export function handleLauncherPath(path: string): string | null {
472
473
  return "/";
473
474
  }
474
475
 
475
- /** Warm-link coverage: subscribe to URLs arriving while this bundle runs. */
476
+ // Warm-link coverage. After Updates.reloadAsync() the React Native
477
+ // Linking 'url' event no longer reaches the reloaded JS context (verified on
478
+ // Android: the launcher's original context gets warm links, a prototype's
479
+ // reloaded context does not). expo-linking has its own native event path and
480
+ // URL store, so listen there — and on every foreground, sweep getLinkingURL()
481
+ // in case the event itself was missed. lastHandled stops the sweep from
482
+ // re-firing on URLs we've already acted on.
483
+ let lastHandled: string | null = null;
484
+
485
+ function handleWarmUrl(url: string | null): void {
486
+ if (!url || url === lastHandled) return;
487
+ lastHandled = url;
488
+ handleLauncherPath(url);
489
+ }
490
+
476
491
  export function subscribeLauncherLinks(): { remove: () => void } {
477
- return Linking.addEventListener("url", ({ url }) => {
478
- handleLauncherPath(url);
492
+ // Seed with the URL that launched this context so the sweeps below don't
493
+ // replay it (+native-intent already handled the initial URL).
494
+ try { lastHandled = ExpoLinking.getLinkingURL() ?? null; } catch {}
495
+ const sweep = () => {
496
+ try { handleWarmUrl(ExpoLinking.getLinkingURL() ?? null); } catch {}
497
+ };
498
+ const linkSub = ExpoLinking.addEventListener("url", ({ url }) => handleWarmUrl(url));
499
+ const stateSub = AppState.addEventListener("change", (state) => {
500
+ if (state === "active") sweep();
479
501
  });
502
+ // The load-bearing path: in a context created by Updates.reloadAsync() the
503
+ // native->JS EVENT bridge (RN Linking, AppState, expo-linking's emitter) is
504
+ // dead on Android, but expo-linking's native lifecycle listener still writes
505
+ // every incoming URL to a static that getLinkingURL() reads — and timers are
506
+ // pure JS. Poll it; the listeners above are kept for contexts where events
507
+ // do work (they win the race and lastHandled dedupes the poll).
508
+ const timer = setInterval(sweep, 1500);
509
+ return {
510
+ remove: () => {
511
+ linkSub.remove();
512
+ stateSub.remove();
513
+ clearInterval(timer);
514
+ },
515
+ };
480
516
  }
481
517
  `
482
518
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calo-design/cli",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "One-line setup for Calo design tooling: logs in with your Calo email and installs the calo-design skill + design-system packages. No GitHub account needed.",
5
5
  "bin": {
6
6
  "calo-design": "bin/cli.js"