bansa 0.0.23 → 0.0.25
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/.oxfmtrc.jsonc +3 -0
- package/README.ko.md +134 -130
- package/README.md +117 -113
- package/dist/atom.d.mts +83 -0
- package/dist/atom.d.mts.map +1 -0
- package/dist/atom.mjs +349 -0
- package/dist/atom.mjs.map +1 -0
- package/dist/browser/index.d.ts +96 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/react.d.mts +25 -0
- package/dist/react.d.mts.map +1 -0
- package/dist/react.mjs +33 -0
- package/dist/react.mjs.map +1 -0
- package/dist/utils.d.mts +17 -0
- package/dist/utils.d.mts.map +1 -0
- package/dist/utils.mjs +68 -0
- package/dist/utils.mjs.map +1 -0
- package/oxlint.config.ts +17 -0
- package/package.json +41 -34
- package/tsconfig.app.json +39 -0
- package/tsconfig.json +29 -18
- package/tsconfig.lib.json +35 -0
- package/dist/atom.d.ts +0 -80
- package/dist/atom.js +0 -419
- package/dist/index.browser.js +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/react.d.ts +0 -15
- package/dist/react.js +0 -40
- package/dist/utils.d.ts +0 -19
- package/dist/utils.js +0 -80
- package/tests/bansa.test.ts +0 -1319
package/.oxfmtrc.jsonc
ADDED
package/README.ko.md
CHANGED
|
@@ -21,11 +21,11 @@ Bansa는 파생 상태, 비동기 값, 의존성과 구독, 생명 주기, 사
|
|
|
21
21
|
일반 값(숫자/문자열/객체 등)을 `$` 함수에 전달하여 생성합니다.
|
|
22
22
|
|
|
23
23
|
```javascript
|
|
24
|
-
import { $ } from
|
|
24
|
+
import { $ } from "bansa";
|
|
25
25
|
|
|
26
26
|
const $count = $(42);
|
|
27
27
|
|
|
28
|
-
const $user = $({ name:
|
|
28
|
+
const $user = $({ name: "John Doe", age: 30 });
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
#### 파생 상태
|
|
@@ -38,13 +38,13 @@ const $user = $({ name: 'John Doe', age: 30 });
|
|
|
38
38
|
const $countDouble = $((get) => get($count) * 2);
|
|
39
39
|
|
|
40
40
|
const $userMessage = $((get) => {
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if (get($count) < 50) return "no hello.";
|
|
42
|
+
return `Hello, ${get($user).name}!`;
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
const $signalExample = $((_, { signal }) => {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
signal.then(() => console.log("$signalExample died"));
|
|
47
|
+
return fetch(`/users/${get($count)}`, { signal }).then((res) => res.json());
|
|
48
48
|
});
|
|
49
49
|
```
|
|
50
50
|
|
|
@@ -62,10 +62,10 @@ const $signalExample = $((_, { signal }) => {
|
|
|
62
62
|
|
|
63
63
|
```typescript
|
|
64
64
|
type AtomState<Value> =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
| { promise: undefined; error: undefined; value: Value } // 성공
|
|
66
|
+
| { promise: undefined; error: any; value?: Value } // 에러
|
|
67
|
+
| { promise: PromiseLike<Value>; error: any; value?: Value } // 로딩
|
|
68
|
+
| { promise: typeof inactive; error: undefined; value?: Value }; // 비활성
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
##### 활성 상태로 유지하기
|
|
@@ -89,7 +89,6 @@ console.log($countDouble.state); // { promise: undefined, error: undefined, valu
|
|
|
89
89
|
|
|
90
90
|
`.get()` 메서드는 상태가 비활성화된 경우, 매우 잠시 동안 해당 상태를 살아 있는 상태로 전환합니다. 당연히 해당 상태와 모든 의존성이 새로 실행됩니다. 매우 잠시 동안은 적어도 현재 마이크로태스크가 끝나기까지를 의미합니다. 즉, 동기적으로 연속해서 `.get()`을 호출하더라도 매번 모든 것이 다시 실행되지는 않습니다.
|
|
91
91
|
|
|
92
|
-
|
|
93
92
|
### 상태 업데이트
|
|
94
93
|
|
|
95
94
|
`.set(updater)` 메서드를 통해 원시 상태의 값을 업데이트할 수 있으며, `updater`가 일반 값이라면 해당 값으로 업데이트하고, 함수라면 상태의 '예비 값' `nextValue`에 대해 `updater(nextValue)`로 업데이트합니다.
|
|
@@ -131,8 +130,8 @@ $count.set(increment);
|
|
|
131
130
|
```javascript
|
|
132
131
|
const $count = $(0);
|
|
133
132
|
const unsubscribe = $count.subscribe((value, { signal }) => {
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
console.log("value", value);
|
|
134
|
+
signal.then(() => console.log("value end", value));
|
|
136
135
|
});
|
|
137
136
|
|
|
138
137
|
// value 0
|
|
@@ -154,8 +153,8 @@ $count.set(2);
|
|
|
154
153
|
|
|
155
154
|
```javascript
|
|
156
155
|
const $merged = $((get) => ({
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
count: get($count),
|
|
157
|
+
countDouble: get($countDouble),
|
|
159
158
|
}));
|
|
160
159
|
|
|
161
160
|
$merged.subscribe(({ count, countDouble }) => console.log(`${count} * 2 = ${countDouble}`));
|
|
@@ -167,21 +166,21 @@ $merged.subscribe(({ count, countDouble }) => console.log(`${count} * 2 = ${coun
|
|
|
167
166
|
|
|
168
167
|
```javascript
|
|
169
168
|
const $user = $(async (get) => {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
const response = await fetch(`/users/${get($count)}`);
|
|
170
|
+
if (!response.ok) throw new Error("Failed to fetch user");
|
|
171
|
+
return response.json();
|
|
173
172
|
});
|
|
174
173
|
$user.watch(() => {
|
|
175
|
-
|
|
174
|
+
console.log($user.state);
|
|
176
175
|
});
|
|
177
176
|
|
|
178
177
|
const $userName = $((get) => get($user).name);
|
|
179
178
|
|
|
180
|
-
const faultyAtom = $(() => Promise.reject(new Error(
|
|
179
|
+
const faultyAtom = $(() => Promise.reject(new Error("Something went wrong")));
|
|
181
180
|
faultyAtom.watch(() => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
if (!faultyAtom.state.promise && faultyAtom.state.error) {
|
|
182
|
+
console.error("An error occurred:", faultyAtom.state.error.message);
|
|
183
|
+
}
|
|
185
184
|
});
|
|
186
185
|
```
|
|
187
186
|
|
|
@@ -193,12 +192,12 @@ faultyAtom.watch(() => {
|
|
|
193
192
|
|
|
194
193
|
```javascript
|
|
195
194
|
const $user = $(async (get, { signal }) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
195
|
+
const count = get($count);
|
|
196
|
+
const json = await fetch(`/users/${count}`, { signal }).then((res) => res.json());
|
|
197
|
+
signal.then(() => {
|
|
198
|
+
console.log(count, json, "not used");
|
|
199
|
+
});
|
|
200
|
+
return json;
|
|
202
201
|
});
|
|
203
202
|
```
|
|
204
203
|
|
|
@@ -207,19 +206,13 @@ const $user = $(async (get, { signal }) => {
|
|
|
207
206
|
기본적으로 `Object.is`로 동등성을 체크하므로 객체나 배열의 경우 참조가 다르면 내용이 같더라도 업데이트가 발생할 수 있으며, 추가로 동등성을 확인하기 위해 상태 선언 시 `equals`를 옵션으로 줄 수 있습니다. 이 경우 `Object.is`로 같은지 확인하고, 다르다면 `equals` 함수로 다시 확인합니다. 둘 중 하나라도 참을 반환하는 경우 값 변경은 무시됩니다.
|
|
208
207
|
|
|
209
208
|
```javascript
|
|
210
|
-
const $user = $(
|
|
211
|
-
{ id: 1, name: 'Alice' },
|
|
212
|
-
{ equals: (next, prev) => next.id === prev.id },
|
|
213
|
-
);
|
|
209
|
+
const $user = $({ id: 1, name: "Alice" }, { equals: (next, prev) => next.id === prev.id });
|
|
214
210
|
|
|
215
|
-
const $user2 = $(
|
|
216
|
-
(get) => get($user),
|
|
217
|
-
{ equals: (next, prev) => next.name === prev.name },
|
|
218
|
-
);
|
|
211
|
+
const $user2 = $((get) => get($user), { equals: (next, prev) => next.name === prev.name });
|
|
219
212
|
|
|
220
|
-
userAtom.set({ id: 1, name:
|
|
213
|
+
userAtom.set({ id: 1, name: "Bob" });
|
|
221
214
|
|
|
222
|
-
userAtom.set({ id: 2, name:
|
|
215
|
+
userAtom.set({ id: 2, name: "Alice" });
|
|
223
216
|
```
|
|
224
217
|
|
|
225
218
|
위 예제에서 첫 번째 업데이트는 `id`가 같으므로 무시됩니다. 두 번째 업데이트는 `id`가 다르므로 `$user`를 업데이트하지만, `name`이 같으므로 `$user2`는 업데이트되지 않습니다.
|
|
@@ -243,7 +236,10 @@ $timers.subscribe(() => console.timeEnd());
|
|
|
243
236
|
```javascript
|
|
244
237
|
const o = () => o;
|
|
245
238
|
const toUndefined = () => undefined;
|
|
246
|
-
Object.setPrototypeOf(
|
|
239
|
+
Object.setPrototypeOf(
|
|
240
|
+
o,
|
|
241
|
+
new Proxy(o, { get: (_, k) => (k === Symbol.toPrimitive ? toUndefined : o) }),
|
|
242
|
+
);
|
|
247
243
|
```
|
|
248
244
|
|
|
249
245
|
이 코드의 `o`는 아무리 프로퍼티 접근 및 호출을 해도 같은 값을 반환합니다. 예를 들어, `o.a.b.c().d()().asdf()()()() === o`는 `true`입니다. 따라서, 셀렉터와 filter/map/reduce 등 간단한 메서드로 이뤄진 대부분의 상태 병합 함수에서 문제 없이 전체 코드를 실행할 수 있게 만듭니다. 하지만 만능은 아니므로 약간의 주의가 필요하며, 가급적 상태 병합에만 사용해야 합니다.
|
|
@@ -252,24 +248,24 @@ Object.setPrototypeOf(o, new Proxy(o, { get: (_, k) => k === Symbol.toPrimitive
|
|
|
252
248
|
|
|
253
249
|
```typescript
|
|
254
250
|
const shallowEquals = (a: any, b: any): boolean => {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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;
|
|
273
269
|
};
|
|
274
270
|
```
|
|
275
271
|
|
|
@@ -291,11 +287,11 @@ const shallowEquals = (a: any, b: any): boolean => {
|
|
|
291
287
|
const $userId = $(123);
|
|
292
288
|
const $postId = $(456);
|
|
293
289
|
const $pageData = $(async (get, { signal }) => {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
290
|
+
const user = await fetch(`/users/${get($userId)}`, { signal }).then((res) => res.json());
|
|
291
|
+
const post = await fetch(`/posts/${get($postId)}`, { signal }).then((res) => res.json());
|
|
292
|
+
userElm.innerHTML = user.name;
|
|
293
|
+
postElm.innerHTML = post.html;
|
|
294
|
+
commentElm.innerHTML = `Hello ${user.name}! Comment to ${post.author}.`;
|
|
299
295
|
});
|
|
300
296
|
```
|
|
301
297
|
|
|
@@ -304,17 +300,23 @@ const $pageData = $(async (get, { signal }) => {
|
|
|
304
300
|
```javascript
|
|
305
301
|
const $userId = $(123);
|
|
306
302
|
const $user = $((get) => fetch(`/users/${get($userId)}`, { signal }).then((res) => res.json()));
|
|
307
|
-
$user.subscribe((user) => {
|
|
303
|
+
$user.subscribe((user) => {
|
|
304
|
+
userElm.innerHTML = user.name;
|
|
305
|
+
});
|
|
308
306
|
|
|
309
307
|
const $postId = $(456);
|
|
310
308
|
const $post = $((get) => fetch(`/posts/${get($postId)}`, { signal }).then((res) => res.json()));
|
|
311
|
-
$post.subscribe((post) => {
|
|
309
|
+
$post.subscribe((post) => {
|
|
310
|
+
postElm.innerHTML = post.html;
|
|
311
|
+
});
|
|
312
312
|
|
|
313
313
|
const $pageData = $$((get) => ({
|
|
314
|
-
|
|
315
|
-
|
|
314
|
+
userName: get($user).name,
|
|
315
|
+
postAuthor: get($post).author,
|
|
316
316
|
}));
|
|
317
|
-
$pageData.subscribe(({ userName, postAuthor }) => {
|
|
317
|
+
$pageData.subscribe(({ userName, postAuthor }) => {
|
|
318
|
+
commentElm.innerHTML = `Hello ${userName}! Comment to ${postAuthor}.`;
|
|
319
|
+
});
|
|
318
320
|
```
|
|
319
321
|
|
|
320
322
|
코드 줄 수가 약간 늘어났지만, 앞서 언급한 문제들이 해결되었습니다.
|
|
@@ -404,30 +406,32 @@ const $reader = $((get) => {
|
|
|
404
406
|
|
|
405
407
|
```javascript
|
|
406
408
|
const delayedState = (initial, minDelay, maxDelay) => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
409
|
+
const $value = $(initial);
|
|
410
|
+
const $delayedValue = $(initial);
|
|
411
|
+
|
|
412
|
+
const $eventStartTime = $(0);
|
|
413
|
+
const $eventLastTime = $(0);
|
|
414
|
+
const $delayedTime = $((get) =>
|
|
415
|
+
Math.min(get($eventStartTime) + maxDelay, get($eventLastTime) + minDelay),
|
|
416
|
+
);
|
|
417
|
+
const $delayedInfo = $((get) => ({
|
|
418
|
+
value: get($value),
|
|
419
|
+
time: get($delayedTime),
|
|
420
|
+
}));
|
|
421
|
+
$delayedInfo.subscribe(({ value, time }, { signal }) => {
|
|
422
|
+
const timeout = Math.max(0, time - Date.now());
|
|
423
|
+
const timer = setTimeout(() => $delayedValue.set(value), timeout);
|
|
424
|
+
signal.then(() => clearTimeout(timer));
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const update = (value, eager = false) => {
|
|
428
|
+
const now = eager ? -Infinity : Date.now();
|
|
429
|
+
if ($value.get() === $delayedValue.get()) $eventStartTime.set(now);
|
|
430
|
+
$eventLastTime.set(now);
|
|
431
|
+
$value.set(value);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
return [$delayedValue, update];
|
|
431
435
|
};
|
|
432
436
|
```
|
|
433
437
|
|
|
@@ -441,46 +445,46 @@ $inputValue.subscribe(console.log);
|
|
|
441
445
|
|
|
442
446
|
```javascript
|
|
443
447
|
const $windowScroll = $((_, { signal }) => {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
448
|
+
let lastTime = Date.now();
|
|
449
|
+
const $scrollY = $(window.scrollY);
|
|
450
|
+
const $scrollOnTop = $((get) => get($scrollY) === 0);
|
|
451
|
+
|
|
452
|
+
const $scrollMovingAvgY = $(0);
|
|
453
|
+
const $scrollDirectionY = $((get) => Math.sign(get($scrollMovingAvgY)));
|
|
454
|
+
|
|
455
|
+
const onScrollChange = () => {
|
|
456
|
+
const now = Date.now();
|
|
457
|
+
lastTime = now;
|
|
458
|
+
|
|
459
|
+
const alpha = 0.995 ** (now - lastTime);
|
|
460
|
+
const scrollY = window.scrollY;
|
|
461
|
+
const deltaY = scrollY - $scrollY.get();
|
|
462
|
+
const movingAvgY = alpha * $scrollMovingAvgY.get() + (1 - alpha) * deltaY;
|
|
463
|
+
|
|
464
|
+
$scrollY.set(scrollY);
|
|
465
|
+
$scrollMovingAvgY.set(movingAvgY);
|
|
466
|
+
};
|
|
467
|
+
window.addEventListener("scroll", onScrollChange, {
|
|
468
|
+
passive: true,
|
|
469
|
+
signal,
|
|
470
|
+
});
|
|
471
|
+
window.addEventListener("resize", onScrollChange, {
|
|
472
|
+
passive: true,
|
|
473
|
+
signal,
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
$scrollY,
|
|
478
|
+
$scrollOnTop,
|
|
479
|
+
$scrollMovingAvgY,
|
|
480
|
+
$scrollDirectionY,
|
|
481
|
+
};
|
|
478
482
|
});
|
|
479
483
|
|
|
480
484
|
const $navHidden = $((get) => {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
+
const { $scrollOnTop, $scrollDirectionY } = get($windowScroll);
|
|
486
|
+
const scrollOnTop = get($scrollOnTop);
|
|
487
|
+
const directionY = get($scrollDirectionY);
|
|
488
|
+
return !scrollOnTop && directionY > 0;
|
|
485
489
|
});
|
|
486
490
|
```
|