tug-ts
TypeScript icon, indicating that this package has built-in type declarations

0.0.11 • Public • Published

tug

Write testable code.

TODO

Install

TODO

Getting started

Define the dependencies

import { CreateContext } from "tug";

type WebAppDependencies = {
  db: () => Promise<Database>;
  currentUserId: () => Promise<string | undefined>;
};
type WebAppCtx = CreateContext<WebAppDependencies>;

Construct tugs

import { Tug } from "tug";

const getOrderById = (id: string) =>
  Tug(async (ctx: WebAppCtx) => {
    const db = await ctx.db();
    const order = await db.collection<OrderData>("orders").findOne({ id });
    if (order == null) {
      throw new Error("order does not exist");
    }
    return order;
  });

const getUserById = (id: string) =>
  Tug(async (ctx: WebAppCtx) => {
    const db = await ctx.db();
    const user = await db.collection<UserData>("users").findOne({ id });
    if (user == null) {
      throw new Error("user does not exist");
    }
    return user;
  });

const canUserEditOrder = (userId: string, orderId: string) =>
  Tug(async (ctx: WebAppCtx) => {
    const user = await ctx.use(getUserById(userId));
    if (user.isAdmin) {
      return true;
    }
    const order = await ctx.use(getOrderById(orderId));
    return order.userId === user.id;
  });

Execute the tug

const deps = { db: databaseGetter };
const userId = "user-123";
const orderId = "order-123";

// returns the `tug` value if the computation succeeds or throws if it fails
const result = await canUserEditOrder(userId, orderId).exec(deps);

Examples

TODO

API Overview

Types

TODO

type WebAppDependencies = {
  db: () => Promise<Database>;
  currentUserId: () => Promise<string | undefined>;
};
type WebAppTug<A> = tug<WebAppDependencies, A>;
type WebAppCtx = CreateContext<WebAppDependencies>;

Context & use function

The tug context object contains the dependencies provided at execution time, and the use function.

TODO

TODO;

TIP: use react promise server TODO

Constructors

const ok1 = Tug.of(1);
const ok2 = Tug(async (_ctx) => 2);
const ok3 = Tug(() => 3);
const ok4 = Tug.right(4);

const fail1 = Tug.left(new Error("it failed"));
const fail2 = Tug(async () => {
  throw new Error("it failed");
});
const fail3 = Tug(() => Promise.reject(new Error("it failed")));

Transform

tug transform

Applies a function f to the current tug value, if the current value is successful. f works like the Tug constructor.

f can use the tug context and it returns a value or promise. Rejections and errors make the tug fail.

const getLoggedInUserId = () =>
  Tug(async (ctx: WebAppCtx) => {
    const userId = await ctx.currentUserId();
    if (userId == null) {
      throw new Error("User is not logged in");
    }
    return userId;
  });

const getLoggedInUser = () =>
  getLoggedInUserId().tug(async (id, ctx) => {
    const db = await ctx.db();
    const user = await db.collection<UserData>("users").findOne({ id });
    if (user == null) {
      throw new Error("user does not exist");
    }
    return user;
  });

Map

Returns a new tug with the contents of the current one applied to function f, if the current one is successful.

const v0 = Tug.of(0);
const v1 = v0.map((it) => it + 1);
console.log(v1.exec({})); // 2

Flat Map & Chain

Returns a new tug with the contents of the current one applied to function f, if the current one is successful.

f must return a tug which will be flattened out with the current result.

const v0 = Tug.of(0);
const v1 = v0.flatMap((it) => Tug.of(it + 1)); // or v0.chain((it) => Tug.of(it + 1));
console.log(v1.exec({})); // 2

Value-Based Exceptions

Use execEither to execute the tug and get the result as an Either.

// returns an Either<any, T> where T is the tug value
const either = await canUserEditOrder(userId, orderId).execEither(deps);
expect(either).toEqual({ _tag: "Right", right: true });

An Either is defined as

type Left<E> = {
  readonly _tag: "Left";
  readonly left: E;
};
type Right<A> = {
  readonly _tag: "Right";
  readonly right: A;
};
type Either<E, A> = Left<E> | Right<A>;

Why use tug?

  • Typed Dependency Injection
  • Value-Based Exceptions
  • Retryable Promises
  • 0 dependencies
  • Less than 2kb (minified & gzipped)

Alternatives

tug is inspired by fp-ts ReaderTaskEither and effect-ts, but focuses on:

  • a simpler API that's easier to learn
  • fluent API inspired by zio
  • simpler Typescript compile errors
  • no focus on pure functional programming and HKT

Fp-ts compatibility

TODO: The internal state of tug is a ReaderTaskEither and can be accessed with the .rte accessor. TODO make it method

Readme

Keywords

none

Package Sidebar

Install

npm i tug-ts

Weekly Downloads

157

Version

0.0.11

License

MIT

Unpacked Size

51.3 kB

Total Files

14

Last publish

Collaborators

  • degroote22