open-index/open-npm huggingface dataset:
npm package: @properdo/fusionauth-oauth2-client
Description: A TypeScript/JavaScript client for FusionAuth's OAuth2 Authorization Code Flow.
Author: properdo
Latest version: 1.0.2

# FusionAuth OAuth2 Client

A TypeScript/JavaScript client for FusionAuth's OAuth2 Authorization Code Flow.

## Features
- Start the OAuth2 Authorization Code Flow
- Exchange authorization code for access token
- Fetch user info
- Type-safe configuration and responses

## Installation

```bash
npm install @properdo/fusionauth-oauth2-client
```

## Usage

### 1. Import and configure the client

```typescript
import { CodeFlow } from '@properdo/fusionauth-oauth2-client';

const client = new CodeFlow({
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
  redirectUri: 'http://localhost:5545/oauth2callback',
  oauth2Host: 'https://auth.example.com',
  scope: 0, // or use bitwise OR of AddressScope, EmailScope, etc.
});
```

#### Understand setting scope of OAuth2

Scopes can be set with bitwise OR, there are 4 scopes in total: `AddressScope`, `EmailScope`, `ProfileScope`, `PhoneScope`.

```typescript
import { CodeFlow, AddressScope, EmailScope } from '@properdo/fusionauth-oauth2-client';

const client = new CodeFlow({
  ...
  // This will call OAuth2 code flow with scope AddressScope and EmailScope, resulting the profile getting also contains the address and email of the user
  scope: AddressScope | EmailScope
});
```

However, you can also not setting any scope at all by putting number 0:

```typescript
const client = new CodeFlow({
  ...
  scope: 0,
});
```

This will call OAuth2 code flow with 0 scope. With this, you also get the `access_token` that verifies the user is indeed logged in on FusionAuth, but calling `getUserInfo(access_token)` will result error as we do not have the specified scope to get user information !!!

### 2. Get the authorization URL

```typescript
const startUrl = client.start();
// Redirect the user to startUrl
```

### 3. Exchange code for access token

```typescript
const code = 'CODE_FROM_QUERY_STRING';
const tokenResponse = await client.getAccessToken(code);

if ('access_token' in tokenResponse) {
  // Success
  console.log('Access Token:', tokenResponse.access_token);
} else {
  // Error
  console.error(tokenResponse.error_description);
}
```

### 4. Fetch user info

```typescript
// The accessToken is taken from function `getAccessToken`;
// This will cost 1 additional request to the FusionAuth server
const accessToken = 'ACCESS_TOKEN';
const userInfo = await client.getUserInfo(accessToken);
console.log(userInfo);
```

## License

MIT
npm package: @properly-typed/string
Description: Strict types for String
Author: temoncher@yandex.ru
Latest version: 0.0.1

## @properly-typed/vuex

Strict types for vuex modules

> **⚠ TypeScript 4.1 needed**
>
> This package makes heavy use of TypeScript 4.1 features like [Key Remapping](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#key-remapping-mapped-types) and [Template Literal Types](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#template-literal-types), so you won't be able to use it with TypeScript version under 4.1

Also this package contains only type definitions, so it won't affect your javascript code in any way.

### Usage

Only type alias can be used configuring module. If you want to pass interfaces into `ModuleConfig` generic, you can pass it into `ConvertToAlias` helper.

```ts
import { ToDictionary, ModuleConfig } from '@properly-typed/vuex';

type AuthActions = {
  authorize: () => void;
};

interface IAuthMutations {
  setIsAuthed: (isAuthed: boolean) => void;
}

interface IAuthModuleState {
  isAuthed: boolean;
}

export type AuthModuleConfig = ModuleConfig<
  true,
  // Usage of interface for State is ok
  IAuthModuleState,
  never,
  // Usage of type alias for mutations/getters/actions is ok
  AuthActions,
  // To use interface for mutations/getters/actions, you need to convert it into type alias
  ToDictionary<IAuthMutations>,
  { account: AuthAccountModuleConfig }
>;
```
npm package: @properly-typed/vuex
Description: Strict types for vuex modules
Author: temoncher@yandex.ru
Latest version: 0.0.7

## @properly-typed/vuex

Strict types for vuex modules

> **⚠ TypeScript 4.1 needed**
>
> This package makes heavy use of TypeScript 4.1 features like [Key Remapping](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#key-remapping-mapped-types) and [Template Literal Types](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#template-literal-types), so you won't be able to use it with TypeScript version under 4.1
npm package: @propero/action-history
Description: Typesafe, extensible history for user defined actions
Author: Propero Team
Latest version: 1.0.0

# Action History

[![Maintainability](https://api.codeclimate.com/v1/badges/025c360bbaaccc37cdd5/maintainability)](https://codeclimate.com/github/propero-oss/action-history/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/025c360bbaaccc37cdd5/test_coverage)](https://codeclimate.com/github/propero-oss/action-history/test_coverage)
[![Build Status](https://app.travis-ci.com/propero-oss/action-history.svg?branch=master)](https://app.travis-ci.com/propero-oss/action-history)

```shell
npm i @propero/action-history
```
```shell
yarn add @propero/action-history
```
```shell
pnpm i @propero/action-history
```

## Documentation

*In progress... maybe... hopefully... within the next decade or so... no guarantees though!*


## Example
```typescript
import { createHistory, HistoryMeta } from "@propero/action-history";

// interface for type safety inside handlers, calls, etc.
// can later be augmented for extensibility
interface Events extends HistoryMeta {
  "myapp:myaction": {
    parameters: { param?: number },
    context: { context: number },
  },
  "myapp:otheraction": {
    reversible: false,
  },
  "myapp:lateaction": {
    context: { latecontext: number },
  }
}

const history = createHistory<Events>();

history.context({ context: 2 });

let myNumber = 0;

history.action("myapp:myaction", async ({ param = 5 }, { context }) => {
  const previous = myNumber;
  myNumber += param;
  myNumber /= context;
  return async () => {
    myNumber = previous;
  };
});

history.action("myapp:otheraction", async () => {
  console.log("other action");
});

await history.exec("myapp:myaction", { param: 2 });
// myNumber = (0 + 2) / 2 = 1
await history.exec("myapp:otheraction");
// myNumber unchanged
await history.exec("myapp:myaction");
// myNumber = (1 + 5) / 2 = 3

await history.exec("history:undo", { steps: 2 });
// myNumber = 0
await history.exec("history:redo");
// myNumber = 1

const actionPromise = history.exec("myapp:lateaction");
// actionPromise waits for action to be registered

history.action("myapp:lateaction", ({}, { latecontext }) => {
  myNumber *= latecontext;
  () => myNumber /= latecontext;
}, ["latecontext"]);
// actionPromise waits for dependent context to be defined

history.context({ latecontext: 10 });
// action is executed

await actionPromise;
// myNumber = 1 * 10 = 10
```
npm package: @propero/easy-api
Description: Build powerful Rest APIs with simple typescript decorators
Author: Propero Team
Latest version: 2.1.0

# Easy API

[![Maintainability](https://api.codeclimate.com/v1/badges/d843b9fcb8d7fe37a6f5/maintainability)](https://codeclimate.com/github/propero-oss/easy-api/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/d843b9fcb8d7fe37a6f5/test_coverage)](https://codeclimate.com/github/propero-oss/easy-api/test_coverage)
[![Build Status](https://app.travis-ci.com/propero-oss/easy-api.svg?branch=master)](https://app.travis-ci.com/propero-oss/easy-api)

## Installing
```shell
npm i @propero/easy-api
```
```shell
yarn add @propero/easy-api
```
```shell
pnpm add @propero/easy-api
```

## Documentation

###### In progress... maybe... hopefully... within the next decade or so... no guarantees though

## Examples

For further examples check out the [example directory](example).

##### Simple Hello World Service
```typescript
import express from "express";
import { Service, createService, Get, Param } from "@propero/easy-api";

@Service("/hello")
class HelloService {
  @Get("/:name?")
  public async hello(@Param("name") name: string = "world") {
    return `Hello ${name}!`;
  }
}

const app = express();
app.use("/api/v1", createService(new HelloService()));
app.listen(3000, () => console.log("http://localhost:3000/api/v1/hello"));
```

##### Simple Custom Injection Decorator
```typescript
import express from "express";
import { createInjectorMiddleware, Post, Catch, CatchError } from "@propero/easy-api";
import { validateOrReject, ValidatorOptions, IsEmail } from "class-validator";
import { plainToClass } from "class-transformer";

const Validated = (Cls: unknown, validateOptions?: ValidatorOptions) => createInjectorMiddleware(() => async (req) => {
  const casted = plainToClass(Cls, req.body);
  await validateOrReject(casted, validateOptions);
  return casted;
});

class SubmitFormBody {
  @IsEmail()
  email: string;
}

@Service("/submit-form")
class SubmitFormService {
  @Post()
  public async onSubmit(@Validated(SubmitFormBody) body: SubmitFormBody) {
    console.log(body);
  }

  // Classes is set to array since class-validator does not throw an error but an array of errors
  @Catch({ status: 400, classes: Array })
  public async onError(@CatchError errors: ValidationError[]) {
    return { errors };
  }
}


const app = express();
app.use(createService(new SubmitFormService()));
app.listen(3000, () => console.log("http://localhost:3000/"));
```
npm package: @propero/easy-filter
Description: Simple parsing and serialization of filter urls
Author: Propero Team
Latest version: 1.0.0

# Easy Filter

[![Maintainability](https://api.codeclimate.com/v1/badges/0ccb2322f2d966cd0703/maintainability)](https://codeclimate.com/github/propero-oss/easy-filter/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/0ccb2322f2d966cd0703/test_coverage)](https://codeclimate.com/github/propero-oss/easy-filter/test_coverage)

```shell
npm i @propero/easy-filter
# Or with yarn
yarn add @propero/easy-filter
```

## Documentation

###### In progress... maybe... hopefully... within the next decade or so... no guarantees though

## Examples

For further examples check out the [example directory](example).

```typescript
import { FilterParser, and, gt, eq } from "@propero/easy-filter";


const parser = new FilterParser();

const parsed = parser.parse("and(lt(age,18),like(firstName,A%))");
/*
{
  "op": "and",
  "filters": [{
    "op": "lt",
    "field": "age",
    "value": "18"
  },{
    "op": "like",
    "field": "firstName",
    "value": "A%"
  }]
}
*/
const serialized = parser.serialize(and(gt("age", "21"), eq("name", "Peter,()")));
// and(gt(age,21),eq(name,Peter\,\(\)))
```
npm package: @propero/easy-store
Description: Simple, intuitive, functional reactive stores for typescript
Author: Propero Team
Latest version: 2.0.0

[![Maintainability](https://api.codeclimate.com/v1/badges/cfc752577cf502adf125/maintainability)](https://codeclimate.com/github/propero-oss/easy-store/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/cfc752577cf502adf125/test_coverage)](https://codeclimate.com/github/propero-oss/easy-store/test_coverage)

# easy-store
Simple, intuitive, functional reactive stores for typescript

    npm i @propero/easy-store

## Getting started
Create a simple store with createStore:

```typescript
import { createStore } from "@propero/easy-store";

// The typings for the store are deferred from initialValue
const myStore = createStore(initialValue);

// If you want to broaden the type of the store, you can explicitly
// define it like this
const myTypedStore = createStore<undefined | number>(undefined);
```

Accessing or updating the current value of a store is as easy as calling getValue and setValue.

```typescript
console.log(myStore.getValue());
myStore.set(newValue);
```

Additionally, you can use the update function to change the value of a store based on its current value.

```typescript
myStore.update(value => newValue);
// myStore.getValue() is now newValue

// also works with promises
myStore.update(async value => {
  await sleep(100);
  return newValue;
});
// myStore.getValue() is newValue when the promise resolves
```

You can subscribe or unsubscribe to change in a store using the sub and unsub functions.

```typescript
myStore.set(0);
myStore.subscribe((newVal, oldVal) => console.log(`Change from ${oldVal} to ${newVal}`));

myStore.set(10);
// => Change from 0 to 10

myStore.set(5);
// => Change from 10 to 5
```

If you have calculated state depending on multiple stores, you can derive a read-only store from them using deriveStore.
The first argument of deriveStore is its dependent stores, the second is a function that takes the store values as arguments and returns the store value.
```typescript
const firstStore = createStore(10);
const secondStore = createStore(20);
const maxStore = deriveStore([firstStore, secondStore], Math.max);
// maxStore.get() => 20

secondStore.set(5);
// maxStore.get() => 10
```

This module also exposes a createSubscribable method to create simple pub-subs.
```typescript
const { sub, unsub, notify } = createSubscribable();
sub(console.log);
notify("Hello World");
// => Hello World
```

All stores and subscribables have a limit to the number of listeners that can be subscribed and will throw once this limit is exceeded.
By default, this limit is set to 100 listeners.
This is, so you don't accidentally subscribe to a store in a loop, event listener or render cycle.
If you need the limit to be higher, you can provide an options object as the last parameter to creation methods like this:
```typescript
const unlimitedStore = createStore(5, { limit: Infinity });
const almostUnlimitedStore = createStore(2, { limit: 10000 });
const unlimitedSubscribable = createSubscribable({ limit: Infinity });
```
npm package: @propero/easy-store-react
Description: React bindings for easy-store
Author: Propero Team
Latest version: 1.0.1

[![Maintainability](https://api.codeclimate.com/v1/badges/d480dbca4abda5c97760/maintainability)](https://codeclimate.com/github/propero-oss/easy-store-react/maintainability)

# easy-store
React bindings for [@propero/easy-store](https://github.com/propero-oss/easy-store).

    npm i @propero/easy-store-react

## Getting started
Using a store in a functional component:

```typescript jsx
import { createStore } from "@propero/easy-store";
import { useStore } from "@propero/easy-store-react";

const countStore = createStore(0);

// Every instance of counter will now use the same count value
export function Counter() {
  const [count, setCount, updateCount] = useStore(countStore);
  
  const increment = () => updateCount(count => count + 1);
  const decrement = () => updateCount(count => count - 1);
  
  return (
    <>
      <button type="button" onClick={decrement}>-</button>
      <span>{count}</span>
      <button type="button" onClick={increment}>+</button>
    </>
  );
}

export function MultipleCounters() {
  return (
    <>
      <Counter />
      <Counter />
    </>
  );
}
```

Using a subscribable:

```typescript jsx
import { createSubscribable } from "@propero/easy-store";
import { useSubscribable } from "src/use-subscribable";

const clickEvents = createSubscribable<[MouseEvent]>();
window.addEventListener("click", clickEvents.notify);

export function Popover({ target, open: initialOpen = false, children }: { target: HTMLElement; open?: boolean; children?: any }) {
  const [open, setOpen] = useState(open);
  
  function onMouseClick(ev: MouseEvent) {
    setOpen(target === ev.target || target.contains(ev.target));
  }
  
  useSubscribable(clickEvents, onMouseClick);
  
  return (
    <div className="popover" style={{display: open ? "block" : "none"}}>
      {children}
    </div>
  );
}
```
npm package: @propero/react-replace-component
Description: Replace react components dynamically at a later time
Author: Propero Team
Latest version: 1.0.0

# react-replace-component
Replace react components dynamically at a later time

    npm i @propero/react-replace-component

## Getting started

```typescript jsx
import { createComponentStore } from "@propero/react-replace-component";

const { useComponent, useComponents, registerComponent, setNamespace } = createComponentStore();

export function Target() {
  return <div className="a" />;
}


export function AnotherComponent() {
  return <div className="aa" />;
}

export function Replacement() {
  return <div className="b" />;
}

export function MyPage() {
  // This allows the component to be replaced at a later time
  const TargetEx = useComponent(Target);
  return <TargetEx />;
}

export function MultipleComponents() {
  // Allows for replacing multiple components, each one can be replaced individually still.
  // Components are suffixed with Ex to avoid shadowing or having to rename properties in destructuring.
  const { TargetEx, AnotherComponentEx } = useComponents({ Target, AnotherComponent });
  return <>
    <TargetEx />
    <AnotherComponentEx />
  </>;
}

// This replaces the Target components with Replacement components.
// Only works if current namespace is default
registerComponent(Target, Replacement);

// Switch themes/extensions/customer specific replacements quickly with namespaces
setNamespace("foo")
```
npm package: @propero/security
Description: safe and simple handling of passwords, tokens, etc
Author: Nora Reichwald
Latest version: 2.0.0

[![Maintainability](https://api.codeclimate.com/v1/badges/70945023a0a33ac65fbb/maintainability)](https://codeclimate.com/github/propero-oss/security/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/70945023a0a33ac65fbb/test_coverage)](https://codeclimate.com/github/propero-oss/security/test_coverage)


# @propero/security
safe and simple handling of passwords, tokens, etc

## Installation
```shell
pnpm add @propero/security
```
```shell
npm i @propero/secutiry
```
```shell
yarn add @propero/security
```

## Passwords
Please use argon2 if possible, as at the time of writing this (21/04/2023), it is the most secure supported adapter.

### Supported Adapters
- argon2
- scrypt
- bcrypt
- pbkdf2


### Usage
```typescript
import { Password } from "@propero/security"
import { argon2 } from "@propero/security/password/adapter/argon2";
import { scrypt } from "@propero/security/password/adapter/scrypt";
import { bcrypt2 } from "@propero/security/password/adapter/bcrypt";
import { pbkdf2 } from "@propero/security/password/adapter/pbkdf2";

// register various adapters
Password
  .use(scrypt())
  .use(pbkdf2());

// create new hash and salt
// this will use the highest priority adapter, in this case scrypt
const password = await Password.hash("foo");

// serialise to string
const text = password.toString();

// parse password strings
const parset = new Password(text);

// verify passwords
await password.verify("foo") // true
await password.verify("bar") // false
await parsed.verify("foo") // true

// add adapters at any point
Password
  .use(bcrypt2())
  .use(argon2());

// upgrade passwords in place
const before = password.hash;
await password.verifyAndUpgrade("foo"); // true
password.hash === before // false, now uses argon2

// usage with typeorm
@Entity("user")
export class User {
  @PrimaryGeneratedColumn("uuid")
  uuid!: string;

  @Column("varchar")
  username!: string;

  // ... user fields

  @Column("varchar", {
    transformer: {
      to: (password) => password.toString(),
      from: (hash) => new Password(hash),
    }
  })
  password!: Password;

}
```

### Writing your own adapters
Any object matching the `PasswordHashAdapter` is a valid adapter.

```typescript
import { Password, PasswordHashAdapter } from "@propero/security";

function plainText(): PasswordHashAdapter {
  return {
    priority: -1, // higher numbers are preferred, 0 is pbkdf2, 40 is argon2
    initialise: async () => {
      // this is run once before the first hash is created or verified
      // it can be omitted
      console.log("don't ever use plain text!!!");
    },
    hashAndSalt: async (password: string) => "$plain$" + password,
    hashMatchesStrategy: (hash: string) => hash.startsWith("$plain$"),
    verify: async (password: string, hash: string) => hash.slice(7) === password,
    hashNeedsUpgrade: async (hash: string) => false,
  }
}

Password.use(plainText());

```
npm package: @propero/typescript-transform-vue-templates
Description: Transform Vue template strings into render functions
Author: Propero Team
Latest version: 1.0.0

# typescript-transform-vue-templates

[![Maintainability](https://api.codeclimate.com/v1/badges/be2ceceb901ab637dbc5/maintainability)](https://codeclimate.com/github/propero-oss/typescript-transform-vue-templates/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/be2ceceb901ab637dbc5/test_coverage)](https://codeclimate.com/github/propero-oss/typescript-transform-vue-templates/test_coverage)
[![Build Status](https://travis-ci.com/propero-oss/typescript-transform-vue-templates.svg?branch=master)](https://travis-ci.com/propero-oss/typescript-transform-vue-templates)
[![npm (scoped)](https://img.shields.io/npm/v/@propero/typescript-transform-vue-templates)](https://www.npmjs.com/package/@propero/typescript-transform-vue-templates)
[![License](https://img.shields.io/npm/l/@propero/typescript-transform-vue-templates)](LICENSE.md)

Template for Typescript Libraries
npm package: @propero/typescript-transformer-source-templates
Description: Generate fresh ASTs from source strings
Author: Propero Team
Latest version: 1.1.0

# @propero/typescript-transformer-source-templates

[![Maintainability](https://api.codeclimate.com/v1/badges/0e6222501c8f6d1b7af9/maintainability)](https://codeclimate.com/github/propero-oss/typescript-transformer-source-templates/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/0e6222501c8f6d1b7af9/test_coverage)](https://codeclimate.com/github/propero-oss/typescript-transformer-source-templates/test_coverage)
[![Build Status](https://travis-ci.com/propero-oss/typescript-transformer-source-templates.svg?branch=master)](https://travis-ci.com/propero-oss/typescript-transformer-source-templates)

Generate fresh ASTs from source strings

```shell script
npm i @propero/typescript-transformer-source-templates
```

## Example Usage
```typescript
import { template } from "@propero/typescript-transformer-source-templates";

const aggregationTemplate = template(`
let VARIABLE = control.getAggregation("AGGREGATION");
if (!Array.isArray(VARIABLE)) VARIABLE = VARIABLE == null ? [] : [VARIABLE];
VARIABLE.forEach(it => rm.renderControl(it));
`);
const headerNodes = aggregationTemplate.statements({ AGGREGATION: 'header', VARIABLE: "_header" });
const contentNodes = aggregationTemplate.statements({ AGGREGATION: 'content', VARIABLE: () => "_content" });
const footerNodes = aggregationTemplate.statements({ AGGREGATION: 'footer' });
```

#### Casting and member templates
```typescript
import { template } from "@propero/typescript-transformer-source-templates";
import { MethodDeclaration } from "typescript";

const methodTemplate = template("render() {RENDER_CODE}");
const render = methodTemplate.objectMember<MethodDeclaration>({ RENDER_CODE: "return 'hello world';" });
```
npm package: @propero/vue-extra-decorators
Description: Additional decorators built on vue-class-component
Author: Propero Team
Latest version: 2.0.2
Deprecated: This package has moved to @rocketbase/vue-extra-decorators and will no longer be maintained here



# Vue Extra Decorators

![GitHub issues](https://img.shields.io/github/issues-raw/propero-oss/vue-extra-decorators)
![npm](https://img.shields.io/npm/dw/@propero/vue-extra-decorators)
![npm (scoped)](https://img.shields.io/npm/v/@propero/vue-extra-decorators)
![npm type definitions](https://img.shields.io/npm/types/@propero/vue-extra-decorators)
![Libraries.io dependency status for latest release, scoped npm package](https://img.shields.io/librariesio/release/npm/@propero/vue-extra-decorators)
![NPM](https://img.shields.io/npm/l/@propero/vue-extra-decorators)
[![Maintainability](https://api.codeclimate.com/v1/badges/f28e27b3e635525f9ccc/maintainability)](https://codeclimate.com/github/propero-oss/vue-extra-decorators/maintainability)


#### Additional decorators built on vue-class-component

```SH
npm i @propero/vue-extra-decorators
```

### Documentation
Documentation can be found inside the [docs](docs/vue-extra-decorators.md) folder.
npm package: @propero/web-utils
Description: various utilities for working with browsers and websites
Author: Propero Team
Latest version: 1.0.0

[![Build Status](https://app.travis-ci.com/propero-oss/curve-2d.svg?branch=master)](https://app.travis-ci.com/propero-oss/curve-2d)
[![Maintainability](https://api.codeclimate.com/v1/badges/68e1717b587f89ed0a75/maintainability)](https://codeclimate.com/github/propero-oss/curve-2d/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/68e1717b587f89ed0a75/test_coverage)](https://codeclimate.com/github/propero-oss/curve-2d/test_coverage)
[![npm](https://img.shields.io/npm/v/@rdme/curve-2d)](https://www.npmjs.com/package/@rdme/curve-2d)
[![NPM](https://img.shields.io/npm/l/@rdme/curve-2d)](LICENSE.md)
![Types](https://img.shields.io/npm/types/@rdme/curve-2d)

# Curve 2d
simple 2d curve library supporting beziers, catmulls, arcs,
lines and any combinations thereof. Also contains some vec2d utils.


## Installing
```
npm i @rdme/curve-2d
```
```
yard add @rdme/curve-2d
```
```
pnpm i @rdme/curve-2d
```

## Documentation
TODO: Docs