2.10.0 • Published 9 months ago

@clipboard-health/util-ts v2.10.0

Weekly downloads
-
License
MIT
Repository
-
Last release
9 months ago

@clipboard-health/util-ts

TypeScript utilities.

Table of contents

Install

npm install @clipboard-health/util-ts

Usage

See ./src/lib for each utility.

ServiceError

// ./examples/serviceError.ts

import { deepEqual, equal } from "node:assert/strict";

import { ERROR_CODES, ServiceError } from "@clipboard-health/util-ts";
import { z } from "zod";

{
  const error = new ServiceError("boom");
  equal(error.toString(), `ServiceError[${error.id}]: [internal]: boom`);
}

try {
  throw new Error("boom");
} catch (error) {
  const serviceError = ServiceError.fromUnknown(error);
  equal(serviceError.toString(), `ServiceError[${serviceError.id}]: [internal]: boom`);
}

{
  const serviceError = ServiceError.fromZodError(
    new z.ZodError([{ code: "custom", path: ["foo"], message: "boom" }]),
  );
  equal(serviceError.toString(), `ServiceError[${serviceError.id}]: [unprocessableEntity]: boom`);
}

{
  const errorWithCause = new ServiceError({
    issues: [{ message: "boom" }],
    cause: new Error("Original error"),
  });
  equal(errorWithCause.toString(), `ServiceError[${errorWithCause.id}]: [internal]: boom`);
}

{
  const multipleIssues = new ServiceError({
    issues: [
      {
        code: ERROR_CODES.badRequest,
        message: "Invalid email format",
        path: ["data", "attributes", "email"],
      },
      {
        code: ERROR_CODES.unprocessableEntity,
        message: "Phone number too short",
        path: ["data", "attributes", "phoneNumber"],
      },
    ],
    cause: new Error("Original error"),
  });

  equal(
    multipleIssues.toString(),
    `ServiceError[${multipleIssues.id}]: [badRequest]: Invalid email format; [unprocessableEntity]: Phone number too short`,
  );

  deepEqual(multipleIssues.toJsonApi(), {
    errors: [
      {
        id: multipleIssues.id,
        status: "400",
        code: "badRequest",
        title: "Invalid or malformed request",
        detail: "Invalid email format",
        source: {
          pointer: "/data/attributes/email",
        },
      },
      {
        id: multipleIssues.id,
        status: "422",
        code: "unprocessableEntity",
        title: "Request failed validation",
        detail: "Phone number too short",
        source: {
          pointer: "/data/attributes/phoneNumber",
        },
      },
    ],
  });
}

ServiceResult

// ./examples/serviceResult.ts

import { ok } from "node:assert/strict";

import {
  either as E,
  ERROR_CODES,
  failure,
  type ServiceResult,
  success,
} from "@clipboard-health/util-ts";

function validateUser(params: { email: string; phone: string }): ServiceResult<{ id: string }> {
  const { email, phone } = params;
  const code = ERROR_CODES.unprocessableEntity;

  if (!email.includes("@")) {
    return failure({ issues: [{ code, message: "Invalid email format" }] });
  }

  if (phone.length !== 12) {
    return failure({ issues: [{ code, message: "Invalid phone number" }] });
  }

  return success({ id: "user-123" });
}

ok(E.isLeft(validateUser({ email: "invalidEmail", phone: "invalidPhoneNumber" })));
ok(E.isRight(validateUser({ email: "user@example.com", phone: "555-555-5555" })));

Functional

pipe

// ./examples/pipe.ts

import { equal } from "node:assert/strict";

import { pipe } from "@clipboard-health/util-ts";

const result = pipe(
  "  hello world  ",
  (s) => s.trim(),
  (s) => s.split(" "),
  (array) => array.map((word) => word.charAt(0).toUpperCase() + word.slice(1)),
  (array) => array.join(" "),
);

equal(result, "Hello World");

option

// ./examples/option.ts

import { equal } from "node:assert/strict";

import { option as O, pipe } from "@clipboard-health/util-ts";

function double(n: number) {
  return n * 2;
}

function inverse(n: number): O.Option<number> {
  return n === 0 ? O.none : O.some(1 / n);
}

const result = pipe(
  O.some(5),
  O.map(double),
  O.flatMap(inverse),
  O.match(
    () => "No result",
    (n) => `Result is ${n}`,
  ),
);

equal(result, "Result is 0.1");

either

// ./examples/either.ts

import { equal } from "node:assert/strict";

import { either as E, pipe } from "@clipboard-health/util-ts";

function double(n: number): number {
  return n * 2;
}

function inverse(n: number): E.Either<string, number> {
  return n === 0 ? E.left("Division by zero") : E.right(1 / n);
}

const result = pipe(
  E.right(5),
  E.map(double),
  E.flatMap(inverse),
  E.match(
    (error) => `Error: ${error}`,
    (result) => `Result is ${result}`,
  ),
);

equal(result, "Result is 0.1");

Local development commands

See package.json scripts for a list of commands.

2.10.0

9 months ago

2.9.4

9 months ago

2.9.6

9 months ago

2.9.5

9 months ago

2.9.7

9 months ago

2.9.3

9 months ago

2.9.2

9 months ago

2.9.1

9 months ago

2.9.0

9 months ago

2.8.8

9 months ago

2.8.7

9 months ago

2.8.6

9 months ago

2.8.5

9 months ago

2.8.4

9 months ago

2.8.3

9 months ago

2.8.2

9 months ago

2.8.1

9 months ago

2.8.0

9 months ago

2.7.0

9 months ago

2.6.0

10 months ago

2.5.0

10 months ago

2.4.0

10 months ago

2.3.0

10 months ago

2.2.8

10 months ago

2.2.7

10 months ago

2.2.6

10 months ago

0.0.0

11 months ago

2.2.5

11 months ago

2.2.4

11 months ago

2.2.3

11 months ago

2.2.2

11 months ago

2.2.1

11 months ago

2.2.0

11 months ago

2.1.3

11 months ago

2.1.2

11 months ago

2.1.1

11 months ago

2.1.0

12 months ago

2.0.1

12 months ago

1.2.1

1 year ago

1.2.0

1 year ago

1.1.0

1 year ago

1.0.0

2 years ago

0.11.7

2 years ago

0.11.6

2 years ago

0.11.5

2 years ago

0.11.4

2 years ago

0.11.3

2 years ago

0.11.2

2 years ago

0.11.1

2 years ago

0.11.0

2 years ago

0.10.0

2 years ago

0.9.1

2 years ago

0.9.0

2 years ago

0.8.0

2 years ago

0.7.4

2 years ago

0.7.3

2 years ago

0.7.2

2 years ago

0.7.1

2 years ago

0.7.0

2 years ago

0.6.1

2 years ago

0.6.0

2 years ago

0.5.9

2 years ago

0.5.8

2 years ago

0.5.7

2 years ago

0.5.6

2 years ago

0.5.5

2 years ago

0.5.4

2 years ago

0.5.3

2 years ago

0.5.2

2 years ago

0.5.1

2 years ago

0.5.0

2 years ago

0.4.0

2 years ago

0.3.4

2 years ago

0.3.3

2 years ago

0.3.2

2 years ago

0.3.1

2 years ago

0.3.0

2 years ago

0.2.0

2 years ago

0.1.0

2 years ago