@nunofyobiz/effect-extras 1.0.0 → 2.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 (128) hide show
  1. package/README.md +50 -6
  2. package/dist/ArrayX.d.ts +415 -0
  3. package/dist/ArrayX.d.ts.map +1 -0
  4. package/dist/ArrayX.js +547 -0
  5. package/dist/ArrayX.js.map +1 -0
  6. package/dist/BigIntX.d.ts +24 -0
  7. package/dist/BigIntX.d.ts.map +1 -0
  8. package/dist/BigIntX.js +30 -0
  9. package/dist/BigIntX.js.map +1 -0
  10. package/dist/BooleanX.d.ts +25 -0
  11. package/dist/BooleanX.d.ts.map +1 -0
  12. package/dist/BooleanX.js +25 -0
  13. package/dist/BooleanX.js.map +1 -0
  14. package/dist/DurationX.d.ts +73 -0
  15. package/dist/DurationX.d.ts.map +1 -0
  16. package/dist/DurationX.js +91 -0
  17. package/dist/DurationX.js.map +1 -0
  18. package/dist/EffectX.d.ts +120 -0
  19. package/dist/EffectX.d.ts.map +1 -0
  20. package/dist/EffectX.js +140 -0
  21. package/dist/EffectX.js.map +1 -0
  22. package/dist/FormDataX.d.ts +49 -0
  23. package/dist/FormDataX.d.ts.map +1 -0
  24. package/dist/FormDataX.js +42 -0
  25. package/dist/FormDataX.js.map +1 -0
  26. package/dist/MapX.d.ts +32 -0
  27. package/dist/MapX.d.ts.map +1 -0
  28. package/dist/MapX.js +49 -0
  29. package/dist/MapX.js.map +1 -0
  30. package/dist/NonNullableX.d.ts +174 -0
  31. package/dist/NonNullableX.d.ts.map +1 -0
  32. package/dist/NonNullableX.js +212 -0
  33. package/dist/NonNullableX.js.map +1 -0
  34. package/dist/NumberX.d.ts +178 -0
  35. package/dist/NumberX.d.ts.map +1 -0
  36. package/dist/NumberX.js +214 -0
  37. package/dist/NumberX.js.map +1 -0
  38. package/dist/OptionX.d.ts +181 -0
  39. package/dist/OptionX.d.ts.map +1 -0
  40. package/dist/OptionX.js +195 -0
  41. package/dist/OptionX.js.map +1 -0
  42. package/dist/OrderX.d.ts +32 -0
  43. package/dist/OrderX.d.ts.map +1 -0
  44. package/dist/OrderX.js +32 -0
  45. package/dist/OrderX.js.map +1 -0
  46. package/dist/PredicateX.d.ts +76 -0
  47. package/dist/PredicateX.d.ts.map +1 -0
  48. package/dist/PredicateX.js +73 -0
  49. package/dist/PredicateX.js.map +1 -0
  50. package/dist/PromiseX.d.ts +32 -0
  51. package/dist/PromiseX.d.ts.map +1 -0
  52. package/dist/PromiseX.js +32 -0
  53. package/dist/PromiseX.js.map +1 -0
  54. package/dist/RecordX.d.ts +323 -0
  55. package/dist/RecordX.d.ts.map +1 -0
  56. package/dist/RecordX.js +326 -0
  57. package/dist/RecordX.js.map +1 -0
  58. package/dist/ResultX.d.ts +50 -0
  59. package/dist/ResultX.d.ts.map +1 -0
  60. package/dist/ResultX.js +50 -0
  61. package/dist/ResultX.js.map +1 -0
  62. package/dist/SchemaX.d.ts +249 -0
  63. package/dist/SchemaX.d.ts.map +1 -0
  64. package/dist/SchemaX.js +243 -0
  65. package/dist/SchemaX.js.map +1 -0
  66. package/dist/SetX.d.ts +121 -0
  67. package/dist/SetX.d.ts.map +1 -0
  68. package/dist/SetX.js +137 -0
  69. package/dist/SetX.js.map +1 -0
  70. package/dist/StringX.d.ts +70 -0
  71. package/dist/StringX.d.ts.map +1 -0
  72. package/dist/StringX.js +81 -0
  73. package/dist/StringX.js.map +1 -0
  74. package/dist/StructX.d.ts +219 -0
  75. package/dist/StructX.d.ts.map +1 -0
  76. package/dist/StructX.js +173 -0
  77. package/dist/StructX.js.map +1 -0
  78. package/dist/WarnResult.d.ts +1146 -0
  79. package/dist/WarnResult.d.ts.map +1 -0
  80. package/dist/WarnResult.js +1060 -0
  81. package/dist/WarnResult.js.map +1 -0
  82. package/dist/index.d.ts +22 -3703
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +21 -1005
  85. package/dist/index.js.map +1 -1
  86. package/package.json +18 -5
  87. package/src/{ArrayX/ArrayX.ts → ArrayX.ts} +29 -27
  88. package/src/{DurationX/DurationX.ts → DurationX.ts} +1 -1
  89. package/src/{RecordX/RecordX.ts → RecordX.ts} +1 -1
  90. package/src/WarnResult.ts +1265 -0
  91. package/src/index.ts +21 -20
  92. package/src/ArrayX/index.ts +0 -1
  93. package/src/BigIntX/index.ts +0 -1
  94. package/src/BooleanX/index.ts +0 -1
  95. package/src/DurationX/index.ts +0 -1
  96. package/src/EffectX/index.ts +0 -1
  97. package/src/FormDataX/index.ts +0 -1
  98. package/src/MapX/index.ts +0 -1
  99. package/src/NonNullableX/index.ts +0 -2
  100. package/src/NumberX/index.ts +0 -1
  101. package/src/OptionX/index.ts +0 -1
  102. package/src/OrderX/index.ts +0 -1
  103. package/src/PredicateX/index.ts +0 -1
  104. package/src/PromiseX/index.ts +0 -1
  105. package/src/RecordX/index.ts +0 -1
  106. package/src/ResultX/index.ts +0 -1
  107. package/src/SchemaX/index.ts +0 -1
  108. package/src/SetX/index.ts +0 -1
  109. package/src/StringX/index.ts +0 -1
  110. package/src/StructX/index.ts +0 -1
  111. package/src/These/These.ts +0 -1173
  112. package/src/These/index.ts +0 -1
  113. /package/src/{BigIntX/BigIntX.ts → BigIntX.ts} +0 -0
  114. /package/src/{BooleanX/BooleanX.ts → BooleanX.ts} +0 -0
  115. /package/src/{EffectX/EffectX.ts → EffectX.ts} +0 -0
  116. /package/src/{FormDataX/FormDataX.ts → FormDataX.ts} +0 -0
  117. /package/src/{MapX/MapX.ts → MapX.ts} +0 -0
  118. /package/src/{NonNullableX/NonNullableX.ts → NonNullableX.ts} +0 -0
  119. /package/src/{NumberX/NumberX.ts → NumberX.ts} +0 -0
  120. /package/src/{OptionX/OptionX.ts → OptionX.ts} +0 -0
  121. /package/src/{OrderX/OrderX.ts → OrderX.ts} +0 -0
  122. /package/src/{PredicateX/PredicateX.ts → PredicateX.ts} +0 -0
  123. /package/src/{PromiseX/PromiseX.ts → PromiseX.ts} +0 -0
  124. /package/src/{ResultX/ResultX.ts → ResultX.ts} +0 -0
  125. /package/src/{SchemaX/SchemaX.ts → SchemaX.ts} +0 -0
  126. /package/src/{SetX/SetX.ts → SetX.ts} +0 -0
  127. /package/src/{StringX/StringX.ts → StringX.ts} +0 -0
  128. /package/src/{StructX/StructX.ts → StructX.ts} +0 -0
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Helpers for working with non-nullable values.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Order } from "effect";
7
+ /**
8
+ * Returns `value` narrowed to `NonNullable<A>`, throwing an `Error` if it is
9
+ * `null` or `undefined`.
10
+ *
11
+ * Use it at trusted boundaries where a value is known to be present but typed as
12
+ * nullable, turning a silent `undefined` into a loud failure. An optional
13
+ * `variableName` is woven into the thrown message to aid debugging. This is the
14
+ * function re-exported as `nn` from the module barrel, a terse shorthand handy
15
+ * inside string interpolations.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
20
+ *
21
+ * assert.deepStrictEqual(NonNullableX.fromNullableOrThrow("value"), "value")
22
+ *
23
+ * assert.throws(
24
+ * () => NonNullableX.fromNullableOrThrow(null, "varName"),
25
+ * /Value is nullable: null \(variable name: varName\)/
26
+ * )
27
+ * ```
28
+ *
29
+ * @category unsafe
30
+ * @since 0.0.0
31
+ */
32
+ export declare const fromNullableOrThrow: <A>(value: A, variableName?: string) => NonNullable<A>;
33
+ /**
34
+ * Branches on whether `value` is nullish, passing the value narrowed to
35
+ * `NonNullable<A>` to the `whenNotNullable` handler.
36
+ *
37
+ * A nullable-aware sibling of `Match.value`: it folds a present-or-absent value
38
+ * into a single `B` without an `if`/`else` or a manual `!= null` check. Note that
39
+ * falsy-but-present values (`""`, `0`, `false`) take the `whenNotNullable`
40
+ * branch — only `null` and `undefined` are treated as absent. Supports both
41
+ * data-first and data-last (pipeable) call styles.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
46
+ * import { pipe } from "effect"
47
+ *
48
+ * // Data-first — present value flows through narrowed
49
+ * assert.deepStrictEqual(
50
+ * NonNullableX.match("value", {
51
+ * whenNullable: () => "nullable",
52
+ * whenNotNullable: (value) => value
53
+ * }),
54
+ * "value"
55
+ * )
56
+ *
57
+ * // Data-last — null takes the whenNullable branch
58
+ * assert.deepStrictEqual(
59
+ * pipe(
60
+ * null,
61
+ * NonNullableX.match({
62
+ * whenNullable: () => "nullable",
63
+ * whenNotNullable: (value) => value
64
+ * })
65
+ * ),
66
+ * "nullable"
67
+ * )
68
+ * ```
69
+ *
70
+ * @category pattern matching
71
+ * @since 0.0.0
72
+ */
73
+ export declare const match: (<A, B>(handlers: {
74
+ whenNullable: () => B;
75
+ whenNotNullable: (value: NonNullable<A>) => B;
76
+ }) => (value: A) => B) & (<A, B>(value: A, handlers: {
77
+ whenNullable: () => B;
78
+ whenNotNullable: (value: NonNullable<A>) => B;
79
+ }) => B);
80
+ /**
81
+ * Applies `map` to `a` only when it is non-nullish, passing nullish inputs
82
+ * through unchanged.
83
+ *
84
+ * This is the nullable-preserving map: a present value is transformed to `B`,
85
+ * while `null` stays `null` and `undefined` stays `undefined`, so nullability is
86
+ * carried through the transformation. Supports both data-first and data-last
87
+ * (pipeable) call styles.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
92
+ * import { pipe } from "effect"
93
+ *
94
+ * // Data-first — present value is transformed
95
+ * assert.deepStrictEqual(NonNullableX.map(1, (v: number) => v + 1), 2)
96
+ *
97
+ * // Data-last — nullish passes through unchanged
98
+ * const value: number | null = null
99
+ * assert.deepStrictEqual(
100
+ * pipe(
101
+ * value,
102
+ * NonNullableX.map<number | null, number>((v) => v + 1)
103
+ * ),
104
+ * null
105
+ * )
106
+ * ```
107
+ *
108
+ * @category mapping
109
+ * @since 0.0.0
110
+ */
111
+ export declare const map: (<A, B>(map: (a: NonNullable<A>) => B) => (a: A) => B | (null & A) | (undefined & A)) & (<A, B>(a: A, map: (a: NonNullable<A>) => B) => B | (null & A) | (undefined & A));
112
+ /**
113
+ * Lifts a total function `(a: A) => B` into one that tolerates nullish input,
114
+ * applying it to present values and passing `null`/`undefined` through unchanged.
115
+ *
116
+ * Where `map` operates on a value, `lift` transforms the function itself,
117
+ * yielding a reusable `(a: A | null | undefined) => B | null | undefined` you can
118
+ * drop into a `pipe`. Use it to adapt a plain transform to a nullable pipeline
119
+ * without wrapping each call site.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
124
+ * import { Number, pipe } from "effect"
125
+ *
126
+ * const addOne = NonNullableX.lift(Number.sum(1))
127
+ *
128
+ * assert.deepStrictEqual(pipe(1, addOne), 2)
129
+ * assert.deepStrictEqual(pipe(null, addOne), null)
130
+ * assert.deepStrictEqual(pipe(undefined, addOne), undefined)
131
+ * ```
132
+ *
133
+ * @category mapping
134
+ * @since 0.0.0
135
+ */
136
+ export declare const lift: <A, B>(map: (a: A) => B) => (a: A | null | undefined) => B | null | undefined;
137
+ /**
138
+ * Extends an `Order.Order<A>` to an `Order.Order<A | null>`, deciding where
139
+ * `null` sorts relative to present values via the `behavior` argument.
140
+ *
141
+ * Pass `"value-null"` to push `null`s to the end and `"null-value"` to pull them
142
+ * to the front; two present values fall back to the wrapped order. Use it to sort
143
+ * collections that mix real values with gaps without a bespoke comparator.
144
+ * Supports both data-first and data-last (pipeable) call styles.
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
149
+ * import { Array, Order, pipe } from "effect"
150
+ *
151
+ * // "value-null" — nulls sorted last
152
+ * assert.deepStrictEqual(
153
+ * pipe(
154
+ * [null, 1, 3, null, 2],
155
+ * Array.sort(NonNullableX.nullableOrder(Order.Number, "value-null"))
156
+ * ),
157
+ * [1, 2, 3, null, null]
158
+ * )
159
+ *
160
+ * // "null-value" — nulls sorted first (data-last)
161
+ * assert.deepStrictEqual(
162
+ * pipe(
163
+ * [null, 1, 3, null, 2],
164
+ * Array.sort(pipe(Order.Number, NonNullableX.nullableOrder("null-value")))
165
+ * ),
166
+ * [null, null, 1, 2, 3]
167
+ * )
168
+ * ```
169
+ *
170
+ * @category ordering
171
+ * @since 0.0.0
172
+ */
173
+ export declare const nullableOrder: ((behavior: "value-null" | "null-value") => <A>(order: Order.Order<A>) => Order.Order<A | null>) & (<A>(order: Order.Order<A>, behavior: "value-null" | "null-value") => Order.Order<A | null>);
174
+ //# sourceMappingURL=NonNullableX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NonNullableX.d.ts","sourceRoot":"","sources":["../src/NonNullableX.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAGL,KAAK,EAGN,MAAM,QAAQ,CAAC;AAGhB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,OAAO,CAAC,EACR,eAAe,MAAM,KACpB,WAAW,CAAC,CAAC,CAOf,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,KAAK,IACf,CAAC,EAAE,CAAC,YAAY;IACf,YAAY,EAAE,MAAM,CAAC,CAAC;IACtB,eAAe,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;CAC/C,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,MACpB,CAAC,EAAE,CAAC,SACI,CAAC,YACE;IACR,YAAY,EAAE,MAAM,CAAC,CAAC;IACtB,eAAe,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;CAC/C,KACE,CAAC,CAWP,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,GAAG,IACb,CAAC,EAAE,CAAC,OACE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,KAC1B,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,MAC9C,CAAC,EAAE,CAAC,KACA,CAAC,OACC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,KAC1B,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAiBtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,IAAI,GACd,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MACtB,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,CAAC,GAAG,IAAI,GAAG,SAMrC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,aAAa,cAEZ,YAAY,GAAG,YAAY,KAClC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,MACvD,CAAC,SACO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,YACX,YAAY,GAAG,YAAY,KAClC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAwC3B,CAAC"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Helpers for working with non-nullable values.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Number as EffectNumber, Match, Predicate } from "effect";
7
+ import { dual } from "effect/Function";
8
+ /**
9
+ * Returns `value` narrowed to `NonNullable<A>`, throwing an `Error` if it is
10
+ * `null` or `undefined`.
11
+ *
12
+ * Use it at trusted boundaries where a value is known to be present but typed as
13
+ * nullable, turning a silent `undefined` into a loud failure. An optional
14
+ * `variableName` is woven into the thrown message to aid debugging. This is the
15
+ * function re-exported as `nn` from the module barrel, a terse shorthand handy
16
+ * inside string interpolations.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
21
+ *
22
+ * assert.deepStrictEqual(NonNullableX.fromNullableOrThrow("value"), "value")
23
+ *
24
+ * assert.throws(
25
+ * () => NonNullableX.fromNullableOrThrow(null, "varName"),
26
+ * /Value is nullable: null \(variable name: varName\)/
27
+ * )
28
+ * ```
29
+ *
30
+ * @category unsafe
31
+ * @since 0.0.0
32
+ */
33
+ export const fromNullableOrThrow = (value, variableName) => {
34
+ if (Predicate.isNotNullish(value)) {
35
+ return value;
36
+ }
37
+ throw new Error(`Value is nullable: ${String(value)}${Predicate.isNotNullish(variableName) ? ` (variable name: ${variableName})` : ""}`);
38
+ };
39
+ /**
40
+ * Branches on whether `value` is nullish, passing the value narrowed to
41
+ * `NonNullable<A>` to the `whenNotNullable` handler.
42
+ *
43
+ * A nullable-aware sibling of `Match.value`: it folds a present-or-absent value
44
+ * into a single `B` without an `if`/`else` or a manual `!= null` check. Note that
45
+ * falsy-but-present values (`""`, `0`, `false`) take the `whenNotNullable`
46
+ * branch — only `null` and `undefined` are treated as absent. Supports both
47
+ * data-first and data-last (pipeable) call styles.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
52
+ * import { pipe } from "effect"
53
+ *
54
+ * // Data-first — present value flows through narrowed
55
+ * assert.deepStrictEqual(
56
+ * NonNullableX.match("value", {
57
+ * whenNullable: () => "nullable",
58
+ * whenNotNullable: (value) => value
59
+ * }),
60
+ * "value"
61
+ * )
62
+ *
63
+ * // Data-last — null takes the whenNullable branch
64
+ * assert.deepStrictEqual(
65
+ * pipe(
66
+ * null,
67
+ * NonNullableX.match({
68
+ * whenNullable: () => "nullable",
69
+ * whenNotNullable: (value) => value
70
+ * })
71
+ * ),
72
+ * "nullable"
73
+ * )
74
+ * ```
75
+ *
76
+ * @category pattern matching
77
+ * @since 0.0.0
78
+ */
79
+ export const match = /*#__PURE__*/dual(2, (value, {
80
+ whenNullable,
81
+ whenNotNullable
82
+ }) => Predicate.isNotNullish(value) ? whenNotNullable(value) : whenNullable());
83
+ /**
84
+ * Applies `map` to `a` only when it is non-nullish, passing nullish inputs
85
+ * through unchanged.
86
+ *
87
+ * This is the nullable-preserving map: a present value is transformed to `B`,
88
+ * while `null` stays `null` and `undefined` stays `undefined`, so nullability is
89
+ * carried through the transformation. Supports both data-first and data-last
90
+ * (pipeable) call styles.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
95
+ * import { pipe } from "effect"
96
+ *
97
+ * // Data-first — present value is transformed
98
+ * assert.deepStrictEqual(NonNullableX.map(1, (v: number) => v + 1), 2)
99
+ *
100
+ * // Data-last — nullish passes through unchanged
101
+ * const value: number | null = null
102
+ * assert.deepStrictEqual(
103
+ * pipe(
104
+ * value,
105
+ * NonNullableX.map<number | null, number>((v) => v + 1)
106
+ * ),
107
+ * null
108
+ * )
109
+ * ```
110
+ *
111
+ * @category mapping
112
+ * @since 0.0.0
113
+ */
114
+ export const map = /*#__PURE__*/dual(2, (a, map) => {
115
+ if (Predicate.isNotNullish(a)) {
116
+ return map(a);
117
+ }
118
+ if (Predicate.isNullish(a)) {
119
+ return a;
120
+ }
121
+ throw new Error(`Value is neither nullable nor non-nullable: ${String(a)}`);
122
+ });
123
+ /**
124
+ * Lifts a total function `(a: A) => B` into one that tolerates nullish input,
125
+ * applying it to present values and passing `null`/`undefined` through unchanged.
126
+ *
127
+ * Where `map` operates on a value, `lift` transforms the function itself,
128
+ * yielding a reusable `(a: A | null | undefined) => B | null | undefined` you can
129
+ * drop into a `pipe`. Use it to adapt a plain transform to a nullable pipeline
130
+ * without wrapping each call site.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
135
+ * import { Number, pipe } from "effect"
136
+ *
137
+ * const addOne = NonNullableX.lift(Number.sum(1))
138
+ *
139
+ * assert.deepStrictEqual(pipe(1, addOne), 2)
140
+ * assert.deepStrictEqual(pipe(null, addOne), null)
141
+ * assert.deepStrictEqual(pipe(undefined, addOne), undefined)
142
+ * ```
143
+ *
144
+ * @category mapping
145
+ * @since 0.0.0
146
+ */
147
+ export const lift = map => a => {
148
+ if (Predicate.isNullish(a)) {
149
+ return a;
150
+ }
151
+ return map(a);
152
+ };
153
+ /**
154
+ * Extends an `Order.Order<A>` to an `Order.Order<A | null>`, deciding where
155
+ * `null` sorts relative to present values via the `behavior` argument.
156
+ *
157
+ * Pass `"value-null"` to push `null`s to the end and `"null-value"` to pull them
158
+ * to the front; two present values fall back to the wrapped order. Use it to sort
159
+ * collections that mix real values with gaps without a bespoke comparator.
160
+ * Supports both data-first and data-last (pipeable) call styles.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * import { NonNullableX } from "@nunofyobiz/effect-extras"
165
+ * import { Array, Order, pipe } from "effect"
166
+ *
167
+ * // "value-null" — nulls sorted last
168
+ * assert.deepStrictEqual(
169
+ * pipe(
170
+ * [null, 1, 3, null, 2],
171
+ * Array.sort(NonNullableX.nullableOrder(Order.Number, "value-null"))
172
+ * ),
173
+ * [1, 2, 3, null, null]
174
+ * )
175
+ *
176
+ * // "null-value" — nulls sorted first (data-last)
177
+ * assert.deepStrictEqual(
178
+ * pipe(
179
+ * [null, 1, 3, null, 2],
180
+ * Array.sort(pipe(Order.Number, NonNullableX.nullableOrder("null-value")))
181
+ * ),
182
+ * [null, null, 1, 2, 3]
183
+ * )
184
+ * ```
185
+ *
186
+ * @category ordering
187
+ * @since 0.0.0
188
+ */
189
+ export const nullableOrder = /*#__PURE__*/dual(2, (order, behavior) => {
190
+ // Prepare to sort them based on their nullability
191
+ const {
192
+ nullableSortCategory,
193
+ valueSortCategory
194
+ } = Match.value(behavior).pipe(Match.when("value-null", () => ({
195
+ nullableSortCategory: 1,
196
+ valueSortCategory: 0
197
+ })), Match.when("null-value", () => ({
198
+ nullableSortCategory: 0,
199
+ valueSortCategory: 1
200
+ })), Match.exhaustive);
201
+ return (a, b) => {
202
+ // Right off the bat, if they are both defined just return the regular ordering
203
+ if (Predicate.isNotNullish(a) && Predicate.isNotNullish(b)) {
204
+ return order(a, b);
205
+ }
206
+ // Otherwise figure out which category each value is
207
+ const aCategory = Predicate.isNotNullish(a) ? valueSortCategory : nullableSortCategory;
208
+ const bCategory = Predicate.isNotNullish(b) ? valueSortCategory : nullableSortCategory;
209
+ return EffectNumber.sign(aCategory - bCategory);
210
+ };
211
+ });
212
+ //# sourceMappingURL=NonNullableX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NonNullableX.js","names":["Number","EffectNumber","Match","Predicate","dual","fromNullableOrThrow","value","variableName","isNotNullish","Error","String","match","whenNullable","whenNotNullable","map","a","isNullish","lift","nullableOrder","order","behavior","nullableSortCategory","valueSortCategory","pipe","when","exhaustive","b","aCategory","bCategory","sign"],"sources":["../src/NonNullableX.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `Number` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Option } from "effect";
7
+ /**
8
+ * Computes the logarithm of `number` in the given `base`, throwing when the
9
+ * inputs fall outside the domain of `log`.
10
+ *
11
+ * Throws when `number <= 0`, when `base` is `<= 0` or `1`, or when `number` and
12
+ * `base` sit on opposite sides of `1` (a fractional base with `number >= 1`, or
13
+ * a base `>= 1` with `number < 1`) — cases where the real logarithm is
14
+ * undefined or non-finite. Reach for it only when the inputs are already known
15
+ * to be valid.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { pipe } from "effect"
20
+ * import { NumberX } from "@nunofyobiz/effect-extras"
21
+ *
22
+ * // data-first
23
+ * assert.deepStrictEqual(NumberX.unsafeLogBase(8, 2), 3)
24
+ * assert.deepStrictEqual(NumberX.unsafeLogBase(100, 10), 2)
25
+ *
26
+ * // data-last (piped)
27
+ * assert.deepStrictEqual(pipe(8, NumberX.unsafeLogBase(2)), 3)
28
+ *
29
+ * // throws outside the domain of log
30
+ * assert.throws(() => NumberX.unsafeLogBase(0, 2))
31
+ * ```
32
+ *
33
+ * @category unsafe
34
+ * @since 0.0.0
35
+ */
36
+ export declare const unsafeLogBase: ((base: number) => (number: number) => number) & ((number: number, base: number) => number);
37
+ /**
38
+ * Expresses `numerator` as a percentage of `total`, throwing on division by
39
+ * zero.
40
+ *
41
+ * Returns `(numerator / total) * 100`. When `total` is `0` the percentage is
42
+ * undefined, so this throws rather than returning `Infinity` or `NaN` — use it
43
+ * only when `total` is known to be non-zero.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { pipe } from "effect"
48
+ * import { NumberX } from "@nunofyobiz/effect-extras"
49
+ *
50
+ * // data-first
51
+ * assert.deepStrictEqual(NumberX.unsafeToPercentOf(1, 2), 50)
52
+ * assert.deepStrictEqual(NumberX.unsafeToPercentOf(2, 1), 200)
53
+ *
54
+ * // data-last (piped)
55
+ * assert.deepStrictEqual(pipe(1, NumberX.unsafeToPercentOf(2)), 50)
56
+ *
57
+ * // throws on division by zero
58
+ * assert.throws(() => NumberX.unsafeToPercentOf(1, 0))
59
+ * ```
60
+ *
61
+ * @category unsafe
62
+ * @since 0.0.0
63
+ */
64
+ export declare const unsafeToPercentOf: ((total: number) => (numerator: number) => number) & ((numerator: number, total: number) => number);
65
+ /**
66
+ * Formats `number` with a fixed number of decimal places, returning a `string`.
67
+ *
68
+ * A pipeable wrapper around `Number.prototype.toFixed` — the result is rounded
69
+ * (not truncated) to `numberDigits` decimals and always carries exactly that
70
+ * many digits after the point.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * import { pipe } from "effect"
75
+ * import { NumberX } from "@nunofyobiz/effect-extras"
76
+ *
77
+ * // data-first
78
+ * assert.deepStrictEqual(NumberX.toFixed(3.236242, 2), "3.24")
79
+ *
80
+ * // data-last (piped)
81
+ * assert.deepStrictEqual(pipe(3.236242, NumberX.toFixed(0)), "3")
82
+ * ```
83
+ *
84
+ * @category conversions
85
+ * @since 0.0.0
86
+ */
87
+ export declare const toFixed: ((numberDigits: number) => (number: number) => string) & ((number: number, numberDigits: number) => string);
88
+ /**
89
+ * Rounds `number` to a fixed number of decimal places, returning a `number`.
90
+ *
91
+ * Unlike {@link toFixed}, the result stays a `number` (no trailing zeroes) — it
92
+ * formats via `toFixed` then parses back, which sidesteps the usual
93
+ * floating-point rounding artifacts. See
94
+ * https://stackoverflow.com/a/29494612/22875620.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * import { pipe } from "effect"
99
+ * import { NumberX } from "@nunofyobiz/effect-extras"
100
+ *
101
+ * // data-first
102
+ * assert.deepStrictEqual(NumberX.roundToDigits(3.236242, 2), 3.24)
103
+ *
104
+ * // data-last (piped)
105
+ * assert.deepStrictEqual(pipe(3.736242, NumberX.roundToDigits(0)), 4)
106
+ * ```
107
+ *
108
+ * @category mapping
109
+ * @since 0.0.0
110
+ */
111
+ export declare const roundToDigits: ((numberDigits: number) => (number: number) => number) & ((number: number, numberDigits: number) => number);
112
+ /**
113
+ * Renders `number` as a `string`, left-padded with zeroes to at least
114
+ * `numberDigits` characters.
115
+ *
116
+ * If the number's string representation is already as long as (or longer than)
117
+ * `numberDigits`, it is returned unchanged.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import { pipe } from "effect"
122
+ * import { NumberX } from "@nunofyobiz/effect-extras"
123
+ *
124
+ * // data-first
125
+ * assert.deepStrictEqual(NumberX.padLeftZeroes(1, 3), "001")
126
+ *
127
+ * // longer than the target width is returned unchanged
128
+ * assert.deepStrictEqual(NumberX.padLeftZeroes(1000, 3), "1000")
129
+ *
130
+ * // data-last (piped)
131
+ * assert.deepStrictEqual(pipe(10, NumberX.padLeftZeroes(3)), "010")
132
+ * ```
133
+ *
134
+ * @category conversions
135
+ * @since 0.0.0
136
+ */
137
+ export declare const padLeftZeroes: ((numberDigits: number) => (number: number) => string) & ((number: number, numberDigits: number) => string);
138
+ /**
139
+ * Converts a `0`-indexed value to its `1`-indexed rank by adding `1`.
140
+ *
141
+ * Handy for presentation where humans count from one (e.g. the element at
142
+ * index `0` is shown as "item 1").
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * import { NumberX } from "@nunofyobiz/effect-extras"
147
+ *
148
+ * assert.deepStrictEqual(NumberX.indexToRank(0), 1)
149
+ * assert.deepStrictEqual(NumberX.indexToRank(4), 5)
150
+ * ```
151
+ *
152
+ * @category mapping
153
+ * @since 0.0.0
154
+ */
155
+ export declare const indexToRank: (index: number) => number;
156
+ /**
157
+ * Converts a `0`-indexed column number into its bijective base-26 spreadsheet
158
+ * label (`A`, `B`, …, `Z`, `AA`, `AB`, …).
159
+ *
160
+ * Returns `None` for a negative `index`; every non-negative index maps to a
161
+ * `Some` label. Indexing is `0`-based here, so `0` is `"A"` and `25` is `"Z"`,
162
+ * unlike the `1`-based numbering shown in most spreadsheet references.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * import { Option } from "effect"
167
+ * import { NumberX } from "@nunofyobiz/effect-extras"
168
+ *
169
+ * assert.deepStrictEqual(NumberX.indexToExcel(0), Option.some("A"))
170
+ * assert.deepStrictEqual(NumberX.indexToExcel(26), Option.some("AA"))
171
+ * assert.deepStrictEqual(NumberX.indexToExcel(-1), Option.none())
172
+ * ```
173
+ *
174
+ * @category conversions
175
+ * @since 0.0.0
176
+ */
177
+ export declare const indexToExcel: (index: number) => Option.Option<string>;
178
+ //# sourceMappingURL=NumberX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberX.d.ts","sourceRoot":"","sources":["../src/NumberX.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAA0B,MAAM,EAAQ,MAAM,QAAQ,CAAC;AA6B9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,aAAa,UACjB,MAAM,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,cACnC,MAAM,QAAQ,MAAM,KAAK,MAAM,CAMzC,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iBAAiB,WAEpB,MAAM,KAAK,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,iBAEpC,MAAM,SAAS,MAAM,KAAK,MAAM,CAM7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,OAAO,kBACH,MAAM,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,cAC3C,MAAM,gBAAgB,MAAM,KAAK,MAAM,CAGjD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,aAAa,kBAET,MAAM,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,cAE3C,MAAM,gBAAgB,MAAM,KAAK,MAAM,CAGjD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,aAAa,kBACT,MAAM,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,cAC3C,MAAM,gBAAgB,MAAM,KAAK,MAAM,CAGjD,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,MAAmB,CAAC;AAIhE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAehE,CAAC"}