bansa 0.0.12 → 0.0.13
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 +1 -1
- package/dist/index.js +246 -219
- 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;V;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,p:{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.p)}catch(n){f(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;A=!1;constructor(t,a){super(),this.d=t,this.m=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.m)||(this.r=a,m(this))}};i.prototype.y=!0;i.prototype.s=!0;i.prototype.u=!1;var A=class extends d{s=!1;u=!1;n=!1;A=!1;f=0;t;i;c;constructor(t,a){super(),this.d=t,this.m=a?.equals,this.b=!!a?.persist;let n=this;this.p={get signal(){return(n.t||=q()).signal}},this.state={promise:g,error:void 0,value:void 0}}};A.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 p=(e,t)=>e instanceof Function?new A(e,t):new i(e,t),z=e=>p((t,a)=>{let n,r,o=e(y=>{let l=t(y,!1);if(l.error)r=l.error;else if(l.promise)(n||=[]).push(l.promise);else return l.value;return c},a);if(r)throw r;if(n)throw Promise.all(n);return o},{equals:T}),C=(e,t)=>{let a=new WeakMap;if(t)for(let[r,o]of t)a.set(r,o instanceof d?o:p(o));let n=(r=>{let o=a.get(r);return o||a.set(r,o=r.d instanceof Function?p((y,l)=>r.d((D,E)=>y(n(D),E),l)):e?.(r)||p(r.d)),o});return n},T=(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,m(e))},m=e=>{e.n||(e.n=!0,h.push(e),b||(b=!0,queueMicrotask(K)))},K=()=>{b=!1;{let t=h;h=[];for(let a of t)a.state.promise=void 0,a.state.error=a.V,a.state.value=a.r,O(a)}let e=v;v=[];for(let t=e.length;t--;){let a=e[t];a.A=!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){f(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.p)}catch(a){f(a)}}if(e.a)for(let t of e.a)t.u=!0}},O=e=>{if(!e.A){if(e.A=!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.p);j(a)?(e.state.promise=a,a.then(n=>{t===e.f&&(V(e),x(n,e.state.value,e.m)?e.state.promise=void 0:(e.r=n,e.V=void 0),m(e))},n=>{t===e.f&&(V(e),n instanceof s?n=n.e:f(n),e.V=n,m(e))})):(V(e),e.state.error=void 0,x(a,e.state.value,e.m)?e.n=!1:e.state.value=e.r=a)}catch(a){V(e),a===P?e.n=!1:(a instanceof s?a=a.e:f(a),e.state.error=a)}},V=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(L,0)))},L=()=>{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.V=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),j=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}},f=e=>{queueMicrotask(()=>{throw e})};export{p as $,z as $$,C as createScope,g as inactive};
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ 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
77
|
export type AtomValuePair<Value> = [Atom<Value>, Value | Atom<Value>];
|
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
|
+
n;
|
|
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
|
+
o: {
|
|
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.o);
|
|
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
|
+
p = false;
|
|
65
|
+
constructor(init, options) {
|
|
66
|
+
super();
|
|
67
|
+
this.l = init;
|
|
68
|
+
this.q = 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.q)) {
|
|
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
|
+
p = false;
|
|
92
|
+
m = 0;
|
|
93
|
+
a;
|
|
94
|
+
j;
|
|
95
|
+
k;
|
|
96
|
+
constructor(init, options) {
|
|
97
|
+
super();
|
|
98
|
+
this.l = init;
|
|
99
|
+
this.q = options?.equals;
|
|
100
|
+
this.s = !!options?.persist;
|
|
101
|
+
const self = this;
|
|
102
|
+
this.o = {
|
|
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,18 @@ 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
|
-
options
|
|
152
|
-
|
|
155
|
+
if (!scopedAtom) scopeMap.set(
|
|
156
|
+
baseAtom,
|
|
157
|
+
scopedAtom = baseAtom.l instanceof Function ? $((get, options) => baseAtom.l(
|
|
158
|
+
(atom, unwrap) => get(scope(atom), unwrap),
|
|
159
|
+
options
|
|
160
|
+
)) : parentScope?.(baseAtom) || $(baseAtom.l)
|
|
161
|
+
);
|
|
153
162
|
return scopedAtom;
|
|
154
163
|
});
|
|
155
164
|
return scope;
|
|
@@ -173,17 +182,18 @@ const shallowEquals = (a, b) => {
|
|
|
173
182
|
return true;
|
|
174
183
|
};
|
|
175
184
|
let pendingUpdateAtoms = false;
|
|
185
|
+
let updateQueue = [];
|
|
176
186
|
let stack = [];
|
|
177
187
|
const requestActivate = (atom) => {
|
|
178
|
-
if (!atom.
|
|
179
|
-
atom.
|
|
188
|
+
if (!atom.i) {
|
|
189
|
+
atom.i = true;
|
|
180
190
|
requestPropagate(atom);
|
|
181
191
|
}
|
|
182
192
|
};
|
|
183
193
|
const requestPropagate = (atom) => {
|
|
184
|
-
if (!atom.
|
|
185
|
-
atom.
|
|
186
|
-
|
|
194
|
+
if (!atom.c) {
|
|
195
|
+
atom.c = true;
|
|
196
|
+
updateQueue.push(atom);
|
|
187
197
|
if (!pendingUpdateAtoms) {
|
|
188
198
|
pendingUpdateAtoms = true;
|
|
189
199
|
queueMicrotask(updateAtoms);
|
|
@@ -193,12 +203,12 @@ const requestPropagate = (atom) => {
|
|
|
193
203
|
const updateAtoms = () => {
|
|
194
204
|
pendingUpdateAtoms = false;
|
|
195
205
|
{
|
|
196
|
-
const updatedAtoms =
|
|
197
|
-
|
|
206
|
+
const updatedAtoms = updateQueue;
|
|
207
|
+
updateQueue = [];
|
|
198
208
|
for (const atom of updatedAtoms) {
|
|
199
209
|
atom.state.promise = void 0;
|
|
200
|
-
atom.state.error = atom.
|
|
201
|
-
atom.state.value = atom.
|
|
210
|
+
atom.state.error = atom.n;
|
|
211
|
+
atom.state.value = atom.d;
|
|
202
212
|
mark(atom);
|
|
203
213
|
}
|
|
204
214
|
}
|
|
@@ -206,47 +216,53 @@ const updateAtoms = () => {
|
|
|
206
216
|
stack = [];
|
|
207
217
|
for (let i = markedAtoms.length; i--; ) {
|
|
208
218
|
const atom = markedAtoms[i];
|
|
209
|
-
atom.
|
|
210
|
-
if (atom.
|
|
211
|
-
atom.
|
|
219
|
+
atom.p = false;
|
|
220
|
+
if (atom.i) {
|
|
221
|
+
atom.c = true;
|
|
212
222
|
execute(atom);
|
|
213
223
|
}
|
|
214
|
-
if (atom.
|
|
224
|
+
if (atom.c) {
|
|
215
225
|
propagate(atom);
|
|
216
226
|
}
|
|
217
227
|
}
|
|
218
228
|
};
|
|
219
229
|
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
|
-
}
|
|
230
|
+
atom.c = false;
|
|
231
|
+
if (atom.f) {
|
|
232
|
+
for (const watcher of atom.f) {
|
|
237
233
|
try {
|
|
238
|
-
|
|
234
|
+
watcher();
|
|
239
235
|
} catch (e) {
|
|
240
236
|
logError(e);
|
|
241
237
|
}
|
|
242
238
|
}
|
|
243
239
|
}
|
|
240
|
+
if (!atom.state.error && !atom.state.promise) {
|
|
241
|
+
if (atom.g) {
|
|
242
|
+
for (const subscriber of atom.g) {
|
|
243
|
+
if (subscriber.a) {
|
|
244
|
+
subscriber.a.abort();
|
|
245
|
+
subscriber.a = void 0;
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
subscriber.t(atom.state.value, subscriber.o);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
logError(e);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (atom.b) {
|
|
255
|
+
for (const child of atom.b) {
|
|
256
|
+
child.i = true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
244
260
|
};
|
|
245
261
|
const mark = (atom) => {
|
|
246
|
-
if (!atom.
|
|
247
|
-
atom.
|
|
248
|
-
if (atom.
|
|
249
|
-
for (const child of atom.
|
|
262
|
+
if (!atom.p) {
|
|
263
|
+
atom.p = true;
|
|
264
|
+
if (atom.b) {
|
|
265
|
+
for (const child of atom.b) {
|
|
250
266
|
mark(child);
|
|
251
267
|
}
|
|
252
268
|
}
|
|
@@ -259,133 +275,144 @@ class Wrapped {
|
|
|
259
275
|
this.e = e;
|
|
260
276
|
}
|
|
261
277
|
}
|
|
278
|
+
const expired = Symbol();
|
|
262
279
|
const execute = (atom) => {
|
|
263
|
-
const counter = ++atom.
|
|
264
|
-
atom.
|
|
265
|
-
atom.
|
|
280
|
+
const counter = ++atom.m;
|
|
281
|
+
atom.h = true;
|
|
282
|
+
atom.i = false;
|
|
266
283
|
atom.state.promise = void 0;
|
|
267
284
|
if (atom.a) {
|
|
268
285
|
atom.a.abort();
|
|
269
286
|
atom.a = void 0;
|
|
270
287
|
}
|
|
271
|
-
const oldDependencies = atom.l;
|
|
272
|
-
if (oldDependencies) {
|
|
273
|
-
atom.l = /* @__PURE__ */ new Set();
|
|
274
|
-
}
|
|
275
288
|
try {
|
|
276
|
-
const value = atom.
|
|
289
|
+
const value = atom.l(
|
|
277
290
|
(anotherAtom, unwrap = true) => {
|
|
278
|
-
if (counter !== atom.
|
|
291
|
+
if (counter !== atom.m) throw expired;
|
|
279
292
|
if (atom !== anotherAtom) {
|
|
280
|
-
if (!anotherAtom.
|
|
293
|
+
if (!anotherAtom.h) {
|
|
281
294
|
execute(anotherAtom);
|
|
282
|
-
if (anotherAtom.
|
|
283
|
-
anotherAtom.b = false;
|
|
295
|
+
if (anotherAtom.c) {
|
|
284
296
|
propagate(anotherAtom);
|
|
285
297
|
}
|
|
286
298
|
}
|
|
287
|
-
|
|
288
|
-
(
|
|
289
|
-
(anotherAtom.d ??= /* @__PURE__ */ new Set()).add(atom);
|
|
299
|
+
(atom.k ||= /* @__PURE__ */ new Set()).add(anotherAtom);
|
|
300
|
+
(anotherAtom.b ||= /* @__PURE__ */ new Set()).add(atom);
|
|
290
301
|
}
|
|
291
302
|
if (!unwrap) return anotherAtom.state;
|
|
292
|
-
if (anotherAtom.state.promise)
|
|
293
|
-
throw new Wrapped(anotherAtom.state.promise);
|
|
294
303
|
if (anotherAtom.state.error)
|
|
295
304
|
throw new Wrapped(anotherAtom.state.error);
|
|
305
|
+
if (anotherAtom.state.promise)
|
|
306
|
+
throw new Wrapped(anotherAtom.state.promise);
|
|
296
307
|
return anotherAtom.state.value;
|
|
297
308
|
},
|
|
298
|
-
atom.
|
|
309
|
+
atom.o
|
|
299
310
|
);
|
|
300
311
|
if (isPromiseLike(value)) {
|
|
301
312
|
atom.state.promise = value;
|
|
302
313
|
value.then(
|
|
303
314
|
(value2) => {
|
|
304
|
-
if (counter === atom.
|
|
305
|
-
|
|
315
|
+
if (counter === atom.m) {
|
|
316
|
+
finalizeExecution(atom);
|
|
317
|
+
if (equals(value2, atom.state.value, atom.q)) {
|
|
306
318
|
atom.state.promise = void 0;
|
|
307
319
|
} else {
|
|
308
|
-
atom.
|
|
309
|
-
atom.
|
|
310
|
-
requestPropagate(atom);
|
|
320
|
+
atom.d = value2;
|
|
321
|
+
atom.n = void 0;
|
|
311
322
|
}
|
|
323
|
+
requestPropagate(atom);
|
|
312
324
|
}
|
|
313
325
|
},
|
|
314
326
|
(e) => {
|
|
315
|
-
if (counter === atom.
|
|
316
|
-
|
|
317
|
-
|
|
327
|
+
if (counter === atom.m) {
|
|
328
|
+
finalizeExecution(atom);
|
|
329
|
+
if (e instanceof Wrapped) {
|
|
330
|
+
e = e.e;
|
|
318
331
|
} else {
|
|
319
|
-
|
|
320
|
-
e = e.e;
|
|
321
|
-
} else {
|
|
322
|
-
logError(e);
|
|
323
|
-
}
|
|
324
|
-
atom.o = e;
|
|
325
|
-
requestPropagate(atom);
|
|
332
|
+
logError(e);
|
|
326
333
|
}
|
|
334
|
+
atom.n = e;
|
|
335
|
+
requestPropagate(atom);
|
|
327
336
|
}
|
|
328
337
|
}
|
|
329
338
|
);
|
|
330
339
|
} else {
|
|
331
|
-
|
|
340
|
+
finalizeExecution(atom);
|
|
332
341
|
atom.state.error = void 0;
|
|
333
|
-
if (equals(value, atom.state.value, atom.
|
|
334
|
-
atom.
|
|
342
|
+
if (equals(value, atom.state.value, atom.q)) {
|
|
343
|
+
atom.c = false;
|
|
335
344
|
} else {
|
|
336
|
-
atom.state.value = atom.
|
|
345
|
+
atom.state.value = atom.d = value;
|
|
337
346
|
}
|
|
338
347
|
}
|
|
339
348
|
} catch (e) {
|
|
340
|
-
|
|
341
|
-
if (
|
|
342
|
-
atom.
|
|
349
|
+
finalizeExecution(atom);
|
|
350
|
+
if (e === expired) {
|
|
351
|
+
atom.c = false;
|
|
343
352
|
} else {
|
|
344
353
|
if (e instanceof Wrapped) {
|
|
345
354
|
e = e.e;
|
|
346
355
|
} else {
|
|
347
356
|
logError(e);
|
|
348
357
|
}
|
|
349
|
-
|
|
350
|
-
atom.state.promise = e;
|
|
351
|
-
} else {
|
|
352
|
-
atom.state.error = e;
|
|
353
|
-
}
|
|
358
|
+
atom.state.error = e;
|
|
354
359
|
}
|
|
355
360
|
}
|
|
361
|
+
};
|
|
362
|
+
const finalizeExecution = (atom) => {
|
|
363
|
+
++atom.m;
|
|
364
|
+
const oldDependencies = atom.j;
|
|
365
|
+
atom.j = atom.k;
|
|
356
366
|
if (oldDependencies) {
|
|
357
367
|
for (const dep of oldDependencies) {
|
|
358
|
-
|
|
359
|
-
|
|
368
|
+
if (!atom.j?.has(dep)) {
|
|
369
|
+
dep.b.delete(atom);
|
|
370
|
+
disableAtom(dep);
|
|
371
|
+
}
|
|
360
372
|
}
|
|
373
|
+
oldDependencies.clear();
|
|
361
374
|
}
|
|
375
|
+
atom.k = oldDependencies;
|
|
362
376
|
};
|
|
363
|
-
let
|
|
377
|
+
let runningGc = false;
|
|
378
|
+
let gcCandidates = /* @__PURE__ */ new Set();
|
|
364
379
|
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;
|
|
380
|
+
if (!atom.r && !atom.s && !atom.b?.size && !atom.f?.size && !atom.g?.size) {
|
|
381
|
+
gcCandidates.add(atom);
|
|
382
|
+
if (!runningGc) {
|
|
383
|
+
runningGc = true;
|
|
384
|
+
setTimeout(gc, 0);
|
|
380
385
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
const gc = () => {
|
|
389
|
+
for (const atom of gcCandidates) {
|
|
390
|
+
if (!atom.r && !atom.s && !atom.b?.size && !atom.f?.size && !atom.g?.size) {
|
|
391
|
+
atom.state.promise = inactive;
|
|
392
|
+
atom.d = atom.n = atom.state.error = atom.state.value = void 0;
|
|
393
|
+
atom.c = atom.i = atom.h = false;
|
|
394
|
+
if (atom.a) {
|
|
395
|
+
atom.a.abort();
|
|
396
|
+
atom.a = void 0;
|
|
397
|
+
}
|
|
398
|
+
if (atom.j) {
|
|
399
|
+
for (const dep of atom.j) {
|
|
400
|
+
dep.b.delete(atom);
|
|
401
|
+
disableAtom(dep);
|
|
402
|
+
}
|
|
403
|
+
atom.j.clear();
|
|
404
|
+
if (atom.k) {
|
|
405
|
+
for (const dep of atom.k) {
|
|
406
|
+
dep.b.delete(atom);
|
|
407
|
+
disableAtom(dep);
|
|
408
|
+
}
|
|
409
|
+
atom.k.clear();
|
|
410
|
+
}
|
|
385
411
|
}
|
|
386
|
-
atom.l.clear();
|
|
387
412
|
}
|
|
388
413
|
}
|
|
414
|
+
gcCandidates.clear();
|
|
415
|
+
runningGc = false;
|
|
389
416
|
};
|
|
390
417
|
const equals = (value, prevValue, equalsFn) => Object.is(value, prevValue) || equalsFn !== void 0 && prevValue !== void 0 && equalsFn(value, prevValue);
|
|
391
418
|
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
|
|