bansa 0.0.12 → 0.0.14
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/index.browser.js +1 -1
- package/dist/index.browser.js.br +0 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +248 -214
- package/dist/react.js +2 -2
- package/package.json +1 -1
- package/tests/bansa.test.ts +97 -7
package/dist/index.browser.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var d=class{r;p;a;o;l;get(){if(this.s||(I(this),u(this)),this.state.error)throw this.state.error;if(this.state.promise)throw this.state.promise;return this.state.value}watch(t){return this.s||k(this),(this.o||=new Set).add(t),()=>{this.o.delete(t),this.o.size||u(this)}}subscribe(t){let a={h:t,A:{get signal(){return(a.t||=q()).signal}}};if(!this.s)k(this);else if(!this.state.error&&!this.state.promise)try{t(this.state.value,a.A)}catch(n){V(n)}return(this.l||=new Set).add(a),()=>{this.l.delete(a),a.t&&(a.t.abort(),a.t=void 0),this.l.size||u(this)}}[Symbol.toPrimitive](){return this.state.value}},i=class extends d{n=!1;m=!1;constructor(t,a){super(),this.d=t,this.V=a?.equals,this.r=t,this.state={promise:void 0,error:void 0,value:t}}set(t){let a=t instanceof Function?t(this.r):t;x(a,this.state.value,this.V)||(this.r=a,y(this))}};i.prototype.y=!0;i.prototype.s=!0;i.prototype.u=!1;var m=class extends d{s=!1;u=!1;n=!1;m=!1;f=0;t;i;c;constructor(t,a){super(),this.d=t,this.V=a?.equals,this.b=!!a?.persist;let n=this;this.A={get signal(){return(n.t||=q()).signal}},this.state={promise:g,error:void 0,value:void 0}}};m.prototype.y=!1;var c=()=>c,_=()=>{};Object.setPrototypeOf(c,new Proxy(c,{get:(e,t)=>t===Symbol.toPrimitive?_:c}));var g=Promise.reject();g.catch(_);var A=(e,t)=>e instanceof Function?new m(e,t):new i(e,t),M=e=>A((t,a)=>{let n,r,l=e(f=>{let o=t(f,!1);if(o.error)r=o.error;else if(o.promise)(n||=[]).push(o.promise);else return o.value;return c},a);if(r)throw r;if(n)throw Promise.all(n);return l},{equals:L}),W=(e,t)=>{let a=new WeakMap;if(t)for(let[r,l]of t)a.set(r,l instanceof d?l:A(l));let n=(r=>{let l=a.get(r);if(!l){let f=e?.(r),o=f||r;a.set(r,l=o.d instanceof Function?A((D,T)=>o.d((E,K)=>D(n(E),K),T),{equals:o.V,persist:o.b}):f||A(o.d))}return l});return n},L=(e,t)=>{if(typeof e!="object"||typeof t!="object"||!e||!t)return!1;let a=e.constructor;if(a!==t.constructor)return!1;if(a===Array){let r=e.length;if(r!==t.length)return!1;for(;(r=r-1|0)>=0;)if(!Object.is(e[r],t[r]))return!1;return!0}let n=0;for(let r in e){if(!(r in t&&Object.is(e[r],t[r])))return!1;n=n+1|0}for(let r in t)if((n=n-1|0)<0)return!1;return!0},b=!1,h=[],v=[],k=e=>{e.u||(e.u=!0,y(e))},y=e=>{e.n||(e.n=!0,h.push(e),b||(b=!0,queueMicrotask(j)))},j=()=>{b=!1;{let t=h;h=[];for(let a of t)a.state.promise=void 0,a.state.error=a.p,a.state.value=a.r,O(a)}let e=v;v=[];for(let t=e.length;t--;){let a=e[t];a.m=!1,a.u&&(a.n=!0,I(a)),a.n&&G(a)}},G=e=>{if(e.n=!1,e.o)for(let t of e.o)try{t()}catch(a){V(a)}if(!e.state.error&&!e.state.promise){if(e.l)for(let t of e.l){t.t&&(t.t.abort(),t.t=void 0);try{t.h(e.state.value,t.A)}catch(a){V(a)}}if(e.a)for(let t of e.a)t.u=!0}},O=e=>{if(!e.m){if(e.m=!0,e.a)for(let t of e.a)O(t);v.push(e)}},s=class{e;constructor(t){this.e=t}},P=Symbol(),I=e=>{let t=++e.f;e.s=!0,e.u=!1,e.state.promise=void 0,e.t&&(e.t.abort(),e.t=void 0);try{let a=e.d((n,r=!0)=>{if(t!==e.f)throw P;if(e!==n&&(n.s||(I(n),n.n&&G(n)),(e.c||=new Set).add(n),(n.a||=new Set).add(e)),!r)return n.state;if(n.state.error)throw new s(n.state.error);if(n.state.promise)throw new s(n.state.promise);return n.state.value},e.A);C(a)?(e.state.promise=a,a.then(n=>{t===e.f&&(p(e),x(n,e.state.value,e.V)?e.state.promise=void 0:(e.r=n,e.p=void 0),y(e))},n=>{t===e.f&&(p(e),n instanceof s?n=n.e:V(n),e.p=n,y(e))})):(p(e),e.state.error=void 0,x(a,e.state.value,e.V)?e.n=!1:e.state.value=e.r=a)}catch(a){p(e),a===P?e.n=!1:(a instanceof s?a=a.e:V(a),e.state.error=a)}},p=e=>{++e.f;let t=e.i;if(e.i=e.c,t){for(let a of t)e.i?.has(a)||(a.a.delete(e),u(a));t.clear()}e.c=t},S=!1,w=new Set,u=e=>{!e.y&&!e.b&&!e.a?.size&&!e.o?.size&&!e.l?.size&&(w.add(e),S||(S=!0,setTimeout(z,0)))},z=()=>{for(let e of w)if(!e.y&&!e.b&&!e.a?.size&&!e.o?.size&&!e.l?.size&&(e.state.promise=g,e.r=e.p=e.state.error=e.state.value=void 0,e.n=e.u=e.s=!1,e.t&&(e.t.abort(),e.t=void 0),e.i)){for(let t of e.i)t.a.delete(e),u(t);if(e.i.clear(),e.c){for(let t of e.c)t.a.delete(e),u(t);e.c.clear()}}w.clear(),S=!1},x=(e,t,a)=>Object.is(e,t)||a!==void 0&&t!==void 0&&a(e,t),C=e=>typeof e?.then=="function",q=()=>{let e=new AbortController,t=e.signal,a=new Promise(n=>{t.then=r=>a.then(r),t.addEventListener("abort",n,{once:!0,passive:!0})});return{abort:()=>e.abort(),signal:t}},V=e=>{queueMicrotask(()=>{throw e})};export{A as $,M as $$,W as createScope,g as inactive};
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -71,9 +71,9 @@ export type AtomScope = {
|
|
|
71
71
|
};
|
|
72
72
|
export type SetLike<Key> = Key[] | Set<Key> | (Key extends object ? WeakSet<Key> : never);
|
|
73
73
|
export type MapLike<Key, Value> = Map<Key, Value> | (Key extends object ? WeakMap<Key, Value> : never) | (Key extends string | number | symbol ? Record<Key, Value> : never);
|
|
74
|
-
export declare const inactive: Promise<
|
|
74
|
+
export declare const inactive: Promise<never>;
|
|
75
75
|
export declare const $: CreateAtom;
|
|
76
76
|
export declare const $$: <Value>(init: AtomGetter<Value>) => DerivedAtom<Value>;
|
|
77
|
-
export type AtomValuePair<Value> = [Atom<Value>, Value | Atom<Value>];
|
|
78
|
-
export declare const createScope: (parentScope?: AtomScope | null, atomValuePairs?:
|
|
77
|
+
export type AtomValuePair<Value> = [Atom<Value>, Value | PrimitiveAtom<Value>] | [DerivedAtom<Value>, Value | Atom<Value>];
|
|
78
|
+
export declare const createScope: <T extends AtomValuePair<unknown>[]>(parentScope?: AtomScope | null, atomValuePairs?: T) => AtomScope;
|
|
79
79
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,104 +1,117 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return this.state.value;
|
|
11
|
-
};
|
|
12
|
-
AtomPrototype.prototype.watch = function(watcher) {
|
|
13
|
-
if (!this.c) {
|
|
14
|
-
requestActivate(this);
|
|
15
|
-
}
|
|
16
|
-
(this.h ??= /* @__PURE__ */ new Set()).add(watcher);
|
|
17
|
-
return () => {
|
|
18
|
-
this.h.delete(watcher);
|
|
19
|
-
if (!this.h.size) {
|
|
1
|
+
class CommonAtomInternal {
|
|
2
|
+
d;
|
|
3
|
+
o;
|
|
4
|
+
b;
|
|
5
|
+
f;
|
|
6
|
+
g;
|
|
7
|
+
get() {
|
|
8
|
+
if (!this.h) {
|
|
9
|
+
execute(this);
|
|
20
10
|
disableAtom(this);
|
|
21
11
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const atomSubscriber = {
|
|
26
|
-
r: subscriber,
|
|
27
|
-
m: {
|
|
28
|
-
get signal() {
|
|
29
|
-
return (atomSubscriber.a ??= createThenableSignal()).signal;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
if (!this.c) {
|
|
34
|
-
requestActivate(this);
|
|
35
|
-
} else if (!this.state.promise && !this.state.error) {
|
|
36
|
-
try {
|
|
37
|
-
subscriber(this.state.value, atomSubscriber.m);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
logError(e);
|
|
40
|
-
}
|
|
12
|
+
if (this.state.error) throw this.state.error;
|
|
13
|
+
if (this.state.promise) throw this.state.promise;
|
|
14
|
+
return this.state.value;
|
|
41
15
|
}
|
|
42
|
-
(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (atomSubscriber.a) {
|
|
46
|
-
atomSubscriber.a.abort();
|
|
47
|
-
atomSubscriber.a = void 0;
|
|
16
|
+
watch(watcher) {
|
|
17
|
+
if (!this.h) {
|
|
18
|
+
requestActivate(this);
|
|
48
19
|
}
|
|
49
|
-
|
|
50
|
-
|
|
20
|
+
(this.f ||= /* @__PURE__ */ new Set()).add(watcher);
|
|
21
|
+
return () => {
|
|
22
|
+
this.f.delete(watcher);
|
|
23
|
+
if (!this.f.size) {
|
|
24
|
+
disableAtom(this);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
subscribe(subscriber) {
|
|
29
|
+
const atomSubscriber = {
|
|
30
|
+
t: subscriber,
|
|
31
|
+
p: {
|
|
32
|
+
get signal() {
|
|
33
|
+
return (atomSubscriber.a ||= createThenableSignal()).signal;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
if (!this.h) {
|
|
38
|
+
requestActivate(this);
|
|
39
|
+
} else if (!this.state.error && !this.state.promise) {
|
|
40
|
+
try {
|
|
41
|
+
subscriber(this.state.value, atomSubscriber.p);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
logError(e);
|
|
44
|
+
}
|
|
51
45
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
value: init
|
|
64
|
-
};
|
|
65
|
-
this.state.value = this.f = init;
|
|
66
|
-
};
|
|
67
|
-
PrimitiveAtomPrototype.prototype.set = function(value) {
|
|
68
|
-
const nextValue = value instanceof Function ? value(this.f) : value;
|
|
69
|
-
if (!equals(nextValue, this.state.value, this.n)) {
|
|
70
|
-
this.f = nextValue;
|
|
71
|
-
requestPropagate(this);
|
|
46
|
+
(this.g ||= /* @__PURE__ */ new Set()).add(atomSubscriber);
|
|
47
|
+
return () => {
|
|
48
|
+
this.g.delete(atomSubscriber);
|
|
49
|
+
if (atomSubscriber.a) {
|
|
50
|
+
atomSubscriber.a.abort();
|
|
51
|
+
atomSubscriber.a = void 0;
|
|
52
|
+
}
|
|
53
|
+
if (!this.g.size) {
|
|
54
|
+
disableAtom(this);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
72
57
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
58
|
+
[Symbol.toPrimitive]() {
|
|
59
|
+
return this.state.value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
class PrimitiveAtomInternal extends CommonAtomInternal {
|
|
63
|
+
c = false;
|
|
64
|
+
q = false;
|
|
65
|
+
constructor(init, options) {
|
|
66
|
+
super();
|
|
67
|
+
this.l = init;
|
|
68
|
+
this.m = options?.equals;
|
|
69
|
+
this.d = init;
|
|
70
|
+
this.state = {
|
|
71
|
+
promise: void 0,
|
|
72
|
+
error: void 0,
|
|
73
|
+
value: init
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
set(value) {
|
|
77
|
+
const nextValue = value instanceof Function ? value(this.d) : value;
|
|
78
|
+
if (!equals(nextValue, this.state.value, this.m)) {
|
|
79
|
+
this.d = nextValue;
|
|
80
|
+
requestPropagate(this);
|
|
94
81
|
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
PrimitiveAtomInternal.prototype.r = true;
|
|
85
|
+
PrimitiveAtomInternal.prototype.h = true;
|
|
86
|
+
PrimitiveAtomInternal.prototype.i = false;
|
|
87
|
+
class DerivedAtomInternal extends CommonAtomInternal {
|
|
88
|
+
h = false;
|
|
89
|
+
i = false;
|
|
90
|
+
c = false;
|
|
91
|
+
q = false;
|
|
92
|
+
n = 0;
|
|
93
|
+
a;
|
|
94
|
+
j;
|
|
95
|
+
k;
|
|
96
|
+
constructor(init, options) {
|
|
97
|
+
super();
|
|
98
|
+
this.l = init;
|
|
99
|
+
this.m = options?.equals;
|
|
100
|
+
this.s = !!options?.persist;
|
|
101
|
+
const self = this;
|
|
102
|
+
this.p = {
|
|
103
|
+
get signal() {
|
|
104
|
+
return (self.a ||= createThenableSignal()).signal;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
this.state = {
|
|
108
|
+
promise: inactive,
|
|
109
|
+
error: void 0,
|
|
110
|
+
value: void 0
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
DerivedAtomInternal.prototype.r = false;
|
|
102
115
|
const ouroboros = () => ouroboros;
|
|
103
116
|
const toUndefined = () => void 0;
|
|
104
117
|
Object.setPrototypeOf(
|
|
@@ -107,28 +120,21 @@ Object.setPrototypeOf(
|
|
|
107
120
|
get: (_, k) => k === Symbol.toPrimitive ? toUndefined : ouroboros
|
|
108
121
|
})
|
|
109
122
|
);
|
|
110
|
-
const inactive = Promise.
|
|
123
|
+
const inactive = Promise.reject();
|
|
124
|
+
inactive.catch(toUndefined);
|
|
111
125
|
const $ = (init, options) => {
|
|
112
126
|
if (init instanceof Function)
|
|
113
|
-
return new
|
|
114
|
-
return new
|
|
127
|
+
return new DerivedAtomInternal(init, options);
|
|
128
|
+
return new PrimitiveAtomInternal(init, options);
|
|
115
129
|
};
|
|
116
130
|
const $$ = (init) => $((get, options) => {
|
|
117
131
|
let promises;
|
|
118
132
|
let error;
|
|
119
|
-
const result = init((atom
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
throw e;
|
|
125
|
-
}
|
|
126
|
-
if (isPromiseLike(e)) {
|
|
127
|
-
(promises ??= []).push(e);
|
|
128
|
-
} else {
|
|
129
|
-
error = e;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
133
|
+
const result = init((atom) => {
|
|
134
|
+
const state = get(atom, false);
|
|
135
|
+
if (state.error) error = state.error;
|
|
136
|
+
else if (state.promise) (promises ||= []).push(state.promise);
|
|
137
|
+
else return state.value;
|
|
132
138
|
return ouroboros;
|
|
133
139
|
}, options);
|
|
134
140
|
if (error) throw error;
|
|
@@ -141,15 +147,25 @@ const createScope = (parentScope, atomValuePairs) => {
|
|
|
141
147
|
const scopeMap = /* @__PURE__ */ new WeakMap();
|
|
142
148
|
if (atomValuePairs) {
|
|
143
149
|
for (const [atom, value] of atomValuePairs) {
|
|
144
|
-
scopeMap.set(atom, value instanceof
|
|
150
|
+
scopeMap.set(atom, value instanceof CommonAtomInternal ? value : $(value));
|
|
145
151
|
}
|
|
146
152
|
}
|
|
147
|
-
const scope = ((baseAtom
|
|
153
|
+
const scope = ((baseAtom) => {
|
|
148
154
|
let scopedAtom = scopeMap.get(baseAtom);
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
if (!scopedAtom) {
|
|
156
|
+
const parentAtom = parentScope?.(baseAtom);
|
|
157
|
+
const realBaseAtom = parentAtom || baseAtom;
|
|
158
|
+
scopeMap.set(
|
|
159
|
+
baseAtom,
|
|
160
|
+
scopedAtom = realBaseAtom.l instanceof Function ? $((get, options) => realBaseAtom.l(
|
|
161
|
+
(atom, unwrap) => get(scope(atom), unwrap),
|
|
162
|
+
options
|
|
163
|
+
), {
|
|
164
|
+
equals: realBaseAtom.m,
|
|
165
|
+
persist: realBaseAtom.s
|
|
166
|
+
}) : parentAtom || $(realBaseAtom.l)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
153
169
|
return scopedAtom;
|
|
154
170
|
});
|
|
155
171
|
return scope;
|
|
@@ -173,17 +189,18 @@ const shallowEquals = (a, b) => {
|
|
|
173
189
|
return true;
|
|
174
190
|
};
|
|
175
191
|
let pendingUpdateAtoms = false;
|
|
192
|
+
let updateQueue = [];
|
|
176
193
|
let stack = [];
|
|
177
194
|
const requestActivate = (atom) => {
|
|
178
|
-
if (!atom.
|
|
179
|
-
atom.
|
|
195
|
+
if (!atom.i) {
|
|
196
|
+
atom.i = true;
|
|
180
197
|
requestPropagate(atom);
|
|
181
198
|
}
|
|
182
199
|
};
|
|
183
200
|
const requestPropagate = (atom) => {
|
|
184
|
-
if (!atom.
|
|
185
|
-
atom.
|
|
186
|
-
|
|
201
|
+
if (!atom.c) {
|
|
202
|
+
atom.c = true;
|
|
203
|
+
updateQueue.push(atom);
|
|
187
204
|
if (!pendingUpdateAtoms) {
|
|
188
205
|
pendingUpdateAtoms = true;
|
|
189
206
|
queueMicrotask(updateAtoms);
|
|
@@ -193,12 +210,12 @@ const requestPropagate = (atom) => {
|
|
|
193
210
|
const updateAtoms = () => {
|
|
194
211
|
pendingUpdateAtoms = false;
|
|
195
212
|
{
|
|
196
|
-
const updatedAtoms =
|
|
197
|
-
|
|
213
|
+
const updatedAtoms = updateQueue;
|
|
214
|
+
updateQueue = [];
|
|
198
215
|
for (const atom of updatedAtoms) {
|
|
199
216
|
atom.state.promise = void 0;
|
|
200
217
|
atom.state.error = atom.o;
|
|
201
|
-
atom.state.value = atom.
|
|
218
|
+
atom.state.value = atom.d;
|
|
202
219
|
mark(atom);
|
|
203
220
|
}
|
|
204
221
|
}
|
|
@@ -207,46 +224,52 @@ const updateAtoms = () => {
|
|
|
207
224
|
for (let i = markedAtoms.length; i--; ) {
|
|
208
225
|
const atom = markedAtoms[i];
|
|
209
226
|
atom.q = false;
|
|
210
|
-
if (atom.
|
|
211
|
-
atom.
|
|
227
|
+
if (atom.i) {
|
|
228
|
+
atom.c = true;
|
|
212
229
|
execute(atom);
|
|
213
230
|
}
|
|
214
|
-
if (atom.
|
|
231
|
+
if (atom.c) {
|
|
215
232
|
propagate(atom);
|
|
216
233
|
}
|
|
217
234
|
}
|
|
218
235
|
};
|
|
219
236
|
const propagate = (atom) => {
|
|
220
|
-
atom.
|
|
221
|
-
if (atom.
|
|
222
|
-
for (const
|
|
223
|
-
child.k = true;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (atom.h) {
|
|
227
|
-
for (const watcher of atom.h) {
|
|
228
|
-
watcher();
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (atom.i && !atom.state.promise && !atom.state.error) {
|
|
232
|
-
for (const subscriber of atom.i) {
|
|
233
|
-
if (subscriber.a) {
|
|
234
|
-
subscriber.a.abort();
|
|
235
|
-
subscriber.a = void 0;
|
|
236
|
-
}
|
|
237
|
+
atom.c = false;
|
|
238
|
+
if (atom.f) {
|
|
239
|
+
for (const watcher of atom.f) {
|
|
237
240
|
try {
|
|
238
|
-
|
|
241
|
+
watcher();
|
|
239
242
|
} catch (e) {
|
|
240
243
|
logError(e);
|
|
241
244
|
}
|
|
242
245
|
}
|
|
243
246
|
}
|
|
247
|
+
if (!atom.state.error && !atom.state.promise) {
|
|
248
|
+
if (atom.g) {
|
|
249
|
+
for (const subscriber of atom.g) {
|
|
250
|
+
if (subscriber.a) {
|
|
251
|
+
subscriber.a.abort();
|
|
252
|
+
subscriber.a = void 0;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
subscriber.t(atom.state.value, subscriber.p);
|
|
256
|
+
} catch (e) {
|
|
257
|
+
logError(e);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (atom.b) {
|
|
262
|
+
for (const child of atom.b) {
|
|
263
|
+
child.i = true;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
244
267
|
};
|
|
245
268
|
const mark = (atom) => {
|
|
246
269
|
if (!atom.q) {
|
|
247
270
|
atom.q = true;
|
|
248
|
-
if (atom.
|
|
249
|
-
for (const child of atom.
|
|
271
|
+
if (atom.b) {
|
|
272
|
+
for (const child of atom.b) {
|
|
250
273
|
mark(child);
|
|
251
274
|
}
|
|
252
275
|
}
|
|
@@ -259,133 +282,144 @@ class Wrapped {
|
|
|
259
282
|
this.e = e;
|
|
260
283
|
}
|
|
261
284
|
}
|
|
285
|
+
const expired = Symbol();
|
|
262
286
|
const execute = (atom) => {
|
|
263
|
-
const counter = ++atom.
|
|
264
|
-
atom.
|
|
265
|
-
atom.
|
|
287
|
+
const counter = ++atom.n;
|
|
288
|
+
atom.h = true;
|
|
289
|
+
atom.i = false;
|
|
266
290
|
atom.state.promise = void 0;
|
|
267
291
|
if (atom.a) {
|
|
268
292
|
atom.a.abort();
|
|
269
293
|
atom.a = void 0;
|
|
270
294
|
}
|
|
271
|
-
const oldDependencies = atom.l;
|
|
272
|
-
if (oldDependencies) {
|
|
273
|
-
atom.l = /* @__PURE__ */ new Set();
|
|
274
|
-
}
|
|
275
295
|
try {
|
|
276
|
-
const value = atom.
|
|
296
|
+
const value = atom.l(
|
|
277
297
|
(anotherAtom, unwrap = true) => {
|
|
278
|
-
if (counter !== atom.
|
|
298
|
+
if (counter !== atom.n) throw expired;
|
|
279
299
|
if (atom !== anotherAtom) {
|
|
280
|
-
if (!anotherAtom.
|
|
300
|
+
if (!anotherAtom.h) {
|
|
281
301
|
execute(anotherAtom);
|
|
282
|
-
if (anotherAtom.
|
|
283
|
-
anotherAtom.b = false;
|
|
302
|
+
if (anotherAtom.c) {
|
|
284
303
|
propagate(anotherAtom);
|
|
285
304
|
}
|
|
286
305
|
}
|
|
287
|
-
|
|
288
|
-
(
|
|
289
|
-
(anotherAtom.d ??= /* @__PURE__ */ new Set()).add(atom);
|
|
306
|
+
(atom.k ||= /* @__PURE__ */ new Set()).add(anotherAtom);
|
|
307
|
+
(anotherAtom.b ||= /* @__PURE__ */ new Set()).add(atom);
|
|
290
308
|
}
|
|
291
309
|
if (!unwrap) return anotherAtom.state;
|
|
292
|
-
if (anotherAtom.state.promise)
|
|
293
|
-
throw new Wrapped(anotherAtom.state.promise);
|
|
294
310
|
if (anotherAtom.state.error)
|
|
295
311
|
throw new Wrapped(anotherAtom.state.error);
|
|
312
|
+
if (anotherAtom.state.promise)
|
|
313
|
+
throw new Wrapped(anotherAtom.state.promise);
|
|
296
314
|
return anotherAtom.state.value;
|
|
297
315
|
},
|
|
298
|
-
atom.
|
|
316
|
+
atom.p
|
|
299
317
|
);
|
|
300
318
|
if (isPromiseLike(value)) {
|
|
301
319
|
atom.state.promise = value;
|
|
302
320
|
value.then(
|
|
303
321
|
(value2) => {
|
|
304
|
-
if (counter === atom.
|
|
305
|
-
|
|
322
|
+
if (counter === atom.n) {
|
|
323
|
+
finalizeExecution(atom);
|
|
324
|
+
if (equals(value2, atom.state.value, atom.m)) {
|
|
306
325
|
atom.state.promise = void 0;
|
|
307
326
|
} else {
|
|
308
|
-
atom.
|
|
327
|
+
atom.d = value2;
|
|
309
328
|
atom.o = void 0;
|
|
310
|
-
requestPropagate(atom);
|
|
311
329
|
}
|
|
330
|
+
requestPropagate(atom);
|
|
312
331
|
}
|
|
313
332
|
},
|
|
314
333
|
(e) => {
|
|
315
|
-
if (counter === atom.
|
|
316
|
-
|
|
317
|
-
|
|
334
|
+
if (counter === atom.n) {
|
|
335
|
+
finalizeExecution(atom);
|
|
336
|
+
if (e instanceof Wrapped) {
|
|
337
|
+
e = e.e;
|
|
318
338
|
} else {
|
|
319
|
-
|
|
320
|
-
e = e.e;
|
|
321
|
-
} else {
|
|
322
|
-
logError(e);
|
|
323
|
-
}
|
|
324
|
-
atom.o = e;
|
|
325
|
-
requestPropagate(atom);
|
|
339
|
+
logError(e);
|
|
326
340
|
}
|
|
341
|
+
atom.o = e;
|
|
342
|
+
requestPropagate(atom);
|
|
327
343
|
}
|
|
328
344
|
}
|
|
329
345
|
);
|
|
330
346
|
} else {
|
|
331
|
-
|
|
347
|
+
finalizeExecution(atom);
|
|
332
348
|
atom.state.error = void 0;
|
|
333
|
-
if (equals(value, atom.state.value, atom.
|
|
334
|
-
atom.
|
|
349
|
+
if (equals(value, atom.state.value, atom.m)) {
|
|
350
|
+
atom.c = false;
|
|
335
351
|
} else {
|
|
336
|
-
atom.state.value = atom.
|
|
352
|
+
atom.state.value = atom.d = value;
|
|
337
353
|
}
|
|
338
354
|
}
|
|
339
355
|
} catch (e) {
|
|
340
|
-
|
|
341
|
-
if (
|
|
342
|
-
atom.
|
|
356
|
+
finalizeExecution(atom);
|
|
357
|
+
if (e === expired) {
|
|
358
|
+
atom.c = false;
|
|
343
359
|
} else {
|
|
344
360
|
if (e instanceof Wrapped) {
|
|
345
361
|
e = e.e;
|
|
346
362
|
} else {
|
|
347
363
|
logError(e);
|
|
348
364
|
}
|
|
349
|
-
|
|
350
|
-
atom.state.promise = e;
|
|
351
|
-
} else {
|
|
352
|
-
atom.state.error = e;
|
|
353
|
-
}
|
|
365
|
+
atom.state.error = e;
|
|
354
366
|
}
|
|
355
367
|
}
|
|
368
|
+
};
|
|
369
|
+
const finalizeExecution = (atom) => {
|
|
370
|
+
++atom.n;
|
|
371
|
+
const oldDependencies = atom.j;
|
|
372
|
+
atom.j = atom.k;
|
|
356
373
|
if (oldDependencies) {
|
|
357
374
|
for (const dep of oldDependencies) {
|
|
358
|
-
|
|
359
|
-
|
|
375
|
+
if (!atom.j?.has(dep)) {
|
|
376
|
+
dep.b.delete(atom);
|
|
377
|
+
disableAtom(dep);
|
|
378
|
+
}
|
|
360
379
|
}
|
|
380
|
+
oldDependencies.clear();
|
|
361
381
|
}
|
|
382
|
+
atom.k = oldDependencies;
|
|
362
383
|
};
|
|
363
|
-
let
|
|
384
|
+
let runningGc = false;
|
|
385
|
+
let gcCandidates = /* @__PURE__ */ new Set();
|
|
364
386
|
const disableAtom = (atom) => {
|
|
365
|
-
if (!atom.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
disabling = false;
|
|
371
|
-
}, 0);
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
atom.state.promise = inactive;
|
|
375
|
-
atom.f = atom.o = atom.state.error = atom.state.value = void 0;
|
|
376
|
-
atom.b = atom.k = atom.c = false;
|
|
377
|
-
if (atom.a) {
|
|
378
|
-
atom.a.abort();
|
|
379
|
-
atom.a = void 0;
|
|
387
|
+
if (!atom.r && !atom.s && !atom.b?.size && !atom.f?.size && !atom.g?.size) {
|
|
388
|
+
gcCandidates.add(atom);
|
|
389
|
+
if (!runningGc) {
|
|
390
|
+
runningGc = true;
|
|
391
|
+
setTimeout(gc, 0);
|
|
380
392
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const gc = () => {
|
|
396
|
+
for (const atom of gcCandidates) {
|
|
397
|
+
if (!atom.r && !atom.s && !atom.b?.size && !atom.f?.size && !atom.g?.size) {
|
|
398
|
+
atom.state.promise = inactive;
|
|
399
|
+
atom.d = atom.o = atom.state.error = atom.state.value = void 0;
|
|
400
|
+
atom.c = atom.i = atom.h = false;
|
|
401
|
+
if (atom.a) {
|
|
402
|
+
atom.a.abort();
|
|
403
|
+
atom.a = void 0;
|
|
404
|
+
}
|
|
405
|
+
if (atom.j) {
|
|
406
|
+
for (const dep of atom.j) {
|
|
407
|
+
dep.b.delete(atom);
|
|
408
|
+
disableAtom(dep);
|
|
409
|
+
}
|
|
410
|
+
atom.j.clear();
|
|
411
|
+
if (atom.k) {
|
|
412
|
+
for (const dep of atom.k) {
|
|
413
|
+
dep.b.delete(atom);
|
|
414
|
+
disableAtom(dep);
|
|
415
|
+
}
|
|
416
|
+
atom.k.clear();
|
|
417
|
+
}
|
|
385
418
|
}
|
|
386
|
-
atom.l.clear();
|
|
387
419
|
}
|
|
388
420
|
}
|
|
421
|
+
gcCandidates.clear();
|
|
422
|
+
runningGc = false;
|
|
389
423
|
};
|
|
390
424
|
const equals = (value, prevValue, equalsFn) => Object.is(value, prevValue) || equalsFn !== void 0 && prevValue !== void 0 && equalsFn(value, prevValue);
|
|
391
425
|
const isPromiseLike = (x) => typeof x?.then === "function";
|
package/dist/react.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, use, useContext, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { createContext, use, useContext, useMemo, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { $, createScope } from ".";
|
|
4
4
|
const ScopeContext = createContext((x) => x);
|
|
5
5
|
const ScopeProvider = ({ value, children }) => {
|
|
6
6
|
const parentScope = useContext(ScopeContext);
|
|
7
|
-
const scope =
|
|
7
|
+
const scope = useMemo(() => createScope(parentScope, value), [parentScope]);
|
|
8
8
|
return /* @__PURE__ */ jsx(ScopeContext.Provider, { value: scope, children });
|
|
9
9
|
};
|
|
10
10
|
const useAtomValue = (atom) => (atom = useContext(ScopeContext)(atom), useSyncExternalStore(
|
package/package.json
CHANGED
package/tests/bansa.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { $, createScope, type ThenableSignal } from '../src/index';
|
|
2
|
+
import { $, createScope, DerivedAtom, type ThenableSignal } from '../src/index';
|
|
3
3
|
|
|
4
4
|
const flushMicrotasks = () =>
|
|
5
5
|
new Promise((resolve) => {
|
|
@@ -445,6 +445,92 @@ describe('Atom Library - Advanced Tests', () => {
|
|
|
445
445
|
expect(derivedAtom3.state.value).toEqual(undefined);
|
|
446
446
|
});
|
|
447
447
|
|
|
448
|
+
it('gc test', async () => {
|
|
449
|
+
const atom1 = $(10);
|
|
450
|
+
|
|
451
|
+
const metrics1 = { mounted: 0, unmounted: 0 };
|
|
452
|
+
const derivedAtom1 = $((get, { signal }) => {
|
|
453
|
+
metrics1.mounted++;
|
|
454
|
+
signal.then(() => {
|
|
455
|
+
metrics1.unmounted++;
|
|
456
|
+
});
|
|
457
|
+
return get(atom1);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const unsub = derivedAtom1.subscribe(nop);
|
|
461
|
+
await flushMicrotasks();
|
|
462
|
+
expect(metrics1).toEqual({ mounted: 1, unmounted: 0 });
|
|
463
|
+
expect(derivedAtom1.state.value).toEqual(10);
|
|
464
|
+
|
|
465
|
+
unsub();
|
|
466
|
+
await flushMicrotasks();
|
|
467
|
+
expect(metrics1).toEqual({ mounted: 1, unmounted: 0 });
|
|
468
|
+
expect(derivedAtom1.state.value).toEqual(10);
|
|
469
|
+
|
|
470
|
+
const unsub2 = derivedAtom1.subscribe(nop);
|
|
471
|
+
await flushMicrotasks();
|
|
472
|
+
expect(metrics1).toEqual({ mounted: 1, unmounted: 0 });
|
|
473
|
+
expect(derivedAtom1.state.value).toEqual(10);
|
|
474
|
+
|
|
475
|
+
atom1.set(20);
|
|
476
|
+
await flushMicrotasks();
|
|
477
|
+
expect(metrics1).toEqual({ mounted: 2, unmounted: 1 });
|
|
478
|
+
expect(derivedAtom1.state.value).toEqual(20);
|
|
479
|
+
|
|
480
|
+
unsub2();
|
|
481
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
482
|
+
expect(metrics1).toEqual({ mounted: 2, unmounted: 2 });
|
|
483
|
+
expect(derivedAtom1.state.value).toEqual(undefined);
|
|
484
|
+
|
|
485
|
+
atom1.set(30);
|
|
486
|
+
await flushMicrotasks();
|
|
487
|
+
expect(metrics1).toEqual({ mounted: 2, unmounted: 2 });
|
|
488
|
+
expect(derivedAtom1.state.value).toEqual(undefined);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('deep scope', async () => {
|
|
492
|
+
const $x1 = $(1);
|
|
493
|
+
const $x2 = $((get) => get($x1) + 1);
|
|
494
|
+
const $x3 = $((get) => get($x2) + 1);
|
|
495
|
+
const $x4 = $((get) => get($x3) + 1);
|
|
496
|
+
const $x5 = $((get) => get($x4) + 1);
|
|
497
|
+
const identity = (x: any) => x;
|
|
498
|
+
const scope0 = createScope(identity);
|
|
499
|
+
const scope1 = createScope(identity, [
|
|
500
|
+
[$x1, 11],
|
|
501
|
+
]);
|
|
502
|
+
const scope2 = createScope(identity, [
|
|
503
|
+
[$x2, 22],
|
|
504
|
+
]);
|
|
505
|
+
|
|
506
|
+
$x5.subscribe(nop);
|
|
507
|
+
await flushMicrotasks();
|
|
508
|
+
|
|
509
|
+
const $y0 = scope0($x5);
|
|
510
|
+
const $y1 = scope1($x5);
|
|
511
|
+
const $y2 = scope2($x5);
|
|
512
|
+
|
|
513
|
+
expect($x5.get()).toBe(5);
|
|
514
|
+
expect($y0.get()).toBe(5);
|
|
515
|
+
expect($y1.get()).toBe(15);
|
|
516
|
+
expect($y2.get()).toBe(25);
|
|
517
|
+
|
|
518
|
+
$x1.set(101);
|
|
519
|
+
await flushMicrotasks();
|
|
520
|
+
expect($x5.get()).toBe(105);
|
|
521
|
+
expect($y0.get()).toBe(105);
|
|
522
|
+
expect($y1.get()).toBe(15);
|
|
523
|
+
expect($y2.get()).toBe(25);
|
|
524
|
+
|
|
525
|
+
scope1($x1).set(201);
|
|
526
|
+
scope2($x1).set(201);
|
|
527
|
+
await flushMicrotasks();
|
|
528
|
+
expect($x5.get()).toBe(205);
|
|
529
|
+
expect($y0.get()).toBe(205);
|
|
530
|
+
expect($y1.get()).toBe(205);
|
|
531
|
+
expect($y2.get()).toBe(25);
|
|
532
|
+
});
|
|
533
|
+
|
|
448
534
|
it('should not provide stale values to conditional dependents', async () => {
|
|
449
535
|
const dataAtom = $([100]);
|
|
450
536
|
const hasFilterAtom = $(false);
|
|
@@ -550,26 +636,30 @@ describe('Atom Library - Advanced Tests', () => {
|
|
|
550
636
|
await new Promise<void>((r) => (resolve = r));
|
|
551
637
|
return count;
|
|
552
638
|
});
|
|
553
|
-
const async2Atom = $((get) => get(asyncAtom));
|
|
554
|
-
const
|
|
639
|
+
const async2Atom = $((get) => get(asyncAtom) % 3);
|
|
640
|
+
const mockFn = vi.fn((get: (atom: DerivedAtom<number>) => any) => get(async2Atom));
|
|
641
|
+
const async3Atom = $(mockFn);
|
|
555
642
|
|
|
556
643
|
async3Atom.subscribe(nop);
|
|
557
644
|
await flushMicrotasks();
|
|
558
645
|
resolve();
|
|
559
646
|
await flushMicrotasks();
|
|
560
|
-
|
|
647
|
+
expect(async3Atom.state.value).toBe(1);
|
|
648
|
+
expect(mockFn).toHaveBeenCalledTimes(2);
|
|
561
649
|
|
|
562
650
|
countAtom.set((c) => c + 1);
|
|
563
651
|
await flushMicrotasks();
|
|
564
652
|
resolve();
|
|
565
653
|
await flushMicrotasks();
|
|
566
|
-
|
|
654
|
+
expect(async3Atom.state.value).toBe(2);
|
|
655
|
+
expect(mockFn).toHaveBeenCalledTimes(3);
|
|
567
656
|
|
|
568
|
-
countAtom.set((c) => c +
|
|
657
|
+
countAtom.set((c) => c + 3);
|
|
569
658
|
await flushMicrotasks();
|
|
570
659
|
resolve();
|
|
571
660
|
await flushMicrotasks();
|
|
572
|
-
|
|
661
|
+
expect(async3Atom.state.value).toBe(2);
|
|
662
|
+
expect(mockFn).toHaveBeenCalledTimes(3);
|
|
573
663
|
});
|
|
574
664
|
});
|
|
575
665
|
|