@fastrelay/js-sdk 0.1.0
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 +134 -0
- package/dist/client.d.ts +159 -0
- package/dist/client.js +515 -0
- package/dist/error.d.ts +28 -0
- package/dist/error.js +24 -0
- package/dist/feed.d.ts +63 -0
- package/dist/feed.js +125 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/polling.d.ts +30 -0
- package/dist/polling.js +82 -0
- package/dist/realtime.d.ts +101 -0
- package/dist/realtime.js +521 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.js +3 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +77 -0
- package/dist/video-upload.d.ts +44 -0
- package/dist/video-upload.js +97 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# fastrelay Feed JS SDK
|
|
2
|
+
|
|
3
|
+
JavaScript/TypeScript SDK for [fastrelay](https://fastrelay.io) activity feeds — feeds, activities, reactions, comments, polls, video uploads, moderation, and realtime updates over WebSocket.
|
|
4
|
+
|
|
5
|
+
- Feed-first API: `fastrelay.feed(group, id)`
|
|
6
|
+
- Client-only by design: user (JWT) auth, no server secrets in the app
|
|
7
|
+
- Zero runtime dependencies — native `fetch`, `FormData`, `WebSocket`
|
|
8
|
+
- Realtime subscriptions with automatic reconnect + token refresh
|
|
9
|
+
- Polling fallback for non-realtime environments
|
|
10
|
+
- Direct-to-storage video uploads (tus / Cloudflare Stream)
|
|
11
|
+
- Structured `FastrelayApiError` with rate-limit metadata
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Node.js >= 18 (>= 22 for native `WebSocket`, or pass `socketFactory`), or any modern browser.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @fastrelay/js-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { FastrelayClient } from '@fastrelay/js-sdk';
|
|
27
|
+
|
|
28
|
+
const fastrelay = new FastrelayClient({
|
|
29
|
+
apiKey: 'your_api_key',
|
|
30
|
+
baseUrl: 'https://api.fastrelay.io',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await fastrelay.connectUser({ id: 'john' }, userJwt, {
|
|
34
|
+
upsertUser: true,
|
|
35
|
+
realtime: true,
|
|
36
|
+
tokenProvider: async () => fetchFreshJwtFromYourBackend(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const timeline = fastrelay.feed('timeline', 'john');
|
|
40
|
+
|
|
41
|
+
// Read
|
|
42
|
+
const page = await timeline.getActivities({ limit: 25 });
|
|
43
|
+
|
|
44
|
+
// Write
|
|
45
|
+
await timeline.addActivity({ type: 'post', text: 'Hello world' });
|
|
46
|
+
|
|
47
|
+
// Realtime — returns an unsubscribe function
|
|
48
|
+
const off = timeline.on('activity.created', (event) => {
|
|
49
|
+
console.log('new activity', event.data);
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
The SDK is client-only: every request is sent with `Authorization: Bearer <user JWT>` once `connectUser()` or `setToken()` has been called. Per-request override via options: `{ auth: 'auto' | 'user' | 'none' }` (`'server'` throws — call server endpoints from your backend).
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
await fastrelay.getActivity('activity_id', { auth: 'user', idempotencyKey: 'key' });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Activities, reactions, comments
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const activity = await fastrelay.addActivity({ feeds: ['user:john'], type: 'post', text: 'hi' });
|
|
65
|
+
await fastrelay.addReaction(activity.id, 'like');
|
|
66
|
+
const comment = await fastrelay.addComment(activity.id, { text: 'Nice!' });
|
|
67
|
+
await fastrelay.addCommentReaction(comment.id, 'like');
|
|
68
|
+
const comments = await fastrelay.listComments(activity.id, { sort: 'top', limit: 10 });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Also available: bookmarks (`addBookmark`/`removeBookmark`/`listBookmarks`), pins (`feed.pinActivity`), polls (`createPoll`, `vote`), follows (`feed.follow`, `feed.listFollowers`), feed members, moderation (`createFlag`, `createMute`), feedback (`submitFeedback`), file upload (`uploadFile`).
|
|
72
|
+
|
|
73
|
+
## Realtime
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const realtime = fastrelay.realtime!;
|
|
77
|
+
realtime.onStateChange((state) => console.log(state)); // connecting/connected/reconnecting/disconnected
|
|
78
|
+
realtime.onError((error) => console.warn(error.code, error.message));
|
|
79
|
+
realtime.onVideoStatus((event) => console.log(event.type, event.videoId));
|
|
80
|
+
|
|
81
|
+
const off = realtime.subscribeToFeed('timeline:john', (event) => { ... });
|
|
82
|
+
off(); // unsubscribes (frames are batched over the socket)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Reconnects use exponential backoff with jitter; close code `4003` triggers a token refresh via `tokenProvider`, `4002`/`4029` are fatal. Duplicate `eventId`s are dropped.
|
|
86
|
+
|
|
87
|
+
## Polling fallback
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { FeedPollingService } from '@fastrelay/js-sdk';
|
|
91
|
+
|
|
92
|
+
const polling = new FeedPollingService(fastrelay);
|
|
93
|
+
const stop = polling.pollFeed('timeline', 'john', {
|
|
94
|
+
limit: 25,
|
|
95
|
+
onPage: (page) => render(page.data),
|
|
96
|
+
});
|
|
97
|
+
// polling.pause() / polling.resume() / polling.dispose()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Video upload
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { uploadVideoBytes } from '@fastrelay/js-sdk';
|
|
104
|
+
|
|
105
|
+
const result = await uploadVideoBytes(fastrelay, {
|
|
106
|
+
data: fileBytes, // Uint8Array | ArrayBuffer
|
|
107
|
+
filename: 'clip.mp4',
|
|
108
|
+
mimeType: 'video/mp4',
|
|
109
|
+
onProgress: ({ fraction }) => console.log(Math.round(fraction * 100), '%'),
|
|
110
|
+
});
|
|
111
|
+
// Wait for realtime `video.ready`, or poll fastrelay.getVideo(result.videoId)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Error handling
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { FastrelayApiError } from '@fastrelay/js-sdk';
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
await fastrelay.getActivity('missing');
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error instanceof FastrelayApiError) {
|
|
123
|
+
console.log(error.status, error.code, error.message, error.rateLimit);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm install
|
|
132
|
+
npm run build # tsc -> dist/
|
|
133
|
+
npm test # node --test (Node 23+ runs TS directly)
|
|
134
|
+
```
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { FastrelayFeed } from './feed.ts';
|
|
2
|
+
import { FastrelayRealtime, type FastrelayRealtimeSocketFactory, type FastrelayTokenProvider } from './realtime.ts';
|
|
3
|
+
import type { CursorPage, FastrelayActivity, FastrelayBookmark, FastrelayComment, FastrelayCommentReaction, FastrelayFeedActivityPin, FastrelayFeedback, FastrelayFile, FastrelayModerationFlag, FastrelayPoll, FastrelayReaction, FastrelayUser, FastrelayUserMute, FastrelayVideo, FastrelayVideoUploadUrl, FeedActivityQuery, NotificationPage } from './types.ts';
|
|
4
|
+
import { type FeedTarget, type QueryMap } from './utils.ts';
|
|
5
|
+
export type FastrelayAuthMode = 'auto' | 'user' | 'server' | 'none';
|
|
6
|
+
export interface FastrelayRequestOptions {
|
|
7
|
+
auth?: FastrelayAuthMode;
|
|
8
|
+
idempotencyKey?: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export interface FastrelayClientOptions {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
token?: string;
|
|
15
|
+
user?: Record<string, unknown>;
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
socketFactory?: FastrelayRealtimeSocketFactory;
|
|
18
|
+
}
|
|
19
|
+
export interface ConnectUserOptions {
|
|
20
|
+
upsertUser?: boolean;
|
|
21
|
+
realtime?: boolean;
|
|
22
|
+
tokenProvider?: FastrelayTokenProvider;
|
|
23
|
+
}
|
|
24
|
+
type ListQuery = {
|
|
25
|
+
limit?: number;
|
|
26
|
+
cursor?: string;
|
|
27
|
+
};
|
|
28
|
+
export declare class FastrelayClient {
|
|
29
|
+
apiKey: string;
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
token?: string;
|
|
32
|
+
user?: Record<string, unknown>;
|
|
33
|
+
realtime?: FastrelayRealtime;
|
|
34
|
+
private readonly fetchImpl;
|
|
35
|
+
private readonly socketFactory?;
|
|
36
|
+
constructor(options: FastrelayClientOptions);
|
|
37
|
+
feed(group: string, id: string): FastrelayFeed;
|
|
38
|
+
connectUser(user: Record<string, unknown>, token: string, options?: ConnectUserOptions): Promise<this>;
|
|
39
|
+
disconnectUser(): this;
|
|
40
|
+
setToken(token: string): this;
|
|
41
|
+
setBaseUrl(baseUrl: string): this;
|
|
42
|
+
close(): void;
|
|
43
|
+
getCapabilities(query?: {
|
|
44
|
+
feed?: string;
|
|
45
|
+
}, options?: FastrelayRequestOptions): Promise<any>;
|
|
46
|
+
getUser(id: string, options?: FastrelayRequestOptions): Promise<FastrelayUser>;
|
|
47
|
+
updateUser(id: string, request: Record<string, unknown>, options?: FastrelayRequestOptions): Promise<FastrelayUser>;
|
|
48
|
+
getOrCreateFeed(group: string, id: string, request?: Record<string, unknown>, options?: FastrelayRequestOptions): Promise<any>;
|
|
49
|
+
getFeedActivities(group: string, id: string, query?: FeedActivityQuery, options?: FastrelayRequestOptions): Promise<CursorPage<FastrelayActivity>>;
|
|
50
|
+
getNotificationFeedActivities(group: string, id: string, query?: FeedActivityQuery, options?: FastrelayRequestOptions): Promise<NotificationPage<FastrelayActivity>>;
|
|
51
|
+
deleteFeed(group: string, id: string, options?: FastrelayRequestOptions): Promise<any>;
|
|
52
|
+
setFeedVisibility(group: string, id: string, level: string, options?: FastrelayRequestOptions): Promise<any>;
|
|
53
|
+
updateFeedSettings(group: string, id: string, request: Record<string, unknown>, options?: FastrelayRequestOptions): Promise<any>;
|
|
54
|
+
addFeedMember(group: string, id: string, request: {
|
|
55
|
+
userId: string;
|
|
56
|
+
role?: string;
|
|
57
|
+
}, options?: FastrelayRequestOptions): Promise<any>;
|
|
58
|
+
removeFeedMember(group: string, id: string, userId: string, options?: FastrelayRequestOptions): Promise<any>;
|
|
59
|
+
listFeedMembers(group: string, id: string, query?: ListQuery, options?: FastrelayRequestOptions): Promise<any>;
|
|
60
|
+
followFeed(group: string, id: string, request: {
|
|
61
|
+
target: FeedTarget;
|
|
62
|
+
activityCopyLimit?: number;
|
|
63
|
+
}, options?: FastrelayRequestOptions): Promise<any>;
|
|
64
|
+
batchFollowFeed(group: string, id: string, request: {
|
|
65
|
+
targets: FeedTarget[];
|
|
66
|
+
activityCopyLimit?: number;
|
|
67
|
+
}, options?: FastrelayRequestOptions): Promise<any>;
|
|
68
|
+
unfollowFeed(group: string, id: string, target: FeedTarget, { keepHistory }?: {
|
|
69
|
+
keepHistory?: boolean;
|
|
70
|
+
}, options?: FastrelayRequestOptions): Promise<any>;
|
|
71
|
+
listFollowers(group: string, id: string, query?: ListQuery, options?: FastrelayRequestOptions): Promise<any>;
|
|
72
|
+
listFollowing(group: string, id: string, query?: ListQuery, options?: FastrelayRequestOptions): Promise<any>;
|
|
73
|
+
listFollowRequests(group: string, id: string, query?: {
|
|
74
|
+
status?: string;
|
|
75
|
+
}, options?: FastrelayRequestOptions): Promise<any>;
|
|
76
|
+
approveFollowRequest(group: string, id: string, requestId: string, options?: FastrelayRequestOptions): Promise<any>;
|
|
77
|
+
rejectFollowRequest(group: string, id: string, requestId: string, options?: FastrelayRequestOptions): Promise<any>;
|
|
78
|
+
addActivity(request: Record<string, unknown>, options?: FastrelayRequestOptions): Promise<FastrelayActivity>;
|
|
79
|
+
getActivity(id: string, options?: FastrelayRequestOptions): Promise<FastrelayActivity>;
|
|
80
|
+
updateActivity(id: string, request: Record<string, unknown>, options?: FastrelayRequestOptions): Promise<FastrelayActivity>;
|
|
81
|
+
deleteActivity(id: string, options?: FastrelayRequestOptions): Promise<any>;
|
|
82
|
+
batchGetActivities(requestOrIds: string[] | Record<string, unknown>, options?: FastrelayRequestOptions): Promise<any>;
|
|
83
|
+
addReaction(activityId: string, type: string, options?: FastrelayRequestOptions): Promise<FastrelayReaction>;
|
|
84
|
+
removeReaction(activityId: string, reactionId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
85
|
+
listReactions(activityId: string, query?: {
|
|
86
|
+
type?: string;
|
|
87
|
+
} & ListQuery, options?: FastrelayRequestOptions): Promise<CursorPage<FastrelayReaction>>;
|
|
88
|
+
addComment(activityId: string, request: {
|
|
89
|
+
text: string;
|
|
90
|
+
parentId?: string;
|
|
91
|
+
mentionedUsers?: string[];
|
|
92
|
+
}, options?: FastrelayRequestOptions): Promise<FastrelayComment>;
|
|
93
|
+
updateComment(commentId: string, request: {
|
|
94
|
+
text: string;
|
|
95
|
+
}, options?: FastrelayRequestOptions): Promise<FastrelayComment>;
|
|
96
|
+
deleteComment(commentId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
97
|
+
listComments(activityId: string, query?: {
|
|
98
|
+
sort?: string;
|
|
99
|
+
} & ListQuery, options?: FastrelayRequestOptions): Promise<CursorPage<FastrelayComment>>;
|
|
100
|
+
listReplies(commentId: string, query?: ListQuery, options?: FastrelayRequestOptions): Promise<CursorPage<FastrelayComment>>;
|
|
101
|
+
addCommentReaction(commentId: string, type: string, options?: FastrelayRequestOptions): Promise<FastrelayCommentReaction>;
|
|
102
|
+
removeCommentReaction(commentId: string, reactionId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
103
|
+
addBookmark(activityId: string, options?: FastrelayRequestOptions): Promise<FastrelayBookmark>;
|
|
104
|
+
removeBookmark(activityId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
105
|
+
listBookmarks(query?: ListQuery, options?: FastrelayRequestOptions): Promise<CursorPage<FastrelayBookmark>>;
|
|
106
|
+
pinActivity(group: string, id: string, activityId: string, options?: FastrelayRequestOptions): Promise<FastrelayFeedActivityPin>;
|
|
107
|
+
unpinActivity(group: string, id: string, activityId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
108
|
+
createPoll(activityId: string, request: {
|
|
109
|
+
question: string;
|
|
110
|
+
options: Array<{
|
|
111
|
+
text: string;
|
|
112
|
+
} & Record<string, string>>;
|
|
113
|
+
maxVotesPerUser?: number;
|
|
114
|
+
anonymous?: boolean;
|
|
115
|
+
expiresAt?: string | Date;
|
|
116
|
+
}, options?: FastrelayRequestOptions): Promise<FastrelayPoll>;
|
|
117
|
+
getPollForActivity(activityId: string, options?: FastrelayRequestOptions): Promise<FastrelayPoll>;
|
|
118
|
+
getPoll(pollId: string, options?: FastrelayRequestOptions): Promise<FastrelayPoll>;
|
|
119
|
+
vote(pollId: string, optionId: string, options?: FastrelayRequestOptions): Promise<FastrelayPoll>;
|
|
120
|
+
removeVote(pollId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
121
|
+
uploadFile(data: Blob | ArrayBuffer | Uint8Array, filename: string, { type }?: {
|
|
122
|
+
type?: string;
|
|
123
|
+
}, options?: FastrelayRequestOptions): Promise<FastrelayFile>;
|
|
124
|
+
deleteFile(fileId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
125
|
+
createVideoUploadUrl(request: {
|
|
126
|
+
filename: string;
|
|
127
|
+
sizeBytes: number;
|
|
128
|
+
mimeType: string;
|
|
129
|
+
}, options?: FastrelayRequestOptions): Promise<FastrelayVideoUploadUrl>;
|
|
130
|
+
getVideo(videoId: string, options?: FastrelayRequestOptions): Promise<FastrelayVideo>;
|
|
131
|
+
deleteVideo(videoId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
132
|
+
submitFeedback(activityId: string, type: 'show_more' | 'show_less', options?: FastrelayRequestOptions): Promise<FastrelayFeedback>;
|
|
133
|
+
createFlag(targetType: string, targetId: string, request: {
|
|
134
|
+
reason: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
}, options?: FastrelayRequestOptions): Promise<FastrelayModerationFlag>;
|
|
137
|
+
deleteFlag(flagId: string, options?: FastrelayRequestOptions): Promise<void>;
|
|
138
|
+
createMute(userId: string, { type, expiresAt }?: {
|
|
139
|
+
type?: string;
|
|
140
|
+
expiresAt?: string | Date;
|
|
141
|
+
}, options?: FastrelayRequestOptions): Promise<FastrelayUserMute>;
|
|
142
|
+
removeMute(userId: string, { type }?: {
|
|
143
|
+
type?: string;
|
|
144
|
+
}, options?: FastrelayRequestOptions): Promise<void>;
|
|
145
|
+
listMutes(query?: {
|
|
146
|
+
type?: string;
|
|
147
|
+
} & ListQuery, options?: FastrelayRequestOptions): Promise<CursorPage<FastrelayUserMute>>;
|
|
148
|
+
getMutedUsers(query?: {
|
|
149
|
+
type?: string;
|
|
150
|
+
} & ListQuery, options?: FastrelayRequestOptions): Promise<CursorPage<FastrelayUserMute>>;
|
|
151
|
+
request(method: string, path: string, { query, body, form, options, }?: {
|
|
152
|
+
query?: QueryMap;
|
|
153
|
+
body?: unknown;
|
|
154
|
+
form?: FormData;
|
|
155
|
+
options?: FastrelayRequestOptions;
|
|
156
|
+
}): Promise<any>;
|
|
157
|
+
private buildAuthorization;
|
|
158
|
+
}
|
|
159
|
+
export {};
|