diff --git a/packages/integration-tests/utils/helpers.ts b/packages/integration-tests/utils/helpers.ts
index afcd5c74683a..443e3e0e57af 100644
--- a/packages/integration-tests/utils/helpers.ts
+++ b/packages/integration-tests/utils/helpers.ts
@@ -3,7 +3,7 @@ import type { EnvelopeItemType, Event, EventEnvelopeHeaders } from '@sentry/type
const envelopeUrlRegex = /\.sentry\.io\/api\/\d+\/envelope\//;
-export const envelopeRequestParser = (request: Request | null): Event => {
+export const envelopeParser = (request: Request | null): unknown[] => {
// https://develop.sentry.dev/sdk/envelopes/
const envelope = request?.postData() || '';
@@ -14,7 +14,11 @@ export const envelopeRequestParser = (request: Request | null): Event => {
} catch (error) {
return line;
}
- })[2];
+ });
+};
+
+export const envelopeRequestParser = (request: Request | null): Event => {
+ return envelopeParser(request)[2] as Event;
};
export const envelopeHeaderRequestParser = (request: Request | null): EventEnvelopeHeaders => {
diff --git a/packages/replay/README.md b/packages/replay/README.md
index 92babf3fe819..c65700a1389c 100644
--- a/packages/replay/README.md
+++ b/packages/replay/README.md
@@ -83,7 +83,7 @@ Sentry.setUser({ email: "jane.doe@example.com" });
### Stopping & re-starting replays
-You can manually stop/re-start Replay capture via `.stop()` & `.start()`:
+Replay recording only starts when it is included in the `integrations` array when calling `Sentry.init` or calling `addIntegration` from the a Sentry client instance. To stop recording you can call the `stop()`.
```js
const replay = new Replay();
@@ -91,9 +91,12 @@ Sentry.init({
integrations: [replay]
});
-// sometime later
-replay.stop();
-replay.start();
+const client = getClient();
+
+// Add replay integration, will start recoring
+client.addIntegration(replay);
+
+replay.stop(); // Stop recording
```
## Loading Replay as a CDN Bundle
@@ -185,19 +188,29 @@ The following options can be configured as options to the integration, in `new R
The following options can be configured as options to the integration, in `new Replay({})`:
-| key | type | default | description |
-| ---------------- | ------------------------ | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| maskAllText | boolean | `true` | Mask _all_ text content. Will pass text content through `maskTextFn` before sending to server. |
-| blockAllMedia | boolean | `true` | Block _all_ media elements (`img, svg, video, object, picture, embed, map, audio`)
-| maskTextFn | (text: string) => string | `(text) => '*'.repeat(text.length)` | Function to customize how text content is masked before sending to server. By default, masks text with `*`. |
-| maskAllInputs | boolean | `true` | Mask values of `` elements. Passes input values through `maskInputFn` before sending to server. |
-| maskInputOptions | Record | `{ password: true }` | Customize which inputs `type` to mask.
Available `` types: `color, date, datetime-local, email, month, number, range, search, tel, text, time, url, week, textarea, select, password`. |
-| maskInputFn | (text: string) => string | `(text) => '*'.repeat(text.length)` | Function to customize how form input values are masked before sending to server. By default, masks values with `*`. |
-| blockClass | string \| RegExp | `'sentry-block'` | Redact all elements that match the class name. See [privacy](#blocking) section for an example. |
-| blockSelector | string | `'[data-sentry-block]'` | Redact all elements that match the DOM selector. See [privacy](#blocking) section for an example. |
-| ignoreClass | string \| RegExp | `'sentry-ignore'` | Ignores all events on the matching input field. See [privacy](#ignoring) section for an example. |
-| maskTextClass | string \| RegExp | `'sentry-mask'` | Mask all elements that match the class name. See [privacy](#masking) section for an example. |
-| maskTextSelector | string | `undefined` | Mask all elements that match the given DOM selector. See [privacy](#masking) section for an example. |
+| key | type | default | description |
+| ---------------- | ------------------------ | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| maskAllText | boolean | `true` | Mask _all_ text content. Will pass text content through `maskTextFn` before sending to server. |
+| maskAllInputs | boolean | `true` | Mask values of `` elements. Passes input values through `maskInputFn` before sending to server. |
+| blockAllMedia | boolean | `true` | Block _all_ media elements (`img, svg, video, object, picture, embed, map, audio`)
+| maskTextFn | (text: string) => string | `(text) => '*'.repeat(text.length)` | Function to customize how text content is masked before sending to server. By default, masks text with `*`. |
+| block | Array | `.sentry-block, [data-sentry-block]` | Redact any elements that match the DOM selectors. See [privacy](#blocking) section for an example. |
+| unblock | Array | `.sentry-unblock, [data-sentry-unblock]`| Do not redact any elements that match the DOM selectors. Useful when using `blockAllMedia`. See [privacy](#blocking) section for an example. |
+| mask | Array | `.sentry-mask, [data-sentry-mask]` | Mask all elements that match the given DOM selectors. See [privacy](#masking) section for an example. |
+| unmask | Array | `.sentry-unmask, [data-sentry-unmask]` | Unmask all elements that match the given DOM selectors. Useful when using `maskAllText`. See [privacy](#masking) section for an example. |
+| ignore | Array | `.sentry-ignore, [data-sentry-ignore]` | Ignores all events on the matching input fields. See [privacy](#ignoring) section for an example. |
+
+#### Deprecated options
+In order to streamline our privacy options, the following have been deprecated in favor for the respective options above.
+
+| deprecated key | replaced by | description |
+| ---------------- | ----------- | ----------- |
+| maskInputOptions | mask | Use CSS selectors in `mask` in order to mask all inputs of a certain type. For example, `input[type="address"]` |
+| blockSelector | block | The selector(s) can be moved directly in the `block` array. |
+| blockClass | block | Convert the class name to a CSS selector and add to `block` array. For example, `first-name` becomes `.first-name`. Regexes can be moved as-is. |
+| maskClass | mask | Convert the class name to a CSS selector and add to `mask` array. For example, `first-name` becomes `.first-name`. Regexes can be moved as-is. |
+| maskSelector | mask | The selector(s) can be moved directly in the `mask` array. |
+| ignoreClass | ignore | Convert the class name to a CSS selector and add to `ignore` array. For example, `first-name` becomes `.first-name`. Regexes can be moved as-is. |
## Privacy
There are several ways to deal with PII. By default, the integration will mask all text content with `*` and block all media elements (`img, svg, video, object, picture, embed, map, audio`). This can be disabled by setting `maskAllText` to `false`. It is also possible to add the following CSS classes to specific DOM elements to prevent recording its contents: `sentry-block`, `sentry-ignore`, and `sentry-mask`. The following sections will show examples of how content is handled by the differing methods.
diff --git a/packages/replay/src/integration.ts b/packages/replay/src/integration.ts
index daf676c82efd..a7753ac44cb5 100644
--- a/packages/replay/src/integration.ts
+++ b/packages/replay/src/integration.ts
@@ -4,6 +4,7 @@ import type { BrowserClientReplayOptions, Integration } from '@sentry/types';
import { DEFAULT_FLUSH_MAX_DELAY, DEFAULT_FLUSH_MIN_DELAY, MASK_ALL_TEXT_SELECTOR } from './constants';
import { ReplayContainer } from './replay';
import type { RecordingOptions, ReplayConfiguration, ReplayPluginOptions } from './types';
+import { getPrivacyOptions } from './util/getPrivacyOptions';
import { isBrowser } from './util/isBrowser';
const MEDIA_SELECTORS = 'img,image,svg,path,rect,area,video,object,picture,embed,map,audio';
@@ -38,27 +39,57 @@ export class Replay implements Integration {
flushMaxDelay = DEFAULT_FLUSH_MAX_DELAY,
stickySession = true,
useCompression = true,
+ _experiments = {},
sessionSampleRate,
errorSampleRate,
maskAllText,
- maskTextSelector,
maskAllInputs = true,
blockAllMedia = true,
- _experiments = {},
- blockClass = 'sentry-block',
- ignoreClass = 'sentry-ignore',
- maskTextClass = 'sentry-mask',
- blockSelector = '[data-sentry-block]',
- ..._recordingOptions
+
+ mask = [],
+ unmask = [],
+ block = [],
+ unblock = [],
+ ignore = [],
+ maskFn,
+
+ // eslint-disable-next-line deprecation/deprecation
+ blockClass,
+ // eslint-disable-next-line deprecation/deprecation
+ blockSelector,
+ // eslint-disable-next-line deprecation/deprecation
+ maskTextClass,
+ // eslint-disable-next-line deprecation/deprecation
+ maskTextSelector,
+ // eslint-disable-next-line deprecation/deprecation
+ ignoreClass,
}: ReplayConfiguration = {}) {
this._recordingOptions = {
maskAllInputs,
- blockClass,
- ignoreClass,
- maskTextClass,
- maskTextSelector,
- blockSelector,
- ..._recordingOptions,
+ maskTextFn: maskFn,
+ maskInputFn: maskFn,
+
+ ...getPrivacyOptions({
+ mask,
+ unmask,
+ block,
+ unblock,
+ ignore,
+ blockClass,
+ blockSelector,
+ maskTextClass,
+ maskTextSelector,
+ ignoreClass,
+ }),
+
+ // Our defaults
+ slimDOMOptions: 'all',
+ inlineStylesheet: true,
+ // Disable inline images as it will increase segment/replay size
+ inlineImages: false,
+ // collect fonts, but be aware that `sentry.io` needs to be an allowed
+ // origin for playback
+ collectFonts: true,
};
this._options = {
diff --git a/packages/replay/src/types.ts b/packages/replay/src/types.ts
index adb676617136..a16b3547e0ec 100644
--- a/packages/replay/src/types.ts
+++ b/packages/replay/src/types.ts
@@ -113,10 +113,70 @@ export interface ReplayPluginOptions extends SessionOptions {
}>;
}
+export interface ReplayIntegrationPrivacyOptions {
+ /**
+ * Mask text content for elements that match the CSS selectors in the list.
+ */
+ mask?: string[];
+
+ /**
+ * Unmask text content for elements that match the CSS selectors in the list.
+ */
+ unmask?: string[];
+
+ /**
+ * Block elements that match the CSS selectors in the list. Blocking replaces
+ * the element with an empty placeholder with the same dimensions.
+ */
+ block?: string[];
+
+ /**
+ * Unblock elements that match the CSS selectors in the list. This is useful when using `blockAllMedia`.
+ */
+ unblock?: string[];
+
+ /**
+ * Ignore input events for elements that match the CSS selectors in the list.
+ */
+ ignore?: string[];
+
+ /**
+ * A callback function to customize how your text is masked.
+ */
+ maskFn?: Pick;
+}
+
// These are optional for ReplayPluginOptions because the plugin sets default values
type OptionalReplayPluginOptions = Partial;
-export interface ReplayConfiguration extends OptionalReplayPluginOptions, RecordingOptions {}
+export interface DeprecatedPrivacyOptions {
+ /**
+ * @deprecated Use `block` which accepts an array of CSS selectors
+ */
+ blockSelector?: RecordingOptions['blockSelector'];
+ /**
+ * @deprecated Use `block` which accepts an array of CSS selectors
+ */
+ blockClass?: RecordingOptions['blockClass'];
+ /**
+ * @deprecated Use `mask` which accepts an array of CSS selectors
+ */
+ maskTextClass?: RecordingOptions['maskTextClass'];
+ /**
+ * @deprecated Use `mask` which accepts an array of CSS selectors
+ */
+ maskTextSelector?: RecordingOptions['maskTextSelector'];
+ /**
+ * @deprecated Use `ignore` which accepts an array of CSS selectors
+ */
+ ignoreClass?: RecordingOptions['ignoreClass'];
+}
+
+export interface ReplayConfiguration
+ extends ReplayIntegrationPrivacyOptions,
+ OptionalReplayPluginOptions,
+ DeprecatedPrivacyOptions,
+ Pick {}
interface CommonEventContext {
/**
diff --git a/packages/replay/src/types/rrweb.ts b/packages/replay/src/types/rrweb.ts
index 1c6ee198eb33..780c7279765a 100644
--- a/packages/replay/src/types/rrweb.ts
+++ b/packages/replay/src/types/rrweb.ts
@@ -34,5 +34,6 @@ export type recordOptions = {
blockClass?: blockClass;
ignoreClass?: string;
maskTextClass?: maskTextClass;
+ maskTextSelector?: string;
blockSelector?: string;
} & Record;
diff --git a/packages/replay/src/util/getPrivacyOptions.ts b/packages/replay/src/util/getPrivacyOptions.ts
new file mode 100644
index 000000000000..0e5c18ff6345
--- /dev/null
+++ b/packages/replay/src/util/getPrivacyOptions.ts
@@ -0,0 +1,95 @@
+import type { DeprecatedPrivacyOptions, ReplayIntegrationPrivacyOptions } from '../types';
+
+type GetPrivacyOptions = Required> & DeprecatedPrivacyOptions;
+interface GetPrivacyReturn {
+ maskTextSelector: string;
+ unmaskTextSelector: string;
+ maskInputSelector: string;
+ unmaskInputSelector: string;
+ blockSelector: string;
+ unblockSelector: string;
+ ignoreSelector: string;
+
+ blockClass?: RegExp;
+ maskTextClass?: RegExp;
+}
+
+function getOption(
+ selectors: string[],
+ defaultSelectors: string[],
+ deprecatedClassOption?: string | RegExp,
+ deprecatedSelectorOption?: string,
+): string {
+ const deprecatedSelectors = typeof deprecatedSelectorOption === 'string' ? deprecatedSelectorOption.split(',') : [];
+
+ const allSelectors = [
+ ...selectors,
+ // @deprecated
+ ...deprecatedSelectors,
+
+ // sentry defaults
+ ...defaultSelectors,
+ ];
+
+ // @deprecated
+ if (typeof deprecatedClassOption !== 'undefined') {
+ // NOTE: No support for RegExp
+ if (typeof deprecatedClassOption === 'string') {
+ allSelectors.push(`.${deprecatedClassOption}`);
+ }
+
+ // eslint-disable-next-line no-console
+ console.warn(
+ '[Replay] You are using a deprecated configuration item for privacy. Read the documentation on how to use the new privacy configuration.',
+ );
+ }
+
+ return allSelectors.join(',');
+}
+
+/**
+ * Returns privacy related configuration for use in rrweb
+ */
+export function getPrivacyOptions({
+ mask,
+ unmask,
+ block,
+ unblock,
+ ignore,
+
+ // eslint-disable-next-line deprecation/deprecation
+ blockClass,
+ // eslint-disable-next-line deprecation/deprecation
+ blockSelector,
+ // eslint-disable-next-line deprecation/deprecation
+ maskTextClass,
+ // eslint-disable-next-line deprecation/deprecation
+ maskTextSelector,
+ // eslint-disable-next-line deprecation/deprecation
+ ignoreClass,
+}: GetPrivacyOptions): GetPrivacyReturn {
+ const maskSelector = getOption(mask, ['.sentry-mask', '[data-sentry-mask]'], maskTextClass, maskTextSelector);
+ const unmaskSelector = getOption(unmask, ['.sentry-unmask', '[data-sentry-unmask]']);
+
+ const options: GetPrivacyReturn = {
+ // We are making the decision to make text and input selectors the same
+ maskTextSelector: maskSelector,
+ unmaskTextSelector: unmaskSelector,
+ maskInputSelector: maskSelector,
+ unmaskInputSelector: unmaskSelector,
+
+ blockSelector: getOption(block, ['.sentry-block', '[data-sentry-block]'], blockClass, blockSelector),
+ unblockSelector: getOption(unblock, ['.sentry-unblock', '[data-sentry-unblock]']),
+ ignoreSelector: getOption(ignore, ['.sentry-ignore', '[data-sentry-ignore]'], ignoreClass),
+ };
+
+ if (blockClass instanceof RegExp) {
+ options.blockClass = blockClass;
+ }
+
+ if (maskTextClass instanceof RegExp) {
+ options.maskTextClass = maskTextClass;
+ }
+
+ return options;
+}
diff --git a/packages/replay/test/integration/integrationSettings.test.ts b/packages/replay/test/integration/integrationSettings.test.ts
index 6c63839b1c06..9e465641dbae 100644
--- a/packages/replay/test/integration/integrationSettings.test.ts
+++ b/packages/replay/test/integration/integrationSettings.test.ts
@@ -10,14 +10,14 @@ describe('Integration | integrationSettings', () => {
it('sets the correct configuration when `blockAllMedia` is disabled', async () => {
const { replay } = await mockSdk({ replayOptions: { blockAllMedia: false } });
- expect(replay['_recordingOptions'].blockSelector).toBe('[data-sentry-block]');
+ expect(replay['_recordingOptions'].blockSelector).toBe('.sentry-block,[data-sentry-block]');
});
it('sets the correct configuration when `blockSelector` is empty and `blockAllMedia` is enabled', async () => {
const { replay } = await mockSdk({ replayOptions: { blockSelector: '' } });
expect(replay['_recordingOptions'].blockSelector).toMatchInlineSnapshot(
- '"img,image,svg,path,rect,area,video,object,picture,embed,map,audio"',
+ '",.sentry-block,[data-sentry-block],img,image,svg,path,rect,area,video,object,picture,embed,map,audio"',
);
});
@@ -27,7 +27,7 @@ describe('Integration | integrationSettings', () => {
});
expect(replay['_recordingOptions'].blockSelector).toMatchInlineSnapshot(
- '"[data-test-blockSelector],img,image,svg,path,rect,area,video,object,picture,embed,map,audio"',
+ '"[data-test-blockSelector],.sentry-block,[data-sentry-block],img,image,svg,path,rect,area,video,object,picture,embed,map,audio"',
);
});
});
@@ -181,13 +181,13 @@ describe('Integration | integrationSettings', () => {
it('works with false', async () => {
const { replay } = await mockSdk({ replayOptions: { maskAllText: false } });
- expect(replay['_recordingOptions'].maskTextSelector).toBe(undefined);
+ expect(replay['_recordingOptions'].maskTextSelector).toBe('.sentry-mask,[data-sentry-mask]');
});
it('maskTextSelector takes precedence over maskAllText when not specifiying maskAllText:true', async () => {
const { replay } = await mockSdk({ replayOptions: { maskTextSelector: '[custom]' } });
- expect(replay['_recordingOptions'].maskTextSelector).toBe('[custom]');
+ expect(replay['_recordingOptions'].maskTextSelector).toBe('[custom],.sentry-mask,[data-sentry-mask]');
});
it('maskAllText takes precedence over maskTextSelector when specifiying maskAllText:true', async () => {
diff --git a/packages/replay/test/integration/rrweb.test.ts b/packages/replay/test/integration/rrweb.test.ts
index 15c8cdba432b..4d74547f18be 100644
--- a/packages/replay/test/integration/rrweb.test.ts
+++ b/packages/replay/test/integration/rrweb.test.ts
@@ -1,4 +1,3 @@
-import { MASK_ALL_TEXT_SELECTOR } from '../../src/constants';
import { resetSdkMock } from '../mocks/resetSdkMock';
import { useFakeTimers } from '../utils/use-fake-timers';
@@ -12,19 +11,27 @@ describe('Integration | rrweb', () => {
it('calls rrweb.record with custom options', async () => {
const { mockRecord } = await resetSdkMock({
replayOptions: {
- ignoreClass: 'sentry-test-ignore',
+ ignore: ['.sentry-test-ignore'],
stickySession: false,
},
});
expect(mockRecord.mock.calls[0][0]).toMatchInlineSnapshot(`
Object {
- "blockClass": "sentry-block",
- "blockSelector": "[data-sentry-block],img,image,svg,path,rect,area,video,object,picture,embed,map,audio",
+ "blockSelector": ".sentry-block,[data-sentry-block],img,image,svg,path,rect,area,video,object,picture,embed,map,audio",
+ "collectFonts": true,
"emit": [Function],
- "ignoreClass": "sentry-test-ignore",
+ "ignoreSelector": ".sentry-test-ignore,.sentry-ignore,[data-sentry-ignore]",
+ "inlineImages": false,
+ "inlineStylesheet": true,
"maskAllInputs": true,
- "maskTextClass": "sentry-mask",
- "maskTextSelector": "${MASK_ALL_TEXT_SELECTOR}",
+ "maskInputFn": undefined,
+ "maskInputSelector": ".sentry-mask,[data-sentry-mask]",
+ "maskTextFn": undefined,
+ "maskTextSelector": "body *:not(style), body *:not(script)",
+ "slimDOMOptions": "all",
+ "unblockSelector": ".sentry-unblock,[data-sentry-unblock]",
+ "unmaskInputSelector": ".sentry-unmask,[data-sentry-unmask]",
+ "unmaskTextSelector": ".sentry-unmask,[data-sentry-unmask]",
}
`);
});
diff --git a/packages/replay/test/unit/util/getPrivacyOptions.test.ts b/packages/replay/test/unit/util/getPrivacyOptions.test.ts
new file mode 100644
index 000000000000..689f9b573f0f
--- /dev/null
+++ b/packages/replay/test/unit/util/getPrivacyOptions.test.ts
@@ -0,0 +1,87 @@
+import { getPrivacyOptions } from '../../../src/util/getPrivacyOptions';
+
+describe('Unit | util | getPrivacyOptions', () => {
+ beforeEach(() => {
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
+ });
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('has correct default options', () => {
+ expect(
+ getPrivacyOptions({
+ mask: ['.custom-mask'],
+ unmask: ['.custom-unmask'],
+ block: ['.custom-block'],
+ unblock: ['.custom-unblock'],
+ ignore: ['.custom-ignore'],
+ }),
+ ).toMatchInlineSnapshot(`
+ Object {
+ "blockSelector": ".custom-block,.sentry-block,[data-sentry-block]",
+ "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore]",
+ "maskInputSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]",
+ "maskTextSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]",
+ "unblockSelector": ".custom-unblock,.sentry-unblock,[data-sentry-unblock]",
+ "unmaskInputSelector": ".custom-unmask,.sentry-unmask,[data-sentry-unmask]",
+ "unmaskTextSelector": ".custom-unmask,.sentry-unmask,[data-sentry-unmask]",
+ }
+ `);
+ });
+
+ it('supports deprecated options', () => {
+ expect(
+ getPrivacyOptions({
+ mask: ['.custom-mask'],
+ unmask: ['.custom-unmask'],
+ block: ['.custom-block'],
+ unblock: ['.custom-unblock'],
+ ignore: ['.custom-ignore'],
+
+ blockClass: 'deprecated-block-class',
+ blockSelector: '.deprecated-block-selector',
+ maskTextClass: 'deprecated-mask-class',
+ maskTextSelector: '.deprecated-mask-selector',
+ ignoreClass: 'deprecated-ignore-class',
+ }),
+ ).toMatchInlineSnapshot(`
+ Object {
+ "blockSelector": ".custom-block,.deprecated-block-selector,.sentry-block,[data-sentry-block],.deprecated-block-class",
+ "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore],.deprecated-ignore-class",
+ "maskInputSelector": ".custom-mask,.deprecated-mask-selector,.sentry-mask,[data-sentry-mask],.deprecated-mask-class",
+ "maskTextSelector": ".custom-mask,.deprecated-mask-selector,.sentry-mask,[data-sentry-mask],.deprecated-mask-class",
+ "unblockSelector": ".custom-unblock,.sentry-unblock,[data-sentry-unblock]",
+ "unmaskInputSelector": ".custom-unmask,.sentry-unmask,[data-sentry-unmask]",
+ "unmaskTextSelector": ".custom-unmask,.sentry-unmask,[data-sentry-unmask]",
+ }
+ `);
+ });
+
+ it('supports deprecated regexp class name', () => {
+ expect(
+ getPrivacyOptions({
+ mask: ['.custom-mask'],
+ unmask: ['.custom-unmask'],
+ block: ['.custom-block'],
+ unblock: ['.custom-unblock'],
+ ignore: ['.custom-ignore'],
+
+ blockClass: /deprecated-block-*/,
+ maskTextClass: /deprecated-mask-*/,
+ }),
+ ).toMatchInlineSnapshot(`
+ Object {
+ "blockClass": /deprecated-block-\\*/,
+ "blockSelector": ".custom-block,.sentry-block,[data-sentry-block]",
+ "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore]",
+ "maskInputSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]",
+ "maskTextClass": /deprecated-mask-\\*/,
+ "maskTextSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]",
+ "unblockSelector": ".custom-unblock,.sentry-unblock,[data-sentry-unblock]",
+ "unmaskInputSelector": ".custom-unmask,.sentry-unmask,[data-sentry-unmask]",
+ "unmaskTextSelector": ".custom-unmask,.sentry-unmask,[data-sentry-unmask]",
+ }
+ `);
+ });
+});