@commercetools-frontend/sentry 0.0.0-FEC-157-preview-releases-20241126230331

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) commercetools GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # @commercetools-frontend/sentry
2
+
3
+ <p align="center">
4
+ <a href="https://www.npmjs.com/package/@commercetools-frontend/sentry"><img src="https://badgen.net/npm/v/@commercetools-frontend/sentry" alt="Latest release (latest dist-tag)" /></a> <a href="https://www.npmjs.com/package/@commercetools-frontend/sentry"><img src="https://badgen.net/npm/v/@commercetools-frontend/sentry/next" alt="Latest release (next dist-tag)" /></a> <a href="https://bundlephobia.com/result?p=@commercetools-frontend/sentry"><img src="https://badgen.net/bundlephobia/minzip/@commercetools-frontend/sentry" alt="Minified + GZipped size" /></a> <a href="https://github.com/commercetools/merchant-center-application-kit/blob/main/LICENSE"><img src="https://badgen.net/github/license/commercetools/merchant-center-application-kit" alt="GitHub license" /></a>
5
+ </p>
6
+
7
+ Components to set up Sentry for a MC application.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ $ npm install --save @commercetools-frontend/sentry
13
+ ```
@@ -0,0 +1,2 @@
1
+ export * from "./declarations/src/index";
2
+ //# sourceMappingURL=commercetools-frontend-sentry.cjs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commercetools-frontend-sentry.cjs.d.ts","sourceRoot":"","sources":["./declarations/src/index.d.ts"],"names":[],"mappings":"AAAA"}
@@ -0,0 +1,255 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _JSON$stringify = require('@babel/runtime-corejs3/core-js-stable/json/stringify');
6
+ var _includesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/includes');
7
+ var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
8
+ var Sentry = require('@sentry/react');
9
+ var browser = require('@sentry/browser');
10
+ var history = require('@commercetools-frontend/browser-history');
11
+ var _Reflect$construct = require('@babel/runtime-corejs3/core-js-stable/reflect/construct');
12
+ var _classCallCheck = require('@babel/runtime-corejs3/helpers/classCallCheck');
13
+ var _createClass = require('@babel/runtime-corejs3/helpers/createClass');
14
+ var _inherits = require('@babel/runtime-corejs3/helpers/inherits');
15
+ var _possibleConstructorReturn = require('@babel/runtime-corejs3/helpers/possibleConstructorReturn');
16
+ var _getPrototypeOf = require('@babel/runtime-corejs3/helpers/getPrototypeOf');
17
+ var _pt = require('prop-types');
18
+ var react = require('react');
19
+
20
+ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
21
+
22
+ function _interopNamespace(e) {
23
+ if (e && e.__esModule) return e;
24
+ var n = Object.create(null);
25
+ if (e) {
26
+ Object.keys(e).forEach(function (k) {
27
+ if (k !== 'default') {
28
+ var d = Object.getOwnPropertyDescriptor(e, k);
29
+ Object.defineProperty(n, k, d.get ? d : {
30
+ enumerable: true,
31
+ get: function () { return e[k]; }
32
+ });
33
+ }
34
+ });
35
+ }
36
+ n["default"] = e;
37
+ return Object.freeze(n);
38
+ }
39
+
40
+ var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
41
+ var _includesInstanceProperty__default = /*#__PURE__*/_interopDefault(_includesInstanceProperty);
42
+ var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
43
+ var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
44
+ var history__default = /*#__PURE__*/_interopDefault(history);
45
+ var _Reflect$construct__default = /*#__PURE__*/_interopDefault(_Reflect$construct);
46
+ var _pt__default = /*#__PURE__*/_interopDefault(_pt);
47
+
48
+ // NOTE: This string will be replaced on build time with the package version.
49
+ var version = "22.35.1";
50
+
51
+ const makeErrorToCapture = error => {
52
+ if (error instanceof Error) return error;
53
+ if (error instanceof ErrorEvent) return new Error(error.message);
54
+ return new Error(_JSON$stringify__default["default"](error.reason || 'Unhandled rejection without a reason'));
55
+ };
56
+ const sendErrorToSentry = error => {
57
+ if (typeof error === 'string') return Sentry__namespace.captureMessage(error);
58
+ const errorToCapture = makeErrorToCapture(error);
59
+ return Sentry__namespace.captureException(errorToCapture);
60
+ };
61
+ const replaceEventValues = (source, replacements) => {
62
+ const replaceEventValue = prop => {
63
+ source[prop] = replacements[prop];
64
+ };
65
+ for (const prop in source) {
66
+ if (Object.prototype.hasOwnProperty.call(source, prop)) {
67
+ var _context;
68
+ const hasPropReplacement = _includesInstanceProperty__default["default"](_context = _Object$keys__default["default"](replacements)).call(_context, prop);
69
+ switch (typeof source[prop]) {
70
+ case 'string':
71
+ if (hasPropReplacement) {
72
+ replaceEventValue(prop);
73
+ }
74
+ break;
75
+ case 'object':
76
+ if (hasPropReplacement) {
77
+ replaceEventValue(prop);
78
+ } else {
79
+ replaceEventValues(source[prop], replacements);
80
+ }
81
+ break;
82
+ }
83
+ }
84
+ }
85
+ return source;
86
+ };
87
+ const redactUnsafeEventFields = event => {
88
+ return replaceEventValues(event, {
89
+ firstName: '[Redacted]',
90
+ lastName: '[Redacted]',
91
+ email: '[Redacted]'
92
+ });
93
+ };
94
+ const boot = () => {
95
+ if (window.app.trackingSentry && window.app.trackingSentry !== 'null') {
96
+ Sentry__namespace.init({
97
+ dsn: window.app.trackingSentry,
98
+ release: window.app.revision,
99
+ environment: `${window.app.env}-${window.app.location}`,
100
+ // In order to reduce the noise in sentry we only track errors that come
101
+ // from our code and ignore errors that come from other services
102
+ // https://blog.sentry.io/2017/03/27/tips-for-reducing-javascript-error-noise.html
103
+ allowUrls: [window.app.cdnUrl, window.app.frontendHost],
104
+ // suppress any resize observer errors, see
105
+ // https://github.com/vercel/next.js/discussions/51551
106
+ ignoreErrors: [/ResizeObserver loop (limit exceeded|completed with undelivered notifications)/i],
107
+ integrations: [new Sentry__namespace.Integrations.GlobalHandlers({
108
+ onunhandledrejection: false,
109
+ onerror: false
110
+ }), new browser.BrowserTracing({
111
+ routingInstrumentation: Sentry__namespace.reactRouterV5Instrumentation(history__default["default"])
112
+ })],
113
+ // Sending 5% of transactions. We can adjust that as we see a need to.
114
+ // Generally we need to find a balance between performance and data volume.
115
+ // If we need more flexible and dynamic way of gathering important samples,
116
+ // we can implement the `tracesSampler` function.
117
+ // https://docs.sentry.io/platforms/javascript/guides/react/configuration/sampling/#sampling-transaction-events
118
+ tracesSampleRate: 0.05,
119
+ beforeSend(event) {
120
+ return redactUnsafeEventFields(event);
121
+ }
122
+ });
123
+ Sentry__namespace.configureScope(scope => {
124
+ scope.setTag('role', 'frontend');
125
+ });
126
+ }
127
+ };
128
+ const isExtraAsObject = extrasOrExtra => typeof extrasOrExtra === 'object';
129
+ const reportErrorToSentry = (error, extraInfo, getIsEnabled) => {
130
+ const isEnabled = getIsEnabled ? getIsEnabled() : Boolean(window.app.trackingSentry);
131
+ if (typeof error === 'string' && !isEnabled) {
132
+ console.warn('[SENTRY]: You called "reportErrorToSentry" with a string argument. ' + '"Error" objects should be preferred so that a stack-trace can be made available in Sentry. ' + 'See: https://docs.sentry.io/clients/javascript/usage/#try-catch');
133
+ }
134
+ if (isEnabled) {
135
+ if (extraInfo?.extra) {
136
+ if (isExtraAsObject(extraInfo.extra)) {
137
+ // See https://docs.sentry.io/platforms/javascript/react/
138
+ Sentry__namespace.setExtras(extraInfo.extra);
139
+ } else {
140
+ Sentry__namespace.setExtra('extra', extraInfo.extra);
141
+ }
142
+ } else if (error instanceof ErrorEvent) {
143
+ Sentry__namespace.setExtras({
144
+ filename: error.filename,
145
+ lineno: error.lineno,
146
+ colno: error.colno,
147
+ error: error.error
148
+ });
149
+ }
150
+ // Generate a unique ID referring to the last generated Sentry error
151
+ const errorId = sendErrorToSentry(error);
152
+
153
+ // The error stack should be available in Sentry, so there is no
154
+ // need to print it in the console as well.
155
+ // We just notify that an error occurred and provide the error ID.
156
+ console.error(`[SENTRY]: An error occured (ID: ${errorId}).`);
157
+ return errorId;
158
+ }
159
+ console.error('[SENTRY]:', error);
160
+ return undefined;
161
+ };
162
+
163
+ function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct__default["default"](Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
164
+ function _isNativeReflectConstruct$1() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct__default["default"](Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct$1 = function () { return !!t; })(); }
165
+ /**
166
+ * This component will let sentry know if any information about the user has
167
+ * changed.
168
+ */
169
+ let SentryUserTracker = /*#__PURE__*/function (_PureComponent) {
170
+ _inherits(SentryUserTracker, _PureComponent);
171
+ var _super = _createSuper$1(SentryUserTracker);
172
+ function SentryUserTracker() {
173
+ var _this;
174
+ _classCallCheck(this, SentryUserTracker);
175
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
176
+ args[_key] = arguments[_key];
177
+ }
178
+ _this = _super.call(this, ...args);
179
+ _this.syncUser = () => {
180
+ if (_this.props.user && window.app.trackingSentry) {
181
+ // to avoid sending personal data to sentry we anonymize the email address
182
+ // by only sending the domain part or the email
183
+ const emailTld = _this.props.user.email.split('@')[1];
184
+ Sentry__namespace.setUser({
185
+ email: `xxx@${emailTld}`,
186
+ id: _this.props.user.id
187
+ });
188
+ }
189
+ };
190
+ return _this;
191
+ }
192
+ _createClass(SentryUserTracker, [{
193
+ key: "componentDidMount",
194
+ value: function componentDidMount() {
195
+ // since the user and project could have been loaded from the apollo cache
196
+ // they could be preset already when mounting
197
+ this.syncUser();
198
+ }
199
+ }, {
200
+ key: "componentDidUpdate",
201
+ value: function componentDidUpdate() {
202
+ this.syncUser();
203
+ }
204
+ }, {
205
+ key: "render",
206
+ value: function render() {
207
+ return null;
208
+ }
209
+ }]);
210
+ return SentryUserTracker;
211
+ }(react.PureComponent);
212
+ SentryUserTracker.displayName = 'SentryUserTracker';
213
+ SentryUserTracker.propTypes = process.env.NODE_ENV !== "production" ? {
214
+ user: _pt__default["default"].shape({
215
+ id: _pt__default["default"].string.isRequired,
216
+ email: _pt__default["default"].string.isRequired
217
+ })
218
+ } : {};
219
+
220
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct__default["default"](Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
221
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct__default["default"](Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); }
222
+ let SentryUserLogoutTracker = /*#__PURE__*/function (_Component) {
223
+ _inherits(SentryUserLogoutTracker, _Component);
224
+ var _super = _createSuper(SentryUserLogoutTracker);
225
+ function SentryUserLogoutTracker() {
226
+ _classCallCheck(this, SentryUserLogoutTracker);
227
+ return _super.apply(this, arguments);
228
+ }
229
+ _createClass(SentryUserLogoutTracker, [{
230
+ key: "componentDidMount",
231
+ value: function componentDidMount() {
232
+ // When the user is not logged in anymore (e.g. on logout) we still track
233
+ // errors but without the user data in context.
234
+ if (window.app.trackingSentry) {
235
+ Sentry__namespace.configureScope(scope => {
236
+ scope.clear();
237
+ });
238
+ }
239
+ }
240
+ }, {
241
+ key: "render",
242
+ value: function render() {
243
+ return null;
244
+ }
245
+ }]);
246
+ return SentryUserLogoutTracker;
247
+ }(react.Component);
248
+ SentryUserLogoutTracker.displayName = 'SentryUserLogoutTracker';
249
+
250
+ exports.SentryUserLogoutTracker = SentryUserLogoutTracker;
251
+ exports.SentryUserTracker = SentryUserTracker;
252
+ exports.boot = boot;
253
+ exports.redactUnsafeEventFields = redactUnsafeEventFields;
254
+ exports.reportErrorToSentry = reportErrorToSentry;
255
+ exports.version = version;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === "production") {
4
+ module.exports = require("./commercetools-frontend-sentry.cjs.prod.js");
5
+ } else {
6
+ module.exports = require("./commercetools-frontend-sentry.cjs.dev.js");
7
+ }
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _JSON$stringify = require('@babel/runtime-corejs3/core-js-stable/json/stringify');
6
+ var _includesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/includes');
7
+ var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
8
+ var Sentry = require('@sentry/react');
9
+ var browser = require('@sentry/browser');
10
+ var history = require('@commercetools-frontend/browser-history');
11
+ var _Reflect$construct = require('@babel/runtime-corejs3/core-js-stable/reflect/construct');
12
+ var _classCallCheck = require('@babel/runtime-corejs3/helpers/classCallCheck');
13
+ var _createClass = require('@babel/runtime-corejs3/helpers/createClass');
14
+ var _inherits = require('@babel/runtime-corejs3/helpers/inherits');
15
+ var _possibleConstructorReturn = require('@babel/runtime-corejs3/helpers/possibleConstructorReturn');
16
+ var _getPrototypeOf = require('@babel/runtime-corejs3/helpers/getPrototypeOf');
17
+ require('prop-types');
18
+ var react = require('react');
19
+
20
+ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
21
+
22
+ function _interopNamespace(e) {
23
+ if (e && e.__esModule) return e;
24
+ var n = Object.create(null);
25
+ if (e) {
26
+ Object.keys(e).forEach(function (k) {
27
+ if (k !== 'default') {
28
+ var d = Object.getOwnPropertyDescriptor(e, k);
29
+ Object.defineProperty(n, k, d.get ? d : {
30
+ enumerable: true,
31
+ get: function () { return e[k]; }
32
+ });
33
+ }
34
+ });
35
+ }
36
+ n["default"] = e;
37
+ return Object.freeze(n);
38
+ }
39
+
40
+ var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
41
+ var _includesInstanceProperty__default = /*#__PURE__*/_interopDefault(_includesInstanceProperty);
42
+ var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
43
+ var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
44
+ var history__default = /*#__PURE__*/_interopDefault(history);
45
+ var _Reflect$construct__default = /*#__PURE__*/_interopDefault(_Reflect$construct);
46
+
47
+ // NOTE: This string will be replaced on build time with the package version.
48
+ var version = "22.35.1";
49
+
50
+ const makeErrorToCapture = error => {
51
+ if (error instanceof Error) return error;
52
+ if (error instanceof ErrorEvent) return new Error(error.message);
53
+ return new Error(_JSON$stringify__default["default"](error.reason || 'Unhandled rejection without a reason'));
54
+ };
55
+ const sendErrorToSentry = error => {
56
+ if (typeof error === 'string') return Sentry__namespace.captureMessage(error);
57
+ const errorToCapture = makeErrorToCapture(error);
58
+ return Sentry__namespace.captureException(errorToCapture);
59
+ };
60
+ const replaceEventValues = (source, replacements) => {
61
+ const replaceEventValue = prop => {
62
+ source[prop] = replacements[prop];
63
+ };
64
+ for (const prop in source) {
65
+ if (Object.prototype.hasOwnProperty.call(source, prop)) {
66
+ var _context;
67
+ const hasPropReplacement = _includesInstanceProperty__default["default"](_context = _Object$keys__default["default"](replacements)).call(_context, prop);
68
+ switch (typeof source[prop]) {
69
+ case 'string':
70
+ if (hasPropReplacement) {
71
+ replaceEventValue(prop);
72
+ }
73
+ break;
74
+ case 'object':
75
+ if (hasPropReplacement) {
76
+ replaceEventValue(prop);
77
+ } else {
78
+ replaceEventValues(source[prop], replacements);
79
+ }
80
+ break;
81
+ }
82
+ }
83
+ }
84
+ return source;
85
+ };
86
+ const redactUnsafeEventFields = event => {
87
+ return replaceEventValues(event, {
88
+ firstName: '[Redacted]',
89
+ lastName: '[Redacted]',
90
+ email: '[Redacted]'
91
+ });
92
+ };
93
+ const boot = () => {
94
+ if (window.app.trackingSentry && window.app.trackingSentry !== 'null') {
95
+ Sentry__namespace.init({
96
+ dsn: window.app.trackingSentry,
97
+ release: window.app.revision,
98
+ environment: `${window.app.env}-${window.app.location}`,
99
+ // In order to reduce the noise in sentry we only track errors that come
100
+ // from our code and ignore errors that come from other services
101
+ // https://blog.sentry.io/2017/03/27/tips-for-reducing-javascript-error-noise.html
102
+ allowUrls: [window.app.cdnUrl, window.app.frontendHost],
103
+ // suppress any resize observer errors, see
104
+ // https://github.com/vercel/next.js/discussions/51551
105
+ ignoreErrors: [/ResizeObserver loop (limit exceeded|completed with undelivered notifications)/i],
106
+ integrations: [new Sentry__namespace.Integrations.GlobalHandlers({
107
+ onunhandledrejection: false,
108
+ onerror: false
109
+ }), new browser.BrowserTracing({
110
+ routingInstrumentation: Sentry__namespace.reactRouterV5Instrumentation(history__default["default"])
111
+ })],
112
+ // Sending 5% of transactions. We can adjust that as we see a need to.
113
+ // Generally we need to find a balance between performance and data volume.
114
+ // If we need more flexible and dynamic way of gathering important samples,
115
+ // we can implement the `tracesSampler` function.
116
+ // https://docs.sentry.io/platforms/javascript/guides/react/configuration/sampling/#sampling-transaction-events
117
+ tracesSampleRate: 0.05,
118
+ beforeSend(event) {
119
+ return redactUnsafeEventFields(event);
120
+ }
121
+ });
122
+ Sentry__namespace.configureScope(scope => {
123
+ scope.setTag('role', 'frontend');
124
+ });
125
+ }
126
+ };
127
+ const isExtraAsObject = extrasOrExtra => typeof extrasOrExtra === 'object';
128
+ const reportErrorToSentry = (error, extraInfo, getIsEnabled) => {
129
+ const isEnabled = getIsEnabled ? getIsEnabled() : Boolean(window.app.trackingSentry);
130
+ if (typeof error === 'string' && !isEnabled) {
131
+ console.warn('[SENTRY]: You called "reportErrorToSentry" with a string argument. ' + '"Error" objects should be preferred so that a stack-trace can be made available in Sentry. ' + 'See: https://docs.sentry.io/clients/javascript/usage/#try-catch');
132
+ }
133
+ if (isEnabled) {
134
+ if (extraInfo?.extra) {
135
+ if (isExtraAsObject(extraInfo.extra)) {
136
+ // See https://docs.sentry.io/platforms/javascript/react/
137
+ Sentry__namespace.setExtras(extraInfo.extra);
138
+ } else {
139
+ Sentry__namespace.setExtra('extra', extraInfo.extra);
140
+ }
141
+ } else if (error instanceof ErrorEvent) {
142
+ Sentry__namespace.setExtras({
143
+ filename: error.filename,
144
+ lineno: error.lineno,
145
+ colno: error.colno,
146
+ error: error.error
147
+ });
148
+ }
149
+ // Generate a unique ID referring to the last generated Sentry error
150
+ const errorId = sendErrorToSentry(error);
151
+
152
+ // The error stack should be available in Sentry, so there is no
153
+ // need to print it in the console as well.
154
+ // We just notify that an error occurred and provide the error ID.
155
+ console.error(`[SENTRY]: An error occured (ID: ${errorId}).`);
156
+ return errorId;
157
+ }
158
+ console.error('[SENTRY]:', error);
159
+ return undefined;
160
+ };
161
+
162
+ function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct__default["default"](Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
163
+ function _isNativeReflectConstruct$1() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct__default["default"](Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct$1 = function () { return !!t; })(); }
164
+ /**
165
+ * This component will let sentry know if any information about the user has
166
+ * changed.
167
+ */
168
+ let SentryUserTracker = /*#__PURE__*/function (_PureComponent) {
169
+ _inherits(SentryUserTracker, _PureComponent);
170
+ var _super = _createSuper$1(SentryUserTracker);
171
+ function SentryUserTracker() {
172
+ var _this;
173
+ _classCallCheck(this, SentryUserTracker);
174
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
175
+ args[_key] = arguments[_key];
176
+ }
177
+ _this = _super.call(this, ...args);
178
+ _this.syncUser = () => {
179
+ if (_this.props.user && window.app.trackingSentry) {
180
+ // to avoid sending personal data to sentry we anonymize the email address
181
+ // by only sending the domain part or the email
182
+ const emailTld = _this.props.user.email.split('@')[1];
183
+ Sentry__namespace.setUser({
184
+ email: `xxx@${emailTld}`,
185
+ id: _this.props.user.id
186
+ });
187
+ }
188
+ };
189
+ return _this;
190
+ }
191
+ _createClass(SentryUserTracker, [{
192
+ key: "componentDidMount",
193
+ value: function componentDidMount() {
194
+ // since the user and project could have been loaded from the apollo cache
195
+ // they could be preset already when mounting
196
+ this.syncUser();
197
+ }
198
+ }, {
199
+ key: "componentDidUpdate",
200
+ value: function componentDidUpdate() {
201
+ this.syncUser();
202
+ }
203
+ }, {
204
+ key: "render",
205
+ value: function render() {
206
+ return null;
207
+ }
208
+ }]);
209
+ return SentryUserTracker;
210
+ }(react.PureComponent);
211
+ SentryUserTracker.displayName = 'SentryUserTracker';
212
+ SentryUserTracker.propTypes = {};
213
+
214
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct__default["default"](Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
215
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct__default["default"](Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); }
216
+ let SentryUserLogoutTracker = /*#__PURE__*/function (_Component) {
217
+ _inherits(SentryUserLogoutTracker, _Component);
218
+ var _super = _createSuper(SentryUserLogoutTracker);
219
+ function SentryUserLogoutTracker() {
220
+ _classCallCheck(this, SentryUserLogoutTracker);
221
+ return _super.apply(this, arguments);
222
+ }
223
+ _createClass(SentryUserLogoutTracker, [{
224
+ key: "componentDidMount",
225
+ value: function componentDidMount() {
226
+ // When the user is not logged in anymore (e.g. on logout) we still track
227
+ // errors but without the user data in context.
228
+ if (window.app.trackingSentry) {
229
+ Sentry__namespace.configureScope(scope => {
230
+ scope.clear();
231
+ });
232
+ }
233
+ }
234
+ }, {
235
+ key: "render",
236
+ value: function render() {
237
+ return null;
238
+ }
239
+ }]);
240
+ return SentryUserLogoutTracker;
241
+ }(react.Component);
242
+ SentryUserLogoutTracker.displayName = 'SentryUserLogoutTracker';
243
+
244
+ exports.SentryUserLogoutTracker = SentryUserLogoutTracker;
245
+ exports.SentryUserTracker = SentryUserTracker;
246
+ exports.boot = boot;
247
+ exports.redactUnsafeEventFields = redactUnsafeEventFields;
248
+ exports.reportErrorToSentry = reportErrorToSentry;
249
+ exports.version = version;
@@ -0,0 +1,218 @@
1
+ import _JSON$stringify from '@babel/runtime-corejs3/core-js-stable/json/stringify';
2
+ import _includesInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/includes';
3
+ import _Object$keys from '@babel/runtime-corejs3/core-js-stable/object/keys';
4
+ import * as Sentry from '@sentry/react';
5
+ import { BrowserTracing } from '@sentry/browser';
6
+ import history from '@commercetools-frontend/browser-history';
7
+ import _Reflect$construct from '@babel/runtime-corejs3/core-js-stable/reflect/construct';
8
+ import _classCallCheck from '@babel/runtime-corejs3/helpers/esm/classCallCheck';
9
+ import _createClass from '@babel/runtime-corejs3/helpers/esm/createClass';
10
+ import _inherits from '@babel/runtime-corejs3/helpers/esm/inherits';
11
+ import _possibleConstructorReturn from '@babel/runtime-corejs3/helpers/esm/possibleConstructorReturn';
12
+ import _getPrototypeOf from '@babel/runtime-corejs3/helpers/esm/getPrototypeOf';
13
+ import _pt from 'prop-types';
14
+ import { PureComponent, Component } from 'react';
15
+
16
+ // NOTE: This string will be replaced on build time with the package version.
17
+ var version = "22.35.1";
18
+
19
+ const makeErrorToCapture = error => {
20
+ if (error instanceof Error) return error;
21
+ if (error instanceof ErrorEvent) return new Error(error.message);
22
+ return new Error(_JSON$stringify(error.reason || 'Unhandled rejection without a reason'));
23
+ };
24
+ const sendErrorToSentry = error => {
25
+ if (typeof error === 'string') return Sentry.captureMessage(error);
26
+ const errorToCapture = makeErrorToCapture(error);
27
+ return Sentry.captureException(errorToCapture);
28
+ };
29
+ const replaceEventValues = (source, replacements) => {
30
+ const replaceEventValue = prop => {
31
+ source[prop] = replacements[prop];
32
+ };
33
+ for (const prop in source) {
34
+ if (Object.prototype.hasOwnProperty.call(source, prop)) {
35
+ var _context;
36
+ const hasPropReplacement = _includesInstanceProperty(_context = _Object$keys(replacements)).call(_context, prop);
37
+ switch (typeof source[prop]) {
38
+ case 'string':
39
+ if (hasPropReplacement) {
40
+ replaceEventValue(prop);
41
+ }
42
+ break;
43
+ case 'object':
44
+ if (hasPropReplacement) {
45
+ replaceEventValue(prop);
46
+ } else {
47
+ replaceEventValues(source[prop], replacements);
48
+ }
49
+ break;
50
+ }
51
+ }
52
+ }
53
+ return source;
54
+ };
55
+ const redactUnsafeEventFields = event => {
56
+ return replaceEventValues(event, {
57
+ firstName: '[Redacted]',
58
+ lastName: '[Redacted]',
59
+ email: '[Redacted]'
60
+ });
61
+ };
62
+ const boot = () => {
63
+ if (window.app.trackingSentry && window.app.trackingSentry !== 'null') {
64
+ Sentry.init({
65
+ dsn: window.app.trackingSentry,
66
+ release: window.app.revision,
67
+ environment: `${window.app.env}-${window.app.location}`,
68
+ // In order to reduce the noise in sentry we only track errors that come
69
+ // from our code and ignore errors that come from other services
70
+ // https://blog.sentry.io/2017/03/27/tips-for-reducing-javascript-error-noise.html
71
+ allowUrls: [window.app.cdnUrl, window.app.frontendHost],
72
+ // suppress any resize observer errors, see
73
+ // https://github.com/vercel/next.js/discussions/51551
74
+ ignoreErrors: [/ResizeObserver loop (limit exceeded|completed with undelivered notifications)/i],
75
+ integrations: [new Sentry.Integrations.GlobalHandlers({
76
+ onunhandledrejection: false,
77
+ onerror: false
78
+ }), new BrowserTracing({
79
+ routingInstrumentation: Sentry.reactRouterV5Instrumentation(history)
80
+ })],
81
+ // Sending 5% of transactions. We can adjust that as we see a need to.
82
+ // Generally we need to find a balance between performance and data volume.
83
+ // If we need more flexible and dynamic way of gathering important samples,
84
+ // we can implement the `tracesSampler` function.
85
+ // https://docs.sentry.io/platforms/javascript/guides/react/configuration/sampling/#sampling-transaction-events
86
+ tracesSampleRate: 0.05,
87
+ beforeSend(event) {
88
+ return redactUnsafeEventFields(event);
89
+ }
90
+ });
91
+ Sentry.configureScope(scope => {
92
+ scope.setTag('role', 'frontend');
93
+ });
94
+ }
95
+ };
96
+ const isExtraAsObject = extrasOrExtra => typeof extrasOrExtra === 'object';
97
+ const reportErrorToSentry = (error, extraInfo, getIsEnabled) => {
98
+ const isEnabled = getIsEnabled ? getIsEnabled() : Boolean(window.app.trackingSentry);
99
+ if (typeof error === 'string' && !isEnabled) {
100
+ console.warn('[SENTRY]: You called "reportErrorToSentry" with a string argument. ' + '"Error" objects should be preferred so that a stack-trace can be made available in Sentry. ' + 'See: https://docs.sentry.io/clients/javascript/usage/#try-catch');
101
+ }
102
+ if (isEnabled) {
103
+ if (extraInfo?.extra) {
104
+ if (isExtraAsObject(extraInfo.extra)) {
105
+ // See https://docs.sentry.io/platforms/javascript/react/
106
+ Sentry.setExtras(extraInfo.extra);
107
+ } else {
108
+ Sentry.setExtra('extra', extraInfo.extra);
109
+ }
110
+ } else if (error instanceof ErrorEvent) {
111
+ Sentry.setExtras({
112
+ filename: error.filename,
113
+ lineno: error.lineno,
114
+ colno: error.colno,
115
+ error: error.error
116
+ });
117
+ }
118
+ // Generate a unique ID referring to the last generated Sentry error
119
+ const errorId = sendErrorToSentry(error);
120
+
121
+ // The error stack should be available in Sentry, so there is no
122
+ // need to print it in the console as well.
123
+ // We just notify that an error occurred and provide the error ID.
124
+ console.error(`[SENTRY]: An error occured (ID: ${errorId}).`);
125
+ return errorId;
126
+ }
127
+ console.error('[SENTRY]:', error);
128
+ return undefined;
129
+ };
130
+
131
+ function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
132
+ function _isNativeReflectConstruct$1() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct$1 = function () { return !!t; })(); }
133
+ /**
134
+ * This component will let sentry know if any information about the user has
135
+ * changed.
136
+ */
137
+ let SentryUserTracker = /*#__PURE__*/function (_PureComponent) {
138
+ _inherits(SentryUserTracker, _PureComponent);
139
+ var _super = _createSuper$1(SentryUserTracker);
140
+ function SentryUserTracker() {
141
+ var _this;
142
+ _classCallCheck(this, SentryUserTracker);
143
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
144
+ args[_key] = arguments[_key];
145
+ }
146
+ _this = _super.call(this, ...args);
147
+ _this.syncUser = () => {
148
+ if (_this.props.user && window.app.trackingSentry) {
149
+ // to avoid sending personal data to sentry we anonymize the email address
150
+ // by only sending the domain part or the email
151
+ const emailTld = _this.props.user.email.split('@')[1];
152
+ Sentry.setUser({
153
+ email: `xxx@${emailTld}`,
154
+ id: _this.props.user.id
155
+ });
156
+ }
157
+ };
158
+ return _this;
159
+ }
160
+ _createClass(SentryUserTracker, [{
161
+ key: "componentDidMount",
162
+ value: function componentDidMount() {
163
+ // since the user and project could have been loaded from the apollo cache
164
+ // they could be preset already when mounting
165
+ this.syncUser();
166
+ }
167
+ }, {
168
+ key: "componentDidUpdate",
169
+ value: function componentDidUpdate() {
170
+ this.syncUser();
171
+ }
172
+ }, {
173
+ key: "render",
174
+ value: function render() {
175
+ return null;
176
+ }
177
+ }]);
178
+ return SentryUserTracker;
179
+ }(PureComponent);
180
+ SentryUserTracker.displayName = 'SentryUserTracker';
181
+ SentryUserTracker.propTypes = process.env.NODE_ENV !== "production" ? {
182
+ user: _pt.shape({
183
+ id: _pt.string.isRequired,
184
+ email: _pt.string.isRequired
185
+ })
186
+ } : {};
187
+
188
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
189
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); }
190
+ let SentryUserLogoutTracker = /*#__PURE__*/function (_Component) {
191
+ _inherits(SentryUserLogoutTracker, _Component);
192
+ var _super = _createSuper(SentryUserLogoutTracker);
193
+ function SentryUserLogoutTracker() {
194
+ _classCallCheck(this, SentryUserLogoutTracker);
195
+ return _super.apply(this, arguments);
196
+ }
197
+ _createClass(SentryUserLogoutTracker, [{
198
+ key: "componentDidMount",
199
+ value: function componentDidMount() {
200
+ // When the user is not logged in anymore (e.g. on logout) we still track
201
+ // errors but without the user data in context.
202
+ if (window.app.trackingSentry) {
203
+ Sentry.configureScope(scope => {
204
+ scope.clear();
205
+ });
206
+ }
207
+ }
208
+ }, {
209
+ key: "render",
210
+ value: function render() {
211
+ return null;
212
+ }
213
+ }]);
214
+ return SentryUserLogoutTracker;
215
+ }(Component);
216
+ SentryUserLogoutTracker.displayName = 'SentryUserLogoutTracker';
217
+
218
+ export { SentryUserLogoutTracker, SentryUserTracker, boot, redactUnsafeEventFields, reportErrorToSentry, version };
@@ -0,0 +1,4 @@
1
+ export { default as version } from './version';
2
+ export * from './sentry';
3
+ export { default as SentryUserTracker } from './sentry-user-tracker';
4
+ export { default as SentryUserLogoutTracker } from './sentry-user-logout-tracker';
@@ -0,0 +1 @@
1
+ export { default } from './sentry-user-logout-tracker';
@@ -0,0 +1,7 @@
1
+ import { Component } from 'react';
2
+ declare class SentryUserLogoutTracker extends Component {
3
+ static displayName: string;
4
+ componentDidMount(): void;
5
+ render(): null;
6
+ }
7
+ export default SentryUserLogoutTracker;
@@ -0,0 +1 @@
1
+ export { default } from './sentry-user-tracker';
@@ -0,0 +1,20 @@
1
+ import { PureComponent } from 'react';
2
+ type SentryUser = {
3
+ id: string;
4
+ email: string;
5
+ };
6
+ type Props = {
7
+ user?: SentryUser;
8
+ };
9
+ /**
10
+ * This component will let sentry know if any information about the user has
11
+ * changed.
12
+ */
13
+ declare class SentryUserTracker extends PureComponent<Props> {
14
+ static displayName: string;
15
+ componentDidMount(): void;
16
+ componentDidUpdate(): void;
17
+ syncUser: () => void;
18
+ render(): null;
19
+ }
20
+ export default SentryUserTracker;
@@ -0,0 +1,10 @@
1
+ import * as Sentry from '@sentry/react';
2
+ import type { Extra, Extras, Event } from '@sentry/types';
3
+ type ReportableEvent = ErrorEvent | PromiseRejectionEvent;
4
+ type Reportable = Error | ReportableEvent | string;
5
+ export declare const redactUnsafeEventFields: (event: Event) => Sentry.Event;
6
+ export declare const boot: () => void;
7
+ export declare const reportErrorToSentry: (error: Reportable, extraInfo?: {
8
+ extra?: Extra | Extras;
9
+ }, getIsEnabled?: () => boolean) => string | undefined;
10
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const _default: "__@APPLICATION_KIT_PACKAGE/VERSION_OF_RELEASE__";
2
+ export default _default;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@commercetools-frontend/sentry",
3
+ "version": "0.0.0-FEC-157-preview-releases-20241126230331",
4
+ "description": "Components to set up Sentry for a MC application",
5
+ "bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/commercetools/merchant-center-application-kit.git",
9
+ "directory": "packages/sentry"
10
+ },
11
+ "homepage": "https://docs.commercetools.com/merchant-center-customizations",
12
+ "keywords": [
13
+ "javascript",
14
+ "frontend",
15
+ "react",
16
+ "toolkit"
17
+ ],
18
+ "license": "MIT",
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "main": "dist/commercetools-frontend-sentry.cjs.js",
23
+ "module": "dist/commercetools-frontend-sentry.esm.js",
24
+ "files": [
25
+ "dist",
26
+ "package.json",
27
+ "LICENSE",
28
+ "README.md"
29
+ ],
30
+ "dependencies": {
31
+ "@babel/runtime": "^7.22.15",
32
+ "@babel/runtime-corejs3": "^7.22.15",
33
+ "@commercetools-frontend/browser-history": "0.0.0-FEC-157-preview-releases-20241126230331",
34
+ "@commercetools-frontend/constants": "0.0.0-FEC-157-preview-releases-20241126230331",
35
+ "@sentry/browser": "7.119.1",
36
+ "@sentry/react": "7.117.0",
37
+ "@sentry/types": "7.117.0",
38
+ "@types/prop-types": "^15.7.5",
39
+ "@types/react": "^17.0.80",
40
+ "prop-types": "15.8.1"
41
+ },
42
+ "devDependencies": {
43
+ "@emotion/react": "^11.11.4",
44
+ "@types/jest": "^29.5.4",
45
+ "jest": "29.7.0",
46
+ "react": "17.0.2",
47
+ "sentry-testkit": "5.0.9",
48
+ "wait-for-expect": "3.0.2"
49
+ },
50
+ "peerDependencies": {
51
+ "react": "17.x"
52
+ }
53
+ }