npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

wajez-utils

v0.1.1

Published

Handy utils for api development.

Downloads

6

Readme

Wajez Utils

Build Status Coverage Status Join the chat at https://gitter.im/wajez/utils Software License

A bunch of handy functions used to build other Wajez packages.

Contents

Installation

yarn add wajez-utils

or

npm i --save wajez-utils

Generating Random Data

generate :: Schema -> (() -> *)

The function generate takes a Schema and returns a function which when called will return some random data based on the given schema.

Schemas can be defined using the following functions:

string :: {minLength: Number, maxLength: Number, match: Regex, choices: [String]} -> Schema
number :: {min: Number, max: Number} -> Schema
boolean :: () -> Schema
buffer :: () -> Schema
date :: {min: Date, max: Date} -> Schema
array :: {schema: Schema, minLength: Number, maxLangth: Number} -> Schema
object :: {*: Schema} -> Schema

Here are some examples:

const U = require('wajez-utils')

// Generate Strings
const anyString = U.generate(U.string())
const yesOrNo = U.generate(U.string({choices: ['yes', 'no']}))
const email = U.generate(U.string({match: /[a-z0-9._+-]{1,20}@[a-z0-9]{3,15}\.[a-z]{2,4}/}))
const lessThen20Chars = U.generate(U.string({maxLength: 20}))

console.log(anyString())
// @RAJPF1y#FqM%!U(8ESsnr@PMM*c03NN^GRFwPY6*hhWNuwf
console.log(yesOrNo())
// no
console.log(email())
// [email protected]
console.log(email())
// [email protected]
console.log(lessThen20Chars())
// ^CRYr@bP

// Generate Numbers
const anyNumber = U.generate(U.number())
const rating = U.generate(U.number({min: 1, max: 5}))

console.log(anyNumber())
// -46
console.log(rating())
// 4

// Generate Booleans
const anyBoolean = U.generate(U.boolean())
console.log(anyBoolean())
// true
console.log(anyBoolean())
// false

// Generate Dates
const anyDate = U.generate(U.date())
const inTheFuture = U.generate(U.date({min: new Date()}))

console.log(anyDate())
// 2018-10-11T02:09:58.000Z
console.log(inTheFuture())
// 2018-10-31T01:46:48.000Z

// Generate Buffers
const anyBuffer = U.generate(U.buffer())
console.log(anyBuffer())
// <Buffer 4d 6f 26 40 69 29 26 4c 77 28 44 58 52 23 74>

// Generate unique values
const uniqueNumber = U.generate(U.unique(U.number()))
uniqueNumber() // 15
uniqueNumber() // -35
uniqueNumber() // 49

// Generate Objects & Arrays
const person = U.object({
  name: U.string({maxLength: 50}),
  age: U.number({min: 0})
})

const project = U.object({
  name: U.string({maxLength: 25}),
  language: U.string({choices: ['javascript', 'c++', 'php', 'python']})
})

// schemas are composable!
const developer = U.object({
  ... person.fields,
  projects: U.array(project, {maxLength: 10})
})

const newDeveloper = U.generate(developer)

console.log(JSON.stringify(newDeveloper()))
// {
//   "name":"9dMK&KKg@&mKPHDr6L]Md6HL$jqGAA]Z",
//   "age":-741,
//   "projects": [{
//     "name":"xZL2YPl9eW]dsKYOQZB",
//     "language":"python"
//   },{
//     "name":")jE&17",
//     "language":"php"
//   }]
// }

Generating Mongoose Model

The schema of a mongoose model can be defined using the model function:

model :: MongooseModel -> Schema
const mongoose = require('mongoose')
const {Schema} = mongoose
const U = require('wajez-utils')

const Username = {
  type: String,
  match: /[a-zA-Z0-9-_\.]{5,20}/
}

const User = mongoose.model('User', new Schema({
  name: {
    type: String,
    minLength: 3,
    maxLength: 25,
    match: /^[a-z ]+$/
  },
  picture: Buffer,
  since: {
    type: Date,
    max: new Date()
  },
  rating: {
    type: Number,
    min: 0,
    max: 5
  },
  links: {
    facebook: Username,
    twitter: Username,
    github: Username
  }
}))

const generateUser = U.generate(U.model(User))

console.log(generateUser())
// {
//   name: 'gwyvehx raxilunaetkwdzwdwcpwdrpqvp mwpbcxwpku',
//   picture: <Buffer 2a 42 78 71 55 57 38 31 79 30 34 49 39 4b 36 70>,
//   since: 2018-02-12T04:31:15.000Z,
//   rating: 2,
//   links: {
//     facebook: 'VeBL6e-pU',
//     twitter: 'gKGLqLV',
//     github: 'vhIC6BDLvNf4MuHOj_'
//   }
// }

Seeding a Mongoose Database

The function seed can be used to fill a mongodb database with random data based on mongoose models.

seed :: {model1: Number, model2: Number, ...} -> [Relation] -> Promise(*)

The first argument of seed is an object associating each model name to the number of records to generate and insert. The second argument is an array of relations.

Relations can be defined using the functions oneOne, oneMany and manyMany.

oneOne(sourceModel, sourceField, targetModel, targetField)
oneMany(sourceModel, sourceField, targetModel, targetField)
manyMany(sourceModel, sourceField, targetModel, targetField)

Here is a full example

const mongoose = require('mongoose')
const {Schema} = mongoose
const {seed, oneOne, oneMany, manyMany} = require('./src')

mongoose.Promise = global.Promise
mongoose.connect(`mongodb://localhost/wajez-utils`)

const User = mongoose.model('User', new Schema({
  posts: [{
    type: Schema.Types.ObjectId,
    ref: 'Post'
  }],
  profile: {
    type: Schema.Types.ObjectId,
    ref: 'Profile'
  },
  name: String
}))

const Profile = mongoose.model('Profile', new Schema({
  picture: Buffer
}))

const Post = mongoose.model('Post', new Schema({
  writer: {
    type: Schema.Types.ObjectId,
    ref: 'User'
  },
  tags: [{
    type: Schema.Types.ObjectId,
    ref: 'Tag'
  }],
  title: String,
  content: String
}))

const Tag = mongoose.model('Tag', new Schema({
  name: String,
  posts: [{
    type: Schema.Types.ObjectId,
    ref: 'Post'
  }]
}))

seed({
  User: 3,
  Profile: 3,
  Post: 5,
  Tag: 4
}, [
  oneOne('User', 'profile', 'Profile', null),
  oneMany('User', 'posts', 'Post', 'writer'),
  manyMany('Post', 'tags', 'Tag', 'posts')
])
.then(data => {
  console.log(JSON.stringify(data))
})

// {
//   "User": [
//     {
//       "posts": [
//         "5aed2cd384702a2207c3e2ed",
//         "5aed2cd384702a2207c3e2ee",
//         "5aed2cd384702a2207c3e2ef"
//       ],
//       "_id": "5aed2cd384702a2207c3e2e7",
//       "profile": "5aed2cd384702a2207c3e2ea",
//       "name": "7MthPn7WA)!3y@SjHigl]vFAK&xRo9FfsdMF)dK]#t#HiF@^s^wvFns",
//       "__v": 0
//     },
//     {
//       "posts": [
//         "5aed2cd384702a2207c3e2f0",
//         "5aed2cd384702a2207c3e2f1"
//       ],
//       "_id": "5aed2cd384702a2207c3e2e8",
//       "profile": "5aed2cd384702a2207c3e2eb",
//       "name": "B]([^8*k6padMySnAI1MS%FX^LoLXAHbE&S)OmXw",
//       "__v": 0
//     },
//     {
//       "posts": [],
//       "_id": "5aed2cd384702a2207c3e2e9",
//       "profile": "5aed2cd384702a2207c3e2ec",
//       "name": "!w3I84iTXYtSJdmVJX]c1[d6#*16nd1mqMiDP)g^&o6R053RnNcyxYr0c*5^X18St6RsquCAorFq)KlHWs",
//       "__v": 0
//     }
//   ],
//   "Profile": [
//     {
//       "_id": "5aed2cd384702a2207c3e2ea",
//       "picture": {
//         "type": "Buffer",
//         "data": [52, 53, 97, 105, 67, 114, 42, 99, 41, 118, 40, 38, 42, 35, 78]
//       },
//       "__v": 0
//     },
//     {
//       "_id": "5aed2cd384702a2207c3e2eb",
//       "picture": {
//         "type": "Buffer",
//         "data": [51, 98, 78, 33, 76, 86]
//       },
//       "__v": 0
//     },
//     {
//       "_id": "5aed2cd384702a2207c3e2ec",
//       "picture": {
//         "type": "Buffer",
//         "data": [69, 69, 48, 37, 66, 87, 33, 83, 93, 89, 38, 67, 82]
//       },
//       "__v": 0
//     }
//   ],
//   "Post": [
//     {
//       "tags": [
//         "5aed2cd384702a2207c3e2f2",
//         "5aed2cd384702a2207c3e2f4"
//       ],
//       "_id": "5aed2cd384702a2207c3e2ed",
//       "writer": "5aed2cd384702a2207c3e2e7",
//       "title": "3F]K8JQnIXsne(whhGn%U*YyrA0vC^pC%pxhKwGU]FAivdhDznMri*Ip&]HT1nY[%DpDegCoBW",
//       "content": "rp",
//       "__v": 0
//     },
//     {
//       "tags": [
//         "5aed2cd384702a2207c3e2f3",
//         "5aed2cd384702a2207c3e2f5"
//       ],
//       "_id": "5aed2cd384702a2207c3e2ee",
//       "writer": "5aed2cd384702a2207c3e2e7",
//       "title": "^8Nkp8B)JHu(YV0s**InJof@J!JGBhlYy6wuQCc#sb^d[K)C]b8jg)PnEDxF#[JFT#]ABT4x%vgWw8CWssemwvmODFSJVdd",
//       "content": "D3cdhdg)hO$@ydIx)T!!St1vy!BfYP1TK2A5$#$g*@o)Qk(xF78a8V5H(QdgokP08&A(mf*tmER6PzoNi(6CeXxn$W%V",
//       "__v": 0
//     },
//     {
//       "tags": [
//         "5aed2cd384702a2207c3e2f2",
//         "5aed2cd384702a2207c3e2f3"
//       ],
//       "_id": "5aed2cd384702a2207c3e2ef",
//       "writer": "5aed2cd384702a2207c3e2e7",
//       "title": "C[dbHT!DaP9KTi3qWpbWYLqIMzZOJE9p[](M0cNnDmnc7h%p#)vzcU%O%l2Dq8YIx0f5eLZe(vFFV#72k0dP6",
//       "content": "(#q#xY)osPBQOTb27h$jPYh[hq38lm37XO$ZZh&zb!zF!2^",
//       "__v": 0
//     },
//     {
//       "tags": [
//         "5aed2cd384702a2207c3e2f2",
//         "5aed2cd384702a2207c3e2f3",
//         "5aed2cd384702a2207c3e2f4"
//       ],
//       "_id": "5aed2cd384702a2207c3e2f0",
//       "writer": "5aed2cd384702a2207c3e2e8",
//       "title": "xGVK&Z%Pf%t@kkozDez[VTedKekVqdnHFj]JnuD@FbrW2R9dhLGIu(oShe9ngv]RY0sV4l!u&tXNh%S@Bl8n**)A0ArOblrEh",
//       "content": "tcNQ$I4oh#fqv2xrh]ioCMQKYB8eXI#IA9xuz4MVDi4*(HKhBe8SIWxTglIR[DAkwWB",
//       "__v": 0
//     },
//     {
//       "tags": [
//         "5aed2cd384702a2207c3e2f2",
//         "5aed2cd384702a2207c3e2f3",
//         "5aed2cd384702a2207c3e2f4"
//       ],
//       "_id": "5aed2cd384702a2207c3e2f1",
//       "writer": "5aed2cd384702a2207c3e2e8",
//       "title": "Lsfm!l)QzkeBUnKBnKgrQ4EpeEMPuT@1GRL$(x#2W]WgaS0TQsuuoIgVnJIa3lJIRFWoVXD(dNjn6fDQ0kvRc&1oEqm!09",
//       "content": "iN7J[oJh7TDI#&*XX6qK7no!9^OwcQdGlMzcjueDVKwQQrAJIIXENvgHHlx3gfLjo%)&btAqGfLJqz",
//       "__v": 0
//     }
//   ],
//   "Tag": [
//     {
//       "posts": [
//         "5aed2cd384702a2207c3e2ed",
//         "5aed2cd384702a2207c3e2ef",
//         "5aed2cd384702a2207c3e2f0",
//         "5aed2cd384702a2207c3e2f1"
//       ],
//       "_id": "5aed2cd384702a2207c3e2f2",
//       "name": "MhT4[w1cVzrNc(%cgJq%A*GknXU%[r7y%#vi$5MZqQTOwKgEAwno76HDoM5V",
//       "__v": 0
//     },
//     {
//       "posts": [
//         "5aed2cd384702a2207c3e2ee",
//         "5aed2cd384702a2207c3e2ef",
//         "5aed2cd384702a2207c3e2f0",
//         "5aed2cd384702a2207c3e2f1"
//       ],
//       "_id": "5aed2cd384702a2207c3e2f3",
//       "name": "BQl2c9ozL39^J$Re@H*ida&!5]V",
//       "__v": 0
//     },
//     {
//       "posts": [
//         "5aed2cd384702a2207c3e2ed",
//         "5aed2cd384702a2207c3e2f0",
//         "5aed2cd384702a2207c3e2f1"
//       ],
//       "_id": "5aed2cd384702a2207c3e2f4",
//       "name": "vgsHTSPoivaAb^)(vE!33G)P8CdHHeNUiA0DdY9a$JiOKHH!5YZCACC3Y&zHbr64xECMdGxf5]dPi[H",
//       "__v": 0
//     },
//     {
//       "posts": [
//         "5aed2cd384702a2207c3e2ee"
//       ],
//       "_id": "5aed2cd384702a2207c3e2f5",
//       "name": "f#(BSFM)Ez749b7IJW5wyyuu$jVgcMRd3NQ7OX0RYXTYoAy",
//       "__v": 0
//     }
//   ]
// }

Converting Data

The function applyConverter can be used to apply a converter on data. A converter is an object which specifies what function to apply on each property of the given data.

We will start with a full example, then explain different ways to use applyConverter.

Full Example

Let's assume I have the following user infos returned from database

const user = {
  _id: 'xxxxxxx',
  _v: 1,
  username: 'webneat',
  password: 'myVerySecurePassword :P',
  profile: {
    _id: 'yyyyyyy',
    _v: 1,
    firstname: 'Amine',
    lastname: 'Ben hammou',
    picture: 'some-url',
  },
  repos: [
    'webNeat/lumen-generators',
    'tarsana/command',
    'wajez/api'
  ]
}

I want to transform it into something like this

{
  username: 'webneat',
  fullName: 'Amine Ben hammou',
  picture: 'some-url',
  repos: [
    {
      name: 'lumen-generators',
      url: 'https://github.com/webNeat/lumen-generators'
    },
    {
      name: 'command',
      url: 'https://github.com/tarsana/command'
    },
    {
      name: 'api',
      url: 'https://github.com/wajez/api'
    }
  ]
}

I would simply do the following

const {applyConverter, I} = require('./src')

const fullName = ({profile: {firstname, lastname}}) => firstname + ' ' + lastname
const repo = name => ({
  name: name.split('/')[1],
  url: `https://github.com/${name}`
})

const convert = applyConverter({
  username: I,
  fullName: fullName,
  picture: _ => _.profile.picture,
  repos: repo
})

console.log(convert(user))
// {
//   username: 'webneat',
//   fullName: 'Amine Ben hammou',
//   picture: 'some-url',
//   repos: [
//     { name: 'lumen-generators', url: 'https://github.com/webNeat/lumen-generators' },
//     { name: 'command', url: 'https://github.com/tarsana/command' },
//     { name: 'api', url: 'https://github.com/wajez/api' }
//   ]
// }

Converter can be a Function

When given a Function as converter, applyConverter will simply apply that function on the value and return the result.

applyConverter(x => x + 1, 5) // 6

Converter can be an Object

When given an object of functions, it will try to apply each function to the corresponding attribute on the value.

const addOne = _ => _ + 1
const triple = _ => _ * 3

const value = { x: 1, y: 2, z: 3 }

applyConverter({ x: triple, y: addOne }, value) // {x: 3, y: 3}

Note that since the key z is not mentioned in the converter, it's not included in the result.

Converter maps over Arrays

When applying a converter to an array, it's applied to each item of the array

const sum = ({x, y}) => x + y

const values = [
  { x: 1, y: 2 },
  { x: 3, y: 4 },
  { x: 5, y: 6 },
]

applyConverter({a: sum}, values) // [{a: 3}, {a: 7}, {a: 11}]

An Array of converters is a converter

const addOne = _ => _ + 1
const triple = _ => _ * 3
const sum = ({x, y}) => x + y

const value = {x: 1, y: 2}

applyConverter(
  [
    {x: addOne, y: triple},
    sum
  ],
  value
) // 8

Converters are recursive

When defining a converter as object, are applied as converters, so they can be functions, objects of converters or array of converters.


const value = {
  x: {
    a: 1,
    y: {
      z: 2
    }
  }
}

applyConverter({x: {y: {z: addOne}}}, value) // {x: {y: {z: 3}}}

Contributing

Feel free to create issues and/or submit Pull Requests!