New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

geojson2svg

Package Overview
Dependencies
Maintainers
1
Versions
43
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

geojson2svg - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

32

dist/geojson2svg.js

@@ -219,11 +219,27 @@ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.geojson2svg=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){

var arr = opt.attributes
opt.attributes = arr.reduce(function(sum, path) {
var key = path.split('.').pop()
var val
try {
val = valueAt(feature, path)
} catch(e) {
val = false
opt.attributes = arr.reduce(function(sum, property) {
if (typeof(property) === 'string') {
var val, key = property.split('.').pop()
try {
val = valueAt(feature, property)
} catch(e) {
val = false
}
if (val) sum[key] = val
} else if (typeof(property) === 'object' && property.type
&& property.property)
{
if (property.type === 'dynamic') {
var val, key = property.key ? property.key
: property.property.split('.').pop()
try {
val = valueAt(feature, property.property)
} catch(e) {
val = false
}
if (val) sum[key] = val
} else if (property.type === 'static' && property.value) {
sum[property.property] = property.value
}
}
if (val) sum[key] = val
return sum

@@ -230,0 +246,0 @@ }, {})

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

!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.geojson2svg=e()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){!function(){function explode(g){return multies.indexOf(g.type)>-1?g.coordinates.map(function(part){var single={};return single.type=g.type.replace("Multi",""),single.coordinates=part,g.crs&&(single.crs=g.crs),single}):!1}function implode(gs){var sameType=gs.every(function(g){return singles.indexOf(g.type)>-1}),crs=gs[0].crs||0,sameCrs=gs.every(function(g){var gcrs=g.crs||0;return gcrs==crs});if(sameType&&sameCrs){var multi={};return multi.type="Multi"+gs[0].type,multi.coordinates=[],0!=crs&&(multi.crs=crs),gs.forEach(function(g){multi.coordinates.push(g.coordinates)}),multi}return!1}var singles=["Point","LineString","Polygon"],multies=["MultiPoint","MultiLineString","MultiPolygon"],multigeojson={explode:explode,implode:implode};"undefined"!=typeof module&&module.exports?module.exports=multigeojson:window&&(window.multigeojson=multigeojson)}()},{}],2:[function(require,module,exports){function getCoordString(coords,res,origin){var coordStr=coords.map(function(coord){return(coord[0]-origin.x)/res+","+(origin.y-coord[1])/res});return coordStr.join(" ")}function point(geom,res,origin,opt){var r=opt&&opt.r?opt.r:1,pointAsCircle=opt&&opt.hasOwnProperty("pointAsCircle")?opt.pointAsCircle:!1,coords=getCoordString([geom.coordinates],res,origin);return pointAsCircle?[coords]:["M"+coords+" m"+-r+",0 a"+r+","+r+" 0 1,1 "+2*r+",0 a"+r+","+r+" 0 1,1 "+-2*r+",0"]}function multiPoint(geom,res,origin,opt){var explode=opt&&opt.hasOwnProperty("explode")?opt.explode:!1,paths=multi.explode(geom).map(function(single){return point(single,res,origin,opt)[0]});return explode?paths:[paths.join(" ")]}function lineString(geom,res,origin,otp){var coords=getCoordString(geom.coordinates,res,origin),path="M"+coords;return[path]}function multiLineString(geom,res,origin,opt){var explode=opt&&opt.hasOwnProperty("explode")?opt.explode:!1,paths=multi.explode(geom).map(function(single){return lineString(single,res,origin,opt)[0]});return explode?paths:[paths.join(" ")]}function polygon(geom,res,origin,opt){var mainStr,holes;mainStr=getCoordString(geom.coordinates[0],res,origin),geom.coordinates.length>1&&(holes=geom.coordinates.slice(1,geom.coordinates.length));var path="M"+mainStr;if(holes)for(var i=0;i<holes.length;i++)path+=" M"+getCoordString(holes[i],res,origin);return path+="Z",[path]}function multiPolygon(geom,res,origin,opt){var explode=opt.hasOwnProperty("explode")?opt.explode:!1,paths=multi.explode(geom).map(function(single){return polygon(single,res,origin,opt)[0]});return explode?paths:[paths.join(" ").replace(/Z/g,"")+"Z"]}var multi=require("multigeojson");module.exports={Point:point,MultiPoint:multiPoint,LineString:lineString,MultiLineString:multiLineString,Polygon:polygon,MultiPolygon:multiPolygon}},{multigeojson:1}],3:[function(require,module,exports){module.exports=function(a,b){var c={};return Object.keys(a).forEach(function(key){c[key]=a[key]}),Object.keys(b).forEach(function(key){c[key]=b[key]}),c}},{}],4:[function(require,module,exports){function pathToSvgJson(path,type,attributes,opt){var svg={},pointAsCircle=opt&&opt.hasOwnProperty("pointAsCircle")?opt.pointAsCircle:!1;"Point"!=type&&"MultiPoint"!=type||!pointAsCircle?(svg={d:path},("Polygon"==type||"MultiPolygon"==type)&&"evenodd"==svg["fill-rule"]):(svg.cx=path.split(",")[0],svg.cy=path.split(",")[1],svg.r=opt&&opt.r?opt.r:"1");for(var key in attributes)svg[key]=attributes[key];return svg}function jsonToSvgElement(json,type,opt){var pointAsCircle=opt&&opt.hasOwnProperty("pointAsCircle")?opt.pointAsCircle:!1,ele="<path";"Point"!=type&&"MultiPoint"!=type||!pointAsCircle||(ele="<circle");for(var key in json)ele+=" "+key+'="'+json[key]+'"';return ele+="/>"}function valueAt(obj,path){function index(prev,cur,i,arr){if(prev.hasOwnProperty(cur))return prev[cur];throw new Error(arr.slice(0,i+1).join(".")+" is not a valid property path")}return path.split(".").reduce(index,obj)}var extend=require("./extend.js");converter=require("./converter.js");var g2svg=function(options){this.options=options||{},this.viewportSize=this.options.viewportSize||{width:256,height:256},this.mapExtent=this.options.mapExtent||{left:-20037508.342789244,right:20037508.342789244,bottom:-20037508.342789244,top:20037508.342789244},this.res=this.calResolution(this.mapExtent,this.viewportSize,this.options.fitTo)};g2svg.prototype.calResolution=function(extent,size,fitTo){var xres=(extent.right-extent.left)/size.width,yres=(extent.top-extent.bottom)/size.height;if(fitTo){if("width"===fitTo.toLowerCase())return xres;if("height"===fitTo.toLowerCase())return yres;throw new Error('"fitTo" option should be "width" or "height" ')}return Math.max(xres,yres)},g2svg.prototype.convert=function(geojson,options){var opt=extend(this.options,options||{}),svgElements=[];if("FeatureCollection"==geojson.type)for(var i=0;i<geojson.features.length;i++)svgElements=svgElements.concat(this.convertFeature(geojson.features[i],opt));else if("Feature"==geojson.type)svgElements=this.convertFeature(geojson,opt);else if("GeometryCollection"==geojson.type)for(var i=0;i<geojson.geometries.length;i++)svgElements=svgElements.concat(this.convertGeometry(geojson.geometries[i],opt));else{if(!converter[geojson.type])return;svgElements=this.convertGeometry(geojson,opt)}return opt.callback&&opt.callback.call(this,svgElements),svgElements},g2svg.prototype.convertFeature=function(feature,options){if(feature||feature.geometry){var opt=extend(this.options,options||{});if(opt.attributes&&opt.attributes instanceof Array){var arr=opt.attributes;opt.attributes=arr.reduce(function(sum,path){var val,key=path.split(".").pop();try{val=valueAt(feature,path)}catch(e){val=!1}return val&&(sum[key]=val),sum},{})}else opt.attributes=opt.attributes||{};var id=opt.attributes.id||feature.id||(feature.properties&&feature.properties.id?feature.properties.id:null);return id&&(opt.attributes.id=id),this.convertGeometry(feature.geometry,opt)}},g2svg.prototype.convertGeometry=function(geom,options){if(converter[geom.type]){var svgJsons,svgEles,opt=extend(this.options,options||{}),output=opt.output||"svg",paths=converter[geom.type].call(this,geom,this.res,{x:this.mapExtent.left,y:this.mapExtent.top},opt);return"svg"==output.toLowerCase()?(svgJsons=paths.map(function(path){return pathToSvgJson(path,geom.type,opt.attributes,opt)}),svgEles=svgJsons.map(function(json){return jsonToSvgElement(json,geom.type,opt)})):paths}},module.exports=g2svg},{"./converter.js":2,"./extend.js":3}],5:[function(require,module,exports){var g2svg=require("./instance.js"),geojson2svg=function(options){return new g2svg(options)};module.exports=geojson2svg},{"./instance.js":4}]},{},[5])(5)});
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.geojson2svg=e()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){!function(){function explode(g){return multies.indexOf(g.type)>-1?g.coordinates.map(function(part){var single={};return single.type=g.type.replace("Multi",""),single.coordinates=part,g.crs&&(single.crs=g.crs),single}):!1}function implode(gs){var sameType=gs.every(function(g){return singles.indexOf(g.type)>-1}),crs=gs[0].crs||0,sameCrs=gs.every(function(g){var gcrs=g.crs||0;return gcrs==crs});if(sameType&&sameCrs){var multi={};return multi.type="Multi"+gs[0].type,multi.coordinates=[],0!=crs&&(multi.crs=crs),gs.forEach(function(g){multi.coordinates.push(g.coordinates)}),multi}return!1}var singles=["Point","LineString","Polygon"],multies=["MultiPoint","MultiLineString","MultiPolygon"],multigeojson={explode:explode,implode:implode};"undefined"!=typeof module&&module.exports?module.exports=multigeojson:window&&(window.multigeojson=multigeojson)}()},{}],2:[function(require,module,exports){function getCoordString(coords,res,origin){var coordStr=coords.map(function(coord){return(coord[0]-origin.x)/res+","+(origin.y-coord[1])/res});return coordStr.join(" ")}function point(geom,res,origin,opt){var r=opt&&opt.r?opt.r:1,pointAsCircle=opt&&opt.hasOwnProperty("pointAsCircle")?opt.pointAsCircle:!1,coords=getCoordString([geom.coordinates],res,origin);return pointAsCircle?[coords]:["M"+coords+" m"+-r+",0 a"+r+","+r+" 0 1,1 "+2*r+",0 a"+r+","+r+" 0 1,1 "+-2*r+",0"]}function multiPoint(geom,res,origin,opt){var explode=opt&&opt.hasOwnProperty("explode")?opt.explode:!1,paths=multi.explode(geom).map(function(single){return point(single,res,origin,opt)[0]});return explode?paths:[paths.join(" ")]}function lineString(geom,res,origin,otp){var coords=getCoordString(geom.coordinates,res,origin),path="M"+coords;return[path]}function multiLineString(geom,res,origin,opt){var explode=opt&&opt.hasOwnProperty("explode")?opt.explode:!1,paths=multi.explode(geom).map(function(single){return lineString(single,res,origin,opt)[0]});return explode?paths:[paths.join(" ")]}function polygon(geom,res,origin,opt){var mainStr,holes;mainStr=getCoordString(geom.coordinates[0],res,origin),geom.coordinates.length>1&&(holes=geom.coordinates.slice(1,geom.coordinates.length));var path="M"+mainStr;if(holes)for(var i=0;i<holes.length;i++)path+=" M"+getCoordString(holes[i],res,origin);return path+="Z",[path]}function multiPolygon(geom,res,origin,opt){var explode=opt.hasOwnProperty("explode")?opt.explode:!1,paths=multi.explode(geom).map(function(single){return polygon(single,res,origin,opt)[0]});return explode?paths:[paths.join(" ").replace(/Z/g,"")+"Z"]}var multi=require("multigeojson");module.exports={Point:point,MultiPoint:multiPoint,LineString:lineString,MultiLineString:multiLineString,Polygon:polygon,MultiPolygon:multiPolygon}},{multigeojson:1}],3:[function(require,module,exports){module.exports=function(a,b){var c={};return Object.keys(a).forEach(function(key){c[key]=a[key]}),Object.keys(b).forEach(function(key){c[key]=b[key]}),c}},{}],4:[function(require,module,exports){function pathToSvgJson(path,type,attributes,opt){var svg={},pointAsCircle=opt&&opt.hasOwnProperty("pointAsCircle")?opt.pointAsCircle:!1;"Point"!=type&&"MultiPoint"!=type||!pointAsCircle?(svg={d:path},("Polygon"==type||"MultiPolygon"==type)&&"evenodd"==svg["fill-rule"]):(svg.cx=path.split(",")[0],svg.cy=path.split(",")[1],svg.r=opt&&opt.r?opt.r:"1");for(var key in attributes)svg[key]=attributes[key];return svg}function jsonToSvgElement(json,type,opt){var pointAsCircle=opt&&opt.hasOwnProperty("pointAsCircle")?opt.pointAsCircle:!1,ele="<path";"Point"!=type&&"MultiPoint"!=type||!pointAsCircle||(ele="<circle");for(var key in json)ele+=" "+key+'="'+json[key]+'"';return ele+="/>"}function valueAt(obj,path){function index(prev,cur,i,arr){if(prev.hasOwnProperty(cur))return prev[cur];throw new Error(arr.slice(0,i+1).join(".")+" is not a valid property path")}return path.split(".").reduce(index,obj)}var extend=require("./extend.js");converter=require("./converter.js");var g2svg=function(options){this.options=options||{},this.viewportSize=this.options.viewportSize||{width:256,height:256},this.mapExtent=this.options.mapExtent||{left:-20037508.342789244,right:20037508.342789244,bottom:-20037508.342789244,top:20037508.342789244},this.res=this.calResolution(this.mapExtent,this.viewportSize,this.options.fitTo)};g2svg.prototype.calResolution=function(extent,size,fitTo){var xres=(extent.right-extent.left)/size.width,yres=(extent.top-extent.bottom)/size.height;if(fitTo){if("width"===fitTo.toLowerCase())return xres;if("height"===fitTo.toLowerCase())return yres;throw new Error('"fitTo" option should be "width" or "height" ')}return Math.max(xres,yres)},g2svg.prototype.convert=function(geojson,options){var opt=extend(this.options,options||{}),svgElements=[];if("FeatureCollection"==geojson.type)for(var i=0;i<geojson.features.length;i++)svgElements=svgElements.concat(this.convertFeature(geojson.features[i],opt));else if("Feature"==geojson.type)svgElements=this.convertFeature(geojson,opt);else if("GeometryCollection"==geojson.type)for(var i=0;i<geojson.geometries.length;i++)svgElements=svgElements.concat(this.convertGeometry(geojson.geometries[i],opt));else{if(!converter[geojson.type])return;svgElements=this.convertGeometry(geojson,opt)}return opt.callback&&opt.callback.call(this,svgElements),svgElements},g2svg.prototype.convertFeature=function(feature,options){if(feature||feature.geometry){var opt=extend(this.options,options||{});if(opt.attributes&&opt.attributes instanceof Array){var arr=opt.attributes;opt.attributes=arr.reduce(function(sum,property){if("string"==typeof property){var val,key=property.split(".").pop();try{val=valueAt(feature,property)}catch(e){val=!1}val&&(sum[key]=val)}else if("object"==typeof property&&property.type&&property.property)if("dynamic"===property.type){var val,key=property.key?property.key:property.property.split(".").pop();try{val=valueAt(feature,property.property)}catch(e){val=!1}val&&(sum[key]=val)}else"static"===property.type&&property.value&&(sum[property.property]=property.value);return sum},{})}else opt.attributes=opt.attributes||{};var id=opt.attributes.id||feature.id||(feature.properties&&feature.properties.id?feature.properties.id:null);return id&&(opt.attributes.id=id),this.convertGeometry(feature.geometry,opt)}},g2svg.prototype.convertGeometry=function(geom,options){if(converter[geom.type]){var svgJsons,svgEles,opt=extend(this.options,options||{}),output=opt.output||"svg",paths=converter[geom.type].call(this,geom,this.res,{x:this.mapExtent.left,y:this.mapExtent.top},opt);return"svg"==output.toLowerCase()?(svgJsons=paths.map(function(path){return pathToSvgJson(path,geom.type,opt.attributes,opt)}),svgEles=svgJsons.map(function(json){return jsonToSvgElement(json,geom.type,opt)})):paths}},module.exports=g2svg},{"./converter.js":2,"./extend.js":3}],5:[function(require,module,exports){var g2svg=require("./instance.js"),geojson2svg=function(options){return new g2svg(options)};module.exports=geojson2svg},{"./instance.js":4}]},{},[5])(5)});
{
"name": "geojson2svg",
"version": "1.1.0",
"version": "1.2.0",
"description": "Converts geojson to svg/path string given svg viewport size and maps extent.",

@@ -5,0 +5,0 @@ "main": "src/main.js",

@@ -60,19 +60,34 @@ # geojson2svg

* **explode:** true | false, default is false. Should multigeojson be exploded to many svg elements or not.
* **attributes:** Attributes which are required to attach as SVG attributes from features can be passed here as list of path in feature, like
* **attributes:** Attributes which are required to attach as SVG attributes from features can be passed here as list of path in feature or json object for static attributes, like shown here
``` {"attributes": ["properties.foo", "properties.bar"]}```
output svg string would be:
**dynamic** ``` {"attributes": ["properties.foo", "properties.bar"]}```
``` <path foo="fooVal-1" bar="barVal-1" d="M0,0 20,10 106,40"/> ```
output: ``` [<path foo="fooVal-1" bar="barVal-1" d="M0,0 20,10 106,40"/>] ```
Note: If a feature does not have value at the mentioned path then the attribute key would not be attached to svg string and even error would not be thrown.
or **static** ``` {"attributes": {"class": "mapstyle"}}```
outut: ```'<path class="mapstyle" d="M0,0 20,10 106,40"/>'```
In case attribute(s) need be attach to all SVG string, a json object containing attribute(key) and values(value) can be passed. If option is like
or **dynamic** and **static** both
{attributes: [
{
property: 'properties.foo',
type: 'dynamic',
key: 'id'
}, {
property: 'properties.baz',
type: 'dynamic'
}, {
property: 'bar',
value: 'barStatic',
type: 'static'
}]
})
``` {"attributes": {"class": "mapstyle"}}```
output: ``` [ '<path d="M128,128 128.00638801979818,127.99361198020182" id="fooVal-1" baz="bazVal-1" bar="barStatic"/>'] ```
returned string would be
```'<path class="mapstyle" d="M0,0 20,10 106,40"/>'```
Note: If a feature does not have value at the mentioned path then the attribute key would not be attached to svg string and even error would not be thrown.

@@ -79,0 +94,0 @@ * **pointAsCircle:** true | false, default is false. For point geojson return circle element for option:

@@ -64,11 +64,27 @@ var extend = require('./extend.js')

var arr = opt.attributes
opt.attributes = arr.reduce(function(sum, path) {
var key = path.split('.').pop()
var val
try {
val = valueAt(feature, path)
} catch(e) {
val = false
opt.attributes = arr.reduce(function(sum, property) {
if (typeof(property) === 'string') {
var val, key = property.split('.').pop()
try {
val = valueAt(feature, property)
} catch(e) {
val = false
}
if (val) sum[key] = val
} else if (typeof(property) === 'object' && property.type
&& property.property)
{
if (property.type === 'dynamic') {
var val, key = property.key ? property.key
: property.property.split('.').pop()
try {
val = valueAt(feature, property.property)
} catch(e) {
val = false
}
if (val) sum[key] = val
} else if (property.type === 'static' && property.value) {
sum[property.property] = property.value
}
}
if (val) sum[key] = val
return sum

@@ -75,0 +91,0 @@ }, {})

@@ -123,2 +123,3 @@ 'use strict';

});
it('add attributes to svg based on each feature properties:', function() {

@@ -152,2 +153,46 @@ var converter = geojson2svg(

});
it('add attributes to svg based on each feature properties and static attributes also:', function() {
var converter = geojson2svg(
{attributes: [
{
property: 'properties.foo',
type: 'dynamic',
key: 'id'
}, {
property: 'properties.baz',
type: 'dynamic'
}, {
property: 'bar',
value: 'barStatic',
type: 'static'
}]
})
var svgStr = converter.convert({
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {type: 'LineString', coordinates: [[0,0], [1000,1000]]},
properties: {foo: 'fooVal-1', bar: 'barVal-1', baz: 'bazVal-1'}
}, {
type: 'Feature',
geometry: {type: 'LineString', coordinates: [[10,10], [100,100]]},
properties: {foo: 'fooVal-2', bar: 'barVal-2'}
}]
})
var svgEle1 = jsdom(svgStr[0]).firstChild.children[1].children[0];
expect(svgEle1).to.respondTo('getAttribute');
expect(svgEle1.getAttribute('id')).to.be.equal('fooVal-1');
expect(svgEle1.getAttribute('bar')).to.be.equal('barStatic');
expect(svgEle1.getAttribute('baz')).to.be.equal('bazVal-1');
expect(svgEle1.getAttribute('foo')).to.be.null;
var svgEle2 = jsdom(svgStr[1]).firstChild.children[1].children[0];
expect(svgEle2).to.respondTo('getAttribute');
expect(svgEle2.getAttribute('id')).to.be.equal('fooVal-2');
expect(svgEle2.getAttribute('bar')).to.be.equal('barStatic');
expect(svgEle2.getAttribute('baz')).to.be.null;
expect(svgEle2.getAttribute('foo')).to.be.null;
});
it('add given attributes in options to all svg elements: '

@@ -154,0 +199,0 @@ + 'pass attributes in constructor', function() {

Sorry, the diff of this file is not supported yet

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