@checkflow/sdk 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +63 -219
  2. package/dist/chunk-CD33QAA6.mjs +131 -0
  3. package/dist/chunk-CQ56DMFR.mjs +83 -0
  4. package/dist/highlighter-D_wZWHlS.d.mts +71 -0
  5. package/dist/highlighter-D_wZWHlS.d.ts +71 -0
  6. package/dist/highlighter-W4XDALRE.mjs +8 -0
  7. package/dist/index.d.mts +41 -0
  8. package/dist/index.d.ts +38 -20
  9. package/dist/index.js +607 -17221
  10. package/dist/index.mjs +411 -0
  11. package/dist/react.d.mts +28 -0
  12. package/dist/react.d.ts +28 -0
  13. package/dist/react.js +743 -0
  14. package/dist/react.mjs +51 -0
  15. package/dist/screenshot-CUMBPE2T.mjs +12 -0
  16. package/dist/vue.d.mts +26 -0
  17. package/dist/vue.d.ts +26 -0
  18. package/dist/vue.js +744 -0
  19. package/dist/vue.mjs +53 -0
  20. package/package.json +38 -51
  21. package/dist/analytics-tracker.d.ts +0 -112
  22. package/dist/annotation/editor.d.ts +0 -72
  23. package/dist/annotation/index.d.ts +0 -9
  24. package/dist/annotation/styles.d.ts +0 -6
  25. package/dist/annotation/toolbar.d.ts +0 -32
  26. package/dist/annotation/types.d.ts +0 -85
  27. package/dist/api-client.d.ts +0 -76
  28. package/dist/checkflow.css +0 -1
  29. package/dist/checkflow.d.ts +0 -112
  30. package/dist/context-capture.d.ts +0 -42
  31. package/dist/error-capture.d.ts +0 -60
  32. package/dist/index.esm.js +0 -17210
  33. package/dist/index.esm.js.map +0 -1
  34. package/dist/index.js.map +0 -1
  35. package/dist/privacy/detector.d.ts +0 -56
  36. package/dist/privacy/index.d.ts +0 -8
  37. package/dist/privacy/masker.d.ts +0 -43
  38. package/dist/privacy/types.d.ts +0 -54
  39. package/dist/react/index.d.ts +0 -77
  40. package/dist/session-recording-rrweb.d.ts +0 -100
  41. package/dist/session-recording.d.ts +0 -74
  42. package/dist/types.d.ts +0 -299
  43. package/dist/vue/index.d.ts +0 -55
  44. package/dist/widget/Widget.d.ts +0 -98
  45. package/dist/widget/index.d.ts +0 -2
package/dist/index.mjs ADDED
@@ -0,0 +1,411 @@
1
+ import {
2
+ captureScreenshot,
3
+ clearScreenshot,
4
+ loadHtml2Canvas
5
+ } from "./chunk-CQ56DMFR.mjs";
6
+ import {
7
+ startHighlighting
8
+ } from "./chunk-CD33QAA6.mjs";
9
+
10
+ // src/collector.ts
11
+ function collectContext() {
12
+ const ua = navigator.userAgent;
13
+ return {
14
+ url: window.location.href,
15
+ viewport: {
16
+ width: window.innerWidth,
17
+ height: window.innerHeight,
18
+ device: window.innerWidth < 768 ? "mobile" : window.innerWidth < 1024 ? "tablet" : "desktop"
19
+ },
20
+ user_agent: ua,
21
+ browser: detectBrowser(ua),
22
+ os: detectOS(ua),
23
+ locale: navigator.language,
24
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
25
+ };
26
+ }
27
+ function collectPerformance() {
28
+ try {
29
+ const entries = performance.getEntriesByType("paint");
30
+ const metrics = {};
31
+ entries.forEach((e) => {
32
+ if (e.name === "first-contentful-paint") metrics.fcp = Math.round(e.startTime);
33
+ });
34
+ const nav = performance.getEntriesByType("navigation")[0];
35
+ if (nav) {
36
+ metrics.dom_load = Math.round(nav.domContentLoadedEventEnd - nav.startTime);
37
+ metrics.load = Math.round(nav.loadEventEnd - nav.startTime);
38
+ }
39
+ return Object.keys(metrics).length > 0 ? metrics : void 0;
40
+ } catch {
41
+ return void 0;
42
+ }
43
+ }
44
+ function detectBrowser(ua) {
45
+ if (ua.includes("Firefox/")) return "Firefox";
46
+ if (ua.includes("Edg/")) return "Edge";
47
+ if (ua.includes("Chrome/")) return "Chrome";
48
+ if (ua.includes("Safari/")) return "Safari";
49
+ return "Unknown";
50
+ }
51
+ function detectOS(ua) {
52
+ if (ua.includes("Windows")) return "Windows";
53
+ if (ua.includes("Mac OS")) return "macOS";
54
+ if (ua.includes("Linux")) return "Linux";
55
+ if (ua.includes("Android")) return "Android";
56
+ if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
57
+ return "Unknown";
58
+ }
59
+
60
+ // src/console-interceptor.ts
61
+ var MAX_LOGS = 50;
62
+ var logs = [];
63
+ var errorsCapture = [];
64
+ var installed = false;
65
+ function installInterceptors() {
66
+ if (installed) return;
67
+ installed = true;
68
+ const origConsole = {
69
+ log: console.log,
70
+ warn: console.warn,
71
+ error: console.error
72
+ };
73
+ ["log", "warn", "error"].forEach((level) => {
74
+ console[level] = (...args) => {
75
+ logs.push({
76
+ level,
77
+ message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" "),
78
+ timestamp: Date.now()
79
+ });
80
+ if (logs.length > MAX_LOGS) logs.shift();
81
+ origConsole[level](...args);
82
+ };
83
+ });
84
+ window.addEventListener("error", (event) => {
85
+ errorsCapture.push({
86
+ message: event.message,
87
+ filename: event.filename,
88
+ lineno: event.lineno,
89
+ colno: event.colno,
90
+ timestamp: Date.now()
91
+ });
92
+ if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
93
+ });
94
+ window.addEventListener("unhandledrejection", (event) => {
95
+ errorsCapture.push({
96
+ message: String(event.reason),
97
+ type: "unhandledrejection",
98
+ timestamp: Date.now()
99
+ });
100
+ if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
101
+ });
102
+ }
103
+ function getConsoleLogs() {
104
+ return [...logs];
105
+ }
106
+ function getJavascriptErrors() {
107
+ return [...errorsCapture];
108
+ }
109
+ function clearLogs() {
110
+ logs = [];
111
+ errorsCapture = [];
112
+ }
113
+
114
+ // src/widget.ts
115
+ var DEFAULT_CONFIG = {
116
+ position: "bottom-right",
117
+ color: "#1e3a5f",
118
+ text: "Report Bug",
119
+ showOnInit: true
120
+ };
121
+ var container = null;
122
+ var onSubmitCallback = null;
123
+ var screenshotDataUrl = null;
124
+ var annotationsData = [];
125
+ function mountWidget(config2 = {}, onSubmit, opts) {
126
+ if (container) return;
127
+ onSubmitCallback = onSubmit;
128
+ const cfg = { ...DEFAULT_CONFIG, ...config2 };
129
+ container = document.createElement("div");
130
+ container.id = "checkflow-widget";
131
+ container.innerHTML = getWidgetHTML(cfg);
132
+ document.body.appendChild(container);
133
+ const btn = container.querySelector("#cf-trigger");
134
+ const form = container.querySelector("#cf-form");
135
+ const closeBtn = container.querySelector("#cf-close");
136
+ const submitBtn = container.querySelector("#cf-submit");
137
+ const screenshotBtn = container.querySelector("#cf-screenshot");
138
+ const highlightBtn = container.querySelector("#cf-highlight");
139
+ const screenshotPreview = container.querySelector("#cf-screenshot-preview");
140
+ btn?.addEventListener("click", () => {
141
+ form.style.display = form.style.display === "none" ? "flex" : "none";
142
+ screenshotDataUrl = null;
143
+ annotationsData = [];
144
+ if (screenshotPreview) screenshotPreview.style.display = "none";
145
+ });
146
+ closeBtn?.addEventListener("click", () => {
147
+ form.style.display = "none";
148
+ });
149
+ screenshotBtn?.addEventListener("click", async () => {
150
+ if (!opts?.onScreenshot) return;
151
+ screenshotBtn.textContent = "Capturing...";
152
+ form.style.display = "none";
153
+ try {
154
+ const data = await opts.onScreenshot();
155
+ if (data) {
156
+ screenshotDataUrl = data;
157
+ if (screenshotPreview) {
158
+ screenshotPreview.querySelector("img").src = data;
159
+ screenshotPreview.style.display = "block";
160
+ }
161
+ }
162
+ } catch {
163
+ }
164
+ form.style.display = "flex";
165
+ screenshotBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg> Screenshot';
166
+ });
167
+ highlightBtn?.addEventListener("click", async () => {
168
+ if (!opts?.onHighlight) return;
169
+ form.style.display = "none";
170
+ try {
171
+ const annotations = await opts.onHighlight();
172
+ if (annotations && annotations.length > 0) {
173
+ annotationsData = annotations;
174
+ highlightBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg> ${annotations.length} highlighted`;
175
+ }
176
+ } catch {
177
+ }
178
+ form.style.display = "flex";
179
+ });
180
+ submitBtn?.addEventListener("click", () => {
181
+ const title = container.querySelector("#cf-title")?.value;
182
+ const desc = container.querySelector("#cf-desc")?.value;
183
+ const type = container.querySelector("#cf-type")?.value;
184
+ const priority = container.querySelector("#cf-priority")?.value;
185
+ if (!title?.trim()) return;
186
+ onSubmitCallback?.({ title, description: desc, type, priority, screenshot: screenshotDataUrl || void 0, annotations: annotationsData.length > 0 ? annotationsData : void 0 });
187
+ container.querySelector("#cf-title").value = "";
188
+ container.querySelector("#cf-desc").value = "";
189
+ screenshotDataUrl = null;
190
+ annotationsData = [];
191
+ if (screenshotPreview) screenshotPreview.style.display = "none";
192
+ form.style.display = "none";
193
+ showToast("Feedback sent!");
194
+ });
195
+ }
196
+ function unmountWidget() {
197
+ if (container) {
198
+ container.remove();
199
+ container = null;
200
+ }
201
+ }
202
+ function showToast(msg) {
203
+ const toast = document.createElement("div");
204
+ toast.textContent = msg;
205
+ toast.style.cssText = "position:fixed;bottom:80px;right:20px;background:#10b981;color:#fff;padding:8px 16px;border-radius:8px;font-size:13px;z-index:100001;font-family:system-ui;box-shadow:0 2px 8px rgba(0,0,0,.15);";
206
+ document.body.appendChild(toast);
207
+ setTimeout(() => toast.remove(), 3e3);
208
+ }
209
+ function getPositionCSS(pos) {
210
+ switch (pos) {
211
+ case "bottom-left":
212
+ return "bottom:20px;left:20px;";
213
+ case "top-right":
214
+ return "top:20px;right:20px;";
215
+ case "top-left":
216
+ return "top:20px;left:20px;";
217
+ default:
218
+ return "bottom:20px;right:20px;";
219
+ }
220
+ }
221
+ function getFormPositionCSS(pos) {
222
+ switch (pos) {
223
+ case "bottom-left":
224
+ return "bottom:70px;left:20px;";
225
+ case "top-right":
226
+ return "top:70px;right:20px;";
227
+ case "top-left":
228
+ return "top:70px;left:20px;";
229
+ default:
230
+ return "bottom:70px;right:20px;";
231
+ }
232
+ }
233
+ function getWidgetHTML(cfg) {
234
+ const posBtn = getPositionCSS(cfg.position);
235
+ const posForm = getFormPositionCSS(cfg.position);
236
+ return `
237
+ <style>
238
+ #cf-trigger{position:fixed;${posBtn}z-index:100000;background:${cfg.color};color:#fff;border:none;border-radius:50px;padding:10px 18px;font-size:13px;font-family:system-ui,-apple-system,sans-serif;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.15);display:flex;align-items:center;gap:6px;transition:transform .15s}
239
+ #cf-trigger:hover{transform:scale(1.05)}
240
+ #cf-trigger svg{width:16px;height:16px}
241
+ #cf-form{position:fixed;${posForm}z-index:100000;background:#fff;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.12);width:360px;padding:16px;font-family:system-ui,-apple-system,sans-serif;display:none;flex-direction:column;gap:10px}
242
+ #cf-form h3{margin:0;font-size:15px;font-weight:600;color:#111}
243
+ #cf-form input,#cf-form textarea,#cf-form select{width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:8px;font-size:13px;font-family:inherit;box-sizing:border-box;outline:none;transition:border .15s}
244
+ #cf-form input:focus,#cf-form textarea:focus,#cf-form select:focus{border-color:${cfg.color}}
245
+ #cf-form textarea{resize:vertical;min-height:60px}
246
+ .cf-row{display:flex;gap:8px}
247
+ .cf-row select{flex:1}
248
+ .cf-tools{display:flex;gap:6px}
249
+ .cf-tool-btn{display:flex;align-items:center;gap:4px;padding:6px 10px;border:1px solid #e2e8f0;border-radius:6px;background:#f8fafc;color:#475569;font-size:11px;cursor:pointer;font-family:inherit;transition:all .15s}
250
+ .cf-tool-btn:hover{background:#f1f5f9;border-color:#cbd5e1}
251
+ .cf-tool-btn svg{width:14px;height:14px}
252
+ #cf-submit{background:${cfg.color};color:#fff;border:none;border-radius:8px;padding:9px;font-size:13px;font-weight:500;cursor:pointer;transition:opacity .15s}
253
+ #cf-submit:hover{opacity:.9}
254
+ #cf-close{position:absolute;top:10px;right:12px;background:none;border:none;cursor:pointer;font-size:18px;color:#94a3b8;line-height:1}
255
+ #cf-screenshot-preview{display:none;border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;max-height:120px}
256
+ #cf-screenshot-preview img{width:100%;height:auto;display:block}
257
+ </style>
258
+ <button id="cf-trigger">
259
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
260
+ ${cfg.text}
261
+ </button>
262
+ <div id="cf-form">
263
+ <button id="cf-close">&times;</button>
264
+ <h3>Report Feedback</h3>
265
+ <input id="cf-title" type="text" placeholder="Title *" />
266
+ <textarea id="cf-desc" placeholder="Description (optional)"></textarea>
267
+ <div class="cf-row">
268
+ <select id="cf-type">
269
+ <option value="BUG">Bug</option>
270
+ <option value="FEATURE">Feature</option>
271
+ <option value="IMPROVEMENT">Improvement</option>
272
+ <option value="QUESTION">Question</option>
273
+ </select>
274
+ <select id="cf-priority">
275
+ <option value="LOW">Low</option>
276
+ <option value="MEDIUM" selected>Medium</option>
277
+ <option value="HIGH">High</option>
278
+ <option value="CRITICAL">Critical</option>
279
+ </select>
280
+ </div>
281
+ <div class="cf-tools">
282
+ <button type="button" class="cf-tool-btn" id="cf-screenshot">
283
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>
284
+ Screenshot
285
+ </button>
286
+ <button type="button" class="cf-tool-btn" id="cf-highlight">
287
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
288
+ Highlight
289
+ </button>
290
+ </div>
291
+ <div id="cf-screenshot-preview"><img src="" alt="screenshot" /></div>
292
+ <button id="cf-submit">Send Feedback</button>
293
+ </div>`;
294
+ }
295
+
296
+ // src/index.ts
297
+ var SDK_VERSION = "1.1.0";
298
+ var DEFAULT_ENDPOINT = "https://api.checkflow.space/api/v1";
299
+ var config = null;
300
+ function init(cfg) {
301
+ config = {
302
+ enabled: true,
303
+ endpoint: DEFAULT_ENDPOINT,
304
+ environment: "production",
305
+ ...cfg
306
+ };
307
+ if (!config.enabled) return;
308
+ installInterceptors();
309
+ if (typeof window !== "undefined") {
310
+ loadHtml2Canvas().catch(() => {
311
+ });
312
+ }
313
+ if (typeof window !== "undefined" && config.widget?.showOnInit !== false) {
314
+ mountWidget(
315
+ config.widget,
316
+ (data) => {
317
+ sendFeedback({
318
+ title: data.title,
319
+ description: data.description,
320
+ type: data.type,
321
+ priority: data.priority,
322
+ screenshot_data: data.screenshot,
323
+ annotations: data.annotations
324
+ });
325
+ },
326
+ {
327
+ onScreenshot: () => captureScreenshot(),
328
+ onHighlight: () => startHighlighting()
329
+ }
330
+ );
331
+ }
332
+ }
333
+ async function sendFeedback(data) {
334
+ if (!config) {
335
+ return { success: false, error: { message: "SDK not initialized. Call init() first.", code: "NOT_INITIALIZED" } };
336
+ }
337
+ const context = typeof window !== "undefined" ? collectContext() : {};
338
+ const perf = typeof window !== "undefined" ? collectPerformance() : void 0;
339
+ const consoleLogs = getConsoleLogs();
340
+ const jsErrors = getJavascriptErrors();
341
+ const payload = {
342
+ ...data,
343
+ ...context,
344
+ environment: config.environment,
345
+ performance_metrics: perf,
346
+ console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
347
+ javascript_errors: jsErrors.length > 0 ? jsErrors : void 0,
348
+ sdk_version: SDK_VERSION
349
+ };
350
+ if (data.screenshot_data) {
351
+ payload.screenshot_data = data.screenshot_data;
352
+ }
353
+ if (data.annotations && data.annotations.length > 0) {
354
+ payload.annotations = data.annotations;
355
+ }
356
+ try {
357
+ const res = await fetch(`${config.endpoint}/sdk/feedback`, {
358
+ method: "POST",
359
+ headers: {
360
+ "Content-Type": "application/json",
361
+ "X-API-Key": config.apiKey
362
+ },
363
+ body: JSON.stringify(payload)
364
+ });
365
+ const json = await res.json();
366
+ if (!res.ok) {
367
+ return { success: false, error: json.error || { message: "Request failed", code: "REQUEST_FAILED" } };
368
+ }
369
+ return { success: true, data: json.data };
370
+ } catch (err) {
371
+ return { success: false, error: { message: err.message, code: "NETWORK_ERROR" } };
372
+ }
373
+ }
374
+ function showWidget() {
375
+ if (!config) return;
376
+ mountWidget(
377
+ config.widget,
378
+ (data) => {
379
+ sendFeedback({
380
+ title: data.title,
381
+ description: data.description,
382
+ type: data.type,
383
+ priority: data.priority,
384
+ screenshot_data: data.screenshot,
385
+ annotations: data.annotations
386
+ });
387
+ },
388
+ {
389
+ onScreenshot: () => captureScreenshot(),
390
+ onHighlight: () => startHighlighting()
391
+ }
392
+ );
393
+ }
394
+ function hideWidget() {
395
+ unmountWidget();
396
+ }
397
+ function destroy() {
398
+ unmountWidget();
399
+ clearLogs();
400
+ clearScreenshot();
401
+ config = null;
402
+ }
403
+ export {
404
+ captureScreenshot,
405
+ destroy,
406
+ hideWidget,
407
+ init,
408
+ sendFeedback,
409
+ showWidget,
410
+ startHighlighting
411
+ };
@@ -0,0 +1,28 @@
1
+ import { a as FeedbackResponse, H as HighlightAnnotation, C as CheckflowConfig } from './highlighter-D_wZWHlS.mjs';
2
+
3
+ /**
4
+ * Initialize Checkflow in a React/Next.js app.
5
+ * Call this in your root layout or _app file.
6
+ */
7
+ declare function useCheckflowInit(config: CheckflowConfig): void;
8
+ /**
9
+ * Hook to send feedback programmatically from React components.
10
+ */
11
+ declare function useCheckflowFeedback(): {
12
+ send: (data: {
13
+ title: string;
14
+ description?: string;
15
+ type?: string;
16
+ priority?: string;
17
+ }) => Promise<FeedbackResponse>;
18
+ screenshot: () => Promise<string | null>;
19
+ highlight: () => Promise<HighlightAnnotation[]>;
20
+ show: () => Promise<void>;
21
+ hide: () => Promise<void>;
22
+ };
23
+ /**
24
+ * Cleanup function for React useEffect.
25
+ */
26
+ declare function destroyCheckflow(): void;
27
+
28
+ export { destroyCheckflow, useCheckflowFeedback, useCheckflowInit };
@@ -0,0 +1,28 @@
1
+ import { a as FeedbackResponse, H as HighlightAnnotation, C as CheckflowConfig } from './highlighter-D_wZWHlS.js';
2
+
3
+ /**
4
+ * Initialize Checkflow in a React/Next.js app.
5
+ * Call this in your root layout or _app file.
6
+ */
7
+ declare function useCheckflowInit(config: CheckflowConfig): void;
8
+ /**
9
+ * Hook to send feedback programmatically from React components.
10
+ */
11
+ declare function useCheckflowFeedback(): {
12
+ send: (data: {
13
+ title: string;
14
+ description?: string;
15
+ type?: string;
16
+ priority?: string;
17
+ }) => Promise<FeedbackResponse>;
18
+ screenshot: () => Promise<string | null>;
19
+ highlight: () => Promise<HighlightAnnotation[]>;
20
+ show: () => Promise<void>;
21
+ hide: () => Promise<void>;
22
+ };
23
+ /**
24
+ * Cleanup function for React useEffect.
25
+ */
26
+ declare function destroyCheckflow(): void;
27
+
28
+ export { destroyCheckflow, useCheckflowFeedback, useCheckflowInit };