# Home

Collection of useful libraries for TypeScript + React

## About

This project features a growing collection of useful services, hooks, and components for the TypeScript and React ecosystems. We pay a lot of attention to DX, code quality, test coverage and full static typing.

{% content-ref url="/pages/-MeeG1hynKDbiiOh4iQk" %}
[\<Router/>](/components/router)
{% endcontent-ref %}

{% content-ref url="/pages/-MefJhqSgQM2IZsZpDnB" %}
[Schema](/services/schema)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeF80BrEndyj5EzdPB" %}
[Form](/services/form)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeFDZTgmmurCkKtNA3" %}
[Accessor](/services/accessor)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeGw2\_qPDO\_o6SM2YY" %}
[Fiber](/services/fiber)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeFAgcZV96KXm0W1WU" %}
[Translator](/services/translator)
{% endcontent-ref %}

{% content-ref url="/pages/-Mg7Kt8XOfsD4Np5cHGW" %}
[Async](/observables/async)
{% endcontent-ref %}

{% content-ref url="/pages/-MfJ4qThZ0llSH30K7PI" %}
[useAsync](/hooks/use-async)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeFDXYVzHIrXuHDnFE" %}
[useStream](/hooks/use-stream)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeFDVtWtL6cM6Te5\_Y" %}
[useAction](/hooks/use-action)
{% endcontent-ref %}


# Fiber

Build a statically typed SDK for your backend in minutes.

Source code is hosted on [GitHub](https://github.com/corets/fiber)

Create a statically typed client for remote, RPC style communication. Consume your backend API through a statically typed facade, without any code generation, without writing any custom API calls or duplicating your backend types on the client side. Fiber works in any TypeScript setup and is fully implementation agnostic.

* Full **static typing** from tail to toe
* **You control** the implementation
* Works for **client to server** and **server to server** communication
* Build an **SDK for your backend** in minutes
* No code generation, **plain TypeScript**

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/fiber
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/fiber
```

{% endtab %}
{% endtabs %}

## Quick start <a href="#quick-start" id="quick-start"></a>

First you create a fiber instance and register all the methods that you want to expose to consumers:

```typescript
import { createFiber } from "@corets/fiber"

const makeResponse = (status: number, result: any) => {  
    return { status, result }
}

const pingPong = (input: string) => {  
    return makeResponse(200, input === "ping" ? "pong" : "ping")
}

export const fiber = createFiber({ pingPong })

export type MyFiber = typeof fiber
```

Next you need to setup your server, we'll be using express in this example:

```typescript
import express from "express"
import { fiber } from "./fiber"

const app = express()

app.post("/fiber/:method", async (req, res) => {  
    try {    
        const method = req.params?.method    
        const args = Object.values(req?.body)    
        const response = await fiber.call(method, ...args)    
        
        res.status(response.status).json(response.result)  
    } catch (err) {    
        console.error(err)    
        res.status(500).send()  
    }
})

app.listen(1337)
```

Next you create a consumer for your fiber, on the client side, we'll use axios in this example:

```typescript
import { createFiberClient } from "@corets/fiber"
import axios from "axios"
import type { MyFiber } from "./fiber"

export const client = createFiberClient<MyFiber>(async (method, ...args) => {  
    const res = await axios.post(`http://localhost:1337/fiber/${method}`, args)  

    return res.data
})
```

Now you have a statically typed, RPC style client, that you can consume immediately:

```typescript
import { client } from "./client"

const answer = await client.pingPong("ping")
```

## createFiber() <a href="#createfiber" id="createfiber"></a>

Create a new fiber instance on the producer side, returns an instance of `SimpleFiber`:

```typescript
import { createFiber } from "@corets/fiber"

const myMethod = () => "hallo"

const fiber = createFiber({ myMethod })
```

## createFiberClient()

Create a statically typed fiber client, returns an instance of `SimpleFiberClient`:

```typescript
import { createFiberClient } from "@corets/fiber"
import type { fiber } from "./fiber"

const client = createFiberClient<typeof fiber>((method, ...args) => { ... })
```

## SimpleFiber.call() <a href="#simplefibercall" id="simplefibercall"></a>

Call a registered fiber method on the producer side:

```typescript
import { createFiber } from "@corets/fiber"

const fiber = createFiber({  
    greet (name: string) {    
        return `Hallo ${name}!`  
    }
})

const greeting = await fiber.call("greet", "John")
```

## SimpleFiberClient.call() <a href="#simplefiberclientcall" id="simplefiberclientcall"></a>

```typescript
import { createFiberClient } from "@corets/fiber"
import type { MyFiber } from "./fiber"

const client = createFiberClient<MyFiber>((method, ...args) => { ... })

const response = await client.call("greeting", "John")
// same as
const response = await client.greeting("John")
```


# Accessor

Turn any object into a statically typed facade.

Source code is hosted on [GitHub](https://github.com/corets/accessor)

This library allows you to create a statically typed object accessor based on the [Proxy API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). The main purpose of this package is to allow dynamic object access in a static manner, without having to rely on dynamic, string based keys. Code won't compile if you try to access a path that does not exist. You keep control over what is returned from the getter, by providing a custom access handler.

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/accessor
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/accessor
```

{% endtab %}
{% endtabs %}

## createAccessor() <a href="#createaccessor" id="createaccessor"></a>

Create an accessor instance of the type `ObjectAccessor`.

```typescript
import { createAccessor } from "@corets/accessor"

const accessor = createAccessor({ some: { field: "value" } })
```

Now you can statically access every object field:

```typescript
accessor.some.field.get()
```

You can provide a custom access handler to customise the access behaviour. Below is an example of how one could statically access translations, proxied by a translation library, using the accessor.

```typescript
import { createAccessor } from "@corets/accessor"

const translations = {  
    pages: {    
        home: {      
            title: "Home {replacement}"    
        },    
        about: {      
            title: "About {replacement}"    
        }  
    }
}

const translator = createSomeSortOfTranslator(translations)

const locales = createAccessor(translations, (source, key, ...replacements: any[]): string => {  
    return translator.get(key, { replacements })
})
```

Now you can indirectly access translations through the accessor, without using any dynamic keys.

```typescript
const translatedHomePageTitle = locales.pages.home.title.get("Page")
```

{% hint style="info" %}
Trying to access a missing key will result in a compilation error. 💥
{% endhint %}

This library is used internally by this translation library, to power its static translations access:

{% content-ref url="/pages/-MeeFAgcZV96KXm0W1WU" %}
[Translator](/services/translator)
{% endcontent-ref %}

## ObjectAccessor.key() <a href="#objectaccessorkey" id="objectaccessorkey"></a>

Get absolute field key:

```typescript
import { createAccessor } from "@corets/accessor"

const accessor = createAccessor({ some: { field: "value" } })

accessor.some.field.key()
```

## ObjectAccessor.keyAt() <a href="#objectaccessorkeyat" id="objectaccessorkeyat"></a>

Get absolute field key, bypassing the static typing:

```typescript
import { createAccessor } from "@corets/accessor"

const accessor = createAccessor({ some: { field: "value" } })

accessor.some.keyAt("field")
```

## ObjectAccessor.get() <a href="#objectaccessorget" id="objectaccessorget"></a>

Get field value coming from the access handler:

```typescript
import { createAccessor } from "@corets/accessor"

const accessor = createAccessor({ some: { field: "value" } })

accessor.some.field.get()
```

## ObjectAccessor.getAt() <a href="#objectaccessorgetat" id="objectaccessorgetat"></a>

Get field value coming from the access handler, bypassing the static typing:

```typescript
import { createAccessor } from "@corets/accessor"

const accessor = createAccessor({ some: { field: "value" } })

accessor.some.getAt("field")
```

## ObjectAccessor.has() <a href="#objectaccessorhas" id="objectaccessorhas"></a>

Check if a field exists:

```typescript
import { createAccessor } from "@corets/accessor"

const accessor = createAccessor({ some: { field: "value" } })

accessor.some.field.has()
```

## ObjectAccessor.hasAt() <a href="#objectaccessorhasat" id="objectaccessorhasat"></a>

Check if a field exists, bypassing the static typing:

```typescript
import { createAccessor } from "@corets/accessor"

const accessor = createAccessor({ some: { field: "value" } })

accessor.some.hasAt("field")
```


# Schema

Delightfully simple, feature packed and customisable tool for validation and sanitization of any kind of data. Can be used on the client side as well as on the server side.

Source code is hosted on [GitHub](https://github.com/corets/schema)

* **Works with React**, React Native, Angular, Vue or **NodeJs**
* Plentitude of **validation methods** for different data types
* Many useful **sanitization methods** for data normalization
* Typed from tail to toe for **amazing developer experience**
* Very **intuitive** and **easy to use**
* Extremely **flexible** and extendable
* Supports **sync and async** logic
* Encourages you to **build reusable schemas**
* **Combine multiple schemas** into more complex ones
* **Conditionally connect** **schemas** for complex scenarios
* Write **custom validation and sanitization** logic with ease
* Easily **override error messages**
* Comes **translated into 6 different languages** (English, German, French, Italian, Spanish and Russian)
* Easily **add a new language** or **override translation**

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/schema
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/schema
```

{% endtab %}
{% endtabs %}

## Concepts

There are two ways to run validations. For simple things like one-liners where you simply want to know if a value matches certain criteria, and get a `true` or `false` as result, you can use the [`Schema.test()`](/services/schema#schematest) method. For proper validation, with error messages instead of a simple `true` / `false`, you can use the [`Schema.validate()`](/services/schema#schemavalidate) method.

Every schema specialises on a specific data type and comes with various assertion and sanitization methods. Some methods you'll be able to find on every schema, others are exclusive to a specific kind of schema.

Assertions are used for validation purposes to ensure that a value is valid.

Sanitization methods are called before validation and are used to normalise the underlying values. For example, you can ensure that a string is capitalised and all object keys are camel-cased. Sanitization can help you reduce the number of validation errors by proactively fixing them in the first place. All sanitization methods start with the prefix `to`, for example: [`string.toTrimmed()`](/services/schema#stringtotrimmed), [`string.toCamelCase()`](/services/schema#stringtocamelcase).

Check out this React form library, it has a support for schemas and changes the way you work with forms:

{% content-ref url="/pages/-MeeF80BrEndyj5EzdPB" %}
[Form](/services/form)
{% endcontent-ref %}

## Quick start <a href="#quick-start" id="quick-start"></a>

Here is an example of all the available schemas and how to import them:

```typescript
import { 
    string, 
    number, 
    array, 
    boolean, 
    date, 
    object, 
    mixed 
} from "@corets/schema"
```

Let's describe a simple user object.

* Property **email** must be of type string and a valid email address
* Property **fullName** must be a string between 3 and 100 characters
* Property **roles** must be an array containing at least one role, valid roles are *admin*, *publisher* and *developer*, no duplicates are allowed
* Property **tags** must be an array of strings, each at least 3 characters long and consisting of letters and dashes

This schema contains some validation as well as some sanitization logic.

```typescript
import { array, object, string } from "@corets/schema"

const roles = ["admin", "publisher", "developer"]

const userSchema = object({  
    email: string().email(),  
    fullName: string().min(3).max(100),  
    roles: array().min(1).someOf(roles).toUnique(),  
    tags: array(string().min(3).alphaDashes())
})
```

Quick check if an object is valid according to the schema:

```typescript
const valid = userSchema.test({ ... })

if (valid) {
    // continue ...
}
```

Get a detailed list of errors:

```typescript
const errors = userSchema.validate({ ... })

if ( ! errors) {
    // continue ...
}
```

The errors object would look something like this:

```typescript
{  
    "field": ["first error", "second error"],  
    "nested.field": ["first error", "second error"],
}
```

Run sanitizers without any validation:

```typescript
const sanitizedValue = userSchema.sanitize({ ... })
```

Test, validate and sanitize at the same time:

```typescript
const [valid, sanitizedValue] = userSchema.sanitizeAndTest({ ... })
const [errors, sanitizedValue] = userSchema.sanitizeAndValidate({ ... })
```

## Combining schemas <a href="#combining-schemas" id="combining-schemas"></a>

Schemas can be freely combined with one another. When describing arrays and objects there is no way around this. Here is an example of two schemas being combined:

```typescript
import { array, string } from "@corets/schema"

const usernameSchema = string().alphaNumeric().between(3, 10)
const usernameListSchema = array(usernameSchema).min(3)
const errors = usernameListSchema.validate(["foo", "bar", "baz"])
```

{% hint style="info" %}
Consider writing schemas that are reusable.
{% endhint %}

## Conditional validations <a href="#conditional-validations" id="conditional-validations"></a>

Schemas can be logically linked together using methods [`Schema.and()`](/services/schema#schemaand), [`Schema.or()`](/services/schema#schemaor) and [`Schema.also()`](/services/schema#schemaalso). A [`Schema.and()`](/services/schema#schemaand) relation will only be executed if the parent schema, that it is linked to, could validate successfully. A [`Schema.or()`](/services/schema#schemaor) relation will only execute if the parent schema failed, the alternative schema will be attempted instead. A [`Schema.also()`](/services/schema#schemaalso) relation will execute regardless of the validation status of the parent schema.

Example of a [`Schema.and()`](/services/schema#schemaand) relation, this schema will first ensure that the value is a string, and only if the first validation passes, it will test if the value is a numeric string:

```typescript
import { string } from "@corets/schema"

string().and(string().numeric())
```

Example of a [`Schema.or()`](/services/schema#schemaor) relation, this schema will first check if the value is a number, and only if it's not, it will test if that value is a numeric string:

```typescript
import { number, string } from "@corets/schema"

number().or(string().numeric())
```

Example of an [`Schema.also()`](/services/schema#schemaalso) relation, this schema will execute both parts regardless of the validation outcome of the parent schema:

```typescript
import { string } from "@corets/schema"

string().also(string().numeric())
```

Conditional schemas can be wrapped into a function, this allows you to define schemas dynamically during the validation:

```typescript
import { string } from "@corets/schema"

string().and(() => string().numeric())
string().or(() => string().numeric())
string().also(() => string().numeric())
```

{% hint style="info" %}
You can freely define validations and sanitizers at runtime using logical links.
{% endhint %}

Logical links can be used to attach custom validation logic:

```typescript
import { string } from "@corets/schema"

string().min(3).max(20)
    .and(async (v) => await isUsernameTaken(v) && "Username is already taken")
```

## Custom validations <a href="#custom-validations" id="custom-validations"></a>

Methods [`Schema.and()`](/services/schema#schemaand), [`Schema.or()`](/services/schema#schemaor) and [`Schema.also()`](/services/schema#schemaalso) do not always have to return another schema. They can be used for your custom validation functions. A custom validation function can return either another schema, a validation result, or an error message.

Example of a validation function returning an error message:

```typescript
import { number } from "@corets/schema"

const customValidation = (value: any) => value < 12 && "Must be bigger than 12"

number().and(customValidation)
number().or(customValidation)
number().also(customValidation)
```

{% hint style="info" %}
You can also return multiple error messages at once using an array.
{% endhint %}

Example of a validation function returning a schema:

```typescript
import { string } from "@corets/schema"

const customValidation = (value: any) => value.includes("@") && string().email()

string().and(customValidation)
string().or(customValidation)
string().also(customValidation)
```

Example of a validation function returning schema validation errors:

```typescript
import { string } from "@corets/schema"

const customValidation = (value: any) => 
    value.includes("@") && string().email().verify(value)

string().and(customValidation)
string().or(customValidation)
string().also(customValidation)
```

## Custom sanitizers <a href="#custom-sanitizers" id="custom-sanitizers"></a>

You can add a custom sanitizer with the method [`Schema.map()`](/services/schema#schemamap), here is an example of a sanitizer that converts everything to a string:

```typescript
import { string } from "@corets/schema"

const customSanitizer = (value: any) => value.toString()
const sanitizedValue = string().map(customSanitizer).sanitize(1234)
```

## Translations <a href="#translations" id="translations"></a>

All the translation logic is handled by this library:

{% content-ref url="/pages/-MeeFAgcZV96KXm0W1WU" %}
[Translator](/services/translator)
{% endcontent-ref %}

You can change default language:

```typescript
import { schemaTranslator } from "@corets/schema"

schemaTranslator.setLanguage("en")
```

You can also request a specific language during validation:

```typescript
import { string } from "@corets/schema"

string().validate("foo", { language: "ru", fallbackLanguage: "en" })
string().verify("foo", { language: "ru", fallbackLanguage: "en" })
string().sanitizeAndValidate("foo", { language: "ru", fallbackLanguage: "en" })
string().sanitizeAndVerify("foo", { language: "ru", fallbackLanguage: "en" })
```

Get a full list of all translations:

```typescript
import { schemaTranslator } from "@corets/schema"

console.log(schemaTranslator.getTranslations())
```

{% hint style="info" %}
You can find all locales [here](https://github.com/corets/schema/tree/master/src/locales). For further examples of how to replace translations, add new languages, etc., please have a look at the [@corets/translator](/services/translator) docs.
{% endhint %}

## Schema.test() <a href="#schematest" id="schematest"></a>

This method simply indicates whether a value is valid or not by returning a boolean.

Example of a failed assertion:

```typescript
import { string } from "@corets/schema"

const schema = string().min(3).alphaNumeric()
// false
const valid = schema.test("foo-bar")

if (valid) {
    // continue ...
}
```

## Schema.validate() <a href="#schemavalidate" id="schemavalidate"></a>

This method returns a validation result containing detailed error messages about why a value did not pass validation.

Example of a failed validation:

```typescript
import { string } from "@corets/schema"

const schema = string().min(3).alphaNumeric()
const errors = schema.validate("foo-bar")

if ( ! errors) {
    // continue ...
}
```

This is what the validation result might look like:

```typescript
{  
    "field": ["first error", "second error"],  
    "nested.field": ["first error", "second error"],
}
```

{% hint style="info" %}
When validating anything but an object, you'll find all the error messages under the key **self**.
{% endhint %}

## Schema.verify() <a href="#schemaverify" id="schemaverify"></a>

This method works the same as [`Schema.validate()`](/services/schema#schemavalidate), except it returns errors in a slightly different format. This format contains additional information about each error message and can be used for further processing of validation errors.

Example of a failed validation:

```typescript
import { string } from "@corets/schema"

const errors = string().min(10).verify("foo")
```

Here is an example of how the error messages would look like:

```typescript
[  
    {    
        "type": "string_min",    
        "message": "Must be at least \"2\" characters long",    
        "args": [10],    
        "value": "foo",    
        "link": undefined,    
        "path": undefined  
    }
]
```

## Schema.sanitize()

This method applies sanitizers and returns the sanitized value:

```typescript
import { string } from "@corets/schema"

const schema = string().toTrimmed().toCamelCase()
const value = schema.sanitize("  foo bar  ")
```

{% hint style="info" %}
All sanitization methods start with the prefix **to**, for example: [string.**toTrimmed()**](/services/schema#stringtotrimmed).
{% endhint %}

## Schema.sanitizeAndTest() <a href="#schemasanitizeandtest" id="schemasanitizeandtest"></a>

This method applies sanitizers on the underlying value and runs validation against the sanitized version. It returns a boolean indicating whether the value is valid, and the sanitized version of the value. Basically this calls the methods [`Schema.sanitize()`](/services/schema#schema-sanitize) and [`Schema.test()`](/services/schema#schematest) sequentially.

Example of a successful test with sanitizers:

```typescript
import { string } from "@corets/schema"

const schema = string().min(4).toCamelCase()
const [valid, value] = schema.sanitizeAndTest("foo bar")
```

Example of a failed test with sanitizers:

```typescript
import { string } from "@corets/schema"

const schema = string().min(4).toTrimmed()
const [valid, value] = schema.sanitizeAndTest("  foo  ")
```

As you can see, even though the string `" foo "` has a length greater than `4`. During the sanitization, value gets trimmed, becomes`"foo"` and therefore the test will fail.

{% hint style="info" %}
All sanitization methods start with the prefix **to**, for example: [string.**toTrimmed()**](/services/schema#stringtotrimmed).
{% endhint %}

## Schema.sanitizeAndValidate() <a href="#schemasanitizeandvalidate" id="schemasanitizeandvalidate"></a>

This method applies sanitizers on the underlying value and runs validation against the sanitized version. It returns a validation result containing detailed error messages about why a value did not pass validation, and the sanitized version of the value. Basically this calls the methods [`Schema.sanitize()`](/services/schema#schema-sanitize) and [`Schema.validate()`](/services/schema#schemavalidate) sequentially.

Example of a successful validation with sanitizers:

```typescript
import { string } from "@corets/schema"

const schema = string().min(4).toCamelCase()
const [errors, value] = schema.sanitizeAndValidate("foo bar")
```

Example of a failed validation with sanitizers:

```typescript
import { string } from "@corets/schema"

const schema = string().min(4).toTrimmed()
const [errors, value] = schema.sanitizeAndValidate("  foo  ")
```

This is what the errors would look like:

```typescript
{  
    "self": ["Must be at least \"4\" characters long"],
}
```

## Schema.sanitizeAndVerify() <a href="#schemasanitizeandverify" id="schemasanitizeandverify"></a>

This method works exactly the same as [`Schema.anitizeAndValidate()`](/services/schema#schemasanitizeandvalidate) except that it returns error messages in a different format with additional information that can be used for further processing. Take a look at [`Schema.verify()`](/services/schema#schemaverify) to learn more about its use cases.

{% hint style="info" %}
All sanitization methods start with the prefix **to**, for example: [string.**toTrimmed()**](/services/schema#stringtotrimmed).
{% endhint %}

## Schema.testAsync() <a href="#schematestasync" id="schematestasync"></a>

This is an async version of the [`Schema.test()`](/services/schema#schematest) method, that allows you to use async validators and sanitizers.

{% hint style="info" %}
This library has no async validation or sanitization methods itself, but you can add your own.
{% endhint %}

## Schema.validateAsync() <a href="#schemavalidateasync" id="schemavalidateasync"></a>

This is an async version of the [`Schema.validate()`](/services/schema#schemavalidate) method, that allows you to use async validators.

{% hint style="info" %}
This library has no async validation or sanitization methods itself, but you can add your own.
{% endhint %}

## Schema.verifyAsync() <a href="#schemaverifyasync" id="schemaverifyasync"></a>

This is an async version of the [`Schema.verify()`](/services/schema#schemaverify) method, that allows you to use async validators.

{% hint style="info" %}
This library has no async validation or sanitization methods itself, but you can add your own.
{% endhint %}

## Schema.sanitizeAsync() <a href="#schemasanitizeasync" id="schemasanitizeasync"></a>

This is an async version of the [`Schema.sanitize()`](/services/schema#schema-sanitize) method, that allows you to use async sanitizers.

{% hint style="info" %}
This library has no async validation or sanitization methods itself, but you can add your own.
{% endhint %}

## Schema.sanitizeAndTestAsync() <a href="#schemasanitizeandtestasync" id="schemasanitizeandtestasync"></a>

This is an async version of the [`Schema.sanitizeAndTest()`](/services/schema#schemasanitizeandtest) method, that allows you to use async validators and sanitizers.

{% hint style="info" %}
This library has no async validation or sanitization methods itself, but you can add your own.
{% endhint %}

## Schema.sanitizeAndValidateAsync() <a href="#schemasanitizeandvalidateasync" id="schemasanitizeandvalidateasync"></a>

This is an async version of the [`Schema.sanitizeAndValidate()`](/services/schema#schemasanitizeandvalidate) method, that allows you to use async validators and sanitizers.

{% hint style="info" %}
This library has no async validation or sanitization methods itself, but you can add your own.
{% endhint %}

## Schema.sanitizeAndVerifyAsync() <a href="#schemasanitizeandverifyasync" id="schemasanitizeandverifyasync"></a>

This is an async version of the [`Schema.sanitizeAndVerify()`](/services/schema#schemasanitizeandverify) method, that allows you to use async validators and sanitizers.

{% hint style="info" %}
This library has no async validation or sanitization methods itself, but you can add your own.
{% endhint %}

## Schema.also() <a href="#schemaalso" id="schemaalso"></a>

Connects multiple schemas or attaches a custom validation function to a schema. Connections made with this link are invoked regardless of the status of the parent schema. This is perfect when you try to combine multiple schemas or attach a custom validation function to a schema to make it behave like one single unit.

```typescript
import { string } from "@corets/schema"

const usernameSchema = string().alphaNumeric()
const advancedUsernameSchema = string().min(3).max(20).also(usernameSchema)
const uniqueUsernameSchema = advancedUsernameSchema.also(
    async (v) => await isUsernameTaken(v) && "Username already taken"
)
```

{% hint style="info" %}
See also [custom validations](/services/schema#custom-validations) for more examples.
{% endhint %}

## Schema.and() <a href="#schemaand" id="schemaand"></a>

Logically connects multiple schemas, or a custom validation function to a schema. Schemas and validation functions connected using this link will only be executed if the parent schema validated successfully. This is a good way to connect expensive validation logic, like a database call, and make sure that it is only invoked when it's really necessary.

```typescript
import { string } from "@corets/schema"

const usernameSchema = string().alphaNumeric()
const advancedUsernameSchema = string().min(3).max(20).and(usernameSchema)
const uniqueUsernameSchema = advancedUsernameSchema.and(
    async (v) => await isUsernameTaken(v) && "Username already taken"
)
```

{% hint style="info" %}
See also [custom validations](/services/schema#conditional-validations) and [conditional validations](/services/schema#conditional-validations) for more examples.
{% endhint %}

## Schema.or() <a href="#schemaor" id="schemaor"></a>

Logically connects multiple schemas, or a custom validation function to a schema. Schemas and validation functions connected using this link will only be executed if the parent schema validation has failed. This is useful if you want to provide an alternative validation procedure.

```typescript
import { string } from "@corets/schema"

const usernameOrEmail = string()
    .alphaNumeric().min(3).max(20)
    .or(v => v.includes("@") && string().email())
```

{% hint style="info" %}
See also [custom validations](/services/schema#conditional-validations) and [conditional validations](/services/schema#conditional-validations) for more examples.
{% endhint %}

## Schema.map() <a href="#schemamap" id="schemamap"></a>

Attach a custom sanitizer function to a schema.

```typescript
import { mixed } from "@corets/schema"

mixed().map(v => v.toString())
```

{% hint style="info" %}
See also [custom sanitizers](/services/schema#custom-sanitizers) for more examples.
{% endhint %}

## schema() <a href="#schema-1" id="schema-1"></a>

This factory is used as a central entry point to create any kind of schema by providing the default value first.

```typescript
import { schema } from "@corets/schema"

const stringSchema = schema("foo").string()
```

The example above is equivalent to this:

```typescript
import { string } from "@corets/schema"

const schema = string().toDefault("foo")
```

{% hint style="info" %}
Defining a default value is optional, but might be useful when validating forms.
{% endhint %}

## schema.string() <a href="#schemastring" id="schemastring"></a>

Create a new [`string()`](/services/schema#string) schema starting with the default value:

```typescript
import { schema } from "@corets/schema"

schema("foo").string()
```

## schema.number() <a href="#schemanumber" id="schemanumber"></a>

Create a new [`number()`](/services/schema#number) schema starting with the default value:

```typescript
import { schema } from "@corets/schema"

schema(123).number()
```

## schema.boolean() <a href="#schemaboolean" id="schemaboolean"></a>

Create a new [`boolean()`](/services/schema#boolean) schema starting with the default value:

```typescript
import { schema } from "@corets/schema"

schema(true).boolean()
```

## schema.date() <a href="#schemadate" id="schemadate"></a>

Create a new [`date()`](/services/schema#date) schema starting with the default value:

```typescript
import { schema } from "@corets/schema"

schema(new Date()).date()
```

## schema.array() <a href="#schemaarray" id="schemaarray"></a>

Create a new [`array()`](/services/schema#array) schema starting with the default value:

```typescript
import { schema } from "@corets/schema"

schema(["foo"]).array()
```

## schema.object() <a href="#schemaobject" id="schemaobject"></a>

Create a new [`object()`](/services/schema#object) schema starting with the default value:

```typescript
import { schema } from "@corets/schema"

schema({ foo: "bar" }).object()
```

## schema.mixed() <a href="#schemamixed" id="schemamixed"></a>

Create a new [`mixed()`](/services/schema#mixed) schema starting with the default value:

```typescript
import { schema } from "@corets/schema"

schema("foo").mixed()
```

## string() <a href="#string" id="string"></a>

Contains various validators and sanitisers for strings:

```typescript
import { string } from "@corets/schema"

string()
```

Create a schema instance without the factory function:

```typescript
import { StringSchema } from "@corets/schema"

new StringSchema()
```

## string.required() <a href="#stringrequired" id="stringrequired"></a>

Value must be a non-empty string:

```typescript
import { string } from "@corets/schema"

string().required()
string().required(true, "Message")
string().required(() => true, () => "Message")
```

## string.optional() <a href="#stringoptional" id="stringoptional"></a>

Value might be a string, opposite of [`string.required()`](/services/schema#stringrequired):

```typescript
import { string } from "@corets/schema"

string().optional()
string().optional("Message")
string().optional(() => "Message")
```

## string.equals() <a href="#stringequals" id="stringequals"></a>

String must be equal to the given value:

```typescript
import { string } from "@corets/schema"

string().equals("foo")
string().equals("foo", "Message")
string().equals(() => "foo", () => "Message")
```

## string.length() <a href="#stringlength" id="stringlength"></a>

String must have an exact length:

```typescript
import { string } from "@corets/schema"

string().length(3)
string().length(3, "Message")
string().length(() => 3, () => "Message")
```

## string.min() <a href="#stringmin" id="stringmin"></a>

String must not be shorter than given value:

```typescript
import { string } from "@corets/schema"

string().min(3)
string().min(3, "Message")
string().min(() => 3, () => "Message")
```

## string.max() <a href="#stringmax" id="stringmax"></a>

String must not be longer than given value:

```typescript
import { string } from "@corets/schema"

string().max(3)
string().max(3, "Message")
string().max(() => 3, () => "Message")
```

## string.between() <a href="#stringbetween" id="stringbetween"></a>

String must have a length between min and max:

```typescript
import { string } from "@corets/schema"

string().between(3, 6)
string().between(3, 6, "Message")
string().between(() => 3, () => 6, () => "Message")
```

## string.matches() <a href="#stringmatches" id="stringmatches"></a>

String must match given RegExp:

```typescript
import { string } from "@corets/schema"

string().matches(/^red/)
string().matches(/^red/, "Message")
string().matches(() => /^red/, () => "Message")
```

## string.email() <a href="#stringemail" id="stringemail"></a>

String must be a valid email address:

```typescript
import { string } from "@corets/schema"

string().email()
string().email("Message")
string().email(() => "Message")
```

## string.url() <a href="#stringurl" id="stringurl"></a>

String must be a valid URL:

```typescript
import { string } from "@corets/schema"

string().url()
string().url("Message")
string().url(() => "Message")
```

## string.startsWith() <a href="#stringstartswith" id="stringstartswith"></a>

String must start with a given value:

```typescript
import { string } from "@corets/schema"

string().startsWith("foo")
string().startsWith("foo", "Message")
string().startsWith(() => "foo", () => "Message")
```

## string.endsWith() <a href="#stringendswith" id="stringendswith"></a>

String must end with a given value:

```typescript
import { string } from "@corets/schema"

string().endsWith("foo")
string().endsWith("foo", "Message")
string().endsWith(() => "foo", () => "Message")
```

## string.includes() <a href="#stringincludes" id="stringincludes"></a>

String must include a given substring:

```typescript
import { string } from "@corets/schema"

string().includes("foo")
string().includes("foo", "Message")
string().includes(() => "foo", () => "Message")
```

## string.omits() <a href="#stringomits" id="stringomits"></a>

String must not include a given substring:

```typescript
import { string } from "@corets/schema"

string().omits("foo")
string().omits("foo", "Message")
string().omits(() => "foo", () => "Message")
```

## string.oneOf() <a href="#stringoneof" id="stringoneof"></a>

String must be one of the whitelisted values:

```typescript
import { string } from "@corets/schema"

string().oneOf(["foo", "bar"])
string().oneOf(["foo", "bar"], "Message")
string().oneOf(() => ["foo", "bar"], () => "Message")
```

## string.noneOf() <a href="#stringnoneof" id="stringnoneof"></a>

String must not be one of the blacklisted values:

```typescript
import { string } from "@corets/schema"

string().noneOf(["foo", "bar"])
string().noneOf(["foo", "bar"], "Message")
string().noneOf(() => ["foo", "bar"], () => "Message")
```

## string.numeric() <a href="#stringnumeric" id="stringnumeric"></a>

String must contain numbers only, including floats:

```typescript
import { string } from "@corets/schema"

string().numeric()
string().numeric("Message")
string().numeric(() => "Message")
```

## string.alpha() <a href="#stringalpha" id="stringalpha"></a>

String must contain letters only:

```typescript
import { string } from "@corets/schema"

string().alpha()
string().alpha("Message")
string().alpha(() => "Message")
```

## string.alphaNumeric() <a href="#stringalphanumeric" id="stringalphanumeric"></a>

String must contain numbers and letters only:

```typescript
import { string } from "@corets/schema"

string().alphaNumeric()
string().alphaNumeric("Message")
string().alphaNumeric(() => "Message")
```

## string.alphaDashes() <a href="#stringalphadashes" id="stringalphadashes"></a>

String must contain letters and dashes `-` only:

```typescript
import { string } from "@corets/schema"

string().alphaDashes()
string().alphaDashes("Message")
string().alphaDashes(() => "Message")
```

## string.alphaUnderscores() <a href="#stringalphaunderscores" id="stringalphaunderscores"></a>

String must contain letters and underscores `_` only:

```typescript
import { string } from "@corets/schema"

string().alphaUnderscores()
string().alphaUnderscores("Message")
string().alphaUnderscores(() => "Message")
```

## string.alphaNumericDashes() <a href="#stringalphanumericdashes" id="stringalphanumericdashes"></a>

String must contain letters, numbers and dashes only:

```typescript
import { string } from "@corets/schema"

string().alphaNumericDashes()
string().alphaNumericDashes("Message")
string().alphaNumericDashes(() => "Message")
```

## string.alphaNumericUnderscores() <a href="#stringalphanumericunderscores" id="stringalphanumericunderscores"></a>

String must contain letters, numbers and underscores only:

```typescript
import { string } from "@corets/schema"

string().alphaNumericUnderscores()
string().alphaNumericUnderscores("Message")
string().alphaNumericUnderscores(() => "Message")
```

## string.date() <a href="#stringdate" id="stringdate"></a>

String must be a valid ISO date string:

```typescript
import { string } from "@corets/schema"

string().date()
string().date("Message")
string().date(() => "Message")
```

## string.dateBefore() <a href="#stringdatebefore" id="stringdatebefore"></a>

String must be a valid ISO date string before the given date:

```typescript
import { string } from "@corets/schema"

string().dateBefore(new Date())
string().dateBefore(new Date(), "Message")
string().dateBefore(() => new Date(), () => "Message")
```

## string.dateBeforeOrEqual() <a href="#stringdatebeforeorsame" id="stringdatebeforeorsame"></a>

Similar to [`string.dateBefore()`](/services/schema#stringdatebefore), but allows dates to be equal:

```typescript
import { string } from "@corets/schema"

string().dateBeforeOrEqual(new Date())
string().dateBeforeOrEqual(new Date(), "Message")
string().dateBeforeOrEqual(() => new Date(), () => "Message")
```

## string.dateAfter() <a href="#stringdateafter" id="stringdateafter"></a>

String must be a valid ISO date string after the given date:

```typescript
import { string } from "@corets/schema"

string().dateAfter(new Date())
string().dateAfter(new Date(), "Message")
string().dateAfter(() => new Date(), () => "Message")
```

## string.dateAfterOrEqual() <a href="#stringdateafterorsame" id="stringdateafterorsame"></a>

Similar to [`string.dateAfter()`](/services/schema#stringdateafter), but allows dates to be equal:

```typescript
import { string } from "@corets/schema"

string().dateAfterOrEqual(new Date())
string().dateAfterOrEqual(new Date(), "Message")
string().dateAfterOrEqual(() => new Date(), () => "Message")
```

## string.dateBetween() <a href="#stringdatebetween" id="stringdatebetween"></a>

String must be a valid ISO date string between the two given dates:

```typescript
import { string } from "@corets/schema"

string().dateBetween(new Date(), new Date())
string().dateBetween(new Date(), new Date(), "Message")
string().dateBetween(() => new Date(), () => new Date(), () => "Message")
```

## string.dateBetweenOrEqual() <a href="#stringdatebetweenorsame" id="stringdatebetweenorsame"></a>

Similar to [`string.dateBetween()`](/services/schema#stringdatebetween), but allows dates to be equal:

```typescript
import { string } from "@corets/schema"

string().dateBetweenOrEqual(new Date(), new Date())
string().dateBetweenOrEqual(new Date(), new Date(), "Message")
string().dateBetweenOrEqual(() => new Date(), () => new Date(), () => "Message")
```

## string.time() <a href="#stringtime" id="stringtime"></a>

String must be a valid ISO time string:

```typescript
import { string } from "@corets/schema"

string().time()
string().time("Message")
string().time(() => "Message")
```

## string.timeBefore() <a href="#stringtimebefore" id="stringtimebefore"></a>

String must be a valid time string before the given time:

```typescript
import { string } from "@corets/schema"

string().timeBefore("10:00")
string().timeBefore("10:00", "Message")
string().timeBefore(() => "10:00", () => "Message")
```

## string.timeBeforeOrEqual() <a href="#stringtimebeforeorsame" id="stringtimebeforeorsame"></a>

Similar to [`string.timeBefore()`](/services/schema#stringtimebefore), but allows times to be equal:

```typescript
import { string } from "@corets/schema"

string().timeBeforeOrEqual("10:00")
string().timeBeforeOrEqual("10:00", "Message")
string().timeBeforeOrEqual(() => "10:00", () => "Message")
```

## string.timeAfter() <a href="#stringtimeafter" id="stringtimeafter"></a>

String must be a valid time string after the given time:

```typescript
import { string } from "@corets/schema"

string().timeAfter("10:00")
string().timeAfter("10:00", "Message")
string().timeAfter(() => "10:00", () => "Message")
```

## string.timeAfterOrEqual() <a href="#stringtimeafterorsame" id="stringtimeafterorsame"></a>

Similar to [`string.timeAfter()`](/services/schema#stringtimeafter), but allows times to be equal:

```typescript
import { string } from "@corets/schema"

string().timeAfterOrEqual("10:00")
string().timeAfterOrEqual("10:00", "Message")
string().timeAfterOrEqual(() => "10:00", () => "Message")
```

## string.timeBetween() <a href="#stringtimebetween" id="stringtimebetween"></a>

String must be a valid time string between the two given times:

```typescript
import { string } from "@corets/schema"

string().timeBetween("10:00", "15:00")
string().timeBetween("10:00", "15:00", "Message")
string().timeBetween(() => "10:00", () => "15:00", () => "Message")
```

## string.timeBetweenOrEqual() <a href="#stringtimebetweenorsame" id="stringtimebetweenorsame"></a>

Similar to [`string.timeBetween()`](/services/schema#stringtimebetween), but allows times to be equal:

```typescript
import { string } from "@corets/schema"

string().timeBetweenOrEqual("10:00", "15:00")
string().timeBetweenOrEqual("10:00", "15:00", "Message")
string().timeBetweenOrEqual(() => "10:00", () => "15:00", () => "Message")
```

## string.dateTime() <a href="#stringdatetime" id="stringdatetime"></a>

String must be a valid ISO date time string:

```typescript
import { string } from "@corets/schema"

string().dateTime()
string().dateTime("Message")
string().dateTime(() => "Message")
```

## string.toDefault() <a href="#stringtodefault" id="stringtodefault"></a>

Provide a fallback value in case the underlying value is not a string:

```typescript
import { string } from "@corets/schema"

string().toDefault("default value")
string().toDefault("default value")
string().toDefault(() => "default value")
```

## string.toUpperCase() <a href="#stringtouppercase" id="stringtouppercase"></a>

Convert string to all upper case:

```typescript
import { string } from "@corets/schema"

string().toUpperCase()
```

## string.toLowerCase() <a href="#stringtolowercase" id="stringtolowercase"></a>

Convert string to all lower case:

```typescript
import { string } from "@corets/schema"

string().toLowerCase()
```

## string.toCapitalized() <a href="#stringtocapitalized" id="stringtocapitalized"></a>

Capitalize first letter:

```typescript
import { string } from "@corets/schema"

string().toCapitalized()
```

## string.toCamelCase() <a href="#stringtocamelcase" id="stringtocamelcase"></a>

Convert string to `camelCase`:

```typescript
import { string } from "@corets/schema"

string().toCamelCase()
```

## string.toSnakeCase() <a href="#stringtosnakecase" id="stringtosnakecase"></a>

Convert string to `snake_case`:

```typescript
import { string } from "@corets/schema"

string().toSnakeCase()
```

## string.toKebabCase() <a href="#stringtokebabcase" id="stringtokebabcase"></a>

Convert string to `kebab-case`:

```typescript
import { string } from "@corets/schema"

string().toKebabCase()
```

## string.toConstantCase() <a href="#stringtoconstantcase" id="stringtoconstantcase"></a>

Convert string to `CONSTANT_CASE`:

```typescript
import { string } from "@corets/schema"

string().toConstantCase()
```

## string.toTrimmed() <a href="#stringtotrimmed" id="stringtotrimmed"></a>

Trim surrounding whitespace:

```typescript
import { string } from "@corets/schema"

string().toTrimmed()
```

## number() <a href="#number" id="number"></a>

Contains various validators and sanitizers for numbers:

```typescript
import { number } from "@corets/schema"

number()
```

Create a schema instance without the factory function:

```typescript
import { NumberSchema } from "@corets/schema"

new NumberSchema()
```

## number.required() <a href="#numberrequired" id="numberrequired"></a>

Value must be a number:

```typescript
import { number } from "@corets/schema"

number().required()
number().required("Message")
number().required(() => false, () => "Message")
```

## number.optional() <a href="#numberoptional" id="numberoptional"></a>

Value might be a number, opposite of [`number.required()`](/services/schema#numberrequired):

```typescript
import { number } from "@corets/schema"

number().optional()
number().optional("Message")
number().optional(() => "Message")
```

## number.equals() <a href="#numberequals" id="numberequals"></a>

Number must be equal to the given value:

```typescript
import { number } from "@corets/schema"

number().equals(3)
number().equals(3, "Message")
number().equals(() => 3, () => "Message")
```

## number.min() <a href="#numbermin" id="numbermin"></a>

Number must not be smaller than the given value:

```typescript
import { number } from "@corets/schema"

number().min(5)
number().min(5, "Message")
number().min(() => 5, () => "Message")
```

## number.max() <a href="#numbermax" id="numbermax"></a>

Number must not be bigger than the given value:

```typescript
import { number } from "@corets/schema"

number().max(10)
number().max(10, "Message")
number().max(() => 10, () => "Message")
```

## number.between() <a href="#numberbetween" id="numberbetween"></a>

Number must be between the two given numbers:

```typescript
import { number } from "@corets/schema"

number().between(5, 10)
number().between(5, 10, "Message")
number().between(() => 5, () => 10, () => "Message")
```

## number.between() <a href="#numberbetween-1" id="numberbetween-1"></a>

Number must be positive - bigger than zero:

```typescript
import { number } from "@corets/schema"

number().positive()
number().positive("Message")
number().positive(() => "Message")
```

## number.negative() <a href="#numbernegative" id="numbernegative"></a>

Number must be negative:

```typescript
import { number } from "@corets/schema"

number().negative()
number().negative("Message")
number().negative(() => "Message")
```

## number.integer() <a href="#numberinteger" id="numberinteger"></a>

Number must be an integer:

```typescript
import { number } from "@corets/schema"

number().integer()
number().integer("Message")
number().integer(() => "Message")
```

## number.oneOf() <a href="#numberoneof" id="numberoneof"></a>

Number must be one of the whitelisted values:

```typescript
import { number } from "@corets/schema"

number().oneOf([10, 20])
number().oneOf([10, 20], "Message")
number().oneOf(() => [10, 20], () => "Message")
```

## number.noneOf() <a href="#numbernoneof" id="numbernoneof"></a>

Number must not be one of the blacklisted values:

```typescript
import { number } from "@corets/schema"

number().noneOf([10, 20])
number().noneOf([10, 20], "Message")
number().noneOf(() => [10, 20], () => "Message")
```

## number.toDefault() <a href="#numbertodefault" id="numbertodefault"></a>

Default value in case the underlying value is not a number:

```typescript
import { number } from "@corets/schema"

number().toDefault(10)
number().toDefault(() => 10)
```

## number.toRounded() <a href="#numbertorounded" id="numbertorounded"></a>

Round value using [`lodash.round()`](https://lodash.com/docs/4.17.15#round):

```typescript
import { number } from "@corets/schema"

const precision = 2

number().toRounded()
number().toRounded(precision)
number().toRounded(() => precision)
```

## number.toFloored() <a href="#numbertofloored" id="numbertofloored"></a>

Round value using [`lodash.floor()`](https://lodash.com/docs/4.17.15#floor):

```typescript
import { number } from "@corets/schema"

const precision = 2

number().toFloored()
number().toFloored(precision)
number().toFloored(() => precision)
```

## number.toCeiled() <a href="#numbertoceiled" id="numbertoceiled"></a>

Round value using [`lodash.ceil()`](https://lodash.com/docs/4.17.15#ceil):

```typescript
import { number } from "@corets/schema"

const precision = 2

number().toCeiled()
number().toCeiled(precision)
number().toCeiled(() => precision)
```

## number.toTrunced() <a href="#numbertotrunced" id="numbertotrunced"></a>

Trunc value using [`Math.trunc()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc)(drops everything after the decimal point):

```typescript
import { number } from "@corets/schema"

number().toTrunced()
```

## boolean() <a href="#boolean" id="boolean"></a>

Contains various validators and sanitizers for booleans:

```typescript
import { boolean } from "@corets/schema"

boolean()
```

Create a schema instance without the factory function:

```typescript
import { BooleanSchema } from "@corets/schema"

new BooleanSchema()
```

## boolean.required() <a href="#booleanrequired" id="booleanrequired"></a>

Value must be a boolean:

```typescript
import { boolean } from "@corets/schema"

boolean().required()
boolean().required(false, "Message")
boolean().required(() => false, () =>"Message")
```

## boolean.optional() <a href="#booleanoptional" id="booleanoptional"></a>

Value might be a boolean, opposite of [`boolean.required()`](/services/schema#booleanrequired):

```typescript
import { boolean } from "@corets/schema"

boolean().optional()
boolean().optional("Message")
boolean().optional(() => "Message")
```

## boolean.equals() <a href="#booleanequals" id="booleanequals"></a>

Number must be equal to the given value:

```typescript
import { boolean } from "@corets/schema"

boolean().equals(true)
boolean().equals(true, "Message")
boolean().equals(() => true, () => "Message")
```

## boolean.toDefault() <a href="#booleantodefault" id="booleantodefault"></a>

Provide a fallback value in case the underlying value is not a boolean:

```typescript
import { boolean } from "@corets/schema"

boolean().toDefault(true)
boolean().toDefault(() => true)
```

## date() <a href="#date" id="date"></a>

Contains various validators and sanitizers for dates:

```typescript
import { date } from "@corets/schema"

date()
```

Create a schema instance without the factory function:

```typescript
import { DateSchema } from "@corets/schema"

new DateSchema()
```

## date.required() <a href="#daterequired" id="daterequired"></a>

Value must be a date:

```typescript
import { date } from "@corets/schema"

date().required()
date().required(false, "Message")
date().required(() => false, () => "Message")
```

## date.optional() <a href="#dateoptional" id="dateoptional"></a>

Value might be a date, opposite of [`date.required()`](/services/schema#daterequired):

```typescript
import { date } from "@corets/schema"

date().optional()
date().optional("Message")
date().optional(() => "Message")
```

## date.equals() <a href="#dateequals" id="dateequals"></a>

Date must be equal to the given value:

```typescript
import { date } from "@corets/schema"

date().equals(new Date())
date().equals(new Date(), "Message")
date().equals(() => new Date(), () => "Message")
```

## date.after() <a href="#dateafter" id="dateafter"></a>

Underlying value must be after the given date:

```typescript
import { date } from "@corets/schema"

date().after(new Date())
date().after(new Date(), "Message")
date().after(() => new Date(), () => "Message")
```

## date.before() <a href="#datebefore" id="datebefore"></a>

Underlying value must be before the given date:

```typescript
import { date } from "@corets/schema"

date().before(new Date())
date().before(new Date(), "Message")
date().before(() => new Date(), () => "Message")
```

## date.between() <a href="#datebetween" id="datebetween"></a>

Underlying value must be between the two dates:

```typescript
import { date } from "@corets/schema"

date().between(new Date(), new Date())
date().between(new Date(), new Date(), "Message")
date().between(() => new Date(), () => new Date(), () => "Message")
```

## date.toDefault() <a href="#datetodefault" id="datetodefault"></a>

Provide a fallback value in case the underlying value is not a date:

```typescript
import { date } from "@corets/schema"

date().toDefault(new Date())
date().toDefault(new Date(), "Message")
date().toDefault(() => new Date(), () => "Message")
```

## array() <a href="#array" id="array"></a>

Contains various validators and sanitizers for arrays:

```typescript
import { array, string } from "@corets/schema"

array()
```

Create a schema instance without the factory function:

```typescript
import { ArraySchema } from "@corets/schema"

new ArraySchema()
```

Create an array schema with a separate schema for its children:

```typescript
import { array, string } from "@corets/schema"

array(string().min(2))
```

## array.required() <a href="#arrayrequired" id="arrayrequired"></a>

Value must be an array:

```typescript
import { array } from "@corets/schema"

array().required()
array().required(false, "Message")
array().required(() => false, () => "Message")
```

## array.optional() <a href="#arrayoptional" id="arrayoptional"></a>

Value might be a array, opposite of [`array.required()`](/services/schema#arrayrequired):

```typescript
import { array } from "@corets/schema"

array().optional()
array().optional("Message")
array().optional(() => "Message")
```

## array.equals() <a href="#arrayequals" id="arrayequals"></a>

Array must be equal to the given value:

```typescript
import { array } from "@corets/schema"

array().equals([1, 2])
array().equals([1, 2], "Message")
array().equals(() => [1, 2], () => "Message")
```

## array.length() <a href="#arraylength" id="arraylength"></a>

Array must have an exact length:

```typescript
import { array } from "@corets/schema"

array().length(3)
array().length(3, "Message")
array().length(() => 3, () => "Message")
```

## array.min() <a href="#arraymin" id="arraymin"></a>

Array must not be shorter than the given length:

```typescript
import { array } from "@corets/schema"

array().min(3)
array().min(3, "Message")
array().min(() => 3, () => "Message")
```

## array.max() <a href="#arraymax" id="arraymax"></a>

Array must not be longer than the given length:

```typescript
import { array } from "@corets/schema"

array().max(3)
array().max(3, "Message")
array().max(() => 3, () => "Message")
```

## array.between() <a href="#arraybetween" id="arraybetween"></a>

Array must have a length between the two given values:

```typescript
import { array } from "@corets/schema"

array().between(3, 5)
array().between(3, 5, "Message")
array().between(() => 3, () => 5, () => "Message")
```

## array.someOf() <a href="#arraysomeof" id="arraysomeof"></a>

Array must only contain whitelisted values:

```typescript
import { array } from "@corets/schema"

array().someOf([3, 4])
array().someOf([3, 4], "Message")
array().someOf(() => [3, 4], () => "Message")
```

## array.noneOf() <a href="#arraynoneof" id="arraynoneof"></a>

Array must not contain any of the blacklisted values:

```typescript
import { array } from "@corets/schema"

array().noneOf([3, 4])
array().noneOf([3, 4], "Message")
array().noneOf(() => [3, 4], () => "Message")
```

## array.shape() <a href="#arrayshape" id="arrayshape"></a>

Specify a schema for array children, every item must be valid according to the given schema:

```typescript
import { array, string } from "@corets/schema"

array().shape(string().min(3))
array().shape(() => string().min(3))
```

You can pass a shape directly to the [`array()`](/services/schema#array) method too:

```typescript
import { array, string } from "@corets/schema"

array(string().min(3))
array(() => string().min(3))
```

## array.toDefault() <a href="#arraytodefault" id="arraytodefault"></a>

Provide a default value in case the underlying value is not an array:

```typescript
import { array } from "@corets/schema"

array().toDefault([1, 2])
array().toDefault(() => [1, 2])
```

## array.toFiltered() <a href="#arraytofiltered" id="arraytofiltered"></a>

Filter out invalid array items manually:

```typescript
import { array } from "@corets/schema"

const isString = (value) => typeof value === "string"

array().toFiltered(isString)
```

## array.toMapped() <a href="#arraytomapped" id="arraytomapped"></a>

Map every array item manually:

```typescript
import { array } from "@corets/schema"

const toUpperCase = (value) => typeof value === "string" 
    ? value.toUpperCase() 
    : value

array().toMapped(toUpperCase)
```

## array.toCompact() <a href="#arraytocompact" id="arraytocompact"></a>

Filter out all `falsey` values like `null`, `undefined`, `""` and `0`:

```typescript
import { array } from "@corets/schema"

array().toCompact()
```

## array.toUnique() <a href="#arraytounique" id="arraytounique"></a>

Filter out all duplicate values:

```typescript
import { array } from "@corets/schema"

array().toUnique()
```

## object() <a href="#object" id="object"></a>

Contains various validators and sanitizers for objects:

```typescript
import { object, string } from "@corets/schema"

object()
```

Create a schema instance without the factory function:

```typescript
import { ObjectSchema } from "@corets/schema"

new ObjectSchema()
```

Create an object schema with a separate schema for each property:

```typescript
import { object, string } from "@corets/schema"

object({
    foo: string().min(2)
})
```

## object.required() <a href="#objectrequired" id="objectrequired"></a>

Value must be an object:

```typescript
import { object } from "@corets/schema"

object().required()
object().required(false, "Message")
object().required(() => false, () => "Message")
```

## object.optional() <a href="#objectoptional" id="objectoptional"></a>

Value might be an object, opposite of [`object.required()`](/services/schema#objectrequired):

```typescript
import { object } from "@corets/schema"

object().optional()
object().optional("Message")
object().optional(() => "Message")
```

## object.equals() <a href="#objectequals" id="objectequals"></a>

Underlying value must be equal to the given value:

```typescript
import { object } from "@corets/schema"

object().equals({foo: "bar"})
object().equals({foo: "bar"}, "Message")
object().equals(() => {foo: "bar"}, () => "Message")
```

## object.shape() <a href="#objectshape" id="objectshape"></a>

Shape an object and set up schemas for all of its properties:

```typescript
import { object, string } from "@corets/schema"

object().shape({
    firstName: string().min(3).max(20) 
})
```

You can pass a shape directly to the [`object()`](/services/schema#object) method too:

```typescript
import { object, string } from "@corets/schema"

object({ 
    firstName: string().min(3).max(20) 
})
```

## object.allowUnknownKeys() <a href="#objectallowunknownkeys" id="objectallowunknownkeys"></a>

Allow object to contain keys that have not been configured through [`object.shape()`](/services/schema#objectshape):

```typescript
import { object, string } from "@corets/schema"

object({ 
    firstName: string().min(3).max(20) 
}).allowUnknownKeys()
```

## object.forbidUnknownKeys() <a href="#objectdisallowunknownkeys" id="objectdisallowunknownkeys"></a>

Forbid object to contain keys that have not been configured through [`object.shape()`](/services/schema#objectshape):

```typescript
import { object, string } from "@corets/schema"

object({ 
    firstName: string().min(3).max(20) 
}).forbidUnknownKeys()
```

## object.shapeUnknownKeys() <a href="#objectshapeunknownkeys" id="objectshapeunknownkeys"></a>

Shape unknown object keys to make sure they adhere to a certain standard:

```typescript
import { object, string } from "@corets/schema"

object({ 
    firstName: string().min(3).max(20) 
}).shapeUnknownKeys(string().min(3).toCamelCase())
```

## object.shapeUnknownValues() <a href="#objectshapeunknownvalues" id="objectshapeunknownvalues"></a>

Shape unknown object values to make sure they adhere to a standard:

```typescript
import { object, string } from "@corets/schema"

object({ 
    firstName: string().min(3).max(20) 
}).shapeUnknownValues(string().min(3).max(20))
```

## object.toDefault() <a href="#objecttodefault" id="objecttodefault"></a>

Provide a fallback value in case the underlying value is not an object:

```typescript
import { object } from "@corets/schema"

object().toDefault({ title: "Foo" })
object().toDefault(() => ({ title: "Foo" }))
```

## object.toCamelCaseKeys() <a href="#objecttocamelcasekeys" id="objecttocamelcasekeys"></a>

Transform all object keys to `camelCase`:

```typescript
import { object } from "@corets/schema"

object().toCamelCaseKeys()
// disable deep mapping
object().toCamelCaseKeys(false)
```

## object.toSnakeCaseKeys() <a href="#objecttosnakecasekeys" id="objecttosnakecasekeys"></a>

Transform all object keys to `snake_case`:

```typescript
import { object } from "@corets/schema"

object().toSnakeCaseKeys()
// disable deep mapping
object().toSnakeCaseKeys(false)
```

## object.toKebabCaseKeys() <a href="#objecttokebabcasekeys" id="objecttokebabcasekeys"></a>

Transform all object keys to `kebab-case`:

```typescript
import { object } from "@corets/schema"

object().toKebabCaseKeys()
// disable deep mapping
object().toKebabCaseKeys(false)
```

## object.toConstantCaseKeys() <a href="#objecttoconstantcasekeys" id="objecttoconstantcasekeys"></a>

Transform all object keys to `CONSTANT_CASE`:

```typescript
import { object } from "@corets/schema"

object().toConstantCaseKeys()
// disable deep mapping
object().toConstantCaseKeys(false)
```

## object.toMappedValues() <a href="#objecttomappedvalues" id="objecttomappedvalues"></a>

Transform all object values:

```typescript
import { object } from "@corets/schema"

object().toMappedValues((value, key) => value)
// disable deep mapping
object().toConstantCaseKeys(false)
```

## object.toMappedKeys() <a href="#objecttomappedkeys" id="objecttomappedkeys"></a>

Transform all object keys:

```typescript
import { object } from "@corets/schema"

object().toMappedKeys((value, key) => key)
// disable deep mapping
object().toMappedKeys((value, key) => key, false)
```

## mixed() <a href="#mixed" id="mixed"></a>

Contains various validators and sanitizers for mixed data types:

```typescript
import { mixed } from "@corets/schema"

mixed()
```

Create a schema instance without the factory function:

```typescript
import { MixedSchema } from "@corets/schema"

new MixedSchema()
```

## mixed.required() <a href="#mixedrequired" id="mixedrequired"></a>

Value must not be `null` nor `undefined`:

```typescript
import { mixed } from "@corets/schema"

mixed().required()
mixed().required(false, "Message")
mixed().required(() => false, () => "Message")
```

## mixed.optional() <a href="#mixedoptional" id="mixedoptional"></a>

Value might als be a `null` or `undefined`, opposite of [`mixed.required()`](/services/schema#mixedrequired):

```typescript
import { mixed } from "@corets/schema"

mixed().optional()
mixed().optional("Message")
mixed().optional(() => "Message")
```

## mixed.equals() <a href="#mixedequals" id="mixedequals"></a>

Underlying value must be equal to the given value:

```typescript
import { mixed } from "@corets/schema"

mixed().equals("yolo")
mixed().equals("yolo", "Message")
mixed().equals(() => "yolo", () => "Message")
```

## mixed.oneOf() <a href="#mixedoneof" id="mixedoneof"></a>

Underlying value must be one of the whitelisted values:

```typescript
import { mixed } from "@corets/schema"

mixed().oneOf(["foo", "bar"])
mixed().oneOf(["foo", "bar"], "Message")
mixed().oneOf(() => ["foo", "bar"], () => "Message")
```

## mixed.noneOf() <a href="#mixednoneof" id="mixednoneof"></a>

Underlying value must not be one of the blacklisted values:

```typescript
import { mixed } from "@corets/schema"

mixed().noneOf(["foo", "bar"])
mixed().noneOf(["foo", "bar"], "Message")
mixed().noneOf(() => ["foo", "bar"], () => "Message")
```

## mixed.toDefault() <a href="#mixedtodefault" id="mixedtodefault"></a>

Provide a fallback value in case the underlying value is `null` or `undefined`:

```typescript
import { mixed } from "@corets/schema"

mixed().toDefault("foo")
mixed().toDefault("foo", "Message")
mixed().toDefault(() => "foo", () => "Message")
```


# Form

Build statically typed forms with ease. A refreshing alternative to existing form libraries.

Source code is hosted at [GitHub](https://github.com/corets/form)

This is an opinionated form library for any JavaScript environment, with focus on React and React Native. The main difference between this project and other form libraries out there, is the ability to define a form *outside* of your presentation layer and just map it inside a component later on. A form built this way will encapsulate all the necessary logic to handle validation, submission, error handling and so on. This leads to clean separation of concerns, easier testing, composability and reusability.

One of the coolest features of this library is the fully static form fields access, anything that can be caught at compile time does not have to be tested explicitly.

* **Works everywhere**, optimised for **React and React Native**
* Strong typings from tail to toe, for **joyful developer experience**
* Can be **tested separately from the UI**
* Treat **forms as a service** and reduce complexity inside components
* John Wick kind of crazy **powerful validation** options
* Fully static field access, **forget your string based keys**
* Many goodies like, status indicators, dirty fields, etc.
* Tested by an army of killer coding ninja monkeys with a **test coverage of 100%**
* Tons of other features and very easy customisation options

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/form
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/form
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in this package:

{% content-ref url="/pages/-MeeFDHWx4UK\_xvFBXwt" %}
[useForm](/hooks/use-form)
{% endcontent-ref %}

Ready to use form bindings for React, to get you started, can be found here:

{% content-ref url="/pages/-MeeFDEwvi5BWuryOk2v" %}
[useFormBinder](/hooks/use-form-binder)
{% endcontent-ref %}

Comes with built in support for the schema package for delightful validation logic:

{% content-ref url="/pages/-MefJhqSgQM2IZsZpDnB" %}
[Schema](/services/schema)
{% endcontent-ref %}

Static fields access is powered by this library:

{% content-ref url="/pages/-MeeFDZTgmmurCkKtNA3" %}
[Accessor](/services/accessor)
{% endcontent-ref %}

## Quick start <a href="#quick-start" id="quick-start"></a>

Use this example as a high level overview of what this library has to offer and whether it suits your personal preference. Here we are going to create a very basic form that has some validation logic and dispatches an HTTP request to a remote endpoint, for processing.

First, let's define our types:

```typescript
export type User = {  
    uuid: string  
    firstName: string  
    lastName: string
}

export type CreateUserForm = {  
    firstName: string  
    lastName: string
}

export type CreateUserResult = {  
    success?: string  
    error?: string  
    user?: User
}
```

Next, define a method that is going to call the API:

```typescript
export const createUser = async (data: CreateUserForm): Promise<User> => 
    ({ id: 1, ...data })
```

Now we can build the form logic:

```typescript
import { createFormFromSchema } from "@corets/form"
import { object, value } from "@corets/schema"

export const createUserForm = () => {  
    return createFormFromSchema<CreateUserForm, CreateUserResult>(object({      
            firstName: value("").string().min(2).max(20).toTrimmed(),      
            lastName: value("").string().min(2).max(20).toTrimmed()    
        }))    
        .handler(async (form) => {      
            try {        
                const user = await createUser(form.get())        
                
                return { success: "User created", user }      
            } catch (error) {        
                return { error: "Could not create user" }      
            }    
        })
}
```

Now let's build the actual form:

```typescript
import React from "react"
import { useForm } from "@corets/use-form"
import { useFormBinder } from "@corets/use-form-binder"

const CreateUserForm = () => {  
    const form = useForm(createUserForm)  
    const bind = useFormBinder(form)  
    const errors = form.getErrors()  
    const result = form.getResult()
    const isSubmitting = form.isSubmitting()

    return (    
        <form {...bind.form()}>
            <div>{isSubmitting && "Loading..."}</div>
            
            <div>{result?.success || result?.error }</div>  
                
            <div>        
                <input {...bind.input("firstName")} placeholder="First name"/>        
                <div>{form.getErrorsAt("firstName")}</div>       
            </div>
            
            <div>        
                <input {...bind.input("lastName")} placeholder="Last name"/>
                <div>{errors.getErrorsAt("lastName")}</div>      
            </div>      
            
            <button {...bind.button()}>Create</button>    
        </form>  
    )
}
```

{% hint style="info" %}
In this example we are using the vanilla form binder shipped through the [@corets/use-form-binder](/hooks/use-form-binder) package, read more about form binders in the [next section](/services/form#binders).
{% endhint %}

## Static fields

One of the unique features of this library is it's static field access. Normally you bind / get / set your form data using dynamic, string based keys, which breaks your compile time safety. This significantly increases the maintenance cost of forms in the future, since you always have to be extremely careful when changing any of the form fields or the form object structure.

There is a better way to do this! Below is a side by side comparison of a form using dynamic and static field access. Keep in mind that you have full IDE / autocomplete support when accessing form fields through the static facade.

{% tabs %}
{% tab title="static" %}

```typescript
import { createForm, ObservableFormField } from "@corets/form"

const form = createForm({ some: { nested: "field" } })
const fields = form.getFields()

fields.some.nested.get().getValue()
fields.some.nested.get().setValue("new value")
```

{% endtab %}

{% tab title="dynamic" %}

```typescript
import { createForm, ObservableForm } from "@corets/form"

const form = createForm({ some: { nested: "field" } })

form.getAt("some.nested")
form.setAt("some.nested", "new value")
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Have a look at the [Form.getFields()](/services/form#formfields) documentation for more details.
{% endhint %}

You can read more about static accessors in the package docs:

{% content-ref url="/pages/-MeeFDZTgmmurCkKtNA3" %}
[Accessor](/services/accessor)
{% endcontent-ref %}

## Component binders <a href="#binders" id="binders"></a>

This library was not designed for any specific framework. You should be able to use it everywhere where you can run JavaScript. This is why it does not ship any logic on how to connect to the UI out of the box. The [@corets/use-form-binder](/hooks/use-form-binder) package provides bindings for vanilla HTML elements, to get you started.

Given the nature of modern frontend development, projects use various component libraries and more often than not you have to write a custom input component. Obviously it is not possible to write one binder to *rule them all*. Therefore the most pragmatic approach is to create dedicated binders for various use cases. The good thing is that **it is super easy to write your own binder!**

Take a look at [how binders are implemented](https://github.com/corets/use-form-binder/blob/master/src/FormBinder.ts) in the @corets/use-form-binder package to get an idea.

Here is an example of a very basic, vanilla text field binder:

```typescript
import { ObservableForm } from "@corets/form"

const createBinder = (form: ObservableForm) => ({  
    input: createInputBinder(form)
})

const createInputBinder = (form: ObservableForm) => (path: string) => {  
    return {    
        name: path,    
        value: form.getAt(path),    
        onChange: (e) => form.setAt(path, e.target.value),  
    }
}
```

That's all there is about it! The best thing is, no matter what component you are going to use, **you can always make it work! ™ 😎**

Now let's use that binder in our form:

```typescript
import React from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"

const Example = () => {  
    const form = useForm(() => createForm({ field: "value" }))  
    const bind = createBinder(form)  
    
    return <input {...bind.input("field")} />
}
```

Another way to create a binder is using the [`Form.getFields()`](/services/form#formfields) method powered by the [@corets/accessor](/services/accessor) library. You no longer need to use dynamic, string based keys to map the form fields, you can use a statically typed facade instead:

```typescript
import { ObservableFormField } from "@corets/form"

const createBinder = () => ({  input: inputBinder })

const inputBinder = (field: ObservableFormField) => {  
    return {    
        name: field.getKey(),    
        value: field.getValue(),    
        onChange: (e) => field.setValue(e.target.value),  
    }
}
```

Now let's use that binder in our form:

```typescript
import React from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"

const Example = () => {  
    const form = useForm(() => createForm({ 
        some: { nested: { field: "value" } } 
    }))  
    
    const bind = createBinder(form)  
    const fields = form.getFields()  
    
    return <input {...bind.input(fields.some.nested.field.get())} />
}
```

{% hint style="info" %}
Read more about static fields [here](/services/form#static-fields).
{% endhint %}

## Optimisation <a href="#optimisation" id="optimisation"></a>

You can reduce the number of re-renders in big forms, by wrapping your UI blocks into the `<Memo/>` component from the [@corets/memo](/components/memo) package. Most likely you will never need this, but if you do, you are covered.

```typescript
import React, { useState } from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"
import { Memo } from "@corets/memo"

const Example = () => {  
    const form = useForm(() => createForm({ field1: "foo", field2: "bar" }))  
    const [someValue, setSomeValue] = useState("some state")  
    
    return (    
        <form>      
            <Memo deps={form.getDeps("field1")}>        
                This section will only ever re-render when some of shared form properties change,        
                like: `submitting`, `submitted` or `result`, or when one of the field 
                related properties receives a change specific to this field, 
                like: `errors`, `values`, `changedFields` or `dirtyFields`.
            </Memo>      
            
            <Memo deps={form.getDeps(["field1", "field2"])}>        
                This section will change when one of the two fields receives a relevant change.      
            </Memo>      
            
            <Memo deps={form.getDeps(["field1", "field2"], { errors: false })}>        
                This block will NOT re-render if there is an error for one of the two fields.
            </Memo>      
            
            <Memo deps={[...form.getDeps(["field1", "field2"]), someValue]}>        
                Include an aditional, custom, value to the list of dependencies for a re-render.      
            </Memo>    
        </form>  
    )
}
```

You can read more about \<Memo/> in the package docs:

{% content-ref url="/pages/-MeeFD7Hl71eTR--pvPW" %}
[\<Memo />](/components/memo)
{% endcontent-ref %}

## Testing <a href="#testing" id="testing"></a>

When writing unit tests, make sure that you set the `debounce` config property to `0`. This disables throttling of the state changes and allows you to write tests in a synchronous manner.

```typescript
import { createForm } from "@corets/form"

const form = createForm().configure({ debounce: 0 })
```

## createForm() <a href="#createform" id="createform"></a>

Create a new form instance:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })
```

Create a new form without the factory function:

```typescript
import { Form } from "@corets/form"

const form = new Form({ data: "foo" })
```

Create a form instance with a specific type:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }

const form = createForm<MyForm>({ data: "foo" })
```

Specify form result type:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }
type MyFormResult = { result: string }

const form = createForm<MyForm, MyFormResult>({ data: "foo" })
```

## createFormFromSchema() <a href="#createformfromschema" id="createformfromschema"></a>

Create a new form instance based on a schema definition from the [@corets/schema](/services/schema) package. This is a convenient helper that allows you to avoid unnecessary boilerplate code when defining initial values for a form. Instead of creating an object, that adheres to the specific form type, with the initial values, you can define those initial values inside the schema definition itself.

Here is a side by side comparison of the two different approaches:

{% tabs %}
{% tab title="createFormFromSchema" %}

```typescript
import { createFormFromSchema } from "@corets/form"
import { object, schema } from "@corets/schema"

type MyForm = { data: string }
type MyFormResult = { result: string }

const formSchema = object<MyForm>({  
    data: schema("foo").string().min(2)
})

const form = createFormFromSchema<MyForm, MyFormResult>(formSchema)
```

{% endtab %}

{% tab title="createForm" %}

```typescript
import { createForm } from "@corets/form"
import { object, string } from "@corets/schema"

type MyForm = { data: string }
type MyFormResult = { result: string }

const formSchema = object<MyForm>({ data: string().min(2) })

const initialValues: MyForm = { data: "foo" }

const form = createForm<MyForm, MyFormResult>(initialValues).schema(formSchema)
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
You should always use **createFormFromSchema** instead of of **createForm** where possible.
{% endhint %}

You can read more about schemas in the package docs:

{% content-ref url="/pages/-MefJhqSgQM2IZsZpDnB" %}
[Schema](/services/schema)
{% endcontent-ref %}

## Form.configure() <a href="#formconfig" id="formconfig"></a>

Alter form behaviour by providing configuration overrides:

```typescript
import { createForm } from "@corets/form"

const form = createForm().configure({    
    // run schema sanitizers on form submit() and validate()? (default: true)
    sanitize: true,    
    // validate on form submit()? (default: true)
    validate: true,    
    // validate fields immediately after a change? (default: true)
    reactive: true,    
    // delay invocation of listeners after a change (default: 10ms)
    debounce: 10,  
})
```

## Form.handler() <a href="#formhandler" id="formhandler"></a>

Specify a handler that is called whenever the form is submitted:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }
type MyFormResult = { result: string }

const form = createForm<MyForm, MyFormResult>({ data: "foo" })
    .handler(async (form) => {    
        const result: MyFormResult = { result: "some value" }    
        
        return result  
    })
```

Result returned from a handler is also returned from the [`Form.submit()`](/services/form#formsubmit) method and is available through the [`Form.getResult()`](/services/form#formgetresult) method later on.

## Form.validator() <a href="#formvalidator" id="formvalidator"></a>

Specify a validation function that is called before the form is submitted or after a form field has been changed, depends on your form config:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string, nested: { field: string } }

const form = createForm<MyForm>({ data: "foo", nested: { field: "bar" } })  
    .validator(async (form) => {    
        const values = form.get()    
        
        // run some validation logic ...
        
        return {      
            "foo": ["Invalid value"],      
            "nested.field": ["Invalid value"]    
        }  
    })
```

If you happen to return validation errors for the whole form, but the validator has been invoked after a field change, errors relating to fields that are not yet changed, will be omitted. Validator will be called in addition to the schema that you might have configured, possible errors will be merged.

## Form.schema() <a href="#formschema" id="formschema"></a>

Configure a validation schema for your form:

```typescript
import { createForm } from "@corets/form"
import { object, string } from "@corets/schema"

type MyForm = { data: string, nested: { field: string } }

const schema = object({  
    data: string().min(2),  
    nested: object({    
        field: string().max(2).toTrimmed()  
    }
)})

const form = createForm<MyForm>({ 
    data: "foo", 
    nested: { field: "bar" } 
}).schema(schema)
```

Schemas are a delightful and powerful way to write your validation logic. They are feature loaded, very flexible and customisable. If you happen to provide a schema and a validator function, both will be invoked and the resulting errors will be merged.

{% hint style="info" %}
Sanitizers that have been added to the schema will be called before any kind of validation happens and will alter the form values if necessary. This applies to the complete validation cycle and not to the reactive one (where validation is triggered only for the changed field).
{% endhint %}

You can read more about schemas in the package docs:

{% content-ref url="/pages/-MefJhqSgQM2IZsZpDnB" %}
[Schema](/services/schema)
{% endcontent-ref %}

## Form.validate() <a href="#formvalidate" id="formvalidate"></a>

Invoking the validate method will trigger the validation process where values get sanitized, schema and validator get called, resulting errors get merged, and so on:

```typescript
import { createForm } from "@corets/form"
import { object, string } from "@corets/schema"

type MyForm = { data: string, nested: { field: string } }

const schema = object<MyForm>({  
    data: string().min(2),  
    nested: object({    
        field: string().max(2)  
    }
)})
const form = createForm<MyForm>({ 
    data: "foo", 
    nested: { field: "" } 
}).schema(schema)

const errors = await form.validate()

if ( ! errors) {
    // continue ...
}
```

Errors object will look something like this:

```typescript
{  
    "data": ["Must be at least \"2\" characters long"],  
    "nested.field": ["Required", "Must be less than \"2\" characters long"],
}
```

You can access errors anytime later:

```typescript
const errors = form.getErrors()
```

You can choose to validate and sanitize only fields that have been changed:

```typescript
const errors = await form.validate({ changed: true })
```

You can choose to disable sanitization of values:

```typescript
const errors = await form.validate({ sanitize: false })
```

You can choose not to persist any possible errors and just do a dry run:

```typescript
const errors = await form.validate({ persist: false })
```

## Form.submit() <a href="#formsubmit" id="formsubmit"></a>

Submitting a form will invoke the validator, schema and handler, depending on the configuration. Whatever is returned from the handler will be returned from the submit method itself and is also stored on the form object for later access.

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }
type MyFormResult = { result: string }

const form = createForm<MyForm, MyFormResult>()  
    .handler(async () => {    
        return { result: "foo" }  
    })
const result = await form.submit()
```

You can access result anytime later:

```typescript
const result = form.getResult()
```

Access validation errors:

```typescript
const errors = form.getErrors()
```

Submit without validation:

```typescript
const result = form.submit({ validate: false })
```

Submit without sanitization:

```typescript
const result = form.submit({ sanitize: false })
```

Validate and sanitize only those form fields that have been changed:

```typescript
const result = form.submit({ changed: true })
```

## Form.listen() <a href="#formlisten" id="formlisten"></a>

Subscribe to any changes on the form object:

```typescript
import { createForm } from "@corets/form"

const form = createForm({})
const unsubscribe = form.listen(form => 
    console.log("something has changed")
)

unsubscribe()
```

Set a custom debounce interval for your listener:

```typescript
import { createForm } from "@corets/form"

const form = createForm({})
const unsubscribe = form.listen(form => 
    console.log("something has changed")
, { debounce: 100 })
```

Disable debounce for your listener:

```typescript
import { createForm } from "@corets/form"

const form = createForm({})
const unsubscribe = form.listen(form => 
    console.log("something has changed")
, { debounce: 0 })
```

## Form.get() <a href="#formget" id="formget"></a>

Get all form values:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })
const values = form.get()
```

## Form.getAt() <a href="#formgetat" id="formgetat"></a>

Get form values at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ nested: { data: "foo"} })
const value = form.getAt("nested.data")
```

## Form.set() <a href="#formset" id="formset"></a>

Replace all form values with the new ones:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })

form.set({ data: "bar" })
```

{% hint style="info" %}
This method overrides all form values and does not track any dirty or changed fields. Use the [Form.setAt()](/services/form#formsetat) method instead, if you want to track dirty and changed fields.
{% endhint %}

## Form.setAt() <a href="#formsetat" id="formsetat"></a>

Replace form values at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ nested: { data: "foo" } })

form.setAt("nested.data", "bar")
```

## Form.put() <a href="#formput" id="formput"></a>

Add some data to the form by doing a merge:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data1: "foo" })

form.put({ data2: "bar" })
```

{% hint style="info" %}
Unlike [Form.set()](/services/form#formset), this method does not override all the form data, but will rather merge it instead. This method does not track any dirty or changed fields. Use [Form.setAt()](/services/form#formsetat) method, if you want to track dirty and changed fields.
{% endhint %}

## Form.clear() <a href="#formclear" id="formclear"></a>

Reset form values, errors, status indicators, etc. This will reset form values to the initial values (the one that you've passed into the [`createForm()`](/services/form#createform) function):

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })

form.clear()
```

{% hint style="info" %}
You might want to call this after a successful form submission.
{% endhint %}

You can replace initial values while clearing the form:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })

form.clear({ data: "bar" })
```

## Form.getErrors() <a href="#formgeterrors" id="formgeterrors"></a>

Get all form errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const errors = form.getErrors()
```

## Form.getErrorsAt() <a href="#formgeterrorsat" id="formgeterrorsat"></a>

Get all errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ nested: { data: "foo" } })
const errors = form.getErrors("nested.data")
```

## Form.setErrors() <a href="#formseterrors" id="formseterrors"></a>

Replace all errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setErorrs({ field: ["first error", "second error"] })
```

## Form.setErrorsAt() <a href="#formseterrorsat" id="formseterrorsat"></a>

Replace all errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setErrorsAt("nested.field", ["first error", "second error"])
```

## Form.addErrors() <a href="#formadderrors" id="formadderrors"></a>

Add some new errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.addErrors({ field: ["first error", "second error"] })
```

## Form.addErrorsAt() <a href="#formadderrorsat" id="formadderrorsat"></a>

Add some new errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.addErrorsAt("nested.field", ["first error", "second error"])
```

## Form.hasErrors() <a href="#formhaserrors" id="formhaserrors"></a>

Check if there are any errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const hasErrors = form.hasErrors()
```

## Form.hasErrorsAt() <a href="#formhaserrorsat" id="formhaserrorsat"></a>

Check if there are any errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const hasErrors = form.hasErrorsAt("nested.field")
```

## Form.clearErrors() <a href="#formclearerrors" id="formclearerrors"></a>

Clear all errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearErrors()
```

## Form.clearErrorsAt() <a href="#formclearerrorsat" id="formclearerrorsat"></a>

Clear all errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearErrorsAt("nested.field")
```

## Form.isDirty() <a href="#formisdirty" id="formisdirty"></a>

Check if any of the form fields are dirty (have been written to):

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isDirty = form.isDirty()
```

{% hint style="info" %}
Works only for fields that have been set using the [Form.setAt()](/services/form#formsetat) method. Methods like [Form.set()](/services/form#formset) or [Form.put()](/services/form#formput) do not track this kind of changes.
{% endhint %}

## Form.isDirtyAt() <a href="#formisdirtyat" id="formisdirtyat"></a>

Check if a specific field is dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isDirty = form.isDirtyAt("nested.field")
```

## Form.getDirty() <a href="#formgetdirty" id="formgetdirty"></a>

Get a list of all dirty fields:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const dirtyFields = form.getDirty()
```

## Form.setDirtyAt() <a href="#formsetdirtyat" id="formsetdirtyat"></a>

Tag some fields as dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setDirtyAt(["field", "nested.field"])
```

## Form.clearDirty() <a href="#formcleardirty" id="formcleardirty"></a>

Clear all dirty fields:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearDirty()
```

## Form.clearDirtyAt() <a href="#formcleardirtyat" id="formcleardirtyat"></a>

Clear a specific dirty field:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearDirtyAt("nested.field")
```

## Form.isChanged() <a href="#formischanged" id="formischanged"></a>

Check if any of the form fields have been changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isChanged = form.isChanged()
```

{% hint style="info" %}
Contrary to the [Form.isDirty()](/services/form#formisdirty) method, for a field to be tracked as changed, its new value has to be different from its initial value provided during the form creation. This only works for fields that have been changed using the [Form.setAt()](/services/form#formsetat) method. Methods like [Form.set()](/services/form#formset) and [Form.put()](/services/form#formput) do not track this kind of changes.
{% endhint %}

## Form.isChangedAt() <a href="#formischangedat" id="formischangedat"></a>

Check if a specific field has been changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isChanged = form.isChangedAt("nested.field")
```

## Form.getChanged() <a href="#formgetchanged" id="formgetchanged"></a>

Get all fields that have been changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const changedFields = form.getChanged()
```

## Form.setChangedAt() <a href="#formsetchangedat" id="formsetchangedat"></a>

Track some fields as changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setChangedAt(["field", "nested.field"])
```

## Form.clearChanged() <a href="#formclearchanged" id="formclearchanged"></a>

Clear all changed fields:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearChanged()
```

## Form.clearChangedAt() <a href="#formclearchangedat" id="formclearchangedat"></a>

Clear a specific changed field:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearChangedAt("nested.field")
```

## Form.getResult() <a href="#formgetresult" id="formgetresult"></a>

Get the result that has been returned from the handler function during the last form submission:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const result = form.getResult()
```

{% hint style="info" %}
Form handler result is also returned directly from the [Form.submit()](/services/form#formsubmit) method after invocation.
{% endhint %}

## Form.setResult() <a href="#formsetresult" id="formsetresult"></a>

Manually replace form result:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setResult({ some: "data" })
```

## Form.clearResult() <a href="#formclearresult" id="formclearresult"></a>

Clear form result:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearResult()
```

## Form.isSubmitting() <a href="#formissubmitting" id="formissubmitting"></a>

Check if form is currently being submitted:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isSubmitting = form.isSubmitting()
```

{% hint style="info" %}
Form enters this state whenever you invoke the [Form.submit()](/services/form#formsubmit) method.
{% endhint %}

## Form.setIsSubmitting() <a href="#formsetsubmitting" id="formsetsubmitting"></a>

Manually change form status:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setIsSubmitting(true)
```

{% hint style="info" %}
Usually you don't have to do this manually.
{% endhint %}

## Form.isSubmitted() <a href="#formissubmitted" id="formissubmitted"></a>

Check if form has been successfully submitted at least once:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isSubmitted = form.isSubmitted()
```

{% hint style="info" %}
Form enters this state after a successful form submission (one that did not cause any validation errors nor did throw an exception from the form handler).
{% endhint %}

## Form.setIsSubmitted() <a href="#formsetsubmitted" id="formsetsubmitted"></a>

Manually change form status:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setIsSubmitted(true)
```

{% hint style="info" %}
Usually you don't have to do this manually.
{% endhint %}

## Form.getDeps() <a href="#formdeps" id="formdeps"></a>

Returns a list of **dependencies** for any specific field. This is useful whenever you need to calculate whether an input field should be re-rendered or not, for example inside [`useMemo()`](https://reactjs.org/docs/hooks-reference.html#usememo), [`useCallback()`](https://reactjs.org/docs/hooks-reference.html#usecallback) or even a [`useEffect()`](https://reactjs.org/docs/hooks-reference.html#useeffect).

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const dependenciesForOneField = form.getDeps("some.field")
const dependenciesForMultipleFields = form.getDeps(["some.field", "another.field"])
```

You can customise what kind of form state should be considered a dependency:

```typescript
form.getDeps("some.field", {  
    // form config (default: true)
    config: true,  
    // value of that specific field (default: true)
    value: true,  
    // dirty fields (default: true)
    isDirty: true,  
    // changed fields config (default: true)
    isChanged: true,  
    // isSubmitting form status (default: true)
    isSubmitting: true,  
    // isSubmitted form status (default: true)
    isSubmitted: true,  
    // errors of that specific field (default: true)
    errors: true,  
    // form result (default: true)
    result: true,
})
```

## Form.getFields() <a href="#formfields" id="formfields"></a>

Returns an accessor object that can be used to get a handle for an individual form field. Each form field is represented by an instance of `FormField`. The main purpose of this approach is to be able to statically access fields, without having to rely on dynamic string based keys.

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { nested: { field: "value" } } })
const fields = form.getFields()

fields.some.nested.field.get().setValue("new value")
// same as
form.setAt("some.nested.field", "new value")
// or even
form.setAt(fields.some.nested.field.key(), "new value")
```

Check out the package docs to learn more about the library used behind the scenes:

{% content-ref url="/pages/-MeeFDZTgmmurCkKtNA3" %}
[Accessor](/services/accessor)
{% endcontent-ref %}

## FormField.getValue() <a href="#formfieldgetvalue" id="formfieldgetvalue"></a>

Get field value:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.getValue()
// same as
form.getAt("some.field")
```

## FormField.setValue() <a href="#formfieldsetvalue" id="formfieldsetvalue"></a>

Set field value:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setValue("new value")
// same as
form.getAt("some.field", "new value")
```

## FormField.getKey() <a href="#formfieldgetkey" id="formfieldgetkey"></a>

Get absolute field key:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.getKey()
```

## FormField.getErrors() <a href="#formfieldgeterrors" id="formfieldgeterrors"></a>

Get field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.getErrors()
// same as
form.getErrorsAt("some.field")
```

## FormField.setErrors() <a href="#formfieldseterrors" id="formfieldseterrors"></a>

Override field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setErrors("new error")
// same as
form.setErrorsAt("some.field", "new error")
```

## FormField.hasErrors() <a href="#formfieldhaserrors" id="formfieldhaserrors"></a>

Check if field has any errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.hasErrors()
// same as
form.hasErrorsAt("some.field")
```

## FormField.addErrors() <a href="#formfieldadderrors" id="formfieldadderrors"></a>

Add some field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.addErrors("new error")
// same as
form.addErrorsAt("some.field", "new error")
```

## FormField.clearErrors() <a href="#formfieldclearerrors" id="formfieldclearerrors"></a>

Clear field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.clearErrors()
// same as
form.clearErrorsAt("some.field")
```

## FormField.isDirty() <a href="#formfieldisdirty" id="formfieldisdirty"></a>

Check if field is dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.isDirty()
// same as
form.isDirtyAt("some.field")
```

## FormField.setDirty() <a href="#formfieldsetdirty" id="formfieldsetdirty"></a>

Tag field as dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setDirty()
// same as
form.setDirtyAt("some.field")
```

## FormField.clearDirty() <a href="#formfieldcleardirty" id="formfieldcleardirty"></a>

Clear dirty status of this field:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.clearDirty()
// same as
form.clearDirtyAt("some.field")
```

## FormField.isChanged() <a href="#formfieldischanged" id="formfieldischanged"></a>

Check if field is changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.isChanged()
// same as
form.isChangedAt("some.field")
```

## FormField.setChanged <a href="#formfieldsetchanged" id="formfieldsetchanged"></a>

Tag field as changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setChanged()
form.setChangedAt("some.field")
```

## FormField.clearChanged() <a href="#formfieldclearchanged" id="formfieldclearchanged"></a>

Clear changed status of this field:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.clearChanged()
form.clearChangedAt("some.field")
```

## FormField.getForm() <a href="#formfieldgetform" id="formfieldgetform"></a>

Get the `Form` instance attached to this field:

```typescript
const form = field.getForm()
```


# Translator

Customisable translator for backend and frontend usage with a statically typed translations facade.

Source code is hosted on [GitHub](https://github.com/corets/schema)

* Works for **server side and client side** translations handling
* Features **static translations access**, without using string based keys
* Extremely **easy to use and customise**
* Not bloated with unnecessary features

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/translator
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/translator
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="/pages/-MeeFD9vXhGU6geoh9vx" %}
[useTranslator](/hooks/use-translator)
{% endcontent-ref %}

## Static translation access

Translations can be accessed in a static manner thanks to the [`createTranslatorAccessor()`](/services/translator#createtranslatoraccessor) helper. Check out [@corets/use-translator](/hooks/use-translator#uselocales) docs for an example usage in React.

## Custom interpolations <a href="#custom-interpolations" id="custom-interpolations"></a>

Provide a custom interpolation function used for replacement of placeholders with values:

```typescript
import { createTranslator } from "@corets/translator"

const customInterpolator = (text: string, match: string, replacement: any) => {  
    return text.replace(`[[${match}]]`, replacement)
}
const translator = createTranslator({}, { interpolator: customInterpolator })
```

You can access the default interpolation function anytime:

```typescript
import { translatorInterpolator } from "@corets/translator"
```

## Custom formatting <a href="#custom-formatting" id="custom-formatting"></a>

Provide a custom formatter function used to format replacements before interpolation:

```typescript
import { createTranslator, translatorFormatter } from "@corets/translator"
import dayjs from "dayjs"

const customFormatter = (language: string, replacement: any, replacements: object) => {  
    if (replacement instanceof Date) {    
        const format = replacements?.format ?? "DD.MM.YYYY"    
        
        return dayjs(replacement).format(format)  
    }  
    
    return defaultTranslatorFormatter(language, replacement, replacements)
} 

const translator = createTranslator({}, { formatter: customFormatter })
```

## Custom placeholders <a href="#custom-placeholder-for-missing-keys" id="custom-placeholder-for-missing-keys"></a>

Provide a custom placeholder function used to generate placeholders for missing translation keys:

```typescript
import { createTranslator } from "@corets/translator"

const customPlaceholder = (language: string, key: string, replacements: object) => `${language}.${key}`

const translator = createTranslator({}, { placeholder: customPlaceholder })
```

You can access the default placeholder function anytime:

```typescript
import { translatorPlaceholder } from "@corets/translator"
```

## createTranslator() <a href="#createtranslator" id="createtranslator"></a>

Create a new `Translator` instance:

```typescript
import { createTranslator } from "@corets/translator"

const translations = {  
    en: { title: "Welcome" },  
    de: { title: "Willkommen" }
}

const translator = createTranslator(translations, { language: "en" })
```

Create a translator instance without the factory function:

```typescript
import { Translator } from "@corets/translator"

const translator = new Translator({}, { language: "en" })
```

## createTranslatorAccessor() <a href="#createtranslatoraccessor" id="createtranslatoraccessor"></a>

Create a statically typed facade for your translations. This allows you to access translations in a statically typed manner, no need to use string based translation keys anymore. Trying to access missing translations will lead to a compilation error! 💥

```typescript
import { createTranslator, createTranslatorAccessor } from "@corets/translator"

const translations = {  
    en: { some: { nested: "value", another: "Hello {greeting}"} },  
    de: { some: { nested: "other"} },
}

const translator = createTranslator(translations, { language: "en" })
const locales = createTranslatorAccessor(translator, translations.en)

locales.some.nested.get()
locales.some.another.get({ replace: { greeting: "World" } })
locales.some.nested.get({ language: "de" })
```

This functionality is powered by the accessor library:

{% content-ref url="/pages/-MeeFDZTgmmurCkKtNA3" %}
[Accessor](/services/accessor)
{% endcontent-ref %}

## Translator.config() <a href="#translatorconfig" id="translatorconfig"></a>

Configure translator:

```typescript
import { createTranslator } from "@corets/translator"

const translator = createTranslator({ ... }, { language: "en" })

translator.config({  
    language: "en",  
    fallbackLanguage: "de",  
    interpolate: true,  
    formatter,  
    interpolator,  
    placeholder,
})
```

## Translator.getLanguage() <a href="#translatorgetlanguage" id="translatorgetlanguage"></a>

Get current language:

```typescript
translator.getLanguage()
```

## Translator.setLanguage() <a href="#translatorsetlanguage" id="translatorsetlanguage"></a>

Change current language:

```typescript
translator.setLanguage("en")
```

## Translator.getLanguages() <a href="#translatorgetlanguages" id="translatorgetlanguages"></a>

Get all registered languages:

```typescript
translator.getLanguages()
```

## Translator.getFallbackLanguage() <a href="#translatorgetfallbacklanguage" id="translatorgetfallbacklanguage"></a>

Get fallback language:

```typescript
transaltor.getFallbackLanguage()
```

{% hint style="info" %}
A fallback language is used whenever a translation key is missing for current language.
{% endhint %}

## Translator.setFallbackLanguage() <a href="#translatorsetfallbacklanguage" id="translatorsetfallbacklanguage"></a>

Change fallback language:

```typescript
translator.setFallbackLanguage("de")
```

## Translator.getTranslations() <a href="#translatorgettranslations" id="translatorgettranslations"></a>

Get all registered translations:

```typescript
translator.getTranslations()
```

Returned object looks something like this:

```typescript
{  
    en: { key: "value" },  
    de: { key: "value" },
}
```

## Translator.getTranslationsForLanguage() <a href="#translatorgettranslationsforlanguage" id="translatorgettranslationsforlanguage"></a>

Get all translations for a specific language:

```typescript
translator.getTranslationsForLanguage("en")
```

The returned object looks something like this:

```typescript
{ key: "value" }
```

## Translator.setTranslations() <a href="#translatorsettranslations" id="translatorsettranslations"></a>

Replace all translations, for all languages, with the new ones:

```typescript
translator.setTranslations({ en: { key: "value" }})
```

## Translator.setTranslationsForLanguage() <a href="#translatorsettranslationsforlanguage" id="translatorsettranslationsforlanguage"></a>

Replace all translations for a specific language:

```typescript
translator.setTranslationsForLanguage("en", { key: "value" })
```

## Translator.addTranslations() <a href="#translatoraddtranslations" id="translatoraddtranslations"></a>

Update all translations, in all languages, using a merge:

```typescript
translator.addTranslations({ de: { key: "value" } })
```

{% hint style="info" %}
Contrary to the [Translator.setTranslations()](/services/translator#translatorsettranslations) method, this one will not replace all translations but do a merge instead.
{% endhint %}

## Translator.addTranslationsForLanguage() <a href="#translatoraddtranslationsforlanguage" id="translatoraddtranslationsforlanguage"></a>

Add translations, for a specific language, using a merge:

```typescript
translator.addTranslationsForLanguage("en", { key: "value" })
```

{% hint style="info" %}
Contrary to the [Translator.setTranslationsForLanguage()](/services/translator#translatorsettranslationsforlanguage) method, this one will not replace all translations but do a merge instead.
{% endhint %}

## Translator.get() <a href="#translatorget" id="translatorget"></a>

Retrieve a translation:

```typescript
translator.get("key")
```

Retrieve a translation with a nested key:

```typescript
translator.get("nested.key")
```

Retrieve translation for another language:

```typescript
translator.get("key", { language: "de" })
```

Retrieve translation with another fallback language:

```typescript
translator.get("key", { fallbackLanguage: "de" })
```

Retrieve translation without interpolating it:

```typescript
translator.get("key", { interpolate: false })
```

Retrieve a translation and interpolate some values, using array syntax:

```typescript
// given translation: {{1}} and {{2}} are colors
translator.get("key", ["red", "blue"])
```

Retrieve a translation and interpolate some values, using object syntax:

```typescript
// given translations: {{color1}} and {{color2}} are colors
translator.get("key", { color1: "red", color2: "blue" })
```

Retrieve translation with a custom formatter, interpolator and placeholder:

```typescript
translator.get("key", { formatter, interpolator, placeholder })
```

{% hint style="info" %}
Check out documentation for [formatter](/services/translator#custom-formatting), [interpolator](/services/translator#custom-interpolations), and [placeholder](/services/translator#custom-placeholder-for-missing-keys).
{% endhint %}

## Translator.has() <a href="#translatorhas" id="translatorhas"></a>

Check if translation exists:

```typescript
translator.has("key")
```

Check if translation exists for a specific language:

```typescript
translator.has("key", { language: "en" })
```

Check if translation exists in the current language or in a specific fallback language:

```typescript
translator.has("key", { fallbackLanguage: "de" })
```

## Translator.t() <a href="#translatort" id="translatort"></a>

Create a standalone translation function:

```typescript
const t = translator.t()

t("key")

// same as
translator.get("key")
```

Create a translation function for a specific scope:

```typescript
const t = translator.t("nested.scope")

t("key")

// same as
translator.get("nested.scope.key")
```

Override scope with `~`:

```typescript
const t = translator.t("nested.key")

t("~key")

// same as
translator.get("key")
```

{% hint style="info" %}
This method supports the same options object as [Translator.get()](/services/translator#translatorget).
{% endhint %}

## Translator.listen() <a href="#translatorlisten" id="translatorlisten"></a>

Listen to any kind of changes, like language change, translations change, etc.:

```typescript
const unsubscribe = translator.listen(() => console.log("something has changed"))

unsubscribe()
```


# \<Router/>

Next-generation router for React tailored towards complex SPA use-cases, makes your application feel like it's been rendered on the server-side.

Source code is hosted on [GitHub](https://github.com/corets/router)

A demo can be found on [CodeSandbox](https://codesandbox.io/s/corets-router-demo-cvq4o?file=/src/App.tsx)

This project was heavily inspired by [react-router](https://reactrouter.com/web/guides/quick-start) and a lot of attention has been paid to keep the API very similar, therefore the transition will be quick and hassle-free. This router implementation offers many convenient features that are usually too hard to implement in regular single-page applications. All of these features are on-demand, if you don't need them, don't use them. Out of the box, this router behaves exactly like the react-router. By adding some additional flags, your app starts to feel totally different. The UX increases drastically, your routes feel like they are being rendered on the server-side.

* Declarative routing inside React
* Nested routing
* Async loading of components
* Supports route loaders
* Supports route unloaders
* Routes feel like they've been rendered on the server
* Controlled mode for premature rendering
* Hooks for various use cases
* Easy URL query manipulation
* Switch, Redirect, Link, and other goodies
* Uses [history (v5)](https://github.com/remix-run/history#readme) for maximal compatibility with other libraries

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/router
```

{% endtab %}

{% tab title="npm" %}

```
npm instal --save @corets/router
```

{% endtab %}
{% endtabs %}

## Philosophy

This router emerged out of necessity to solve common SPA needs that are incredibly hard / almost impossible to solve without granular control over the router and its transitions. This is why this is not an addon on top of react-router, nor is it a fork. A totally new approach was needed to solve many problems in a developer-friendly way. To fully understand the reasons, let's have a look at the problems every single page application has to live with.

### Code splitting

Imagine you take advantage of code splitting and lazy load your route implementation upon first navigation.&#x20;

To achieve that, you have to wrap your route component into another component to display some sort of a loading overlay. As soon as the implementation has been loaded, you render the actual page.

For a brief moment, you will have to show a loading overlay to indicate the progress. Users will no longer see the previous screen and just stare and the loading overlay until the new route is ready to render. You can immediately tell that this is a SPA based on the bad UX.

Router solves this problem by fetching your route implementation behind the scenes, without unmounting the current route. You can show a loading bar on the top of the page in the meantime. Users will see the previous route plus the loading bar until the new route is ready for the transition. This feels more natural and is also what Google and Facebook do in their applications.

### Route loaders

Imagine a route that renders a table with data, where data is fetched from the server through an API call.

To achieve that, you need to display a global loading overlay until the data has been loaded. You need to manually orchestrate the loading overlay if there are multiple components that you have to wait for. Another trick that many SPAs fall back to is the use of *skeletons* - you render some placeholders that indicate that there is more to come, but the view is not fully ready yet.&#x20;

For a brief moment, you have no data to show and your users have either to stare at the loading overlay or at some placeholders without any meaningful content until the page is fully ready to render.

Router solves this problem by allowing every component to register a loader. It will wait for all loaders to fully resolve prior to doing the transition. You can show a progress bar at the top of the page, your users never have to stare on an empty screen or at placeholder components. Preloaders can be freely registered from any component that is being rendered inside the new route. You don't need to orchestrate anything manually. Adopting this pattern is a matter of minutes.

Route loaders can also return an unloader that is triggered when the route is being navigated away from.

### Route unloaders

Imagine your current route triggers an action that opens a modal. Inside that modal, you have a link to another route. By clicking that link, the modal disappears immediately as the transition begins.

Your components don't have the means to do anything prior to transitioning away. The transition starts immediately, the modal simply disappears and new content is shown. It would feel more natural if the modal would gracefully close, maybe with a nice animation, just before the new route is rendered.

Router solves this problem by allowing every component to register an unloader. It will wait for all unloaders to fully resolve prior to doing the transition. You can do whatever is necessary, like sending some data to the server, running animations, etc. Unloading of the current route is part of the route transition life cycle, therefore you can show a progress bar at the top of the page indicating users that the next route is still being loaded.

### Controlled mode

Imagine you try to load another route that needs to fetch some data. Wouldn't it be cool, if you could show a small overlay, covering the currently rendered screen, and providing some additional information to the users, something like "Preparing the rocket to launch", "Sending out the droids", etc.?

This can be achieved thanks to the controlled mode. This mode can be applied to a single route or to all routes at once. This mode allows a route to render content prior to it being fully ready to render / fully loaded.

Router achieves this behaviour by having two routes mounted at the same time, while contrary to the un-controlled mode, the route that is still being in transition will not be hidden, it will be visible side by side with the previous screen. Now it's up to you to decide what you want to show and when. Thankfully there are many convenient hooks that make dealing with the life cycle a piece of cake.

### Life cycle

Let's have a look at what the typical router/route life cycle looks like:

* Mount router
* Mount routes
* Register routes with router
* Figure out active routes
* Load route implementations if necessary
* Mount new routes in hidden mode, render normally in controlled mode
* Wait for route loaders to complete&#x20;
* Wait for route unloaders to complete
* Unmount the previous routes
* Show new routes

If any new routes are registered during the life cycle execution, the router will incorporate them in the transition. While performing transitions, users can be presented with a progress indicator, but they will never lose the currently rendered screen. You don't need to show any skeletons while fetching relevant data. Each route feels like it has been rendered on the server.

### Summary

As you can see, this router implementation is incredibly powerful and flexible. You can have regular routes side by side with routes that preload content, unload gracefully, allow children components to delay transitions while async operations are being executed, render multiple routes side by side in the controlled mode, etc. You can freely mix and match different behaviors based on your requirements.

## Quick start

Basic router setup with some routes:

```typescript
import { render } from "react-dom"
import { Router, Route } from "@corets/router"

render(
  <Router>
    <Route path="/some/path">Content</Route>
  </Router>,
  document.getElementById("root")
)
```

## Patch matching

Paths can be matched in a regular or an exact mode. Path parameters can be matched in different ways using the additional `?`, `+` and `*` modifiers.

```typescript
// matches /some/path/*
<Route path="/some/path" />
```

### Exact mode

Match path in the exact mode:

```typescript
// matches /some/path only
<Route path="/some/path" exact />
```

### Path parameters

Match path parameters that can be retrieved later:

```typescript
// matches /some/123/*, :param is "123"
<Route path="/some/:param" />
```

### Zero or one

Match zero or exactly one path segment:

```typescript
// matches /some/*, :param is "null"
// matches /some/123/*, :param is "123"
<Route path="/some/:param?" />
```

### One or many

Match one or multiple path segments:

```typescript
// matches /some/123/*, :param is "123"
// matches /some/123/456/*, :param is "123/456"
<Route path="/some/:param+" />
```

### Zero or many

Match zero or multiple path segments:

```typescript
// matches /some/*, :param is "null"
// matches /some/123/*, :param is "123"
// matches /some/123/456/*, :param is "123/456"
<Route path="/some/:param*" />
```

## Path parameters

Parameters can be accessed inside a route using the [`useParams()`](/components/router#useparams) and [`useRoute()`](/components/router#useroute) hooks:

```typescript
import { useParams, useRoute } from "@corets/router"

const params = useParams()
// or
const { params } = useRoute()
```

## Conditional routes

Due to the nature of how this router implementation works, it is considered a bad practice to mount and unmount routes conditionally:

* Manually unmounting a route bypasses it's unloaders
* Conditionally mounting routes inside a [`<Switch />`](/components/router#less-than-switch-greater-than) component might lead to unexpected behaviour

Don't panic, we've got you covered! You can use the [`<Group />`](/components/router#less-than-group-greater-than), [`<Switch />`](/components/router#less-than-switch-greater-than) or [`<Route />`](/components/router#less-than-route-greater-than) components whenever you plan to toggle available routes at runtime:

```typescript
import { render } from "react-dom"
import { Route, Router, Group } from "@corets/router"

render(
  <Router>
    <Group disabled={isAuthenticated}>
      <Switch>
        <Route ... />
        <Route ... />
      </Switch>
    </Group>
  
    <Switch enabled={isAuthenticated}>
      <Route ... />
      <Route ... disabled={isAuthenticated} />
    </Switch>
  </Router>
) 
```

## \<Router />

All routes must be wrapped inside this component:

```typescript
import { render } from "react-dom"
import { Router, Route } from "@corets/router"

render(
  <Router>
    <Route>This route is always rendered</Route>
    <Route path="/some/path">Renders only if path matches</Route>
  </Router>,
  document.getElementById("root")
)
```

### Base path

Define a base path for the router:

```typescript
<Router base="/custom/base/path" />
```

### Loaders and unloaders

Enable loaders and unloaders for all routes:

```typescript
<Router loadable unloadable />
```

Enable loaders and unloaders for a specific route:

```typescript
<Route loadable unloadable />
```

### Loader and unloader threshold

Define how long the router waits for loaders and unloaders to register, default is `5ms`:

```typescript
<Router wait={10} />
```

### Debugging

Enable detailed life cycle logs:

```typescript
<Router debug />
```

### Custom history

Pass a custom [history](https://github.com/remix-run/history#readme) instance, this is useful for testing or when working with other libraries:

```typescript
<Router history={history} />
```

### Testing

During the tests you might want to provide a history instance pointing to a specific location:

```typescript
import { render } from "@testing-library/react"
import { Router, createTestHistory } from "@corets/router"

const testHistory = createTestHistory("/test/path")

render(<Router history={testHistory}>...</Router>)
```

## \<Route />

The Route component is used to connect your components to a specific location path:

```typescript
import { render } from "react-dom"
import { Router, Route } from "@corets/router"

render(
  <Router>
    <Route path="/some/path">Content</Route>
  </Router>,
  document.getElementById("root")
)
```

### Render function

Provide a custom render function:

```typescript
<Route render={() => <div>Content</div>} />
```

### Render component

Provide a component to render:

```typescript
<Route render={SomeComponent} />
```

### Async component

Load component implementation asynchronously:

```typescript
<Route load={() => import("./some/path/MyComponent").then(m => m.MyComponent)} />
```

### Async render function

Provide a render function with some async logic:

```typescript
<Route load={async () => {
  const something = await doSomething()
  
  return <div>{something}</div>
}} />
```

### Route path

Specify a path for the route:

```typescript
<Route path="/some/path" />
```

Route without a path will always match:

```typescript
<Route>404</Route>
```

### Exact path

Match route path in the exact mode:

```typescript
<Route path="/some/path" exact />
```

### Absolute path

When nesting routes, you might want to provide an absolute route path instead of the relative one:

```typescript
// matches /foo/*
<Route path"/foo">
  
  // matches /foo/bar/*
  <Route path="/foo/bar" absolute />
  
  // matches /foo/foo/bar*
  <Route path="/foo/bar" />
</Route>
```

### Nested routes

Routes can be freely nested inside each other, parent route's path is automatically used as the path prefix:

```typescript
<Route>
  Main content
  
  <Route path="/foo">Other content</Route>
  <Route path="/bar">Other content</Route>
</Route>
```

### Loaders

You can add a loader by using the [`useRouteLoader()`](/components/router#userouteloader) hook:

```typescript
const MyComponent = () => {
  useRouteLoader(async () => {
    // load relevant data...
    
    // optionally return an unloader
    return async () => {
      // unloading logic...
    }
  })
  
  return <div>Content</div>
}

<Route loadable render={ MyComponent } />
```

{% hint style="info" %}
You have to set the *loadable* property on the route or the router to enable this feature. Make sure to also set the *unloadable* property on the route or the router if you want to return an unloader from the useRouteLoader hook.
{% endhint %}

### Unloaders

You can add an unloader by using the [`useRouteUnloader()`](/components/router#userouteunloader) hook.

```typescript
const MyComponent = () => {
  useRouteUnloader(async () => {
    // do your thing
  })

  return <div>Content</div>
}

<Route unloadable render={ MyComponent } />
```

{% hint style="info" %}
You have to set the *unloadable* property on the route or the router to enable this feature.
{% endhint %}

## \<Link />

Navigate to another route by clicking a link.

```typescript
import { render } from "react-dom"
import { Link } from "@corets/router"

render(
  <Link to="/route/path" />, 
  document.getElementById("root")
)
```

### Active links

Links that match the current path, have the property `data-active` set to `true`. You can change the styling of active links using this simple CSS rule:

```css
a[data-active] {
  // ....
}
```

### Exact links

You can narrow down what paths will be considered as matching, by setting the `exact` property. Check the [path matching](/components/router#patch-matching) section for more details.

```typescript
render(
  <Link to="/route/path" exact />, 
  document.getElementById("root")
)
```

### Modifier keys

By default, links respect the  `ctrl+click`, `alt+click` and `cmd+click` events as well as the `target` property. Links clicked using a modifier key will use the default browser behavior instead of triggering the normal navigation. This can be disabled by setting the `intercept` property to `false`:

```typescript
<Link to="/route/path" intercept={false} />
```

## \<Switch />

This component will render the first route that matches the current path, it accepts the same props as the [`<Group/>`](/components/router#less-than-group-greater-than) component:

```typescript
import { render } from "react-dom"
import { Switch, Route } from "@corets/router"

render(
  <Switch>
    <Route path="/foo">Some content</Route>
    <Route path="/bar">Other content</Route>
    <Route>404</Route>
  </Switch>,
  document.getElementById("root")
)
```

## \<Group />

This component can be used to apply specific settings to all of its child routes. The `disabled` flag is especially useful whenever you need to disable some routes without having to unmount them:

```typescript
import { render } from "react-dom"
import { Switch, Route } from "@corets/router"

render(
  <Group
    disabled
    loadable
    unloadable
    controlled
  >
    <Route ... />
    <Route ... />
    <Route ... />
  </Group>
)
```

## \<Redirect />

This component will trigger a redirect to another path upon render:

```typescript
import { render } from "react-dom"
import { Redirect } from "@corets/router"

render(
  <Redirect to="/foo" />,
  document.getElementById("root")
)
```

## useRouter()

Retrieves router handle from the context, it exposes some useful methods and properties:

```typescript
import { useRouter } from "@corets/router"

const router = useRouter()
```

### Detect loading

Check if the router is loading anything anywhere on the page, same as the [`useRouterIsLoading()`](/components/router#userouterisloading) hook:

```typescript
router.isLoading()
```

### Detect unloading

Check if the router is unloading anything anywhere on the page, same as the [`useRouterIsUnloading()`](/components/router#userouterisunloading) hook:

```typescript
router.isUnloading()
```

### Detect visibility

Check if the router is showing anything anywhere on the page, same as the [`useRouterIsVisible()`](/components/router#userouterisvisible) hook:

```typescript
router.isVisible()
```

### Redirects

You can use the router instance to redirect to another path, same as the [`useRedirect()`](/components/router#useredirect) hook:

```typescript
router.redirect("/some/path")
```

## useRoute()

Retrieves a route handle from the context, it exposes some useful methods and properties:

```typescript
import { useRoute } from "@corets/router"

const route = useRoute()
```

### Detect loading

Check if the route is loading, same as the [`useRouteIsLoading()`](/components/router#userouteisloading) hook:

```typescript
route.isLoading()
```

### Detect unloading

Check if the route is unloading, same as the [`useRouteIsUnloading()`](/components/router#userouteisunloading) hook:

```typescript
route.isUnloading()
```

### Detect visibility

Check if the route is visible same as the [`useRouteIsVisible()`](/components/router#userouteisvisible) hook:

```typescript
route.isVisible()
```

### Redirects

Route handle can be used to redirect to another path, same as [`useRedirect()`](/components/router#useredirect) hook:

```typescript
route.redirect("/some/path")
```

### Parameters

Route parameters can be accessed directly through the route handle,  same as the [`useParams()`](/components/router#useparams) hook:

```typescript
route.params
```

### Query

Route query can be accessed directly through the route handle, there is also the [`useQuery()`](/components/router#usequery) hook that can also be used to modify the query:

```typescript
route.query
```

### Status

Route status can be accessed directly through the route handle, same as the [`useRouteStatus()`](/components/router#useroutestatus) hook:

```typescript
route.status
```

## useRoutes()

Returns an object with all the routes that were detected by the router:

```typescript
import { useRouteLoader } from "@corets/router"

const routes = useRoutes()
```

## useRouteLoader()

Route loaders can be used to preload data prior to triggering the route transition:

```typescript
import { useRouteLoader } from "@corets/router"

useRouteLoader(async () => {
  // do your thang
  
  // optionally return an unloader...
  return async () => {
    // unloading logic
  }
})
```

Make sure to also set the *unloadable* property on the route or the router if you want to return an unloader from the useRouteLoader hook.&#x20;

You can also create a route loader without the callback and resolve it manually:

```typescript
import { useEffect } from "react"
import { useRouteLoader, useRouteIsLoading, RouteStatus } from "@corets/router"

const isRouteLoading = useRouteIsLoading()
const routeLoader = useRouteLoader()

useEffect(() => {
  if (isRouteLoading) {
    // do your thang
    routeLoader.done()
  }
}, [isRouteLoading])
```

Check if this particular loader is running:

```typescript
routeLoader.isRunning()
```

## useRouteUnloader()

Route unloaders can be used to run some logic prior to the route is being unmounted:

```typescript
import { useRouteUnloader } from "@corets/router"

useRouteUnloader(async () => {
  // do your thang
})
```

You can also create a route unloader without the callback and resolve it manually:

```typescript
import { useEffect } from "react"
import { useRouteUnloader, useRouteIsUnloading, RouteStatus } from "@corets/router"

const isRouteUnloading = useRouteIsUnloading()
const routeUnloader = useRouteUnloader()

useEffect(() => {
  if (isRouteUnloading) {
    // do your thang
    routeUnloader.done()
  }
}, [isRouteUnloading])
```

Check if this particular unloader is running:

```typescript
routeUnloader.isRunning()
```

## useRouteStatus()

Get status of the current route:

```typescript
import { useRouteStatus } from "@corets/router"

const routeStatus = useRouteStatus()
```

## useRouterIsLoading()

Check if the router is loading anything anywhere on the page:

```typescript
import { useRouterIsLoading } from "@corets/router"

const isLoading = useRouterIsLoading()
```

## useRouterIsUnloading()

Check if the router is unloading anything anywhere on the page:

```typescript
import { useRouterIsUnloading } from "@corets/router"

const isUnloading = useRouterIsUnloading()
```

## useRouterIsVisible()

Check if the router is showing anything anywhere on the page:

```typescript
import { useRouterIsShowing } from "@corets/router"

const isShowing = useRouterIsShowing()
```

## useRouteIsLoading()

Check if the route is loading:

```typescript
import { useRouteIsLoading } from "@corets/router"

const isLoading = useRouteIsLoading()
```

## useRouteIsUnloading()

Check if the route is unloading:

```typescript
import { useRouteIsUnloading } from "@corets/router"

const isUnloading = useRouteIsUnloading()
```

## useRouteIsVisible()

Check if the route is visible:

```typescript
import { useRouteIsShowing } from "@corets/router"

const isLoading = useRouteIsShowing()
```

## useHistory()

Returns [history](https://github.com/remix-run/history#readme) instance from the context:

```typescript
import { useHistory } from "@corets/router"

const history = useHistory()
```

## useLocation()

Returns current location object:

```typescript
import { useLocation } from "@corets/router"

const location = useLocation()
```

## useMatch()

Matches current location against the given pattern:

```typescript
import { useMatch } from "@corets/router"

const [matches, params] = useMatch("/some/path/:id")
```

## useParams()

Retrieve route params:

```typescript
import { useParams } from "@corets/router"

const params = useParams()
```

Retrieve with default parameters:

```typescript
const params = useParams({ some: "value" })
```

## useQuery()

Retrieve a query handle that can be used to read and write data into the query part of the URL. By providing default values for each query part, you define a list of query fields that you want to control. You won't be able to read or modify query parts that are not part of this list. This allows different components to work with different parts of the query without having to worry about each other's reads and writes:

```typescript
import { useQuery } from "@corets/router"

// given this query: foo=value1 & baz=value2
const query = useQuery({ 
  foo: "fallback value", 
  bar: "fallback value"
})

// returns { foo: "value1", bar: "fallback value" }
query.get()
```

You can update a part of the query, and preserve the previous values, this operation will never modify query parts that are not part of the list with the default values:

```typescript
// given this query: foo=value1 & baz=value2
const query = useQuery({
  foo: "fallback value",
  bar: "fallback value"
})

// query becomes: foo=value1 & bar=value3 & baz=value2
query.put({ bar: "value3" })

// returns { foo: "value1", bar: "value3" }
query.get()
```

You can override the whole query, query parts that were omitted will be stripped from the final query, this operation will never modify query parts that are not part of the list with the default values. Omitted query pieces will be replaced with the default value:

```typescript
// given this query: foo=value1 & baz=value2
const query = useQuery({
  foo: "fallback value",
  bar: "fallback value"
})

// query becomes: bar=value1 & baz=value2
query.set({ bar: "value1" })

// returns { foo: "fallback value", bar: "value1" }
query.get()
```

## useRedirect()

Retrieve a redirect function:

```typescript
import { useRedirect } from "@corets/router"

const redirect = useRedirect()

redirect("/some/path")
```

Redirect with query:

```typescript
redirect("/some/path", { query: { some: "value"} })
```

Redirect with hash:

```typescript
redirect("/some/path", { hash: "#hash" })
```

Redirect and preserve query from the current URL:&#x20;

```typescript
redirect("/some/path", { preserveQuery: true })
```

Redirect and preserve hash from the current URL:

```typescript
redirect("/some/path", { preserveHash: true })
```

## usePathWithBase()

Create a path according to the base path that has been configured on the router:

```typescript
import { usePathWithBase } from "@corets/router"

// given router base is /foo
// returns /foo/bar
usePathWithBase("/bar")
```

You can specify a custom base path:

```typescript
usePathWithBase("/bar", "/base")
```

## useQueryStringifier()

Retrieve a helper to turn any object into a URL query string:

```typescript
import { useQueryStringifier } from "@corets/router"

const strigifier = useQueryStringifier()

// returns foo=bar&bar=baz
strigifier({ foo: "bar", bar: "baz" })
```

{% hint style="info" %}
The **?** character is not part of the final query, you have to add it manually.
{% endhint %}

## useQueryParser()

Retrieve a helper to parse a URL query string:

```typescript
import { useQueryParser } from "@corets/router"

const parser = useQueryParser()

// returns { foo: "bar", bar: "baz" }
parser("foo=bar&bar=baz")
```


# \<Memo />

React component used to isolate children from unnecessary re-renders.

Source code is hosted on [GitHub](https://github.com/corets/memo)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/memo
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/memo
```

{% endtab %}
{% endtabs %}

## \<Memo /> <a href="#memo-1" id="memo-1"></a>

Memoizes children components to prevent unnecessary re-renders. Children get re-rendered only when one of the fields passed into the `deps` array changes, very similar to how `useEffect`, `useMemo` and `useCallback` work. You can also display the render count for debugging purposes by setting the `showRenders` property to `true`.

```typescript
import React, { useState } from "react"
import { Memo } from "@corets/memo"

const Example = () => {
  const [a, setA] = useState(0)
  const [b, setB] = useState(0)
  const incrementFirstValue = () => setA(a + 1)
  const incrementSecondValue  = () => setB(b + 1)
  
  return (
    <div>
      <div>A: {a}</div>
      <div>B: {b}</div>
      
      <Memo deps={[b]} showRenders>
        <div>Memo: will only re-render when <code>b</code> changes</div>
        <div>A: {a}</div>
        <div>B: {b}</div>
      </Memo>
      
      <button onClick={incrementFirstValue}>Increment A</button>
      <button onClick={incrementSecondValue}>Increment B</button>
    </div>
  )
}
```


# Async

Abstraction for async operations that can be hooked into React.

Source code is hosted in [GitHub](https://github.com/corets/async)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/async
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/async
```

{% endtab %}
{% endtabs %}

Seamless React integrations are shipped inside separate packages:

{% content-ref url="/pages/-MfJ4qThZ0llSH30K7PI" %}
[useAsync](/hooks/use-async)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeFDXYVzHIrXuHDnFE" %}
[useStream](/hooks/use-stream)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeFDVtWtL6cM6Te5\_Y" %}
[useAction](/hooks/use-action)
{% endcontent-ref %}

## createAsync()

Create an observable async operation:

```typescript
import { createAsync } from "@corets/async"

const query = createAsync(async () => "some data")
```

Create an observable async operation without the factory function:

```typescript
import { Async } from "@corets/async"

const query = new Async(async () => "some data")
```

## Async.getResult()

Retrieve result from the last invocation:

```typescript
query.getResult()
```

## Async.getError()

Retrieve probable error from the last invocation:

```typescript
query.getError()
```

## Async.getState()

Retrieve all the details in a single call, contains values like `isRunning`, `isCancelled`, `result`, etc.:

```typescript
query.getState()
```

## Async.isRunning()

Check if the async operation is being executed right now:

```typescript
query.isRunning()
```

## Async.isCancelled()

Check if the latest async invocation has been cancelled:

```typescript
query.isCancelled()
```

## Async.isErrored()

Check if the latest async invocation errored:

```typescript
query.isErrored()
```

## Async.run()

Invoke async operation:

```typescript
import { createAsync } from "@corets/async"

const query = createAsync(async (argument: number) => "some data")
const queryWithArguments = createAsync(async (argument: number) => `The value is ${argument}`)

const result = await query.run()
const anotherResult = await queryWithArguments.run(1337)
```

## Async.resolve()

Manually resolve async result, makes it available on the instance using [`getResult()`](/observables/async#async-getresult):

```typescript
import { createAsync } from "@corets/async"

const query = createAsync(async (argument: number) => "some data")

const resultFromValue = await query.resolve("another value")
const resultFromPromise = await query.resolve(Promise.resolve("another value"))
const resultFromProducer = await query.resolve(async () => "another value")
```

## Async.cancel()

Cancel current async invocation:

```typescript
query.cancel()
```

## Async.listen()

Subscribe to state changes, the listener receives the same object that you get from [`getState()`](/observables/async#async-getstate):

```typescript
const unsubscribe = query.listen((state) => console.log("async state changed", state))
```


# Value

Simple value that can be hooked into React.

Source code is hosted on [GitHub](https://github.com/corets/value)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/value
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/value
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="/pages/-MeeCjonF5MnSCR9TohM" %}
[useValue](/hooks/use-value)
{% endcontent-ref %}

These packages provide extended functionality and are built on top of this library:

{% content-ref url="/pages/-MeeBQ0F094TDCsvnMbA" %}
[Store](/observables/store)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeBIuqvkAwUjx0ORFs" %}
[List](/observables/list)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeEDpap1iIyRi7d5Py" %}
[Local Storage Value](/observables/local-storage-value)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeEM7lap525qmHR0yU" %}
[Local Storage Store](/observables/local-storage-store)
{% endcontent-ref %}

{% content-ref url="/pages/-MeeEJek1b6Kf63VvtQ5" %}
[Local Storage List](/observables/local-storage-list)
{% endcontent-ref %}

## createValue()

Create a new observable value:

```typescript
import { createValue } from "@corets/value"

const count = createValue(0)
```

Create a new observable value without the factory function:

```typescript
import { Value } from "@corets/value"

const value = new Value(0)
```

Create a new observable value with a custom differ:

```typescript
import { createValue } from "@corets/value"

const differ = (oldValue, newValue) => true
const count = createValue(0, { differ })
```

## Value.get()

Get current value:

```typescript
value.get()
```

## Value.set()

Update current value:

```typescript
value.set("new value")
```

## Value.listen()

Listen to value changes:

```typescript
const unsubscribe = value.listen((value) => console.log(value))

unsubscribe()
```

Invoke listener immediately:

```typescript
value.listen((value) => console.log(value), { immediate: true })
```

Listen with a custom differ:

```typescript
const differ = (oldValue, newValue) => true

value.listen((value) => console.log(value), { differ })
```

## Value.use()

Convenience method for people used to React's `useState` syntax:

```typescript
import { createValue } from "@corets/value"

const [value, setValue] = createValue(1).use()
```


# Store

Simple store that can be hooked into React.

Source code is hosted on [GitHub](https://github.com/corets/store)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/store
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/store
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="/pages/-MeeGEsjHpNC1z04Kqfm" %}
[useStore](/hooks/use-store)
{% endcontent-ref %}

There is a version of this package that syncs directly to the `localStorage`:

{% content-ref url="/pages/-MeeEM7lap525qmHR0yU" %}
[Local Storage Store](/observables/local-storage-store)
{% endcontent-ref %}

## createStore()

Creates a new instance of observable store:

```typescript
import { createStore } from "@corets/store"

const store = createStore({ some: "data" })
```

Create a new instance without the factory function:

```typescript
import { Store } from "@corets/store"

const store = new Store({ some: "data" })
```

Create a new instance with a custom differ:

```typescript
import { createStore } from "@corets/store"

const differ = (oldValue, newValue) => true
const store = createStore({ some: "data" }, { differ })
```

## Store.get() <a href="#storeget" id="storeget"></a>

Retrieve everything from the store:

```typescript
store.get()
```

## Store.set() <a href="#storeset" id="storeset"></a>

Replace everything in the store:

```typescript
store.set({ foo: "bar" })
```

## Store.put() <a href="#storeput" id="storeput"></a>

Update store by merging new and old state:

```typescript
store.put({ some: "data" })
```

## Store.listen() <a href="#storelisten" id="storelisten"></a>

Listen to changes:

```typescript
store.listen((value) => console.log(value))
```

Listen to changes with a custom differ:

```typescript
const differ = (oldValue, newValue) => true

store.listen((value) => console.log(value), { differ })
```

Listen to a subset of data, using a custom mapper:

```typescript
const mapper = {value} => ({  })

store.listen((value) => console.log(value), { mapper })
```

## Store.use() <a href="#storeuse" id="storeuse"></a>

Convenience method for people used to React's `useState` syntax:

```typescript
import { createStore } from "@corets/store"

const [store, setStore] = createStore({ foo: "bar" }).use()
```


# List

Simple list that can be hooked into React.

Source code is hosted on [GitHub](https://github.com/corets/list)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/list
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/list
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="/pages/-MeeGIckpb\_jmyQLy7TY" %}
[useList](/hooks/use-list)
{% endcontent-ref %}

There is a version of this package that syncs directly to the `localStorage`:

{% content-ref url="/pages/-MeeEJek1b6Kf63VvtQ5" %}
[Local Storage List](/observables/local-storage-list)
{% endcontent-ref %}

## createList() <a href="#createlist" id="createlist"></a>

Creates a new instance of observable list:

```typescript
import { createList } from "@corets/list"

const list = createList(["some", "data"])
```

Create a new instance without the factory function:

```typescript
import { List } from "@corets/list"

const list = new List(["some", "data"])
```

Create a new instance with a custom differ:

```typescript
import { createList } from "@corets/list"

const differ = (oldValue, newValue) => true
const list = createList(["some", "data"], { differ })
```

## List.get() <a href="#listget" id="listget"></a>

Retrieve everything from the list:

```typescript
list.get()
```

## List.getAt() <a href="#listgetat" id="listgetat"></a>

Retrieve a value at the specific index:

```typescript
list.getAt(0)
```

## List.set() <a href="#listset" id="listset"></a>

Replace everything in the list:

```typescript
list.set(["mango", "potato"])
```

## List.add() <a href="#listadd" id="listadd"></a>

Append some new values to the list:

```typescript
list.add("oranges", "beans")
```

## List.addAt() <a href="#listaddat" id="listaddat"></a>

Add a value at the specific index:

```typescript
list.addAt(1, "chili")
```

## List.has() <a href="#listhas" id="listhas"></a>

Check if a specific value is in the list:

```typescript
list.has("oranges")
```

## List.hasAt() <a href="#listhasat" id="listhasat"></a>

Check if there is a value at the specific index:

```typescript
list.hasAt(1)
```

## List.remove() <a href="#listremove" id="listremove"></a>

Remove a value from the list:

```typescript
list.remove("beans")
```

## List.removeAt() <a href="#listremoveat" id="listremoveat"></a>

Remove value at the specific index:

```typescript
list.removeAt(1)
```

## List.indexOf() <a href="#listindexof" id="listindexof"></a>

Get index of a specific value:

```typescript
list.indexOf("oranges")
```

## List.filter() <a href="#listfilter" id="listfilter"></a>

Filter values in the list:

```typescript
list.filter((value, index) => true)
```

## List.map() <a href="#listmap" id="listmap"></a>

Map values in the list:

```typescript
list.map((value, index) => newValue)
```

## List.forEach() <a href="#listforeach" id="listforeach"></a>

Iterate over values in the list:

```typescript
list.forEach((value, index) => {})
```

## List.listen() <a href="#listlisten" id="listlisten"></a>

Listen to changes:

```typescript
list.listen(state => console.log(state))
```

Invoke listener immediately:

```typescript
list.listen(state => console.log(state), { immediate: true })
```

Listen with a custom differ:

```typescript
const differ = (oldValue, newValue) => true

list.listen(state => console.log(state), { differ })
```

## List.use() <a href="#listuse" id="listuse"></a>

Convenience method for people used to React's `useState` syntax:

```typescript
import { createList } from "@corets/list"

const [list, setList] = createList([1, 2]).use()
```


# Local Storage Value

Simple observable value that can be subscribed to, in React, using Hooks. State is automatically synced to the localStorage.

Source code is hosted on [GitHub](https://github.com/corets/local-storage-value)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/local-storage-value
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/local-storage-value
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="/pages/-MeeCjonF5MnSCR9TohM" %}
[useValue](/hooks/use-value)
{% endcontent-ref %}

This is a `localStorage` version of this package:

{% content-ref url="/pages/-MeeBGx8X2I9UAm40nt5" %}
[Value](/observables/value)
{% endcontent-ref %}

## Quick Start

Example of how to use this package in React:

```typescript
import React from "react"
import { createLocalStorageValue } from "@corets/local-storage-value"
import { useValue } from "@corets/use-value"

const Example = () => {
  const counter = useValue(() => createLocalStorageValue("storageKey", 1))
  const increment = () => counter.set(counter.get() + 1)
  
  return (
    <div>
      <button onClick={increment}>{counter.get()}</button>
    </div>
  )
}
```

## createLocalStorageValue() <a href="#createlocalstoragevalue" id="createlocalstoragevalue"></a>

Create a new observable value:

```typescript
import { createLocalStorageValue } from "@corets/local-storage-value"

const value = createLocalStorageValue("storageKey", "some data")
```


# Local Storage Store

Simple observable store that can be subscribed to, in React, using Hooks. State is automatically synced to the localStorage.

Source code is hosted on [GitHub](https://github.com/corets/local-storage-store)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/local-storage-store
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/local-storage-store
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="/pages/-MeeGEsjHpNC1z04Kqfm" %}
[useStore](/hooks/use-store)
{% endcontent-ref %}

This is a `localStorage` version of this package:

{% content-ref url="/pages/-MeeBQ0F094TDCsvnMbA" %}
[Store](/observables/store)
{% endcontent-ref %}

## Quick Start <a href="#react" id="react"></a>

Example of how to use this package in React:

```typescript
import React from "react"
import { createLocalStorageStore } from "@corets/local-storage-store"
import { useStore } from "@corets/use-store"

const Example = () => {
  const counterStore = useStore(() => createLocalStorageStore("storageKey", { counter: 0 }))
  const increment = () => counterStore.set({ counter: counterStore.get().counter + 1 })
  return (
    <div>
      <button onClick={increment}>{counterStore.get().counter}</button>
    </div>
  )
}
```

## createLocalStorageStore() <a href="#createlocalstoragestore" id="createlocalstoragestore"></a>

Create a new observable store:

```typescript
import { createLocalStorageStore } from "@corets/local-storage-store"

const store = createLocalStorageStore("storageKey", { some: "data" })
```


# Local Storage List

Simple observable list that can be subscribed to, in React, using Hooks. State is automatically synced to the localStorage.

Source code is hosted on [GitHub](https://github.com/corets/local-storage-list)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/local-storage-list
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/local-storage-list
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="/pages/-MeeGIckpb\_jmyQLy7TY" %}
[useList](/hooks/use-list)
{% endcontent-ref %}

This is a `localStorage` version of this package:

{% content-ref url="/pages/-MeeBIuqvkAwUjx0ORFs" %}
[List](/observables/list)
{% endcontent-ref %}

## Quick Start <a href="#react" id="react"></a>

Example of how to use this package in React:

```typescript
import React from "react"
import { createLocalStorageList } from "@corets/local-storage-list"
import { useList } from "@corets/use-list"

const Example = () => {
  const list = useList(() => createLocalStorageList("storageKey", ["apple", "banana"]))
  const addFruit = () => list.add("pineapple")
  return (
    <div>
      Fruits: {list.get().join(",")}
      <button onClick={addFruit}>Add fruit</button>
    </div>
  )
}
```

## createLocalStorageList() <a href="#createlocalstoragelist" id="createlocalstoragelist"></a>

Create a new observable list:

```typescript
import { createLocalStorageList } from "@corets/local-storage-list"

const list = createLocalStorageList("storageKey", ["some", "data"])
```


# useAsync

React hook for async operations.

Source code is hosted on [GitHub](https://github.com/corets/use-async)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-async
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/use-async
```

{% endtab %}
{% endtabs %}

This library is built on top of this package:

{% content-ref url="/pages/-Mg7Kt8XOfsD4Np5cHGW" %}
[Async](/observables/async)
{% endcontent-ref %}

## useAsync() <a href="#useasync" id="useasync"></a>

Takes a function or an instance of [`Async`](/observables/async#createasync), invokes it immediately, and keeps the state in sync with React:

```typescript
import React from "react"
import { createAsync } from "@corets/async"
import { useAsync } from "@corets/use-async"

const sharedQuery = createAsync(async () => "some global data")
    
const Example = () => {
  const query = useAsync(async () => `Current date is ${new Date().toISOString()}`)
  const globalQuery = useAsync(sharedQuery, ["some", "dependencies"])
  
  const handleReload = () => query.run()
  
  if (query.isRunning()) {
    return <h1>Loading ...</h1>
  }
  
  if (query.isErrored()) {
    return <h1>There was an error :(</h1>
  }
  
  return (
    <div>
      Result: {query.getResult()} 
      <button onClick={handleReload}>Reload</button>
    </div>
  )
}
```

{% hint style="info" %}
Check out [Async](/observables/async) documentation for more details.
{% endhint %}


# useAffect

React hook for async side effects

Source code is hosted on [GitHub](https://github.com/corets/use-affect)

{% tabs %}
{% tab title="yarn" %}

```sh
yarn add @corets/use-affect
```

{% endtab %}

{% tab title="npm" %}

```sh
npm install --save @corets/use-async
```

{% endtab %}
{% endtabs %}

This library is built on top of this package:

{% content-ref url="/pages/-Mg7Kt8XOfsD4Np5cHGW" %}
[Async](/observables/async)
{% endcontent-ref %}

## useAffect()

Takes a function or an instance of [`Async`](/observables/async#createasync), invokes it immediately, and keeps the state in sync with React. Function or Async instance may return an unsubscribe callback that is invoked before the component is unmounted.

This hook is especially useful when working with various subscription based data streams:

```typescript
import React, {useState} from "react"
import { createAsync } from "@corets/async"
import { useAsync } from "@corets/use-async"

const sharedValue = createAsync(async () => new Promise((resolve) => {
  const interval = setInterval(() => {
    console.log(new Date())    
  }, 1000)
  
  return () => clearInterval(interval)
))
    
const Example = () => {
  const [date, setDate] = useState<Date|null>()
  
  useAffect(async () => {
    return new Promise((resolve) => {
      const interval = setInterval(() => {
        setDate(new Date())
      }, 1000)
  
      return () => clearInterval(interval)
    )
  })
  
  return (
    <div>
      Current date is: {date?.toISOString()} 
    </div>
  )
}
```

{% hint style="info" %}
Check out [Async](/observables/async) and [useAsync](/hooks/use-async) documentation for more details and a comparison between those two hooks.
{% endhint %}


# useStream

React hook for repeating async operations.

Source code is hosted on [GitHub](https://github.com/corets/use-stream)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-stream
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-stream
```

{% endtab %}
{% endtabs %}

This library is built on top of these packages:

{% content-ref url="/pages/-Mg7Kt8XOfsD4Np5cHGW" %}
[Async](/observables/async)
{% endcontent-ref %}

{% content-ref url="/pages/-MfJ4qThZ0llSH30K7PI" %}
[useAsync](/hooks/use-async)
{% endcontent-ref %}

## useStream() <a href="#usestream" id="usestream"></a>

Takes a function or an instance of [`Async`](/observables/async) and turns it into a repeating stream of data:

```typescript
import React from "react"
import { createAsync } from "@corets/async"
import { useStream } from "@corets/use-stream"

const sharedQuery = createAsync(async () => "some global data")
    
const Example = () => {
  const stream = useStream(1000, async () => `Current date is ${new Date().toISOString()}`)
  const globalStream = useStream(5000, sharedQuery, ["some", "dependencies"])
  
  const handleReload = () => stream.run()
  const handleCancel = () => stream.cancel()
  
  if (stream.isRunning() && ! stream.getResult()) {
    return <h1>Loading for the first time</h1>
  }
  
  if (stream.isErrored()) {
    return <h1>There was an error :(</h1>
  }
  
  return (
    <div>
      Result: {stream.getResult()} 
      {stream.isRunning() && "Refreshing ..."}
      <button onClick={handleReload}>Reload</button>
      <button onClick={handleCancel}>Cancel</button>
    </div>
  )
}
```

{% hint style="info" %}
Check out [Async](/observables/async) and [useAsync](/hooks/use-async) documentation for more details.
{% endhint %}


# useAction

React hook for imperative async operations.

Source code is hosted on [GitHub](https://github.com/corets/use-action)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-action
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-action
```

{% endtab %}
{% endtabs %}

This library is built on top of this package:

{% content-ref url="/pages/-Mg7Kt8XOfsD4Np5cHGW" %}
[Async](/observables/async)
{% endcontent-ref %}

## useAction() <a href="#useaction" id="useaction"></a>

Takes a function or an instance of [`Async`](/observables/async) and turns it into an observable async operation that can be triggered manually any time in the future.

```typescript
import React, { useState } from "react"
import { Async } from "@corets/async"
import { useAction } from "@corets/use-action"

const sharedAction = new Async(async (argument: number) => `Global value is ${argument}`)

const Example = () => {
    const action = useAction(async (argument: number) => `Value is ${argument}`)
    const globalAction = useAction(sharedAction)
    
    const handleRun = () => action.run(Math.random())
    const handleCancel = () => action.cancel()
    
    if (action.isRunning()) {
        return <h1>Executing action...</h1>
    }
    
    if (action.isErrored()) {
        return <h1>There was an error :(</h1>
    }
    
    if (action.isCancelled()) {
        return <h1>Action has been cancelled</h1>
    }
    
    return (
        <div>
            Result: {action.getResult()}
            <button onClick={handleRun}>Run</button>
            <button onClick={handleCancel}>Cancel</button>
        </div>
    )
}
```

{% hint style="info" %}
Check out [Async](/observables/async) documentation for more details.
{% endhint %}


# useDebounce

React hook for debounced values and functions.

Source code is hosted on [GitHub](https://github.com/corets/use-debounce)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-debounce
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-debounce
```

{% endtab %}
{% endtabs %}

## useDebounce() <a href="#usedebounce" id="usedebounce"></a>

This hook is built on top of [`debounce()`](https://lodash.com/docs/4.17.15#debounce) from lodash and accepts the same arguments.

Example of a debounced value:

```typescript
import React, { useState, useEffect } from "react"
import { useDebounce } from "@corets/use-debounce"

const Example = () => {
  const [input, setInput] = useState("")
  const debouncedInput = useDebounce(input, 300)
  
  const handleSearch = () => {
    // fetch some data based on input...
  }
  
  const handleChange = (e) => setInput(e.target.value)
  
  // react to changes of the debounced input
  useEffect(handleSearch, [debouncedInput])
  
  return (
    <input type="text" onChange={handleChange} />
  )
}
```

Example of a debounced function:

```typescript
import React, { useState, useEffect } from "react"
import { useDebounce } from "@corets/use-debounce"

const Example = () => {
  const [input, setInput] = useState("")
  
  const handleSearch = useDebounce((query: string) => {
    // you can also access variable "input" directly, if you want to
    // fetch some data based on input...
  }, 300)
  
  const handleChange = (e) => {
    setInput(e.target.value)
    // trigger search immediately, this call will be debounced
    handleSearch(e.target.value)
  }
  
  return (
    <input type="text" onChange={handleChange} />
  )
}
```

{% hint style="info" %}
This hook accepts a third argument, identical to the one expected by the [debounce()](https://lodash.com/docs/4.17.15#debounce) function from lodash.
{% endhint %}


# useThrottle

React hook for throttled values and functions.

Source code is hosted on [GitHub](https://github.com/corets/use-throttle)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-throttle
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-throttle
```

{% endtab %}
{% endtabs %}

## useThrottle() <a href="#usethrottle" id="usethrottle"></a>

This hook is built on top of [`throttle()`](https://lodash.com/docs/4.17.15#throttle) from lodash and accepts the same arguments.

Example of a throttled value:

```typescript
import React, { useState, useEffect } from "react"
import { useThrottle } from "@corets/use-throttle"

const Example = () => {
  const [input, setInput] = useState("")
  const throttledInput = useThrottle(input, 300)
  
  const handleSearch = () => {
    // fetch some data based on input...
  }
  
  const handleChange = (e) => setInput(e.target.value)
  
  // react to changes of the debounced input
  useEffect(handleSearch, [throttledInput])
  
  return (
    <input type="text" onChange={handleChange} />
  )
}
```

Example of a throttled function:

```typescript
import React, { useState, useEffect } from "react"
import { useThrottle } from "@corets/use-throttle"

const Example = () => {
  const [input, setInput] = useState("")
  
  const handleSearch = useThrottle((query: string) => {
    // you can also access variable "input" directly, if you want to
    // fetch some data based on input...
  }, 300)
  
  const handleChange = (e) => {
    setInput(e.target.value)
    
    // trigger search immediately, this call will be throttled
    handleSearch(e.target.value)
  }
  
  return (
    <input type="text" onChange={handleChange} />
  )
}
```

{% hint style="info" %}
This hook accepts a third argument, identical to the one expected by the [throttle()](https://lodash.com/docs/4.17.15#throttle) function from lodash.
{% endhint %}


# usePrevious

Track previous versions of values.

Source code is hosted on [GitHub](https://github.com/corets/use-previous)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-previous
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-previous
```

{% endtab %}
{% endtabs %}

## usePrevious() <a href="#useprevious" id="useprevious"></a>

Get the previous version of a value:

```typescript
import React, { useState } from "react"
import { usePrevious } from "@corets/use-previous"

const Example = () => {
  const [count, setCount] = useState(0)
  const previousCount = usePrevious(count)
  
  return (
    <div>
      <div>count: {count}</div>
      <div>previous count: {previousCount}</div>
    </div>
  )
}
```


# useIdle

Detect when a user goes idle.

Source code is hosted on [GitHub](https://github.com/corets/use-idle)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-idle
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-idle
```

{% endtab %}
{% endtabs %}

## useIdle() <a href="#useidle" id="useidle"></a>

Detect when a user has been inactive for a certain amount of time:

```typescript
import React, { useEffect } from "react"
import { useIdle } from "@corets/use-idle"

const Example = () => {
  const isIdle = useIdle({
    // check every 10s
    interval: 10 * 1000,
    // regard user as idle after 30min
    threshold: 30 * 60 * 1000,
    // specify events that should reset the idle status (default: ["mousemove", "mouseup", "keyup", "focus", "resize"])
    events: ["mousemove"],
    // local storage key for the last activity timestamp (default: last-activity)
    storageKey: "custom-storage-key"
  })
  
  useEffect(() => {
    if (isIdle) {
      // do something, for example: logout()
    }
  }, [isIdle])
  
  return (
    <div>My App</div>
  )
}
```


# useValue

React hooks for the @corets/value package.

Source code is hosted on [GitHub](https://github.com/corets/use-value)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-value
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-value
```

{% endtab %}
{% endtabs %}

This is a React integration for this package:

{% content-ref url="/pages/-MeeBGx8X2I9UAm40nt5" %}
[Value](/observables/value)
{% endcontent-ref %}

## useValue() <a href="#usevalue" id="usevalue"></a>

Use observable values inside React components:

```typescript
import React from "react"
import { createValue } from "@corets/value"
import { useValue } from "@corets/use-value"

const globalValue = createValue(0)

const Example = () => {
  const value1 = useValue(0)
  const value2 = useValue(() => 0)
  const value3 = useValue(globalValue)
  
  // alternative syntax
  const [value, setValue] = useValue(globalValue).use()
  
  const increment = () => value1.set(value1.get() + 1)
  
  return <button onClick={increment}>Count: {value1.get()}</button>
}
```


# useList

React hooks for the @corets/list package.

Source code is hosted on [GitHub](https://github.com/corets/use-list)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-list
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-list
```

{% endtab %}
{% endtabs %}

This is a React integration for this package:

{% content-ref url="/pages/-MeeBIuqvkAwUjx0ORFs" %}
[List](/observables/list)
{% endcontent-ref %}

## useList() <a href="#uselist" id="uselist"></a>

Use observable lists inside React components:

```jsx
import React from "react"
import { createList } from "@corets/list"
import { useList } from "@corets/use-list"

const globalList = createList(["apple", "oranges"])

const Example = () => {
  const list1 = useList(["apples", "oranges"])
  const list2 = useList(() => createList(["apples", "oranges"]))
  const list3 = useList(globalList)
  // alternative syntax
  const [list, setList] = useList(globalList).use()
  
  const addItem = () => list1.add("tomatoes")
  
  return (
    <div>
      <button onClick={addItem}>Items: {list1.get().join(",")}</button>
    </div>
  )
}
```


# useStore

React hooks for the @corets/store package.

Source code is hosted on [GitHub](https://github.com/corets/use-store)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-store
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/use-store
```

{% endtab %}
{% endtabs %}

This is a React integration for this package:

{% content-ref url="/pages/-MeeBQ0F094TDCsvnMbA" %}
[Store](/observables/store)
{% endcontent-ref %}

## useStore() <a href="#usestore" id="usestore"></a>

Use observable stores inside React components:

```typescript
import React from "react"
import { createStore } from "@corets/store"
import { useStore } from "@corets/use-store"

const globalStore = createStore({ count: 0 })

const Example = () => {
  const store1 = useStore(() => ({ count: 0 }))
  const store2 = useStore(globalStore)
  
  // alternative syntax
  const [store, setStore] = useStore(globalStore).use()
  
  const increment = () => store1.set({ count: store1.get().count + 1 })
  
  return (
    <div>
      <button onClick={increment}>Count: {store1.get().count}</button>
    </div>
  )
}
```


# useForm

React hooks for the @corets/form package.

Source code is hosted on [GitHub](https://github.com/corets/use-form)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-form
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/use-form
```

{% endtab %}
{% endtabs %}

This is a React integration for this package:

{% content-ref url="/pages/-MeeF80BrEndyj5EzdPB" %}
[Form](/services/form)
{% endcontent-ref %}

## useForm() <a href="#useform" id="useform"></a>

Use forms inside React components:

```typescript
import React from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"

const Example = () => {
  const form = useForm(() => createForm({ text: "foo" }))
  
  return (
    <>
      <input 
        value={form.get("text")} 
        onChange={(e) => form.set("text", e.target.value)} />

      // with the static field
      
      <input 
        value={form.getFields().text.get().getValue()} 
        onChange={(e) => form.getFields().text.get().setValue(e.target.value)} />
    </>
  )
}
```


# useFormBinder

React bindings for the @corets/form package.

Source code is hosted on [GitHub](https://github.com/corets/use-form-binder)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-form-binder
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/use-form-binder
```

{% endtab %}
{% endtabs %}

React bindings of vanilla HTML elements for this package:

{% content-ref url="/pages/-MeeF80BrEndyj5EzdPB" %}
[Form](/services/form)
{% endcontent-ref %}

## useFormBinder() <a href="#useformbinder" id="useformbinder"></a>

Create an input binder for vanilla HTML input elements:

```typescript
import React from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"
import { useFormBinder } from "@corets/use-form-binder"

const Example = () => {
  const form = useForm(() => createForm({
    input: "text",
    checkbox: true,
    radio: "second",
    select: "second",
  }))
  const bind = useFormBinder(form)
  
  return (
    <form { ...bind.form() }>
      <input type="text" { ...bind.input("input") } />
      
      <input type="checkbox" { ...bind.checkbox("checkbox") } />
      
      <input type="radio" { ...bind.radio("radio", "first") } />
      <input type="radio" { ...bind.radio("radio", "second") } />
      
      <select { ...bind.select("select") }>
        <option value="first">Option 1</option>
        <option value="second">Option 2</option>
        <option value="third">Option 3</option>
      </select>
      
      <button { ...bind.submit() }>Submit</button>
    </form>
  )
}
```


# useTranslator

React hooks for the @corets/translator package.

Source code is hosted on [GitHub](https://github.com/corets/use-translator)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-translator
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/use-translator
```

{% endtab %}
{% endtabs %}

To learn how to use translations in a static way check the [`useLocales()`](/hooks/use-translator#uselocales) section.

This is a React integration for this package:

{% content-ref url="/pages/-MeeFAgcZV96KXm0W1WU" %}
[Translator](/services/translator)
{% endcontent-ref %}

## &#x20;\<TranslatorProvider /> <a href="#translatorcontext" id="translatorcontext"></a>

You can share a translator instance through a dedicated `TranslatorProvider` component. Methods like `useTranslator`, `useTranslate` and `useLanguage` automatically try to find an instance in the context, so you don't have to pass it manually. This is the recommended way to use this library:

```typescript
import React from "react"
import { render } from "react-dom"
import { createTranslator } from "@corets/translator"
import {
  TranslatorProvider,
  useTranslator,
  useTranslate,
  useLanguage,
} from "@corets/use-translator"

const translations = {
  en: {
    title: "Foo",
    nested: { title: "Bar" }
  }
}

const translator = createTranslator(translations, { language: "en" })

const Example = () => {
  const t = useTranslate()

  return <div>Regular access: {t("title")}</div>
}

render(
  <TranslatorProvider instance={translator}>
    <Example />
  </TranslatorProvider>,
  document.getElementById("root")
)
```

## useLocales()

Translations can be accessed statically thanks to the `createTranslatorAccessor` helper. All you have to do is to create a small hook where you choose your default / most up to date language that is used for the static typing.

```typescript
import React from "react"
import { render } from "react-dom"
import { createTranslatorAccessor } from "@corets/translator"

const translations = {
  en: {
    title: "Foo",
    nested: { title: "Bar" }
  }
}

const translator = createTranslator(translations, { language: "en" })

const useLocales = () => createTanslatorAccessor(useTranslator(translator), translations.en)

const Example () => {
  const loc = useLocales()
  
  return <div>{loc.nested.title.get()}</div>
}

render(<Example />, document.getElementById("root"))
```

## useTranslator() <a href="#usetranslator" id="usetranslator"></a>

Use translator inside React components:

```typescript
import React from "react"
import { render } from "react-dom"
import { createTranslator } from "@corets/translator"
import { useTranslator } from "@corets/use-translator"

const translations = {
  en: {
    title: "Foo",
    nested: { title: "Bar" }
  }
}

const translator = createTranslator(translations, { language: "en" })

const Example = () => {
  const translator = useTranslator(translator)
  
  return (
    <div>Title: {translator.get("title")}</div>
  )
}

render(<Example />, document.getElementById("root"))
```

## useTranslate() <a href="#usetranslate" id="usetranslate"></a>

Use a translation function inside React components:

```typescript
import React from "react"
import { render } from "react-dom"
import { createTranslator } from "@corets/translator"
import { useTranslate } from "@corets/use-translator"

const translations = {
  en: {
    title: "Foo",
    nested: { title: "Bar" }
  }
}

const translator = createTranslator(translations, { language: "en" })

const Exymple = () => {
  const t = useTranslate(translator)
  const tt = useTranslate(translator, { "nested" })
  
  return (
    <div>
      <div>Title: {t("title")}</div>
      <div>Nested title: {tt("title")}, same as: {t("nested.title")}</div>
    </div>
  )
}

render(<Example />, document.getElementById("root"))
```

## useLanguage() <a href="#uselanguage" id="uselanguage"></a>

Use language related information inside your React components:

```typescript
import React from "react"
import { render } from "react-dom"
import { createTranslator } from "@corets/translator"
import { useLanguage } from "@corets/use-translator"

const translations = {
  en: {
    title: "Foo",
    nested: { title: "Bar" }
  }
}

const translator = createTranslator(translations, { language: "en" })

const Example = () => {
  const language = useLanguage(translator)
  
  return (
    <div>
      <div>Current: {language.current}</div>
      <div>Fallback: {language.fallback}</div>
      <div>Available: {JSON.stringify(language.available)}</div>
      <button onClick={()=> language.set("de")}>Set language to "de"</button>
    </div>
  )
}

render(<Example />, document.getElementById("root"))
```


# useQuery

Hooks for react-router query parameters.

Source code is hosted on [GitHub](https://github.com/corets/use-query)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/use-query
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/use-query
```

{% endtab %}
{% endtabs %}

## useQuery() <a href="#usequery" id="usequery"></a>

Read and update URL query from your React component:

```typescript
import React from "react"
import { useQuery } from "@corets/use-query"

const Example = () => {
  const [query, setQuery, patchQuery] = useQuery({
    page: 1,
    order: "asc"
  })
  
  // update page and also reset order to the initial value "asc"
  const handleGoToNextPage = () => setQuery({ page: query.page + 1 })
  
  // update order, but keep page as is
  const handleToggleSort = () => patchQuery({ order: query.order === "asc" ? "desc" : "asc" })
  
  return (
    <div>
      <div>Page: {query.page}</div>
      <div>Order: {query.order}</div>
      <button onClick={handleGoToNextPage}>Go to next page</button>
      <button onClick={handleToggleSort}>Change sorting order</button>
    </div>
  )
}
```

By default, parameters like `""`, `null`, `undefined`, `0` and `"0"` are stripped, the default value will be used instead. Updating the query with one of those values won't change anything. You can alter this behavior by providing a second argument, overriding values that should be stripped away:

```typescript
useQuery({ some: "value" }, ["", null, undefined])
```


# Tag

Type tagging and branding for TypeScript.

Source code is hosted on [GitHub](https://github.com/corets/tag)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/tag
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/tag
```

{% endtab %}
{% endtabs %}

Type **tagging,** also known as **branding**, is a common practice in advanced TypeScript setups. The main purpose of this approach is to make certain primitive types more predictable. Using branded types leads to a better traceability of data in the project and encourages developers to be more aware when working with critical, primitive data.

## Quick Start <a href="#quick-start" id="quick-start"></a>

Let's have a look at this example below:

```typescript
type UUID = string

type User = { id: UUID }
```

We have created a type alias `UUID` that is used on the type `User`. Right now, any `string` value is a valid `UUID`:

```typescript
const user: User = { id: "some-uuid" }
```

This is very implicit, not traceable, and is not very safe since you pay less attention to what is passed around, since everything is just a `string`.

What if we could make this more explicit?

```typescript
import { Tag } from "@corets/tag"

type UUID = Tag<string, "uuid">

type User = {
    id: UUID
}

// this will not work since string is not assignable to UUID
const user1: User = { id: "some-uuid" }

// exlicitly cast it to UUID
const user2: User = { id: "some-uuid" as UUID }
```

Now we are using a branded `string` instead of plain `string`. You can not assign a plain `string` to a `UUID` anymore, if you do so, you have to cast it explicitly. Now you also have full traceability on where `UUID`s are used in the project.

## Tag\<type, alias> <a href="#tagtype-alias" id="tagtype-alias"></a>

Creates a branded type for any primitive value:

```typescript
import { Tag } from "@corets/tag"

const Email = Tag<string, "email">

// this will work
const email2: Email = "foo@bar.com" as Email

// this will not work
const email1: Email = "foo@bar.com"
```

{% hint style="info" %}
You get full traceability of where branded types are used in your project, since you always have to cast a primitive value to that specific type.
{% endhint %}


# Input Helpers

Various helpers related to input specific functionality.

Source code is hosted on [GitHub](https://github.com/corets/input-helpers)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/input-helpers
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/input-helpers
```

{% endtab %}
{% endtabs %}

## Quick Start <a href="#select-a-file-and-send-to-server" id="select-a-file-and-send-to-server"></a>

Prompt user to select a file and send it to a server:

```typescript
import React from "react"
import { selectFile } from "@corets/input-helpers"
import axios from "axios"

const Example = () => {
  const handleSelectFile = async () => {
    const file = await selectFile()
    
    if ( ! file) return
    
    submitFile(file)
  }
  
  const submitFile = async (file: File) => {
    const formData = new FormData()
    formData.append('file', file)
    
    await axios.post(`/endpoint`, formData, {
      headers: { 'content-type': 'multipart/form-data' },
    })
  }
  
  return <button onClick={handleSelectFile}/>
}
```

Prompt user to select a file and show a preview:

```typescript
import React, { useState } from "react"
import { selectFileOfType } from "@corets/input-helpers"

const Example = () => {
  const [selectedFile, setSelectedFile] = useState()
  
  const handleSelectFileOfType = async () => {
    const file = await selectFileOfType('image/*')
    if (!file) return
    
    setSelectedFile(file)
  }
  
  return (
    <div>
      <button onClick={handleSelectFileOfType}/>
      {selectedFile && (
        <img src={URL.createObjectURL(selectedFile)}/>
      )}
    </div>
  )
}
```

## selectFile() <a href="#selectfile" id="selectfile"></a>

Prompt user to select one file:

```typescript
import { selectFile } from "./selectFile"

const file = await selectFile()

if (file) {
  // some something with the file...
}
```

## selectFiles() <a href="#selectfiles" id="selectfiles"></a>

Prompt user to select multiple files:

```typescript
import { selectFiles } from "@corets/input-helpers"

const files = await selectFiles()

if (files.length > 0) {
  // do something with the files...
}
```

## selectFileOfType() <a href="#selectfileoftype" id="selectfileoftype"></a>

Prompt user to select a file of a specific type:

```typescript
import { selectFileOfType } from "@corets/input-helpers"

const file = await selectFileOfType('image/*')

if (file) {
  // so something with the file
}
```

{% hint style="info" %}
Read more about file types [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept).
{% endhint %}

## selectFilesOfType() <a href="#selectfilesoftype" id="selectfilesoftype"></a>

Prompt user to select multiple files of a specific type:

```typescript
import { selectFilesOfType } from "@corets/input-helpers"

const files = await selectFilesOfType('image/*')

if (files.length > 0) {
  // so something with the files...
}
```


# Promise Helpers

Various helpers related to promise specific functionality.

Source code is hosted on [GitHub](https://github.com/corets/promise-helpers)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/promise-helpers
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/promise-helpers
```

{% endtab %}
{% endtabs %}

## createTimeout() <a href="#createtimeout" id="createtimeout"></a>

A promisified version of the `setTimeout` function:

```typescript
import { createTimeout } from "@corets/promise-helpers"

await createTimeout(2000)
```

## createPromise() <a href="#createpromise" id="createpromise"></a>

Create a promise that you can pass around without specifying the resolve function upfront:

```typescript
import { createPromise } from "@corets/promise-helpers"

const runWhenResolved = async (promise: Promise<any>) => {
  try {
    const result =  await promise
    console.log("promise resolved with:", result)
  } catch (err) {
    console.error("an error was thrown from promise:", err)
  }
}

const promise = createPromise()

// pass promise to another function
runWhenResolved(promise)

promise.resolve("some data")
// or
promise.reject("reason...")
```


# Save Helpers

Various helpers related to file saving specific functionality.

Source code is hosted on [GitHub](https://github.com/corets/save-helpers)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/save-helpers
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/save-helpers
```

{% endtab %}
{% endtabs %}

## saveAsFile() <a href="#saveasfile" id="saveasfile"></a>

Save any kind of data as a file (works only in the browser):

```typescript
import { saveAsFile } from "@corets/save-helpers"

const fileName = "data.csv"
const data = "name,age\nJohn,30\nSnow,40"

saveAsFile(fileName, data)
```


# Pagination Helpers

Various helpers related to pagination specific functionality.

Source code is hosted on [GitHub](https://github.com/corets/pagination-helpers)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/pagination-helpers
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/pagination-helpers
```

{% endtab %}
{% endtabs %}

## createPagination()

Calculates total amount of pages, visible pages, more / less indicators, total items, etc., this helper can be used to build a pagination like this:

```
< << ... 6 7 8 [9] 10 11 12 ... >> >
< << 7 8 [9] 10 11 >> >
<  ... 7 8 [9] 10 11  ... >
< [9] >
[ v Select page ]
```

Calculate pagination:

```typescript
import { createPagination } from "@corets/pagination-helpers"

// active page
const currentPage = 7

// total number of items for pagination
const totalItems = 1337

// how many items are visible on a single page
const pageSize = 20

// how many pages you want to display to the left and right of the current page
const surroundBy = 2
const pagination = createPagination({ currentPage, totalItems, pageSize, surroundBy })
```

With the calculated result below you can build every possible kind of pagination you can think of:

```typescript
{
  // active page
  currentPage: 7,
  // number of the first page, obviously...
  firstPage: 1,
  // number of the last page
  lastPage: 67,
  // first visible page based on the surroundBy
  firstVisiblePage: 5,
  // last visible page based on the surroundBy
  lastVisiblePage: 9,
  // all visible pages based on the surroundBy
  visiblePages: [ 5, 6, 7, 8, 9 ],
  // all available pages, useful for a dropdown
  allPages: [
     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
    13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
    37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
    49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
    61, 62, 63, 64, 65, 66, 67
  ],
  // are there any previous pages?
  hasPrevious: true,
  // are there any next pages?
  hasNext: true,
  // are there any pages in between the first page and the first visible page?
  hasLess: true,
  // are there any pages in between the last page and the last visible page?
  hasMore: true,
  // how many items to skip, useful for an api call
  itemsOffset: 120,
  // how many items are visible per page
  pageSize: 20,
  // is current page the first one?
  isFirstPage: false,
  // is current page the last one?
  isLastPage: false
}
```


# Clipboard Helpers

Various helpers related to clipboard specific functionality.

Source code is hosted on [GitHub](https://github.com/corets/clipboard-helpers)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/clipboard-helpers
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/clipboard-helpers
```

{% endtab %}
{% endtabs %}

## copyToClipboard() <a href="#copytoclipboard" id="copytoclipboard"></a>

Copy a value to clipboard:

```typescript
import { copyToClipboard } from "@corets/clipboard-helpers"

copyToClipboard("some value")
```

Behind the scenes, a hidden input element is rendered to perform the copy action. Sometimes you might want to specify a custom render target, for example when using a focus trap:

```typescript
import { copyToClipboard } from "@corets/clipboard-helpers"

copyToClipboard("some value", "#target")
// or
copyToClipboard("some value", document.getElementById("target"))
```


# Calendar Helpers

Various helpers related to calendar specific functionality.

Source code is hosted on [GitHub](https://github.com/corets/calendar-helpers)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/calendar-helpers
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/calendar-helpers
```

{% endtab %}
{% endtabs %}

## createCalendarMonthView() <a href="#createcalendarmonthview" id="createcalendarmonthview"></a>

Generate a 6 x 7 multidimensional array containing dates based on the given date:

```typescript
import { createCalendarMonthView } from "@corets/calendar-helpers"

const today = new Date("2020-04-26")
const weeks = createCalendarMonthView(today)
```

This array can be used to render a custom calendar widget or a month view:

```typescript
[
  // 2020-03-30, 2020-03-31, 2020-04-01, 2020-04-02, 2020-04-03, 2020-04-04, 2020-04-05
  [Date, Date, Date, Date, Date, Date, Date],
  // 2020-04-06, 2020-04-07, 2020-04-08, 2020-04-09, 2020-04-10, 2020-04-11, 2020-04-12
  [Date, Date, Date, Date, Date, Date, Date],
  // 2020-04-13, 2020-04-14, 2020-04-15, 2020-04-16, 2020-04-17, 2020-04-18, 2020-04-19
  [Date, Date, Date, Date, Date, Date, Date],
  // 2020-04-20, 2020-04-21, 2020-04-22, 2020-04-23, 2020-04-24, 2020-04-25, 2020-04-26
  [Date, Date, Date, Date, Date, Date, Date],
  // 2020-04-27, 2020-04-28, 2020-04-29, 2020-04-30, 2020-05-01, 2020-05-02, 2020-05-03
  [Date, Date, Date, Date, Date, Date, Date],
  // 2020-05-04, 2020-05-05, 2020-05-06, 2020-05-07, 2020-05-08, 2020-05-09, 2020-05-10
  [Date, Date, Date, Date, Date, Date, Date],
]
```


# Local Storage Helpers

Various helpers related to localStorage specific functionality.

Source code is hosted on [GitHub](https://github.com/corets/local-storage-helpers)

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/local-storage-helpers
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save @corets/local-storage-helpers
```

{% endtab %}
{% endtabs %}

## readLocalStorage() <a href="#readlocalstorage" id="readlocalstorage"></a>

Reads a value from the `localStorage`:

```typescript
import { readLocalStorage } from "@corets/local-storage-helpers"

type AuthData = { token?: string }

const defaultData: AuthData = { token: undefined }

const data = readLocalStorage<AuthData>("auth", defaultData)
```

## writeLocalStorage() <a href="#writelocalstorage" id="writelocalstorage"></a>

Writes a value to the `localStorage`:

```typescript
import { writeLocalStorage } from "@corets/local-storage-helpers"

const data = { token: "foo" }

writeLocalStorage("auth", data)
```


