@brainfish-ai/web-tracker 0.0.4-aplha.19 → 0.0.4-beta.1

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 (58) hide show
  1. package/dist/index.cjs.js +30 -0
  2. package/dist/index.cjs.js.gz +0 -0
  3. package/dist/index.cjs.js.map +1 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +2839 -967
  6. package/dist/index.js.gz +0 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/src/common/isLocal.d.ts +1 -0
  9. package/dist/src/index.d.ts +24 -0
  10. package/dist/src/record/eventBuffer.d.ts +7 -0
  11. package/dist/src/record/index.d.ts +16 -0
  12. package/dist/src/record/recorder.d.ts +41 -0
  13. package/dist/src/record/recordingManager.d.ts +17 -0
  14. package/dist/src/record/sender.d.ts +11 -0
  15. package/dist/src/record/stasher.d.ts +23 -0
  16. package/dist/src/record/timer.d.ts +11 -0
  17. package/dist/src/redact/built-ins/NameRedactor.d.ts +6 -0
  18. package/dist/src/redact/built-ins/SimpleRegexpRedactor.d.ts +10 -0
  19. package/dist/src/redact/built-ins/simple-regexp-patterns.d.ts +12 -0
  20. package/dist/src/redact/built-ins/well-known-names.d.ts +2 -0
  21. package/dist/src/redact/composition.d.ts +2 -0
  22. package/dist/src/redact/index.d.ts +10 -0
  23. package/{src/redact/types.ts → dist/src/redact/types.d.ts} +15 -22
  24. package/dist/src/redact/utils.d.ts +4 -0
  25. package/dist/src/screenshot/capture.d.ts +2 -0
  26. package/dist/src/screenshot/redact.d.ts +3 -0
  27. package/dist/src/screenshot/snapshot.d.ts +2 -0
  28. package/dist/src/setupTests.d.ts +0 -0
  29. package/dist/src/tracker.d.ts +9 -0
  30. package/dist/tracker.cjs.js +2 -0
  31. package/dist/tracker.cjs.js.map +1 -0
  32. package/dist/tracker.d.ts +1 -0
  33. package/dist/tracker.js +15 -0
  34. package/dist/tracker.js.map +1 -0
  35. package/package.json +10 -6
  36. package/index.ts +0 -2
  37. package/postcss.config.cjs +0 -6
  38. package/set-version.js +0 -13
  39. package/src/index.ts +0 -192
  40. package/src/redact/built-ins/NameRedactor.ts +0 -41
  41. package/src/redact/built-ins/SimpleRegexpRedactor.ts +0 -22
  42. package/src/redact/built-ins/simple-regexp-patterns.ts +0 -20
  43. package/src/redact/built-ins/well-known-names.ts +0 -11543
  44. package/src/redact/composition.ts +0 -54
  45. package/src/redact/index.ts +0 -21
  46. package/src/redact/utils.ts +0 -15
  47. package/src/screenshot/capture.test.ts +0 -83
  48. package/src/screenshot/capture.ts +0 -51
  49. package/src/screenshot/redact.test.ts +0 -74
  50. package/src/screenshot/redact.ts +0 -57
  51. package/src/screenshot/snapshot.ts +0 -35
  52. package/src/setupTests.ts +0 -1
  53. package/src/tracker.ts +0 -31
  54. package/src/types.d.ts +0 -31
  55. package/src/vite-env.d.ts +0 -7
  56. package/tailwind.config.cjs +0 -9
  57. package/tsconfig.json +0 -16
  58. package/vite.config.ts +0 -55
@@ -1,54 +0,0 @@
1
- import { NameRedactor } from './built-ins/NameRedactor';
2
- import * as simpleRegexpBuiltIns from './built-ins/simple-regexp-patterns';
3
- import { SimpleRegexpRedactor } from './built-ins/SimpleRegexpRedactor';
4
- import {
5
- AsyncCustomRedactorConfig,
6
- IAsyncRedactor,
7
- CompositeRedactorOptions,
8
- SyncCustomRedactorConfig,
9
- ISyncRedactor,
10
- } from './types';
11
- import { isSimpleRegexpCustomRedactorConfig, snakeCase } from './utils';
12
-
13
- function normalizeCustomRedactorConfig(redactorConfig: any) {
14
- return isSimpleRegexpCustomRedactorConfig(redactorConfig)
15
- ? new SimpleRegexpRedactor({
16
- regexpPattern: redactorConfig.regexpPattern,
17
- replaceWith: redactorConfig.replaceWith,
18
- })
19
- : redactorConfig;
20
- }
21
-
22
- export function composeChildRedactors<T extends AsyncCustomRedactorConfig>(opts: CompositeRedactorOptions<T> = {}) {
23
- const childRedactors: T extends SyncCustomRedactorConfig
24
- ? Array<ISyncRedactor>
25
- : Array<IAsyncRedactor | ISyncRedactor> = [] as any;
26
-
27
- if (opts.customRedactors && opts.customRedactors.before) {
28
- opts.customRedactors.before.map(normalizeCustomRedactorConfig).forEach((redactor) => childRedactors.push(redactor));
29
- }
30
-
31
- for (const regexpName of Object.keys(simpleRegexpBuiltIns)) {
32
- if (
33
- !opts.builtInRedactors ||
34
- !(opts.builtInRedactors as any)[regexpName] ||
35
- (opts.builtInRedactors as any)[regexpName].enabled !== false
36
- ) {
37
- childRedactors.push(
38
- new SimpleRegexpRedactor({
39
- regexpPattern: (simpleRegexpBuiltIns as any)[regexpName],
40
- replaceWith: opts.globalReplaceWith || snakeCase(regexpName).toUpperCase(),
41
- })
42
- );
43
- }
44
- }
45
-
46
- if (!opts.builtInRedactors || !opts.builtInRedactors.names || opts.builtInRedactors.names.enabled !== false) {
47
- childRedactors.push(new NameRedactor(opts.globalReplaceWith));
48
- }
49
-
50
- if (opts.customRedactors && opts.customRedactors.after) {
51
- opts.customRedactors.after.map(normalizeCustomRedactorConfig).forEach((redactor) => childRedactors.push(redactor));
52
- }
53
- return childRedactors;
54
- }
@@ -1,21 +0,0 @@
1
- import { composeChildRedactors } from './composition';
2
- import { CompositeRedactorOptions, ISyncRedactor, SyncCustomRedactorConfig } from './types';
3
-
4
- /** @public */
5
- export interface SyncCompositeRedactorOptions extends CompositeRedactorOptions<SyncCustomRedactorConfig> {}
6
-
7
- /** @public */
8
- export class SyncRedactor implements ISyncRedactor {
9
- private childRedactors: ISyncRedactor[] = [];
10
-
11
- constructor(opts?: SyncCompositeRedactorOptions) {
12
- this.childRedactors = composeChildRedactors(opts);
13
- }
14
-
15
- redact = (textToRedact: string) => {
16
- for (const redactor of this.childRedactors) {
17
- textToRedact = redactor.redact(textToRedact);
18
- }
19
- return textToRedact;
20
- };
21
- }
@@ -1,15 +0,0 @@
1
- import { SimpleRegexpCustomRedactorConfig, AsyncCustomRedactorConfig, ISyncRedactor, IRedactor } from './types';
2
-
3
- export function isSimpleRegexpCustomRedactorConfig(
4
- redactor: AsyncCustomRedactorConfig
5
- ): redactor is SimpleRegexpCustomRedactorConfig {
6
- return typeof (redactor as SimpleRegexpCustomRedactorConfig).regexpPattern !== 'undefined';
7
- }
8
-
9
- export function isSyncRedactor(redactor: IRedactor): redactor is ISyncRedactor {
10
- return typeof (redactor as ISyncRedactor).redact === 'function';
11
- }
12
-
13
- export function snakeCase(str: string) {
14
- return str.replace(/[A-Z]/g, (letter, index) => (index !== 0 ? `_${letter.toLowerCase()}` : letter.toLowerCase()));
15
- }
@@ -1,83 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { captureScreenshot } from './capture';
3
- import { toPng } from 'html-to-image';
4
-
5
- // Mock the html-to-image module
6
- vi.mock('html-to-image', () => ({
7
- toPng: vi.fn(),
8
- }));
9
-
10
- describe('captureScreenshot', () => {
11
- let mockDomBody: HTMLElement;
12
-
13
- beforeEach(() => {
14
- // Create a mock DOM body
15
- mockDomBody = document.createElement('div');
16
- document.body.appendChild(mockDomBody);
17
-
18
- // Mock document properties
19
- vi.spyOn(document.documentElement, 'scrollHeight', 'get').mockReturnValue(1000);
20
- vi.spyOn(document.documentElement, 'scrollWidth', 'get').mockReturnValue(800);
21
-
22
- // Mock styleSheets
23
- Object.defineProperty(document, 'styleSheets', {
24
- value: [
25
- {
26
- cssRules: [
27
- { cssText: 'body { background-color: white; }' },
28
- { cssText: 'h1 { color: black; }' },
29
- ],
30
- },
31
- ],
32
- configurable: true,
33
- });
34
-
35
- // Mock toPng function
36
- vi.mocked(toPng).mockResolvedValue('mock-image-data');
37
- });
38
-
39
- afterEach(() => {
40
- document.body.removeChild(mockDomBody);
41
- vi.restoreAllMocks();
42
- });
43
-
44
- it('should capture a screenshot of the entire page', async () => {
45
- const result = await captureScreenshot(mockDomBody);
46
-
47
- expect(result).toBe('mock-image-data');
48
- expect(toPng).toHaveBeenCalledWith(mockDomBody, expect.objectContaining({
49
- quality: 0.7,
50
- width: 800,
51
- height: 1000,
52
- style: { transform: 'scale(1)' },
53
- skipFonts: true,
54
- cacheBust: true,
55
- backgroundColor: 'white',
56
- fetchRequestInit: { mode: 'cors' },
57
- }));
58
- });
59
-
60
- it('should handle errors when accessing stylesheet rules', async () => {
61
- const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
62
-
63
- // Mock styleSheets to throw an error
64
- Object.defineProperty(document, 'styleSheets', {
65
- get: () => ({
66
- [Symbol.iterator]: function* () {
67
- yield {
68
- cssRules: {
69
- [Symbol.iterator]: function* () {
70
- throw new Error('Access denied');
71
- },
72
- },
73
- };
74
- },
75
- }),
76
- configurable: true,
77
- });
78
-
79
- await captureScreenshot(mockDomBody);
80
-
81
- expect(consoleLogSpy).toHaveBeenCalledWith('Error accessing stylesheet rules:', expect.any(Error));
82
- });
83
- });
@@ -1,51 +0,0 @@
1
- import { toPng } from 'html-to-image';
2
-
3
- // Function to capture a screenshot of the entire page
4
- async function captureScreenshot(domBody: HTMLElement): Promise<string> {
5
- const height = document.documentElement.scrollHeight;
6
- const width = document.documentElement.scrollWidth;
7
-
8
- // Collect all stylesheet rules
9
- const styles = Array.from(document.styleSheets)
10
- .map(styleSheet => {
11
- try {
12
- return Array.from(styleSheet.cssRules)
13
- .map(rule => rule.cssText)
14
- .join('');
15
- } catch (e) {
16
- console.log('Error accessing stylesheet rules:', e);
17
- return '';
18
- }
19
- })
20
- .join('\n');
21
-
22
- // Create a new style element with all the rules
23
- const styleElement = document.createElement('style');
24
- styleElement.textContent = styles;
25
- domBody.appendChild(styleElement);
26
-
27
- // Capture the screenshot
28
- const image = await toPng(domBody, {
29
- quality: 0.7, // Set quality to 0.7
30
- width, // Set width to the scroll width
31
- height, // Set height to the scroll height
32
- style: {
33
- transform: 'scale(1)', // Set scale to 1 to avoid scaling
34
- },
35
- skipFonts: true, // Avoid embedding web fonts to bypass SecurityError
36
- cacheBust: true, // Prevent caching
37
- backgroundColor: 'white', // Set background color to white
38
- fetchRequestInit: {
39
- mode: 'cors', // Enable CORS
40
- },
41
- })
42
-
43
- // Remove the style element
44
- domBody.removeChild(styleElement);
45
-
46
- return image;
47
- }
48
-
49
- export { captureScreenshot };
50
-
51
-
@@ -1,74 +0,0 @@
1
- import { redactTextContent, redactPIIInDOM } from './redact';
2
- import { describe, it, expect, beforeEach } from 'vitest';
3
-
4
- describe('redactTextContent', () => {
5
- it('should redact email addresses', () => {
6
- const text = 'Contact me at john.doe@example.com';
7
- const redacted = redactTextContent(text);
8
- expect(redacted).toBe('Contact me at EMAIL_ADDRESS');
9
- });
10
-
11
- it('should redact phone numbers', () => {
12
- const text = 'My phone number is 123-456-7890';
13
- const redacted = redactTextContent(text);
14
- expect(redacted).toBe('My phone number is PHONE_NUMBER');
15
- });
16
-
17
- it('should redact SSNs', () => {
18
- const text = 'SSN: 123-45-6789';
19
- const redacted = redactTextContent(text);
20
- expect(redacted).toBe('SSN: US_SOCIAL_SECURITY_NUMBER');
21
- });
22
-
23
- it('should redact passport numbers', () => {
24
- const text = 'Passport number: A1234567';
25
- const redacted = redactTextContent(text);
26
- expect(redacted).toBe('Passport number: PASSPORT_NUMBER');
27
- });
28
-
29
- it('should return the original text if no PII is found', () => {
30
- const text = 'No PII here';
31
- const redacted = redactTextContent(text);
32
- expect(redacted).toBe(text);
33
- });
34
-
35
- it('should redact credit card numbers', () => {
36
- const text = 'Credit Card: 1234-5678-1234-5678';
37
- const redacted = redactTextContent(text);
38
- expect(redacted).toBe('Credit Card: CREDIT_CARD_NUMBER');
39
- });
40
- });
41
-
42
- describe('redactPIIInDOM', () => {
43
- beforeEach(() => {
44
- document.body.innerHTML = `
45
- <div id="content">
46
- <p>Email: john.doe@example.com</p>
47
- <p>Phone: 123-456-7890</p>
48
- <p>SSN: 123-45-6789</p>
49
- <p>Passport: A1234567</p>
50
- <p>No PII here</p>
51
- </div>
52
- `;
53
- });
54
-
55
- it('should redact PII in text nodes', () => {
56
- const element = document.getElementById('content') as Element;
57
- redactPIIInDOM(element);
58
-
59
- expect(element.innerHTML).toContain('Email: EMAIL_ADDRESS');
60
- expect(element.innerHTML).toContain('Phone: PHONE_NUMBER');
61
- expect(element.innerHTML).toContain('SSN: US_SOCIAL_SECURITY_NUMBER');
62
- expect(element.innerHTML).toContain('Passport: PASSPORT_NUMBER');
63
- expect(element.innerHTML).toContain('No PII here');
64
- });
65
-
66
- it('should not change text content without PII', () => {
67
- const element = document.querySelector('p:last-child') as Element;
68
- const originalText = element.textContent;
69
-
70
- redactPIIInDOM(element);
71
-
72
- expect(element.textContent).toBe(originalText);
73
- });
74
- });
@@ -1,57 +0,0 @@
1
- import { SyncRedactor } from "./../redact/index";
2
-
3
- const customRedactors = {
4
- before: [
5
- {
6
- regexpPattern: /\b[A-Z]\d{7}\b/g,
7
- replaceWith: 'PASSPORT_NUMBER'
8
- },
9
- {
10
- regexpPattern: /(?<=password: )(.*)/gi,
11
- replaceWith: '[REDACTED]'
12
- }
13
- ],
14
- };
15
-
16
- // Function to redact PII in text content
17
- function redactTextContent(text: string): string {
18
- const redactor = new SyncRedactor({
19
- builtInRedactors: {
20
- names: {
21
- replaceWith: 'ANONYMOUS_PERSON'
22
- }
23
- },
24
- customRedactors
25
- });
26
- return redactor.redact(text);
27
- }
28
-
29
- // Function to recursively find and redact PII in text nodes
30
- function redactPIIInDOM(element: Element) {
31
- const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);
32
- let currentNode;
33
-
34
- while ((currentNode = treeWalker.nextNode())) {
35
- const originalText = currentNode.nodeValue as string;
36
- const redactedText = redactTextContent(originalText);
37
-
38
- if (originalText !== redactedText) {
39
- currentNode.nodeValue = redactedText;
40
- }
41
- }
42
-
43
- // Redact PII in input fields
44
- redactPIIInInputs(element);
45
- }
46
-
47
- // Function to redact PII from passwords in input fields
48
- function redactPIIInInputs(element: Element) {
49
- const inputs = Array.from(element.querySelectorAll('input[type="password"]'));
50
- inputs.forEach(input => {
51
- const passwordInput = input as HTMLInputElement;
52
- passwordInput.value = '[REDACTED]';
53
- });
54
- }
55
-
56
- export { redactPIIInDOM, redactTextContent };
57
-
@@ -1,35 +0,0 @@
1
- import { captureScreenshot } from "./capture";
2
- import { redactPIIInDOM } from "./redact";
3
-
4
- async function screenshot() {
5
- try {
6
- // Wait for page to load
7
- await new Promise((resolve) => {
8
- if (document.readyState === 'complete') {
9
- resolve(null);
10
- } else {
11
- window.addEventListener('load', resolve);
12
- }
13
- });
14
-
15
- // Clone the document body to avoid modifying the original
16
- const clonedBody = document.body.cloneNode(true) as HTMLElement;
17
-
18
- // Redact PII in text content
19
- redactPIIInDOM(clonedBody);
20
-
21
- // Capture the screenshot
22
- const imageDataUrl = await captureScreenshot(clonedBody);
23
-
24
- // if local env, print the imageDataUrl to the console
25
- if (window.location.hostname === 'localhost') {
26
- console.log(imageDataUrl);
27
- }
28
-
29
- return imageDataUrl;
30
- } catch (error) {
31
- console.error('An error occurred:', error);
32
- }
33
- }
34
-
35
- export { screenshot };
package/src/setupTests.ts DELETED
@@ -1 +0,0 @@
1
- // Setup for Jest tests
package/src/tracker.ts DELETED
@@ -1,31 +0,0 @@
1
- import { Tracker } from './index';
2
-
3
- declare global {
4
- interface Window {
5
- BrainfishAnalytics: {
6
- q?: [string, ...any[]];
7
- (method: string, ...args: any[]): void;
8
- };
9
- }
10
- }
11
-
12
- ((window) => {
13
- if (window.BrainfishAnalytics && 'q' in window.BrainfishAnalytics) {
14
- const queue = window.BrainfishAnalytics.q || [];
15
- const tracker = new Tracker(queue.shift()[1]) as any;
16
- queue.forEach((item) => {
17
- if (item[0] in tracker) {
18
- tracker[item[0]](...item.slice(1));
19
- }
20
- });
21
-
22
- window.BrainfishAnalytics = (t, ...args) => {
23
- const fn = tracker[t] ? tracker[t].bind(tracker) : undefined;
24
- if (typeof fn === 'function') {
25
- fn(...args);
26
- } else {
27
- console.warn(`Method ${t} does not exist on BrainfishAnalytics`);
28
- }
29
- };
30
- }
31
- })(window);
package/src/types.d.ts DELETED
@@ -1,31 +0,0 @@
1
- import type { Tracker, TrackerOptions, TrackProperties } from "./index";
2
-
3
- type ExposedMethodsNames =
4
- | 'track'
5
- | 'identify'
6
- | 'setGlobalProperties'
7
- | 'alias'
8
- | 'increment'
9
- | 'decrement'
10
- | 'clear';
11
-
12
- export type ExposedMethods = {
13
- [K in ExposedMethodsNames]: Tracker[K] extends (...args: any[]) => any
14
- ? [K, ...Parameters<Tracker[K]>]
15
- : never;
16
- }[ExposedMethodsNames];
17
-
18
- export type TrackerMethodNames = ExposedMethodsNames | 'init' | 'screenView';
19
- export type TrackerMethods =
20
- | ExposedMethods
21
- | ['init', TrackerOptions]
22
- | ['screenView', string | TrackProperties, TrackProperties];
23
-
24
- declare global {
25
- interface Window {
26
- BrainfishAnalytics: {
27
- q?: [string, ...any[]];
28
- (method: string, ...args: any[]): void;
29
- };
30
- }
31
- }
package/src/vite-env.d.ts DELETED
@@ -1,7 +0,0 @@
1
- interface ImportMetaEnv {
2
- readonly PACKAGE_VERSION: string;
3
- }
4
-
5
- interface ImportMeta {
6
- readonly env: ImportMetaEnv;
7
- }
@@ -1,9 +0,0 @@
1
- const { withAnimations } = require('animated-tailwindcss')
2
-
3
- module.exports = withAnimations({
4
- content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
5
- theme: {
6
- extend: {}
7
- },
8
- plugins: []
9
- });
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "strict": true,
4
- "target": "ESNext",
5
- "module": "ESNext",
6
- "moduleResolution": "node",
7
- "allowSyntheticDefaultImports": true,
8
- "esModuleInterop": true,
9
- "types": ["vite/client", "jest"],
10
- "baseUrl": ".",
11
- "declaration": true,
12
- "resolveJsonModule": true
13
- },
14
- "include": ["src", "./package.json"],
15
- "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts", "src/**/tests/**"]
16
- }
package/vite.config.ts DELETED
@@ -1,55 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import { terser } from 'rollup-plugin-terser';
3
- import dts from 'vite-plugin-dts';
4
- import compression from 'vite-plugin-compression';
5
- import { visualizer } from 'rollup-plugin-visualizer';
6
- import tsconfigPaths from 'vite-tsconfig-paths';
7
- import { version } from './package.json';
8
-
9
- export default defineConfig({
10
- plugins: [
11
- tsconfigPaths(),
12
- dts({
13
- tsconfigPath: './tsconfig.json',
14
- insertTypesEntry: true,
15
- include: ['src'],
16
- }),
17
- compression(),
18
- visualizer(),
19
- ],
20
- build: {
21
- target: 'es2015',
22
- sourcemap: true,
23
- lib: {
24
- entry: 'index.ts',
25
- name: 'BrainfishAnalytics',
26
- formats: ['es', 'cjs'],
27
- fileName: (format, entry) => `${entry}${format === 'es' ? '' : '.cjs'}.js`,
28
- },
29
- minify: true,
30
- terserOptions: {
31
- compress: {
32
- drop_console: true,
33
- drop_debugger: true,
34
- },
35
- },
36
- rollupOptions: {
37
- treeshake: true,
38
- plugins: [terser()],
39
- external: ['@google-cloud/dlp'],
40
- output: {
41
- inlineDynamicImports: true, // Ensures all imports are inlined into the main bundle
42
- manualChunks: undefined, // Disables code splitting
43
- },
44
- },
45
- },
46
- test: {
47
- environment: 'jsdom',
48
- },
49
- define: {
50
- 'import.meta.env.PACKAGE_VERSION': JSON.stringify(version),
51
- },
52
- optimizeDeps: {
53
- include: ['@brainfish-ai/tracker-sdk'],
54
- },
55
- });