Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@emartech/me-sdk-bridge

Package Overview
Dependencies
Maintainers
0
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@emartech/me-sdk-bridge - npm Package Compare versions

Comparing version 1.12.0 to 1.13.0

2

dist/index.min.js

@@ -1,1 +0,1 @@

!function(e){const o={msgId:1},n=[{name:"buttonClicked",attr:"me-button-clicked",exec:function(e,t,n){a("buttonClicked",{buttonId:e},n)}},{name:"requestPushPermission",attr:"me-request-push-permission",exec:function(e,t,n){a("requestPushPermission",{},n)}},{name:"triggerAppEvent",attr:"me-trigger-app-event",exec:function(e,t,n){a("triggerAppEvent",{name:e.type,payload:e.payload},n)},fnWrap:function(e,t,n){this.exec({type:e,payload:t},void 0,n)}},{name:"triggerMEEvent",attr:"me-trigger-event",exec:function(e,t,n){a("triggerMEEvent",{name:e.type,payload:e.payload},n)},fnWrap:function(e,t,n){this.exec({type:e,payload:t},void 0,n)}},{name:"openExternalLink",attr:"href",exec:function(e,t,n){if("http"!==e.slice(0,4))return c(e+" is an invalid URL"),n();a("openExternalLink",{url:e},n)}},{name:"makeNetworkRequest",attr:"me-make-network-request",exec:function(e,t,n){a("makeNetworkRequest",{request:e},n)},fnWrap:function(e,t,n,o,i){this.exec({url:e,method:t,headers:n,payload:o},void 0,i)}},{name:"close",attr:"me-close",exec:function(e,t,n){!1===e?(n||s)():a("close",{},n)}},{name:"gotoStep",attr:"me-goto-step",exec:function(e,t,n){var o="ems-hidden",i=document.querySelector("[me-step]:not(."+o+")"),r=document.querySelector('[me-step="'+e+'"]');i?i.classList.add(o):c("Cannot find active step"),r?r.classList.remove(o):c('Cannot find next step with name "'+e+'"'),n()}},{name:"copyToClipboard",attr:"me-copy-to-clipboard",exec:function(e,t,n){if(!("string"==typeof e&&0<e.length))return c("parameter is not a string"),n();a("copyToClipboard",{text:e},n),t.classList.add("ems-inapp-button--copied-to-clipboard")}}],i=[],r={state:o,handleResponse:function(e){i[e.id]&&(i[e.id](e),delete i[e.id])},send:function(e,t){window.webkit?window.webkit.messageHandlers[e].postMessage(t):window.Android?window.Android[e](JSON.stringify(t)):window.EMSInappWebBridge&&window.EMSInappWebBridge[e](JSON.stringify(t))}};function a(e,t,n){t.id=(o.msgId++).toString(),i[t.id]=n||s,r.send(e,t)}function c(e){console.error(e)}function s(){}n.forEach(function(e){r[e.name]=e.fnWrap?e.fnWrap.bind(e):e.exec}),e.MEIAM=r,e.onload=function(){window.NodeList&&!NodeList.prototype.forEach&&(NodeList.prototype.forEach=Array.prototype.forEach);var e=n.map(function(e){return e.attr});document.querySelectorAll("["+e.join("],[")+"]").forEach(function(e){e.addEventListener("click",function(e){const o=this;var t=n.filter(function(e){return o.hasAttribute(e.attr)});e.preventDefault(),t.forEach(function(t){let n;try{n=JSON.parse(o.getAttribute(t.attr))}catch(e){n=o.getAttribute(t.attr)}try{t.exec(n,o,s)}catch(e){c(e)}})}.bind(e))}),document.querySelectorAll("[me-step]").forEach(function(e,t){0<t&&e.classList.add("ems-hidden")})}}(this);
!function(e){const o={msgId:1},n=[{name:"buttonClicked",attr:"me-button-clicked",exec:function(e,t,n){a("buttonClicked",{buttonId:e},n)}},{name:"requestPushPermission",attr:"me-request-push-permission",exec:function(e,t,n){a("requestPushPermission",{},n)}},{name:"triggerAppEvent",attr:"me-trigger-app-event",exec:function(e,t,n){a("triggerAppEvent",{name:e.type,payload:e.payload},n)},fnWrap:function(e,t,n){this.exec({type:e,payload:t},void 0,n)}},{name:"triggerMEEvent",attr:"me-trigger-event",exec:function(e,t,n){a("triggerMEEvent",{name:e.type,payload:e.payload},n)},fnWrap:function(e,t,n){this.exec({type:e,payload:t},void 0,n)}},{name:"openExternalLink",attr:"href",exec:function(e,t,n){if("http"!==e.slice(0,4))return c(e+" is an invalid URL"),n();a("openExternalLink",{url:e},n)}},{name:"makeNetworkRequest",attr:"me-make-network-request",exec:function(e,t,n){a("makeNetworkRequest",{request:e},n)},fnWrap:function(e,t,n,o,i){this.exec({url:e,method:t,headers:n,payload:o},void 0,i)}},{name:"close",attr:"me-close",exec:function(e,t,n){!1===e?(n||d)():a("close",{},n)}},{name:"gotoStep",attr:"me-goto-step",exec:function(e,t,n){var o="ems-hidden",i=document.querySelector("[me-step]:not(."+o+")"),r=document.querySelector('[me-step="'+e+'"]');i?i.classList.add(o):c("Cannot find active step"),r?r.classList.remove(o):c('Cannot find next step with name "'+e+'"'),n()}},{name:"copyToClipboard",attr:"me-copy-to-clipboard",exec:function(e,t,n){if(!("string"==typeof e&&0<e.length))return c("parameter is not a string"),n();a("copyToClipboard",{text:e},n),t.classList.add("ems-inapp-button--copied-to-clipboard")}}],i=[],r={state:o,handleResponse:function(e){i[e.id]&&(i[e.id](e),delete i[e.id])},send:function(e,t){window.webkit?window.webkit.messageHandlers[e].postMessage(t):window.Android?window.Android[e](JSON.stringify(t)):window.EMSInappWebBridge&&window.EMSInappWebBridge[e](JSON.stringify(t))}};function a(e,t,n){t.id=(o.msgId++).toString(),i[t.id]=n||d,r.send(e,t)}function c(e){console.error(e)}function d(){}function t(){var e=n.map(function(e){return e.attr});document.querySelectorAll("["+e.join("],[")+"]").forEach(function(e){e.addEventListener("click",function(e){const o=this;var t=n.filter(function(e){return o.hasAttribute(e.attr)});e.preventDefault(),t.forEach(function(t){let n;try{n=JSON.parse(o.getAttribute(t.attr))}catch(e){n=o.getAttribute(t.attr)}try{t.exec(n,o,d)}catch(e){c(e)}})}.bind(e))})}function s(){document.querySelectorAll("[me-step]").forEach(function(e,t){0<t&&e.classList.add("ems-hidden")})}n.forEach(function(e){r[e.name]=e.fnWrap?e.fnWrap.bind(e):e.exec}),e.MEIAM=r,window.EMSInappWebBridge&&(t(),s()),e.onload=function(){window.NodeList&&!NodeList.prototype.forEach&&(NodeList.prototype.forEach=Array.prototype.forEach),window.EMSInappWebBridge||(t(),s())}}(this);

@@ -163,2 +163,17 @@ (function (exports) {

function registerEventListeners () {
const attrs = meActions.map(function (action) { return action.attr })
document.querySelectorAll('[' + attrs.join('],[') + ']').forEach(function (el) {
el.addEventListener('click', onClick.bind(el))
})
}
function initializeSteps () {
document.querySelectorAll('[me-step]').forEach(function (el, idx) {
if (idx > 0) {
el.classList.add('ems-hidden')
}
})
}
// proxy all actions on MEIAM

@@ -171,2 +186,7 @@ meActions.forEach(function (meAction) {

if (window.EMSInappWebBridge) {
registerEventListeners()
initializeSteps()
}
// Previously we used event delegation, where we registered a click event listener on the document

@@ -189,14 +209,7 @@ // (git commit 7b5c4bf38c2992ea43bbeb91d88aeba0ca4ca3ac).

const attrs = meActions.map(function (action) { return action.attr })
document.querySelectorAll('[' + attrs.join('],[') + ']').forEach(function (el) {
el.addEventListener('click', onClick.bind(el))
})
// initialize steps
document.querySelectorAll('[me-step]').forEach(function (el, idx) {
if (idx > 0) {
el.classList.add('ems-hidden')
}
})
if (!window.EMSInappWebBridge) {
registerEventListeners()
initializeSteps()
}
}
})(this)

@@ -6,301 +6,354 @@ const chai = require('chai')

const sandbox = sinon.createSandbox()
chai.use(sinonChai)
describe('me-sdk-bridge', function () {
let meiam
let callbackSpy
let elementMock
beforeEach(function () {
meiam = require('./index').MEIAM
meiam.state.msgId = 1
sandbox.stub(meiam, 'send').returns(true)
callbackSpy = sandbox.spy()
elementMock = {
classList: {
add: sandbox.stub()
}
describe('me-sdk-bridge', () => {
const listenerSpy = sinon.spy(() => {})
const addClassListSpy = sinon.spy(() => {})
const testObject = {
addEventListener: listenerSpy,
classList: {
add: addClassListSpy
}
})
}
const testObject2 = {
addEventListener: listenerSpy,
classList: {
add: addClassListSpy
}
}
afterEach(function () {
sandbox.restore()
})
describe('EMSInappWebBridge NOT registered on window', function () {
const sandbox = sinon.createSandbox()
let meiam
let callbackSpy
let elementMock
it('should increase message id with every meiam.send call', function () {
meiam.close()
expect(meiam.send).to.have.been.calledWith('close', { id: '1' })
meiam.close()
expect(meiam.send).to.have.been.calledWith('close', { id: '2' })
meiam.close()
expect(meiam.send).to.have.been.calledWith('close', { id: '3' })
})
describe('#close', function () {
let message
beforeEach(function () {
message = { id: '1' }
global.window = {}
global.document = {
querySelectorAll: sinon.spy(() => [testObject, testObject2])
}
meiam = require('./index').MEIAM
meiam.state.msgId = 1
sandbox.stub(meiam, 'send').returns(true)
callbackSpy = sandbox.spy()
elementMock = {
classList: {
add: sandbox.stub()
}
}
})
it('should call meiam.send', function () {
meiam.close()
afterEach(function () {
sandbox.restore()
})
expect(meiam.send).to.have.been.calledWith('close', message)
it('should not add event listeners to element before onLoad', () => {
expect(listenerSpy).not.to.have.been.called
})
it('should not call meiam.send when the attribute value is "false"', function () {
meiam.close(false, elementMock)
it('should not initialize steps before onLoad', () => {
expect(addClassListSpy).not.to.have.been.called
})
expect(meiam.send).to.not.have.been.calledWith('close', message)
it('should increase message id with every meiam.send call', function () {
meiam.close()
expect(meiam.send).to.have.been.calledWith('close', { id: '1' })
meiam.close()
expect(meiam.send).to.have.been.calledWith('close', { id: '2' })
meiam.close()
expect(meiam.send).to.have.been.calledWith('close', { id: '3' })
})
it('should store callback when enabled', function () {
meiam.close('', elementMock, callbackSpy)
describe('#close', function () {
let message
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
beforeEach(function () {
message = { id: '1' }
})
it('should store callback when disabled', function () {
meiam.close(false, elementMock, callbackSpy)
it('should call meiam.send', function () {
meiam.close()
expect(callbackSpy).to.have.been.calledOnce
})
})
expect(meiam.send).to.have.been.calledWith('close', message)
})
describe('#buttonClicked', function () {
let buttonId
let message
it('should not call meiam.send when the attribute value is "false"', function () {
meiam.close(false, elementMock)
beforeEach(function () {
buttonId = 'testButton'
message = { id: '1', buttonId }
})
expect(meiam.send).to.not.have.been.calledWith('close', message)
})
it('should call meiam.send method with the buttonId in the message', function () {
meiam.buttonClicked(buttonId, elementMock)
it('should store callback when enabled', function () {
meiam.close('', elementMock, callbackSpy)
expect(meiam.send).to.have.been.calledWith('buttonClicked', message)
})
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
it('should store callback', function () {
meiam.buttonClicked(buttonId, elementMock, callbackSpy)
it('should store callback when disabled', function () {
meiam.close(false, elementMock, callbackSpy)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
expect(callbackSpy).to.have.been.calledOnce
})
})
})
describe('#openExternalLink', function () {
let message
let url
describe('#buttonClicked', function () {
let buttonId
let message
beforeEach(function () {
url = 'http://lobab.hu'
message = { id: '1', url }
})
beforeEach(function () {
buttonId = 'testButton'
message = { id: '1', buttonId }
})
it('should do nothing if the url does not start with http', function () {
meiam.openExternalLink('htt://notvalid.com', elementMock, callbackSpy)
expect(meiam.send).not.to.have.been.called
it('should call meiam.send method with the buttonId in the message', function () {
meiam.buttonClicked(buttonId, elementMock)
meiam.handleResponse({ id: '1' })
expect(callbackSpy).to.have.been.calledOnce
expect(meiam.send).to.have.been.calledWith('buttonClicked', message)
})
it('should store callback', function () {
meiam.buttonClicked(buttonId, elementMock, callbackSpy)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should call meiam.send on openExternalLink method with the url in the message', function () {
meiam.openExternalLink(url, elementMock, callbackSpy)
describe('#openExternalLink', function () {
let message
let url
expect(meiam.send).to.have.been.calledWith('openExternalLink', message)
beforeEach(function () {
url = 'http://lobab.hu'
message = { id: '1', url }
})
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
it('should do nothing if the url does not start with http', function () {
meiam.openExternalLink('htt://notvalid.com', elementMock, callbackSpy)
expect(meiam.send).not.to.have.been.called
it('should store callback', function () {
meiam.openExternalLink(url, elementMock, callbackSpy)
meiam.handleResponse({ id: '1' })
expect(callbackSpy).to.have.been.calledOnce
})
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should call meiam.send on openExternalLink method with the url in the message', function () {
meiam.openExternalLink(url, elementMock, callbackSpy)
describe('#requestPushPermission', function () {
let message
expect(meiam.send).to.have.been.calledWith('openExternalLink', message)
beforeEach(function () {
message = { id: '1' }
})
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
it('should call meiam.send', function () {
meiam.requestPushPermission(undefined, elementMock)
it('should store callback', function () {
meiam.openExternalLink(url, elementMock, callbackSpy)
expect(meiam.send).to.have.been.calledWith('requestPushPermission', message)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should store callback', function () {
meiam.requestPushPermission(undefined, elementMock, callbackSpy)
describe('#requestPushPermission', function () {
let message
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
beforeEach(function () {
message = { id: '1' }
})
describe('#triggerAppEvent', function () {
let eventName
let payload
let message
it('should call meiam.send', function () {
meiam.requestPushPermission(undefined, elementMock)
beforeEach(function () {
eventName = 'eventName'
payload = { lo: 'bab' }
message = { id: '1', name: eventName, payload }
})
expect(meiam.send).to.have.been.calledWith('requestPushPermission', message)
})
it('should call meiam.send with the payload in the message', function () {
meiam.triggerAppEvent(eventName, payload)
it('should store callback', function () {
meiam.requestPushPermission(undefined, elementMock, callbackSpy)
expect(meiam.send).to.have.been.calledWith('triggerAppEvent', message)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should store callback', function () {
meiam.triggerAppEvent(eventName, payload, callbackSpy)
describe('#triggerAppEvent', function () {
let eventName
let payload
let message
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
beforeEach(function () {
eventName = 'eventName'
payload = { lo: 'bab' }
message = { id: '1', name: eventName, payload }
})
describe('#triggerMEEvent', function () {
let eventName
let payload
let message
it('should call meiam.send with the payload in the message', function () {
meiam.triggerAppEvent(eventName, payload)
beforeEach(function () {
eventName = 'meEventName'
payload = { lo: 'bab' }
message = { id: '1', name: eventName, payload }
})
expect(meiam.send).to.have.been.calledWith('triggerAppEvent', message)
})
it('should call meiam.send with the payload in the message', function () {
meiam.triggerMEEvent(eventName, payload)
it('should store callback', function () {
meiam.triggerAppEvent(eventName, payload, callbackSpy)
expect(meiam.send).to.have.been.calledWith('triggerMEEvent', message)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should store callback', function () {
meiam.triggerMEEvent(eventName, payload, callbackSpy)
describe('#triggerMEEvent', function () {
let eventName
let payload
let message
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
beforeEach(function () {
eventName = 'meEventName'
payload = { lo: 'bab' }
message = { id: '1', name: eventName, payload }
})
it('should call meiam.send with the payload in the message', function () {
meiam.triggerMEEvent(eventName, payload)
expect(meiam.send).to.have.been.calledWith('triggerMEEvent', message)
})
it('should store callback', function () {
meiam.triggerMEEvent(eventName, payload, callbackSpy)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
})
describe('#makeNetworkRequest', function () {
let url
let method
let headers
let payload
let message
describe('#makeNetworkRequest', function () {
let url
let method
let headers
let payload
let message
beforeEach(function () {
url = 'https://www.google.com'
method = 'POST'
headers = { 'content-type': 'application/json' }
payload = { payloadKey: 'payloadvalue' }
message = {
id: '1',
request: {
beforeEach(function () {
url = 'https://www.google.com'
method = 'POST'
headers = { 'content-type': 'application/json' }
payload = { payloadKey: 'payloadvalue' }
message = {
id: '1',
request: {
url,
method,
headers,
payload
}
}
})
it('should call meiam.send with the request details in the message', function () {
meiam.makeNetworkRequest(
url,
method,
headers,
payload
}
}
})
payload,
callbackSpy
)
it('should call meiam.send with the request details in the message', function () {
meiam.makeNetworkRequest(
url,
method,
headers,
payload,
callbackSpy
)
expect(meiam.send).to.have.been.calledWith('makeNetworkRequest', message)
})
expect(meiam.send).to.have.been.calledWith('makeNetworkRequest', message)
it('should store callback', function () {
meiam.makeNetworkRequest(
url,
method,
headers,
payload,
callbackSpy
)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should store callback', function () {
meiam.makeNetworkRequest(
url,
method,
headers,
payload,
callbackSpy
)
describe('#handleResponse', function () {
it('should not explode when called with a non-existing id', function () {
expect(() => {
meiam.handleResponse({ id: 'yolo' })
}).not.to.throw()
})
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should invoke the callback function only once', function () {
meiam.close('', elementMock, callbackSpy)
describe('#handleResponse', function () {
it('should not explode when called with a non-existing id', function () {
expect(() => {
meiam.handleResponse({ id: 'yolo' })
}).not.to.throw()
meiam.handleResponse({ id: '1' })
meiam.handleResponse({ id: '1' })
expect(callbackSpy).to.have.been.calledOnce
})
})
it('should invoke the callback function only once', function () {
meiam.close('', elementMock, callbackSpy)
describe('#copyToClipboard', function () {
let message
let text
meiam.handleResponse({ id: '1' })
meiam.handleResponse({ id: '1' })
expect(callbackSpy).to.have.been.calledOnce
})
})
beforeEach(function () {
text = 'this is something to copy to clipboard'
message = { id: '1', text }
})
describe('#copyToClipboard', function () {
let message
let text
it('should do nothing if the text is not a string', function () {
meiam.copyToClipboard(true, elementMock, callbackSpy)
expect(meiam.send).not.to.have.been.called
beforeEach(function () {
text = 'this is something to copy to clipboard'
message = { id: '1', text }
})
meiam.handleResponse({ id: '1' })
expect(callbackSpy).to.have.been.calledOnce
})
it('should do nothing if the text is not a string', function () {
meiam.copyToClipboard(true, elementMock, callbackSpy)
expect(meiam.send).not.to.have.been.called
it('should call meiam.send on copyToClipboard method with the text in the message', function () {
meiam.copyToClipboard(text, elementMock, callbackSpy)
meiam.handleResponse({ id: '1' })
expect(callbackSpy).to.have.been.calledOnce
})
expect(meiam.send).to.have.been.calledWith('copyToClipboard', message)
it('should call meiam.send on copyToClipboard method with the text in the message', function () {
meiam.copyToClipboard(text, elementMock, callbackSpy)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
expect(meiam.send).to.have.been.calledWith('copyToClipboard', message)
it('should add class to the clicked element', function () {
meiam.copyToClipboard(text, elementMock, callbackSpy)
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
expect(elementMock.classList.add).to.have.been.calledWithExactly('ems-inapp-button--copied-to-clipboard')
})
it('should add class to the clicked element', function () {
meiam.copyToClipboard(text, elementMock, callbackSpy)
it('should store callback', function () {
meiam.copyToClipboard(text, elementMock, callbackSpy)
expect(elementMock.classList.add).to.have.been.calledWithExactly('ems-inapp-button--copied-to-clipboard')
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
})
})
})
it('should store callback', function () {
meiam.copyToClipboard(text, elementMock, callbackSpy)
describe('EMSInappWebBridge registered on window', () => {
beforeEach(function () {
delete require.cache[require.resolve('./index')]
global.window = {}
Object.assign(window, {
EMSInappWebBridge: {}
})
global.document = {
querySelectorAll: sinon.spy(() => [testObject, testObject2])
}
require('./index').MEIAM
})
meiam.handleResponse({ id: message.id })
expect(callbackSpy).to.have.been.calledOnce
afterEach(() => {
sinon.reset()
})
it('should add event listeners to element', () => {
expect(listenerSpy).to.have.been.called
})
it('should add initialize steps', () => {
expect(addClassListSpy).to.have.been.calledOnce
})
})
})

@@ -35,13 +35,13 @@ {

"chai": "4.3.7",
"cypress": "^12.3.0",
"cypress": "^12.17.1",
"eslint": "^8.32.0",
"eslint-config-standard": "^17.0.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-mocha": "^10.1.0",
"http-server": "14.1.1",
"mocha": "^10.2.0",
"semantic-release": "^19.0.5",
"semantic-release": "^21.0.1",
"sinon": "15.0.1",
"sinon-chai": "3.7.0",
"standard": "^17.0.0",
"start-server-and-test": "1.15.2",
"start-server-and-test": "2.0.0",
"uglify-js": "^3.17.4"

@@ -53,3 +53,3 @@ },

},
"version": "1.12.0"
"version": "1.13.0"
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc