@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
package/dist/SetX.js ADDED
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions for working with `Set`.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { dual } from "effect/Function";
7
+ /**
8
+ * Runs a mutating function against a copy of `set`, leaving the original
9
+ * untouched, and returns the mutated copy.
10
+ *
11
+ * Use it to reuse imperative `Set` mutation code (`.add`, `.delete`) without
12
+ * sacrificing immutability: the callback may freely mutate the set it receives
13
+ * because it operates on a fresh clone. Supports both data-first and data-last
14
+ * (pipeable) call styles.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { SetX } from "@nunofyobiz/effect-extras"
19
+ * import { pipe } from "effect"
20
+ *
21
+ * const original = new Set(["a", "b"])
22
+ *
23
+ * const result = pipe(
24
+ * original,
25
+ * SetX.safelyMutate((set) => {
26
+ * set.delete("a")
27
+ * return set.add("c")
28
+ * })
29
+ * )
30
+ *
31
+ * assert.deepStrictEqual(result, new Set(["b", "c"]))
32
+ * // The original is left intact
33
+ * assert.deepStrictEqual(original, new Set(["a", "b"]))
34
+ * ```
35
+ *
36
+ * @category combinators
37
+ * @since 0.0.0
38
+ */
39
+ export const safelyMutate = /*#__PURE__*/dual(2, (set, mutate) => {
40
+ const copy = new Set(set);
41
+ return mutate(copy);
42
+ });
43
+ /**
44
+ * Returns a new `Set` with `value` added, leaving the input set unchanged.
45
+ *
46
+ * When `value` is already present the input set is returned as-is (no copy),
47
+ * making repeated adds of existing members allocation-free. Supports both
48
+ * data-first and data-last (pipeable) call styles.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { SetX } from "@nunofyobiz/effect-extras"
53
+ * import { pipe } from "effect"
54
+ *
55
+ * // Data-first — adds a new element into a fresh set
56
+ * assert.deepStrictEqual(
57
+ * SetX.add(new Set(["a", "b"]), "c"),
58
+ * new Set(["a", "b", "c"])
59
+ * )
60
+ *
61
+ * // Data-last — existing element leaves the set unchanged
62
+ * assert.deepStrictEqual(
63
+ * pipe(new Set(["a", "b"]), SetX.add("b")),
64
+ * new Set(["a", "b"])
65
+ * )
66
+ * ```
67
+ *
68
+ * @category combinators
69
+ * @since 0.0.0
70
+ */
71
+ export const add = /*#__PURE__*/dual(2, (set, value) => set.has(value) ? set : new Set(set).add(value));
72
+ /**
73
+ * Returns a new `Set` with `value` removed, leaving the input set unchanged.
74
+ *
75
+ * When `value` is absent the input set is returned as-is (no copy). Supports both
76
+ * data-first and data-last (pipeable) call styles.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { SetX } from "@nunofyobiz/effect-extras"
81
+ * import { pipe } from "effect"
82
+ *
83
+ * // Data-first — removes an existing element into a fresh set
84
+ * assert.deepStrictEqual(
85
+ * SetX.remove(new Set(["a", "b", "c"]), "c"),
86
+ * new Set(["a", "b"])
87
+ * )
88
+ *
89
+ * // Data-last — absent element leaves the set unchanged
90
+ * assert.deepStrictEqual(
91
+ * pipe(new Set(["a", "b"]), SetX.remove("z")),
92
+ * new Set(["a", "b"])
93
+ * )
94
+ * ```
95
+ *
96
+ * @category combinators
97
+ * @since 0.0.0
98
+ */
99
+ export const remove = /*#__PURE__*/dual(2, (set, value) => {
100
+ if (set.has(value)) {
101
+ const newSet = new Set(set);
102
+ newSet.delete(value);
103
+ return newSet;
104
+ }
105
+ return set;
106
+ });
107
+ /**
108
+ * Returns a new `Set` with `value` added if it was absent or removed if it was
109
+ * present, leaving the input set unchanged.
110
+ *
111
+ * Use it for membership toggles (selection state, feature flags by key) where a
112
+ * value's presence should flip on each call. Supports both data-first and
113
+ * data-last (pipeable) call styles.
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * import { SetX } from "@nunofyobiz/effect-extras"
118
+ * import { pipe } from "effect"
119
+ *
120
+ * // Data-first — absent value gets added
121
+ * assert.deepStrictEqual(
122
+ * SetX.toggle(new Set(["a", "b"]), "c"),
123
+ * new Set(["a", "b", "c"])
124
+ * )
125
+ *
126
+ * // Data-last — present value gets removed
127
+ * assert.deepStrictEqual(
128
+ * pipe(new Set(["a", "b", "c"]), SetX.toggle("b")),
129
+ * new Set(["a", "c"])
130
+ * )
131
+ * ```
132
+ *
133
+ * @category combinators
134
+ * @since 0.0.0
135
+ */
136
+ export const toggle = /*#__PURE__*/dual(2, (set, value) => set.has(value) ? remove(set, value) : add(set, value));
137
+ //# sourceMappingURL=SetX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SetX.js","names":["dual","safelyMutate","set","mutate","copy","Set","add","value","has","remove","newSet","delete","toggle"],"sources":["../src/SetX.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;AAKA,SAASA,IAAI,QAAQ,iBAAiB;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,OAAO,MAAMC,YAAY,gBAAGD,IAAI,CAG9B,CAAC,EAAE,CAAIE,GAAW,EAAEC,MAA+B,KAAY;EAC/D,MAAMC,IAAI,GAAG,IAAIC,GAAG,CAACH,GAAG,CAAC;EACzB,OAAOC,MAAM,CAACC,IAAI,CAAC;AACrB,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,OAAO,MAAME,GAAG,gBAAGN,IAAI,CAIrB,CAAC,EACD,CAAIE,GAAW,EAAEK,KAAQ,KACvBL,GAAG,CAACM,GAAG,CAACD,KAAK,CAAC,GAAGL,GAAG,GAAG,IAAIG,GAAG,CAACH,GAAG,CAAC,CAACI,GAAG,CAACC,KAAK,CAAC,CACjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,OAAO,MAAME,MAAM,gBAAGT,IAAI,CAGxB,CAAC,EAAE,CAAIE,GAAW,EAAEK,KAAQ,KAAY;EACxC,IAAIL,GAAG,CAACM,GAAG,CAACD,KAAK,CAAC,EAAE;IAClB,MAAMG,MAAM,GAAG,IAAIL,GAAG,CAACH,GAAG,CAAC;IAC3BQ,MAAM,CAACC,MAAM,CAACJ,KAAK,CAAC;IACpB,OAAOG,MAAM;EACf;EACA,OAAOR,GAAG;AACZ,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,OAAO,MAAMU,MAAM,gBAAGZ,IAAI,CAIxB,CAAC,EACD,CAAIE,GAAW,EAAEK,KAAQ,KACvBL,GAAG,CAACM,GAAG,CAACD,KAAK,CAAC,GAAGE,MAAM,CAACP,GAAG,EAAEK,KAAK,CAAC,GAAGD,GAAG,CAACJ,GAAG,EAAEK,KAAK,CAAC,CACxD","ignoreList":[]}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Prepends `start` to `string_`.
3
+ *
4
+ * v4's `String` module has no native `prepend` (only `concat`), so this fills
5
+ * the gap as a pipeable helper.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { pipe } from "effect"
10
+ * import { StringX } from "@nunofyobiz/effect-extras"
11
+ *
12
+ * // data-first
13
+ * assert.deepStrictEqual(StringX.prepend("world", "hello "), "hello world")
14
+ *
15
+ * // data-last (piped)
16
+ * assert.deepStrictEqual(pipe("world", StringX.prepend("hello ")), "hello world")
17
+ * ```
18
+ *
19
+ * @category combinators
20
+ * @since 0.0.0
21
+ */
22
+ export declare const prepend: ((start: string) => (string_: string) => string) & ((string_: string, start: string) => string);
23
+ /**
24
+ * Wraps `string_` between `start` and `end`.
25
+ *
26
+ * No v4 native equivalent — handy for quoting, bracketing, or fencing a value.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { pipe } from "effect"
31
+ * import { StringX } from "@nunofyobiz/effect-extras"
32
+ *
33
+ * // data-first
34
+ * assert.deepStrictEqual(StringX.surround("value", "[", "]"), "[value]")
35
+ *
36
+ * // data-last (piped)
37
+ * assert.deepStrictEqual(pipe("value", StringX.surround("(", ")")), "(value)")
38
+ * ```
39
+ *
40
+ * @category combinators
41
+ * @since 0.0.0
42
+ */
43
+ export declare const surround: ((start: string, end: string) => (string_: string) => string) & ((string_: string, start: string, end: string) => string);
44
+ /**
45
+ * Prepends `start` to `string_` unless `string_` already starts with it.
46
+ *
47
+ * Idempotent: applying it to an already-prefixed string is a no-op, so
48
+ * `ensurePrepend("foofoo", "foo")` stays `"foofoo"` rather than gaining a
49
+ * second `"foo"`.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * import { pipe } from "effect"
54
+ * import { StringX } from "@nunofyobiz/effect-extras"
55
+ *
56
+ * // data-first — adds the prefix when missing
57
+ * assert.deepStrictEqual(StringX.ensurePrepend("bar", "foo"), "foobar")
58
+ *
59
+ * // idempotent — already prefixed, returned unchanged
60
+ * assert.deepStrictEqual(StringX.ensurePrepend("foobar", "foo"), "foobar")
61
+ *
62
+ * // data-last (piped)
63
+ * assert.deepStrictEqual(pipe("bar", StringX.ensurePrepend("foo")), "foobar")
64
+ * ```
65
+ *
66
+ * @category combinators
67
+ * @since 0.0.0
68
+ */
69
+ export declare const ensurePrepend: ((start: string) => (string_: string) => string) & ((string_: string, start: string) => string);
70
+ /**
71
+ * Replaces the inclusive line range `[startLine, endLine]` of `content` with
72
+ * `replacement` lines, returning the rejoined string.
73
+ *
74
+ * `content` is split on `\n`; the zero-based lines `startLine` through `endLine`
75
+ * (both inclusive) are dropped and `replacement` is spliced into their place.
76
+ * Pass an empty `replacement` to delete the range. Indices clamp naturally via
77
+ * `Array.take`/`Array.drop`, so out-of-range values don't throw.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * import { pipe } from "effect"
82
+ * import { StringX } from "@nunofyobiz/effect-extras"
83
+ *
84
+ * // data-first — replace lines 1..2 with a single line
85
+ * assert.deepStrictEqual(
86
+ * StringX.replaceLineRange("a\nb\nc\nd", 1, 2, ["X"]),
87
+ * "a\nX\nd",
88
+ * )
89
+ *
90
+ * // an empty replacement deletes the range
91
+ * assert.deepStrictEqual(StringX.replaceLineRange("a\nb\nc\nd", 1, 2, []), "a\nd")
92
+ *
93
+ * // data-last (piped)
94
+ * assert.deepStrictEqual(
95
+ * pipe("a\nb\nc", StringX.replaceLineRange(1, 1, ["X", "Y"])),
96
+ * "a\nX\nY\nc",
97
+ * )
98
+ * ```
99
+ *
100
+ * @category combinators
101
+ * @since 0.0.0
102
+ */
103
+ export declare const replaceLineRange: ((startLine: number, endLine: number, replacement: readonly string[]) => (content: string) => string) & ((content: string, startLine: number, endLine: number, replacement: readonly string[]) => string);
104
+ /**
105
+ * Inserts `lines` immediately before the line at `anchorIndex`, preserving the
106
+ * anchor line and everything after it; returns the rejoined string.
107
+ *
108
+ * `content` is split on `\n` and `lines` are spliced in just before the
109
+ * zero-based `anchorIndex`. An `anchorIndex` of `0` prepends, and one at or past
110
+ * the end appends.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * import { pipe } from "effect"
115
+ * import { StringX } from "@nunofyobiz/effect-extras"
116
+ *
117
+ * // data-first — insert before line 1, keeping the anchor and the rest
118
+ * assert.deepStrictEqual(StringX.insertBeforeLine("a\nb\nc", 1, ["X"]), "a\nX\nb\nc")
119
+ *
120
+ * // an anchor at the end appends
121
+ * assert.deepStrictEqual(StringX.insertBeforeLine("a\nb", 2, ["X"]), "a\nb\nX")
122
+ *
123
+ * // data-last (piped)
124
+ * assert.deepStrictEqual(pipe("a\nb", StringX.insertBeforeLine(0, ["X"])), "X\na\nb")
125
+ * ```
126
+ *
127
+ * @category combinators
128
+ * @since 0.0.0
129
+ */
130
+ export declare const insertBeforeLine: ((anchorIndex: number, lines: readonly string[]) => (content: string) => string) & ((content: string, anchorIndex: number, lines: readonly string[]) => string);
131
+ //# sourceMappingURL=StringX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StringX.d.ts","sourceRoot":"","sources":["../src/StringX.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,OAAO,WACV,MAAM,KAAK,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,eACpC,MAAM,SAAS,MAAM,KAAK,MAAM,CAC0B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,QAAQ,WACX,MAAM,OAAO,MAAM,KAAK,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,eACjD,MAAM,SAAS,MAAM,OAAO,MAAM,KAAK,MAAM,CAKxD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,aAAa,WAChB,MAAM,KAAK,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,eACpC,MAAM,SAAS,MAAM,KAAK,MAAM,CAO1C,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,gBAAgB,eAEd,MAAM,WACR,MAAM,eACF,SAAS,MAAM,EAAE,KAC3B,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,eAErB,MAAM,aACJ,MAAM,WACR,MAAM,eACF,SAAS,MAAM,EAAE,KAC3B,MAAM,CAmBZ,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,gBAAgB,iBAEZ,MAAM,SACZ,SAAS,MAAM,EAAE,KACrB,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,eACtB,MAAM,eAAe,MAAM,SAAS,SAAS,MAAM,EAAE,KAAK,MAAM,CAc3E,CAAC"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `String` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Array } from "effect";
7
+ import { dual } from "effect/Function";
8
+ /**
9
+ * Prepends `start` to `string_`.
10
+ *
11
+ * v4's `String` module has no native `prepend` (only `concat`), so this fills
12
+ * the gap as a pipeable helper.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { pipe } from "effect"
17
+ * import { StringX } from "@nunofyobiz/effect-extras"
18
+ *
19
+ * // data-first
20
+ * assert.deepStrictEqual(StringX.prepend("world", "hello "), "hello world")
21
+ *
22
+ * // data-last (piped)
23
+ * assert.deepStrictEqual(pipe("world", StringX.prepend("hello ")), "hello world")
24
+ * ```
25
+ *
26
+ * @category combinators
27
+ * @since 0.0.0
28
+ */
29
+ export const prepend = /*#__PURE__*/dual(2, (string_, start) => `${start}${string_}`);
30
+ /**
31
+ * Wraps `string_` between `start` and `end`.
32
+ *
33
+ * No v4 native equivalent — handy for quoting, bracketing, or fencing a value.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import { pipe } from "effect"
38
+ * import { StringX } from "@nunofyobiz/effect-extras"
39
+ *
40
+ * // data-first
41
+ * assert.deepStrictEqual(StringX.surround("value", "[", "]"), "[value]")
42
+ *
43
+ * // data-last (piped)
44
+ * assert.deepStrictEqual(pipe("value", StringX.surround("(", ")")), "(value)")
45
+ * ```
46
+ *
47
+ * @category combinators
48
+ * @since 0.0.0
49
+ */
50
+ export const surround = /*#__PURE__*/dual(3, (string_, start, end) => `${start}${string_}${end}`);
51
+ /**
52
+ * Prepends `start` to `string_` unless `string_` already starts with it.
53
+ *
54
+ * Idempotent: applying it to an already-prefixed string is a no-op, so
55
+ * `ensurePrepend("foofoo", "foo")` stays `"foofoo"` rather than gaining a
56
+ * second `"foo"`.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * import { pipe } from "effect"
61
+ * import { StringX } from "@nunofyobiz/effect-extras"
62
+ *
63
+ * // data-first — adds the prefix when missing
64
+ * assert.deepStrictEqual(StringX.ensurePrepend("bar", "foo"), "foobar")
65
+ *
66
+ * // idempotent — already prefixed, returned unchanged
67
+ * assert.deepStrictEqual(StringX.ensurePrepend("foobar", "foo"), "foobar")
68
+ *
69
+ * // data-last (piped)
70
+ * assert.deepStrictEqual(pipe("bar", StringX.ensurePrepend("foo")), "foobar")
71
+ * ```
72
+ *
73
+ * @category combinators
74
+ * @since 0.0.0
75
+ */
76
+ export const ensurePrepend = /*#__PURE__*/dual(2, (string_, start) => {
77
+ if (string_.startsWith(start)) {
78
+ return string_;
79
+ }
80
+ return `${start}${string_}`;
81
+ });
82
+ /**
83
+ * Replaces the inclusive line range `[startLine, endLine]` of `content` with
84
+ * `replacement` lines, returning the rejoined string.
85
+ *
86
+ * `content` is split on `\n`; the zero-based lines `startLine` through `endLine`
87
+ * (both inclusive) are dropped and `replacement` is spliced into their place.
88
+ * Pass an empty `replacement` to delete the range. Indices clamp naturally via
89
+ * `Array.take`/`Array.drop`, so out-of-range values don't throw.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { pipe } from "effect"
94
+ * import { StringX } from "@nunofyobiz/effect-extras"
95
+ *
96
+ * // data-first — replace lines 1..2 with a single line
97
+ * assert.deepStrictEqual(
98
+ * StringX.replaceLineRange("a\nb\nc\nd", 1, 2, ["X"]),
99
+ * "a\nX\nd",
100
+ * )
101
+ *
102
+ * // an empty replacement deletes the range
103
+ * assert.deepStrictEqual(StringX.replaceLineRange("a\nb\nc\nd", 1, 2, []), "a\nd")
104
+ *
105
+ * // data-last (piped)
106
+ * assert.deepStrictEqual(
107
+ * pipe("a\nb\nc", StringX.replaceLineRange(1, 1, ["X", "Y"])),
108
+ * "a\nX\nY\nc",
109
+ * )
110
+ * ```
111
+ *
112
+ * @category combinators
113
+ * @since 0.0.0
114
+ */
115
+ export const replaceLineRange = /*#__PURE__*/dual(4, (content, startLine, endLine, replacement) => {
116
+ const lines = content.split("\n");
117
+ return Array.join([...Array.take(lines, startLine), ...replacement, ...Array.drop(lines, endLine + 1)], "\n");
118
+ });
119
+ /**
120
+ * Inserts `lines` immediately before the line at `anchorIndex`, preserving the
121
+ * anchor line and everything after it; returns the rejoined string.
122
+ *
123
+ * `content` is split on `\n` and `lines` are spliced in just before the
124
+ * zero-based `anchorIndex`. An `anchorIndex` of `0` prepends, and one at or past
125
+ * the end appends.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * import { pipe } from "effect"
130
+ * import { StringX } from "@nunofyobiz/effect-extras"
131
+ *
132
+ * // data-first — insert before line 1, keeping the anchor and the rest
133
+ * assert.deepStrictEqual(StringX.insertBeforeLine("a\nb\nc", 1, ["X"]), "a\nX\nb\nc")
134
+ *
135
+ * // an anchor at the end appends
136
+ * assert.deepStrictEqual(StringX.insertBeforeLine("a\nb", 2, ["X"]), "a\nb\nX")
137
+ *
138
+ * // data-last (piped)
139
+ * assert.deepStrictEqual(pipe("a\nb", StringX.insertBeforeLine(0, ["X"])), "X\na\nb")
140
+ * ```
141
+ *
142
+ * @category combinators
143
+ * @since 0.0.0
144
+ */
145
+ export const insertBeforeLine = /*#__PURE__*/dual(3, (content, anchorIndex, lines) => {
146
+ const split = content.split("\n");
147
+ return Array.join([...Array.take(split, anchorIndex), ...lines, ...Array.drop(split, anchorIndex)], "\n");
148
+ });
149
+ //# sourceMappingURL=StringX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StringX.js","names":["Array","dual","prepend","string_","start","surround","end","ensurePrepend","startsWith","replaceLineRange","content","startLine","endLine","replacement","lines","split","join","take","drop","insertBeforeLine","anchorIndex"],"sources":["../src/StringX.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;AAKA,SAASA,KAAK,QAAQ,QAAQ;AAC9B,SAASC,IAAI,QAAQ,iBAAiB;AAEtC;;;;;;;;;;;;;;;;;;;;;AAqBA,OAAO,MAAMC,OAAO,gBAAGD,IAAI,CAGzB,CAAC,EAAE,CAACE,OAAe,EAAEC,KAAa,KAAa,GAAGA,KAAK,GAAGD,OAAO,EAAE,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;AAoBA,OAAO,MAAME,QAAQ,gBAAGJ,IAAI,CAI1B,CAAC,EACD,CAACE,OAAe,EAAEC,KAAa,EAAEE,GAAW,KAC1C,GAAGF,KAAK,GAAGD,OAAO,GAAGG,GAAG,EAAE,CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,OAAO,MAAMC,aAAa,gBAAGN,IAAI,CAG/B,CAAC,EAAE,CAACE,OAAe,EAAEC,KAAa,KAAY;EAC9C,IAAID,OAAO,CAACK,UAAU,CAACJ,KAAK,CAAC,EAAE;IAC7B,OAAOD,OAAO;EAChB;EAEA,OAAO,GAAGC,KAAK,GAAGD,OAAO,EAAE;AAC7B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,OAAO,MAAMM,gBAAgB,gBAAGR,IAAI,CAalC,CAAC,EACD,CACES,OAAe,EACfC,SAAiB,EACjBC,OAAe,EACfC,WAA8B,KACpB;EACV,MAAMC,KAAK,GAAGJ,OAAO,CAACK,KAAK,CAAC,IAAI,CAAC;EACjC,OAAOf,KAAK,CAACgB,IAAI,CACf,CACE,GAAGhB,KAAK,CAACiB,IAAI,CAACH,KAAK,EAAEH,SAAS,CAAC,EAC/B,GAAGE,WAAW,EACd,GAAGb,KAAK,CAACkB,IAAI,CAACJ,KAAK,EAAEF,OAAO,GAAG,CAAC,CAAC,CAClC,EACD,IAAI,CACL;AACH,CAAC,CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,OAAO,MAAMO,gBAAgB,gBAAGlB,IAAI,CAOlC,CAAC,EACD,CAACS,OAAe,EAAEU,WAAmB,EAAEN,KAAwB,KAAY;EACzE,MAAMC,KAAK,GAAGL,OAAO,CAACK,KAAK,CAAC,IAAI,CAAC;EACjC,OAAOf,KAAK,CAACgB,IAAI,CACf,CACE,GAAGhB,KAAK,CAACiB,IAAI,CAACF,KAAK,EAAEK,WAAW,CAAC,EACjC,GAAGN,KAAK,EACR,GAAGd,KAAK,CAACkB,IAAI,CAACH,KAAK,EAAEK,WAAW,CAAC,CAClC,EACD,IAAI,CACL;AACH,CAAC,CACF","ignoreList":[]}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `Struct` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Option } from "effect";
7
+ /**
8
+ * Describes the shape of a per-field transformation over an object `O`: a record
9
+ * `T` whose values are functions taking the corresponding field of `O`.
10
+ *
11
+ * Used to type the "evolve" pattern, where each provided key maps to a function
12
+ * that receives that field's current value. Copied from Effect's internal
13
+ * `Struct.evolve()` type, which is not exported — it would be better to use
14
+ * theirs directly if it ever becomes public.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { StructX } from "@nunofyobiz/effect-extras"
19
+ *
20
+ * type Transform = StructX.PartialTransform<
21
+ * { a: number; b: string },
22
+ * { a: (n: number) => boolean }
23
+ * >
24
+ *
25
+ * const evolve: Transform = { a: (n) => n > 0 }
26
+ *
27
+ * assert.deepStrictEqual(evolve.a(1), true)
28
+ * ```
29
+ *
30
+ * @category models
31
+ * @since 0.0.0
32
+ */
33
+ export type PartialTransform<O, T> = {
34
+ [K in keyof T]: T[K] extends (a: O[K & keyof O]) => unknown ? T[K] : (a: O[K & keyof O]) => unknown;
35
+ };
36
+ /**
37
+ * Builds a singleton record `{ [name]: value }` when `value` is defined, or an
38
+ * empty object `{}` when it is `undefined` — meant to be spread into an object
39
+ * literal.
40
+ *
41
+ * This is the canonical fix for `exactOptionalPropertyTypes: true` (which this
42
+ * repo enables for Effect Schema). Under that flag, spreading
43
+ * `{ key: maybeUndefined }` into an object whose key is `key?: T` is a type
44
+ * error: the property must be *absent*, not present-but-`undefined`. Spreading
45
+ * `...defined("key", maybeUndefined)` instead conditionally omits the property
46
+ * altogether. Note that `null` and other falsy-but-defined values (`0`, `""`,
47
+ * `false`) are kept — only `undefined` is dropped.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * import { StructX } from "@nunofyobiz/effect-extras"
52
+ *
53
+ * // Defined value → singleton record
54
+ * assert.deepStrictEqual(StructX.defined("name", "Ada"), { name: "Ada" })
55
+ *
56
+ * // undefined → property omitted entirely
57
+ * assert.deepStrictEqual(StructX.defined("name", undefined), {})
58
+ *
59
+ * // Falsy-but-defined values are kept
60
+ * assert.deepStrictEqual(StructX.defined("name", 0), { name: 0 })
61
+ *
62
+ * // Typical use: spread to conditionally include an optional field
63
+ * const maybeAge: number | undefined = undefined
64
+ * assert.deepStrictEqual({ id: 1, ...StructX.defined("age", maybeAge) }, {
65
+ * id: 1,
66
+ * })
67
+ * ```
68
+ *
69
+ * @category constructors
70
+ * @since 0.0.0
71
+ */
72
+ export declare const defined: <const K extends string, V>(name: K, value: V | undefined) => Partial<Record<K, Exclude<V, undefined>>>;
73
+ /**
74
+ * Removes every property whose value is `undefined` from `record`, narrowing
75
+ * each remaining property type to exclude `undefined`.
76
+ *
77
+ * The bulk counterpart of {@link defined}: instead of building one optional
78
+ * field, it filters a whole object. Very useful for update/patch actions where
79
+ * `undefined` means "leave this field unchanged" while every other value —
80
+ * including `null`, `0`, `""`, `false`, and `Option.none()` — means "set the
81
+ * field to exactly this".
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * import { StructX } from "@nunofyobiz/effect-extras"
86
+ *
87
+ * // Drops `b` (undefined) but keeps every other value, including null/0/false
88
+ * assert.deepStrictEqual(
89
+ * StructX.filterDefined({ a: "x", b: undefined, c: 0, d: null, e: false }),
90
+ * { a: "x", c: 0, d: null, e: false },
91
+ * )
92
+ * ```
93
+ *
94
+ * @category filtering
95
+ * @since 0.0.0
96
+ */
97
+ export declare const filterDefined: <R extends Record<string, unknown>>(record: R) => Partial<{ [P in keyof R]: Exclude<R[P], undefined>; }>;
98
+ /**
99
+ * Builds a singleton record `{ [name]: value }` when `value` is `Some`, or an
100
+ * empty object `{}` when it is `None` — meant to be spread into an object
101
+ * literal.
102
+ *
103
+ * The `Option`-valued sibling of {@link defined}: where `defined` keys off
104
+ * `undefined`, this keys off `Option` presence. Spread `...some("key", opt)` to
105
+ * conditionally include a field only when the `Option` carries a value, which
106
+ * stays correct under `exactOptionalPropertyTypes: true`.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * import { Option } from "effect"
111
+ * import { StructX } from "@nunofyobiz/effect-extras"
112
+ *
113
+ * // Some → singleton record
114
+ * assert.deepStrictEqual(StructX.some("name", Option.some("Ada")), {
115
+ * name: "Ada",
116
+ * })
117
+ *
118
+ * // None → property omitted entirely
119
+ * assert.deepStrictEqual(StructX.some("name", Option.none()), {})
120
+ * ```
121
+ *
122
+ * @category constructors
123
+ * @since 0.0.0
124
+ */
125
+ export declare const some: <const K extends string, V>(name: K, value: Option.Option<V>) => Partial<Record<K, V>>;
126
+ /**
127
+ * Looks up `key` in a record of `Option` values and returns a singleton record
128
+ * when the value is `Some`, or `{}` when the key is absent or its value is
129
+ * `None`.
130
+ *
131
+ * Combines `Record.get` + `Option.flatten` + a singleton builder in one call —
132
+ * ideal for spreading an optional field out of a lookup table into an object
133
+ * literal under `exactOptionalPropertyTypes: true`. By default the output key
134
+ * matches the lookup key; pass `renameKeyTo` to emit the value under a different
135
+ * name.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * import { Option } from "effect"
140
+ * import { StructX } from "@nunofyobiz/effect-extras"
141
+ *
142
+ * const lookup = {
143
+ * a: Option.some(100),
144
+ * b: Option.none<number>(),
145
+ * }
146
+ *
147
+ * // Some → singleton record under the same key
148
+ * assert.deepStrictEqual(StructX.pickSome(lookup, "a"), { a: 100 })
149
+ *
150
+ * // None → property omitted entirely
151
+ * assert.deepStrictEqual(StructX.pickSome(lookup, "b"), {})
152
+ *
153
+ * // Rename the output key
154
+ * assert.deepStrictEqual(StructX.pickSome(lookup, "a", "count"), { count: 100 })
155
+ * ```
156
+ *
157
+ * @category constructors
158
+ * @since 0.0.0
159
+ */
160
+ export declare function pickSome<const K extends string, V>(record: Record<K, Option.Option<V>>, key: K): Partial<Record<K, V>>;
161
+ export declare function pickSome<const K1 extends string, V, const K2 extends string>(record: Record<K1, Option.Option<V>>, key: K1, renameKeyTo: K2): Partial<Record<K2, V>>;
162
+ /**
163
+ * Builds a singleton record `{ [name]: value }` when `value` is truthy, or an
164
+ * empty object `{}` otherwise — meant to be spread into an object literal.
165
+ *
166
+ * Stricter than {@link defined}: it drops not just `undefined` but every falsy
167
+ * value (`null`, `false`, `0`, `""`, `NaN`), and narrows the value type
168
+ * accordingly. Use it to spread a field only when it carries a meaningful,
169
+ * non-falsy value.
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * import { StructX } from "@nunofyobiz/effect-extras"
174
+ *
175
+ * // Truthy value → singleton record
176
+ * assert.deepStrictEqual(StructX.truthy("name", "Ada"), { name: "Ada" })
177
+ *
178
+ * // Falsy values → property omitted entirely
179
+ * assert.deepStrictEqual(StructX.truthy("name", ""), {})
180
+ * assert.deepStrictEqual(StructX.truthy("name", 0), {})
181
+ * assert.deepStrictEqual(StructX.truthy("name", false), {})
182
+ * ```
183
+ *
184
+ * @category constructors
185
+ * @since 0.0.0
186
+ */
187
+ export declare const truthy: <const K extends string, V>(name: K, value: V) => Partial<Record<K, Exclude<NonNullable<V>, false | 0 | "">>>;
188
+ /**
189
+ * Returns `true` when `object` has `key` set to a non-nullish value, narrowing
190
+ * that property to `NonNullable` on the type level.
191
+ *
192
+ * A type guard combining `hasProperty` with a nullish check: after a successful
193
+ * test, TypeScript treats `object[key]` as present and free of `null` /
194
+ * `undefined`. Handy as a predicate passed to `Array.filter` to keep only the
195
+ * elements whose `key` is populated.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * import { pipe } from "effect"
200
+ * import { StructX } from "@nunofyobiz/effect-extras"
201
+ *
202
+ * // data-first
203
+ * assert.deepStrictEqual(
204
+ * StructX.hasNotNullableProperty({ id: "1" }, "id"),
205
+ * true,
206
+ * )
207
+ *
208
+ * // data-last (pipeable); a null value fails the guard
209
+ * assert.deepStrictEqual(
210
+ * pipe({ id: null }, StructX.hasNotNullableProperty("id")),
211
+ * false,
212
+ * )
213
+ * ```
214
+ *
215
+ * @category refinements
216
+ * @since 0.0.0
217
+ */
218
+ export declare const hasNotNullableProperty: (<T, K extends keyof T>(key: K) => (object: T) => object is T & Record<K, NonNullable<T[K]>>) & (<T, K extends keyof T>(object: T, key: K) => object is T & Record<K, NonNullable<T[K]>>);
219
+ //# sourceMappingURL=StructX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StructX.d.ts","sourceRoot":"","sources":["../src/StructX.ts"],"names":[],"mappings":"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"}