@nunofyobiz/effect-extras 2.1.0 → 3.0.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 +1 -1
  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
@@ -0,0 +1,1074 @@
1
+ /**
2
+ * The `InclusiveOr` data type — an inclusive-or carrying a `left`, a `right`, or both at once.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Array, Data, Effect, Option, Predicate, Struct, pipe } from "effect";
7
+ import { constUndefined, dual, identity } from "effect/Function";
8
+ const taggedEnum = /*#__PURE__*/Data.taggedEnum();
9
+ /**
10
+ * Constructs a `LeftOnly` — an `InclusiveOr` that carries only a `left`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
15
+ *
16
+ * const value = InclusiveOr.LeftOnly({ left: 1 })
17
+ *
18
+ * assert.deepStrictEqual(value._tag, "LeftOnly")
19
+ * assert.deepStrictEqual(value.left, 1)
20
+ * ```
21
+ *
22
+ * @category constructors
23
+ * @since 0.0.0
24
+ */
25
+ export const LeftOnly = taggedEnum.LeftOnly;
26
+ /**
27
+ * Constructs a `RightOnly` — an `InclusiveOr` that carries only a `right`.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
32
+ *
33
+ * const value = InclusiveOr.RightOnly({ right: "a" })
34
+ *
35
+ * assert.deepStrictEqual(value._tag, "RightOnly")
36
+ * assert.deepStrictEqual(value.right, "a")
37
+ * ```
38
+ *
39
+ * @category constructors
40
+ * @since 0.0.0
41
+ */
42
+ export const RightOnly = taggedEnum.RightOnly;
43
+ /**
44
+ * Constructs a `LeftAndRight` — an `InclusiveOr` that carries both a `left` and a
45
+ * `right`.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
50
+ *
51
+ * const value = InclusiveOr.LeftAndRight({ left: 1, right: "a" })
52
+ *
53
+ * assert.deepStrictEqual(value._tag, "LeftAndRight")
54
+ * assert.deepStrictEqual(value.left, 1)
55
+ * assert.deepStrictEqual(value.right, "a")
56
+ * ```
57
+ *
58
+ * @category constructors
59
+ * @since 0.0.0
60
+ */
61
+ export const LeftAndRight = taggedEnum.LeftAndRight;
62
+ /**
63
+ * Builds per-tag refinements for `InclusiveOr`. `is("LeftOnly")` is a type guard that
64
+ * narrows an `InclusiveOr` to its `LeftOnly` member, and likewise for `"RightOnly"` and
65
+ * `"LeftAndRight"`.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
70
+ *
71
+ * assert.deepStrictEqual(InclusiveOr.is("LeftOnly")(InclusiveOr.LeftOnly({ left: 1 })), true)
72
+ * assert.deepStrictEqual(
73
+ * InclusiveOr.is("LeftOnly")(InclusiveOr.RightOnly({ right: "a" })),
74
+ * false
75
+ * )
76
+ * ```
77
+ *
78
+ * @category guards
79
+ * @since 0.0.0
80
+ */
81
+ export const is = taggedEnum.$is;
82
+ /**
83
+ * Folds an `InclusiveOr` over its three tags. Provide a handler for `LeftOnly`,
84
+ * `RightOnly`, and `LeftAndRight` and `match` returns a function from an `InclusiveOr`
85
+ * to the handlers' common result type.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
90
+ *
91
+ * const describe = InclusiveOr.match({
92
+ * LeftOnly: ({ left }) => `left ${left}`,
93
+ * RightOnly: ({ right }) => `right ${right}`,
94
+ * LeftAndRight: ({ left, right }) => `both ${left}/${right}`
95
+ * })
96
+ *
97
+ * assert.deepStrictEqual(
98
+ * describe(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
99
+ * "both 1/a"
100
+ * )
101
+ * ```
102
+ *
103
+ * @category pattern matching
104
+ * @since 0.0.0
105
+ */
106
+ export const match = taggedEnum.$match;
107
+ /**
108
+ * Builds an `InclusiveOr` known to carry a `left`, choosing `LeftAndRight` when `right`
109
+ * is present and `LeftOnly` otherwise.
110
+ *
111
+ * Use it when the `left` is mandatory and the `right` is an optional companion:
112
+ * pass an absent (`null`/`undefined`) `right` to get a `LeftOnly`, or a present
113
+ * one to get a `LeftAndRight`. The return type `WithLeft<L, R>` reflects that a
114
+ * `left` is always present.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
119
+ *
120
+ * assert.deepStrictEqual(
121
+ * InclusiveOr.WithLeft({ left: 1, right: "a" }),
122
+ * InclusiveOr.LeftAndRight({ left: 1, right: "a" })
123
+ * )
124
+ *
125
+ * assert.deepStrictEqual(
126
+ * InclusiveOr.WithLeft({ left: 1 }),
127
+ * InclusiveOr.LeftOnly({ left: 1 })
128
+ * )
129
+ * ```
130
+ *
131
+ * @category constructors
132
+ * @since 0.0.0
133
+ */
134
+ export const WithLeft = ({
135
+ left,
136
+ right
137
+ }) => Predicate.isNotNullish(right) ? LeftAndRight({
138
+ left,
139
+ right
140
+ }) : LeftOnly({
141
+ left
142
+ });
143
+ /**
144
+ * Builds an `InclusiveOr` known to carry a `right`, choosing `LeftAndRight` when `left`
145
+ * is present and `RightOnly` otherwise.
146
+ *
147
+ * The mirror of `WithLeft`: the `right` is mandatory and the `left` is an
148
+ * optional companion. Pass an absent (`null`/`undefined`) `left` to get a
149
+ * `RightOnly`, or a present one to get a `LeftAndRight`. The return type
150
+ * `WithRight<L, R>` reflects that a `right` is always present.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
155
+ *
156
+ * assert.deepStrictEqual(
157
+ * InclusiveOr.WithRight({ left: 1, right: "a" }),
158
+ * InclusiveOr.LeftAndRight({ left: 1, right: "a" })
159
+ * )
160
+ *
161
+ * assert.deepStrictEqual(
162
+ * InclusiveOr.WithRight({ right: "a" }),
163
+ * InclusiveOr.RightOnly({ right: "a" })
164
+ * )
165
+ * ```
166
+ *
167
+ * @category constructors
168
+ * @since 0.0.0
169
+ */
170
+ export const WithRight = ({
171
+ left,
172
+ right
173
+ }) => Predicate.isNotNullish(left) ? LeftAndRight({
174
+ left,
175
+ right
176
+ }) : RightOnly({
177
+ right
178
+ });
179
+ /**
180
+ * Builds an `InclusiveOr` from a pair of possibly-nullish inputs, wrapping the result in
181
+ * an `Option` so the all-absent case is expressible.
182
+ *
183
+ * Returns `Option.some(LeftAndRight)` when both are present, `Option.some(LeftOnly)`
184
+ * or `Option.some(RightOnly)` when exactly one is present, and `Option.none()`
185
+ * when both are nullish. Use it as the total entry point for turning two optional
186
+ * values into an `InclusiveOr`.
187
+ *
188
+ * @example
189
+ * ```ts
190
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
191
+ * import { Option } from "effect"
192
+ *
193
+ * assert.deepStrictEqual(
194
+ * InclusiveOr.optionFromNullables({ left: 1, right: "a" }),
195
+ * Option.some(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))
196
+ * )
197
+ *
198
+ * assert.deepStrictEqual(
199
+ * InclusiveOr.optionFromNullables({ left: 1, right: null }),
200
+ * Option.some(InclusiveOr.LeftOnly({ left: 1 }))
201
+ * )
202
+ *
203
+ * assert.deepStrictEqual(
204
+ * InclusiveOr.optionFromNullables({ left: null, right: undefined }),
205
+ * Option.none()
206
+ * )
207
+ * ```
208
+ *
209
+ * @category constructors
210
+ * @since 0.0.0
211
+ */
212
+ export const optionFromNullables = ({
213
+ left,
214
+ right
215
+ }) => {
216
+ if (Predicate.isNotNullish(left) && Predicate.isNotNullish(right)) {
217
+ return Option.some(LeftAndRight({
218
+ left,
219
+ right
220
+ }));
221
+ }
222
+ if (Predicate.isNotNullish(left)) {
223
+ return Option.some(LeftOnly({
224
+ left
225
+ }));
226
+ }
227
+ if (Predicate.isNotNullish(right)) {
228
+ return Option.some(RightOnly({
229
+ right
230
+ }));
231
+ }
232
+ return Option.none();
233
+ };
234
+ /**
235
+ * Builds an `InclusiveOr` from a pair of possibly-nullish inputs, falling back to the
236
+ * `orElse` thunk when both are absent.
237
+ *
238
+ * The non-optional companion to `optionFromNullables`: it unwraps the same logic
239
+ * but resolves the all-absent case with `orElse` instead of an `Option`. The
240
+ * default `orElse` throws, so omit it only when at least one side is guaranteed
241
+ * present.
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
246
+ *
247
+ * assert.deepStrictEqual(
248
+ * InclusiveOr.fromNullables({ left: 1, right: "a" }),
249
+ * InclusiveOr.LeftAndRight({ left: 1, right: "a" })
250
+ * )
251
+ *
252
+ * // Both absent — fall back via orElse instead of throwing
253
+ * assert.deepStrictEqual(
254
+ * InclusiveOr.fromNullables({
255
+ * left: null,
256
+ * right: null,
257
+ * orElse: () => InclusiveOr.LeftOnly({ left: 0 })
258
+ * }),
259
+ * InclusiveOr.LeftOnly({ left: 0 })
260
+ * )
261
+ * ```
262
+ *
263
+ * @category constructors
264
+ * @since 0.0.0
265
+ */
266
+ export const fromNullables = ({
267
+ left,
268
+ right,
269
+ orElse = () => {
270
+ throw new Error("Both left and right are nullable");
271
+ }
272
+ }) => pipe(optionFromNullables({
273
+ left,
274
+ right
275
+ }), Option.getOrElse(orElse));
276
+ /**
277
+ * Folds an `InclusiveOr` from the left's perspective, collapsing the three tags into two
278
+ * handlers.
279
+ *
280
+ * Both `LeftOnly` and `LeftAndRight` carry a `left`, so they route to the `Left`
281
+ * handler; only `RightOnly` lacks a `left` and routes to `RightOnly`. Use it when
282
+ * you care about the `left` value and treat the right-only case as the exception.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
287
+ *
288
+ * const onLeft = InclusiveOr.matchLeft({
289
+ * Left: (left: number) => `left ${left}`,
290
+ * RightOnly: (right: string) => `right ${right}`
291
+ * })
292
+ *
293
+ * assert.deepStrictEqual(onLeft(InclusiveOr.LeftOnly({ left: 1 })), "left 1")
294
+ * assert.deepStrictEqual(
295
+ * onLeft(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
296
+ * "left 1"
297
+ * )
298
+ * assert.deepStrictEqual(onLeft(InclusiveOr.RightOnly({ right: "a" })), "right a")
299
+ * ```
300
+ *
301
+ * @category pattern matching
302
+ * @since 0.0.0
303
+ */
304
+ export const matchLeft = ({
305
+ Left,
306
+ RightOnly
307
+ }) => inclusiveOr =>
308
+ // `$match` widens the common return to `Unify<A>`; narrow it back to `A`.
309
+ match(inclusiveOr, {
310
+ LeftOnly: ({
311
+ left
312
+ }) => Left(left),
313
+ RightOnly: ({
314
+ right
315
+ }) => RightOnly(right),
316
+ LeftAndRight: ({
317
+ left
318
+ }) => Left(left)
319
+ });
320
+ /**
321
+ * Folds an `InclusiveOr` from the right's perspective, collapsing the three tags into two
322
+ * handlers.
323
+ *
324
+ * The mirror of `matchLeft`: both `RightOnly` and `LeftAndRight` carry a `right`,
325
+ * so they route to the `Right` handler; only `LeftOnly` lacks a `right` and routes
326
+ * to `LeftOnly`. Use it when you care about the `right` value and treat the
327
+ * left-only case as the exception.
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
332
+ *
333
+ * const onRight = InclusiveOr.matchRight({
334
+ * LeftOnly: (left: number) => `left ${left}`,
335
+ * Right: (right: string) => `right ${right}`
336
+ * })
337
+ *
338
+ * assert.deepStrictEqual(onRight(InclusiveOr.RightOnly({ right: "a" })), "right a")
339
+ * assert.deepStrictEqual(
340
+ * onRight(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
341
+ * "right a"
342
+ * )
343
+ * assert.deepStrictEqual(onRight(InclusiveOr.LeftOnly({ left: 1 })), "left 1")
344
+ * ```
345
+ *
346
+ * @category pattern matching
347
+ * @since 0.0.0
348
+ */
349
+ export const matchRight = ({
350
+ LeftOnly,
351
+ Right
352
+ }) => inclusiveOr =>
353
+ // `$match` widens the common return to `Unify<A>`; narrow it back to `A`.
354
+ match(inclusiveOr, {
355
+ LeftOnly: ({
356
+ left
357
+ }) => LeftOnly(left),
358
+ RightOnly: ({
359
+ right
360
+ }) => Right(right),
361
+ LeftAndRight: ({
362
+ right
363
+ }) => Right(right)
364
+ });
365
+ /**
366
+ * Completes an `InclusiveOr` into a guaranteed `LeftAndRight` by filling whichever side
367
+ * is missing from the matching `orElse` thunk.
368
+ *
369
+ * A `LeftAndRight` passes through unchanged; a `LeftOnly` gains a `right` from
370
+ * `orElseRight`; a `RightOnly` gains a `left` from `orElseLeft`. Use it to
371
+ * normalise a partial `InclusiveOr` into the both-present shape before reading both
372
+ * sides.
373
+ *
374
+ * @example
375
+ * ```ts
376
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
377
+ *
378
+ * const fill = InclusiveOr.orElse({
379
+ * orElseLeft: () => 0,
380
+ * orElseRight: () => "default"
381
+ * })
382
+ *
383
+ * assert.deepStrictEqual(
384
+ * fill(InclusiveOr.LeftOnly({ left: 1 })),
385
+ * InclusiveOr.LeftAndRight({ left: 1, right: "default" })
386
+ * )
387
+ * assert.deepStrictEqual(
388
+ * fill(InclusiveOr.RightOnly({ right: "a" })),
389
+ * InclusiveOr.LeftAndRight({ left: 0, right: "a" })
390
+ * )
391
+ * ```
392
+ *
393
+ * @category getters
394
+ * @since 0.0.0
395
+ */
396
+ export const orElse = ({
397
+ orElseLeft,
398
+ orElseRight
399
+ }) => match({
400
+ LeftOnly: ({
401
+ left
402
+ }) => LeftAndRight({
403
+ left,
404
+ right: orElseRight()
405
+ }),
406
+ RightOnly: ({
407
+ right
408
+ }) => LeftAndRight({
409
+ left: orElseLeft(),
410
+ right
411
+ }),
412
+ LeftAndRight: ({
413
+ left,
414
+ right
415
+ }) => LeftAndRight({
416
+ left,
417
+ right
418
+ })
419
+ });
420
+ /**
421
+ * Completes an `InclusiveOr` into a `LeftAndRight` whose missing side is filled with
422
+ * `undefined`.
423
+ *
424
+ * A specialisation of `orElse` that supplies `undefined` for whichever side is
425
+ * absent, so the result always exposes both `left` and `right` keys (each
426
+ * possibly `undefined`). Use it when you want to destructure both sides without
427
+ * branching on the tag.
428
+ *
429
+ * @example
430
+ * ```ts
431
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
432
+ *
433
+ * assert.deepStrictEqual(
434
+ * InclusiveOr.orUndefined(InclusiveOr.LeftOnly({ left: 1 })),
435
+ * InclusiveOr.LeftAndRight({ left: 1, right: undefined })
436
+ * )
437
+ * assert.deepStrictEqual(
438
+ * InclusiveOr.orUndefined(InclusiveOr.RightOnly({ right: "a" })),
439
+ * InclusiveOr.LeftAndRight({ left: undefined, right: "a" })
440
+ * )
441
+ * ```
442
+ *
443
+ * @category getters
444
+ * @since 0.0.0
445
+ */
446
+ export const orUndefined = /*#__PURE__*/orElse({
447
+ orElseLeft: () => undefined,
448
+ orElseRight: () => undefined
449
+ });
450
+ /**
451
+ * Extracts the `left` of an `InclusiveOr`, falling back to `orElseReturn` when no `left`
452
+ * is present.
453
+ *
454
+ * `LeftOnly` and `LeftAndRight` return their `left`; `RightOnly` returns the
455
+ * result of `orElseReturn`. Use it to read the left side with a default in one
456
+ * step.
457
+ *
458
+ * @example
459
+ * ```ts
460
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
461
+ *
462
+ * const leftOrZero = InclusiveOr.leftOrElse(() => 0)
463
+ *
464
+ * assert.deepStrictEqual(leftOrZero(InclusiveOr.LeftOnly({ left: 1 })), 1)
465
+ * assert.deepStrictEqual(
466
+ * leftOrZero(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
467
+ * 1
468
+ * )
469
+ * assert.deepStrictEqual(leftOrZero(InclusiveOr.RightOnly({ right: "a" })), 0)
470
+ * ```
471
+ *
472
+ * @category getters
473
+ * @since 0.0.0
474
+ */
475
+ export const leftOrElse = orElseReturn => inclusiveOr => pipe(inclusiveOr, orElse({
476
+ orElseLeft: orElseReturn,
477
+ orElseRight: constUndefined
478
+ }), Struct.get("left"));
479
+ /**
480
+ * Extracts the `left` of an `InclusiveOr`, returning `undefined` when no `left` is
481
+ * present.
482
+ *
483
+ * A specialisation of `leftOrElse` whose fallback is `undefined`: `LeftOnly` and
484
+ * `LeftAndRight` yield their `left`, while `RightOnly` yields `undefined`.
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
489
+ *
490
+ * assert.deepStrictEqual(InclusiveOr.leftOrUndefined(InclusiveOr.LeftOnly({ left: 1 })), 1)
491
+ * assert.deepStrictEqual(
492
+ * InclusiveOr.leftOrUndefined(InclusiveOr.RightOnly({ right: "a" })),
493
+ * undefined
494
+ * )
495
+ * ```
496
+ *
497
+ * @category getters
498
+ * @since 0.0.0
499
+ */
500
+ export const leftOrUndefined = /*#__PURE__*/leftOrElse(() => undefined);
501
+ /**
502
+ * Extracts the `right` of an `InclusiveOr`, falling back to `orElseReturn` when no
503
+ * `right` is present.
504
+ *
505
+ * The mirror of `leftOrElse`: `RightOnly` and `LeftAndRight` return their
506
+ * `right`; `LeftOnly` returns the result of `orElseReturn`.
507
+ *
508
+ * @example
509
+ * ```ts
510
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
511
+ *
512
+ * const rightOrDefault = InclusiveOr.rightOrElse(() => "default")
513
+ *
514
+ * assert.deepStrictEqual(
515
+ * rightOrDefault(InclusiveOr.RightOnly({ right: "a" })),
516
+ * "a"
517
+ * )
518
+ * assert.deepStrictEqual(
519
+ * rightOrDefault(InclusiveOr.LeftOnly({ left: 1 })),
520
+ * "default"
521
+ * )
522
+ * ```
523
+ *
524
+ * @category getters
525
+ * @since 0.0.0
526
+ */
527
+ export const rightOrElse = orElseReturn => inclusiveOr => pipe(inclusiveOr, orElse({
528
+ orElseLeft: constUndefined,
529
+ orElseRight: orElseReturn
530
+ }), Struct.get("right"));
531
+ /**
532
+ * Extracts the `right` of an `InclusiveOr`, returning `undefined` when no `right` is
533
+ * present.
534
+ *
535
+ * A specialisation of `rightOrElse` whose fallback is `undefined`: `RightOnly` and
536
+ * `LeftAndRight` yield their `right`, while `LeftOnly` yields `undefined`.
537
+ *
538
+ * @example
539
+ * ```ts
540
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
541
+ *
542
+ * assert.deepStrictEqual(
543
+ * InclusiveOr.rightOrUndefined(InclusiveOr.RightOnly({ right: "a" })),
544
+ * "a"
545
+ * )
546
+ * assert.deepStrictEqual(
547
+ * InclusiveOr.rightOrUndefined(InclusiveOr.LeftOnly({ left: 1 })),
548
+ * undefined
549
+ * )
550
+ * ```
551
+ *
552
+ * @category getters
553
+ * @since 0.0.0
554
+ */
555
+ export const rightOrUndefined = /*#__PURE__*/rightOrElse(() => undefined);
556
+ /**
557
+ * Extracts the `right` of an `InclusiveOr` as an `Option`.
558
+ *
559
+ * `RightOnly` and `LeftAndRight` yield `Option.some(right)`; `LeftOnly` yields
560
+ * `Option.none()`. Use it when you want to chain the right side through `Option`
561
+ * combinators rather than fall back to a default eagerly.
562
+ *
563
+ * @example
564
+ * ```ts
565
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
566
+ * import { Option } from "effect"
567
+ *
568
+ * assert.deepStrictEqual(
569
+ * InclusiveOr.rightOption(InclusiveOr.RightOnly({ right: "a" })),
570
+ * Option.some("a")
571
+ * )
572
+ * assert.deepStrictEqual(
573
+ * InclusiveOr.rightOption(InclusiveOr.LeftOnly({ left: 1 })),
574
+ * Option.none()
575
+ * )
576
+ * ```
577
+ *
578
+ * @category getters
579
+ * @since 0.0.0
580
+ */
581
+ export const rightOption = inclusiveOr => pipe(inclusiveOr, matchRight({
582
+ LeftOnly: () => Option.none(),
583
+ Right: Option.some
584
+ }));
585
+ /**
586
+ * Extracts the `left` of an `InclusiveOr` as an `Option`.
587
+ *
588
+ * The mirror of `rightOption`: `LeftOnly` and `LeftAndRight` yield
589
+ * `Option.some(left)`, while `RightOnly` yields `Option.none()`.
590
+ *
591
+ * @example
592
+ * ```ts
593
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
594
+ * import { Option } from "effect"
595
+ *
596
+ * assert.deepStrictEqual(
597
+ * InclusiveOr.leftOption(InclusiveOr.LeftOnly({ left: 1 })),
598
+ * Option.some(1)
599
+ * )
600
+ * assert.deepStrictEqual(
601
+ * InclusiveOr.leftOption(InclusiveOr.RightOnly({ right: "a" })),
602
+ * Option.none()
603
+ * )
604
+ * ```
605
+ *
606
+ * @category getters
607
+ * @since 0.0.0
608
+ */
609
+ export const leftOption = inclusiveOr => pipe(inclusiveOr, matchLeft({
610
+ Left: Option.some,
611
+ RightOnly: () => Option.none()
612
+ }));
613
+ /**
614
+ * Transforms both sides of an `InclusiveOr`, applying `mapLeft` to any `left` and
615
+ * `mapRight` to any `right`.
616
+ *
617
+ * Each constructor is rebuilt with its mapped contents, so `LeftOnly` maps only
618
+ * the left, `RightOnly` only the right, and `LeftAndRight` both. The tag is
619
+ * preserved. Use it as the bifunctor map over `InclusiveOr`.
620
+ *
621
+ * @example
622
+ * ```ts
623
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
624
+ *
625
+ * const both = InclusiveOr.mapBoth({
626
+ * mapLeft: (left: number) => left + 1,
627
+ * mapRight: (right: string) => right.toUpperCase()
628
+ * })
629
+ *
630
+ * assert.deepStrictEqual(
631
+ * both(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
632
+ * InclusiveOr.LeftAndRight({ left: 2, right: "A" })
633
+ * )
634
+ * assert.deepStrictEqual(
635
+ * both(InclusiveOr.LeftOnly({ left: 1 })),
636
+ * InclusiveOr.LeftOnly({ left: 2 })
637
+ * )
638
+ * ```
639
+ *
640
+ * @category mapping
641
+ * @since 0.0.0
642
+ */
643
+ export const mapBoth = ({
644
+ mapLeft,
645
+ mapRight
646
+ }) => match({
647
+ LeftOnly: ({
648
+ left
649
+ }) => LeftOnly({
650
+ left: mapLeft(left)
651
+ }),
652
+ RightOnly: ({
653
+ right
654
+ }) => RightOnly({
655
+ right: mapRight(right)
656
+ }),
657
+ LeftAndRight: ({
658
+ left,
659
+ right
660
+ }) => LeftAndRight({
661
+ left: mapLeft(left),
662
+ right: mapRight(right)
663
+ })
664
+ });
665
+ /**
666
+ * Effectful `mapBoth`: transforms each present side through an `Effect`,
667
+ * reassembling the results into an `InclusiveOr` inside an `Effect`.
668
+ *
669
+ * For `LeftAndRight` both effects run via `Effect.all` and their results are
670
+ * combined; `LeftOnly`/`RightOnly` run only the relevant effect. Errors and
671
+ * requirements from both mappers are unioned into the result type. Use it when
672
+ * mapping an `InclusiveOr`'s sides requires effects (validation, IO).
673
+ *
674
+ * @example
675
+ * ```ts
676
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
677
+ * import { Effect } from "effect"
678
+ *
679
+ * const both = InclusiveOr.mapBothEffect({
680
+ * mapLeft: (left: number) => Effect.succeed(left + 1),
681
+ * mapRight: (right: string) => Effect.succeed(right.toUpperCase())
682
+ * })
683
+ *
684
+ * assert.deepStrictEqual(
685
+ * Effect.runSync(both(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))),
686
+ * InclusiveOr.LeftAndRight({ left: 2, right: "A" })
687
+ * )
688
+ * ```
689
+ *
690
+ * @category sequencing
691
+ * @since 0.0.0
692
+ */
693
+ export const mapBothEffect = ({
694
+ mapLeft,
695
+ mapRight
696
+ }) => match({
697
+ LeftOnly: ({
698
+ left
699
+ }) => pipe(mapLeft(left), Effect.map(left2 => LeftOnly({
700
+ left: left2
701
+ }))),
702
+ RightOnly: ({
703
+ right
704
+ }) => pipe(mapRight(right), Effect.map(right2 => RightOnly({
705
+ right: right2
706
+ }))),
707
+ LeftAndRight: ({
708
+ left,
709
+ right
710
+ }) => pipe(Effect.all({
711
+ left: mapLeft(left),
712
+ right: mapRight(right)
713
+ }), Effect.map(({
714
+ left: left2,
715
+ right: right2
716
+ }) => LeftAndRight({
717
+ left: left2,
718
+ right: right2
719
+ })))
720
+ });
721
+ /**
722
+ * Transforms the `left` of an `InclusiveOr`, leaving any `right` untouched.
723
+ *
724
+ * A specialisation of `mapBoth` with the right mapper set to `identity`:
725
+ * `LeftOnly` and `LeftAndRight` have their `left` mapped, while `RightOnly` passes
726
+ * through unchanged.
727
+ *
728
+ * @example
729
+ * ```ts
730
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
731
+ *
732
+ * const inc = InclusiveOr.mapLeft((left: number) => left + 1)
733
+ *
734
+ * assert.deepStrictEqual(
735
+ * inc(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
736
+ * InclusiveOr.LeftAndRight({ left: 2, right: "a" })
737
+ * )
738
+ * assert.deepStrictEqual(
739
+ * inc(InclusiveOr.RightOnly({ right: "a" })),
740
+ * InclusiveOr.RightOnly({ right: "a" })
741
+ * )
742
+ * ```
743
+ *
744
+ * @category mapping
745
+ * @since 0.0.0
746
+ */
747
+ export const mapLeft = mapLeft => mapBoth({
748
+ mapLeft,
749
+ mapRight: identity
750
+ });
751
+ /**
752
+ * Chains the `left` of an `InclusiveOr` into a new `InclusiveOr`, flattening the result.
753
+ *
754
+ * Whenever a `left` is present (`LeftOnly` or `LeftAndRight`) it is passed to
755
+ * `mapLeft`, whose returned `InclusiveOr` replaces the original; `RightOnly` passes
756
+ * through unchanged. Use it to sequence left-driven computations that themselves
757
+ * produce an `InclusiveOr`.
758
+ *
759
+ * @example
760
+ * ```ts
761
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
762
+ *
763
+ * const chain = InclusiveOr.flatMapLeft((left: number) =>
764
+ * left > 0
765
+ * ? InclusiveOr.LeftOnly({ left: left * 10 })
766
+ * : InclusiveOr.RightOnly({ right: "non-positive" })
767
+ * )
768
+ *
769
+ * assert.deepStrictEqual(
770
+ * chain(InclusiveOr.LeftOnly({ left: 2 })),
771
+ * InclusiveOr.LeftOnly({ left: 20 })
772
+ * )
773
+ * assert.deepStrictEqual(
774
+ * chain(InclusiveOr.RightOnly({ right: "a" })),
775
+ * InclusiveOr.RightOnly({ right: "a" })
776
+ * )
777
+ * ```
778
+ *
779
+ * @category sequencing
780
+ * @since 0.0.0
781
+ */
782
+ export const flatMapLeft = mapLeft => match({
783
+ LeftOnly: ({
784
+ left
785
+ }) => mapLeft(left),
786
+ RightOnly: ({
787
+ right
788
+ }) => RightOnly({
789
+ right
790
+ }),
791
+ LeftAndRight: ({
792
+ left
793
+ }) => mapLeft(left)
794
+ });
795
+ /**
796
+ * Effectful `mapLeft`: transforms the `left` of an `InclusiveOr` through an `Effect`,
797
+ * leaving any `right` untouched.
798
+ *
799
+ * A specialisation of `mapBothEffect` with the right mapper set to
800
+ * `Effect.succeed`: the `left` (when present) is mapped effectfully and the
801
+ * `right` is carried through unchanged.
802
+ *
803
+ * @example
804
+ * ```ts
805
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
806
+ * import { Effect } from "effect"
807
+ *
808
+ * const inc = InclusiveOr.mapLeftEffect((left: number) => Effect.succeed(left + 1))
809
+ *
810
+ * assert.deepStrictEqual(
811
+ * Effect.runSync(inc(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))),
812
+ * InclusiveOr.LeftAndRight({ left: 2, right: "a" })
813
+ * )
814
+ * ```
815
+ *
816
+ * @category sequencing
817
+ * @since 0.0.0
818
+ */
819
+ export const mapLeftEffect = mapLeft => mapBothEffect({
820
+ mapLeft,
821
+ mapRight: Effect.succeed
822
+ });
823
+ /**
824
+ * Effectful `flatMapLeft`: chains the `left` of an `InclusiveOr` into an `Effect` that
825
+ * yields a new `InclusiveOr`, flattening the result.
826
+ *
827
+ * When a `left` is present it is passed to `mapLeft`, whose effectful `InclusiveOr`
828
+ * replaces the original; `RightOnly` is lifted unchanged via `Effect.succeed`. Use
829
+ * it to sequence left-driven effectful computations that produce an `InclusiveOr`.
830
+ *
831
+ * @example
832
+ * ```ts
833
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
834
+ * import { Effect } from "effect"
835
+ *
836
+ * const chain = InclusiveOr.flatMapLeftEffect((left: number) =>
837
+ * Effect.succeed(InclusiveOr.LeftOnly({ left: left * 10 }))
838
+ * )
839
+ *
840
+ * assert.deepStrictEqual(
841
+ * Effect.runSync(chain(InclusiveOr.LeftOnly({ left: 2 }))),
842
+ * InclusiveOr.LeftOnly({ left: 20 })
843
+ * )
844
+ * assert.deepStrictEqual(
845
+ * Effect.runSync(chain(InclusiveOr.RightOnly({ right: "a" }))),
846
+ * InclusiveOr.RightOnly({ right: "a" })
847
+ * )
848
+ * ```
849
+ *
850
+ * @category sequencing
851
+ * @since 0.0.0
852
+ */
853
+ export const flatMapLeftEffect = mapLeft => match({
854
+ LeftOnly: ({
855
+ left
856
+ }) => mapLeft(left),
857
+ RightOnly: ({
858
+ right
859
+ }) => Effect.succeed(RightOnly({
860
+ right
861
+ })),
862
+ LeftAndRight: ({
863
+ left
864
+ }) => mapLeft(left)
865
+ });
866
+ /**
867
+ * Transforms the `right` of an `InclusiveOr`, leaving any `left` untouched.
868
+ *
869
+ * The mirror of `mapLeft`: `RightOnly` and `LeftAndRight` have their `right`
870
+ * mapped, while `LeftOnly` passes through unchanged.
871
+ *
872
+ * @example
873
+ * ```ts
874
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
875
+ *
876
+ * const upper = InclusiveOr.mapRight((right: string) => right.toUpperCase())
877
+ *
878
+ * assert.deepStrictEqual(
879
+ * upper(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
880
+ * InclusiveOr.LeftAndRight({ left: 1, right: "A" })
881
+ * )
882
+ * assert.deepStrictEqual(
883
+ * upper(InclusiveOr.LeftOnly({ left: 1 })),
884
+ * InclusiveOr.LeftOnly({ left: 1 })
885
+ * )
886
+ * ```
887
+ *
888
+ * @category mapping
889
+ * @since 0.0.0
890
+ */
891
+ export const mapRight = mapRight => mapBoth({
892
+ mapLeft: identity,
893
+ mapRight
894
+ });
895
+ /**
896
+ * Chains the `right` of an `InclusiveOr` into a new `InclusiveOr`, flattening the result.
897
+ *
898
+ * The mirror of `flatMapLeft`: whenever a `right` is present (`RightOnly` or
899
+ * `LeftAndRight`) it is passed to `mapRight`, whose returned `InclusiveOr` replaces the
900
+ * original; `LeftOnly` passes through unchanged.
901
+ *
902
+ * @example
903
+ * ```ts
904
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
905
+ *
906
+ * const chain = InclusiveOr.flatMapRight((right: string) =>
907
+ * InclusiveOr.RightOnly({ right: right.toUpperCase() })
908
+ * )
909
+ *
910
+ * assert.deepStrictEqual(
911
+ * chain(InclusiveOr.RightOnly({ right: "a" })),
912
+ * InclusiveOr.RightOnly({ right: "A" })
913
+ * )
914
+ * assert.deepStrictEqual(
915
+ * chain(InclusiveOr.LeftOnly({ left: 1 })),
916
+ * InclusiveOr.LeftOnly({ left: 1 })
917
+ * )
918
+ * ```
919
+ *
920
+ * @category sequencing
921
+ * @since 0.0.0
922
+ */
923
+ export const flatMapRight = mapRight => match({
924
+ LeftOnly: ({
925
+ left
926
+ }) => LeftOnly({
927
+ left
928
+ }),
929
+ RightOnly: ({
930
+ right
931
+ }) => mapRight(right),
932
+ LeftAndRight: ({
933
+ right
934
+ }) => mapRight(right)
935
+ });
936
+ /**
937
+ * Effectful `mapRight`: transforms the `right` of an `InclusiveOr` through an `Effect`,
938
+ * leaving any `left` untouched.
939
+ *
940
+ * The mirror of `mapLeftEffect`: the `right` (when present) is mapped effectfully
941
+ * and the `left` is carried through unchanged via `Effect.succeed`.
942
+ *
943
+ * @example
944
+ * ```ts
945
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
946
+ * import { Effect } from "effect"
947
+ *
948
+ * const upper = InclusiveOr.mapRightEffect((right: string) =>
949
+ * Effect.succeed(right.toUpperCase())
950
+ * )
951
+ *
952
+ * assert.deepStrictEqual(
953
+ * Effect.runSync(upper(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))),
954
+ * InclusiveOr.LeftAndRight({ left: 1, right: "A" })
955
+ * )
956
+ * ```
957
+ *
958
+ * @category sequencing
959
+ * @since 0.0.0
960
+ */
961
+ export const mapRightEffect = mapRight => mapBothEffect({
962
+ mapLeft: Effect.succeed,
963
+ mapRight
964
+ });
965
+ /**
966
+ * Effectful `flatMapRight`: chains the `right` of an `InclusiveOr` into an `Effect` that
967
+ * yields a new `InclusiveOr`, flattening the result.
968
+ *
969
+ * The mirror of `flatMapLeftEffect`: when a `right` is present it is passed to
970
+ * `mapRight`, whose effectful `InclusiveOr` replaces the original; `LeftOnly` is lifted
971
+ * unchanged via `Effect.succeed`.
972
+ *
973
+ * @example
974
+ * ```ts
975
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
976
+ * import { Effect } from "effect"
977
+ *
978
+ * const chain = InclusiveOr.flatMapRightEffect((right: string) =>
979
+ * Effect.succeed(InclusiveOr.RightOnly({ right: right.toUpperCase() }))
980
+ * )
981
+ *
982
+ * assert.deepStrictEqual(
983
+ * Effect.runSync(chain(InclusiveOr.RightOnly({ right: "a" }))),
984
+ * InclusiveOr.RightOnly({ right: "A" })
985
+ * )
986
+ * assert.deepStrictEqual(
987
+ * Effect.runSync(chain(InclusiveOr.LeftOnly({ left: 1 }))),
988
+ * InclusiveOr.LeftOnly({ left: 1 })
989
+ * )
990
+ * ```
991
+ *
992
+ * @category sequencing
993
+ * @since 0.0.0
994
+ */
995
+ export const flatMapRightEffect = mapRight => match({
996
+ LeftOnly: ({
997
+ left
998
+ }) => Effect.succeed(LeftOnly({
999
+ left
1000
+ })),
1001
+ RightOnly: ({
1002
+ right
1003
+ }) => mapRight(right),
1004
+ LeftAndRight: ({
1005
+ right
1006
+ }) => mapRight(right)
1007
+ });
1008
+ /**
1009
+ * Zips two arrays into one, calling `f` with an `InclusiveOr` for each index so
1010
+ * that length mismatches are handled explicitly rather than truncated.
1011
+ *
1012
+ * Unlike `Array.zipWith` (which stops at the shorter array), this walks to the
1013
+ * length of the *longer* array. The first array's element fills the `left` side
1014
+ * and the second array's element fills the `right` side, so at each index `f`
1015
+ * receives an `InclusiveOr<A, B>`: `LeftAndRight` when both arrays have an
1016
+ * element, `LeftOnly` when only the first does, and `RightOnly` when only the
1017
+ * second does. Use it when the "extra" tail of either array still carries
1018
+ * meaning.
1019
+ *
1020
+ * @example
1021
+ * ```ts
1022
+ * import { InclusiveOr } from "@nunofyobiz/effect-extras"
1023
+ * import { pipe } from "effect"
1024
+ *
1025
+ * const describe = InclusiveOr.match({
1026
+ * LeftOnly: ({ left }) => `left ${left}`,
1027
+ * RightOnly: ({ right }) => `right ${right}`,
1028
+ * LeftAndRight: ({ left, right }) => `both ${left}/${right}`
1029
+ * })
1030
+ *
1031
+ * // data-first
1032
+ * assert.deepStrictEqual(InclusiveOr.zip([1, 2, 3], [10, 20], describe), [
1033
+ * "both 1/10",
1034
+ * "both 2/20",
1035
+ * "left 3"
1036
+ * ])
1037
+ *
1038
+ * // data-last (pipeable)
1039
+ * assert.deepStrictEqual(pipe([1, 2, 3], InclusiveOr.zip([10, 20], describe)), [
1040
+ * "both 1/10",
1041
+ * "both 2/20",
1042
+ * "left 3"
1043
+ * ])
1044
+ * ```
1045
+ *
1046
+ * @category combinators
1047
+ * @since 0.0.0
1048
+ */
1049
+ export const zip = /*#__PURE__*/dual(3, (array1, array2, f) => {
1050
+ const newLength = Math.max(array1.length, array2.length);
1051
+ if (newLength === 0) {
1052
+ return [];
1053
+ }
1054
+ return Array.makeBy(newLength, index => {
1055
+ if (index < array1.length && index < array2.length) {
1056
+ return f(LeftAndRight({
1057
+ left: array1[index],
1058
+ right: array2[index]
1059
+ }));
1060
+ }
1061
+ if (index < array1.length) {
1062
+ return f(LeftOnly({
1063
+ left: array1[index]
1064
+ }));
1065
+ }
1066
+ if (index < array2.length) {
1067
+ return f(RightOnly({
1068
+ right: array2[index]
1069
+ }));
1070
+ }
1071
+ throw new Error(`Index ${index} is out of bounds for array1 and array2`);
1072
+ });
1073
+ });
1074
+ //# sourceMappingURL=InclusiveOr.js.map