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 ๐Ÿ™

ยฉ 2026 โ€“ย Pkg Stats / Ryan Hefner

@traversable/valibot

v0.0.26

Published

<br> <h1 align="center">แฏ“๐˜๐—ฟ๐—ฎ๐˜ƒ๐—ฒ๐—ฟ๐˜€๐—ฎ๐—ฏ๐—น๐—ฒ/๐˜ƒ๐—ฎ๐—น๐—ถ๐—ฏ๐—ผ๐˜</h1> <br>

Readme

Requirements

@traversable/valibot has a peer dependency on valibot.

What's it all about?

Read the blog post, Introducing: @traversable/valibot (3 min read).

Getting started

$ pnpm add @traversable/valibot valibot

Here's an example of importing the library:

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

// see below for specific examples

Table of contents

Fuzz-tested, production ready

Advanced

Features

vx.check

vx.check converts a Valibot schema into a super-performant type-guard.

Notes

  • Better performance than v.is, v.parse and v.safeParse
  • Works in any environment that supports defining functions using the Function constructor, including (as of May 2025) Cloudflare workers ๐ŸŽ‰

Performance comparison

Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

                โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                โ”‚        Average  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  v.is         โ”‚  40.22x faster  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  v.parse      โ”‚  52.34x faster  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  v.safeParse  โ”‚  54.18x faster  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

v.parse and v.safeParse clone the object they're parsing, and return an array of issues if any are encountered.

Those features are incredibly useful in the right context.

But in contexts where all you need is to know whether a value is valid or not, it'd be nice to have a faster alternative, that doesn't allocate.

vx.check takes a valibot schema, and returns a type guard. It's performance is more than an order of magnitude faster than v.parse and v.safeParse.

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

const Address = v.object({
  street1: v.string(),
  street2: v.exactOptional(v.string()),
  city: v.string(),
})

const addressCheck = vx.check(Address)

addressCheck({ street1: '221B Baker St', city: 'London' }) // => true
addressCheck({ street1: '221B Baker St' })                 // => false

See also

vx.check.writeable

vx.check.writable converts a Valibot schema into a super-performant type-guard.

Compared to vx.check, vx.check.writeable returns the check function in stringified ("writeable") form.

Notes

  • Useful when you're consuming a set of valibot schemas and writing them all to disc
  • Also useful for testing purposes or for troubleshooting, since it gives you a way to "see" exactly what the check functions check

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

const addressCheck = vx.check.writeable(
  v.object({
    street1: v.string(),
    street2: v.exactOptional(v.string()),
    city: v.string(),
  }),
  { typeName: 'Address' }
)

console.log(addressCheck) 
// =>
// type Address = { street1: string; street2?: string; city: string; }
// function check(value: Address) {
//   return (
//     !!value &&
//     typeof value === "object" &&
//     typeof value.street1 === "string" &&
//     (!Object.hasOwn(value, "street2") || typeof value?.street2 === "string") &&
//     typeof value.city === "string"
//   );
// }

See also

vx.deepClone

vx.deepClone lets users derive a specialized "deep copy" function that works with values that have been already validated.

Because the values have already been validated, clone times are significantly faster than alternatives like window.structuredClone and Lodash.cloneDeep.

Performance comparison

Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

                           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                           โ”‚        Average  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Lodash.cloneDeep        โ”‚   9.18x faster  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  window.structuredClone  โ”‚  19.41x faster  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

This article goes into more detail about what makes vx.deepClone so fast.

Example

import { assert } from 'vitest'
import * as v from 'valibot'
import { vx } from '@traversable/valibot'

const Address = v.object({
  street1: v.string(),
  street2: v.exactOptional(v.string()),
  city: v.string(),
})

const clone = vx.deepClone(Address)

const sherlock = { street1: '221 Baker St', street2: '#B', city: 'London' }
const harry = { street1: '4 Privet Dr', city: 'Little Whinging' }

const sherlockCloned = clone(sherlock)
const harryCloned = clone(harry)

// values are deeply equal:
assert.deepEqual(sherlockCloned, sherlock) // โœ…
assert.deepEqual(harryCloned, harry)       // โœ…

// values are fresh copies:
assert.notEqual(sherlockCloned, sherlock)  // โœ…
assert.notEqual(harryCloned, harry)        // โœ…

See also

vx.deepClone.writeable

vx.deepClone lets users derive a specialized "deep clone" function that works with values that have been already validated.

Compared to vx.deepClone, vx.deepClone.writeable returns the clone function in stringified ("writeable") form.

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

const deepClone = vx.deepClone.writeable(
  v.object({
    street1: v.string(),
    street2: v.exactOptional(v.string()),
    city: v.string(),
  }), 
  { typeName: 'Address' }
)

console.log(deepClone) 
// =>
// type Address = { street1: string; street2?: string; city: string; }
// function deepClone(prev: Address) {
//   return {
//     street1: prev.street1,
//     ...prev.street2 !== undefined && { street2: prev.street2 },
//     city: prev.city
//   }
// }

See also

vx.deepEqual

vx.deepEqual lets users derive a specialized "deep equal" function that works with values that have been already validated.

Because the values have already been validated, comparison times are significantly faster than alternatives like NodeJS.isDeepStrictEqual and Lodash.isEqual.

Performance comparison

Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

                             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                             โ”‚   Array (avg)  โ”‚  Object (avg)  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  NodeJS.isDeepStrictEqual  โ”‚  40.3x faster  โ”‚  56.5x faster  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Lodash.isEqual            โ”‚  53.7x faster  โ”‚  60.1x faster  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

This article goes into more detail about what makes vx.deepEqual so fast.

Notes

  • Works in any environment that supports defining functions using the Function constructor, including (as of May 2025) Cloudflare workers ๐ŸŽ‰

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

const deepEqual = vx.deepEqual(
  v.object({
    street1: v.string(),
    street2: v.exactOptional(v.string()),
    city: v.string(),
  })
)

deepEqual(
  { street1: '221B Baker St', city: 'London' },
  { street1: '221B Baker St', city: 'London' }
) // => true

deepEqual(
  { street1: '221B Baker St', city: 'London' },
  { street1: '4 Privet Dr', city: 'Little Whinging' }
) // => false

See also

vx.deepEqual.writeable

Notes

  • Useful when you're consuming a set of valibot schemas and writing them all to disc
  • Also useful for testing purposes or for troubleshooting, since it gives you a way to "see" exactly what the deep equal functions are doing

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

const deepEqual = vx.deepEqual.writeable(
  v.object({
    street1: v.string(),
    street2: v.exactOptional(v.string()),
    city: v.string(),
  }),
  { typeName: 'Address' }
)

console.log(deepEqual)
// =>
// type Address = { street1: string; street2?: string; city: string; }
// function deepEqual(x: Address, y: Address) {
//   if (x === y) return true;
//   if (x.street1 !== y.street1) return false;
//   if (x.street2 !== y.street2) return false;
//   if (x.city !== y.city) return false;
//   return true;
// }

See also

vx.defaultValue

vx.defaultValues converts a Valibot schema into a "default value' that respects the structure of the schema.

A common use case for vx.defaultValue is creating default values for forms.

[!NOTE] By default, vx.defaultValue does not make any assumptions about what "default" means for primitive types, which is why it returns undefined when it encounters a leaf value. This behavior is configurable.

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

const MySchema = v.object({
  a: v.number(),
  b: v.object({
    c: v.string(),
    d: v.array(v.boolean())
  })
})

// by default, primitives are initialized as `undefined`:
const defaultOne = vx.defaultValue(MySchema)
console.log(defaultOne) // => { a: undefined, b: { c: undefined, d: [] } }

// to configure this behavior, use the `fallbacks` property:
const defaultTwo = vx.defaultValue(MySchema, { fallbacks: { number: 0, string: '' } })
console.log(defaultTwo) // => { a: 0, b: { c: '', d: [] } }

vx.fromConstant

Convert a blob of JSON data into a valibot schema that represents the blob's least upper bound.

Example

import type * as v from 'valibot'
import { vx } from '@traversable/valibot'

let example = vx.fromConstant({ abc: 'ABC', def: [1, 2, 3] })
//  ^? let example: v.ObjectSchema<{ readonly abc: 'ABC', readonly def: readonly [1, 2, 3] }>

console.log(vx.toString(example))
// => v.object({ abc: v.literal("ABC"), def: v.tuple([v.literal(1), v.literal(2), v.literal(3)]) })

See also

vx.fromConstant.writeable

Convert a blob of JSON data into a stringified valibot schema that represents the blob's least upper bound.

Example

import { vx } from '@traversable/valibot'

let ex_01 = vx.fromConstant.writeable({ abc: 'ABC', def: [1, 2, 3] })

console.log(ex_01)
// => v.object({ abc: v.literal("ABC"), def: v.tuple([ v.literal(1), v.literal(2), v.literal(3) ]) })

See also

vx.fromJson

Convert a blob of JSON data into a valibot schema that represents the blob's greatest lower bound.

Example

import type * as v from '@traversable/valibot'
import { vx } from '@traversable/valibot'

let ex_01 = vx.fromJson({ abc: 'ABC', def: [] })

console.log(vx.toString(ex_01))
// => v.object({ abc: v.string(), def: v.array(v.unknown()) })

let ex_02 = vx.fromJson({ abc: 'ABC', def: [123] })

console.log(vx.toString(ex_02))
// => v.object({ abc: v.string(), def: v.array(v.number()) })

let ex_03 = vx.fromJson({ abc: 'ABC', def: [123, null]})

console.log(vx.toString(ex_03))
// => v.object({ abc: v.string(), def: v.array(v.union([v.number(), v.null()])) })

See also

vx.fromJson.writeable

Convert a blob of JSON data into a stringified valibot schema that represents the blob's greatest lower bound.

Example

import { vx } from '@traversable/valibot'

let ex_01 = vx.fromJson.writeable({ abc: 'ABC', def: [] })

console.log(ex_01)
// => v.object({ abc: v.string(), def: v.array(v.unknown()) })

let ex_02 = vx.fromJson.writeable({ abc: 'ABC', def: [123] })

console.log(ex_02)
// => v.object({ abc: v.string(), def: v.array(v.number()) })

let ex_03 = vx.fromJson.writeable({ abc: 'ABC', def: [123, null]})

console.log(ex_03)
// => v.object({ abc: v.string(), def: v.array(v.union([v.number(), v.null()])) })

See also

vx.toString

Convert a valibot schema into a string that constructs the same valibot schema.

Useful for writing/debugging tests that involve randomly generated schemas.

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

console.log(
  vx.toString(
    v.map(v.array(v.boolean()), v.set(v.optional(v.number())))
  )
) // => v.map(v.array(v.boolean()), v.set(v.optional(v.number())))

console.log(
  vx.toString(
    v.tupleWithRest([v.number(), v.number()], v.boolean())
  )
) // => v.tupleWithRest([v.number(), v.number()], v.boolean())

vx.toType

Convert a valibot schema into a string that represents its type.

To preserve JSDoc annotations for object properties, pass preserveJsDocs: true in the options object.

[!NOTE] By default, the type will be returned as an "inline" type. To give the type a name, use the typeName option.

Example

import * as v from 'valibot'
import { vx } from '@traversable/valibot'

console.log(
  vx.toType(
    v.object({
      a: v.exactOptional(v.literal(1)),
      b: v.literal(2),
      c: v.exactOptional(v.literal(3))
    })
  )
) // => { a?: 1, b: 2, c?: 3 }

console.log(
  vx.toType(
    v.intersection([
      v.object({ a: v.literal(1) }),
      v.object({ b: v.literal(2) })
    ])
  )
) // => { a: 1 } & { b: 2 }

// To give the generated type a name, use the `typeName` option:
console.log(
  vx.toType(
    v.object({ a: v.exactOptional(v.number()) }),
    { typeName: 'MyType' }
  )
) // => type MyType = { a?: number }

// To preserve JSDoc annotations, use the `preserveJsDocs` option:
console.log(
  vx.toType(
    v.object({
      street1: v.string().describe('Street 1 description'),
      street2: v.string().exactOptional().describe('Street 2 description'),
      city: v.string(),
    }),
    { typeName: 'Address', preserveJsDocs: true }
  )
) 
// => 
// type Address = {
//   /**
//    * Street 1 description
//    */
//   street1: string 
//   /**
//    * Street 2 description
//    */
//   street2?: string
//   city: string
// }

Advanced Features

vx.fold

[!NOTE] vx.fold is an advanced API.

Use vx.fold to define a recursive traversal of a valibot schema. Useful when building a schema rewriter.

vx.fold is a powertool. Most of @traversable/valibot uses vx.fold under the hood.

Compared to the rest of the library, it's fairly "low-level", so unless you're doing something pretty advanced you probably won't need to use it directly.

Example

Let's write a function that takes an arbitrary valibot schema, and generates mock data that satisfies the schema (a.k.a. a "faker").

[!NOTE] You can play with this example on StackBlitz

import * as v from 'valibot'
import { faker } from '@faker-js/faker'
import { fold, tagged } from '@traversable/valibot'

type Fake = () => unknown

const fake = fold<Fake>((x) => {
  //              ๐™˜__๐™˜ this type parameter fills in the "holes" below
  switch (true) {
    case tagged('array')(x): return () => faker.helpers.multiple(
      () => x.item()
      //       ^? method items: Fake
      //                        ๐™˜__๐™˜
    )
    case tagged('never')(x): return () => void 0
    case tagged('unknown')(x): return () => void 0
    case tagged('any')(x): return () => void 0
    case tagged('void')(x): return () => void 0
    case tagged('null')(x): return () => null
    case tagged('undefined')(x): return () => undefined
    case tagged('symbol')(x): return () => Symbol()
    case tagged('boolean')(x): return () => faker.datatype.boolean()
    case tagged('NaN')(x): return () => NaN
    case tagged('bigint')(x): return () => faker.number.bigInt()
    case tagged('number')(x): return () => faker.number.float()
    case tagged('string')(x): return () => faker.string.alpha()
    case tagged('date')(x): return () => faker.date.recent()
    case tagged('literal')(x): return () => x.literal
    case tagged('enum')(x): return () => faker.helpers.arrayElement(Object.values(x.enum))
    case tagged('lazy')(x): return x.getter()
    case tagged('nonOptional')(x): return () => x.wrapped()
    case tagged('nonNullable')(x): return () => x.wrapped()
    case tagged('nonNullish')(x): return () => x.wrapped()
    case tagged('nullable')(x): return () => faker.helpers.arrayElement([x.wrapped(), null])
    case tagged('optional')(x): return () => faker.helpers.arrayElement([x.wrapped(), undefined])
    case tagged('exactOptional')(x): return () => faker.helpers.arrayElement([x.wrapped(), undefined])
    case tagged('undefinedable')(x): return () => faker.helpers.arrayElement([x.wrapped(), undefined])
    case tagged('nullish')(x): return () => faker.helpers.arrayElement([x.wrapped(), null, undefined])
    case tagged('set')(x): return () => new Set([x.value()])
    case tagged('map')(x): return () => new Map([[x.key(), x.value()]])
    case tagged('record')(x): return () => Object.fromEntries([[x.key(), x.value() ]])
    case tagged('blob')(x): return () => new Blob(faker.lorem.lines().split('\n'))
    case tagged('file')(x): return () => new File(faker.lorem.lines().split('\n'), faker.system.commonFileName())
    case tagged('intersect')(x): return () => Object.assign({}, ...x.options.map((option) => option()))
    case tagged('union')(x): return () => faker.helpers.arrayElement(x.options.map((option) => option()))
    case tagged('variant')(x): return () => faker.helpers.arrayElement(x.options)
    case tagged('looseTuple')(x):
    case tagged('strictTuple')(x):
    case tagged('tupleWithRest')(x):
    case tagged('tuple')(x): return () => x.items.map((item) => item())
    case tagged('looseObject')(x):
    case tagged('strictObject')(x):
    case tagged('objectWithRest')(x):
    case tagged('object')(x): return () => Object.fromEntries(Object.entries(x.entries).map(([k, v]) => [k, v()]))
    case tagged('custom')(x):
    case tagged('promise')(x):
    case tagged('function')(x):
    case tagged('instance')(x):
    case tagged('picklist')(x): { throw Error('Unsupported schema: ' + x.type) }
    default: { x satisfies never; throw Error('Illegal state') }
    //         ๐™˜_______________๐™˜
    //        exhaustiveness check works
  }
})

// Let's test it out:
const mock = fake(
  v.object({
    abc: v.array(v.string()), 
    def: v.optional(
      v.tuple([
        v.number(), 
        v.boolean()
      ])
    )
  })
)

console.log(mock())
// => {
//  abc: [
//     'annus iure consequatur',
//     'aer suus autem',
//     'delectus patrocinor deporto',
//     'benevolentia tonsor odit',
//     'stabilis dolor tres',
//     'mollitia quibusdam vociferor'
//   ],
//   def: [-882, false]
// }

vx.Functor

[!NOTE] vx.Functor is an advanced API

vx.Functor is the primary abstraction that powers @traversable/valibot.

vx.Functor is a powertool. Most of @traversable/valibot uses vx.Functor under the hood.

Compared to the rest of the library, it's fairly "low-level", so unless you're doing something pretty advanced you probably won't need to use it directly.