redux.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const core = require('@sentry/core');
  3. const utils = require('@sentry/utils');
  4. /* eslint-disable @typescript-eslint/no-explicit-any */
  5. const ACTION_BREADCRUMB_CATEGORY = 'redux.action';
  6. const ACTION_BREADCRUMB_TYPE = 'info';
  7. const defaultOptions = {
  8. attachReduxState: true,
  9. actionTransformer: action => action,
  10. stateTransformer: state => state || null,
  11. };
  12. /**
  13. * Creates an enhancer that would be passed to Redux's createStore to log actions and the latest state to Sentry.
  14. *
  15. * @param enhancerOptions Options to pass to the enhancer
  16. */
  17. function createReduxEnhancer(enhancerOptions) {
  18. // Note: We return an any type as to not have type conflicts.
  19. const options = {
  20. ...defaultOptions,
  21. ...enhancerOptions,
  22. };
  23. return (next) =>
  24. (reducer, initialState) => {
  25. options.attachReduxState &&
  26. core.getGlobalScope().addEventProcessor((event, hint) => {
  27. try {
  28. // @ts-expect-error try catch to reduce bundle size
  29. if (event.type === undefined && event.contexts.state.state.type === 'redux') {
  30. hint.attachments = [
  31. ...(hint.attachments || []),
  32. // @ts-expect-error try catch to reduce bundle size
  33. { filename: 'redux_state.json', data: JSON.stringify(event.contexts.state.state.value) },
  34. ];
  35. }
  36. } catch (_) {
  37. // empty
  38. }
  39. return event;
  40. });
  41. const sentryReducer = (state, action) => {
  42. const newState = reducer(state, action);
  43. const scope = core.getCurrentScope();
  44. /* Action breadcrumbs */
  45. const transformedAction = options.actionTransformer(action);
  46. if (typeof transformedAction !== 'undefined' && transformedAction !== null) {
  47. scope.addBreadcrumb({
  48. category: ACTION_BREADCRUMB_CATEGORY,
  49. data: transformedAction,
  50. type: ACTION_BREADCRUMB_TYPE,
  51. });
  52. }
  53. /* Set latest state to scope */
  54. const transformedState = options.stateTransformer(newState);
  55. if (typeof transformedState !== 'undefined' && transformedState !== null) {
  56. const client = core.getClient();
  57. const options = client && client.getOptions();
  58. const normalizationDepth = (options && options.normalizeDepth) || 3; // default state normalization depth to 3
  59. // Set the normalization depth of the redux state to the configured `normalizeDepth` option or a sane number as a fallback
  60. const newStateContext = { state: { type: 'redux', value: transformedState } };
  61. utils.addNonEnumerableProperty(
  62. newStateContext,
  63. '__sentry_override_normalization_depth__',
  64. 3 + // 3 layers for `state.value.transformedState`
  65. normalizationDepth, // rest for the actual state
  66. );
  67. scope.setContext('state', newStateContext);
  68. } else {
  69. scope.setContext('state', null);
  70. }
  71. /* Allow user to configure scope with latest state */
  72. const { configureScopeWithState } = options;
  73. if (typeof configureScopeWithState === 'function') {
  74. configureScopeWithState(scope, newState);
  75. }
  76. return newState;
  77. };
  78. return next(sentryReducer, initialState);
  79. };
  80. }
  81. exports.createReduxEnhancer = createReduxEnhancer;
  82. //# sourceMappingURL=redux.js.map