0.0.738 • Published 1 year ago

lifttl v0.0.738

Weekly downloads
381
License
ISC
Repository
bitbucket
Last release
1 year ago

LiftTL

Strongly typed JavaScript / Typescript framework to build your rest api applications in Node.js.

  • Similar api like graphQL but with better performance boost.

  • Nicely done playground included!

  • This library is still in beta stages. So don't use it in production!

Install LiftTL from npm

npm i lifttl or yarn add lifttl

Import library

es6:

import { Server, createResolver, model } from 'lifttl' // and many more

module:

const { Server, createResolver, model } = require('lifttl') // and many more...

Use

Basic example

basic example how to use resolvers, add it to server etc...

import { Server, createResolver, model, makeRequired } from 'lifttl'

const UserModel = model({
  user_id: { type: 'string' },
})
const userResolver = createResolver({
  name: 'user',
  io: UserModel,
  params: makeRequired(UserModel, ['user_id']), // use userModel but change user_id prop to required so parameter must be there
  resolve: ({ params, io, opt, Error }) => {
    return new UserModel({ user_id: params.user_id }) // You can return new UserModel or null or undefined as well
  },
})

new Server({
  resolvers: [userResolver],
  playground: true,
})

For check this example run it & open playground url

Multiple resolvers example

Same as basic example but with multiple resolver

import { Server, createResolver, model, makeRequired } from 'lifttl'

const UserModel = model({
  user_id: { type: 'string' },
  book_id: { type: 'string' },
})

const userResolver = createResolver({
  name: 'user',
  io: UserModel,
  params: makeRequired(UserModel, ['user_id']),
  resolve: ({ params, io, opt, Error }) => {
    return new UserModel({ user_id: params.user_id })
  },
})

/**
 * Right now we create bookModel
 */
const BookModel = model({
  book_id: { type: 'string' },
  name: { type: 'string' },
})
/**
 * Book resolver
 */
const bookResolver = createResolver({
  name: 'books',
  io: BookModel,
  isArray: true, // if you use isArray prop, you should return BookModel[] or null or undefined
  params: makeRequired(BookModel, ['book_id']),
  resolve: ({ params, io, opt, Error }) => {
    return [new BookModel({ name: 'Harry Potter' }), new BookModel({ name: 'Pulp Fiction' })]
  },
})

new Server({
  resolvers: [userResolver],
  playground: true,
})

For check this example run it & open playground url

Multiple nested resolvers example

Adding nested bookResolver into userResolver.book. You can use as nested resolvers as you want, as well as you can use nested resolvers with normal resolvers...doesn't matters

import { Server, createResolver, model, makeRequired } from 'lifttl'
const UserModel = model({
  user_id: { type: 'string' },
  book_id: { type: 'string' },
})

const userResolver = createResolver({
  name: 'user',
  io: UserModel,
  params: makeRequired(UserModel, ['user_id']),
  resolve: ({ params, io, opt, Error }) => {
    return new UserModel({ user_id: params.user_id })
  },
})

/**
 * Right now we create bookModel
 */
const BookModel = model({
  book_id: { type: 'string' },
  name: { type: 'string' },
})
/**
 * Book resolver
 */
const bookResolver = createResolver({
  name: 'books', // this will generate for you books prop inside userResolvermodel
  io: BookModel,
  isArray: true, // if you use isArray prop, you should return BookModel[] or null or undefined
  params: makeRequired(BookModel, ['book_id']),
  resolve: ({ params, io, opt, Error }) => {
    return [new BookModel({ name: 'Harry Potter' }), new BookModel({ name: 'Pulp Fiction' })]
  },
})

/**
 * Adding bookResolver to userResolver
 */

userResolver.addConnector({
  resolver: bookResolver,
  relation: { book_id: 'book_id' }, // means that book_id return from userResolver === book_id to bookResolver param. Relation parent-return === children-params
  paramRelation: {}, // Same but for relation parent-params === children-params
})

new Server({
  resolvers: [userResolver],
  playground: true,
})

For check this example run it & open playground url

Setters Example

For settings data we are using setters. Of course you can set data inside resolver. But resolvers meant to be more like getter as setter.

import { Server, createResolver, model, makeRequired } from 'lifttl'

const UserModel = model({
  user_id: { type: 'string' },
  book_id: { type: 'string' },
})

const userResolver = createResolver({
  name: 'user',
  io: UserModel,
  params: makeRequired(UserModel, ['user_id']),
  resolve: ({ params, io, opt, Error }) => {
    return new UserModel({ user_id: params.user_id })
  },
})

/**
 * Setter dont have return option. So you dont need to return something from setter.
 */
userResolver.addSetter(
  'register_user',
  makeRequired(UserModel, ['user_id', 'book_id']),
  ({ params, busboy }) => {
    // We provide as well busboy instance if upload server options is provide.. so upload files will be easy as it is. We provide busboy instance here https://github.com/mscdex/busboy
    // params passed here
    console.log(params)
    /**
     * File uplopad examples:
     *
     */
    // if (busboy) {
    //     busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
    //         console.log(filename, 'this is your file')
    //         file.resume() // If you dont make any changes with the file use file.resume for less memoery consumptions as files buffers stays in memory...
    //     })
    // }
  }
)

new Server({
  resolvers: [userResolver],
  upload: true, // If you want to upload file via setter
  playground: true,
})

For check this example run it & open playground url

Custom Error handling

  • for create custom error handling we provide Error function to each resolve, setters & middlewares callbacks.
  • Error handler can be used as well for success statuses. For example status:201 | user_created

use

const userResolver = createResolver({
  name: 'user',
  io: UserModel,
  params: makeRequired(UserModel, ['user_id']),
  resolve: ({ params, io, opt, Error }) => {
    if (!params.user_id) {
      return Error({ msg: 'You dont provide user_id', code: 400 }) // check if user_id params is provided. If not we return error messagge to the user
    } else {
      return new UserModel({ user_id: params.user_id })
    }
  },
})

Error options

NameTypeDescriptionRequiredDefault
msgstringstring of error messaggefalse''
codenumberreturn response codefalse400
successbooleanIf you dont provide code you can just provide successfalsefalse

Methods

Server options

NameTypeDescriptionRequiredDefault
resolversarrayArray of resolverstrue
portnumberport of the serverfalse4000
clusterModebooleanCluster mode enables multiCpu instances for better performancefalsefalse
numCPUsnumberNumber of cpu's when clusterMode is enabledfalserequire('os').cpus().length - 1
http2booleanEnable http2 -> for that you have to provide cert & key optionsfalse
httpsbooleanEnable https -> for that you have to provide cert & key optionsfalse
keyBufferKey file -> fs.readFileSync('path/to/file')false
certBufferCert file -> -> fs.readFileSync('path/to/file')false
playgroundbooleanEnable or disable playground / Default port:3005falsefalse
playgroundPortnumberPlayground portfalse3005
allowedOrigins'*' or string[]Allowed originsfalse
uploadbooleanIf enable file upload.falsefalse
beforeEndeventEvent trigger before end of response send to the clientfalse

Resolver options

  • Resolvers return as well another object which can be modified.
NameTypeDescriptionRequiredDefault
namestringName of resolvertrue
ioModelReturnmodel for input / output handlingtrue
paramsModelReturnmodel for resolver paramstrue
isArraybooleanIf resolver return array of models or notfalsefalse
middlewaresarraycallbackArray of middleware functions which can be used for authorization or another purposesfalse[]
middlewaresOnlyModelbooleanIf true middelwares will be used only for return of model & not settersfalsefalse
resolveasync callbackResolve callback with http req, http res, opt, Error, io, paramstrue

resolve callback options

NameTypeDescriptionRequiredDefault
optobjectObject with info & as well can be used as store per request
ioModelReturnmodel for input / output handling
paramsModelReturnmodel for resolver paramstrue
Errorfunctionfunction properties are: success:boolean, msg:string, code:number
reqhttp.IncomingMessagehttp request
reshttp.ServerResponsehttp response

'Resolver' returned methods

use

const userResolver = createResolver({
    ...
})
userResolver.addSetter('registerUser', UserModel, async({params,busboy, Error}) => {

})
userResolver.addConnector({
  resolver: bookResolver,
  relation: { user_id: 'user_type' },
  paramRelation: {},
})
NameTypeDescriptionRequiredDefault
addSetterfunctionAdd setter function to the resolver
addConnectorfunctionadd connector or relation beetween another resolver (child / parent relation)

addSetter options

  • addSetter allows you to add setter to resolvers. Setters means to set something. So inside this setter you can for example create user or add book to database....

    NameTypeDescriptionRequiredDefault
    namestringName of setter. Name will be add to the final modeltrue
    paramsfunctionadd connector or relation beetween another resolver (child / parent relation)true
    resolveasync callbackResolve callback with http req, http res, opt, Error, io, params , Busboytrue
    middlewaresarraycallbackArray of middleware functions which can be used for authorization or another purposes[]
    limitsbusboyLimits objBusboy limits for file uploading

addConnector options

  • addConnetors allows you to add relation beetween current resolver & another resolver as relation parent / child

    NameTypeDescriptionRequiredDefault
    resolverresolverChild resolvertrue
    relationobjectprop is taken from parent io model & will be transfered to custom child param modeltrue{}
    paramRelationobjectprop is taken from parent param model & will be transfered to custom child param modeltrue{}
Example
  • relation can be => { user_id: 'user_type' } where user_id from parent IO model will be transfered to user_type child resolver param Model.

  • paramRelation can be => { user_id: 'user_type' } where user_id from parent param model will be transfered to user_type child resolver param Model.

Middelwares

  • middleware is callback function which have to return boolean | ErrorReturn | Promise<boolean | ErrorReturn>. If returns true proccess can continue,otherwise process will stop.

For example you will use middleware if you want to check if client / user is authorized.

use

const someMiddleware1 = ({ params, opt, Error }) => {
  return true
}
const someMiddleware2 = ({ params, opt, Error }) => {
  return Error({ ms })
  return false
}
const userResolver = createResolver({
  name: 'user',
  io: UserModel,
  middlewares: [someMiddleware, someMiddleware2],
  params: makeRequired(UserModel, ['user_id']),
  resolve: ({ params, io, opt, Error }) => {
    return new UserModel()
  },
})

Middleware Callback options

NameTypeDescriptionRequiredDefault
optobjectObject with info & as well can be used as store per request
ioModelReturnmodel for input / output handling
Errorfunctionfunction properties are: success:boolean, msg:string, code:number

Another scripts

  • library comes with cli command create so if you install library you can use npx lifttl create command to build you app faster!

Benchmark

  • compared apollo graphql, graphql.js & express -for testing I used bombardier tool.

LiftTL clusterMode & num of cpu-6v example from "Multiple nested resolvers example"

StatisticsAvgStdevMax
Reqs/sec73209.0220940.07103736.40
Latency1.70ms7.18ms257.07ms

HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 52.09MB/s

liftTL normalMode example from "Multiple nested resolvers example"

StatisticsAvgStdevMax
Reqs/sec12375.373024.9529486.88
Latency4.92ms657.69us52.50ms

HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 18.05MB/s

graphQL.js example from https://graphql.org/graphql-js/ & node http server

StatisticsAvgStdevMax
Reqs/sec13684.791056.4014957.20
Latency9.13ms0.99ms72.42ms

HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 3.52MB/s

apollo GraphQL example from https://www.apollographql.com/docs/apollo-server/getting-started/

StatisticsAvgStdevMax
Reqs/sec5252.22724.856368.73
Latency23.83ms2.23ms145.26ms

HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 3.09MB/s

express.js example from https://expressjs.com/en/starter/hello-world.html

StatisticsAvgStdevMax
Reqs/sec17433.232404.0621519.79
Latency7.17ms0.86ms56.33ms

HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 7.28MB/s

Scripts used:

for liftTL

bombardier -c 125 -n 1000000 --header='content-type: application/json;odata=verbose' --method=POST --body='{"user_resolver":{"user_id":true,"book":{"name":true,"author":true,"book_id":true},"name":true,"samko":true,"$params":{"get_user_by_name":"John doe"}}}' http://localhost:4000

for apollo-server bombardier -c 125 -n 1000000 --header='content-type: application/json;odata=verbose' --method=POST --body='{"operationName":null,"variables":{},"query":"{\n books {\n title\n author\n }\n}\n"}' http://localhost:4000

for grapqhl.js with simple http server bombardier -c 125 -n 1000000 --header='content-type: application/json;odata=verbose' --method=POST --body='{ hello }' http://localhost:4000

*Todo - finish readme.

Models api:

Javascript / Typescript type definitions by generic functions.

Most easy way how to create interfaces / enums / models for another purposes.

Import all library

es6:

import { model, enums, required, modelInclude, modelExclude, isArray } from 'lifttl'

module:

const { model, enums, required, modelInclude, modelExclude, isArray } = require('lifttl')

Use

Creating of basic models

import { model } from 'typelift'

const userModel = model({
  user_id: { type: 'uuid', required },
  comment_id: { type: 'uuid', required },
  email: { type: 'string', format: 'email' },
})

const newUser = new userModel({ user_id: '' }) // Intellisense will help you with creating of new model. So each time you will know about types inside model. No matters if you are using js or ts.

console.log(newUser.scheme) // output JSONSCHEMA7 of model

Creating of nested models

import { model, required } from 'typelift'

const bookModel = model({ book_id: { type: 'string' } })

const userModel = model({
  user_id: { type: 'uuid', required },
  comment_id: { type: 'uuid', required },
  email: { type: 'string', format: 'email' },
  book: { type: bookModel }, //you can use nested models as well.
})

const newUser = new userModel({ book: { book_id: '' } })

Creating of enums

import { model, enums } from 'typelift'

const USER_TYPE = enums('ADMIN', 'NORMAL')

const userModel = model({
  user_id: { type: 'uuid' },
  comment_id: { type: 'uuid' },
  email: { type: 'string', format: 'email' },
  user_type: { type: USER_TYPE },
})

const newUser = new userModel({ user_type: 'ADMIN' })

Merging models

import { modelMerge } from 'typelift'

const userModel = model({
  user_id: { type: 'uuid' },
})

const paginationModel = model({
  page: { type: 'number' },
  limit: { type: 'number' },
})

const userParamsModel = modelMerge(userModel, paginationModel) // merge two models together

Make required fields after model creation

import { makeRequired } from 'typelift'

const userModel = model({
  user_id: { type: 'uuid' },
  name: { type: 'string' },
})

const userModelWithRequiredProps = makeRequired(userModel, ['user_id']) // make user_id prop required

Available types

Date boolean number string uuid timeUuid

Each type can be as well undefined until you dont provide required prop into the model like this:

import { model, required } from 'typelift'

const userModel = model({
  user_id: { type: 'uuid', required }, // Required means that for creating new userModel instance the new value will be filled as well.
})

const newUser = new userModel() // newUser will automatically generate new uuid for user_id prop becouse you provide required parameter = true

Available Formats

'date' | 'time' | 'date-time' | 'uri' | 'uri-reference' | 'uri-template' | 'email' | 'hostname' | 'ipv4' | 'uuid'

for type string you can provide as well format property like this

import { model } from 'typelift'

const userModel = model({
  email: { type: 'string', format: 'email' }, // email is type string with format of email
})

Patterns

As well you can use custom regexes like this:

import { model } from 'typelift'

const userModel = model({
  email: { type: 'string', pattern: '[abc]+' }, // validation use new RegExp("[abc]+")
})

Working nicely with javascript coded with love in typescript.

0.0.722

2 years ago

0.0.721

2 years ago

0.0.726

2 years ago

0.0.725

2 years ago

0.0.724

2 years ago

0.0.723

2 years ago

0.0.733

1 year ago

0.0.732

1 year ago

0.0.731

1 year ago

0.0.730

2 years ago

0.0.737

1 year ago

0.0.736

1 year ago

0.0.735

1 year ago

0.0.734

1 year ago

0.0.727

2 years ago

0.0.738

1 year ago

0.0.711

2 years ago

0.0.710

2 years ago

0.0.715

2 years ago

0.0.714

2 years ago

0.0.713

2 years ago

0.0.712

2 years ago

0.0.709

2 years ago

0.0.720

2 years ago

0.0.718

2 years ago

0.0.717

2 years ago

0.0.716

2 years ago

0.0.705

2 years ago

0.0.616

2 years ago

0.0.617

2 years ago

0.0.700

2 years ago

0.0.704

2 years ago

0.0.703

2 years ago

0.0.702

2 years ago

0.0.701

2 years ago

0.0.557

2 years ago

0.0.558

2 years ago

0.0.528

2 years ago

0.0.533

2 years ago

0.0.531

2 years ago

0.0.530

2 years ago

0.0.529

2 years ago

0.0.546

2 years ago

0.0.545

2 years ago

0.0.541

2 years ago

0.0.540

2 years ago

0.0.601

2 years ago

0.0.600

2 years ago

0.0.605

2 years ago

0.0.604

2 years ago

0.0.603

2 years ago

0.0.602

2 years ago

0.0.612

2 years ago

0.0.610

2 years ago

0.0.615

2 years ago

0.0.614

2 years ago

0.0.613

2 years ago

0.0.609

2 years ago

0.0.608

2 years ago

0.0.607

2 years ago

0.0.606

2 years ago

0.0.527

2 years ago

0.0.524

3 years ago

0.0.523

3 years ago

0.0.522

3 years ago

0.0.526

2 years ago

0.0.525

3 years ago

0.0.521

3 years ago

0.0.513

3 years ago

0.0.512

3 years ago

0.0.511

3 years ago

0.0.510

3 years ago

0.0.517

3 years ago

0.0.516

3 years ago

0.0.515

3 years ago

0.0.514

3 years ago

0.0.509

3 years ago

0.0.520

3 years ago

0.0.519

3 years ago

0.0.518

3 years ago

0.0.508

3 years ago

0.0.507

3 years ago

0.0.506

3 years ago

0.0.505

3 years ago

0.0.504

3 years ago

0.0.503

3 years ago

0.0.502

3 years ago

0.0.501

3 years ago

0.0.500

3 years ago

0.0.436

3 years ago

0.0.435

3 years ago

0.0.434

3 years ago

0.0.433

3 years ago

0.0.432

3 years ago

0.0.431

3 years ago

0.0.430

3 years ago

0.0.429

3 years ago

0.0.428

3 years ago

0.0.427

3 years ago

0.0.426

3 years ago

0.0.424

3 years ago

0.0.423

3 years ago

0.0.422

3 years ago

0.0.418

3 years ago

0.0.421

3 years ago

0.0.420

3 years ago

0.0.419

3 years ago

0.0.417

3 years ago

0.0.416

3 years ago

0.0.415

3 years ago

0.0.414

3 years ago

0.0.413

3 years ago

0.0.412

3 years ago

0.0.411

3 years ago

0.0.410

3 years ago

0.0.409

3 years ago

0.0.407

3 years ago

0.0.406

3 years ago

0.0.404

3 years ago

0.0.403

3 years ago

0.0.402

3 years ago

0.0.401

3 years ago

0.0.400

3 years ago

0.0.399

3 years ago

0.0.397

3 years ago

0.0.396

3 years ago

0.0.395

3 years ago

0.0.394

3 years ago

0.0.393

3 years ago

0.0.392

3 years ago

0.0.391

3 years ago

0.0.390

3 years ago

0.0.389

3 years ago

0.0.388

3 years ago

0.0.384

3 years ago

0.0.383

3 years ago

0.0.387

3 years ago

0.0.386

3 years ago

0.0.385

3 years ago

0.0.382

3 years ago

0.0.381

3 years ago

0.0.373

3 years ago

0.0.380

3 years ago

0.0.369

3 years ago

0.0.372

3 years ago

0.0.371

3 years ago

0.0.370

3 years ago

0.0.368

3 years ago

0.0.367

3 years ago

0.0.366

3 years ago

0.0.365

3 years ago

0.0.341

3 years ago

0.0.359

3 years ago

0.0.357

3 years ago

0.0.356

3 years ago

0.0.355

3 years ago

0.0.354

3 years ago

0.0.353

3 years ago

0.0.352

3 years ago

0.0.351

4 years ago

0.0.350

4 years ago

0.0.349

4 years ago

0.0.348

4 years ago

0.0.347

4 years ago

0.0.346

4 years ago

0.0.345

4 years ago

0.0.344

4 years ago

0.0.343

4 years ago

0.0.340

4 years ago

0.0.342

4 years ago

0.0.339

4 years ago

0.0.338

4 years ago

0.0.337

4 years ago

0.0.335

4 years ago

0.0.334

4 years ago

0.0.333

4 years ago

0.0.332

4 years ago

0.0.331

4 years ago

0.0.330

4 years ago

0.0.329

4 years ago

0.0.328

4 years ago

0.0.327

4 years ago

0.0.326

4 years ago

0.0.325

4 years ago

0.0.324

4 years ago

0.0.323

4 years ago

0.0.322

4 years ago

0.0.321

4 years ago

0.0.319

4 years ago

0.0.320

4 years ago

0.0.318

4 years ago

0.0.317

4 years ago

0.0.316

4 years ago

0.0.315

4 years ago

0.0.314

4 years ago

0.0.313

4 years ago

0.0.312

4 years ago

0.0.311

4 years ago

0.0.310

4 years ago

0.0.309

4 years ago

0.0.308

4 years ago

0.0.307

4 years ago

0.0.306

4 years ago

0.0.305

4 years ago

0.0.304

4 years ago

0.0.303

4 years ago

0.0.302

4 years ago

0.0.301

4 years ago

0.0.300

4 years ago

0.0.299

4 years ago

0.0.298

4 years ago

0.0.296

4 years ago

0.0.297

4 years ago

0.0.295

4 years ago

0.0.294

4 years ago

0.0.293

4 years ago

0.0.292

4 years ago

0.0.291

4 years ago

0.0.289

4 years ago

0.0.290

4 years ago

0.0.288

4 years ago

0.0.287

4 years ago

0.0.286

4 years ago

0.0.285

4 years ago

0.0.284

4 years ago

0.0.283

4 years ago

0.0.282

4 years ago

0.0.281

4 years ago

0.0.280

4 years ago

0.0.279

4 years ago

0.0.278

4 years ago

0.0.277

4 years ago

0.0.276

4 years ago

0.0.275

4 years ago

0.0.274

4 years ago

0.0.273

4 years ago

0.0.272

4 years ago

0.0.271

4 years ago

0.0.270

4 years ago

0.0.269

4 years ago

0.0.268

4 years ago

0.0.267

4 years ago

0.0.266

4 years ago

0.0.265

4 years ago

0.0.264

4 years ago

0.0.263

4 years ago

0.0.262

4 years ago

0.0.261

4 years ago

0.0.260

4 years ago

0.0.259

4 years ago

0.0.258

4 years ago

0.0.257

4 years ago

0.0.256

4 years ago

0.0.255

4 years ago

0.0.252

4 years ago

0.0.251

4 years ago

0.0.254

4 years ago

0.0.253

4 years ago

0.0.250

4 years ago

0.0.249

4 years ago

0.0.248

4 years ago

0.0.247

4 years ago

0.0.246

4 years ago

0.0.245

4 years ago

0.0.244

4 years ago

0.0.243

4 years ago

0.0.242

4 years ago

0.0.241

4 years ago

0.0.240

4 years ago

0.0.238

4 years ago

0.0.237

4 years ago

0.0.239

4 years ago

0.0.236

4 years ago

0.0.235

4 years ago

0.0.234

4 years ago

0.0.233

4 years ago

0.0.230

4 years ago

0.0.232

4 years ago

0.0.231

4 years ago

0.0.229

4 years ago

0.0.228

4 years ago

0.0.227

4 years ago

0.0.226

4 years ago

0.0.225

4 years ago

0.0.224

4 years ago

0.0.223

4 years ago

0.0.222

4 years ago

0.0.221

4 years ago

0.0.220

4 years ago

0.0.219

4 years ago

0.0.218

4 years ago

0.0.217

4 years ago

0.0.216

4 years ago

0.0.215

4 years ago

0.0.214

4 years ago

0.0.213

4 years ago

0.0.212

4 years ago

0.0.211

4 years ago

0.0.210

4 years ago

0.0.209

4 years ago

0.0.208

4 years ago

0.0.207

4 years ago

0.0.206

4 years ago

0.0.205

4 years ago

0.0.204

4 years ago

0.0.203

4 years ago

0.0.202

4 years ago

0.0.201

4 years ago

0.0.200

4 years ago

0.0.199

4 years ago

0.0.197

4 years ago

0.0.198

4 years ago

0.0.195

4 years ago

0.0.194

4 years ago

0.0.193

4 years ago

0.0.192

4 years ago

0.0.191

4 years ago

0.0.190

4 years ago

0.0.189

4 years ago

0.0.186

4 years ago

0.0.185

4 years ago

0.0.184

4 years ago

0.0.183

4 years ago

0.0.188

4 years ago

0.0.187

4 years ago

0.0.182

4 years ago

0.0.181

4 years ago

0.0.180

4 years ago

0.0.179

4 years ago

0.0.178

4 years ago

0.0.176

4 years ago

0.0.175

4 years ago

0.0.174

4 years ago

0.0.173

4 years ago

0.0.172

4 years ago

0.0.171

4 years ago

0.0.170

4 years ago

0.0.169

4 years ago

0.0.168

4 years ago

0.0.167

4 years ago

0.0.166

4 years ago

0.0.165

4 years ago

0.0.164

4 years ago

0.0.163

4 years ago

0.0.162

4 years ago

0.0.161

4 years ago

0.0.160

4 years ago

0.0.159

4 years ago

0.0.158

4 years ago

0.0.157

4 years ago

0.0.156

4 years ago

0.0.155

4 years ago

0.0.154

4 years ago

0.0.153

4 years ago

0.0.152

4 years ago

0.0.151

4 years ago

0.0.150

4 years ago

0.0.149

4 years ago

0.0.148

4 years ago

0.0.147

4 years ago

0.0.146

4 years ago

0.0.145

4 years ago

0.0.144

4 years ago

0.0.143

4 years ago

0.0.139

4 years ago

0.0.138

4 years ago

0.0.137

4 years ago

0.0.136

4 years ago

0.0.131

4 years ago

0.0.130

4 years ago

0.0.135

4 years ago

0.0.134

4 years ago

0.0.133

4 years ago

0.0.132

4 years ago

0.0.128

4 years ago

0.0.129

4 years ago

0.0.142

4 years ago

0.0.141

4 years ago

0.0.140

4 years ago

0.0.127

4 years ago

0.0.126

4 years ago

0.0.125

4 years ago

0.0.124

4 years ago

0.0.123

4 years ago

0.0.122

4 years ago

0.0.121

4 years ago

0.0.120

4 years ago

0.0.119

4 years ago

0.0.118

4 years ago

0.0.117

4 years ago

0.0.116

4 years ago

0.0.115

4 years ago

0.0.114

4 years ago

0.0.113

4 years ago

0.0.112

4 years ago

0.0.111

4 years ago

0.0.109

4 years ago

0.0.108

4 years ago

0.0.110

4 years ago

0.0.106

4 years ago

0.0.105

4 years ago

0.0.107

4 years ago

0.0.104

4 years ago

0.0.103

4 years ago

0.0.102

4 years ago

0.0.101

4 years ago

0.0.1

4 years ago