@koderlabs/tasks-sdk-web-reporter 0.1.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/LICENSE +179 -0
- package/README.md +9 -0
- package/dist/chunk-LDKYNLXK.js +309 -0
- package/dist/chunk-LDKYNLXK.js.map +1 -0
- package/dist/index.cjs +2013 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +362 -0
- package/dist/index.d.ts +362 -0
- package/dist/index.js +1657 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.umd.js +385 -0
- package/dist/metadata-BXXSPXC5.js +13 -0
- package/dist/metadata-BXXSPXC5.js.map +1 -0
- package/package.json +72 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { Integration, ClientInterface } from '@koderlabs/tasks-sdk';
|
|
2
|
+
import { Serializable } from '@koderlabs/tasks-sdk-types';
|
|
3
|
+
|
|
4
|
+
interface DrawCommand {
|
|
5
|
+
/** 'highlight' = red cutout rect with dim overlay; 'hide' = opaque black box */
|
|
6
|
+
type: 'highlight' | 'hide';
|
|
7
|
+
/** Normalized 0..1 against captured-image width */
|
|
8
|
+
x: number;
|
|
9
|
+
/** Normalized 0..1 against captured-image height */
|
|
10
|
+
y: number;
|
|
11
|
+
/** Normalized 0..1 width */
|
|
12
|
+
w: number;
|
|
13
|
+
/** Normalized 0..1 height */
|
|
14
|
+
h: number;
|
|
15
|
+
}
|
|
16
|
+
type WidgetApi = {
|
|
17
|
+
show(): void;
|
|
18
|
+
hide(): void;
|
|
19
|
+
isVisible(): boolean;
|
|
20
|
+
capture(mode?: 'fullscreen' | 'advanced'): Promise<void>;
|
|
21
|
+
cancelCapture(): void;
|
|
22
|
+
setReporter(info: {
|
|
23
|
+
email: string;
|
|
24
|
+
fullName?: string;
|
|
25
|
+
}): void;
|
|
26
|
+
clearReporter(): void;
|
|
27
|
+
setCustomData(data?: Record<string, Serializable>): void;
|
|
28
|
+
setNetworkRecordingSettings(s: {
|
|
29
|
+
excludedKeys?: string[];
|
|
30
|
+
excludedDomains?: string[];
|
|
31
|
+
}): void;
|
|
32
|
+
on(event: WidgetEventName, listener: (e: WidgetEvent) => void): void;
|
|
33
|
+
off(event: WidgetEventName, listener: (e: WidgetEvent) => void): void;
|
|
34
|
+
unload(): void;
|
|
35
|
+
};
|
|
36
|
+
type WidgetEventName = 'load' | 'loaderror' | 'show' | 'hide' | 'capture' | 'feedbackbeforesend' | 'feedbacksent' | 'feedbackerror' | 'feedbackdiscarded';
|
|
37
|
+
/** Mutable fields in feedbackbeforesend — cannot mutate description/screenshot */
|
|
38
|
+
type MutableFeedbackField = 'assignee' | 'labels' | 'customFields' | 'priority' | 'issueType';
|
|
39
|
+
interface BeforeSendPayload {
|
|
40
|
+
values: {
|
|
41
|
+
description: string;
|
|
42
|
+
title?: string;
|
|
43
|
+
email?: string;
|
|
44
|
+
assignee?: string;
|
|
45
|
+
labels?: string[];
|
|
46
|
+
customFields?: Record<string, unknown>;
|
|
47
|
+
priority?: string;
|
|
48
|
+
issueType?: string;
|
|
49
|
+
};
|
|
50
|
+
setValue(field: MutableFeedbackField, value: unknown): void;
|
|
51
|
+
cancel(): void;
|
|
52
|
+
}
|
|
53
|
+
type WidgetEvent = {
|
|
54
|
+
name: 'load';
|
|
55
|
+
} | {
|
|
56
|
+
name: 'loaderror';
|
|
57
|
+
error: Error;
|
|
58
|
+
} | {
|
|
59
|
+
name: 'show';
|
|
60
|
+
} | {
|
|
61
|
+
name: 'hide';
|
|
62
|
+
} | {
|
|
63
|
+
name: 'capture';
|
|
64
|
+
canvas: HTMLCanvasElement;
|
|
65
|
+
} | (BeforeSendPayload & {
|
|
66
|
+
name: 'feedbackbeforesend';
|
|
67
|
+
}) | {
|
|
68
|
+
name: 'feedbacksent';
|
|
69
|
+
ticketKey: string;
|
|
70
|
+
ticketUrl: string;
|
|
71
|
+
} | {
|
|
72
|
+
name: 'feedbackerror';
|
|
73
|
+
error: Error;
|
|
74
|
+
} | {
|
|
75
|
+
name: 'feedbackdiscarded';
|
|
76
|
+
};
|
|
77
|
+
interface NetworkRecordingSettings {
|
|
78
|
+
enabled?: boolean;
|
|
79
|
+
excludedKeys?: string[];
|
|
80
|
+
excludedDomains?: string[];
|
|
81
|
+
}
|
|
82
|
+
interface WidgetOptions {
|
|
83
|
+
/**
|
|
84
|
+
* Hotkey string, e.g. 'ctrl+shift+i'. Set to false to disable.
|
|
85
|
+
* Mac users should use 'meta+shift+i' or keep ctrl for cross-platform.
|
|
86
|
+
*/
|
|
87
|
+
hotkey?: string | false;
|
|
88
|
+
/** Auto-inject a floating trigger button into the host page. Default: true. */
|
|
89
|
+
autoInject?: boolean;
|
|
90
|
+
/** Prefer getDisplayMedia (asks browser permission, pixel-perfect) over
|
|
91
|
+
* html2canvas (no prompt, DOM-rendered). Default: false — seamless UX.
|
|
92
|
+
* Flip to true when the page has WebGL / cross-origin iframes / canvas
|
|
93
|
+
* content html2canvas can't render. */
|
|
94
|
+
useNativeScreenshot?: boolean;
|
|
95
|
+
/** Capture screenshot automatically when the widget opens, instead of
|
|
96
|
+
* requiring an explicit click. Default: true. Set false to keep the
|
|
97
|
+
* "Add Screenshot" button as the trigger. */
|
|
98
|
+
autoCapture?: boolean;
|
|
99
|
+
/** Show "Powered by InstantTasks" branding. Default: true. */
|
|
100
|
+
showBranding?: boolean;
|
|
101
|
+
/** Default reporter info. Can be overridden via setReporter(). */
|
|
102
|
+
reporter?: {
|
|
103
|
+
email: string;
|
|
104
|
+
fullName?: string;
|
|
105
|
+
};
|
|
106
|
+
/** Arbitrary key/value appended to every submission. */
|
|
107
|
+
customData?: Record<string, Serializable>;
|
|
108
|
+
/** Whether to ask the user for their email. Default: false. */
|
|
109
|
+
collectEmail?: boolean;
|
|
110
|
+
/** Suppress console diagnostics. Default: false. */
|
|
111
|
+
silent?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Expose `window.InstantTasks.show()` for manual triggering from DevTools.
|
|
114
|
+
* Default: true in non-production, false in production. In production this
|
|
115
|
+
* defaults off because any third-party script (analytics, A/B testing,
|
|
116
|
+
* vendor injects) can read `window.InstantTasks` and trigger the widget.
|
|
117
|
+
*/
|
|
118
|
+
exposeGlobal?: boolean;
|
|
119
|
+
/** Network recording settings. */
|
|
120
|
+
networkRecording?: NetworkRecordingSettings;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Capability detection for native screenshot capture.
|
|
125
|
+
* Ported from: sentry-javascript/packages/feedback/src/util/isScreenshotSupported.ts
|
|
126
|
+
*
|
|
127
|
+
* Returns false on:
|
|
128
|
+
* - Mobile UA (Android, iPhone, iPad via UA, etc.)
|
|
129
|
+
* - iPad-as-Mac (Macintosh UA with maxTouchPoints > 1)
|
|
130
|
+
* - Insecure context (non-HTTPS except localhost)
|
|
131
|
+
* - Missing getDisplayMedia API
|
|
132
|
+
*/
|
|
133
|
+
declare function isNativeCaptureSupported(): boolean;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Hotkey parser and listener factory.
|
|
137
|
+
*
|
|
138
|
+
* Parses strings like 'ctrl+shift+i', 'meta+shift+i'.
|
|
139
|
+
* Modifiers: ctrl, meta, alt, shift (order-insensitive).
|
|
140
|
+
* Ignores keypresses when the focused element is an input, textarea,
|
|
141
|
+
* or any contenteditable element (to avoid interfering with user typing).
|
|
142
|
+
*/
|
|
143
|
+
interface ParsedHotkey {
|
|
144
|
+
ctrl: boolean;
|
|
145
|
+
meta: boolean;
|
|
146
|
+
alt: boolean;
|
|
147
|
+
shift: boolean;
|
|
148
|
+
key: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Parse a hotkey string into a structured predicate.
|
|
152
|
+
* Returns null for invalid or falsy input.
|
|
153
|
+
*/
|
|
154
|
+
declare function parseHotkey(raw: string | false | undefined): ParsedHotkey | null;
|
|
155
|
+
/**
|
|
156
|
+
* Register a keydown listener for the given hotkey string.
|
|
157
|
+
* Returns a cleanup function to remove the listener.
|
|
158
|
+
*
|
|
159
|
+
* @param hotkeyStr - e.g. 'ctrl+shift+i' or false to disable.
|
|
160
|
+
* @param handler - Called when the hotkey fires outside typing targets.
|
|
161
|
+
* @param target - Element to attach to (defaults to window).
|
|
162
|
+
*/
|
|
163
|
+
declare function registerHotkey(hotkeyStr: string | false | undefined, handler: () => void, target?: EventTarget): () => void;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Metadata collector.
|
|
167
|
+
*
|
|
168
|
+
* Collects:
|
|
169
|
+
* - url: current window.location.href
|
|
170
|
+
* - userAgent: navigator.userAgent
|
|
171
|
+
* - viewport: { width, height }
|
|
172
|
+
* - appVersion: from <meta name="app-version" content="…"> if present
|
|
173
|
+
* - consoleTail: last N console messages (ring buffer, opt-in via captureConsole)
|
|
174
|
+
* - customData: lazy callback result (Jam-style pattern)
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
interface ConsoleEntry {
|
|
178
|
+
level: 'log' | 'warn' | 'error' | 'info' | 'debug';
|
|
179
|
+
args: string[];
|
|
180
|
+
ts: number;
|
|
181
|
+
}
|
|
182
|
+
interface BrowserInfo {
|
|
183
|
+
name?: string;
|
|
184
|
+
version?: string;
|
|
185
|
+
major?: string;
|
|
186
|
+
mobile?: boolean;
|
|
187
|
+
}
|
|
188
|
+
interface OsInfo {
|
|
189
|
+
name?: string;
|
|
190
|
+
version?: string;
|
|
191
|
+
}
|
|
192
|
+
interface DeviceInfo {
|
|
193
|
+
deviceMemoryGB?: number;
|
|
194
|
+
hardwareConcurrency?: number;
|
|
195
|
+
jsHeapUsedMB?: number;
|
|
196
|
+
jsHeapTotalMB?: number;
|
|
197
|
+
}
|
|
198
|
+
interface NetworkInfo {
|
|
199
|
+
online?: boolean;
|
|
200
|
+
effectiveType?: string;
|
|
201
|
+
downlinkMbps?: number;
|
|
202
|
+
rttMs?: number;
|
|
203
|
+
saveData?: boolean;
|
|
204
|
+
}
|
|
205
|
+
interface CollectedMetadata {
|
|
206
|
+
url: string;
|
|
207
|
+
userAgent: string;
|
|
208
|
+
viewport: {
|
|
209
|
+
width: number;
|
|
210
|
+
height: number;
|
|
211
|
+
};
|
|
212
|
+
appVersion?: string;
|
|
213
|
+
consoleTail?: ConsoleEntry[];
|
|
214
|
+
customData?: Record<string, Serializable>;
|
|
215
|
+
/** Physical screen resolution (independent of window size). */
|
|
216
|
+
screen?: {
|
|
217
|
+
width: number;
|
|
218
|
+
height: number;
|
|
219
|
+
dpr: number;
|
|
220
|
+
};
|
|
221
|
+
/** Parsed from userAgentData (Chromium) or UA regex. */
|
|
222
|
+
browser?: BrowserInfo;
|
|
223
|
+
/** Parsed OS name + version. */
|
|
224
|
+
os?: OsInfo;
|
|
225
|
+
/** Hardware capabilities. */
|
|
226
|
+
device?: DeviceInfo;
|
|
227
|
+
/** Network conditions at submit time. */
|
|
228
|
+
network?: NetworkInfo;
|
|
229
|
+
/** BCP-47 language tags from `navigator.languages`. */
|
|
230
|
+
languages?: string[];
|
|
231
|
+
/** IANA timezone name (e.g. `America/New_York`). */
|
|
232
|
+
timezone?: string;
|
|
233
|
+
/** UTC offset in minutes at submit time. */
|
|
234
|
+
timezoneOffsetMin?: number;
|
|
235
|
+
/** User-agent preference signals. */
|
|
236
|
+
preferences?: {
|
|
237
|
+
colorScheme?: 'light' | 'dark';
|
|
238
|
+
reducedMotion?: boolean;
|
|
239
|
+
contrast?: 'no-preference' | 'more' | 'less';
|
|
240
|
+
};
|
|
241
|
+
/** Document referrer at the moment of the report. */
|
|
242
|
+
referrer?: string;
|
|
243
|
+
/** Navigation timing snapshot — useful for "was the page slow?" reports. */
|
|
244
|
+
pageLoad?: {
|
|
245
|
+
domContentLoadedMs?: number;
|
|
246
|
+
loadCompleteMs?: number;
|
|
247
|
+
ttfbMs?: number;
|
|
248
|
+
};
|
|
249
|
+
/** Wall-clock at submit time (server already has receive time; this is the user's). */
|
|
250
|
+
clientTime?: string;
|
|
251
|
+
}
|
|
252
|
+
/** Patch global console to capture a ring buffer of log calls. */
|
|
253
|
+
declare function patchConsole(): () => void;
|
|
254
|
+
/**
|
|
255
|
+
* Collect current-page metadata snapshot.
|
|
256
|
+
*
|
|
257
|
+
* @param captureConsole - Include console ring buffer. Default: false.
|
|
258
|
+
* @param customDataFn - Lazy callback that returns extra key/value data.
|
|
259
|
+
*/
|
|
260
|
+
declare function collectMetadata(captureConsole?: boolean, customDataFn?: () => Record<string, Serializable> | Promise<Record<string, Serializable>>): Promise<CollectedMetadata>;
|
|
261
|
+
/**
|
|
262
|
+
* Format collected metadata as a collapsible markdown block.
|
|
263
|
+
* Appended to the ticket description body.
|
|
264
|
+
*/
|
|
265
|
+
/**
|
|
266
|
+
* Format collected metadata as an HTML block.
|
|
267
|
+
*
|
|
268
|
+
* Output is HTML — not markdown — because ticket descriptions in
|
|
269
|
+
* InstantTasks are stored as TipTap-compatible HTML and the renderer
|
|
270
|
+
* does NOT post-process markdown. Returning `**bold**` and bare `\n`
|
|
271
|
+
* would render as literal asterisks on one collapsed line.
|
|
272
|
+
*/
|
|
273
|
+
declare function formatMetadataBlock(meta: CollectedMetadata): string;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @koderlabs/tasks-sdk-web-reporter
|
|
277
|
+
*
|
|
278
|
+
* Browser in-app bug-reporter integration for the InstantTasks SDK.
|
|
279
|
+
* `reporterIntegration(opts)` (alias: `widgetIntegration` for back-compat).
|
|
280
|
+
*
|
|
281
|
+
* Usage:
|
|
282
|
+
* import { init } from '@koderlabs/tasks-sdk';
|
|
283
|
+
* import { reporterIntegration } from '@koderlabs/tasks-sdk-web-reporter';
|
|
284
|
+
*
|
|
285
|
+
* const client = init({
|
|
286
|
+
* projectId: 'FE',
|
|
287
|
+
* accessKey: 'sk_live_…',
|
|
288
|
+
* integrations: [reporterIntegration({ hotkey: 'ctrl+shift+i' })],
|
|
289
|
+
* });
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
type Listener = (e: WidgetEvent) => void;
|
|
293
|
+
declare class WidgetIntegration implements Integration {
|
|
294
|
+
readonly name = "widget";
|
|
295
|
+
private opts;
|
|
296
|
+
/** Caller-supplied options, before defaults were applied. Used to decide
|
|
297
|
+
* which fields can be overwritten by server-side project config. */
|
|
298
|
+
private _initialOpts;
|
|
299
|
+
private client;
|
|
300
|
+
private shell;
|
|
301
|
+
private fabBtn;
|
|
302
|
+
/** Outer shadow-host element for the FAB. Tracked separately from the
|
|
303
|
+
* button so capture flows can hide the *entire* widget chrome (not just
|
|
304
|
+
* the modal) from screenshots. */
|
|
305
|
+
private fabHost;
|
|
306
|
+
private _visible;
|
|
307
|
+
private _reporter;
|
|
308
|
+
private _customData;
|
|
309
|
+
/** Reserved for upcoming network capture filters (Phase I).
|
|
310
|
+
* Public so the field stays type-checked and isn't tree-shaken; safe to
|
|
311
|
+
* read but currently has no effect. */
|
|
312
|
+
networkSettings: {
|
|
313
|
+
excludedKeys?: string[];
|
|
314
|
+
excludedDomains?: string[];
|
|
315
|
+
};
|
|
316
|
+
private listeners;
|
|
317
|
+
private cleanupHotkey;
|
|
318
|
+
private cleanupConsole;
|
|
319
|
+
constructor(opts: WidgetOptions);
|
|
320
|
+
setup(client: ClientInterface): void;
|
|
321
|
+
/**
|
|
322
|
+
* GET /sdk/v1/config — uses the SDK's own configured endpoint + access
|
|
323
|
+
* key (the client already has both). Merges into this.opts only for
|
|
324
|
+
* fields the caller didn't explicitly set.
|
|
325
|
+
*/
|
|
326
|
+
private _applyRemoteConfig;
|
|
327
|
+
/** Wires hotkey + FAB + debug surface. Called after _applyRemoteConfig. */
|
|
328
|
+
private _wireRuntime;
|
|
329
|
+
teardown(): void;
|
|
330
|
+
show(): void;
|
|
331
|
+
hide(): void;
|
|
332
|
+
isVisible(): boolean;
|
|
333
|
+
capture(mode?: 'fullscreen' | 'advanced'): Promise<void>;
|
|
334
|
+
cancelCapture(): void;
|
|
335
|
+
setReporter(info: {
|
|
336
|
+
email: string;
|
|
337
|
+
fullName?: string;
|
|
338
|
+
}): void;
|
|
339
|
+
clearReporter(): void;
|
|
340
|
+
setCustomData(data?: Record<string, unknown>): void;
|
|
341
|
+
setNetworkRecordingSettings(s: {
|
|
342
|
+
excludedKeys?: string[];
|
|
343
|
+
excludedDomains?: string[];
|
|
344
|
+
}): void;
|
|
345
|
+
on(event: WidgetEventName, listener: Listener): void;
|
|
346
|
+
off(event: WidgetEventName, listener: Listener): void;
|
|
347
|
+
unload(): void;
|
|
348
|
+
private _openModal;
|
|
349
|
+
private _injectFab;
|
|
350
|
+
private _emit;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Create an in-app reporter integration for the InstantTasks SDK client.
|
|
354
|
+
*
|
|
355
|
+
* @param opts - Reporter configuration options.
|
|
356
|
+
* @returns An Integration that can be passed to `init({ integrations: [...] })`.
|
|
357
|
+
*/
|
|
358
|
+
declare function reporterIntegration(opts?: WidgetOptions): Integration & WidgetApi;
|
|
359
|
+
/** @deprecated Renamed to `reporterIntegration`. Will be removed in v1. */
|
|
360
|
+
declare const widgetIntegration: typeof reporterIntegration;
|
|
361
|
+
|
|
362
|
+
export { type DrawCommand, WidgetIntegration as ReporterIntegration, type WidgetApi, type WidgetEvent, type WidgetEventName, WidgetIntegration, type WidgetOptions, collectMetadata, reporterIntegration as default, formatMetadataBlock, isNativeCaptureSupported, parseHotkey, patchConsole, registerHotkey, reporterIntegration, widgetIntegration };
|