@pylonsync/react 0.3.292 → 0.3.293

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.
@@ -0,0 +1,93 @@
1
+ import { type SyncEngine } from '@pylonsync/sync';
2
+ export interface RoomPeer {
3
+ user_id: string;
4
+ data: any;
5
+ joined_at: string;
6
+ }
7
+ export interface RoomSnapshot {
8
+ room: string;
9
+ peers: RoomPeer[];
10
+ }
11
+ export interface UseRoomOptions {
12
+ /** Base URL of the pylon server. */
13
+ baseUrl?: string;
14
+ /** Auth token for API requests. */
15
+ token?: string;
16
+ /** Initial presence data sent on join. */
17
+ initialPresence?: Record<string, any>;
18
+ /**
19
+ * How often (ms) to poll for peer updates WHEN the WebSocket
20
+ * push channel is unavailable. With a connected WebSocket the SDK
21
+ * uses `room-subscribe` push and the polling timer never fires.
22
+ * Defaults to 5_000.
23
+ */
24
+ heartbeatInterval?: number;
25
+ }
26
+ export interface UseRoomReturn {
27
+ /** Current peers in the room (excluding self). */
28
+ peers: RoomPeer[];
29
+ /** Whether currently connected to the room. */
30
+ isConnected: boolean;
31
+ /** Update your presence data (e.g. cursor position, typing status). */
32
+ setPresence: (data: Record<string, any>) => void;
33
+ /** Broadcast a message to the room on a given topic. */
34
+ broadcast: (topic: string, data: any) => void;
35
+ /** Leave the room manually. */
36
+ leave: () => void;
37
+ /** Error message, if any. */
38
+ error: string | null;
39
+ }
40
+ interface SharedRoom {
41
+ /** Number of live React hooks holding this room. */
42
+ refs: number;
43
+ /** Has the join() request resolved with a server response? */
44
+ joined: boolean;
45
+ /** Cached snapshot for late subscribers (mounting after the join landed). */
46
+ peers: RoomPeer[];
47
+ isConnected: boolean;
48
+ error: string | null;
49
+ /** Last presence data the caller pushed via setPresence. */
50
+ presence: Record<string, any>;
51
+ /** Active heartbeat poll (only set when polling fallback is active). */
52
+ interval: ReturnType<typeof setInterval> | null;
53
+ /** Pending teardown — set when refcount hit 0 but we haven't actually
54
+ * fired leave yet, to absorb StrictMode mount/unmount/mount races. */
55
+ pendingTeardown: ReturnType<typeof setTimeout> | null;
56
+ /** Subscriber callbacks; each useRoom hook registers one. */
57
+ subs: Set<() => void>;
58
+ /** Transport identity (frozen at create time — changing it changes the key). */
59
+ baseUrl: string;
60
+ token: string | undefined;
61
+ roomId: string;
62
+ userId: string;
63
+ heartbeatInterval: number;
64
+ /** Sync engine instance the room is attached to (for WS push).
65
+ * `null` when none provided / running in SSR or test harness that
66
+ * never invoked init(). */
67
+ engine: SyncEngine | null;
68
+ /** Unsubscribe handle returned by engine.subscribeRoom. `null` when
69
+ * we're polling. Mutually exclusive with `interval` — exactly one
70
+ * is set whenever the room is live. */
71
+ wsUnsubscribe: (() => void) | null;
72
+ /** Timer that fires PUSH_SNAPSHOT_TIMEOUT_MS after we send the first
73
+ * room-subscribe. If a snapshot lands first we cancel it; if it
74
+ * fires we assume the server doesn't speak the push protocol and
75
+ * fall back to polling. */
76
+ pushSnapshotTimer: ReturnType<typeof setTimeout> | null;
77
+ /** Connection-status unsubscribe — fires whenever the engine's
78
+ * transport state changes so we can flip between push and polling. */
79
+ connectionStatusUnsubscribe: (() => void) | null;
80
+ }
81
+ declare function releaseRoom(room: SharedRoom): void;
82
+ export declare const __roomRegistryInternals: {
83
+ reset(): void;
84
+ acquire(baseUrl: string, roomId: string, userId: string, token: string | undefined, initialPresence: Record<string, any>, heartbeatInterval: number, engine?: SyncEngine | null): SharedRoom;
85
+ release: typeof releaseRoom;
86
+ size(): number;
87
+ /** Diagnostic: is this room currently on the WS push path? */
88
+ isWsAttached(room: SharedRoom): boolean;
89
+ /** Diagnostic: is the polling timer running for this room? */
90
+ isPolling(room: SharedRoom): boolean;
91
+ };
92
+ export declare function useRoom(roomId: string, userId: string, options?: UseRoomOptions): UseRoomReturn;
93
+ export {};
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The current query string as a reactive `URLSearchParams`. Re-renders on
3
+ * client navigation. Returns empty params during SSR / first hydration —
4
+ * use the `searchParams` page prop for server-side values.
5
+ *
6
+ * ```tsx
7
+ * const params = useSearchParams();
8
+ * const tab = params.get("tab") ?? "overview";
9
+ * ```
10
+ */
11
+ export declare function useSearchParams(): URLSearchParams;
12
+ /**
13
+ * The current pathname (no query/hash), reactive to client navigation.
14
+ * Returns "/" during SSR / first hydration — use the `url` page prop for
15
+ * server-side values.
16
+ */
17
+ export declare function usePathname(): string;
18
+ /**
19
+ * The current route's dynamic params — e.g. `/dashboard/[projectId]` →
20
+ * `{ projectId: "p_1" }`. Reactive to client navigation, so a deep child gets
21
+ * the new params after a `<Link>` click without prop-drilling. Returns `{}`
22
+ * during SSR / first hydration — use the `params` page prop for server-side
23
+ * values. Drop-in for Next's `useParams`.
24
+ *
25
+ * ```tsx
26
+ * const { projectId } = useParams<{ projectId: string }>();
27
+ * ```
28
+ */
29
+ export declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
30
+ /**
31
+ * Client-side redirect — replaces the current history entry with `href`.
32
+ * Drop-in for Next's `redirect` when called from a client component
33
+ * (effect/handler). For a redirect decided during a server render, use the
34
+ * `response.redirect()` API on the page's `PageProps` instead.
35
+ */
36
+ export declare function redirect(href: string): void;
37
+ /** Error thrown by {@link notFound}; the SSR not-found boundary renders it. */
38
+ export declare class NotFoundError extends Error {
39
+ readonly digest = "PYLON_NOT_FOUND";
40
+ constructor();
41
+ }
42
+ /**
43
+ * Render the nearest `not-found.tsx` boundary from a client component by
44
+ * throwing — drop-in for Next's `notFound`. For a 404 decided during a server
45
+ * render, prefer `response.notFound()` on the page's `PageProps` so the
46
+ * response carries a real 404 status.
47
+ */
48
+ export declare function notFound(): never;
49
+ /** Imperative navigation handle (Next-style `useRouter`). */
50
+ export interface PylonRouter {
51
+ /** Navigate to `href`, pushing a new history entry. */
52
+ push(href: string): void;
53
+ /** Navigate to `href`, replacing the current history entry. */
54
+ replace(href: string): void;
55
+ /** Go back one history entry. */
56
+ back(): void;
57
+ /** Go forward one history entry. */
58
+ forward(): void;
59
+ /** Re-fetch + re-render the current route (fresh server data). */
60
+ refresh(): void;
61
+ /** Warm the SSR HTML + chunks for `href` ahead of a navigation. */
62
+ prefetch(href: string): void;
63
+ }
64
+ /**
65
+ * Programmatic client navigation. Methods are no-ops before hydration /
66
+ * during SSR (there's no client runtime yet), so they're safe to call from
67
+ * effects and event handlers.
68
+ *
69
+ * ```tsx
70
+ * const router = useRouter();
71
+ * <button onClick={() => router.push("/dashboard")}>Go</button>
72
+ * ```
73
+ */
74
+ export declare function useRouter(): PylonRouter;
@@ -0,0 +1,41 @@
1
+ import { SyncEngine, type ResolvedSession } from "@pylonsync/sync";
2
+ export type { ResolvedSession };
3
+ export interface UseSessionReturn {
4
+ /** Server-resolved session. `userId=null` means anonymous. */
5
+ session: ResolvedSession;
6
+ /** Convenience accessors. */
7
+ userId: string | null;
8
+ tenantId: string | null;
9
+ isAdmin: boolean;
10
+ isAuthenticated: boolean;
11
+ /**
12
+ * Switch the active tenant. POSTs to /api/auth/select-org, verifies
13
+ * membership server-side, resets the local replica so stale rows
14
+ * from the previous tenant disappear, and refreshes the cached
15
+ * session — all in one await. Throws (with `.code`/`.status`) on
16
+ * NOT_A_MEMBER and other server errors.
17
+ */
18
+ selectOrg: (orgId: string) => Promise<void>;
19
+ /** Drop the active tenant (back to no-org state). */
20
+ clearOrg: () => Promise<void>;
21
+ /** Revoke the current session server-side and refresh. */
22
+ signOut: () => Promise<void>;
23
+ /**
24
+ * Escape hatch — refresh the cached session without mutating it.
25
+ * Most apps shouldn't need this; the helpers above already include
26
+ * a refresh. Useful when app code calls /api/auth/* via its own
27
+ * client and needs the engine to observe the change.
28
+ */
29
+ refresh: () => Promise<void>;
30
+ }
31
+ /**
32
+ * Subscribe to the server-resolved session held by the sync engine.
33
+ *
34
+ * The engine fetches `/api/auth/me` on start and on token flips, caches
35
+ * the result, and notifies the store on change — so this hook is
36
+ * purely a reader. The exposed helpers (selectOrg, clearOrg, signOut)
37
+ * combine the server fetch + cache refresh + replica reset into one
38
+ * call so app code doesn't need to remember the manual
39
+ * `notifySessionChanged()` step after every auth mutation.
40
+ */
41
+ export declare function useSession(sync: SyncEngine): UseSessionReturn;
@@ -0,0 +1,58 @@
1
+ export interface UseShardOptions {
2
+ /** Subscriber ID (usually the logged-in user ID). Required for multiplayer. */
3
+ subscriberId: string;
4
+ /**
5
+ * Auth token. Sent over the WebSocket as a Sec-WebSocket-Protocol
6
+ * subprotocol header in the form `"bearer.<token>"`. This keeps the token
7
+ * out of URLs — proxy logs, browser devtools network panel, and error
8
+ * telemetry typically record the URL but not the subprotocol value.
9
+ *
10
+ * The pylon shard server reads either the subprotocol header or the
11
+ * legacy `?token=` query param (which is still accepted but deprecated —
12
+ * scheduled for removal in a future release).
13
+ */
14
+ token?: string;
15
+ /** Override the base URL. Defaults to `window.location.host`. */
16
+ baseUrl?: string;
17
+ /** Override the shard WS port (default: HTTP port + 3). */
18
+ wsPort?: number;
19
+ /** Explicit WebSocket URL. Overrides baseUrl/wsPort. */
20
+ wsUrl?: string;
21
+ /** If true, falls back to SSE + HTTP POST if WebSocket fails (default: true). */
22
+ sseFallback?: boolean;
23
+ /** Reconnect on unexpected close (default: true). */
24
+ autoReconnect?: boolean;
25
+ /** Reconnect backoff in ms (default: starts at 500, maxes at 10_000). */
26
+ reconnectBackoffMs?: number;
27
+ }
28
+ export interface UseShardReturn<TSnapshot = unknown, TInput = unknown> {
29
+ snapshot: TSnapshot | null;
30
+ tick: number;
31
+ connected: boolean;
32
+ error: Error | null;
33
+ /** Send an input to the shard. Returns a client sequence number. */
34
+ send: (input: TInput) => number;
35
+ /** Close the connection early. */
36
+ close: () => void;
37
+ }
38
+ export interface ShardClient<TSnapshot = unknown, TInput = unknown> {
39
+ onSnapshot: (fn: (snapshot: TSnapshot, tick: number) => void) => void;
40
+ onError: (fn: (err: Error) => void) => void;
41
+ onOpen: (fn: () => void) => void;
42
+ onClose: (fn: () => void) => void;
43
+ send: (input: TInput) => number;
44
+ close: () => void;
45
+ readonly connected: boolean;
46
+ }
47
+ /**
48
+ * Connect to a shard without React — returns a typed client you can wire
49
+ * into any framework.
50
+ */
51
+ export declare function connectShard<TSnapshot = unknown, TInput = unknown>(shardId: string, options: UseShardOptions): ShardClient<TSnapshot, TInput>;
52
+ /**
53
+ * React hook that subscribes to a shard's snapshots and provides a send fn.
54
+ *
55
+ * The hook re-renders when a new snapshot arrives or the connection state
56
+ * changes. The `send` fn is stable across re-renders.
57
+ */
58
+ export declare function useShard<TSnapshot = unknown, TInput = unknown>(shardId: string, options: UseShardOptions): UseShardReturn<TSnapshot, TInput>;
@@ -0,0 +1,30 @@
1
+ import { type SyncConnectionStatus, type SyncEngine } from "@pylonsync/sync";
2
+ export type { SyncConnectionStatus };
3
+ /**
4
+ * Subscribe to the sync engine's connection status. Re-renders whenever
5
+ * the WebSocket transitions ("connecting" → "connected" → "reconnecting"
6
+ * → "offline"), so apps can render a small indicator without polling
7
+ * or wiring their own event listeners.
8
+ *
9
+ * Common use case: surface a "reconnecting…" banner during the cold
10
+ * start after a Fly autostop or a flaky network. Live queries keep
11
+ * returning the last-known data during reconnect, so without this hook
12
+ * users see stale data with no signal that anything is wrong.
13
+ *
14
+ * ```tsx
15
+ * function ConnectionBanner() {
16
+ * const status = useSyncStatus();
17
+ * if (status === "connected") return null;
18
+ * return (
19
+ * <div className="banner">
20
+ * {status === "reconnecting" ? "Reconnecting…" : "Offline"}
21
+ * </div>
22
+ * );
23
+ * }
24
+ * ```
25
+ *
26
+ * Pass an explicit `engine` if your app uses a non-default sync
27
+ * instance; otherwise omit and the hook reads the singleton wired up
28
+ * by `init()`.
29
+ */
30
+ export declare function useSyncStatus(engine?: SyncEngine): SyncConnectionStatus;
package/package.json CHANGED
@@ -3,22 +3,28 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.292",
6
+ "version": "0.3.293",
7
7
  "type": "module",
8
- "main": "src/index.ts",
9
- "types": "src/index.ts",
8
+ "main": "./src/index.ts",
9
+ "types": "./dist/index.d.ts",
10
10
  "scripts": {
11
- "build": "tsc -p tsconfig.json --noEmit",
12
- "check": "tsc -p tsconfig.json --noEmit"
11
+ "build": "tsc -p tsconfig.build.json",
12
+ "check": "tsc -p tsconfig.json --noEmit",
13
+ "prepack": "bun run build"
13
14
  },
14
15
  "dependencies": {
15
- "@pylonsync/sdk": "0.3.292",
16
- "@pylonsync/sync": "0.3.292"
16
+ "@pylonsync/sdk": "0.3.293",
17
+ "@pylonsync/sync": "0.3.293"
17
18
  },
18
19
  "peerDependencies": {
19
20
  "react": ">=19.0.0"
20
21
  },
21
22
  "devDependencies": {
22
23
  "@types/react": "^19.0.0"
23
- }
24
+ },
25
+ "files": [
26
+ "src",
27
+ "dist",
28
+ "README.md"
29
+ ]
24
30
  }
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "types": ["bun-types"]
5
- },
6
- "include": [
7
- "src"
8
- ]
9
- }
10
-