@nunofyobiz/effect-extras 2.1.0 → 3.1.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 (46) hide show
  1. package/README.md +2 -1
  2. package/dist/ArrayX.d.ts +0 -34
  3. package/dist/ArrayX.d.ts.map +1 -1
  4. package/dist/ArrayX.js +4 -58
  5. package/dist/ArrayX.js.map +1 -1
  6. package/dist/InclusiveOr.d.ts +1123 -0
  7. package/dist/InclusiveOr.d.ts.map +1 -0
  8. package/dist/InclusiveOr.js +1074 -0
  9. package/dist/InclusiveOr.js.map +1 -0
  10. package/dist/NonNullableX.d.ts.map +1 -1
  11. package/dist/NonNullableX.js +5 -0
  12. package/dist/NonNullableX.js.map +1 -1
  13. package/dist/OptionX.d.ts +8 -2
  14. package/dist/OptionX.d.ts.map +1 -1
  15. package/dist/OptionX.js +7 -1
  16. package/dist/OptionX.js.map +1 -1
  17. package/dist/PredicateX.d.ts +32 -0
  18. package/dist/PredicateX.d.ts.map +1 -1
  19. package/dist/PredicateX.js +38 -0
  20. package/dist/PredicateX.js.map +1 -1
  21. package/dist/RecordX.d.ts +128 -1
  22. package/dist/RecordX.d.ts.map +1 -1
  23. package/dist/RecordX.js +162 -1
  24. package/dist/RecordX.js.map +1 -1
  25. package/dist/StringX.d.ts +61 -0
  26. package/dist/StringX.d.ts.map +1 -1
  27. package/dist/StringX.js +68 -0
  28. package/dist/StringX.js.map +1 -1
  29. package/dist/WarnResult.d.ts +115 -70
  30. package/dist/WarnResult.d.ts.map +1 -1
  31. package/dist/WarnResult.js +141 -210
  32. package/dist/WarnResult.js.map +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +1 -0
  36. package/dist/index.js.map +1 -1
  37. package/package.json +2 -2
  38. package/src/ArrayX.ts +4 -86
  39. package/src/InclusiveOr.ts +1255 -0
  40. package/src/NonNullableX.ts +5 -0
  41. package/src/OptionX.ts +8 -2
  42. package/src/PredicateX.ts +41 -0
  43. package/src/RecordX.ts +183 -1
  44. package/src/StringX.ts +113 -0
  45. package/src/WarnResult.ts +297 -227
  46. package/src/index.ts +1 -0
package/src/WarnResult.ts CHANGED
@@ -4,8 +4,9 @@
4
4
  *
5
5
  * @since 0.0.0
6
6
  */
7
- import { Data, Effect, Option, Predicate, Struct, pipe } from "effect";
8
- import { constUndefined, identity } from "effect/Function";
7
+ import { Data, Effect, Option, pipe } from "effect";
8
+ import { dual } from "effect/Function";
9
+ import * as InclusiveOr from "./InclusiveOr.js";
9
10
 
10
11
  /**
11
12
  * A result that may come with a success value and may come with warnings — both
@@ -23,7 +24,7 @@ import { constUndefined, identity } from "effect/Function";
23
24
  * ```ts
24
25
  * import { WarnResult } from "@nunofyobiz/effect-extras"
25
26
  *
26
- * const both: WarnResult.WarnResult<string, number> =
27
+ * const both: WarnResult.WarnResult<number, string> =
27
28
  * WarnResult.SuccessWithWarnings({
28
29
  * warnings: "rounded down",
29
30
  * success: 1
@@ -35,7 +36,7 @@ import { constUndefined, identity } from "effect/Function";
35
36
  * @category models
36
37
  * @since 0.0.0
37
38
  */
38
- export type WarnResult<W, A> = Data.TaggedEnum<{
39
+ export type WarnResult<A, W> = Data.TaggedEnum<{
39
40
  WarningsOnly: {
40
41
  readonly warnings: W;
41
42
  };
@@ -68,7 +69,7 @@ export type WarnResult<W, A> = Data.TaggedEnum<{
68
69
  * @category models
69
70
  * @since 0.0.0
70
71
  */
71
- export type WarningsOnly<W> = WarnResult<W, never> & {
72
+ export type WarningsOnly<W> = WarnResult<never, W> & {
72
73
  _tag: "WarningsOnly";
73
74
  };
74
75
 
@@ -90,7 +91,7 @@ export type WarningsOnly<W> = WarnResult<W, never> & {
90
91
  * @category models
91
92
  * @since 0.0.0
92
93
  */
93
- export type SuccessOnly<A> = WarnResult<never, A> & {
94
+ export type SuccessOnly<A> = WarnResult<A, never> & {
94
95
  _tag: "SuccessOnly";
95
96
  };
96
97
 
@@ -102,7 +103,7 @@ export type SuccessOnly<A> = WarnResult<never, A> & {
102
103
  * ```ts
103
104
  * import { WarnResult } from "@nunofyobiz/effect-extras"
104
105
  *
105
- * const value: WarnResult.SuccessWithWarnings<string, number> =
106
+ * const value: WarnResult.SuccessWithWarnings<number, string> =
106
107
  * WarnResult.SuccessWithWarnings({
107
108
  * warnings: "rounded down",
108
109
  * success: 1
@@ -118,7 +119,7 @@ export type SuccessOnly<A> = WarnResult<never, A> & {
118
119
  * @category models
119
120
  * @since 0.0.0
120
121
  */
121
- export type SuccessWithWarnings<W, A> = WarnResult<W, A> & {
122
+ export type SuccessWithWarnings<A, W> = WarnResult<A, W> & {
122
123
  _tag: "SuccessWithWarnings";
123
124
  };
124
125
 
@@ -130,7 +131,7 @@ export type SuccessWithWarnings<W, A> = WarnResult<W, A> & {
130
131
  * ```ts
131
132
  * import { WarnResult } from "@nunofyobiz/effect-extras"
132
133
  *
133
- * const value: WarnResult.WithWarnings<string, number> = WarnResult.WarningsOnly({
134
+ * const value: WarnResult.WithWarnings<number, string> = WarnResult.WarningsOnly({
134
135
  * warnings: "skipped 2 rows"
135
136
  * })
136
137
  *
@@ -140,7 +141,7 @@ export type SuccessWithWarnings<W, A> = WarnResult<W, A> & {
140
141
  * @category models
141
142
  * @since 0.0.0
142
143
  */
143
- export type WithWarnings<W, A> = WarningsOnly<W> | SuccessWithWarnings<W, A>;
144
+ export type WithWarnings<A, W> = WarningsOnly<W> | SuccessWithWarnings<A, W>;
144
145
 
145
146
  /**
146
147
  * Any `WarnResult` that is guaranteed to carry a `success` value — either
@@ -150,7 +151,7 @@ export type WithWarnings<W, A> = WarningsOnly<W> | SuccessWithWarnings<W, A>;
150
151
  * ```ts
151
152
  * import { WarnResult } from "@nunofyobiz/effect-extras"
152
153
  *
153
- * const value: WarnResult.WithSuccess<string, number> = WarnResult.SuccessOnly({
154
+ * const value: WarnResult.WithSuccess<number, string> = WarnResult.SuccessOnly({
154
155
  * success: 1
155
156
  * })
156
157
  *
@@ -160,7 +161,7 @@ export type WithWarnings<W, A> = WarningsOnly<W> | SuccessWithWarnings<W, A>;
160
161
  * @category models
161
162
  * @since 0.0.0
162
163
  */
163
- export type WithSuccess<W, A> = SuccessOnly<A> | SuccessWithWarnings<W, A>;
164
+ export type WithSuccess<A, W> = SuccessOnly<A> | SuccessWithWarnings<A, W>;
164
165
 
165
166
  interface WarnResultDefinition extends Data.TaggedEnum.WithGenerics<2> {
166
167
  readonly taggedEnum: WarnResult<this["A"], this["B"]>;
@@ -277,6 +278,60 @@ export const is = taggedEnum.$is;
277
278
  */
278
279
  export const match = taggedEnum.$match;
279
280
 
281
+ /**
282
+ * Relabels a `WarnResult` as the equivalent terminology-free `InclusiveOr`,
283
+ * mapping `warnings` to `left` and `success` to `right`. The inverse of
284
+ * `fromInclusiveOr`.
285
+ *
286
+ * `WarnResult` is the use-case-driven (`warnings`/`success`) face of the generic
287
+ * `InclusiveOr` (`left`/`right`); every derived `WarnResult` operation is just this
288
+ * relabeling around the corresponding `InclusiveOr` operation, so all the logic
289
+ * lives in `InclusiveOr` and is never duplicated here.
290
+ */
291
+ const toInclusiveOr = <A, W>(
292
+ warnResult: WarnResult<A, W>,
293
+ ): InclusiveOr.InclusiveOr<W, A> =>
294
+ match(warnResult, {
295
+ WarningsOnly: ({ warnings }) => InclusiveOr.LeftOnly({ left: warnings }),
296
+ SuccessOnly: ({ success }) => InclusiveOr.RightOnly({ right: success }),
297
+ SuccessWithWarnings: ({ warnings, success }) =>
298
+ InclusiveOr.LeftAndRight({ left: warnings, right: success }),
299
+ });
300
+
301
+ /**
302
+ * Relabels an `InclusiveOr` as the equivalent `WarnResult`, mapping `left` to
303
+ * `warnings` and `right` to `success`. The inverse of `toInclusiveOr`.
304
+ *
305
+ * Overloaded so the precise member subtype survives the round trip — e.g. an
306
+ * `InclusiveOr.LeftAndRight` becomes a `SuccessWithWarnings` rather than a widened
307
+ * `WarnResult` — which keeps the delegating helpers' return types as exact as the
308
+ * hand-written ones were.
309
+ */
310
+ function fromInclusiveOr<W>(io: InclusiveOr.LeftOnly<W>): WarningsOnly<W>;
311
+ function fromInclusiveOr<A>(io: InclusiveOr.RightOnly<A>): SuccessOnly<A>;
312
+ function fromInclusiveOr<A, W>(
313
+ io: InclusiveOr.LeftAndRight<W, A>,
314
+ ): SuccessWithWarnings<A, W>;
315
+ function fromInclusiveOr<A, W>(
316
+ io: InclusiveOr.WithLeft<W, A>,
317
+ ): WithWarnings<A, W>;
318
+ function fromInclusiveOr<A, W>(
319
+ io: InclusiveOr.WithRight<W, A>,
320
+ ): WithSuccess<A, W>;
321
+ function fromInclusiveOr<A, W>(
322
+ io: InclusiveOr.InclusiveOr<W, A>,
323
+ ): WarnResult<A, W>;
324
+ function fromInclusiveOr<A, W>(
325
+ io: InclusiveOr.InclusiveOr<W, A>,
326
+ ): WarnResult<A, W> {
327
+ return InclusiveOr.match(io, {
328
+ LeftOnly: ({ left }) => WarningsOnly({ warnings: left }),
329
+ RightOnly: ({ right }) => SuccessOnly({ success: right }),
330
+ LeftAndRight: ({ left, right }) =>
331
+ SuccessWithWarnings({ warnings: left, success: right }),
332
+ });
333
+ }
334
+
280
335
  /**
281
336
  * Builds a `WarnResult` known to carry `warnings`, choosing `SuccessWithWarnings`
282
337
  * when a `success` value is present and `WarningsOnly` otherwise.
@@ -284,7 +339,7 @@ export const match = taggedEnum.$match;
284
339
  * Use it when the `warnings` are mandatory and the `success` value is an optional
285
340
  * companion: pass an absent (`null`/`undefined`) `success` to get a
286
341
  * `WarningsOnly`, or a present one to get a `SuccessWithWarnings`. The return type
287
- * `WithWarnings<W, A>` reflects that `warnings` are always present.
342
+ * `WithWarnings<A, W>` reflects that `warnings` are always present.
288
343
  *
289
344
  * @example
290
345
  * ```ts
@@ -304,16 +359,14 @@ export const match = taggedEnum.$match;
304
359
  * @category constructors
305
360
  * @since 0.0.0
306
361
  */
307
- export const WithWarnings = <W, A>({
362
+ export const WithWarnings = <A, W>({
308
363
  warnings,
309
364
  success,
310
365
  }: {
311
366
  warnings: W;
312
367
  success?: A | undefined;
313
- }): WithWarnings<W, A> =>
314
- Predicate.isNotNullish(success)
315
- ? SuccessWithWarnings({ warnings, success })
316
- : WarningsOnly({ warnings });
368
+ }): WithWarnings<A, W> =>
369
+ fromInclusiveOr(InclusiveOr.WithLeft({ left: warnings, right: success }));
317
370
 
318
371
  /**
319
372
  * Builds a `WarnResult` known to carry a `success` value, choosing
@@ -322,7 +375,7 @@ export const WithWarnings = <W, A>({
322
375
  * The mirror of `WithWarnings`: the `success` value is mandatory and the
323
376
  * `warnings` are an optional companion. Pass absent (`null`/`undefined`)
324
377
  * `warnings` to get a `SuccessOnly`, or present ones to get a
325
- * `SuccessWithWarnings`. The return type `WithSuccess<W, A>` reflects that a
378
+ * `SuccessWithWarnings`. The return type `WithSuccess<A, W>` reflects that a
326
379
  * `success` value is always present.
327
380
  *
328
381
  * @example
@@ -343,16 +396,14 @@ export const WithWarnings = <W, A>({
343
396
  * @category constructors
344
397
  * @since 0.0.0
345
398
  */
346
- export const WithSuccess = <W, A>({
399
+ export const WithSuccess = <A, W>({
347
400
  warnings,
348
401
  success,
349
402
  }: {
350
403
  warnings?: W | undefined;
351
404
  success: A;
352
- }): WithSuccess<W, A> =>
353
- Predicate.isNotNullish(warnings)
354
- ? SuccessWithWarnings({ warnings, success })
355
- : SuccessOnly({ success });
405
+ }): WithSuccess<A, W> =>
406
+ fromInclusiveOr(InclusiveOr.WithRight({ left: warnings, right: success }));
356
407
 
357
408
  /**
358
409
  * Builds a `WarnResult` from a pair of possibly-nullish inputs, wrapping the
@@ -388,24 +439,17 @@ export const WithSuccess = <W, A>({
388
439
  * @category constructors
389
440
  * @since 0.0.0
390
441
  */
391
- export const optionFromNullables = <W, A>({
442
+ export const optionFromNullables = <A, W>({
392
443
  warnings,
393
444
  success,
394
445
  }: {
395
446
  warnings?: W | null | undefined;
396
447
  success?: A | null | undefined;
397
- }): Option.Option<WarnResult<W, A>> => {
398
- if (Predicate.isNotNullish(warnings) && Predicate.isNotNullish(success)) {
399
- return Option.some(SuccessWithWarnings({ warnings, success }));
400
- }
401
- if (Predicate.isNotNullish(warnings)) {
402
- return Option.some(WarningsOnly({ warnings }));
403
- }
404
- if (Predicate.isNotNullish(success)) {
405
- return Option.some(SuccessOnly({ success }));
406
- }
407
- return Option.none();
408
- };
448
+ }): Option.Option<WarnResult<A, W>> =>
449
+ Option.map(
450
+ InclusiveOr.optionFromNullables({ left: warnings, right: success }),
451
+ (io) => fromInclusiveOr(io),
452
+ );
409
453
 
410
454
  /**
411
455
  * Builds a `WarnResult` from a pair of possibly-nullish inputs, falling back to
@@ -439,7 +483,7 @@ export const optionFromNullables = <W, A>({
439
483
  * @category constructors
440
484
  * @since 0.0.0
441
485
  */
442
- export const fromNullables = <W, A>({
486
+ export const fromNullables = <A, W>({
443
487
  warnings,
444
488
  success,
445
489
  orElse = () => {
@@ -448,8 +492,8 @@ export const fromNullables = <W, A>({
448
492
  }: {
449
493
  warnings?: W | null | undefined;
450
494
  success?: A | null | undefined;
451
- orElse?: () => WarnResult<W, A>;
452
- }): WarnResult<W, A> =>
495
+ orElse?: () => WarnResult<A, W>;
496
+ }): WarnResult<A, W> =>
453
497
  pipe(optionFromNullables({ warnings, success }), Option.getOrElse(orElse));
454
498
 
455
499
  /**
@@ -488,25 +532,16 @@ export const fromNullables = <W, A>({
488
532
  * @since 0.0.0
489
533
  */
490
534
  export const matchWarnings =
491
- <W, A, Z>({
535
+ <A, W, Z>({
492
536
  Warnings,
493
537
  SuccessOnly,
494
538
  }: {
495
539
  Warnings: (warnings: W) => Z;
496
540
  SuccessOnly: (success: A) => Z;
497
541
  }) =>
498
- (warnResult: WarnResult<W, A>): Z =>
499
- pipe(
500
- warnResult,
501
-
502
- match({
503
- WarningsOnly: ({ warnings }) => Warnings(warnings),
504
- SuccessOnly: ({ success }) => SuccessOnly(success),
505
- SuccessWithWarnings: ({ warnings }) => Warnings(warnings),
506
- }),
507
-
508
- // Make Typescript happy
509
- (a) => a as Z,
542
+ (warnResult: WarnResult<A, W>): Z =>
543
+ InclusiveOr.matchLeft({ Left: Warnings, RightOnly: SuccessOnly })(
544
+ toInclusiveOr(warnResult),
510
545
  );
511
546
 
512
547
  /**
@@ -546,25 +581,16 @@ export const matchWarnings =
546
581
  * @since 0.0.0
547
582
  */
548
583
  export const matchSuccess =
549
- <W, A, Z>({
584
+ <A, W, Z>({
550
585
  WarningsOnly,
551
586
  Success,
552
587
  }: {
553
588
  WarningsOnly: (warnings: W) => Z;
554
589
  Success: (success: A) => Z;
555
590
  }) =>
556
- (warnResult: WarnResult<W, A>): Z =>
557
- pipe(
558
- warnResult,
559
-
560
- match({
561
- WarningsOnly: ({ warnings }) => WarningsOnly(warnings),
562
- SuccessOnly: ({ success }) => Success(success),
563
- SuccessWithWarnings: ({ success }) => Success(success),
564
- }),
565
-
566
- // Make Typescript happy
567
- (a) => a as Z,
591
+ (warnResult: WarnResult<A, W>): Z =>
592
+ InclusiveOr.matchRight({ LeftOnly: WarningsOnly, Right: Success })(
593
+ toInclusiveOr(warnResult),
568
594
  );
569
595
 
570
596
  /**
@@ -598,23 +624,23 @@ export const matchSuccess =
598
624
  * @category getters
599
625
  * @since 0.0.0
600
626
  */
601
- export const orElse = <W2, A2>({
602
- orElseWarnings,
603
- orElseSuccess,
604
- }: {
605
- orElseWarnings: () => W2;
606
- orElseSuccess: () => A2;
607
- }): (<W, A>(
608
- warnResult: WarnResult<W, A>,
609
- ) => SuccessWithWarnings<W | W2, A | A2>) =>
610
- match({
611
- WarningsOnly: ({ warnings }) =>
612
- SuccessWithWarnings({ warnings, success: orElseSuccess() }),
613
- SuccessOnly: ({ success }) =>
614
- SuccessWithWarnings({ warnings: orElseWarnings(), success }),
615
- SuccessWithWarnings: ({ warnings, success }) =>
616
- SuccessWithWarnings({ warnings, success }),
617
- });
627
+ export const orElse =
628
+ <A2, W2>({
629
+ orElseWarnings,
630
+ orElseSuccess,
631
+ }: {
632
+ orElseWarnings: () => W2;
633
+ orElseSuccess: () => A2;
634
+ }): (<A, W>(
635
+ warnResult: WarnResult<A, W>,
636
+ ) => SuccessWithWarnings<A | A2, W | W2>) =>
637
+ <A, W>(warnResult: WarnResult<A, W>): SuccessWithWarnings<A | A2, W | W2> =>
638
+ fromInclusiveOr(
639
+ InclusiveOr.orElse({
640
+ orElseLeft: orElseWarnings,
641
+ orElseRight: orElseSuccess,
642
+ })(toInclusiveOr(warnResult)),
643
+ );
618
644
 
619
645
  /**
620
646
  * Completes a `WarnResult` into a `SuccessWithWarnings` whose missing side is
@@ -680,15 +706,8 @@ export const orUndefined = orElse({
680
706
  */
681
707
  export const warningsOrElse =
682
708
  <Z>(orElseReturn: () => Z) =>
683
- <W, A>(warnResult: WarnResult<W, A>): W | Z =>
684
- pipe(
685
- warnResult,
686
- orElse({
687
- orElseWarnings: orElseReturn,
688
- orElseSuccess: constUndefined,
689
- }),
690
- Struct.get("warnings"),
691
- );
709
+ <A, W>(warnResult: WarnResult<A, W>): W | Z =>
710
+ InclusiveOr.leftOrElse(orElseReturn)(toInclusiveOr(warnResult));
692
711
 
693
712
  /**
694
713
  * Extracts the `warnings` of a `WarnResult`, returning `undefined` when no
@@ -745,15 +764,8 @@ export const warningsOrUndefined = warningsOrElse(() => undefined);
745
764
  */
746
765
  export const successOrElse =
747
766
  <Z>(orElseReturn: () => Z) =>
748
- <W, A>(warnResult: WarnResult<W, A>): A | Z =>
749
- pipe(
750
- warnResult,
751
- orElse({
752
- orElseWarnings: constUndefined,
753
- orElseSuccess: orElseReturn,
754
- }),
755
- Struct.get("success"),
756
- );
767
+ <A, W>(warnResult: WarnResult<A, W>): A | Z =>
768
+ InclusiveOr.rightOrElse(orElseReturn)(toInclusiveOr(warnResult));
757
769
 
758
770
  /**
759
771
  * Extracts the `success` value of a `WarnResult`, returning `undefined` when no
@@ -807,16 +819,9 @@ export const successOrUndefined = successOrElse(() => undefined);
807
819
  * @category getters
808
820
  * @since 0.0.0
809
821
  */
810
- export const successOption = <W, A>(
811
- warnResult: WarnResult<W, A>,
812
- ): Option.Option<A> =>
813
- pipe(
814
- warnResult,
815
- matchSuccess({
816
- WarningsOnly: () => Option.none(),
817
- Success: Option.some,
818
- }),
819
- );
822
+ export const successOption = <A, W>(
823
+ warnResult: WarnResult<A, W>,
824
+ ): Option.Option<A> => InclusiveOr.rightOption(toInclusiveOr(warnResult));
820
825
 
821
826
  /**
822
827
  * Extracts the `warnings` of a `WarnResult` as an `Option`.
@@ -842,16 +847,9 @@ export const successOption = <W, A>(
842
847
  * @category getters
843
848
  * @since 0.0.0
844
849
  */
845
- export const warningsOption = <W, A>(
846
- warnResult: WarnResult<W, A>,
847
- ): Option.Option<W> =>
848
- pipe(
849
- warnResult,
850
- matchWarnings({
851
- Warnings: Option.some,
852
- SuccessOnly: () => Option.none(),
853
- }),
854
- );
850
+ export const warningsOption = <A, W>(
851
+ warnResult: WarnResult<A, W>,
852
+ ): Option.Option<W> => InclusiveOr.leftOption(toInclusiveOr(warnResult));
855
853
 
856
854
  /**
857
855
  * Transforms both sides of a `WarnResult`, applying `mapWarnings` to any
@@ -884,23 +882,20 @@ export const warningsOption = <W, A>(
884
882
  * @category mapping
885
883
  * @since 0.0.0
886
884
  */
887
- export const mapBoth = <W1, A1, W2, A2>({
888
- mapWarnings,
889
- mapSuccess,
890
- }: {
891
- mapWarnings: (warnings: W1) => W2;
892
- mapSuccess: (success: A1) => A2;
893
- }): ((warnResult: WarnResult<W1, A1>) => WarnResult<W2, A2>) =>
894
- match({
895
- WarningsOnly: ({ warnings }) =>
896
- WarningsOnly({ warnings: mapWarnings(warnings) }),
897
- SuccessOnly: ({ success }) => SuccessOnly({ success: mapSuccess(success) }),
898
- SuccessWithWarnings: ({ warnings, success }) =>
899
- SuccessWithWarnings({
900
- warnings: mapWarnings(warnings),
901
- success: mapSuccess(success),
902
- }),
903
- });
885
+ export const mapBoth =
886
+ <A1, W1, A2, W2>({
887
+ mapWarnings,
888
+ mapSuccess,
889
+ }: {
890
+ mapWarnings: (warnings: W1) => W2;
891
+ mapSuccess: (success: A1) => A2;
892
+ }): ((warnResult: WarnResult<A1, W1>) => WarnResult<A2, W2>) =>
893
+ (warnResult) =>
894
+ fromInclusiveOr(
895
+ InclusiveOr.mapBoth({ mapLeft: mapWarnings, mapRight: mapSuccess })(
896
+ toInclusiveOr(warnResult),
897
+ ),
898
+ );
904
899
 
905
900
  /**
906
901
  * Effectful `mapBoth`: transforms each present side through an `Effect`,
@@ -932,39 +927,22 @@ export const mapBoth = <W1, A1, W2, A2>({
932
927
  * @category sequencing
933
928
  * @since 0.0.0
934
929
  */
935
- export const mapBothEffect = <W1, A1, W2, A2, EW, EA, RW, RA>({
936
- mapWarnings,
937
- mapSuccess,
938
- }: {
939
- mapWarnings: (warnings: W1) => Effect.Effect<W2, EW, RW>;
940
- mapSuccess: (success: A1) => Effect.Effect<A2, EA, RA>;
941
- }): ((
942
- warnResult: WarnResult<W1, A1>,
943
- ) => Effect.Effect<WarnResult<W2, A2>, EW | EA, RW | RA>) =>
944
- match({
945
- WarningsOnly: ({ warnings }) =>
946
- pipe(
947
- mapWarnings(warnings),
948
- Effect.map((warnings2) => WarningsOnly({ warnings: warnings2 })),
949
- ),
950
-
951
- SuccessOnly: ({ success }) =>
952
- pipe(
953
- mapSuccess(success),
954
- Effect.map((success2) => SuccessOnly({ success: success2 })),
955
- ),
956
-
957
- SuccessWithWarnings: ({ warnings, success }) =>
958
- pipe(
959
- Effect.all({
960
- warnings: mapWarnings(warnings),
961
- success: mapSuccess(success),
962
- }),
963
- Effect.map(({ warnings: warnings2, success: success2 }) =>
964
- SuccessWithWarnings({ warnings: warnings2, success: success2 }),
965
- ),
966
- ),
967
- });
930
+ export const mapBothEffect =
931
+ <A1, W1, A2, W2, EA, EW, RA, RW>({
932
+ mapWarnings,
933
+ mapSuccess,
934
+ }: {
935
+ mapWarnings: (warnings: W1) => Effect.Effect<W2, EW, RW>;
936
+ mapSuccess: (success: A1) => Effect.Effect<A2, EA, RA>;
937
+ }): ((
938
+ warnResult: WarnResult<A1, W1>,
939
+ ) => Effect.Effect<WarnResult<A2, W2>, EW | EA, RW | RA>) =>
940
+ (warnResult) =>
941
+ pipe(
942
+ toInclusiveOr(warnResult),
943
+ InclusiveOr.mapBothEffect({ mapLeft: mapWarnings, mapRight: mapSuccess }),
944
+ Effect.map((io) => fromInclusiveOr(io)),
945
+ );
968
946
 
969
947
  /**
970
948
  * Transforms the `warnings` of a `WarnResult`, leaving any `success` value
@@ -995,10 +973,14 @@ export const mapBothEffect = <W1, A1, W2, A2, EW, EA, RW, RA>({
995
973
  * @category mapping
996
974
  * @since 0.0.0
997
975
  */
998
- export const mapWarnings = <W1, W2>(
999
- mapWarnings: (warnings: W1) => W2,
1000
- ): (<A>(warnResult: WarnResult<W1, A>) => WarnResult<W2, A>) =>
1001
- mapBoth({ mapWarnings, mapSuccess: identity });
976
+ export const mapWarnings =
977
+ <W1, W2>(
978
+ mapWarnings: (warnings: W1) => W2,
979
+ ): (<A>(warnResult: WarnResult<A, W1>) => WarnResult<A, W2>) =>
980
+ (warnResult) =>
981
+ fromInclusiveOr(
982
+ InclusiveOr.mapLeft(mapWarnings)(toInclusiveOr(warnResult)),
983
+ );
1002
984
 
1003
985
  /**
1004
986
  * Chains the `warnings` of a `WarnResult` into a new `WarnResult`, flattening the
@@ -1032,14 +1014,16 @@ export const mapWarnings = <W1, W2>(
1032
1014
  * @category sequencing
1033
1015
  * @since 0.0.0
1034
1016
  */
1035
- export const flatMapWarnings = <W1, W2, A2>(
1036
- mapWarnings: (warnings: W1) => WarnResult<W2, A2>,
1037
- ): (<A1>(warnResult: WarnResult<W1, A1>) => WarnResult<W2, A1 | A2>) =>
1038
- match({
1039
- WarningsOnly: ({ warnings }) => mapWarnings(warnings),
1040
- SuccessOnly: ({ success }) => SuccessOnly({ success }),
1041
- SuccessWithWarnings: ({ warnings }) => mapWarnings(warnings),
1042
- });
1017
+ export const flatMapWarnings =
1018
+ <A2, W1, W2>(
1019
+ mapWarnings: (warnings: W1) => WarnResult<A2, W2>,
1020
+ ): (<A1>(warnResult: WarnResult<A1, W1>) => WarnResult<A1 | A2, W2>) =>
1021
+ (warnResult) =>
1022
+ fromInclusiveOr(
1023
+ InclusiveOr.flatMapLeft((warnings: W1) =>
1024
+ toInclusiveOr(mapWarnings(warnings)),
1025
+ )(toInclusiveOr(warnResult)),
1026
+ );
1043
1027
 
1044
1028
  /**
1045
1029
  * Effectful `mapWarnings`: transforms the `warnings` of a `WarnResult` through an
@@ -1069,12 +1053,18 @@ export const flatMapWarnings = <W1, W2, A2>(
1069
1053
  * @category sequencing
1070
1054
  * @since 0.0.0
1071
1055
  */
1072
- export const mapWarningsEffect = <W1, W2, EW, RW>(
1073
- mapWarnings: (warnings: W1) => Effect.Effect<W2, EW, RW>,
1074
- ): (<A>(
1075
- warnResult: WarnResult<W1, A>,
1076
- ) => Effect.Effect<WarnResult<W2, A>, EW, RW>) =>
1077
- mapBothEffect({ mapWarnings, mapSuccess: Effect.succeed });
1056
+ export const mapWarningsEffect =
1057
+ <W1, W2, EW, RW>(
1058
+ mapWarnings: (warnings: W1) => Effect.Effect<W2, EW, RW>,
1059
+ ): (<A>(
1060
+ warnResult: WarnResult<A, W1>,
1061
+ ) => Effect.Effect<WarnResult<A, W2>, EW, RW>) =>
1062
+ (warnResult) =>
1063
+ pipe(
1064
+ toInclusiveOr(warnResult),
1065
+ InclusiveOr.mapLeftEffect(mapWarnings),
1066
+ Effect.map((io) => fromInclusiveOr(io)),
1067
+ );
1078
1068
 
1079
1069
  /**
1080
1070
  * Effectful `flatMapWarnings`: chains the `warnings` of a `WarnResult` into an
@@ -1107,16 +1097,20 @@ export const mapWarningsEffect = <W1, W2, EW, RW>(
1107
1097
  * @category sequencing
1108
1098
  * @since 0.0.0
1109
1099
  */
1110
- export const flatMapWarningsEffect = <W1, W2, A2, EW, RW>(
1111
- mapWarnings: (warnings: W1) => Effect.Effect<WarnResult<W2, A2>, EW, RW>,
1112
- ): (<A1>(
1113
- warnResult: WarnResult<W1, A1>,
1114
- ) => Effect.Effect<WarnResult<W2, A1 | A2>, EW, RW>) =>
1115
- match({
1116
- WarningsOnly: ({ warnings }) => mapWarnings(warnings),
1117
- SuccessOnly: ({ success }) => Effect.succeed(SuccessOnly({ success })),
1118
- SuccessWithWarnings: ({ warnings }) => mapWarnings(warnings),
1119
- });
1100
+ export const flatMapWarningsEffect =
1101
+ <A2, W1, W2, EW, RW>(
1102
+ mapWarnings: (warnings: W1) => Effect.Effect<WarnResult<A2, W2>, EW, RW>,
1103
+ ): (<A1>(
1104
+ warnResult: WarnResult<A1, W1>,
1105
+ ) => Effect.Effect<WarnResult<A1 | A2, W2>, EW, RW>) =>
1106
+ (warnResult) =>
1107
+ pipe(
1108
+ toInclusiveOr(warnResult),
1109
+ InclusiveOr.flatMapLeftEffect((warnings: W1) =>
1110
+ Effect.map(mapWarnings(warnings), toInclusiveOr),
1111
+ ),
1112
+ Effect.map((io) => fromInclusiveOr(io)),
1113
+ );
1120
1114
 
1121
1115
  /**
1122
1116
  * Transforms the `success` value of a `WarnResult`, leaving any `warnings`
@@ -1144,10 +1138,14 @@ export const flatMapWarningsEffect = <W1, W2, A2, EW, RW>(
1144
1138
  * @category mapping
1145
1139
  * @since 0.0.0
1146
1140
  */
1147
- export const mapSuccess = <A1, A2>(
1148
- mapSuccess: (success: A1) => A2,
1149
- ): (<W>(warnResult: WarnResult<W, A1>) => WarnResult<W, A2>) =>
1150
- mapBoth({ mapWarnings: identity, mapSuccess });
1141
+ export const mapSuccess =
1142
+ <A1, A2>(
1143
+ mapSuccess: (success: A1) => A2,
1144
+ ): (<W>(warnResult: WarnResult<A1, W>) => WarnResult<A2, W>) =>
1145
+ (warnResult) =>
1146
+ fromInclusiveOr(
1147
+ InclusiveOr.mapRight(mapSuccess)(toInclusiveOr(warnResult)),
1148
+ );
1151
1149
 
1152
1150
  /**
1153
1151
  * Chains the `success` value of a `WarnResult` into a new `WarnResult`, flattening
@@ -1179,14 +1177,16 @@ export const mapSuccess = <A1, A2>(
1179
1177
  * @category sequencing
1180
1178
  * @since 0.0.0
1181
1179
  */
1182
- export const flatMapSuccess = <W2, A1, A2>(
1183
- mapSuccess: (success: A1) => WarnResult<W2, A2>,
1184
- ): (<W1>(warnResult: WarnResult<W1, A1>) => WarnResult<W1 | W2, A2>) =>
1185
- match({
1186
- WarningsOnly: ({ warnings }) => WarningsOnly({ warnings }),
1187
- SuccessOnly: ({ success }) => mapSuccess(success),
1188
- SuccessWithWarnings: ({ success }) => mapSuccess(success),
1189
- });
1180
+ export const flatMapSuccess =
1181
+ <A1, A2, W2>(
1182
+ mapSuccess: (success: A1) => WarnResult<A2, W2>,
1183
+ ): (<W1>(warnResult: WarnResult<A1, W1>) => WarnResult<A2, W1 | W2>) =>
1184
+ (warnResult) =>
1185
+ fromInclusiveOr(
1186
+ InclusiveOr.flatMapRight((success: A1) =>
1187
+ toInclusiveOr(mapSuccess(success)),
1188
+ )(toInclusiveOr(warnResult)),
1189
+ );
1190
1190
 
1191
1191
  /**
1192
1192
  * Effectful `mapSuccess`: transforms the `success` value of a `WarnResult` through
@@ -1216,12 +1216,18 @@ export const flatMapSuccess = <W2, A1, A2>(
1216
1216
  * @category sequencing
1217
1217
  * @since 0.0.0
1218
1218
  */
1219
- export const mapSuccessEffect = <A1, A2, EA, RA>(
1220
- mapSuccess: (success: A1) => Effect.Effect<A2, EA, RA>,
1221
- ): (<W>(
1222
- warnResult: WarnResult<W, A1>,
1223
- ) => Effect.Effect<WarnResult<W, A2>, EA, RA>) =>
1224
- mapBothEffect({ mapWarnings: Effect.succeed, mapSuccess });
1219
+ export const mapSuccessEffect =
1220
+ <A1, A2, EA, RA>(
1221
+ mapSuccess: (success: A1) => Effect.Effect<A2, EA, RA>,
1222
+ ): (<W>(
1223
+ warnResult: WarnResult<A1, W>,
1224
+ ) => Effect.Effect<WarnResult<A2, W>, EA, RA>) =>
1225
+ (warnResult) =>
1226
+ pipe(
1227
+ toInclusiveOr(warnResult),
1228
+ InclusiveOr.mapRightEffect(mapSuccess),
1229
+ Effect.map((io) => fromInclusiveOr(io)),
1230
+ );
1225
1231
 
1226
1232
  /**
1227
1233
  * Effectful `flatMapSuccess`: chains the `success` value of a `WarnResult` into an
@@ -1253,13 +1259,77 @@ export const mapSuccessEffect = <A1, A2, EA, RA>(
1253
1259
  * @category sequencing
1254
1260
  * @since 0.0.0
1255
1261
  */
1256
- export const flatMapSuccessEffect = <W2, A1, A2, EA, RA>(
1257
- mapSuccess: (success: A1) => Effect.Effect<WarnResult<W2, A2>, EA, RA>,
1258
- ): (<W1>(
1259
- warnResult: WarnResult<W1, A1>,
1260
- ) => Effect.Effect<WarnResult<W1 | W2, A2>, EA, RA>) =>
1261
- match({
1262
- WarningsOnly: ({ warnings }) => Effect.succeed(WarningsOnly({ warnings })),
1263
- SuccessOnly: ({ success }) => mapSuccess(success),
1264
- SuccessWithWarnings: ({ success }) => mapSuccess(success),
1265
- });
1262
+ export const flatMapSuccessEffect =
1263
+ <A1, A2, W2, EA, RA>(
1264
+ mapSuccess: (success: A1) => Effect.Effect<WarnResult<A2, W2>, EA, RA>,
1265
+ ): (<W1>(
1266
+ warnResult: WarnResult<A1, W1>,
1267
+ ) => Effect.Effect<WarnResult<A2, W1 | W2>, EA, RA>) =>
1268
+ (warnResult) =>
1269
+ pipe(
1270
+ toInclusiveOr(warnResult),
1271
+ InclusiveOr.flatMapRightEffect((success: A1) =>
1272
+ Effect.map(mapSuccess(success), toInclusiveOr),
1273
+ ),
1274
+ Effect.map((io) => fromInclusiveOr(io)),
1275
+ );
1276
+
1277
+ /**
1278
+ * Zips two arrays into one, calling `f` with a `WarnResult` for each index so
1279
+ * that length mismatches are handled explicitly rather than truncated.
1280
+ *
1281
+ * Unlike `Array.zipWith` (which stops at the shorter array), this walks to the
1282
+ * length of the *longer* array. The first array's element fills the `warnings`
1283
+ * side and the second array's element fills the `success` side, so at each index
1284
+ * `f` receives a `WarnResult.WarnResult<B, A>`: `SuccessWithWarnings` when both
1285
+ * arrays have an element, `WarningsOnly` when only the first does, and
1286
+ * `SuccessOnly` when only the second does. Use it when the "extra" tail of either
1287
+ * array still carries meaning.
1288
+ *
1289
+ * @example
1290
+ * ```ts
1291
+ * import { WarnResult } from "@nunofyobiz/effect-extras"
1292
+ * import { pipe } from "effect"
1293
+ *
1294
+ * const describe = WarnResult.match({
1295
+ * WarningsOnly: ({ warnings }) => `warnings ${warnings}`,
1296
+ * SuccessOnly: ({ success }) => `success ${success}`,
1297
+ * SuccessWithWarnings: ({ warnings, success }) => `both ${warnings}/${success}`
1298
+ * })
1299
+ *
1300
+ * // data-first
1301
+ * assert.deepStrictEqual(WarnResult.zip([1, 2, 3], [10, 20], describe), [
1302
+ * "both 1/10",
1303
+ * "both 2/20",
1304
+ * "warnings 3"
1305
+ * ])
1306
+ *
1307
+ * // data-last (pipeable)
1308
+ * assert.deepStrictEqual(pipe([1, 2, 3], WarnResult.zip([10, 20], describe)), [
1309
+ * "both 1/10",
1310
+ * "both 2/20",
1311
+ * "warnings 3"
1312
+ * ])
1313
+ * ```
1314
+ *
1315
+ * @category combinators
1316
+ * @since 0.0.0
1317
+ */
1318
+ export const zip: {
1319
+ <A, B, C>(
1320
+ array2: readonly B[],
1321
+ f: (warnResult: WarnResult<B, A>) => C,
1322
+ ): (array1: readonly A[]) => C[];
1323
+ <A, B, C>(
1324
+ array1: readonly A[],
1325
+ array2: readonly B[],
1326
+ f: (warnResult: WarnResult<B, A>) => C,
1327
+ ): C[];
1328
+ } = dual(
1329
+ 3,
1330
+ <A, B, C>(
1331
+ array1: readonly A[],
1332
+ array2: readonly B[],
1333
+ f: (warnResult: WarnResult<B, A>) => C,
1334
+ ): C[] => InclusiveOr.zip(array1, array2, (io) => f(fromInclusiveOr(io))),
1335
+ );