@pylonsync/react 0.3.36 → 0.3.42

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.36",
6
+ "version": "0.3.42",
7
7
  "type": "module",
8
8
  "main": "src/index.ts",
9
9
  "types": "src/index.ts",
@@ -12,8 +12,8 @@
12
12
  "check": "tsc -p tsconfig.json --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@pylonsync/sdk": "0.3.36",
16
- "@pylonsync/sync": "0.3.36"
15
+ "@pylonsync/sdk": "0.3.42",
16
+ "@pylonsync/sync": "0.3.42"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "react": ">=19.0.0"
package/src/db.ts CHANGED
@@ -42,9 +42,16 @@ let _started = false;
42
42
  * import { init } from "@pylonsync/react";
43
43
  * init({ baseUrl: "http://localhost:4321" });
44
44
  * ```
45
+ *
46
+ * Omitting `baseUrl` in a browser context falls back to
47
+ * `window.location.origin` — the right answer for same-origin
48
+ * deployments (Next.js + Vercel rewrites, embedded SPA). Passing an
49
+ * explicit `baseUrl` always wins. We deliberately do NOT default to
50
+ * `http://localhost:4321` in browsers — that footgun caused production
51
+ * dashboards to fire requests at the engineer's dev port.
45
52
  */
46
53
  export function init(config?: Partial<SyncEngineConfig> & { baseUrl?: string }) {
47
- _sync = createSyncEngine(config?.baseUrl ?? "http://localhost:4321", config);
54
+ _sync = createSyncEngine(config?.baseUrl, config);
48
55
  _started = false;
49
56
  // Keep the React-side helpers in sync — a single init() should fully
50
57
  // namespace this app's storage without a separate configureClient call.
@@ -56,7 +63,13 @@ export function init(config?: Partial<SyncEngineConfig> & { baseUrl?: string })
56
63
 
57
64
  function getSync(): SyncEngine {
58
65
  if (!_sync) {
59
- _sync = createSyncEngine("http://localhost:4321");
66
+ // Lazy fallback for callers that never invoked init(). Same
67
+ // resolution rules as init: browser → window.location.origin,
68
+ // SSR → localhost:4321 (the pylon dev default). The browser case
69
+ // is critical: without it a useQuery hook that fires before the
70
+ // app's SyncProvider effect lands would leak `localhost:4321`
71
+ // requests in production.
72
+ _sync = createSyncEngine();
60
73
  }
61
74
  if (!_started) {
62
75
  _started = true;
package/src/index.ts CHANGED
@@ -61,6 +61,9 @@ export type {
61
61
  export { useSession } from "./useSession";
62
62
  export type { UseSessionReturn, ResolvedSession } from "./useSession";
63
63
 
64
+ export { useSyncStatus } from "./useSyncStatus";
65
+ export type { SyncConnectionStatus } from "./useSyncStatus";
66
+
64
67
  // One-liner API
65
68
  export { db, init } from "./db";
66
69
 
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import {
4
+ type SyncConnectionStatus,
5
+ type SyncEngine,
6
+ } from "@pylonsync/sync";
7
+ import { useSyncExternalStore } from "react";
8
+ import { db } from "./db";
9
+
10
+ export type { SyncConnectionStatus };
11
+
12
+ /**
13
+ * Subscribe to the sync engine's connection status. Re-renders whenever
14
+ * the WebSocket transitions ("connecting" → "connected" → "reconnecting"
15
+ * → "offline"), so apps can render a small indicator without polling
16
+ * or wiring their own event listeners.
17
+ *
18
+ * Common use case: surface a "reconnecting…" banner during the cold
19
+ * start after a Fly autostop or a flaky network. Live queries keep
20
+ * returning the last-known data during reconnect, so without this hook
21
+ * users see stale data with no signal that anything is wrong.
22
+ *
23
+ * ```tsx
24
+ * function ConnectionBanner() {
25
+ * const status = useSyncStatus();
26
+ * if (status === "connected") return null;
27
+ * return (
28
+ * <div className="banner">
29
+ * {status === "reconnecting" ? "Reconnecting…" : "Offline"}
30
+ * </div>
31
+ * );
32
+ * }
33
+ * ```
34
+ *
35
+ * Pass an explicit `engine` if your app uses a non-default sync
36
+ * instance; otherwise omit and the hook reads the singleton wired up
37
+ * by `init()`.
38
+ */
39
+ export function useSyncStatus(engine?: SyncEngine): SyncConnectionStatus {
40
+ const sync = engine ?? db.sync;
41
+ return useSyncExternalStore(
42
+ (cb) => sync.store.subscribe(cb),
43
+ () => sync.connectionStatus(),
44
+ // SSR snapshot — engine is offline until start() runs in the
45
+ // browser, so claiming "connecting" here would mislead consumers
46
+ // rendered server-side.
47
+ () => "offline" as SyncConnectionStatus,
48
+ );
49
+ }