@nlozgachev/pipekit 0.1.6 → 0.1.7

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.
@@ -0,0 +1,242 @@
1
+ import { Result } from "./Result.js";
2
+ export var These;
3
+ (function (These) {
4
+ /**
5
+ * Creates a These holding only an error/warning (no success value).
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * These.toErr("Something went wrong");
10
+ * ```
11
+ */
12
+ These.toErr = (error) => Result.toErr(error);
13
+ /**
14
+ * Creates a These holding only a success value (no error).
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * These.toOk(42);
19
+ * ```
20
+ */
21
+ These.toOk = (value) => Result.toOk(value);
22
+ /**
23
+ * Creates a These holding both an error/warning and a success value.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * These.toBoth("Deprecated API used", result);
28
+ * ```
29
+ */
30
+ These.toBoth = (error, value) => ({
31
+ kind: "Both",
32
+ error,
33
+ value,
34
+ });
35
+ /**
36
+ * Type guard — checks if a These holds only an error/warning.
37
+ */
38
+ These.isErr = (data) => data.kind === "Error";
39
+ /**
40
+ * Type guard — checks if a These holds only a success value.
41
+ */
42
+ These.isOk = (data) => data.kind === "Ok";
43
+ /**
44
+ * Type guard — checks if a These holds both an error/warning and a success value.
45
+ */
46
+ These.isBoth = (data) => data.kind === "Both";
47
+ /**
48
+ * Returns true if the These contains a success value (Ok or Both).
49
+ */
50
+ These.hasValue = (data) => data.kind === "Ok" || data.kind === "Both";
51
+ /**
52
+ * Returns true if the These contains an error/warning (Err or Both).
53
+ */
54
+ These.hasError = (data) => data.kind === "Error" || data.kind === "Both";
55
+ /**
56
+ * Transforms the success value, leaving the error unchanged.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * pipe(These.toOk(5), These.map(n => n * 2)); // Ok(10)
61
+ * pipe(These.toBoth("warn", 5), These.map(n => n * 2)); // Both("warn", 10)
62
+ * pipe(These.toErr("err"), These.map(n => n * 2)); // Err("err")
63
+ * ```
64
+ */
65
+ These.map = (f) => (data) => {
66
+ if (These.isErr(data))
67
+ return data;
68
+ if (These.isOk(data))
69
+ return These.toOk(f(data.value));
70
+ return These.toBoth(data.error, f(data.value));
71
+ };
72
+ /**
73
+ * Transforms the error/warning value, leaving the success value unchanged.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * pipe(These.toErr("err"), These.mapErr(e => e.toUpperCase())); // Err("ERR")
78
+ * pipe(These.toBoth("warn", 5), These.mapErr(e => e.toUpperCase())); // Both("WARN", 5)
79
+ * ```
80
+ */
81
+ These.mapErr = (f) => (data) => {
82
+ if (These.isOk(data))
83
+ return data;
84
+ if (These.isErr(data))
85
+ return These.toErr(f(data.error));
86
+ return These.toBoth(f(data.error), data.value);
87
+ };
88
+ /**
89
+ * Transforms both the error and success values independently.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * pipe(
94
+ * These.toBoth("warn", 5),
95
+ * These.bimap(e => e.toUpperCase(), n => n * 2)
96
+ * ); // Both("WARN", 10)
97
+ * ```
98
+ */
99
+ These.bimap = (onErr, onOk) => (data) => {
100
+ if (These.isErr(data))
101
+ return These.toErr(onErr(data.error));
102
+ if (These.isOk(data))
103
+ return These.toOk(onOk(data.value));
104
+ return These.toBoth(onErr(data.error), onOk(data.value));
105
+ };
106
+ /**
107
+ * Chains These computations by passing the success value to f.
108
+ * - Err propagates unchanged.
109
+ * - Ok(a) applies f(a) directly.
110
+ * - Both(e, a): applies f(a); if the result is Ok(b), returns Both(e, b)
111
+ * to preserve the warning. Otherwise returns f(a) as-is.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * const double = (n: number): These<string, number> => These.toOk(n * 2);
116
+ *
117
+ * pipe(These.toOk(5), These.chain(double)); // Ok(10)
118
+ * pipe(These.toBoth("warn", 5), These.chain(double)); // Both("warn", 10)
119
+ * pipe(These.toErr("err"), These.chain(double)); // Err("err")
120
+ * ```
121
+ */
122
+ These.chain = (f) => (data) => {
123
+ if (These.isErr(data))
124
+ return data;
125
+ if (These.isOk(data))
126
+ return f(data.value);
127
+ const result = f(data.value);
128
+ return These.isOk(result) ? These.toBoth(data.error, result.value) : result;
129
+ };
130
+ /**
131
+ * Extracts a value from a These by providing handlers for all three cases.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * pipe(
136
+ * these,
137
+ * These.fold(
138
+ * e => `Error: ${e}`,
139
+ * a => `Value: ${a}`,
140
+ * (e, a) => `Both: ${e} / ${a}`
141
+ * )
142
+ * );
143
+ * ```
144
+ */
145
+ These.fold = (onErr, onOk, onBoth) => (data) => {
146
+ if (These.isErr(data))
147
+ return onErr(data.error);
148
+ if (These.isOk(data))
149
+ return onOk(data.value);
150
+ return onBoth(data.error, data.value);
151
+ };
152
+ /**
153
+ * Pattern matches on a These, returning the result of the matching case.
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * pipe(
158
+ * these,
159
+ * These.match({
160
+ * err: e => `Error: ${e}`,
161
+ * ok: a => `Value: ${a}`,
162
+ * both: (e, a) => `Both: ${e} / ${a}`
163
+ * })
164
+ * );
165
+ * ```
166
+ */
167
+ These.match = (cases) => (data) => {
168
+ if (These.isErr(data))
169
+ return cases.err(data.error);
170
+ if (These.isOk(data))
171
+ return cases.ok(data.value);
172
+ return cases.both(data.error, data.value);
173
+ };
174
+ /**
175
+ * Returns the success value, or a default if the These has no success value.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * pipe(These.toOk(5), These.getOrElse(0)); // 5
180
+ * pipe(These.toBoth("warn", 5), These.getOrElse(0)); // 5
181
+ * pipe(These.toErr("err"), These.getOrElse(0)); // 0
182
+ * ```
183
+ */
184
+ These.getOrElse = (defaultValue) => (data) => These.hasValue(data) ? data.value : defaultValue;
185
+ /**
186
+ * Executes a side effect on the success value without changing the These.
187
+ * Useful for logging or debugging.
188
+ */
189
+ These.tap = (f) => (data) => {
190
+ if (These.hasValue(data))
191
+ f(data.value);
192
+ return data;
193
+ };
194
+ /**
195
+ * Swaps the roles of error and success values.
196
+ * - Err(e) → Ok(e)
197
+ * - Ok(a) → Err(a)
198
+ * - Both(e, a) → Both(a, e)
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * These.swap(These.toErr("err")); // Ok("err")
203
+ * These.swap(These.toOk(5)); // Err(5)
204
+ * These.swap(These.toBoth("warn", 5)); // Both(5, "warn")
205
+ * ```
206
+ */
207
+ These.swap = (data) => {
208
+ if (These.isErr(data))
209
+ return These.toOk(data.error);
210
+ if (These.isOk(data))
211
+ return These.toErr(data.value);
212
+ return These.toBoth(data.value, data.error);
213
+ };
214
+ /**
215
+ * Converts a These to an Option.
216
+ * Ok and Both produce Some; Err produces None.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * These.toOption(These.toOk(42)); // Some(42)
221
+ * These.toOption(These.toBoth("warn", 42)); // Some(42)
222
+ * These.toOption(These.toErr("err")); // None
223
+ * ```
224
+ */
225
+ These.toOption = (data) => These.hasValue(data) ? { kind: "Some", value: data.value } : { kind: "None" };
226
+ /**
227
+ * Converts a These to a Result, discarding any warning from Both.
228
+ * Ok and Both produce Ok; Err produces Err.
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * These.toResult(These.toOk(42)); // Ok(42)
233
+ * These.toResult(These.toBoth("warn", 42)); // Ok(42)
234
+ * These.toResult(These.toErr("err")); // Err("err")
235
+ * ```
236
+ */
237
+ These.toResult = (data) => {
238
+ if (These.hasValue(data))
239
+ return Result.toOk(data.value);
240
+ return data;
241
+ };
242
+ })(These || (These = {}));
@@ -4,5 +4,8 @@ export * from "./Rec.js";
4
4
  export * from "./RemoteData.js";
5
5
  export * from "./Result.js";
6
6
  export * from "./Task.js";
7
+ export * from "./TaskOption.js";
7
8
  export * from "./TaskResult.js";
9
+ export * from "./TaskValidation.js";
10
+ export * from "./These.js";
8
11
  export * from "./Validation.js";
@@ -0,0 +1,28 @@
1
+ export var Brand;
2
+ (function (Brand) {
3
+ /**
4
+ * Creates a branding constructor for brand K over type T.
5
+ * The resulting function performs an unchecked cast — only use when the raw
6
+ * value is known to satisfy the brand's invariants.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * type PositiveNumber = Brand<"PositiveNumber", number>;
11
+ * const toPositiveNumber = Brand.make<"PositiveNumber", number>();
12
+ *
13
+ * const n: PositiveNumber = toPositiveNumber(42);
14
+ * ```
15
+ */
16
+ Brand.make = () => (value) => value;
17
+ /**
18
+ * Strips the brand and returns the underlying value.
19
+ * Since Brand<K, T> extends T this is rarely needed, but can improve readability.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const userId: UserId = toUserId("user-123");
24
+ * const raw: string = Brand.unwrap(userId); // "user-123"
25
+ * ```
26
+ */
27
+ Brand.unwrap = (branded) => branded;
28
+ })(Brand || (Brand = {}));
@@ -1 +1,2 @@
1
+ export * from "./Brand.js";
1
2
  export * from "./NonEmptyList.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nlozgachev/pipekit",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Simple functional programming toolkit for TypeScript",
5
5
  "keywords": [
6
6
  "functional",
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaskOption = void 0;
4
+ const Option_js_1 = require("./Option.js");
5
+ const Task_js_1 = require("./Task.js");
6
+ var TaskOption;
7
+ (function (TaskOption) {
8
+ /**
9
+ * Wraps a value in a Some inside a Task.
10
+ */
11
+ TaskOption.of = (value) => Task_js_1.Task.of(Option_js_1.Option.of(value));
12
+ /**
13
+ * Creates a TaskOption that resolves to None.
14
+ */
15
+ TaskOption.none = () => Task_js_1.Task.of(Option_js_1.Option.toNone());
16
+ /**
17
+ * Lifts an Option into a TaskOption.
18
+ */
19
+ TaskOption.fromOption = (option) => Task_js_1.Task.of(option);
20
+ /**
21
+ * Lifts a Task into a TaskOption by wrapping its result in Some.
22
+ */
23
+ TaskOption.fromTask = (task) => Task_js_1.Task.map(Option_js_1.Option.of)(task);
24
+ /**
25
+ * Creates a TaskOption from a Promise-returning function.
26
+ * Returns Some if the promise resolves, None if it rejects.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const fetchUser = TaskOption.tryCatch(() =>
31
+ * fetch("/user/1").then(r => r.json())
32
+ * );
33
+ * ```
34
+ */
35
+ TaskOption.tryCatch = (f) => () => f().then(Option_js_1.Option.of).catch(() => Option_js_1.Option.toNone());
36
+ /**
37
+ * Transforms the value inside a TaskOption.
38
+ */
39
+ TaskOption.map = (f) => (data) => Task_js_1.Task.map(Option_js_1.Option.map(f))(data);
40
+ /**
41
+ * Chains TaskOption computations. If the first resolves to Some, passes the
42
+ * value to f. If the first resolves to None, propagates None.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * pipe(
47
+ * findUser("123"),
48
+ * TaskOption.chain(user => findOrg(user.orgId))
49
+ * )();
50
+ * ```
51
+ */
52
+ TaskOption.chain = (f) => (data) => Task_js_1.Task.chain((option) => Option_js_1.Option.isSome(option) ? f(option.value) : Task_js_1.Task.of(Option_js_1.Option.toNone()))(data);
53
+ /**
54
+ * Applies a function wrapped in a TaskOption to a value wrapped in a TaskOption.
55
+ * Both Tasks run in parallel.
56
+ */
57
+ TaskOption.ap = (arg) => (data) => () => Promise.all([data(), arg()]).then(([of_, oa]) => Option_js_1.Option.ap(oa)(of_));
58
+ /**
59
+ * Extracts a value from a TaskOption by providing handlers for both cases.
60
+ */
61
+ TaskOption.fold = (onNone, onSome) => (data) => Task_js_1.Task.map(Option_js_1.Option.fold(onNone, onSome))(data);
62
+ /**
63
+ * Pattern matches on a TaskOption, returning a Task of the result.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * pipe(
68
+ * findUser("123"),
69
+ * TaskOption.match({
70
+ * some: user => `Hello, ${user.name}`,
71
+ * none: () => "User not found"
72
+ * })
73
+ * )();
74
+ * ```
75
+ */
76
+ TaskOption.match = (cases) => (data) => Task_js_1.Task.map(Option_js_1.Option.match(cases))(data);
77
+ /**
78
+ * Returns the value or a default if the TaskOption resolves to None.
79
+ */
80
+ TaskOption.getOrElse = (defaultValue) => (data) => Task_js_1.Task.map(Option_js_1.Option.getOrElse(defaultValue))(data);
81
+ /**
82
+ * Executes a side effect on the value without changing the TaskOption.
83
+ * Useful for logging or debugging.
84
+ */
85
+ TaskOption.tap = (f) => (data) => Task_js_1.Task.map(Option_js_1.Option.tap(f))(data);
86
+ /**
87
+ * Filters the value inside a TaskOption. Returns None if the predicate fails.
88
+ */
89
+ TaskOption.filter = (predicate) => (data) => Task_js_1.Task.map(Option_js_1.Option.filter(predicate))(data);
90
+ /**
91
+ * Converts a TaskOption to a TaskResult, using onNone to produce the error value.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * pipe(
96
+ * findUser("123"),
97
+ * TaskOption.toTaskResult(() => "User not found")
98
+ * );
99
+ * ```
100
+ */
101
+ TaskOption.toTaskResult = (onNone) => (data) => Task_js_1.Task.map(Option_js_1.Option.toResult(onNone))(data);
102
+ })(TaskOption || (exports.TaskOption = TaskOption = {}));
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaskValidation = void 0;
4
+ const Task_js_1 = require("./Task.js");
5
+ const Validation_js_1 = require("./Validation.js");
6
+ var TaskValidation;
7
+ (function (TaskValidation) {
8
+ /**
9
+ * Wraps a value in a valid TaskValidation.
10
+ */
11
+ TaskValidation.of = (value) => Task_js_1.Task.of(Validation_js_1.Validation.of(value));
12
+ /**
13
+ * Creates a failed TaskValidation with a single error.
14
+ */
15
+ TaskValidation.fail = (error) => Task_js_1.Task.of(Validation_js_1.Validation.fail(error));
16
+ /**
17
+ * Lifts a Validation into a TaskValidation.
18
+ */
19
+ TaskValidation.fromValidation = (validation) => Task_js_1.Task.of(validation);
20
+ /**
21
+ * Creates a TaskValidation from a Promise-returning function.
22
+ * Catches any errors and transforms them using the onError function.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const fetchUser = (id: string): TaskValidation<string, User> =>
27
+ * TaskValidation.tryCatch(
28
+ * () => fetch(`/users/${id}`).then(r => r.json()),
29
+ * e => `Failed to fetch user: ${e}`
30
+ * );
31
+ * ```
32
+ */
33
+ TaskValidation.tryCatch = (f, onError) => () => f()
34
+ .then((Validation_js_1.Validation.of))
35
+ .catch((e) => Validation_js_1.Validation.fail(onError(e)));
36
+ /**
37
+ * Transforms the success value inside a TaskValidation.
38
+ */
39
+ TaskValidation.map = (f) => (data) => Task_js_1.Task.map(Validation_js_1.Validation.map(f))(data);
40
+ /**
41
+ * Chains TaskValidation computations. If the first is Valid, passes the value
42
+ * to f. If the first is Invalid, propagates the errors.
43
+ *
44
+ * Note: chain short-circuits on first error. Use ap to accumulate errors.
45
+ */
46
+ TaskValidation.chain = (f) => (data) => Task_js_1.Task.chain((validation) => Validation_js_1.Validation.isValid(validation)
47
+ ? f(validation.value)
48
+ : Task_js_1.Task.of(Validation_js_1.Validation.toInvalid(validation.errors)))(data);
49
+ /**
50
+ * Applies a function wrapped in a TaskValidation to a value wrapped in a
51
+ * TaskValidation. Both Tasks run in parallel and errors from both sides
52
+ * are accumulated.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * pipe(
57
+ * TaskValidation.of((name: string) => (age: number) => ({ name, age })),
58
+ * TaskValidation.ap(validateName(name)),
59
+ * TaskValidation.ap(validateAge(age))
60
+ * )();
61
+ * ```
62
+ */
63
+ TaskValidation.ap = (arg) => (data) => () => Promise.all([data(), arg()]).then(([vf, va]) => Validation_js_1.Validation.ap(va)(vf));
64
+ /**
65
+ * Extracts a value from a TaskValidation by providing handlers for both cases.
66
+ */
67
+ TaskValidation.fold = (onInvalid, onValid) => (data) => Task_js_1.Task.map(Validation_js_1.Validation.fold(onInvalid, onValid))(data);
68
+ /**
69
+ * Pattern matches on a TaskValidation, returning a Task of the result.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * pipe(
74
+ * validateForm(input),
75
+ * TaskValidation.match({
76
+ * valid: data => save(data),
77
+ * invalid: errors => showErrors(errors)
78
+ * })
79
+ * )();
80
+ * ```
81
+ */
82
+ TaskValidation.match = (cases) => (data) => Task_js_1.Task.map(Validation_js_1.Validation.match(cases))(data);
83
+ /**
84
+ * Returns the success value or a default value if the TaskValidation is invalid.
85
+ */
86
+ TaskValidation.getOrElse = (defaultValue) => (data) => Task_js_1.Task.map(Validation_js_1.Validation.getOrElse(defaultValue))(data);
87
+ /**
88
+ * Executes a side effect on the success value without changing the TaskValidation.
89
+ * Useful for logging or debugging.
90
+ */
91
+ TaskValidation.tap = (f) => (data) => Task_js_1.Task.map(Validation_js_1.Validation.tap(f))(data);
92
+ /**
93
+ * Recovers from an Invalid state by providing a fallback TaskValidation.
94
+ */
95
+ TaskValidation.recover = (fallback) => (data) => Task_js_1.Task.chain((validation) => Validation_js_1.Validation.isValid(validation) ? Task_js_1.Task.of(validation) : fallback())(data);
96
+ })(TaskValidation || (exports.TaskValidation = TaskValidation = {}));