@nunofyobiz/effect-extras 2.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 (127) hide show
  1. package/README.md +36 -2
  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 -3772
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +21 -1011
  85. package/dist/index.js.map +1 -1
  86. package/package.json +18 -5
  87. package/src/{ArrayX/ArrayX.ts → ArrayX.ts} +3 -3
  88. package/src/{DurationX/DurationX.ts → DurationX.ts} +1 -1
  89. package/src/{RecordX/RecordX.ts → RecordX.ts} +1 -1
  90. package/src/index.ts +21 -20
  91. package/src/ArrayX/index.ts +0 -1
  92. package/src/BigIntX/index.ts +0 -1
  93. package/src/BooleanX/index.ts +0 -1
  94. package/src/DurationX/index.ts +0 -1
  95. package/src/EffectX/index.ts +0 -1
  96. package/src/FormDataX/index.ts +0 -1
  97. package/src/MapX/index.ts +0 -1
  98. package/src/NonNullableX/index.ts +0 -2
  99. package/src/NumberX/index.ts +0 -1
  100. package/src/OptionX/index.ts +0 -1
  101. package/src/OrderX/index.ts +0 -1
  102. package/src/PredicateX/index.ts +0 -1
  103. package/src/PromiseX/index.ts +0 -1
  104. package/src/RecordX/index.ts +0 -1
  105. package/src/ResultX/index.ts +0 -1
  106. package/src/SchemaX/index.ts +0 -1
  107. package/src/SetX/index.ts +0 -1
  108. package/src/StringX/index.ts +0 -1
  109. package/src/StructX/index.ts +0 -1
  110. package/src/WarnResult/index.ts +0 -1
  111. /package/src/{BigIntX/BigIntX.ts → BigIntX.ts} +0 -0
  112. /package/src/{BooleanX/BooleanX.ts → BooleanX.ts} +0 -0
  113. /package/src/{EffectX/EffectX.ts → EffectX.ts} +0 -0
  114. /package/src/{FormDataX/FormDataX.ts → FormDataX.ts} +0 -0
  115. /package/src/{MapX/MapX.ts → MapX.ts} +0 -0
  116. /package/src/{NonNullableX/NonNullableX.ts → NonNullableX.ts} +0 -0
  117. /package/src/{NumberX/NumberX.ts → NumberX.ts} +0 -0
  118. /package/src/{OptionX/OptionX.ts → OptionX.ts} +0 -0
  119. /package/src/{OrderX/OrderX.ts → OrderX.ts} +0 -0
  120. /package/src/{PredicateX/PredicateX.ts → PredicateX.ts} +0 -0
  121. /package/src/{PromiseX/PromiseX.ts → PromiseX.ts} +0 -0
  122. /package/src/{ResultX/ResultX.ts → ResultX.ts} +0 -0
  123. /package/src/{SchemaX/SchemaX.ts → SchemaX.ts} +0 -0
  124. /package/src/{SetX/SetX.ts → SetX.ts} +0 -0
  125. /package/src/{StringX/StringX.ts → StringX.ts} +0 -0
  126. /package/src/{StructX/StructX.ts → StructX.ts} +0 -0
  127. /package/src/{WarnResult/WarnResult.ts → WarnResult.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaX.d.ts","sourceRoot":"","sources":["../src/SchemaX.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAU,MAAM,EAAwB,MAAM,QAAQ,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,qBAAqB,4FAKjC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,eAAe,sOAK3B,CAAC;AAaF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,iBAAiB,GAxC3B,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,uDAwCM,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,IAChE,CAAC,CAAC,eAAe,CAAC,CAAC;AAQrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,IAAI,GACf,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EACzC,KAAK,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAErD,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAC7B,GAAG,MAAM,IAAI,KACZ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAOxC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,IAAI,GACf,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EACzC,KAAK,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAErD,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAC7B,GAAG,MAAM,IAAI,KACZ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAOxC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EACzD,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAE,CAOhE,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,WAAW,GACtB,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EACzC,KAAK,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAErD,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAC7B,GAAG,MAAM,IAAI,KACZ,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAE,CACpC,CAAC"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `Schema` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { BigInt, Schema, SchemaGetter, Struct } from "effect";
7
+ /**
8
+ * A `Schema` for a non-empty string that is trimmed on both decode and encode.
9
+ *
10
+ * Leading and trailing whitespace is stripped, and the resulting value must be
11
+ * non-empty — so a whitespace-only input (e.g. `" "`) fails the
12
+ * `NonEmptyString` refinement after trimming. Use it wherever user-supplied text
13
+ * should be normalized and guaranteed to carry content.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { Effect, Schema } from "effect"
18
+ * import { SchemaX } from "@nunofyobiz/effect-extras"
19
+ *
20
+ * const decoded = Effect.runSync(
21
+ * Schema.decodeEffect(SchemaX.TrimmedNonEmptyString)(" hello "),
22
+ * )
23
+ * assert.deepStrictEqual(decoded, "hello")
24
+ * ```
25
+ *
26
+ * @category constructors
27
+ * @since 0.0.0
28
+ */
29
+ export const TrimmedNonEmptyString = /*#__PURE__*/Schema.NonEmptyString.pipe(/*#__PURE__*/Schema.decode({
30
+ decode: /*#__PURE__*/SchemaGetter.transform(s => s.trim()),
31
+ encode: /*#__PURE__*/SchemaGetter.transform(s => s.trim())
32
+ }));
33
+ /**
34
+ * A `Schema` for a URL-safe file path built on top of {@link
35
+ * TrimmedNonEmptyString}.
36
+ *
37
+ * On decode the path is `decodeURIComponent`-ed (percent-escapes are expanded);
38
+ * on encode it is `encodeURIComponent`-ed back into its URL-safe form. The
39
+ * underlying value is also trimmed and required to be non-empty. Use it at the
40
+ * boundary between stored/transmitted encoded paths and the decoded paths your
41
+ * code works with.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { Effect, Schema } from "effect"
46
+ * import { SchemaX } from "@nunofyobiz/effect-extras"
47
+ *
48
+ * // Decoding expands percent-escapes
49
+ * const decoded = Effect.runSync(
50
+ * Schema.decodeEffect(SchemaX.URLSafeFilePath)("a%2Fb.txt"),
51
+ * )
52
+ * assert.deepStrictEqual(decoded, "a/b.txt")
53
+ *
54
+ * // Encoding produces the URL-safe form
55
+ * const encoded = Effect.runSync(
56
+ * Schema.encodeEffect(SchemaX.URLSafeFilePath)("a/b.txt"),
57
+ * )
58
+ * assert.deepStrictEqual(encoded, "a%2Fb.txt")
59
+ * ```
60
+ *
61
+ * @category constructors
62
+ * @since 0.0.0
63
+ */
64
+ export const URLSafeFilePath = /*#__PURE__*/TrimmedNonEmptyString.pipe(/*#__PURE__*/Schema.decode({
65
+ decode: /*#__PURE__*/SchemaGetter.transform(path => decodeURIComponent(path)),
66
+ encode: /*#__PURE__*/SchemaGetter.transform(path => encodeURIComponent(path))
67
+ }));
68
+ // Internal — only used to construct nonNegativeBigInt below.
69
+ const clampMinBigInt = min => schema => schema.pipe(Schema.decode({
70
+ decode: SchemaGetter.transform(value => BigInt.max(value, min)),
71
+ encode: SchemaGetter.transform(value => BigInt.max(value, min))
72
+ }));
73
+ /**
74
+ * Transforms a `bigint` `Schema` so its value is clamped to be non-negative,
75
+ * mapping any value below `0n` up to `0n` on both decode and encode.
76
+ *
77
+ * Unlike a refinement that *rejects* negative input, this *coerces* it: a
78
+ * negative `bigint` decodes to `0n` rather than failing. Apply it to a `bigint`
79
+ * schema whenever negative magnitudes are meaningless and should be floored at
80
+ * zero rather than treated as errors.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { Effect, Schema } from "effect"
85
+ * import { SchemaX } from "@nunofyobiz/effect-extras"
86
+ *
87
+ * const NonNegative = SchemaX.nonNegativeBigInt(Schema.BigInt)
88
+ *
89
+ * // Negative values are clamped up to 0n
90
+ * assert.deepStrictEqual(
91
+ * Effect.runSync(Schema.decodeEffect(NonNegative)(-5n)),
92
+ * 0n,
93
+ * )
94
+ *
95
+ * // Non-negative values pass through unchanged
96
+ * assert.deepStrictEqual(
97
+ * Effect.runSync(Schema.decodeEffect(NonNegative)(7n)),
98
+ * 7n,
99
+ * )
100
+ * ```
101
+ *
102
+ * @category combinators
103
+ * @since 0.0.0
104
+ */
105
+ export const nonNegativeBigInt = /*#__PURE__*/clampMinBigInt(0n);
106
+ // ---------------------------------------------------------------------------
107
+ // Schema.Struct utilities — v4 removed the `.pick(...)`, `.omit(...)`, and
108
+ // `Schema.partial(...)` methods that v3 had on `Schema.Struct` instances. These
109
+ // helpers restore the same ergonomics on top of v4's `mapFields` primitive.
110
+ // ---------------------------------------------------------------------------
111
+ /**
112
+ * Returns a new `Schema.Struct` containing only the named `keys` of `schema`.
113
+ *
114
+ * Restores the v3 ergonomics of `mySchema.pick("a", "b")` — v4 removed the
115
+ * `.pick(...)` method from `Schema.Struct` instances, so this rebuilds it on top
116
+ * of v4's `mapFields` primitive. Each picked field keeps its original schema,
117
+ * including any refinements.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import { Effect, Schema } from "effect"
122
+ * import { SchemaX } from "@nunofyobiz/effect-extras"
123
+ *
124
+ * const Source = Schema.Struct({
125
+ * a: Schema.Number,
126
+ * b: Schema.String,
127
+ * c: Schema.Boolean,
128
+ * })
129
+ *
130
+ * const Picked = SchemaX.pick(Source, "a", "b")
131
+ *
132
+ * const decoded = Effect.runSync(
133
+ * Schema.decodeEffect(Picked)({ a: 1, b: "hi" }),
134
+ * )
135
+ * assert.deepStrictEqual(decoded, { a: 1, b: "hi" })
136
+ * ```
137
+ *
138
+ * @category combinators
139
+ * @since 0.0.0
140
+ */
141
+ export const pick = (schema, ...keys) => schema.mapFields(fields => Struct.pick(fields, keys));
142
+ /**
143
+ * Returns a new `Schema.Struct` with the named `keys` of `schema` removed.
144
+ *
145
+ * The complement of {@link pick}, restoring the v3 ergonomics of
146
+ * `mySchema.omit("a")` that v4 dropped from `Schema.Struct` instances. Every
147
+ * surviving field keeps its original schema, including any refinements.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * import { Effect, Schema } from "effect"
152
+ * import { SchemaX } from "@nunofyobiz/effect-extras"
153
+ *
154
+ * const Source = Schema.Struct({
155
+ * a: Schema.Number,
156
+ * b: Schema.String,
157
+ * c: Schema.Boolean,
158
+ * })
159
+ *
160
+ * const Omitted = SchemaX.omit(Source, "c")
161
+ *
162
+ * const decoded = Effect.runSync(
163
+ * Schema.decodeEffect(Omitted)({ a: 1, b: "hi" }),
164
+ * )
165
+ * assert.deepStrictEqual(decoded, { a: 1, b: "hi" })
166
+ * ```
167
+ *
168
+ * @category combinators
169
+ * @since 0.0.0
170
+ */
171
+ export const omit = (schema, ...keys) => schema.mapFields(fields => Struct.omit(fields, keys));
172
+ /**
173
+ * Returns a new `Schema.Struct` in which every field of `schema` is made
174
+ * optional.
175
+ *
176
+ * Restores the v3 `Schema.partial(mySchema)` behaviour that v4 removed, by
177
+ * wrapping each field in `Schema.optional`. A decoded value may therefore omit
178
+ * any field; fields that *are* present still have to satisfy their original
179
+ * schema, refinements included.
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * import { Effect, Schema } from "effect"
184
+ * import { SchemaX } from "@nunofyobiz/effect-extras"
185
+ *
186
+ * const Source = Schema.Struct({ a: Schema.Number, b: Schema.String })
187
+ * const Partial = SchemaX.partial(Source)
188
+ *
189
+ * // All fields may be absent
190
+ * assert.deepStrictEqual(Effect.runSync(Schema.decodeEffect(Partial)({})), {})
191
+ *
192
+ * // A present subset still decodes
193
+ * assert.deepStrictEqual(
194
+ * Effect.runSync(Schema.decodeEffect(Partial)({ a: 1 })),
195
+ * { a: 1 },
196
+ * )
197
+ * ```
198
+ *
199
+ * @category combinators
200
+ * @since 0.0.0
201
+ */
202
+ export const partial = schema => schema.mapFields(fields => {
203
+ const result = {};
204
+ for (const key of Object.keys(fields)) {
205
+ result[key] = Schema.optional(fields[key]);
206
+ }
207
+ return result;
208
+ });
209
+ /**
210
+ * Returns a new `Schema.Struct` containing only the named `keys` of `schema`,
211
+ * with every picked field made optional.
212
+ *
213
+ * Equivalent to `partial(pick(schema, ...keys))`, but reads more directly for
214
+ * the common "partial update over a subset of fields" pattern: select the
215
+ * mutable fields of an entity, then allow any of them to be omitted in the
216
+ * update payload. Composes {@link pick} and {@link partial} in one call.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * import { Effect, Schema } from "effect"
221
+ * import { SchemaX } from "@nunofyobiz/effect-extras"
222
+ *
223
+ * const Source = Schema.Struct({
224
+ * a: Schema.Number,
225
+ * b: Schema.String,
226
+ * c: Schema.Boolean,
227
+ * })
228
+ *
229
+ * const Update = SchemaX.pickPartial(Source, "a", "b")
230
+ *
231
+ * // Only picked fields are known, and each may be omitted
232
+ * assert.deepStrictEqual(Effect.runSync(Schema.decodeEffect(Update)({})), {})
233
+ * assert.deepStrictEqual(
234
+ * Effect.runSync(Schema.decodeEffect(Update)({ a: 1 })),
235
+ * { a: 1 },
236
+ * )
237
+ * ```
238
+ *
239
+ * @category combinators
240
+ * @since 0.0.0
241
+ */
242
+ export const pickPartial = (schema, ...keys) => partial(pick(schema, ...keys));
243
+ //# sourceMappingURL=SchemaX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaX.js","names":["BigInt","Schema","SchemaGetter","Struct","TrimmedNonEmptyString","NonEmptyString","pipe","decode","transform","s","trim","encode","URLSafeFilePath","path","decodeURIComponent","encodeURIComponent","clampMinBigInt","min","schema","value","max","nonNegativeBigInt","pick","keys","mapFields","fields","omit","partial","result","key","Object","optional","pickPartial"],"sources":["../src/SchemaX.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;AAKA,SAASA,MAAM,EAAEC,MAAM,EAAEC,YAAY,EAAEC,MAAM,QAAQ,QAAQ;AAE7D;;;;;;;;;;;;;;;;;;;;;;AAsBA,OAAO,MAAMC,qBAAqB,gBAAGH,MAAM,CAACI,cAAc,CAACC,IAAI,cAC7DL,MAAM,CAACM,MAAM,CAAC;EACZA,MAAM,eAAEL,YAAY,CAACM,SAAS,CAAEC,CAAC,IAAKA,CAAC,CAACC,IAAI,EAAE,CAAC;EAC/CC,MAAM,eAAET,YAAY,CAACM,SAAS,CAAEC,CAAC,IAAKA,CAAC,CAACC,IAAI,EAAE;CAC/C,CAAC,CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,OAAO,MAAME,eAAe,gBAAGR,qBAAqB,CAACE,IAAI,cACvDL,MAAM,CAACM,MAAM,CAAC;EACZA,MAAM,eAAEL,YAAY,CAACM,SAAS,CAAEK,IAAI,IAAKC,kBAAkB,CAACD,IAAI,CAAC,CAAC;EAClEF,MAAM,eAAET,YAAY,CAACM,SAAS,CAAEK,IAAI,IAAKE,kBAAkB,CAACF,IAAI,CAAC;CAClE,CAAC,CACH;AAED;AACA,MAAMG,cAAc,GACjBC,GAAW,IACsBC,MAAS,IACzCA,MAAM,CAACZ,IAAI,CACTL,MAAM,CAACM,MAAM,CAAC;EACZA,MAAM,EAAEL,YAAY,CAACM,SAAS,CAAEW,KAAK,IAAKnB,MAAM,CAACoB,GAAG,CAACD,KAAK,EAAEF,GAAG,CAAC,CAAC;EACjEN,MAAM,EAAET,YAAY,CAACM,SAAS,CAAEW,KAAK,IAAKnB,MAAM,CAACoB,GAAG,CAACD,KAAK,EAAEF,GAAG,CAAC;CACjE,CAAC,CACH;AAEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,OAAO,MAAMI,iBAAiB,gBAAGL,cAAc,CAAC,EAAE,CAAC;AA8BnD;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,OAAO,MAAMM,IAAI,GAAGA,CAIlBJ,MAA6B,EAC7B,GAAGK,IAAU,KAEbL,MAAM,CAACM,SAAS,CACbC,MAAM,IACLtB,MAAM,CAACmB,IAAI,CAACG,MAAM,EAAEF,IAAiC,CAGpD,CACJ;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,OAAO,MAAMG,IAAI,GAAGA,CAIlBR,MAA6B,EAC7B,GAAGK,IAAU,KAEbL,MAAM,CAACM,SAAS,CACbC,MAAM,IACLtB,MAAM,CAACuB,IAAI,CAACD,MAAM,EAAEF,IAAiC,CAGpD,CACJ;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,OAAO,MAAMI,OAAO,GAClBT,MAA6B,IAE7BA,MAAM,CAACM,SAAS,CAAEC,MAAM,IAAI;EAC1B,MAAMG,MAAM,GAAyD,EAAE;EACvE,KAAK,MAAMC,GAAG,IAAIC,MAAM,CAACP,IAAI,CAACE,MAAM,CAAqB,EAAE;IACzDG,MAAM,CAACC,GAAG,CAAC,GAAG5B,MAAM,CAAC8B,QAAQ,CAACN,MAAM,CAACI,GAAG,CAAC,CAAC;EAC5C;EACA,OAAOD,MAA6D;AACtE,CAAC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,OAAO,MAAMI,WAAW,GAAGA,CAIzBd,MAA6B,EAC7B,GAAGK,IAAU,KAEbI,OAAO,CAACL,IAAI,CAACJ,MAAM,EAAE,GAAGK,IAAI,CAAC,CAAC","ignoreList":[]}
package/dist/SetX.d.ts ADDED
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Runs a mutating function against a copy of `set`, leaving the original
3
+ * untouched, and returns the mutated copy.
4
+ *
5
+ * Use it to reuse imperative `Set` mutation code (`.add`, `.delete`) without
6
+ * sacrificing immutability: the callback may freely mutate the set it receives
7
+ * because it operates on a fresh clone. Supports both data-first and data-last
8
+ * (pipeable) call styles.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { SetX } from "@nunofyobiz/effect-extras"
13
+ * import { pipe } from "effect"
14
+ *
15
+ * const original = new Set(["a", "b"])
16
+ *
17
+ * const result = pipe(
18
+ * original,
19
+ * SetX.safelyMutate((set) => {
20
+ * set.delete("a")
21
+ * return set.add("c")
22
+ * })
23
+ * )
24
+ *
25
+ * assert.deepStrictEqual(result, new Set(["b", "c"]))
26
+ * // The original is left intact
27
+ * assert.deepStrictEqual(original, new Set(["a", "b"]))
28
+ * ```
29
+ *
30
+ * @category combinators
31
+ * @since 0.0.0
32
+ */
33
+ export declare const safelyMutate: (<A>(mutate: (set: Set<A>) => Set<A>) => (set: Set<A>) => Set<A>) & (<A>(set: Set<A>, mutate: (set: Set<A>) => Set<A>) => Set<A>);
34
+ /**
35
+ * Returns a new `Set` with `value` added, leaving the input set unchanged.
36
+ *
37
+ * When `value` is already present the input set is returned as-is (no copy),
38
+ * making repeated adds of existing members allocation-free. Supports both
39
+ * data-first and data-last (pipeable) call styles.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { SetX } from "@nunofyobiz/effect-extras"
44
+ * import { pipe } from "effect"
45
+ *
46
+ * // Data-first — adds a new element into a fresh set
47
+ * assert.deepStrictEqual(
48
+ * SetX.add(new Set(["a", "b"]), "c"),
49
+ * new Set(["a", "b", "c"])
50
+ * )
51
+ *
52
+ * // Data-last — existing element leaves the set unchanged
53
+ * assert.deepStrictEqual(
54
+ * pipe(new Set(["a", "b"]), SetX.add("b")),
55
+ * new Set(["a", "b"])
56
+ * )
57
+ * ```
58
+ *
59
+ * @category combinators
60
+ * @since 0.0.0
61
+ */
62
+ export declare const add: (<A>(value: A) => (set: Set<A>) => Set<A>) & (<A>(set: Set<A>, value: A) => Set<A>);
63
+ /**
64
+ * Returns a new `Set` with `value` removed, leaving the input set unchanged.
65
+ *
66
+ * When `value` is absent the input set is returned as-is (no copy). Supports both
67
+ * data-first and data-last (pipeable) call styles.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import { SetX } from "@nunofyobiz/effect-extras"
72
+ * import { pipe } from "effect"
73
+ *
74
+ * // Data-first — removes an existing element into a fresh set
75
+ * assert.deepStrictEqual(
76
+ * SetX.remove(new Set(["a", "b", "c"]), "c"),
77
+ * new Set(["a", "b"])
78
+ * )
79
+ *
80
+ * // Data-last — absent element leaves the set unchanged
81
+ * assert.deepStrictEqual(
82
+ * pipe(new Set(["a", "b"]), SetX.remove("z")),
83
+ * new Set(["a", "b"])
84
+ * )
85
+ * ```
86
+ *
87
+ * @category combinators
88
+ * @since 0.0.0
89
+ */
90
+ export declare const remove: (<A>(value: A) => (set: Set<A>) => Set<A>) & (<A>(set: Set<A>, value: A) => Set<A>);
91
+ /**
92
+ * Returns a new `Set` with `value` added if it was absent or removed if it was
93
+ * present, leaving the input set unchanged.
94
+ *
95
+ * Use it for membership toggles (selection state, feature flags by key) where a
96
+ * value's presence should flip on each call. Supports both data-first and
97
+ * data-last (pipeable) call styles.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * import { SetX } from "@nunofyobiz/effect-extras"
102
+ * import { pipe } from "effect"
103
+ *
104
+ * // Data-first — absent value gets added
105
+ * assert.deepStrictEqual(
106
+ * SetX.toggle(new Set(["a", "b"]), "c"),
107
+ * new Set(["a", "b", "c"])
108
+ * )
109
+ *
110
+ * // Data-last — present value gets removed
111
+ * assert.deepStrictEqual(
112
+ * pipe(new Set(["a", "b", "c"]), SetX.toggle("b")),
113
+ * new Set(["a", "c"])
114
+ * )
115
+ * ```
116
+ *
117
+ * @category combinators
118
+ * @since 0.0.0
119
+ */
120
+ export declare const toggle: (<A>(value: A) => (set: Set<A>) => Set<A>) & (<A>(set: Set<A>, value: A) => Set<A>);
121
+ //# sourceMappingURL=SetX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SetX.d.ts","sourceRoot":"","sources":["../src/SetX.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,YAAY,IACtB,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAC9D,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAI3D,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,GAAG,IACb,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MACvC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAKrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,MAAM,IAChB,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MACvC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAQpC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,MAAM,IAChB,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MACvC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAKrC,CAAC"}
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,70 @@
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
+ //# sourceMappingURL=StringX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StringX.d.ts","sourceRoot":"","sources":["../src/StringX.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `String` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { dual } from "effect/Function";
7
+ /**
8
+ * Prepends `start` to `string_`.
9
+ *
10
+ * v4's `String` module has no native `prepend` (only `concat`), so this fills
11
+ * the gap as a pipeable helper.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { pipe } from "effect"
16
+ * import { StringX } from "@nunofyobiz/effect-extras"
17
+ *
18
+ * // data-first
19
+ * assert.deepStrictEqual(StringX.prepend("world", "hello "), "hello world")
20
+ *
21
+ * // data-last (piped)
22
+ * assert.deepStrictEqual(pipe("world", StringX.prepend("hello ")), "hello world")
23
+ * ```
24
+ *
25
+ * @category combinators
26
+ * @since 0.0.0
27
+ */
28
+ export const prepend = /*#__PURE__*/dual(2, (string_, start) => `${start}${string_}`);
29
+ /**
30
+ * Wraps `string_` between `start` and `end`.
31
+ *
32
+ * No v4 native equivalent — handy for quoting, bracketing, or fencing a value.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * import { pipe } from "effect"
37
+ * import { StringX } from "@nunofyobiz/effect-extras"
38
+ *
39
+ * // data-first
40
+ * assert.deepStrictEqual(StringX.surround("value", "[", "]"), "[value]")
41
+ *
42
+ * // data-last (piped)
43
+ * assert.deepStrictEqual(pipe("value", StringX.surround("(", ")")), "(value)")
44
+ * ```
45
+ *
46
+ * @category combinators
47
+ * @since 0.0.0
48
+ */
49
+ export const surround = /*#__PURE__*/dual(3, (string_, start, end) => `${start}${string_}${end}`);
50
+ /**
51
+ * Prepends `start` to `string_` unless `string_` already starts with it.
52
+ *
53
+ * Idempotent: applying it to an already-prefixed string is a no-op, so
54
+ * `ensurePrepend("foofoo", "foo")` stays `"foofoo"` rather than gaining a
55
+ * second `"foo"`.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { pipe } from "effect"
60
+ * import { StringX } from "@nunofyobiz/effect-extras"
61
+ *
62
+ * // data-first — adds the prefix when missing
63
+ * assert.deepStrictEqual(StringX.ensurePrepend("bar", "foo"), "foobar")
64
+ *
65
+ * // idempotent — already prefixed, returned unchanged
66
+ * assert.deepStrictEqual(StringX.ensurePrepend("foobar", "foo"), "foobar")
67
+ *
68
+ * // data-last (piped)
69
+ * assert.deepStrictEqual(pipe("bar", StringX.ensurePrepend("foo")), "foobar")
70
+ * ```
71
+ *
72
+ * @category combinators
73
+ * @since 0.0.0
74
+ */
75
+ export const ensurePrepend = /*#__PURE__*/dual(2, (string_, start) => {
76
+ if (string_.startsWith(start)) {
77
+ return string_;
78
+ }
79
+ return `${start}${string_}`;
80
+ });
81
+ //# sourceMappingURL=StringX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StringX.js","names":["dual","prepend","string_","start","surround","end","ensurePrepend","startsWith"],"sources":["../src/StringX.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;AAKA,SAASA,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","ignoreList":[]}