@foru-ms/sdk 1.1.1 → 1.2.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.
Files changed (47) hide show
  1. package/README.md +293 -11
  2. package/dist/Client.d.ts +63 -4
  3. package/dist/Client.js +124 -22
  4. package/dist/errors.d.ts +52 -0
  5. package/dist/errors.js +95 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.js +3 -0
  8. package/dist/resources/Integrations.d.ts +7 -0
  9. package/dist/resources/Integrations.js +6 -0
  10. package/dist/resources/Posts.d.ts +12 -0
  11. package/dist/resources/Posts.js +44 -0
  12. package/dist/resources/PrivateMessages.d.ts +4 -0
  13. package/dist/resources/PrivateMessages.js +6 -0
  14. package/dist/resources/SSO.d.ts +10 -0
  15. package/dist/resources/SSO.js +11 -0
  16. package/dist/resources/Tags.d.ts +8 -0
  17. package/dist/resources/Tags.js +24 -0
  18. package/dist/resources/Threads.d.ts +20 -0
  19. package/dist/resources/Threads.js +85 -0
  20. package/dist/resources/Users.d.ts +10 -0
  21. package/dist/resources/Users.js +26 -0
  22. package/dist/resources/Webhooks.d.ts +69 -0
  23. package/dist/resources/Webhooks.js +115 -0
  24. package/dist/response-types.d.ts +105 -0
  25. package/dist/response-types.js +2 -0
  26. package/dist/utils.d.ts +80 -0
  27. package/dist/utils.js +138 -0
  28. package/examples/README.md +38 -0
  29. package/examples/authentication.ts +79 -0
  30. package/examples/error-handling.ts +133 -0
  31. package/examples/managing-threads.ts +130 -0
  32. package/examples/pagination.ts +81 -0
  33. package/examples/webhooks.ts +176 -0
  34. package/package.json +1 -1
  35. package/src/Client.ts +165 -25
  36. package/src/errors.ts +95 -0
  37. package/src/index.ts +3 -0
  38. package/src/resources/Integrations.ts +11 -0
  39. package/src/resources/Posts.ts +56 -0
  40. package/src/resources/PrivateMessages.ts +10 -0
  41. package/src/resources/SSO.ts +17 -0
  42. package/src/resources/Tags.ts +32 -0
  43. package/src/resources/Threads.ts +109 -0
  44. package/src/resources/Users.ts +36 -0
  45. package/src/resources/Webhooks.ts +131 -0
  46. package/src/response-types.ts +113 -0
  47. package/src/utils.ts +182 -0
@@ -0,0 +1,79 @@
1
+ import { ForumClient, AuthenticationError } from '@foru-ms/sdk';
2
+
3
+ /**
4
+ * Example: Authentication with Foru.ms SDK
5
+ * Demonstrates user registration, login, and token management
6
+ */
7
+
8
+ async function main() {
9
+ // Initialize client with API key
10
+ const client = new ForumClient({
11
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
12
+ });
13
+
14
+ try {
15
+ // Example 1: User Registration
16
+ console.log('=== User Registration ===');
17
+ const newUser = await client.auth.register({
18
+ username: 'john_doe',
19
+ email: 'john@example.com',
20
+ password: 'securePassword123',
21
+ displayName: 'John Doe',
22
+ });
23
+ console.log('User registered:', newUser);
24
+
25
+ // Example 2: User Login
26
+ console.log('\n=== User Login ===');
27
+ const loginResponse = await client.auth.login({
28
+ login: 'john@example.com', // Can be username or email
29
+ password: 'securePassword123',
30
+ });
31
+ console.log('Login successful, token:', loginResponse.token);
32
+
33
+ // Store the token for authenticated requests
34
+ client.setToken(loginResponse.token);
35
+
36
+ // Example 3: Get Current User
37
+ console.log('\n=== Get Current User ===');
38
+ const currentUser = await client.auth.me();
39
+ console.log('Current user:', currentUser);
40
+
41
+ // Example 4: Password Reset Flow
42
+ console.log('\n=== Password Reset ===');
43
+
44
+ // Step 1: Request password reset
45
+ await client.auth.forgotPassword('john@example.com');
46
+ console.log('Password reset email sent');
47
+
48
+ // Step 2: Reset password with token (received via email)
49
+ // Note: In a real app, this would be done after user clicks the email link
50
+ const resetToken = 'token_from_email';
51
+ await client.auth.resetPassword({
52
+ password: 'newSecurePassword123',
53
+ token: resetToken,
54
+ });
55
+ console.log('Password reset successful');
56
+
57
+ // Example 5: Check Authentication Status
58
+ console.log('\n=== Authentication Status ===');
59
+ console.log('Is authenticated:', client.isAuthenticated());
60
+ console.log('Current token:', client.token);
61
+
62
+ // Example 6: Logout (clear token)
63
+ console.log('\n=== Logout ===');
64
+ client.clearToken();
65
+ console.log('Token cleared');
66
+ console.log('Is authenticated:', client.isAuthenticated());
67
+
68
+ } catch (error) {
69
+ if (error instanceof AuthenticationError) {
70
+ console.error('Authentication failed:', error.message);
71
+ console.error('Status code:', error.statusCode);
72
+ } else {
73
+ console.error('An error occurred:', error);
74
+ }
75
+ }
76
+ }
77
+
78
+ // Run the example
79
+ main().catch(console.error);
@@ -0,0 +1,133 @@
1
+ import {
2
+ ForumClient,
3
+ ForumAPIError,
4
+ AuthenticationError,
5
+ AuthorizationError,
6
+ NotFoundError,
7
+ ValidationError,
8
+ RateLimitError,
9
+ ServerError,
10
+ NetworkError
11
+ } from '@foru-ms/sdk';
12
+
13
+ /**
14
+ * Example: Error Handling
15
+ * Demonstrates proper error handling with custom error classes
16
+ */
17
+
18
+ async function main() {
19
+ const client = new ForumClient({
20
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
21
+ maxRetries: 3,
22
+ enableRetry: true,
23
+ });
24
+
25
+ // Example 1: Catch specific error types
26
+ console.log('=== Handling Specific Errors ===');
27
+ try {
28
+ await client.auth.login({
29
+ login: 'nonexistent@example.com',
30
+ password: 'wrong_password',
31
+ });
32
+ } catch (error) {
33
+ if (error instanceof AuthenticationError) {
34
+ console.log('Authentication failed');
35
+ console.log('Message:', error.message);
36
+ console.log('Status:', error.statusCode);
37
+ } else if (error instanceof NetworkError) {
38
+ console.log('Network error occurred');
39
+ console.log('Cause:', error.cause);
40
+ } else {
41
+ console.log('Unexpected error:', error);
42
+ }
43
+ }
44
+
45
+ // Example 2: Handle not found errors
46
+ console.log('\n=== Handling Not Found ===');
47
+ try {
48
+ await client.threads.retrieve('non-existent-id');
49
+ } catch (error) {
50
+ if (error instanceof NotFoundError) {
51
+ console.log('Thread not found');
52
+ console.log('Status:', error.statusCode); // 404
53
+ }
54
+ }
55
+
56
+ // Example 3: Handle validation errors
57
+ console.log('\n=== Handling Validation Errors ===');
58
+ try {
59
+ await client.threads.create({
60
+ title: '', // Invalid: empty title
61
+ body: 'Test',
62
+ userId: 'user-123',
63
+ });
64
+ } catch (error) {
65
+ if (error instanceof ValidationError) {
66
+ console.log('Validation failed');
67
+ console.log('Details:', error.response);
68
+ }
69
+ }
70
+
71
+ // Example 4: Handle rate limits
72
+ console.log('\n=== Handling Rate Limits ===');
73
+ try {
74
+ // Make many requests quickly
75
+ for (let i = 0; i < 100; i++) {
76
+ await client.threads.list();
77
+ }
78
+ } catch (error) {
79
+ if (error instanceof RateLimitError) {
80
+ console.log('Rate limit exceeded');
81
+ console.log('Retry after:', error.retryAfter, 'seconds');
82
+ console.log('Wait until:', new Date(Date.now() + error.retryAfter! * 1000));
83
+ }
84
+ }
85
+
86
+ // Example 5: Handle server errors with retry
87
+ console.log('\n=== Handling Server Errors ===');
88
+ const clientWithoutRetry = new ForumClient({
89
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
90
+ enableRetry: false, // Disable auto-retry
91
+ });
92
+
93
+ try {
94
+ // This might fail with 500 error
95
+ await clientWithoutRetry.threads.list();
96
+ } catch (error) {
97
+ if (error instanceof ServerError) {
98
+ console.log('Server error occurred');
99
+ console.log('Status:', error.statusCode);
100
+ console.log('You might want to retry manually');
101
+ }
102
+ }
103
+
104
+ // Example 6: Generic error handling
105
+ console.log('\n=== Generic Error Handling ===');
106
+ try {
107
+ await client.threads.delete('thread-123');
108
+ } catch (error) {
109
+ if (error instanceof ForumAPIError) {
110
+ // All API errors inherit from ForumAPIError
111
+ console.log('API Error:', error.message);
112
+ console.log('Status Code:', error.statusCode);
113
+ console.log('Response:', error.response);
114
+ } else {
115
+ // Non-API errors (network, etc.)
116
+ console.log('Unexpected error:', error);
117
+ }
118
+ }
119
+
120
+ // Example 7: Authorization errors
121
+ console.log('\n=== Handling Authorization Errors ===');
122
+ try {
123
+ // Try to access admin-only endpoint
124
+ await client.users.delete('some-user-id');
125
+ } catch (error) {
126
+ if (error instanceof AuthorizationError) {
127
+ console.log('Permission denied');
128
+ console.log('Status:', error.statusCode); // 403
129
+ }
130
+ }
131
+ }
132
+
133
+ main().catch(console.error);
@@ -0,0 +1,130 @@
1
+ import { ForumClient } from '@foru-ms/sdk';
2
+
3
+ /**
4
+ * Example: Managing Threads
5
+ * Demonstrates thread creation, updates, and interactions
6
+ */
7
+
8
+ async function main() {
9
+ const client = new ForumClient({
10
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
11
+ });
12
+
13
+ // Set user token for authenticated actions
14
+ client.setToken(process.env.FORU_USER_TOKEN || 'user_token');
15
+
16
+ const userId = 'user-123';
17
+
18
+ // Example 1: Create a Thread
19
+ console.log('=== Creating a Thread ===');
20
+ const newThread = await client.threads.create({
21
+ title: 'How to use the SDK?',
22
+ body: 'I need help getting started with the Foru.ms SDK.',
23
+ userId,
24
+ tags: ['help', 'sdk'],
25
+ });
26
+ console.log('Thread created:', newThread.id);
27
+
28
+ // Example 2: Create a Thread with Poll
29
+ console.log('\n=== Creating a Thread with Poll ===');
30
+ const pollThread = await client.threads.create({
31
+ title: 'What\'s your favorite feature?',
32
+ body: 'Vote for your favorite SDK feature!',
33
+ userId,
34
+ poll: {
35
+ title: 'Favorite Feature',
36
+ options: [
37
+ { title: 'Type Safety', color: '#3B82F6' },
38
+ { title: 'Auto Pagination', color: '#10B981' },
39
+ { title: 'Error Handling', color: '#F59E0B' },
40
+ ],
41
+ },
42
+ });
43
+ console.log('Poll thread created:', pollThread.id);
44
+
45
+ // Example 3: Update a Thread
46
+ console.log('\n=== Updating a Thread ===');
47
+ const updatedThread = await client.threads.update(newThread.id, {
48
+ title: 'How to use the SDK? [SOLVED]',
49
+ locked: false,
50
+ pinned: true,
51
+ });
52
+ console.log('Thread updated');
53
+
54
+ // Example 4: Like a Thread
55
+ console.log('\n=== Liking a Thread ===');
56
+ await client.threads.like(newThread.id, userId);
57
+ console.log('Thread liked');
58
+
59
+ // Get likes
60
+ const likes = await client.threads.getLikes(newThread.id);
61
+ console.log('Total likes:', likes.count);
62
+
63
+ // Example 5: Subscribe to a Thread
64
+ console.log('\n=== Subscribing to Thread ===');
65
+ await client.threads.subscribe(newThread.id, userId);
66
+ console.log('Subscribed to thread');
67
+
68
+ // Example 6: Vote in a Poll
69
+ console.log('\n=== Voting in Poll ===');
70
+ await client.threads.vote(pollThread.id, 'option-id-1', userId);
71
+ console.log('Vote cast');
72
+
73
+ // Get poll results
74
+ const pollResults = await client.threads.getPoll(pollThread.id, userId);
75
+ console.log('Poll results:', pollResults);
76
+
77
+ // Example 7: Add Posts to Thread
78
+ console.log('\n=== Adding Posts ===');
79
+ const post = await client.posts.create({
80
+ threadId: newThread.id,
81
+ body: 'Thanks for posting! Check out our documentation.',
82
+ userId: 'moderator-123',
83
+ });
84
+ console.log('Post created:', post.id);
85
+
86
+ // Reply to the post
87
+ const reply = await client.posts.create({
88
+ threadId: newThread.id,
89
+ body: 'Thank you! That helps a lot.',
90
+ userId,
91
+ parentId: post.id, // This makes it a reply
92
+ });
93
+ console.log('Reply created:', reply.id);
94
+
95
+ // Example 8: Get Thread Posts
96
+ console.log('\n=== Getting Thread Posts ===');
97
+ const threadPosts = await client.threads.getPosts(newThread.id, {
98
+ filter: 'newest',
99
+ });
100
+ console.log('Total posts:', threadPosts.count);
101
+
102
+ // Example 9: Upvote/Downvote Threads
103
+ console.log('\n=== Voting on Threads ===');
104
+ await client.threads.upvote(newThread.id, userId);
105
+ console.log('Thread upvoted');
106
+
107
+ const upvotes = await client.threads.getUpvotes(newThread.id);
108
+ console.log('Total upvotes:', upvotes.count);
109
+
110
+ // Example 10: List Threads with Filters
111
+ console.log('\n=== Listing Threads ===');
112
+ const threads = await client.threads.list({
113
+ filter: 'newest',
114
+ tagId: 'help',
115
+ query: 'SDK',
116
+ });
117
+ console.log('Found threads:', threads.threads.length);
118
+
119
+ // Example 11: Get Subscribers
120
+ console.log('\n=== Getting Subscribers ===');
121
+ const subscribers = await client.threads.getSubscribers(newThread.id);
122
+ console.log('Total subscribers:', subscribers.count);
123
+
124
+ // Example 12: Delete a Thread
125
+ console.log('\n=== Deleting a Thread ===');
126
+ const deleted = await client.threads.delete(newThread.id, { userId });
127
+ console.log('Thread deleted:', deleted.deleted);
128
+ }
129
+
130
+ main().catch(console.error);
@@ -0,0 +1,81 @@
1
+ import { ForumClient } from '@foru-ms/sdk';
2
+
3
+ /**
4
+ * Example: Working with Pagination
5
+ * Demonstrates different ways to paginate through results
6
+ */
7
+
8
+ async function main() {
9
+ const client = new ForumClient({
10
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
11
+ });
12
+
13
+ // Example 1: Manual Pagination
14
+ console.log('=== Manual Pagination ===');
15
+ let cursor: string | undefined;
16
+ let pageCount = 0;
17
+ const maxPages = 3;
18
+
19
+ do {
20
+ const response = await client.threads.list({ cursor, filter: 'newest' });
21
+ console.log(`Page ${++pageCount}:`, response.threads.length, 'threads');
22
+
23
+ // Process threads
24
+ response.threads.forEach(thread => {
25
+ console.log(` - ${thread.title}`);
26
+ });
27
+
28
+ cursor = response.nextThreadCursor;
29
+ } while (cursor && pageCount < maxPages);
30
+
31
+ // Example 2: Auto-pagination with async iterator
32
+ console.log('\n=== Auto-Pagination (Async Iterator) ===');
33
+ let count = 0;
34
+ const maxItems = 10;
35
+
36
+ for await (const thread of client.pagination.paginateAll(
37
+ (cursor) => client.threads.list({ cursor, filter: 'newest' })
38
+ )) {
39
+ console.log(`Thread ${++count}:`, thread.title);
40
+
41
+ if (count >= maxItems) break; // Limit for demo
42
+ }
43
+
44
+ // Example 3: Fetch all pages at once
45
+ console.log('\n=== Fetch All Pages ===');
46
+ const allThreads = await client.pagination.fetchAllPages(
47
+ (cursor) => client.threads.list({ cursor }),
48
+ 3 // Max 3 pages
49
+ );
50
+ console.log('Total threads fetched:', allThreads.length);
51
+
52
+ // Example 4: Paginate with filters
53
+ console.log('\n=== Pagination with Filters ===');
54
+ cursor = undefined;
55
+ pageCount = 0;
56
+
57
+ do {
58
+ const response = await client.posts.list({
59
+ cursor,
60
+ filter: 'newest',
61
+ type: 'created', // Only posts created by user
62
+ userId: 'user-123',
63
+ });
64
+
65
+ console.log(`Page ${++pageCount}:`, response.posts.length, 'posts');
66
+ cursor = response.nextPostCursor;
67
+ } while (cursor && pageCount < 2);
68
+
69
+ // Example 5: Check rate limits during pagination
70
+ console.log('\n=== Rate Limit Monitoring ===');
71
+ await client.threads.list();
72
+
73
+ if (client.lastRateLimitInfo) {
74
+ console.log('Rate Limit Info:');
75
+ console.log(' Limit:', client.lastRateLimitInfo.limit);
76
+ console.log(' Remaining:', client.lastRateLimitInfo.remaining);
77
+ console.log(' Resets at:', new Date(client.lastRateLimitInfo.reset * 1000));
78
+ }
79
+ }
80
+
81
+ main().catch(console.error);
@@ -0,0 +1,176 @@
1
+ import { ForumClient } from '@foru-ms/sdk';
2
+ import express from 'express';
3
+
4
+ /**
5
+ * Example: Webhooks
6
+ * Demonstrates setting up, handling, and verifying webhooks
7
+ */
8
+
9
+ async function setupWebhooks() {
10
+ const client = new ForumClient({
11
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
12
+ });
13
+
14
+ // Example 1: Create a Webhook
15
+ console.log('=== Creating a Webhook ===');
16
+ const webhook = await client.webhooks.create({
17
+ name: 'Thread Activity Monitor',
18
+ url: 'https://your-domain.com/webhooks/foru',
19
+ events: [
20
+ 'thread.created',
21
+ 'thread.updated',
22
+ 'thread.deleted',
23
+ 'post.created',
24
+ ],
25
+ });
26
+ console.log('Webhook created:', webhook.webhook.id);
27
+ console.log('Secret:', webhook.webhook.secret); // Save this securely!
28
+
29
+ // Example 2: List All Webhooks
30
+ console.log('\n=== Listing Webhooks ===');
31
+ const webhooks = await client.webhooks.list();
32
+ console.log('Total webhooks:', webhooks.webhooks.length);
33
+
34
+ // Example 3: Update a Webhook
35
+ console.log('\n=== Updating a Webhook ===');
36
+ await client.webhooks.update(webhook.webhook.id, {
37
+ active: true,
38
+ events: [
39
+ 'thread.created',
40
+ 'thread.updated',
41
+ 'thread.deleted',
42
+ 'post.created',
43
+ 'post.updated', // Added new event
44
+ ],
45
+ });
46
+ console.log('Webhook updated');
47
+
48
+ // Example 4: Get Webhook Deliveries
49
+ console.log('\n=== Getting Webhook Deliveries ===');
50
+ const deliveries = await client.webhooks.getDeliveries(webhook.webhook.id);
51
+ console.log('Total deliveries:', deliveries.total);
52
+ console.log('Recent deliveries:', deliveries.deliveries.slice(0, 5));
53
+
54
+ return webhook.webhook.secret;
55
+ }
56
+
57
+ /**
58
+ * Example webhook handler using Express
59
+ */
60
+ function setupWebhookHandler(secret: string) {
61
+ const app = express();
62
+ const client = new ForumClient({
63
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
64
+ });
65
+
66
+ // Important: Use raw body for signature verification
67
+ app.use(express.json({
68
+ verify: (req: any, res, buf) => {
69
+ req.rawBody = buf.toString();
70
+ }
71
+ }));
72
+
73
+ app.post('/webhooks/foru', (req: any, res) => {
74
+ // Example 5: Verify Webhook Signature with Timestamp
75
+ const signature = req.headers['x-webhook-signature'];
76
+ const timestamp = req.headers['x-webhook-timestamp'];
77
+ const event = req.headers['x-webhook-event'];
78
+
79
+ // Verify timestamp age (prevent replay attacks)
80
+ const age = Date.now() - parseInt(timestamp);
81
+ if (age > 5 * 60 * 1000) { // 5 minutes
82
+ console.error('Webhook timestamp too old');
83
+ return res.status(400).send('Webhook timestamp too old');
84
+ }
85
+
86
+ // Verify signature
87
+ const isValid = client.webhooks.verifySignature(
88
+ req.body,
89
+ signature,
90
+ timestamp,
91
+ secret
92
+ );
93
+
94
+ if (!isValid) {
95
+ console.error('Invalid webhook signature');
96
+ return res.status(401).send('Invalid signature');
97
+ }
98
+
99
+ // Process webhook payload
100
+ console.log('Received webhook event:', event);
101
+ const payload = req.body;
102
+
103
+ switch (event) {
104
+ case 'thread.created':
105
+ handleThreadCreated(payload);
106
+ break;
107
+ case 'thread.updated':
108
+ handleThreadUpdated(payload);
109
+ break;
110
+ case 'thread.deleted':
111
+ handleThreadDeleted(payload);
112
+ break;
113
+ case 'post.created':
114
+ handlePostCreated(payload);
115
+ break;
116
+ default:
117
+ console.log('Unhandled event type:', event);
118
+ }
119
+
120
+ // Always respond quickly to acknowledge receipt
121
+ res.status(200).send('OK');
122
+ });
123
+
124
+ const PORT = process.env.PORT || 3000;
125
+ app.listen(PORT, () => {
126
+ console.log(`\nWebhook handler listening on port ${PORT}`);
127
+ console.log(`Endpoint: http://localhost:${PORT}/webhooks/foru`);
128
+ });
129
+ }
130
+
131
+ // Event handler functions
132
+ function handleThreadCreated(thread: any) {
133
+ console.log('New thread created:', thread.id);
134
+ console.log('Title:', thread.title);
135
+ console.log('Author:', thread.userId);
136
+
137
+ // Example: Send notification, update database, etc.
138
+ }
139
+
140
+ function handleThreadUpdated(thread: any) {
141
+ console.log('Thread updated:', thread.id);
142
+ console.log('Title:', thread.title);
143
+ }
144
+
145
+ function handleThreadDeleted(thread: any) {
146
+ console.log('Thread deleted:', thread.id);
147
+ }
148
+
149
+ function handlePostCreated(post: any) {
150
+ console.log('New post created:', post.id);
151
+ console.log('Thread:', post.threadId);
152
+ console.log('Author:', post.userId);
153
+
154
+ // Example: Check for spam, auto-moderate, etc.
155
+ }
156
+
157
+ // Main execution
158
+ async function main() {
159
+ try {
160
+ // Setup webhooks
161
+ const secret = await setupWebhooks();
162
+
163
+ // Start webhook handler server
164
+ // setupWebhookHandler(secret);
165
+
166
+ console.log('\n=== Webhook Setup Complete ===');
167
+ console.log('Remember to save your webhook secret securely!');
168
+
169
+ } catch (error) {
170
+ console.error('Error setting up webhooks:', error);
171
+ }
172
+ }
173
+
174
+ // For demonstration, we'll just setup webhooks
175
+ // Uncomment the line above to also start the webhook handler server
176
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foru-ms/sdk",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "JavaScript SDK for Foru.ms",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",