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

electrodb

Package Overview
Dependencies
Maintainers
1
Versions
163
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

electrodb - npm Package Compare versions

Comparing version 0.9.19 to 0.9.20

src/errors.js

2

package.json
{
"name": "electrodb",
"version": "0.9.19",
"version": "0.9.20",
"description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -340,3 +340,3 @@ const { QueryTypes, MethodTypes } = require("./types");

}
return entity.go(state.query.method, params, options);
return entity.go(state.query.method, params, options)
},

@@ -343,0 +343,0 @@ children: [],

"use strict";
const { Schema } = require("./schema");
const { ElectroInstance, KeyTypes, QueryTypes, MethodTypes, Comparisons, ExpressionTypes } = require("./types");
const { ElectroInstance, KeyTypes, QueryTypes, MethodTypes, Comparisons, ExpressionTypes, ModelVersions } = require("./types");
const { FilterFactory, FilterTypes } = require("./filters");
const { WhereFactory } = require("./where");
const { clauses } = require("./clauses");
const validations = require("./validations");
const { clauses } = require("./clauses");
const utilities = require("./util");

@@ -13,17 +14,10 @@ class Entity {

this.client = config.client;
this.model = this._parseModel(model);
this._filterBuilder = new FilterFactory(
this.model.schema.attributes,
FilterTypes,
);
this._whereBuilder = new WhereFactory(
this.model.schema.attributes,
FilterTypes
)
this._clausesWithFilters = this._filterBuilder.injectFilterClauses(
clauses,
this.model.filters,
);
this.model = this._parseModel(model, config);
/** start beta/v1 condition **/
this.model.table = config.table || model.table;
/** end beta/v1 condition **/
this._filterBuilder = new FilterFactory(this.model.schema.attributes, FilterTypes);
this._whereBuilder = new WhereFactory(this.model.schema.attributes, FilterTypes);
this._clausesWithFilters = this._filterBuilder.injectFilterClauses(clauses, this.model.filters);
this._clausesWithFilters = this._whereBuilder.injectWhereClauses(this._clausesWithFilters);
this.scan = this._makeChain("", this._clausesWithFilters, clauses.index).scan();

@@ -34,11 +28,24 @@ this.query = {};

this.query[accessPattern] = (...values) => {
return this._makeChain(index, this._clausesWithFilters, clauses.index).query(
...values,
);
return this._makeChain(index, this._clausesWithFilters, clauses.index).query(...values);
};
}
this.modelAttributeIdentifier = "__edb_e__"
config.identifiers = config.identifiers || {};
this.identifiers = {
entity: config.identifiers.entity || "__edb_e__",
version: config.identifiers.version || "__edb_v__",
};
this._instance = ElectroInstance.entity;
}
setIdentifier(type = "", identifier = "") {
if (!this.identifiers[type]) {
throw new Error(`Invalid identifier type: ${type}. Valid indentifiers include ${Object.keys(this.identifiers[type]).join(", ")}`);
}
if (typeof identifier === "string" && identifier.length > 0) {
this.identifiers[type] = identifier;
} else {
throw new Error(`Invalid Identifier. Value must be string with length greater than zero.`);
}
}
find(facets = {}) {

@@ -71,3 +78,10 @@ let match = this._findBestIndexKeyMatch(facets);

collection(collection = "", clauses = {}, facets = {}) {
collection(collection = "", clauses = {}, facets = {}, expressions = {}) {
let options = {
expressions: {
names: expressions.names || {},
values: expressions.values|| {},
expression: expressions.expression || ""
}
};
let index = this.model.translations.collections.fromCollectionToIndex[

@@ -79,5 +93,5 @@ collection

}
return this._makeChain(index, this._clausesWithFilters, clauses.index).collection(
return this._makeChain(index, this._clausesWithFilters, clauses.index, options).collection(
collection,
facets,
facets
);

@@ -111,3 +125,3 @@ }

}
}
};
return this._makeChain(index, this._clausesWithFilters, clauses.index, options).create(attributes);

@@ -127,3 +141,3 @@ }

}
}
};
return this._makeChain(index, this._clausesWithFilters, clauses.index, options).patch(facets);

@@ -225,4 +239,4 @@ }

}
if (config.pager) {

@@ -274,3 +288,3 @@ let nextPage = response.LastEvaluatedKey || null;

let match = key.match(regex);
let results = {}
let results = {};
if (!match) {

@@ -312,3 +326,3 @@ if (Object.keys(backupFacets || {}).length === 0) {

}
let pager = this._deconstructIndex(index, lastEvaluated, lastReturned);

@@ -318,7 +332,7 @@ let tableIndex = "";

if (index !== tableIndex) {
pager = {...pager, ...this._deconstructIndex(tableIndex, lastEvaluated, lastReturned)};
pager = {...pager, ...this._deconstructIndex(tableIndex, lastEvaluated, lastReturned)};
}
if (Object.keys(pager).length === 0) {
// In this case no suitable record could be found be the deconstructed pager.
// In this case no suitable record could be found be the deconstructed pager.
// This can be valid in cases where a scan is performed but returns no results.

@@ -332,4 +346,4 @@ return null;

_constructPagerIndex(index = "", item) {
let pk = this._expectFacets(item, this.model.facets.byIndex[index].pk)
let sk = this._expectFacets(item, this.model.facets.byIndex[index].sk)
let pk = this._expectFacets(item, this.model.facets.byIndex[index].pk);
let sk = this._expectFacets(item, this.model.facets.byIndex[index].sk);
let keys = this._makeIndexKeys(index, pk, sk);

@@ -350,3 +364,3 @@ return this._makeParameterKey(index, keys.pk, ...keys.sk);

}
_applyParameterOptions(params, ...options) {

@@ -423,3 +437,3 @@ let config = {

_makeCreateConditions(index) {
let filter = [`attribute_not_exists(pk)`]
let filter = [`attribute_not_exists(pk)`];
if (this.model.lookup.indexHasSortKeys[index]) {

@@ -432,3 +446,3 @@ filter.push(`attribute_not_exists(sk)`);

_makePatchConditions(index) {
let filter = [`attribute_exists(pk)`]
let filter = [`attribute_exists(pk)`];
if (this.model.lookup.indexHasSortKeys[index]) {

@@ -473,3 +487,2 @@ filter.push(`attribute_exists(sk)`);

case MethodTypes.patch:
case MethodTypes.patch:
params = this._makeUpdateParams(

@@ -505,2 +518,16 @@ update,

getIdentifierExpressions() {
return {
names: {
[`#${this.identifiers.entity}_${this.model.entity}`]: this.identifiers.entity,
[`#${this.identifiers.version}_${this.model.entity}`]: this.identifiers.version,
},
values: {
[`:${this.identifiers.entity}_${this.model.entity}`]: this.model.entity,
[`:${this.identifiers.version}_${this.model.entity}`]: this.model.version,
},
expression: `(#${this.identifiers.entity}_${this.model.entity} = :${this.identifiers.entity}_${this.model.entity} AND #${this.identifiers.version}_${this.model.entity} = :${this.identifiers.version}_${this.model.entity})`
}
}
/* istanbul ignore next */

@@ -531,5 +558,7 @@ _makeScanParam(filter = {}) {

};
params.ExpressionAttributeNames["#" + this.modelAttributeIdentifier] = this.modelAttributeIdentifier;
params.ExpressionAttributeValues[":" + this.modelAttributeIdentifier] = this.model.entity;
params.FilterExpression = `${params.FilterExpression} AND #${this.modelAttributeIdentifier} = :${this.modelAttributeIdentifier}`
params.ExpressionAttributeNames["#" + this.identifiers.entity] = this.identifiers.entity;
params.ExpressionAttributeNames["#" + this.identifiers.version] = this.identifiers.version;
params.ExpressionAttributeValues[":" + this.identifiers.entity] = this.model.entity;
params.ExpressionAttributeValues[":" + this.identifiers.version] = this.model.version;
params.FilterExpression = `${params.FilterExpression} AND #${this.identifiers.entity} = :${this.identifiers.entity} AND #${this.identifiers.version} = :${this.identifiers.version}`;
if (hasSortKey) {

@@ -549,4 +578,3 @@ params.FilterExpression = `${params.FilterExpression} AND begins_with(#sk, :sk))`;

let TableName = this.model.table;
let params = {Key, TableName};
return params;
return {Key, TableName};
}

@@ -569,3 +597,3 @@

let params = {
return {
UpdateExpression,

@@ -577,3 +605,2 @@ ExpressionAttributeNames,

};
return params;
}

@@ -586,11 +613,11 @@

let transatedFields = this.model.schema.translateToFields(setAttributes);
let params = {
return {
Item: {
...transatedFields,
...updatedKeys,
[this.modelAttributeIdentifier]: this.model.entity,
[this.identifiers.entity]: this.model.entity,
[this.identifiers.version]: this.model.version,
},
TableName: this.model.table,
};
return params;
}

@@ -629,3 +656,3 @@

/* istanbul ignore next */
_expressionAttributeBuilder(item = {}, options = {}, {noDuplicateNames} = {}) {
_expressionAttributeBuilder(item = {}, options = {}) {
let {

@@ -706,2 +733,3 @@ require = [],

parameters = this._makeBeginsWithQueryParams(
chainState.options,
chainState.index,

@@ -715,2 +743,3 @@ chainState.filter,

parameters = this._makeBeginsWithQueryParams(
chainState.options,
chainState.index,

@@ -776,3 +805,3 @@ chainState.filter,

_makeBeginsWithQueryParams(index, filter, pk, sk) {
_makeBeginsWithQueryParams(options, index, filter, pk, sk) {
let keyExpressions = this._queryKeyExpressionAttributeBuilder(index, pk, sk);

@@ -783,7 +812,12 @@ let KeyConditionExpression = "#pk = :pk";

}
let customExpressions = {
names: (options.expressions && options.expressions.names) || {},
values: (options.expressions && options.expressions.values) || {},
expression: (options.expressions && options.expressions.expression) || ""
};
let params = {
KeyConditionExpression,
TableName: this.model.table,
ExpressionAttributeNames: this._mergeExpressionsAttributes(filter.ExpressionAttributeNames, keyExpressions.ExpressionAttributeNames),
ExpressionAttributeValues: this._mergeExpressionsAttributes(filter.ExpressionAttributeValues, keyExpressions.ExpressionAttributeValues),
ExpressionAttributeNames: this._mergeExpressionsAttributes(filter.ExpressionAttributeNames, keyExpressions.ExpressionAttributeNames, customExpressions.names),
ExpressionAttributeValues: this._mergeExpressionsAttributes(filter.ExpressionAttributeValues, keyExpressions.ExpressionAttributeValues, customExpressions.values),
};

@@ -793,4 +827,5 @@ if (index) {

}
if (filter.FilterExpression) {
params.FilterExpression = filter.FilterExpression;
let expressions = [customExpressions.expression, filter.FilterExpression].filter(Boolean).join(" AND ");
if (expressions.length) {
params.FilterExpression = expressions;
}

@@ -1035,3 +1070,3 @@ return params;

_makeKeyPrefixes(service, entity, version = "1", tableIndex) {
_makeKeyPrefixes(service, entity, version = "1", tableIndex, modelVersion) {
/*

@@ -1042,3 +1077,3 @@ Collections will prefix the sort key so they can be queried with

of a customKey AND a collection, the collection is ignored to favor
the custom key.
the custom key.
*/

@@ -1055,5 +1090,5 @@

}
}
};
let pk = `$${service}_${version}`;
let pk = `$${service}`;
let sk = "";

@@ -1068,2 +1103,10 @@

/** start beta/v1 condition **/
if (modelVersion === ModelVersions.v1) {
sk = `${sk}_${version}`;
} else {
pk = `${pk}_${version}`;
}
/** end beta/v1 condition **/
// If no sk, append the sk properties to the pk

@@ -1100,47 +1143,47 @@ if (Object.keys(tableIndex.sk).length === 0) {

/* istanbul ignore next */
_getPrefixes({ collection = "", customFacets = {}, sk } = {}) {
/*
Collections will prefix the sort key so they can be queried with
a "begins_with" operator when crossing entities. It is also possible
that the user defined a custom key on either the PK or SK. In the case
of a customKey AND a collection, the collection is ignored to favor
the custom key.
*/
// _getPrefixes({ collection = "", customFacets = {}, sk } = {}) {
// /*
// Collections will prefix the sort key so they can be queried with
// a "begins_with" operator when crossing entities. It is also possible
// that the user defined a custom key on either the PK or SK. In the case
// of a customKey AND a collection, the collection is ignored to favor
// the custom key.
// */
//
// let keys = {
// pk: {
// prefix: "",
// isCustom: false,
// },
// sk: {
// prefix: "",
// isCustom: false,
// },
// };
//
// if (collection) {
// keys.pk.prefix = this.model.prefixes.pk;
// keys.sk.prefix = `$${collection}#${this.model.entity}`;
// } else {
// keys.pk.prefix = this.model.prefixes.pk;
// keys.sk.prefix = this.model.prefixes.sk;
// }
//
// if (sk === undefined) {
// keys.pk.prefix += keys.sk.prefix;
// }
//
// if (customFacets.pk) {
// keys.pk.prefix = "";
// keys.pk.isCustom = customFacets.pk;
// }
//
// if (customFacets.sk) {
// keys.sk.prefix = "";
// keys.sk.isCustom = customFacets.sk;
// }
//
// return keys;
// }
let keys = {
pk: {
prefix: "",
isCustom: false,
},
sk: {
prefix: "",
isCustom: false,
},
};
if (collection) {
keys.pk.prefix = this.model.prefixes.pk;
keys.sk.prefix = `$${collection}#${this.model.entity}`;
} else {
keys.pk.prefix = this.model.prefixes.pk;
keys.sk.prefix = this.model.prefixes.sk;
}
if (sk === undefined) {
keys.pk.prefix += keys.sk.prefix;
}
if (customFacets.pk) {
keys.pk.prefix = "";
keys.pk.isCustom = customFacets.pk;
}
if (customFacets.sk) {
keys.sk.prefix = "";
keys.sk.isCustom = customFacets.sk;
}
return keys;
}
/* istanbul ignore next */

@@ -1336,5 +1379,5 @@ _makeIndexKeys(index = "", pkFacets = {}, ...skFacets) {

customFacets.pk = parsedPKFacets.isCustom;
// labels can be set via the attribute definiton or as part of the facetTemplate.
// labels can be set via the attribute definiton or as part of the facetTemplate.
facets.labels = Object.assign({}, facets.labels, facetLabels);
let pk = {

@@ -1350,3 +1393,3 @@ accessPattern,

let sk = {};
let parsedSKFacets = {}
let parsedSKFacets = {};
if (hasSk) {

@@ -1431,4 +1474,4 @@ parsedSKFacets = this._parseFacets(index.sk.facets);

};
attributes.forEach(({index, type, name}, j) => {

@@ -1452,3 +1495,3 @@ let next = attributes[j + 1] !== undefined ? attributes[j + 1].name : "";

}
return {

@@ -1482,7 +1525,7 @@ facets,

_normalizePrefixes(service, entity, version, indexes) {
_normalizePrefixes(service, entity, version, indexes, modelVersion) {
let prefixes = {};
for (let accessPattern of Object.keys(indexes)) {
let item = indexes[accessPattern];
prefixes[item.index] = this._makeKeyPrefixes(service, entity, version, item);
prefixes[item.index] = this._makeKeyPrefixes(service, entity, version, item, modelVersion);
}

@@ -1492,5 +1535,27 @@ return prefixes;

_parseModel(model) {
let { service, entity, table, version = "1" } = model;
_parseModel(model, config = {}) {
/** start beta/v1 condition **/
let modelVersion = utilities.getModelVersion(model);
let service, entity, version, table;
switch(modelVersion) {
case ModelVersions.beta:
service = model.service;
entity = model.entity;
version = model.version;
table = config.table || model.table;
break;
case ModelVersions.v1:
service = model.model && model.model.service;
entity = model.model && model.model.entity;
version = model.model && model.model.version;
table = config.table || model.table;
break;
default:
throw new Error("Invalid model");
}
if(typeof table !== "string" || table.length === 0) {
throw new Error(`config.table must be string`);
}
/** end beta/v1 condition **/
let {

@@ -1507,4 +1572,5 @@ facets,

let filters = this._normalizeFilters(model.filters);
let prefixes = this._normalizePrefixes(service, entity, version, indexes);
let prefixes = this._normalizePrefixes(service, entity, version, indexes, modelVersion);
return {
modelVersion,
service,

@@ -1511,0 +1577,0 @@ version,

const { Entity } = require("./entity");
const { clauses } = require("./clauses");
const { ElectroInstance } = require("./types");
const { ElectroInstance, ElectroInstanceTypes, ModelVersions } = require("./types");
const { FilterFactory, FilterTypes } = require("./filters");
const { getInstanceType, getModelVersion, applyBetaModelOverrides } = require("./util");
const v = require("./validations");
class Service {
constructor(service = {}, config = {}) {
this.service = {
name: service.service,
table: service.table,
version: service.version,
};
constructor(service = "", config = {}) {
this.service = {};
/** start beta/v1 condition **/
this._modelOverrides = {};
this._modelVersion = ModelVersions.v1;
if (v.isObjectHasLength(service)) {
this._modelVersion = ModelVersions.beta;
this._modelOverrides = {
table: service.table,
service: service.service,
version: service.version,
};
} else if (v.isStringHasLength(service)) {
this._modelVersion = ModelVersions.v1;
this.service.name = service;
this.service.table = config.table;
this._modelOverrides.table = config.table;
} else {
throw new Error(`Invalid service name: ${JSON.stringify(service)}. Service name must have length greater than zero`);
}
/** end beta/v1 condition **/
this.config = config;

@@ -22,14 +39,51 @@ this.client = config.client;

join(model = {}, config = {}) {
let name = model.entity;
model.service = this.service.name;
model.table = this.service.table;
model.version = this.service.version;
join(instance = {}, config = {}) {
let options = { ...config, ...this.config };
this.entities[name] = new Entity(model, options);
let entity = {};
let type = getInstanceType(instance);
/** start beta/v1 condition **/
let modelVersion = getModelVersion(instance);
/** end beta/v1 condition **/
switch(type) {
case ElectroInstanceTypes.model:
entity = new Entity(instance, options);
break;
case ElectroInstanceTypes.entity:
entity = instance;
break;
default:
/** start beta/v1 condition **/
if (modelVersion !== this._modelVersion) {
throw new Error("Invalid instance: Valid instances to join include Models and Entity instances. Additionally, all models must be in the same format (v1 vs beta). Review https://github.com/tywalch/electrodb#version-v1-migration for more detail.");
} else if (modelVersion === ModelVersions.beta) {
instance = applyBetaModelOverrides(instance, this._modelOverrides);
} else {
throw new Error(`Invalid instance: Valid instances to join include Models and Entity instances.`);
}
entity = new Entity(instance, options);
/** end beta/v1 condition **/
break;
}
let name = entity.model.entity;
if (this.service.name !== this.service.name) {
throw new Error(``)
}
if (this.service.table) {
entity.table = this.service.table;
}
if (this.service.version) {
entity.model.version = this.service.version;
}
this.entities[name] = entity;
for (let collection of this.entities[name].model.collections) {
this._addCollectionEntity(collection, name, this.entities[name]);
this.collections[collection] = (...facets) => {
let { entities, attributes } = this.collectionSchema[collection];
return this._makeCollectionChain(collection, attributes, clauses, Object.values(entities)[0], ...facets);
let { entities, attributes, identifiers } = this.collectionSchema[collection];
return this._makeCollectionChain(collection, attributes, clauses, identifiers, Object.values(entities)[0], ...facets);
};

@@ -41,6 +95,6 @@ }

_makeCollectionChain(name = "", attributes = {}, clauses = {}, entity = {}, facets = {}) {
_makeCollectionChain(name = "", attributes = {}, clauses = {}, identifiers = {}, entity = {}, facets = {}) {
let filterBuilder = new FilterFactory(attributes, FilterTypes);
clauses = filterBuilder.injectFilterClauses(clauses);
return new Proxy(entity.collection(name, clauses, facets), {
return new Proxy(entity.collection(name, clauses, facets, identifiers), {
get: (target, prop) => {

@@ -174,9 +228,32 @@ if (prop === "go") {

attributes: {},
identifiers: {
names: {},
values: {},
expression: ""
}
};
if (this.collectionSchema[collection].entities[name] !== undefined) {
throw new Error(`Entity with name ${name} has already been joined to this service.`);
}
this.collectionSchema[collection].keys = this._processEntityKeys(this.collectionSchema[collection].keys, providedIndex);
this.collectionSchema[collection].attributes = this._processEntityAttributes(this.collectionSchema[collection].attributes, entity.model.schema.attributes);
this.collectionSchema[collection].entities[name] = entity;
this.collectionSchema[collection].identifiers = this._processEntityIdentifiers(this.collectionSchema[collection].identifiers, entity.getIdentifierExpressions());
}
_processEntityIdentifiers(existing = {}, {names, values, expression} = {}) {
let identifiers = {};
if (names) {
identifiers.names = Object.assign({}, existing.names, names);
}
if (values) {
identifiers.values = Object.assign({}, existing.values, values);
}
if (expression) {
identifiers.expression = [existing.expression, expression].filter(Boolean).join(" OR ");
}
return identifiers;
}
}
module.exports = { Service };

@@ -53,9 +53,22 @@ const KeyTypes = {

FilterExpression: "FilterExpression"
}
};
const ElectroInstance = {
entity: Symbol("entity"),
service: Symbol("service")
}
service: Symbol("service"),
electro: Symbol("electro"),
};
const ElectroInstanceTypes = {
electro: "electro",
service: "service",
entity: "entity",
model: "model"
};
const ModelVersions = {
beta: "beta",
v1: "v1"
};
module.exports = {

@@ -67,5 +80,7 @@ KeyTypes,

Comparisons,
ModelVersions,
AttributeTypes,
ExpressionTypes,
ElectroInstance,
ElectroInstanceTypes,
};

@@ -104,6 +104,45 @@ const Validator = require("jsonschema").Validator;

const Model = {
const Modelv1= {
type: "object",
required: true,
properties: {
model: {
type: "object",
required: true,
properties: {
entity: {
type: "string",
required: true
},
version: {
type: "string",
required: true
},
service: {
type: "string",
required: true
}
}
},
attributes: {
type: "object",
patternProperties: {
["."]: { $ref: "/Attribute" },
},
},
indexes: {
type: "object",
minProperties: 1,
patternProperties: {
["."]: { $ref: "/Index" },
},
},
filters: { $ref: "/Filters" },
},
};
const ModelBeta = {
type: "object",
required: true,
properties: {
service: {

@@ -119,3 +158,2 @@ type: "string",

type: "string",
required: true,
},

@@ -157,29 +195,63 @@ version: {

v.addSchema(Filters, "/Filters");
v.addSchema(Model, "/Model");
v.addSchema(ModelBeta, "/ModelBeta");
v.addSchema(Modelv1, "/Modelv1");
function validateModel(model = {}) {
let errors = v.validate(model, "/Model").errors;
if (errors.length) {
throw new Error(
errors
.map((err) => {
let message = `${err.property}`;
switch (err.argument) {
case "isFunction":
return `${message} must be a function`;
case "isFunctionOrString":
return `${message} must be either a function or string`;
case "isFunctionOrRegexp":
return `${message} must be either a function or Regexp`;
default:
return `${message} ${err.message}`;
}
})
.join(", "),
);
/** start beta/v1 condition **/
let betaErrors = v.validate(model, "/ModelBeta").errors;
if (betaErrors.length) {
/** end/v1 condition **/
let errors = v.validate(model, "/Modelv1").errors;
if (errors.length) {
throw new Error(
errors
.map((err) => {
let message = `${err.property}`;
switch (err.argument) {
case "isFunction":
return `${message} must be a function`;
case "isFunctionOrString":
return `${message} must be either a function or string`;
case "isFunctionOrRegexp":
return `${message} must be either a function or Regexp`;
default:
return `${message} ${err.message}`;
}
})
.join(", "),
);
}
}
}
function testModel(model) {
let isModel = false;
let error = "";
try {
validateModel(model);
isModel = true;
} catch(err) {
error = err.message;
}
return [isModel, error];
}
function isStringHasLength(str) {
return typeof str === "string" && str.length > 0;
}
function isObjectHasLength(obj) {
return typeof obj === "object" && Object.keys(obj).length > 0;
}
function isArrayHasLength(arr) {
return Array.isArray(arr) && arr.length > 0;
}
module.exports = {
model: validateModel,
testModel,
isArrayHasLength,
isStringHasLength,
isObjectHasLength,
};

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

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