effect-lens 0.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 +237 -0
- package/dist/Lens.d.ts +194 -0
- package/dist/Lens.js +146 -0
- package/dist/Lens.js.map +1 -0
- package/dist/PropertyPath.d.ts +23 -0
- package/dist/PropertyPath.js +30 -0
- package/dist/PropertyPath.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Effect Lens
|
|
2
|
+
|
|
3
|
+
A Lens type for [Effect](https://effect.website/) to easily manage nested state.
|
|
4
|
+
|
|
5
|
+
A proper documentation is currently being written. In the meantime, you can take a look at the quickstart below and at the `packages/example` directory.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
```
|
|
9
|
+
npm install effect-lens
|
|
10
|
+
yarn add effect-lens
|
|
11
|
+
bun add effect-lens
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Peer dependencies
|
|
15
|
+
- `effect` 3.21+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
A Lens is an effectful abstraction for focusing on (i.e., getting, subscribing to, setting, or modifying) a specific part of a larger immutable data structure, without losing the surrounding context.
|
|
21
|
+
|
|
22
|
+
Picture it as a proxy to a separate data source with a similar API to Effect's `SubscriptionRef`, that can point to
|
|
23
|
+
a nested part of a data structure (I.E.: a `SubscriptionRef` holds an array of numbers, a Lens can proxy that array
|
|
24
|
+
or the number at a specific index).
|
|
25
|
+
|
|
26
|
+
What makes a Lens effectful is the fact that the proxy logic uses effects, which means reading from or writing to a
|
|
27
|
+
Lens can fail or have requirements:
|
|
28
|
+
```typescript
|
|
29
|
+
Lens<
|
|
30
|
+
A, // Type of the value the lens is focused on
|
|
31
|
+
ER, // Errors that can happen when reading
|
|
32
|
+
EW, // Errors that can happen when writing
|
|
33
|
+
RE, // Requirements for reading
|
|
34
|
+
RW // Requirements for writing
|
|
35
|
+
>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Creating a Lens
|
|
40
|
+
|
|
41
|
+
#### From an exisiting type
|
|
42
|
+
We provide a few helpers to create Lenses from some Effect types:
|
|
43
|
+
```typescript
|
|
44
|
+
// The ref is the data source
|
|
45
|
+
const ref = yield* SubscriptionRef.make([12, 87, 69])
|
|
46
|
+
|
|
47
|
+
// The lens acts as a proxy that allows reading, subscribing from and writing to that
|
|
48
|
+
// data source with a similar API to Effect's SubscriptionRef
|
|
49
|
+
const lens = Lens.fromSubscriptionRef(ref)
|
|
50
|
+
// ^ Lens.Lens<number[], never, never, never, never>
|
|
51
|
+
|
|
52
|
+
const value = yield* Lens.get(lens)
|
|
53
|
+
yield* Effect.forkScoped(Stream.runForEach(lens.changes, Console.log))
|
|
54
|
+
yield* Lens.update(lens, Array.replace(1, 1664))
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Currently available:
|
|
58
|
+
- `fromSubscriptionRef`
|
|
59
|
+
- `fromSynchronizedRef` (note: since `SynchronizedRef` is not reactive (does not produces a stream of value changes), the resulting Lens' `changes` stream will only emit the current value of the lens when evaluated, and nothing else)
|
|
60
|
+
|
|
61
|
+
More to come!
|
|
62
|
+
|
|
63
|
+
#### Manually
|
|
64
|
+
You can also create Lenses manually using `make` by providing a getter, a stream of changes and either a `set` or `modify` function depending on your needs.
|
|
65
|
+
|
|
66
|
+
You can get pretty creative! Here's an example of a Lens that points to a specific key of the browser `LocalStorage`:
|
|
67
|
+
```typescript
|
|
68
|
+
// \/ Lens<Option.Option<string>, PlatformError, PlatformError, never, never>
|
|
69
|
+
const lens = Effect.all([
|
|
70
|
+
KeyValueStore.KeyValueStore,
|
|
71
|
+
Effect.succeed("someKey"),
|
|
72
|
+
]).pipe(
|
|
73
|
+
Effect.map(([kv, key]) => Lens.make({
|
|
74
|
+
get: kv.get(key),
|
|
75
|
+
|
|
76
|
+
changes: kv.get(key).pipe(
|
|
77
|
+
Effect.map(Stream.make),
|
|
78
|
+
Effect.map(a => Stream.concat(
|
|
79
|
+
a,
|
|
80
|
+
BrowserStream.fromEventListenerWindow("storage").pipe(
|
|
81
|
+
Stream.filter(event => event.key === key),
|
|
82
|
+
Stream.map(event => Option.fromNullable(event.newValue)),
|
|
83
|
+
),
|
|
84
|
+
)),
|
|
85
|
+
Stream.unwrap,
|
|
86
|
+
),
|
|
87
|
+
|
|
88
|
+
set: a => Option.isSome(a)
|
|
89
|
+
? kv.set(key, a.value)
|
|
90
|
+
: kv.remove(key),
|
|
91
|
+
})),
|
|
92
|
+
|
|
93
|
+
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
|
|
94
|
+
Lens.unwrap,
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Note: while Lens supports asynchronous effects for the proxy logic, we would recommend keeping them synchronous to preserve atomicity.
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
### Focusing
|
|
102
|
+
|
|
103
|
+
Lenses can focus on a nested part of the data type they point to.
|
|
104
|
+
|
|
105
|
+
What does this mean? Let's say you have a Lens with this signature:
|
|
106
|
+
```typescript
|
|
107
|
+
Lens<{ readonly a: string, readonly b: number }, never, never, never, never>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
*Focusing this Lens on `a`* means deriving a new Lens that points to the `a` field of the struct the current Lens points to, resulting in a:
|
|
111
|
+
```typescript
|
|
112
|
+
Lens<string, never, never, never, never>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Focuses Lenses work just the same as a Lens that points directly to a data source and can be read, subscribed to or written to.
|
|
116
|
+
|
|
117
|
+
Writing to them will properly update parent Lenses or data sources. Such updates can be performed in both a mutable or an immutable manner depending on your choice.
|
|
118
|
+
|
|
119
|
+
This is a very powerful pattern as it enables you to keep your state in some shared data store while allowing you to pass specific parts of that state to some parts of your application. Very useful for frontend development!
|
|
120
|
+
|
|
121
|
+
#### Using built-in transforms
|
|
122
|
+
We provide a few helpers to create focused Lenses:
|
|
123
|
+
```typescript
|
|
124
|
+
interface User {
|
|
125
|
+
readonly name: string
|
|
126
|
+
readonly age: DateTime.Utc
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// The state of your app
|
|
130
|
+
const ref = yield* SubscriptionRef.make<{
|
|
131
|
+
readonly users: readonly User[]
|
|
132
|
+
}>({
|
|
133
|
+
users: [
|
|
134
|
+
{ name: "Jean Dupont", age: yield* DateTime.make("03/25/1969") },
|
|
135
|
+
{ name: "Juan Joya Borja", age: yield* DateTime.make("04/05/1956") },
|
|
136
|
+
{ name: "Benzemonstre", age: yield* DateTime.make("06/12/2000") },
|
|
137
|
+
]
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// \/ Lens<User, NoSuchElementException, NoSuchElementException, never, never>
|
|
141
|
+
const jeanDupontLens = ref.pipe(
|
|
142
|
+
Lens.fromSubscriptionRef, // Creates a lens that proxies the ref
|
|
143
|
+
Lens.focusField("users"), // Creates a focused lens that points to the users field
|
|
144
|
+
Lens.focusArrayAt(0), // Creates a focused lens that points to the first entry of the user array
|
|
145
|
+
)
|
|
146
|
+
// Reading or writing from this lense can fail with NoSuchElementException
|
|
147
|
+
// This is because of Lens.focusArrayAt(0), as reading and writing to an array is an unsafe operation
|
|
148
|
+
|
|
149
|
+
const jeanDupont = yield* Lens.get(jeanDupontLens)
|
|
150
|
+
|
|
151
|
+
yield* Lens.set(
|
|
152
|
+
// You can focus even further down
|
|
153
|
+
Lens.focusField(jeanDupontLens, "age"),
|
|
154
|
+
yield* DateTime.make("03/25/1970"),
|
|
155
|
+
)
|
|
156
|
+
// Mutations with the parent state are performed immutably by default
|
|
157
|
+
// unless you use a specific mutable transform such as 'focusMutableField'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Currently available:
|
|
161
|
+
| Name | Description | Parent state mutation behavior | Notes |
|
|
162
|
+
| - | - | - | - |
|
|
163
|
+
| `focusField` | Focuses to the field of an object. Replaces the parent object immutably when writing to the focused field | Immutable | |
|
|
164
|
+
| `focusMutableField` | Focuses to the field of an object. Mutates the parent object in place via the writable field | Mutable | Type-safe: will not allow you to mutate `readonly` fields |
|
|
165
|
+
| `focusArrayAt` | Focuses to an indexed entry of an array. Replaces the parent array immutably when writing to the focused index | Immutable | |
|
|
166
|
+
| `focusMutableArrayAt` | Focuses to an indexed entry of an array. Mutates the parent array in place at the focused index | Mutable | Type-safe: will not allow you to mutate `readonly` arrays |
|
|
167
|
+
| `focusChunkAt` | Focuses to an indexed entry of a `Chunk`. Replaces the parent `Chunk` immutably when writing to the focused element | Immutable | |
|
|
168
|
+
|
|
169
|
+
Also more to come!
|
|
170
|
+
|
|
171
|
+
#### Manually
|
|
172
|
+
You can create focused Lenses by composing them manually using `map`, `mapEffect` and `unwrap`:
|
|
173
|
+
```typescript
|
|
174
|
+
interface User {
|
|
175
|
+
readonly name: string
|
|
176
|
+
readonly age: DateTime.Utc
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const ref = yield* SubscriptionRef.make<readonly User[]>([
|
|
180
|
+
{ name: "Jean Dupont", age: yield* DateTime.make("03/25/1969") },
|
|
181
|
+
{ name: "Juan Joya Borja", age: yield* DateTime.make("04/05/1956") },
|
|
182
|
+
{ name: "Benzemonstre", age: yield* DateTime.make("06/12/2000") },
|
|
183
|
+
])
|
|
184
|
+
|
|
185
|
+
// \/ Lens<User, NoSuchElementException, NoSuchElementException, never, never>
|
|
186
|
+
const benzemonstreLens = ref.pipe(
|
|
187
|
+
Lens.fromSubscriptionRef,
|
|
188
|
+
|
|
189
|
+
// Manually focus
|
|
190
|
+
Lens.mapEffect(
|
|
191
|
+
// Getter:
|
|
192
|
+
Array.get(2),
|
|
193
|
+
// Setter:
|
|
194
|
+
(a, b) => Array.replaceOption(a, 2, b),
|
|
195
|
+
// ^ The current Lens value (readonly User[])
|
|
196
|
+
// ^ The new focused value to push (User)
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
// Both Array.get and Array.replaceOption return an Option
|
|
200
|
+
// When evaluated by the lens, Option<A> becomes Effect<A, NoSuchElementException>
|
|
201
|
+
// As you can see, this is automatically tracked by the Lens type
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
### Subscribable
|
|
206
|
+
|
|
207
|
+
Lens implements both Effect's `Subscribable` and `Readable`, which you can use as a constraint to allow some parts of your app to only read and subscribe to the Lenses you provide them:
|
|
208
|
+
```typescript
|
|
209
|
+
const ref = yield* SubscriptionRef.make<{
|
|
210
|
+
readonly users: readonly User[]
|
|
211
|
+
}>({ users: [...] })
|
|
212
|
+
|
|
213
|
+
const someFunctionThatShouldOnlyHaveReadonlyAccessToTheState = (
|
|
214
|
+
usersSub: Subscribable.Subscribable<readonly User[], never, never>
|
|
215
|
+
) => Effect.gen(function*() {
|
|
216
|
+
// Do whatever
|
|
217
|
+
const usersCountSub = Subscribable.map(usersSub, a => a.length)
|
|
218
|
+
const users = yield* usersSub.get
|
|
219
|
+
yield* Effect.forkScoped(Stream.runForEach(users.changes, ...))
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const lens = ref.pipe(
|
|
223
|
+
Lens.fromSubscriptionRef,
|
|
224
|
+
Lens.focusField("users"),
|
|
225
|
+
)
|
|
226
|
+
yield* someFunctionThatShouldOnlyHaveReadonlyAccessToTheState(lens)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
## Todo
|
|
231
|
+
|
|
232
|
+
This library is already ready to use! However, there is always more to do...
|
|
233
|
+
- Finish those docs
|
|
234
|
+
- Provide an API reference
|
|
235
|
+
- Add new adapters for various data source types
|
|
236
|
+
- Add new focus transforms
|
|
237
|
+
- Provide a preview version for Effect 4 beta
|
package/dist/Lens.d.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { Chunk, Effect, Pipeable, Readable, Stream, Subscribable, type SubscriptionRef, type SynchronizedRef } from "effect";
|
|
2
|
+
import type { NoSuchElementException } from "effect/Cause";
|
|
3
|
+
export declare const LensTypeId: unique symbol;
|
|
4
|
+
export type LensTypeId = typeof LensTypeId;
|
|
5
|
+
/**
|
|
6
|
+
* A bidirectional view into some shared state that exposes:
|
|
7
|
+
*
|
|
8
|
+
* 1. a `get` effect for reading the current value of type `A`,
|
|
9
|
+
* 2. a `changes` stream that emits every subsequent update to `A`, and
|
|
10
|
+
* 3. a `modify` effect that can transform the current value.
|
|
11
|
+
*/
|
|
12
|
+
export interface Lens<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never> extends Subscribable.Subscribable<A, ER, RR> {
|
|
13
|
+
readonly [LensTypeId]: LensTypeId;
|
|
14
|
+
readonly modify: <B, E1 = never, R1 = never>(f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>;
|
|
15
|
+
}
|
|
16
|
+
declare const LensImpl_base: Pipeable.PipeableConstructor;
|
|
17
|
+
/**
|
|
18
|
+
* Internal `Lens` implementation.
|
|
19
|
+
*/
|
|
20
|
+
export declare class LensImpl<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never> extends LensImpl_base implements Lens<A, ER, EW, RR, RW> {
|
|
21
|
+
readonly get: Effect.Effect<A, ER, RR>;
|
|
22
|
+
readonly changes: Stream.Stream<A, ER, RR>;
|
|
23
|
+
readonly modify: <B, E1 = never, R1 = never>(f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>;
|
|
24
|
+
readonly [Readable.TypeId]: Readable.TypeId;
|
|
25
|
+
readonly [Subscribable.TypeId]: Subscribable.TypeId;
|
|
26
|
+
readonly [LensTypeId]: LensTypeId;
|
|
27
|
+
constructor(get: Effect.Effect<A, ER, RR>, changes: Stream.Stream<A, ER, RR>, modify: <B, E1 = never, R1 = never>(f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Checks whether a value is a `Lens`.
|
|
31
|
+
*/
|
|
32
|
+
export declare const isLens: (u: unknown) => u is Lens<unknown, unknown, unknown, unknown, unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* Creates a `Lens` by supplying how to read the current value, observe changes, and apply transformations.
|
|
35
|
+
*
|
|
36
|
+
* Either `modify` or `set` needs to be supplied.
|
|
37
|
+
*/
|
|
38
|
+
export declare const make: <A, ER, EW, RR, RW>(options: {
|
|
39
|
+
readonly get: Effect.Effect<A, ER, RR>;
|
|
40
|
+
readonly changes: Stream.Stream<A, ER, RR>;
|
|
41
|
+
} & ({
|
|
42
|
+
readonly modify: <B, E1 = never, R1 = never>(f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>;
|
|
43
|
+
} | {
|
|
44
|
+
readonly set: (a: A) => Effect.Effect<void, EW, RW>;
|
|
45
|
+
})) => Lens<A, ER, EW, RR, RW>;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a `Lens` that proxies a `SubscriptionRef`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const fromSubscriptionRef: <A>(ref: SubscriptionRef.SubscriptionRef<A>) => Lens<A, never, never, never, never>;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a `Lens` that proxies a `SynchronizedRef`.
|
|
52
|
+
*
|
|
53
|
+
* Note: since `SynchronizedRef` does not provide any kind of reactivity mechanism, the produced `Lens` will be non-reactive.
|
|
54
|
+
* This means its `changes` stream will only emit the current value once when evaluated and nothing else.
|
|
55
|
+
*/
|
|
56
|
+
export declare const fromSynchronizedRef: <A>(ref: SynchronizedRef.SynchronizedRef<A>) => Lens<A, never, never, never, never>;
|
|
57
|
+
/**
|
|
58
|
+
* Flattens an effectful `Lens`.
|
|
59
|
+
*/
|
|
60
|
+
export declare const unwrap: <A, ER, EW, RR, RW, E1, R1>(effect: Effect.Effect<Lens<A, ER, EW, RR, RW>, E1, R1>) => Lens<A, ER | E1, EW | E1, RR | R1, RW | R1>;
|
|
61
|
+
/**
|
|
62
|
+
* Derives a new `Lens` by applying synchronous getters and setters over the focused value.
|
|
63
|
+
*/
|
|
64
|
+
export declare const map: {
|
|
65
|
+
<A, ER, EW, RR, RW, B>(self: Lens<A, ER, EW, RR, RW>, get: (a: NoInfer<A>) => B, set: (a: NoInfer<A>, b: B) => NoInfer<A>): Lens<B, ER, EW, RR, RW>;
|
|
66
|
+
<A, ER, EW, RR, RW, B>(get: (a: NoInfer<A>) => B, set: (a: NoInfer<A>, b: B) => NoInfer<A>): (self: Lens<A, ER, EW, RR, RW>) => Lens<B, ER, EW, RR, RW>;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Derives a new `Lens` by applying effectful getters and setters over the focused value.
|
|
70
|
+
*/
|
|
71
|
+
export declare const mapEffect: {
|
|
72
|
+
<A, ER, EW, RR, RW, B, EGet = never, RGet = never, ESet = never, RSet = never>(self: Lens<A, ER, EW, RR, RW>, get: (a: NoInfer<A>) => Effect.Effect<B, EGet, RGet>, set: (a: NoInfer<A>, b: B) => Effect.Effect<NoInfer<A>, ESet, RSet>): Lens<B, ER | EGet, EW | ESet, RR | RGet, RW | RSet>;
|
|
73
|
+
<A, ER, EW, RR, RW, B, EGet = never, RGet = never, ESet = never, RSet = never>(get: (a: NoInfer<A>) => Effect.Effect<B, EGet, RGet>, set: (a: NoInfer<A>, b: B) => Effect.Effect<NoInfer<A>, ESet, RSet>): (self: Lens<A, ER, EW, RR, RW>) => Lens<B, ER | EGet, EW | ESet, RR | RGet, RW | RSet>;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Allows transforming only the `changes` stream of a `Lens` while keeping the focus type intact.
|
|
77
|
+
*/
|
|
78
|
+
export declare const mapStream: {
|
|
79
|
+
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (changes: Stream.Stream<NoInfer<A>, NoInfer<ER>, NoInfer<RR>>) => Stream.Stream<NoInfer<A>, NoInfer<ER>, NoInfer<RR>>): Lens<A, ER, EW, RR, RW>;
|
|
80
|
+
<A, ER, EW, RR, RW>(f: (changes: Stream.Stream<NoInfer<A>, NoInfer<ER>, NoInfer<RR>>) => Stream.Stream<NoInfer<A>, NoInfer<ER>, NoInfer<RR>>): (self: Lens<A, ER, EW, RR, RW>) => Lens<A, ER, EW, RR, RW>;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Narrows the focus to a field of an object. Replaces the object in an immutable fashion when written to.
|
|
84
|
+
*/
|
|
85
|
+
export declare const focusField: {
|
|
86
|
+
<A extends object, K extends keyof A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, key: K): Lens<A[K], ER, EW, RR, RW>;
|
|
87
|
+
<A extends object, K extends keyof A, ER, EW, RR, RW>(key: K): (self: Lens<A, ER, EW, RR, RW>) => Lens<A[K], ER, EW, RR, RW>;
|
|
88
|
+
};
|
|
89
|
+
export declare namespace focusMutableField {
|
|
90
|
+
type WritableKeys<T> = {
|
|
91
|
+
[K in keyof T]-?: IfEquals<{
|
|
92
|
+
[P in K]: T[K];
|
|
93
|
+
}, {
|
|
94
|
+
-readonly [P in K]: T[K];
|
|
95
|
+
}, K, never>;
|
|
96
|
+
}[keyof T];
|
|
97
|
+
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? A : B;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Narrows the focus to a mutable field of an object. Mutates the object in place when written to.
|
|
101
|
+
*/
|
|
102
|
+
export declare const focusMutableField: {
|
|
103
|
+
<A extends object, K extends focusMutableField.WritableKeys<A>, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, key: K): Lens<A[K], ER, EW, RR, RW>;
|
|
104
|
+
<A extends object, K extends focusMutableField.WritableKeys<A>, ER, EW, RR, RW>(key: K): (self: Lens<A, ER, EW, RR, RW>) => Lens<A[K], ER, EW, RR, RW>;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Narrows the focus to an indexed element of an array. Replaces the array in an immutable fashion when written to.
|
|
108
|
+
*/
|
|
109
|
+
export declare const focusArrayAt: {
|
|
110
|
+
<A extends readonly any[], ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, index: number): Lens<A[number]>;
|
|
111
|
+
<A extends readonly any[], ER, EW, RR, RW>(index: number): (self: Lens<A, ER, EW, RR, RW>) => Lens<A[number], ER | NoSuchElementException, EW | NoSuchElementException, RR, RW>;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Narrows the focus to an indexed element of a mutable array. Mutates the array in place when written to.
|
|
115
|
+
*/
|
|
116
|
+
export declare const focusMutableArrayAt: {
|
|
117
|
+
<A, ER, EW, RR, RW>(self: Lens<A[], ER, EW, RR, RW>, index: number): Lens<A, ER | NoSuchElementException, EW | NoSuchElementException, RR, RW>;
|
|
118
|
+
<A, ER, EW, RR, RW>(index: number): (self: Lens<A[], ER, EW, RR, RW>) => Lens<A, ER | NoSuchElementException, EW | NoSuchElementException, RR, RW>;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Narrows the focus to an indexed element of `Chunk`. Replaces the `Chunk` in an immutable fashion when written to.
|
|
122
|
+
*/
|
|
123
|
+
export declare const focusChunkAt: {
|
|
124
|
+
<A, ER, EW, RR, RW>(self: Lens<Chunk.Chunk<A>, ER, EW, RR, RW>, index: number): Lens<A, ER | NoSuchElementException, EW, RR, RW>;
|
|
125
|
+
<A, ER, EW, RR, RW>(index: number): (self: Lens<Chunk.Chunk<A>, ER, EW, RR, RW>) => Lens<A, ER | NoSuchElementException, EW, RR, RW>;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Reads the current value from a `Lens`.
|
|
129
|
+
*/
|
|
130
|
+
export declare const get: <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER, RR>;
|
|
131
|
+
/**
|
|
132
|
+
* Sets the value of a `Lens`.
|
|
133
|
+
*/
|
|
134
|
+
export declare const set: {
|
|
135
|
+
<A, ER, EW, RR, RW>(value: A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<void, ER | EW, RR | RW>;
|
|
136
|
+
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A): Effect.Effect<void, ER | EW, RR | RW>;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Sets a `Lens` to a new value and returns the previous value.
|
|
140
|
+
*/
|
|
141
|
+
export declare const getAndSet: {
|
|
142
|
+
<A, ER, EW, RR, RW>(value: A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>;
|
|
143
|
+
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A): Effect.Effect<A, ER | EW, RR | RW>;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Applies a synchronous transformation to the value of a `Lens`, discarding the previous value.
|
|
147
|
+
*/
|
|
148
|
+
export declare const update: {
|
|
149
|
+
<A, ER, EW, RR, RW>(f: (a: A) => A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<void, ER | EW, RR | RW>;
|
|
150
|
+
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A): Effect.Effect<void, ER | EW, RR | RW>;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Applies an effectful transformation to the value of a `Lens`, discarding the previous value.
|
|
154
|
+
*/
|
|
155
|
+
export declare const updateEffect: {
|
|
156
|
+
<A, ER, EW, RR, RW, E, R>(f: (a: A) => Effect.Effect<A, E, R>): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<void, ER | EW | E, RR | RW | R>;
|
|
157
|
+
<A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<void, ER | EW | E, RR | RW | R>;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Applies a synchronous transformation the value of a `Lens` while returning the previous value.
|
|
161
|
+
*/
|
|
162
|
+
export declare const getAndUpdate: {
|
|
163
|
+
<A, ER, EW, RR, RW>(f: (a: A) => A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>;
|
|
164
|
+
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A): Effect.Effect<A, ER | EW, RR | RW>;
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Applies an effectful transformation the value of a `Lens` while returning the previous value.
|
|
168
|
+
*/
|
|
169
|
+
export declare const getAndUpdateEffect: {
|
|
170
|
+
<A, ER, EW, RR, RW, E, R>(f: (a: A) => Effect.Effect<A, E, R>): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW | E, RR | RW | R>;
|
|
171
|
+
<A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, ER | EW | E, RR | RW | R>;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Sets the value of a `Lens` and returns the new value.
|
|
175
|
+
*/
|
|
176
|
+
export declare const setAndGet: {
|
|
177
|
+
<A, ER, EW, RR, RW>(value: A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>;
|
|
178
|
+
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A): Effect.Effect<A, ER | EW, RR | RW>;
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Applies a synchronous update the value of a `Lens` and returns the new value.
|
|
182
|
+
*/
|
|
183
|
+
export declare const updateAndGet: {
|
|
184
|
+
<A, ER, EW, RR, RW>(f: (a: A) => A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>;
|
|
185
|
+
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A): Effect.Effect<A, ER | EW, RR | RW>;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Applies an effectful update to the value of a `Lens` and returns the new value.
|
|
189
|
+
*/
|
|
190
|
+
export declare const updateAndGetEffect: {
|
|
191
|
+
<A, ER, EW, RR, RW, E, R>(f: (a: A) => Effect.Effect<A, E, R>): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW | E, RR | RW | R>;
|
|
192
|
+
<A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, ER | EW | E, RR | RW | R>;
|
|
193
|
+
};
|
|
194
|
+
export {};
|
package/dist/Lens.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Array, Chunk, Effect, Function, Pipeable, Predicate, Readable, Stream, Subscribable } from "effect";
|
|
2
|
+
export const LensTypeId = Symbol.for("@effect-fc/Lens/Lens");
|
|
3
|
+
/**
|
|
4
|
+
* Internal `Lens` implementation.
|
|
5
|
+
*/
|
|
6
|
+
export class LensImpl extends Pipeable.Class() {
|
|
7
|
+
get;
|
|
8
|
+
changes;
|
|
9
|
+
modify;
|
|
10
|
+
[Readable.TypeId] = Readable.TypeId;
|
|
11
|
+
[Subscribable.TypeId] = Subscribable.TypeId;
|
|
12
|
+
[LensTypeId] = LensTypeId;
|
|
13
|
+
constructor(get, changes, modify) {
|
|
14
|
+
super();
|
|
15
|
+
this.get = get;
|
|
16
|
+
this.changes = changes;
|
|
17
|
+
this.modify = modify;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Checks whether a value is a `Lens`.
|
|
22
|
+
*/
|
|
23
|
+
export const isLens = (u) => Predicate.hasProperty(u, LensTypeId);
|
|
24
|
+
/**
|
|
25
|
+
* Creates a `Lens` by supplying how to read the current value, observe changes, and apply transformations.
|
|
26
|
+
*
|
|
27
|
+
* Either `modify` or `set` needs to be supplied.
|
|
28
|
+
*/
|
|
29
|
+
export const make = (options) => new LensImpl(options.get, options.changes, Predicate.hasProperty(options, "modify")
|
|
30
|
+
? options.modify
|
|
31
|
+
: (f) => Effect.flatMap(options.get, a => Effect.flatMap(f(a), ([b, next]) => Effect.as(options.set(next), b))));
|
|
32
|
+
/**
|
|
33
|
+
* Creates a `Lens` that proxies a `SubscriptionRef`.
|
|
34
|
+
*/
|
|
35
|
+
export const fromSubscriptionRef = (ref) => make({
|
|
36
|
+
get get() { return ref.get; },
|
|
37
|
+
get changes() { return ref.changes; },
|
|
38
|
+
modify: (f) => ref.modifyEffect(f),
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Creates a `Lens` that proxies a `SynchronizedRef`.
|
|
42
|
+
*
|
|
43
|
+
* Note: since `SynchronizedRef` does not provide any kind of reactivity mechanism, the produced `Lens` will be non-reactive.
|
|
44
|
+
* This means its `changes` stream will only emit the current value once when evaluated and nothing else.
|
|
45
|
+
*/
|
|
46
|
+
export const fromSynchronizedRef = (ref) => make({
|
|
47
|
+
get get() { return ref.get; },
|
|
48
|
+
get changes() { return Stream.unwrap(Effect.map(ref.get, Stream.make)); },
|
|
49
|
+
modify: (f) => ref.modifyEffect(f),
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Flattens an effectful `Lens`.
|
|
53
|
+
*/
|
|
54
|
+
export const unwrap = (effect) => make({
|
|
55
|
+
get: Effect.flatMap(effect, l => l.get),
|
|
56
|
+
changes: Stream.unwrap(Effect.map(effect, l => l.changes)),
|
|
57
|
+
modify: (f) => Effect.flatMap(effect, l => l.modify(f)),
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Derives a new `Lens` by applying synchronous getters and setters over the focused value.
|
|
61
|
+
*/
|
|
62
|
+
export const map = Function.dual(3, (self, get, set) => make({
|
|
63
|
+
get get() { return Effect.map(self.get, get); },
|
|
64
|
+
get changes() { return Stream.map(self.changes, get); },
|
|
65
|
+
modify: (f) => self.modify(a => Effect.flatMap(f(get(a)), ([c, next]) => Effect.succeed([c, set(a, next)]))),
|
|
66
|
+
}));
|
|
67
|
+
/**
|
|
68
|
+
* Derives a new `Lens` by applying effectful getters and setters over the focused value.
|
|
69
|
+
*/
|
|
70
|
+
export const mapEffect = Function.dual(3, (self, get, set) => make({
|
|
71
|
+
get get() { return Effect.flatMap(self.get, get); },
|
|
72
|
+
get changes() { return Stream.mapEffect(self.changes, get); },
|
|
73
|
+
modify: (f) => self.modify(a => Effect.flatMap(get(a), b => Effect.flatMap(f(b), ([c, bNext]) => Effect.flatMap(set(a, bNext), nextA => Effect.succeed([c, nextA]))))),
|
|
74
|
+
}));
|
|
75
|
+
/**
|
|
76
|
+
* Allows transforming only the `changes` stream of a `Lens` while keeping the focus type intact.
|
|
77
|
+
*/
|
|
78
|
+
export const mapStream = Function.dual(2, (self, f) => make({
|
|
79
|
+
get get() { return self.get; },
|
|
80
|
+
get changes() { return f(self.changes); },
|
|
81
|
+
get modify() { return self.modify; },
|
|
82
|
+
}));
|
|
83
|
+
/**
|
|
84
|
+
* Narrows the focus to a field of an object. Replaces the object in an immutable fashion when written to.
|
|
85
|
+
*/
|
|
86
|
+
export const focusField = Function.dual(2, (self, key) => map(self, a => a[key], (a, b) => Object.setPrototypeOf({ ...a, [key]: b }, Object.getPrototypeOf(a))));
|
|
87
|
+
/**
|
|
88
|
+
* Narrows the focus to a mutable field of an object. Mutates the object in place when written to.
|
|
89
|
+
*/
|
|
90
|
+
export const focusMutableField = Function.dual(2, (self, key) => map(self, a => a[key], (a, b) => { a[key] = b; return a; }));
|
|
91
|
+
/**
|
|
92
|
+
* Narrows the focus to an indexed element of an array. Replaces the array in an immutable fashion when written to.
|
|
93
|
+
*/
|
|
94
|
+
export const focusArrayAt = Function.dual(2, (self, index) => mapEffect(self, Array.get(index), (a, b) => Array.replaceOption(a, index, b)));
|
|
95
|
+
/**
|
|
96
|
+
* Narrows the focus to an indexed element of a mutable array. Mutates the array in place when written to.
|
|
97
|
+
*/
|
|
98
|
+
export const focusMutableArrayAt = Function.dual(2, (self, index) => mapEffect(self, Array.get(index), (a, b) => Effect.flatMap(Array.get(a, index), () => Effect.as(Effect.sync(() => { a[index] = b; }), a))));
|
|
99
|
+
/**
|
|
100
|
+
* Narrows the focus to an indexed element of `Chunk`. Replaces the `Chunk` in an immutable fashion when written to.
|
|
101
|
+
*/
|
|
102
|
+
export const focusChunkAt = Function.dual(2, (self, index) => mapEffect(self, Chunk.get(index), (a, b) => Effect.succeed(Chunk.replace(a, index, b))));
|
|
103
|
+
/**
|
|
104
|
+
* Reads the current value from a `Lens`.
|
|
105
|
+
*/
|
|
106
|
+
export const get = (self) => self.get;
|
|
107
|
+
/**
|
|
108
|
+
* Sets the value of a `Lens`.
|
|
109
|
+
*/
|
|
110
|
+
export const set = Function.dual(2, (self, value) => self.modify(() => Effect.succeed([void 0, value])));
|
|
111
|
+
/**
|
|
112
|
+
* Sets a `Lens` to a new value and returns the previous value.
|
|
113
|
+
*/
|
|
114
|
+
export const getAndSet = Function.dual(2, (self, value) => self.modify(a => Effect.succeed([a, value])));
|
|
115
|
+
/**
|
|
116
|
+
* Applies a synchronous transformation to the value of a `Lens`, discarding the previous value.
|
|
117
|
+
*/
|
|
118
|
+
export const update = Function.dual(2, (self, f) => self.modify(a => Effect.succeed([void 0, f(a)])));
|
|
119
|
+
/**
|
|
120
|
+
* Applies an effectful transformation to the value of a `Lens`, discarding the previous value.
|
|
121
|
+
*/
|
|
122
|
+
export const updateEffect = Function.dual(2, (self, f) => self.modify(a => Effect.flatMap(f(a), next => Effect.succeed([void 0, next]))));
|
|
123
|
+
/**
|
|
124
|
+
* Applies a synchronous transformation the value of a `Lens` while returning the previous value.
|
|
125
|
+
*/
|
|
126
|
+
export const getAndUpdate = Function.dual(2, (self, f) => self.modify(a => Effect.succeed([a, f(a)])));
|
|
127
|
+
/**
|
|
128
|
+
* Applies an effectful transformation the value of a `Lens` while returning the previous value.
|
|
129
|
+
*/
|
|
130
|
+
export const getAndUpdateEffect = Function.dual(2, (self, f) => self.modify(a => Effect.flatMap(f(a), next => Effect.succeed([a, next]))));
|
|
131
|
+
/**
|
|
132
|
+
* Sets the value of a `Lens` and returns the new value.
|
|
133
|
+
*/
|
|
134
|
+
export const setAndGet = Function.dual(2, (self, value) => self.modify(() => Effect.succeed([value, value])));
|
|
135
|
+
/**
|
|
136
|
+
* Applies a synchronous update the value of a `Lens` and returns the new value.
|
|
137
|
+
*/
|
|
138
|
+
export const updateAndGet = Function.dual(2, (self, f) => self.modify(a => {
|
|
139
|
+
const next = f(a);
|
|
140
|
+
return Effect.succeed([next, next]);
|
|
141
|
+
}));
|
|
142
|
+
/**
|
|
143
|
+
* Applies an effectful update to the value of a `Lens` and returns the new value.
|
|
144
|
+
*/
|
|
145
|
+
export const updateAndGetEffect = Function.dual(2, (self, f) => self.modify(a => Effect.flatMap(f(a), next => Effect.succeed([next, next]))));
|
|
146
|
+
//# sourceMappingURL=Lens.js.map
|
package/dist/Lens.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Lens.js","sourceRoot":"","sources":["../src/Lens.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Equivalence, Option } from "effect";
|
|
2
|
+
export type PropertyPath = readonly PropertyKey[];
|
|
3
|
+
type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
4
|
+
export type Paths<T, D extends number = 5, Seen = never> = readonly [] | (D extends never ? readonly [] : T extends Seen ? readonly [] : T extends readonly any[] ? {
|
|
5
|
+
[K in keyof T as K extends number ? K : never]: readonly [K] | readonly [K, ...Paths<T[K], Prev[D], Seen | T>];
|
|
6
|
+
} extends infer O ? O[keyof O] : never : T extends object ? {
|
|
7
|
+
[K in keyof T as K extends string | number | symbol ? K : never]-?: NonNullable<T[K]> extends infer V ? readonly [K] | readonly [K, ...Paths<V, Prev[D], Seen>] : never;
|
|
8
|
+
} extends infer O ? O[keyof O] : never : never);
|
|
9
|
+
export type ValueFromPath<T, P extends readonly any[]> = P extends readonly [infer Head, ...infer Tail] ? Head extends keyof T ? ValueFromPath<T[Head], Tail> : T extends readonly any[] ? Head extends number ? ValueFromPath<T[number], Tail> : never : never : T;
|
|
10
|
+
export declare const equivalence: Equivalence.Equivalence<PropertyPath>;
|
|
11
|
+
export declare const unsafeGet: {
|
|
12
|
+
<T, const P extends Paths<T>>(path: P): (self: T) => ValueFromPath<T, P>;
|
|
13
|
+
<T, const P extends Paths<T>>(self: T, path: P): ValueFromPath<T, P>;
|
|
14
|
+
};
|
|
15
|
+
export declare const get: {
|
|
16
|
+
<T, const P extends Paths<T>>(path: P): (self: T) => Option.Option<ValueFromPath<T, P>>;
|
|
17
|
+
<T, const P extends Paths<T>>(self: T, path: P): Option.Option<ValueFromPath<T, P>>;
|
|
18
|
+
};
|
|
19
|
+
export declare const immutableSet: {
|
|
20
|
+
<T, const P extends Paths<T>>(path: P, value: ValueFromPath<T, P>): (self: T) => Option.Option<T>;
|
|
21
|
+
<T, const P extends Paths<T>>(self: T, path: P, value: ValueFromPath<T, P>): Option.Option<T>;
|
|
22
|
+
};
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Array, Equivalence, Function, Option, Predicate } from "effect";
|
|
2
|
+
export const equivalence = Equivalence.array(Equivalence.strict());
|
|
3
|
+
export const unsafeGet = Function.dual(2, (self, path) => path.reduce((acc, key) => acc?.[key], self));
|
|
4
|
+
export const get = Function.dual(2, (self, path) => path.reduce((acc, key) => Option.isSome(acc)
|
|
5
|
+
? Predicate.hasProperty(acc.value, key)
|
|
6
|
+
? Option.some(acc.value[key])
|
|
7
|
+
: Option.none()
|
|
8
|
+
: acc, Option.some(self)));
|
|
9
|
+
export const immutableSet = Function.dual(3, (self, path, value) => {
|
|
10
|
+
const key = Array.head(path);
|
|
11
|
+
if (Option.isNone(key))
|
|
12
|
+
return Option.some(value);
|
|
13
|
+
if (!Predicate.hasProperty(self, key.value))
|
|
14
|
+
return Option.none();
|
|
15
|
+
const child = immutableSet(self[key.value], Option.getOrThrow(Array.tail(path)), value);
|
|
16
|
+
if (Option.isNone(child))
|
|
17
|
+
return child;
|
|
18
|
+
if (Array.isArray(self))
|
|
19
|
+
return typeof key.value === "number"
|
|
20
|
+
? Option.some([
|
|
21
|
+
...self.slice(0, key.value),
|
|
22
|
+
child.value,
|
|
23
|
+
...self.slice(key.value + 1),
|
|
24
|
+
])
|
|
25
|
+
: Option.none();
|
|
26
|
+
if (typeof self === "object")
|
|
27
|
+
return Option.some(Object.assign(Object.create(Object.getPrototypeOf(self)), { ...self, [key.value]: child.value }));
|
|
28
|
+
return Option.none();
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=PropertyPath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PropertyPath.js","sourceRoot":"","sources":["../src/PropertyPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAyCxE,MAAM,CAAC,MAAM,WAAW,GAA0C,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAA;AAEzG,MAAM,CAAC,MAAM,SAAS,GAGlB,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAA8B,IAAO,EAAE,IAAO,EAAuB,EAAE,CACxF,IAAI,CAAC,MAAM,CAAC,CAAC,GAAQ,EAAE,GAAQ,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CACxD,CAAA;AAED,MAAM,CAAC,MAAM,GAAG,GAGZ,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAA8B,IAAO,EAAE,IAAO,EAAsC,EAAE,CACvG,IAAI,CAAC,MAAM,CACP,CAAC,GAAuB,EAAE,GAAQ,EAAsB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IACzE,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;QACnC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE;IACnB,CAAC,CAAC,GAAG,EAET,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CACpB,CACJ,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAGrB,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAA8B,IAAO,EAAE,IAAO,EAAE,KAA0B,EAAoB,EAAE;IACjH,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAoB,CAAC,CAAA;IAC5C,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;QAClB,OAAO,MAAM,CAAC,IAAI,CAAC,KAAU,CAAC,CAAA;IAClC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC;QACvC,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;IAExB,MAAM,KAAK,GAAG,YAAY,CAAW,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAoB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACjH,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;QACpB,OAAO,KAAK,CAAA;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QACnB,OAAO,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;YAChC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;gBACV,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC;gBAC3B,KAAK,CAAC,KAAK;gBACX,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;aAC1B,CAAC;YACP,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IAEvB,IAAI,OAAO,IAAI,KAAK,QAAQ;QACxB,OAAO,MAAM,CAAC,IAAI,CACd,MAAM,CAAC,MAAM,CACT,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAC1C,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,CACxC,CACJ,CAAA;IAEL,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;AACxB,CAAC,CAAC,CAAA"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "effect-lens",
|
|
3
|
+
"description": "An effectful Lens type to easily manage nested state",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"./README.md",
|
|
8
|
+
"./dist"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"url": "git+https://github.com/Thiladev/effect-lens.git"
|
|
13
|
+
},
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./*": [
|
|
21
|
+
{
|
|
22
|
+
"types": "./dist/*/index.d.ts",
|
|
23
|
+
"default": "./dist/*/index.js"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"types": "./dist/*.d.ts",
|
|
27
|
+
"default": "./dist/*.js"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc",
|
|
33
|
+
"lint:tsc": "tsc --noEmit",
|
|
34
|
+
"lint:biome": "biome lint",
|
|
35
|
+
"pack": "npm pack",
|
|
36
|
+
"clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo",
|
|
37
|
+
"clean:dist": "rm -rf dist",
|
|
38
|
+
"clean:modules": "rm -rf node_modules"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"effect": "^3.21.0"
|
|
42
|
+
}
|
|
43
|
+
}
|