@nunofyobiz/effect-extras 2.0.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 (133) hide show
  1. package/README.md +38 -3
  2. package/dist/ArrayX.d.ts +381 -0
  3. package/dist/ArrayX.d.ts.map +1 -0
  4. package/dist/ArrayX.js +493 -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/InclusiveOr.d.ts +1123 -0
  27. package/dist/InclusiveOr.d.ts.map +1 -0
  28. package/dist/InclusiveOr.js +1074 -0
  29. package/dist/InclusiveOr.js.map +1 -0
  30. package/dist/MapX.d.ts +32 -0
  31. package/dist/MapX.d.ts.map +1 -0
  32. package/dist/MapX.js +49 -0
  33. package/dist/MapX.js.map +1 -0
  34. package/dist/NonNullableX.d.ts +174 -0
  35. package/dist/NonNullableX.d.ts.map +1 -0
  36. package/dist/NonNullableX.js +217 -0
  37. package/dist/NonNullableX.js.map +1 -0
  38. package/dist/NumberX.d.ts +178 -0
  39. package/dist/NumberX.d.ts.map +1 -0
  40. package/dist/NumberX.js +214 -0
  41. package/dist/NumberX.js.map +1 -0
  42. package/dist/OptionX.d.ts +187 -0
  43. package/dist/OptionX.d.ts.map +1 -0
  44. package/dist/OptionX.js +201 -0
  45. package/dist/OptionX.js.map +1 -0
  46. package/dist/OrderX.d.ts +32 -0
  47. package/dist/OrderX.d.ts.map +1 -0
  48. package/dist/OrderX.js +32 -0
  49. package/dist/OrderX.js.map +1 -0
  50. package/dist/PredicateX.d.ts +108 -0
  51. package/dist/PredicateX.d.ts.map +1 -0
  52. package/dist/PredicateX.js +111 -0
  53. package/dist/PredicateX.js.map +1 -0
  54. package/dist/PromiseX.d.ts +32 -0
  55. package/dist/PromiseX.d.ts.map +1 -0
  56. package/dist/PromiseX.js +32 -0
  57. package/dist/PromiseX.js.map +1 -0
  58. package/dist/RecordX.d.ts +450 -0
  59. package/dist/RecordX.d.ts.map +1 -0
  60. package/dist/RecordX.js +487 -0
  61. package/dist/RecordX.js.map +1 -0
  62. package/dist/ResultX.d.ts +50 -0
  63. package/dist/ResultX.d.ts.map +1 -0
  64. package/dist/ResultX.js +50 -0
  65. package/dist/ResultX.js.map +1 -0
  66. package/dist/SchemaX.d.ts +249 -0
  67. package/dist/SchemaX.d.ts.map +1 -0
  68. package/dist/SchemaX.js +243 -0
  69. package/dist/SchemaX.js.map +1 -0
  70. package/dist/SetX.d.ts +121 -0
  71. package/dist/SetX.d.ts.map +1 -0
  72. package/dist/SetX.js +137 -0
  73. package/dist/SetX.js.map +1 -0
  74. package/dist/StringX.d.ts +131 -0
  75. package/dist/StringX.d.ts.map +1 -0
  76. package/dist/StringX.js +149 -0
  77. package/dist/StringX.js.map +1 -0
  78. package/dist/StructX.d.ts +219 -0
  79. package/dist/StructX.d.ts.map +1 -0
  80. package/dist/StructX.js +173 -0
  81. package/dist/StructX.js.map +1 -0
  82. package/dist/WarnResult.d.ts +1191 -0
  83. package/dist/WarnResult.d.ts.map +1 -0
  84. package/dist/WarnResult.js +991 -0
  85. package/dist/WarnResult.js.map +1 -0
  86. package/dist/index.d.ts +23 -3772
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +22 -1011
  89. package/dist/index.js.map +1 -1
  90. package/package.json +18 -5
  91. package/src/{ArrayX/ArrayX.ts → ArrayX.ts} +6 -88
  92. package/src/{DurationX/DurationX.ts → DurationX.ts} +1 -1
  93. package/src/InclusiveOr.ts +1255 -0
  94. package/src/{NonNullableX/NonNullableX.ts → NonNullableX.ts} +5 -0
  95. package/src/{OptionX/OptionX.ts → OptionX.ts} +8 -2
  96. package/src/{PredicateX/PredicateX.ts → PredicateX.ts} +41 -0
  97. package/src/{RecordX/RecordX.ts → RecordX.ts} +184 -2
  98. package/src/StringX.ts +210 -0
  99. package/src/{WarnResult/WarnResult.ts → WarnResult.ts} +297 -227
  100. package/src/index.ts +22 -20
  101. package/src/ArrayX/index.ts +0 -1
  102. package/src/BigIntX/index.ts +0 -1
  103. package/src/BooleanX/index.ts +0 -1
  104. package/src/DurationX/index.ts +0 -1
  105. package/src/EffectX/index.ts +0 -1
  106. package/src/FormDataX/index.ts +0 -1
  107. package/src/MapX/index.ts +0 -1
  108. package/src/NonNullableX/index.ts +0 -2
  109. package/src/NumberX/index.ts +0 -1
  110. package/src/OptionX/index.ts +0 -1
  111. package/src/OrderX/index.ts +0 -1
  112. package/src/PredicateX/index.ts +0 -1
  113. package/src/PromiseX/index.ts +0 -1
  114. package/src/RecordX/index.ts +0 -1
  115. package/src/ResultX/index.ts +0 -1
  116. package/src/SchemaX/index.ts +0 -1
  117. package/src/SetX/index.ts +0 -1
  118. package/src/StringX/StringX.ts +0 -97
  119. package/src/StringX/index.ts +0 -1
  120. package/src/StructX/index.ts +0 -1
  121. package/src/WarnResult/index.ts +0 -1
  122. /package/src/{BigIntX/BigIntX.ts → BigIntX.ts} +0 -0
  123. /package/src/{BooleanX/BooleanX.ts → BooleanX.ts} +0 -0
  124. /package/src/{EffectX/EffectX.ts → EffectX.ts} +0 -0
  125. /package/src/{FormDataX/FormDataX.ts → FormDataX.ts} +0 -0
  126. /package/src/{MapX/MapX.ts → MapX.ts} +0 -0
  127. /package/src/{NumberX/NumberX.ts → NumberX.ts} +0 -0
  128. /package/src/{OrderX/OrderX.ts → OrderX.ts} +0 -0
  129. /package/src/{PromiseX/PromiseX.ts → PromiseX.ts} +0 -0
  130. /package/src/{ResultX/ResultX.ts → ResultX.ts} +0 -0
  131. /package/src/{SchemaX/SchemaX.ts → SchemaX.ts} +0 -0
  132. /package/src/{SetX/SetX.ts → SetX.ts} +0 -0
  133. /package/src/{StructX/StructX.ts → StructX.ts} +0 -0
@@ -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"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `Number` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Number as EffectNumber, Option, pipe } from "effect";
7
+ import { dual } from "effect/Function";
8
+ // Internal — used by unsafeLogBase.
9
+ const logBase = /*#__PURE__*/dual(2, (number, base) => {
10
+ if (number <= 0) {
11
+ return Option.none();
12
+ }
13
+ if (base <= 0 || base === 1) {
14
+ return Option.none();
15
+ }
16
+ if (base < 1 && number >= 1) {
17
+ return Option.none();
18
+ }
19
+ if (base >= 1 && number < 1) {
20
+ return Option.none();
21
+ }
22
+ return Option.some(Math.log(number) / Math.log(base));
23
+ });
24
+ /**
25
+ * Computes the logarithm of `number` in the given `base`, throwing when the
26
+ * inputs fall outside the domain of `log`.
27
+ *
28
+ * Throws when `number <= 0`, when `base` is `<= 0` or `1`, or when `number` and
29
+ * `base` sit on opposite sides of `1` (a fractional base with `number >= 1`, or
30
+ * a base `>= 1` with `number < 1`) — cases where the real logarithm is
31
+ * undefined or non-finite. Reach for it only when the inputs are already known
32
+ * to be valid.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * import { pipe } from "effect"
37
+ * import { NumberX } from "@nunofyobiz/effect-extras"
38
+ *
39
+ * // data-first
40
+ * assert.deepStrictEqual(NumberX.unsafeLogBase(8, 2), 3)
41
+ * assert.deepStrictEqual(NumberX.unsafeLogBase(100, 10), 2)
42
+ *
43
+ * // data-last (piped)
44
+ * assert.deepStrictEqual(pipe(8, NumberX.unsafeLogBase(2)), 3)
45
+ *
46
+ * // throws outside the domain of log
47
+ * assert.throws(() => NumberX.unsafeLogBase(0, 2))
48
+ * ```
49
+ *
50
+ * @category unsafe
51
+ * @since 0.0.0
52
+ */
53
+ export const unsafeLogBase = /*#__PURE__*/dual(2, (number, base) => Option.getOrThrowWith(logBase(number, base), () => new Error(`Error calculating log base ${base} of ${number}`)));
54
+ /**
55
+ * Converts a number to a percentage of some total.
56
+ *
57
+ * Internal — used by unsafeToPercentOf.
58
+ */
59
+ const toPercentOf = /*#__PURE__*/dual(2, (numerator, total) => pipe(EffectNumber.divide(numerator, total), Option.map(ratio => ratio * 100)));
60
+ /**
61
+ * Expresses `numerator` as a percentage of `total`, throwing on division by
62
+ * zero.
63
+ *
64
+ * Returns `(numerator / total) * 100`. When `total` is `0` the percentage is
65
+ * undefined, so this throws rather than returning `Infinity` or `NaN` — use it
66
+ * only when `total` is known to be non-zero.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { pipe } from "effect"
71
+ * import { NumberX } from "@nunofyobiz/effect-extras"
72
+ *
73
+ * // data-first
74
+ * assert.deepStrictEqual(NumberX.unsafeToPercentOf(1, 2), 50)
75
+ * assert.deepStrictEqual(NumberX.unsafeToPercentOf(2, 1), 200)
76
+ *
77
+ * // data-last (piped)
78
+ * assert.deepStrictEqual(pipe(1, NumberX.unsafeToPercentOf(2)), 50)
79
+ *
80
+ * // throws on division by zero
81
+ * assert.throws(() => NumberX.unsafeToPercentOf(1, 0))
82
+ * ```
83
+ *
84
+ * @category unsafe
85
+ * @since 0.0.0
86
+ */
87
+ export const unsafeToPercentOf = /*#__PURE__*/dual(2, (numerator, total) => Option.getOrThrowWith(toPercentOf(numerator, total), () => new Error(`Division by zero when dividing ${numerator} by ${total}`)));
88
+ /**
89
+ * Formats `number` with a fixed number of decimal places, returning a `string`.
90
+ *
91
+ * A pipeable wrapper around `Number.prototype.toFixed` — the result is rounded
92
+ * (not truncated) to `numberDigits` decimals and always carries exactly that
93
+ * many digits after the point.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * import { pipe } from "effect"
98
+ * import { NumberX } from "@nunofyobiz/effect-extras"
99
+ *
100
+ * // data-first
101
+ * assert.deepStrictEqual(NumberX.toFixed(3.236242, 2), "3.24")
102
+ *
103
+ * // data-last (piped)
104
+ * assert.deepStrictEqual(pipe(3.236242, NumberX.toFixed(0)), "3")
105
+ * ```
106
+ *
107
+ * @category conversions
108
+ * @since 0.0.0
109
+ */
110
+ export const toFixed = /*#__PURE__*/dual(2, (number, numberDigits) => number.toFixed(numberDigits));
111
+ /**
112
+ * Rounds `number` to a fixed number of decimal places, returning a `number`.
113
+ *
114
+ * Unlike {@link toFixed}, the result stays a `number` (no trailing zeroes) — it
115
+ * formats via `toFixed` then parses back, which sidesteps the usual
116
+ * floating-point rounding artifacts. See
117
+ * https://stackoverflow.com/a/29494612/22875620.
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.roundToDigits(3.236242, 2), 3.24)
126
+ *
127
+ * // data-last (piped)
128
+ * assert.deepStrictEqual(pipe(3.736242, NumberX.roundToDigits(0)), 4)
129
+ * ```
130
+ *
131
+ * @category mapping
132
+ * @since 0.0.0
133
+ */
134
+ export const roundToDigits = /*#__PURE__*/dual(2, (number, numberDigits) => Number(number.toFixed(numberDigits)));
135
+ /**
136
+ * Renders `number` as a `string`, left-padded with zeroes to at least
137
+ * `numberDigits` characters.
138
+ *
139
+ * If the number's string representation is already as long as (or longer than)
140
+ * `numberDigits`, it is returned unchanged.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import { pipe } from "effect"
145
+ * import { NumberX } from "@nunofyobiz/effect-extras"
146
+ *
147
+ * // data-first
148
+ * assert.deepStrictEqual(NumberX.padLeftZeroes(1, 3), "001")
149
+ *
150
+ * // longer than the target width is returned unchanged
151
+ * assert.deepStrictEqual(NumberX.padLeftZeroes(1000, 3), "1000")
152
+ *
153
+ * // data-last (piped)
154
+ * assert.deepStrictEqual(pipe(10, NumberX.padLeftZeroes(3)), "010")
155
+ * ```
156
+ *
157
+ * @category conversions
158
+ * @since 0.0.0
159
+ */
160
+ export const padLeftZeroes = /*#__PURE__*/dual(2, (number, numberDigits) => number.toString().padStart(numberDigits, "0"));
161
+ /**
162
+ * Converts a `0`-indexed value to its `1`-indexed rank by adding `1`.
163
+ *
164
+ * Handy for presentation where humans count from one (e.g. the element at
165
+ * index `0` is shown as "item 1").
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * import { NumberX } from "@nunofyobiz/effect-extras"
170
+ *
171
+ * assert.deepStrictEqual(NumberX.indexToRank(0), 1)
172
+ * assert.deepStrictEqual(NumberX.indexToRank(4), 5)
173
+ * ```
174
+ *
175
+ * @category mapping
176
+ * @since 0.0.0
177
+ */
178
+ export const indexToRank = index => index + 1;
179
+ const EXCEL_COLUMNS_BASE_CHARS = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZ"];
180
+ /**
181
+ * Converts a `0`-indexed column number into its bijective base-26 spreadsheet
182
+ * label (`A`, `B`, …, `Z`, `AA`, `AB`, …).
183
+ *
184
+ * Returns `None` for a negative `index`; every non-negative index maps to a
185
+ * `Some` label. Indexing is `0`-based here, so `0` is `"A"` and `25` is `"Z"`,
186
+ * unlike the `1`-based numbering shown in most spreadsheet references.
187
+ *
188
+ * @example
189
+ * ```ts
190
+ * import { Option } from "effect"
191
+ * import { NumberX } from "@nunofyobiz/effect-extras"
192
+ *
193
+ * assert.deepStrictEqual(NumberX.indexToExcel(0), Option.some("A"))
194
+ * assert.deepStrictEqual(NumberX.indexToExcel(26), Option.some("AA"))
195
+ * assert.deepStrictEqual(NumberX.indexToExcel(-1), Option.none())
196
+ * ```
197
+ *
198
+ * @category conversions
199
+ * @since 0.0.0
200
+ */
201
+ export const indexToExcel = index => {
202
+ if (index < 0) {
203
+ return Option.none();
204
+ }
205
+ const baseChars = EXCEL_COLUMNS_BASE_CHARS;
206
+ let excel = "";
207
+ const base = baseChars.length;
208
+ do {
209
+ excel = baseChars[index % base] + excel;
210
+ index = Math.floor(index / base) - 1;
211
+ } while (index >= 0);
212
+ return Option.some(excel);
213
+ };
214
+ //# sourceMappingURL=NumberX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberX.js","names":["Number","EffectNumber","Option","pipe","dual","logBase","number","base","none","some","Math","log","unsafeLogBase","getOrThrowWith","Error","toPercentOf","numerator","total","divide","map","ratio","unsafeToPercentOf","toFixed","numberDigits","roundToDigits","padLeftZeroes","toString","padStart","indexToRank","index","EXCEL_COLUMNS_BASE_CHARS","indexToExcel","baseChars","excel","length","floor"],"sources":["../src/NumberX.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `Option` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Option } from "effect";
7
+ /**
8
+ * Combines two `Option`s into an `Option` of a tuple, succeeding only when both
9
+ * are `Some`.
10
+ *
11
+ * Returns `Some([a, b])` when both inputs are `Some`, and `None` if either is
12
+ * `None`. Useful when an operation needs two optional values present at once.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { Option, pipe } from "effect"
17
+ * import { OptionX } from "@nunofyobiz/effect-extras"
18
+ *
19
+ * // Both Some — succeeds with the pair
20
+ * assert.deepStrictEqual(
21
+ * OptionX.tupleOf(Option.some(1), Option.some("a")),
22
+ * Option.some([1, "a"]),
23
+ * )
24
+ *
25
+ * // Either None collapses to None
26
+ * assert.deepStrictEqual(
27
+ * OptionX.tupleOf(Option.some(1), Option.none()),
28
+ * Option.none(),
29
+ * )
30
+ *
31
+ * // Data-last (piped): the piped Option fills the first tuple slot
32
+ * assert.deepStrictEqual(
33
+ * pipe(Option.some(1), OptionX.tupleOf(Option.some("a"))),
34
+ * Option.some([1, "a"]),
35
+ * )
36
+ * ```
37
+ *
38
+ * @category combinators
39
+ * @since 0.0.0
40
+ */
41
+ export declare const tupleOf: (<B>(b: Option.Option<B>) => <A>(a: Option.Option<A>) => Option.Option<[A, B]>) & (<A, B>(a: Option.Option<A>, b: Option.Option<B>) => Option.Option<[A, B]>);
42
+ /**
43
+ * Runs a side effect with the value of an `Option` when it is `Some`, doing
44
+ * nothing when it is `None`.
45
+ *
46
+ * A shorthand for the "if Some, do something" branch of `Option.match` where the
47
+ * `None` case is a no-op. The callback's return value is ignored — `ifSome`
48
+ * always returns `void`.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { Option } from "effect"
53
+ * import { OptionX } from "@nunofyobiz/effect-extras"
54
+ *
55
+ * const log: Array<number> = []
56
+ * OptionX.ifSome(Option.some(1), (value) => log.push(value))
57
+ * OptionX.ifSome(Option.none<number>(), (value) => log.push(value))
58
+ *
59
+ * assert.deepStrictEqual(log, [1])
60
+ * ```
61
+ *
62
+ * @category sequencing
63
+ * @since 0.0.0
64
+ */
65
+ export declare const ifSome: (<A>(ifSome: (value: A) => void) => (self: Option.Option<A>) => void) & (<A>(self: Option.Option<A>, ifSome: (value: A) => void) => void);
66
+ /**
67
+ * Runs a side effect with the value of an `Option` when it is `Some`, then
68
+ * returns the `Option` unchanged.
69
+ *
70
+ * The pass-through counterpart of {@link ifSome}: it taps into a `Some` value
71
+ * (for logging, metrics, debugging) without breaking a `pipe` chain, since it
72
+ * returns the original `Option`. For `None` it is a no-op.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * import { Option, pipe } from "effect"
77
+ * import { OptionX } from "@nunofyobiz/effect-extras"
78
+ *
79
+ * const log: Array<number> = []
80
+ * const result = pipe(
81
+ * Option.some(1),
82
+ * OptionX.inspectSome((value) => log.push(value)),
83
+ * Option.map((value) => value + 1),
84
+ * )
85
+ *
86
+ * assert.deepStrictEqual(result, Option.some(2))
87
+ * assert.deepStrictEqual(log, [1])
88
+ * ```
89
+ *
90
+ * @category sequencing
91
+ * @since 0.0.0
92
+ */
93
+ export declare const inspectSome: (<A>(function_: (value: A) => void) => (self: Option.Option<A>) => Option.Option<A>) & (<A>(self: Option.Option<A>, function_: (value: A) => void) => Option.Option<A>);
94
+ /**
95
+ * Normalizes a possibly-nullish `Option` into a plain `Option`, mapping `null`
96
+ * and `undefined` to `None`.
97
+ *
98
+ * Handy at boundaries where an `Option` value might itself arrive as `null` or
99
+ * `undefined` (for example an optional field that holds an `Option`): the result
100
+ * is always a well-formed `Option`, never `null`/`undefined`.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * import { Option } from "effect"
105
+ * import { OptionX } from "@nunofyobiz/effect-extras"
106
+ *
107
+ * assert.deepStrictEqual(OptionX.fromNullableOption(Option.some(1)), Option.some(1))
108
+ * assert.deepStrictEqual(OptionX.fromNullableOption(null), Option.none())
109
+ * assert.deepStrictEqual(OptionX.fromNullableOption(undefined), Option.none())
110
+ * ```
111
+ *
112
+ * @category constructors
113
+ * @since 0.0.0
114
+ */
115
+ export declare const fromNullableOption: <A>(nullableOption: Option.Option<A> | null | undefined) => Option.Option<A>;
116
+ /**
117
+ * Maps the value of an `Option` when it is `Some`, returning `null` when it is
118
+ * `None`.
119
+ *
120
+ * A shorthand for `pipe(self, Option.map(map), Option.getOrNull)`. The `null`
121
+ * fallback makes it especially convenient in JSX/React, where rendering `null`
122
+ * skips output — `mapSomeOrNull(value, (v) => render(v))` replaces a more verbose
123
+ * `Option.match` with `onNone: () => null`.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { Option, pipe } from "effect"
128
+ * import { OptionX } from "@nunofyobiz/effect-extras"
129
+ *
130
+ * // data-first
131
+ * assert.deepStrictEqual(OptionX.mapSomeOrNull(Option.some(1), (v) => v + 1), 2)
132
+ *
133
+ * // None maps to null
134
+ * assert.deepStrictEqual(
135
+ * OptionX.mapSomeOrNull(Option.none<number>(), (v) => v + 1),
136
+ * null,
137
+ * )
138
+ *
139
+ * // data-last (piped)
140
+ * assert.deepStrictEqual(
141
+ * pipe(Option.some(1), OptionX.mapSomeOrNull((v) => v + 1)),
142
+ * 2,
143
+ * )
144
+ * ```
145
+ *
146
+ * @category mapping
147
+ * @since 0.0.0
148
+ */
149
+ export declare const mapSomeOrNull: (<A, B>(map: (a: A) => B) => (self: Option.Option<A>) => B | null) & (<A, B>(self: Option.Option<A>, map: (a: A) => B) => B | null);
150
+ /**
151
+ * Maps the value of an `Option` when it is `Some`, returning `undefined` when it
152
+ * is `None`.
153
+ *
154
+ * The `undefined`-returning counterpart of {@link mapSomeOrNull}: a shorthand for
155
+ * `pipe(self, Option.map(map), Option.getOrUndefined)`. Reach for it when the
156
+ * consuming API expects `undefined` rather than `null` for "absent" (for example
157
+ * an optional prop or a value spread into an object).
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * import { Option, pipe } from "effect"
162
+ * import { OptionX } from "@nunofyobiz/effect-extras"
163
+ *
164
+ * // data-first
165
+ * assert.deepStrictEqual(
166
+ * OptionX.mapSomeOrUndefined(Option.some(1), (v) => v + 1),
167
+ * 2,
168
+ * )
169
+ *
170
+ * // None maps to undefined
171
+ * assert.deepStrictEqual(
172
+ * OptionX.mapSomeOrUndefined(Option.none<number>(), (v) => v + 1),
173
+ * undefined,
174
+ * )
175
+ *
176
+ * // data-last (piped)
177
+ * assert.deepStrictEqual(
178
+ * pipe(Option.some(1), OptionX.mapSomeOrUndefined((v) => v + 1)),
179
+ * 2,
180
+ * )
181
+ * ```
182
+ *
183
+ * @category mapping
184
+ * @since 0.0.0
185
+ */
186
+ export declare const mapSomeOrUndefined: (<A, B>(map: (a: A) => B) => (self: Option.Option<A>) => B | undefined) & (<A, B>(self: Option.Option<A>, map: (a: A) => B) => B | undefined);
187
+ //# sourceMappingURL=OptionX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OptionX.d.ts","sourceRoot":"","sources":["../src/OptionX.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAmB,MAAM,QAAQ,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,OAAO,IACjB,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAC5E,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAK1E,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,MAAM,IAChB,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,MAClE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,CAW/D,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,WAAW,IACrB,CAAC,aACW,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAC1B,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAChD,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAU/E,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAClC,gBAAgB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,KAClD,MAAM,CAAC,MAAM,CAAC,CAAC,CACuD,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,aAAa,IACvB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,MAC/D,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAG7D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,kBAAkB,IAC5B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,SAAS,MACpE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS,CAGlE,CAAC"}