@firtoz/maybe-error 1.0.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/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # @firtoz/maybe-error
2
+
3
+ Type-safe result handling with the MaybeError pattern for TypeScript.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Type-safe error handling** - Full TypeScript support with discriminated unions
8
+ - 🚀 **Zero dependencies** - Lightweight and fast
9
+ - 📦 **Tree-shakeable** - Import only what you need
10
+ - 🎯 **Simple API** - Easy to use and understand
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @firtoz/maybe-error
16
+ # or
17
+ yarn add @firtoz/maybe-error
18
+ # or
19
+ pnpm add @firtoz/maybe-error
20
+ # or
21
+ bun add @firtoz/maybe-error
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Basic Usage
27
+
28
+ ```typescript
29
+ import { MaybeError, success, fail } from '@firtoz/maybe-error';
30
+
31
+ // Function that might fail
32
+ function divide(a: number, b: number): MaybeError<number> {
33
+ if (b === 0) {
34
+ return fail("Division by zero");
35
+ }
36
+ return success(a / b);
37
+ }
38
+
39
+ // Usage
40
+ const result = divide(10, 2);
41
+
42
+ if (result.success) {
43
+ console.log("Result:", result.result); // TypeScript knows result exists
44
+ } else {
45
+ console.error("Error:", result.error); // TypeScript knows error exists
46
+ }
47
+ ```
48
+
49
+ ### With Custom Error Types
50
+
51
+ ```typescript
52
+ import { MaybeError, success, fail } from '@firtoz/maybe-error';
53
+
54
+ type ValidationError = {
55
+ field: string;
56
+ message: string;
57
+ };
58
+
59
+ function validateEmail(email: string): MaybeError<string, ValidationError> {
60
+ if (!email.includes('@')) {
61
+ return fail({
62
+ field: 'email',
63
+ message: 'Email must contain @ symbol'
64
+ });
65
+ }
66
+ return success(email);
67
+ }
68
+
69
+ const result = validateEmail("user@example.com");
70
+
71
+ if (result.success) {
72
+ console.log("Valid email:", result.result);
73
+ } else {
74
+ console.error(`Validation error in ${result.error.field}: ${result.error.message}`);
75
+ }
76
+ ```
77
+
78
+ ### Async Functions
79
+
80
+ ```typescript
81
+ import { MaybeError, success, fail } from '@firtoz/maybe-error';
82
+
83
+ async function fetchUser(id: string): Promise<MaybeError<User, string>> {
84
+ try {
85
+ const response = await fetch(`/api/users/${id}`);
86
+ if (!response.ok) {
87
+ return fail(`HTTP ${response.status}: ${response.statusText}`);
88
+ }
89
+ const user = await response.json();
90
+ return success(user);
91
+ } catch (error) {
92
+ return fail(error instanceof Error ? error.message : 'Unknown error');
93
+ }
94
+ }
95
+
96
+ // Usage
97
+ const userResult = await fetchUser("123");
98
+ if (userResult.success) {
99
+ console.log("User:", userResult.result);
100
+ } else {
101
+ console.error("Failed to fetch user:", userResult.error);
102
+ }
103
+ ```
104
+
105
+ ## API Reference
106
+
107
+ ### Types
108
+
109
+ #### `MaybeError<T, TError>`
110
+
111
+ A discriminated union type representing either success or failure.
112
+
113
+ ```typescript
114
+ type MaybeError<T = undefined, TError = string> =
115
+ | DefiniteSuccess<T>
116
+ | DefiniteError<TError>;
117
+ ```
118
+
119
+ #### `DefiniteSuccess<T>`
120
+
121
+ Represents a successful result.
122
+
123
+ ```typescript
124
+ type DefiniteSuccess<T = undefined> = {
125
+ success: true;
126
+ } & (T extends undefined ? { result?: T } : { result: T });
127
+ ```
128
+
129
+ #### `DefiniteError<TError>`
130
+
131
+ Represents a failed result.
132
+
133
+ ```typescript
134
+ type DefiniteError<TError = string> = {
135
+ success: false;
136
+ error: TError;
137
+ };
138
+ ```
139
+
140
+ #### `AssumeSuccess<T>`
141
+
142
+ Utility type to extract the success type from a MaybeError.
143
+
144
+ ```typescript
145
+ type AssumeSuccess<T extends MaybeError<unknown>> = // extracted success type
146
+ ```
147
+
148
+ ### Functions
149
+
150
+ #### `success<T>(result?: T): DefiniteSuccess<T>`
151
+
152
+ Creates a success result.
153
+
154
+ ```typescript
155
+ const result1 = success(); // No result value
156
+ const result2 = success("Hello"); // With result value
157
+ const result3 = success({ id: 1, name: "John" }); // With object result
158
+ ```
159
+
160
+ #### `fail<TError>(error: TError): DefiniteError<TError>`
161
+
162
+ Creates a failure result.
163
+
164
+ ```typescript
165
+ const error1 = fail("Something went wrong");
166
+ const error2 = fail({ code: 404, message: "Not found" });
167
+ const error3 = fail(new Error("Custom error"));
168
+ ```
169
+
170
+ ## Examples
171
+
172
+ ### Chaining Operations
173
+
174
+ ```typescript
175
+ import { MaybeError, success, fail } from '@firtoz/maybe-error';
176
+
177
+ function parseNumber(str: string): MaybeError<number> {
178
+ const num = Number(str);
179
+ if (isNaN(num)) {
180
+ return fail(`"${str}" is not a valid number`);
181
+ }
182
+ return success(num);
183
+ }
184
+
185
+ function sqrt(num: number): MaybeError<number> {
186
+ if (num < 0) {
187
+ return fail("Cannot calculate square root of negative number");
188
+ }
189
+ return success(Math.sqrt(num));
190
+ }
191
+
192
+ // Chain operations
193
+ function parseAndSqrt(str: string): MaybeError<number> {
194
+ const parseResult = parseNumber(str);
195
+ if (!parseResult.success) {
196
+ return parseResult; // Forward the error
197
+ }
198
+
199
+ return sqrt(parseResult.result);
200
+ }
201
+
202
+ // Usage
203
+ const result = parseAndSqrt("16");
204
+ if (result.success) {
205
+ console.log("Square root:", result.result); // 4
206
+ } else {
207
+ console.error("Error:", result.error);
208
+ }
209
+ ```
210
+
211
+ ### With Promise.all
212
+
213
+ ```typescript
214
+ import { MaybeError, success, fail } from '@firtoz/maybe-error';
215
+
216
+ async function fetchMultipleUsers(ids: string[]): Promise<MaybeError<User[]>> {
217
+ try {
218
+ const promises = ids.map(id => fetchUser(id));
219
+ const results = await Promise.all(promises);
220
+
221
+ // Check if any failed
222
+ const errors = results.filter(r => !r.success);
223
+ if (errors.length > 0) {
224
+ return fail(`Failed to fetch ${errors.length} users`);
225
+ }
226
+
227
+ // All succeeded, extract results
228
+ const users = results.map(r => r.success ? r.result : null).filter(Boolean);
229
+ return success(users as User[]);
230
+ } catch (error) {
231
+ return fail(error instanceof Error ? error.message : 'Unknown error');
232
+ }
233
+ }
234
+ ```
235
+
236
+ ## Contributing
237
+
238
+ Contributions are welcome! This package is part of the [router-toolkit monorepo](https://github.com/firtoz/router-toolkit).
239
+
240
+ ## License
241
+
242
+ MIT © [Firtina Ozbalikchi](https://github.com/firtoz)
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@firtoz/maybe-error",
3
+ "version": "1.0.0",
4
+ "description": "Type-safe result handling with MaybeError pattern",
5
+ "main": "./src/index.ts",
6
+ "module": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "import": "./src/index.ts",
12
+ "require": "./src/index.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "src/**/*",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "echo 'No build step - using TypeScript source directly'",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "biome check src",
23
+ "format": "biome format src --write",
24
+ "test": "echo 'No tests yet'"
25
+ },
26
+ "keywords": [
27
+ "typescript",
28
+ "error-handling",
29
+ "result-pattern",
30
+ "maybe-error",
31
+ "type-safe"
32
+ ],
33
+ "author": "Firtina Ozbalikchi <firtoz@github.com>",
34
+ "license": "MIT",
35
+ "homepage": "https://github.com/firtoz/router-toolkit#readme",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/firtoz/router-toolkit.git",
39
+ "directory": "packages/maybe-error"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/firtoz/router-toolkit/issues"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ }
50
+ }
@@ -0,0 +1,45 @@
1
+ export type DefiniteError<TError = string> = {
2
+ success: false;
3
+ error: TError;
4
+ };
5
+
6
+ export type DefiniteSuccess<T = undefined> = {
7
+ success: true;
8
+ } & (T extends undefined
9
+ ? {
10
+ result?: T;
11
+ }
12
+ : {
13
+ result: T;
14
+ });
15
+
16
+ export type MaybeError<T = undefined, TError = string> =
17
+ | DefiniteSuccess<T>
18
+ | DefiniteError<TError>;
19
+
20
+ export type AssumeSuccess<T extends MaybeError<unknown>> = Exclude<
21
+ T,
22
+ undefined
23
+ > extends MaybeError<infer U>
24
+ ? U
25
+ : never;
26
+
27
+ export const success = <T = undefined>(
28
+ ...params: T extends undefined ? [] : [T]
29
+ ): DefiniteSuccess<T> => {
30
+ if (params.length === 0) {
31
+ return { success: true } as unknown as DefiniteSuccess<T>;
32
+ }
33
+
34
+ return {
35
+ success: true,
36
+ result: params[0],
37
+ } as unknown as DefiniteSuccess<T>;
38
+ };
39
+
40
+ export const fail = <TError = string>(error: TError): DefiniteError<TError> => {
41
+ return {
42
+ success: false,
43
+ error,
44
+ };
45
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./MaybeError";