effect 4.0.0-beta.28 → 4.0.0-beta.29
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/dist/Brand.d.ts +1 -1
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Brand.js +1 -1
- package/dist/Brand.js.map +1 -1
- package/dist/Function.d.ts +1 -9
- package/dist/Function.d.ts.map +1 -1
- package/dist/Function.js +2 -10
- package/dist/Function.js.map +1 -1
- package/dist/Newtype.d.ts +291 -0
- package/dist/Newtype.d.ts.map +1 -0
- package/dist/Newtype.js +161 -0
- package/dist/Newtype.js.map +1 -0
- package/dist/Schema.d.ts +1 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaAST.d.ts.map +1 -1
- package/dist/SchemaAST.js +1 -1
- package/dist/SchemaAST.js.map +1 -1
- package/dist/index.d.ts +69 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -3
- package/dist/index.js.map +1 -1
- package/dist/unstable/cli/Prompt.js +4 -8
- package/dist/unstable/cli/Prompt.js.map +1 -1
- package/dist/unstable/http/HttpClient.d.ts.map +1 -1
- package/dist/unstable/http/HttpClient.js +3 -7
- package/dist/unstable/http/HttpClient.js.map +1 -1
- package/dist/unstable/reactivity/Atom.d.ts +7 -2
- package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
- package/dist/unstable/reactivity/Atom.js +31 -7
- package/dist/unstable/reactivity/Atom.js.map +1 -1
- package/dist/unstable/reactivity/AtomRegistry.js +26 -2
- package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/Brand.ts +1 -1
- package/src/Function.ts +2 -10
- package/src/Newtype.ts +308 -0
- package/src/Schema.ts +1 -1
- package/src/SchemaAST.ts +1 -4
- package/src/index.ts +70 -3
- package/src/unstable/cli/Prompt.ts +4 -9
- package/src/unstable/http/HttpClient.ts +5 -6
- package/src/unstable/reactivity/Atom.ts +45 -12
- package/src/unstable/reactivity/AtomRegistry.ts +30 -2
package/src/Newtype.ts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight wrapper types that prevent accidental mixing of structurally
|
|
3
|
+
* identical values (e.g. `UserId` vs `OrderId`, both `string` at runtime).
|
|
4
|
+
*
|
|
5
|
+
* **Mental model**
|
|
6
|
+
*
|
|
7
|
+
* - **Newtype** — a compile-time wrapper around a **carrier** type (the
|
|
8
|
+
* underlying primitive or object). At runtime the value is unchanged; the
|
|
9
|
+
* tag exists only in the type system.
|
|
10
|
+
* - **Key** — a unique string literal that distinguishes one newtype from
|
|
11
|
+
* another (e.g. `"Label"`, `"UserId"`).
|
|
12
|
+
* - **Carrier** — the underlying type the newtype wraps (e.g. `string`,
|
|
13
|
+
* `number`).
|
|
14
|
+
* - **Iso** — a lossless two-way conversion between a newtype and its carrier,
|
|
15
|
+
* created with {@link makeIso}. Use `iso.set(carrier)` to wrap and
|
|
16
|
+
* `iso.get(newtype)` to unwrap.
|
|
17
|
+
*
|
|
18
|
+
* **Common tasks**
|
|
19
|
+
*
|
|
20
|
+
* - Define a newtype → declare an `interface` extending
|
|
21
|
+
* `Newtype.Newtype<Key, Carrier>`
|
|
22
|
+
* - Wrap / unwrap values → {@link makeIso} (returns an `Optic.Iso`)
|
|
23
|
+
* - Unwrap only → {@link value}
|
|
24
|
+
* - Lift an `Equivalence` → {@link makeEquivalence}
|
|
25
|
+
* - Lift an `Order` → {@link makeOrder}
|
|
26
|
+
* - Lift a `Combiner` → {@link makeCombiner}
|
|
27
|
+
* - Lift a `Reducer` → {@link makeReducer}
|
|
28
|
+
*
|
|
29
|
+
* **Gotchas**
|
|
30
|
+
*
|
|
31
|
+
* - Newtypes are **purely compile-time**. There is zero runtime overhead;
|
|
32
|
+
* `value` and `makeIso` use identity casts.
|
|
33
|
+
* - Two newtypes sharing the same key string will be assignable to each other.
|
|
34
|
+
* Choose unique key strings.
|
|
35
|
+
* - A newtype value is **not** assignable to its carrier type without
|
|
36
|
+
* explicitly unwrapping via {@link value} or an iso.
|
|
37
|
+
*
|
|
38
|
+
* **Quickstart**
|
|
39
|
+
*
|
|
40
|
+
* **Example** (defining and using a newtype)
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { Newtype } from "effect"
|
|
44
|
+
*
|
|
45
|
+
* // 1. Define a newtype
|
|
46
|
+
* interface Label extends Newtype.Newtype<"Label", string> {}
|
|
47
|
+
*
|
|
48
|
+
* // 2. Create an iso for wrapping/unwrapping
|
|
49
|
+
* const labelIso = Newtype.makeIso<Label>()
|
|
50
|
+
*
|
|
51
|
+
* // 3. Wrap a raw string
|
|
52
|
+
* const myLabel: Label = labelIso.set("hello")
|
|
53
|
+
*
|
|
54
|
+
* // 4. Unwrap back to string
|
|
55
|
+
* const raw: string = labelIso.get(myLabel) // "hello"
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* **See also**
|
|
59
|
+
*
|
|
60
|
+
* - {@link Newtype} (the tagged interface)
|
|
61
|
+
* - {@link makeIso} (wrap and unwrap)
|
|
62
|
+
* - {@link value} (unwrap only)
|
|
63
|
+
*
|
|
64
|
+
* @since 4.0.0
|
|
65
|
+
*/
|
|
66
|
+
import type * as Combiner from "./Combiner.ts"
|
|
67
|
+
import type * as Equivalence from "./Equivalence.ts"
|
|
68
|
+
import { cast } from "./Function.ts"
|
|
69
|
+
import * as Optic from "./Optic.ts"
|
|
70
|
+
import type * as Order from "./Order.ts"
|
|
71
|
+
import type * as Reducer from "./Reducer.ts"
|
|
72
|
+
|
|
73
|
+
const TypeId = "~effect/Newtype"
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A tagged interface that wraps a carrier type under a unique key, preventing
|
|
77
|
+
* accidental interchange of structurally identical values.
|
|
78
|
+
*
|
|
79
|
+
* - Define your newtype as an `interface` extending
|
|
80
|
+
* `Newtype<"MyKey", CarrierType>`.
|
|
81
|
+
* - The tag is compile-time only; no runtime wrapper is allocated.
|
|
82
|
+
* - Use {@link makeIso} to create a two-way conversion, or {@link value} to
|
|
83
|
+
* unwrap.
|
|
84
|
+
*
|
|
85
|
+
* **Example** (defining a newtype)
|
|
86
|
+
*
|
|
87
|
+
* ```ts
|
|
88
|
+
* import { Newtype } from "effect"
|
|
89
|
+
*
|
|
90
|
+
* interface UserId extends Newtype.Newtype<"UserId", number> {}
|
|
91
|
+
* interface OrderId extends Newtype.Newtype<"OrderId", number> {}
|
|
92
|
+
*
|
|
93
|
+
* // UserId and OrderId are not assignable to each other
|
|
94
|
+
* // even though both wrap `number`.
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @see {@link makeIso} — create an iso to wrap and unwrap
|
|
98
|
+
* @see {@link value} — unwrap a newtype value
|
|
99
|
+
*
|
|
100
|
+
* @since 4.0.0
|
|
101
|
+
*/
|
|
102
|
+
export interface Newtype<in out Key extends string, out Carrier> {
|
|
103
|
+
readonly [TypeId]: {
|
|
104
|
+
readonly key: Key
|
|
105
|
+
readonly carrier: Carrier
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @since 4.0.0
|
|
111
|
+
*/
|
|
112
|
+
export declare namespace Newtype {
|
|
113
|
+
/**
|
|
114
|
+
* A type that matches any `Newtype`, useful as a generic constraint:
|
|
115
|
+
* `<N extends Newtype.Any>`.
|
|
116
|
+
*
|
|
117
|
+
* @see {@link Newtype} — the base tagged interface
|
|
118
|
+
*
|
|
119
|
+
* @since 4.0.0
|
|
120
|
+
*/
|
|
121
|
+
export type Any = Newtype<any, unknown>
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Extracts the key literal type from a newtype.
|
|
125
|
+
*
|
|
126
|
+
* - Useful in generic code that needs to inspect or constrain the key.
|
|
127
|
+
*
|
|
128
|
+
* @since 4.0.0
|
|
129
|
+
*/
|
|
130
|
+
export type Key<N extends Any> = N extends Newtype<infer Key, unknown> ? Key : never
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Extracts the carrier (underlying) type from a newtype.
|
|
134
|
+
*
|
|
135
|
+
* - Useful when you need to refer to the wrapped type in generic utilities.
|
|
136
|
+
*
|
|
137
|
+
* @since 4.0.0
|
|
138
|
+
*/
|
|
139
|
+
export type Carrier<N extends Any> = N extends Newtype<infer _Key, infer Carrier> ? Carrier : never
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Unwraps a newtype value, returning the underlying carrier value.
|
|
144
|
+
*
|
|
145
|
+
* - Use when you only need to read the inner value and do not need to wrap new
|
|
146
|
+
* values.
|
|
147
|
+
* - For both wrapping and unwrapping, prefer {@link makeIso}.
|
|
148
|
+
* - Zero runtime cost: this is an identity cast.
|
|
149
|
+
*
|
|
150
|
+
* **Example** (unwrapping a newtype)
|
|
151
|
+
*
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { Newtype } from "effect"
|
|
154
|
+
*
|
|
155
|
+
* interface Label extends Newtype.Newtype<"Label", string> {}
|
|
156
|
+
*
|
|
157
|
+
* const iso = Newtype.makeIso<Label>()
|
|
158
|
+
* const label = iso.set("hello")
|
|
159
|
+
*
|
|
160
|
+
* const raw: string = Newtype.value(label) // "hello"
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
* @see {@link makeIso} — two-way conversion (wrap and unwrap)
|
|
164
|
+
*
|
|
165
|
+
* @since 4.0.0
|
|
166
|
+
*/
|
|
167
|
+
export const value: <N extends Newtype.Any>(newtype: N) => Newtype.Carrier<N> = cast
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Creates an `Optic.Iso` for a newtype, providing both wrapping (`set`) and
|
|
171
|
+
* unwrapping (`get`).
|
|
172
|
+
*
|
|
173
|
+
* - Use this as the primary way to construct and deconstruct newtype values.
|
|
174
|
+
* - The returned iso composes with other optics via the standard `Optic` API.
|
|
175
|
+
* - Zero runtime cost: both directions are identity casts.
|
|
176
|
+
*
|
|
177
|
+
* **Example** (wrapping and unwrapping with an iso)
|
|
178
|
+
*
|
|
179
|
+
* ```ts
|
|
180
|
+
* import { Newtype } from "effect"
|
|
181
|
+
*
|
|
182
|
+
* interface Label extends Newtype.Newtype<"Label", string> {}
|
|
183
|
+
*
|
|
184
|
+
* const labelIso = Newtype.makeIso<Label>()
|
|
185
|
+
*
|
|
186
|
+
* const label: Label = labelIso.set("world")
|
|
187
|
+
* const str: string = labelIso.get(label) // "world"
|
|
188
|
+
* ```
|
|
189
|
+
*
|
|
190
|
+
* @see {@link value} — unwrap only
|
|
191
|
+
*
|
|
192
|
+
* @since 4.0.0
|
|
193
|
+
*/
|
|
194
|
+
export function makeIso<N extends Newtype.Any>(): Optic.Iso<N, Newtype.Carrier<N>> {
|
|
195
|
+
return Optic.makeIso(value, cast)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Lifts an `Equivalence` for the carrier type into an `Equivalence` for the
|
|
200
|
+
* newtype.
|
|
201
|
+
*
|
|
202
|
+
* - Use when you need to compare two newtype values for equality.
|
|
203
|
+
* - The returned equivalence delegates to the provided carrier equivalence.
|
|
204
|
+
* - Zero runtime cost beyond the underlying equivalence check.
|
|
205
|
+
*
|
|
206
|
+
* **Example** (comparing newtypes)
|
|
207
|
+
*
|
|
208
|
+
* ```ts
|
|
209
|
+
* import { Newtype, Equivalence } from "effect"
|
|
210
|
+
*
|
|
211
|
+
* interface Label extends Newtype.Newtype<"Label", string> {}
|
|
212
|
+
*
|
|
213
|
+
* const eq = Newtype.makeEquivalence<Label>(Equivalence.String)
|
|
214
|
+
* const iso = Newtype.makeIso<Label>()
|
|
215
|
+
*
|
|
216
|
+
* eq(iso.set("a"), iso.set("a")) // true
|
|
217
|
+
* eq(iso.set("a"), iso.set("b")) // false
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* @see {@link makeOrder} — lift an `Order` for the carrier
|
|
221
|
+
*
|
|
222
|
+
* @since 4.0.0
|
|
223
|
+
*/
|
|
224
|
+
export const makeEquivalence: <N extends Newtype.Any>(
|
|
225
|
+
equivalence: Equivalence.Equivalence<Newtype.Carrier<N>>
|
|
226
|
+
) => Equivalence.Equivalence<N> = cast
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Lifts an `Order` for the carrier type into an `Order` for the newtype.
|
|
230
|
+
*
|
|
231
|
+
* - Use when you need to sort or compare newtype values.
|
|
232
|
+
* - The returned order delegates to the provided carrier order.
|
|
233
|
+
*
|
|
234
|
+
* **Example** (ordering newtypes)
|
|
235
|
+
*
|
|
236
|
+
* ```ts
|
|
237
|
+
* import { Newtype, Order } from "effect"
|
|
238
|
+
*
|
|
239
|
+
* interface Score extends Newtype.Newtype<"Score", number> {}
|
|
240
|
+
*
|
|
241
|
+
* const ord = Newtype.makeOrder<Score>(Order.Number)
|
|
242
|
+
* const iso = Newtype.makeIso<Score>()
|
|
243
|
+
*
|
|
244
|
+
* ord(iso.set(1), iso.set(2)) // -1
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
247
|
+
* @see {@link makeEquivalence} — lift an `Equivalence` for the carrier
|
|
248
|
+
*
|
|
249
|
+
* @since 4.0.0
|
|
250
|
+
*/
|
|
251
|
+
export const makeOrder: <N extends Newtype.Any>(order: Order.Order<Newtype.Carrier<N>>) => Order.Order<N> = cast
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Lifts a `Combiner` for the carrier type into a `Combiner` for the newtype.
|
|
255
|
+
*
|
|
256
|
+
* - Use when you need to combine (e.g. concatenate, add) newtype values.
|
|
257
|
+
* - The returned combiner delegates to the provided carrier combiner.
|
|
258
|
+
*
|
|
259
|
+
* **Example** (combining newtypes)
|
|
260
|
+
*
|
|
261
|
+
* ```ts
|
|
262
|
+
* import { Newtype, Combiner } from "effect"
|
|
263
|
+
*
|
|
264
|
+
* interface Amount extends Newtype.Newtype<"Amount", number> {}
|
|
265
|
+
*
|
|
266
|
+
* const sum = Combiner.make<number>((a, b) => a + b)
|
|
267
|
+
* const combiner = Newtype.makeCombiner<Amount>(sum)
|
|
268
|
+
* const iso = Newtype.makeIso<Amount>()
|
|
269
|
+
*
|
|
270
|
+
* const total = combiner.combine(iso.set(10), iso.set(20))
|
|
271
|
+
* Newtype.value(total) // 30
|
|
272
|
+
* ```
|
|
273
|
+
*
|
|
274
|
+
* @see {@link makeReducer} — lift a `Reducer` for the carrier
|
|
275
|
+
*
|
|
276
|
+
* @since 4.0.0
|
|
277
|
+
*/
|
|
278
|
+
export const makeCombiner: <N extends Newtype.Any>(
|
|
279
|
+
combiner: Combiner.Combiner<Newtype.Carrier<N>>
|
|
280
|
+
) => Combiner.Combiner<N> = cast
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Lifts a `Reducer` for the carrier type into a `Reducer` for the newtype.
|
|
284
|
+
*
|
|
285
|
+
* - Use when you need to fold/reduce over a collection of newtype values.
|
|
286
|
+
* - The returned reducer delegates to the provided carrier reducer.
|
|
287
|
+
*
|
|
288
|
+
* **Example** (reducing newtypes)
|
|
289
|
+
*
|
|
290
|
+
* ```ts
|
|
291
|
+
* import { Newtype, Reducer } from "effect"
|
|
292
|
+
*
|
|
293
|
+
* interface Score extends Newtype.Newtype<"Score", number> {}
|
|
294
|
+
*
|
|
295
|
+
* const sum = Reducer.make<number>((a, b) => a + b, 0)
|
|
296
|
+
* const reducer = Newtype.makeReducer<Score>(sum)
|
|
297
|
+
* const iso = Newtype.makeIso<Score>()
|
|
298
|
+
*
|
|
299
|
+
* const total = reducer.combineAll([iso.set(1), iso.set(2), iso.set(3)])
|
|
300
|
+
* Newtype.value(total) // 6
|
|
301
|
+
* ```
|
|
302
|
+
*
|
|
303
|
+
* @see {@link makeCombiner} — lift a `Combiner` for the carrier
|
|
304
|
+
*
|
|
305
|
+
* @since 4.0.0
|
|
306
|
+
*/
|
|
307
|
+
export const makeReducer: <N extends Newtype.Any>(reducer: Reducer.Reducer<Newtype.Carrier<N>>) => Reducer.Reducer<N> =
|
|
308
|
+
cast
|
package/src/Schema.ts
CHANGED
|
@@ -3590,7 +3590,7 @@ const getUUIDRegExp = (version?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8): globalThis.RegE
|
|
|
3590
3590
|
* @category String checks
|
|
3591
3591
|
* @since 4.0.0
|
|
3592
3592
|
*/
|
|
3593
|
-
export function isUUID(version
|
|
3593
|
+
export function isUUID(version?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8, annotations?: Annotations.Filter) {
|
|
3594
3594
|
const regExp = getUUIDRegExp(version)
|
|
3595
3595
|
return isPattern(
|
|
3596
3596
|
regExp,
|
package/src/SchemaAST.ts
CHANGED
|
@@ -3206,10 +3206,7 @@ export const bigIntString = appendChecks(string, [isStringBigInt()])
|
|
|
3206
3206
|
|
|
3207
3207
|
const bigIntToString = new Link(
|
|
3208
3208
|
bigIntString,
|
|
3209
|
-
|
|
3210
|
-
Getter.transform(globalThis.BigInt),
|
|
3211
|
-
Getter.String()
|
|
3212
|
-
)
|
|
3209
|
+
Transformation.bigintFromString
|
|
3213
3210
|
)
|
|
3214
3211
|
|
|
3215
3212
|
const REGEXP_PATTERN = "Symbol\\((.*)\\)"
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ export {
|
|
|
10
10
|
/**
|
|
11
11
|
* @since 2.0.0
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
cast,
|
|
14
14
|
/**
|
|
15
15
|
* @since 2.0.0
|
|
16
16
|
*/
|
|
@@ -958,12 +958,12 @@ export * as Duration from "./Duration.ts"
|
|
|
958
958
|
* ```ts
|
|
959
959
|
* import { Data, Effect } from "effect"
|
|
960
960
|
*
|
|
961
|
-
* class
|
|
961
|
+
* class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
|
|
962
962
|
*
|
|
963
963
|
* // Effect that may fail
|
|
964
964
|
* const divide = (a: number, b: number) =>
|
|
965
965
|
* b === 0
|
|
966
|
-
* ? Effect.fail(new
|
|
966
|
+
* ? Effect.fail(new DiscountRateError())
|
|
967
967
|
* : Effect.succeed(a / b)
|
|
968
968
|
*
|
|
969
969
|
* // Error handling
|
|
@@ -2314,6 +2314,73 @@ export * as MutableList from "./MutableList.ts"
|
|
|
2314
2314
|
*/
|
|
2315
2315
|
export * as MutableRef from "./MutableRef.ts"
|
|
2316
2316
|
|
|
2317
|
+
/**
|
|
2318
|
+
* Lightweight wrapper types that prevent accidental mixing of structurally
|
|
2319
|
+
* identical values (e.g. `UserId` vs `OrderId`, both `string` at runtime).
|
|
2320
|
+
*
|
|
2321
|
+
* **Mental model**
|
|
2322
|
+
*
|
|
2323
|
+
* - **Newtype** — a compile-time wrapper around a **carrier** type (the
|
|
2324
|
+
* underlying primitive or object). At runtime the value is unchanged; the
|
|
2325
|
+
* tag exists only in the type system.
|
|
2326
|
+
* - **Key** — a unique string literal that distinguishes one newtype from
|
|
2327
|
+
* another (e.g. `"Label"`, `"UserId"`).
|
|
2328
|
+
* - **Carrier** — the underlying type the newtype wraps (e.g. `string`,
|
|
2329
|
+
* `number`).
|
|
2330
|
+
* - **Iso** — a lossless two-way conversion between a newtype and its carrier,
|
|
2331
|
+
* created with {@link makeIso}. Use `iso.set(carrier)` to wrap and
|
|
2332
|
+
* `iso.get(newtype)` to unwrap.
|
|
2333
|
+
*
|
|
2334
|
+
* **Common tasks**
|
|
2335
|
+
*
|
|
2336
|
+
* - Define a newtype → declare an `interface` extending
|
|
2337
|
+
* `Newtype.Newtype<Key, Carrier>`
|
|
2338
|
+
* - Wrap / unwrap values → {@link makeIso} (returns an `Optic.Iso`)
|
|
2339
|
+
* - Unwrap only → {@link value}
|
|
2340
|
+
* - Lift an `Equivalence` → {@link makeEquivalence}
|
|
2341
|
+
* - Lift an `Order` → {@link makeOrder}
|
|
2342
|
+
* - Lift a `Combiner` → {@link makeCombiner}
|
|
2343
|
+
* - Lift a `Reducer` → {@link makeReducer}
|
|
2344
|
+
*
|
|
2345
|
+
* **Gotchas**
|
|
2346
|
+
*
|
|
2347
|
+
* - Newtypes are **purely compile-time**. There is zero runtime overhead;
|
|
2348
|
+
* `value` and `makeIso` use identity casts.
|
|
2349
|
+
* - Two newtypes sharing the same key string will be assignable to each other.
|
|
2350
|
+
* Choose unique key strings.
|
|
2351
|
+
* - A newtype value is **not** assignable to its carrier type without
|
|
2352
|
+
* explicitly unwrapping via {@link value} or an iso.
|
|
2353
|
+
*
|
|
2354
|
+
* **Quickstart**
|
|
2355
|
+
*
|
|
2356
|
+
* **Example** (defining and using a newtype)
|
|
2357
|
+
*
|
|
2358
|
+
* ```ts
|
|
2359
|
+
* import { Newtype } from "effect"
|
|
2360
|
+
*
|
|
2361
|
+
* // 1. Define a newtype
|
|
2362
|
+
* interface Label extends Newtype.Newtype<"Label", string> {}
|
|
2363
|
+
*
|
|
2364
|
+
* // 2. Create an iso for wrapping/unwrapping
|
|
2365
|
+
* const labelIso = Newtype.makeIso<Label>()
|
|
2366
|
+
*
|
|
2367
|
+
* // 3. Wrap a raw string
|
|
2368
|
+
* const myLabel: Label = labelIso.set("hello")
|
|
2369
|
+
*
|
|
2370
|
+
* // 4. Unwrap back to string
|
|
2371
|
+
* const raw: string = labelIso.get(myLabel) // "hello"
|
|
2372
|
+
* ```
|
|
2373
|
+
*
|
|
2374
|
+
* **See also**
|
|
2375
|
+
*
|
|
2376
|
+
* - {@link Newtype} (the tagged interface)
|
|
2377
|
+
* - {@link makeIso} (wrap and unwrap)
|
|
2378
|
+
* - {@link value} (unwrap only)
|
|
2379
|
+
*
|
|
2380
|
+
* @since 4.0.0
|
|
2381
|
+
*/
|
|
2382
|
+
export * as Newtype from "./Newtype.ts"
|
|
2383
|
+
|
|
2317
2384
|
/**
|
|
2318
2385
|
* @since 2.0.0
|
|
2319
2386
|
*
|
|
@@ -3108,10 +3108,6 @@ interface TextState {
|
|
|
3108
3108
|
readonly error: string | undefined
|
|
3109
3109
|
}
|
|
3110
3110
|
|
|
3111
|
-
const getValue = (state: TextState, options: TextOptionsReq): string => {
|
|
3112
|
-
return state.value.length > 0 ? state.value : options.default
|
|
3113
|
-
}
|
|
3114
|
-
|
|
3115
3111
|
const renderClearScreen = Effect.fnUntraced(function*(state: TextState, options: TextOptionsReq) {
|
|
3116
3112
|
const terminal = yield* Terminal.Terminal
|
|
3117
3113
|
const columns = yield* terminal.columns
|
|
@@ -3132,7 +3128,7 @@ const renderTextInput = (
|
|
|
3132
3128
|
submitted: boolean,
|
|
3133
3129
|
renderOptions?: RenderOptions | undefined
|
|
3134
3130
|
) => {
|
|
3135
|
-
const text =
|
|
3131
|
+
const text = nextState.value
|
|
3136
3132
|
if (renderOptions?.plain === true) {
|
|
3137
3133
|
switch (options.type) {
|
|
3138
3134
|
case "hidden": {
|
|
@@ -3269,11 +3265,10 @@ const processTab = (state: TextState, options: TextOptionsReq) => {
|
|
|
3269
3265
|
if (state.value === options.default) {
|
|
3270
3266
|
return Effect.succeed(Action.Beep())
|
|
3271
3267
|
}
|
|
3272
|
-
const value =
|
|
3273
|
-
const cursor = value.length
|
|
3268
|
+
const value = state.value.length === 0 ? options.default : state.value
|
|
3274
3269
|
return Effect.succeed(
|
|
3275
3270
|
Action.NextFrame({
|
|
3276
|
-
state: { ...state, value, cursor, error: undefined }
|
|
3271
|
+
state: { ...state, value, cursor: value.length, error: undefined }
|
|
3277
3272
|
})
|
|
3278
3273
|
)
|
|
3279
3274
|
}
|
|
@@ -3317,7 +3312,7 @@ const handleTextProcess = (options: TextOptionsReq) => {
|
|
|
3317
3312
|
}
|
|
3318
3313
|
case "enter":
|
|
3319
3314
|
case "return": {
|
|
3320
|
-
const value =
|
|
3315
|
+
const value = state.value
|
|
3321
3316
|
return Effect.match(options.validate(value), {
|
|
3322
3317
|
onFailure: (error) =>
|
|
3323
3318
|
Action.NextFrame({
|
|
@@ -6,7 +6,6 @@ import * as Cause from "../../Cause.ts"
|
|
|
6
6
|
import { Clock } from "../../Clock.ts"
|
|
7
7
|
import * as Duration from "../../Duration.ts"
|
|
8
8
|
import * as Effect from "../../Effect.ts"
|
|
9
|
-
import * as Exit from "../../Exit.ts"
|
|
10
9
|
import * as Fiber from "../../Fiber.ts"
|
|
11
10
|
import { constant, constFalse, constTrue, dual, flow, identity } from "../../Function.ts"
|
|
12
11
|
import * as Inspectable from "../../Inspectable.ts"
|
|
@@ -1735,12 +1734,12 @@ class InterruptibleResponse implements HttpClientResponse.HttpClientResponse {
|
|
|
1735
1734
|
get stream() {
|
|
1736
1735
|
return Stream.suspend(() => {
|
|
1737
1736
|
responseRegistry.unregister(this.original)
|
|
1738
|
-
return Stream.
|
|
1739
|
-
|
|
1737
|
+
return Stream.ensuring(
|
|
1738
|
+
this.original.stream,
|
|
1739
|
+
Effect.sync(() => {
|
|
1740
1740
|
this.controller.abort()
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
})
|
|
1741
|
+
})
|
|
1742
|
+
)
|
|
1744
1743
|
})
|
|
1745
1744
|
}
|
|
1746
1745
|
|
|
@@ -25,7 +25,7 @@ import * as Scope from "../../Scope.ts"
|
|
|
25
25
|
import * as ServiceMap from "../../ServiceMap.ts"
|
|
26
26
|
import * as Stream from "../../Stream.ts"
|
|
27
27
|
import * as SubscriptionRef from "../../SubscriptionRef.ts"
|
|
28
|
-
import type { NoInfer } from "../../Types.ts"
|
|
28
|
+
import type { Mutable, NoInfer } from "../../Types.ts"
|
|
29
29
|
import * as KeyValueStore from "../persistence/KeyValueStore.ts"
|
|
30
30
|
import * as AsyncResult from "./AsyncResult.ts"
|
|
31
31
|
import { AtomRegistry } from "./AtomRegistry.ts"
|
|
@@ -56,6 +56,7 @@ export interface Atom<A> extends Pipeable, Inspectable.Inspectable {
|
|
|
56
56
|
readonly refresh?: (f: <A>(atom: Atom<A>) => void) => void
|
|
57
57
|
readonly label?: readonly [name: string, stack: string]
|
|
58
58
|
readonly idleTTL?: number
|
|
59
|
+
readonly initialValueTarget?: Atom<A>
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/**
|
|
@@ -792,7 +793,7 @@ export const context: (options: {
|
|
|
792
793
|
}))
|
|
793
794
|
get.subscribe(atom, (value) => get.setSelf(value))
|
|
794
795
|
return get.once(atom)
|
|
795
|
-
}) as any as A
|
|
796
|
+
}, { initialValueTarget: atom }) as any as A
|
|
796
797
|
return factory
|
|
797
798
|
}
|
|
798
799
|
|
|
@@ -1600,16 +1601,35 @@ export const transform: {
|
|
|
1600
1601
|
* @since 4.0.0
|
|
1601
1602
|
* @category combinators
|
|
1602
1603
|
*/
|
|
1603
|
-
<R extends Atom<any>, B>(
|
|
1604
|
+
<R extends Atom<any>, B>(
|
|
1605
|
+
f: (get: Context, atom: R) => B,
|
|
1606
|
+
options?: {
|
|
1607
|
+
readonly initialValueTarget?: Atom<B> | undefined
|
|
1608
|
+
}
|
|
1609
|
+
): (self: R) => [R] extends [Writable<infer _, infer RW>] ? Writable<B, RW> : Atom<B>
|
|
1604
1610
|
/**
|
|
1605
1611
|
* @since 4.0.0
|
|
1606
1612
|
* @category combinators
|
|
1607
1613
|
*/
|
|
1608
|
-
<R extends Atom<any>, B>(
|
|
1614
|
+
<R extends Atom<any>, B>(
|
|
1615
|
+
self: R,
|
|
1616
|
+
f: (get: Context, atom: R) => B,
|
|
1617
|
+
options?: {
|
|
1618
|
+
readonly initialValueTarget?: Atom<B> | undefined
|
|
1619
|
+
}
|
|
1620
|
+
): [R] extends [Writable<infer _, infer RW>] ? Writable<B, RW> : Atom<B>
|
|
1609
1621
|
} = dual(
|
|
1610
|
-
|
|
1611
|
-
(<A, B>(
|
|
1612
|
-
|
|
1622
|
+
(args) => isAtom(args[0]),
|
|
1623
|
+
(<A, B>(
|
|
1624
|
+
self: Atom<A>,
|
|
1625
|
+
f: (get: Context, atom: Atom<A>, options?: {
|
|
1626
|
+
readonly initialValueTarget?: Atom<B> | undefined
|
|
1627
|
+
}) => B,
|
|
1628
|
+
options?: {
|
|
1629
|
+
readonly initialValueTarget?: Atom<B> | undefined
|
|
1630
|
+
}
|
|
1631
|
+
): Atom<B> => {
|
|
1632
|
+
const atom = removeTtl(
|
|
1613
1633
|
isWritable(self)
|
|
1614
1634
|
? writable(
|
|
1615
1635
|
(get) => f(get, self),
|
|
@@ -1626,9 +1646,22 @@ export const transform: {
|
|
|
1626
1646
|
refresh(self)
|
|
1627
1647
|
}
|
|
1628
1648
|
)
|
|
1629
|
-
)
|
|
1649
|
+
)
|
|
1650
|
+
if (options?.initialValueTarget) {
|
|
1651
|
+
;(atom as Mutable<Atom<B>>).initialValueTarget = getInitialValueTarget(options.initialValueTarget)
|
|
1652
|
+
}
|
|
1653
|
+
return atom
|
|
1654
|
+
}) as any
|
|
1630
1655
|
)
|
|
1631
1656
|
|
|
1657
|
+
const getInitialValueTarget = <A>(atom: Atom<A>): Atom<A> => {
|
|
1658
|
+
let target = atom
|
|
1659
|
+
while (target.initialValueTarget) {
|
|
1660
|
+
target = target.initialValueTarget
|
|
1661
|
+
}
|
|
1662
|
+
return target
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1632
1665
|
/**
|
|
1633
1666
|
* @since 4.0.0
|
|
1634
1667
|
* @category combinators
|
|
@@ -1712,7 +1745,7 @@ export const debounce: {
|
|
|
1712
1745
|
timeout = setTimeout(update, millis) as any
|
|
1713
1746
|
})
|
|
1714
1747
|
return value
|
|
1715
|
-
})
|
|
1748
|
+
}, { initialValueTarget: self })
|
|
1716
1749
|
}
|
|
1717
1750
|
)
|
|
1718
1751
|
|
|
@@ -1748,7 +1781,7 @@ export const withRefresh: {
|
|
|
1748
1781
|
const handle = setTimeout(() => get.refresh(self), millis) as any
|
|
1749
1782
|
get.addFinalizer(() => clearTimeout(handle))
|
|
1750
1783
|
return get(self)
|
|
1751
|
-
})
|
|
1784
|
+
}, { initialValueTarget: self })
|
|
1752
1785
|
}
|
|
1753
1786
|
)
|
|
1754
1787
|
|
|
@@ -1849,7 +1882,7 @@ export const swr: {
|
|
|
1849
1882
|
get.refresh(self)
|
|
1850
1883
|
}
|
|
1851
1884
|
return current
|
|
1852
|
-
})
|
|
1885
|
+
}, { initialValueTarget: self })
|
|
1853
1886
|
}
|
|
1854
1887
|
) as any
|
|
1855
1888
|
|
|
@@ -2088,7 +2121,7 @@ export const makeRefreshOnSignal = <_>(signal: Atom<_>) => <A extends Atom<any>>
|
|
|
2088
2121
|
get.subscribe(signal, (_) => get.refresh(self))
|
|
2089
2122
|
get.subscribe(self, (value) => get.setSelf(value))
|
|
2090
2123
|
return get.once(self)
|
|
2091
|
-
}) as any
|
|
2124
|
+
}, { initialValueTarget: self }) as any
|
|
2092
2125
|
|
|
2093
2126
|
/**
|
|
2094
2127
|
* @since 4.0.0
|
|
@@ -317,7 +317,11 @@ class RegistryImpl implements AtomRegistry {
|
|
|
317
317
|
}
|
|
318
318
|
if (initialValues !== undefined) {
|
|
319
319
|
for (const [atom, value] of initialValues) {
|
|
320
|
-
|
|
320
|
+
let target = atom
|
|
321
|
+
while (target.initialValueTarget) {
|
|
322
|
+
target = target.initialValueTarget
|
|
323
|
+
}
|
|
324
|
+
this.ensureNode(target).setInitialValue(value)
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
327
|
}
|
|
@@ -558,6 +562,7 @@ class NodeImpl<A> {
|
|
|
558
562
|
state: NodeState = NodeState.uninitialized
|
|
559
563
|
lifetime: Lifetime<A> | undefined
|
|
560
564
|
writeContext: WriteContextImpl<A>
|
|
565
|
+
preserveInitialValueOnBuild = false
|
|
561
566
|
|
|
562
567
|
parents: Array<NodeImpl<any>> = []
|
|
563
568
|
previousParents: Array<NodeImpl<any>> | undefined
|
|
@@ -588,7 +593,12 @@ class NodeImpl<A> {
|
|
|
588
593
|
this.lifetime = makeLifetime(this)
|
|
589
594
|
const value = this.atom.read(this.lifetime)
|
|
590
595
|
if ((this.state & NodeFlags.waitingForValue) !== 0) {
|
|
591
|
-
this.
|
|
596
|
+
if (this.preserveInitialValueOnBuild) {
|
|
597
|
+
this.preserveInitialValueOnBuild = false
|
|
598
|
+
this.state = NodeState.valid
|
|
599
|
+
} else {
|
|
600
|
+
this.setValue(value)
|
|
601
|
+
}
|
|
592
602
|
}
|
|
593
603
|
|
|
594
604
|
if (this.previousParents) {
|
|
@@ -613,6 +623,24 @@ class NodeImpl<A> {
|
|
|
613
623
|
return Option.some(this._value)
|
|
614
624
|
}
|
|
615
625
|
|
|
626
|
+
setInitialValue(value: A): void {
|
|
627
|
+
if ((this.state & NodeFlags.initialized) === 0) {
|
|
628
|
+
this.preserveInitialValueOnBuild = true
|
|
629
|
+
this.state = NodeState.stale
|
|
630
|
+
this._value = value
|
|
631
|
+
|
|
632
|
+
if (batchState.phase === BatchPhase.collect) {
|
|
633
|
+
batchState.notify.add(this)
|
|
634
|
+
} else {
|
|
635
|
+
this.notify()
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
this.setValue(value)
|
|
642
|
+
}
|
|
643
|
+
|
|
616
644
|
setValue(value: A): void {
|
|
617
645
|
if ((this.state & NodeFlags.initialized) === 0) {
|
|
618
646
|
this.state = NodeState.valid
|