@foru-ms/sdk 0.1.0 → 1.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +189 -0
  3. package/dist/Client.d.ts +18 -0
  4. package/dist/Client.js +18 -0
  5. package/dist/index.d.ts +9 -0
  6. package/dist/index.js +9 -0
  7. package/dist/resources/Auth.d.ts +12 -0
  8. package/dist/resources/Auth.js +19 -0
  9. package/dist/resources/Integrations.d.ts +20 -0
  10. package/dist/resources/Integrations.js +30 -0
  11. package/dist/resources/Notifications.d.ts +32 -0
  12. package/dist/resources/Notifications.js +48 -0
  13. package/dist/resources/Posts.d.ts +13 -0
  14. package/dist/resources/Posts.js +55 -0
  15. package/dist/resources/PrivateMessages.d.ts +29 -0
  16. package/dist/resources/PrivateMessages.js +44 -0
  17. package/dist/resources/Reports.d.ts +46 -0
  18. package/dist/resources/Reports.js +56 -0
  19. package/dist/resources/Roles.d.ts +26 -0
  20. package/dist/resources/Roles.js +44 -0
  21. package/dist/resources/SSO.d.ts +17 -0
  22. package/dist/resources/SSO.js +25 -0
  23. package/dist/resources/Search.d.ts +11 -0
  24. package/dist/resources/Search.js +20 -0
  25. package/dist/resources/Stats.d.ts +13 -0
  26. package/dist/resources/Stats.js +22 -0
  27. package/dist/resources/Tags.d.ts +27 -0
  28. package/dist/resources/Tags.js +47 -0
  29. package/dist/resources/Threads.d.ts +14 -0
  30. package/dist/resources/Threads.js +61 -0
  31. package/dist/resources/Users.d.ts +47 -0
  32. package/dist/resources/Users.js +54 -0
  33. package/dist/resources/Webhooks.d.ts +35 -0
  34. package/dist/resources/Webhooks.js +44 -0
  35. package/dist/types.d.ts +149 -0
  36. package/package.json +17 -4
  37. package/src/Client.ts +27 -0
  38. package/src/index.ts +9 -0
  39. package/src/resources/Auth.ts +28 -0
  40. package/src/resources/Integrations.ts +39 -0
  41. package/src/resources/Notifications.ts +69 -0
  42. package/src/resources/Posts.ts +67 -0
  43. package/src/resources/PrivateMessages.ts +67 -0
  44. package/src/resources/Reports.ts +93 -0
  45. package/src/resources/Roles.ts +64 -0
  46. package/src/resources/SSO.ts +33 -0
  47. package/src/resources/Search.ts +27 -0
  48. package/src/resources/Stats.ts +31 -0
  49. package/src/resources/Tags.ts +68 -0
  50. package/src/resources/Threads.ts +74 -0
  51. package/src/resources/Users.ts +91 -0
  52. package/src/resources/Webhooks.ts +60 -0
  53. package/src/types.ts +165 -0
@@ -0,0 +1,33 @@
1
+ import { ForumClient } from '../Client';
2
+ import { SSOProvider, SSOProviderListResponse } from '../types';
3
+
4
+ export class SSOResource {
5
+ private client: ForumClient;
6
+
7
+ constructor(client: ForumClient) {
8
+ this.client = client;
9
+ }
10
+
11
+ async list(): Promise<SSOProviderListResponse> {
12
+ return this.client.request<SSOProviderListResponse>('/sso', {
13
+ method: 'GET',
14
+ });
15
+ }
16
+
17
+ async create(payload: {
18
+ provider: 'OKTA' | 'AUTH0' | 'SAML';
19
+ domain: string;
20
+ config: any;
21
+ }): Promise<{ ssoProvider: SSOProvider }> {
22
+ return this.client.request<{ ssoProvider: SSOProvider }>('/sso', {
23
+ method: 'POST',
24
+ body: JSON.stringify(payload),
25
+ });
26
+ }
27
+
28
+ async delete(id: string): Promise<{ success: boolean }> {
29
+ return this.client.request<{ success: boolean }>(`/sso/${id}`, {
30
+ method: 'DELETE',
31
+ });
32
+ }
33
+ }
@@ -0,0 +1,27 @@
1
+ import { ForumClient } from '../Client';
2
+ import { SearchResponse } from '../types';
3
+
4
+ export class SearchResource {
5
+ private client: ForumClient;
6
+
7
+ constructor(client: ForumClient) {
8
+ this.client = client;
9
+ }
10
+
11
+ async search(params: {
12
+ query: string;
13
+ type: 'threads' | 'posts' | 'users' | 'tags';
14
+ cursor?: string;
15
+ }): Promise<SearchResponse> {
16
+ const searchParams = new URLSearchParams();
17
+ Object.entries(params).forEach(([key, value]) => {
18
+ if (value !== undefined) {
19
+ searchParams.append(key, value as string);
20
+ }
21
+ });
22
+
23
+ return this.client.request<SearchResponse>(`/search?${searchParams.toString()}`, {
24
+ method: 'GET',
25
+ });
26
+ }
27
+ }
@@ -0,0 +1,31 @@
1
+ import { ForumClient } from '../Client';
2
+ import { StatsResponse } from '../types';
3
+
4
+ export class StatsResource {
5
+ private client: ForumClient;
6
+
7
+ constructor(client: ForumClient) {
8
+ this.client = client;
9
+ }
10
+
11
+ async get(params?: {
12
+ filter?: 'newest' | 'oldest';
13
+ threadCursor?: string;
14
+ postCursor?: string;
15
+ userCursor?: string;
16
+ reportCursor?: string;
17
+ }): Promise<StatsResponse> {
18
+ const searchParams = new URLSearchParams();
19
+ if (params) {
20
+ Object.entries(params).forEach(([key, value]) => {
21
+ if (value !== undefined) {
22
+ searchParams.append(key, value as string);
23
+ }
24
+ });
25
+ }
26
+
27
+ return this.client.request<StatsResponse>(`/stats?${searchParams.toString()}`, {
28
+ method: 'GET',
29
+ });
30
+ }
31
+ }
@@ -25,4 +25,72 @@ export class TagsResource {
25
25
  method: 'GET',
26
26
  });
27
27
  }
28
+ async create(payload: {
29
+ name: string;
30
+ description?: string;
31
+ color?: string;
32
+ extendedData?: Record<string, any>;
33
+ }): Promise<import('../types').Tag> {
34
+ return this.client.request<import('../types').Tag>('/tag', {
35
+ method: 'POST',
36
+ body: JSON.stringify(payload),
37
+ });
38
+ }
39
+
40
+ async retrieve(id: string, params?: { userId?: string }): Promise<import('../types').Tag & { isSubscribed?: boolean }> {
41
+ const searchParams = new URLSearchParams();
42
+ if (params?.userId) searchParams.append('userId', params.userId);
43
+
44
+ return this.client.request<import('../types').Tag & { isSubscribed?: boolean }>(`/tag/${id}?${searchParams.toString()}`, {
45
+ method: 'GET',
46
+ });
47
+ }
48
+
49
+ async update(id: string, payload: {
50
+ name?: string;
51
+ description?: string;
52
+ color?: string;
53
+ extendedData?: Record<string, any>;
54
+ }): Promise<import('../types').Tag> {
55
+ return this.client.request<import('../types').Tag>(`/tag/${id}`, {
56
+ method: 'PUT',
57
+ body: JSON.stringify(payload),
58
+ });
59
+ }
60
+
61
+ async delete(id: string): Promise<import('../types').Tag & { deleted: boolean }> {
62
+ return this.client.request<import('../types').Tag & { deleted: boolean }>(`/tag/${id}`, {
63
+ method: 'DELETE',
64
+ });
65
+ }
66
+
67
+ async subscribe(id: string, userId: string): Promise<any> {
68
+ return this.client.request(`/tag/${id}/subscribers`, {
69
+ method: 'POST',
70
+ body: JSON.stringify({ userId }),
71
+ });
72
+ }
73
+
74
+ async unsubscribe(id: string, userId: string): Promise<any> {
75
+ return this.client.request(`/tag/${id}/subscribers?userId=${userId}`, {
76
+ method: 'DELETE',
77
+ });
78
+ }
79
+
80
+ async listSubscribed(params: {
81
+ userId: string;
82
+ query?: string;
83
+ cursor?: string;
84
+ }): Promise<TagListResponse> {
85
+ const searchParams = new URLSearchParams();
86
+ Object.entries(params).forEach(([key, value]) => {
87
+ if (value !== undefined) {
88
+ searchParams.append(key, value as string);
89
+ }
90
+ });
91
+
92
+ return this.client.request<TagListResponse>(`/tags/subscribed?${searchParams.toString()}`, {
93
+ method: 'GET',
94
+ });
95
+ }
28
96
  }
@@ -56,4 +56,78 @@ export class ThreadsResource {
56
56
  body: payload ? JSON.stringify(payload) : undefined,
57
57
  });
58
58
  }
59
+ async getPosts(id: string, params?: {
60
+ query?: string;
61
+ cursor?: string;
62
+ filter?: 'newest' | 'oldest';
63
+ }): Promise<any> {
64
+ const searchParams = new URLSearchParams();
65
+ if (params) {
66
+ Object.entries(params).forEach(([key, value]) => {
67
+ if (value !== undefined) {
68
+ searchParams.append(key, value as string);
69
+ }
70
+ });
71
+ }
72
+ return this.client.request(`/thread/${id}/posts?${searchParams.toString()}`, { method: 'GET' });
73
+ }
74
+
75
+ async like(id: string, userId?: string, extendedData?: any): Promise<any> {
76
+ return this.client.request(`/thread/${id}/likes`, {
77
+ method: 'POST',
78
+ body: JSON.stringify({ userId, extendedData }),
79
+ });
80
+ }
81
+
82
+ async unlike(id: string, userId: string): Promise<any> {
83
+ return this.client.request(`/thread/${id}/likes?userId=${userId}`, {
84
+ method: 'DELETE',
85
+ });
86
+ }
87
+
88
+ async dislike(id: string, userId?: string, extendedData?: any): Promise<any> {
89
+ return this.client.request(`/thread/${id}/dislikes`, {
90
+ method: 'POST',
91
+ body: JSON.stringify({ userId, extendedData }),
92
+ });
93
+ }
94
+
95
+ async undislike(id: string, userId: string): Promise<any> {
96
+ return this.client.request(`/thread/${id}/dislikes?userId=${userId}`, {
97
+ method: 'DELETE',
98
+ });
99
+ }
100
+
101
+ async subscribe(id: string, userId: string, extendedData?: any): Promise<any> {
102
+ return this.client.request(`/thread/${id}/subscribers`, {
103
+ method: 'POST',
104
+ body: JSON.stringify({ userId, extendedData }),
105
+ });
106
+ }
107
+
108
+ async unsubscribe(id: string, userId: string): Promise<any> {
109
+ return this.client.request(`/thread/${id}/subscribers?userId=${userId}`, {
110
+ method: 'DELETE',
111
+ });
112
+ }
113
+
114
+ async vote(id: string, optionId: string, userId: string): Promise<any> {
115
+ return this.client.request(`/thread/${id}/poll/votes`, {
116
+ method: 'POST',
117
+ body: JSON.stringify({ optionId, userId }),
118
+ });
119
+ }
120
+
121
+ async voteUpdate(id: string, optionId: string, userId: string): Promise<any> {
122
+ return this.client.request(`/thread/${id}/poll/votes`, {
123
+ method: 'PUT',
124
+ body: JSON.stringify({ optionId, userId }),
125
+ });
126
+ }
127
+
128
+ async unvote(id: string, userId: string): Promise<any> {
129
+ return this.client.request(`/thread/${id}/poll/votes?userId=${userId}`, {
130
+ method: 'DELETE',
131
+ });
132
+ }
59
133
  }
@@ -32,4 +32,95 @@ export class UsersResource {
32
32
  method: 'GET',
33
33
  });
34
34
  }
35
+
36
+ async create(payload: {
37
+ username: string;
38
+ email: string;
39
+ password: string;
40
+ displayName?: string;
41
+ emailVerified?: boolean;
42
+ roles?: string[];
43
+ bio?: string;
44
+ signature?: string;
45
+ url?: string;
46
+ extendedData?: Record<string, any>;
47
+ }): Promise<User> {
48
+ return this.client.request<User>('/user', {
49
+ method: 'POST',
50
+ body: JSON.stringify(payload),
51
+ });
52
+ }
53
+
54
+ async update(id: string, payload: {
55
+ username?: string;
56
+ email?: string;
57
+ password?: string;
58
+ displayName?: string;
59
+ emailVerified?: boolean;
60
+ roles?: string[];
61
+ bio?: string;
62
+ signature?: string;
63
+ url?: string;
64
+ extendedData?: Record<string, any>;
65
+ }): Promise<User> {
66
+ return this.client.request<User>(`/user/${id}`, {
67
+ method: 'PUT',
68
+ body: JSON.stringify(payload),
69
+ });
70
+ }
71
+
72
+ async delete(id: string): Promise<User & { deleted: boolean }> {
73
+ return this.client.request<User & { deleted: boolean }>(`/user/${id}`, {
74
+ method: 'DELETE',
75
+ });
76
+ }
77
+
78
+ async getFollowers(id: string, params?: {
79
+ query?: string;
80
+ cursor?: string;
81
+ filter?: 'newest' | 'oldest';
82
+ }): Promise<{ followers: User[]; nextUserCursor?: string; count: number }> {
83
+ const searchParams = new URLSearchParams();
84
+ if (params) {
85
+ Object.entries(params).forEach(([key, value]) => {
86
+ if (value !== undefined) {
87
+ searchParams.append(key, value as string);
88
+ }
89
+ });
90
+ }
91
+ return this.client.request<{ followers: User[]; nextUserCursor?: string; count: number }>(`/user/${id}/followers?${searchParams.toString()}`, {
92
+ method: 'GET',
93
+ });
94
+ }
95
+
96
+ async follow(id: string, followerId: string, extendedData?: any): Promise<any> {
97
+ return this.client.request(`/user/${id}/followers`, {
98
+ method: 'POST',
99
+ body: JSON.stringify({ followerId, extendedData }),
100
+ });
101
+ }
102
+
103
+ async unfollow(id: string, followerId: string): Promise<any> {
104
+ return this.client.request(`/user/${id}/followers?followerId=${followerId}`, {
105
+ method: 'DELETE',
106
+ });
107
+ }
108
+
109
+ async getFollowing(id: string, params?: {
110
+ query?: string;
111
+ cursor?: string;
112
+ filter?: 'newest' | 'oldest';
113
+ }): Promise<{ following: any[]; nextUserCursor?: string; count: number }> {
114
+ const searchParams = new URLSearchParams();
115
+ if (params) {
116
+ Object.entries(params).forEach(([key, value]) => {
117
+ if (value !== undefined) {
118
+ searchParams.append(key, value as string);
119
+ }
120
+ });
121
+ }
122
+ return this.client.request<{ following: any[]; nextUserCursor?: string; count: number }>(`/user/${id}/following?${searchParams.toString()}`, {
123
+ method: 'GET',
124
+ });
125
+ }
35
126
  }
@@ -0,0 +1,60 @@
1
+ import { ForumClient } from '../Client';
2
+ import { Webhook, WebhookListResponse } from '../types';
3
+
4
+ export class WebhooksResource {
5
+ private client: ForumClient;
6
+
7
+ constructor(client: ForumClient) {
8
+ this.client = client;
9
+ }
10
+
11
+ async list(): Promise<WebhookListResponse> {
12
+ return this.client.request<WebhookListResponse>('/webhooks', {
13
+ method: 'GET',
14
+ });
15
+ }
16
+
17
+ async create(payload: {
18
+ name: string;
19
+ url: string;
20
+ events: string[];
21
+ }): Promise<{ webhook: Webhook }> {
22
+ return this.client.request<{ webhook: Webhook }>('/webhooks', {
23
+ method: 'POST',
24
+ body: JSON.stringify(payload),
25
+ });
26
+ }
27
+
28
+ async retrieve(id: string): Promise<{ webhook: Webhook }> {
29
+ return this.client.request<{ webhook: Webhook }>(`/webhooks/${id}`, {
30
+ method: 'GET',
31
+ });
32
+ }
33
+
34
+ async update(id: string, payload: {
35
+ name?: string;
36
+ url?: string;
37
+ events?: string[];
38
+ active?: boolean;
39
+ }): Promise<{ webhook: Webhook }> {
40
+ return this.client.request<{ webhook: Webhook }>(`/webhooks/${id}`, {
41
+ method: 'PATCH',
42
+ body: JSON.stringify(payload),
43
+ });
44
+ }
45
+
46
+ async delete(id: string): Promise<{ success: boolean }> {
47
+ return this.client.request<{ success: boolean }>(`/webhooks/${id}`, {
48
+ method: 'DELETE',
49
+ });
50
+ }
51
+
52
+ async getDeliveries(id: string, params?: { cursor?: string }): Promise<{ deliveries: any[]; total: number; nextCursor?: string }> {
53
+ const searchParams = new URLSearchParams();
54
+ if (params?.cursor) searchParams.append('cursor', params.cursor);
55
+
56
+ return this.client.request<{ deliveries: any[]; total: number; nextCursor?: string }>(`/webhooks/${id}/deliveries?${searchParams.toString()}`, {
57
+ method: 'GET',
58
+ });
59
+ }
60
+ }
package/src/types.ts CHANGED
@@ -153,4 +153,169 @@ export interface UserListResponse {
153
153
  export interface TagListResponse {
154
154
  tags: Tag[];
155
155
  nextTagCursor?: string;
156
+ count?: number;
156
157
  }
158
+
159
+ export interface Notification {
160
+ id: string;
161
+ threadId?: string;
162
+ postId?: string;
163
+ privateMessageId?: string;
164
+ notifierId: string;
165
+ notifiedId: string;
166
+ type: string;
167
+ description?: string;
168
+ read: boolean;
169
+ createdAt: string;
170
+ extendedData?: Record<string, any>;
171
+ post?: Post;
172
+ thread?: Thread;
173
+ notifier?: User;
174
+ message?: string; // Computed field from API
175
+ isSystem?: boolean;
176
+ }
177
+
178
+ export interface NotificationListResponse {
179
+ list: Notification[];
180
+ nextCursor?: string;
181
+ count: number;
182
+ }
183
+
184
+ export interface SearchResponse {
185
+ type?: 'tags' | 'posts' | 'threads' | 'users';
186
+ tags?: Tag[];
187
+ posts?: Post[];
188
+ threads?: Thread[];
189
+ nextCursor?: string;
190
+ }
191
+
192
+ export interface Webhook {
193
+ id: string;
194
+ name: string;
195
+ url: string;
196
+ events: { events: string[] } | string[]; // Accessing normalized events is better
197
+ active: boolean;
198
+ secret?: string; // Only on creation
199
+ createdAt: string;
200
+ updatedAt: string;
201
+ _count?: {
202
+ deliveries: number;
203
+ };
204
+ }
205
+
206
+ export interface WebhookListResponse {
207
+ webhooks: Webhook[];
208
+ }
209
+
210
+ export interface StatsResponse {
211
+ counts: {
212
+ threads: number;
213
+ posts: number;
214
+ users: number;
215
+ reports: number;
216
+ };
217
+ latest: {
218
+ id: string;
219
+ createdAt: string;
220
+ type: 'thread' | 'post' | 'user' | 'report';
221
+ [key: string]: any;
222
+ }[];
223
+ usage: Record<string, any>;
224
+ cursors: {
225
+ threadCursor: string | null;
226
+ postCursor: string | null;
227
+ userCursor: string | null;
228
+ reportCursor: string | null;
229
+ };
230
+ }
231
+
232
+ export interface Integration {
233
+ id: string;
234
+ type: 'SLACK' | 'DISCORD' | 'SALESFORCE' | 'HUBSPOT' | 'OKTA' | 'AUTH0';
235
+ name: string;
236
+ active: boolean;
237
+ createdAt: string;
238
+ config?: any;
239
+ }
240
+
241
+ export interface IntegrationListResponse {
242
+ integrations: Integration[];
243
+ }
244
+
245
+ export interface PrivateMessage {
246
+ id: string;
247
+ senderId: string;
248
+ recipientId: string;
249
+ title?: string;
250
+ body: string;
251
+ read: boolean;
252
+ parentId?: string;
253
+ children?: PrivateMessage[];
254
+ sender?: User;
255
+ recipient?: User;
256
+ createdAt: string;
257
+ updatedAt: string;
258
+ extendedData?: Record<string, any>;
259
+ }
260
+
261
+ export interface PrivateMessageListResponse {
262
+ privateMessages: PrivateMessage[];
263
+ nextPrivateMessageCursor?: string;
264
+ count: number;
265
+ }
266
+
267
+ export interface Report {
268
+ id: string;
269
+ reporterId: string;
270
+ reportedId?: string;
271
+ threadId?: string;
272
+ postId?: string;
273
+ privateMessageId?: string;
274
+ type: string;
275
+ description?: string;
276
+ read: boolean;
277
+ createdAt: string;
278
+ post?: Post;
279
+ thread?: Thread;
280
+ privateMessage?: PrivateMessage;
281
+ reported?: User;
282
+ extendedData?: Record<string, any>;
283
+ }
284
+
285
+ export interface ReportListResponse {
286
+ list: Report[];
287
+ nextCursor?: string;
288
+ count: number;
289
+ }
290
+
291
+ export interface Role {
292
+ id: string;
293
+ name: string;
294
+ description?: string;
295
+ color?: string;
296
+ users?: User[];
297
+ createdAt: string;
298
+ updatedAt: string;
299
+ extendedData?: Record<string, any>;
300
+ }
301
+
302
+ export interface RoleListResponse {
303
+ roles: Role[];
304
+ nextCursor?: string;
305
+ count: number;
306
+ }
307
+
308
+ export interface SSOProvider {
309
+ id: string;
310
+ provider: 'OKTA' | 'AUTH0' | 'SAML';
311
+ domain: string;
312
+ active: boolean;
313
+ createdAt: string;
314
+ config?: any;
315
+ }
316
+
317
+ export interface SSOProviderListResponse {
318
+ ssoProviders: SSOProvider[];
319
+ }
320
+
321
+