nalloc 0.2.2 → 0.5.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.
Files changed (67) hide show
  1. package/README.md +326 -178
  2. package/build/codemod-cli.cjs +153 -0
  3. package/build/codemod-cli.cjs.map +1 -0
  4. package/build/codemod-cli.d.ts +2 -0
  5. package/build/codemod-cli.js +103 -0
  6. package/build/codemod-cli.js.map +1 -0
  7. package/build/codemod.cjs +652 -0
  8. package/build/codemod.cjs.map +1 -0
  9. package/build/codemod.d.ts +29 -0
  10. package/build/codemod.js +634 -0
  11. package/build/codemod.js.map +1 -0
  12. package/build/eslint.cjs +221 -0
  13. package/build/eslint.cjs.map +1 -0
  14. package/build/eslint.d.ts +36 -0
  15. package/build/eslint.js +198 -0
  16. package/build/eslint.js.map +1 -0
  17. package/build/http.cjs +31 -0
  18. package/build/http.cjs.map +1 -0
  19. package/build/http.d.ts +31 -0
  20. package/build/http.js +13 -0
  21. package/build/http.js.map +1 -0
  22. package/build/nonempty.cjs +35 -0
  23. package/build/nonempty.cjs.map +1 -0
  24. package/build/nonempty.d.ts +34 -0
  25. package/build/nonempty.js +14 -0
  26. package/build/nonempty.js.map +1 -0
  27. package/build/option.cjs +1 -1
  28. package/build/option.cjs.map +1 -1
  29. package/build/option.d.ts +2 -2
  30. package/build/option.js +1 -1
  31. package/build/option.js.map +1 -1
  32. package/build/result.cjs +10 -18
  33. package/build/result.cjs.map +1 -1
  34. package/build/result.d.ts +3 -34
  35. package/build/result.js +10 -15
  36. package/build/result.js.map +1 -1
  37. package/build/safe.cjs +8 -0
  38. package/build/safe.cjs.map +1 -1
  39. package/build/safe.d.ts +3 -0
  40. package/build/safe.js +2 -0
  41. package/build/safe.js.map +1 -1
  42. package/build/schema.cjs +32 -0
  43. package/build/schema.cjs.map +1 -0
  44. package/build/schema.d.ts +44 -0
  45. package/build/schema.js +14 -0
  46. package/build/schema.js.map +1 -0
  47. package/package.json +63 -10
  48. package/src/__tests__/codemod.ts +211 -0
  49. package/src/__tests__/eslint.ts +99 -0
  50. package/src/__tests__/fixtures/tsconfig.json +10 -0
  51. package/src/__tests__/http.ts +64 -0
  52. package/src/__tests__/iter.ts +18 -0
  53. package/src/__tests__/nonempty.ts +46 -0
  54. package/src/__tests__/nonempty.types.ts +38 -0
  55. package/src/__tests__/option.ts +4 -0
  56. package/src/__tests__/result.ts +104 -129
  57. package/src/__tests__/result.types.ts +2 -2
  58. package/src/__tests__/schema.ts +58 -0
  59. package/src/codemod-cli.ts +108 -0
  60. package/src/codemod.ts +623 -0
  61. package/src/eslint.ts +145 -0
  62. package/src/http.ts +42 -0
  63. package/src/nonempty.ts +48 -0
  64. package/src/option.ts +3 -4
  65. package/src/result.ts +18 -49
  66. package/src/safe.ts +3 -0
  67. package/src/schema.ts +52 -0
@@ -49,11 +49,9 @@ import {
49
49
  safeTry,
50
50
  safeTryAsync,
51
51
  fromPromise,
52
- fromSchema,
53
52
  gen,
54
- genAsync
53
+ genAsync,
55
54
  } from '../result.js';
56
- import type { StandardSchema } from '../result.js';
57
55
  import { ok, err, isOk, isErr, optionOf as optOf, none } from '../types.js';
58
56
 
59
57
  describe('Result', () => {
@@ -86,7 +84,6 @@ describe('Result', () => {
86
84
  expect(isErr(failure)).toBe(true);
87
85
  expect((failure as any).error.message).toBe('failed');
88
86
  });
89
-
90
87
  });
91
88
 
92
89
  describe('type guards', () => {
@@ -127,7 +124,7 @@ describe('Result', () => {
127
124
 
128
125
  describe('map', () => {
129
126
  it('maps Ok value', () => {
130
- const result = map(ok(2), x => x * 3);
127
+ const result = map(ok(2), (x) => x * 3);
131
128
  expect(unwrap(result)).toBe(6);
132
129
  });
133
130
 
@@ -140,7 +137,7 @@ describe('Result', () => {
140
137
 
141
138
  describe('mapErr', () => {
142
139
  it('maps Err value', () => {
143
- const result = mapErr(err('error'), e => e.toUpperCase());
140
+ const result = mapErr(err('error'), (e) => e.toUpperCase());
144
141
  expect(isErr(result)).toBe(true);
145
142
  expect((result as any).error).toBe('ERROR');
146
143
  });
@@ -154,12 +151,12 @@ describe('Result', () => {
154
151
 
155
152
  describe('flatMap', () => {
156
153
  it('flatMaps Ok value', () => {
157
- const result = flatMap(ok(2), x => ok(x * 3));
154
+ const result = flatMap(ok(2), (x) => ok(x * 3));
158
155
  expect(unwrap(result)).toBe(6);
159
156
  });
160
157
 
161
158
  it('can return Err from mapper', () => {
162
- const result = flatMap(ok(2), x => err(`error: ${x}`));
159
+ const result = flatMap(ok(2), (x) => err(`error: ${x}`));
163
160
  expect(isErr(result)).toBe(true);
164
161
  expect((result as any).error).toBe('error: 2');
165
162
  });
@@ -173,7 +170,7 @@ describe('Result', () => {
173
170
 
174
171
  describe('andThen', () => {
175
172
  it('aliases flatMap for Ok', () => {
176
- const result = andThen(ok(2), x => ok(x * 4));
173
+ const result = andThen(ok(2), (x) => ok(x * 4));
177
174
  expect(unwrap(result)).toBe(8);
178
175
  });
179
176
 
@@ -188,7 +185,7 @@ describe('Result', () => {
188
185
  it('runs for Ok and returns original', () => {
189
186
  let seen = 0;
190
187
  const res = ok(7);
191
- const out = tap(res, value => {
188
+ const out = tap(res, (value) => {
192
189
  seen = value;
193
190
  });
194
191
  expect(out).toBe(res);
@@ -209,7 +206,7 @@ describe('Result', () => {
209
206
  it('runs for Err and returns original', () => {
210
207
  let seen = '';
211
208
  const res = err('nope');
212
- const out = tapErr(res, error => {
209
+ const out = tapErr(res, (error) => {
213
210
  seen = error;
214
211
  });
215
212
  expect(out).toBe(res);
@@ -228,12 +225,20 @@ describe('Result', () => {
228
225
 
229
226
  describe('bimap', () => {
230
227
  it('maps Ok value', () => {
231
- const result = bimap(ok(2), x => x * 3, e => e);
228
+ const result = bimap(
229
+ ok(2),
230
+ (x) => x * 3,
231
+ (e) => e,
232
+ );
232
233
  expect(unwrap(result)).toBe(6);
233
234
  });
234
235
 
235
236
  it('maps Err value', () => {
236
- const result = bimap(err('error'), (x: number) => x * 3, e => e.toUpperCase());
237
+ const result = bimap(
238
+ err('error'),
239
+ (x: number) => x * 3,
240
+ (e) => e.toUpperCase(),
241
+ );
237
242
  expect(isErr(result)).toBe(true);
238
243
  expect((result as any).error).toBe('ERROR');
239
244
  });
@@ -247,7 +252,11 @@ describe('Result', () => {
247
252
  it('throws error value for Err', () => {
248
253
  const e = err('error');
249
254
  let caught: unknown;
250
- try { unwrap(e); } catch (x) { caught = x; }
255
+ try {
256
+ unwrap(e);
257
+ } catch (x) {
258
+ caught = x;
259
+ }
251
260
  expect(caught).toBe('error');
252
261
  });
253
262
  });
@@ -279,7 +288,7 @@ describe('Result', () => {
279
288
 
280
289
  it('calls function for Err', () => {
281
290
  let called = false;
282
- const result = unwrapOrElse(err('error'), e => {
291
+ const result = unwrapOrElse(err('error'), (e) => {
283
292
  called = true;
284
293
  expect(e).toBe('error');
285
294
  return 99;
@@ -291,7 +300,7 @@ describe('Result', () => {
291
300
 
292
301
  describe('mapOr', () => {
293
302
  it('maps Ok value', () => {
294
- const result = mapOr(ok(2), 10, x => x * 3);
303
+ const result = mapOr(ok(2), 10, (x) => x * 3);
295
304
  expect(result).toBe(6);
296
305
  });
297
306
 
@@ -303,16 +312,24 @@ describe('Result', () => {
303
312
 
304
313
  describe('mapOrElse', () => {
305
314
  it('maps Ok value', () => {
306
- const result = mapOrElse(ok(2), () => 10, x => x * 3);
315
+ const result = mapOrElse(
316
+ ok(2),
317
+ () => 10,
318
+ (x) => x * 3,
319
+ );
307
320
  expect(result).toBe(6);
308
321
  });
309
322
 
310
323
  it('returns default for Err', () => {
311
324
  let called = false;
312
- const result = mapOrElse(err('error'), () => {
313
- called = true;
314
- return 10;
315
- }, (x: number) => x * 3);
325
+ const result = mapOrElse(
326
+ err('error'),
327
+ () => {
328
+ called = true;
329
+ return 10;
330
+ },
331
+ (x: number) => x * 3,
332
+ );
316
333
  expect(result).toBe(10);
317
334
  expect(called).toBe(true);
318
335
  });
@@ -383,7 +400,7 @@ describe('Result', () => {
383
400
 
384
401
  it('calls function if Err', () => {
385
402
  let called = false;
386
- const result = orElse(err('error'), e => {
403
+ const result = orElse(err('error'), (e) => {
387
404
  called = true;
388
405
  expect(e).toBe('error');
389
406
  return ok(2);
@@ -462,7 +479,6 @@ describe('Result', () => {
462
479
  expect(unwrap(result)).toBe(42);
463
480
  });
464
481
 
465
-
466
482
  it('returns outer Err', () => {
467
483
  const result = flatten(err('outer error'));
468
484
  expect(isErr(result)).toBe(true);
@@ -474,8 +490,8 @@ describe('Result', () => {
474
490
  it('calls ok branch for Ok', () => {
475
491
  const result = match(
476
492
  ok(42),
477
- x => `value: ${x}`,
478
- e => `error: ${e}`,
493
+ (x) => `value: ${x}`,
494
+ (e) => `error: ${e}`,
479
495
  );
480
496
  expect(result).toBe('value: 42');
481
497
  });
@@ -484,7 +500,7 @@ describe('Result', () => {
484
500
  const result = match(
485
501
  err('failed'),
486
502
  (x: number) => `value: ${x}`,
487
- e => `error: ${e}`,
503
+ (e) => `error: ${e}`,
488
504
  );
489
505
  expect(result).toBe('error: failed');
490
506
  });
@@ -566,11 +582,11 @@ describe('Result', () => {
566
582
 
567
583
  describe('isOkAnd', () => {
568
584
  it('returns true if Ok and predicate true', () => {
569
- expect(isOkAnd(ok(5), x => x > 3)).toBe(true);
585
+ expect(isOkAnd(ok(5), (x) => x > 3)).toBe(true);
570
586
  });
571
587
 
572
588
  it('returns false if Ok and predicate false', () => {
573
- expect(isOkAnd(ok(2), x => x > 3)).toBe(false);
589
+ expect(isOkAnd(ok(2), (x) => x > 3)).toBe(false);
574
590
  });
575
591
 
576
592
  it('returns false for Err', () => {
@@ -580,11 +596,11 @@ describe('Result', () => {
580
596
 
581
597
  describe('isErrAnd', () => {
582
598
  it('returns true if Err and predicate true', () => {
583
- expect(isErrAnd(err('error'), e => e.length > 3)).toBe(true);
599
+ expect(isErrAnd(err('error'), (e) => e.length > 3)).toBe(true);
584
600
  });
585
601
 
586
602
  it('returns false if Err and predicate false', () => {
587
- expect(isErrAnd(err('no'), e => e.length > 3)).toBe(false);
603
+ expect(isErrAnd(err('no'), (e) => e.length > 3)).toBe(false);
588
604
  });
589
605
 
590
606
  it('returns false for Ok', () => {
@@ -598,12 +614,11 @@ describe('Result', () => {
598
614
  () => {
599
615
  throw new Error('boom');
600
616
  },
601
- error => (error as Error).message,
617
+ (error) => (error as Error).message,
602
618
  );
603
619
  expect(isErr(result)).toBe(true);
604
620
  expect((result as any).error).toBe('boom');
605
621
  });
606
-
607
622
  });
608
623
 
609
624
  describe('wrap', () => {
@@ -643,7 +658,7 @@ describe('Result', () => {
643
658
  });
644
659
 
645
660
  it('throws Err error for failed Result', () => {
646
- const fn = (x: number) => x > 0 ? ok(x) : err('negative');
661
+ const fn = (x: number) => (x > 0 ? ok(x) : err('negative'));
647
662
  const throwing = toThrowable(fn);
648
663
  expect(() => throwing(-1)).toThrow('negative');
649
664
  });
@@ -715,22 +730,14 @@ describe('Result', () => {
715
730
  });
716
731
 
717
732
  it('partitionAsync separates async results', async () => {
718
- const promises = [
719
- Promise.resolve(ok(1)),
720
- Promise.resolve(err('a')),
721
- Promise.resolve(ok(2)),
722
- ];
733
+ const promises = [Promise.resolve(ok(1)), Promise.resolve(err('a')), Promise.resolve(ok(2))];
723
734
  const [oks, errs] = await partitionAsync(promises);
724
735
  expect(oks).toEqual([1, 2]);
725
736
  expect(errs).toEqual(['a']);
726
737
  });
727
738
 
728
739
  it('partitionAsync handles rejected promises as errors', async () => {
729
- const promises = [
730
- Promise.resolve(ok(1)),
731
- Promise.reject(new Error('rejected')),
732
- Promise.resolve(ok(2)),
733
- ];
740
+ const promises = [Promise.resolve(ok(1)), Promise.reject(new Error('rejected')), Promise.resolve(ok(2))];
734
741
  const [oks, errs] = await partitionAsync(promises);
735
742
  expect(oks).toEqual([1, 2]);
736
743
  expect(errs).toHaveLength(1);
@@ -748,12 +755,6 @@ describe('Result', () => {
748
755
  expect(isOk(outcome)).toBe(true);
749
756
  expect(unwrap(outcome)).toBe(3);
750
757
  });
751
-
752
- it('any returns empty errors for empty array', () => {
753
- const outcome = any([]);
754
- expect(isErr(outcome)).toBe(true);
755
- expect((outcome as any).error).toEqual([]);
756
- });
757
758
  });
758
759
 
759
760
  describe('tryCatchMaybePromise', () => {
@@ -785,8 +786,10 @@ describe('Result', () => {
785
786
 
786
787
  it('uses onError mapper for sync throw', () => {
787
788
  const result = tryCatchMaybePromise(
788
- () => { throw new Error('fail'); },
789
- (e) => `mapped: ${(e as Error).message}`
789
+ () => {
790
+ throw new Error('fail');
791
+ },
792
+ (e) => `mapped: ${(e as Error).message}`,
790
793
  );
791
794
  expect(isErr(result)).toBe(true);
792
795
  expect((result as any).error).toBe('mapped: fail');
@@ -795,7 +798,7 @@ describe('Result', () => {
795
798
  it('uses onError mapper for async rejection', async () => {
796
799
  const result = await tryCatchMaybePromise(
797
800
  () => Promise.reject(new Error('fail')),
798
- (e) => `mapped: ${(e as Error).message}`
801
+ (e) => `mapped: ${(e as Error).message}`,
799
802
  );
800
803
  expect(isErr(result)).toBe(true);
801
804
  expect((result as any).error).toBe('mapped: fail');
@@ -824,10 +827,7 @@ describe('Result', () => {
824
827
  });
825
828
 
826
829
  it('handles all async values', async () => {
827
- const result = await settleMaybePromise([
828
- Promise.resolve(1),
829
- Promise.resolve(2)
830
- ]);
830
+ const result = await settleMaybePromise([Promise.resolve(1), Promise.resolve(2)]);
831
831
  expect(result).toEqual([1, 2]);
832
832
  });
833
833
 
@@ -852,28 +852,20 @@ describe('Result', () => {
852
852
  });
853
853
 
854
854
  it('preserves input order with interleaved sync/async', async () => {
855
- const result = await partitionMaybePromise([
856
- Promise.resolve(ok(1)),
857
- ok(2),
858
- Promise.resolve(err('a')),
859
- err('b'),
855
+ const result = await partitionMaybePromise([Promise.resolve(ok(1)), ok(2), Promise.resolve(err('a')), err('b')]);
856
+ expect(result).toEqual([
857
+ [1, 2],
858
+ ['a', 'b'],
860
859
  ]);
861
- expect(result).toEqual([[1, 2], ['a', 'b']]);
862
860
  });
863
861
 
864
862
  it('handles all async values', async () => {
865
- const result = await partitionMaybePromise([
866
- Promise.resolve(ok(1)),
867
- Promise.resolve(err('a'))
868
- ]);
863
+ const result = await partitionMaybePromise([Promise.resolve(ok(1)), Promise.resolve(err('a'))]);
869
864
  expect(result).toEqual([[1], ['a']]);
870
865
  });
871
866
 
872
867
  it('handles rejected promises as errors', async () => {
873
- const result = await partitionMaybePromise([
874
- ok(1),
875
- Promise.reject(new Error('rejected'))
876
- ]);
868
+ const result = await partitionMaybePromise([ok(1), Promise.reject(new Error('rejected'))]);
877
869
  expect(result[0]).toEqual([1]);
878
870
  expect(result[1]).toHaveLength(1);
879
871
  expect((result[1][0] as Error).message).toBe('rejected');
@@ -944,7 +936,7 @@ describe('Result', () => {
944
936
  });
945
937
 
946
938
  it('propagates Err through chain', () => {
947
- const parse = (s: string) => s === 'bad' ? err('parse error') : ok(Number(s));
939
+ const parse = (s: string) => (s === 'bad' ? err('parse error') : ok(Number(s)));
948
940
  const result = safeTry(() => {
949
941
  const a = unwrap(parse('10'));
950
942
  const b = unwrap(parse('bad'));
@@ -1001,10 +993,7 @@ describe('Result', () => {
1001
993
  });
1002
994
 
1003
995
  it('uses onError mapper for rejected promise', async () => {
1004
- const result = await fromPromise(
1005
- Promise.reject(new Error('boom')),
1006
- (e) => `mapped: ${(e as Error).message}`,
1007
- );
996
+ const result = await fromPromise(Promise.reject(new Error('boom')), (e) => `mapped: ${(e as Error).message}`);
1008
997
  expect(isErr(result)).toBe(true);
1009
998
  expect((result as { error: string }).error).toBe('mapped: boom');
1010
999
  });
@@ -1022,53 +1011,9 @@ describe('Result', () => {
1022
1011
  });
1023
1012
  });
1024
1013
 
1025
- describe('fromSchema', () => {
1026
- const validSchema: StandardSchema<string> = {
1027
- '~standard': {
1028
- validate: (value) => typeof value === 'string'
1029
- ? { value }
1030
- : { issues: [{ message: 'Expected string' }] },
1031
- },
1032
- };
1033
-
1034
- const asyncSchema: StandardSchema<number> = {
1035
- '~standard': {
1036
- validate: (value) => Promise.resolve(
1037
- typeof value === 'number'
1038
- ? { value }
1039
- : { issues: [{ message: 'Expected number' }] },
1040
- ),
1041
- },
1042
- };
1043
-
1044
- it('returns Ok for valid sync schema', () => {
1045
- const result = fromSchema(validSchema, 'hello');
1046
- expect(isOk(result)).toBe(true);
1047
- expect(result).toBe('hello');
1048
- });
1049
-
1050
- it('returns Err with issues for invalid sync schema', () => {
1051
- const result = fromSchema(validSchema, 42);
1052
- expect(isErr(result)).toBe(true);
1053
- expect((result as any).error).toEqual([{ message: 'Expected string' }]);
1054
- });
1055
-
1056
- it('returns Ok for valid async schema', async () => {
1057
- const result = await fromSchema(asyncSchema, 42);
1058
- expect(isOk(result)).toBe(true);
1059
- expect(result).toBe(42);
1060
- });
1061
-
1062
- it('returns Err for invalid async schema', async () => {
1063
- const result = await fromSchema(asyncSchema, 'hello');
1064
- expect(isErr(result)).toBe(true);
1065
- expect((result as any).error).toEqual([{ message: 'Expected number' }]);
1066
- });
1067
- });
1068
-
1069
1014
  describe('gen', () => {
1070
1015
  it('returns Ok for successful execution', () => {
1071
- const result = gen(function*($) {
1016
+ const result = gen(function* ($) {
1072
1017
  const a = yield* $(ok(10));
1073
1018
  const b = yield* $(ok(5));
1074
1019
  return a + b;
@@ -1078,7 +1023,7 @@ describe('Result', () => {
1078
1023
  });
1079
1024
 
1080
1025
  it('short-circuits on first Err', () => {
1081
- const result = gen(function*($) {
1026
+ const result = gen(function* ($) {
1082
1027
  const a = yield* $(ok(10));
1083
1028
  const b = yield* $(err('fail') as Result<number, string>);
1084
1029
  const c = yield* $(ok(5));
@@ -1089,8 +1034,8 @@ describe('Result', () => {
1089
1034
  });
1090
1035
 
1091
1036
  it('propagates Err through chain', () => {
1092
- const parse = (s: string) => s === 'bad' ? err('parse error') : ok(Number(s));
1093
- const result = gen(function*($) {
1037
+ const parse = (s: string) => (s === 'bad' ? err('parse error') : ok(Number(s)));
1038
+ const result = gen(function* ($) {
1094
1039
  const a = yield* $(parse('10'));
1095
1040
  const b = yield* $(parse('bad'));
1096
1041
  const c = yield* $(parse('5'));
@@ -1101,17 +1046,33 @@ describe('Result', () => {
1101
1046
  });
1102
1047
 
1103
1048
  it('returns Ok for empty generator', () => {
1104
- const result = gen(function*() {
1049
+ const result = gen(function* () {
1105
1050
  return 42;
1106
1051
  });
1107
1052
  expect(isOk(result)).toBe(true);
1108
1053
  expect(result).toBe(42);
1109
1054
  });
1055
+
1056
+ it('runs try/finally cleanup when short-circuiting on Err', () => {
1057
+ let cleanedUp = false;
1058
+ const result = gen(function* ($) {
1059
+ try {
1060
+ const a = yield* $(ok(10));
1061
+ const b = yield* $(err('fail') as Result<number, string>);
1062
+ return a + b;
1063
+ } finally {
1064
+ cleanedUp = true;
1065
+ }
1066
+ });
1067
+ expect(isErr(result)).toBe(true);
1068
+ expect((result as any).error).toBe('fail');
1069
+ expect(cleanedUp).toBe(true);
1070
+ });
1110
1071
  });
1111
1072
 
1112
1073
  describe('genAsync', () => {
1113
1074
  it('returns Ok for successful async execution', async () => {
1114
- const result = await genAsync(async function*($) {
1075
+ const result = await genAsync(async function* ($) {
1115
1076
  const a = yield* $(ok(10));
1116
1077
  const b = yield* $(await fromPromise(Promise.resolve(5)));
1117
1078
  return a + b;
@@ -1121,7 +1082,7 @@ describe('Result', () => {
1121
1082
  });
1122
1083
 
1123
1084
  it('short-circuits on first Err', async () => {
1124
- const result = await genAsync(async function*($) {
1085
+ const result = await genAsync(async function* ($) {
1125
1086
  const a = yield* $(ok(10));
1126
1087
  const b = yield* $(err('async fail') as Result<number, string>);
1127
1088
  return a + b;
@@ -1131,7 +1092,7 @@ describe('Result', () => {
1131
1092
  });
1132
1093
 
1133
1094
  it('handles rejected promises via fromPromise', async () => {
1134
- const result = await genAsync(async function*($) {
1095
+ const result = await genAsync(async function* ($) {
1135
1096
  const a = yield* $(await fromPromise(Promise.resolve(10)));
1136
1097
  const b = yield* $(await fromPromise(Promise.reject('boom')));
1137
1098
  return a + b;
@@ -1139,7 +1100,21 @@ describe('Result', () => {
1139
1100
  expect(isErr(result)).toBe(true);
1140
1101
  expect((result as any).error).toBe('boom');
1141
1102
  });
1142
- });
1143
1103
 
1104
+ it('runs try/finally cleanup when short-circuiting on Err', async () => {
1105
+ let cleanedUp = false;
1106
+ const result = await genAsync(async function* ($) {
1107
+ try {
1108
+ const a = yield* $(ok(10));
1109
+ const b = yield* $(err('async fail') as Result<number, string>);
1110
+ return a + b;
1111
+ } finally {
1112
+ cleanedUp = true;
1113
+ }
1114
+ });
1115
+ expect(isErr(result)).toBe(true);
1116
+ expect((result as any).error).toBe('async fail');
1117
+ expect(cleanedUp).toBe(true);
1118
+ });
1119
+ });
1144
1120
  });
1145
-
@@ -138,7 +138,7 @@ const collected = collect(results);
138
138
  assert<Equals<typeof collected, Result<number[], string>>>;
139
139
 
140
140
  const collectedAll = collectAll(results);
141
- assert<Equals<typeof collectedAll, Result<number[], string[]>>>;
141
+ assert<Equals<typeof collectedAll, Result<number[], [string, ...string[]]>>>;
142
142
 
143
143
  const transposed = transpose(ok(42 as Option<number>) as Result<Option<number>, string>);
144
144
  assert<Equals<typeof transposed, Option<Result<number, string>>>>;
@@ -234,7 +234,7 @@ const allResult = all([ok(1), ok(2)]);
234
234
  assert<Equals<typeof allResult, Result<readonly number[], unknown>>>;
235
235
 
236
236
  const anyResult = any([err('a'), ok(3)]);
237
- assert<Equals<typeof anyResult, Result<number, string[]>>>;
237
+ assert<Equals<typeof anyResult, Result<number, [string, ...string[]]>>>;
238
238
 
239
239
  const partitionAsyncResult = partitionAsync([Promise.resolve(ok(1)), Promise.resolve(err('a'))]);
240
240
  assert<Equals<typeof partitionAsyncResult, Promise<[number[], string[]]>>>;
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { fromSchema, wrapSchema } from '../schema.js';
3
+ import type { StandardSchema } from '../schema.js';
4
+ import { isOk, isErr } from '../types.js';
5
+
6
+ describe('Schema', () => {
7
+ const validSchema: StandardSchema<string> = {
8
+ '~standard': {
9
+ validate: (value) => (typeof value === 'string' ? { value } : { issues: [{ message: 'Expected string' }] }),
10
+ },
11
+ };
12
+
13
+ const asyncSchema: StandardSchema<number> = {
14
+ '~standard': {
15
+ validate: (value) => Promise.resolve(typeof value === 'number' ? { value } : { issues: [{ message: 'Expected number' }] }),
16
+ },
17
+ };
18
+
19
+ describe('fromSchema', () => {
20
+ it('returns Ok for valid sync schema', () => {
21
+ const result = fromSchema(validSchema, 'hello');
22
+ expect(isOk(result)).toBe(true);
23
+ expect(result).toBe('hello');
24
+ });
25
+
26
+ it('returns Err with issues for invalid sync schema', () => {
27
+ const result = fromSchema(validSchema, 42);
28
+ expect(isErr(result)).toBe(true);
29
+ expect((result as any).error).toEqual([{ message: 'Expected string' }]);
30
+ });
31
+
32
+ it('returns Ok for valid async schema', async () => {
33
+ const result = await fromSchema(asyncSchema, 42);
34
+ expect(isOk(result)).toBe(true);
35
+ expect(result).toBe(42);
36
+ });
37
+
38
+ it('returns Err for invalid async schema', async () => {
39
+ const result = await fromSchema(asyncSchema, 'hello');
40
+ expect(isErr(result)).toBe(true);
41
+ expect((result as any).error).toEqual([{ message: 'Expected number' }]);
42
+ });
43
+ });
44
+
45
+ describe('wrapSchema', () => {
46
+ it('creates a reusable sync validator', () => {
47
+ const parse = wrapSchema(validSchema);
48
+ expect(parse('hello')).toBe('hello');
49
+ expect(isErr(parse(42))).toBe(true);
50
+ });
51
+
52
+ it('creates a reusable async validator', async () => {
53
+ const parse = wrapSchema(asyncSchema);
54
+ expect(await parse(42)).toBe(42);
55
+ expect(isErr(await parse('hello'))).toBe(true);
56
+ });
57
+ });
58
+ });