reflet-sdk 0.1.4 → 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 +351 -127
- package/dist/{chunk-U7TQSXOE.js → chunk-GD7FQQBT.js} +3 -3
- package/dist/chunk-GD7FQQBT.js.map +1 -0
- package/dist/index.d.ts +12 -17
- package/dist/index.js +1 -1
- package/dist/react.d.ts +6 -6
- package/dist/react.js +5 -5
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-U7TQSXOE.js.map +0 -1
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 →
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
<
|
|
86
|
+
<YourApp />
|
|
77
87
|
</RefletProvider>
|
|
78
88
|
);
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
// Use hooks in your
|
|
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 (
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
130
|
-
|
|
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
|
|
152
|
-
| `user` | `RefletUser` | No | User information for identification |
|
|
153
|
-
| `userToken` | `string` | No | Signed JWT from your server (
|
|
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
|
-
| `
|
|
163
|
-
| `
|
|
164
|
-
| `
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
|
176
|
-
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
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
|
|
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(
|
|
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
|
|
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('
|
|
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
|
-
|
|
393
|
+
// User not identified or invalid token
|
|
394
|
+
console.log('Please sign in to vote');
|
|
251
395
|
} else if (error instanceof RefletError) {
|
|
252
|
-
|
|
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
|
-
|
|
402
|
+
### Common Error Scenarios
|
|
258
403
|
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
import { RefletProvider, useFeedbackList, useCreateFeedback, useVote } from 'reflet-sdk/react';
|
|
263
|
-
import { useState } from 'react';
|
|
412
|
+
---
|
|
264
413
|
|
|
265
|
-
|
|
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
|
-
|
|
273
|
-
e.preventDefault();
|
|
274
|
-
await createFeedback({ title, description: '' });
|
|
275
|
-
setTitle('');
|
|
276
|
-
refetch();
|
|
277
|
-
};
|
|
416
|
+
### Generic Pattern (Any Framework)
|
|
278
417
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
###
|
|
465
|
+
### React with Any State Management
|
|
316
466
|
|
|
317
467
|
```tsx
|
|
318
|
-
//
|
|
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
|
-
|
|
325
|
-
|
|
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=
|
|
330
|
-
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
|
-
|
|
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
|
|
@@ -59,7 +59,7 @@ var Reflet = class {
|
|
|
59
59
|
this.user = void 0;
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
* Get the
|
|
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-
|
|
282
|
-
//# sourceMappingURL=chunk-
|
|
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
|
|
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
|
|
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?:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
37
|
+
interface OrganizationSettings {
|
|
43
38
|
allowAnonymousVoting?: boolean;
|
|
44
39
|
requireApproval?: boolean;
|
|
45
40
|
defaultStatus?: FeedbackStatus;
|
|
46
41
|
}
|
|
47
|
-
interface
|
|
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
|
-
|
|
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
|
|
226
|
+
* Get the organization configuration
|
|
232
227
|
*/
|
|
233
|
-
getConfig(): Promise<
|
|
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
|
|
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-
|
|
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,
|
|
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
|
|
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
|
|
61
|
+
* Hook to fetch organization configuration
|
|
62
62
|
*/
|
|
63
|
-
declare function
|
|
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,
|
|
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-
|
|
2
|
-
export { Reflet } from './chunk-
|
|
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
|
|
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
|
|
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,
|
|
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":";;;;;AA0BA,IAAM,aAAA,GAAgB,cAAyC,IAAI,CAAA;AAsC5D,SAAS,cAAA,CAAe;AAAA,EAC7B,SAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA,EAAM,WAAA;AAAA,EACN,SAAA;AAAA,EACA;AACF,CAAA,EAAwB;AACtB,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAiC,WAAW,CAAA;AACpE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAE5C,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAM;AAC3B,IAAA,MAAM,MAAA,GAAuB;AAAA,MAC3B,SAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,IAAI,OAAO,MAAM,CAAA;AAAA,EAC1B,GAAG,CAAC,SAAA,EAAW,OAAA,EAAS,IAAA,EAAM,SAAS,CAAC,CAAA;AAGxC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAA,CAAO,aAAa,SAAS,CAAA;AAAA,IAC/B,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,QAAQ,IAAI,CAAA;AAAA,IACrB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,IAAA,EAAM,SAAS,CAAC,CAAA;AAG5B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,UAAA,CAAW,IAAI,CAAA;AAAA,EACjB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,OAAO;AAAA,MACL,MAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,OAAA,EAAS,IAAI;AAAA,GACxB;AAEA,EAAA,uBACE,GAAA,CAAC,aAAA,CAAc,QAAA,EAAd,EAAuB,OAAe,QAAA,EAAS,CAAA;AAEpD;AAMO,SAAS,gBAAA,GAAuC;AACrD,EAAA,MAAM,OAAA,GAAU,WAAW,aAAa,CAAA;AACxC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,EACzE;AACA,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,eAAA,GAA0B;AACxC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,gBAAA,EAAiB;AACpC,EAAA,OAAO,MAAA;AACT;ACxFO,SAAS,cAAA,GAA8C;AAC5D,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,QAAAA,EAAkC;AAC1D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,SAAS,IAAI,CAAA;AAC/C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,KAAA,GAAQ,YAAY,YAAY;AACpC,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,SAAA,EAAU;AACtC,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA;AAAA,QACE,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,8BAA8B;AAAA,OACvE;AAAA,IACF,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAAC,UAAU,MAAM;AACd,IAAA,KAAA,EAAM;AAAA,EACR,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAS,KAAA,EAAM;AAClD;AAmBO,SAAS,eAAA,CACd,OAAA,GAAkC,EAAC,EACyC;AAC5E,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,EAAE,OAAA,GAAU,IAAA,EAAM,GAAG,QAAO,GAAI,OAAA;AAGtC,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,QAAQ,MAAA,EAAQ,KAAA,EAAO,QAAO,GAAI,MAAA;AAE5D,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAID,QAAAA,EAIrB;AACH,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,SAAS,OAAO,CAAA;AAClD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,KAAA,GAAQ,YAAY,YAAY;AACpC,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,IAAA,CAAK;AAAA,QAC/B,QAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,KAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA;AAAA,QACE,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,0BAA0B;AAAA,OACnE;AAAA,IACF,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,QAAA,EAAU,QAAQ,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,MAAM,CAAC,CAAA;AAE5D,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,KAAA,EAAM;AAAA,IACR;AAAA,EACF,CAAA,EAAG,CAAC,KAAA,EAAO,OAAO,CAAC,CAAA;AAEnB,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAS,KAAA,EAAM;AAClD;AAcO,SAAS,YACd,UAAA,EACgC;AAChC,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAID,QAAAA,EAAqC;AAC7D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,IAAIA,QAAAA,CAAS,CAAC,CAAC,UAAU,CAAA;AACvD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,KAAA,GAAQ,YAAY,YAAY;AACpC,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,OAAA,CAAQ,MAAS,CAAA;AACjB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,GAAA,CAAI,UAAU,CAAA;AAC1C,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA;AAAA,QACE,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,0BAA0B;AAAA,OACnE;AAAA,IACF,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,UAAU,CAAC,CAAA;AAEvB,EAAAC,UAAU,MAAM;AACd,IAAA,KAAA,EAAM;AAAA,EACR,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAS,KAAA,EAAM;AAClD;AASO,SAAS,WAAA,CACd,UAAA,EACA,MAAA,GAA8B,QAAA,EACH;AAC3B,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAID,QAAAA,EAAgC;AACxD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,IAAIA,QAAAA,CAAS,CAAC,CAAC,UAAU,CAAA;AACvD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,KAAA,GAAQ,YAAY,YAAY;AACpC,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,OAAA,CAAQ,MAAS,CAAA;AACjB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,WAAA,CAAY,YAAY,MAAM,CAAA;AAC1D,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA;AAAA,QACE,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,0BAA0B;AAAA,OACnE;AAAA,IACF,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,UAAA,EAAY,MAAM,CAAC,CAAA;AAE/B,EAAAC,UAAU,MAAM;AACd,IAAA,KAAA,EAAM;AAAA,EACR,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAS,KAAA,EAAM;AAClD;AASO,SAAS,UAAA,GAAsC;AACpD,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAID,QAAAA,EAA8B;AACtD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,SAAS,IAAI,CAAA;AAC/C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,KAAA,GAAQ,YAAY,YAAY;AACpC,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,UAAA,EAAW;AACvC,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA;AAAA,QACE,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,yBAAyB;AAAA,OAClE;AAAA,IACF,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAAC,UAAU,MAAM;AACd,IAAA,KAAA,EAAM;AAAA,EACR,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAS,KAAA,EAAM;AAClD;AASO,SAAS,aAAa,KAAA,EAAkD;AAC7E,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAID,QAAAA,EAAuC;AAC/D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,SAAS,IAAI,CAAA;AAC/C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,KAAA,GAAQ,YAAY,YAAY;AACpC,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,YAAA,CAAa,KAAK,CAAA;AAC9C,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA;AAAA,QACE,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,2BAA2B;AAAA,OACpE;AAAA,IACF,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,KAAK,CAAC,CAAA;AAElB,EAAAC,UAAU,MAAM;AACd,IAAA,KAAA,EAAM;AAAA,EACR,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAS,KAAA,EAAM;AAClD;AAmBO,SAAS,iBAAA,GAGd;AACA,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAID,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,QAAAA,EAGrB;AAEH,EAAA,MAAM,MAAA,GAAS,WAAA;AAAA,IACb,OAAO,MAAA,KAAiC;AACtC,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA;AACzC,QAAA,OAAA,CAAQ,MAAM,CAAA;AACd,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,MAAME,SACJ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,2BAA2B,CAAA;AACpE,QAAA,QAAA,CAASA,MAAK,CAAA;AACd,QAAA,MAAMA,MAAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,OAAA,CAAQ,MAAS,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAO,MAAM,KAAA,EAAM;AACjD;AAkBO,SAAS,OAAA,GAGd;AACA,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIF,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,QAAAA,EAAgD;AAExE,EAAA,MAAM,MAAA,GAAS,WAAA;AAAA,IACb,OAAO;AAAA,MACL,UAAA;AAAA,MACA,IAAA,GAAO;AAAA,KACT,KAGM;AACJ,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,IAAA,CAAK,YAAY,IAAI,CAAA;AACjD,QAAA,OAAA,CAAQ,MAAM,CAAA;AACd,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,MAAME,SAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,gBAAgB,CAAA;AACrE,QAAA,QAAA,CAASA,MAAK,CAAA;AACd,QAAA,MAAMA,MAAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,OAAA,CAAQ,MAAS,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAO,MAAM,KAAA,EAAM;AACjD;AASO,SAAS,aAAA,GAGd;AACA,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIF,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,QAAAA,EAAgC;AAExD,EAAA,MAAM,MAAA,GAAS,WAAA;AAAA,IACb,OAAO,MAAA,KAA6B;AAClC,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AAC1C,QAAA,OAAA,CAAQ,MAAM,CAAA;AACd,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,MAAME,SACJ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,uBAAuB,CAAA;AAChE,QAAA,QAAA,CAASA,MAAK,CAAA;AACd,QAAA,MAAMA,MAAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,OAAA,CAAQ,MAAS,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAO,MAAM,KAAA,EAAM;AACjD;AASO,SAAS,eAAA,GAKd;AACA,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIF,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,OAAO,UAAA,KAAuB;AAC5B,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,SAAA,CAAU,UAAU,CAAA;AAChD,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,MAAME,SACJ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,qBAAqB,CAAA;AAC9D,QAAA,QAAA,CAASA,MAAK,CAAA;AACd,QAAA,MAAMA,MAAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,OAAO,UAAA,KAAuB;AAC5B,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,WAAA,CAAY,UAAU,CAAA;AAClD,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,MAAMA,SACJ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,uBAAuB,CAAA;AAChE,QAAA,QAAA,CAASA,MAAK,CAAA;AACd,QAAA,MAAMA,MAAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,OAAO,EAAE,SAAA,EAAW,WAAA,EAAa,SAAA,EAAW,KAAA,EAAM;AACpD","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 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/client.ts"],"names":[],"mappings":"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","file":"chunk-U7TQSXOE.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 (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 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"]}
|