utilful 1.2.1 → 2.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 CHANGED
@@ -10,10 +10,10 @@ A collection of TypeScript utilities that I use across my projects.
10
10
  - [CSV](#csv)
11
11
  - [Emitter](#emitter)
12
12
  - [JSON](#json)
13
- - [Lazy](#lazy)
14
13
  - [Module](#module)
15
14
  - [Object](#object)
16
15
  - [Path](#path)
16
+ - [Result](#result)
17
17
  - [String](#string)
18
18
 
19
19
  ## Installation
@@ -168,29 +168,6 @@ Clones the given JSON value.
168
168
  declare function cloneJSON<T>(value: T): T
169
169
  ```
170
170
 
171
- ### Lazy
172
-
173
- A simple general purpose memoizer utility.
174
-
175
- - Lazily computes a value when accessed
176
- - Auto-caches the result by overwriting the getter
177
- - Typesafe
178
-
179
- Useful for deferring initialization or expensive operations. Unlike a simple getter, there is no runtime overhead after the first invokation, since the getter itself is overwritten with the memoized value.
180
-
181
- ```ts
182
- declare function lazy<T>(getter: () => T): { value: T }
183
- ```
184
-
185
- **Example:**
186
-
187
- ```ts
188
- const myValue = lazy(() => 'Hello, World!')
189
- console.log(myValue.value) // Computes value, overwrites getter
190
- console.log(myValue.value) // Returns cached value
191
- console.log(myValue.value) // Returns cached value
192
- ```
193
-
194
171
  ### Module
195
172
 
196
173
  #### `interopDefault`
@@ -215,6 +192,28 @@ async function loadModule() {
215
192
 
216
193
  ### Object
217
194
 
195
+ #### `memoize`
196
+
197
+ A simple general purpose memoizer utility.
198
+
199
+ - Lazily computes a value when accessed
200
+ - Auto-caches the result by overwriting the getter
201
+
202
+ Useful for deferring initialization or expensive operations. Unlike a simple getter, there is no runtime overhead after the first invokation, since the getter itself is overwritten with the memoized value.
203
+
204
+ ```ts
205
+ declare function memoize<T>(getter: () => T): { value: T }
206
+ ```
207
+
208
+ **Example:**
209
+
210
+ ```ts
211
+ const myValue = lazy(() => 'Hello, World!')
212
+ console.log(myValue.value) // Computes value, overwrites getter
213
+ console.log(myValue.value) // Returns cached value
214
+ console.log(myValue.value) // Returns cached value
215
+ ```
216
+
218
217
  #### `objectKeys`
219
218
 
220
219
  Strictly typed `Object.keys`.
@@ -327,6 +326,133 @@ const url = withQuery('https://example.com', {
327
326
  })
328
327
  ```
329
328
 
329
+ ### Result
330
+
331
+ The `Result` type that represents either success (`Ok`) or failure (`Err`). It helps to handle errors in a more explicit and type-safe way, without relying on exceptions.
332
+
333
+ A common use case for `Result` is error handling in functions that might fail. Here's an example of a function that divides two numbers and returns a `Result`:
334
+
335
+ ```ts
336
+ import { err, ok } from 'utilful'
337
+
338
+ function divide(a: number, b: number) {
339
+ if (b === 0) {
340
+ return err('Division by zero')
341
+ }
342
+ return ok(a / b)
343
+ }
344
+
345
+ const result = divide(10, 2)
346
+ if (result.ok)
347
+ console.log('Result:', result.value)
348
+ else
349
+ console.error('Error:', result.error)
350
+ ```
351
+
352
+ #### `Result`
353
+
354
+ The `Result` type represents either success (`Ok`) or failure (`Err`).
355
+
356
+ **Type Definition:**
357
+
358
+ ```ts
359
+ type Result<T, E> = Ok<T> | Err<E>
360
+ ```
361
+
362
+ #### `Ok`
363
+
364
+ The `Ok` type wraps a successful value.
365
+
366
+ **Example:**
367
+
368
+ ```ts
369
+ const result = new Ok(42)
370
+ ```
371
+
372
+ #### `Err`
373
+
374
+ The `Err` type wraps an error value.
375
+
376
+ **Example:**
377
+
378
+ ```ts
379
+ const result = new Err('Something went wrong')
380
+ ```
381
+
382
+ #### `ok`
383
+
384
+ Shorthand function to create an `Ok` result. Use it to wrap a successful value.
385
+
386
+ **Type Definition:**
387
+
388
+ ```ts
389
+ function ok<T>(value: T): Ok<T>
390
+ ```
391
+
392
+ #### `err`
393
+
394
+ Shorthand function to create an `Err` result. Use it to wrap an error value.
395
+
396
+ **Type Definition:**
397
+
398
+ ```ts
399
+ function err<E extends string = string>(err: E): Err<E>
400
+ function err<E = unknown>(err: E): Err<E>
401
+ ```
402
+
403
+ #### `toResult`
404
+
405
+ Wraps a function that might throw an error and returns a `Result` with the result of the function.
406
+
407
+ **Type Definition:**
408
+
409
+ ```ts
410
+ function toResult<T, E = unknown>(fn: () => T): Result<T, E>
411
+ function toResult<T, E = unknown>(promise: Promise<T>): Promise<Result<T, E>>
412
+ ```
413
+
414
+ #### `unwrapResult`
415
+
416
+ Unwraps a `Result`, `Ok`, or `Err` value and returns the value or error in an object. If the result is an `Ok`, the object contains the value and an `undefined` error. If the result is an `Err`, the object contains an `undefined` value and the error.
417
+
418
+ **Example:**
419
+
420
+ ```ts
421
+ const result = toResult(() => JSON.parse('{"foo":"bar"}'))
422
+ const { value, error } = unwrapResult(result)
423
+ ```
424
+
425
+ **Type Definition:**
426
+
427
+ ```ts
428
+ function unwrapResult<T>(result: Ok<T>): { value: T, error: undefined }
429
+ function unwrapResult<E>(result: Err<E>): { value: undefined, error: E }
430
+ function unwrapResult<T, E>(result: Result<T, E>): { value: T, error: undefined } | { value: undefined, error: E }
431
+ ```
432
+
433
+ #### `tryCatch`
434
+
435
+ A simpler alternative to `toResult` + `unwrapResult`. It executes a function that might throw an error and directly returns the result in a `ResultData` format. Works with both synchronous functions and promises.
436
+
437
+ **Example:**
438
+
439
+ ```ts
440
+ import { tryCatch } from 'utilful'
441
+
442
+ // Synchronous usage
443
+ const { value, error } = tryCatch(() => JSON.parse('{"foo":"bar"}'))
444
+
445
+ // Asynchronous usage
446
+ const { value, error } = await tryCatch(fetch('https://api.example.com/data').then(r => r.json()))
447
+ ```
448
+
449
+ **Type Definition:**
450
+
451
+ ```ts
452
+ function tryCatch<T, E = unknown>(fn: () => T): { value: T, error: undefined } | { value: undefined, error: E }
453
+ function tryCatch<T, E = unknown>(promise: Promise<T>): Promise<{ value: T, error: undefined } | { value: undefined, error: E }>
454
+ ```
455
+
330
456
  ### String
331
457
 
332
458
  #### `template`
package/dist/csv.d.mts CHANGED
@@ -34,8 +34,8 @@ declare function createCSV<T extends Record<string, unknown>>(data: T[], columns
34
34
  * Within quoted values, double quotes are escaped by doubling them.
35
35
  *
36
36
  * @example
37
- * escapeCSVValue('hello, world'); // "hello, world"
38
- * escapeCSVValue('contains "quotes"'); // "contains ""quotes"""
37
+ * escapeCSVValue('hello, world') // "hello, world"
38
+ * escapeCSVValue('contains "quotes"') // "contains ""quotes"""
39
39
  */
40
40
  declare function escapeCSVValue(value: unknown, options?: {
41
41
  /** @default ',' */
package/dist/csv.d.ts CHANGED
@@ -34,8 +34,8 @@ declare function createCSV<T extends Record<string, unknown>>(data: T[], columns
34
34
  * Within quoted values, double quotes are escaped by doubling them.
35
35
  *
36
36
  * @example
37
- * escapeCSVValue('hello, world'); // "hello, world"
38
- * escapeCSVValue('contains "quotes"'); // "contains ""quotes"""
37
+ * escapeCSVValue('hello, world') // "hello, world"
38
+ * escapeCSVValue('contains "quotes"') // "contains ""quotes"""
39
39
  */
40
40
  declare function escapeCSVValue(value: unknown, options?: {
41
41
  /** @default ',' */
package/dist/index.d.mts CHANGED
@@ -2,9 +2,9 @@ export { MaybeArray, toArray } from './array.mjs';
2
2
  export { CSVRow, createCSV, escapeCSVValue, parseCSV } from './csv.mjs';
3
3
  export { Emitter, EventHandlerList, EventHandlerMap, EventType, Handler, WildCardEventHandlerList, WildcardHandler, createEmitter } from './emitter.mjs';
4
4
  export { cloneJSON, tryParseJSON } from './json.mjs';
5
- export { lazy } from './lazy.mjs';
6
5
  export { interopDefault } from './module.mjs';
7
- export { deepApply, objectEntries, objectKeys } from './object.mjs';
6
+ export { deepApply, memoize, objectEntries, objectKeys } from './object.mjs';
8
7
  export { QueryObject, QueryValue, getPathname, joinURL, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash } from './path.mjs';
8
+ export { Err, ErrData, Ok, OkData, Result, ResultData, err, ok, toResult, tryCatch, unwrapResult } from './result.mjs';
9
9
  export { generateRandomId, template } from './string.mjs';
10
10
  export { AutocompletableString, LooseAutocomplete, UnifyIntersection } from './types.mjs';
package/dist/index.d.ts CHANGED
@@ -2,9 +2,9 @@ export { MaybeArray, toArray } from './array.js';
2
2
  export { CSVRow, createCSV, escapeCSVValue, parseCSV } from './csv.js';
3
3
  export { Emitter, EventHandlerList, EventHandlerMap, EventType, Handler, WildCardEventHandlerList, WildcardHandler, createEmitter } from './emitter.js';
4
4
  export { cloneJSON, tryParseJSON } from './json.js';
5
- export { lazy } from './lazy.js';
6
5
  export { interopDefault } from './module.js';
7
- export { deepApply, objectEntries, objectKeys } from './object.js';
6
+ export { deepApply, memoize, objectEntries, objectKeys } from './object.js';
8
7
  export { QueryObject, QueryValue, getPathname, joinURL, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash } from './path.js';
8
+ export { Err, ErrData, Ok, OkData, Result, ResultData, err, ok, toResult, tryCatch, unwrapResult } from './result.js';
9
9
  export { generateRandomId, template } from './string.js';
10
10
  export { AutocompletableString, LooseAutocomplete, UnifyIntersection } from './types.js';
package/dist/index.mjs CHANGED
@@ -2,8 +2,8 @@ export { toArray } from './array.mjs';
2
2
  export { createCSV, escapeCSVValue, parseCSV } from './csv.mjs';
3
3
  export { createEmitter } from './emitter.mjs';
4
4
  export { cloneJSON, tryParseJSON } from './json.mjs';
5
- export { lazy } from './lazy.mjs';
6
5
  export { interopDefault } from './module.mjs';
7
- export { deepApply, objectEntries, objectKeys } from './object.mjs';
6
+ export { deepApply, memoize, objectEntries, objectKeys } from './object.mjs';
8
7
  export { getPathname, joinURL, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash } from './path.mjs';
8
+ export { Err, Ok, err, ok, toResult, tryCatch, unwrapResult } from './result.mjs';
9
9
  export { generateRandomId, template } from './string.mjs';
package/dist/object.d.mts CHANGED
@@ -1,3 +1,20 @@
1
+ /**
2
+ * A simple general purpose memoizer utility.
3
+ * - Lazily computes a value when accessed
4
+ * - Auto-caches the result by overwriting the getter
5
+ *
6
+ * @remarks
7
+ * Useful for deferring initialization or expensive operations. Unlike a simple getter, there is no runtime overhead after the first invokation, since the getter itself is overwritten with the memoized value.
8
+ *
9
+ * @example
10
+ * const myValue = lazy(() => 'Hello, World!')
11
+ * console.log(myValue.value) // Computes value, overwrites getter
12
+ * console.log(myValue.value) // Returns cached value
13
+ * console.log(myValue.value) // Returns cached value
14
+ */
15
+ declare function memoize<T>(getter: () => T): {
16
+ value: T;
17
+ };
1
18
  /**
2
19
  * Strictly typed `Object.keys`.
3
20
  */
@@ -11,4 +28,4 @@ declare function objectEntries<T extends Record<any, any>>(obj: T): Array<[keyof
11
28
  */
12
29
  declare function deepApply<T extends Record<any, any>>(data: T, callback: (item: T, key: keyof T, value: T[keyof T]) => void): void;
13
30
 
14
- export { deepApply, objectEntries, objectKeys };
31
+ export { deepApply, memoize, objectEntries, objectKeys };
package/dist/object.d.ts CHANGED
@@ -1,3 +1,20 @@
1
+ /**
2
+ * A simple general purpose memoizer utility.
3
+ * - Lazily computes a value when accessed
4
+ * - Auto-caches the result by overwriting the getter
5
+ *
6
+ * @remarks
7
+ * Useful for deferring initialization or expensive operations. Unlike a simple getter, there is no runtime overhead after the first invokation, since the getter itself is overwritten with the memoized value.
8
+ *
9
+ * @example
10
+ * const myValue = lazy(() => 'Hello, World!')
11
+ * console.log(myValue.value) // Computes value, overwrites getter
12
+ * console.log(myValue.value) // Returns cached value
13
+ * console.log(myValue.value) // Returns cached value
14
+ */
15
+ declare function memoize<T>(getter: () => T): {
16
+ value: T;
17
+ };
1
18
  /**
2
19
  * Strictly typed `Object.keys`.
3
20
  */
@@ -11,4 +28,4 @@ declare function objectEntries<T extends Record<any, any>>(obj: T): Array<[keyof
11
28
  */
12
29
  declare function deepApply<T extends Record<any, any>>(data: T, callback: (item: T, key: keyof T, value: T[keyof T]) => void): void;
13
30
 
14
- export { deepApply, objectEntries, objectKeys };
31
+ export { deepApply, memoize, objectEntries, objectKeys };
package/dist/object.mjs CHANGED
@@ -1,3 +1,12 @@
1
+ function memoize(getter) {
2
+ return {
3
+ get value() {
4
+ const value = getter();
5
+ Object.defineProperty(this, "value", { value });
6
+ return value;
7
+ }
8
+ };
9
+ }
1
10
  function objectKeys(obj) {
2
11
  return Object.keys(obj);
3
12
  }
@@ -22,4 +31,4 @@ function isObject(value) {
22
31
  return Object.prototype.toString.call(value) === "[object Object]";
23
32
  }
24
33
 
25
- export { deepApply, objectEntries, objectKeys };
34
+ export { deepApply, memoize, objectEntries, objectKeys };
@@ -0,0 +1,32 @@
1
+ type Result<T, E> = Ok<T> | Err<E>;
2
+ interface OkData<T> {
3
+ value: T;
4
+ error: undefined;
5
+ }
6
+ interface ErrData<E> {
7
+ value: undefined;
8
+ error: E;
9
+ }
10
+ type ResultData<T, E> = OkData<T> | ErrData<E>;
11
+ declare class Ok<T> {
12
+ readonly value: T;
13
+ readonly ok = true;
14
+ constructor(value: T);
15
+ }
16
+ declare class Err<E> {
17
+ readonly error: E;
18
+ readonly ok = false;
19
+ constructor(error: E);
20
+ }
21
+ declare function ok<T>(value: T): Ok<T>;
22
+ declare function err<E extends string = string>(error: E): Err<E>;
23
+ declare function err<E = unknown>(error: E): Err<E>;
24
+ declare function toResult<T, E = unknown>(fn: () => T): Result<T, E>;
25
+ declare function toResult<T, E = unknown>(promise: Promise<T>): Promise<Result<T, E>>;
26
+ declare function unwrapResult<T>(result: Ok<T>): OkData<T>;
27
+ declare function unwrapResult<E>(result: Err<E>): ErrData<E>;
28
+ declare function unwrapResult<T, E>(result: Result<T, E>): ResultData<T, E>;
29
+ declare function tryCatch<T, E = unknown>(fn: () => T): ResultData<T, E>;
30
+ declare function tryCatch<T, E = unknown>(promise: Promise<T>): Promise<ResultData<T, E>>;
31
+
32
+ export { Err, type ErrData, Ok, type OkData, type Result, type ResultData, err, ok, toResult, tryCatch, unwrapResult };
@@ -0,0 +1,32 @@
1
+ type Result<T, E> = Ok<T> | Err<E>;
2
+ interface OkData<T> {
3
+ value: T;
4
+ error: undefined;
5
+ }
6
+ interface ErrData<E> {
7
+ value: undefined;
8
+ error: E;
9
+ }
10
+ type ResultData<T, E> = OkData<T> | ErrData<E>;
11
+ declare class Ok<T> {
12
+ readonly value: T;
13
+ readonly ok = true;
14
+ constructor(value: T);
15
+ }
16
+ declare class Err<E> {
17
+ readonly error: E;
18
+ readonly ok = false;
19
+ constructor(error: E);
20
+ }
21
+ declare function ok<T>(value: T): Ok<T>;
22
+ declare function err<E extends string = string>(error: E): Err<E>;
23
+ declare function err<E = unknown>(error: E): Err<E>;
24
+ declare function toResult<T, E = unknown>(fn: () => T): Result<T, E>;
25
+ declare function toResult<T, E = unknown>(promise: Promise<T>): Promise<Result<T, E>>;
26
+ declare function unwrapResult<T>(result: Ok<T>): OkData<T>;
27
+ declare function unwrapResult<E>(result: Err<E>): ErrData<E>;
28
+ declare function unwrapResult<T, E>(result: Result<T, E>): ResultData<T, E>;
29
+ declare function tryCatch<T, E = unknown>(fn: () => T): ResultData<T, E>;
30
+ declare function tryCatch<T, E = unknown>(promise: Promise<T>): Promise<ResultData<T, E>>;
31
+
32
+ export { Err, type ErrData, Ok, type OkData, type Result, type ResultData, err, ok, toResult, tryCatch, unwrapResult };
@@ -0,0 +1,43 @@
1
+ class Ok {
2
+ value;
3
+ ok = true;
4
+ constructor(value) {
5
+ this.value = value;
6
+ }
7
+ }
8
+ class Err {
9
+ error;
10
+ ok = false;
11
+ constructor(error) {
12
+ this.error = error;
13
+ }
14
+ }
15
+ function ok(value) {
16
+ return new Ok(value);
17
+ }
18
+ function err(error) {
19
+ return new Err(error);
20
+ }
21
+ function toResult(fnOrPromise) {
22
+ if (fnOrPromise instanceof Promise) {
23
+ return fnOrPromise.then(ok).catch(err);
24
+ }
25
+ try {
26
+ return ok(fnOrPromise());
27
+ } catch (error) {
28
+ return err(error);
29
+ }
30
+ }
31
+ function unwrapResult(result) {
32
+ return result.ok ? { value: result.value, error: void 0 } : { value: void 0, error: result.error };
33
+ }
34
+ function tryCatch(fnOrPromise) {
35
+ if (fnOrPromise instanceof Promise) {
36
+ return toResult(fnOrPromise).then(
37
+ (result) => unwrapResult(result)
38
+ );
39
+ }
40
+ return unwrapResult(toResult(fnOrPromise));
41
+ }
42
+
43
+ export { Err, Ok, err, ok, toResult, tryCatch, unwrapResult };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "utilful",
3
3
  "type": "module",
4
- "version": "1.2.1",
4
+ "version": "2.0.0",
5
5
  "packageManager": "pnpm@10.5.2",
6
6
  "description": "A collection of TypeScript utilities",
7
7
  "author": "Johann Schopplich <hello@johannschopplich.com>",
@@ -36,10 +36,6 @@
36
36
  "types": "./dist/json.d.mts",
37
37
  "default": "./dist/json.mjs"
38
38
  },
39
- "./lazy": {
40
- "types": "./dist/lazy.d.mts",
41
- "default": "./dist/lazy.mjs"
42
- },
43
39
  "./module": {
44
40
  "types": "./dist/module.d.mts",
45
41
  "default": "./dist/module.mjs"
@@ -52,6 +48,10 @@
52
48
  "types": "./dist/path.d.mts",
53
49
  "default": "./dist/path.mjs"
54
50
  },
51
+ "./result": {
52
+ "types": "./dist/result.d.mts",
53
+ "default": "./dist/result.mjs"
54
+ },
55
55
  "./string": {
56
56
  "types": "./dist/string.d.mts",
57
57
  "default": "./dist/string.mjs"
@@ -61,7 +61,7 @@
61
61
  "default": "./dist/types.mjs"
62
62
  }
63
63
  },
64
- "types": "./dist/index.d.ts",
64
+ "types": "./dist/index.d.mts",
65
65
  "files": [
66
66
  "dist"
67
67
  ],
package/dist/lazy.d.mts DELETED
@@ -1,20 +0,0 @@
1
- /**
2
- * A simple general purpose memoizer utility.
3
- * - Lazily computes a value when accessed
4
- * - Auto-caches the result by overwriting the getter
5
- * - Typesafe
6
- *
7
- * @remarks
8
- * Useful for deferring initialization or expensive operations. Unlike a simple getter, there is no runtime overhead after the first invokation, since the getter itself is overwritten with the memoized value.
9
- *
10
- * @example
11
- * const myValue = lazy(() => 'Hello, World!')
12
- * console.log(myValue.value) // Computes value, overwrites getter
13
- * console.log(myValue.value) // Returns cached value
14
- * console.log(myValue.value) // Returns cached value
15
- */
16
- declare function lazy<T>(getter: () => T): {
17
- value: T;
18
- };
19
-
20
- export { lazy };
package/dist/lazy.d.ts DELETED
@@ -1,20 +0,0 @@
1
- /**
2
- * A simple general purpose memoizer utility.
3
- * - Lazily computes a value when accessed
4
- * - Auto-caches the result by overwriting the getter
5
- * - Typesafe
6
- *
7
- * @remarks
8
- * Useful for deferring initialization or expensive operations. Unlike a simple getter, there is no runtime overhead after the first invokation, since the getter itself is overwritten with the memoized value.
9
- *
10
- * @example
11
- * const myValue = lazy(() => 'Hello, World!')
12
- * console.log(myValue.value) // Computes value, overwrites getter
13
- * console.log(myValue.value) // Returns cached value
14
- * console.log(myValue.value) // Returns cached value
15
- */
16
- declare function lazy<T>(getter: () => T): {
17
- value: T;
18
- };
19
-
20
- export { lazy };
package/dist/lazy.mjs DELETED
@@ -1,11 +0,0 @@
1
- function lazy(getter) {
2
- return {
3
- get value() {
4
- const value = getter();
5
- Object.defineProperty(this, "value", { value });
6
- return value;
7
- }
8
- };
9
- }
10
-
11
- export { lazy };