@restless-stream/node 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 +312 -0
- package/package.json +23 -2
package/README.md
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# @restless-stream/node
|
|
2
|
+
|
|
3
|
+
Official Node.js helpers for Restless Stream: <https://restlessapi.stream>
|
|
4
|
+
|
|
5
|
+
This package builds on `@restless-stream/core` and re-exports the core SDK. It adds a Node WebSocket transport based on `ws`, support for WebSocket handshake headers, default Bearer-token auth, reconnect and resume behavior, and Node-friendly SSE helpers.
|
|
6
|
+
|
|
7
|
+
Use this package for server-side Node.js applications. Use `@restless-stream/core` for browser-compatible primitives and `@restless-stream/react` for React hooks.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Node.js `>=20.19.0`.
|
|
12
|
+
- A Restless Stream account and API key.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @restless-stream/node
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @restless-stream/node
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
yarn add @restless-stream/node
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Getting Started
|
|
29
|
+
|
|
30
|
+
Subscribe directly to a REST endpoint over WebSocket. The client API key is sent as `Authorization: Bearer <key>` during the WebSocket handshake.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createNodeRestlessClient } from '@restless-stream/node';
|
|
34
|
+
|
|
35
|
+
const client = createNodeRestlessClient({
|
|
36
|
+
apiKey: process.env.RESTLESS_API_KEY,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
for await (const event of client.direct.subscribeWebSocket({
|
|
40
|
+
url: 'https://api.example.com/orders',
|
|
41
|
+
method: 'GET',
|
|
42
|
+
pollingInterval: 30,
|
|
43
|
+
})) {
|
|
44
|
+
console.log(event);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
You can also create or fetch a managed stream with the inherited core REST methods, then subscribe to `stream.wsUrl` or `stream.sseUrl`.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const stream = await client.streams.create({
|
|
52
|
+
name: 'Orders',
|
|
53
|
+
description: 'Live order feed',
|
|
54
|
+
status: 'ACTIVE',
|
|
55
|
+
method: 'GET',
|
|
56
|
+
url: 'https://api.example.com/orders',
|
|
57
|
+
payloadMode: 'FULL_DATA',
|
|
58
|
+
pollingInterval: 30,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
for await (const event of client.streams.subscribeWs({ wsUrl: stream.wsUrl })) {
|
|
62
|
+
console.log(event);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Client API
|
|
67
|
+
|
|
68
|
+
`createNodeRestlessClient(options)` accepts all `createRestlessClient` options from `@restless-stream/core`.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const client = createNodeRestlessClient({
|
|
72
|
+
apiKey: process.env.RESTLESS_API_KEY,
|
|
73
|
+
apiBaseUrl: 'https://api.restlessapi.stream',
|
|
74
|
+
streamBaseUrl: 'https://stream.restlessapi.stream',
|
|
75
|
+
headers: { 'X-App': 'worker' },
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The returned client includes all core REST and URL APIs, plus Node-specific streaming methods:
|
|
80
|
+
|
|
81
|
+
| Method | Description |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `client.subscribeWebSocket(options)` | Top-level Node WebSocket subscription helper. |
|
|
84
|
+
| `client.subscribeWs(options)` | Alias for `subscribeWebSocket`. |
|
|
85
|
+
| `client.streams.subscribeWebSocket(options)` | Managed stream WebSocket subscription helper. |
|
|
86
|
+
| `client.streams.subscribeWs(options)` | Alias for `streams.subscribeWebSocket`. |
|
|
87
|
+
| `client.direct.subscribeWebSocket(input, options?)` | Direct stream WebSocket subscription helper using the Node transport. |
|
|
88
|
+
| `client.subscribeSse(options)` | Top-level Node SSE helper. |
|
|
89
|
+
| `client.streams.subscribeSse(options)` | Managed stream SSE helper. |
|
|
90
|
+
|
|
91
|
+
All core stream management methods are also available, including `streams.list`, `streams.get`, `streams.create`, `streams.update`, `streams.start`, `streams.stop`, `streams.delete`, `streams.validateApiKey`, `streams.creditUsageStats`, and `streams.connectionSnippets`.
|
|
92
|
+
|
|
93
|
+
## WebSocket Subscriptions
|
|
94
|
+
|
|
95
|
+
Use the standalone helper when you already have a WebSocket URL.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { subscribeWebSocket } from '@restless-stream/node';
|
|
99
|
+
|
|
100
|
+
for await (const event of subscribeWebSocket({
|
|
101
|
+
wsUrl: 'wss://stream.restlessapi.stream/ws?streamId=stream_123',
|
|
102
|
+
apiKey: process.env.RESTLESS_API_KEY,
|
|
103
|
+
})) {
|
|
104
|
+
console.log(event);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
WebSocket options:
|
|
109
|
+
|
|
110
|
+
| Option | Description |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `wsUrl` | WebSocket URL returned by Restless Stream. |
|
|
113
|
+
| `websocketUrl` | Alias for `wsUrl`. |
|
|
114
|
+
| `url` | Alias for `wsUrl`; useful when adapting generic stream records. |
|
|
115
|
+
| `apiKey` | Sends `Authorization: Bearer <apiKey>` unless an Authorization header is already provided. |
|
|
116
|
+
| `protocols` | Optional WebSocket subprotocol or list of subprotocols. |
|
|
117
|
+
| `headers` | Additional WebSocket handshake headers. |
|
|
118
|
+
| `signal` | `AbortSignal` used to stop iteration and close the socket. |
|
|
119
|
+
| `cursor` | Initial cursor used for resume. Updated automatically from received events. |
|
|
120
|
+
| `since` | Initial timestamp or cursor used only until a cursor is observed. |
|
|
121
|
+
| `reconnect` | `false`, `true`, or reconnect policy object. Defaults to enabled. |
|
|
122
|
+
| `clientOptions` | Extra options passed to the `ws` client. |
|
|
123
|
+
| `parse` | Set `false` to yield raw text frames instead of parsed events. |
|
|
124
|
+
| `parser` | Custom parser for text or JSON messages. |
|
|
125
|
+
|
|
126
|
+
`headers` override `clientOptions.headers` with the same key. If `apiKey` is set, an `Authorization` header is added only when no Authorization header already exists.
|
|
127
|
+
|
|
128
|
+
## Reconnect and Resume
|
|
129
|
+
|
|
130
|
+
Reconnects are enabled by default for abnormal closes and connection errors. Normal close codes `1000` and `1001` end the iterator.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
for await (const event of subscribeWebSocket({
|
|
134
|
+
wsUrl: 'wss://stream.restlessapi.stream/ws?streamId=stream_123',
|
|
135
|
+
apiKey: process.env.RESTLESS_API_KEY,
|
|
136
|
+
since: '2026-01-01T00:00:00Z',
|
|
137
|
+
reconnect: {
|
|
138
|
+
maxRetries: 5,
|
|
139
|
+
initialDelayMs: 250,
|
|
140
|
+
maxDelayMs: 5000,
|
|
141
|
+
factor: 2,
|
|
142
|
+
},
|
|
143
|
+
})) {
|
|
144
|
+
console.log(event);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Reconnect policy fields:
|
|
149
|
+
|
|
150
|
+
| Field | Default | Description |
|
|
151
|
+
| --- | --- | --- |
|
|
152
|
+
| `enabled` | `true` | Enables reconnects after retryable closes and errors. |
|
|
153
|
+
| `maxRetries` | `Infinity` | Maximum reconnect attempts. |
|
|
154
|
+
| `initialDelayMs` | `250` | First reconnect delay. |
|
|
155
|
+
| `maxDelayMs` | `5000` | Maximum reconnect delay. |
|
|
156
|
+
| `factor` | `2` | Exponential backoff multiplier. |
|
|
157
|
+
|
|
158
|
+
Resume cursor detection checks received events in this order:
|
|
159
|
+
|
|
160
|
+
| Source | Description |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| `cursor` | Preferred cursor field. |
|
|
163
|
+
| `id` | Generic event ID. |
|
|
164
|
+
| `eventId` | Browser-style event ID field. |
|
|
165
|
+
| `lastEventId` | Last-event ID field. |
|
|
166
|
+
| `meta.timestamp` | Restless runtime event timestamp converted to Unix milliseconds. |
|
|
167
|
+
|
|
168
|
+
The subscription appends `cursor` on reconnect once a cursor is known. Before that, it appends `since` when provided.
|
|
169
|
+
|
|
170
|
+
## Message Parsing
|
|
171
|
+
|
|
172
|
+
By default, WebSocket frames are parsed as JSON.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
for await (const event of subscribeWebSocket<{ type: string }>({
|
|
176
|
+
wsUrl: 'wss://stream.restlessapi.stream/ws?streamId=stream_123',
|
|
177
|
+
})) {
|
|
178
|
+
console.log(event.type);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Yield raw text frames with `parse: false`.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
for await (const text of subscribeWebSocket<string>({
|
|
186
|
+
wsUrl: 'wss://stream.restlessapi.stream/ws?streamId=stream_123',
|
|
187
|
+
parse: false,
|
|
188
|
+
})) {
|
|
189
|
+
console.log(text);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Provide a custom parser when you want your own validation or transformation.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
for await (const event of subscribeWebSocket({
|
|
197
|
+
wsUrl: 'wss://stream.restlessapi.stream/ws?streamId=stream_123',
|
|
198
|
+
parser(message) {
|
|
199
|
+
const value = typeof message === 'string' ? JSON.parse(message) : message;
|
|
200
|
+
return { receivedAt: new Date(), value };
|
|
201
|
+
},
|
|
202
|
+
})) {
|
|
203
|
+
console.log(event.receivedAt, event.value);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Application-level `ping` and `pong` messages are hidden from the iterator. A received `ping` is answered with `pong` when the socket is open.
|
|
208
|
+
|
|
209
|
+
## SSE Subscriptions
|
|
210
|
+
|
|
211
|
+
Node SSE helpers delegate to the core SSE implementation and yield raw SSE message objects with `data`, optional `parsed`, `id`, `event`, and `retry` fields.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import { subscribeSse } from '@restless-stream/node';
|
|
215
|
+
|
|
216
|
+
for await (const message of subscribeSse({
|
|
217
|
+
sseUrl: 'https://stream.restlessapi.stream?streamId=stream_123',
|
|
218
|
+
clientOptions: { apiKey: process.env.RESTLESS_API_KEY },
|
|
219
|
+
cursor: '1700000000000',
|
|
220
|
+
reconnect: false,
|
|
221
|
+
})) {
|
|
222
|
+
console.log(message.id, message.parsed ?? message.data);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
You can also pass an existing client explicitly.
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
for await (const message of subscribeSse(client, {
|
|
230
|
+
sseUrl: stream.sseUrl,
|
|
231
|
+
since: '2026-01-01T00:00:00Z',
|
|
232
|
+
})) {
|
|
233
|
+
console.log(message);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
SSE options include `sseUrl`, `cursor`, `since`, `signal`, `reconnect`, `reconnectDelayMs`, and `maxReconnectDelayMs`.
|
|
238
|
+
|
|
239
|
+
Use `buildNodeSseUrl` to append resume parameters without subscribing.
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import { buildNodeSseUrl } from '@restless-stream/node';
|
|
243
|
+
|
|
244
|
+
const url = buildNodeSseUrl({
|
|
245
|
+
sseUrl: stream.sseUrl,
|
|
246
|
+
cursor: '42',
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Direct Subscriptions
|
|
251
|
+
|
|
252
|
+
Direct WebSocket subscriptions build the runtime URL through the core client and then use the Node transport.
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
for await (const event of client.direct.subscribeWebSocket(
|
|
256
|
+
{
|
|
257
|
+
url: 'https://api.example.com/search',
|
|
258
|
+
method: 'POST',
|
|
259
|
+
body: { query: 'restless' },
|
|
260
|
+
payloadMode: 'FULL_DATA',
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
cursor: '42',
|
|
264
|
+
reconnect: { maxRetries: 3 },
|
|
265
|
+
},
|
|
266
|
+
)) {
|
|
267
|
+
console.log(event);
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
For reusable URLs, create a direct session first with the inherited core API.
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
const session = await client.direct.createSession({
|
|
275
|
+
dedupeKey: 'orders-feed-v1',
|
|
276
|
+
method: 'GET',
|
|
277
|
+
url: 'https://api.example.com/orders',
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
for await (const event of client.streams.subscribeWs({ wsUrl: session.wsUrl })) {
|
|
281
|
+
console.log(event);
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Public Exports
|
|
286
|
+
|
|
287
|
+
| Export | Purpose |
|
|
288
|
+
| --- | --- |
|
|
289
|
+
| `createNodeRestlessClient` | Creates a core client with Node-specific streaming helpers. |
|
|
290
|
+
| `subscribeWebSocket` | Standalone Node WebSocket async iterable. |
|
|
291
|
+
| `subscribeSse` | Standalone or client-bound Node SSE helper. |
|
|
292
|
+
| `buildNodeSseUrl` | Adds `cursor` and `since` resume params to an SSE URL. |
|
|
293
|
+
| `CoreRestlessClientOptions` | Type alias for core client options. |
|
|
294
|
+
| `NodeRestlessClientOptions` | Node client options. |
|
|
295
|
+
| `NodeRestlessClient` | Node client interface. |
|
|
296
|
+
| `NodeStreamsClient` | Node stream resource interface. |
|
|
297
|
+
| `NodeDirectClient` | Node direct resource interface. |
|
|
298
|
+
| `NodeWebSocketSubscriptionOptions` | WebSocket subscription options. |
|
|
299
|
+
| `NodeWebSocketReconnectOptions` | WebSocket reconnect policy. |
|
|
300
|
+
| `NodeSseSubscriptionOptions` | SSE subscription options. |
|
|
301
|
+
| `NodeSseHelperOptions` | Standalone SSE helper options. |
|
|
302
|
+
| Core exports | Everything exported by `@restless-stream/core` is re-exported. |
|
|
303
|
+
|
|
304
|
+
## Development
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
cd packages/typescript
|
|
308
|
+
pnpm install
|
|
309
|
+
pnpm --filter @restless-stream/node typecheck
|
|
310
|
+
pnpm --filter @restless-stream/node test
|
|
311
|
+
pnpm --filter @restless-stream/node build
|
|
312
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@restless-stream/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Node.js SDK helpers for Restless Stream.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/restless-stream/sdk.git",
|
|
9
|
+
"directory": "packages/typescript/node"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/restless-stream/sdk/tree/main/packages/typescript/node#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/restless-stream/sdk/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"restless-stream",
|
|
17
|
+
"sdk",
|
|
18
|
+
"sse",
|
|
19
|
+
"websocket",
|
|
20
|
+
"streaming",
|
|
21
|
+
"node"
|
|
22
|
+
],
|
|
5
23
|
"type": "module",
|
|
6
24
|
"main": "./dist/index.cjs",
|
|
7
25
|
"module": "./dist/index.js",
|
|
@@ -9,6 +27,9 @@
|
|
|
9
27
|
"files": [
|
|
10
28
|
"dist"
|
|
11
29
|
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
12
33
|
"exports": {
|
|
13
34
|
".": {
|
|
14
35
|
"types": "./dist/index.d.ts",
|
|
@@ -18,7 +39,7 @@
|
|
|
18
39
|
},
|
|
19
40
|
"dependencies": {
|
|
20
41
|
"ws": "^8.18.0",
|
|
21
|
-
"@restless-stream/core": "0.1.
|
|
42
|
+
"@restless-stream/core": "0.1.1"
|
|
22
43
|
},
|
|
23
44
|
"devDependencies": {
|
|
24
45
|
"@types/ws": "^8.18.1"
|