
Research
/Security News
Shai Hulud Strikes Again (v2)
Another wave of Shai-Hulud campaign has hit npm with more than 500 packages and 700+ versions affected.
@clipboard-health/util-ts
Advanced tools
TypeScript utilities.
npm install @clipboard-health/util-ts
See ./src/lib for each utility.
import { deepEqual, strictEqual } from "node:assert/strict";
import { ERROR_CODES, ServiceError } from "@clipboard-health/util-ts";
import { z } from "zod";
{
const error = new ServiceError("boom");
strictEqual(error.toString(), `ServiceError[${error.id}]: [internal]: boom`);
}
try {
throw new Error("boom");
} catch (error) {
const serviceError = ServiceError.fromUnknown(error);
strictEqual(serviceError.toString(), `ServiceError[${serviceError.id}]: [internal]: boom`);
}
{
const serviceError = ServiceError.fromZodError(
new z.ZodError([{ code: "custom", path: ["foo"], message: "boom" }]),
);
strictEqual(serviceError.toString(), `ServiceError[${serviceError.id}]: [badRequest]: boom`);
}
{
const errorWithCause = new ServiceError({
issues: [{ message: "boom" }],
cause: new Error("Original error"),
});
strictEqual(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"),
});
strictEqual(
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",
},
},
],
});
}
import { ok } from "node:assert/strict";
import {
ERROR_CODES,
failure,
isFailure,
isSuccess,
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(isFailure(validateUser({ email: "invalidEmail", phone: "invalidPhoneNumber" })));
ok(isSuccess(validateUser({ email: "user@example.com", phone: "555-555-5555" })));
tryCatchAsyncimport { ok, strictEqual } from "node:assert/strict";
import { isFailure, isSuccess, ServiceError, tryCatchAsync } from "@clipboard-health/util-ts";
async function example() {
const successResult = await tryCatchAsync(
async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
return (await response.json()) as { id: number };
},
(error) => new ServiceError(`Failed to fetch: ${String(error)}`),
);
ok(isSuccess(successResult));
strictEqual(successResult.value.id, 1);
const failureResult = await tryCatchAsync(
async () => await Promise.reject(new Error("Network error")),
(error) => new ServiceError(`Failed to fetch: ${String(error)}`),
);
ok(isFailure(failureResult));
strictEqual(failureResult.error.issues[0]?.message, "Failed to fetch: Error: Network error");
}
// eslint-disable-next-line unicorn/prefer-top-level-await
void example();
tryCatchimport { ok, strictEqual } from "node:assert/strict";
import { isFailure, isSuccess, parseJson, ServiceError, tryCatch } from "@clipboard-health/util-ts";
const successResult = tryCatch(
() => parseJson<{ name: string }>('{"name": "John"}'),
(error) => new ServiceError(`Parse error: ${String(error)}`),
);
ok(isSuccess(successResult));
strictEqual(successResult.value.name, "John");
const failureResult = tryCatch(
() => parseJson("invalid json"),
(error) => new ServiceError(`Parse error: ${String(error)}`),
);
ok(isFailure(failureResult));
ok(failureResult.error.issues[0]?.message?.includes("Parse error"));
fromSafeParseReturnTypeimport { ok, strictEqual } from "node:assert/strict";
import { fromSafeParseReturnType, isFailure, isSuccess } from "@clipboard-health/util-ts";
import { z } from "zod";
const schema = z.object({ name: z.string(), age: z.number() });
const validData = { name: "John", age: 30 };
const successResult = fromSafeParseReturnType(schema.safeParse(validData));
ok(isSuccess(successResult));
strictEqual(successResult.value.name, "John");
const invalidData = { name: "John", age: "thirty" };
const failureResult = fromSafeParseReturnType(schema.safeParse(invalidData));
ok(isFailure(failureResult));
ok(failureResult.error.issues.length > 0);
pipeimport { strictEqual } 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(" "),
);
strictEqual(result, "Hello World");
optionimport { strictEqual } 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}`,
),
);
strictEqual(result, "Result is 0.1");
eitherimport { strictEqual } 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}`,
),
);
strictEqual(result, "Result is 0.1");
See package.json scripts for a list of commands.
FAQs
TypeScript utilities.
The npm package @clipboard-health/util-ts receives a total of 39,262 weekly downloads. As such, @clipboard-health/util-ts popularity was classified as popular.
We found that @clipboard-health/util-ts demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Another wave of Shai-Hulud campaign has hit npm with more than 500 packages and 700+ versions affected.

Product
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.

Security News
ENISA has become a CVE Program Root, giving the EU a central authority for coordinating vulnerability reporting, disclosure, and cross-border response.