reflet-sdk 0.1.3 → 0.1.5

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 CHANGED
@@ -18,22 +18,25 @@ bun add reflet-sdk
18
18
 
19
19
  ### 1. Get Your API Keys
20
20
 
21
- Go to your Reflet dashboard → Board Settings → API Keys & Widget to generate your keys:
21
+ Go to your Reflet dashboard → Settings → API Keys & Widget to generate your keys:
22
22
 
23
23
  - **Public Key** (`fb_pub_...`) - Safe to use in frontend code
24
24
  - **Secret Key** (`fb_sec_...`) - Keep secure, use only on your server
25
25
 
26
- ### 2. Basic Usage (Vanilla JavaScript/TypeScript)
26
+ ### 2. Basic Usage (Any JavaScript/TypeScript Environment)
27
+
28
+ The core SDK works in any JavaScript environment - browser, Node.js, Deno, Bun, or any framework.
27
29
 
28
30
  ```typescript
29
31
  import { Reflet } from 'reflet-sdk';
30
32
 
33
+ // Initialize the client
31
34
  const reflet = new Reflet({
32
35
  publicKey: 'fb_pub_your_key_here',
33
36
  user: {
34
- id: 'user_123',
35
- email: 'jane@example.com',
36
- name: 'Jane Doe',
37
+ id: 'user_123', // Required: unique user ID from your system
38
+ email: 'jane@example.com', // Optional: for notifications
39
+ name: 'Jane Doe', // Optional: display name
37
40
  },
38
41
  });
39
42
 
@@ -49,7 +52,7 @@ const { feedbackId } = await reflet.create({
49
52
  description: 'Would love to have a dark theme option!',
50
53
  });
51
54
 
52
- // Vote on feedback
55
+ // Vote on feedback (toggles vote on/off)
53
56
  await reflet.vote('feedback_id');
54
57
 
55
58
  // Add a comment
@@ -59,26 +62,33 @@ await reflet.comment({
59
62
  });
60
63
  ```
61
64
 
62
- ### 3. React Usage
65
+ ### 3. React Integration
66
+
67
+ For React applications, use the provided hooks for a better developer experience.
63
68
 
64
69
  ```tsx
65
70
  import { RefletProvider, useFeedbackList, useVote, useCreateFeedback } from 'reflet-sdk/react';
66
71
 
67
- // Wrap your app with the provider
72
+ // 1. Wrap your app with the provider (do this once at the root)
68
73
  function App() {
69
- const currentUser = useCurrentUser(); // Your auth system
74
+ // Get the current user from your authentication system
75
+ const currentUser = getCurrentUser(); // Replace with your auth logic
70
76
 
71
77
  return (
72
78
  <RefletProvider
73
79
  publicKey="fb_pub_your_key_here"
74
- user={currentUser}
80
+ user={currentUser ? {
81
+ id: currentUser.id,
82
+ email: currentUser.email,
83
+ name: currentUser.name,
84
+ } : undefined}
75
85
  >
76
- <FeedbackPage />
86
+ <YourApp />
77
87
  </RefletProvider>
78
88
  );
79
89
  }
80
90
 
81
- // Use hooks in your components
91
+ // 2. Use hooks anywhere in your app
82
92
  function FeedbackPage() {
83
93
  const { data, isLoading, error } = useFeedbackList({ sortBy: 'votes' });
84
94
  const { mutate: vote } = useVote();
@@ -103,39 +113,157 @@ function FeedbackPage() {
103
113
  }
104
114
  ```
105
115
 
106
- ### 4. Server-Side User Signing (Most Secure)
116
+ ### 4. Server-Side User Signing (Recommended for Production)
107
117
 
108
- For production apps, sign user tokens on your server to prevent spoofing:
118
+ For production apps, sign user tokens on your server to prevent client-side spoofing.
119
+
120
+ **On your server** (works with any backend: Express, Fastify, Hono, serverless functions, etc.):
109
121
 
110
122
  ```typescript
111
- // On your server (Next.js API route, Express, etc.)
112
123
  import { signUser } from 'reflet-sdk/server';
113
124
 
114
- export async function getUserToken(user: User) {
125
+ // This function uses Web Crypto API - works in Node.js 18+,
126
+ // Deno, Bun, Cloudflare Workers, Vercel Edge, etc.
127
+ export async function generateUserToken(user: YourUserType) {
115
128
  const { token, expiresAt } = await signUser(
116
129
  {
117
- id: user.id,
118
- email: user.email,
119
- name: user.name,
120
- avatar: user.avatarUrl,
130
+ id: user.id, // Required: unique identifier
131
+ email: user.email, // Optional
132
+ name: user.name, // Optional
133
+ avatar: user.avatar, // Optional
121
134
  },
122
- process.env.REFLET_SECRET_KEY!
135
+ process.env.REFLET_SECRET_KEY! // Your secret key from Reflet dashboard
123
136
  );
124
137
 
125
138
  return { token, expiresAt };
126
139
  }
127
140
  ```
128
141
 
129
- ```tsx
130
- // On the client
142
+ **On the client** (pass the token from your server):
143
+
144
+ ```typescript
131
145
  import { Reflet } from 'reflet-sdk';
132
146
 
133
147
  const reflet = new Reflet({
134
148
  publicKey: 'fb_pub_your_key_here',
135
- userToken: tokenFromServer, // JWT from your server
149
+ userToken: tokenFromServer, // JWT received from your server
150
+ });
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Important: What NOT To Do
156
+
157
+ ### Security
158
+
159
+ ```typescript
160
+ // ❌ WRONG: Never expose your secret key in client-side code
161
+ const reflet = new Reflet({
162
+ publicKey: 'fb_pub_xxx',
163
+ secretKey: 'fb_sec_xxx', // NEVER DO THIS - secret keys are for servers only
164
+ });
165
+
166
+ // ❌ WRONG: Never hardcode secret keys
167
+ const token = await signUser(user, 'fb_sec_hardcoded_key');
168
+
169
+ // ✅ CORRECT: Use environment variables on the server
170
+ const token = await signUser(user, process.env.REFLET_SECRET_KEY!);
171
+ ```
172
+
173
+ ### User Identification
174
+
175
+ ```typescript
176
+ // ❌ WRONG: Don't use unstable or guessable user IDs
177
+ const reflet = new Reflet({
178
+ publicKey: 'fb_pub_xxx',
179
+ user: { id: 'user@email.com' }, // Email as ID - can change, easily guessed
180
+ });
181
+
182
+ // ❌ WRONG: Don't use sequential IDs
183
+ const reflet = new Reflet({
184
+ publicKey: 'fb_pub_xxx',
185
+ user: { id: '1' }, // Sequential - easily enumerable
186
+ });
187
+
188
+ // ✅ CORRECT: Use your system's stable, unique user identifier
189
+ const reflet = new Reflet({
190
+ publicKey: 'fb_pub_xxx',
191
+ user: { id: 'usr_a1b2c3d4e5' }, // UUID, CUID, or database ID
192
+ });
193
+ ```
194
+
195
+ ### Client Initialization
196
+
197
+ ```typescript
198
+ // ❌ WRONG: Don't create multiple client instances
199
+ function handleVote(id: string) {
200
+ const reflet = new Reflet({ publicKey: 'fb_pub_xxx' }); // New instance every call
201
+ await reflet.vote(id);
202
+ }
203
+
204
+ // ✅ CORRECT: Create one instance and reuse it
205
+ const reflet = new Reflet({ publicKey: 'fb_pub_xxx', user: currentUser });
206
+
207
+ function handleVote(id: string) {
208
+ await reflet.vote(id);
209
+ }
210
+ ```
211
+
212
+ ### React Provider
213
+
214
+ ```tsx
215
+ // ❌ WRONG: Don't use hooks outside the provider
216
+ function App() {
217
+ return (
218
+ <div>
219
+ <FeedbackList /> {/* Error: No RefletProvider */}
220
+ <RefletProvider publicKey="fb_pub_xxx">
221
+ <OtherStuff />
222
+ </RefletProvider>
223
+ </div>
224
+ );
225
+ }
226
+
227
+ // ✅ CORRECT: Wrap at the root, use hooks inside
228
+ function App() {
229
+ return (
230
+ <RefletProvider publicKey="fb_pub_xxx" user={currentUser}>
231
+ <FeedbackList />
232
+ <OtherStuff />
233
+ </RefletProvider>
234
+ );
235
+ }
236
+ ```
237
+
238
+ ### Token Handling
239
+
240
+ ```typescript
241
+ // ❌ WRONG: Don't sign tokens on the client
242
+ import { signUser } from 'reflet-sdk/server';
243
+ // This exposes your secret key in client bundle!
244
+
245
+ // ❌ WRONG: Don't mix user and userToken
246
+ const reflet = new Reflet({
247
+ publicKey: 'fb_pub_xxx',
248
+ user: { id: '123' },
249
+ userToken: 'jwt_token', // Pick one, not both
250
+ });
251
+
252
+ // ✅ CORRECT: Use user for simple cases (client-side identification)
253
+ const reflet = new Reflet({
254
+ publicKey: 'fb_pub_xxx',
255
+ user: { id: '123', email: 'user@example.com' },
256
+ });
257
+
258
+ // ✅ CORRECT: Use userToken for production (server-signed)
259
+ const reflet = new Reflet({
260
+ publicKey: 'fb_pub_xxx',
261
+ userToken: tokenFromYourServer,
136
262
  });
137
263
  ```
138
264
 
265
+ ---
266
+
139
267
  ## API Reference
140
268
 
141
269
  ### `Reflet` Client
@@ -148,63 +276,75 @@ const reflet = new Reflet(config: RefletConfig);
148
276
 
149
277
  | Option | Type | Required | Description |
150
278
  |--------|------|----------|-------------|
151
- | `publicKey` | `string` | Yes | Your board's public API key |
152
- | `user` | `RefletUser` | No | User information for identification |
153
- | `userToken` | `string` | No | Signed JWT from your server (alternative to `user`) |
279
+ | `publicKey` | `string` | Yes | Your organization's public API key (`fb_pub_...`) |
280
+ | `user` | `RefletUser` | No | User information for client-side identification |
281
+ | `userToken` | `string` | No | Signed JWT from your server (use instead of `user` for production) |
154
282
  | `baseUrl` | `string` | No | Custom API endpoint (defaults to Reflet production API) |
155
283
 
284
+ > **Note:** Provide either `user` OR `userToken`, not both. If neither is provided, the user can only read feedback (no voting, commenting, or creating).
285
+
156
286
  #### Methods
157
287
 
158
- | Method | Description |
159
- |--------|-------------|
160
- | `list(params?)` | List feedback with optional filtering |
161
- | `get(feedbackId)` | Get single feedback with full details |
162
- | `create(params)` | Submit new feedback |
163
- | `vote(feedbackId, type?)` | Vote on feedback (toggle) |
164
- | `comment(params)` | Add a comment |
165
- | `subscribe(feedbackId)` | Subscribe to updates |
166
- | `unsubscribe(feedbackId)` | Unsubscribe from updates |
167
- | `getConfig()` | Get board configuration |
168
- | `getRoadmap()` | Get roadmap data |
169
- | `getChangelog(limit?)` | Get changelog entries |
288
+ | Method | Description | Requires User |
289
+ |--------|-------------|---------------|
290
+ | `list(params?)` | List feedback with optional filtering | No |
291
+ | `get(feedbackId)` | Get single feedback with full details | No |
292
+ | `getConfig()` | Get organization configuration | No |
293
+ | `getRoadmap()` | Get roadmap data | No |
294
+ | `getChangelog(limit?)` | Get changelog entries | No |
295
+ | `create(params)` | Submit new feedback | **Yes** |
296
+ | `vote(feedbackId, type?)` | Vote on feedback (toggle) | **Yes** |
297
+ | `comment(params)` | Add a comment | **Yes** |
298
+ | `getComments(feedbackId)` | Get comments for a feedback | No |
299
+ | `subscribe(feedbackId)` | Subscribe to updates | **Yes** |
300
+ | `unsubscribe(feedbackId)` | Unsubscribe from updates | **Yes** |
170
301
 
171
302
  ### React Hooks
172
303
 
173
- | Hook | Description |
174
- |------|-------------|
175
- | `useFeedbackList(options?)` | Fetch feedback list with filters |
176
- | `useFeedback(feedbackId)` | Fetch single feedback item |
177
- | `useComments(feedbackId)` | Fetch comments for feedback |
178
- | `useVote()` | Vote mutation hook |
179
- | `useCreateFeedback()` | Create feedback mutation hook |
180
- | `useAddComment()` | Add comment mutation hook |
181
- | `useSubscription()` | Subscribe/unsubscribe hooks |
182
- | `useBoardConfig()` | Fetch board configuration |
183
- | `useRoadmap()` | Fetch roadmap data |
184
- | `useChangelog(limit?)` | Fetch changelog entries |
304
+ All hooks must be used inside a `<RefletProvider>`.
305
+
306
+ | Hook | Description | Requires User |
307
+ |------|-------------|---------------|
308
+ | `useFeedbackList(options?)` | Fetch feedback list with filters | No |
309
+ | `useFeedback(feedbackId)` | Fetch single feedback item | No |
310
+ | `useComments(feedbackId)` | Fetch comments for feedback | No |
311
+ | `useOrganizationConfig()` | Fetch organization configuration | No |
312
+ | `useRoadmap()` | Fetch roadmap data | No |
313
+ | `useChangelog(limit?)` | Fetch changelog entries | No |
314
+ | `useVote()` | Vote mutation hook | **Yes** |
315
+ | `useCreateFeedback()` | Create feedback mutation hook | **Yes** |
316
+ | `useAddComment()` | Add comment mutation hook | **Yes** |
317
+ | `useSubscription()` | Subscribe/unsubscribe hooks | **Yes** |
185
318
 
186
319
  ### Server Utilities
187
320
 
188
- Works in Node.js, Edge runtimes, Cloudflare Workers, and Convex.
321
+ Works in any JavaScript runtime with Web Crypto API support: Node.js 18+, Deno, Bun, Cloudflare Workers, Vercel Edge, AWS Lambda, etc.
189
322
 
190
323
  ```typescript
191
324
  import { signUser, verifyUser } from 'reflet-sdk/server';
192
325
 
193
326
  // Sign a user token (async - uses Web Crypto API)
194
- const { token, expiresAt } = await signUser(user, secretKey, expiresInSeconds?);
327
+ const { token, expiresAt } = await signUser(
328
+ { id: 'user_123', email: 'user@example.com', name: 'Jane' },
329
+ secretKey,
330
+ expiresInSeconds // Optional, defaults to 24 hours
331
+ );
195
332
 
196
- // Verify a token (optional - server-side validation)
333
+ // Verify a token (optional - for server-side validation)
197
334
  const user = await verifyUser(token, secretKey);
335
+ // Returns user data if valid, null if invalid/expired
198
336
  ```
199
337
 
338
+ ---
339
+
200
340
  ## Types
201
341
 
202
342
  ```typescript
203
343
  interface RefletUser {
204
- id: string;
205
- email?: string;
206
- name?: string;
207
- avatar?: string;
344
+ id: string; // Required: unique identifier from your system
345
+ email?: string; // Optional: used for notifications
346
+ name?: string; // Optional: display name
347
+ avatar?: string; // Optional: avatar URL
208
348
  }
209
349
 
210
350
  interface FeedbackItem {
@@ -214,14 +354,14 @@ interface FeedbackItem {
214
354
  status: FeedbackStatus;
215
355
  voteCount: number;
216
356
  commentCount: number;
217
- hasVoted: boolean;
357
+ hasVoted: boolean; // Whether current user voted
218
358
  author?: FeedbackAuthor;
219
359
  tags: FeedbackTag[];
220
360
  createdAt: string;
221
361
  }
222
362
 
223
363
  interface FeedbackDetail extends FeedbackItem {
224
- isSubscribed: boolean;
364
+ isSubscribed: boolean; // Whether current user is subscribed
225
365
  comments: Comment[];
226
366
  }
227
367
 
@@ -234,111 +374,195 @@ type FeedbackStatus =
234
374
  | 'closed';
235
375
  ```
236
376
 
377
+ ---
378
+
237
379
  ## Error Handling
238
380
 
239
- The SDK provides typed errors for better error handling:
381
+ The SDK provides typed errors for precise error handling:
240
382
 
241
383
  ```typescript
242
384
  import { RefletError, RefletAuthError, RefletNotFoundError } from 'reflet-sdk';
243
385
 
244
386
  try {
245
- await reflet.vote('invalid_id');
387
+ await reflet.vote('feedback_id');
246
388
  } catch (error) {
247
389
  if (error instanceof RefletNotFoundError) {
390
+ // Feedback item doesn't exist
248
391
  console.log('Feedback not found');
249
392
  } else if (error instanceof RefletAuthError) {
250
- console.log('Authentication required');
393
+ // User not identified or invalid token
394
+ console.log('Please sign in to vote');
251
395
  } else if (error instanceof RefletError) {
252
- console.log('API error:', error.message);
396
+ // Other API errors (rate limit, server error, etc.)
397
+ console.log('Error:', error.message, 'Status:', error.status);
253
398
  }
254
399
  }
255
400
  ```
256
401
 
257
- ## Examples
402
+ ### Common Error Scenarios
258
403
 
259
- ### Feedback Widget Component
404
+ | Error | Cause | Solution |
405
+ |-------|-------|----------|
406
+ | `RefletAuthError` | No user/token provided | Pass `user` or `userToken` to the client |
407
+ | `RefletAuthError` | Invalid or expired token | Generate a new token on your server |
408
+ | `RefletNotFoundError` | Invalid feedback ID | Check the ID exists |
409
+ | `RefletError` (403) | Organization is private | Use a secret key or make organization public |
410
+ | `RefletError` (429) | Rate limited | Reduce request frequency |
260
411
 
261
- ```tsx
262
- import { RefletProvider, useFeedbackList, useCreateFeedback, useVote } from 'reflet-sdk/react';
263
- import { useState } from 'react';
412
+ ---
264
413
 
265
- function FeedbackWidget() {
266
- const [isOpen, setIsOpen] = useState(false);
267
- const { data, refetch } = useFeedbackList({ limit: 5, sortBy: 'votes' });
268
- const { mutate: createFeedback, isLoading } = useCreateFeedback();
269
- const { mutate: vote } = useVote();
270
- const [title, setTitle] = useState('');
414
+ ## Framework Integration Examples
271
415
 
272
- const handleSubmit = async (e: React.FormEvent) => {
273
- e.preventDefault();
274
- await createFeedback({ title, description: '' });
275
- setTitle('');
276
- refetch();
277
- };
416
+ ### Generic Pattern (Any Framework)
278
417
 
279
- return (
280
- <div className="feedback-widget">
281
- <button onClick={() => setIsOpen(!isOpen)}>
282
- Feedback
283
- </button>
284
-
285
- {isOpen && (
286
- <div className="feedback-panel">
287
- <form onSubmit={handleSubmit}>
288
- <input
289
- value={title}
290
- onChange={(e) => setTitle(e.target.value)}
291
- placeholder="What's on your mind?"
292
- />
293
- <button type="submit" disabled={isLoading}>
294
- Submit
295
- </button>
296
- </form>
297
-
298
- <ul>
299
- {data?.items.map((item) => (
300
- <li key={item.id}>
301
- <button onClick={() => vote({ feedbackId: item.id })}>
302
- {item.voteCount}
303
- </button>
304
- <span>{item.title}</span>
305
- </li>
306
- ))}
307
- </ul>
308
- </div>
309
- )}
310
- </div>
418
+ ```typescript
419
+ // 1. Create a singleton client instance
420
+ // feedback-client.ts
421
+ import { Reflet } from 'reflet-sdk';
422
+
423
+ let refletInstance: Reflet | null = null;
424
+
425
+ export function getRefletClient(user?: { id: string; email?: string; name?: string }) {
426
+ if (!refletInstance || user) {
427
+ refletInstance = new Reflet({
428
+ publicKey: process.env.REFLET_PUBLIC_KEY || 'fb_pub_xxx',
429
+ user,
430
+ });
431
+ }
432
+ return refletInstance;
433
+ }
434
+
435
+ // 2. Use it anywhere
436
+ const reflet = getRefletClient(currentUser);
437
+ const feedback = await reflet.list();
438
+ ```
439
+
440
+ ### Server-Side Token Generation (Any Backend)
441
+
442
+ ```typescript
443
+ // api/reflet-token.ts (or your preferred API route pattern)
444
+ import { signUser } from 'reflet-sdk/server';
445
+
446
+ export async function handleTokenRequest(authenticatedUser: YourUserType) {
447
+ // Verify user is authenticated with YOUR auth system first
448
+ if (!authenticatedUser) {
449
+ throw new Error('Unauthorized');
450
+ }
451
+
452
+ const { token, expiresAt } = await signUser(
453
+ {
454
+ id: authenticatedUser.id,
455
+ email: authenticatedUser.email,
456
+ name: authenticatedUser.displayName,
457
+ },
458
+ process.env.REFLET_SECRET_KEY!
311
459
  );
460
+
461
+ return { token, expiresAt };
312
462
  }
313
463
  ```
314
464
 
315
- ### Next.js App Router Integration
465
+ ### React with Any State Management
316
466
 
317
467
  ```tsx
318
- // app/providers.tsx
319
- 'use client';
320
-
468
+ // Works with Redux, Zustand, Jotai, MobX, or plain React state
321
469
  import { RefletProvider } from 'reflet-sdk/react';
322
- import { useSession } from 'next-auth/react';
323
470
 
324
- export function Providers({ children }: { children: React.ReactNode }) {
325
- const { data: session } = useSession();
471
+ function App() {
472
+ // Get user from your state management solution
473
+ const user = useYourAuthHook(); // useSelector, useStore, useAtom, etc.
326
474
 
327
475
  return (
328
476
  <RefletProvider
329
- publicKey={process.env.NEXT_PUBLIC_REFLET_KEY!}
330
- user={session?.user ? {
331
- id: session.user.id,
332
- email: session.user.email!,
333
- name: session.user.name!,
334
- } : undefined}
477
+ publicKey="fb_pub_xxx"
478
+ user={user ? { id: user.id, email: user.email, name: user.name } : undefined}
335
479
  >
336
- {children}
480
+ <YourApp />
337
481
  </RefletProvider>
338
482
  );
339
483
  }
340
484
  ```
341
485
 
486
+ ### Vanilla JavaScript (No Framework)
487
+
488
+ ```html
489
+ <script type="module">
490
+ import { Reflet } from 'https://esm.sh/reflet-sdk';
491
+
492
+ const reflet = new Reflet({
493
+ publicKey: 'fb_pub_xxx',
494
+ user: { id: 'user_123' },
495
+ });
496
+
497
+ // Load and display feedback
498
+ const { items } = await reflet.list();
499
+
500
+ document.getElementById('feedback-list').innerHTML = items
501
+ .map(item => `
502
+ <div>
503
+ <h3>${item.title}</h3>
504
+ <p>${item.description}</p>
505
+ <button onclick="vote('${item.id}')">${item.voteCount} votes</button>
506
+ </div>
507
+ `)
508
+ .join('');
509
+
510
+ window.vote = async (id) => {
511
+ await reflet.vote(id);
512
+ location.reload(); // Simple refresh, or update DOM manually
513
+ };
514
+ </script>
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Troubleshooting
520
+
521
+ ### "Authentication required" error
522
+
523
+ **Cause:** Trying to vote, comment, or create feedback without user identification.
524
+
525
+ **Solution:** Provide `user` or `userToken` when initializing the client:
526
+
527
+ ```typescript
528
+ const reflet = new Reflet({
529
+ publicKey: 'fb_pub_xxx',
530
+ user: { id: 'your_user_id' }, // Add this
531
+ });
532
+ ```
533
+
534
+ ### "Organization not found" or 404 errors
535
+
536
+ **Cause:** Invalid public key or organization doesn't exist.
537
+
538
+ **Solution:**
539
+ 1. Verify your public key in Reflet dashboard → Settings → API Keys
540
+ 2. Ensure the organization exists and is active
541
+
542
+ ### "Network error" or connection issues
543
+
544
+ **Cause:** Cannot reach Reflet API.
545
+
546
+ **Solution:**
547
+ 1. Check your internet connection
548
+ 2. Verify no firewall/proxy is blocking requests
549
+ 3. The SDK connects to `https://harmless-clam-802.convex.site` by default
550
+
551
+ ### Token expired errors
552
+
553
+ **Cause:** Server-signed token has expired (default: 24 hours).
554
+
555
+ **Solution:** Generate and send a fresh token from your server:
556
+
557
+ ```typescript
558
+ // On your server
559
+ const { token } = await signUser(user, secretKey, 86400); // 24 hours
560
+
561
+ // Refresh before expiry or on 401 errors
562
+ ```
563
+
564
+ ---
565
+
342
566
  ## License
343
567
 
344
568
  MIT
@@ -29,7 +29,7 @@ var RefletNotFoundError = class extends RefletError {
29
29
  };
30
30
 
31
31
  // src/client.ts
32
- var DEFAULT_API_URL = "https://harmless-clam-802.convex.cloud";
32
+ var DEFAULT_API_URL = "https://harmless-clam-802.convex.site";
33
33
  var Reflet = class {
34
34
  publicKey;
35
35
  baseUrl;
@@ -59,7 +59,7 @@ var Reflet = class {
59
59
  this.user = void 0;
60
60
  }
61
61
  /**
62
- * Get the board configuration
62
+ * Get the organization configuration
63
63
  */
64
64
  async getConfig() {
65
65
  return await this.request("GET", "/api/v1/feedback");
@@ -278,5 +278,5 @@ var Reflet = class {
278
278
  };
279
279
 
280
280
  export { Reflet, RefletAuthError, RefletError, RefletNotFoundError, RefletValidationError };
281
- //# sourceMappingURL=chunk-C342WLTD.js.map
282
- //# sourceMappingURL=chunk-C342WLTD.js.map
281
+ //# sourceMappingURL=chunk-GD7FQQBT.js.map
282
+ //# sourceMappingURL=chunk-GD7FQQBT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/client.ts"],"names":[],"mappings":"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","file":"chunk-GD7FQQBT.js","sourcesContent":["/**\n * Reflet SDK Types\n */\n\n// ============================================\n// Configuration\n// ============================================\n\nexport interface RefletConfig {\n /** Your organization's public API key (fb_pub_xxx) */\n publicKey: string;\n /** API base URL (optional, defaults to Reflet production API) */\n baseUrl?: string;\n /** User identification for SSO */\n user?: RefletUser;\n /** Pre-signed user token (alternative to user object) */\n userToken?: string;\n}\n\nexport interface RefletUser {\n /** Unique identifier for the user in your system */\n id: string;\n /** User's email address */\n email?: string;\n /** User's display name */\n name?: string;\n /** URL to user's avatar image */\n avatar?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================\n// API Responses\n// ============================================\n\nexport interface OrganizationConfig {\n id: string;\n name: string;\n slug: string;\n description?: string;\n isPublic: boolean;\n settings?: OrganizationSettings;\n logo?: string;\n primaryColor?: string;\n statuses: OrganizationStatus[];\n}\n\nexport interface OrganizationSettings {\n allowAnonymousVoting?: boolean;\n requireApproval?: boolean;\n defaultStatus?: FeedbackStatus;\n}\n\nexport interface OrganizationStatus {\n id: string;\n name: string;\n color: string;\n icon?: string;\n order: number;\n}\n\nexport type FeedbackStatus =\n | \"open\"\n | \"under_review\"\n | \"planned\"\n | \"in_progress\"\n | \"completed\"\n | \"closed\";\n\n// ============================================\n// Feedback\n// ============================================\n\nexport interface FeedbackItem {\n id: string;\n title: string;\n description: string;\n status: FeedbackStatus;\n voteCount: number;\n commentCount: number;\n isPinned: boolean;\n hasVoted: boolean;\n createdAt: number;\n updatedAt: number;\n completedAt?: number;\n tags: FeedbackTag[];\n organizationStatus: {\n id: string;\n name: string;\n color: string;\n } | null;\n author: FeedbackAuthor | null;\n}\n\nexport interface FeedbackDetail extends FeedbackItem {\n isSubscribed: boolean;\n}\n\nexport interface FeedbackTag {\n id: string;\n name: string;\n color: string;\n}\n\nexport interface FeedbackAuthor {\n name?: string;\n email?: string;\n avatar?: string;\n isExternal: boolean;\n}\n\nexport interface FeedbackListResponse {\n items: FeedbackItem[];\n total: number;\n hasMore: boolean;\n}\n\nexport interface FeedbackListParams {\n /** Filter by custom status ID */\n statusId?: string;\n /** Filter by status name */\n status?: FeedbackStatus;\n /** Search in title and description */\n search?: string;\n /** Sort order */\n sortBy?: \"votes\" | \"newest\" | \"oldest\" | \"comments\";\n /** Number of items to return (max 100) */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n}\n\nexport interface CreateFeedbackParams {\n title: string;\n description: string;\n}\n\nexport interface CreateFeedbackResponse {\n feedbackId: string;\n isApproved: boolean;\n}\n\nexport interface VoteResponse {\n voted: boolean;\n voteCount: number;\n}\n\n// ============================================\n// Comments\n// ============================================\n\nexport interface Comment {\n id: string;\n body: string;\n isOfficial: boolean;\n author: FeedbackAuthor | null;\n replies: CommentReply[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface CommentReply {\n id: string;\n body: string;\n isOfficial: boolean;\n author: FeedbackAuthor | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface AddCommentParams {\n feedbackId: string;\n body: string;\n parentId?: string;\n}\n\nexport interface AddCommentResponse {\n commentId: string;\n}\n\n// ============================================\n// Subscriptions\n// ============================================\n\nexport interface SubscribeResponse {\n subscribed: boolean;\n alreadySubscribed?: boolean;\n}\n\nexport interface UnsubscribeResponse {\n unsubscribed: boolean;\n wasNotSubscribed?: boolean;\n}\n\n// ============================================\n// Roadmap\n// ============================================\n\nexport interface Roadmap {\n lanes: RoadmapLane[];\n}\n\nexport interface RoadmapLane {\n id: string;\n name: string;\n color: string;\n items: RoadmapItem[];\n}\n\nexport interface RoadmapItem {\n id: string;\n title: string;\n status: FeedbackStatus;\n voteCount: number;\n}\n\n// ============================================\n// Changelog\n// ============================================\n\nexport interface ChangelogEntry {\n id: string;\n title: string;\n description?: string;\n version?: string;\n publishedAt?: number;\n feedback: { id: string; title: string }[];\n}\n\n// ============================================\n// Errors\n// ============================================\n\nexport class RefletError extends Error {\n readonly status: number;\n readonly code?: string;\n\n constructor(message: string, status: number, code?: string) {\n super(message);\n this.name = \"RefletError\";\n this.status = status;\n this.code = code;\n }\n}\n\nexport class RefletAuthError extends RefletError {\n constructor(message: string) {\n super(message, 401, \"AUTH_ERROR\");\n this.name = \"RefletAuthError\";\n }\n}\n\nexport class RefletValidationError extends RefletError {\n constructor(message: string) {\n super(message, 400, \"VALIDATION_ERROR\");\n this.name = \"RefletValidationError\";\n }\n}\n\nexport class RefletNotFoundError extends RefletError {\n constructor(message: string) {\n super(message, 404, \"NOT_FOUND\");\n this.name = \"RefletNotFoundError\";\n }\n}\n","import {\n type AddCommentParams,\n type AddCommentResponse,\n type ChangelogEntry,\n type Comment,\n type CreateFeedbackParams,\n type CreateFeedbackResponse,\n type FeedbackDetail,\n type FeedbackListParams,\n type FeedbackListResponse,\n type OrganizationConfig,\n RefletAuthError,\n type RefletConfig,\n RefletError,\n RefletNotFoundError,\n type RefletUser,\n type Roadmap,\n type SubscribeResponse,\n type UnsubscribeResponse,\n type VoteResponse,\n} from \"./types\";\n\n// Default to Reflet production API (HTTP routes use .convex.site)\nconst DEFAULT_API_URL = \"https://harmless-clam-802.convex.site\";\n\n/**\n * Reflet SDK Client\n *\n * @example\n * ```ts\n * import { Reflet } from 'reflet-sdk';\n *\n * const reflet = new Reflet({\n * publicKey: 'fb_pub_xxx',\n * user: { id: 'user_123', email: 'user@example.com', name: 'John' }\n * });\n *\n * // List feedback\n * const { items } = await reflet.list({ status: 'open' });\n *\n * // Vote on feedback\n * await reflet.vote('feedback_id');\n *\n * // Submit new feedback\n * await reflet.create({ title: 'Great idea', description: 'Details...' });\n * ```\n */\nexport class Reflet {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private userToken: string | undefined;\n private user: RefletUser | undefined;\n\n constructor(config: RefletConfig) {\n this.publicKey = config.publicKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_API_URL;\n this.userToken = config.userToken;\n this.user = config.user;\n\n if (!this.publicKey) {\n throw new Error(\"Reflet: publicKey is required\");\n }\n }\n\n /**\n * Update the user identification\n */\n setUser(user: RefletUser | undefined): void {\n this.user = user;\n this.userToken = undefined;\n }\n\n /**\n * Set the user token directly (for server-signed tokens)\n */\n setUserToken(token: string | undefined): void {\n this.userToken = token;\n this.user = undefined;\n }\n\n /**\n * Get the organization configuration\n */\n async getConfig(): Promise<OrganizationConfig> {\n return await this.request<OrganizationConfig>(\"GET\", \"/api/v1/feedback\");\n }\n\n /**\n * List feedback items with optional filtering\n */\n async list(params?: FeedbackListParams): Promise<FeedbackListResponse> {\n const searchParams = new URLSearchParams();\n\n if (params?.statusId) {\n searchParams.set(\"statusId\", params.statusId);\n }\n if (params?.status) {\n searchParams.set(\"status\", params.status);\n }\n if (params?.search) {\n searchParams.set(\"search\", params.search);\n }\n if (params?.sortBy) {\n searchParams.set(\"sortBy\", params.sortBy);\n }\n if (params?.limit) {\n searchParams.set(\"limit\", String(params.limit));\n }\n if (params?.offset) {\n searchParams.set(\"offset\", String(params.offset));\n }\n\n const query = searchParams.toString();\n const url = `/api/v1/feedback/list${query ? `?${query}` : \"\"}`;\n\n return await this.request<FeedbackListResponse>(\"GET\", url);\n }\n\n /**\n * Get a single feedback item by ID\n */\n async get(feedbackId: string): Promise<FeedbackDetail> {\n const result = await this.request<FeedbackDetail | null>(\n \"GET\",\n `/api/v1/feedback/item?id=${encodeURIComponent(feedbackId)}`\n );\n\n if (!result) {\n throw new RefletNotFoundError(\"Feedback not found\");\n }\n\n return result;\n }\n\n /**\n * Create new feedback\n * Requires user identification\n */\n async create(params: CreateFeedbackParams): Promise<CreateFeedbackResponse> {\n return await this.request<CreateFeedbackResponse>(\n \"POST\",\n \"/api/v1/feedback/create\",\n params\n );\n }\n\n /**\n * Toggle vote on feedback\n * Requires user identification\n */\n async vote(\n feedbackId: string,\n type: \"upvote\" | \"downvote\" = \"upvote\"\n ): Promise<VoteResponse> {\n return await this.request<VoteResponse>(\"POST\", \"/api/v1/feedback/vote\", {\n feedbackId,\n voteType: type,\n });\n }\n\n /**\n * Get comments for a feedback item\n */\n async getComments(\n feedbackId: string,\n sortBy: \"newest\" | \"oldest\" = \"oldest\"\n ): Promise<Comment[]> {\n return await this.request<Comment[]>(\n \"GET\",\n `/api/v1/feedback/comments?feedbackId=${encodeURIComponent(feedbackId)}&sortBy=${sortBy}`\n );\n }\n\n /**\n * Add a comment to feedback\n * Requires user identification\n */\n async comment(params: AddCommentParams): Promise<AddCommentResponse> {\n return await this.request<AddCommentResponse>(\n \"POST\",\n \"/api/v1/feedback/comment\",\n params\n );\n }\n\n /**\n * Subscribe to feedback updates\n * Requires user identification\n */\n async subscribe(feedbackId: string): Promise<SubscribeResponse> {\n return await this.request<SubscribeResponse>(\n \"POST\",\n \"/api/v1/feedback/subscribe\",\n {\n feedbackId,\n }\n );\n }\n\n /**\n * Unsubscribe from feedback updates\n * Requires user identification\n */\n async unsubscribe(feedbackId: string): Promise<UnsubscribeResponse> {\n return await this.request<UnsubscribeResponse>(\n \"POST\",\n \"/api/v1/feedback/unsubscribe\",\n {\n feedbackId,\n }\n );\n }\n\n /**\n * Get roadmap data\n */\n async getRoadmap(): Promise<Roadmap> {\n return await this.request<Roadmap>(\"GET\", \"/api/v1/feedback/roadmap\");\n }\n\n /**\n * Get changelog entries\n */\n async getChangelog(limit?: number): Promise<ChangelogEntry[]> {\n const url = limit\n ? `/api/v1/feedback/changelog?limit=${limit}`\n : \"/api/v1/feedback/changelog\";\n\n return await this.request<ChangelogEntry[]>(\"GET\", url);\n }\n\n /**\n * Build request headers with authentication\n */\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.publicKey}`,\n };\n\n const token = this.userToken ?? this.generateUserToken();\n if (token) {\n headers[\"X-User-Token\"] = token;\n }\n\n return headers;\n }\n\n /**\n * Parse JSON response text safely\n */\n private parseJsonSafely<T>(\n text: string,\n status: number\n ): T | { error: string } {\n try {\n return JSON.parse(text) as T | { error: string };\n } catch (parseError) {\n throw new RefletError(\n `Invalid response: ${parseError instanceof Error ? parseError.message : \"Failed to parse JSON\"}`,\n status\n );\n }\n }\n\n /**\n * Throw appropriate error based on status code\n */\n private throwHttpError(message: string, status: number): never {\n if (status === 401) {\n throw new RefletAuthError(message);\n }\n if (status === 404) {\n throw new RefletNotFoundError(message);\n }\n throw new RefletError(message, status);\n }\n\n /**\n * Make an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n let response: Response;\n try {\n response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers: this.buildHeaders(),\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch (networkError) {\n const message =\n networkError instanceof Error\n ? networkError.message\n : \"Failed to connect\";\n throw new RefletError(`Network error: ${message}`, 0);\n }\n\n const text = await response.text();\n\n // Handle empty response\n if (!text) {\n if (!response.ok) {\n this.throwHttpError(\n `Request failed with status ${response.status}`,\n response.status\n );\n }\n return {} as T;\n }\n\n const data = this.parseJsonSafely<T>(text, response.status);\n\n if (!response.ok) {\n const errorMessage =\n (data as { error?: string }).error ??\n `Request failed with status ${response.status}`;\n this.throwHttpError(errorMessage, response.status);\n }\n\n return data as T;\n }\n\n /**\n * Generate a simple user token for client-side use\n * Note: For production, use server-side token signing\n */\n private generateUserToken(): string | undefined {\n if (!this.user) {\n return undefined;\n }\n\n // Simple base64 encoding for client-side use\n // In production, tokens should be signed server-side\n const payload = {\n id: this.user.id,\n email: this.user.email,\n name: this.user.name,\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + 86_400, // 24 hours\n };\n\n const header = btoa(JSON.stringify({ alg: \"none\", typ: \"JWT\" }));\n const payloadB64 = btoa(JSON.stringify(payload));\n\n return `${header}.${payloadB64}.`;\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Reflet SDK Types
3
3
  */
4
4
  interface RefletConfig {
5
- /** Your board's public API key (fb_pub_xxx) */
5
+ /** Your organization's public API key (fb_pub_xxx) */
6
6
  publicKey: string;
7
7
  /** API base URL (optional, defaults to Reflet production API) */
8
8
  baseUrl?: string;
@@ -23,28 +23,23 @@ interface RefletUser {
23
23
  /** Additional metadata */
24
24
  metadata?: Record<string, unknown>;
25
25
  }
26
- interface BoardConfig {
26
+ interface OrganizationConfig {
27
27
  id: string;
28
28
  name: string;
29
29
  slug: string;
30
30
  description?: string;
31
31
  isPublic: boolean;
32
- settings?: BoardSettings;
33
- organization: {
34
- id: string;
35
- name: string;
36
- slug: string;
37
- logo?: string;
38
- primaryColor?: string;
39
- };
40
- statuses: BoardStatus[];
32
+ settings?: OrganizationSettings;
33
+ logo?: string;
34
+ primaryColor?: string;
35
+ statuses: OrganizationStatus[];
41
36
  }
42
- interface BoardSettings {
37
+ interface OrganizationSettings {
43
38
  allowAnonymousVoting?: boolean;
44
39
  requireApproval?: boolean;
45
40
  defaultStatus?: FeedbackStatus;
46
41
  }
47
- interface BoardStatus {
42
+ interface OrganizationStatus {
48
43
  id: string;
49
44
  name: string;
50
45
  color: string;
@@ -65,7 +60,7 @@ interface FeedbackItem {
65
60
  updatedAt: number;
66
61
  completedAt?: number;
67
62
  tags: FeedbackTag[];
68
- boardStatus: {
63
+ organizationStatus: {
69
64
  id: string;
70
65
  name: string;
71
66
  color: string;
@@ -228,9 +223,9 @@ declare class Reflet {
228
223
  */
229
224
  setUserToken(token: string | undefined): void;
230
225
  /**
231
- * Get the board configuration
226
+ * Get the organization configuration
232
227
  */
233
- getConfig(): Promise<BoardConfig>;
228
+ getConfig(): Promise<OrganizationConfig>;
234
229
  /**
235
230
  * List feedback items with optional filtering
236
231
  */
@@ -299,4 +294,4 @@ declare class Reflet {
299
294
  private generateUserToken;
300
295
  }
301
296
 
302
- export { type AddCommentParams, type AddCommentResponse, type BoardConfig, type BoardSettings, type BoardStatus, type ChangelogEntry, type Comment, type CommentReply, type CreateFeedbackParams, type CreateFeedbackResponse, type FeedbackAuthor, type FeedbackDetail, type FeedbackItem, type FeedbackListParams, type FeedbackListResponse, type FeedbackStatus, type FeedbackTag, Reflet, RefletAuthError, type RefletConfig, RefletError, RefletNotFoundError, type RefletUser, RefletValidationError, type Roadmap, type RoadmapItem, type RoadmapLane, type SubscribeResponse, type UnsubscribeResponse, type VoteResponse };
297
+ export { type AddCommentParams, type AddCommentResponse, type ChangelogEntry, type Comment, type CommentReply, type CreateFeedbackParams, type CreateFeedbackResponse, type FeedbackAuthor, type FeedbackDetail, type FeedbackItem, type FeedbackListParams, type FeedbackListResponse, type FeedbackStatus, type FeedbackTag, type OrganizationConfig, type OrganizationSettings, type OrganizationStatus, Reflet, RefletAuthError, type RefletConfig, RefletError, RefletNotFoundError, type RefletUser, RefletValidationError, type Roadmap, type RoadmapItem, type RoadmapLane, type SubscribeResponse, type UnsubscribeResponse, type VoteResponse };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Reflet, RefletAuthError, RefletError, RefletNotFoundError, RefletValidationError } from './chunk-C342WLTD.js';
1
+ export { Reflet, RefletAuthError, RefletError, RefletNotFoundError, RefletValidationError } from './chunk-GD7FQQBT.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { RefletUser, Reflet, FeedbackListParams, AddCommentParams, BoardConfig, ChangelogEntry, Comment, CreateFeedbackParams, FeedbackDetail, FeedbackItem, Roadmap } from './index.js';
2
- export { FeedbackStatus, FeedbackTag, RefletConfig, RoadmapItem, RoadmapLane } from './index.js';
1
+ import { RefletUser, Reflet, FeedbackListParams, AddCommentParams, ChangelogEntry, Comment, CreateFeedbackParams, FeedbackDetail, FeedbackItem, OrganizationConfig, Roadmap } from './index.js';
2
+ export { FeedbackStatus, FeedbackTag, OrganizationSettings, OrganizationStatus, RefletConfig, RoadmapItem, RoadmapLane } from './index.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
5
5
 
@@ -10,7 +10,7 @@ interface RefletContextValue {
10
10
  setUser: (user: RefletUser | undefined) => void;
11
11
  }
12
12
  interface RefletProviderProps {
13
- /** Your board's public API key */
13
+ /** Your organization's public API key */
14
14
  publicKey: string;
15
15
  /** API base URL (optional, defaults to Reflet production API) */
16
16
  baseUrl?: string;
@@ -58,9 +58,9 @@ interface UseMutationResult<TData, TVariables> {
58
58
  reset: () => void;
59
59
  }
60
60
  /**
61
- * Hook to fetch board configuration
61
+ * Hook to fetch organization configuration
62
62
  */
63
- declare function useBoardConfig(): UseQueryResult<BoardConfig>;
63
+ declare function useOrganizationConfig(): UseQueryResult<OrganizationConfig>;
64
64
  interface UseFeedbackListOptions extends FeedbackListParams {
65
65
  /** Auto-fetch on mount */
66
66
  enabled?: boolean;
@@ -155,4 +155,4 @@ declare function useSubscription(): {
155
155
  error: Error | null;
156
156
  };
157
157
 
158
- export { AddCommentParams, BoardConfig, ChangelogEntry, Comment, CreateFeedbackParams, FeedbackDetail, FeedbackItem, FeedbackListParams, Reflet, RefletProvider, type RefletProviderProps, RefletUser, Roadmap, type UseFeedbackListOptions, useAddComment, useBoardConfig, useChangelog, useComments, useCreateFeedback, useFeedback, useFeedbackList, useRefletClient, useRefletContext, useRoadmap, useSubscription, useVote };
158
+ export { AddCommentParams, ChangelogEntry, Comment, CreateFeedbackParams, FeedbackDetail, FeedbackItem, FeedbackListParams, OrganizationConfig, Reflet, RefletProvider, type RefletProviderProps, RefletUser, Roadmap, type UseFeedbackListOptions, useAddComment, useChangelog, useComments, useCreateFeedback, useFeedback, useFeedbackList, useOrganizationConfig, useRefletClient, useRefletContext, useRoadmap, useSubscription, useVote };
package/dist/react.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Reflet } from './chunk-C342WLTD.js';
2
- export { Reflet } from './chunk-C342WLTD.js';
1
+ import { Reflet } from './chunk-GD7FQQBT.js';
2
+ export { Reflet } from './chunk-GD7FQQBT.js';
3
3
  import { createContext, useState, useMemo, useEffect, useContext, useCallback } from 'react';
4
4
  import { jsx } from 'react/jsx-runtime';
5
5
 
@@ -54,7 +54,7 @@ function useRefletClient() {
54
54
  const { client } = useRefletContext();
55
55
  return client;
56
56
  }
57
- function useBoardConfig() {
57
+ function useOrganizationConfig() {
58
58
  const client = useRefletClient();
59
59
  const [data, setData] = useState();
60
60
  const [isLoading, setIsLoading] = useState(true);
@@ -67,7 +67,7 @@ function useBoardConfig() {
67
67
  setData(result);
68
68
  } catch (err) {
69
69
  setError(
70
- err instanceof Error ? err : new Error("Failed to fetch board config")
70
+ err instanceof Error ? err : new Error("Failed to fetch organization config")
71
71
  );
72
72
  } finally {
73
73
  setIsLoading(false);
@@ -350,6 +350,6 @@ function useSubscription() {
350
350
  return { subscribe, unsubscribe, isLoading, error };
351
351
  }
352
352
 
353
- export { RefletProvider, useAddComment, useBoardConfig, useChangelog, useComments, useCreateFeedback, useFeedback, useFeedbackList, useRefletClient, useRefletContext, useRoadmap, useSubscription, useVote };
353
+ export { RefletProvider, useAddComment, useChangelog, useComments, useCreateFeedback, useFeedback, useFeedbackList, useOrganizationConfig, useRefletClient, useRefletContext, useRoadmap, useSubscription, useVote };
354
354
  //# sourceMappingURL=react.js.map
355
355
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-context.tsx","../src/react-hooks.ts"],"names":["useState","useEffect","error"],"mappings":"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","file":"react.js","sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { Reflet } from \"./client\";\nimport type { RefletConfig, RefletUser } from \"./types\";\n\n// ============================================\n// Context Types\n// ============================================\n\ninterface RefletContextValue {\n client: Reflet;\n isReady: boolean;\n user: RefletUser | undefined;\n setUser: (user: RefletUser | undefined) => void;\n}\n\n// ============================================\n// Context\n// ============================================\n\nconst RefletContext = createContext<RefletContextValue | null>(null);\n\n// ============================================\n// Provider\n// ============================================\n\nexport interface RefletProviderProps {\n /** Your board's public API key */\n publicKey: string;\n /** API base URL (optional, defaults to Reflet production API) */\n baseUrl?: string;\n /** Initial user identification */\n user?: RefletUser;\n /** Pre-signed user token (alternative to user) */\n userToken?: string;\n /** Children */\n children: ReactNode;\n}\n\n/**\n * Provider component for Reflet SDK\n *\n * @example\n * ```tsx\n * import { RefletProvider } from 'reflet-sdk/react';\n *\n * function App() {\n * return (\n * <RefletProvider\n * publicKey=\"fb_pub_xxx\"\n * user={{ id: 'user_123', email: 'user@example.com', name: 'John' }}\n * >\n * <YourApp />\n * </RefletProvider>\n * );\n * }\n * ```\n */\nexport function RefletProvider({\n publicKey,\n baseUrl,\n user: initialUser,\n userToken,\n children,\n}: RefletProviderProps) {\n const [user, setUser] = useState<RefletUser | undefined>(initialUser);\n const [isReady, setIsReady] = useState(false);\n\n const client = useMemo(() => {\n const config: RefletConfig = {\n publicKey,\n baseUrl,\n user,\n userToken,\n };\n return new Reflet(config);\n }, [publicKey, baseUrl, user, userToken]);\n\n // Update client when user changes\n useEffect(() => {\n if (userToken) {\n client.setUserToken(userToken);\n } else {\n client.setUser(user);\n }\n }, [client, user, userToken]);\n\n // Mark as ready after mount\n useEffect(() => {\n setIsReady(true);\n }, []);\n\n const value = useMemo(\n () => ({\n client,\n isReady,\n user,\n setUser,\n }),\n [client, isReady, user]\n );\n\n return (\n <RefletContext.Provider value={value}>{children}</RefletContext.Provider>\n );\n}\n\n// ============================================\n// Hook to access context\n// ============================================\n\nexport function useRefletContext(): RefletContextValue {\n const context = useContext(RefletContext);\n if (!context) {\n throw new Error(\"useRefletContext must be used within a RefletProvider\");\n }\n return context;\n}\n\nexport function useRefletClient(): Reflet {\n const { client } = useRefletContext();\n return client;\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { useRefletClient } from \"./react-context\";\nimport type {\n AddCommentParams,\n BoardConfig,\n ChangelogEntry,\n Comment,\n CreateFeedbackParams,\n FeedbackDetail,\n FeedbackItem,\n FeedbackListParams,\n Roadmap,\n} from \"./types\";\n\n// ============================================\n// Types\n// ============================================\n\ninterface UseQueryResult<T> {\n data: T | undefined;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n}\n\ninterface UseMutationResult<TData, TVariables> {\n mutate: (variables: TVariables) => Promise<TData>;\n isLoading: boolean;\n error: Error | null;\n data: TData | undefined;\n reset: () => void;\n}\n\n// ============================================\n// Board Config Hook\n// ============================================\n\n/**\n * Hook to fetch board configuration\n */\nexport function useBoardConfig(): UseQueryResult<BoardConfig> {\n const client = useRefletClient();\n const [data, setData] = useState<BoardConfig | undefined>();\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getConfig();\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch board config\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Feedback List Hook\n// ============================================\n\nexport interface UseFeedbackListOptions extends FeedbackListParams {\n /** Auto-fetch on mount */\n enabled?: boolean;\n}\n\n/**\n * Hook to fetch feedback list with filtering\n *\n * @example\n * ```tsx\n * const { data, isLoading } = useFeedbackList({ status: 'open', sortBy: 'votes' });\n * ```\n */\nexport function useFeedbackList(\n options: UseFeedbackListOptions = {}\n): UseQueryResult<{ items: FeedbackItem[]; total: number; hasMore: boolean }> {\n const client = useRefletClient();\n const { enabled = true, ...params } = options;\n\n // Create stable reference using individual params\n const { statusId, status, search, sortBy, limit, offset } = params;\n\n const [data, setData] = useState<{\n items: FeedbackItem[];\n total: number;\n hasMore: boolean;\n }>();\n const [isLoading, setIsLoading] = useState(enabled);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.list({\n statusId,\n status,\n search,\n sortBy,\n limit,\n offset,\n });\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch feedback\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, statusId, status, search, sortBy, limit, offset]);\n\n useEffect(() => {\n if (enabled) {\n fetch();\n }\n }, [fetch, enabled]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Single Feedback Hook\n// ============================================\n\n/**\n * Hook to fetch a single feedback item\n *\n * @example\n * ```tsx\n * const { data: feedback, isLoading } = useFeedback('feedback_id');\n * ```\n */\nexport function useFeedback(\n feedbackId: string | undefined\n): UseQueryResult<FeedbackDetail> {\n const client = useRefletClient();\n const [data, setData] = useState<FeedbackDetail | undefined>();\n const [isLoading, setIsLoading] = useState(!!feedbackId);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n if (!feedbackId) {\n setData(undefined);\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.get(feedbackId);\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch feedback\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, feedbackId]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Comments Hook\n// ============================================\n\n/**\n * Hook to fetch comments for a feedback item\n */\nexport function useComments(\n feedbackId: string | undefined,\n sortBy: \"newest\" | \"oldest\" = \"oldest\"\n): UseQueryResult<Comment[]> {\n const client = useRefletClient();\n const [data, setData] = useState<Comment[] | undefined>();\n const [isLoading, setIsLoading] = useState(!!feedbackId);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n if (!feedbackId) {\n setData(undefined);\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getComments(feedbackId, sortBy);\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch comments\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, feedbackId, sortBy]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Roadmap Hook\n// ============================================\n\n/**\n * Hook to fetch roadmap data\n */\nexport function useRoadmap(): UseQueryResult<Roadmap> {\n const client = useRefletClient();\n const [data, setData] = useState<Roadmap | undefined>();\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getRoadmap();\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch roadmap\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Changelog Hook\n// ============================================\n\n/**\n * Hook to fetch changelog entries\n */\nexport function useChangelog(limit?: number): UseQueryResult<ChangelogEntry[]> {\n const client = useRefletClient();\n const [data, setData] = useState<ChangelogEntry[] | undefined>();\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getChangelog(limit);\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch changelog\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, limit]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Create Feedback Mutation Hook\n// ============================================\n\n/**\n * Hook to create new feedback\n *\n * @example\n * ```tsx\n * const { mutate: createFeedback, isLoading } = useCreateFeedback();\n *\n * const handleSubmit = async (data) => {\n * const result = await createFeedback(data);\n * console.log('Created:', result.feedbackId);\n * };\n * ```\n */\nexport function useCreateFeedback(): UseMutationResult<\n { feedbackId: string; isApproved: boolean },\n CreateFeedbackParams\n> {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<{\n feedbackId: string;\n isApproved: boolean;\n }>();\n\n const mutate = useCallback(\n async (params: CreateFeedbackParams) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.create(params);\n setData(result);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to create feedback\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setError(null);\n }, []);\n\n return { mutate, isLoading, error, data, reset };\n}\n\n// ============================================\n// Vote Mutation Hook\n// ============================================\n\n/**\n * Hook to vote on feedback\n *\n * @example\n * ```tsx\n * const { mutate: vote } = useVote();\n *\n * <button onClick={() => vote({ feedbackId: 'xxx' })}>\n * Vote\n * </button>\n * ```\n */\nexport function useVote(): UseMutationResult<\n { voted: boolean; voteCount: number },\n { feedbackId: string; type?: \"upvote\" | \"downvote\" }\n> {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<{ voted: boolean; voteCount: number }>();\n\n const mutate = useCallback(\n async ({\n feedbackId,\n type = \"upvote\",\n }: {\n feedbackId: string;\n type?: \"upvote\" | \"downvote\";\n }) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.vote(feedbackId, type);\n setData(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(\"Failed to vote\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setError(null);\n }, []);\n\n return { mutate, isLoading, error, data, reset };\n}\n\n// ============================================\n// Comment Mutation Hook\n// ============================================\n\n/**\n * Hook to add a comment\n */\nexport function useAddComment(): UseMutationResult<\n { commentId: string },\n AddCommentParams\n> {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<{ commentId: string }>();\n\n const mutate = useCallback(\n async (params: AddCommentParams) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.comment(params);\n setData(result);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to add comment\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setError(null);\n }, []);\n\n return { mutate, isLoading, error, data, reset };\n}\n\n// ============================================\n// Subscribe Mutation Hook\n// ============================================\n\n/**\n * Hook to subscribe/unsubscribe to feedback\n */\nexport function useSubscription(): {\n subscribe: (feedbackId: string) => Promise<{ subscribed: boolean }>;\n unsubscribe: (feedbackId: string) => Promise<{ unsubscribed: boolean }>;\n isLoading: boolean;\n error: Error | null;\n} {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const subscribe = useCallback(\n async (feedbackId: string) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.subscribe(feedbackId);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to subscribe\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const unsubscribe = useCallback(\n async (feedbackId: string) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.unsubscribe(feedbackId);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to unsubscribe\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n return { subscribe, unsubscribe, isLoading, error };\n}\n"]}
1
+ {"version":3,"sources":["../src/react-context.tsx","../src/react-hooks.ts"],"names":["useState","useEffect","error"],"mappings":"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","file":"react.js","sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { Reflet } from \"./client\";\nimport type { RefletConfig, RefletUser } from \"./types\";\n\n// ============================================\n// Context Types\n// ============================================\n\ninterface RefletContextValue {\n client: Reflet;\n isReady: boolean;\n user: RefletUser | undefined;\n setUser: (user: RefletUser | undefined) => void;\n}\n\n// ============================================\n// Context\n// ============================================\n\nconst RefletContext = createContext<RefletContextValue | null>(null);\n\n// ============================================\n// Provider\n// ============================================\n\nexport interface RefletProviderProps {\n /** Your organization's public API key */\n publicKey: string;\n /** API base URL (optional, defaults to Reflet production API) */\n baseUrl?: string;\n /** Initial user identification */\n user?: RefletUser;\n /** Pre-signed user token (alternative to user) */\n userToken?: string;\n /** Children */\n children: ReactNode;\n}\n\n/**\n * Provider component for Reflet SDK\n *\n * @example\n * ```tsx\n * import { RefletProvider } from 'reflet-sdk/react';\n *\n * function App() {\n * return (\n * <RefletProvider\n * publicKey=\"fb_pub_xxx\"\n * user={{ id: 'user_123', email: 'user@example.com', name: 'John' }}\n * >\n * <YourApp />\n * </RefletProvider>\n * );\n * }\n * ```\n */\nexport function RefletProvider({\n publicKey,\n baseUrl,\n user: initialUser,\n userToken,\n children,\n}: RefletProviderProps) {\n const [user, setUser] = useState<RefletUser | undefined>(initialUser);\n const [isReady, setIsReady] = useState(false);\n\n const client = useMemo(() => {\n const config: RefletConfig = {\n publicKey,\n baseUrl,\n user,\n userToken,\n };\n return new Reflet(config);\n }, [publicKey, baseUrl, user, userToken]);\n\n // Update client when user changes\n useEffect(() => {\n if (userToken) {\n client.setUserToken(userToken);\n } else {\n client.setUser(user);\n }\n }, [client, user, userToken]);\n\n // Mark as ready after mount\n useEffect(() => {\n setIsReady(true);\n }, []);\n\n const value = useMemo(\n () => ({\n client,\n isReady,\n user,\n setUser,\n }),\n [client, isReady, user]\n );\n\n return (\n <RefletContext.Provider value={value}>{children}</RefletContext.Provider>\n );\n}\n\n// ============================================\n// Hook to access context\n// ============================================\n\nexport function useRefletContext(): RefletContextValue {\n const context = useContext(RefletContext);\n if (!context) {\n throw new Error(\"useRefletContext must be used within a RefletProvider\");\n }\n return context;\n}\n\nexport function useRefletClient(): Reflet {\n const { client } = useRefletContext();\n return client;\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { useRefletClient } from \"./react-context\";\nimport type {\n AddCommentParams,\n ChangelogEntry,\n Comment,\n CreateFeedbackParams,\n FeedbackDetail,\n FeedbackItem,\n FeedbackListParams,\n OrganizationConfig,\n Roadmap,\n} from \"./types\";\n\n// ============================================\n// Types\n// ============================================\n\ninterface UseQueryResult<T> {\n data: T | undefined;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n}\n\ninterface UseMutationResult<TData, TVariables> {\n mutate: (variables: TVariables) => Promise<TData>;\n isLoading: boolean;\n error: Error | null;\n data: TData | undefined;\n reset: () => void;\n}\n\n// ============================================\n// Organization Config Hook\n// ============================================\n\n/**\n * Hook to fetch organization configuration\n */\nexport function useOrganizationConfig(): UseQueryResult<OrganizationConfig> {\n const client = useRefletClient();\n const [data, setData] = useState<OrganizationConfig | undefined>();\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getConfig();\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error\n ? err\n : new Error(\"Failed to fetch organization config\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Feedback List Hook\n// ============================================\n\nexport interface UseFeedbackListOptions extends FeedbackListParams {\n /** Auto-fetch on mount */\n enabled?: boolean;\n}\n\n/**\n * Hook to fetch feedback list with filtering\n *\n * @example\n * ```tsx\n * const { data, isLoading } = useFeedbackList({ status: 'open', sortBy: 'votes' });\n * ```\n */\nexport function useFeedbackList(\n options: UseFeedbackListOptions = {}\n): UseQueryResult<{ items: FeedbackItem[]; total: number; hasMore: boolean }> {\n const client = useRefletClient();\n const { enabled = true, ...params } = options;\n\n // Create stable reference using individual params\n const { statusId, status, search, sortBy, limit, offset } = params;\n\n const [data, setData] = useState<{\n items: FeedbackItem[];\n total: number;\n hasMore: boolean;\n }>();\n const [isLoading, setIsLoading] = useState(enabled);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.list({\n statusId,\n status,\n search,\n sortBy,\n limit,\n offset,\n });\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch feedback\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, statusId, status, search, sortBy, limit, offset]);\n\n useEffect(() => {\n if (enabled) {\n fetch();\n }\n }, [fetch, enabled]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Single Feedback Hook\n// ============================================\n\n/**\n * Hook to fetch a single feedback item\n *\n * @example\n * ```tsx\n * const { data: feedback, isLoading } = useFeedback('feedback_id');\n * ```\n */\nexport function useFeedback(\n feedbackId: string | undefined\n): UseQueryResult<FeedbackDetail> {\n const client = useRefletClient();\n const [data, setData] = useState<FeedbackDetail | undefined>();\n const [isLoading, setIsLoading] = useState(!!feedbackId);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n if (!feedbackId) {\n setData(undefined);\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.get(feedbackId);\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch feedback\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, feedbackId]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Comments Hook\n// ============================================\n\n/**\n * Hook to fetch comments for a feedback item\n */\nexport function useComments(\n feedbackId: string | undefined,\n sortBy: \"newest\" | \"oldest\" = \"oldest\"\n): UseQueryResult<Comment[]> {\n const client = useRefletClient();\n const [data, setData] = useState<Comment[] | undefined>();\n const [isLoading, setIsLoading] = useState(!!feedbackId);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n if (!feedbackId) {\n setData(undefined);\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getComments(feedbackId, sortBy);\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch comments\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, feedbackId, sortBy]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Roadmap Hook\n// ============================================\n\n/**\n * Hook to fetch roadmap data\n */\nexport function useRoadmap(): UseQueryResult<Roadmap> {\n const client = useRefletClient();\n const [data, setData] = useState<Roadmap | undefined>();\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getRoadmap();\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch roadmap\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Changelog Hook\n// ============================================\n\n/**\n * Hook to fetch changelog entries\n */\nexport function useChangelog(limit?: number): UseQueryResult<ChangelogEntry[]> {\n const client = useRefletClient();\n const [data, setData] = useState<ChangelogEntry[] | undefined>();\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetch = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.getChangelog(limit);\n setData(result);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error(\"Failed to fetch changelog\")\n );\n } finally {\n setIsLoading(false);\n }\n }, [client, limit]);\n\n useEffect(() => {\n fetch();\n }, [fetch]);\n\n return { data, isLoading, error, refetch: fetch };\n}\n\n// ============================================\n// Create Feedback Mutation Hook\n// ============================================\n\n/**\n * Hook to create new feedback\n *\n * @example\n * ```tsx\n * const { mutate: createFeedback, isLoading } = useCreateFeedback();\n *\n * const handleSubmit = async (data) => {\n * const result = await createFeedback(data);\n * console.log('Created:', result.feedbackId);\n * };\n * ```\n */\nexport function useCreateFeedback(): UseMutationResult<\n { feedbackId: string; isApproved: boolean },\n CreateFeedbackParams\n> {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<{\n feedbackId: string;\n isApproved: boolean;\n }>();\n\n const mutate = useCallback(\n async (params: CreateFeedbackParams) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.create(params);\n setData(result);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to create feedback\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setError(null);\n }, []);\n\n return { mutate, isLoading, error, data, reset };\n}\n\n// ============================================\n// Vote Mutation Hook\n// ============================================\n\n/**\n * Hook to vote on feedback\n *\n * @example\n * ```tsx\n * const { mutate: vote } = useVote();\n *\n * <button onClick={() => vote({ feedbackId: 'xxx' })}>\n * Vote\n * </button>\n * ```\n */\nexport function useVote(): UseMutationResult<\n { voted: boolean; voteCount: number },\n { feedbackId: string; type?: \"upvote\" | \"downvote\" }\n> {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<{ voted: boolean; voteCount: number }>();\n\n const mutate = useCallback(\n async ({\n feedbackId,\n type = \"upvote\",\n }: {\n feedbackId: string;\n type?: \"upvote\" | \"downvote\";\n }) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.vote(feedbackId, type);\n setData(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(\"Failed to vote\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setError(null);\n }, []);\n\n return { mutate, isLoading, error, data, reset };\n}\n\n// ============================================\n// Comment Mutation Hook\n// ============================================\n\n/**\n * Hook to add a comment\n */\nexport function useAddComment(): UseMutationResult<\n { commentId: string },\n AddCommentParams\n> {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<{ commentId: string }>();\n\n const mutate = useCallback(\n async (params: AddCommentParams) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.comment(params);\n setData(result);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to add comment\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setError(null);\n }, []);\n\n return { mutate, isLoading, error, data, reset };\n}\n\n// ============================================\n// Subscribe Mutation Hook\n// ============================================\n\n/**\n * Hook to subscribe/unsubscribe to feedback\n */\nexport function useSubscription(): {\n subscribe: (feedbackId: string) => Promise<{ subscribed: boolean }>;\n unsubscribe: (feedbackId: string) => Promise<{ unsubscribed: boolean }>;\n isLoading: boolean;\n error: Error | null;\n} {\n const client = useRefletClient();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const subscribe = useCallback(\n async (feedbackId: string) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.subscribe(feedbackId);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to subscribe\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n const unsubscribe = useCallback(\n async (feedbackId: string) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await client.unsubscribe(feedbackId);\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Failed to unsubscribe\");\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client]\n );\n\n return { subscribe, unsubscribe, isLoading, error };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflet-sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Official Reflet SDK - Collect user feedback, feature requests, and bug reports from your app",
5
5
  "license": "MIT",
6
6
  "author": "Reflet <hello@reflet.app>",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/types.ts","../src/client.ts"],"names":[],"mappings":"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","file":"chunk-C342WLTD.js","sourcesContent":["/**\n * Reflet SDK Types\n */\n\n// ============================================\n// Configuration\n// ============================================\n\nexport interface RefletConfig {\n /** Your board's public API key (fb_pub_xxx) */\n publicKey: string;\n /** API base URL (optional, defaults to Reflet production API) */\n baseUrl?: string;\n /** User identification for SSO */\n user?: RefletUser;\n /** Pre-signed user token (alternative to user object) */\n userToken?: string;\n}\n\nexport interface RefletUser {\n /** Unique identifier for the user in your system */\n id: string;\n /** User's email address */\n email?: string;\n /** User's display name */\n name?: string;\n /** URL to user's avatar image */\n avatar?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================\n// API Responses\n// ============================================\n\nexport interface BoardConfig {\n id: string;\n name: string;\n slug: string;\n description?: string;\n isPublic: boolean;\n settings?: BoardSettings;\n organization: {\n id: string;\n name: string;\n slug: string;\n logo?: string;\n primaryColor?: string;\n };\n statuses: BoardStatus[];\n}\n\nexport interface BoardSettings {\n allowAnonymousVoting?: boolean;\n requireApproval?: boolean;\n defaultStatus?: FeedbackStatus;\n}\n\nexport interface BoardStatus {\n id: string;\n name: string;\n color: string;\n icon?: string;\n order: number;\n}\n\nexport type FeedbackStatus =\n | \"open\"\n | \"under_review\"\n | \"planned\"\n | \"in_progress\"\n | \"completed\"\n | \"closed\";\n\n// ============================================\n// Feedback\n// ============================================\n\nexport interface FeedbackItem {\n id: string;\n title: string;\n description: string;\n status: FeedbackStatus;\n voteCount: number;\n commentCount: number;\n isPinned: boolean;\n hasVoted: boolean;\n createdAt: number;\n updatedAt: number;\n completedAt?: number;\n tags: FeedbackTag[];\n boardStatus: {\n id: string;\n name: string;\n color: string;\n } | null;\n author: FeedbackAuthor | null;\n}\n\nexport interface FeedbackDetail extends FeedbackItem {\n isSubscribed: boolean;\n}\n\nexport interface FeedbackTag {\n id: string;\n name: string;\n color: string;\n}\n\nexport interface FeedbackAuthor {\n name?: string;\n email?: string;\n avatar?: string;\n isExternal: boolean;\n}\n\nexport interface FeedbackListResponse {\n items: FeedbackItem[];\n total: number;\n hasMore: boolean;\n}\n\nexport interface FeedbackListParams {\n /** Filter by custom status ID */\n statusId?: string;\n /** Filter by status name */\n status?: FeedbackStatus;\n /** Search in title and description */\n search?: string;\n /** Sort order */\n sortBy?: \"votes\" | \"newest\" | \"oldest\" | \"comments\";\n /** Number of items to return (max 100) */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n}\n\nexport interface CreateFeedbackParams {\n title: string;\n description: string;\n}\n\nexport interface CreateFeedbackResponse {\n feedbackId: string;\n isApproved: boolean;\n}\n\nexport interface VoteResponse {\n voted: boolean;\n voteCount: number;\n}\n\n// ============================================\n// Comments\n// ============================================\n\nexport interface Comment {\n id: string;\n body: string;\n isOfficial: boolean;\n author: FeedbackAuthor | null;\n replies: CommentReply[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface CommentReply {\n id: string;\n body: string;\n isOfficial: boolean;\n author: FeedbackAuthor | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface AddCommentParams {\n feedbackId: string;\n body: string;\n parentId?: string;\n}\n\nexport interface AddCommentResponse {\n commentId: string;\n}\n\n// ============================================\n// Subscriptions\n// ============================================\n\nexport interface SubscribeResponse {\n subscribed: boolean;\n alreadySubscribed?: boolean;\n}\n\nexport interface UnsubscribeResponse {\n unsubscribed: boolean;\n wasNotSubscribed?: boolean;\n}\n\n// ============================================\n// Roadmap\n// ============================================\n\nexport interface Roadmap {\n lanes: RoadmapLane[];\n}\n\nexport interface RoadmapLane {\n id: string;\n name: string;\n color: string;\n items: RoadmapItem[];\n}\n\nexport interface RoadmapItem {\n id: string;\n title: string;\n status: FeedbackStatus;\n voteCount: number;\n}\n\n// ============================================\n// Changelog\n// ============================================\n\nexport interface ChangelogEntry {\n id: string;\n title: string;\n description?: string;\n version?: string;\n publishedAt?: number;\n feedback: { id: string; title: string }[];\n}\n\n// ============================================\n// Errors\n// ============================================\n\nexport class RefletError extends Error {\n readonly status: number;\n readonly code?: string;\n\n constructor(message: string, status: number, code?: string) {\n super(message);\n this.name = \"RefletError\";\n this.status = status;\n this.code = code;\n }\n}\n\nexport class RefletAuthError extends RefletError {\n constructor(message: string) {\n super(message, 401, \"AUTH_ERROR\");\n this.name = \"RefletAuthError\";\n }\n}\n\nexport class RefletValidationError extends RefletError {\n constructor(message: string) {\n super(message, 400, \"VALIDATION_ERROR\");\n this.name = \"RefletValidationError\";\n }\n}\n\nexport class RefletNotFoundError extends RefletError {\n constructor(message: string) {\n super(message, 404, \"NOT_FOUND\");\n this.name = \"RefletNotFoundError\";\n }\n}\n","import {\n type AddCommentParams,\n type AddCommentResponse,\n type BoardConfig,\n type ChangelogEntry,\n type Comment,\n type CreateFeedbackParams,\n type CreateFeedbackResponse,\n type FeedbackDetail,\n type FeedbackListParams,\n type FeedbackListResponse,\n RefletAuthError,\n type RefletConfig,\n RefletError,\n RefletNotFoundError,\n type RefletUser,\n type Roadmap,\n type SubscribeResponse,\n type UnsubscribeResponse,\n type VoteResponse,\n} from \"./types\";\n\n// Default to Reflet production API\nconst DEFAULT_API_URL = \"https://harmless-clam-802.convex.cloud\";\n\n/**\n * Reflet SDK Client\n *\n * @example\n * ```ts\n * import { Reflet } from 'reflet-sdk';\n *\n * const reflet = new Reflet({\n * publicKey: 'fb_pub_xxx',\n * user: { id: 'user_123', email: 'user@example.com', name: 'John' }\n * });\n *\n * // List feedback\n * const { items } = await reflet.list({ status: 'open' });\n *\n * // Vote on feedback\n * await reflet.vote('feedback_id');\n *\n * // Submit new feedback\n * await reflet.create({ title: 'Great idea', description: 'Details...' });\n * ```\n */\nexport class Reflet {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private userToken: string | undefined;\n private user: RefletUser | undefined;\n\n constructor(config: RefletConfig) {\n this.publicKey = config.publicKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_API_URL;\n this.userToken = config.userToken;\n this.user = config.user;\n\n if (!this.publicKey) {\n throw new Error(\"Reflet: publicKey is required\");\n }\n }\n\n /**\n * Update the user identification\n */\n setUser(user: RefletUser | undefined): void {\n this.user = user;\n this.userToken = undefined;\n }\n\n /**\n * Set the user token directly (for server-signed tokens)\n */\n setUserToken(token: string | undefined): void {\n this.userToken = token;\n this.user = undefined;\n }\n\n /**\n * Get the board configuration\n */\n async getConfig(): Promise<BoardConfig> {\n return await this.request<BoardConfig>(\"GET\", \"/api/v1/feedback\");\n }\n\n /**\n * List feedback items with optional filtering\n */\n async list(params?: FeedbackListParams): Promise<FeedbackListResponse> {\n const searchParams = new URLSearchParams();\n\n if (params?.statusId) {\n searchParams.set(\"statusId\", params.statusId);\n }\n if (params?.status) {\n searchParams.set(\"status\", params.status);\n }\n if (params?.search) {\n searchParams.set(\"search\", params.search);\n }\n if (params?.sortBy) {\n searchParams.set(\"sortBy\", params.sortBy);\n }\n if (params?.limit) {\n searchParams.set(\"limit\", String(params.limit));\n }\n if (params?.offset) {\n searchParams.set(\"offset\", String(params.offset));\n }\n\n const query = searchParams.toString();\n const url = `/api/v1/feedback/list${query ? `?${query}` : \"\"}`;\n\n return await this.request<FeedbackListResponse>(\"GET\", url);\n }\n\n /**\n * Get a single feedback item by ID\n */\n async get(feedbackId: string): Promise<FeedbackDetail> {\n const result = await this.request<FeedbackDetail | null>(\n \"GET\",\n `/api/v1/feedback/item?id=${encodeURIComponent(feedbackId)}`\n );\n\n if (!result) {\n throw new RefletNotFoundError(\"Feedback not found\");\n }\n\n return result;\n }\n\n /**\n * Create new feedback\n * Requires user identification\n */\n async create(params: CreateFeedbackParams): Promise<CreateFeedbackResponse> {\n return await this.request<CreateFeedbackResponse>(\n \"POST\",\n \"/api/v1/feedback/create\",\n params\n );\n }\n\n /**\n * Toggle vote on feedback\n * Requires user identification\n */\n async vote(\n feedbackId: string,\n type: \"upvote\" | \"downvote\" = \"upvote\"\n ): Promise<VoteResponse> {\n return await this.request<VoteResponse>(\"POST\", \"/api/v1/feedback/vote\", {\n feedbackId,\n voteType: type,\n });\n }\n\n /**\n * Get comments for a feedback item\n */\n async getComments(\n feedbackId: string,\n sortBy: \"newest\" | \"oldest\" = \"oldest\"\n ): Promise<Comment[]> {\n return await this.request<Comment[]>(\n \"GET\",\n `/api/v1/feedback/comments?feedbackId=${encodeURIComponent(feedbackId)}&sortBy=${sortBy}`\n );\n }\n\n /**\n * Add a comment to feedback\n * Requires user identification\n */\n async comment(params: AddCommentParams): Promise<AddCommentResponse> {\n return await this.request<AddCommentResponse>(\n \"POST\",\n \"/api/v1/feedback/comment\",\n params\n );\n }\n\n /**\n * Subscribe to feedback updates\n * Requires user identification\n */\n async subscribe(feedbackId: string): Promise<SubscribeResponse> {\n return await this.request<SubscribeResponse>(\n \"POST\",\n \"/api/v1/feedback/subscribe\",\n {\n feedbackId,\n }\n );\n }\n\n /**\n * Unsubscribe from feedback updates\n * Requires user identification\n */\n async unsubscribe(feedbackId: string): Promise<UnsubscribeResponse> {\n return await this.request<UnsubscribeResponse>(\n \"POST\",\n \"/api/v1/feedback/unsubscribe\",\n {\n feedbackId,\n }\n );\n }\n\n /**\n * Get roadmap data\n */\n async getRoadmap(): Promise<Roadmap> {\n return await this.request<Roadmap>(\"GET\", \"/api/v1/feedback/roadmap\");\n }\n\n /**\n * Get changelog entries\n */\n async getChangelog(limit?: number): Promise<ChangelogEntry[]> {\n const url = limit\n ? `/api/v1/feedback/changelog?limit=${limit}`\n : \"/api/v1/feedback/changelog\";\n\n return await this.request<ChangelogEntry[]>(\"GET\", url);\n }\n\n /**\n * Build request headers with authentication\n */\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.publicKey}`,\n };\n\n const token = this.userToken ?? this.generateUserToken();\n if (token) {\n headers[\"X-User-Token\"] = token;\n }\n\n return headers;\n }\n\n /**\n * Parse JSON response text safely\n */\n private parseJsonSafely<T>(\n text: string,\n status: number\n ): T | { error: string } {\n try {\n return JSON.parse(text) as T | { error: string };\n } catch (parseError) {\n throw new RefletError(\n `Invalid response: ${parseError instanceof Error ? parseError.message : \"Failed to parse JSON\"}`,\n status\n );\n }\n }\n\n /**\n * Throw appropriate error based on status code\n */\n private throwHttpError(message: string, status: number): never {\n if (status === 401) {\n throw new RefletAuthError(message);\n }\n if (status === 404) {\n throw new RefletNotFoundError(message);\n }\n throw new RefletError(message, status);\n }\n\n /**\n * Make an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n let response: Response;\n try {\n response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers: this.buildHeaders(),\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch (networkError) {\n const message =\n networkError instanceof Error\n ? networkError.message\n : \"Failed to connect\";\n throw new RefletError(`Network error: ${message}`, 0);\n }\n\n const text = await response.text();\n\n // Handle empty response\n if (!text) {\n if (!response.ok) {\n this.throwHttpError(\n `Request failed with status ${response.status}`,\n response.status\n );\n }\n return {} as T;\n }\n\n const data = this.parseJsonSafely<T>(text, response.status);\n\n if (!response.ok) {\n const errorMessage =\n (data as { error?: string }).error ??\n `Request failed with status ${response.status}`;\n this.throwHttpError(errorMessage, response.status);\n }\n\n return data as T;\n }\n\n /**\n * Generate a simple user token for client-side use\n * Note: For production, use server-side token signing\n */\n private generateUserToken(): string | undefined {\n if (!this.user) {\n return undefined;\n }\n\n // Simple base64 encoding for client-side use\n // In production, tokens should be signed server-side\n const payload = {\n id: this.user.id,\n email: this.user.email,\n name: this.user.name,\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + 86_400, // 24 hours\n };\n\n const header = btoa(JSON.stringify({ alg: \"none\", typ: \"JWT\" }));\n const payloadB64 = btoa(JSON.stringify(payload));\n\n return `${header}.${payloadB64}.`;\n }\n}\n"]}