@pylonsync/react 0.3.292 → 0.3.294
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/Form.d.ts +11 -0
- package/dist/Image.d.ts +39 -0
- package/dist/Link.d.ts +27 -0
- package/dist/db.d.ts +163 -0
- package/dist/hooks.d.ts +388 -0
- package/dist/index.d.ts +189 -0
- package/dist/ssr.d.ts +415 -0
- package/dist/typed.d.ts +75 -0
- package/dist/useRoom.d.ts +93 -0
- package/dist/useRouter.d.ts +74 -0
- package/dist/useSession.d.ts +41 -0
- package/dist/useShard.d.ts +58 -0
- package/dist/useSyncStatus.d.ts +30 -0
- package/package.json +14 -8
- package/tsconfig.json +0 -10
|
@@ -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.
|
|
6
|
+
"version": "0.3.294",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"main": "src/index.ts",
|
|
9
|
-
"types": "
|
|
8
|
+
"main": "./src/index.ts",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc -p tsconfig.json
|
|
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.
|
|
16
|
-
"@pylonsync/sync": "0.3.
|
|
16
|
+
"@pylonsync/sdk": "0.3.294",
|
|
17
|
+
"@pylonsync/sync": "0.3.294"
|
|
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
|
}
|