@parcae/sdk 0.0.1 → 0.0.3

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 +256 -0
  2. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,256 @@
1
+ # @parcae/sdk
2
+
3
+ Client SDK for Parcae backends. Pluggable transport layer (Socket.IO or SSE), React provider, and hooks for realtime data fetching.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @parcae/sdk @parcae/model
9
+ ```
10
+
11
+ ## Create a Client
12
+
13
+ ```typescript
14
+ import { createClient } from "@parcae/sdk";
15
+
16
+ // Socket.IO (default) — bidirectional, realtime subscriptions
17
+ const client = createClient({ url: "http://localhost:3000" });
18
+
19
+ // SSE — HTTP + Server-Sent Events, simpler infrastructure
20
+ const client = createClient({ url: "http://localhost:3000", transport: "sse" });
21
+
22
+ // Custom transport
23
+ const client = createClient({ url: "http://localhost:3000", transport: myTransport });
24
+ ```
25
+
26
+ ### ClientConfig
27
+
28
+ ```typescript
29
+ interface ClientConfig {
30
+ url: string;
31
+ key?: string | null | (() => Promise<string | null>);
32
+ version?: string; // default: "v1"
33
+ transport?: "socket" | "sse" | Transport; // default: "socket"
34
+ }
35
+ ```
36
+
37
+ ### ParcaeClient
38
+
39
+ `createClient()` returns a `ParcaeClient` with the following API:
40
+
41
+ ```typescript
42
+ interface ParcaeClient {
43
+ // HTTP methods (delegated to transport)
44
+ get(path, data?): Promise<any>;
45
+ post(path, data?): Promise<any>;
46
+ put(path, data?): Promise<any>;
47
+ patch(path, data?): Promise<any>;
48
+ delete(path, data?): Promise<any>;
49
+
50
+ // Realtime
51
+ subscribe(event, handler): () => void;
52
+ unsubscribe(event, handler?): void;
53
+ send(event, ...args): void;
54
+
55
+ // Connection
56
+ readonly isConnected: boolean;
57
+ readonly isLoading: boolean;
58
+ loading: Promise<void>;
59
+ on(event, handler): void;
60
+ off(event, handler?): void;
61
+ disconnect(): void;
62
+ reconnect(): Promise<void>;
63
+
64
+ // Auth
65
+ setKey(key): Promise<void>;
66
+ readonly authVersion: number;
67
+ }
68
+ ```
69
+
70
+ The client also calls `Model.use(new FrontendAdapter(transport))` automatically, so `Model.where()`, `Model.findById()`, and other static methods work immediately after creating a client.
71
+
72
+ ## Transports
73
+
74
+ ### SocketTransport
75
+
76
+ Default transport. Socket.IO over WebSocket.
77
+
78
+ - Bidirectional communication
79
+ - Connection pooling (shared socket per url:version)
80
+ - Request/response via `"call"` event with request IDs
81
+ - gzip + compress-json response encoding
82
+ - GET request deduplication (in-flight coalescing)
83
+ - 30s connection wait timeout for pending requests
84
+ - Auth via `"authenticate"` event
85
+
86
+ ### SSETransport
87
+
88
+ HTTP + Server-Sent Events. Better for read-heavy workloads or environments where WebSocket is not available.
89
+
90
+ - Standard `fetch()` for request/response
91
+ - `EventSource` per subscription channel at `/__events/{event}`
92
+ - Control messages via POST to `/__control`
93
+ - Bearer token auth via `Authorization` header
94
+
95
+ ## React
96
+
97
+ ### ParcaeProvider
98
+
99
+ Wrap your app in `ParcaeProvider` to make the client available to hooks.
100
+
101
+ ```tsx
102
+ import { ParcaeProvider } from "@parcae/sdk/react";
103
+
104
+ // With a pre-created client
105
+ const client = createClient({ url: "http://localhost:3000" });
106
+
107
+ <ParcaeProvider client={client}>
108
+ <App />
109
+ </ParcaeProvider>
110
+
111
+ // Or with inline config
112
+ <ParcaeProvider
113
+ url="http://localhost:3000"
114
+ apiKey={token}
115
+ userId={user.id}
116
+ transport="socket"
117
+ onReady={(client) => console.log("connected")}
118
+ onError={(err) => console.error(err)}
119
+ >
120
+ <App />
121
+ </ParcaeProvider>
122
+ ```
123
+
124
+ Re-authenticates automatically when `userId` changes. Forwards transport errors via `onError`.
125
+
126
+ ### useQuery
127
+
128
+ Reactive data fetching with realtime subscriptions. Returns typed model instances that update in place when the server pushes changes.
129
+
130
+ ```tsx
131
+ import { useQuery } from "@parcae/sdk/react";
132
+
133
+ function PostList() {
134
+ const { items, loading, error, refetch } = useQuery(
135
+ Post.where({ published: true }).orderBy("createdAt", "desc")
136
+ );
137
+
138
+ if (loading) return <p>Loading...</p>;
139
+ if (error) return <p>Error: {error.message}</p>;
140
+
141
+ return items.map((post) => (
142
+ <article key={post.id}>
143
+ <h2>{post.title}</h2>
144
+ <Suspense fallback="...">
145
+ <span>{post.user.name}</span>
146
+ </Suspense>
147
+ </article>
148
+ ));
149
+ }
150
+ ```
151
+
152
+ **How it works:**
153
+
154
+ 1. On mount, calls `chain.find()` to fetch initial data
155
+ 2. Sends `subscribe:query` to the server with the serialized query steps
156
+ 3. Server re-evaluates the query when matching models change
157
+ 4. Diff ops (`add`, `remove`, `update`) are pushed and applied client-side
158
+ 5. Uses `useSyncExternalStore` for tear-safe rendering
159
+
160
+ **Caching:**
161
+
162
+ - Global query cache keyed by `modelType:authVersion:steps`
163
+ - Reference counting with 60s GC timeout after last subscriber unmounts
164
+ - Debounced diff-op application (default 100ms)
165
+
166
+ Pass `null` or `undefined` to skip the query:
167
+
168
+ ```tsx
169
+ const { items } = useQuery(userId ? Post.where({ user: userId }) : null);
170
+ ```
171
+
172
+ ### useApi
173
+
174
+ Pre-bound HTTP methods from the client.
175
+
176
+ ```tsx
177
+ import { useApi } from "@parcae/sdk/react";
178
+
179
+ function UploadButton() {
180
+ const { post } = useApi();
181
+
182
+ const upload = async (file) => {
183
+ const result = await post("/v1/media/upload", { file });
184
+ };
185
+ }
186
+ ```
187
+
188
+ ### useSDK
189
+
190
+ Raw client instance access.
191
+
192
+ ```tsx
193
+ import { useSDK } from "@parcae/sdk/react";
194
+
195
+ const client = useSDK();
196
+ client.send("some:event", data);
197
+ ```
198
+
199
+ ### useConnectionStatus
200
+
201
+ Connection state for the transport.
202
+
203
+ ```tsx
204
+ import { useConnectionStatus } from "@parcae/sdk/react";
205
+
206
+ function StatusBadge() {
207
+ const { isConnected, isLoading } = useConnectionStatus();
208
+
209
+ if (isLoading) return <span>Connecting...</span>;
210
+ return <span>{isConnected ? "Online" : "Offline"}</span>;
211
+ }
212
+ ```
213
+
214
+ ### useSetting
215
+
216
+ Persistent key-value user settings. GETs on mount, PUTs on update.
217
+
218
+ ```tsx
219
+ import { useSetting } from "@parcae/sdk/react";
220
+
221
+ function ThemeToggle() {
222
+ const [theme, setTheme, { isLoading }] = useSetting("theme", "light");
223
+
224
+ return (
225
+ <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
226
+ {theme}
227
+ </button>
228
+ );
229
+ }
230
+ ```
231
+
232
+ ## Exports
233
+
234
+ ```typescript
235
+ // Main
236
+ import { createClient, SocketTransport, SSETransport } from "@parcae/sdk";
237
+ import type { ClientConfig, ParcaeClient, Transport } from "@parcae/sdk";
238
+
239
+ // React
240
+ import {
241
+ ParcaeProvider,
242
+ useQuery,
243
+ useApi,
244
+ useSDK,
245
+ useConnectionStatus,
246
+ useSetting,
247
+ useParcae,
248
+ } from "@parcae/sdk/react";
249
+
250
+ // Re-exports from @parcae/model
251
+ import { Model, FrontendAdapter } from "@parcae/sdk";
252
+ ```
253
+
254
+ ## License
255
+
256
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parcae/sdk",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Parcae SDK — client transport and React hooks for Parcae backends",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "clean": "rm -rf dist"
27
27
  },
28
28
  "dependencies": {
29
- "@parcae/model": "^0.0.1",
29
+ "@parcae/model": "^0.0.3",
30
30
  "compress-json": "^3.1.0",
31
31
  "eventemitter3": "^5.0.1",
32
32
  "pako": "^2.1.0",