nalloc 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 +282 -0
- package/build/devtools.cjs +79 -0
- package/build/devtools.cjs.map +1 -0
- package/build/devtools.d.ts +82 -0
- package/build/devtools.js +43 -0
- package/build/devtools.js.map +1 -0
- package/build/index.cjs +76 -0
- package/build/index.cjs.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +5 -0
- package/build/index.js.map +1 -0
- package/build/option.cjs +279 -0
- package/build/option.cjs.map +1 -0
- package/build/option.d.ts +356 -0
- package/build/option.js +157 -0
- package/build/option.js.map +1 -0
- package/build/result.cjs +381 -0
- package/build/result.cjs.map +1 -0
- package/build/result.d.ts +442 -0
- package/build/result.js +229 -0
- package/build/result.js.map +1 -0
- package/build/safe.cjs +88 -0
- package/build/safe.cjs.map +1 -0
- package/build/safe.d.ts +29 -0
- package/build/safe.js +18 -0
- package/build/safe.js.map +1 -0
- package/build/testing.cjs +111 -0
- package/build/testing.cjs.map +1 -0
- package/build/testing.d.ts +85 -0
- package/build/testing.js +81 -0
- package/build/testing.js.map +1 -0
- package/build/types.cjs +78 -0
- package/build/types.cjs.map +1 -0
- package/build/types.d.ts +137 -0
- package/build/types.js +36 -0
- package/build/types.js.map +1 -0
- package/build/unsafe.cjs +82 -0
- package/build/unsafe.cjs.map +1 -0
- package/build/unsafe.d.ts +27 -0
- package/build/unsafe.js +11 -0
- package/build/unsafe.js.map +1 -0
- package/package.json +93 -0
- package/src/__tests__/index.ts +13 -0
- package/src/__tests__/option.ts +610 -0
- package/src/__tests__/option.types.ts +120 -0
- package/src/__tests__/result.ts +721 -0
- package/src/__tests__/result.types.ts +249 -0
- package/src/__tests__/safe.ts +24 -0
- package/src/__tests__/tooling.ts +86 -0
- package/src/__tests__/unsafe.ts +24 -0
- package/src/devtools.ts +97 -0
- package/src/index.ts +18 -0
- package/src/option.ts +510 -0
- package/src/result.ts +676 -0
- package/src/safe.ts +58 -0
- package/src/testing.ts +159 -0
- package/src/types.ts +201 -0
- package/src/unsafe.ts +47 -0
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
of,
|
|
4
|
+
ofAsync,
|
|
5
|
+
isSomeErr,
|
|
6
|
+
map,
|
|
7
|
+
mapErr,
|
|
8
|
+
flatMap,
|
|
9
|
+
andThen,
|
|
10
|
+
bimap,
|
|
11
|
+
tap,
|
|
12
|
+
tapErr,
|
|
13
|
+
unwrap,
|
|
14
|
+
unwrapErr,
|
|
15
|
+
unwrapOr,
|
|
16
|
+
unwrapOrElse,
|
|
17
|
+
mapOr,
|
|
18
|
+
mapOrElse,
|
|
19
|
+
expect as expectRes,
|
|
20
|
+
expectErr,
|
|
21
|
+
and,
|
|
22
|
+
or,
|
|
23
|
+
orElse,
|
|
24
|
+
toOption,
|
|
25
|
+
toErrorOption,
|
|
26
|
+
zip,
|
|
27
|
+
zipWith,
|
|
28
|
+
flatten,
|
|
29
|
+
match,
|
|
30
|
+
partition,
|
|
31
|
+
collect,
|
|
32
|
+
collectAll,
|
|
33
|
+
transpose,
|
|
34
|
+
isOkAnd,
|
|
35
|
+
isErrAnd,
|
|
36
|
+
tryCatch,
|
|
37
|
+
tryAsync,
|
|
38
|
+
fromPromise,
|
|
39
|
+
mapAsync,
|
|
40
|
+
andThenAsync,
|
|
41
|
+
matchAsync,
|
|
42
|
+
unwrapOrReturn,
|
|
43
|
+
assertOk,
|
|
44
|
+
assertErr,
|
|
45
|
+
all,
|
|
46
|
+
any,
|
|
47
|
+
partitionAsync
|
|
48
|
+
} from '../result.js';
|
|
49
|
+
import { ok, err, isOk, isErr, optionOf as optOf, none } from '../types.js';
|
|
50
|
+
import { formatResult, inspectResult } from '../devtools.js';
|
|
51
|
+
|
|
52
|
+
describe('Result', () => {
|
|
53
|
+
describe('constructors', () => {
|
|
54
|
+
it('ok creates Ok result', () => {
|
|
55
|
+
const result = ok(42);
|
|
56
|
+
expect(isOk(result)).toBe(true);
|
|
57
|
+
expect(result).toBe(42);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('ok does not throw when passed an Err', () => {
|
|
61
|
+
const error = err('test error');
|
|
62
|
+
expect(() => ok(error)).not.toThrow();
|
|
63
|
+
expect(isErr(ok(error))).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('err creates Err result', () => {
|
|
67
|
+
const result = err('error');
|
|
68
|
+
expect(isErr(result)).toBe(true);
|
|
69
|
+
expect(result.error).toBe('error');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('of catches errors and returns Result', () => {
|
|
73
|
+
const success = of(() => 42);
|
|
74
|
+
expect(isOk(success)).toBe(true);
|
|
75
|
+
expect(unwrap(success)).toBe(42);
|
|
76
|
+
|
|
77
|
+
const failure = of(() => {
|
|
78
|
+
throw new Error('failed');
|
|
79
|
+
});
|
|
80
|
+
expect(isErr(failure)).toBe(true);
|
|
81
|
+
expect((failure as any).error.message).toBe('failed');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('ofAsync catches async errors', async () => {
|
|
85
|
+
const success = await ofAsync(async () => 42);
|
|
86
|
+
expect(isOk(success)).toBe(true);
|
|
87
|
+
expect(unwrap(success)).toBe(42);
|
|
88
|
+
|
|
89
|
+
const failure = await ofAsync(async () => {
|
|
90
|
+
throw new Error('async failed');
|
|
91
|
+
});
|
|
92
|
+
expect(isErr(failure)).toBe(true);
|
|
93
|
+
expect((failure as any).error.message).toBe('async failed');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('type guards', () => {
|
|
98
|
+
it('isOk returns true for Ok', () => {
|
|
99
|
+
expect(isOk(ok(42))).toBe(true);
|
|
100
|
+
expect(isOk(ok(0))).toBe(true);
|
|
101
|
+
expect(isOk(ok(''))).toBe(true);
|
|
102
|
+
expect(isOk(ok(null))).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('isOk returns false for Err', () => {
|
|
106
|
+
expect(isOk(err('error'))).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('isErr returns true for Err', () => {
|
|
110
|
+
expect(isErr(err('error'))).toBe(true);
|
|
111
|
+
expect(isErr(err(null))).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('isErr returns false for Ok', () => {
|
|
115
|
+
expect(isErr(ok(42))).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('isSomeErr returns true for Err with non-null value', () => {
|
|
119
|
+
expect(isSomeErr(err('error'))).toBe(true);
|
|
120
|
+
expect(isSomeErr(err(0))).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('isSomeErr returns false for Err with null/undefined', () => {
|
|
124
|
+
expect(isSomeErr(err(null))).toBe(false);
|
|
125
|
+
expect(isSomeErr(err(undefined))).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('isSomeErr returns false for Ok', () => {
|
|
129
|
+
expect(isSomeErr(ok(42))).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('map', () => {
|
|
134
|
+
it('maps Ok value', () => {
|
|
135
|
+
const result = map(ok(2), x => x * 3);
|
|
136
|
+
expect(unwrap(result)).toBe(6);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('ignores Err', () => {
|
|
140
|
+
const result = map(err('error'), (x: number) => x * 3);
|
|
141
|
+
expect(isErr(result)).toBe(true);
|
|
142
|
+
expect((result as any).error).toBe('error');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('mapErr', () => {
|
|
147
|
+
it('maps Err value', () => {
|
|
148
|
+
const result = mapErr(err('error'), e => e.toUpperCase());
|
|
149
|
+
expect(isErr(result)).toBe(true);
|
|
150
|
+
expect((result as any).error).toBe('ERROR');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('ignores Ok', () => {
|
|
154
|
+
const result = mapErr(ok(42), (e: string) => e.toUpperCase());
|
|
155
|
+
expect(isOk(result)).toBe(true);
|
|
156
|
+
expect(unwrap(result)).toBe(42);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('flatMap', () => {
|
|
161
|
+
it('flatMaps Ok value', () => {
|
|
162
|
+
const result = flatMap(ok(2), x => ok(x * 3));
|
|
163
|
+
expect(unwrap(result)).toBe(6);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('can return Err from mapper', () => {
|
|
167
|
+
const result = flatMap(ok(2), x => err(`error: ${x}`));
|
|
168
|
+
expect(isErr(result)).toBe(true);
|
|
169
|
+
expect((result as any).error).toBe('error: 2');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('ignores Err', () => {
|
|
173
|
+
const result = flatMap(err('error'), (x: number) => ok(x * 3));
|
|
174
|
+
expect(isErr(result)).toBe(true);
|
|
175
|
+
expect((result as any).error).toBe('error');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('andThen', () => {
|
|
180
|
+
it('aliases flatMap for Ok', () => {
|
|
181
|
+
const result = andThen(ok(2), x => ok(x * 4));
|
|
182
|
+
expect(unwrap(result)).toBe(8);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('returns Err for Err', () => {
|
|
186
|
+
const result = andThen(err('error'), (x: number) => ok(x * 4));
|
|
187
|
+
expect(isErr(result)).toBe(true);
|
|
188
|
+
expect((result as any).error).toBe('error');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('tap', () => {
|
|
193
|
+
it('runs for Ok and returns original', () => {
|
|
194
|
+
let seen = 0;
|
|
195
|
+
const res = ok(7);
|
|
196
|
+
const out = tap(res, value => {
|
|
197
|
+
seen = value;
|
|
198
|
+
});
|
|
199
|
+
expect(out).toBe(res);
|
|
200
|
+
expect(seen).toBe(7);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('does not run for Err', () => {
|
|
204
|
+
let called = false;
|
|
205
|
+
const out = tap(err('boom'), () => {
|
|
206
|
+
called = true;
|
|
207
|
+
});
|
|
208
|
+
expect(isErr(out)).toBe(true);
|
|
209
|
+
expect(called).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('tapErr', () => {
|
|
214
|
+
it('runs for Err and returns original', () => {
|
|
215
|
+
let seen = '';
|
|
216
|
+
const res = err('nope');
|
|
217
|
+
const out = tapErr(res, error => {
|
|
218
|
+
seen = error;
|
|
219
|
+
});
|
|
220
|
+
expect(out).toBe(res);
|
|
221
|
+
expect(seen).toBe('nope');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('does not run for Ok', () => {
|
|
225
|
+
let called = false;
|
|
226
|
+
const out = tapErr(ok(5), () => {
|
|
227
|
+
called = true;
|
|
228
|
+
});
|
|
229
|
+
expect(isOk(out)).toBe(true);
|
|
230
|
+
expect(called).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('bimap', () => {
|
|
235
|
+
it('maps Ok value', () => {
|
|
236
|
+
const result = bimap(ok(2), x => x * 3, e => e);
|
|
237
|
+
expect(unwrap(result)).toBe(6);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('maps Err value', () => {
|
|
241
|
+
const result = bimap(err('error'), (x: number) => x * 3, e => e.toUpperCase());
|
|
242
|
+
expect(isErr(result)).toBe(true);
|
|
243
|
+
expect((result as any).error).toBe('ERROR');
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('unwrap', () => {
|
|
248
|
+
it('returns value for Ok', () => {
|
|
249
|
+
expect(unwrap(ok(42))).toBe(42);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('throws for Err', () => {
|
|
253
|
+
expect(() => unwrap(err('error'))).toThrow('Called unwrap on Err: error');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('unwrapErr', () => {
|
|
258
|
+
it('returns error for Err', () => {
|
|
259
|
+
expect(unwrapErr(err('error'))).toBe('error');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('throws for Ok', () => {
|
|
263
|
+
expect(() => unwrapErr(ok(42))).toThrow('Called unwrapErr on Ok: 42');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('unwrapOr', () => {
|
|
268
|
+
it('returns value for Ok', () => {
|
|
269
|
+
expect(unwrapOr(ok(42), 0)).toBe(42);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('returns default for Err', () => {
|
|
273
|
+
expect(unwrapOr(err('error'), 99)).toBe(99);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('unwrapOrElse', () => {
|
|
278
|
+
it('returns value for Ok', () => {
|
|
279
|
+
expect(unwrapOrElse(ok(42), () => 0)).toBe(42);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('calls function for Err', () => {
|
|
283
|
+
let called = false;
|
|
284
|
+
const result = unwrapOrElse(err('error'), e => {
|
|
285
|
+
called = true;
|
|
286
|
+
expect(e).toBe('error');
|
|
287
|
+
return 99;
|
|
288
|
+
});
|
|
289
|
+
expect(result).toBe(99);
|
|
290
|
+
expect(called).toBe(true);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('mapOr', () => {
|
|
295
|
+
it('maps Ok value', () => {
|
|
296
|
+
const result = mapOr(ok(2), 10, x => x * 3);
|
|
297
|
+
expect(result).toBe(6);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('returns default for Err', () => {
|
|
301
|
+
const result = mapOr(err('error'), 10, (x: number) => x * 3);
|
|
302
|
+
expect(result).toBe(10);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
describe('mapOrElse', () => {
|
|
307
|
+
it('maps Ok value', () => {
|
|
308
|
+
const result = mapOrElse(ok(2), () => 10, x => x * 3);
|
|
309
|
+
expect(result).toBe(6);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('returns default for Err', () => {
|
|
313
|
+
let called = false;
|
|
314
|
+
const result = mapOrElse(err('error'), () => {
|
|
315
|
+
called = true;
|
|
316
|
+
return 10;
|
|
317
|
+
}, (x: number) => x * 3);
|
|
318
|
+
expect(result).toBe(10);
|
|
319
|
+
expect(called).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
describe('expect', () => {
|
|
324
|
+
it('returns value for Ok', () => {
|
|
325
|
+
expect(expectRes(ok(42), 'error')).toBe(42);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('throws with message for Err', () => {
|
|
329
|
+
expect(() => expectRes(err('failed'), 'custom error')).toThrow('custom error: failed');
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('expectErr', () => {
|
|
334
|
+
it('returns error for Err', () => {
|
|
335
|
+
expect(expectErr(err('error'), 'msg')).toBe('error');
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('throws with message for Ok', () => {
|
|
339
|
+
expect(() => expectErr(ok(42), 'custom error')).toThrow('custom error: 42');
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
describe('and', () => {
|
|
344
|
+
it('returns second if first is Ok', () => {
|
|
345
|
+
const result = and(ok(1), ok(2));
|
|
346
|
+
expect(unwrap(result)).toBe(2);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('returns first Err if first is Err', () => {
|
|
350
|
+
const result = and(err('first'), ok(2));
|
|
351
|
+
expect(isErr(result)).toBe(true);
|
|
352
|
+
expect((result as any).error).toBe('first');
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('returns second Err if first is Ok and second is Err', () => {
|
|
356
|
+
const result = and(ok(1), err('second'));
|
|
357
|
+
expect(isErr(result)).toBe(true);
|
|
358
|
+
expect((result as any).error).toBe('second');
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe('or', () => {
|
|
363
|
+
it('returns first if Ok', () => {
|
|
364
|
+
const result = or(ok(1), ok(2));
|
|
365
|
+
expect(unwrap(result)).toBe(1);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('returns second if first is Err', () => {
|
|
369
|
+
const result = or(err('error'), ok(2));
|
|
370
|
+
expect(unwrap(result)).toBe(2);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('returns second Err if both Err', () => {
|
|
374
|
+
const result = or(err('first'), err('second'));
|
|
375
|
+
expect(isErr(result)).toBe(true);
|
|
376
|
+
expect((result as any).error).toBe('second');
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe('orElse', () => {
|
|
381
|
+
it('returns first if Ok', () => {
|
|
382
|
+
const result = orElse(ok(1), () => ok(2));
|
|
383
|
+
expect(unwrap(result)).toBe(1);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('calls function if Err', () => {
|
|
387
|
+
let called = false;
|
|
388
|
+
const result = orElse(err('error'), e => {
|
|
389
|
+
called = true;
|
|
390
|
+
expect(e).toBe('error');
|
|
391
|
+
return ok(2);
|
|
392
|
+
});
|
|
393
|
+
expect(unwrap(result)).toBe(2);
|
|
394
|
+
expect(called).toBe(true);
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe('toOption', () => {
|
|
399
|
+
it('converts Ok to Some', () => {
|
|
400
|
+
const opt = toOption(ok(3));
|
|
401
|
+
expect(opt).toBe(3);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('converts Err to None', () => {
|
|
405
|
+
const opt = toOption(err('nope'));
|
|
406
|
+
expect(opt).toBe(none);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe('toErrorOption', () => {
|
|
411
|
+
it('converts Err to Some', () => {
|
|
412
|
+
const opt = toErrorOption(err('fail'));
|
|
413
|
+
expect(opt).toBe('fail');
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('converts Ok to None', () => {
|
|
417
|
+
const opt = toErrorOption(ok(3));
|
|
418
|
+
expect(opt).toBe(none);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
describe('zip', () => {
|
|
423
|
+
it('zips two Ok results', () => {
|
|
424
|
+
const result = zip(ok(1), ok('a'));
|
|
425
|
+
expect(unwrap(result)).toEqual([1, 'a']);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('returns first Err', () => {
|
|
429
|
+
const result = zip(err('first'), ok('a'));
|
|
430
|
+
expect(isErr(result)).toBe(true);
|
|
431
|
+
expect((result as any).error).toBe('first');
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
describe('zipWith', () => {
|
|
436
|
+
it('combines Ok results', () => {
|
|
437
|
+
const result = zipWith(ok(2), ok(3), (a, b) => a + b);
|
|
438
|
+
expect(unwrap(result)).toBe(5);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('returns Err from left', () => {
|
|
442
|
+
const result = zipWith(err('fail'), ok(3), (a, b) => a + b);
|
|
443
|
+
expect(isErr(result)).toBe(true);
|
|
444
|
+
expect((result as any).error).toBe('fail');
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
describe('flatten', () => {
|
|
449
|
+
it('flattens nested Ok', () => {
|
|
450
|
+
const nested = ok(ok(42));
|
|
451
|
+
const result = flatten(nested);
|
|
452
|
+
expect(unwrap(result)).toBe(42);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
it('returns outer Err', () => {
|
|
457
|
+
const result = flatten(err('outer error'));
|
|
458
|
+
expect(isErr(result)).toBe(true);
|
|
459
|
+
expect((result as any).error).toBe('outer error');
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
describe('match', () => {
|
|
464
|
+
it('calls ok branch for Ok', () => {
|
|
465
|
+
const result = match(
|
|
466
|
+
ok(42),
|
|
467
|
+
x => `value: ${x}`,
|
|
468
|
+
e => `error: ${e}`,
|
|
469
|
+
);
|
|
470
|
+
expect(result).toBe('value: 42');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('calls err branch for Err', () => {
|
|
474
|
+
const result = match(
|
|
475
|
+
err('failed'),
|
|
476
|
+
(x: number) => `value: ${x}`,
|
|
477
|
+
e => `error: ${e}`,
|
|
478
|
+
);
|
|
479
|
+
expect(result).toBe('error: failed');
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
describe('partition', () => {
|
|
484
|
+
it('partitions array of Results', () => {
|
|
485
|
+
const results = [ok(1), err('a'), ok(2), err('b'), ok(3)];
|
|
486
|
+
const [oks, errs] = partition(results);
|
|
487
|
+
expect(oks).toEqual([1, 2, 3]);
|
|
488
|
+
expect(errs).toEqual(['a', 'b']);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('handles all Ok', () => {
|
|
492
|
+
const results = [ok(1), ok(2), ok(3)];
|
|
493
|
+
const [oks, errs] = partition(results);
|
|
494
|
+
expect(oks).toEqual([1, 2, 3]);
|
|
495
|
+
expect(errs).toEqual([]);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('handles all Err', () => {
|
|
499
|
+
const results = [err('a'), err('b'), err('c')];
|
|
500
|
+
const [oks, errs] = partition(results);
|
|
501
|
+
expect(oks).toEqual([]);
|
|
502
|
+
expect(errs).toEqual(['a', 'b', 'c']);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
describe('collect', () => {
|
|
507
|
+
it('collects all Ok values', () => {
|
|
508
|
+
const results = [ok(1), ok(2), ok(3)];
|
|
509
|
+
const collected = collect(results);
|
|
510
|
+
expect(isOk(collected)).toBe(true);
|
|
511
|
+
expect(unwrap(collected)).toEqual([1, 2, 3]);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('returns first Err', () => {
|
|
515
|
+
const results = [ok(1), err('first'), ok(2), err('second')];
|
|
516
|
+
const collected = collect(results);
|
|
517
|
+
expect(isErr(collected)).toBe(true);
|
|
518
|
+
expect((collected as any).error).toBe('first');
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
describe('collectAll', () => {
|
|
523
|
+
it('collects all Ok values if no errors', () => {
|
|
524
|
+
const results = [ok(1), ok(2), ok(3)];
|
|
525
|
+
const collected = collectAll(results);
|
|
526
|
+
expect(isOk(collected)).toBe(true);
|
|
527
|
+
expect(unwrap(collected)).toEqual([1, 2, 3]);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('collects all errors if any', () => {
|
|
531
|
+
const results = [ok(1), err('a'), ok(2), err('b')];
|
|
532
|
+
const collected = collectAll(results);
|
|
533
|
+
expect(isErr(collected)).toBe(true);
|
|
534
|
+
expect((collected as any).error).toEqual(['a', 'b']);
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
describe('transpose', () => {
|
|
539
|
+
it('transposes Ok(Some)', () => {
|
|
540
|
+
const result = transpose(ok(optOf(42)));
|
|
541
|
+
expect(isOk(unwrap(result))).toBe(true);
|
|
542
|
+
expect(unwrap(unwrap(result))).toBe(42);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('transposes Ok(None)', () => {
|
|
546
|
+
const result = transpose(ok(none));
|
|
547
|
+
expect(result).toBe(undefined);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('transposes Err', () => {
|
|
551
|
+
const result = transpose(err('error'));
|
|
552
|
+
expect(isErr(result)).toBe(true);
|
|
553
|
+
expect((result as any).error).toBe('error');
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
describe('isOkAnd', () => {
|
|
558
|
+
it('returns true if Ok and predicate true', () => {
|
|
559
|
+
expect(isOkAnd(ok(5), x => x > 3)).toBe(true);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('returns false if Ok and predicate false', () => {
|
|
563
|
+
expect(isOkAnd(ok(2), x => x > 3)).toBe(false);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('returns false for Err', () => {
|
|
567
|
+
expect(isOkAnd(err('error'), () => true)).toBe(false);
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
describe('isErrAnd', () => {
|
|
572
|
+
it('returns true if Err and predicate true', () => {
|
|
573
|
+
expect(isErrAnd(err('error'), e => e.length > 3)).toBe(true);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it('returns false if Err and predicate false', () => {
|
|
577
|
+
expect(isErrAnd(err('no'), e => e.length > 3)).toBe(false);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it('returns false for Ok', () => {
|
|
581
|
+
expect(isErrAnd(ok(42), () => true)).toBe(false);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
describe('try helpers', () => {
|
|
586
|
+
it('tryCatch maps thrown error', () => {
|
|
587
|
+
const result = tryCatch<number, string>(
|
|
588
|
+
() => {
|
|
589
|
+
throw new Error('boom');
|
|
590
|
+
},
|
|
591
|
+
error => (error as Error).message,
|
|
592
|
+
);
|
|
593
|
+
expect(isErr(result)).toBe(true);
|
|
594
|
+
expect((result as any).error).toBe('boom');
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('tryAsync maps async rejection', async () => {
|
|
598
|
+
const result = await tryAsync<number, string>(
|
|
599
|
+
async () => {
|
|
600
|
+
throw new Error('boom');
|
|
601
|
+
},
|
|
602
|
+
error => (error as Error).message,
|
|
603
|
+
);
|
|
604
|
+
expect(isErr(result)).toBe(true);
|
|
605
|
+
expect((result as any).error).toBe('boom');
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('fromPromise resolves to Ok', async () => {
|
|
609
|
+
const result = await fromPromise(Promise.resolve(5));
|
|
610
|
+
expect(isOk(result)).toBe(true);
|
|
611
|
+
expect(unwrap(result)).toBe(5);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('fromPromise maps rejection', async () => {
|
|
615
|
+
const result = await fromPromise(
|
|
616
|
+
Promise.reject(new Error('fail')),
|
|
617
|
+
error => (error as Error).message.toUpperCase(),
|
|
618
|
+
);
|
|
619
|
+
expect(isErr(result)).toBe(true);
|
|
620
|
+
expect((result as any).error).toBe('FAIL');
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
describe('async mapping', () => {
|
|
625
|
+
it('mapAsync maps Ok value', async () => {
|
|
626
|
+
const result = await mapAsync(ok(2), async value => value * 3);
|
|
627
|
+
expect(isOk(result)).toBe(true);
|
|
628
|
+
expect(unwrap(result)).toBe(6);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('mapAsync returns Err unchanged', async () => {
|
|
632
|
+
const result = await mapAsync(err('nope'), async (value: number) => value * 3);
|
|
633
|
+
expect(isErr(result)).toBe(true);
|
|
634
|
+
expect((result as any).error).toBe('nope');
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it('andThenAsync chains Ok value', async () => {
|
|
638
|
+
const result = await andThenAsync(ok(2), async value => ok(value * 4));
|
|
639
|
+
expect(isOk(result)).toBe(true);
|
|
640
|
+
expect(unwrap(result)).toBe(8);
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it('matchAsync selects branch', async () => {
|
|
644
|
+
const okValue = await matchAsync(
|
|
645
|
+
ok(5),
|
|
646
|
+
async value => value * 2,
|
|
647
|
+
async () => 0,
|
|
648
|
+
);
|
|
649
|
+
expect(okValue).toBe(10);
|
|
650
|
+
|
|
651
|
+
const errValue = await matchAsync(
|
|
652
|
+
err('fail'),
|
|
653
|
+
async () => 0,
|
|
654
|
+
async error => error.length,
|
|
655
|
+
);
|
|
656
|
+
expect(errValue).toBe(4);
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
describe('control helpers', () => {
|
|
661
|
+
it('unwrapOrReturn returns fallback for Err', () => {
|
|
662
|
+
const value = unwrapOrReturn(err('oops'), () => 'fallback');
|
|
663
|
+
expect(value).toBe('fallback');
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it('assertOk narrows success', () => {
|
|
667
|
+
const result = ok({ id: 1 });
|
|
668
|
+
assertOk(result);
|
|
669
|
+
expect(result.id).toBe(1);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it('assertErr narrows failure', () => {
|
|
673
|
+
const result = err('oops');
|
|
674
|
+
assertErr(result);
|
|
675
|
+
expect(result.error).toBe('oops');
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
describe('collections', () => {
|
|
680
|
+
it('all aggregates Ok values', () => {
|
|
681
|
+
const outcome = all([ok(1), ok(2), ok(3)]);
|
|
682
|
+
expect(isOk(outcome)).toBe(true);
|
|
683
|
+
expect(unwrap(outcome)).toEqual([1, 2, 3]);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it('any returns first Ok', () => {
|
|
687
|
+
const outcome = any([err('a'), ok(2), err('b')]);
|
|
688
|
+
expect(isOk(outcome)).toBe(true);
|
|
689
|
+
expect(unwrap(outcome)).toBe(2);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('any aggregates errors when none Ok', () => {
|
|
693
|
+
const outcome = any([err('a'), err('b')]);
|
|
694
|
+
expect(isErr(outcome)).toBe(true);
|
|
695
|
+
expect((outcome as any).error).toEqual(['a', 'b']);
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
it('partitionAsync separates async results', async () => {
|
|
699
|
+
const promises = [
|
|
700
|
+
Promise.resolve(ok(1)),
|
|
701
|
+
Promise.resolve(err('a')),
|
|
702
|
+
Promise.resolve(ok(2)),
|
|
703
|
+
];
|
|
704
|
+
const [oks, errs] = await partitionAsync(promises);
|
|
705
|
+
expect(oks).toEqual([1, 2]);
|
|
706
|
+
expect(errs).toEqual(['a']);
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
describe('introspection helpers', () => {
|
|
711
|
+
it('inspectResult exposes discriminated metadata', () => {
|
|
712
|
+
expect(inspectResult(ok(4))).toEqual({ status: 'ok', value: 4 });
|
|
713
|
+
expect(inspectResult(err('error'))).toEqual({ status: 'err', error: 'error' });
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it('formatResult prints friendly string', () => {
|
|
717
|
+
expect(formatResult(ok(1))).toBe('Ok(1)');
|
|
718
|
+
expect(formatResult(err(new Error('boom')))).toBe('Err(boom)');
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
});
|