@posiwise/common-services 0.2.13 → 0.2.15
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.
|
@@ -984,10 +984,12 @@ class AuthService {
|
|
|
984
984
|
this.integrationsApi = integrationsApi;
|
|
985
985
|
this.document = document;
|
|
986
986
|
this.platformSubject = new BehaviorSubject('');
|
|
987
|
+
this.userDataSubject = new BehaviorSubject({});
|
|
987
988
|
this.twitterEndpoint = '/twitter';
|
|
988
989
|
this.header_key = 'browser';
|
|
989
990
|
this.platform = '';
|
|
990
991
|
this.platform$ = this.platformSubject.asObservable();
|
|
992
|
+
this.userData$ = this.userDataSubject.asObservable();
|
|
991
993
|
// Set Header Key
|
|
992
994
|
this.setHeaderKey();
|
|
993
995
|
}
|
|
@@ -1108,11 +1110,12 @@ class AuthService {
|
|
|
1108
1110
|
phonegap: true
|
|
1109
1111
|
};
|
|
1110
1112
|
}
|
|
1111
|
-
// Send DELETE request with token intact, then clear tokens after response
|
|
1112
|
-
// Use tap to clear tokens without changing the response stream
|
|
1113
1113
|
return this.http.delete('session', options).pipe(tap$1(() => {
|
|
1114
|
-
// Clear tokens AFTER logout succeeds
|
|
1115
1114
|
this.secureTokenStorage.clearTokens().subscribe();
|
|
1115
|
+
// Reset Crisp session on logout so next visitor is anonymous again
|
|
1116
|
+
if (window && window.$crisp && typeof window.$crisp.push === 'function') {
|
|
1117
|
+
window.$crisp.push(['do', 'session:reset']);
|
|
1118
|
+
}
|
|
1116
1119
|
}));
|
|
1117
1120
|
}
|
|
1118
1121
|
/**
|
|
@@ -1130,6 +1133,23 @@ class AuthService {
|
|
|
1130
1133
|
getToken$() {
|
|
1131
1134
|
return this.secureTokenStorage.getToken$();
|
|
1132
1135
|
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Get user data (email, name, userId) after login
|
|
1138
|
+
* Similar to getToken$() but returns user information
|
|
1139
|
+
* @returns Observable with user data: { email?: string; name?: string; userId?: string }
|
|
1140
|
+
*/
|
|
1141
|
+
getUserData$() {
|
|
1142
|
+
console.log(this.userData$);
|
|
1143
|
+
return this.userData$;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Set user data in the BehaviorSubject
|
|
1147
|
+
* Called after user logs in to update user information across the app
|
|
1148
|
+
* @param userData User data object with email, name, and userId
|
|
1149
|
+
*/
|
|
1150
|
+
setUserData(userData) {
|
|
1151
|
+
this.userDataSubject.next(userData);
|
|
1152
|
+
}
|
|
1133
1153
|
getNewsletterSubscription(token) {
|
|
1134
1154
|
return this.http.get(`${NEWSLETTER_CONFIRMATION_PATH}?token=${token}`);
|
|
1135
1155
|
}
|
|
@@ -1195,7 +1215,33 @@ class AuthService {
|
|
|
1195
1215
|
return this.userService.getUserInfo();
|
|
1196
1216
|
}));
|
|
1197
1217
|
sequence$.subscribe(resp => {
|
|
1198
|
-
|
|
1218
|
+
const firstName = resp.first_name ?? '';
|
|
1219
|
+
const email = resp.email ?? '';
|
|
1220
|
+
const userId = resp.id?.toString() ?? '';
|
|
1221
|
+
localStorage.setItem('name', firstName);
|
|
1222
|
+
localStorage.setItem('userEmail', email);
|
|
1223
|
+
localStorage.setItem('userId', userId);
|
|
1224
|
+
this.setUserData({
|
|
1225
|
+
name: firstName,
|
|
1226
|
+
email: email,
|
|
1227
|
+
userId: userId
|
|
1228
|
+
});
|
|
1229
|
+
// Identify logged-in user in Crisp if widget is already loaded
|
|
1230
|
+
if (window && window.$crisp && typeof window.$crisp.push === 'function') {
|
|
1231
|
+
if (email) {
|
|
1232
|
+
window.$crisp.push(['set', 'user:email', email]);
|
|
1233
|
+
}
|
|
1234
|
+
if (firstName) {
|
|
1235
|
+
window.$crisp.push(['set', 'user:nickname', firstName]);
|
|
1236
|
+
}
|
|
1237
|
+
if (userId) {
|
|
1238
|
+
window.$crisp.push([
|
|
1239
|
+
'set',
|
|
1240
|
+
'session:data',
|
|
1241
|
+
[['user_id', [userId]]]
|
|
1242
|
+
]);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1199
1245
|
this.router.navigate(['home']);
|
|
1200
1246
|
if ($('.cc-dismiss').length > 0) {
|
|
1201
1247
|
$('.cc-dismiss')[0].click();
|
|
@@ -2920,97 +2966,7 @@ class SentryErrorHandler {
|
|
|
2920
2966
|
// NOTE: We intentionally do not enable Performance Tracing here.
|
|
2921
2967
|
// The previous integration (`BrowserTracing` + `routingInstrumentation`) was part of
|
|
2922
2968
|
// the legacy `@sentry/angular-ivy` API and isn't available in `@sentry/angular` v10.
|
|
2923
|
-
beforeSend(event, hint)
|
|
2924
|
-
const initialExceptionValuesLength = Array.isArray(event?.exception?.values)
|
|
2925
|
-
? event.exception.values.length
|
|
2926
|
-
: undefined;
|
|
2927
|
-
const safeIncludes = (val, needle) => typeof val === 'string' && val.includes(needle);
|
|
2928
|
-
// Check if the event is of type CloseEvent and ignore it
|
|
2929
|
-
if (event?.exception?.values) {
|
|
2930
|
-
event.exception.values = event.exception.values.filter(value => {
|
|
2931
|
-
// Update the condition based on the actual structure of the event
|
|
2932
|
-
return !(value.type === 'CloseEvent' ||
|
|
2933
|
-
safeIncludes(value.value, 'CloseEvent'));
|
|
2934
|
-
});
|
|
2935
|
-
}
|
|
2936
|
-
if (event?.exception?.values) {
|
|
2937
|
-
event.exception.values = event.exception.values.filter(value => {
|
|
2938
|
-
// Check for the specific error message and ignore it
|
|
2939
|
-
return !(value.type === 'Error' &&
|
|
2940
|
-
safeIncludes(value.value, 'ResizeObserver loop completed'));
|
|
2941
|
-
});
|
|
2942
|
-
}
|
|
2943
|
-
// originates from vendor.js file
|
|
2944
|
-
if (event?.exception?.values) {
|
|
2945
|
-
const isJsonParsingError = event.exception.values.some(value => {
|
|
2946
|
-
return (value.type === 'SyntaxError' &&
|
|
2947
|
-
value?.value?.includes("expected ':' after property name in"));
|
|
2948
|
-
});
|
|
2949
|
-
if (isJsonParsingError) {
|
|
2950
|
-
return null;
|
|
2951
|
-
}
|
|
2952
|
-
}
|
|
2953
|
-
// Check if the event is a ChunkLoadError and ignore it (it could be because of poor network)
|
|
2954
|
-
if (event?.exception?.values) {
|
|
2955
|
-
event.exception.values = event.exception.values.filter(value => {
|
|
2956
|
-
return !(value.type === 'ChunkLoadError' ||
|
|
2957
|
-
safeIncludes(value.value, 'ChunkLoadError: Loading chunk'));
|
|
2958
|
-
});
|
|
2959
|
-
}
|
|
2960
|
-
// Check if the event is a script loading error and ignore it
|
|
2961
|
-
if (event?.exception?.values) {
|
|
2962
|
-
event.exception.values = event.exception.values.filter(value => {
|
|
2963
|
-
return !(value.type === 'Error' &&
|
|
2964
|
-
safeIncludes(value.value, 'Could not load "util"'));
|
|
2965
|
-
});
|
|
2966
|
-
}
|
|
2967
|
-
// Check if the event is a timeout error and ignore it
|
|
2968
|
-
if (event?.exception?.values) {
|
|
2969
|
-
event.exception.values = event.exception.values.filter(value => {
|
|
2970
|
-
return !(value.type === 'Error' &&
|
|
2971
|
-
safeIncludes(value.value, 'Error loading plotly.js library from'));
|
|
2972
|
-
});
|
|
2973
|
-
}
|
|
2974
|
-
// Check if the event is related to cross-origin frame access
|
|
2975
|
-
if (event?.exception?.values) {
|
|
2976
|
-
const isCrossOriginFrameError = event.exception.values.some(value => {
|
|
2977
|
-
return (value.type === 'SecurityError' &&
|
|
2978
|
-
value?.value?.includes("Failed to read a named property 'navigator' from 'Window': Blocked a frame with origin"));
|
|
2979
|
-
});
|
|
2980
|
-
if (isCrossOriginFrameError) {
|
|
2981
|
-
// Exclude cross-origin frame errors from being reported to Sentry
|
|
2982
|
-
return null;
|
|
2983
|
-
}
|
|
2984
|
-
}
|
|
2985
|
-
// Check if it's a non-error exception
|
|
2986
|
-
const isNonErrorException = event?.exception?.values?.[0]?.value?.startsWith('Non-Error exception captured') ??
|
|
2987
|
-
hint?.originalException?.message?.startsWith('Non-Error exception captured');
|
|
2988
|
-
if (isNonErrorException) {
|
|
2989
|
-
// We want to ignore those kinds of errors
|
|
2990
|
-
return null;
|
|
2991
|
-
}
|
|
2992
|
-
// Filter out Edge browser (145.0.0+) synthetic isTrusted events
|
|
2993
|
-
// These events spam Sentry as "unlabeled events"
|
|
2994
|
-
const isEdge = /Edg\//.test(navigator.userAgent);
|
|
2995
|
-
if (isEdge && hint?.originalException) {
|
|
2996
|
-
const originalException = hint.originalException;
|
|
2997
|
-
if (originalException &&
|
|
2998
|
-
typeof originalException === 'object' &&
|
|
2999
|
-
originalException['isTrusted'] === true) {
|
|
3000
|
-
// Block Edge synthetic browser events from being sent to Sentry
|
|
3001
|
-
return null;
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
// If we started with exception values but filtered them all out, drop the event.
|
|
3005
|
-
// Otherwise Sentry will group it as an "unlabeled event" and spam the issue stream.
|
|
3006
|
-
if (typeof initialExceptionValuesLength === 'number' &&
|
|
3007
|
-
initialExceptionValuesLength > 0 &&
|
|
3008
|
-
Array.isArray(event?.exception?.values) &&
|
|
3009
|
-
event.exception.values.length === 0) {
|
|
3010
|
-
return null;
|
|
3011
|
-
}
|
|
3012
|
-
return event;
|
|
3013
|
-
},
|
|
2969
|
+
beforeSend: (event, hint) => SentryErrorHandler.beforeSendFilter(event, hint),
|
|
3014
2970
|
denyUrls: [/drift.*\.js/i, /analytics.*\.js/i, /polyfills.*\.js/i, /vendor.*\.js/i],
|
|
3015
2971
|
environment,
|
|
3016
2972
|
// We ignore Server Errors. We have to define here since Angular
|
|
@@ -3193,15 +3149,41 @@ class SentryErrorHandler {
|
|
|
3193
3149
|
return 'string_error';
|
|
3194
3150
|
}
|
|
3195
3151
|
if (error && typeof error === 'object') {
|
|
3152
|
+
return this.classifyObjectError(error);
|
|
3153
|
+
}
|
|
3154
|
+
return 'unknown';
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Classifies object-type errors
|
|
3158
|
+
*/
|
|
3159
|
+
classifyObjectError(error) {
|
|
3160
|
+
if (typeof error === 'object' && error !== null) {
|
|
3196
3161
|
if ('isTrusted' in error) {
|
|
3197
3162
|
return 'browser_event';
|
|
3198
3163
|
}
|
|
3199
3164
|
if ('type' in error) {
|
|
3200
|
-
return
|
|
3165
|
+
return this.classifyErrorByType(error);
|
|
3201
3166
|
}
|
|
3202
|
-
return 'object_error';
|
|
3203
3167
|
}
|
|
3204
|
-
return '
|
|
3168
|
+
return 'object_error';
|
|
3169
|
+
}
|
|
3170
|
+
/**
|
|
3171
|
+
* Determines error type classification based on the 'type' property
|
|
3172
|
+
*/
|
|
3173
|
+
classifyErrorByType(error) {
|
|
3174
|
+
const typedError = error;
|
|
3175
|
+
const rawType = typedError['type'];
|
|
3176
|
+
let errorType;
|
|
3177
|
+
if (typeof rawType === 'string') {
|
|
3178
|
+
errorType = rawType;
|
|
3179
|
+
}
|
|
3180
|
+
else if (rawType != null) {
|
|
3181
|
+
errorType = JSON.stringify(rawType);
|
|
3182
|
+
}
|
|
3183
|
+
else {
|
|
3184
|
+
errorType = 'unknown';
|
|
3185
|
+
}
|
|
3186
|
+
return `event_${errorType}`.toLowerCase();
|
|
3205
3187
|
}
|
|
3206
3188
|
isEdgeSyntheticEvent(error) {
|
|
3207
3189
|
// Check if running in Edge browser
|
|
@@ -3261,6 +3243,102 @@ class SentryErrorHandler {
|
|
|
3261
3243
|
const enhancedError = new Error(enhancedErrorMessage);
|
|
3262
3244
|
captureException(enhancedError);
|
|
3263
3245
|
}
|
|
3246
|
+
/**
|
|
3247
|
+
* Filters Sentry events before sending — extracted from beforeSend config
|
|
3248
|
+
* to reduce cognitive complexity (S3776).
|
|
3249
|
+
*
|
|
3250
|
+
* Drops or filters exception values for known noisy/irrelevant error types:
|
|
3251
|
+
* CloseEvent, ResizeObserver, JSON parsing, ChunkLoadError, script loading,
|
|
3252
|
+
* plotly timeout, cross-origin frame, non-error exceptions, and Edge synthetic events.
|
|
3253
|
+
*/
|
|
3254
|
+
static beforeSendFilter(event, hint) {
|
|
3255
|
+
const initialExceptionValuesLength = Array.isArray(event?.exception?.values)
|
|
3256
|
+
? event.exception.values.length
|
|
3257
|
+
: undefined;
|
|
3258
|
+
// Apply all exception-value filters in a single pass
|
|
3259
|
+
if (event?.exception?.values) {
|
|
3260
|
+
event.exception.values = SentryErrorHandler.filterExceptionValues(event.exception.values);
|
|
3261
|
+
}
|
|
3262
|
+
// Check for early-return (drop) conditions
|
|
3263
|
+
const dropReason = SentryErrorHandler.shouldDropEvent(event, hint);
|
|
3264
|
+
if (dropReason) {
|
|
3265
|
+
return null;
|
|
3266
|
+
}
|
|
3267
|
+
// If we started with exception values but filtered them all out, drop the event.
|
|
3268
|
+
if (typeof initialExceptionValuesLength === 'number' &&
|
|
3269
|
+
initialExceptionValuesLength > 0 &&
|
|
3270
|
+
Array.isArray(event?.exception?.values) &&
|
|
3271
|
+
event.exception.values.length === 0) {
|
|
3272
|
+
return null;
|
|
3273
|
+
}
|
|
3274
|
+
return event;
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Filters out known noisy exception values in a single pass.
|
|
3278
|
+
*/
|
|
3279
|
+
static filterExceptionValues(values) {
|
|
3280
|
+
const safeIncludes = (val, needle) => typeof val === 'string' && val.includes(needle);
|
|
3281
|
+
return values.filter(value => {
|
|
3282
|
+
// CloseEvent
|
|
3283
|
+
if (value.type === 'CloseEvent' || safeIncludes(value.value, 'CloseEvent')) {
|
|
3284
|
+
return false;
|
|
3285
|
+
}
|
|
3286
|
+
// ResizeObserver loop completed
|
|
3287
|
+
if (value.type === 'Error' &&
|
|
3288
|
+
safeIncludes(value.value, 'ResizeObserver loop completed')) {
|
|
3289
|
+
return false;
|
|
3290
|
+
}
|
|
3291
|
+
// ChunkLoadError
|
|
3292
|
+
if (value.type === 'ChunkLoadError' ||
|
|
3293
|
+
safeIncludes(value.value, 'ChunkLoadError: Loading chunk')) {
|
|
3294
|
+
return false;
|
|
3295
|
+
}
|
|
3296
|
+
// Script loading error
|
|
3297
|
+
if (value.type === 'Error' && safeIncludes(value.value, 'Could not load "util"')) {
|
|
3298
|
+
return false;
|
|
3299
|
+
}
|
|
3300
|
+
// Plotly timeout error
|
|
3301
|
+
if (value.type === 'Error' &&
|
|
3302
|
+
safeIncludes(value.value, 'Error loading plotly.js library from')) {
|
|
3303
|
+
return false;
|
|
3304
|
+
}
|
|
3305
|
+
return true;
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
3308
|
+
/**
|
|
3309
|
+
* Determines whether the entire event should be dropped (return truthy to drop).
|
|
3310
|
+
*/
|
|
3311
|
+
static shouldDropEvent(event, hint) {
|
|
3312
|
+
// JSON parsing error from vendor.js
|
|
3313
|
+
const hasJsonParsingError = event?.exception?.values?.some(value => value.type === 'SyntaxError' &&
|
|
3314
|
+
value?.value?.includes("expected ':' after property name in"));
|
|
3315
|
+
if (hasJsonParsingError) {
|
|
3316
|
+
return true;
|
|
3317
|
+
}
|
|
3318
|
+
// Cross-origin frame access error
|
|
3319
|
+
const hasCrossOriginError = event?.exception?.values?.some(value => value.type === 'SecurityError' &&
|
|
3320
|
+
value?.value?.includes("Failed to read a named property 'navigator' from 'Window': Blocked a frame with origin"));
|
|
3321
|
+
if (hasCrossOriginError) {
|
|
3322
|
+
return true;
|
|
3323
|
+
}
|
|
3324
|
+
// Non-error exception
|
|
3325
|
+
const isNonErrorException = event?.exception?.values?.[0]?.value?.startsWith('Non-Error exception captured') ??
|
|
3326
|
+
hint?.originalException?.message?.startsWith('Non-Error exception captured');
|
|
3327
|
+
if (isNonErrorException) {
|
|
3328
|
+
return true;
|
|
3329
|
+
}
|
|
3330
|
+
// Edge browser synthetic isTrusted events
|
|
3331
|
+
const isEdge = /Edg\//.test(navigator.userAgent);
|
|
3332
|
+
if (isEdge && hint?.originalException) {
|
|
3333
|
+
const originalException = hint.originalException;
|
|
3334
|
+
if (originalException &&
|
|
3335
|
+
typeof originalException === 'object' &&
|
|
3336
|
+
originalException['isTrusted'] === true) {
|
|
3337
|
+
return true;
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
return false;
|
|
3341
|
+
}
|
|
3264
3342
|
logToConsole(error) {
|
|
3265
3343
|
// Attempt to print in the console
|
|
3266
3344
|
try {
|