@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.
- package/README.md +36 -2
- package/dist/ArrayX.d.ts +415 -0
- package/dist/ArrayX.d.ts.map +1 -0
- package/dist/ArrayX.js +547 -0
- package/dist/ArrayX.js.map +1 -0
- package/dist/BigIntX.d.ts +24 -0
- package/dist/BigIntX.d.ts.map +1 -0
- package/dist/BigIntX.js +30 -0
- package/dist/BigIntX.js.map +1 -0
- package/dist/BooleanX.d.ts +25 -0
- package/dist/BooleanX.d.ts.map +1 -0
- package/dist/BooleanX.js +25 -0
- package/dist/BooleanX.js.map +1 -0
- package/dist/DurationX.d.ts +73 -0
- package/dist/DurationX.d.ts.map +1 -0
- package/dist/DurationX.js +91 -0
- package/dist/DurationX.js.map +1 -0
- package/dist/EffectX.d.ts +120 -0
- package/dist/EffectX.d.ts.map +1 -0
- package/dist/EffectX.js +140 -0
- package/dist/EffectX.js.map +1 -0
- package/dist/FormDataX.d.ts +49 -0
- package/dist/FormDataX.d.ts.map +1 -0
- package/dist/FormDataX.js +42 -0
- package/dist/FormDataX.js.map +1 -0
- package/dist/MapX.d.ts +32 -0
- package/dist/MapX.d.ts.map +1 -0
- package/dist/MapX.js +49 -0
- package/dist/MapX.js.map +1 -0
- package/dist/NonNullableX.d.ts +174 -0
- package/dist/NonNullableX.d.ts.map +1 -0
- package/dist/NonNullableX.js +212 -0
- package/dist/NonNullableX.js.map +1 -0
- package/dist/NumberX.d.ts +178 -0
- package/dist/NumberX.d.ts.map +1 -0
- package/dist/NumberX.js +214 -0
- package/dist/NumberX.js.map +1 -0
- package/dist/OptionX.d.ts +181 -0
- package/dist/OptionX.d.ts.map +1 -0
- package/dist/OptionX.js +195 -0
- package/dist/OptionX.js.map +1 -0
- package/dist/OrderX.d.ts +32 -0
- package/dist/OrderX.d.ts.map +1 -0
- package/dist/OrderX.js +32 -0
- package/dist/OrderX.js.map +1 -0
- package/dist/PredicateX.d.ts +76 -0
- package/dist/PredicateX.d.ts.map +1 -0
- package/dist/PredicateX.js +73 -0
- package/dist/PredicateX.js.map +1 -0
- package/dist/PromiseX.d.ts +32 -0
- package/dist/PromiseX.d.ts.map +1 -0
- package/dist/PromiseX.js +32 -0
- package/dist/PromiseX.js.map +1 -0
- package/dist/RecordX.d.ts +323 -0
- package/dist/RecordX.d.ts.map +1 -0
- package/dist/RecordX.js +326 -0
- package/dist/RecordX.js.map +1 -0
- package/dist/ResultX.d.ts +50 -0
- package/dist/ResultX.d.ts.map +1 -0
- package/dist/ResultX.js +50 -0
- package/dist/ResultX.js.map +1 -0
- package/dist/SchemaX.d.ts +249 -0
- package/dist/SchemaX.d.ts.map +1 -0
- package/dist/SchemaX.js +243 -0
- package/dist/SchemaX.js.map +1 -0
- package/dist/SetX.d.ts +121 -0
- package/dist/SetX.d.ts.map +1 -0
- package/dist/SetX.js +137 -0
- package/dist/SetX.js.map +1 -0
- package/dist/StringX.d.ts +70 -0
- package/dist/StringX.d.ts.map +1 -0
- package/dist/StringX.js +81 -0
- package/dist/StringX.js.map +1 -0
- package/dist/StructX.d.ts +219 -0
- package/dist/StructX.d.ts.map +1 -0
- package/dist/StructX.js +173 -0
- package/dist/StructX.js.map +1 -0
- package/dist/WarnResult.d.ts +1146 -0
- package/dist/WarnResult.d.ts.map +1 -0
- package/dist/WarnResult.js +1060 -0
- package/dist/WarnResult.js.map +1 -0
- package/dist/index.d.ts +22 -3772
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -1011
- package/dist/index.js.map +1 -1
- package/package.json +18 -5
- package/src/{ArrayX/ArrayX.ts → ArrayX.ts} +3 -3
- package/src/{DurationX/DurationX.ts → DurationX.ts} +1 -1
- package/src/{RecordX/RecordX.ts → RecordX.ts} +1 -1
- package/src/index.ts +21 -20
- package/src/ArrayX/index.ts +0 -1
- package/src/BigIntX/index.ts +0 -1
- package/src/BooleanX/index.ts +0 -1
- package/src/DurationX/index.ts +0 -1
- package/src/EffectX/index.ts +0 -1
- package/src/FormDataX/index.ts +0 -1
- package/src/MapX/index.ts +0 -1
- package/src/NonNullableX/index.ts +0 -2
- package/src/NumberX/index.ts +0 -1
- package/src/OptionX/index.ts +0 -1
- package/src/OrderX/index.ts +0 -1
- package/src/PredicateX/index.ts +0 -1
- package/src/PromiseX/index.ts +0 -1
- package/src/RecordX/index.ts +0 -1
- package/src/ResultX/index.ts +0 -1
- package/src/SchemaX/index.ts +0 -1
- package/src/SetX/index.ts +0 -1
- package/src/StringX/index.ts +0 -1
- package/src/StructX/index.ts +0 -1
- package/src/WarnResult/index.ts +0 -1
- /package/src/{BigIntX/BigIntX.ts → BigIntX.ts} +0 -0
- /package/src/{BooleanX/BooleanX.ts → BooleanX.ts} +0 -0
- /package/src/{EffectX/EffectX.ts → EffectX.ts} +0 -0
- /package/src/{FormDataX/FormDataX.ts → FormDataX.ts} +0 -0
- /package/src/{MapX/MapX.ts → MapX.ts} +0 -0
- /package/src/{NonNullableX/NonNullableX.ts → NonNullableX.ts} +0 -0
- /package/src/{NumberX/NumberX.ts → NumberX.ts} +0 -0
- /package/src/{OptionX/OptionX.ts → OptionX.ts} +0 -0
- /package/src/{OrderX/OrderX.ts → OrderX.ts} +0 -0
- /package/src/{PredicateX/PredicateX.ts → PredicateX.ts} +0 -0
- /package/src/{PromiseX/PromiseX.ts → PromiseX.ts} +0 -0
- /package/src/{ResultX/ResultX.ts → ResultX.ts} +0 -0
- /package/src/{SchemaX/SchemaX.ts → SchemaX.ts} +0 -0
- /package/src/{SetX/SetX.ts → SetX.ts} +0 -0
- /package/src/{StringX/StringX.ts → StringX.ts} +0 -0
- /package/src/{StructX/StructX.ts → StructX.ts} +0 -0
- /package/src/{WarnResult/WarnResult.ts → WarnResult.ts} +0 -0
package/dist/RecordX.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic, framework-agnostic extensions to Effect's `Record` module.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.0.0
|
|
5
|
+
*/
|
|
6
|
+
import { Array, Option, Predicate, Record, pipe } from "effect";
|
|
7
|
+
import { dual } from "effect/Function";
|
|
8
|
+
import * as ArrayX from "./ArrayX.js";
|
|
9
|
+
/**
|
|
10
|
+
* Returns `true` when `record` has at least one entry, narrowing it to a
|
|
11
|
+
* known-non-empty `Record`.
|
|
12
|
+
*
|
|
13
|
+
* The negation of `Record.isEmptyRecord`, packaged as a type guard so it reads
|
|
14
|
+
* naturally at call sites that want to branch on "this record has something in
|
|
15
|
+
* it" without a manual `!Record.isEmptyRecord(...)`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
20
|
+
*
|
|
21
|
+
* assert.deepStrictEqual(RecordX.isNonEmptyRecord({ a: 1 }), true)
|
|
22
|
+
* assert.deepStrictEqual(RecordX.isNonEmptyRecord({}), false)
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @category guards
|
|
26
|
+
* @since 0.0.0
|
|
27
|
+
*/
|
|
28
|
+
export const isNonEmptyRecord = record => pipe(record, Predicate.not(Record.isEmptyRecord));
|
|
29
|
+
/**
|
|
30
|
+
* Modifies the value at `key` in `self` with `f`, leaving the record unchanged
|
|
31
|
+
* if the key doesn't exist.
|
|
32
|
+
*
|
|
33
|
+
* v4's `Record.modify` returns `Option<Record>` — `None` when the key is
|
|
34
|
+
* absent. This helper picks the "do nothing if absent" semantics that v3's
|
|
35
|
+
* `Record.modify` had implicitly, and that most call sites assume. The modifier
|
|
36
|
+
* is never invoked when the key is missing.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { pipe } from "effect"
|
|
41
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
42
|
+
*
|
|
43
|
+
* // data-first
|
|
44
|
+
* assert.deepStrictEqual(
|
|
45
|
+
* RecordX.modifyIfExists({ a: 1, b: 2 }, "b", (n) => n * 10),
|
|
46
|
+
* { a: 1, b: 20 },
|
|
47
|
+
* )
|
|
48
|
+
*
|
|
49
|
+
* // a missing key leaves the record untouched
|
|
50
|
+
* const counts: Record<string, number> = { a: 1, b: 2 }
|
|
51
|
+
* assert.deepStrictEqual(
|
|
52
|
+
* RecordX.modifyIfExists(counts, "missing", (n) => n + 1),
|
|
53
|
+
* { a: 1, b: 2 },
|
|
54
|
+
* )
|
|
55
|
+
*
|
|
56
|
+
* // data-last (pipeable)
|
|
57
|
+
* assert.deepStrictEqual(
|
|
58
|
+
* pipe({ a: 1, b: 2 }, RecordX.modifyIfExists("a", (n) => n * 10)),
|
|
59
|
+
* { a: 10, b: 2 },
|
|
60
|
+
* )
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @category mapping
|
|
64
|
+
* @since 0.0.0
|
|
65
|
+
*/
|
|
66
|
+
export const modifyIfExists = /*#__PURE__*/dual(3, (self, key, f) => pipe(Record.modify(self, key, f), Option.getOrElse(() => self)));
|
|
67
|
+
/**
|
|
68
|
+
* Returns the smallest value of `record` (per `order`) that matches
|
|
69
|
+
* `predicate`, narrowed to the refined type `B`, or `Option.none()` if none
|
|
70
|
+
* match.
|
|
71
|
+
*
|
|
72
|
+
* The `Record` counterpart of `ArrayX.takeFirstWhere`: it considers only the
|
|
73
|
+
* record's values, keeps those satisfying `predicate`, and returns the minimum
|
|
74
|
+
* of them by `order`. Keys are ignored entirely.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { Option, Order, pipe } from "effect"
|
|
79
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
80
|
+
*
|
|
81
|
+
* const isEven = (n: number): n is number => n % 2 === 0
|
|
82
|
+
*
|
|
83
|
+
* // data-first
|
|
84
|
+
* assert.deepStrictEqual(
|
|
85
|
+
* RecordX.takeFirstWhere({ a: 3, b: 4, c: 2 }, isEven, Order.Number),
|
|
86
|
+
* Option.some(2),
|
|
87
|
+
* )
|
|
88
|
+
*
|
|
89
|
+
* // data-last (pipeable); no even values yields None
|
|
90
|
+
* assert.deepStrictEqual(
|
|
91
|
+
* pipe({ a: 1, b: 3 }, RecordX.takeFirstWhere(isEven, Order.Number)),
|
|
92
|
+
* Option.none(),
|
|
93
|
+
* )
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @category getters
|
|
97
|
+
* @since 0.0.0
|
|
98
|
+
*/
|
|
99
|
+
export const takeFirstWhere = /*#__PURE__*/dual(3, (record, predicate, order) => ArrayX.takeFirstWhere(Record.values(record), predicate, order));
|
|
100
|
+
/**
|
|
101
|
+
* Returns the largest value of `record` (per `order`) that matches `predicate`,
|
|
102
|
+
* narrowed to the refined type `B`, or `Option.none()` if none match.
|
|
103
|
+
*
|
|
104
|
+
* The mirror of {@link takeFirstWhere}: it considers only the record's values,
|
|
105
|
+
* keeps those satisfying `predicate`, and returns the maximum of them by
|
|
106
|
+
* `order`. Keys are ignored entirely.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* import { Option, Order, pipe } from "effect"
|
|
111
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
112
|
+
*
|
|
113
|
+
* const isEven = (n: number): n is number => n % 2 === 0
|
|
114
|
+
*
|
|
115
|
+
* // data-first
|
|
116
|
+
* assert.deepStrictEqual(
|
|
117
|
+
* RecordX.takeLastWhere({ a: 3, b: 4, c: 2 }, isEven, Order.Number),
|
|
118
|
+
* Option.some(4),
|
|
119
|
+
* )
|
|
120
|
+
*
|
|
121
|
+
* // data-last (pipeable); no even values yields None
|
|
122
|
+
* assert.deepStrictEqual(
|
|
123
|
+
* pipe({ a: 1, b: 3 }, RecordX.takeLastWhere(isEven, Order.Number)),
|
|
124
|
+
* Option.none(),
|
|
125
|
+
* )
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @category getters
|
|
129
|
+
* @since 0.0.0
|
|
130
|
+
*/
|
|
131
|
+
export const takeLastWhere = /*#__PURE__*/dual(3, (record, predicate, order) => ArrayX.takeLastWhere(Record.values(record), predicate, order));
|
|
132
|
+
/**
|
|
133
|
+
* Returns the largest value of `record` according to `order`, wrapped in an
|
|
134
|
+
* `Option` so empty records are handled safely.
|
|
135
|
+
*
|
|
136
|
+
* Sorts the record's values by `order` and takes the last one, yielding
|
|
137
|
+
* `Option.none()` when the record is empty and `Option.some(max)` otherwise.
|
|
138
|
+
* Keys are ignored — only values participate in the ordering.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* import { Option, Order, pipe } from "effect"
|
|
143
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
144
|
+
*
|
|
145
|
+
* // data-first
|
|
146
|
+
* assert.deepStrictEqual(
|
|
147
|
+
* RecordX.takeLast({ a: 3, b: 5, c: 1 }, Order.Number),
|
|
148
|
+
* Option.some(5),
|
|
149
|
+
* )
|
|
150
|
+
*
|
|
151
|
+
* // data-last (pipeable); empty record yields None
|
|
152
|
+
* assert.deepStrictEqual(
|
|
153
|
+
* pipe({}, RecordX.takeLast(Order.Number)),
|
|
154
|
+
* Option.none(),
|
|
155
|
+
* )
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @category getters
|
|
159
|
+
* @since 0.0.0
|
|
160
|
+
*/
|
|
161
|
+
export const takeLast = /*#__PURE__*/dual(2, (record, order) => pipe(Record.values(record), Array.sort(order), Array.last));
|
|
162
|
+
/**
|
|
163
|
+
* Re-types the keys of a `Record` to a different key type `K2` without touching
|
|
164
|
+
* the runtime value.
|
|
165
|
+
*
|
|
166
|
+
* A purely type-level reinterpretation: the record is returned as-is, but the
|
|
167
|
+
* compiler now treats its keys as `K2` instead of the inferred `K1`. Use it at a
|
|
168
|
+
* boundary where you know the keys conform to a narrower branded or literal key
|
|
169
|
+
* type that TypeScript can't infer from the value alone. It performs no
|
|
170
|
+
* validation — the caller is responsible for the keys actually matching `K2`.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
175
|
+
*
|
|
176
|
+
* type UserId = string & { readonly _brand: "UserId" }
|
|
177
|
+
*
|
|
178
|
+
* const byId: Record<string, number> = { u1: 10, u2: 20 }
|
|
179
|
+
* const branded = RecordX.keysAs<UserId>()(byId)
|
|
180
|
+
*
|
|
181
|
+
* // Same runtime value, keys now seen as `UserId`
|
|
182
|
+
* assert.deepStrictEqual(branded, { u1: 10, u2: 20 })
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* @category conversions
|
|
186
|
+
* @since 0.0.0
|
|
187
|
+
*/
|
|
188
|
+
export const keysAs = () => record => record;
|
|
189
|
+
/**
|
|
190
|
+
* Returns the value at `key` in `record`, throwing an `Error` if the key is
|
|
191
|
+
* absent.
|
|
192
|
+
*
|
|
193
|
+
* The unsafe, get-or-explode counterpart to `Record.get` (which returns an
|
|
194
|
+
* `Option`). The thrown error names the missing key and lists the record's
|
|
195
|
+
* existing keys to aid debugging. Reach for {@link getOrThrowWith} when you need
|
|
196
|
+
* a custom error; prefer `Record.get` whenever absence is a real possibility.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* import { pipe } from "effect"
|
|
201
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
202
|
+
*
|
|
203
|
+
* const record: Record<string, number> = { a: 1, b: 2 }
|
|
204
|
+
*
|
|
205
|
+
* // data-first
|
|
206
|
+
* assert.deepStrictEqual(RecordX.getOrThrow(record, "a"), 1)
|
|
207
|
+
*
|
|
208
|
+
* // data-last (pipeable)
|
|
209
|
+
* assert.deepStrictEqual(pipe(record, RecordX.getOrThrow("b")), 2)
|
|
210
|
+
*
|
|
211
|
+
* // missing key throws
|
|
212
|
+
* assert.throws(() => RecordX.getOrThrow(record, "missing"))
|
|
213
|
+
* ```
|
|
214
|
+
*
|
|
215
|
+
* @category unsafe
|
|
216
|
+
* @since 0.0.0
|
|
217
|
+
*/
|
|
218
|
+
export const getOrThrow = /*#__PURE__*/dual(2, (record, key) => getOrThrowWith(record, key, key => new Error(`Key ${String(key)} not found in record\nExisting keys=${String(Record.keys(record))}`)));
|
|
219
|
+
/**
|
|
220
|
+
* Returns the value at `key` in `record`, throwing the result of `onNone(key)`
|
|
221
|
+
* if the key is absent.
|
|
222
|
+
*
|
|
223
|
+
* Like {@link getOrThrow}, but lets the caller supply the thrown value (an
|
|
224
|
+
* `Error`, a string, or any custom failure) computed from the missing key. Use
|
|
225
|
+
* it when the default "key not found" message isn't descriptive enough for the
|
|
226
|
+
* call site.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* import { pipe } from "effect"
|
|
231
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
232
|
+
*
|
|
233
|
+
* const record: Record<string, number> = { a: 1 }
|
|
234
|
+
* const onNone = (key: string) => new Error(`no ${key}`)
|
|
235
|
+
*
|
|
236
|
+
* // data-first
|
|
237
|
+
* assert.deepStrictEqual(RecordX.getOrThrowWith(record, "a", onNone), 1)
|
|
238
|
+
*
|
|
239
|
+
* // data-last (pipeable)
|
|
240
|
+
* assert.deepStrictEqual(pipe(record, RecordX.getOrThrowWith("a", onNone)), 1)
|
|
241
|
+
*
|
|
242
|
+
* // missing key throws the custom error
|
|
243
|
+
* assert.throws(() => RecordX.getOrThrowWith(record, "missing", onNone))
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @category unsafe
|
|
247
|
+
* @since 0.0.0
|
|
248
|
+
*/
|
|
249
|
+
export const getOrThrowWith = /*#__PURE__*/dual(3, (record, key, onNone) => Record.get(record, key).pipe(Option.getOrThrowWith(() => onNone(key))));
|
|
250
|
+
/**
|
|
251
|
+
* Inserts or updates the value at `key`, deriving the new value from the current
|
|
252
|
+
* one via `upsert`.
|
|
253
|
+
*
|
|
254
|
+
* The `upsert` function receives an `Option` of the existing value —
|
|
255
|
+
* `Option.some(value)` when the key is present, `Option.none()` when it's
|
|
256
|
+
* absent — and returns the value to store. This unifies "insert if missing" and
|
|
257
|
+
* "update if present" into a single pass, with `Option.match` as the natural way
|
|
258
|
+
* to handle both cases.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* import { Option, pipe } from "effect"
|
|
263
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
264
|
+
*
|
|
265
|
+
* const bump = Option.match({
|
|
266
|
+
* onNone: () => 1,
|
|
267
|
+
* onSome: (n: number) => n + 1,
|
|
268
|
+
* })
|
|
269
|
+
*
|
|
270
|
+
* // data-first: updates an existing entry
|
|
271
|
+
* assert.deepStrictEqual(RecordX.upsert({ a: 1 }, "a", bump), { a: 2 })
|
|
272
|
+
*
|
|
273
|
+
* // data-last (pipeable): inserts a missing entry
|
|
274
|
+
* const counts: Record<string, number> = { a: 1 }
|
|
275
|
+
* assert.deepStrictEqual(pipe(counts, RecordX.upsert("b", bump)), {
|
|
276
|
+
* a: 1,
|
|
277
|
+
* b: 1,
|
|
278
|
+
* })
|
|
279
|
+
* ```
|
|
280
|
+
*
|
|
281
|
+
* @category mapping
|
|
282
|
+
* @since 0.0.0
|
|
283
|
+
*/
|
|
284
|
+
export const upsert = /*#__PURE__*/dual(3, (record, key, upsert) => {
|
|
285
|
+
const existingValue = Record.get(record, key);
|
|
286
|
+
const updatedValue = upsert(existingValue);
|
|
287
|
+
return Record.set(record, key, updatedValue);
|
|
288
|
+
});
|
|
289
|
+
/**
|
|
290
|
+
* Indexes an iterable of values into a `Record`, keying each value by the result
|
|
291
|
+
* of `identify`.
|
|
292
|
+
*
|
|
293
|
+
* Builds a lookup table from a collection: every value is stored under the key
|
|
294
|
+
* `identify(value)`. When two values produce the same key the later one wins, so
|
|
295
|
+
* the result holds the last value seen per key. Useful for turning a list of
|
|
296
|
+
* records into a by-id map.
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```ts
|
|
300
|
+
* import { pipe } from "effect"
|
|
301
|
+
* import { RecordX } from "@nunofyobiz/effect-extras"
|
|
302
|
+
*
|
|
303
|
+
* const items = [
|
|
304
|
+
* { id: "a", n: 1 },
|
|
305
|
+
* { id: "b", n: 2 },
|
|
306
|
+
* { id: "a", n: 3 }, // wins over the earlier "a"
|
|
307
|
+
* ]
|
|
308
|
+
*
|
|
309
|
+
* // data-first
|
|
310
|
+
* assert.deepStrictEqual(
|
|
311
|
+
* RecordX.collectBy(items, (item) => item.id),
|
|
312
|
+
* { a: { id: "a", n: 3 }, b: { id: "b", n: 2 } },
|
|
313
|
+
* )
|
|
314
|
+
*
|
|
315
|
+
* // data-last (pipeable)
|
|
316
|
+
* assert.deepStrictEqual(
|
|
317
|
+
* pipe(items, RecordX.collectBy((item) => item.id)),
|
|
318
|
+
* { a: { id: "a", n: 3 }, b: { id: "b", n: 2 } },
|
|
319
|
+
* )
|
|
320
|
+
* ```
|
|
321
|
+
*
|
|
322
|
+
* @category constructors
|
|
323
|
+
* @since 0.0.0
|
|
324
|
+
*/
|
|
325
|
+
export const collectBy = /*#__PURE__*/dual(2, (values, identify) => Array.reduce(values, {}, (accumulator, value) => Record.set(accumulator, identify(value), value)));
|
|
326
|
+
//# sourceMappingURL=RecordX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecordX.js","names":["Array","Option","Predicate","Record","pipe","dual","ArrayX","isNonEmptyRecord","record","not","isEmptyRecord","modifyIfExists","self","key","f","modify","getOrElse","takeFirstWhere","predicate","order","values","takeLastWhere","takeLast","sort","last","keysAs","getOrThrow","getOrThrowWith","Error","String","keys","onNone","get","upsert","existingValue","updatedValue","set","collectBy","identify","reduce","accumulator","value"],"sources":["../src/RecordX.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic, framework-agnostic extensions to Effect's `Result` module.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.0.0
|
|
5
|
+
*/
|
|
6
|
+
import { Option, Result } from "effect";
|
|
7
|
+
/**
|
|
8
|
+
* Lifts an `Option` into a `Result` with a `void` failure: `Some(value)` becomes
|
|
9
|
+
* `Result.succeed(value)` and `None` becomes `Result.failVoid`.
|
|
10
|
+
*
|
|
11
|
+
* Useful in v4 where `Array.filterMap` and `Record.filterMap` expect
|
|
12
|
+
* `Result`-returning predicates (a `Success` keeps the value, a `Failure` drops
|
|
13
|
+
* it); in v3 those APIs accepted `Option`-returning predicates directly:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { Array } from "effect"
|
|
17
|
+
* import { ResultX } from "@nunofyobiz/effect-extras"
|
|
18
|
+
*
|
|
19
|
+
* declare const items: ReadonlyArray<number>
|
|
20
|
+
* declare const maybeTransform: (item: number) => import("effect").Option.Option<string>
|
|
21
|
+
*
|
|
22
|
+
* // v3: Array.filterMap(items, (item) => maybeTransform(item))
|
|
23
|
+
* // v4:
|
|
24
|
+
* Array.filterMap(items, (item) => ResultX.fromOption(maybeTransform(item)))
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Effect ships `Result.fromOption(option, onNone)` which requires a non-`void`
|
|
28
|
+
* failure value; this helper specializes to the common "drop the item, no error
|
|
29
|
+
* needed" case used by `filterMap`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { Option, Result } from "effect"
|
|
34
|
+
* import { ResultX } from "@nunofyobiz/effect-extras"
|
|
35
|
+
*
|
|
36
|
+
* assert.deepStrictEqual(
|
|
37
|
+
* ResultX.fromOption(Option.some(1)),
|
|
38
|
+
* Result.succeed(1),
|
|
39
|
+
* )
|
|
40
|
+
* assert.deepStrictEqual(
|
|
41
|
+
* ResultX.fromOption(Option.none<number>()),
|
|
42
|
+
* Result.failVoid,
|
|
43
|
+
* )
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @category conversions
|
|
47
|
+
* @since 0.0.0
|
|
48
|
+
*/
|
|
49
|
+
export declare const fromOption: <A>(option: Option.Option<A>) => Result.Result<A, void>;
|
|
50
|
+
//# sourceMappingURL=ResultX.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResultX.d.ts","sourceRoot":"","sources":["../src/ResultX.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAC1B,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KACvB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CACgD,CAAC"}
|
package/dist/ResultX.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic, framework-agnostic extensions to Effect's `Result` module.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.0.0
|
|
5
|
+
*/
|
|
6
|
+
import { Option, Result } from "effect";
|
|
7
|
+
/**
|
|
8
|
+
* Lifts an `Option` into a `Result` with a `void` failure: `Some(value)` becomes
|
|
9
|
+
* `Result.succeed(value)` and `None` becomes `Result.failVoid`.
|
|
10
|
+
*
|
|
11
|
+
* Useful in v4 where `Array.filterMap` and `Record.filterMap` expect
|
|
12
|
+
* `Result`-returning predicates (a `Success` keeps the value, a `Failure` drops
|
|
13
|
+
* it); in v3 those APIs accepted `Option`-returning predicates directly:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { Array } from "effect"
|
|
17
|
+
* import { ResultX } from "@nunofyobiz/effect-extras"
|
|
18
|
+
*
|
|
19
|
+
* declare const items: ReadonlyArray<number>
|
|
20
|
+
* declare const maybeTransform: (item: number) => import("effect").Option.Option<string>
|
|
21
|
+
*
|
|
22
|
+
* // v3: Array.filterMap(items, (item) => maybeTransform(item))
|
|
23
|
+
* // v4:
|
|
24
|
+
* Array.filterMap(items, (item) => ResultX.fromOption(maybeTransform(item)))
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Effect ships `Result.fromOption(option, onNone)` which requires a non-`void`
|
|
28
|
+
* failure value; this helper specializes to the common "drop the item, no error
|
|
29
|
+
* needed" case used by `filterMap`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { Option, Result } from "effect"
|
|
34
|
+
* import { ResultX } from "@nunofyobiz/effect-extras"
|
|
35
|
+
*
|
|
36
|
+
* assert.deepStrictEqual(
|
|
37
|
+
* ResultX.fromOption(Option.some(1)),
|
|
38
|
+
* Result.succeed(1),
|
|
39
|
+
* )
|
|
40
|
+
* assert.deepStrictEqual(
|
|
41
|
+
* ResultX.fromOption(Option.none<number>()),
|
|
42
|
+
* Result.failVoid,
|
|
43
|
+
* )
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @category conversions
|
|
47
|
+
* @since 0.0.0
|
|
48
|
+
*/
|
|
49
|
+
export const fromOption = option => Option.isSome(option) ? Result.succeed(option.value) : Result.failVoid;
|
|
50
|
+
//# sourceMappingURL=ResultX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResultX.js","names":["Option","Result","fromOption","option","isSome","succeed","value","failVoid"],"sources":["../src/ResultX.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;AAKA,SAASA,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,OAAO,MAAMC,UAAU,GACrBC,MAAwB,IAExBH,MAAM,CAACI,MAAM,CAACD,MAAM,CAAC,GAAGF,MAAM,CAACI,OAAO,CAACF,MAAM,CAACG,KAAK,CAAC,GAAGL,MAAM,CAACM,QAAQ","ignoreList":[]}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic, framework-agnostic extensions to Effect's `Schema` module.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.0.0
|
|
5
|
+
*/
|
|
6
|
+
import { Schema } 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 declare const TrimmedNonEmptyString: Schema.decodeTo<Schema.toType<Schema.NonEmptyString>, Schema.NonEmptyString, never, never>;
|
|
30
|
+
/**
|
|
31
|
+
* A `Schema` for a URL-safe file path built on top of {@link
|
|
32
|
+
* TrimmedNonEmptyString}.
|
|
33
|
+
*
|
|
34
|
+
* On decode the path is `decodeURIComponent`-ed (percent-escapes are expanded);
|
|
35
|
+
* on encode it is `encodeURIComponent`-ed back into its URL-safe form. The
|
|
36
|
+
* underlying value is also trimmed and required to be non-empty. Use it at the
|
|
37
|
+
* boundary between stored/transmitted encoded paths and the decoded paths your
|
|
38
|
+
* code works with.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { Effect, Schema } from "effect"
|
|
43
|
+
* import { SchemaX } from "@nunofyobiz/effect-extras"
|
|
44
|
+
*
|
|
45
|
+
* // Decoding expands percent-escapes
|
|
46
|
+
* const decoded = Effect.runSync(
|
|
47
|
+
* Schema.decodeEffect(SchemaX.URLSafeFilePath)("a%2Fb.txt"),
|
|
48
|
+
* )
|
|
49
|
+
* assert.deepStrictEqual(decoded, "a/b.txt")
|
|
50
|
+
*
|
|
51
|
+
* // Encoding produces the URL-safe form
|
|
52
|
+
* const encoded = Effect.runSync(
|
|
53
|
+
* Schema.encodeEffect(SchemaX.URLSafeFilePath)("a/b.txt"),
|
|
54
|
+
* )
|
|
55
|
+
* assert.deepStrictEqual(encoded, "a%2Fb.txt")
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @category constructors
|
|
59
|
+
* @since 0.0.0
|
|
60
|
+
*/
|
|
61
|
+
export declare const URLSafeFilePath: Schema.decodeTo<Schema.toType<Schema.decodeTo<Schema.toType<Schema.NonEmptyString>, Schema.NonEmptyString, never, never>>, Schema.decodeTo<Schema.toType<Schema.NonEmptyString>, Schema.NonEmptyString, never, never>, never, never>;
|
|
62
|
+
/**
|
|
63
|
+
* Transforms a `bigint` `Schema` so its value is clamped to be non-negative,
|
|
64
|
+
* mapping any value below `0n` up to `0n` on both decode and encode.
|
|
65
|
+
*
|
|
66
|
+
* Unlike a refinement that *rejects* negative input, this *coerces* it: a
|
|
67
|
+
* negative `bigint` decodes to `0n` rather than failing. Apply it to a `bigint`
|
|
68
|
+
* schema whenever negative magnitudes are meaningless and should be floored at
|
|
69
|
+
* zero rather than treated as errors.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { Effect, Schema } from "effect"
|
|
74
|
+
* import { SchemaX } from "@nunofyobiz/effect-extras"
|
|
75
|
+
*
|
|
76
|
+
* const NonNegative = SchemaX.nonNegativeBigInt(Schema.BigInt)
|
|
77
|
+
*
|
|
78
|
+
* // Negative values are clamped up to 0n
|
|
79
|
+
* assert.deepStrictEqual(
|
|
80
|
+
* Effect.runSync(Schema.decodeEffect(NonNegative)(-5n)),
|
|
81
|
+
* 0n,
|
|
82
|
+
* )
|
|
83
|
+
*
|
|
84
|
+
* // Non-negative values pass through unchanged
|
|
85
|
+
* assert.deepStrictEqual(
|
|
86
|
+
* Effect.runSync(Schema.decodeEffect(NonNegative)(7n)),
|
|
87
|
+
* 7n,
|
|
88
|
+
* )
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @category combinators
|
|
92
|
+
* @since 0.0.0
|
|
93
|
+
*/
|
|
94
|
+
export declare const nonNegativeBigInt: <S extends Schema.Schema<bigint>>(schema: S) => Schema.decodeTo<Schema.toType<S>, S, never, never>;
|
|
95
|
+
/**
|
|
96
|
+
* Extracts the "constructor input" type of a `Schema` — what `.make({...})`
|
|
97
|
+
* accepts.
|
|
98
|
+
*
|
|
99
|
+
* Differs from `S["Type"]` (the decoded type) in that fields carrying
|
|
100
|
+
* constructor defaults (e.g. an `Overrideable` timestamp) are *optional* in
|
|
101
|
+
* `MakeIn` but *required* in `Type`. Use it in signatures that accept a value to
|
|
102
|
+
* construct, so callers can pass a plain object literal without supplying the
|
|
103
|
+
* defaulted, override-branded fields.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { Schema } from "effect"
|
|
108
|
+
* import { SchemaX } from "@nunofyobiz/effect-extras"
|
|
109
|
+
*
|
|
110
|
+
* const Person = Schema.Struct({ name: Schema.String, age: Schema.Number })
|
|
111
|
+
*
|
|
112
|
+
* const input: SchemaX.MakeIn<typeof Person> = { name: "Ada", age: 36 }
|
|
113
|
+
*
|
|
114
|
+
* assert.deepStrictEqual(input, { name: "Ada", age: 36 })
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @category models
|
|
118
|
+
* @since 0.0.0
|
|
119
|
+
*/
|
|
120
|
+
export type MakeIn<S extends {
|
|
121
|
+
readonly "~type.make.in": unknown;
|
|
122
|
+
}> = S["~type.make.in"];
|
|
123
|
+
/**
|
|
124
|
+
* Returns a new `Schema.Struct` containing only the named `keys` of `schema`.
|
|
125
|
+
*
|
|
126
|
+
* Restores the v3 ergonomics of `mySchema.pick("a", "b")` — v4 removed the
|
|
127
|
+
* `.pick(...)` method from `Schema.Struct` instances, so this rebuilds it on top
|
|
128
|
+
* of v4's `mapFields` primitive. Each picked field keeps its original schema,
|
|
129
|
+
* including any refinements.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* import { Effect, Schema } from "effect"
|
|
134
|
+
* import { SchemaX } from "@nunofyobiz/effect-extras"
|
|
135
|
+
*
|
|
136
|
+
* const Source = Schema.Struct({
|
|
137
|
+
* a: Schema.Number,
|
|
138
|
+
* b: Schema.String,
|
|
139
|
+
* c: Schema.Boolean,
|
|
140
|
+
* })
|
|
141
|
+
*
|
|
142
|
+
* const Picked = SchemaX.pick(Source, "a", "b")
|
|
143
|
+
*
|
|
144
|
+
* const decoded = Effect.runSync(
|
|
145
|
+
* Schema.decodeEffect(Picked)({ a: 1, b: "hi" }),
|
|
146
|
+
* )
|
|
147
|
+
* assert.deepStrictEqual(decoded, { a: 1, b: "hi" })
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @category combinators
|
|
151
|
+
* @since 0.0.0
|
|
152
|
+
*/
|
|
153
|
+
export declare const pick: <const Fields extends Schema.Struct.Fields, const Keys extends readonly (keyof Fields & string)[]>(schema: Schema.Struct<Fields>, ...keys: Keys) => Schema.Struct<Pick<Fields, Keys[number]>>;
|
|
154
|
+
/**
|
|
155
|
+
* Returns a new `Schema.Struct` with the named `keys` of `schema` removed.
|
|
156
|
+
*
|
|
157
|
+
* The complement of {@link pick}, restoring the v3 ergonomics of
|
|
158
|
+
* `mySchema.omit("a")` that v4 dropped from `Schema.Struct` instances. Every
|
|
159
|
+
* surviving field keeps its original schema, including any refinements.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* import { Effect, Schema } from "effect"
|
|
164
|
+
* import { SchemaX } from "@nunofyobiz/effect-extras"
|
|
165
|
+
*
|
|
166
|
+
* const Source = Schema.Struct({
|
|
167
|
+
* a: Schema.Number,
|
|
168
|
+
* b: Schema.String,
|
|
169
|
+
* c: Schema.Boolean,
|
|
170
|
+
* })
|
|
171
|
+
*
|
|
172
|
+
* const Omitted = SchemaX.omit(Source, "c")
|
|
173
|
+
*
|
|
174
|
+
* const decoded = Effect.runSync(
|
|
175
|
+
* Schema.decodeEffect(Omitted)({ a: 1, b: "hi" }),
|
|
176
|
+
* )
|
|
177
|
+
* assert.deepStrictEqual(decoded, { a: 1, b: "hi" })
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @category combinators
|
|
181
|
+
* @since 0.0.0
|
|
182
|
+
*/
|
|
183
|
+
export declare const omit: <const Fields extends Schema.Struct.Fields, const Keys extends readonly (keyof Fields & string)[]>(schema: Schema.Struct<Fields>, ...keys: Keys) => Schema.Struct<Omit<Fields, Keys[number]>>;
|
|
184
|
+
/**
|
|
185
|
+
* Returns a new `Schema.Struct` in which every field of `schema` is made
|
|
186
|
+
* optional.
|
|
187
|
+
*
|
|
188
|
+
* Restores the v3 `Schema.partial(mySchema)` behaviour that v4 removed, by
|
|
189
|
+
* wrapping each field in `Schema.optional`. A decoded value may therefore omit
|
|
190
|
+
* any field; fields that *are* present still have to satisfy their original
|
|
191
|
+
* schema, refinements included.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* import { Effect, Schema } from "effect"
|
|
196
|
+
* import { SchemaX } from "@nunofyobiz/effect-extras"
|
|
197
|
+
*
|
|
198
|
+
* const Source = Schema.Struct({ a: Schema.Number, b: Schema.String })
|
|
199
|
+
* const Partial = SchemaX.partial(Source)
|
|
200
|
+
*
|
|
201
|
+
* // All fields may be absent
|
|
202
|
+
* assert.deepStrictEqual(Effect.runSync(Schema.decodeEffect(Partial)({})), {})
|
|
203
|
+
*
|
|
204
|
+
* // A present subset still decodes
|
|
205
|
+
* assert.deepStrictEqual(
|
|
206
|
+
* Effect.runSync(Schema.decodeEffect(Partial)({ a: 1 })),
|
|
207
|
+
* { a: 1 },
|
|
208
|
+
* )
|
|
209
|
+
* ```
|
|
210
|
+
*
|
|
211
|
+
* @category combinators
|
|
212
|
+
* @since 0.0.0
|
|
213
|
+
*/
|
|
214
|
+
export declare const partial: <Fields extends Schema.Struct.Fields>(schema: Schema.Struct<Fields>) => Schema.Struct<{ [K in keyof Fields]: Schema.optional<Fields[K]>; }>;
|
|
215
|
+
/**
|
|
216
|
+
* Returns a new `Schema.Struct` containing only the named `keys` of `schema`,
|
|
217
|
+
* with every picked field made optional.
|
|
218
|
+
*
|
|
219
|
+
* Equivalent to `partial(pick(schema, ...keys))`, but reads more directly for
|
|
220
|
+
* the common "partial update over a subset of fields" pattern: select the
|
|
221
|
+
* mutable fields of an entity, then allow any of them to be omitted in the
|
|
222
|
+
* update payload. Composes {@link pick} and {@link partial} in one call.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* import { Effect, Schema } from "effect"
|
|
227
|
+
* import { SchemaX } from "@nunofyobiz/effect-extras"
|
|
228
|
+
*
|
|
229
|
+
* const Source = Schema.Struct({
|
|
230
|
+
* a: Schema.Number,
|
|
231
|
+
* b: Schema.String,
|
|
232
|
+
* c: Schema.Boolean,
|
|
233
|
+
* })
|
|
234
|
+
*
|
|
235
|
+
* const Update = SchemaX.pickPartial(Source, "a", "b")
|
|
236
|
+
*
|
|
237
|
+
* // Only picked fields are known, and each may be omitted
|
|
238
|
+
* assert.deepStrictEqual(Effect.runSync(Schema.decodeEffect(Update)({})), {})
|
|
239
|
+
* assert.deepStrictEqual(
|
|
240
|
+
* Effect.runSync(Schema.decodeEffect(Update)({ a: 1 })),
|
|
241
|
+
* { a: 1 },
|
|
242
|
+
* )
|
|
243
|
+
* ```
|
|
244
|
+
*
|
|
245
|
+
* @category combinators
|
|
246
|
+
* @since 0.0.0
|
|
247
|
+
*/
|
|
248
|
+
export declare const pickPartial: <const Fields extends Schema.Struct.Fields, const Keys extends readonly (keyof Fields & string)[]>(schema: Schema.Struct<Fields>, ...keys: Keys) => Schema.Struct<{ [K in Keys[number]]: Schema.optional<Fields[K]>; }>;
|
|
249
|
+
//# sourceMappingURL=SchemaX.d.ts.map
|