
Research
TeamPCP Compromises Telnyx Python SDK to Deliver Credential-Stealing Malware
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.
okay-error
Advanced tools
A small opinionated library to bring Rust-like results idiomatically to TypeScript.
okay-errorTyped, chain‑friendly, JSON‑safe Results for TypeScript
A small opinionated TypeScript library providing strongly-typed Result objects with chaining capabilities, inspired by Rust std::result.
Ok is { ok: true, value }, an Err is { ok: false, error }. Log it, persist it, send it over the wire.Result<T, E>), not thrown from the shadows. Rely on the type checker to ensure you handle every possible failure.cause() helper; walk the cause links later to see the full logical call stack.map, flatMap, or feel familiar to JS arrays.JSON.parse, call result to get a plain Result object.okay-error
npm i okay-error
Here's how okay-error changes error handling from exceptions to data:
// Traditional approach with try-catch
try {
const user = getUserById(123);
const greeting = formatGreeting(user.name);
console.log(greeting);
} catch (error) {
// Error source and type information can be ambiguous
console.error('Something went wrong', error);
}
// Alternative approach with Result
import { ok, err, result, annotate } from 'okay-error';
// Define functions that return Result types
function getUserById(id: number) {
try {
if (id <= 0) {
return err('InvalidId', { id });
}
// Simulating database lookup
const user = { id, name: 'Ada' };
return ok(user);
} catch (error) {
// Convert any unexpected errors
return err('DbError', { cause: error });
}
}
// Using the Result-returning function
const userResult = getUserById(123);
if (!userResult.ok) {
// Typed error handling with precise context
console.error(`Database error: ${userResult.error.type}`);
return;
}
// Chain operations on successful results
const greeted = userResult
.map(u => u.name.toUpperCase()) // Ok<string>
.flatMap(name =>
name.startsWith('A')
? ok(`Hello ${name}!`) // Return Ok for success
: err('NameTooShort', { min: 1 }) // Return Err for failure
)
.or('Hi stranger!'); // Use fallback if any step failed
console.log(greeted); // "Hello ADA!"
Context propagation allows you to wrap lower-level errors with higher-level context as they move up through your application's layers so you know where the error occurred.
function readConfig(): Result<string, ConfigErr> { /* ... */ }
function boot(): Result<void, BootErr> {
const cfg = readConfig();
if (!cfg.ok) {
// Add higher-level context while preserving the original error
return err('BootConfig', { phase: 'init', ...cause(cfg) });
}
return ok();
}
cause creates a new object { cause: error } that can be spread into your error payload. This creates a discoverable, traceable error chain that's useful for debugging:
Err {
type: "BootConfig",
phase: "init",
cause: Err {
type: "ConfigFileMissing",
path: "/etc/app.json",
cause: Err { type: "IO", errno: "ENOENT" }
}
}
okay-error can be used with async code to handle errors as data:
import { result } from 'okay-error';
// Wrap fetch with Result to handle both network and parsing errors
async function fetchUserData(userId: string) {
// First, handle the network request
const response = await result(fetch(`/api/users/${userId}`));
if (!response.ok) {
return annotate(response, 'NetworkError', { userId });
}
// Then handle the JSON parsing
const data = await result(response.value.json());
if (!data.ok) {
return annotate(data, 'ParseError', { userId });
}
// Validate the data
if (!data.value.name) {
return err('ValidationError', {
userId,
message: 'User name is required'
});
}
return ok(data.value);
}
// Usage with proper error handling
async function displayUserProfile(userId: string) {
const userData = await fetchUserData(userId);
if (!userData.ok) {
// Each error has context about where it happened
switch (userData.error.type) {
case 'NetworkError':
console.error('Connection failed');
break;
case 'ParseError':
console.error('Invalid response format');
break;
case 'ValidationError':
console.error(userData.error.message);
break;
}
return;
}
// Work with the data safely
console.log(`Welcome, ${userData.value.name}!`);
}
| ✔ | Feature | Example |
|---|---|---|
| Typed constructors | err({ type: 'Timeout', ms: 2000 }) or err('Timeout', { ms: 2000 }) | |
map, flatMap, or | ok(1).map(x=>x+1).flatMap(fn).or(0) | |
| Works with Promise | await result(fetch(url)) | |
| Cause‑chain + optional stack frame | annotate(err(...), 'DB', {...}) | |
| JSON serialisable | JSON.stringify(err('X', {})) | |
| Re‑hydrate after JSON | const plain = result(JSON.parse(raw)) |
| function | purpose |
|---|---|
ok(value) | success result |
err(type, payload?) | typed error, payload is merged with { type } |
err({ ... }) | error from arbitrary value (object, string, etc) |
result(x) | wrap a sync fn, a Promise, or re‑hydrate a raw object |
| function | purpose |
|---|---|
cause(error) | wrap an error as a cause for another error |
match(result, { ok, err }) | pattern match on Result (success/failure) |
match(type, cases) | pattern match on a discriminant string (exhaustive) |
type Result<T, E = unknown> = Ok<T> | Err<E>;
const errOut = err('DbConn', { host: 'db.local' }); // preferred
const raw = JSON.stringify(errOut);
const back = result(JSON.parse(raw)); // re‑hydrated
import { err, cause } from 'okay-error';
// Preferred: use err(type, payload) and cause()
const ioError = err('IO', { errno: 'ENOENT' });
const configError = err('ConfigFileMissing', { path: '/etc/app.json', ...cause(ioError) });
const bootError = err('BootConfig', { phase: 'init', ...cause(configError) });
// You can also chain inline:
const chained = err('BootConfig', cause(
err('ConfigFileMissing', cause(
err('IO', { errno: 'ENOENT' })
))
));
// Now you can navigate the error chain
console.log(bootError.error.type); // 'BootConfig'
console.log(bootError.error.cause.type); // 'ConfigFileMissing'
cause() helperThe cause(error) function is the idiomatic way to link any parent error as the cause of the current error—this parent could be a lower-level error, a related error, or any error that led to the current one:
const base = err('Base', { info: 123 })
const wrapped = err('Higher', { ...cause(base), context: 'extra' })
// wrapped.error.cause === base
This is preferred over annotate, and is composable for deep error chains.
matchThe match function is overloaded:
match(result, { ok, err }) to branch on Result objects.match(type, { ...cases }) to branch on discriminant string unions (exhaustive, type-safe).matchType is now an alias for the discriminant string overload for backwards compatibility.// Result matching
const result = divide(10, 2);
const message = match(result, {
ok: (value) => `Result: ${value}`,
err: (error) => `Error: ${error.message}`
});
console.log(result); // "Result: 5"
// With an error case
const errorResult = divide(10, 0).match({
ok: (value) => `Result: ${value}`,
err: (error) => `Error: ${error.message}`
});
console.log(errorResult); // "Error: Cannot divide by zero"
When using match with a discriminant string union, TypeScript will enforce exhaustiveness, ensuring you handle all possible cases. This provides an additional layer of type safety for error handling.
// Define a discriminated union of error types
type ApiError =
| { type: 'NotFound'; id: string }
| { type: 'Timeout'; ms: number }
| { type: 'Unauthorized'; reason: string };
// Function that returns different error types
function fetchData(id: string): Result<{ name: string }, ApiError> {
// ...
}
// Use match to handle each error type differently
const response = fetchData('slow');
if (!response.ok) {
const errorMessage = match(response.error.type, {
NotFound: () => `Item ${response.error.id} could not be found`,
Timeout: () => `Request timed out after ${response.error.ms}ms`,
Unauthorized: () => `Access denied: ${response.error.reason}`
});
console.log(errorMessage); // "Request timed out after 5000ms"
}
// Warning: match requires a discriminated union
// If you're not using a discriminated union, use match instead
MIT
FAQs
A small opinionated library to bring Rust-like results idiomatically to TypeScript.
The npm package okay-error receives a total of 7,283 weekly downloads. As such, okay-error popularity was classified as popular.
We found that okay-error demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer 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
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.

Security News
/Research
Widespread GitHub phishing campaign uses fake Visual Studio Code security alerts in Discussions to trick developers into visiting malicious website.