bansa 0.0.25 → 0.0.26

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 CHANGED
@@ -1,24 +1,24 @@
1
1
  # Bansa
2
2
 
3
- English | [한국어](https://github.com/cgiosy/bansa/blob/main/README.ko.md)
3
+ [English](https://github.com/cgiosy/bansa/blob/main/README.md) | 한국어
4
4
 
5
- ## Introduction
5
+ ## 소개
6
6
 
7
- Bansa is a library that makes it easy to manage derived state, asynchronous values, dependencies and subscriptions, lifecycles, and side effects. Similar to [Jotai](https://jotai.org/), it follows a bottom-up approach using atoms.
7
+ Bansa 파생 상태, 비동기 값, 의존성과 구독, 생명 주기, 사이드 이펙트를 쉽게 관리할 있는 라이브러리입니다. [Jotai](https://jotai.org/) 유사하게, atom을 사용한 상향식 접근 방식을 따릅니다.
8
8
 
9
- It is a framework-independent library that can be used in a pure JavaScript environment without any other libraries or frameworks, as well as with React, Vue, Svelte, and others.
9
+ 어떤 라이브러리나 프레임워크도 사용하지 않는 순수 JavaScript 환경은 물론이고, React, Vue, Svelte 등에서도 사용 가능한 프레임워크 독립적 라이브러리입니다.
10
10
 
11
- ## Concepts
11
+ ## 개념
12
12
 
13
- ### State
13
+ ### 상태
14
14
 
15
- You can create a state with the `$` function. There are two types of states.
15
+ `$` 함수로 상태를 만들 있습니다. 상태는 종류가 있습니다.
16
16
 
17
- #### Primitive State
17
+ #### 원시 상태
18
18
 
19
- The most basic unit of state. It can be updated to any value using the `.set` method.
19
+ 가장 기본적인 상태 단위로, 값이 정적이며, `.set` 메서드를 통해 임의의 값으로 업데이트할 있습니다.
20
20
 
21
- It is created by passing a normal value (number/string/object, etc.) to the `$` function.
21
+ 일반 (숫자/문자열/객체 ) `$` 함수에 전달하여 생성합니다.
22
22
 
23
23
  ```javascript
24
24
  import { $ } from "bansa";
@@ -28,11 +28,11 @@ const $count = $(42);
28
28
  const $user = $({ name: "John Doe", age: 30 });
29
29
  ```
30
30
 
31
- #### Derived State
31
+ #### 파생 상태
32
32
 
33
- A state whose value is computed by a function and has a lifecycle. It cannot be updated directly; it can only be re-executed when the value of a state it depends on changes. If the state is not active (i.e., there are no subscribers), the function will not run, and it is treated as having no dependencies.
33
+ 값을 함수로 계산하며, 생명 주기를 가지는 상태입니다. 값을 직접 업데이트할 없으며, 의존 중인 상태의 값이 바뀔 때에만 재실행될 있습니다. 활성화된 상태가 아니라면, 해당 상태를 구독 중인 곳이 존재하지 않는다면 함수는 실행되지 않으며, 의존성 또한 없는 것으로 취급됩니다.
34
34
 
35
- It is created by passing a function to `$`. The arguments to this function are a `get` function, which can read the values of other states, and `{ signal }`, which represents the state's lifetime. The `signal` will be discussed in more detail in another section.
35
+ `$`에 함수를 전달하여 생성합니다. 전달하는 함수의 인자로는 다른 상태의 값을 읽을 있는 `get` 함수와 상태의 수명을 나타내는 `{ signal }`이 주어집니다. `signal`에 대해선 다른 파트에서 자세히 다룹니다.
36
36
 
37
37
  ```javascript
38
38
  const $countDouble = $((get) => get($count) * 2);
@@ -48,35 +48,21 @@ const $signalExample = $((_, { signal }) => {
48
48
  });
49
49
  ```
50
50
 
51
- Here, `$countDouble` depends on `$count`, so if the value of `$count` changes, the value of `$countDouble` can be automatically recalculated.
51
+ `$countDouble`은 `$count`에 의존하므로, `$count`의 값이 변경되면 `$countDouble`의 값이 자동으로 다시 계산될 수 있습니다.
52
52
 
53
- `$userMessage` depends on `$count`, and if the value of `$count` is not less than `50`, it also depends on `$user`. This means if `$count` is less than `50`, `$userMessage` will not be recalculated even if the value of `$user` changes.
53
+ `$userMessage`는 `$count`에 의존하며, `$count`의 값이 `50` 미만이 아니라면 추가로 `$user`에도 의존합니다. 즉, `$count`가 `50` 미만이라면 `$user`의 값이 바뀌더라도 다시 계산되지 않습니다.
54
54
 
55
- This explanation describes the behavior when the state is active. It will not execute in either case until it is subscribed to using the `.subscribe()` or `.watch()` methods, which will be discussed later.
55
+ 설명은 상태가 활성화된 상황일 때를 설명한 것이며, 후술할 `.subscribe()` 또는 `.watch()` 메서드로 구독되기 전까지는 어느 쪽이든 실행되지 않습니다.
56
56
 
57
- ##### Reading `state` (Preventing unwrap)
57
+ ##### 활성 상태로 유지하기
58
58
 
59
- The second parameter of `get` is an optional `unwrap` option. The default value is `true`, so it always returns the unwrapped value. If set to `false`, it returns the `state` of type `AtomState<Value>`.
59
+ `$`의 번째 파라미터로 옵션을 전달할 있습니다. 옵션 객체에서 `persist`가 `true`로 설정되어 있다면, 해당 객체는 활성화되면 다시 비활성화되지 않습니다. 활성 상태를 유지하려고 무의미한 구독을 추가하는 대신 사용할 있습니다.
60
60
 
61
- `state` is a read-only object representing the current status of the state. It is useful for handling situations where the value is not ready, such as with asynchronous states or expected errors (e.g., for showing a placeholder). `value` holds the last successfully resolved value. `promise` and `error` hold their respective values if the state is currently loading or has encountered an error. The exact type is as follows:
61
+ 값이 거의 바뀌지 않고, 언제든 다시 있게 준비해둬야 하는 경우 유용합니다. 대표적으론 정적인 에셋을 `fetch` 또는 `import`하는 상황이 있습니다.
62
62
 
63
- ```typescript
64
- type AtomState<Value> =
65
- | { promise: undefined; error: undefined; value: Value } // Success
66
- | { promise: undefined; error: any; value?: Value } // Error
67
- | { promise: PromiseLike<Value>; error: any; value?: Value } // Loading
68
- | { promise: typeof inactive; error: undefined; value?: Value }; // Inactive
69
- ```
70
-
71
- ##### Keeping a state active
72
-
73
- You can pass an options object as the second parameter to `$`. If `persist` is set to `true` in the options object, the state will not be deactivated once it becomes active. This can be used instead of adding a meaningless subscription just to keep the state active.
74
-
75
- This is useful for values that rarely change but need to be ready for use at any time. A typical example is fetching or importing static assets.
63
+ ### 상태 직접 읽기
76
64
 
77
- ### Reading State Directly
78
-
79
- You can read the current value of a state using the `atom.get()` method or `atom.state` property.
65
+ `atom.get()` 메서드나 `atom.state`을 통해 상태의 현재 값을 읽을 수 있습니다.
80
66
 
81
67
  ```javascript
82
68
  console.log($count.get()); // 42
@@ -85,13 +71,27 @@ console.log($countDouble.get()); // 84
85
71
  console.log($countDouble.state); // { promise: undefined, error: undefined, value: 84 }
86
72
  ```
87
73
 
88
- For derived states, `.get()` can throw. It throws the `Promise` during asynchronous loading and throws the error when in an error state. This is useful when you want to primarily handle the success case and push all exception handling into a `catch` block or similar.
74
+ 파생 상태는 `.get()` 했을 throw 있습니다. 비동기 로딩 중일 해당 `Promise`를 throw하며, 오류 상태일 때는 해당 오류를 throw합니다. 값이 성공적으로 계산된 상황 위주로 처리하고, 예외 상황은 전부 `catch` 블록 등으로 밀어넣고 싶은 상황에서 유용합니다.
75
+
76
+ `.get()` 메서드는 상태가 비활성화된 경우, 매우 잠시 동안 해당 상태를 살아 있는 상태로 전환합니다. 당연히 해당 상태와 모든 의존성이 새로 실행됩니다. 매우 잠시 동안은 적어도 현재 마이크로태스크가 끝나기까지를 의미합니다. 즉, 동기적으로 연속해서 `.get()`을 호출하더라도 매번 모든 것이 다시 실행되지는 않습니다.
77
+
78
+ 비동기 상태를 다룰 때에는 거의 사용할 일이 없습니다. 대신 `.subscribe`, 또는 `.watch`와 `.state`을 사용하세요.
89
79
 
90
- If a state is inactive, the `.get()` method temporarily transitions it to an active state. Naturally, the state and all its dependencies will be re-executed. "Temporarily" means at least until the end of the current microtask. This means that calling `.get()` synchronously multiple times in a row will not cause everything to re-execute each time.
80
+ ##### `.state`
91
81
 
92
- ### Updating State
82
+ `state`은 상태의 현재 상태를 나타내는 읽기 전용 객체입니다. 비동기 상태거나 오류가 예상되는, 값이 준비되지 않은 상황을 처리(placeholder를 보여주는 등)해야 하는 상황에서 유용합니다. `value`는 마지막으로 성공했을 때의 값을 가집니다. `error`와 `promise`는 현재 로딩 중이거나 에러가 발생한 경우 해당 값을 가집니다. `active`는 활성 상태 여부를 나타냅니다. 정확한 타입은 다음과 같습니다:
93
83
 
94
- You can update the value of a primitive state using the `.set(updater)` method. If `updater` is a normal value, the state is updated to that value. If it's a function, the state is updated with `updater(nextValue)`, where `nextValue` is the state's 'pending value'.
84
+ ```typescript
85
+ type AtomState<Value> =
86
+ | { active: false; error: undefined; promise: undefined; value?: Value } // 비활성
87
+ | { active: true; error: undefined; promise: undefined; value: Value } // 성공
88
+ | { active: true; error: any; promise: undefined; value?: Value } // 에러
89
+ | { active: true; error: any; promise: PromiseLike<Value>; value?: Value }; // 로딩
90
+ ```
91
+
92
+ ### 상태 업데이트
93
+
94
+ `.set(updater)` 메서드를 통해 원시 상태의 값을 업데이트할 수 있으며, `updater`가 일반 값이라면 해당 값으로 업데이트하고, 함수라면 상태의 '예비 값' `nextValue`에 대해 `updater(nextValue)`로 업데이트합니다.
95
95
 
96
96
  ```javascript
97
97
  console.log($count.get()); // 42
@@ -105,9 +105,9 @@ $count.set(increment);
105
105
  console.log($count.get()); // 101
106
106
  ```
107
107
 
108
- All updates are batched per microtask. This means multiple synchronous updates are processed at once. In particular, if a single state is updated multiple times, it is treated as if it were updated only once with the final value.
108
+ 모든 업데이트는 마이크로태스크를 단위로 배치 처리됩니다. 즉, 동기적으로 발생하는 여러 업데이트는 번에 처리되며, 특히 하나의 상태가 여러 업데이트됐을 경우 마지막 번만 업데이트한 것으로 취급됩니다.
109
109
 
110
- If the `updater` is a function, it can access the last received 'pending value' `nextValue`. Therefore, when `.set` is called multiple times synchronously as shown below, `$count` will be incremented by `3`, but the update still happens only once.
110
+ `updater`가 함수라면, 마지막으로 들어온 '예비 ' `nextValue`에 접근할 있습니다. 따라서, 다음과 같이 동기적으로 여러 번의 `.set`을 호출했을 `$count`는 `3`만큼 증가하게 됩니다. 단, 업데이트는 여전히 번만 됩니다.
111
111
 
112
112
  ```javascript
113
113
  $count.set(increment);
@@ -115,17 +115,17 @@ $count.set(increment);
115
115
  $count.set(increment);
116
116
  ```
117
117
 
118
- If you must update based on the current value, you can use `.get()` or `.state`, like `$count.set($count.state.value + 1)`.
118
+ 반드시 현재 값을 기준으로 업데이트해야 한다면, `$count.set($count.state.value + 1)` 같이 `.get()` 또는 `.state`을 사용할 있습니다.
119
119
 
120
- ### Subscribing to State
120
+ ### 상태 구독
121
121
 
122
- You can detect updates with the `.subscribe(listener)` or `.watch(listener)` methods. Each method returns an unsubscribe function.
122
+ `.subscribe(listener)` 또는 `.watch(listener)` 메서드로 업데이트를 감지할 있습니다. 메서드는 구독 중단 함수를 반환합니다.
123
123
 
124
- Upon subscription, if the state was inactive, an update is scheduled. During the update, the state and all its dependencies are activated. Upon unsubscription, if there are no more subscribers to the state, its deactivation is scheduled, and its dependencies are also checked for deactivation.
124
+ 구독 해당 상태가 비활성화된 상태였다면 업데이트가 예약되며, 업데이트 해당 상태와 의존성까지 모두 활성화됩니다. 구독 해제 해당 상태를 구독하는 곳이 더이상 없다면 비활성화가 예약되며, 의존성도 비활성화 대상인지 확인됩니다.
125
125
 
126
- `.subscribe` calls the given function when the state is successfully updated. If the state has already been successfully updated, the function is called once with the current value upon subscription. The listener is called with the state's value as the first argument and `{ signal }` as the second. The `signal` is linked to the state's lifetime.
126
+ `.subscribe`는 상태가 성공적으로 업데이트되었을 주어진 함수를 호출합니다. 이미 업데이트가 성공적으로 상태일 경우 구독 해당 값으로 호출합니다. 호출 번째 인자는 해당 상태의 값, 번째 인자는 `{ signal }`이 주어지며, `signal`은 상태의 수명과 연동됩니다.
127
127
 
128
- `.watch` calls the given function whenever the state changes. It can be used when you need to handle error or asynchronous states as well.
128
+ `.watch`는 해당 상태가 변화할 주어진 함수를 호출합니다. 오류나 비동기 상태를 추가적으로 처리하려는 경우 있습니다.
129
129
 
130
130
  ```javascript
131
131
  const $count = $(0);
@@ -144,12 +144,12 @@ unsubscribe();
144
144
  // value end 1
145
145
 
146
146
  $count.set(2);
147
- // (no output)
147
+ // (출력 없음)
148
148
  ```
149
149
 
150
- `.subscribe()` returns a function that can be used to unsubscribe. It is important to call this function when a component unmounts to prevent memory leaks.
150
+ `.subscribe()`는 구독을 해제할 있는 함수를 반환합니다. 컴포넌트가 언마운트될 함수를 호출하여 메모리 누수를 방지하는 것이 중요합니다.
151
151
 
152
- If you want to subscribe to multiple states simultaneously, you should declare another state.
152
+ 만약 여러 상태를 동시에 구독하고 싶다면, 상태를 하나 선언해야 합니다.
153
153
 
154
154
  ```javascript
155
155
  const $merged = $((get) => ({
@@ -160,9 +160,9 @@ const $merged = $((get) => ({
160
160
  $merged.subscribe(({ count, countDouble }) => console.log(`${count} * 2 = ${countDouble}`));
161
161
  ```
162
162
 
163
- ### Asynchronous State
163
+ ### 비동기 상태
164
164
 
165
- For a derived state where the function returns a `Promise`, you can use the automatically unwrapped value when you `get` or `subscribe` to it. If you want to handle loading or failure cases, you can use `watch` or `state`.
165
+ 함수에서 `Promise`가 반환된 파생 상태의 경우, 해당 상태를 `get`하거나 `subscribe`했을 자동으로 unwrap된 값을 있습니다. 로딩이나 실패했을 때를 다루고 싶다면 `watch`나 `state`을 사용할 수 있습니다.
166
166
 
167
167
  ```javascript
168
168
  const $user = $(async (get) => {
@@ -184,11 +184,11 @@ faultyAtom.watch(() => {
184
184
  });
185
185
  ```
186
186
 
187
- ### State Lifetime (`signal`)
187
+ ### 상태의 수명 (`signal`)
188
188
 
189
- The `options.signal` passed as an argument to a derived function can be used like an `AbortSignal` and a `Promise` (strictly speaking, a thenable). It is `abort`ed and `resolve`d when the state's lifetime changes, such as when the state is updated or deactivated.
189
+ 파생 함수의 인자로 전달되는 `options.signal`은 `AbortSignal` `Promise` (엄밀히는 thenable)처럼 사용 가능합니다. 상태가 업데이트됐거나, 상태가 비활성화되는 상태의 수명이 변했을 `abort` `resolve`됩니다.
190
190
 
191
- Like an `AbortSignal`, it can be passed to existing web APIs like `fetch` or `addEventListener` for cancellation or unsubscription. Like a `Promise`, you can use `signal.then` to write your own cleanup functions.
191
+ `AbortSignal`처럼 `fetch`나 `addEventListener`같은 기존 API에 전달하여 취소나 구독 중단 등에 사용할 있으며, `Promise`처럼 `signal.then`을 통해 자체 cleanup 함수를 수도 있습니다.
192
192
 
193
193
  ```javascript
194
194
  const $user = $(async (get, { signal }) => {
@@ -201,9 +201,9 @@ const $user = $(async (get, { signal }) => {
201
201
  });
202
202
  ```
203
203
 
204
- ### Custom Update Condition (Equality Check)
204
+ ### 커스텀 업데이트 조건(동등성 확인)
205
205
 
206
- By default, equality is checked with `Object.is`, so for objects or arrays, an update can occur if the reference is different even if the content is the same. To perform additional equality checks, you can provide an `equals` option when declaring the state. In this case, it first checks with `Object.is`, and if they are different, it checks again with the `equals` function. If either returns true, the value change is ignored.
206
+ 기본적으로 `Object.is`로 동등성을 체크하므로 객체나 배열의 경우 참조가 다르면 내용이 같더라도 업데이트가 발생할 있으며, 추가로 동등성을 확인하기 위해 상태 선언 `equals`를 옵션으로 있습니다. 경우 `Object.is`로 같은지 확인하고, 다르다면 `equals` 함수로 다시 확인합니다. 하나라도 참을 반환하는 경우 값 변경은 무시됩니다.
207
207
 
208
208
  ```javascript
209
209
  const $user = $({ id: 1, name: "Alice" }, { equals: (next, prev) => next.id === prev.id });
@@ -215,23 +215,23 @@ userAtom.set({ id: 1, name: "Bob" });
215
215
  userAtom.set({ id: 2, name: "Alice" });
216
216
  ```
217
217
 
218
- In the example above, the first update is ignored because the `id` is the same. The second update has a different `id`, so `$user` is updated, but since the `name` is the same, `$user2` is not updated.
218
+ 예제에서 번째 업데이트는 `id`가 같으므로 무시됩니다. 번째 업데이트는 `id`가 다르므로 `$user`를 업데이트하지만, `name`이 같으므로 `$user2`는 업데이트되지 않습니다.
219
219
 
220
- ### Merging Multiple States
220
+ ### 여러 상태 병합하기
221
221
 
222
- You can create a new state by merging multiple states with `$$`. It's actually the same as `$`, but while `$`'s `get` function throws immediately when it encounters a `Promise` or an error, `$$`'s `get` function returns a special object to track maximum dependencies with minimum re-executions.
222
+ `$$`로 여러 상태를 병합한 새로운 상태를 만들 있습니다. `$`와 사용법은 같지만, `$`의 `get` 함수는 `Promise`나 에러를 만났을 즉시 throw하는 반면, `$$`의 `get` 함수는 특별한 객체를 반환하여 최소한의 재실행으로 최대한의 의존성을 추적합니다. 또한, 배열이나 객체를 반환 단계 깊은 비교를 수행합니다.
223
223
 
224
- The following code takes 5 seconds to merge states with `$`, whereas it takes only 1 second with `$$`.
224
+ 다음 코드는 `$`로 상태를 병합하면 5초가 걸리는 반면에, `$$`로 상태를 병합하면 1초만이 걸립니다.
225
225
 
226
226
  ```javascript
227
227
  const timer = (time) => new Promise((resolve) => setTimeout(() => resolve(1), time));
228
- const a = [1, 2, 3, 4, 5].map(() => $(() => timer(1000)));
229
- const merged = $$((get) => a.map(get));
228
+ const timerAtoms = [1, 2, 3, 4, 5].map(() => $(() => timer(1000)));
229
+ const $timers = $$((get) => timerAtoms.map(get));
230
230
  console.time();
231
- merged.subscribe(() => console.timeEnd());
231
+ $timers.subscribe(() => console.timeEnd());
232
232
  ```
233
233
 
234
- For reference, the value that `$$`'s `get` function returns instead of throwing when it encounters a `Promise` or an error is created through the following process:
234
+ 참고로 `$$`의 `get` 함수가 `Promise`나 에러를 만났을 throw 대신 반환하는 값은 다음 과정으로 만들어집니다:
235
235
 
236
236
  ```javascript
237
237
  const o = () => o;
@@ -242,19 +242,46 @@ Object.setPrototypeOf(
242
242
  );
243
243
  ```
244
244
 
245
- The `o` in this code returns the same value no matter how many properties are accessed or functions are called. For example, `o.a.b.c().d()().asdf()()()() === o` is `true`. Therefore, it allows most state-merging functions composed of selectors and simple methods like filter/map/reduce to execute without issues. However, it's not a silver bullet, so some caution is needed, and it should preferably be used only for state merging.
245
+ 코드의 `o`는 아무리 프로퍼티 접근 호출을 해도 같은 값을 반환합니다. 예를 들어, `o.a.b.c().d()().asdf()()()() === o`는 `true`입니다. 따라서, 셀렉터와 filter/map/reduce 간단한 메서드로 이뤄진 대부분의 상태 병합 함수에서 문제 없이 전체 코드를 실행할 있게 만듭니다. 하지만 만능은 아니므로 약간의 주의가 필요하며, 가급적 상태 병합에만 사용해야 합니다.
246
+
247
+ 또한 상태 병합 시 특정 값들에서 부분씩만 가져와 새로운 객체를 만드는 경우가 흔하므로, `$$`로 병합한 상태의 의존성들이 너무 자주 재실행되지 않도록 `$`의 `equals`를 다음으로 설정합니다:
248
+
249
+ ```typescript
250
+ const shallowEquals = (a: any, b: any): boolean => {
251
+ if (typeof a !== "object" || typeof b !== "object" || !a || !b) return false;
252
+ const c = a.constructor;
253
+ if (c !== b.constructor) return false;
254
+
255
+ if (c === Array) {
256
+ let i = a.length;
257
+ if (i !== b.length) return false;
258
+ while ((i = (i - 1) | 0) >= 0) if (!Object.is(a[i], b[i])) return false;
259
+ return true;
260
+ }
261
+
262
+ let n = 0;
263
+ for (const k in a) {
264
+ if (!(k in b && Object.is(a[k], b[k]))) return false;
265
+ n = (n + 1) | 0;
266
+ }
267
+ for (const _ in b) if ((n = (n - 1) | 0) < 0) return false;
268
+ return true;
269
+ };
270
+ ```
271
+
272
+ 정확히 한 단계만 더 비교하므로, `{ arr: [...] }`와 같은 객체의 경우 `arr`의 내용이 같더라도 참조가 다르다면 의존성이 재실행됩니다. 필요하다면 `arr` 부분은 다른 `$$`로 감싼 뒤 합치거나, 상태를 직접 `$`로 `equals`를 주고 만드세요.
246
273
 
247
- ## In-Depth
274
+ ## 상세
248
275
 
249
- ### How much should I split the state?
276
+ ### 상태를 얼마나 쪼개는 좋나요?
250
277
 
251
- Split your state as much as possible, as long as it doesn't significantly harm code readability. Also, wrap as much logic as you can in as many layers of state as possible.
278
+ 코드의 가독성을 크게 해치지 않는 선에서 최대한 많이 쪼개세요. 또한 최대한 많은 로직을, 최대한 많은 단계의 상태로 감싸세요.
252
279
 
253
- In fact, the reason `subscribe` wasn't designed to take a `get` function like `$` is to encourage splitting states as much as possible, so that `subscribe` only deals with the 'final state'.
280
+ 사실, `$`처럼 `get`으로 상태의 값을 가져오는 방식으로 `subscribe`를 만들지 않은 이유 또한 상태를 최대한 많이 쪼개고, `subscribe`는 '최종 상태'만을 다루도록 하기 위함입니다.
254
281
 
255
- Implicit 'intermediate states' remain 'hidden,' causing you to lose many of the library's benefits and potentially face issues like unnecessary recalculations, inability to reuse intermediate values, complicated dependency tracking, code repetition, broken side-effect idempotency, and the inability to manage fine-grained lifecycles and subscriptions.
282
+ 암묵적인 '중간 상태'들은 '숨겨져' 있게 되므로 라이브러리의 혜택을 상당수 잃고, 불필요한 재계산 중간 활용 불가능, 복잡해지는 의존성 파악, 코드의 반복, 사이드 이펙트의 멱등성 깨짐, 세세한 생명 주기 구독 관리 불가능 등의 문제를 겪을 있습니다.
256
283
 
257
- For example, the following shows a situation where not splitting the state enough leads to 'unnecessary recalculations and inability to reuse intermediate values'.
284
+ 예를 들어, 다음은 상태를 쪼개서 '불필요한 재계산 중간 활용 불가능' 발생하는 상황을 보여줍니다.
258
285
 
259
286
  ```javascript
260
287
  const $userId = $(123);
@@ -268,7 +295,7 @@ const $pageData = $(async (get, { signal }) => {
268
295
  });
269
296
  ```
270
297
 
271
- This code looks simple and clean, but if only one of `userId` or `postId` is updated, both requests are sent again. The latencies of `user` and `post` are summed up (which can be solved with `Promise.all`, but this increases code complexity). Unrelated side effects coexist, mixing contexts. And even if other values in `user` or `post` don't change, the `innerHTML` is updated, causing the DOM to be completely replaced. There are several problems. It should be split as follows:
298
+ 코드는 간단하고 깔끔해 보이지만, `userId`와 `postId` 하나만 업데이트되어도 개의 요청이 다시 보내지며, `user`와 `post`의 레이턴시가 합산되고 (`Promise.all`로 해결 가능하지만 코드 복잡도가 상승합니다.), 관계 없는 사이드 이펙트가 함께 존재해 맥락이 섞이고, `user`나 `post`의 다른 값이 바뀌지 않더라도 `innerHTML`를 바꾸어 DOM 완전히 갈아엎어지는 등, 여러 문제가 있습니다. 다음과 같이 쪼개야 합니다.
272
299
 
273
300
  ```javascript
274
301
  const $userId = $(123);
@@ -292,17 +319,17 @@ $pageData.subscribe(({ userName, postAuthor }) => {
292
319
  });
293
320
  ```
294
321
 
295
- The number of lines of code has increased slightly, but the previously mentioned problems have been resolved.
322
+ 코드 수가 약간 늘어났지만, 앞서 언급한 문제들이 해결되었습니다.
296
323
 
297
- This might be too simple of an example to be fully convincing, but in real-world scenarios, it's easy to accidentally mix states in a moment of carelessness. Also, the desire to handle everything in one place can often be hard to resist.
324
+ 아마 너무 간단한 예시라 별로 와닿지 않을 수도 있지만, 현실에서는 아차 하는 순간 자신도 모르게 상태를 뒤섞기 쉬우며, 모든 것을 곳에서 처리하려는 욕망을 참기 어려운 경우도 종종 발생합니다.
298
325
 
299
- It's important to always be mindful of this and to split, wrap, and layer your states.
326
+ 항상 이를 신경쓰며 상태를 쪼개고, 감싸고, 단계를 나누는 것이 중요합니다.
300
327
 
301
- ### How to implement `onMount`/`onCleanup`?
328
+ ### `onMount`/`onCleanup`은 어떻게 하나요?
302
329
 
303
- Sometimes you need to call a function not every time a state's value changes, but when the state becomes active and inactive (i.e., when subscribers start appearing and when there are no longer any subscribers). In other words, you need functionality like `onMount`/`onCleanup` (or `onDestroy`, etc.).
330
+ 상태의 값이 바뀔 때마다가 아니라, 상태가 활성화됐을 때와 비활성화됐을 (상태를 구독한 곳이 생기기 시작했을 때와 더이상 아무 곳에서도 구독하고 있지 않을 ) 함수를 호출해야 하는 경우가 있습니다. 즉, `onMount`/`onCleanup` (또는 `onDestroy` )와 같은 기능이 필요합니다.
304
331
 
305
- This can be solved in two ways. One is to create and return a state from within another state:
332
+ 이는 가지 방법으로 해결할 있습니다. 하나는 상태 안에서 상태를 만들어 반환하는 것입니다:
306
333
 
307
334
  ```javascript
308
335
  const $shared = $((_, { signal }) => {
@@ -318,7 +345,7 @@ const $a = $((get) => {
318
345
  });
319
346
  ```
320
347
 
321
- If modifications to `$state` only occur within `onMount` and `onCleanup` (for example, when subscribing to external events), this is the cleanest pattern. The following is an example that applies this to manage a connection shared by multiple places:
348
+ `$state`의 수정이 `onMount` `onCleanup` 내에서만 발생하는 경우(가령 외부 이벤트를 구독하는 상황), 가장 깔끔한 패턴입니다. 다음은 이를 응용하여 여러 곳에서 공유하는 연결을 다루는 예시입니다:
322
349
 
323
350
  ```javascript
324
351
  const $wsConnection = $(() => {
@@ -357,7 +384,7 @@ const $alice = lastMessage("alice");
357
384
  const $bob = lastMessage("bob");
358
385
  ```
359
386
 
360
- If the state needs to be modifiable from the outside, you can create two states like this:
387
+ 만약 외부에서 상태를 수정할 있어야 한다면, 다음처럼 상태를 만들 있습니다:
361
388
 
362
389
  ```javascript
363
390
  const $writer = $(0);
@@ -371,11 +398,11 @@ const $reader = $((get) => {
371
398
  });
372
399
  ```
373
400
 
374
- Now, you can read from `$reader` and write to `$writer`. Since `$shared` does not depend on `$writer`, `$shared` will not be updated even if `$writer` is modified.
401
+ 이제 읽기는 `$reader`로, 쓰기는 `$writer`로 하면 됩니다. `$shared`는 `$writer`에 의존하지 않으므로, `$writer`가 수정되더라도 `$shared`는 업데이트되지 않습니다.
375
402
 
376
- ## Examples
403
+ ## 예제
377
404
 
378
- #### Debounce-Throttling
405
+ #### 디바운스-스로틀링
379
406
 
380
407
  ```javascript
381
408
  const delayedState = (initial, minDelay, maxDelay) => {
@@ -414,7 +441,7 @@ inputElm.addEventListener("input", (e) => updateInput(e.currentTarget.value));
414
441
  $inputValue.subscribe(console.log);
415
442
  ```
416
443
 
417
- ### Scroll Direction Detection
444
+ ### 스크롤 방향 감지
418
445
 
419
446
  ```javascript
420
447
  const $windowScroll = $((_, { signal }) => {
package/dist/atom.d.mts CHANGED
@@ -16,23 +16,27 @@ type AtomSubscribe<Value> = (value: Value, options: AtomSubscriberOptions) => vo
16
16
  type AtomInit<Value> = Value | AtomGetter<Value>;
17
17
  type AtomUpdater<Value> = Value | AtomReducer<Value>;
18
18
  type AtomInactiveState<Value> = {
19
- promise: typeof inactive;
19
+ active: false;
20
20
  error: any;
21
+ promise: undefined;
21
22
  value?: Value;
22
23
  };
23
24
  type AtomPromiseState<Value> = {
24
- promise: PromiseLike<Value>;
25
+ active: true;
25
26
  error: any;
27
+ promise: PromiseLike<Value>;
26
28
  value?: Value;
27
29
  };
28
30
  type AtomSuccessState<Value> = {
29
- promise: undefined;
31
+ active: true;
30
32
  error: undefined;
33
+ promise: undefined;
31
34
  value: Value;
32
35
  };
33
36
  type AtomErrorState<Value> = {
34
- promise: undefined;
37
+ active: true;
35
38
  error: any;
39
+ promise: undefined;
36
40
  value?: Value;
37
41
  };
38
42
  type AtomState<Value> = AtomInactiveState<Value> | AtomPromiseState<Value> | AtomSuccessState<Value> | AtomErrorState<Value>;
@@ -47,10 +51,7 @@ type AtomGetOptions = {
47
51
  type ThenableSignal = AbortSignal & {
48
52
  then: (f: () => void) => void;
49
53
  };
50
- type GetAtom = {
51
- <Value>(anotherAtom: Atom<Value>, unwrap?: true): Value;
52
- <Value>(anotherAtom: Atom<Value>, unwrap: false): AtomState<Value>;
53
- };
54
+ type GetAtom = <Value>(anotherAtom: Atom<Value>) => Value;
54
55
  type CreateAtom = {
55
56
  <Value>(init: AtomGetter<Value>, options?: AtomOptions<Value>): DerivedAtom<Value>;
56
57
  <Value>(init: Value, options?: AtomOptions<Value>): PrimitiveAtom<Value>;
@@ -72,12 +73,11 @@ type AtomScope = {
72
73
  };
73
74
  type SetLike<Key> = Key[] | Set<Key> | (Key extends object ? WeakSet<Key> : never);
74
75
  type MapLike<Key, Value> = Map<Key, Value> | (Key extends object ? WeakMap<Key, Value> : never) | (Key extends string | number | symbol ? Record<Key, Value> : never);
75
- declare const inactive: Promise<never>;
76
76
  declare const $: CreateAtom;
77
77
  declare const isAtom: (x: unknown) => x is Atom<unknown>;
78
78
  declare const isPrimitiveAtom: (x: unknown) => x is PrimitiveAtom<unknown>;
79
79
  type AtomValuePair<Value> = [Atom<Value>, Value | PrimitiveAtom<Value>] | [DerivedAtom<Value>, Value | Atom<Value>];
80
80
  declare const createScope: <T extends AtomValuePair<unknown>[]>(parentScope?: AtomScope | null, atomValuePairs?: T) => AtomScope;
81
81
  //#endregion
82
- export { $, Atom, AtomEquals, AtomErrorState, AtomGetOptions, AtomGetter, AtomInactiveState, AtomInit, AtomOptions, AtomPromiseState, AtomReducer, AtomScope, AtomState, AtomSubscribe, AtomSubscriberOptions, AtomSuccessState, AtomUpdater, AtomValuePair, AtomWatcher, CommonAtom, DerivedAtom, GetAtom, MapLike, PrimitiveAtom, SetLike, ThenableSignal, createScope, inactive, isAtom, isPrimitiveAtom };
82
+ export { $, Atom, AtomEquals, AtomErrorState, AtomGetOptions, AtomGetter, AtomInactiveState, AtomInit, AtomOptions, AtomPromiseState, AtomReducer, AtomScope, AtomState, AtomSubscribe, AtomSubscriberOptions, AtomSuccessState, AtomUpdater, AtomValuePair, AtomWatcher, CommonAtom, DerivedAtom, GetAtom, MapLike, PrimitiveAtom, SetLike, ThenableSignal, createScope, isAtom, isPrimitiveAtom };
83
83
  //# sourceMappingURL=atom.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"atom.d.mts","names":[],"sources":["../src/atom.ts"],"mappings":";KAAY,IAAA,UAAc,aAAA,CAAc,KAAA,IAAS,WAAA,CAAY,KAAA;AAAA,KACjD,UAAA;EAAA,SACD,GAAA,QAAW,KAAA;EAAA,SACX,KAAA,GAAQ,OAAA,EAAS,WAAA;EAAA,SACjB,SAAA,GAAY,UAAA,EAAY,aAAA,CAAc,KAAA;EAAA,SACtC,KAAA,EAAO,SAAA,CAAU,KAAA;AAAA;AAAA,KAEhB,aAAA,UAAuB,UAAA,CAAW,KAAA;EAAA,SACnC,GAAA,GAAM,KAAA,EAAO,WAAA,CAAY,KAAA;EAAA,SACzB,KAAA,EAAO,gBAAA,CAAiB,KAAA;AAAA;AAAA,KAEvB,WAAA,UAAqB,UAAA,CAAW,KAAA;AAAA,KAEhC,WAAA;AAAA,KACA,aAAA,WAAwB,KAAA,EAAO,KAAA,EAAO,OAAA,EAAS,qBAAA;AAAA,KAC/C,QAAA,UAAkB,KAAA,GAAQ,UAAA,CAAW,KAAA;AAAA,KACrC,WAAA,UAAqB,KAAA,GAAQ,WAAA,CAAY,KAAA;AAAA,KAEzC,iBAAA;EACV,OAAA,SAAgB,QAAA;EAChB,KAAA;EACA,KAAA,GAAQ,KAAA;AAAA;AAAA,KAEE,gBAAA;EACV,OAAA,EAAS,WAAA,CAAY,KAAA;EACrB,KAAA;EACA,KAAA,GAAQ,KAAA;AAAA;AAAA,KAEE,gBAAA;EACV,OAAA;EACA,KAAA;EACA,KAAA,EAAO,KAAA;AAAA;AAAA,KAEG,cAAA;EACV,OAAA;EACA,KAAA;EACA,KAAA,GAAQ,KAAA;AAAA;AAAA,KAEE,SAAA,UACR,iBAAA,CAAkB,KAAA,IAClB,gBAAA,CAAiB,KAAA,IACjB,gBAAA,CAAiB,KAAA,IACjB,cAAA,CAAe,KAAA;AAAA,KAEP,qBAAA;EAAA,SAAmC,MAAA,EAAQ,cAAA;AAAA;AAAA,KAC3C,UAAA,WACV,GAAA,EAAK,OAAA,EACL,OAAA,EAAS,cAAA,KACN,KAAA,GAAQ,WAAA,CAAY,KAAA;AAAA,KACb,WAAA,WAAsB,KAAA,EAAO,KAAA,KAAU,KAAA;AAAA,KAEvC,cAAA;EAAA,SAA4B,MAAA,EAAQ,cAAA;AAAA;AAAA,KACpC,cAAA,GAAiB,WAAA;EAAgB,IAAA,GAAO,CAAA;AAAA;AAAA,KAMxC,OAAA;EAAA,QACF,WAAA,EAAa,IAAA,CAAK,KAAA,GAAQ,MAAA,UAAgB,KAAA;EAAA,QAC1C,WAAA,EAAa,IAAA,CAAK,KAAA,GAAQ,MAAA,UAAgB,SAAA,CAAU,KAAA;AAAA;AAAA,KAGzD,UAAA;EAAA,QACK,IAAA,EAAM,UAAA,CAAW,KAAA,GAAQ,OAAA,GAAU,WAAA,CAAY,KAAA,IAAS,WAAA,CAAY,KAAA;EAAA,QACpE,IAAA,EAAM,KAAA,EAAO,OAAA,GAAU,WAAA,CAAY,KAAA,IAAS,aAAA,CAAc,KAAA;EAAA,QAC1D,IAAA,EAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,GAAQ,OAAA,GAAU,WAAA,CAAY,KAAA,IAAS,IAAA,CAAK,KAAA;AAAA;AAAA,KAEnE,WAAA;EACV,MAAA,GAAS,UAAA,CAAW,KAAA;EACpB,OAAA;EACA,KAAA;AAAA;AAAA,KAGU,UAAA,WAAqB,KAAA,EAAO,KAAA,EAAO,SAAA,EAAW,KAAA;AAAA,KAC9C,SAAA;EAAA,QACF,QAAA,EAAU,aAAA,CAAc,KAAA,IAAS,aAAA,CAAc,KAAA;EAAA,QAC/C,QAAA,EAAU,WAAA,CAAY,KAAA,IAAS,WAAA,CAAY,KAAA;EAAA,QAC3C,QAAA,EAAU,IAAA,CAAK,KAAA,IAAS,IAAA,CAAK,KAAA;EAAA,QAC7B,QAAA,EAAU,aAAA,CAAc,KAAA,GAAQ,MAAA,SAAe,aAAA,CAAc,KAAA;EAAA,QAC7D,QAAA,EAAU,WAAA,CAAY,KAAA,GAAQ,MAAA,SAAe,WAAA,CAAY,KAAA;EAAA,QACzD,QAAA,EAAU,IAAA,CAAK,KAAA,GAAQ,MAAA,SAAe,IAAA,CAAK,KAAA;AAAA;AAAA,KAGzC,OAAA,QAAe,GAAA,KAAQ,GAAA,CAAI,GAAA,KAAQ,GAAA,kBAAqB,OAAA,CAAQ,GAAA;AAAA,KAChE,OAAA,eACR,GAAA,CAAI,GAAA,EAAK,KAAA,KACR,GAAA,kBAAqB,OAAA,CAAQ,GAAA,EAAK,KAAA,cAClC,GAAA,oCAAuC,MAAA,CAAO,GAAA,EAAK,KAAA;AAAA,cAiL3C,QAAA,EAAQ,OAAA;AAAA,cAGR,CAAA,EAAG,UAAA;AAAA,cAQH,MAAA,GAAU,CAAA,cAAa,CAAA,IAAK,IAAA;AAAA,cAE5B,eAAA,GAAmB,CAAA,cAAa,CAAA,IAAK,aAAA;AAAA,KAGtC,aAAA,WACP,IAAA,CAAK,KAAA,GAAQ,KAAA,GAAQ,aAAA,CAAc,KAAA,MACnC,WAAA,CAAY,KAAA,GAAQ,KAAA,GAAQ,IAAA,CAAK,KAAA;AAAA,cACzB,WAAA,aAAyB,aAAA,aACpC,WAAA,GAAc,SAAA,SACd,cAAA,GAAiB,CAAA,KAChB,SAAA"}
1
+ {"version":3,"file":"atom.d.mts","names":[],"sources":["../src/atom.ts"],"mappings":"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"}