reflet-sdk 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +415 -127
- package/dist/client-BYuSKJw5.js +270 -0
- package/dist/client-BYuSKJw5.js.map +1 -0
- package/dist/client-Bi-GlSii.d.ts +129 -0
- package/dist/client-Bi-GlSii.d.ts.map +1 -0
- package/dist/feedback.d.ts +130 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +2276 -0
- package/dist/feedback.js.map +1 -0
- package/dist/index.d.ts +3 -302
- package/dist/index.js +2 -3
- package/dist/react-context-DZw_HHjj.js +85 -0
- package/dist/react-context-DZw_HHjj.js.map +1 -0
- package/dist/react.d.ts +208 -89
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +1248 -340
- package/dist/react.js.map +1 -1
- package/dist/server.d.ts +18 -16
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +118 -117
- package/dist/server.js.map +1 -1
- package/dist/types-xyOadyfX.d.ts +269 -0
- package/dist/types-xyOadyfX.d.ts.map +1 -0
- package/package.json +29 -9
- package/dist/chunk-U7TQSXOE.js +0 -282
- package/dist/chunk-U7TQSXOE.js.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
//#region src/types.ts
|
|
2
|
+
var RefletError = class extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
constructor(message, status, code) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "RefletError";
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var RefletAuthError = class extends RefletError {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message, 401, "AUTH_ERROR");
|
|
15
|
+
this.name = "RefletAuthError";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var RefletValidationError = class extends RefletError {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message, 400, "VALIDATION_ERROR");
|
|
21
|
+
this.name = "RefletValidationError";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var RefletNotFoundError = class extends RefletError {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message, 404, "NOT_FOUND");
|
|
27
|
+
this.name = "RefletNotFoundError";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/client.ts
|
|
32
|
+
const DEFAULT_API_URL = "https://harmless-clam-802.convex.site";
|
|
33
|
+
/** btoa is Latin1-only — a name like "José" would throw. */
|
|
34
|
+
function base64Utf8(value) {
|
|
35
|
+
let binary = "";
|
|
36
|
+
for (const byte of new TextEncoder().encode(value)) binary += String.fromCharCode(byte);
|
|
37
|
+
return btoa(binary);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Reflet SDK Client
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Reflet } from 'reflet-sdk';
|
|
45
|
+
*
|
|
46
|
+
* const reflet = new Reflet({
|
|
47
|
+
* publicKey: 'fb_pub_xxx',
|
|
48
|
+
* user: { id: 'user_123', email: 'user@example.com', name: 'John' }
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* // List feedback
|
|
52
|
+
* const { items } = await reflet.list({ status: 'open' });
|
|
53
|
+
*
|
|
54
|
+
* // Vote on feedback
|
|
55
|
+
* await reflet.vote('feedback_id');
|
|
56
|
+
*
|
|
57
|
+
* // Submit new feedback
|
|
58
|
+
* await reflet.create({ title: 'Great idea', description: 'Details...' });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
var Reflet = class {
|
|
62
|
+
publicKey;
|
|
63
|
+
baseUrl;
|
|
64
|
+
userToken;
|
|
65
|
+
user;
|
|
66
|
+
constructor(config) {
|
|
67
|
+
this.publicKey = config.publicKey;
|
|
68
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_API_URL;
|
|
69
|
+
this.userToken = config.userToken;
|
|
70
|
+
this.user = config.user;
|
|
71
|
+
if (!this.publicKey) throw new Error("Reflet: publicKey is required");
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Update the user identification
|
|
75
|
+
*/
|
|
76
|
+
setUser(user) {
|
|
77
|
+
this.user = user;
|
|
78
|
+
this.userToken = void 0;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Set the user token directly (for server-signed tokens)
|
|
82
|
+
*/
|
|
83
|
+
setUserToken(token) {
|
|
84
|
+
this.userToken = token;
|
|
85
|
+
this.user = void 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the organization configuration
|
|
89
|
+
*/
|
|
90
|
+
async getConfig() {
|
|
91
|
+
return await this.request("GET", "/api/v1/feedback");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* List feedback items with optional filtering
|
|
95
|
+
*/
|
|
96
|
+
async list(params) {
|
|
97
|
+
const searchParams = new URLSearchParams();
|
|
98
|
+
if (params?.statusId) searchParams.set("statusId", params.statusId);
|
|
99
|
+
if (params?.status) searchParams.set("status", params.status);
|
|
100
|
+
if (params?.search) searchParams.set("search", params.search);
|
|
101
|
+
if (params?.sortBy) searchParams.set("sortBy", params.sortBy);
|
|
102
|
+
if (params?.limit) searchParams.set("limit", String(params.limit));
|
|
103
|
+
if (params?.offset) searchParams.set("offset", String(params.offset));
|
|
104
|
+
const query = searchParams.toString();
|
|
105
|
+
const url = `/api/v1/feedback/list${query ? `?${query}` : ""}`;
|
|
106
|
+
return await this.request("GET", url);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get a single feedback item by ID
|
|
110
|
+
*/
|
|
111
|
+
async get(feedbackId) {
|
|
112
|
+
const result = await this.request("GET", `/api/v1/feedback/item?id=${encodeURIComponent(feedbackId)}`);
|
|
113
|
+
if (!result) throw new RefletNotFoundError("Feedback not found");
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Create new feedback
|
|
118
|
+
* Works anonymously or with user identification
|
|
119
|
+
*/
|
|
120
|
+
async create(params) {
|
|
121
|
+
return await this.request("POST", "/api/v1/feedback/create", params);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Ask the API for a one-shot storage URL to upload a screenshot to.
|
|
125
|
+
*/
|
|
126
|
+
async getScreenshotUploadUrl() {
|
|
127
|
+
return await this.request("POST", "/api/v1/feedback/screenshot/upload-url");
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Attach an already uploaded screenshot to a feedback item.
|
|
131
|
+
*/
|
|
132
|
+
async saveScreenshot(params) {
|
|
133
|
+
return await this.request("POST", "/api/v1/feedback/screenshot/save", params);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Toggle vote on feedback
|
|
137
|
+
* Requires user identification
|
|
138
|
+
*/
|
|
139
|
+
async vote(feedbackId, type = "upvote") {
|
|
140
|
+
return await this.request("POST", "/api/v1/feedback/vote", {
|
|
141
|
+
feedbackId,
|
|
142
|
+
voteType: type
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get comments for a feedback item
|
|
147
|
+
*/
|
|
148
|
+
async getComments(feedbackId, sortBy = "oldest") {
|
|
149
|
+
return await this.request("GET", `/api/v1/feedback/comments?feedbackId=${encodeURIComponent(feedbackId)}&sortBy=${sortBy}`);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Add a comment to feedback
|
|
153
|
+
* Requires user identification
|
|
154
|
+
*/
|
|
155
|
+
async comment(params) {
|
|
156
|
+
return await this.request("POST", "/api/v1/feedback/comment", params);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Subscribe to feedback updates
|
|
160
|
+
* Requires user identification
|
|
161
|
+
*/
|
|
162
|
+
async subscribe(feedbackId) {
|
|
163
|
+
return await this.request("POST", "/api/v1/feedback/subscribe", { feedbackId });
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Unsubscribe from feedback updates
|
|
167
|
+
* Requires user identification
|
|
168
|
+
*/
|
|
169
|
+
async unsubscribe(feedbackId) {
|
|
170
|
+
return await this.request("POST", "/api/v1/feedback/unsubscribe", { feedbackId });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get roadmap data
|
|
174
|
+
*/
|
|
175
|
+
async getRoadmap() {
|
|
176
|
+
return await this.request("GET", "/api/v1/feedback/roadmap");
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get changelog entries
|
|
180
|
+
*/
|
|
181
|
+
async getChangelog(limit) {
|
|
182
|
+
const url = limit ? `/api/v1/feedback/changelog?limit=${limit}` : "/api/v1/feedback/changelog";
|
|
183
|
+
return await this.request("GET", url);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Build request headers with authentication
|
|
187
|
+
*/
|
|
188
|
+
buildHeaders() {
|
|
189
|
+
const headers = {
|
|
190
|
+
Authorization: `Bearer ${this.publicKey}`,
|
|
191
|
+
"Content-Type": "application/json"
|
|
192
|
+
};
|
|
193
|
+
const token = this.userToken ?? this.generateUserToken();
|
|
194
|
+
if (token) headers["X-User-Token"] = token;
|
|
195
|
+
return headers;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Parse JSON response text safely.
|
|
199
|
+
* Returns `unknown` — callers must narrow before use.
|
|
200
|
+
*/
|
|
201
|
+
parseJsonSafely(text, status) {
|
|
202
|
+
try {
|
|
203
|
+
return JSON.parse(text);
|
|
204
|
+
} catch (parseError) {
|
|
205
|
+
throw new RefletError(`Invalid response: ${parseError instanceof Error ? parseError.message : "Failed to parse JSON"}`, status);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Type guard for API error responses
|
|
210
|
+
*/
|
|
211
|
+
isErrorResponse(data) {
|
|
212
|
+
return typeof data === "object" && data !== null && "error" in data && typeof data.error === "string";
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Throw appropriate error based on status code
|
|
216
|
+
*/
|
|
217
|
+
throwHttpError(message, status) {
|
|
218
|
+
if (status === 401) throw new RefletAuthError(message);
|
|
219
|
+
if (status === 404) throw new RefletNotFoundError(message);
|
|
220
|
+
throw new RefletError(message, status);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Make an authenticated API request
|
|
224
|
+
*/
|
|
225
|
+
async request(method, path, body) {
|
|
226
|
+
let response;
|
|
227
|
+
try {
|
|
228
|
+
response = await fetch(`${this.baseUrl}${path}`, {
|
|
229
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
230
|
+
headers: this.buildHeaders(),
|
|
231
|
+
method
|
|
232
|
+
});
|
|
233
|
+
} catch (networkError) {
|
|
234
|
+
throw new RefletError(`Network error: ${networkError instanceof Error ? networkError.message : "Failed to connect"}`, 0);
|
|
235
|
+
}
|
|
236
|
+
const text = await response.text();
|
|
237
|
+
if (!text) {
|
|
238
|
+
if (!response.ok) this.throwHttpError(`Request failed with status ${response.status}`, response.status);
|
|
239
|
+
return {};
|
|
240
|
+
}
|
|
241
|
+
const data = this.parseJsonSafely(text, response.status);
|
|
242
|
+
if (!response.ok) {
|
|
243
|
+
const errorMessage = this.isErrorResponse(data) ? data.error : `Request failed with status ${response.status}`;
|
|
244
|
+
this.throwHttpError(errorMessage, response.status);
|
|
245
|
+
}
|
|
246
|
+
return data;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Unsigned identity from the `user` option. The API attributes reports with
|
|
250
|
+
* it but refuses voting, commenting and subscribing — those need `signUser`.
|
|
251
|
+
*/
|
|
252
|
+
generateUserToken() {
|
|
253
|
+
if (!this.user) return;
|
|
254
|
+
const payload = {
|
|
255
|
+
email: this.user.email,
|
|
256
|
+
exp: Math.floor(Date.now() / 1e3) + 86400,
|
|
257
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
258
|
+
id: this.user.id,
|
|
259
|
+
name: this.user.name
|
|
260
|
+
};
|
|
261
|
+
return `${base64Utf8(JSON.stringify({
|
|
262
|
+
alg: "none",
|
|
263
|
+
typ: "JWT"
|
|
264
|
+
}))}.${base64Utf8(JSON.stringify(payload))}.`;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
//#endregion
|
|
268
|
+
export { RefletValidationError as a, RefletNotFoundError as i, RefletAuthError as n, RefletError as r, Reflet as t };
|
|
269
|
+
|
|
270
|
+
//# sourceMappingURL=client-BYuSKJw5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-BYuSKJw5.js","names":[],"sources":["../src/types.ts","../src/client.ts"],"sourcesContent":["/**\n * Reflet SDK Types\n */\n\n// ============================================\n// Configuration\n// ============================================\n\nexport interface RefletConfig {\n /** API base URL (optional, defaults to Reflet production API) */\n baseUrl?: string;\n /** Your organization's public API key (fb_pub_xxx) */\n publicKey: 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 /** URL to user's avatar image */\n avatar?: string;\n /** User's email address */\n email?: string;\n /** Unique identifier for the user in your system */\n id: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n /** User's display name */\n name?: string;\n}\n\n// ============================================\n// API Responses\n// ============================================\n\nexport interface OrganizationConfig {\n description?: string;\n id: string;\n isPublic: boolean;\n logo?: string;\n name: string;\n primaryColor?: string;\n settings?: OrganizationSettings;\n slug: string;\n statuses: OrganizationStatus[];\n}\n\nexport interface OrganizationSettings {\n allowAnonymousVoting?: boolean;\n defaultStatus?: FeedbackStatus;\n requireApproval?: boolean;\n}\n\nexport interface OrganizationStatus {\n color: string;\n icon?: string;\n id: string;\n name: 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 author: FeedbackAuthor | null;\n commentCount: number;\n completedAt?: number;\n createdAt: number;\n description: string;\n hasVoted: boolean;\n id: string;\n isPinned: boolean;\n organizationStatus: {\n id: string;\n name: string;\n color: string;\n } | null;\n status: FeedbackStatus;\n tags: FeedbackTag[];\n title: string;\n updatedAt: number;\n voteCount: number;\n}\n\nexport interface FeedbackDetail extends FeedbackItem {\n isSubscribed: boolean;\n}\n\nexport interface FeedbackTag {\n color: string;\n id: string;\n name: string;\n}\n\nexport interface FeedbackAuthor {\n avatar?: string;\n email?: string;\n isExternal: boolean;\n name?: string;\n}\n\nexport interface FeedbackListResponse {\n hasMore: boolean;\n items: FeedbackItem[];\n total: number;\n}\n\nexport interface FeedbackListParams {\n /** Number of items to return (max 100) */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Search in title and description */\n search?: string;\n /** Sort order */\n sortBy?: \"votes\" | \"newest\" | \"oldest\" | \"comments\";\n /** Filter by status name */\n status?: FeedbackStatus;\n /** Filter by custom status ID */\n statusId?: string;\n}\n\nexport interface ElementRect {\n height: number;\n width: number;\n x: number;\n y: number;\n}\n\nexport interface ElementSelection {\n /** React component names owning the element, innermost first. */\n componentStack: string[];\n /** Truncated outerHTML of the selected element. */\n html: string;\n /** Short human-readable description, e.g. `button \"Sign in\"`. */\n label: string;\n rect: ElementRect;\n selector: string;\n /** `src/components/login-form.tsx:46:19` when the React build exposes it. */\n sourceLocation?: string;\n}\n\nexport interface ConsoleEvent {\n level: \"error\" | \"warn\";\n message: string;\n timestamp: number;\n}\n\nexport interface PageContext {\n browser?: string;\n device?: string;\n language?: string;\n os?: string;\n pageTitle?: string;\n referrer?: string;\n screen?: { height: number; width: number };\n timezone?: string;\n url?: string;\n userAgent?: string;\n viewport?: { devicePixelRatio: number; height: number; width: number };\n}\n\n/** Everything the widget knows about where and how the feedback was written. */\nexport interface FeedbackContext extends PageContext {\n consoleEvents?: ConsoleEvent[];\n metadata?: Record<string, string>;\n sdkVersion?: string;\n selection?: ElementSelection;\n}\n\nexport interface CreateFeedbackParams {\n context?: FeedbackContext;\n description: string;\n title: string;\n}\n\nexport interface ScreenshotAnnotation {\n color?: string;\n endX?: number;\n endY?: number;\n height?: number;\n points?: Array<{ x: number; y: number }>;\n text?: string;\n type: \"arrow\" | \"blur\" | \"highlight\" | \"pen\" | \"rectangle\" | \"text\";\n width?: number;\n x: number;\n y: number;\n}\n\nexport interface SaveScreenshotParams {\n annotatedStorageId?: string;\n annotations?: ScreenshotAnnotation[];\n feedbackId: string;\n filename?: string;\n height?: number;\n mimeType?: string;\n pageUrl?: string;\n size?: number;\n storageId: string;\n width?: number;\n}\n\nexport interface CreateFeedbackResponse {\n feedbackId: string;\n isApproved: boolean;\n}\n\nexport interface VoteResponse {\n voteCount: number;\n voted: boolean;\n}\n\n// ============================================\n// Comments\n// ============================================\n\nexport interface Comment {\n author: FeedbackAuthor | null;\n body: string;\n createdAt: number;\n id: string;\n isOfficial: boolean;\n replies: CommentReply[];\n updatedAt: number;\n}\n\nexport interface CommentReply {\n author: FeedbackAuthor | null;\n body: string;\n createdAt: number;\n id: string;\n isOfficial: boolean;\n updatedAt: number;\n}\n\nexport interface AddCommentParams {\n body: string;\n feedbackId: string;\n parentId?: string;\n}\n\nexport interface AddCommentResponse {\n commentId: string;\n}\n\n// ============================================\n// Subscriptions\n// ============================================\n\nexport interface SubscribeResponse {\n alreadySubscribed?: boolean;\n subscribed: 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 color: string;\n id: string;\n items: RoadmapItem[];\n name: string;\n}\n\nexport interface RoadmapItem {\n id: string;\n status: FeedbackStatus;\n title: string;\n voteCount: number;\n}\n\n// ============================================\n// Changelog\n// ============================================\n\nexport interface ChangelogEntry {\n description?: string;\n feedback: { id: string; title: string }[];\n id: string;\n publishedAt?: number;\n title: string;\n version?: 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 SaveScreenshotParams,\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/** btoa is Latin1-only — a name like \"José\" would throw. */\nfunction base64Utf8(value: string): string {\n let binary = \"\";\n for (const byte of new TextEncoder().encode(value)) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary);\n}\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 * Works anonymously or with 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 * Ask the API for a one-shot storage URL to upload a screenshot to.\n */\n async getScreenshotUploadUrl(): Promise<{ uploadUrl: string }> {\n return await this.request<{ uploadUrl: string }>(\n \"POST\",\n \"/api/v1/feedback/screenshot/upload-url\"\n );\n }\n\n /**\n * Attach an already uploaded screenshot to a feedback item.\n */\n async saveScreenshot(\n params: SaveScreenshotParams\n ): Promise<{ screenshotId: string }> {\n return await this.request<{ screenshotId: string }>(\n \"POST\",\n \"/api/v1/feedback/screenshot/save\",\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 Authorization: `Bearer ${this.publicKey}`,\n \"Content-Type\": \"application/json\",\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 * Returns `unknown` — callers must narrow before use.\n */\n private parseJsonSafely(text: string, status: number): unknown {\n try {\n return JSON.parse(text);\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 * Type guard for API error responses\n */\n private isErrorResponse(data: unknown): data is { error: string } {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"error\" in data &&\n typeof (data as Record<string, unknown>).error === \"string\"\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 body: body ? JSON.stringify(body) : undefined,\n headers: this.buildHeaders(),\n method,\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 // Successful empty body (e.g. 204 No Content) — return empty object\n // narrowed through unknown for type-safe assertion\n const emptyResponse: unknown = {};\n return emptyResponse as T;\n }\n\n const data = this.parseJsonSafely(text, response.status);\n\n if (!response.ok) {\n const errorMessage = this.isErrorResponse(data)\n ? data.error\n : `Request failed with status ${response.status}`;\n this.throwHttpError(errorMessage, response.status);\n }\n\n // Standard unknown → T assertion for runtime-parsed JSON\n return data as T;\n }\n\n /**\n * Unsigned identity from the `user` option. The API attributes reports with\n * it but refuses voting, commenting and subscribing — those need `signUser`.\n */\n private generateUserToken(): string | undefined {\n if (!this.user) {\n return;\n }\n\n const payload = {\n email: this.user.email,\n exp: Math.floor(Date.now() / 1000) + 86_400, // 24 hours\n iat: Math.floor(Date.now() / 1000),\n id: this.user.id,\n name: this.user.name,\n };\n\n const header = base64Utf8(JSON.stringify({ alg: \"none\", typ: \"JWT\" }));\n const payloadB64 = base64Utf8(JSON.stringify(payload));\n\n // Empty signature — the server treats this identity as unverified\n return `${header}.${payloadB64}.`;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { A as SubscribeResponse, E as Roadmap, M as VoteResponse, _ as OrganizationConfig, d as FeedbackDetail, i as Comment, j as UnsubscribeResponse, k as SaveScreenshotParams, m as FeedbackListResponse, n as AddCommentResponse, o as CreateFeedbackParams, p as FeedbackListParams, r as ChangelogEntry, s as CreateFeedbackResponse, t as AddCommentParams, w as RefletUser, x as RefletConfig } from "./types-xyOadyfX.js";
|
|
2
|
+
//#region src/client.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reflet SDK Client
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Reflet } from 'reflet-sdk';
|
|
9
|
+
*
|
|
10
|
+
* const reflet = new Reflet({
|
|
11
|
+
* publicKey: 'fb_pub_xxx',
|
|
12
|
+
* user: { id: 'user_123', email: 'user@example.com', name: 'John' }
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* // List feedback
|
|
16
|
+
* const { items } = await reflet.list({ status: 'open' });
|
|
17
|
+
*
|
|
18
|
+
* // Vote on feedback
|
|
19
|
+
* await reflet.vote('feedback_id');
|
|
20
|
+
*
|
|
21
|
+
* // Submit new feedback
|
|
22
|
+
* await reflet.create({ title: 'Great idea', description: 'Details...' });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare class Reflet {
|
|
26
|
+
private readonly publicKey;
|
|
27
|
+
private readonly baseUrl;
|
|
28
|
+
private userToken;
|
|
29
|
+
private user;
|
|
30
|
+
constructor(config: RefletConfig);
|
|
31
|
+
/**
|
|
32
|
+
* Update the user identification
|
|
33
|
+
*/
|
|
34
|
+
setUser(user: RefletUser | undefined): void;
|
|
35
|
+
/**
|
|
36
|
+
* Set the user token directly (for server-signed tokens)
|
|
37
|
+
*/
|
|
38
|
+
setUserToken(token: string | undefined): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get the organization configuration
|
|
41
|
+
*/
|
|
42
|
+
getConfig(): Promise<OrganizationConfig>;
|
|
43
|
+
/**
|
|
44
|
+
* List feedback items with optional filtering
|
|
45
|
+
*/
|
|
46
|
+
list(params?: FeedbackListParams): Promise<FeedbackListResponse>;
|
|
47
|
+
/**
|
|
48
|
+
* Get a single feedback item by ID
|
|
49
|
+
*/
|
|
50
|
+
get(feedbackId: string): Promise<FeedbackDetail>;
|
|
51
|
+
/**
|
|
52
|
+
* Create new feedback
|
|
53
|
+
* Works anonymously or with user identification
|
|
54
|
+
*/
|
|
55
|
+
create(params: CreateFeedbackParams): Promise<CreateFeedbackResponse>;
|
|
56
|
+
/**
|
|
57
|
+
* Ask the API for a one-shot storage URL to upload a screenshot to.
|
|
58
|
+
*/
|
|
59
|
+
getScreenshotUploadUrl(): Promise<{
|
|
60
|
+
uploadUrl: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Attach an already uploaded screenshot to a feedback item.
|
|
64
|
+
*/
|
|
65
|
+
saveScreenshot(params: SaveScreenshotParams): Promise<{
|
|
66
|
+
screenshotId: string;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Toggle vote on feedback
|
|
70
|
+
* Requires user identification
|
|
71
|
+
*/
|
|
72
|
+
vote(feedbackId: string, type?: "upvote" | "downvote"): Promise<VoteResponse>;
|
|
73
|
+
/**
|
|
74
|
+
* Get comments for a feedback item
|
|
75
|
+
*/
|
|
76
|
+
getComments(feedbackId: string, sortBy?: "newest" | "oldest"): Promise<Comment[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Add a comment to feedback
|
|
79
|
+
* Requires user identification
|
|
80
|
+
*/
|
|
81
|
+
comment(params: AddCommentParams): Promise<AddCommentResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* Subscribe to feedback updates
|
|
84
|
+
* Requires user identification
|
|
85
|
+
*/
|
|
86
|
+
subscribe(feedbackId: string): Promise<SubscribeResponse>;
|
|
87
|
+
/**
|
|
88
|
+
* Unsubscribe from feedback updates
|
|
89
|
+
* Requires user identification
|
|
90
|
+
*/
|
|
91
|
+
unsubscribe(feedbackId: string): Promise<UnsubscribeResponse>;
|
|
92
|
+
/**
|
|
93
|
+
* Get roadmap data
|
|
94
|
+
*/
|
|
95
|
+
getRoadmap(): Promise<Roadmap>;
|
|
96
|
+
/**
|
|
97
|
+
* Get changelog entries
|
|
98
|
+
*/
|
|
99
|
+
getChangelog(limit?: number): Promise<ChangelogEntry[]>;
|
|
100
|
+
/**
|
|
101
|
+
* Build request headers with authentication
|
|
102
|
+
*/
|
|
103
|
+
private buildHeaders;
|
|
104
|
+
/**
|
|
105
|
+
* Parse JSON response text safely.
|
|
106
|
+
* Returns `unknown` — callers must narrow before use.
|
|
107
|
+
*/
|
|
108
|
+
private parseJsonSafely;
|
|
109
|
+
/**
|
|
110
|
+
* Type guard for API error responses
|
|
111
|
+
*/
|
|
112
|
+
private isErrorResponse;
|
|
113
|
+
/**
|
|
114
|
+
* Throw appropriate error based on status code
|
|
115
|
+
*/
|
|
116
|
+
private throwHttpError;
|
|
117
|
+
/**
|
|
118
|
+
* Make an authenticated API request
|
|
119
|
+
*/
|
|
120
|
+
private request;
|
|
121
|
+
/**
|
|
122
|
+
* Unsigned identity from the `user` option. The API attributes reports with
|
|
123
|
+
* it but refuses voting, commenting and subscribing — those need `signUser`.
|
|
124
|
+
*/
|
|
125
|
+
private generateUserToken;
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { Reflet as t };
|
|
129
|
+
//# sourceMappingURL=client-Bi-GlSii.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-Bi-GlSii.d.ts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cAyDa;mBACM;mBACA;UACT;UACA;EAER,YAAY,QAAQ;;;;EAcpB,QAAQ,MAAM;;;;EAQd,aAAa;;;;EAQP,aAAa,QAAQ;;;;EAOrB,KAAK,SAAS,qBAAqB,QAAQ;;;;EA+B3C,IAAI,qBAAqB,QAAQ;;;;;EAiBjC,OAAO,QAAQ,uBAAuB,QAAQ;;;;EAW9C,0BAA0B;IAAU;;;;;EAUpC,eACJ,QAAQ,uBACP;IAAU;;;;;;EAYP,KACJ,oBACA,+BACC,QAAQ;;;;EAUL,YACJ,oBACA,+BACC,QAAQ;;;;;EAWL,QAAQ,QAAQ,mBAAmB,QAAQ;;;;;EAY3C,UAAU,qBAAqB,QAAQ;;;;;EAcvC,YAAY,qBAAqB,QAAQ;;;;EAazC,cAAc,QAAQ;;;;EAOtB,aAAa,iBAAiB,QAAQ;;;;UAWpC;;;;;UAkBA;;;;UAcA;;;;UAYA;;;;UAaM;;;;;UAqDN"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { c as ElementSelection, u as FeedbackContext, w as RefletUser } from "./types-xyOadyfX.js";
|
|
2
|
+
//#region src/feedback/types.d.ts
|
|
3
|
+
declare const FEEDBACK_WIDGET_CATEGORIES: readonly ["bug", "idea", "question"];
|
|
4
|
+
type FeedbackWidgetCategory = (typeof FEEDBACK_WIDGET_CATEGORIES)[number];
|
|
5
|
+
declare const ANNOTATION_TOOLS: readonly ["pen", "arrow", "rectangle", "highlight", "blur"];
|
|
6
|
+
type AnnotationTool = (typeof ANNOTATION_TOOLS)[number];
|
|
7
|
+
interface Point {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}
|
|
11
|
+
interface Annotation {
|
|
12
|
+
color: string;
|
|
13
|
+
end: Point;
|
|
14
|
+
id: string;
|
|
15
|
+
/** Freehand path, in image pixels. Only set for the pen tool. */
|
|
16
|
+
points?: Point[];
|
|
17
|
+
start: Point;
|
|
18
|
+
tool: AnnotationTool;
|
|
19
|
+
}
|
|
20
|
+
interface CapturedImage {
|
|
21
|
+
blob: Blob;
|
|
22
|
+
height: number;
|
|
23
|
+
mimeType: string;
|
|
24
|
+
/** Object URL owned by the caller — revoke it when the capture is dropped. */
|
|
25
|
+
objectUrl: string;
|
|
26
|
+
width: number;
|
|
27
|
+
}
|
|
28
|
+
interface FeedbackWidgetLabels {
|
|
29
|
+
annotateHint: string;
|
|
30
|
+
attachScreenshot: string;
|
|
31
|
+
back: string;
|
|
32
|
+
cancel: string;
|
|
33
|
+
categoryBug: string;
|
|
34
|
+
categoryIdea: string;
|
|
35
|
+
categoryQuestion: string;
|
|
36
|
+
clearAnnotations: string;
|
|
37
|
+
descriptionPlaceholder: string;
|
|
38
|
+
dismissForDays: string;
|
|
39
|
+
done: string;
|
|
40
|
+
emailLabel: string;
|
|
41
|
+
emailPlaceholder: string;
|
|
42
|
+
errorGeneric: string;
|
|
43
|
+
errorTitleRequired: string;
|
|
44
|
+
pickElement: string;
|
|
45
|
+
pickElementHint: string;
|
|
46
|
+
recapture: string;
|
|
47
|
+
removeScreenshot: string;
|
|
48
|
+
retakeHint: string;
|
|
49
|
+
submit: string;
|
|
50
|
+
successMessage: string;
|
|
51
|
+
successTitle: string;
|
|
52
|
+
title: string;
|
|
53
|
+
titlePlaceholder: string;
|
|
54
|
+
trigger: string;
|
|
55
|
+
undo: string;
|
|
56
|
+
}
|
|
57
|
+
interface RefletFeedbackProps {
|
|
58
|
+
/** API base URL override. Falls back to the RefletProvider value. */
|
|
59
|
+
baseUrl?: string;
|
|
60
|
+
/** Record console errors and warnings to attach to bug reports. */
|
|
61
|
+
captureConsole?: boolean;
|
|
62
|
+
/** Take a screenshot of the viewport as soon as the panel opens. */
|
|
63
|
+
captureOnOpen?: boolean;
|
|
64
|
+
categories?: FeedbackWidgetCategory[];
|
|
65
|
+
defaultCategory?: FeedbackWidgetCategory;
|
|
66
|
+
/** Let reporters hide the launcher for this many days. */
|
|
67
|
+
dismissForDays?: number;
|
|
68
|
+
/** Render nothing when false — lets an app gate the widget per user. */
|
|
69
|
+
enabled?: boolean;
|
|
70
|
+
/** Keyboard shortcut opening the panel. Set to null to disable. */
|
|
71
|
+
hotkey?: string | null;
|
|
72
|
+
labels?: Partial<FeedbackWidgetLabels>;
|
|
73
|
+
/** Extra fields merged into the reported context. */
|
|
74
|
+
metadata?: Record<string, string>;
|
|
75
|
+
/** Distance in px from the viewport edges. */
|
|
76
|
+
offset?: number;
|
|
77
|
+
onClose?: () => void;
|
|
78
|
+
onDismiss?: (result: {
|
|
79
|
+
until: number;
|
|
80
|
+
}) => void;
|
|
81
|
+
onOpen?: () => void;
|
|
82
|
+
onSubmit?: (result: {
|
|
83
|
+
feedbackId: string;
|
|
84
|
+
}) => void;
|
|
85
|
+
position?: "bottom-left" | "bottom-right" | "top-left" | "top-right";
|
|
86
|
+
primaryColor?: string;
|
|
87
|
+
publicKey?: string;
|
|
88
|
+
theme?: "auto" | "dark" | "light";
|
|
89
|
+
user?: RefletUser;
|
|
90
|
+
userToken?: string;
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/feedback/core/capture.d.ts
|
|
94
|
+
interface CaptureOptions {
|
|
95
|
+
/** Capture this element instead of the visible viewport. */
|
|
96
|
+
element?: Element;
|
|
97
|
+
excludeSelectors?: string[];
|
|
98
|
+
maxWidth?: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Renders what the user is looking at right now.
|
|
102
|
+
*
|
|
103
|
+
* Runs off the DOM rather than `getDisplayMedia` on purpose: a screen-share
|
|
104
|
+
* permission prompt on every report would cost more reports than it captures.
|
|
105
|
+
* Returns null instead of throwing — a screenshot is never worth losing the
|
|
106
|
+
* written feedback.
|
|
107
|
+
*/
|
|
108
|
+
declare function captureViewport(options?: CaptureOptions): Promise<CapturedImage | null>;
|
|
109
|
+
declare function releaseCapture(image: CapturedImage | null | undefined): void;
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/feedback/core/element-selector.d.ts
|
|
112
|
+
/** Everything an agent needs to find the picked element back in the codebase. */
|
|
113
|
+
declare function buildElementSelection(element: Element): ElementSelection;
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/feedback/core/page-context.d.ts
|
|
116
|
+
/** Everything about the page that a bug report needs and the DOM already knows. */
|
|
117
|
+
declare function collectPageContext(options?: {
|
|
118
|
+
sdkVersion?: string;
|
|
119
|
+
}): FeedbackContext;
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/feedback/widget.d.ts
|
|
122
|
+
/**
|
|
123
|
+
* Floating feedback button with a screenshot, annotation and element picker
|
|
124
|
+
* flow. Mount it once, anywhere below `RefletProvider` or with its own
|
|
125
|
+
* `publicKey`.
|
|
126
|
+
*/
|
|
127
|
+
declare function RefletFeedback(props: RefletFeedbackProps): import("react").JSX.Element | null;
|
|
128
|
+
//#endregion
|
|
129
|
+
export { type Annotation, type AnnotationTool, type CapturedImage, type FeedbackWidgetCategory, type FeedbackWidgetLabels, RefletFeedback, type RefletFeedbackProps, buildElementSelection, captureViewport, collectPageContext, releaseCapture };
|
|
130
|
+
//# sourceMappingURL=feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.d.ts","names":[],"sources":["../src/feedback/types.ts","../src/feedback/core/capture.ts","../src/feedback/core/element-selector.ts","../src/feedback/core/page-context.ts","../src/feedback/widget.tsx"],"mappings":";;cAKM;KAEM,iCACF;cAEJ;KAQM,yBAAyB;UAEpB;EACf;EACA;;UAGe;EACf;EACA,KAAK;EACL;;EAEA,SAAS;EACT,OAAO;EACP,MAAM;;UAGS;EACf,MAAM;EACN;EACA;;EAEA;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;UAiCe;;EAEf;;EAEA;;EAEA;EACA,aAAa;EACb,kBAAkB;;EAElB;;EAEA;;EAEA;EACA,SAAS,QAAQ;;EAEjB,WAAW;;EAEX;EACA;EACA,aAAa;IAAU;;EACvB;EACA,YAAY;IAAU;;EACtB;EACA;EACA;EACA;EACA,OAAO;EACP;;;;UC7He;;EAEf,UAAU;EACV;EACA;;;;;;;;;;iBA6EoB,gBACpB,UAAS,iBACR,QAAQ;iBAqCK,eAAe,OAAO;;;;iBCetB,sBAAsB,SAAS,UAAU;;;;iBCnDzC,mBAAmB;EACjC;IACE;;;;;;;;iBCrEY,eAAe,OAAO,sCAAmB,IAAA"}
|