@bworlds/launchkit 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/README.md +172 -0
- package/dist/events.d.ts +57 -0
- package/dist/index.d.ts +61 -0
- package/dist/launchkit.cjs +441 -0
- package/dist/launchkit.cjs.map +1 -0
- package/dist/launchkit.js +9977 -0
- package/dist/launchkit.js.map +1 -0
- package/dist/launchkit.umd.js +441 -0
- package/dist/launchkit.umd.js.map +1 -0
- package/dist/privacy.d.ts +41 -0
- package/dist/recorder.d.ts +72 -0
- package/dist/screenshot.d.ts +40 -0
- package/dist/types.d.ts +97 -0
- package/dist/ui/InviteDialog.d.ts +41 -0
- package/dist/ui/Toolbar.d.ts +64 -0
- package/dist/ui/injectStyles.d.ts +6 -0
- package/dist/uploader.d.ts +36 -0
- package/package.json +60 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy utilities for masking sensitive inputs
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Get the CSS selector string for masking
|
|
6
|
+
*/
|
|
7
|
+
export declare function getMaskSelector(): string;
|
|
8
|
+
/**
|
|
9
|
+
* Get rrweb maskTextSelector for privacy
|
|
10
|
+
*/
|
|
11
|
+
export declare function getMaskTextSelector(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Check if an element should be masked
|
|
14
|
+
*/
|
|
15
|
+
export declare function shouldMaskElement(element: Element): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Get rrweb privacy configuration
|
|
18
|
+
*/
|
|
19
|
+
export declare function getPrivacyConfig(enabled: boolean): {
|
|
20
|
+
maskAllInputs: boolean;
|
|
21
|
+
maskTextSelector: null;
|
|
22
|
+
maskInputOptions: {
|
|
23
|
+
password?: undefined;
|
|
24
|
+
};
|
|
25
|
+
maskInputFn?: undefined;
|
|
26
|
+
blockSelector?: undefined;
|
|
27
|
+
ignoreSelector?: undefined;
|
|
28
|
+
} | {
|
|
29
|
+
maskAllInputs: boolean;
|
|
30
|
+
maskTextSelector: string;
|
|
31
|
+
maskInputOptions: {
|
|
32
|
+
password: boolean;
|
|
33
|
+
};
|
|
34
|
+
maskInputFn: (text: string, element: HTMLElement) => string;
|
|
35
|
+
blockSelector: string;
|
|
36
|
+
ignoreSelector: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Mask a text value
|
|
40
|
+
*/
|
|
41
|
+
export declare function maskText(text: string, maskChar?: string): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type eventWithTime } from 'rrweb';
|
|
2
|
+
import type { RecorderConfig, RecorderState, ScreenshotMarker, Uploader } from './types';
|
|
3
|
+
export declare class Recorder {
|
|
4
|
+
private config;
|
|
5
|
+
private state;
|
|
6
|
+
private events;
|
|
7
|
+
private stopFn;
|
|
8
|
+
private startTime;
|
|
9
|
+
private pauseTime;
|
|
10
|
+
private totalPausedDuration;
|
|
11
|
+
private chunkIndex;
|
|
12
|
+
private maxDurationTimer;
|
|
13
|
+
private chunkTimer;
|
|
14
|
+
private uploader;
|
|
15
|
+
private metadata;
|
|
16
|
+
private onEventCallback;
|
|
17
|
+
constructor(config: RecorderConfig);
|
|
18
|
+
/**
|
|
19
|
+
* Set the uploader instance
|
|
20
|
+
*/
|
|
21
|
+
setUploader(uploader: Uploader): void;
|
|
22
|
+
/**
|
|
23
|
+
* Set callback for new events (used by screenshot system)
|
|
24
|
+
*/
|
|
25
|
+
onEvent(callback: (event: eventWithTime) => void): void;
|
|
26
|
+
/**
|
|
27
|
+
* Get current recorder state
|
|
28
|
+
*/
|
|
29
|
+
getState(): RecorderState;
|
|
30
|
+
/**
|
|
31
|
+
* Get elapsed recording time in milliseconds
|
|
32
|
+
*/
|
|
33
|
+
getElapsedTime(): number;
|
|
34
|
+
/**
|
|
35
|
+
* Get all recorded events
|
|
36
|
+
*/
|
|
37
|
+
getEvents(): eventWithTime[];
|
|
38
|
+
/**
|
|
39
|
+
* Start recording
|
|
40
|
+
*/
|
|
41
|
+
start(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Pause recording
|
|
44
|
+
*/
|
|
45
|
+
pause(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Resume recording
|
|
48
|
+
*/
|
|
49
|
+
resume(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Stop recording and finalize
|
|
52
|
+
* @param screenshotMarkers - Markers for server-side screenshot generation
|
|
53
|
+
* @returns Recording result from the API, or null if no uploader
|
|
54
|
+
*/
|
|
55
|
+
stop(screenshotMarkers?: ScreenshotMarker[]): Promise<import('./types').RecordingResult | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Handle incoming rrweb event
|
|
58
|
+
*/
|
|
59
|
+
private handleEvent;
|
|
60
|
+
/**
|
|
61
|
+
* Flush current events as a chunk
|
|
62
|
+
*/
|
|
63
|
+
private flushChunk;
|
|
64
|
+
/**
|
|
65
|
+
* Update state and notify listeners
|
|
66
|
+
*/
|
|
67
|
+
private setState;
|
|
68
|
+
/**
|
|
69
|
+
* Reset recorder to initial state
|
|
70
|
+
*/
|
|
71
|
+
reset(): void;
|
|
72
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ScreenshotMarker, ScreenshotEventType } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Screenshot marker system - tracks timestamps for server-side screenshot generation
|
|
4
|
+
*/
|
|
5
|
+
export declare class ScreenshotMarkerTracker {
|
|
6
|
+
private markers;
|
|
7
|
+
private recordingStartTime;
|
|
8
|
+
private isRecording;
|
|
9
|
+
/**
|
|
10
|
+
* Set recording start time for timestamp calculation
|
|
11
|
+
*/
|
|
12
|
+
setStartTime(startTime: number): void;
|
|
13
|
+
/**
|
|
14
|
+
* Get all captured markers
|
|
15
|
+
*/
|
|
16
|
+
getMarkers(): ScreenshotMarker[];
|
|
17
|
+
/**
|
|
18
|
+
* Get marker count
|
|
19
|
+
*/
|
|
20
|
+
getCount(): number;
|
|
21
|
+
/**
|
|
22
|
+
* Mark a timestamp for screenshot generation
|
|
23
|
+
* (renamed from capture() for clarity, but keeping similar signature)
|
|
24
|
+
*/
|
|
25
|
+
capture(eventType: ScreenshotEventType): ScreenshotMarker | null;
|
|
26
|
+
/**
|
|
27
|
+
* Mark with debounce for scroll events
|
|
28
|
+
*/
|
|
29
|
+
private captureDebounceTimer;
|
|
30
|
+
captureDebounced(eventType: ScreenshotEventType, delay?: number): void;
|
|
31
|
+
/**
|
|
32
|
+
* Stop tracking (called when recording stops)
|
|
33
|
+
*/
|
|
34
|
+
stop(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Reset state
|
|
37
|
+
*/
|
|
38
|
+
reset(): void;
|
|
39
|
+
}
|
|
40
|
+
export { ScreenshotMarkerTracker as ScreenshotCapture };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { eventWithTime } from 'rrweb';
|
|
2
|
+
export type ToolbarPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
3
|
+
export type RecorderState = 'idle' | 'recording' | 'paused' | 'stopped';
|
|
4
|
+
export interface RecorderConfig {
|
|
5
|
+
/** API key for authentication */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** API endpoint (defaults to https://api.bworlds.com) */
|
|
8
|
+
apiEndpoint?: string;
|
|
9
|
+
/** Maximum recording duration in ms (default: 15 minutes) */
|
|
10
|
+
maxDuration?: number;
|
|
11
|
+
/** Mask sensitive inputs (default: true) */
|
|
12
|
+
maskInputs?: boolean;
|
|
13
|
+
/** Automatically open playback page when recording stops (default: true) */
|
|
14
|
+
autoOpenPlayback?: boolean;
|
|
15
|
+
/** Toolbar configuration */
|
|
16
|
+
toolbar?: {
|
|
17
|
+
/** Position of the toolbar (default: bottom-right) */
|
|
18
|
+
position?: ToolbarPosition;
|
|
19
|
+
/** Whether to show the toolbar (default: true) */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
};
|
|
22
|
+
/** Callback when recording completes */
|
|
23
|
+
onComplete?: (result: RecordingResult) => void;
|
|
24
|
+
/** Callback when recording state changes */
|
|
25
|
+
onStateChange?: (state: RecorderState) => void;
|
|
26
|
+
/** Callback on error */
|
|
27
|
+
onError?: (error: Error) => void;
|
|
28
|
+
}
|
|
29
|
+
export interface RecordingResult {
|
|
30
|
+
/** Unique recording ID */
|
|
31
|
+
recordingId: string;
|
|
32
|
+
/** URL to view the recording */
|
|
33
|
+
playbackUrl: string;
|
|
34
|
+
/** Duration in milliseconds */
|
|
35
|
+
duration: number;
|
|
36
|
+
/** Number of events recorded */
|
|
37
|
+
eventCount: number;
|
|
38
|
+
/** Number of screenshots captured */
|
|
39
|
+
screenshotCount: number;
|
|
40
|
+
}
|
|
41
|
+
export interface RecordingMetadata {
|
|
42
|
+
/** URL where recording was made */
|
|
43
|
+
url: string;
|
|
44
|
+
/** User agent string */
|
|
45
|
+
userAgent: string;
|
|
46
|
+
/** Viewport dimensions */
|
|
47
|
+
viewport: {
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
};
|
|
51
|
+
/** Recording start timestamp */
|
|
52
|
+
startedAt: number;
|
|
53
|
+
}
|
|
54
|
+
export interface Screenshot {
|
|
55
|
+
/** Screenshot ID */
|
|
56
|
+
id: string;
|
|
57
|
+
/** Timestamp relative to recording start */
|
|
58
|
+
timestamp: number;
|
|
59
|
+
/** Event type that triggered the screenshot */
|
|
60
|
+
eventType: ScreenshotEventType;
|
|
61
|
+
/** Base64 encoded PNG data */
|
|
62
|
+
dataUrl: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Screenshot marker - timestamp and event type for server-side generation
|
|
66
|
+
*/
|
|
67
|
+
export interface ScreenshotMarker {
|
|
68
|
+
/** Timestamp relative to recording start (ms) */
|
|
69
|
+
timestamp: number;
|
|
70
|
+
/** Event type that triggered the marker */
|
|
71
|
+
eventType: ScreenshotEventType;
|
|
72
|
+
}
|
|
73
|
+
export type ScreenshotEventType = 'load' | 'navigation' | 'form_submit' | 'modal_open' | 'scroll' | 'manual';
|
|
74
|
+
export interface EventChunk {
|
|
75
|
+
/** Chunk index (0-based) */
|
|
76
|
+
index: number;
|
|
77
|
+
/** Events in this chunk */
|
|
78
|
+
events: eventWithTime[];
|
|
79
|
+
/** Timestamp when chunk was created */
|
|
80
|
+
timestamp: number;
|
|
81
|
+
}
|
|
82
|
+
export interface SessionToken {
|
|
83
|
+
/** Token for API authentication */
|
|
84
|
+
token: string;
|
|
85
|
+
/** Recording ID this token is for */
|
|
86
|
+
recordingId: string;
|
|
87
|
+
/** Expiration timestamp */
|
|
88
|
+
expiresAt: number;
|
|
89
|
+
}
|
|
90
|
+
export interface Uploader {
|
|
91
|
+
/** Initialize recording session */
|
|
92
|
+
start(metadata: RecordingMetadata): Promise<SessionToken>;
|
|
93
|
+
/** Upload event chunk */
|
|
94
|
+
uploadChunk(chunk: EventChunk): Promise<void>;
|
|
95
|
+
/** Complete recording with screenshot markers for server-side generation */
|
|
96
|
+
complete(duration: number, eventCount: number, screenshotMarkers: ScreenshotMarker[]): Promise<RecordingResult>;
|
|
97
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controller for managing the InviteDialog lifecycle
|
|
3
|
+
*/
|
|
4
|
+
export declare class InviteDialogController {
|
|
5
|
+
private container;
|
|
6
|
+
private email;
|
|
7
|
+
private onCompleteCallback;
|
|
8
|
+
constructor(email: string);
|
|
9
|
+
/**
|
|
10
|
+
* Check if invite has already been completed
|
|
11
|
+
*/
|
|
12
|
+
static isCompleted(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Clear completion state (for testing)
|
|
15
|
+
*/
|
|
16
|
+
static clearCompletion(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Mount the dialog to the DOM
|
|
19
|
+
*/
|
|
20
|
+
mount(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Unmount the dialog from the DOM
|
|
23
|
+
*/
|
|
24
|
+
unmount(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Set completion callback
|
|
27
|
+
*/
|
|
28
|
+
onComplete(callback: () => void): void;
|
|
29
|
+
/**
|
|
30
|
+
* Handle dialog completion
|
|
31
|
+
*/
|
|
32
|
+
private handleComplete;
|
|
33
|
+
/**
|
|
34
|
+
* Handle login click (closes dialog)
|
|
35
|
+
*/
|
|
36
|
+
private handleLogin;
|
|
37
|
+
/**
|
|
38
|
+
* Render the dialog
|
|
39
|
+
*/
|
|
40
|
+
private render;
|
|
41
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { RecorderState, ToolbarPosition, RecordingResult } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Toolbar controller for managing the Preact component lifecycle
|
|
4
|
+
*/
|
|
5
|
+
export declare class ToolbarController {
|
|
6
|
+
private container;
|
|
7
|
+
private position;
|
|
8
|
+
private state;
|
|
9
|
+
private elapsedTime;
|
|
10
|
+
private result;
|
|
11
|
+
private updateInterval;
|
|
12
|
+
private getElapsedTime;
|
|
13
|
+
private onStartCallback;
|
|
14
|
+
private onPauseCallback;
|
|
15
|
+
private onResumeCallback;
|
|
16
|
+
private onStopCallback;
|
|
17
|
+
private onScreenshotCallback;
|
|
18
|
+
private onRestartCallback;
|
|
19
|
+
constructor(position?: ToolbarPosition);
|
|
20
|
+
/**
|
|
21
|
+
* Mount the toolbar to the DOM
|
|
22
|
+
*/
|
|
23
|
+
mount(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Unmount the toolbar from the DOM
|
|
26
|
+
*/
|
|
27
|
+
unmount(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Set callbacks
|
|
30
|
+
*/
|
|
31
|
+
setCallbacks(callbacks: {
|
|
32
|
+
onStart?: () => void;
|
|
33
|
+
onPause?: () => void;
|
|
34
|
+
onResume?: () => void;
|
|
35
|
+
onStop?: () => void;
|
|
36
|
+
onScreenshot?: () => void;
|
|
37
|
+
onRestart?: () => void;
|
|
38
|
+
getElapsedTime?: () => number;
|
|
39
|
+
}): void;
|
|
40
|
+
/**
|
|
41
|
+
* Update state
|
|
42
|
+
*/
|
|
43
|
+
setState(state: RecorderState): void;
|
|
44
|
+
/**
|
|
45
|
+
* Set recording result
|
|
46
|
+
*/
|
|
47
|
+
setResult(result: RecordingResult): void;
|
|
48
|
+
/**
|
|
49
|
+
* Reset to idle state (for restart)
|
|
50
|
+
*/
|
|
51
|
+
reset(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Start time updates
|
|
54
|
+
*/
|
|
55
|
+
private startTimeUpdates;
|
|
56
|
+
/**
|
|
57
|
+
* Stop time updates
|
|
58
|
+
*/
|
|
59
|
+
private stopTimeUpdates;
|
|
60
|
+
/**
|
|
61
|
+
* Render the toolbar
|
|
62
|
+
*/
|
|
63
|
+
private render;
|
|
64
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Uploader, RecordingMetadata, SessionToken, EventChunk, ScreenshotMarker, RecordingResult } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Mock uploader that logs to console
|
|
4
|
+
* Will be replaced with real API implementation later
|
|
5
|
+
*/
|
|
6
|
+
export declare class MockUploader implements Uploader {
|
|
7
|
+
private recordingId;
|
|
8
|
+
private sessionToken;
|
|
9
|
+
private metadata;
|
|
10
|
+
private chunks;
|
|
11
|
+
private screenshotMarkers;
|
|
12
|
+
start(metadata: RecordingMetadata): Promise<SessionToken>;
|
|
13
|
+
uploadChunk(chunk: EventChunk): Promise<void>;
|
|
14
|
+
complete(duration: number, _eventCount: number, screenshotMarkers: ScreenshotMarker[]): Promise<RecordingResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Get all recorded data (for testing/debugging)
|
|
17
|
+
*/
|
|
18
|
+
getData(): {
|
|
19
|
+
metadata: RecordingMetadata | null;
|
|
20
|
+
chunks: EventChunk[];
|
|
21
|
+
screenshotMarkers: ScreenshotMarker[];
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Real API uploader
|
|
26
|
+
*/
|
|
27
|
+
export declare class ApiUploader implements Uploader {
|
|
28
|
+
private apiEndpoint;
|
|
29
|
+
private apiKey;
|
|
30
|
+
private sessionToken;
|
|
31
|
+
private recordingId;
|
|
32
|
+
constructor(apiEndpoint: string, apiKey: string);
|
|
33
|
+
start(metadata: RecordingMetadata): Promise<SessionToken>;
|
|
34
|
+
uploadChunk(chunk: EventChunk): Promise<void>;
|
|
35
|
+
complete(duration: number, eventCount: number, screenshotMarkers: ScreenshotMarker[]): Promise<RecordingResult>;
|
|
36
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bworlds/launchkit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Launch kit for builders - demo recording, feedback tools, and more",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/launchkit.cjs",
|
|
7
|
+
"module": "./dist/launchkit.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/launchkit.js",
|
|
13
|
+
"require": "./dist/launchkit.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite build --watch",
|
|
21
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
22
|
+
"lint": "eslint src --max-warnings=0",
|
|
23
|
+
"type-check": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"preact": "^10.25.4",
|
|
27
|
+
"rrweb": "^2.0.0-alpha.18"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@eslint/js": "^9.28.0",
|
|
31
|
+
"@preact/preset-vite": "^2.9.4",
|
|
32
|
+
"@types/node": "^22.15.21",
|
|
33
|
+
"eslint": "^9.28.0",
|
|
34
|
+
"prettier": "^3.5.3",
|
|
35
|
+
"typescript": "^5.8.3",
|
|
36
|
+
"typescript-eslint": "^8.33.0",
|
|
37
|
+
"vite": "^6.3.5",
|
|
38
|
+
"vite-plugin-dts": "^4.5.4"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"launchkit",
|
|
43
|
+
"recording",
|
|
44
|
+
"demo",
|
|
45
|
+
"rrweb",
|
|
46
|
+
"session-replay",
|
|
47
|
+
"bworlds",
|
|
48
|
+
"feedback"
|
|
49
|
+
],
|
|
50
|
+
"author": "BWorlds",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "https://github.com/vulk-corp/bworlds.git",
|
|
58
|
+
"directory": "packages/launchkit"
|
|
59
|
+
}
|
|
60
|
+
}
|