soundcloud-iframe-analytics
Advanced tools
Comparing version 1.0.6 to 2.0.0
@@ -1,2 +0,2 @@ | ||
!function(e){var n={};function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)t.d(o,r,function(n){return e[n]}.bind(null,r));return o},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=3)}([function(e,n,t){n.loadScript=t(1),n.loadScriptPromised=t(2)},function(e,n){e.exports=function(e,n,t){var o,r,i=document;r=i.createElement("script"),o=i.getElementsByTagName("script")[0],t&&Object.keys(t).forEach((function(e){r[e]=t[e]})),r.async=1,r.src=e,r.onload=function(){n()},r.onerror=function(){n(new Error("failed to load: "+e))},o.parentNode.insertBefore(r,o)}},function(e,n){e.exports=function(e,n){var t,o,r=document;return new Promise((function(i,u){o=r.createElement("script"),t=r.getElementsByTagName("script")[0],n&&Object.keys(n).forEach((function(e){o[e]=n[e]})),o.async=1,o.src=e,o.onload=function(){i()},o.onerror=function(){u(new Error("failed to load: "+e))},t.parentNode.insertBefore(o,t)}))}},function(e,n,t){"use strict";t.r(n);var o,r=t(0),i=t.n(r),u=!0;function d(e,n,t,r){if(u){var i=function(){if(o)return o;"function"==typeof window.gtag?o=c.GlobalSiteTag:"function"==typeof window.ga?o=c.GA:"_gaq"in window&&"function"==typeof window._gaq.push&&(o=c.Legacy);return o||(u=!1,null)}();i&&i.event(e,n,t,r)}}var c={GlobalSiteTag:{event:function(e,n,t,o){window.gtag("event",n,{event_category:e,event_label:t,value:o})}},GA:{event:function(e,n,t,o){window.ga("send","event",e,n,t)}},Legacy:{event:function(e,n,t,o){window._gaq.push(["_trackEvent",e,n,t])}}};function s(){var e=document.getElementsByTagName("iframe"),n=[];if(f(e,(function(e){e.hasAttribute("src")&&e.getAttribute("src").indexOf("soundcloud.com")>-1&&n.push(e)})),0!==n.length){var t=function(){f(n,(function(e){!function(e){var n,t=SC.Widget.Events,o=!1,r="",i=0,u={};e.bind(t.READY,(function(){})),e.bind(t.ERROR,(function(){d("SoundCloud","Error",r)})),e.bind(t.PLAY_PROGRESS,(function(t){n&&n.id===t.soundId&&function(e,n){if("number"!=typeof n.relativePosition)return;var t;e.progress=function(e){return Math.round(100*e.relativePosition)}(n),e.progress>=99&&e.progstep<4?(t="4/4",e.progstep=4):e.progress>=75&&e.progstep<3?(t="3/4",e.progstep=3):e.progress>=50&&e.progstep<2?(t="2/4",e.progstep=2):e.progress>=25&&e.progstep<1&&(t="1/4",e.progstep=1);"string"==typeof t&&(e.scrubbed&&(t+=" with scrubbing"),d("SoundCloud","Progress ".concat(t),e.title))}(n,t),o||(o=!0,setTimeout((function(){o=!1,e.getCurrentSound((function(e){i!==e.id&&(r=e.title,i=e.id,u={})}))}),0===i?0:2500))})),e.bind(t.PLAY,(function(){e.getCurrentSound((function(e){r=e.title,i=e.id,!(n=a(u,e.title,e.id)).started||n.finished?(n.started=!0,n.finished=!1,n.paused=!1,n.scrubbed=!1,n.progress=0,n.progstep=0,d("SoundCloud","Playback started",r)):n.paused&&(n.paused=!1,d("SoundCloud","Playback resumed",r))}))})),e.bind(t.PAUSE,(function(){n=a(u,r,i),e.getCurrentSound((function(e){e.id!==n.id||n.finished||(n.paused=!0,d("SoundCloud","Playback paused",r))}))})),e.bind(t.SEEK,(function(e){(n=a(u,r,i)).scrubbed||n.paused||n.finished||(n.scrubbed=!0,d("SoundCloud","Playback scrubbed",r))})),e.bind(t.FINISH,(function(){(n=a(u,r,i)).finished||(n.finished=!0,d("SoundCloud",n.scrubbed?"Played in full with scrubbing":"Played in full",r))}))}(SC.Widget(e))}))};"SC"in window&&"function"==typeof SC.Widget?t():i.a.loadScript("https://w.soundcloud.com/player/api.js",t)}}function a(e,n,t){return e.hasOwnProperty(n)?0===e[n].id&&(e[n].id=t):e[n]={title:n,id:t,started:!1,paused:!1,scrubbed:!1,finished:!1,progress:0,progstep:0},e[n]}function f(e,n){for(var t=0,o=e.length;t<o;++t)n(e[t])}document.addEventListener("DOMContentLoaded",(function e(){s(),document.removeEventListener("DOMContentLoaded",e)}))}]); | ||
!function(n){var t={};function e(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var o in n)e.d(r,o,function(t){return n[t]}.bind(null,o));return r},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=3)}([function(n,t,e){t.loadScript=e(1),t.loadScriptPromised=e(2)},function(n,t){n.exports=function(n,t,e){var r,o,i=document;o=i.createElement("script"),r=i.getElementsByTagName("script")[0],e&&Object.keys(e).forEach((function(n){o[n]=e[n]})),o.async=1,o.src=n,o.onload=function(){t()},o.onerror=function(){t(new Error("failed to load: "+n))},r.parentNode.insertBefore(o,r)}},function(n,t){n.exports=function(n,t){var e,r,o=document;return new Promise((function(i,u){r=o.createElement("script"),e=o.getElementsByTagName("script")[0],t&&Object.keys(t).forEach((function(n){r[n]=t[n]})),r.async=1,r.src=n,r.onload=function(){i()},r.onerror=function(){u(new Error("failed to load: "+n))},e.parentNode.insertBefore(r,e)}))}},function(n,t,e){"use strict";e.r(t),e.d(t,"init",(function(){return s})),e.d(t,"attachSoundCloudAnalytics",(function(){return f}));var r,o=e(0),i=e.n(o),u=!0;function d(n,t,e,o){if(u){var i=function(){if(r)return r;"function"==typeof window.gtag?r=c.GlobalSiteTag:"function"==typeof window.ga?r=c.GA:"_gaq"in window&&"function"==typeof window._gaq.push&&(r=c.Legacy);return r||(u=!1,null)}();i&&i.event(n,t,e,o)}}var c={GlobalSiteTag:{event:function(n,t,e,r){window.gtag("event",t,{event_category:n,event_label:e,value:r})}},GA:{event:function(n,t,e,r){window.ga("send","event",n,t,e)}},Legacy:{event:function(n,t,e,r){window._gaq.push(["_trackEvent",n,t,e])}}},a="SC"in window&&"function"==typeof window.SC.Widget?window.SC.Widget:null;function s(){return new Promise((function(n,t){var e=function(){var t=document.getElementsByTagName("iframe"),e=[];p(t,(function(n){n.hasAttribute("src")&&n.getAttribute("src").includes("soundcloud.com")&&e.push(n)}));var r=[];p(e,(function(n){r.push(f(n))})),n(r.filter(Boolean))};a?e():i.a.loadScript("https://w.soundcloud.com/player/api.js",(function(){a=a||window.SC.Widget,e()}),t)}))}function f(n){if(!n)return null;var t,e=n.getAttribute("data-sia"),r=n.getAttribute("src");if(e&&e===r)return!1;n.setAttribute("data-sia",r);try{t=a(n)}catch(n){return null}var o,i=a.Events,u=!1,c="",s=0,f={};return t.bind(i.ERROR,(function(){d("SoundCloud","Error",c)})),t.bind(i.PLAY_PROGRESS,(function(n){o&&o.id===n.soundId&&function(n,t){if("number"!=typeof t.relativePosition)return;var e;n.progress=function(n){return Math.round(100*n.relativePosition)}(t),n.progress>=99&&n.progstep<4?(e="4/4",n.progstep=4):n.progress>=75&&n.progstep<3?(e="3/4",n.progstep=3):n.progress>=50&&n.progstep<2?(e="2/4",n.progstep=2):n.progress>=25&&n.progstep<1&&(e="1/4",n.progstep=1);"string"==typeof e&&(n.scrubbed&&(e+=" with scrubbing"),d("SoundCloud","Progress ".concat(e),n.title))}(o,n),u||(u=!0,setTimeout((function(){u=!1,t.getCurrentSound((function(n){s!==n.id&&(c=n.title,s=n.id,f={})}))}),0===s?0:2500))})),t.bind(i.PLAY,(function(){t.getCurrentSound((function(n){c=n.title,s=n.id,!(o=l(f,n.title,n.id)).started||o.finished?(o.started=!0,o.finished=!1,o.paused=!1,o.scrubbed=!1,o.progress=0,o.progstep=0,d("SoundCloud","Playback started",c)):o.paused&&(o.paused=!1,d("SoundCloud","Playback resumed",c))}))})),t.bind(i.PAUSE,(function(){o=l(f,c,s),t.getCurrentSound((function(n){n.id!==o.id||o.finished||(o.paused=!0,d("SoundCloud","Playback paused",c))}))})),t.bind(i.SEEK,(function(n){(o=l(f,c,s)).scrubbed||o.paused||o.finished||(o.scrubbed=!0,d("SoundCloud","Playback scrubbed",c))})),t.bind(i.FINISH,(function(){(o=l(f,c,s)).finished||(o.finished=!0,d("SoundCloud",o.scrubbed?"Played in full with scrubbing":"Played in full",c))})),{element:n,widget:t,dispose:function(){[i.ERROR,i.PLAY_PROGRESS,i.PLAY,i.PAUSE,i.SEEK,i.FINISH].forEach((function(n){t.unbind(n)})),n.removeAttribute("data-sia")}}}function l(n,t,e){return n.hasOwnProperty(t)?0===n[t].id&&(n[t].id=e):n[t]={title:t,id:e,started:!1,paused:!1,scrubbed:!1,finished:!1,progress:0,progstep:0},n[t]}function p(n,t){for(var e=0,r=n.length;e<r;++e)t(n[e])}}]); | ||
//# sourceMappingURL=sia.min.js.map |
{ | ||
"name": "soundcloud-iframe-analytics", | ||
"version": "1.0.6", | ||
"description": "Automated Google Analytics tracking of user interaction on embedded SoundCloud iframes", | ||
"version": "2.0.0", | ||
"description": "Google Analytics tracking of user interaction on embedded SoundCloud iframes", | ||
"author": "Igor Zinken", | ||
@@ -11,3 +11,3 @@ "license": "MIT", | ||
"build": "webpack -p --progress --mode production --config webpack.config.build.js", | ||
"test": "webpack -p --progress --mode development --config webpack.config.test.js && npm run dev", | ||
"test": "jest", | ||
"prepublish": "npm run build" | ||
@@ -24,3 +24,2 @@ }, | ||
"babel-loader": "8.1.0", | ||
"chai": "^1.9.2", | ||
"clean-webpack-plugin": "0.1.19", | ||
@@ -32,12 +31,10 @@ "cross-env": "5.1.4", | ||
"html-webpack-plugin": "3.2.0", | ||
"mocha": "^7.1.2", | ||
"mocha-loader": "^5.0.0", | ||
"jest": "^26.1.0", | ||
"node-sass": "^4.14.0", | ||
"sass-loader": "7.0.1", | ||
"sinon": "^2.3.4", | ||
"style-loader": "0.21.0", | ||
"url-loader": "1.0.1", | ||
"webpack": "4.43.0", | ||
"webpack-cli": "3.3.11", | ||
"webpack-dev-server": ">=3.1.11", | ||
"webpack": "^4.43.0", | ||
"webpack-cli": "2.0.14", | ||
"webpack-dev-server": "^3.11.0", | ||
"webpack-merge": "4.1.2" | ||
@@ -44,0 +41,0 @@ }, |
125
README.md
@@ -1,8 +0,6 @@ | ||
SoundCloud IFRAME Analytics | ||
=========================== | ||
# SoundCloud iframe Analytics | ||
A minimal library that attaches Google Analytics event tracking to user interactions | ||
performed on embedded SoundCloud iframes, both on single tracks as well as playlists. It should | ||
work from IE8 upwards - though the real concern with compatibility is probably restricted to the | ||
requirements of the SoundCloud embed itself - | ||
SoundCloud Iframe Analytics (SIA) is a minimal library that attaches Google Analytics event tracking | ||
to user interactions performed on SoundCloud iframes embedded within your HTML page, both on single | ||
tracks as well as full playlists. | ||
@@ -13,3 +11,3 @@ This allows you to track user behaviour as well as have the events act as beacons to | ||
Several versions of Google Analytics trackers are supported, namely: | ||
Multiple versions of the Google Analytics tracker are supported, namely: | ||
@@ -26,38 +24,83 @@ * Global Site Tag (gtag) | ||
npm install soundcloud-iframe-analytics --save-dev | ||
``` | ||
npm install soundcloud-iframe-analytics --save-dev | ||
``` | ||
## How to integrate within your application | ||
Firstly, embed the Analytics tracking code as provided by Google into your template(s). | ||
First, embed the Analytics tracking code as provided by Google into your HTML template(s). | ||
### The easy way, just drop in the JS file | ||
Then, add a SoundCloud iframe embed similar to the below: | ||
Embed the SoundCloud iframes according to the embed code provided by SoundCloud. You do not need to make | ||
any changes to your markup. E.g. simply inject one or more instances of: | ||
<iframe width="100%" height="300" | ||
scrolling="no" frameborder="no" | ||
src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/{STRING_ID}&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true"> | ||
```html | ||
<iframe | ||
width="100%" height="300" | ||
scrolling="no" frameborder="no" | ||
src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/{STRING_ID}&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true"> | ||
</iframe> | ||
``` | ||
Include the minimized production version of the script (_./dist/sia.min.js_) at the bottom of your page | ||
and the script will automatically attach event listeners to the iframes. | ||
Basically, you embed the SoundCloud iframes according to the embed code provided by SoundCloud. | ||
You do not need to make any changes to the generated markup. | ||
### The easy way : automatically attach tracking to static HTML pages | ||
When your HTML pages are static / contain the iframe content upon delivery, you can | ||
easily attach the Analytics tracking by adding the following snippet to your JavaScript code: | ||
```js | ||
import { init } from "soundcloud-iframe-analytics"; | ||
async function readyHandler() { | ||
document.removeEventListener( "DOMContentLoaded", readyHandler ); | ||
const embeds = await init(); | ||
} | ||
document.addEventListener( "DOMContentLoaded", readyHandler ); | ||
``` | ||
The above will run once when the document finishes loading. It will then scan the document for | ||
iframes with SoundCloud content and attach the listeners automatically. The returned value is | ||
a list of successfully bound listeners for each iframe, where each value is wrapped inside | ||
an object like so: | ||
```js | ||
{ | ||
element : HTMLIFrameElement, | ||
widget : SC.Widget, | ||
dispose : Function | ||
} | ||
``` | ||
In case you are wondering what those are good for, it's good to know that if your SoundCloud | ||
content remains on the page throughout its lifetime, you can safely ignore these. But if you | ||
are curious, you are likely someone who is looking for... | ||
### The "I want full control" way | ||
Alternatively, you can import the ES6 modules from the _./src_ folder and embed the library | ||
within your custom application code. You can use this in case you already use the SoundCloud Widget | ||
API to inject/control SoundCloud content in your page. | ||
In case your page is an SPA that injects/removes SoundCloud iframes at runtime, you | ||
need to keep track of additionally added iframes _after_ the document has finished loading. | ||
You probably also want to clean up after yourself when you no longer need these iframes. | ||
You can attach Analytics triggers to Widget events by passing an existing instance of _SC.Widget_ to | ||
the _attachSoundCloudAnalytics()_ function of the _SoundCloud.js_ file, e.g.: | ||
You can attach Analytics triggers to injected iframes by passing their reference to the | ||
_attachSoundCloudAnalytics()_-method. Your pseudo code would look like: | ||
```JavaScript | ||
import { attachSoundCloudAnalytics } from "soundcloud-iframe-analytics/src/soundcloud/SoundCloud"; | ||
```js | ||
import { init, attachSoundCloudAnalytics } from "soundcloud-iframe-analytics"; | ||
const existingWidget = ...; // SC.Widget instance created elsewhere in your application | ||
attachSoundCloudAnalytics( existingWidget ); | ||
async function executedOnce() { | ||
await init(); // loads SoundCloud SDK | ||
} | ||
function executeAfterNewIframeIsInjected( iframeReference ) { | ||
const result = attachSoundCloudAnalytics( iframeReference ); | ||
if ( result !== null ) { | ||
// SoundCloud Analytics attached successfully | ||
// invoke dispose() when the iframe is no longer needed / removed from page | ||
const { element, widget, dispose } = result; | ||
} | ||
} | ||
``` | ||
Bob's your uncle. | ||
And Bob's your uncle. SIA will automatically detect whether the same iframe is | ||
passed for attachment of Analytics events and will deduplicate everything accordingly. | ||
@@ -98,3 +141,5 @@ ## Event message format | ||
npm install | ||
``` | ||
npm install | ||
``` | ||
@@ -107,7 +152,11 @@ ### Local development | ||
npm run dev | ||
``` | ||
npm run dev | ||
``` | ||
### Creating a production build | ||
npm run build | ||
``` | ||
npm run build | ||
``` | ||
@@ -118,13 +167,13 @@ Build output will be stored in _./dist_-folder. | ||
Unit tests are run via Mocha, which is installed as a dependency, along | ||
with Chai as the assertion library. You can run the tests by using: | ||
Unit tests are run via Jest, which is installed as a dependency. | ||
You can run the tests by using: | ||
npm test | ||
``` | ||
npm test | ||
``` | ||
Unit tests go in the _./test_-folder. The file name for a unit test must | ||
be equal to the file it is testing, but contain the suffix ".test", | ||
e.g. _Functions.js_ will have a test file _Functions.test.js_. | ||
be equal to the file it is testing, but contain the suffix ".spec.js", | ||
e.g. _Functions.js_ will have a test file _Functions.spec.js_. | ||
Tests will be available at _http://localhost:8080/test/test.html_. | ||
### Configuration | ||
@@ -131,0 +180,0 @@ |
@@ -1,19 +0,21 @@ | ||
const path = require('path'); | ||
const merge = require('webpack-merge'); | ||
const CleanWebpackPlugin = require('clean-webpack-plugin'); | ||
const webpackConfig = require('./webpack.config'); | ||
const path = require( "path" ); | ||
const merge = require( "webpack-merge" ); | ||
const CleanWebpackPlugin = require( "clean-webpack-plugin" ); | ||
const webpackConfig = require( "./webpack.config" ); | ||
const OUTPUT_FOLDER = "dist"; | ||
module.exports = merge( webpackConfig, { | ||
devtool: 'source-map', | ||
devtool: "source-map", | ||
output: { | ||
path: path.join(__dirname, 'dist'), | ||
filename: '[name].min.js' | ||
path : path.join( __dirname, OUTPUT_FOLDER ), | ||
filename : "[name].min.js" | ||
}, | ||
plugins: [ | ||
new CleanWebpackPlugin(['dist']) | ||
new CleanWebpackPlugin([ OUTPUT_FOLDER ]) | ||
] | ||
}); |
@@ -1,13 +0,33 @@ | ||
const merge = require('webpack-merge'); | ||
const webpackConfig = require('./webpack.config'); | ||
const path = require( "path" ); | ||
const webpack = require( "webpack" ); | ||
const merge = require( "webpack-merge" ); | ||
const HtmlWebpackPlugin = require( "html-webpack-plugin" ); | ||
const webpackConfig = require( "./webpack.config" ); | ||
const DEMO_FOLDER = path.join( __dirname, "demo" ); | ||
module.exports = merge( webpackConfig, { | ||
devtool: 'eval', | ||
entry: { | ||
sia: `${DEMO_FOLDER}/index.js` | ||
}, | ||
devtool: "eval", | ||
output: { | ||
pathinfo : true, | ||
publicPath: '/', | ||
filename : '[name].js' | ||
} | ||
publicPath: "/", | ||
filename : "[name].js" | ||
}, | ||
plugins: [ | ||
new webpack.DefinePlugin({ | ||
IS_DEV: true | ||
}), | ||
new HtmlWebpackPlugin({ | ||
template : `${DEMO_FOLDER}/index.ejs`, | ||
title : "dev" | ||
}) | ||
], | ||
}); |
@@ -1,11 +0,7 @@ | ||
const path = require('path'); | ||
const webpack = require('webpack'); | ||
const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
const path = require( "path" ); | ||
const webpack = require( "webpack" ); | ||
// Is the current build a development build | ||
const IS_DEV = (process.env.NODE_ENV === 'dev'); | ||
const dirNode = "node_modules"; | ||
const dirApp = path.join( __dirname, "src" ); | ||
const dirNode = 'node_modules'; | ||
const dirApp = path.join(__dirname, 'src'); | ||
/** | ||
@@ -16,3 +12,3 @@ * Webpack Configuration | ||
entry: { | ||
sia: path.join(dirApp, 'index') | ||
sia: "./index" | ||
}, | ||
@@ -25,12 +21,2 @@ resolve: { | ||
}, | ||
plugins: [ | ||
new webpack.DefinePlugin({ | ||
IS_DEV: IS_DEV | ||
}), | ||
new HtmlWebpackPlugin({ | ||
template: path.join(__dirname, 'index.ejs'), | ||
title: "dev" | ||
}) | ||
], | ||
module: { | ||
@@ -41,3 +27,3 @@ rules: [ | ||
test: /\.js$/, | ||
loader: 'babel-loader', | ||
loader: "babel-loader", | ||
exclude: /(node_modules)/, | ||
@@ -44,0 +30,0 @@ options: { |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
65052
19
650
178
0
18