3.6.0 • Published 6 months ago

@clipboard-health/util-ts v3.6.0

Weekly downloads
-
License
MIT
Repository
github
Last release
6 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

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

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

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

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

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.11.0

1 year ago

2.11.1

1 year ago

3.2.2

8 months ago

3.2.1

8 months ago

3.2.0

8 months ago

3.6.0

6 months ago

3.2.3

7 months ago

2.11.2

1 year ago

2.11.3

1 year ago

3.1.3

9 months ago

3.1.2

9 months ago

3.1.1

9 months ago

3.1.0

10 months ago

3.1.7

8 months ago

3.1.6

8 months ago

3.1.5

9 months ago

2.14.7

10 months ago

3.5.0

6 months ago

3.1.4

9 months ago

2.14.5

11 months ago

2.14.6

11 months ago

2.14.3

11 months ago

2.14.4

11 months ago

2.14.1

1 year ago

2.14.2

1 year ago

2.14.0

1 year ago

3.4.0

6 months ago

3.0.2

10 months ago

3.0.1

10 months ago

3.0.0

10 months ago

2.13.0

1 year ago

2.12.0

1 year ago

3.3.1

7 months ago

3.3.0

7 months ago

3.3.4

6 months ago

3.3.3

6 months ago

3.3.2

7 months ago

2.12.1

1 year ago

2.10.0

1 year ago

2.9.4

1 year ago

2.9.6

1 year ago

2.9.5

1 year ago

2.9.7

1 year ago

2.9.3

1 year ago

2.9.2

1 year ago

2.9.1

1 year ago

2.9.0

1 year ago

2.8.8

1 year ago

2.8.7

1 year ago

2.8.6

1 year ago

2.8.5

1 year ago

2.8.4

1 year ago

2.8.3

1 year ago

2.8.2

1 year ago

2.8.1

1 year ago

2.8.0

1 year ago

2.7.0

1 year ago

2.6.0

1 year ago

2.5.0

1 year ago

2.4.0

1 year ago

2.3.0

1 year ago

2.2.8

1 year ago

2.2.7

1 year ago

2.2.6

1 year ago

0.0.0

1 year ago

2.2.5

1 year ago

2.2.4

1 year ago

2.2.3

1 year ago

2.2.2

1 year ago

2.2.1

1 year ago

2.2.0

1 year ago

2.1.3

1 year ago

2.1.2

1 year ago

2.1.1

1 year ago

2.1.0

1 year ago

2.0.1

1 year ago

1.2.1

1 year ago

1.2.0

2 years ago

1.1.0

2 years 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

3 years ago

0.5.7

3 years ago

0.5.6

3 years ago

0.5.5

3 years ago

0.5.4

3 years ago

0.5.3

3 years ago

0.5.2

3 years ago

0.5.1

3 years ago

0.5.0

3 years ago

0.4.0

3 years ago

0.3.4

3 years ago

0.3.3

3 years ago

0.3.2

3 years ago

0.3.1

3 years ago

0.3.0

3 years ago

0.2.0

3 years ago

0.1.0

3 years ago