@lesto/errors 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +36 -0
- package/src/errors.ts +32 -0
- package/src/index.ts +14 -0
- package/src/result.ts +52 -0
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lesto/errors",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Lesto's shared error foundation — the LestoError base every package error extends, plus a Result type.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/index.ts",
|
|
10
|
+
"import": "./src/index.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:cov": "vitest run --coverage",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"lint": "oxlint src test",
|
|
18
|
+
"format": "oxfmt src test",
|
|
19
|
+
"format:check": "oxfmt --check src test"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/lesto-run/lesto.git",
|
|
30
|
+
"directory": "packages/errors"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://lesto.run",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/lesto-run/lesto/issues"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Errors carry codes, not just prose.
|
|
3
|
+
*
|
|
4
|
+
* Every failure in Lesto surfaces a stable, machine-readable `code`. Logs,
|
|
5
|
+
* tests, API responses, and the MCP surface branch on the code — never on a
|
|
6
|
+
* message string, which is free to change for humans without breaking machines.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** The root of every Lesto error. Generic over its code union for exhaustiveness. */
|
|
10
|
+
export class LestoError<Code extends string = string> extends Error {
|
|
11
|
+
readonly code: Code;
|
|
12
|
+
|
|
13
|
+
readonly details: Readonly<Record<string, unknown>>;
|
|
14
|
+
|
|
15
|
+
constructor(code: Code, message: string, details: Record<string, unknown> = {}) {
|
|
16
|
+
super(message);
|
|
17
|
+
|
|
18
|
+
this.name = "LestoError";
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.details = Object.freeze({ ...details });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** True iff `value` is a `LestoError` (or a subclass of one). */
|
|
25
|
+
export function isLestoError(value: unknown): value is LestoError {
|
|
26
|
+
return value instanceof LestoError;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** True iff `value` is a `LestoError` whose code matches exactly. */
|
|
30
|
+
export function hasCode(value: unknown, code: string): boolean {
|
|
31
|
+
return isLestoError(value) && value.code === code;
|
|
32
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lesto/errors — the shared error foundation every Lesto package builds on.
|
|
3
|
+
*
|
|
4
|
+
* class QueueError extends LestoError<"QUEUE_HANDLER_NOT_FOUND"> { ... }
|
|
5
|
+
*
|
|
6
|
+
* const result = mightFail();
|
|
7
|
+
* if (isErr(result)) return result.error;
|
|
8
|
+
* use(unwrap(result));
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export { hasCode, isLestoError, LestoError } from "./errors";
|
|
12
|
+
|
|
13
|
+
export { err, isErr, isOk, ok, unwrap } from "./result";
|
|
14
|
+
export type { Result } from "./result";
|
package/src/result.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result — the explicit success-or-failure value.
|
|
3
|
+
*
|
|
4
|
+
* A function that can fail returns a `Result` instead of throwing, so the
|
|
5
|
+
* failure is visible in the type and the caller is forced to handle it. The
|
|
6
|
+
* two arms are discriminated by `ok`, narrowable with `isOk` / `isErr`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { LestoError } from "./errors";
|
|
10
|
+
|
|
11
|
+
export type Result<T, E = LestoError> = { ok: true; value: T } | { ok: false; error: E };
|
|
12
|
+
|
|
13
|
+
/** Wrap a value as a successful result. */
|
|
14
|
+
export function ok<T>(value: T): Result<T, never> {
|
|
15
|
+
return { ok: true, value };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Wrap an error as a failed result. */
|
|
19
|
+
export function err<E>(error: E): Result<never, E> {
|
|
20
|
+
return { ok: false, error };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Narrow a result to its success arm. */
|
|
24
|
+
export function isOk<T, E>(result: Result<T, E>): result is { ok: true; value: T } {
|
|
25
|
+
return result.ok;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Narrow a result to its failure arm. */
|
|
29
|
+
export function isErr<T, E>(result: Result<T, E>): result is { ok: false; error: E } {
|
|
30
|
+
return !result.ok;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Pull the value out of a result, throwing on failure.
|
|
35
|
+
*
|
|
36
|
+
* If the error is already an `Error`, it is thrown as-is so its stack and type
|
|
37
|
+
* survive. Otherwise it is wrapped in a `LestoError` so callers always catch an
|
|
38
|
+
* `Error`, never a bare string or object.
|
|
39
|
+
*/
|
|
40
|
+
export function unwrap<T, E>(result: Result<T, E>): T {
|
|
41
|
+
if (result.ok) {
|
|
42
|
+
return result.value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { error } = result;
|
|
46
|
+
|
|
47
|
+
if (error instanceof Error) {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw new LestoError("UNWRAP_NON_ERROR", "Tried to unwrap a failed result.", { error });
|
|
52
|
+
}
|