@o3osatoshi/toolkit
v1.1.0
Published
Utility helpers for error shaping and Zod integration, designed for TypeScript apps.
Maintainers
Readme
@o3osatoshi/toolkit
Utility helpers for error shaping and Zod integration, designed for TypeScript apps.
Install
pnpm add @o3osatoshi/toolkit # runtime
pnpm add -D typescript vitest # dev (optional for typecheck/tests)Quick Start
Structured errors
import { newError } from "@o3osatoshi/toolkit";
throw newError({
layer: "Application",
kind: "Validation",
action: "CreateUser",
reason: "email format is invalid",
impact: "user cannot be registered",
hint: "ensure email has @",
cause: new Error("zod: Expected string, received number"),
});
// name: ApplicationValidationError
// message example: "CreateUser failed because email format is invalid. Impact: user cannot be registered. Hint: ensure email has @. Cause: zod: Expected string, received number."Zod integration
import { z } from "zod";
import { parseWith, parseAsyncWith, newZodError } from "@o3osatoshi/toolkit";
const userSchema = z.object({ name: z.string(), age: z.number().min(0) });
const parseUser = parseWith(userSchema, { action: "ParseUser", layer: "UI" });
const res = parseUser({ name: "alice", age: 20 }); // Result<User, Error>
// Async
const parseUserAsync = parseAsyncWith(userSchema, { action: "ParseUser", layer: "UI" });
const resAsync = await parseUserAsync({ name: "alice", age: 20 }); // ResultAsync<User, Error>
// When you catch a ZodError and want a normalized Error:
try {
userSchema.parse({ name: 1 });
} catch (e) {
throw newZodError({ action: "ParseUser", cause: e });
}Error kinds
newError expects a kind that conveys how callers or HTTP layers should react. The defaults below are what toHttpErrorResponse uses when translating errors into status codes (override as needed):
| Kind | Default HTTP status | Description |
| ---------------- | ------------------- | ----------- |
| BadRequest | 400 | Malformed payload or transport-level input issue caught before validation. |
| Validation | 400 | Domain/application validation failure (e.g., Zod, business rules). |
| Unauthorized | 401 | Authentication missing, expired, or invalid. |
| Forbidden | 403 | Authenticated caller lacks permission. |
| NotFound | 404 | Entity, route, or resource is missing. |
| MethodNotAllowed | 405 | HTTP verb is not supported for the requested resource. |
| Conflict | 409 | Version/state conflict such as optimistic locking. |
| Integrity | 409 | Constraint violation (unique/index/foreign key). |
| Deadlock | 409 | Database or concurrency deadlock detected by the engine. |
| Unprocessable | 422 | Semantically invalid request despite being well-formed. |
| RateLimit | 429 | Quota exceeded or throttling triggered. |
| Canceled | 408 | Caller aborted or connection closed mid-flight. |
| Config | 500 | Server-side misconfiguration or missing secrets. |
| Serialization | 500 | Encode/decode failure when handling data. |
| Unknown | 500 | Fallback when no other kind matches. |
| BadGateway | 502 | Upstream dependency returned an invalid or 5xx response. |
| Unavailable | 503 | Dependency temporarily offline or service paused. |
| Timeout | 504 | Operation exceeded configured timeout. |
Note: Some gateways use non‑standard 499 (Client Closed Request). The default mapping here uses 408 for broader compatibility.
API
newError(opts)- Builds an
Errorwith a consistentname(<Layer><Kind>Error) and richmessagecomposed fromaction,reason,impact,hint, and a summarizedcause.
- Builds an
isZodError(e)- Robust detection using
instanceofor duck-typing fallback when Zod instances differ.
- Robust detection using
summarizeZodIssue(issue)/summarizeZodError(err)- Human‑readable messages for common Zod issues.
newZodError(opts)- Wraps a
ZodError(or issues array) into a structuredErrorvianewError.
- Wraps a
parseWith(schema, ctx)/parseAsyncWith(schema, ctx)- Create functions returning
neverthrowResult/ResultAsyncfrom a Zod schema. Errors are normalized vianewZodError.
- Create functions returning
createEnv(schema, opts?)- Validate environment variables with Zod and return a fully typed configuration object. Accepts an optional
namelabel (used in error messages) andsourcemap (useful for tests or SSR).
- Validate environment variables with Zod and return a fully typed configuration object. Accepts an optional
Env- String union type for canonical deployment environments (
"development" | "local" | "production" | "staging"), shared across packages (for example, telemetry configuration) to avoid ad‑hoc string literals.
- String union type for canonical deployment environments (
EnvOf<T>,EnvSchema- Helper types for
createEnv.EnvSchemamodels the map of variable names to Zod validators, andEnvOf<T>infers the resulting runtime shape from that schema.
- Helper types for
@o3osatoshi/toolkit/next- Helpers for Next.js Server Actions. See below.
Next.js helpers (@o3osatoshi/toolkit/next)
Lightweight utilities for useActionState and Server Actions.
import {
err,
ok,
type ActionState,
userMessageFromError,
} from "@o3osatoshi/toolkit/next";
// Server Action example
export const createItem = async (
_prev: ActionState | undefined,
formData: FormData,
): Promise<ActionState> => {
try {
const data = await doSomething(formData);
return ok(data);
} catch (e) {
return err(e as Error); // user-facing message derived from toolkit error metadata
}
};err(error)– acceptsError | ActionError | stringand returns{ ok: false, error }with a user-friendly message derived from@o3osatoshi/toolkiterrors (kind,reason,hint,impactare considered). Falls back to a generic safe message.ok(data)– wraps success payload as{ ok: true, data }.userMessageFromError(error)– converts anError(ideally produced bynewError) into user-facing copy, using kind-based defaults and any structured message fields.
Notes
- ESM + CJS outputs are provided; typings are included.
- Target Node >= 22 (see
engines). - When targeting different Zod instances (e.g., multiple versions),
isZodErroruses duck typing to remain resilient. pnpm -C packages/toolkit devusestsup --watch --no-cleanto avoid briefdistremovals that can break dependent watch builds (e.g.@o3osatoshi/logging). Runpnpm -C packages/toolkit cleanafter export changes to prevent stale artifacts.
