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

bss

Package Overview
Dependencies
Maintainers
1
Versions
69
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bss - npm Package Compare versions

Comparing version 1.6.3 to 3.0.0-alpha

dist/bss.esm.js

363

lib/index.js

@@ -1,363 +0,6 @@

/* eslint no-invalid-this: 0 */
import pseudos from './pseudos.js'
import popular from './popular.js'
import create from './create.js'
import {
classPrefix,
createClass,
setDebug,
getSheet,
getRules,
insert
} from './sheet'
const bss = create()
bss.create = create
import {
hyphenToCamelCase,
vendorValuePrefix,
lowercaseFirst,
objectToRules,
selectorSplit,
cssProperties,
stylesToCss,
vendorRegex,
vendorMap,
initials,
memoize,
isProp,
assign,
add
} from './utils'
const shorts = Object.create(null)
function bss(input, value) {
const b = chain(bss)
input && assign(b.__style, parse.apply(null, arguments))
return b
}
function setProp(prop, value) {
Object.defineProperty(bss, prop, {
configurable: true,
value
})
}
Object.defineProperties(bss, {
__style: {
configurable: true,
writable: true,
value: {}
},
valueOf: {
configurable: true,
writable: true,
value: function() {
return '.' + this.class
}
},
toString: {
configurable: true,
writable: true,
value: function() {
return this.class
}
}
})
setProp('setDebug', setDebug)
setProp('$keyframes', keyframes)
setProp('$media', $media)
setProp('$import', $import)
setProp('$nest', $nest)
setProp('getSheet', getSheet)
setProp('getRules', getRules)
setProp('helper', helper)
setProp('css', css)
setProp('classPrefix', classPrefix)
function chain(instance) {
const newInstance = Object.create(bss, {
__style: {
value: assign({}, instance.__style)
},
style: {
enumerable: true,
get: function() {
return Object.keys(this.__style).reduce((acc, key) => {
if (typeof this.__style[key] === 'number' || typeof this.__style[key] === 'string')
acc[key.replace(/^!/, '')] = this.__style[key]
return acc
}, {})
}
}
})
if (instance === bss)
bss.__style = {}
return newInstance
}
cssProperties.forEach(prop => {
const vendor = prop.match(vendorRegex)
if (vendor) {
const unprefixed = lowercaseFirst(prop.replace(vendorRegex, '$2'))
if (cssProperties.indexOf(unprefixed) === -1) {
if (unprefixed === 'flexDirection')
vendorValuePrefix.flex = '-' + vendor[1].toLowerCase() + '-flex'
vendorMap[unprefixed] = prop
setProp(unprefixed, setter(prop))
setProp(short(unprefixed), bss[unprefixed])
return
}
}
setProp(prop, setter(prop))
setProp(short(prop), bss[prop])
})
setProp('content', function Content(arg) {
const b = chain(this)
arg === null || arg === undefined || arg === false
? delete b.__style.content
: b.__style.content = '"' + arg + '"'
return b
})
Object.defineProperty(bss, 'class', {
set: function(value) {
this.__class = value
},
get: function() {
return this.__class || createClass(this.__style)
}
})
function $media(value, style) {
const b = chain(this)
if (value)
b.__style['@media ' + value] = parse(style)
return b
}
function $import(value) {
if (value && !/^('|"|url\('|url\(")/.test(value))
value = '"' + value + '"'
if (value)
insert('@import ' + value + ';', 0)
return chain(this)
}
function $nest(selector, properties) {
const b = chain(this)
if (arguments.length === 1)
Object.keys(selector).forEach(x => addNest(b.__style, x, selector[x]))
else if (selector)
addNest(b.__style, selector, properties)
return b
}
function addNest(style, selector, properties) {
const prop = selector.split(selectorSplit).map(x => {
x = x.trim()
return (x.charAt(0) === ':' || x.charAt(0) === '[' ? '' : ' ') + x
}).join(',&')
prop in style
? assign(style[prop], parse(properties))
: style[prop] = parse(properties)
}
pseudos.forEach(name =>
setProp('$' + hyphenToCamelCase(name.replace(/:/g, '')), function Pseudo(value, style) {
const b = chain(this)
if (isTagged(value))
b.__style[name] = parse.apply(null, arguments)
else if (value || style)
b.__style[name + (style ? '(' + value + ')' : '')] = parse(style || value)
return b
})
)
function setter(prop) {
return function CssProperty(value) {
const b = chain(this)
if (!value && value !== 0)
delete b.__style[prop]
else if (arguments.length > 0)
add(b.__style, prop, Array.prototype.slice.call(arguments))
return b
}
}
function css(selector, style) {
if (arguments.length === 1)
Object.keys(selector).forEach(key => addCss(key, selector[key]))
else
addCss(selector, style)
return chain(this)
}
function addCss(selector, style) {
objectToRules(parse(style), selector, '', true).forEach(rule => insert(rule))
}
function helper(name, styling) {
if (arguments.length === 1)
return Object.keys(name).forEach(key => helper(key, name[key]))
delete bss[name] // Needed to avoid weird get calls in chrome
if (typeof styling === 'function') {
helper[name] = styling
Object.defineProperty(bss, name, {
configurable: true,
value: function Helper(input) {
const b = chain(this)
const result = isTagged(input)
? styling(raw(input, arguments))
: styling.apply(null, arguments)
assign(b.__style, result.__style)
return b
}
})
} else {
helper[name] = parse(styling)
Object.defineProperty(bss, name, {
configurable: true,
get: function() {
const b = chain(this)
assign(b.__style, parse(styling))
return b
}
})
}
}
bss.helper('$animate', (value, props) =>
bss.animation(bss.$keyframes(props) + ' ' + value)
)
function short(prop) {
const acronym = initials(prop)
, short = popular[acronym] && popular[acronym] !== prop ? prop : acronym
shorts[short] = prop
return short
}
const stringToObject = memoize(string => {
let last = ''
, prev
return string.trim().replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*(?![^("]*[)"])/g, '').split(/;(?![^("]*[)"])|\n/).reduce((acc, line) => {
if (!line)
return acc
line = last + line.trim()
const [key, ...tokens] = line.replace(/[ :]+/, ' ').split(' ')
last = line.charAt(line.length - 1) === ',' ? line : ''
if (last)
return acc
if (line.charAt(0) === ',' || !isProp.test(key)) {
acc[prev] += ' ' + line
return acc
}
if (!key)
return acc
const prop = key.charAt(0) === '-' && key.charAt(1) === '-'
? key
: hyphenToCamelCase(key)
prev = shorts[prop] || prop
if (key in helper) {
typeof helper[key] === 'function'
? assign(acc, helper[key](...tokens).__style)
: assign(acc, helper[key])
} else if (prop in helper) {
typeof helper[prop] === 'function'
? assign(acc, helper[prop](...tokens).__style)
: assign(acc, helper[prop])
} else if (tokens.length > 0) {
add(acc, prev, tokens)
}
return acc
}, {})
})
let count = 0
const keyframeCache = {}
function keyframes(props) {
const content = Object.keys(props).reduce((acc, key) =>
acc + key + '{' + stylesToCss(parse(props[key])) + '}'
, '')
if (content in keyframeCache)
return keyframeCache[content]
const name = classPrefix + count++
keyframeCache[content] = name
insert('@keyframes ' + name + '{' + content + '}')
return name
}
function parse(input, value) {
if (typeof input === 'string') {
if (typeof value === 'string' || typeof value === 'number')
return ({ [input] : value })
return stringToObject(input)
} else if (isTagged(input)) {
return stringToObject(raw(input, arguments))
}
return input.__style || sanitize(input)
}
function isTagged(input) {
return Array.isArray(input) && typeof input[0] === 'string'
}
function raw(input, args) {
let str = ''
for (let i = 0; i < input.length; i++)
str += input[i] + (args[i + 1] || args[i + 1] === 0 ? args[i + 1] : '')
return str
}
function sanitize(styles) {
return Object.keys(styles).reduce((acc, key) => {
const value = styles[key]
key = shorts[key] || key
if (!value && value !== 0 && value !== '')
return acc
if (key === 'content' && value.charAt(0) !== '"')
acc[key] = '"' + value + '"'
else if (typeof value === 'object')
acc[key] = sanitize(value)
else
add(acc, key, value)
return acc
}, {})
}
export default bss
{
"name": "bss",
"version": "1.6.3",
"version": "3.0.0-alpha",
"description": "Better Style Sheets",
"main": "bss.js",
"module": "bss.esm.js",
"main": "dist/bss.js",
"module": "dist/bss.esm.js",
"scripts": {

@@ -20,4 +20,4 @@ "test": "TEST=true npm run build && ospec",

"rollup-plugin-filesize": "6.1.0",
"rollup-plugin-uglify": "6.0.2"
"rollup-plugin-terser": "5.1.2"
}
}
import buble from 'rollup-plugin-buble'
import { uglify } from 'rollup-plugin-uglify'
import { terser } from 'rollup-plugin-terser'
import filesize from 'rollup-plugin-filesize'

@@ -9,3 +9,3 @@

output: {
file: 'bss.js',
file: 'dist/bss.js',
exports: 'default',

@@ -25,3 +25,3 @@ format: 'umd',

output: {
file: 'bss.min.js',
file: 'dist/bss.min.js',
exports: 'default',

@@ -34,3 +34,3 @@ format: 'umd',

buble(),
uglify({ mangle: true, compress: true }),
terser({ mangle: true, compress: true }),
filesize()

@@ -41,3 +41,3 @@ ]

output: {
file: 'bss.esm.js',
file: 'dist/bss.esm.js',
format: 'esm',

@@ -44,0 +44,0 @@ sourcemap: true

@@ -53,354 +53,108 @@ const o = require('ospec')

const b = require('../bss')
const b = require('../dist/bss')
const cn = () => '.' + b.prefix + b.count
o.spec('bss', function() {
o('inputs', function() {
o(b`foo: bar; baz: boo;`.style).deepEquals({ foo: 'bar', baz: 'boo' })
o(b`foo: bar;`.style).deepEquals({ foo: 'bar' })
o(b`foo: bar`.style).deepEquals({ foo: 'bar' })
o(b`foo bar`.style).deepEquals({ foo: 'bar' })
o(b({ foo: 'bar' }).style).deepEquals({ foo: 'bar' })
o(b('foo', 'bar').style).deepEquals({ foo: 'bar' })
})
o('object input with pseduos', function() {
const cls = b({ ':hover': { background: 'red' } }).class
o(b.getSheet()).equals(`.${cls}.${cls}:hover{background:red;}`)
o('White space around colon', () => {
b`position:absolute;
position: absolute;
position :absolute;
position : absolute;
position:
absolute;
position:
absolute`
o(b.rules.pop()).equals(cn() + '{position:absolute;position:absolute;position:absolute;position:absolute;position:absolute;position:absolute;}')
})
o('object input with at-rules', function() {
const cls = b({ '@media (max-width:600px)': { background: 'red' } }).class
o(b.getSheet()).equals(`@media (max-width:600px){.${cls}.${cls}{background:red;}}`)
o('Multiline property values', () => {
b`position: absolute;
transform: translate(-50%, -50%)
rotate(-45deg);`
o(b.rules.pop()).equals(cn() + '{position:absolute;transform:translate(-50%, -50%) rotate(-45deg);}')
})
o('Chained $nest in @', function() {
const cls = b.$media('(min-width: 0px)',
b`
font-family: Helvetica;
`.$nest('h1', b.c('red'))
).class
o(b.getSheet()).equals(`@media (min-width: 0px){.${cls}.${cls}{font-family:Helvetica;}.${cls} h1{color:red;}}`)
o('Comments in strings', () => {
b`position: absolute; // This is absolute
transform: translate(-50%, -50%) // This is multi line
rotate(-45deg); // And here it ends`
o(b.rules.pop()).equals(cn() + '{position:absolute;transform:translate(-50%, -50%) rotate(-45deg);}')
})
o('object input using shortname properties', function() {
o(b({ bc: 'red' }).style).deepEquals({ backgroundColor: 'red' })
o('@keyframes', () => {
b`
@keyframes wat {
from { margin-top: 50px; }
50% { margin-top: 150px; }
to { margin-top: 100px; }
}
`
o(b.rules.pop()).equals('@keyframes wat{from{margin-top:50px;}50%{margin-top:150px;}to{margin-top:100px;}}')
})
o('multiline input', function() {
o(b('transform scale(1)\nrotate(0)').style).deepEquals({ transform: 'scale(1) rotate(0)' })
o('@media', () => {
b`
@media screen and (min-width: 900px) {
article {
padding: 1rem 3rem;
}
}
`
o(b.rules.pop()).equals('@media screen and (min-width: 900px){' + cn() + ' article{padding:1rem 3rem;}}')
})
o('default css properties', function() {
o(b.bc('green').style).deepEquals({ backgroundColor: 'green' })
o(b.p(20, 10, '50%').style).deepEquals({ padding: '20px 10px 50%' })
o(b`p 20 10 50%`.style).deepEquals({ padding: '20px 10px 50%' })
o(b({ padding: '20 10 50%' }).style).deepEquals({ padding: '20px 10px 50%' })
o(b.backgroundColor('red').style).deepEquals({ backgroundColor: 'red' })
o('@supports', () => {
b`
@supports (display: flex) {
article {
display: flex;
}
}
`
o(b.rules.pop()).equals('@supports (display: flex){' + cn() + ' article{display:flex;}}')
})
o('css doulbe class for specificity generation', function() {
const cls = b`foo: bar;`.class
o(b.getSheet()).equals(`.${cls}.${cls}{foo:bar;}`)
o('@media inside @supports', () => {
b`
@supports (display: flex) {
@media screen and (min-width: 900px) {
article {
display: flex;
}
}
}
`
o(b.rules.pop()).equals('@supports (display: flex){@media screen and (min-width: 900px){' + cn() + ' article{display:flex;}}}')
})
o('common style class reuse', function() {
const cls = b`foo: bar;`.class
, cls2 = b`foo: bar;`.class
o(cls).equals(cls2)
o(b.getSheet()).equals(`.${cls}.${cls}{foo:bar;}`)
/*
o('Inline animation', () => {
b`
animation 1s {
from { margin-bottom 0 },
50% { margin-top 50 },
to { margin-top 100 }
}
`
o(b.rules.pop()).equals('')
})
o('values can have colons', function() {
const cls = b`
backgroundImage: url(https://bss.com/)
`.class
o(b.getSheet()).equals(`.${cls}.${cls}{background-image:url(https://bss.com/);}`)
o('Multiple inline animation', () => {
b`
animation 1s {
from { margin-top 50px }
50% { margin-top 150px }
to { margin-top 100px }
}, {
20% { transform translateX(50px) }
50% { transform translateX(150px) }
80% { transform translateX(100px) }
}
`
o(b.rules.pop()).equals('')
})
*/
o('values can have valid semicolons', function() {
const cls = b`
backgroundImage: url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)
content: "a;here"
`.class
o(b.getSheet()).equals(`.${cls}.${cls}{background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);content:"a;here";}`)
})
o('@import', function() {
b.$import('sanitize.css')
o(b.getSheet()).equals('@import "sanitize.css";')
b.$import('"sanitize.css"')
o(b.getSheet()).equals('@import "sanitize.css";')
b.$import('url("sanitize.css")')
o(b.getSheet()).equals('@import url("sanitize.css");')
})
o('pseudo', function() {
const cls = b.$hover(b.bc('green')).class
o(b.getSheet()).equals(`.${cls}.${cls}:hover{background-color:green;}`)
})
o('same named props', function() {
const cls1 = b`
c blue
bc white
`.class
const cls2 = b`
c blue
bc white
c white
`.class
o(b.getSheet()).equals([
`.${cls1}.${cls1}{color:blue;background-color:white;}`,
`.${cls2}.${cls2}{color:blue;background-color:white;color:white;}`
].join(''))
})
o('same named properties string', function() {
const cls = b`
display -webkit-flex
display flex
`.class
o(b.getSheet()).equals(`.${cls}.${cls}{display:-webkit-flex;display:flex;}`)
})
o('same named properties function', function() {
const cls = b.d('-webkit-flex').d('flex').class
o(b.getSheet()).equals(`.${cls}.${cls}{display:-webkit-flex;display:flex;}`)
})
o('same named properties style', function() {
o(b.d('-webkit-flex').d('flex').style).deepEquals({ display:'flex' })
})
o('empty content string is set to ""', function() {
const cls = b.$before(b.content('')).$after(b({ content: '' })).class
o(b.getSheet()).equals(`.${cls}.${cls}::before{content:"";}.${cls}.${cls}::after{content:"";}`)
})
o('adds vendor prefix', function() {
const cls = b({ overflowScrolling: 'none' }).class
o(b.getSheet()).equals(`.${cls}.${cls}{-webkit-overflow-scrolling:none;}`)
const cls2 = b({ appearance: 'none' }).class
o(b.getSheet()).equals(`.${cls2}.${cls2}{-moz-appearance:none;}`)
const cls3 = b('appearance none').class
o(b.getSheet()).equals(`.${cls3}.${cls3}{-moz-appearance:none;}`)
})
o('support variables in tagged template literals', function() {
o(b`display ${ 'flex' }`.style).deepEquals({ display: 'flex' })
})
o('support 0 in tagged template literals', function() {
o(b`top ${ 0 }`.style).deepEquals({ top: '0' })
})
o('support variables in tagged template literals in pseudos', function() {
const cls = b.$hover`display ${ 'flex' }`.class
o(b.getSheet()).equals(`.${cls}.${cls}:hover{display:flex;}`)
})
o('allows vendor prefix', function() {
const cls = b('-webkit-overflow-scrolling touch').class
o(b.getSheet()).equals(`.${cls}.${cls}{-webkit-overflow-scrolling:touch;}`)
})
o('allows css variables', function() {
const cls = b('--primaryColor 250 250 250').class
o(b.getSheet()).equals(`.${cls}.${cls}{--primaryColor:250 250 250;}`)
})
o('single class for less specificity when using $nest', function() {
const cls = b.$nest('li', b('-webkit-overflow-scrolling touch')).class
o(b.getSheet()).equals(`.${cls} li{-webkit-overflow-scrolling:touch;}`)
})
o('nest multiple selectors', function() {
const cls = b.$nest('th, tr', b('background blue')).class
o(b.getSheet()).equals(`.${cls} th,.${cls} tr{background:blue;}`)
})
o('nest objects', function() {
const cls = b.$nest({ th : b('background blue') }).class
o(b.getSheet()).equals(`.${cls} th{background:blue;}`)
})
o('nest with ampersand', function() {
const cls = b.$nest({ 'th &' : b('background blue') }).class
o(b.getSheet()).equals(`th .${cls}{background:blue;}`)
const cls2 = b.$nest({ 'th&' : b('background blue') }).class
o(b.getSheet()).equals(`th.${cls2}{background:blue;}`)
const cls3 = b.$nest({ '& th' : b('background blue') }).class
o(b.getSheet()).equals(`.${cls3} th{background:blue;}`)
})
o('nest multiple identical selectors', function() {
const cls = b.$nest('p.broken', 'background: purple')
.$nest('p.broken', 'color: yellow').class
o(b.getSheet()).equals(`.${cls} p.broken{background:purple;color:yellow;}`)
})
o('add px', function() {
o(b`w 1`.style).deepEquals({ width: '1px' })
o(b('width 1').style).deepEquals({ width: '1px' })
o(b({ width: 1 }).style).deepEquals({ width: '1px' })
o(b({ width: true }).style).deepEquals({ width: 'true' })
o(b`boxShadow 1 1 10 black`.style).deepEquals({ boxShadow: '1px 1px 10px black' })
o(b`border 1 solid black`.style).deepEquals({ border: '1px solid black' })
o(b({ boxShadow: '1 1 10 black'}).style).deepEquals({ boxShadow: '1px 1px 10px black' })
o(b({ border: '1 solid black' }).style).deepEquals({ border: '1px solid black' })
o(b.w(1).style).deepEquals({ width: '1px' })
})
o('do not add px to 0', function() {
o(b`w 0`.style).deepEquals({ width: '0' })
})
o('clears empty', function() {
o(b.width(false && 20).style).deepEquals({})
o(b.width(undefined && 20).style).deepEquals({})
o(b.width(null && 20).style).deepEquals({})
o(b.width('').style).deepEquals({})
})
o.spec('helpers', function() {
o('can have any name', function() {
b.helper('fooBar', b`foo bar`)
b.helper('foo-bar', b`fiz baz`)
o(b`
fooBar
foo-bar
`.style).deepEquals({ foo: 'bar', fiz: 'baz' })
})
o('without args', function() {
b.helper('foobar', b`foo bar`)
o(b.foobar.style).deepEquals({ foo: 'bar' })
})
o('parsed', function() {
b.helper('foobar', `foo bar`)
o(b.foobar.style).deepEquals({ foo: 'bar' })
})
o('with args (object notation)', function() {
b.helper('foo', arg => b({ foo: arg }))
o(b.foo('bar').style).deepEquals({ foo: 'bar' })
})
o('with args (bss notation)', function() {
b.helper('foo', arg => b`foo ${arg}`)
o(b.foo('bar').style).deepEquals({ foo: 'bar' })
})
o('with and without args mixed', function() {
b.helper('foo', arg => b`foo ${arg}`)
b.helper('baz', b`baz foz`)
o(b.foo('bar').baz.style).deepEquals({ foo: 'bar', baz: 'foz' })
})
o('multiple helpers in object', function() {
b.helper({
foo: b`bar baz`,
bar: b`foo bar`
})
o(b.foo.bar.style).deepEquals({ bar: 'baz', foo: 'bar' })
})
o('helpers in strings', function() {
b.helper({
size: (w, h) => b(`width ${w};height ${h}`),
pointer: b('cursor pointer')
})
o(b`
size 20 20
pointer
`.style).deepEquals({ width: '20px', height: '20px', cursor: 'pointer' })
})
o('helpers as template literals', function() {
b.helper({
desktop: s => b.$media('(min-width:1024px)', s)
})
const cls = b.desktop`display flex`.class
o(b.getSheet()).equals(`@media (min-width:1024px){.${cls}.${cls}{display:flex;}}`)
})
o('helpers as template literals with variables', function() {
b.helper({
desktop: s => b.$media('(min-width:1024px)', s)
})
const cls = b.desktop`display ${ 'flex' }`.class
o(b.getSheet()).equals(`@media (min-width:1024px){.${cls}.${cls}{display:flex;}}`)
})
})
o('css', function() {
b.css('html', 'background blue')
o(b.getSheet()).equals('html{background:blue;}')
})
o('css objects', function() {
b.css({ html: 'background blue' })
o(b.getSheet()).equals('html{background:blue;}')
})
o('css nest', function() {
b.css('html', b('background blue').$nest('li', 'background red'))
o(b.getSheet()).equals('html{background:blue;}html li{background:red;}')
})
o('$keyframes', function() {
const anim = b.$keyframes({
from: 'bc red'
})
o(b.getSheet()).equals(`@keyframes ${anim}{from{background-color:red;}}`)
})
o('$animate', function() {
const cls = b.$animate('1s', {
from: 'bc black'
}).class
const sheet = b.getSheet()
o(sheet).equals(`@keyframes ${cls}{from{background-color:black;}}.${cls}.${cls}{animation:${cls} 1s;}`)
})
o('Override valueOf', function() {
const newValueOf = function() {
return 'test'
}
b.valueOf = newValueOf
o(b.valueOf).equals(newValueOf)
o('' + b.bc('red')).equals('test')
})
o('Multiline css', function() {
o(b`
position : absolute;
transform : translate(-50%, -50%)
rotate(-45deg);
`.style).deepEquals({
position: 'absolute',
transform: 'translate(-50%, -50%) rotate(-45deg)'
})
})
o('Comments in strings', function() {
o(b`
position : absolute; // This is absolute
transform : translate(-50%, -50%) // This is multi line
rotate(-45deg); // And here it ends
`.style).deepEquals({
position: 'absolute',
transform: 'translate(-50%, -50%) rotate(-45deg)'
})
})
})
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