@ntangled/kit 0.0.0-alpha.6 → 0.0.1
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 +181 -31
- package/dist/safe.d.mts +46 -0
- package/dist/safe.d.ts +46 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,59 +1,209 @@
|
|
|
1
1
|
# @ntangled/kit
|
|
2
2
|
|
|
3
|
-
A TypeScript
|
|
3
|
+
A lightweight, zero-dependency utility for TypeScript. Works seamlessly in **Node.js**, **Bun** and the **Browser**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> Disclaimer: This is my first "serious" npm lib. There will be typos, bad formulated paragraphs and more - and you are more than welcome to notify me.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Features
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
> Throwing `null` will result in a NullError in the error entry. Use `instance of NullError` if you expect `null` to be thrown.
|
|
9
|
+
- **Universal**: Works in any environment (Node, Bun, Deno, Browsers).
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
- **Dual-Build**: Ships with ESM and CommonJS support.
|
|
13
12
|
|
|
14
|
-
- **
|
|
15
|
-
- **px**: Safely executes an async callback, returns `[error, result]`.
|
|
16
|
-
- **rx**: Safely executes a sync callback, returns `[error, result]`.
|
|
17
|
-
- **pw**: Wraps an async function, returns a new function that always returns `[error, result]`.
|
|
18
|
-
- **rw**: Wraps a sync function, returns a new function that always returns `[error, result]`.
|
|
13
|
+
- **Type-Safe**: Full TypeScript support with inference for both sync and async functions.
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
- **Tiny**: Zero dependencies and minimal footprint.
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @ntangled/kit
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
yarn add @ntangled/kit
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bun add @ntangled/kit
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm install @ntangled/kit
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
### `s` (Safe)
|
|
38
|
+
|
|
39
|
+
A lightweight utility for **Go-style error handling** in TypeScript.
|
|
40
|
+
|
|
41
|
+
Stop nesting your code in `try/catch` blocks. Use `w` (wrap) and `x` (execute) to handle errors as values.
|
|
42
|
+
|
|
43
|
+
#### Usage
|
|
44
|
+
|
|
45
|
+
You can import the core utilities from the main entry point or directly from the `safe` subpath. Combine this with `Guard clauses` (_fast exist_) and your error handling becomes much easier.
|
|
46
|
+
|
|
47
|
+
> Guard clauses are awesome and makes the code more readable and easier to extend. It is not a library but a design pattern you can learn in less than a minute.
|
|
23
48
|
|
|
24
49
|
```ts
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
50
|
+
import { x, w } from '@ntangled/kit/safe';
|
|
51
|
+
// or
|
|
52
|
+
import { s } from '@ntangled/kit';
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### Simple Example
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { x } from '@ntangled/kit/safe';
|
|
59
|
+
|
|
60
|
+
const foo = () => {
|
|
61
|
+
const [error, value] = x(() => 'Hello safe' as const);
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
type error is : {} | null
|
|
65
|
+
{} meaning everything sinde an error can be every thing
|
|
66
|
+
|
|
67
|
+
type value is : null | 'Hello safe'
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
if (error !== null) return console.error(error);
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
type error is : null
|
|
74
|
+
type value is : 'Hello safe'
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
console.log(result.foo); // 'bar'
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
foo();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
> Note on conditions: The `error` must explicitly be match to not `null` because of the type system.
|
|
84
|
+
|
|
85
|
+
> Note on return: The early return `Guard clause` is what make the type system work.
|
|
86
|
+
|
|
87
|
+
If the function you call doesn't return anything you can simply just omit the second entry (`value`). The error will still be return so you can handle it.
|
|
88
|
+
|
|
89
|
+
#### `x` (Execute)
|
|
90
|
+
|
|
91
|
+
Executes a function immediately and returns a `[error, result]` tuple.
|
|
92
|
+
|
|
93
|
+
**Synchronous**
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const [error, result] = x(() => JSON.parse('{"foo": "bar"}'));
|
|
97
|
+
|
|
98
|
+
if (error !== null) return console.error(error);
|
|
99
|
+
console.log(result.foo); // 'bar'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Asynchronous**
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const [fetchErr, response] = await x(() => fetch('https://api.example.com'));
|
|
106
|
+
if (fetchErr) {
|
|
107
|
+
// Handle network error
|
|
28
108
|
}
|
|
29
109
|
```
|
|
30
110
|
|
|
31
|
-
####
|
|
111
|
+
#### `w` (Wrap)
|
|
112
|
+
|
|
113
|
+
Wraps a function and returns a new version of that function that always returns a `[error, result]` tuple. This is perfect for defining "safe" versions of existing functions.
|
|
114
|
+
|
|
115
|
+
Wrapping is useful for functions that already are defined like core JavaScript functions like `JSON.parse`, third-party libraries or your own functions if they already are used and rewriting them is hard do to the other implementations.
|
|
32
116
|
|
|
33
117
|
```ts
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
118
|
+
const safeJsonParse = w(JSON.parse);
|
|
119
|
+
|
|
120
|
+
const [err, data] = safeJsonParse('invalid json');
|
|
121
|
+
// err is now a SyntaxError instead of throwing
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Handling Errors
|
|
125
|
+
|
|
126
|
+
If you have a function that can trow multiple different errors and you want to handle them accordingly you can use the `instanceof` operator. By using this operator TypeScript knows the type within the scope so you can work with its properties.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { x, NullError } from '@ntangled/kit/safe';
|
|
130
|
+
|
|
131
|
+
const foo () => {
|
|
132
|
+
const [error] = x(bar); // bar is just a random function name
|
|
133
|
+
|
|
134
|
+
if (error !== null) {
|
|
135
|
+
// in this scope we know error exits
|
|
136
|
+
|
|
137
|
+
if (error instanceof NullError) return console.error('A null was thrown');
|
|
138
|
+
if (error instanceof TypeError) return console.error('A TypeError was thrown');
|
|
139
|
+
// ...
|
|
140
|
+
|
|
141
|
+
return console.error('A unknown error was thrown');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// if there is no error you can safely continue your code here...
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Making a custom error**
|
|
149
|
+
|
|
150
|
+
Making custom errors can be useful. With this you can add properties to the error and then access them after using the `instanceof` operator.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
export class ValueError extends Error {
|
|
154
|
+
constructor(message: string, value: string) {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = 'ValueError';
|
|
157
|
+
|
|
158
|
+
// this is a custom value added when the error is thrown
|
|
159
|
+
this.value = value;
|
|
160
|
+
|
|
161
|
+
// this makes the instanceof work
|
|
162
|
+
Object.setPrototypeOf(this, ValueError.prototype);
|
|
163
|
+
}
|
|
37
164
|
}
|
|
165
|
+
|
|
166
|
+
// usage
|
|
167
|
+
throw new ValueError('message arg', 'value arg');
|
|
38
168
|
```
|
|
39
169
|
|
|
40
|
-
####
|
|
170
|
+
#### Handling `null` Errors
|
|
171
|
+
|
|
172
|
+
If your code throws `null` (which is technically possible in JS), this library catches it and returns a specific `NullError` class to ensure your error variable is always an actual object.
|
|
41
173
|
|
|
42
174
|
```ts
|
|
43
|
-
|
|
44
|
-
|
|
175
|
+
import { NullError } from '@ntangled/kit/safe';
|
|
176
|
+
|
|
177
|
+
const [error] = x(() => {
|
|
178
|
+
throw null;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
console.log(error instanceof NullError); // true
|
|
45
182
|
```
|
|
46
183
|
|
|
47
|
-
####
|
|
184
|
+
#### Why use this?
|
|
185
|
+
|
|
186
|
+
Standard `try/catch` blocks create extra indentation and scope-lock your variables:
|
|
187
|
+
|
|
188
|
+
**The old way:**
|
|
48
189
|
|
|
49
190
|
```ts
|
|
50
|
-
|
|
51
|
-
|
|
191
|
+
let data;
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
data = JSON.parse(str);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
// handle error
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(data); // data is available here, but the code is messy
|
|
52
200
|
```
|
|
53
201
|
|
|
54
|
-
|
|
202
|
+
**The `safe` way:**
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const [error, data] = x(() => JSON.parse(str));
|
|
206
|
+
if (error) return handle(error);
|
|
55
207
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
- Works for both sync and async functions.
|
|
59
|
-
- Prevents uncaught exceptions and null errors.
|
|
208
|
+
console.log(data); // Clean, flat, and type-safe
|
|
209
|
+
```
|
package/dist/safe.d.mts
CHANGED
|
@@ -8,8 +8,54 @@ import { NonNull } from './types.mjs';
|
|
|
8
8
|
declare class NullError extends Error {
|
|
9
9
|
constructor();
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Wraps a callback function (sync or async) and returns a function that executes the callback,
|
|
13
|
+
* capturing errors and returning them in a tuple. For async callbacks, errors are caught and returned
|
|
14
|
+
* as the first element of the tuple, and the resolved value as the second. For sync callbacks, errors
|
|
15
|
+
* are caught and returned similarly.
|
|
16
|
+
*
|
|
17
|
+
* @template Callback - The type of the callback function.
|
|
18
|
+
* @param callback - The callback function to wrap.
|
|
19
|
+
* @returns A function that, when called, returns a tuple: [error, result]. If no error occurs, error is null.
|
|
20
|
+
* For async callbacks, returns a Promise resolving to the tuple.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Synchronous usage
|
|
24
|
+
* const safeAdd = w((a: number, b: number) => a + b);
|
|
25
|
+
* const [err, sum] = safeAdd(1, 2);
|
|
26
|
+
*
|
|
27
|
+
* // Asynchronous usage
|
|
28
|
+
* const safeFetch = w(async (url: string) => fetch(url));
|
|
29
|
+
* const [err, response] = await safeFetch('https://example.com');
|
|
30
|
+
*/
|
|
11
31
|
declare function w<Callback extends (...params: any[]) => Promise<any>>(callback: Callback): (...params: Parameters<Callback>) => Promise<[NonNull, null] | [null, Awaited<ReturnType<Callback>>]>;
|
|
12
32
|
declare function w<Callback extends (...params: any[]) => any>(callback: Callback): (...params: Parameters<Callback>) => [NonNull, null] | [null, ReturnType<Callback>];
|
|
33
|
+
/**
|
|
34
|
+
* Executes a callback function (sync or async) immediately, capturing errors and returning them in a tuple.
|
|
35
|
+
* You can pass an already defined function (e.g. x(foo())) or an anonymous function (e.g. x(() => 'hello world')).
|
|
36
|
+
* Both can be async.
|
|
37
|
+
*
|
|
38
|
+
* @template Callback - The type of the callback function.
|
|
39
|
+
* @param callback - The callback function to execute.
|
|
40
|
+
* @param params - Parameters to pass to the callback function.
|
|
41
|
+
* @returns A tuple: [error, result]. If no error occurs, error is null.
|
|
42
|
+
* For async callbacks, returns a Promise resolving to the tuple.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Synchronous usage with anonymous function
|
|
46
|
+
* const [err, result] = x(() => 'hello world');
|
|
47
|
+
*
|
|
48
|
+
* // Synchronous usage with defined function
|
|
49
|
+
* function foo() { return 42; }
|
|
50
|
+
* const [err, result] = x(foo);
|
|
51
|
+
*
|
|
52
|
+
* // Asynchronous usage with anonymous function
|
|
53
|
+
* const [err, result] = await x(async () => await fetch('https://example.com'));
|
|
54
|
+
*
|
|
55
|
+
* // Asynchronous usage with defined function
|
|
56
|
+
* async function fetchData() { return await fetch('https://example.com'); }
|
|
57
|
+
* const [err, result] = await x(fetchData);
|
|
58
|
+
*/
|
|
13
59
|
declare function x<Callback extends (...params: any[]) => Promise<any>>(callback: Callback, ...params: Parameters<Callback>): Promise<[NonNull, null] | [null, Awaited<ReturnType<Callback>>]>;
|
|
14
60
|
declare function x<Callback extends (...params: any[]) => any>(callback: Callback, ...params: Parameters<Callback>): [NonNull, null] | [null, ReturnType<Callback>];
|
|
15
61
|
|
package/dist/safe.d.ts
CHANGED
|
@@ -8,8 +8,54 @@ import { NonNull } from './types.js';
|
|
|
8
8
|
declare class NullError extends Error {
|
|
9
9
|
constructor();
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Wraps a callback function (sync or async) and returns a function that executes the callback,
|
|
13
|
+
* capturing errors and returning them in a tuple. For async callbacks, errors are caught and returned
|
|
14
|
+
* as the first element of the tuple, and the resolved value as the second. For sync callbacks, errors
|
|
15
|
+
* are caught and returned similarly.
|
|
16
|
+
*
|
|
17
|
+
* @template Callback - The type of the callback function.
|
|
18
|
+
* @param callback - The callback function to wrap.
|
|
19
|
+
* @returns A function that, when called, returns a tuple: [error, result]. If no error occurs, error is null.
|
|
20
|
+
* For async callbacks, returns a Promise resolving to the tuple.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Synchronous usage
|
|
24
|
+
* const safeAdd = w((a: number, b: number) => a + b);
|
|
25
|
+
* const [err, sum] = safeAdd(1, 2);
|
|
26
|
+
*
|
|
27
|
+
* // Asynchronous usage
|
|
28
|
+
* const safeFetch = w(async (url: string) => fetch(url));
|
|
29
|
+
* const [err, response] = await safeFetch('https://example.com');
|
|
30
|
+
*/
|
|
11
31
|
declare function w<Callback extends (...params: any[]) => Promise<any>>(callback: Callback): (...params: Parameters<Callback>) => Promise<[NonNull, null] | [null, Awaited<ReturnType<Callback>>]>;
|
|
12
32
|
declare function w<Callback extends (...params: any[]) => any>(callback: Callback): (...params: Parameters<Callback>) => [NonNull, null] | [null, ReturnType<Callback>];
|
|
33
|
+
/**
|
|
34
|
+
* Executes a callback function (sync or async) immediately, capturing errors and returning them in a tuple.
|
|
35
|
+
* You can pass an already defined function (e.g. x(foo())) or an anonymous function (e.g. x(() => 'hello world')).
|
|
36
|
+
* Both can be async.
|
|
37
|
+
*
|
|
38
|
+
* @template Callback - The type of the callback function.
|
|
39
|
+
* @param callback - The callback function to execute.
|
|
40
|
+
* @param params - Parameters to pass to the callback function.
|
|
41
|
+
* @returns A tuple: [error, result]. If no error occurs, error is null.
|
|
42
|
+
* For async callbacks, returns a Promise resolving to the tuple.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Synchronous usage with anonymous function
|
|
46
|
+
* const [err, result] = x(() => 'hello world');
|
|
47
|
+
*
|
|
48
|
+
* // Synchronous usage with defined function
|
|
49
|
+
* function foo() { return 42; }
|
|
50
|
+
* const [err, result] = x(foo);
|
|
51
|
+
*
|
|
52
|
+
* // Asynchronous usage with anonymous function
|
|
53
|
+
* const [err, result] = await x(async () => await fetch('https://example.com'));
|
|
54
|
+
*
|
|
55
|
+
* // Asynchronous usage with defined function
|
|
56
|
+
* async function fetchData() { return await fetch('https://example.com'); }
|
|
57
|
+
* const [err, result] = await x(fetchData);
|
|
58
|
+
*/
|
|
13
59
|
declare function x<Callback extends (...params: any[]) => Promise<any>>(callback: Callback, ...params: Parameters<Callback>): Promise<[NonNull, null] | [null, Awaited<ReturnType<Callback>>]>;
|
|
14
60
|
declare function x<Callback extends (...params: any[]) => any>(callback: Callback, ...params: Parameters<Callback>): [NonNull, null] | [null, ReturnType<Callback>];
|
|
15
61
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ntangled/kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.1",
|
|
4
4
|
"description": "A TypeScript utility package for safe function execution and other helpers.",
|
|
5
5
|
"author": "Stephan D. <stephan.mdd@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ntangled/kit#readme",
|