@pylonsync/react 0.3.149 → 0.3.152

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/README.md +154 -0
  2. package/package.json +3 -3
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # @pylonsync/react
2
+
3
+ React hooks for [Pylon](https://pylonsync.com) — live queries, optimistic mutations, reactive server functions, search, presence, all backed by a local sync replica that re-renders on every change.
4
+
5
+ ```sh
6
+ bun add @pylonsync/react
7
+ # or: npm i @pylonsync/react
8
+ ```
9
+
10
+ ## Quick start
11
+
12
+ ```tsx
13
+ // app entry — once, anywhere before your first hook fires
14
+ import { init } from "@pylonsync/react";
15
+
16
+ init({ baseUrl: "http://localhost:4321" });
17
+
18
+ // any component
19
+ import { db } from "@pylonsync/react";
20
+
21
+ type Todo = { id: string; title: string; done: boolean };
22
+
23
+ export function TodoList() {
24
+ const { data: todos, loading } = db.useQuery<Todo>("Todo", {
25
+ where: { done: false },
26
+ orderBy: { createdAt: "desc" },
27
+ });
28
+
29
+ const create = db.useMutation<{ title: string }>("createTodo");
30
+
31
+ if (loading) return <Spinner />;
32
+ return (
33
+ <>
34
+ <ul>{todos.map((t) => <li key={t.id}>{t.title}</li>)}</ul>
35
+ <button onClick={() => create.mutate({ title: "Buy milk" })}>+ Add</button>
36
+ </>
37
+ );
38
+ }
39
+ ```
40
+
41
+ `db.useQuery` subscribes to the local sync replica. Inserts (yours or anyone else's), updates, deletes — every component reading this entity re-renders automatically. No queryClient, no manual invalidation, no polling.
42
+
43
+ ## The `db` namespace
44
+
45
+ `db.*` is the ergonomic API for apps that use a single global sync engine (set up via `init()` once at startup). It owns engine lifecycle, optimistic update bookkeeping, and storage namespacing.
46
+
47
+ | Method | What it does |
48
+ | --- | --- |
49
+ | `db.useQuery<T>(entity, opts?)` | Live list of rows |
50
+ | `db.useQueryOne<T>(entity, id)` | Live single row |
51
+ | `db.useInfiniteQuery<T>(entity, opts)` | Cursor-paginated list with `loadMore()` |
52
+ | `db.useReactiveQuery<T>(fnName, args?)` | Convex-style auto-rerunning server query |
53
+ | `db.useAggregate<R>(entity, spec)` | Live count / sum / avg / groupBy |
54
+ | `db.useSearch<T>(entity, spec)` | Full-text + faceted live search |
55
+ | `db.useMutation<I, O>(fnName, opts?)` | Server function call w/ optional optimistic update |
56
+ | `db.useEntity(entity)` | Optimistic CRUD bound to one entity |
57
+ | `db.insert/update/delete` | Imperative writes (returns id) |
58
+ | `db.fn<T>(name, args?)` | Imperative server function call |
59
+ | `db.streamFn(name, args?)` | Async iterable of SSE chunks |
60
+ | `db.uploadFile(blob, opts?)` | Upload to `/api/files/upload` |
61
+ | `db.setPresence(data)` | Publish presence for the current user |
62
+ | `db.publishTopic(topic, data)` | Fire-and-forget pubsub |
63
+ | `db.sync` | The underlying `SyncEngine` for escape hatches |
64
+
65
+ ## Auth
66
+
67
+ ```tsx
68
+ import { useSession } from "@pylonsync/react";
69
+
70
+ function NavBar() {
71
+ const { auth, signOut } = useSession(db.sync);
72
+ if (!auth) return <a href="/login">Sign in</a>;
73
+ return (
74
+ <>
75
+ <span>{auth.user.displayName}</span>
76
+ <button onClick={signOut}>Sign out</button>
77
+ </>
78
+ );
79
+ }
80
+ ```
81
+
82
+ `useSession` returns a live `auth` object (re-renders when the user signs in, signs out, switches org, or has their session revoked from another device). It also exposes `selectOrg`, `clearOrg`, and `refresh` for multi-tenant flows.
83
+
84
+ ## Optimistic mutations
85
+
86
+ ```tsx
87
+ const send = db.useMutation<
88
+ { channelId: string; body: string },
89
+ { messageId: string }
90
+ >("sendMessage", {
91
+ optimistic: (args, ctx) => ({
92
+ entity: "Message",
93
+ data: {
94
+ id: ctx.id, // ghost id; server's reply merges in-place
95
+ channelId: args.channelId,
96
+ body: args.body,
97
+ authorId: me.id,
98
+ createdAt: ctx.now,
99
+ },
100
+ }),
101
+ });
102
+
103
+ await send.mutate({ channelId, body: "Hi!" });
104
+ // Message appears in `db.useQuery("Message", ...)` before the server responds.
105
+ ```
106
+
107
+ The framework paints the ghost into the local store, threads `ctx.id` to the server (where your handler should re-use it), and reconciles on server broadcast. See the [optimistic updates concept doc](https://pylonsync.com/docs/concepts/optimistic-updates) for the full pattern.
108
+
109
+ ## Live presence + rooms
110
+
111
+ ```tsx
112
+ import { useRoom } from "@pylonsync/react";
113
+
114
+ function Editor({ documentId }: { documentId: string }) {
115
+ const room = useRoom(db.sync, `doc:${documentId}`);
116
+ return (
117
+ <div>
118
+ {room.peers.map((p) => (
119
+ <Cursor key={p.id} x={p.presence.x} y={p.presence.y} />
120
+ ))}
121
+ <textarea
122
+ onChange={(e) =>
123
+ room.setPresence({ cursor: e.target.selectionStart })
124
+ }
125
+ />
126
+ </div>
127
+ );
128
+ }
129
+ ```
130
+
131
+ ## Configuration
132
+
133
+ ```ts
134
+ init({
135
+ baseUrl: "http://localhost:4321",
136
+ // Optional — namespaces localStorage keys so multiple Pylon apps
137
+ // on the same origin don't fight over `pylon:token`.
138
+ appName: "my-app",
139
+ });
140
+ ```
141
+
142
+ In browser contexts, omitting `baseUrl` falls back to `window.location.origin` — the right answer for Next.js + Vercel deploys that use a `next.config.js` rewrite to proxy `/api/*` to the backend. In SSR it falls back to `http://localhost:4321` (the `pylon dev` default).
143
+
144
+ ## With Next.js
145
+
146
+ Use [`@pylonsync/next`](https://npmjs.com/package/@pylonsync/next) for App Router server helpers, middleware, and cookie-aware auth. The full deploy story including Vercel env vars and CORS is at [pylonsync.com/docs/operations/vercel](https://pylonsync.com/docs/operations/vercel).
147
+
148
+ ## With React Native
149
+
150
+ Use [`@pylonsync/react-native`](https://npmjs.com/package/@pylonsync/react-native), which ships the same `db.*` API on top of an AsyncStorage-backed sync replica.
151
+
152
+ ## License
153
+
154
+ MIT
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.149",
6
+ "version": "0.3.152",
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.149",
16
- "@pylonsync/sync": "0.3.149"
15
+ "@pylonsync/sdk": "0.3.152",
16
+ "@pylonsync/sync": "0.3.152"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "react": ">=19.0.0"