@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.
- package/README.md +256 -0
- 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.
|
|
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.
|
|
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",
|