@restless-stream/react 0.1.0 → 0.1.1
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 +263 -0
- package/package.json +23 -2
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# @restless-stream/react
|
|
2
|
+
|
|
3
|
+
Lightweight React bindings for Restless Stream: <https://restlessapi.stream>
|
|
4
|
+
|
|
5
|
+
This package provides a `RestlessProvider`, access to the core Restless Stream client through context, and hooks for consuming SSE stream URLs in React components.
|
|
6
|
+
|
|
7
|
+
Use this package when a React app already has a Restless Stream SSE URL, managed stream object, or direct-session response. Use `@restless-stream/core` or `@restless-stream/node` on a trusted backend to create streams or sessions when you do not want to expose API keys to the browser.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- React `>=18.2 <20`.
|
|
12
|
+
- A Restless Stream account and API key when creating a client in the provider.
|
|
13
|
+
- An SSE URL from a managed stream or direct session.
|
|
14
|
+
|
|
15
|
+
Do not put long-lived API keys in public browser bundles. For browser applications, prefer generating stream URLs or direct sessions on your backend and passing only the runtime URL to React.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @restless-stream/react @restless-stream/core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @restless-stream/react @restless-stream/core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add @restless-stream/react @restless-stream/core
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`@restless-stream/core` is installed as a dependency by package managers, but adding it explicitly can make shared client types easier to import in your application.
|
|
32
|
+
|
|
33
|
+
## Getting Started
|
|
34
|
+
|
|
35
|
+
Wrap your app in `RestlessProvider`, then subscribe with `useStreamSubscription`.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { RestlessProvider, useStreamSubscription } from '@restless-stream/react';
|
|
39
|
+
|
|
40
|
+
function StreamEvents({ sseUrl }: { sseUrl: string }) {
|
|
41
|
+
const { error, events, status } = useStreamSubscription({
|
|
42
|
+
enabled: Boolean(sseUrl),
|
|
43
|
+
maxEvents: 100,
|
|
44
|
+
sseUrl,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (error) {
|
|
48
|
+
return <p>{error.message}</p>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return <pre>{JSON.stringify({ status, events }, null, 2)}</pre>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function App({ apiKey, sseUrl }: { apiKey: string; sseUrl: string }) {
|
|
55
|
+
return (
|
|
56
|
+
<RestlessProvider apiKey={apiKey}>
|
|
57
|
+
<StreamEvents sseUrl={sseUrl} />
|
|
58
|
+
</RestlessProvider>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If you do not want to use context, pass an explicit core client to the hook.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { createRestlessClient } from '@restless-stream/core';
|
|
67
|
+
import { useStreamSubscription } from '@restless-stream/react';
|
|
68
|
+
|
|
69
|
+
const client = createRestlessClient({ apiKey: 'rs_server_or_private_runtime_key' });
|
|
70
|
+
|
|
71
|
+
function StreamEvents({ sseUrl }: { sseUrl: string }) {
|
|
72
|
+
const subscription = useStreamSubscription({ client, sseUrl });
|
|
73
|
+
return <pre>{JSON.stringify(subscription.latestEvent, null, 2)}</pre>;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Provider API
|
|
78
|
+
|
|
79
|
+
`RestlessProvider` creates or provides a core Restless Stream client.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<RestlessProvider
|
|
83
|
+
apiKey={apiKey}
|
|
84
|
+
apiBaseUrl="https://api.restlessapi.stream"
|
|
85
|
+
streamBaseUrl="https://stream.restlessapi.stream"
|
|
86
|
+
headers={{ 'X-App': 'dashboard' }}
|
|
87
|
+
>
|
|
88
|
+
<App />
|
|
89
|
+
</RestlessProvider>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Provider props:
|
|
93
|
+
|
|
94
|
+
| Prop | Description |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `client` | Existing `RestlessClient`. When provided, config props are ignored. |
|
|
97
|
+
| `config` | Full core client config object. |
|
|
98
|
+
| `apiKey` | API key passed to `createRestlessClient`. |
|
|
99
|
+
| `apiBaseUrl` | REST API base URL. |
|
|
100
|
+
| `streamBaseUrl` | Runtime stream base URL. |
|
|
101
|
+
| `fetch` | Custom fetch implementation. |
|
|
102
|
+
| `headers` | Default REST headers. |
|
|
103
|
+
| `children` | React children. |
|
|
104
|
+
|
|
105
|
+
Use `useRestlessClient()` to read the client from context.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { useRestlessClient } from '@restless-stream/react';
|
|
109
|
+
|
|
110
|
+
function StreamCount() {
|
|
111
|
+
const client = useRestlessClient();
|
|
112
|
+
// Use the client in effects, event handlers, or data loading code.
|
|
113
|
+
return <button onClick={() => void client.streams.list()}>Refresh</button>;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`useRestlessClient` throws when used outside `RestlessProvider`.
|
|
118
|
+
|
|
119
|
+
## `useStreamSubscription`
|
|
120
|
+
|
|
121
|
+
`useStreamSubscription(options)` subscribes to an SSE URL through the core client's `streams.subscribeSse` method.
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
const {
|
|
125
|
+
connect,
|
|
126
|
+
disconnect,
|
|
127
|
+
error,
|
|
128
|
+
events,
|
|
129
|
+
isConnected,
|
|
130
|
+
isConnecting,
|
|
131
|
+
latestEvent,
|
|
132
|
+
reset,
|
|
133
|
+
status,
|
|
134
|
+
} = useStreamSubscription({
|
|
135
|
+
sseUrl,
|
|
136
|
+
enabled: true,
|
|
137
|
+
maxEvents: 100,
|
|
138
|
+
reconnect: true,
|
|
139
|
+
maxReconnectAttempts: 3,
|
|
140
|
+
reconnectDelayMs: (attempt) => attempt * 1000,
|
|
141
|
+
onEvent: (event) => console.log(event),
|
|
142
|
+
onError: (error) => console.error(error),
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Options:
|
|
147
|
+
|
|
148
|
+
| Option | Description |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `sseUrl` | Runtime SSE URL to subscribe to. |
|
|
151
|
+
| `client` | Explicit core `RestlessClient`. Uses provider context when omitted. |
|
|
152
|
+
| `enabled` | Starts the subscription automatically. Defaults to `true`. |
|
|
153
|
+
| `maxEvents` | Maximum number of events kept in state. Defaults to `100`. Use `0` to keep none. |
|
|
154
|
+
| `maxReconnectAttempts` | Hook-level retry count after subscription failures. Defaults to `0`. |
|
|
155
|
+
| `onEvent` | Callback fired for each parsed Restless Stream event. |
|
|
156
|
+
| `onError` | Callback fired when the subscription throws. |
|
|
157
|
+
| `reconnect` | Enables hook-level reconnect attempts. Defaults to `false`. |
|
|
158
|
+
| `reconnectDelayMs` | Number or function returning the delay for each reconnect attempt. Defaults to `1000`. |
|
|
159
|
+
| `headers` | Additional SSE request headers. |
|
|
160
|
+
| `allowApiKeyInUrl` | Opts into API key query-parameter auth for runtime URLs. Use carefully. |
|
|
161
|
+
| `cursor` | Initial cursor or event ID. |
|
|
162
|
+
| `since` | Initial timestamp or cursor. |
|
|
163
|
+
|
|
164
|
+
Result fields:
|
|
165
|
+
|
|
166
|
+
| Field | Description |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| `events` | Bounded array of received events. |
|
|
169
|
+
| `latestEvent` | Most recent event, or `null`. |
|
|
170
|
+
| `error` | Last normalized `Error`, or `null`. |
|
|
171
|
+
| `status` | `idle`, `connecting`, `open`, `reconnecting`, `closed`, or `error`. |
|
|
172
|
+
| `isConnected` | `true` when `status === 'open'`. |
|
|
173
|
+
| `isConnecting` | `true` when `status` is `connecting` or `reconnecting`. |
|
|
174
|
+
| `connect()` | Starts or restarts the subscription. |
|
|
175
|
+
| `disconnect()` | Aborts the active subscription and sets status to `closed`. |
|
|
176
|
+
| `reset()` | Clears buffered events and error state. |
|
|
177
|
+
|
|
178
|
+
Manual connection example:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
function ManualStream({ sseUrl }: { sseUrl: string }) {
|
|
182
|
+
const stream = useStreamSubscription({
|
|
183
|
+
enabled: false,
|
|
184
|
+
sseUrl,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<section>
|
|
189
|
+
<p>Status: {stream.status}</p>
|
|
190
|
+
<button onClick={stream.connect}>Connect</button>
|
|
191
|
+
<button onClick={stream.disconnect}>Disconnect</button>
|
|
192
|
+
<button onClick={stream.reset}>Clear events</button>
|
|
193
|
+
</section>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## `useManagedStream`
|
|
199
|
+
|
|
200
|
+
`useManagedStream` is a convenience wrapper for managed stream objects. It accepts either `sseUrl` directly or a `stream` object with an `sseUrl` field.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
function ManagedStream({ stream }: { stream: { sseUrl?: string | null } | null }) {
|
|
204
|
+
const { latestEvent, status } = useManagedStream({
|
|
205
|
+
stream,
|
|
206
|
+
maxEvents: 25,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return <pre>{JSON.stringify({ latestEvent, status }, null, 2)}</pre>;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The hook auto-enables only when a non-empty URL is available, unless you pass `enabled` explicitly.
|
|
214
|
+
|
|
215
|
+
## `useDirectStream`
|
|
216
|
+
|
|
217
|
+
`useDirectStream` is a convenience wrapper for direct stream URLs or direct-session responses. It accepts `directUrl`, `sseUrl`, or `session.sseUrl`.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
function DirectSessionEvents({ session }: { session: { sseUrl?: string | null } | null }) {
|
|
221
|
+
const { latestEvent, status } = useDirectStream({
|
|
222
|
+
session,
|
|
223
|
+
reconnect: true,
|
|
224
|
+
maxReconnectAttempts: 3,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return <pre>{JSON.stringify({ latestEvent, status }, null, 2)}</pre>;
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`useDirectStream` does not create direct sessions. Create sessions with a trusted backend or the core SDK, then pass the returned `sseUrl` into React.
|
|
232
|
+
|
|
233
|
+
## Browser Auth Notes
|
|
234
|
+
|
|
235
|
+
`allowApiKeyInUrl` is supported by the underlying core client, but URLs can appear in logs, analytics, browser history, and referrer headers. Prefer header-based auth from trusted server runtimes or backend-created runtime URLs.
|
|
236
|
+
|
|
237
|
+
## Public Exports
|
|
238
|
+
|
|
239
|
+
| Export | Purpose |
|
|
240
|
+
| --- | --- |
|
|
241
|
+
| `RestlessProvider` | React context provider for a core Restless client. |
|
|
242
|
+
| `RestlessProviderProps` | Provider props type. |
|
|
243
|
+
| `useRestlessClient` | Reads the client from provider context. |
|
|
244
|
+
| `useStreamSubscription` | Main SSE subscription hook. |
|
|
245
|
+
| `useManagedStream` | Hook wrapper for managed stream objects or managed SSE URLs. |
|
|
246
|
+
| `useDirectStream` | Hook wrapper for direct URLs or direct-session responses. |
|
|
247
|
+
| `DEFAULT_MAX_EVENTS` | Default event buffer size, `100`. |
|
|
248
|
+
| `StreamSubscriptionStatus` | Subscription status union. |
|
|
249
|
+
| `StreamSubscriptionControls` | `connect`, `disconnect`, and `reset` controls. |
|
|
250
|
+
| `UseStreamSubscriptionOptions` | Main hook options type. |
|
|
251
|
+
| `UseStreamSubscriptionResult` | Main hook result type. |
|
|
252
|
+
| `UseManagedStreamOptions` | Managed stream hook options type. |
|
|
253
|
+
| `UseDirectStreamOptions` | Direct stream hook options type. |
|
|
254
|
+
|
|
255
|
+
## Development
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
cd packages/typescript
|
|
259
|
+
pnpm install
|
|
260
|
+
pnpm --filter @restless-stream/react typecheck
|
|
261
|
+
pnpm --filter @restless-stream/react test
|
|
262
|
+
pnpm --filter @restless-stream/react build
|
|
263
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@restless-stream/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Lightweight React bindings for the Restless Stream SDK.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/restless-stream/sdk.git",
|
|
9
|
+
"directory": "packages/typescript/react"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/restless-stream/sdk/tree/main/packages/typescript/react#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/restless-stream/sdk/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"restless-stream",
|
|
17
|
+
"sdk",
|
|
18
|
+
"sse",
|
|
19
|
+
"streaming",
|
|
20
|
+
"react",
|
|
21
|
+
"hooks"
|
|
22
|
+
],
|
|
5
23
|
"type": "module",
|
|
6
24
|
"sideEffects": false,
|
|
7
25
|
"main": "./dist/index.cjs",
|
|
@@ -17,11 +35,14 @@
|
|
|
17
35
|
"files": [
|
|
18
36
|
"dist"
|
|
19
37
|
],
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
20
41
|
"peerDependencies": {
|
|
21
42
|
"react": ">=18.2 <20"
|
|
22
43
|
},
|
|
23
44
|
"dependencies": {
|
|
24
|
-
"@restless-stream/core": "0.1.
|
|
45
|
+
"@restless-stream/core": "0.1.1"
|
|
25
46
|
},
|
|
26
47
|
"devDependencies": {
|
|
27
48
|
"@testing-library/react": "^16.3.0",
|