@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.
@@ -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 };
@@ -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,6 @@
1
+ /**
2
+ * Inject toolbar styles into the document
3
+ * This ensures styles work even when loaded via UMD script tag
4
+ */
5
+ export declare function injectStyles(): void;
6
+ export declare function removeStyles(): void;
@@ -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
+ }