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

uniqorm

Package Overview
Dependencies
Maintainers
1
Versions
123
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

uniqorm - npm Package Compare versions

Comparing version 2.20.3 to 2.21.0

18

lib/DataField.js

@@ -152,9 +152,8 @@ /* UNIQORM

* @param {Object} [values]
* @param {*} [context]
* @param {boolean} [toCreate]
* @param {Object} [context]
*/
validate(v, values, context) {
validate(v, values, toCreate, context) {
try {
this._validate(this._parse(v));
if (this.validator)
this.validator(v, this, values, context);
this._validate(this._parse(v), values, toCreate, context);
} catch (e) {

@@ -191,6 +190,11 @@ if (e instanceof ValidationError)

*
* @param {*} value
* @param {*} v
* @param {Object} [values]
* @param {boolean} [toCreate]
* @param {Object} [context]
* @protected
*/
_validate(value) {
_validate(v, values, toCreate, context) {
if (this.validator)
this.validator(v, this, values, toCreate, context);
}

@@ -197,0 +201,0 @@

@@ -37,4 +37,4 @@ /* UNIQORM

*
* @param {Model} model
* @param {Object} options
* @param {Model} options.model
* @param {Array} options.properties

@@ -52,4 +52,4 @@ * @param {Array} [options.where]

*/
constructor(options) {
this.model = options.model;
constructor(model, options) {
this.model = model;
this.connection = options.connection;

@@ -233,3 +233,3 @@ this.properties = options.properties;

/* 5. Phase: Create Query */
this._query = this.connection
this._query = (this.connection || this.model.orm.pool)
.select(...selectColumns)

@@ -312,4 +312,3 @@ .from(this.model.tableNameFull + ' t')

// Create a new Finder for nested query
fnd = new Finder({
model: field.foreignModel,
fnd = new Finder(field.foreignModel, {
connection: finder.connection,

@@ -316,0 +315,0 @@ autoCommit: finder.autoCommit,

@@ -14,18 +14,32 @@ /* UNIQORM

function normalizeFindOptions(options, ignoreUnknownProperties) {
const result = merge({}, options);
result.properties =
normalizeProperties(options.properties, ignoreUnknownProperties);
result.where = !options.where || Array.isArray(options.where) ?
options.where : [options.where];
result.sort = !options.sort || Array.isArray(options.sort) ?
options.sort : [options.sort];
result.ignoreUnknownProperties = ignoreUnknownProperties;
return result;
/**
*
* @param {Object} options
* @return {Object}
*/
function normalizeFindOptions(options) {
const o = {
...options,
properties: normalizeProperties(options.properties),
where: options.where || options.filter ?
makeArray(options.where || options.filter) : [],
sort: options.sort ? makeArray(options.sort) : [],
limit: options.limit,
offset: options.offset
};
delete o.filter;
return o;
}
function normalizeProperties(properties, ignoreUnknownProperties) {
/**
*
* @param {Object} properties
* @return {Object}
*/
function normalizeProperties(properties) {
if (!properties)
return null;
if (typeof properties !== 'object')
properties = [properties];

@@ -42,3 +56,3 @@ let i = 0;

value.fieldName = value.fieldName || key;
target[key] = normalizeFindOptions(value, ignoreUnknownProperties);
target[key] = normalizeFindOptions(value);
}

@@ -48,7 +62,7 @@ i++;

const addProperties = (target, value) => {
const addProperties = (target, properties) => {
if (Array.isArray(value)) {
if (Array.isArray(properties)) {
const COLUMN_PATTERN = /^([a-zA-Z][\w$]*)(?:\.?([\w$]+))? *([\w$]+)?$/;
for (const v of value) {
for (const v of properties) {
if (typeof v === 'string') {

@@ -72,5 +86,5 @@ const m = v.match(COLUMN_PATTERN);

/* istanbul ignore else */
if (isPlainObject(value)) {
for (const v of Object.getOwnPropertyNames(value))
addProperty(target, v, value[v]);
if (isPlainObject(properties)) {
for (const v of Object.getOwnPropertyNames(properties))
addProperty(target, v, properties[v]);
}

@@ -84,2 +98,32 @@ }

/**
*
* @param {*} keyValues
* @param {Array<String>} keyFields
* @return {Object}
*/
function normalizeKeyValues(keyValues, keyFields) {
if (keyValues == null)
throw new ArgumentError('You must provide key values');
if (keyFields.length > 1 || typeof keyValues === 'object') {
/* istanbul ignore next */
if (typeof keyValues !== 'object')
throw new ArgumentError('You must provide all key values in an object instance.');
const result = {};
for (const f of keyFields) {
if (keyValues[f] === undefined)
throw new ArgumentError('You must provide all key values in an object instance.');
result[f] = keyValues[f];
}
/*istanbul ignore else */
if (typeof keyValues === 'object')
merge(result, keyValues, {combine: true});
return result;
}
return {
[keyFields[0]]: keyValues
};
}
function mapConditions(obj, fn) {

@@ -133,4 +177,5 @@ if (Array.isArray(obj))

normalizeProperties,
normalizeKeyValues,
makeArray,
mapConditions
};

@@ -18,3 +18,2 @@ /* UNIQORM

const merge = require('putil-merge');
const promisify = require('putil-promisify');

@@ -29,2 +28,3 @@ const Association = require('./Association');

normalizeFindOptions,
normalizeKeyValues,
mapConditions,

@@ -245,7 +245,8 @@ makeArray

* @param {Object} [options]
* @param {Object} [options.connection]
* @param {Boolean} [options.autoCommit]
* @param {Object|Array} options.properties
* @param {Object|Array<Object>} [options.where]
* @param {Object} [options.connection]
* @param {Boolean} [options.autoCommit]
* @param {Boolean} [options.ignoreUnknownProperties]
* @param {Function} [options.prepare]
* @param {Function} [options.trace]

@@ -255,18 +256,21 @@ * @param {*} [options.context]

*/
get(keyValues, options = {}) {
return promisify(() => {
let where = options.where || options.filter;
where = where ? (Array.isArray(where) ? where : [where]) : [];
const opts = merge({
where: [this._prepareKeyValues(keyValues), ...where],
limit: 1
}, options, {adjunct: true});
async get(keyValues, options = {}) {
const args = {
keyValues: this.normalizeKeyValues(keyValues),
options: this.normalizeGetOptions(options)
};
args.options.ignoreUnknownProperties =
options.ignoreUnknownProperties != null ?
options.ignoreUnknownProperties : this.orm.options.ignoreUnknownProperties;
args.options.where.unshift(args.keyValues);
return this.find(opts)
.then(result => {
result.instance = result.instances[0];
delete result.instances;
return result;
});
});
if (options.prepare)
await options.prepare(args);
return (new Finder(this, args.options)).execute()
.then(result => {
result.instance = result.instances[0];
delete result.instances;
return result;
});
}

@@ -278,2 +282,4 @@

* @param {Object} [options]
* @param {Object} [options.connection]
* @param {Boolean} [options.autoCommit]
* @param {Object|Array<string>} [options.properties]

@@ -284,5 +290,4 @@ * @param {Object|Array<Object>} [options.where]

* @param {Number} [options.offset]
* @param {Object} [options.connection]
* @param {Boolean} [options.autoCommit]
* @param {Boolean} [options.ignoreUnknownProperties]
* @param {Function} [options.prepare]
* @param {Function} [options.trace]

@@ -292,24 +297,12 @@ * @param {*} [options.context]

*/
find(options = {}) {
return promisify(() => {
if (options.properties && typeof options.properties !== 'object')
options.properties = [options.properties];
if (typeof options.properties !== 'object' ||
(Array.isArray(options.properties) && !options.properties.length)) {
options.properties = this.getDataFields();
}
const ignoreUnknownProperties = options.ignoreUnknownProperties != null ?
options.ignoreUnknownProperties : this.orm.options.ignoreUnknownProperties;
const opts = normalizeFindOptions(options, ignoreUnknownProperties);
opts.model = this;
opts.connection = options.connection || this.orm.pool;
opts.ignoreUnknownProperties = ignoreUnknownProperties;
opts.trace = options.trace;
opts.sort = opts.sort || this._defaultSort;
/* istanbul ignore next */
opts.where = opts.where || opts.filter; // backward support
return (new Finder(opts)).execute();
});
async find(options = {}) {
const args = {
options: this.normalizeFindOptions(options)
};
args.options.ignoreUnknownProperties =
options.ignoreUnknownProperties != null ?
options.ignoreUnknownProperties : this.orm.options.ignoreUnknownProperties;
if (options.prepare)
await options.prepare(args);
return (new Finder(this, args.options)).execute();
}

@@ -324,57 +317,124 @@

* @param {Boolean} [options.autoCommit]
* @param {string|Array<string>} [options.returning]
* @param {Boolean} [options.ignoreUnknownProperties]
* @param {Function} [options.prepare]
* @param {Function} [options.validate]
* @param {*} [options.context]
* @param {string|Array<string>} [options.returning]
* @return {Promise<{executeTime:number, queriesExecuted:number, rowsAffected:number, instance:Object}>}
*/
create(values, options = {}) {
return promisify(() => {
async create(values, options = {}) {
if (typeof values !== 'object' || Array.isArray(values))
throw new ArgumentError('You must provide values');
if (typeof values !== 'object' || Array.isArray(values))
throw new ArgumentError('You must provide values');
values = Object.assign({}, values); // Clone object
const args = {
values,
options: {...options}
};
args.options.ignoreUnknownProperties =
options.ignoreUnknownProperties != null ?
options.ignoreUnknownProperties : this.orm.options.ignoreUnknownProperties;
this.validateValuesForCreate(values, options.context, {
ignoreUnknownProperties: options.ignoreUnknownProperties
});
for (const n of Object.keys(this.fields)) {
const field = this.fields[n];
if (field instanceof DataField) {
const v = values[n];
if (v == null && field.defaultValue != null)
values[n] = field.defaultValue;
}
if (options.prepare)
await options.prepare(args);
this.validateValuesForCreate(args.values,
args.options.ignoreUnknownProperties,
args.options.context);
if (options.validate)
await options.validate(values, args.options);
const dataValues = this._preparePostValues(args.values);
const returning = this.normalizeReturning(args.options.returning);
const dbobj = (args.options.connection || this.orm.pool);
return dbobj
.insert(this.tableNameFull, dataValues)
.returning(returning && returning.dataTypes)
.execute({
objectRows: true,
autoCommit: args.options.autoCommit
}).then(resp => {
const ret = {
executeTime: resp.executeTime,
queriesExecuted: 1,
rowsAffected: resp.rowsAffected
};
if (returning) {
const rows = resp.rows;
returning.context = args.options.context;
return this._prepareReturningResponse(dbobj, rows, returning)
.then(o => {
/* istanbul ignore else */
if (o)
ret.instance = o[0];
return ret;
});
}
return ret;
});
}
/**
* Creates many instances into database
*
* @param {Array<Object>} [values]
* @param {Object} [options]
* @param {Object} [options.connection]
* @param {Boolean} [options.autoCommit]
* @param {Boolean} [options.ignoreUnknownProperties]
* @param {Function} [options.prepare]
* @param {Function} [options.validate]
* @param {*} [options.context]
* @return {Promise<{executeTime:number, queriesExecuted:number, rowsAffected:number, instance:Object}>}
*/
async createMany(values, options = {}) {
if (!Array.isArray(values))
throw new ArgumentError('You must provide array of values');
const args = {
values,
options: {...options}
};
args.options.ignoreUnknownProperties =
options.ignoreUnknownProperties != null ?
options.ignoreUnknownProperties : this.orm.options.ignoreUnknownProperties;
if (options.prepare)
await options.prepare(args);
for (const v of args.values) {
this.validateValuesForCreate(v,
args.options.ignoreUnknownProperties,
args.options.context);
if (options.validate)
await options.validate(v, args.options);
}
let rowsAffected = 0;
let queriesExecuted = 0;
const t = Date.now();
const process = async (connection) => {
for (const v of args.values) {
const dataValues = this._preparePostValues(v);
const resp = await connection
.insert(this.tableNameFull, dataValues)
.execute({objectRows: true});
rowsAffected += (resp.rowsAffected || 0);
queriesExecuted++;
}
const dataValues = this._normalizeValues(values);
const returning = this._prepareReturningOptions(options.returning);
};
if (args.options.connection)
await process(args.options.connection);
else await this.orm.pool.acquire((connection) => process(connection));
const dbobj = (options.connection || this.orm.pool);
return dbobj
.insert(this.tableNameFull, dataValues)
.returning(returning && returning.dataTypes)
.execute({
objectRows: true,
autoCommit: options.autoCommit
}).then(resp => {
const ret = {
executeTime: resp.executeTime,
queriesExecuted: 1,
rowsAffected: resp.rowsAffected
};
if (returning) {
const rows = resp.rows;
returning.context = options.context;
return this._prepareReturningResponse(dbobj, rows, returning)
.then(o => {
/* istanbul ignore else */
if (o)
ret.instance = o[0];
return ret;
});
}
return ret;
});
});
return {
rowsAffected,
executeTime: Date.now() - t,
queriesExecuted
};
}

@@ -388,8 +448,9 @@

* @param {Object} [options]
* @param {Object|Array} [options.where]
* @param {Boolean} [options.autoCommit]
* @param {Object} [options.connection]
* @param {Object|Array} [options.where]
* @param {string|Array<string>} [options.returning]
* @param {Boolean} [options.ignoreUnknownProperties]
* @param {Function} [options.prepare]
* @param {*} [options.context]
* @param {string|Array<string>} [options.returning]
* @return {Promise<{executeTime:number, queriesExecuted:number, rowsAffected:number, instances:Array<Object>}>}

@@ -399,4 +460,4 @@ */

return this.updateMany(values, merge({
where: this._prepareKeyValues(keyValues)
}, options, {adjunct: true})).then(ret => {
where: this.normalizeKeyValues(keyValues)
}, options, {combine: true})).then(ret => {
if (ret.instances) {

@@ -415,49 +476,66 @@ ret.instance = ret.instances[0];

* @param {Object} [options]
* @param {Object|Array} [options.where]
* @param {Boolean} [options.autoCommit]
* @param {Object} [options.connection]
* @param {Object|Array} [options.where]
* @param {string|Array<string>} [options.returning]
* @param {Boolean} [options.ignoreUnknownProperties]
* @param {Function} [options.prepare]
* @param {Function} [options.validate]
* @param {*} [options.context]
* @param {string|Array<string>} [options.returning]
* @return {Promise<{executeTime:number, queriesExecuted:number, rowsAffected:number, instances:Array<Object>}>}
*/
updateMany(values, options = {}) {
return promisify(() => {
async updateMany(values, options = {}) {
if (typeof values !== 'object' || Array.isArray(values))
throw new ArgumentError('You must provide values');
this.validateValuesForUpdate(values, options.context, {
ignoreUnknownProperties: options.ignoreUnknownProperties
});
const dataValues = this._normalizeValues(values);
const returning = this._prepareReturningOptions(options.returning);
const args = {
values,
options: {...options}
};
args.options.ignoreUnknownProperties =
options.ignoreUnknownProperties != null ?
options.ignoreUnknownProperties : this.orm.options.ignoreUnknownProperties;
args.options.where = makeArray(args.options.where);
let where = this._mapConditions(options.where);
where = Array.isArray(where) ? /* istanbul ignore next */ where : [where];
if (options.prepare)
await options.prepare(args);
const dbobj = (options.connection || this.orm.pool);
return dbobj
.update(this.tableNameFull, dataValues)
.where(...where)
.returning(returning && returning.dataTypes)
.execute({
objectRows: true,
autoCommit: options.autoCommit
}).then(resp => {
const ret = {
executeTime: resp.executeTime,
queriesExecuted: 1,
rowsAffected: resp.rowsAffected
};
if (returning) {
const rows = resp.rows;
returning.context = options.context;
return this._prepareReturningResponse(dbobj, rows, returning)
.then(o => {
/* istanbul ignore else */
if (o)
ret.instances = o;
return ret;
});
}
return ret;
});
});
this.validateValuesForUpdate(args.values,
args.options.ignoreUnknownProperties,
args.options.context);
if (options.validate)
await options.validate(values, args.options);
const dataValues = this._preparePostValues(values);
const returning = this.normalizeReturning(options.returning);
const where = makeArray(this._mapConditions(options.where));
const dbobj = (options.connection || this.orm.pool);
return dbobj
.update(this.tableNameFull, dataValues)
.where(...where)
.returning(returning && returning.dataTypes)
.execute({
objectRows: true,
autoCommit: options.autoCommit
}).then(resp => {
const ret = {
executeTime: resp.executeTime,
queriesExecuted: 1,
rowsAffected: resp.rowsAffected
};
if (returning) {
const rows = resp.rows;
returning.context = options.context;
return this._prepareReturningResponse(dbobj, rows, returning)
.then(o => {
/* istanbul ignore else */
if (o)
ret.instances = o;
return ret;
});
}
return ret;
});
}

@@ -476,4 +554,4 @@

return this.destroyMany(merge({
where: this._prepareKeyValues(keyValues)
}, options, {adjunct: true}));
where: this.normalizeKeyValues(keyValues)
}, options, {combine: true}));
}

@@ -485,28 +563,32 @@

* @param {Object} [options]
* @param {Object} [options.connection]
* @param {Boolean} [options.autoCommit]
* @param {Object|Array<Object>} [options.where]
* @param {Boolean} [options.autoCommit]
* @param {Object} [options.connection]
* @param {Boolean} [options.ignoreUnknownProperties]
* @param {Function} [options.prepare]
* @return {Promise<{executeTime:number, queriesExecuted:number, rowsAffected:number}>}
*/
destroyMany(/*istanbul ignore next*/ options = {}) {
return promisify(() => {
async destroyMany(/*istanbul ignore next*/ options = {}) {
/*istanbul ignore else*/
if (options.where) {
options.where = this._mapConditions(options.where);
options.where = Array.isArray(options.where) ?
/* istanbul ignore next */ options.where : [options.where];
}
const args = {options};
args.options.ignoreUnknownProperties =
options.ignoreUnknownProperties != null ?
options.ignoreUnknownProperties : this.orm.options.ignoreUnknownProperties;
args.options.where = makeArray(args.options.where || []);
const dbobj = (options.connection || this.orm.pool);
return dbobj
.delete(this.tableNameFull)
.where(...options.where)
.execute({
autoCommit: options.autoCommit
}).then((result) => {
result.queriesExecuted = 1;
return result;
});
});
if (options.prepare)
await options.prepare(args);
const where = this._mapConditions(makeArray(args.options.where));
const dbobj = (options.connection || this.orm.pool);
return dbobj
.delete(this.tableNameFull)
.where(...where)
.execute({
autoCommit: options.autoCommit
}).then((result) => {
result.queriesExecuted = 1;
return result;
});
}

@@ -553,31 +635,52 @@

_prepareKeyValues(keyValues) {
/**
*
* @param {*} keyValues
* @return {Object}
*/
normalizeKeyValues(keyValues) {
/* istanbul ignore next */
if (!(this.keyFields && this.keyFields.length))
throw new ArgumentError('Model "%s" has no key fields');
return normalizeKeyValues(keyValues, this.keyFields);
}
if (this.keyFields.length > 1 || typeof keyValues === 'object') {
/* istanbul ignore next */
if (typeof keyValues !== 'object')
throw new ArgumentError('You must all provide all key values in object instance.');
const result = {};
for (const f of this.keyFields) {
if (keyValues[f] === undefined)
throw new ArgumentError('You must provide all key values in object instance.');
result[f] = keyValues[f];
}
/*istanbul ignore else */
if (typeof keyValues === 'object')
merge(result, keyValues, {adjunct: true});
return result;
}
if (keyValues === undefined)
throw new ArgumentError('You must all provide all key values');
return {
[this.keyFields[0]]: keyValues != null ? keyValues :
/* istanbul ignore next */null
};
/**
*
* @param {Object} options
* @return {Object}
*/
normalizeFindOptions(options = {}) {
if (!options.properties || !Object.keys(options.properties).length)
options = {...options, properties: this.getDataFields()};
options = normalizeFindOptions(options);
if (!options.sort && this._defaultSort)
options.sort = this._defaultSort;
return options;
}
_prepareReturningOptions(value) {
/**
*
* @param {Object} options
* @return {Object}
*/
normalizeGetOptions(options = {}) {
options = this.normalizeFindOptions(options);
delete options.sort;
delete options.offset;
options.limit = 1;
return options;
}
// noinspection JSMethodCanBeStatic
/**
*
* @param {Object} properties
* @return {Object}
*/
normalizeProperties(properties) {
return normalizeProperties(properties);
}
normalizeReturning(value) {
if (!value)

@@ -587,3 +690,3 @@ return;

(typeof value === 'object' ? value : [value]);
properties = normalizeProperties(properties);
properties = this.normalizeProperties(properties);
/* Be sure key fields exists in properties */

@@ -667,3 +770,2 @@ /* istanbul ignore else */

const opts = {
model: this,
connection: dbobj,

@@ -679,3 +781,3 @@ properties: options.properties,

promises.push((new Finder(opts)).execute()
promises.push((new Finder(this, opts)).execute()
.then(result => result.instances[0]));

@@ -704,12 +806,8 @@ }

* @param {Object} values
* @param {*} [context]
* @param {Object} [options]
* @param {boolean} [options.ignoreUnknownProperties=false]
* @param {boolean} [ignoreUnknownProperties=false]
* @param {Object} [context]
* @return {Object}
*/
validateValuesForCreate(values, context, options = {}) {
return this._validateValues(values, context, {
...options,
forCreate: true
});
validateValuesForCreate(values, ignoreUnknownProperties, context) {
return this._validateValues(values, ignoreUnknownProperties, true, context);
}

@@ -720,12 +818,8 @@

* @param {Object} values
* @param {*} [context]
* @param {Object} [options]
* @param {boolean} [options.ignoreUnknownProperties=false]
* @param {boolean} [ignoreUnknownProperties=false]
* @param {Object} [context]
* @return {Object}
*/
validateValuesForUpdate(values, context, options = {}) {
return this._validateValues(values, context, {
...options,
forCreate: false
});
validateValuesForUpdate(values, ignoreUnknownProperties, context) {
return this._validateValues(values, ignoreUnknownProperties, false, context);
}

@@ -736,9 +830,8 @@

* @param {Object} values
* @param {*} [context]
* @param {Object} [options]
* @param {boolean} [options.ignoreUnknownProperties=false]
* @param {boolean} [options.forCreate=false]
* @param {boolean} [ignoreUnknownProperties=false]
* @param {boolean} [toCreate=false]
* @param {Object} [context]
* @private
*/
_validateValues(values, context, options = {}) {
_validateValues(values, ignoreUnknownProperties, toCreate, context) {
if (typeof values !== 'object' || Array.isArray(values))

@@ -748,3 +841,3 @@ throw new ArgumentError('You must provide values');

/* istanbul ignore else */
if (!options.ignoreUnknownProperties) {
if (!ignoreUnknownProperties) {
for (const name of Object.keys(values)) {

@@ -763,3 +856,3 @@ this.getField(name);

if (field.required && field.defaultValue == null && v == null &&
(options.forCreate || v !== undefined)
(toCreate || v !== undefined)
) {

@@ -772,3 +865,3 @@ throw new ValidationError('Value required for "%s"', name)

}
field.validate(v, values, context);
field.validate(v, values, toCreate, context);
}

@@ -783,3 +876,3 @@ }

*/
_normalizeValues(values) {
_preparePostValues(values) {
const result = {};

@@ -786,0 +879,0 @@ for (const name of Object.keys(this.fields)) {

{
"name": "uniqorm",
"description": "Multi dialect and multi schema ORM framework for enterprise level NodeJS applications",
"version": "2.20.3",
"version": "2.21.0",
"author": "Panates Ltd.",

@@ -29,3 +29,3 @@ "contributors": [

"putil-isplainobject": "^1.0.2",
"putil-merge": "^3.2.0",
"putil-merge": "^3.3.0",
"putil-promisify": "^1.4.0",

@@ -43,3 +43,3 @@ "putil-waterfall": "^2.1.1"

"rejected-or-not": "^1.0.1",
"sqb": "^3.8.6",
"sqb": "^3.8.7",
"sqb-connect-pg": "^3.1.8"

@@ -51,3 +51,3 @@ },

"engines": {
"node": ">= 6.0"
"node": ">= 8.0"
},

@@ -54,0 +54,0 @@ "files": [

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