ethstats-server/web-app/.meteor/local/build/programs/server/packages/ddp.js.map

1 line
192 KiB
Plaintext

{"version":3,"sources":["ddp/common.js","ddp/stream_client_nodejs.js","ddp/stream_client_common.js","ddp/stream_server.js","ddp/heartbeat.js","ddp/livedata_server.js","ddp/writefence.js","ddp/crossbar.js","ddp/livedata_common.js","ddp/random_stream.js","ddp/livedata_connection.js","ddp/server_convenience.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,G;AACA,iB;AACA,kD;AACA,G;AACA,S;AACA,kB;;;;;;;;;;;;;;;;;;;ACLA,6C;AACA,6C;AACA,4C;AACA,E;AACA,6E;AACA,gF;AACA,kB;AACA,E;AACA,gF;AACA,6E;AACA,2C;AACA,0D;AACA,kB;AACA,0B;;AAEA,2B;AACA,e;AACA,c;;AAEA,sD;AACA,2B;;AAEA,4C;;AAEA,iC;;AAEA,e;AACA,2B;AACA,E;;AAEA,+C;;AAEA,uE;AACA,oE;AACA,wB;AACA,yB;AACA,oB;AACA,uC;AACA,6B;AACA,K;AACA,I;;AAEA,yC;AACA,8B;AACA,oB;AACA,wB;AACA,I;;AAEA,iC;AACA,oB;;AAEA,iC;AACA,wE;AACA,gF;AACA,0E;AACA,sB;AACA,wE;AACA,K;;AAEA,mC;AACA,8E;AACA,qE;AACA,0B;AACA,yB;AACA,a;AACA,K;;AAEA,uC;AACA,yE;AACA,uE;AACA,gF;AACA,gF;AACA,sC;AACA,mD;AACA,K;;AAEA,iC;;AAEA,oB;AACA,4C;AACA,wC;AACA,sC;AACA,yB;;AAEA,sE;AACA,kD;AACA,2E;AACA,I;;AAEA,mC;AACA,oB;;AAEA,iC;AACA,sB;AACA,+B;AACA,yB;AACA,qB;;AAEA,kE;AACA,6B;AACA,S;AACA,K;AACA,I;;AAEA,sC;AACA,oB;;AAEA,+B;AACA,yC;AACA,kC;AACA,K;AACA,I;;AAEA,sC;AACA,oB;AACA,gD;AACA,yE;AACA,8E;AACA,mC;AACA,0E;AACA,K;AACA,iB;AACA,I;;AAEA,kC;AACA,oB;AACA,iE;;AAEA,2E;AACA,kE;AACA,kB;AACA,sD;;AAEA,kD;AACA,gD;AACA,gD;AACA,mB;AACA,+C;AACA,M;;AAEA,gF;AACA,uE;AACA,6E;AACA,6E;AACA,yE;AACA,0B;;AAEA,wD;AACA,4C;;AAEA,iC;AACA,6C;AACA,mB;AACA,6B;AACA,+D;AACA,Q;AACA,4B;;AAEA,+D;AACA,qC;AACA,mC;;AAEA,8D;AACA,gE;AACA,sE;AACA,mC;AACA,iB;AACA,iC;AACA,uB;AACA,M;;AAEA,0E;AACA,yC;AACA,qD;;AAEA,yE;AACA,oD;AACA,mE;AACA,O;;;AAGA,qE;AACA,6B;AACA,O;;;AAGA,gF;AACA,6D;AACA,2C;AACA,e;;AAEA,+D;AACA,+B;AACA,S;AACA,O;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACpMA,0E;AACA,wC;AACA,uC;AACA,+C;AACA,E;AACA,oC;AACA,qC;AACA,qD;AACA,E;;AAEA,6C;AACA,6D;AACA,sD;AACA,mF;AACA,yC;AACA,sD;AACA,iD;AACA,2D;AACA,wB;AACA,2B;AACA,G;;AAEA,uD;AACA,iD;AACA,gB;AACA,oB;AACA,4C;AACA,wD;AACA,6E;AACA,4C;AACA,c;AACA,0E;AACA,mE;;AAEA,0E;AACA,4E;AACA,wD;AACA,4C;AACA,8C;AACA,O;;AAEA,2C;AACA,4B;AACA,uE;AACA,0D;AACA,2C;AACA,G;;AAEA,uC;AACA,2D;AACA,sC;AACA,G;;AAEA,8D;AACA,sE;AACA,kE;AACA,sE;AACA,+B;AACA,I;AACA,kE;AACA,sE;AACA,iE;AACA,uE;AACA,2C;;AAEA,yB;AACA,yB;AACA,M;AACA,+B;AACA,E;;AAEA,8B;AACA,6C;AACA,E;;AAEA,iC;AACA,iD;AACA,a;AACA,E;;AAEA,uC;;;AAGA,+C;;AAEA,4B;AACA,iC;AACA,oB;;AAEA,wE;AACA,qD;;AAEA,mC;AACA,qC;AACA,6C;AACA,I;;;AAGA,mC;AACA,oB;AACA,4B;;AAEA,kB;;AAEA,+D;AACA,c;AACA,6D;;AAEA,mD;;AAEA,qC;;AAEA,wB;AACA,0B;AACA,2B;AACA,uB;AACA,mB;AACA,M;;;AAGA,oF;AACA,sC;AACA,+B;AACA,uC;AACA,M;;AAEA,oB;AACA,4B;AACA,gC;;AAEA,I;;AAEA,yB;AACA,iC;AACA,oB;AACA,4B;;AAEA,sB;AACA,mC;AACA,K;;AAEA,iC;AACA,2D;AACA,K;;AAEA,uC;AACA,0C;AACA,2B;AACA,2D;AACA,sB;AACA,a;AACA,K;;AAEA,wC;AACA,qD;AACA,oC;AACA,6B;AACA,K;;AAEA,wB;AACA,qE;AACA,qB;AACA,I;;AAEA,kC;AACA,oB;AACA,4B;;AAEA,qE;AACA,uD;AACA,iC;AACA,a;;AAEA,2E;AACA,mE;AACA,mE;AACA,6B;AACA,6B;AACA,sC;AACA,K;;AAEA,oB;AACA,wB;;AAEA,0B;AACA,0D;AACA,uB;AACA,mB;AACA,M;;AAEA,6C;AACA,iD;;AAEA,yB;AACA,I;;AAEA,gE;AACA,0C;AACA,oB;;AAEA,8B;AACA,wE;AACA,I;;AAEA,kE;AACA,iB;AACA,wB;AACA,0E;AACA,+C;AACA,uB;AACA,I;;AAEA,sC;AACA,oB;;AAEA,oB;AACA,6B;AACA,8E;AACA,uC;AACA,sC;AACA,oC;AACA,Q;AACA,4C;AACA,sE;AACA,Y;AACA,2C;AACA,0C;AACA,K;;AAEA,yC;AACA,yB;AACA,I;;AAEA,0B;AACA,oB;;AAEA,iC;AACA,a;;AAEA,uC;AACA,6C;AACA,yC;AACA,wC;AACA,yB;;AAEA,6B;AACA,I;;;AAGA,kC;AACA,uB;AACA,oB;AACA,6B;AACA,oC;AACA,8B;AACA,G;AACA,G;;AAEA,2C;AACA,6C;AACA,oB;AACA,2B;AACA,G;;AAEA,gD;AACA,8C;;;;;;;;;;;;;;;;;;;ACzQA,6B;;AAEA,uE;;AAEA,4B;AACA,kB;AACA,mC;AACA,yB;;AAEA,+E;AACA,8D;AACA,uC;AACA,6E;AACA,iD;AACA,4B;AACA,0E;AACA,G;;AAEA,kB;AACA,qC;AACA,uB;AACA,wB;AACA,uB;AACA,uE;AACA,+C;AACA,2B;AACA,+E;AACA,0E;AACA,6E;AACA,4E;AACA,+E;AACA,8D;AACA,gC;AACA,wE;AACA,oE;AACA,wB;AACA,4C;AACA,I;;AAEA,8E;AACA,uE;AACA,0D;AACA,yD;AACA,qC;AACA,oC;;AAEA,mD;AACA,wB;AACA,6E;AACA,G;AACA,+E;AACA,4E;AACA,8E;AACA,kC;AACA,sH;AACA,gE;AACA,mH;;AAEA,oC;AACA,oC;;AAEA,kD;AACA,mC;AACA,yB;AACA,M;AACA,oC;AACA,+D;AACA,O;AACA,mC;;AAEA,uE;AACA,mE;AACA,uE;AACA,uE;AACA,+B;AACA,kD;;AAEA,wE;AACA,kE;AACA,6D;AACA,uB;AACA,O;AACA,K;;AAEA,E;;AAEA,kC;AACA,iD;AACA,8C;AACA,iC;AACA,oB;AACA,+C;AACA,kD;AACA,uB;AACA,O;AACA,I;;AAEA,8B;AACA,4B;AACA,oB;AACA,uC;AACA,I;;AAEA,oE;AACA,sD;AACA,0C;AACA,oB;AACA,iE;AACA,6D;AACA,oE;AACA,oD;AACA,2G;AACA,oD;AACA,wD;AACA,wE;AACA,2C;;AAEA,gE;AACA,iE;AACA,gE;AACA,2D;AACA,6B;;AAEA,6E;AACA,mC;AACA,+C;AACA,+D;AACA,gE;AACA,0D;AACA,8C;AACA,S;AACA,8D;AACA,8C;AACA,W;AACA,Q;AACA,iD;AACA,O;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;AC1IA,qB;AACA,gE;AACA,uE;AACA,iC;AACA,iE;AACA,yD;;AAEA,gC;AACA,kB;;AAEA,qD;AACA,mD;AACA,oC;AACA,sC;;AAEA,uC;AACA,sC;AACA,E;;AAEA,+B;AACA,qB;AACA,oB;AACA,wC;AACA,uC;AACA,I;;AAEA,sB;AACA,oB;AACA,gB;AACA,wC;AACA,I;;AAEA,6C;AACA,oB;AACA,sD;AACA,iD;AACA,4B;AACA,M;AACA,I;;AAEA,4C;AACA,oB;AACA,qD;AACA,gD;AACA,2B;AACA,M;AACA,I;;AAEA,6C;AACA,oB;AACA,wC;AACA,yD;AACA,2C;AACA,K;AACA,I;;AAEA,4C;AACA,oB;AACA,uC;AACA,wD;AACA,0C;AACA,K;AACA,I;;AAEA,sE;AACA,wC;AACA,oB;AACA,yC;AACA,qB;AACA,uB;AACA,uC;AACA,I;;AAEA,qE;AACA,oC;AACA,uC;AACA,oB;AACA,wC;AACA,sB;AACA,I;;AAEA,6B;AACA,oB;AACA,kE;AACA,sE;AACA,wC;AACA,0C;AACA,0C;AACA,K;AACA,I;;AAEA,6B;AACA,oB;;AAEA,oE;AACA,0C;AACA,uC;AACA,yC;AACA,0C;AACA,K;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACrGA,e;;AAEA,kC;;AAEA,8B;AACA,6D;AACA,6D;AACA,2E;AACA,E;AACA,oE;AACA,qE;AACA,a;;AAEA,0D;AACA,uC;AACA,kB;AACA,kD;AACA,4E;AACA,E;;AAEA,yC;;AAEA,0B;AACA,oB;AACA,iB;AACA,2D;AACA,yC;AACA,O;AACA,e;AACA,I;;AAEA,mE;AACA,oB;AACA,mD;AACA,sB;AACA,a;AACA,6C;;AAEA,oE;AACA,gB;AACA,wB;AACA,a;;AAEA,iC;AACA,qD;AACA,yC;AACA,iE;AACA,gF;AACA,mC;AACA,oB;AACA,0C;AACA,oC;AACA,c;AACA,O;AACA,K;AACA,oC;AACA,iC;AACA,uC;AACA,4C;AACA,sE;AACA,qD;AACA,K;AACA,I;;AAEA,wD;AACA,kD;AACA,oB;AACA,mD;AACA,sB;AACA,a;;AAEA,6D;AACA,+B;;AAEA,sC;AACA,qE;AACA,6C;AACA,mC;AACA,a;AACA,K;AACA,6C;AACA,Y;AACA,iB;AACA,0D;AACA,oE;AACA,S;AACA,K;;AAEA,c;AACA,yE;AACA,iE;AACA,qC;AACA,O;AACA,wB;AACA,Y;AACA,2D;AACA,kF;AACA,K;;AAEA,G;AACA,G;;AAEA,G;AACA,oD;AACA,sE;AACA,gG;AACA,+B;AACA,G;AACA,yE;AACA,kB;AACA,uC;AACA,sB;AACA,oC;AACA,E;;AAEA,2D;;;AAGA,2C;;AAEA,wB;AACA,oB;AACA,qC;AACA,I;;AAEA,6B;AACA,oB;AACA,sE;AACA,4C;;AAEA,uC;AACA,yE;AACA,Q;;AAEA,uC;AACA,wD;AACA,O;AACA,O;AACA,I;;AAEA,8C;AACA,oB;AACA,oB;AACA,yE;AACA,uC;AACA,qC;AACA,4B;AACA,Q;AACA,sC;AACA,0B;AACA,Q;AACA,qC;AACA,gC;AACA,O;AACA,O;AACA,4D;AACA,I;;AAEA,oD;AACA,oB;AACA,qC;AACA,sB;AACA,mB;AACA,mB;AACA,0C;AACA,mC;AACA,K;AACA,gD;AACA,6B;AACA,0C;AACA,0B;AACA,+D;AACA,O;AACA,c;AACA,qE;AACA,Q;AACA,uE;AACA,I;;AAEA,uD;AACA,oB;AACA,2B;AACA,qC;AACA,iB;AACA,6E;AACA,2C;AACA,8B;AACA,mE;AACA,U;AACA,2E;AACA,O;AACA,mE;AACA,I;;AAEA,8C;AACA,oB;AACA,qC;AACA,mB;AACA,gE;AACA,gB;AACA,K;AACA,gD;AACA,sC;AACA,iC;AACA,sD;AACA,gC;AACA,Y;AACA,uB;AACA,4D;AACA,+B;AACA,gE;AACA,6D;AACA,S;;AAEA,+D;AACA,K;AACA,G;AACA,G;;AAEA,gF;AACA,gF;AACA,gF;;AAEA,2D;AACA,kB;AACA,wB;;AAEA,uB;AACA,yB;;AAEA,2B;AACA,uB;;AAEA,qE;AACA,0D;AACA,gD;;AAEA,uB;AACA,6B;;AAEA,yC;AACA,uB;AACA,2B;;AAEA,qB;;AAEA,4B;;AAEA,oE;AACA,gF;AACA,uC;AACA,yB;;AAEA,6E;AACA,yE;AACA,0C;;AAEA,4D;AACA,sE;AACA,0B;;AAEA,8D;AACA,4B;;;AAGA,gE;AACA,mD;AACA,+B;;AAEA,gD;AACA,gD;;AAEA,sE;AACA,sE;AACA,6C;AACA,2B;AACA,gB;AACA,wB;AACA,mB;AACA,M;AACA,4B;AACA,yE;AACA,yB;AACA,sC;AACA,c;AACA,sD;AACA,yB;AACA,O;AACA,M;AACA,yC;AACA,oC;AACA,I;;AAEA,6C;AACA,gD;AACA,8D;AACA,qB;AACA,8B;AACA,W;;AAEA,8D;AACA,oC;AACA,mD;AACA,iD;AACA,8B;AACA,qB;AACA,Q;AACA,6B;AACA,iC;AACA,O;AACA,O;AACA,2B;AACA,G;;AAEA,2D;AACA,+B;AACA,E;;AAEA,6B;;AAEA,yC;AACA,oB;AACA,wB;AACA,uD;AACA,U;AACA,yD;AACA,gD;AACA,S;AACA,K;AACA,I;;AAEA,oD;AACA,oB;AACA,wB;AACA,oF;AACA,I;;AAEA,sD;AACA,oB;AACA,0B;AACA,a;;AAEA,0B;AACA,iB;AACA,uB;AACA,mC;AACA,e;AACA,sB;AACA,S;AACA,K;AACA,I;;AAEA,8C;AACA,oB;AACA,wB;AACA,sE;AACA,I;;AAEA,iC;AACA,oB;AACA,Y;AACA,0C;AACA,8C;AACA,6C;AACA,M;AACA,I;;AAEA,gD;AACA,oB;AACA,sD;AACA,kD;AACA,K;AACA,uD;AACA,iE;AACA,+C;AACA,e;AACA,I;;AAEA,oE;AACA,oB;AACA,sD;AACA,+C;AACA,I;;AAEA,8D;AACA,oB;AACA,sD;AACA,yC;AACA,yB;AACA,kD;AACA,K;AACA,I;;AAEA,sE;AACA,oB;AACA,sD;AACA,iD;AACA,I;;AAEA,mC;AACA,oB;AACA,8E;AACA,6E;AACA,qE;AACA,mE;AACA,yC;AACA,uC;AACA,O;AACA,I;;AAEA,0D;AACA,sB;AACA,oB;;AAEA,+D;AACA,wE;AACA,8B;;AAEA,yB;AACA,uB;AACA,a;;AAEA,2C;AACA,wB;AACA,8B;;AAEA,yB;AACA,4B;AACA,4B;AACA,K;;AAEA,sB;AACA,0B;AACA,wC;AACA,K;;AAEA,6D;AACA,kC;;AAEA,8B;AACA,6D;AACA,qE;AACA,gF;AACA,yC;;AAEA,sE;AACA,qE;AACA,wD;AACA,mB;AACA,S;AACA,O;;AAEA,8B;AACA,qC;AACA,I;;AAEA,wE;AACA,yD;AACA,wB;AACA,oB;AACA,sB;AACA,+B;AACA,qD;AACA,0C;AACA,K;AACA,I;;AAEA,6B;AACA,kD;AACA,oB;AACA,6C;AACA,yB;AACA,8C;AACA,mB;AACA,I;;AAEA,kE;AACA,+D;AACA,qD;AACA,I;AACA,oE;AACA,qE;AACA,oE;AACA,sE;AACA,mE;AACA,qB;AACA,I;AACA,oE;AACA,oE;AACA,oE;AACA,Y;AACA,qC;AACA,oB;AACA,iD;AACA,a;;AAEA,qE;AACA,mE;AACA,oE;AACA,yC;AACA,M;AACA,wE;AACA,wE;AACA,qE;AACA,gD;AACA,2D;AACA,+B;AACA,gD;AACA,yB;AACA,2B;AACA,wC;AACA,iB;AACA,a;AACA,K;AACA,2D;AACA,yB;AACA,2B;AACA,wC;AACA,iB;AACA,a;AACA,K;;AAEA,8B;AACA,2B;AACA,a;AACA,8B;;AAEA,mC;AACA,qD;AACA,iB;AACA,mC;AACA,e;AACA,O;;AAEA,yB;AACA,2B;;AAEA,mC;AACA,uB;AACA,iC;AACA,0B;AACA,wB;AACA,U;;AAEA,mD;AACA,mE;AACA,Y;AACA,6C;AACA,8D;AACA,e;AACA,M;;AAEA,kB;AACA,I;;AAEA,sB;AACA,yB;AACA,sB;;AAEA,kC;AACA,yC;AACA,2C;AACA,kE;AACA,sD;AACA,e;AACA,O;;AAEA,oD;AACA,mB;AACA,mC;AACA,mE;AACA,e;AACA,O;;AAEA,yC;AACA,oE;AACA,gE;AACA,qB;AACA,e;;AAEA,2D;AACA,qE;;AAEA,M;;AAEA,2B;AACA,sB;;AAEA,qC;AACA,M;;AAEA,qC;AACA,sB;;AAEA,kC;AACA,wD;AACA,oC;AACA,yC;AACA,6C;AACA,kE;AACA,4E;AACA,2D;AACA,e;AACA,O;;AAEA,8C;;AAEA,kE;AACA,iE;AACA,c;AACA,4C;AACA,wC;AACA,8D;AACA,gE;AACA,6D;AACA,4D;AACA,gD;AACA,uB;AACA,mB;AACA,8C;AACA,S;;AAEA,yB;AACA,4D;AACA,qB;AACA,mB;AACA,oC;AACA,6D;AACA,oB;AACA,e;AACA,O;;AAEA,wC;AACA,gC;AACA,Q;;AAEA,6C;AACA,4B;AACA,4B;AACA,6B;AACA,yB;AACA,0C;AACA,8B;AACA,S;AACA,W;AACA,gF;AACA,2E;AACA,4C;AACA,+E;AACA,a;AACA,W;AACA,mB;AACA,0B;AACA,O;;AAEA,2D;AACA,iE;;AAEA,wC;AACA,iE;;AAEA,uC;AACA,mB;AACA,gE;AACA,iE;AACA,gE;AACA,K;AACA,I;;AAEA,0B;AACA,oB;AACA,+B;AACA,mC;AACA,I;;AAEA,8C;AACA,oB;AACA,mE;AACA,8D;AACA,mC;AACA,Q;AACA,wD;AACA,6D;AACA,kE;AACA,W;AACA,Q;AACA,sD;AACA,wD;AACA,+C;AACA,W;AACA,O;AACA,O;AACA,I;;AAEA,oE;AACA,sB;AACA,gC;AACA,oB;;AAEA,sD;AACA,0E;AACA,qC;;AAEA,4E;AACA,yE;AACA,M;AACA,6E;AACA,0E;AACA,8E;AACA,+E;AACA,+B;AACA,2C;;AAEA,4E;AACA,sC;AACA,kC;AACA,wB;AACA,O;;AAEA,+E;AACA,+E;AACA,yB;AACA,4B;AACA,yC;AACA,8B;AACA,yB;;AAEA,qE;AACA,uC;AACA,yB;AACA,6B;;AAEA,yD;AACA,wD;AACA,yE;AACA,0D;AACA,oD;AACA,O;;AAEA,4E;AACA,2E;AACA,Y;AACA,4C;AACA,8B;;AAEA,8E;AACA,4E;AACA,0D;AACA,yC;AACA,6B;AACA,2C;AACA,2C;AACA,2C;AACA,gC;AACA,O;AACA,O;AACA,I;;AAEA,+D;AACA,oB;;AAEA,+B;AACA,0C;AACA,c;AACA,mC;AACA,Q;AACA,oC;;AAEA,sB;AACA,I;;AAEA,qC;AACA,8C;AACA,oB;;AAEA,uB;;AAEA,0C;AACA,6C;AACA,mD;AACA,2C;AACA,oC;AACA,K;;AAEA,6C;;AAEA,gB;AACA,6C;AACA,c;AACA,0D;AACA,sC;AACA,K;;AAEA,wB;AACA,I;;AAEA,+E;AACA,kD;AACA,4C;AACA,oB;;AAEA,gD;AACA,wB;AACA,O;AACA,yB;;AAEA,gD;AACA,wB;AACA,O;AACA,6B;AACA,I;;AAEA,2D;AACA,oE;AACA,kC;AACA,+B;AACA,oB;;AAEA,sE;AACA,kE;AACA,kE;AACA,sE;AACA,kC;AACA,M;AACA,oE;AACA,gF;;AAEA,iC;AACA,uC;;AAEA,8D;AACA,mC;AACA,kB;AACA,wD;;AAEA,mE;AACA,oE;AACA,oE;AACA,qE;AACA,kE;AACA,qE;AACA,kE;AACA,qC;;AAEA,2E;AACA,kB;;AAEA,kE;AACA,G;AACA,G;;AAEA,gF;AACA,gF;AACA,gF;;AAEA,4D;;AAEA,0E;AACA,U;AACA,G;AACA,+C;AACA,sB;AACA,qB;AACA,G;AACA,6B;AACA,qD;AACA,kB;AACA,6C;;AAEA,K;AACA,wH;AACA,kB;AACA,sB;AACA,2B;AACA,c;AACA,K;AACA,kE;;AAEA,0B;;AAEA,4E;AACA,wC;AACA,iC;AACA,oB;;AAEA,8B;;AAEA,uE;AACA,yD;AACA,mE;AACA,6B;AACA,0D;AACA,U;AACA,iD;AACA,G;;AAEA,iC;AACA,4B;;AAEA,+D;AACA,2B;;AAEA,mE;AACA,qB;AACA,uB;;AAEA,8B;AACA,sB;;AAEA,kD;;AAEA,K;AACA,kH;AACA,kB;AACA,2B;AACA,kB;AACA,c;AACA,K;AACA,+B;;AAEA,kD;AACA,mD;AACA,sD;;AAEA,kD;AACA,sD;AACA,wD;AACA,wC;;AAEA,oB;AACA,8C;AACA,qC;AACA,I;;AAEA,2D;AACA,oC;AACA,E;;AAEA,kC;AACA,4B;AACA,sE;AACA,kD;AACA,M;AACA,uE;AACA,wE;AACA,iE;;AAEA,oB;AACA,S;AACA,yC;AACA,uD;AACA,0E;AACA,kE;AACA,yC;AACA,0C;AACA,iB;AACA,oB;AACA,a;AACA,K;;AAEA,oD;AACA,8B;AACA,a;;AAEA,uE;AACA,0E;AACA,0E;AACA,gF;AACA,+D;AACA,M;AACA,0E;AACA,8E;AACA,8E;AACA,6C;AACA,iE;AACA,6C;AACA,4C;AACA,wC;AACA,c;AACA,W;AACA,iC;AACA,mC;AACA,M;AACA,wB;AACA,W;AACA,iC;AACA,mB;AACA,sB;AACA,e;AACA,O;AACA,gF;AACA,oC;AACA,mB;AACA,gC;AACA,2C;AACA,mC;AACA,mF;AACA,e;AACA,O;AACA,wC;AACA,8E;AACA,yD;AACA,+B;AACA,4C;AACA,yD;AACA,qD;AACA,+B;AACA,0E;AACA,+B;AACA,iB;AACA,S;AACA,+C;AACA,Q;;AAEA,W;AACA,oC;AACA,mC;AACA,W;AACA,mB;AACA,sB;AACA,e;AACA,O;AACA,mB;AACA,qB;AACA,kE;AACA,oE;AACA,2B;AACA,0E;AACA,qD;AACA,K;AACA,I;;AAEA,6E;AACA,4E;AACA,yE;AACA,2E;AACA,+B;AACA,2B;AACA,oB;AACA,0B;AACA,a;AACA,6B;AACA,8B;AACA,6D;AACA,uC;AACA,I;;AAEA,mC;AACA,oB;AACA,2C;AACA,wC;AACA,6B;AACA,2C;AACA,iB;AACA,O;AACA,I;;AAEA,6C;AACA,oC;AACA,oB;AACA,yC;AACA,wE;AACA,+E;AACA,uB;AACA,yD;AACA,sE;AACA,W;AACA,S;AACA,O;AACA,I;;AAEA,kE;AACA,qE;AACA,sE;AACA,gE;AACA,mC;AACA,0B;AACA,oB;AACA,4B;AACA,uE;AACA,kB;AACA,I;;AAEA,K;AACA,kS;AACA,kB;AACA,0D;AACA,c;AACA,2B;AACA,K;AACA,2B;AACA,oB;AACA,8B;AACA,a;AACA,iE;AACA,I;;AAEA,gF;AACA,+E;AACA,6E;AACA,oC;;AAEA,K;AACA,2I;AACA,kB;AACA,c;AACA,2B;AACA,K;AACA,qB;AACA,oB;AACA,8B;AACA,a;AACA,0D;AACA,I;;AAEA,K;AACA,uH;AACA,kB;AACA,2B;AACA,c;AACA,iD;AACA,K;AACA,+B;AACA,oB;AACA,8B;AACA,iB;AACA,Q;AACA,yC;AACA,I;;AAEA,+E;AACA,0E;AACA,kB;AACA,+B;AACA,oB;AACA,+D;AACA,I;;AAEA,K;AACA,yH;AACA,kB;AACA,2B;AACA,c;AACA,0F;AACA,8C;AACA,+F;AACA,K;AACA,gD;AACA,oB;AACA,8B;AACA,a;AACA,wC;AACA,+D;AACA,8E;AACA,I;;AAEA,K;AACA,4H;AACA,kB;AACA,2B;AACA,c;AACA,8F;AACA,kD;AACA,yS;AACA,K;AACA,kD;AACA,oB;AACA,8B;AACA,a;AACA,wC;AACA,gF;AACA,I;;AAEA,K;AACA,6H;AACA,kB;AACA,2B;AACA,c;AACA,mG;AACA,qE;AACA,K;AACA,0C;AACA,oB;AACA,8B;AACA,a;AACA,wC;AACA,sE;AACA,gE;AACA,+C;AACA,wE;AACA,I;;AAEA,K;AACA,yQ;AACA,kB;AACA,2B;AACA,c;AACA,K;AACA,sB;AACA,oB;AACA,8B;AACA,a;AACA,8B;AACA,2D;AACA,uB;AACA,sD;AACA,yB;AACA,K;AACA,G;AACA,G;;AAEA,gF;AACA,gF;AACA,gF;;AAEA,6B;AACA,kB;;AAEA,sE;AACA,oE;AACA,iE;AACA,+C;AACA,I;AACA,uD;AACA,2E;AACA,4C;AACA,6B;AACA,4B;AACA,6D;AACA,wB;AACA,K;;AAEA,mE;AACA,wE;AACA,gE;AACA,uB;AACA,oC;AACA,iD;AACA,K;;AAEA,6B;AACA,uC;;AAEA,4B;;AAEA,+C;;AAEA,wC;;AAEA,iD;AACA,uD;AACA,iC;;AAEA,yD;AACA,+C;AACA,2B;AACA,gD;AACA,qC;AACA,M;;AAEA,0C;AACA,qC;AACA,+C;AACA,O;AACA,W;AACA,a;AACA,sC;AACA,uB;AACA,mC;AACA,iB;AACA,S;AACA,uC;AACA,wC;AACA,iB;AACA,S;;AAEA,oC;AACA,sC;AACA,gD;AACA,mB;AACA,W;AACA,6B;AACA,6C;AACA,mB;AACA,iB;AACA,S;;AAEA,qC;AACA,+C;AACA,iB;AACA,S;AACA,kD;AACA,mB;AACA,iC;AACA,yE;AACA,0C;AACA,O;AACA,O;;AAEA,oC;AACA,kC;AACA,2B;AACA,wC;AACA,iB;AACA,O;AACA,O;AACA,K;AACA,E;;AAEA,4B;;AAEA,K;AACA,+F;AACA,kB;AACA,8F;AACA,qB;AACA,K;AACA,+B;AACA,oB;AACA,8C;AACA,I;;AAEA,0C;AACA,oB;;AAEA,2E;AACA,mE;AACA,8C;AACA,mC;AACA,2C;AACA,kD;AACA,8C;AACA,sE;AACA,qB;AACA,a;AACA,K;;AAEA,gE;AACA,0D;AACA,wE;;AAEA,kC;AACA,+E;AACA,+E;AACA,wB;AACA,mE;AACA,qB;AACA,a;AACA,K;;AAEA,kD;AACA,yD;AACA,6E;AACA,6E;AACA,oE;AACA,oD;AACA,gC;AACA,yD;AACA,kB;AACA,O;AACA,I;AACA,K;AACA,yC;AACA,I;AACA,8C;AACA,8C;AACA,4B;AACA,I;AACA,gE;AACA,0E;AACA,8C;AACA,I;AACA,yD;AACA,mE;AACA,oE;AACA,sB;AACA,I;AACA,wB;AACA,kE;AACA,oE;AACA,mE;AACA,uC;AACA,K;;AAEA,K;AACA,mC;AACA,qB;AACA,kB;AACA,qJ;AACA,qQ;AACA,K;AACA,8C;AACA,oB;;AAEA,4B;;AAEA,gD;AACA,uE;AACA,a;AACA,K;;AAEA,kD;AACA,iE;AACA,gE;AACA,kE;AACA,+D;AACA,kE;AACA,kE;AACA,6C;AACA,2C;AACA,6C;AACA,sB;AACA,yE;AACA,2E;AACA,yE;AACA,2C;AACA,Q;AACA,kE;AACA,Q;AACA,sC;AACA,Q;AACA,gF;AACA,0D;AACA,O;AACA,K;;AAEA,a;AACA,4C;AACA,U;AACA,oD;AACA,wE;AACA,6E;AACA,+D;AACA,gD;AACA,kD;AACA,4B;AACA,gD;AACA,mB;AACA,S;AACA,S;AACA,K;AACA,I;;AAEA,sC;AACA,oB;AACA,oC;AACA,uC;AACA,K;AACA,I;;AAEA,K;AACA,gF;AACA,oB;AACA,6F;AACA,qB;AACA,K;AACA,+B;AACA,oB;AACA,2C;AACA,qC;AACA,4E;AACA,wC;AACA,O;AACA,I;;AAEA,0C;AACA,oE;AACA,4C;AACA,wD;AACA,mE;AACA,gC;AACA,4C;AACA,I;;AAEA,qC;AACA,wC;AACA,mD;AACA,oB;;AAEA,2E;AACA,gC;AACA,qD;AACA,yB;AACA,mB;AACA,K;AACA,4B;;AAEA,iB;AACA,mE;AACA,sE;AACA,+B;AACA,yD;AACA,wC;AACA,iB;AACA,sD;AACA,Q;;AAEA,sB;AACA,6C;AACA,kB;AACA,mB;AACA,4D;AACA,Y;AACA,qE;AACA,gE;AACA,+B;AACA,wB;AACA,kC;AACA,kF;AACA,Q;AACA,4B;AACA,2D;AACA,8B;AACA,0C;AACA,sC;AACA,8C;AACA,U;AACA,kD;AACA,O;;AAEA,6C;AACA,4B;AACA,uB;AACA,6B;AACA,+B;AACA,wD;AACA,S;AACA,W;AACA,+E;AACA,0C;AACA,0E;AACA,0B;AACA,W;AACA,qC;AACA,mB;AACA,sB;AACA,O;AACA,K;;AAEA,+E;AACA,4E;AACA,gF;AACA,6E;AACA,mD;AACA,mB;AACA,kC;AACA,uB;AACA,K;AACA,kB;AACA,sB;AACA,kB;AACA,I;;AAEA,wC;AACA,oB;AACA,2C;AACA,gB;AACA,gC;AACA,Q;AACA,kB;AACA,G;AACA,G;;AAEA,yD;AACA,2D;AACA,2E;AACA,wD;AACA,K;AACA,wB;AACA,gD;AACA,G;AACA,wB;AACA,E;;AAEA,iD;;;AAGA,8E;AACA,uB;AACA,2D;AACA,sD;AACA,qB;;AAEA,4E;AACA,e;AACA,4B;AACA,2D;AACA,mC;AACA,kG;AACA,sB;AACA,K;AACA,G;;AAEA,gF;AACA,yE;AACA,8E;AACA,qB;AACA,iC;AACA,yD;AACA,sC;AACA,+E;AACA,qD;AACA,G;;AAEA,wD;AACA,E;;;AAGA,8E;AACA,oC;AACA,yE;AACA,oB;AACA,yC;AACA,kD;AACA,qC;AACA,G;AACA,gC;AACA,E;;;;;;;;;;;;;;;;;;;ACjlDA,+B;AACA,wD;;AAEA,oE;AACA,iE;AACA,kE;AACA,E;AACA,qC;AACA,kB;;AAEA,qB;AACA,qB;AACA,uB;AACA,8B;AACA,iC;AACA,E;;AAEA,qE;AACA,sE;AACA,gB;AACA,E;AACA,8D;;AAEA,2C;AACA,sE;AACA,mE;AACA,qE;AACA,sE;AACA,6D;AACA,2B;AACA,oB;;AAEA,qB;AACA,2C;;AAEA,mB;AACA,+E;;AAEA,8B;AACA,0B;AACA,Y;AACA,8B;AACA,sB;AACA,sE;AACA,yB;AACA,kC;AACA,0B;AACA,O;AACA,M;AACA,I;;AAEA,kE;AACA,0C;AACA,oB;AACA,oB;AACA,oD;AACA,iD;AACA,sB;AACA,sB;AACA,I;;AAEA,2D;AACA,mC;AACA,oB;AACA,mB;AACA,qE;AACA,wC;AACA,yC;AACA,I;;AAEA,sE;AACA,2B;AACA,oB;AACA,4B;AACA,qC;AACA,yB;AACA,O;AACA,e;AACA,kB;AACA,I;;AAEA,2B;AACA,oB;AACA,mB;AACA,wD;AACA,iD;AACA,wB;AACA,iE;AACA,qC;AACA,K;AACA,I;;AAEA,oE;AACA,uC;AACA,uB;AACA,oB;AACA,qB;AACA,iE;AACA,wB;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACpGA,8E;AACA,yE;AACA,4E;;AAEA,0C;AACA,kB;AACA,0B;;AAEA,kB;AACA,gF;AACA,gC;AACA,kC;AACA,uD;AACA,2C;AACA,E;;AAEA,yC;AACA,iE;AACA,0D;AACA,kE;AACA,6B;AACA,I;AACA,8D;AACA,2C;AACA,I;AACA,iE;AACA,c;AACA,wC;AACA,oB;AACA,2B;;AAEA,kD;AACA,+C;AACA,K;;AAEA,4E;AACA,qE;AACA,0D;AACA,kD;AACA,K;AACA,wD;;AAEA,yC;AACA,8C;AACA,4C;AACA,K;;AAEA,Y;AACA,yB;AACA,6C;AACA,kD;AACA,iD;AACA,S;AACA,0D;AACA,gE;AACA,wD;AACA,S;AACA,O;AACA,M;AACA,I;;AAEA,gE;AACA,sE;AACA,gC;AACA,I;AACA,+D;AACA,sE;AACA,I;AACA,oE;AACA,iC;AACA,oB;;AAEA,uD;AACA,oD;AACA,K;;AAEA,qE;AACA,a;;AAEA,gC;AACA,8D;AACA,yB;AACA,qD;AACA,mD;AACA,6B;AACA,O;AACA,O;;AAEA,+E;AACA,+E;AACA,sE;AACA,2E;AACA,4C;AACA,oE;AACA,+E;AACA,8E;AACA,wC;AACA,uC;AACA,8C;AACA,0D;AACA,O;AACA,O;AACA,I;;AAEA,+E;AACA,I;AACA,c;AACA,qD;AACA,uD;AACA,4B;AACA,8D;AACA,uE;AACA,8D;AACA,uD;AACA,wB;AACA,uE;AACA,2E;AACA,8B;AACA,8E;AACA,yE;AACA,0C;AACA,8C;AACA,8E;AACA,4E;AACA,6E;AACA,sE;AACA,6E;AACA,+C;AACA,0C;AACA,yC;AACA,mB;AACA,K;AACA,+D;AACA,0D;AACA,+C;AACA,mB;AACA,K;;AAEA,wD;AACA,yC;AACA,sD;AACA,O;AACA,G;AACA,G;;AAEA,+E;AACA,2E;AACA,gF;AACA,6E;AACA,4B;AACA,2D;AACA,6C;AACA,G;;;;;;;;;;;;;;;;;;;ACxJA,8D;AACA,2D;AACA,iD;;AAEA,6D;;AAEA,2E;AACA,oB;AACA,G;AACA,6E;AACA,8B;AACA,0B;AACA,qB;AACA,G;AACA,uC;AACA,kB;;AAEA,sE;AACA,qE;AACA,iE;AACA,gE;AACA,wE;AACA,sE;AACA,yC;;AAEA,K;AACA,mG;AACA,oB;AACA,wB;AACA,+B;AACA,c;AACA,oB;AACA,K;AACA,2C;;AAEA,mE;AACA,qE;AACA,c;AACA,oD;AACA,8B;;AAEA,oB;;AAEA,K;AACA,gG;AACA,oB;AACA,kB;AACA,+B;AACA,c;AACA,K;AACA,+B;;AAEA,gE;AACA,yB;AACA,wD;;AAEA,+D;;AAEA,K;AACA,uN;AACA,kB;AACA,sB;AACA,+B;AACA,c;AACA,K;AACA,uC;;AAEA,+C;AACA,uC;;AAEA,uE;AACA,2B;AACA,E;;AAEA,sC;AACA,K;AACA,yH;AACA,kB;AACA,+B;AACA,c;AACA,K;AACA,wB;AACA,oB;AACA,+B;AACA,oB;AACA,I;;AAEA,K;AACA,qC;AACA,kB;AACA,+B;AACA,c;AACA,oG;AACA,K;AACA,+B;AACA,oB;AACA,4B;AACA,gF;AACA,yB;AACA,4B;AACA,G;AACA,G;;AAEA,qC;AACA,O;AACA,wC;AACA,e;AACA,yE;AACA,gB;AACA,G;AACA,kC;AACA,gD;AACA,sE;AACA,gB;AACA,G;;AAEA,6E;;AAEA,qE;AACA,gB;AACA,8B;AACA,8B;AACA,sB;AACA,6C;AACA,uC;AACA,O;AACA,uB;AACA,G;;AAEA,2D;AACA,0B;AACA,+D;AACA,K;;AAEA,a;AACA,E;;AAEA,+B;AACA,8B;AACA,sE;AACA,qB;AACA,6B;AACA,qB;AACA,8C;AACA,gC;AACA,0B;AACA,gC;AACA,O;AACA,O;AACA,4B;AACA,6B;AACA,+B;AACA,yB;AACA,G;AACA,0B;AACA,2D;AACA,2B;AACA,+D;AACA,K;AACA,6C;AACA,kD;AACA,G;AACA,8B;AACA,E;;AAEA,oE;AACA,qE;AACA,gE;AACA,wD;AACA,wD;;;;;;;;;;;;;;;;;;;ACzKA,2E;AACA,E;AACA,4E;AACA,6E;AACA,mE;AACA,E;AACA,4E;AACA,+E;AACA,0E;AACA,+E;AACA,4E;AACA,6E;AACA,E;AACA,+D;AACA,6D;AACA,gE;AACA,8C;AACA,E;AACA,mC;AACA,4D;AACA,2D;AACA,iF;AACA,gF;AACA,mC;AACA,kB;;AAEA,uD;;AAEA,sB;AACA,E;;AAEA,kE;AACA,gE;AACA,wE;AACA,yC;AACA,wB;AACA,8B;AACA,E;;AAEA,6E;AACA,6E;AACA,oE;AACA,mC;AACA,wE;AACA,6E;AACA,0D;AACA,2C;AACA,c;AACA,qB;AACA,G;AACA,e;AACA,oC;AACA,mD;AACA,kB;AACA,G;AACA,wC;AACA,sB;AACA,0D;AACA,4B;AACA,O;AACA,G;AACA,sC;AACA,E;;AAEA,sD;AACA,6E;AACA,+D;AACA,oC;AACA,2C;AACA,uC;AACA,E;;AAEA,qD;AACA,8C;AACA,yD;AACA,uE;AACA,oD;AACA,yE;AACA,gD;AACA,iE;AACA,8B;AACA,E;;AAEA,kC;AACA,kF;AACA,uE;AACA,qE;AACA,8B;AACA,oB;;AAEA,gD;AACA,4B;AACA,gD;AACA,qD;AACA,4C;AACA,8C;AACA,S;AACA,O;AACA,yF;AACA,K;AACA,oB;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACtGA,sB;AACA,iC;AACA,oC;AACA,0D;AACA,C;;AAEA,gD;AACA,2C;AACA,W;AACA,gF;AACA,qE;AACA,gC;AACA,2E;AACA,6E;AACA,E;AACA,qE;AACA,oC;AACA,E;AACA,oE;AACA,iE;AACA,qE;AACA,8D;AACA,8D;AACA,mE;AACA,oE;AACA,+B;AACA,0C;AACA,kB;AACA,sB;AACA,gC;AACA,4D;AACA,iC;AACA,M;AACA,6B;AACA,4B;AACA,0C;AACA,iC;AACA,iD;AACA,gB;AACA,wB;AACA,c;;AAEA,wE;AACA,gF;AACA,gB;AACA,0B;;AAEA,6D;AACA,gC;AACA,uB;AACA,U;AACA,uD;AACA,2B;AACA,+B;AACA,6C;AACA,mE;AACA,oE;AACA,kE;AACA,gE;AACA,gB;AACA,iD;AACA,gD;AACA,O;AACA,G;;AAEA,6B;AACA,oE;AACA,4E;AACA,mD;AACA,4C;AACA,yB;AACA,4D;;AAEA,sD;AACA,oD;;AAEA,2E;AACA,gF;AACA,6E;AACA,0B;AACA,4B;;AAEA,gF;AACA,iB;AACA,I;AACA,0E;AACA,+E;AACA,4E;AACA,+E;AACA,mB;AACA,I;AACA,uD;AACA,+C;AACA,4E;AACA,8B;AACA,I;AACA,gF;AACA,+D;AACA,I;AACA,oE;AACA,+E;AACA,+E;AACA,gF;AACA,6E;AACA,+C;AACA,I;AACA,a;AACA,kC;AACA,mC;AACA,6D;AACA,0D;AACA,4D;AACA,8E;AACA,uE;AACA,6E;AACA,+E;AACA,+E;AACA,0D;AACA,qC;;AAEA,2E;AACA,yE;AACA,gF;AACA,+B;AACA,oC;AACA,8E;AACA,4D;AACA,8E;AACA,gC;AACA,8D;AACA,8E;AACA,6D;AACA,6B;;AAEA,uE;AACA,qB;AACA,uE;AACA,qC;AACA,yE;AACA,gF;AACA,4E;AACA,gB;AACA,kC;;AAEA,gF;AACA,oC;AACA,8E;AACA,8E;AACA,6D;AACA,6E;AACA,gF;AACA,uE;AACA,mD;AACA,I;AACA,iE;;AAEA,+D;AACA,6C;AACA,yE;AACA,yE;AACA,W;AACA,uC;AACA,yE;AACA,wE;AACA,0D;AACA,uE;AACA,gB;AACA,4B;;AAEA,iE;AACA,qC;AACA,kD;AACA,4B;;AAEA,sE;AACA,W;AACA,a;AACA,e;AACA,uE;AACA,uD;AACA,gE;AACA,+E;AACA,0D;AACA,2E;AACA,+E;AACA,2B;;AAEA,qB;AACA,sB;AACA,4C;;AAEA,gE;AACA,4E;AACA,uD;AACA,oC;AACA,+B;AACA,yD;AACA,mC;AACA,qB;AACA,c;AACA,sB;AACA,O;AACA,O;AACA,G;;AAEA,sC;AACA,S;AACA,kC;AACA,iB;AACA,sD;AACA,a;AACA,K;;AAEA,mC;AACA,uE;AACA,wE;AACA,qC;AACA,mC;AACA,kE;AACA,a;AACA,K;;AAEA,kC;AACA,8C;AACA,oC;AACA,4B;AACA,K;AACA,mC;AACA,gE;AACA,8C;AACA,+C;AACA,c;AACA,yB;AACA,wF;AACA,yE;AACA,4D;AACA,O;AACA,K;AACA,kC;AACA,iC;AACA,8C;AACA,0B;AACA,uC;AACA,K;AACA,kC;AACA,4B;AACA,uC;AACA,O;AACA,K;AACA,qF;AACA,+B;AACA,iC;AACA,gC;AACA,kC;AACA,iC;AACA,iC;AACA,gC;AACA,Q;AACA,qE;AACA,I;;AAEA,6B;AACA,6D;AACA,qE;AACA,2C;AACA,+B;AACA,4B;AACA,wC;AACA,2E;AACA,0C;AACA,6C;AACA,oB;;AAEA,oE;AACA,qE;AACA,oE;AACA,4D;AACA,qE;AACA,kE;AACA,0B;;AAEA,gF;AACA,wD;AACA,qD;AACA,8D;AACA,4C;AACA,K;;AAEA,uE;AACA,kB;AACA,+C;AACA,4B;AACA,O;;AAEA,oE;AACA,oE;AACA,oE;AACA,oE;AACA,oB;AACA,yB;AACA,kE;AACA,Q;AACA,qC;;AAEA,wE;AACA,6C;AACA,oD;AACA,kB;AACA,mB;AACA,e;AACA,uB;AACA,0B;AACA,S;AACA,O;AACA,I;;AAEA,kC;AACA,0B;AACA,6B;AACA,6B;AACA,K;AACA,I;;AAEA,wB;AACA,iF;AACA,6E;AACA,uF;AACA,U;AACA,0C;AACA,sC;AACA,gD;AACA,G;AACA,E;;AAEA,gF;AACA,sE;AACA,+E;AACA,2E;AACA,+C;AACA,wC;AACA,kB;;AAEA,sC;AACA,mC;AACA,2B;;AAEA,oC;AACA,wC;AACA,kC;AACA,sE;AACA,4B;AACA,4B;AACA,4B;;AAEA,kC;AACA,yD;AACA,E;AACA,mC;AACA,8E;AACA,oE;AACA,4B;AACA,oB;AACA,8E;AACA,gF;AACA,c;AACA,yB;AACA,uE;;AAEA,8E;AACA,Y;AACA,8B;;AAEA,4B;;AAEA,+E;AACA,Y;AACA,mB;AACA,wE;;AAEA,iC;AACA,0C;AACA,I;AACA,6E;AACA,qC;AACA,qC;AACA,oB;AACA,kD;AACA,4E;AACA,0B;AACA,mE;;AAEA,kC;AACA,6D;;AAEA,+E;AACA,8C;AACA,oD;AACA,K;AACA,I;AACA,2E;AACA,oE;AACA,4E;AACA,2D;AACA,yC;AACA,oB;AACA,yB;AACA,kE;AACA,uC;AACA,wC;AACA,gC;AACA,I;AACA,8E;AACA,uE;AACA,4E;AACA,sE;AACA,4B;AACA,oB;AACA,6B;AACA,gC;AACA,I;AACA,2C;AACA,0B;AACA,oB;AACA,gC;AACA,G;AACA,G;;AAEA,gC;AACA,qE;AACA,gF;AACA,gF;AACA,gD;AACA,oB;;AAEA,6B;AACA,mB;;AAEA,0E;AACA,2C;AACA,mB;AACA,kE;AACA,qD;AACA,2C;AACA,4C;AACA,6E;AACA,qC;AACA,gB;AACA,e;;AAEA,+B;;AAEA,qD;AACA,iB;AACA,8C;AACA,qC;AACA,0B;AACA,S;AACA,wB;AACA,iD;AACA,K;;AAEA,gB;AACA,I;;AAEA,K;AACA,qB;AACA,wE;AACA,oC;AACA,kB;AACA,4E;AACA,+B;AACA,uE;AACA,wB;AACA,wE;AACA,sE;AACA,2E;AACA,6C;AACA,K;AACA,2E;AACA,oB;;AAEA,0D;AACA,uB;AACA,wB;AACA,gD;AACA,oC;AACA,yC;AACA,6B;AACA,wE;AACA,iD;AACA,uE;AACA,0B;AACA,iC;AACA,O;AACA,K;;AAEA,uE;AACA,uE;AACA,4B;AACA,M;AACA,wC;AACA,M;AACA,wC;AACA,yD;AACA,yD;AACA,c;AACA,M;AACA,sE;AACA,oE;AACA,sD;AACA,M;AACA,8E;AACA,4E;AACA,uB;AACA,+D;AACA,iD;AACA,yC;AACA,O;;AAEA,W;AACA,mB;AACA,uB;AACA,8C;;AAEA,8B;AACA,+E;AACA,+E;AACA,6E;AACA,yE;AACA,8C;AACA,4B;AACA,qD;AACA,O;;AAEA,wE;AACA,+C;AACA,8B;AACA,kE;AACA,2B;AACA,mD;AACA,O;;AAEA,6B;AACA,iD;AACA,O;AACA,Y;AACA,oE;AACA,uB;AACA,iC;AACA,e;AACA,mB;AACA,oC;AACA,wB;AACA,qB;AACA,0C;AACA,yC;AACA,iD;AACA,yC;AACA,uC;AACA,yB;AACA,4B;AACA,yD;AACA,iD;AACA,U;AACA,0B;AACA,wD;AACA,wB;;AAEA,iC;AACA,+B;AACA,W;AACA,S;AACA,Q;AACA,mE;AACA,K;;AAEA,0C;AACA,kB;AACA,yB;AACA,4C;AACA,iB;;AAEA,uC;AACA,Q;AACA,0B;AACA,8C;AACA,4C;AACA,uB;AACA,6C;AACA,kC;AACA,4B;AACA,Q;AACA,wB;AACA,M;;AAEA,yB;AACA,8E;AACA,8E;AACA,6E;AACA,uE;AACA,wE;AACA,uB;AACA,yC;AACA,2C;AACA,kD;;AAEA,wC;AACA,+C;AACA,+C;AACA,0B;AACA,W;AACA,S;AACA,K;;AAEA,kB;AACA,I;;AAEA,a;AACA,2F;AACA,mE;AACA,qD;AACA,oB;AACA,yB;AACA,sB;AACA,e;AACA,sB;AACA,e;AACA,4B;AACA,qB;AACA,sB;AACA,Q;AACA,6B;AACA,mB;AACA,wB;AACA,Y;AACA,mE;AACA,O;AACA,O;;AAEA,6D;AACA,a;AACA,kB;AACA,I;;AAEA,+B;AACA,oB;AACA,2C;AACA,qC;AACA,4E;AACA,wC;AACA,O;AACA,I;;AAEA,K;AACA,qB;AACA,+D;AACA,oB;AACA,kD;AACA,gE;AACA,uN;AACA,K;AACA,0D;AACA,oE;AACA,4C;AACA,wD;AACA,mE;AACA,gC;AACA,4C;AACA,I;;AAEA,qC;AACA,4E;AACA,4E;AACA,2D;AACA,+E;AACA,4E;AACA,2E;AACA,2E;AACA,2E;AACA,6E;AACA,6E;AACA,2E;AACA,yE;AACA,4E;AACA,8E;AACA,6E;AACA,6E;AACA,2E;AACA,6D;AACA,wC;;AAEA,K;AACA,qB;AACA,4D;AACA,oB;AACA,kD;AACA,+C;AACA,8B;AACA,wM;AACA,8Q;AACA,6G;AACA,K;AACA,mD;AACA,oB;;AAEA,2E;AACA,gC;AACA,qD;AACA,yB;AACA,mB;AACA,K;AACA,4B;;AAEA,mB;AACA,oE;AACA,oC;AACA,yD;AACA,wC;AACA,iB;AACA,sD;AACA,Q;AACA,K;;AAEA,8E;AACA,uC;AACA,6B;;AAEA,mE;AACA,iC;AACA,a;AACA,0B;AACA,6B;AACA,2C;AACA,kB;AACA,Q;AACA,S;;AAEA,iD;AACA,kE;;AAEA,yE;AACA,8E;AACA,yE;AACA,8D;AACA,+E;AACA,6E;AACA,6E;AACA,wE;AACA,4E;AACA,iD;AACA,0B;AACA,2C;AACA,gC;AACA,kD;AACA,O;AACA,wB;AACA,M;;AAEA,sE;AACA,4E;AACA,0E;AACA,8E;AACA,iD;AACA,M;AACA,yE;AACA,yE;AACA,4E;AACA,wE;AACA,a;;AAEA,0C;AACA,e;AACA,wC;AACA,+B;AACA,Q;;AAEA,6C;AACA,2B;AACA,8B;AACA,6B;AACA,iE;AACA,S;;AAEA,+B;AACA,8B;;AAEA,W;AACA,4E;AACA,8C;AACA,wF;AACA,gC;AACA,4E;AACA,0C;AACA,wD;AACA,4E;AACA,+D;AACA,e;AACA,kB;AACA,6D;AACA,W;AACA,W;AACA,O;AACA,iB;AACA,0B;AACA,O;;AAEA,+B;AACA,oD;AACA,K;;AAEA,oE;AACA,+D;AACA,wC;AACA,8B;AACA,qB;AACA,6C;AACA,yB;AACA,O;AACA,oB;AACA,wB;AACA,6B;AACA,K;;AAEA,gE;AACA,iE;AACA,sD;AACA,M;AACA,oE;AACA,iB;AACA,2C;AACA,2E;AACA,4D;AACA,K;;;AAGA,sE;AACA,iD;;AAEA,+D;AACA,oB;AACA,4B;AACA,sE;AACA,sE;AACA,uE;AACA,0B;AACA,mC;AACA,uE;AACA,4C;AACA,U;AACA,c;AACA,iE;AACA,qC;AACA,gC;AACA,qC;AACA,O;AACA,K;AACA,sE;AACA,yE;AACA,+D;AACA,mB;AACA,oB;AACA,mB;AACA,mB;AACA,oB;AACA,M;;AAEA,6C;AACA,8B;AACA,sC;AACA,K;;AAEA,2C;AACA,2B;AACA,yB;AACA,uB;AACA,iD;AACA,2B;AACA,sB;AACA,O;;AAEA,uB;AACA,gE;AACA,yC;AACA,gD;AACA,Y;AACA,8E;AACA,wD;AACA,qD;AACA,qD;AACA,uE;AACA,wE;AACA,K;;AAEA,0D;AACA,mD;AACA,kC;;AAEA,yD;AACA,oC;AACA,iB;AACA,2B;AACA,K;AACA,iE;AACA,I;;AAEA,gF;AACA,uE;AACA,e;AACA,+B;AACA,oB;AACA,uC;AACA,wB;AACA,O;AACA,I;AACA,8E;AACA,gF;AACA,mE;AACA,mD;AACA,oB;AACA,+C;AACA,0E;;AAEA,yB;AACA,mD;AACA,4C;AACA,gD;AACA,qB;AACA,e;AACA,4C;AACA,2D;AACA,sD;AACA,yE;AACA,6E;AACA,uC;AACA,+E;AACA,2B;AACA,oD;AACA,gB;AACA,mE;AACA,mC;AACA,wC;AACA,wC;AACA,oD;AACA,S;AACA,S;AACA,O;AACA,kC;AACA,2D;AACA,K;AACA,I;;AAEA,kE;AACA,wD;AACA,gC;AACA,oB;AACA,6D;AACA,qE;AACA,sD;AACA,Q;AACA,mE;AACA,mE;AACA,oD;AACA,4D;AACA,uC;AACA,O;AACA,O;AACA,I;;AAEA,8D;AACA,yB;AACA,oB;AACA,yC;AACA,I;;AAEA,6D;AACA,mE;AACA,mC;AACA,qC;AACA,oB;AACA,wC;AACA,I;;AAEA,K;AACA,wE;AACA,kB;AACA,qB;AACA,K;AACA,2C;AACA,oB;AACA,8D;AACA,I;;AAEA,K;AACA,mG;;AAEA,8D;AACA,kB;AACA,qB;AACA,K;AACA,8C;AACA,oB;AACA,iE;AACA,I;;AAEA,K;AACA,oD;AACA,kB;AACA,qB;AACA,K;AACA,+C;AACA,oB;AACA,kE;AACA,I;;AAEA,sB;AACA,oB;AACA,uD;AACA,I;;AAEA,K;AACA,0B;AACA,K;AACA,uB;AACA,oB;AACA,yB;AACA,gC;AACA,wB;AACA,I;;AAEA,gC;AACA,oB;AACA,+E;AACA,gC;AACA,a;AACA,0B;AACA,yB;AACA,iC;AACA,I;;AAEA,gF;AACA,2E;AACA,6B;AACA,sC;AACA,oB;AACA,kD;AACA,0D;AACA,I;;AAEA,6E;AACA,uC;AACA,0C;AACA,oB;AACA,+D;AACA,I;;AAEA,uC;AACA,oB;;AAEA,oE;AACA,uC;AACA,mD;AACA,iD;AACA,gC;AACA,+B;AACA,gE;AACA,U;AACA,+B;AACA,oC;AACA,S;AACA,S;AACA,8B;AACA,K;;AAEA,8D;AACA,4B;AACA,+B;;AAEA,4C;AACA,+E;AACA,wC;AACA,K;;AAEA,uC;AACA,+E;AACA,4E;AACA,gF;AACA,+E;AACA,0E;AACA,a;AACA,K;;AAEA,oE;;AAEA,+E;AACA,mC;AACA,uC;;AAEA,4B;AACA,8E;AACA,gB;AACA,wC;AACA,iC;AACA,K;;AAEA,mC;AACA,oC;;AAEA,8E;AACA,gD;AACA,gF;AACA,qE;AACA,gC;AACA,oD;AACA,oB;AACA,0C;AACA,O;;AAEA,2E;AACA,uE;AACA,uC;AACA,M;AACA,wE;AACA,gF;AACA,4D;AACA,yC;AACA,4B;AACA,uD;AACA,kC;AACA,gF;AACA,0E;AACA,4E;AACA,0E;AACA,gF;AACA,yC;AACA,4E;AACA,8E;AACA,8D;AACA,Y;AACA,6E;AACA,uE;AACA,+E;AACA,2E;AACA,+B;AACA,mE;AACA,S;AACA,S;AACA,K;;AAEA,+C;;AAEA,+E;AACA,sC;AACA,wC;AACA,8B;AACA,2C;AACA,iC;AACA,wB;AACA,W;AACA,kC;AACA,O;AACA,sC;AACA,K;AACA,I;;;AAGA,mD;AACA,oB;AACA,6D;AACA,8C;AACA,I;;;AAGA,kC;AACA,oB;;AAEA,2C;AACA,qB;;AAEA,uC;AACA,sD;;AAEA,8B;AACA,8C;;AAEA,+C;AACA,6C;AACA,S;AACA,qD;AACA,yD;AACA,S;;AAEA,uC;AACA,e;;AAEA,oD;AACA,8E;AACA,oC;AACA,4E;AACA,0D;AACA,S;AACA,iD;AACA,Y;AACA,gD;AACA,K;;AAEA,mD;AACA,oD;AACA,oD;AACA,gF;AACA,yC;AACA,S;AACA,gC;;AAEA,4D;AACA,4C;AACA,oB;AACA,2D;AACA,wC;AACA,a;AACA,gB;AACA,gE;AACA,8B;AACA,oE;AACA,qE;AACA,gD;AACA,+D;AACA,0D;AACA,8E;AACA,qD;AACA,S;AACA,S;;AAEA,gC;AACA,4D;AACA,K;;AAEA,oC;AACA,I;;AAEA,2E;AACA,yE;AACA,+B;AACA,yC;AACA,oB;AACA,+C;AACA,oC;AACA,oC;AACA,U;AACA,O;AACA,I;;AAEA,oD;AACA,oB;AACA,sC;AACA,+B;AACA,K;AACA,kC;AACA,I;;AAEA,4C;AACA,oB;AACA,kD;AACA,kB;AACA,oE;AACA,mD;AACA,I;;AAEA,2C;AACA,oB;AACA,8C;AACA,2D;AACA,oB;AACA,0C;AACA,2C;AACA,sE;AACA,4C;AACA,kC;AACA,Y;AACA,qD;AACA,K;AACA,I;;AAEA,6C;AACA,oB;AACA,uC;AACA,wD;AACA,oB;AACA,2C;AACA,6E;AACA,oE;AACA,Y;AACA,qD;AACA,K;AACA,I;;AAEA,6C;AACA,oB;AACA,uC;AACA,wD;AACA,oB;AACA,0C;AACA,2C;AACA,4E;AACA,qC;AACA,Y;AACA,iD;AACA,uB;AACA,mC;AACA,kB;AACA,S;AACA,K;AACA,I;;AAEA,6C;AACA,oB;AACA,sC;AACA,6C;AACA,yE;AACA,2E;AACA,uB;AACA,2E;AACA,gD;AACA,4D;AACA,iE;AACA,kD;AACA,kD;AACA,6E;AACA,8E;AACA,+E;AACA,wC;;AAEA,yE;AACA,+E;AACA,uE;AACA,yD;AACA,2B;AACA,yD;AACA,uC;AACA,a;AACA,sC;AACA,yD;AACA,gB;AACA,a;;AAEA,2E;AACA,6E;AACA,gE;AACA,uE;AACA,S;AACA,S;AACA,oD;;AAEA,gF;AACA,iD;AACA,2D;AACA,2B;AACA,sE;AACA,2C;AACA,8D;AACA,O;AACA,I;;AAEA,2C;AACA,oB;AACA,2E;AACA,wE;AACA,4D;AACA,uC;AACA,wD;AACA,mD;AACA,sC;AACA,uB;AACA,iB;AACA,0D;AACA,4B;AACA,iB;AACA,6D;AACA,+B;AACA,sC;AACA,S;AACA,O;AACA,I;;AAEA,qE;AACA,gF;AACA,2C;AACA,iD;AACA,oB;AACA,wC;AACA,yC;AACA,M;AACA,oC;AACA,wC;AACA,gC;AACA,0C;AACA,6E;AACA,6B;AACA,2B;AACA,O;AACA,M;AACA,6D;AACA,mD;AACA,2D;AACA,gE;AACA,yD;AACA,kD;AACA,a;AACA,qD;AACA,oC;AACA,0D;AACA,S;AACA,S;AACA,O;AACA,wC;AACA,gF;AACA,qC;AACA,yB;AACA,K;AACA,I;;AAEA,mC;AACA,oB;;AAEA,2E;AACA,0B;AACA,6B;;AAEA,yD;AACA,kC;;AAEA,2D;AACA,4C;AACA,a;;AAEA,6C;AACA,kE;AACA,gE;;AAEA,yC;;AAEA,gD;AACA,wD;AACA,uE;AACA,K;;AAEA,6C;AACA,qC;AACA,6C;AACA,K;;AAEA,uB;AACA,4C;AACA,K;AACA,I;;AAEA,+B;AACA,2E;AACA,4E;AACA,sE;AACA,yE;AACA,e;AACA,I;;AAEA,oC;AACA,mE;;AAEA,oB;;AAEA,mC;AACA,uD;AACA,mD;AACA,yE;AACA,a;AACA,K;AACA,sE;AACA,U;AACA,yD;AACA,gC;AACA,gC;AACA,c;AACA,K;;AAEA,a;AACA,gF;AACA,a;AACA,K;;AAEA,+E;AACA,gF;AACA,kC;AACA,oC;;AAEA,8B;AACA,uC;AACA,0C;AACA,4B;AACA,Y;AACA,kE;AACA,c;AACA,6C;AACA,K;AACA,I;;AAEA,+E;AACA,8E;AACA,mE;AACA,2C;AACA,oB;AACA,yC;AACA,a;;AAEA,2E;AACA,0E;AACA,kD;AACA,qD;AACA,6D;AACA,0C;AACA,uE;AACA,oD;;AAEA,6D;AACA,oD;AACA,uC;AACA,K;;AAEA,oC;AACA,yB;AACA,I;;AAEA,6D;AACA,8B;AACA,uC;AACA,oB;AACA,iD;AACA,a;AACA,mE;AACA,sB;AACA,O;AACA,I;;AAEA,mC;AACA,8D;AACA,6B;AACA,mD;AACA,I;;AAEA,oE;AACA,oB;AACA,mE;AACA,uC;;AAEA,uB;;AAEA,8C;AACA,a;;AAEA,yE;AACA,4E;AACA,+D;AACA,mD;AACA,iE;AACA,qC;AACA,a;AACA,K;;AAEA,gF;AACA,6E;AACA,yC;AACA,sD;AACA,8C;AACA,kE;AACA,8D;;AAEA,0E;AACA,uD;AACA,0B;AACA,S;;AAEA,yC;AACA,K;;AAEA,kD;AACA,yD;AACA,gD;AACA,O;AACA,I;;AAEA,qE;AACA,+B;AACA,oB;AACA,2C;AACA,I;;AAEA,2E;AACA,kE;AACA,8B;AACA,oB;AACA,uD;AACA,2B;AACA,gC;AACA,K;AACA,G;AACA,G;;AAEA,qC;;AAEA,yC;AACA,Y;AACA,8B;AACA,qC;AACA,W;AACA,qD;;AAEA,G;AACA,oI;AACA,kB;AACA,6D;AACA,G;AACA,uC;AACA,yC;AACA,+C;AACA,a;AACA,E;;AAEA,kE;AACA,iC;AACA,E;AACA,oB;AACA,0C;AACA,gD;AACA,sD;AACA,uB;AACA,O;AACA,K;AACA,E;;;;;;;;;;;;;;;;;;;AClmDA,sE;AACA,4C;AACA,E;AACA,gF;AACA,8E;AACA,mD;AACA,E;AACA,6E;AACA,gF;AACA,6E;AACA,2E;AACA,mB;AACA,qB;AACA,+C;AACA,0D;AACA,6C;AACA,G;;AAEA,6B;;AAEA,4C;AACA,uD;AACA,I;;AAEA,0D;AACA,kC;AACA,iE;AACA,0B;AACA,qE;AACA,Y;AACA,Q;AACA,wC;AACA,uB;AACA,4C;AACA,I;;AAEA,mE;AACA,qD;AACA,gD;AACA,uB;AACA,uC;AACA,S;AACA,C;;AAEA,iE;AACA,gE;AACA,wB;AACA,sC","file":"/packages/ddp.js","sourcesContent":["/**\n * @namespace DDP\n * @summary The namespace for DDP-related methods.\n */\nDDP = {};\nLivedataTest = {};\n","// @param endpoint {String} URL to Meteor app\n// \"http://subdomain.meteor.com/\" or \"/\" or\n// \"ddp+sockjs://foo-**.meteor.com/sockjs\"\n//\n// We do some rewriting of the URL to eventually make it \"ws://\" or \"wss://\",\n// whatever was passed in. At the very least, what Meteor.absoluteUrl() returns\n// us should work.\n//\n// We don't do any heartbeating. (The logic that did this in sockjs was removed,\n// because it used a built-in sockjs mechanism. We could do it with WebSocket\n// ping frames or with DDP-level messages.)\nLivedataTest.ClientStream = function (endpoint, options) {\n var self = this;\n options = options || {};\n\n self.options = _.extend({\n retry: true\n }, options);\n\n self.client = null; // created in _launchConnection\n self.endpoint = endpoint;\n\n self.headers = self.options.headers || {};\n\n self._initCommon(self.options);\n\n //// Kickoff!\n self._launchConnection();\n};\n\n_.extend(LivedataTest.ClientStream.prototype, {\n\n // data is a utf8 string. Data sent while not connected is dropped on\n // the floor, and it is up the user of this API to retransmit lost\n // messages on 'reset'\n send: function (data) {\n var self = this;\n if (self.currentStatus.connected) {\n self.client.send(data);\n }\n },\n\n // Changes where this connection points\n _changeUrl: function (url) {\n var self = this;\n self.endpoint = url;\n },\n\n _onConnect: function (client) {\n var self = this;\n\n if (client !== self.client) {\n // This connection is not from the last call to _launchConnection.\n // But _launchConnection calls _cleanup which closes previous connections.\n // It's our belief that this stifles future 'open' events, but maybe\n // we are wrong?\n throw new Error(\"Got open from inactive client \" + !!self.client);\n }\n\n if (self._forcedToDisconnect) {\n // We were asked to disconnect between trying to open the connection and\n // actually opening it. Let's just pretend this never happened.\n self.client.close();\n self.client = null;\n return;\n }\n\n if (self.currentStatus.connected) {\n // We already have a connection. It must have been the case that we\n // started two parallel connection attempts (because we wanted to\n // 'reconnect now' on a hanging connection and we had no way to cancel the\n // connection attempt.) But this shouldn't happen (similarly to the client\n // !== self.client check above).\n throw new Error(\"Two parallel connections?\");\n }\n\n self._clearConnectionTimer();\n\n // update status\n self.currentStatus.status = \"connected\";\n self.currentStatus.connected = true;\n self.currentStatus.retryCount = 0;\n self.statusChanged();\n\n // fire resets. This must come after status change so that clients\n // can call send from within a reset callback.\n _.each(self.eventCallbacks.reset, function (callback) { callback(); });\n },\n\n _cleanup: function (maybeError) {\n var self = this;\n\n self._clearConnectionTimer();\n if (self.client) {\n var client = self.client;\n self.client = null;\n client.close();\n\n _.each(self.eventCallbacks.disconnect, function (callback) {\n callback(maybeError);\n });\n }\n },\n\n _clearConnectionTimer: function () {\n var self = this;\n\n if (self.connectionTimer) {\n clearTimeout(self.connectionTimer);\n self.connectionTimer = null;\n }\n },\n\n _getProxyUrl: function (targetUrl) {\n var self = this;\n // Similar to code in tools/http-helpers.js.\n var proxy = process.env.HTTP_PROXY || process.env.http_proxy || null;\n // if we're going to a secure url, try the https_proxy env variable first.\n if (targetUrl.match(/^wss:/)) {\n proxy = process.env.HTTPS_PROXY || process.env.https_proxy || proxy;\n }\n return proxy;\n },\n\n _launchConnection: function () {\n var self = this;\n self._cleanup(); // cleanup the old socket, if there was one.\n\n // Since server-to-server DDP is still an experimental feature, we only\n // require the module if we actually create a server-to-server\n // connection.\n var FayeWebSocket = Npm.require('faye-websocket');\n\n var targetUrl = toWebsocketUrl(self.endpoint);\n var fayeOptions = { headers: self.headers };\n var proxyUrl = self._getProxyUrl(targetUrl);\n if (proxyUrl) {\n fayeOptions.proxy = { origin: proxyUrl };\n };\n\n // We would like to specify 'ddp' as the subprotocol here. The npm module we\n // used to use as a client would fail the handshake if we ask for a\n // subprotocol and the server doesn't send one back (and sockjs doesn't).\n // Faye doesn't have that behavior; it's unclear from reading RFC 6455 if\n // Faye is erroneous or not. So for now, we don't specify protocols.\n var subprotocols = [];\n\n var client = self.client = new FayeWebSocket.Client(\n targetUrl, subprotocols, fayeOptions);\n\n self._clearConnectionTimer();\n self.connectionTimer = Meteor.setTimeout(\n function () {\n self._lostConnection(\n new DDP.ConnectionError(\"DDP connection timed out\"));\n },\n self.CONNECT_TIMEOUT);\n\n self.client.on('open', Meteor.bindEnvironment(function () {\n return self._onConnect(client);\n }, \"stream connect callback\"));\n\n var clientOnIfCurrent = function (event, description, f) {\n self.client.on(event, Meteor.bindEnvironment(function () {\n // Ignore events from any connection we've already cleaned up.\n if (client !== self.client)\n return;\n f.apply(this, arguments);\n }, description));\n };\n\n clientOnIfCurrent('error', 'stream error callback', function (error) {\n if (!self.options._dontPrintErrors)\n Meteor._debug(\"stream error\", error.message);\n\n // Faye's 'error' object is not a JS error (and among other things,\n // doesn't stringify well). Convert it to one.\n self._lostConnection(new DDP.ConnectionError(error.message));\n });\n\n\n clientOnIfCurrent('close', 'stream close callback', function () {\n self._lostConnection();\n });\n\n\n clientOnIfCurrent('message', 'stream message callback', function (message) {\n // Ignore binary frames, where message.data is a Buffer\n if (typeof message.data !== \"string\")\n return;\n\n _.each(self.eventCallbacks.message, function (callback) {\n callback(message.data);\n });\n });\n }\n});\n","// XXX from Underscore.String (http://epeli.github.com/underscore.string/)\nvar startsWith = function(str, starts) {\n return str.length >= starts.length &&\n str.substring(0, starts.length) === starts;\n};\nvar endsWith = function(str, ends) {\n return str.length >= ends.length &&\n str.substring(str.length - ends.length) === ends;\n};\n\n// @param url {String} URL to Meteor app, eg:\n// \"/\" or \"madewith.meteor.com\" or \"https://foo.meteor.com\"\n// or \"ddp+sockjs://ddp--****-foo.meteor.com/sockjs\"\n// @returns {String} URL to the endpoint with the specific scheme and subPath, e.g.\n// for scheme \"http\" and subPath \"sockjs\"\n// \"http://subdomain.meteor.com/sockjs\" or \"/sockjs\"\n// or \"https://ddp--1234-foo.meteor.com/sockjs\"\nvar translateUrl = function(url, newSchemeBase, subPath) {\n if (! newSchemeBase) {\n newSchemeBase = \"http\";\n }\n\n var ddpUrlMatch = url.match(/^ddp(i?)\\+sockjs:\\/\\//);\n var httpUrlMatch = url.match(/^http(s?):\\/\\//);\n var newScheme;\n if (ddpUrlMatch) {\n // Remove scheme and split off the host.\n var urlAfterDDP = url.substr(ddpUrlMatch[0].length);\n newScheme = ddpUrlMatch[1] === \"i\" ? newSchemeBase : newSchemeBase + \"s\";\n var slashPos = urlAfterDDP.indexOf('/');\n var host =\n slashPos === -1 ? urlAfterDDP : urlAfterDDP.substr(0, slashPos);\n var rest = slashPos === -1 ? '' : urlAfterDDP.substr(slashPos);\n\n // In the host (ONLY!), change '*' characters into random digits. This\n // allows different stream connections to connect to different hostnames\n // and avoid browser per-hostname connection limits.\n host = host.replace(/\\*/g, function () {\n return Math.floor(Random.fraction()*10);\n });\n\n return newScheme + '://' + host + rest;\n } else if (httpUrlMatch) {\n newScheme = !httpUrlMatch[1] ? newSchemeBase : newSchemeBase + \"s\";\n var urlAfterHttp = url.substr(httpUrlMatch[0].length);\n url = newScheme + \"://\" + urlAfterHttp;\n }\n\n // Prefix FQDNs but not relative URLs\n if (url.indexOf(\"://\") === -1 && !startsWith(url, \"/\")) {\n url = newSchemeBase + \"://\" + url;\n }\n\n // XXX This is not what we should be doing: if I have a site\n // deployed at \"/foo\", then DDP.connect(\"/\") should actually connect\n // to \"/\", not to \"/foo\". \"/\" is an absolute path. (Contrast: if\n // deployed at \"/foo\", it would be reasonable for DDP.connect(\"bar\")\n // to connect to \"/foo/bar\").\n //\n // We should make this properly honor absolute paths rather than\n // forcing the path to be relative to the site root. Simultaneously,\n // we should set DDP_DEFAULT_CONNECTION_URL to include the site\n // root. See also client_convenience.js #RationalizingRelativeDDPURLs\n url = Meteor._relativeToSiteRootUrl(url);\n\n if (endsWith(url, \"/\"))\n return url + subPath;\n else\n return url + \"/\" + subPath;\n};\n\ntoSockjsUrl = function (url) {\n return translateUrl(url, \"http\", \"sockjs\");\n};\n\ntoWebsocketUrl = function (url) {\n var ret = translateUrl(url, \"ws\", \"websocket\");\n return ret;\n};\n\nLivedataTest.toSockjsUrl = toSockjsUrl;\n\n\n_.extend(LivedataTest.ClientStream.prototype, {\n\n // Register for callbacks.\n on: function (name, callback) {\n var self = this;\n\n if (name !== 'message' && name !== 'reset' && name !== 'disconnect')\n throw new Error(\"unknown event type: \" + name);\n\n if (!self.eventCallbacks[name])\n self.eventCallbacks[name] = [];\n self.eventCallbacks[name].push(callback);\n },\n\n\n _initCommon: function (options) {\n var self = this;\n options = options || {};\n\n //// Constants\n\n // how long to wait until we declare the connection attempt\n // failed.\n self.CONNECT_TIMEOUT = options.connectTimeoutMs || 10000;\n\n self.eventCallbacks = {}; // name -> [callback]\n\n self._forcedToDisconnect = false;\n\n //// Reactive status\n self.currentStatus = {\n status: \"connecting\",\n connected: false,\n retryCount: 0\n };\n\n\n self.statusListeners = typeof Tracker !== 'undefined' && new Tracker.Dependency;\n self.statusChanged = function () {\n if (self.statusListeners)\n self.statusListeners.changed();\n };\n\n //// Retry logic\n self._retry = new Retry;\n self.connectionTimer = null;\n\n },\n\n // Trigger a reconnect.\n reconnect: function (options) {\n var self = this;\n options = options || {};\n\n if (options.url) {\n self._changeUrl(options.url);\n }\n\n if (options._sockjsOptions) {\n self.options._sockjsOptions = options._sockjsOptions;\n }\n\n if (self.currentStatus.connected) {\n if (options._force || options.url) {\n // force reconnect.\n self._lostConnection(new DDP.ForcedReconnectError);\n } // else, noop.\n return;\n }\n\n // if we're mid-connection, stop it.\n if (self.currentStatus.status === \"connecting\") {\n // Pretend it's a clean close.\n self._lostConnection();\n }\n\n self._retry.clear();\n self.currentStatus.retryCount -= 1; // don't count manual retries\n self._retryNow();\n },\n\n disconnect: function (options) {\n var self = this;\n options = options || {};\n\n // Failed is permanent. If we're failed, don't let people go back\n // online by calling 'disconnect' then 'reconnect'.\n if (self._forcedToDisconnect)\n return;\n\n // If _permanent is set, permanently disconnect a stream. Once a stream\n // is forced to disconnect, it can never reconnect. This is for\n // error cases such as ddp version mismatch, where trying again\n // won't fix the problem.\n if (options._permanent) {\n self._forcedToDisconnect = true;\n }\n\n self._cleanup();\n self._retry.clear();\n\n self.currentStatus = {\n status: (options._permanent ? \"failed\" : \"offline\"),\n connected: false,\n retryCount: 0\n };\n\n if (options._permanent && options._error)\n self.currentStatus.reason = options._error;\n\n self.statusChanged();\n },\n\n // maybeError is set unless it's a clean protocol-level close.\n _lostConnection: function (maybeError) {\n var self = this;\n\n self._cleanup(maybeError);\n self._retryLater(maybeError); // sets status. no need to do it here.\n },\n\n // fired when we detect that we've gone online. try to reconnect\n // immediately.\n _online: function () {\n // if we've requested to be offline by disconnecting, don't reconnect.\n if (this.currentStatus.status != \"offline\")\n this.reconnect();\n },\n\n _retryLater: function (maybeError) {\n var self = this;\n\n var timeout = 0;\n if (self.options.retry ||\n (maybeError && maybeError.errorType === \"DDP.ForcedReconnectError\")) {\n timeout = self._retry.retryLater(\n self.currentStatus.retryCount,\n _.bind(self._retryNow, self)\n );\n self.currentStatus.status = \"waiting\";\n self.currentStatus.retryTime = (new Date()).getTime() + timeout;\n } else {\n self.currentStatus.status = \"failed\";\n delete self.currentStatus.retryTime;\n }\n\n self.currentStatus.connected = false;\n self.statusChanged();\n },\n\n _retryNow: function () {\n var self = this;\n\n if (self._forcedToDisconnect)\n return;\n\n self.currentStatus.retryCount += 1;\n self.currentStatus.status = \"connecting\";\n self.currentStatus.connected = false;\n delete self.currentStatus.retryTime;\n self.statusChanged();\n\n self._launchConnection();\n },\n\n\n // Get current status. Reactive.\n status: function () {\n var self = this;\n if (self.statusListeners)\n self.statusListeners.depend();\n return self.currentStatus;\n }\n});\n\nDDP.ConnectionError = Meteor.makeErrorType(\n \"DDP.ConnectionError\", function (message) {\n var self = this;\n self.message = message;\n});\n\nDDP.ForcedReconnectError = Meteor.makeErrorType(\n \"DDP.ForcedReconnectError\", function () {});\n","var url = Npm.require('url');\n\nvar pathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || \"\";\n\nStreamServer = function () {\n var self = this;\n self.registration_callbacks = [];\n self.open_sockets = [];\n\n // Because we are installing directly onto WebApp.httpServer instead of using\n // WebApp.app, we have to process the path prefix ourselves.\n self.prefix = pathPrefix + '/sockjs';\n // routepolicy is only a weak dependency, because we don't need it if we're\n // just doing server-to-server DDP as a client.\n if (Package.routepolicy) {\n Package.routepolicy.RoutePolicy.declare(self.prefix + '/', 'network');\n }\n\n // set up sockjs\n var sockjs = Npm.require('sockjs');\n var serverOptions = {\n prefix: self.prefix,\n log: function() {},\n // this is the default, but we code it explicitly because we depend\n // on it in stream_client:HEARTBEAT_TIMEOUT\n heartbeat_delay: 45000,\n // The default disconnect_delay is 5 seconds, but if the server ends up CPU\n // bound for that much time, SockJS might not notice that the user has\n // reconnected because the timer (of disconnect_delay ms) can fire before\n // SockJS processes the new connection. Eventually we'll fix this by not\n // combining CPU-heavy processing with SockJS termination (eg a proxy which\n // converts to Unix sockets) but for now, raise the delay.\n disconnect_delay: 60 * 1000,\n // Set the USE_JSESSIONID environment variable to enable setting the\n // JSESSIONID cookie. This is useful for setting up proxies with\n // session affinity.\n jsessionid: !!process.env.USE_JSESSIONID\n };\n\n // If you know your server environment (eg, proxies) will prevent websockets\n // from ever working, set $DISABLE_WEBSOCKETS and SockJS clients (ie,\n // browsers) will not waste time attempting to use them.\n // (Your server will still have a /websocket endpoint.)\n if (process.env.DISABLE_WEBSOCKETS)\n serverOptions.websocket = false;\n\n self.server = sockjs.createServer(serverOptions);\n if (!Package.webapp) {\n throw new Error(\"Cannot create a DDP server without the webapp package\");\n }\n // Install the sockjs handlers, but we want to keep around our own particular\n // request handler that adjusts idle timeouts while we have an outstanding\n // request. This compensates for the fact that sockjs removes all listeners\n // for \"request\" to add its own.\n Package.webapp.WebApp.httpServer.removeListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback);\n self.server.installHandlers(Package.webapp.WebApp.httpServer);\n Package.webapp.WebApp.httpServer.addListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback);\n\n // Support the /websocket endpoint\n self._redirectWebsocketEndpoint();\n\n self.server.on('connection', function (socket) {\n socket.send = function (data) {\n socket.write(data);\n };\n socket.on('close', function () {\n self.open_sockets = _.without(self.open_sockets, socket);\n });\n self.open_sockets.push(socket);\n\n // XXX COMPAT WITH 0.6.6. Send the old style welcome message, which\n // will force old clients to reload. Remove this once we're not\n // concerned about people upgrading from a pre-0.7.0 release. Also,\n // remove the clause in the client that ignores the welcome message\n // (livedata_connection.js)\n socket.send(JSON.stringify({server_id: \"0\"}));\n\n // call all our callbacks when we get a new socket. they will do the\n // work of setting up handlers and such for specific messages.\n _.each(self.registration_callbacks, function (callback) {\n callback(socket);\n });\n });\n\n};\n\n_.extend(StreamServer.prototype, {\n // call my callback when a new socket connects.\n // also call it for all current connections.\n register: function (callback) {\n var self = this;\n self.registration_callbacks.push(callback);\n _.each(self.all_sockets(), function (socket) {\n callback(socket);\n });\n },\n\n // get a list of all sockets\n all_sockets: function () {\n var self = this;\n return _.values(self.open_sockets);\n },\n\n // Redirect /websocket to /sockjs/websocket in order to not expose\n // sockjs to clients that want to use raw websockets\n _redirectWebsocketEndpoint: function() {\n var self = this;\n // Unfortunately we can't use a connect middleware here since\n // sockjs installs itself prior to all existing listeners\n // (meaning prior to any connect middlewares) so we need to take\n // an approach similar to overshadowListeners in\n // https://github.com/sockjs/sockjs-node/blob/cf820c55af6a9953e16558555a31decea554f70e/src/utils.coffee\n _.each(['request', 'upgrade'], function(event) {\n var httpServer = Package.webapp.WebApp.httpServer;\n var oldHttpServerListeners = httpServer.listeners(event).slice(0);\n httpServer.removeAllListeners(event);\n\n // request and upgrade have different arguments passed but\n // we only care about the first one which is always request\n var newListener = function(request /*, moreArguments */) {\n // Store arguments for use within the closure below\n var args = arguments;\n\n // Rewrite /websocket and /websocket/ urls to /sockjs/websocket while\n // preserving query string.\n var parsedUrl = url.parse(request.url);\n if (parsedUrl.pathname === pathPrefix + '/websocket' ||\n parsedUrl.pathname === pathPrefix + '/websocket/') {\n parsedUrl.pathname = self.prefix + '/websocket';\n request.url = url.format(parsedUrl);\n }\n _.each(oldHttpServerListeners, function(oldListener) {\n oldListener.apply(httpServer, args);\n });\n };\n httpServer.addListener(event, newListener);\n });\n }\n});\n","// Heartbeat options:\n// heartbeatInterval: interval to send pings, in milliseconds.\n// heartbeatTimeout: timeout to close the connection if a reply isn't\n// received, in milliseconds.\n// sendPing: function to call to send a ping on the connection.\n// onTimeout: function to call to close the connection.\n\nHeartbeat = function (options) {\n var self = this;\n\n self.heartbeatInterval = options.heartbeatInterval;\n self.heartbeatTimeout = options.heartbeatTimeout;\n self._sendPing = options.sendPing;\n self._onTimeout = options.onTimeout;\n\n self._heartbeatIntervalHandle = null;\n self._heartbeatTimeoutHandle = null;\n};\n\n_.extend(Heartbeat.prototype, {\n stop: function () {\n var self = this;\n self._clearHeartbeatIntervalTimer();\n self._clearHeartbeatTimeoutTimer();\n },\n\n start: function () {\n var self = this;\n self.stop();\n self._startHeartbeatIntervalTimer();\n },\n\n _startHeartbeatIntervalTimer: function () {\n var self = this;\n self._heartbeatIntervalHandle = Meteor.setTimeout(\n _.bind(self._heartbeatIntervalFired, self),\n self.heartbeatInterval\n );\n },\n\n _startHeartbeatTimeoutTimer: function () {\n var self = this;\n self._heartbeatTimeoutHandle = Meteor.setTimeout(\n _.bind(self._heartbeatTimeoutFired, self),\n self.heartbeatTimeout\n );\n },\n\n _clearHeartbeatIntervalTimer: function () {\n var self = this;\n if (self._heartbeatIntervalHandle) {\n Meteor.clearTimeout(self._heartbeatIntervalHandle);\n self._heartbeatIntervalHandle = null;\n }\n },\n\n _clearHeartbeatTimeoutTimer: function () {\n var self = this;\n if (self._heartbeatTimeoutHandle) {\n Meteor.clearTimeout(self._heartbeatTimeoutHandle);\n self._heartbeatTimeoutHandle = null;\n }\n },\n\n // The heartbeat interval timer is fired when we should send a ping.\n _heartbeatIntervalFired: function () {\n var self = this;\n self._heartbeatIntervalHandle = null;\n self._sendPing();\n // Wait for a pong.\n self._startHeartbeatTimeoutTimer();\n },\n\n // The heartbeat timeout timer is fired when we sent a ping, but we\n // timed out waiting for the pong.\n _heartbeatTimeoutFired: function () {\n var self = this;\n self._heartbeatTimeoutHandle = null;\n self._onTimeout();\n },\n\n pingReceived: function () {\n var self = this;\n // We know the connection is alive if we receive a ping, so we\n // don't need to send a ping ourselves. Reset the interval timer.\n if (self._heartbeatIntervalHandle) {\n self._clearHeartbeatIntervalTimer();\n self._startHeartbeatIntervalTimer();\n }\n },\n\n pongReceived: function () {\n var self = this;\n\n // Receiving a pong means we won't timeout, so clear the timeout\n // timer and start the interval again.\n if (self._heartbeatTimeoutHandle) {\n self._clearHeartbeatTimeoutTimer();\n self._startHeartbeatIntervalTimer();\n }\n }\n});\n","DDPServer = {};\n\nvar Fiber = Npm.require('fibers');\n\n// This file contains classes:\n// * Session - The server's connection to a single DDP client\n// * Subscription - A single subscription for a single client\n// * Server - An entire server that may talk to > 1 client. A DDP endpoint.\n//\n// Session and Subscription are file scope. For now, until we freeze\n// the interface, Server is package scope (in the future it should be\n// exported.)\n\n// Represents a single document in a SessionCollectionView\nvar SessionDocumentView = function () {\n var self = this;\n self.existsIn = {}; // set of subscriptionHandle\n self.dataByKey = {}; // key-> [ {subscriptionHandle, value} by precedence]\n};\n\n_.extend(SessionDocumentView.prototype, {\n\n getFields: function () {\n var self = this;\n var ret = {};\n _.each(self.dataByKey, function (precedenceList, key) {\n ret[key] = precedenceList[0].value;\n });\n return ret;\n },\n\n clearField: function (subscriptionHandle, key, changeCollector) {\n var self = this;\n // Publish API ignores _id if present in fields\n if (key === \"_id\")\n return;\n var precedenceList = self.dataByKey[key];\n\n // It's okay to clear fields that didn't exist. No need to throw\n // an error.\n if (!precedenceList)\n return;\n\n var removedValue = undefined;\n for (var i = 0; i < precedenceList.length; i++) {\n var precedence = precedenceList[i];\n if (precedence.subscriptionHandle === subscriptionHandle) {\n // The view's value can only change if this subscription is the one that\n // used to have precedence.\n if (i === 0)\n removedValue = precedence.value;\n precedenceList.splice(i, 1);\n break;\n }\n }\n if (_.isEmpty(precedenceList)) {\n delete self.dataByKey[key];\n changeCollector[key] = undefined;\n } else if (removedValue !== undefined &&\n !EJSON.equals(removedValue, precedenceList[0].value)) {\n changeCollector[key] = precedenceList[0].value;\n }\n },\n\n changeField: function (subscriptionHandle, key, value,\n changeCollector, isAdd) {\n var self = this;\n // Publish API ignores _id if present in fields\n if (key === \"_id\")\n return;\n\n // Don't share state with the data passed in by the user.\n value = EJSON.clone(value);\n\n if (!_.has(self.dataByKey, key)) {\n self.dataByKey[key] = [{subscriptionHandle: subscriptionHandle,\n value: value}];\n changeCollector[key] = value;\n return;\n }\n var precedenceList = self.dataByKey[key];\n var elt;\n if (!isAdd) {\n elt = _.find(precedenceList, function (precedence) {\n return precedence.subscriptionHandle === subscriptionHandle;\n });\n }\n\n if (elt) {\n if (elt === precedenceList[0] && !EJSON.equals(value, elt.value)) {\n // this subscription is changing the value of this field.\n changeCollector[key] = value;\n }\n elt.value = value;\n } else {\n // this subscription is newly caring about this field\n precedenceList.push({subscriptionHandle: subscriptionHandle, value: value});\n }\n\n }\n});\n\n/**\n * Represents a client's view of a single collection\n * @param {String} collectionName Name of the collection it represents\n * @param {Object.<String, Function>} sessionCallbacks The callbacks for added, changed, removed\n * @class SessionCollectionView\n */\nvar SessionCollectionView = function (collectionName, sessionCallbacks) {\n var self = this;\n self.collectionName = collectionName;\n self.documents = {};\n self.callbacks = sessionCallbacks;\n};\n\nLivedataTest.SessionCollectionView = SessionCollectionView;\n\n\n_.extend(SessionCollectionView.prototype, {\n\n isEmpty: function () {\n var self = this;\n return _.isEmpty(self.documents);\n },\n\n diff: function (previous) {\n var self = this;\n LocalCollection._diffObjects(previous.documents, self.documents, {\n both: _.bind(self.diffDocument, self),\n\n rightOnly: function (id, nowDV) {\n self.callbacks.added(self.collectionName, id, nowDV.getFields());\n },\n\n leftOnly: function (id, prevDV) {\n self.callbacks.removed(self.collectionName, id);\n }\n });\n },\n\n diffDocument: function (id, prevDV, nowDV) {\n var self = this;\n var fields = {};\n LocalCollection._diffObjects(prevDV.getFields(), nowDV.getFields(), {\n both: function (key, prev, now) {\n if (!EJSON.equals(prev, now))\n fields[key] = now;\n },\n rightOnly: function (key, now) {\n fields[key] = now;\n },\n leftOnly: function(key, prev) {\n fields[key] = undefined;\n }\n });\n self.callbacks.changed(self.collectionName, id, fields);\n },\n\n added: function (subscriptionHandle, id, fields) {\n var self = this;\n var docView = self.documents[id];\n var added = false;\n if (!docView) {\n added = true;\n docView = new SessionDocumentView();\n self.documents[id] = docView;\n }\n docView.existsIn[subscriptionHandle] = true;\n var changeCollector = {};\n _.each(fields, function (value, key) {\n docView.changeField(\n subscriptionHandle, key, value, changeCollector, true);\n });\n if (added)\n self.callbacks.added(self.collectionName, id, changeCollector);\n else\n self.callbacks.changed(self.collectionName, id, changeCollector);\n },\n\n changed: function (subscriptionHandle, id, changed) {\n var self = this;\n var changedResult = {};\n var docView = self.documents[id];\n if (!docView)\n throw new Error(\"Could not find element with id \" + id + \" to change\");\n _.each(changed, function (value, key) {\n if (value === undefined)\n docView.clearField(subscriptionHandle, key, changedResult);\n else\n docView.changeField(subscriptionHandle, key, value, changedResult);\n });\n self.callbacks.changed(self.collectionName, id, changedResult);\n },\n\n removed: function (subscriptionHandle, id) {\n var self = this;\n var docView = self.documents[id];\n if (!docView) {\n var err = new Error(\"Removed nonexistent document \" + id);\n throw err;\n }\n delete docView.existsIn[subscriptionHandle];\n if (_.isEmpty(docView.existsIn)) {\n // it is gone from everyone\n self.callbacks.removed(self.collectionName, id);\n delete self.documents[id];\n } else {\n var changed = {};\n // remove this subscription from every precedence list\n // and record the changes\n _.each(docView.dataByKey, function (precedenceList, key) {\n docView.clearField(subscriptionHandle, key, changed);\n });\n\n self.callbacks.changed(self.collectionName, id, changed);\n }\n }\n});\n\n/******************************************************************************/\n/* Session */\n/******************************************************************************/\n\nvar Session = function (server, version, socket, options) {\n var self = this;\n self.id = Random.id();\n\n self.server = server;\n self.version = version;\n\n self.initialized = false;\n self.socket = socket;\n\n // set to null when the session is destroyed. multiple places below\n // use this to determine if the session is alive or not.\n self.inQueue = new Meteor._DoubleEndedQueue();\n\n self.blocked = false;\n self.workerRunning = false;\n\n // Sub objects for active subscriptions\n self._namedSubs = {};\n self._universalSubs = [];\n\n self.userId = null;\n\n self.collectionViews = {};\n\n // Set this to false to not send messages when collectionViews are\n // modified. This is done when rerunning subs in _setUserId and those messages\n // are calculated via a diff instead.\n self._isSending = true;\n\n // If this is true, don't start a newly-created universal publisher on this\n // session. The session will take care of starting it when appropriate.\n self._dontStartNewUniversalSubs = false;\n\n // when we are rerunning subscriptions, any ready messages\n // we want to buffer up for when we are done rerunning subscriptions\n self._pendingReady = [];\n\n // List of callbacks to call when this connection is closed.\n self._closeCallbacks = [];\n\n\n // XXX HACK: If a sockjs connection, save off the URL. This is\n // temporary and will go away in the near future.\n self._socketUrl = socket.url;\n\n // Allow tests to disable responding to pings.\n self._respondToPings = options.respondToPings;\n\n // This object is the public interface to the session. In the public\n // API, it is called the `connection` object. Internally we call it\n // a `connectionHandle` to avoid ambiguity.\n self.connectionHandle = {\n id: self.id,\n close: function () {\n self.close();\n },\n onClose: function (fn) {\n var cb = Meteor.bindEnvironment(fn, \"connection onClose callback\");\n if (self.inQueue) {\n self._closeCallbacks.push(cb);\n } else {\n // if we're already closed, call the callback.\n Meteor.defer(cb);\n }\n },\n clientAddress: self._clientAddress(),\n httpHeaders: self.socket.headers\n };\n\n socket.send(stringifyDDP({msg: 'connected',\n session: self.id}));\n // On initial connect, spin up all the universal publishers.\n Fiber(function () {\n self.startUniversalSubs();\n }).run();\n\n if (version !== 'pre1' && options.heartbeatInterval !== 0) {\n self.heartbeat = new Heartbeat({\n heartbeatInterval: options.heartbeatInterval,\n heartbeatTimeout: options.heartbeatTimeout,\n onTimeout: function () {\n self.close();\n },\n sendPing: function () {\n self.send({msg: 'ping'});\n }\n });\n self.heartbeat.start();\n }\n\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"sessions\", 1);\n};\n\n_.extend(Session.prototype, {\n\n sendReady: function (subscriptionIds) {\n var self = this;\n if (self._isSending)\n self.send({msg: \"ready\", subs: subscriptionIds});\n else {\n _.each(subscriptionIds, function (subscriptionId) {\n self._pendingReady.push(subscriptionId);\n });\n }\n },\n\n sendAdded: function (collectionName, id, fields) {\n var self = this;\n if (self._isSending)\n self.send({msg: \"added\", collection: collectionName, id: id, fields: fields});\n },\n\n sendChanged: function (collectionName, id, fields) {\n var self = this;\n if (_.isEmpty(fields))\n return;\n\n if (self._isSending) {\n self.send({\n msg: \"changed\",\n collection: collectionName,\n id: id,\n fields: fields\n });\n }\n },\n\n sendRemoved: function (collectionName, id) {\n var self = this;\n if (self._isSending)\n self.send({msg: \"removed\", collection: collectionName, id: id});\n },\n\n getSendCallbacks: function () {\n var self = this;\n return {\n added: _.bind(self.sendAdded, self),\n changed: _.bind(self.sendChanged, self),\n removed: _.bind(self.sendRemoved, self)\n };\n },\n\n getCollectionView: function (collectionName) {\n var self = this;\n if (_.has(self.collectionViews, collectionName)) {\n return self.collectionViews[collectionName];\n }\n var ret = new SessionCollectionView(collectionName,\n self.getSendCallbacks());\n self.collectionViews[collectionName] = ret;\n return ret;\n },\n\n added: function (subscriptionHandle, collectionName, id, fields) {\n var self = this;\n var view = self.getCollectionView(collectionName);\n view.added(subscriptionHandle, id, fields);\n },\n\n removed: function (subscriptionHandle, collectionName, id) {\n var self = this;\n var view = self.getCollectionView(collectionName);\n view.removed(subscriptionHandle, id);\n if (view.isEmpty()) {\n delete self.collectionViews[collectionName];\n }\n },\n\n changed: function (subscriptionHandle, collectionName, id, fields) {\n var self = this;\n var view = self.getCollectionView(collectionName);\n view.changed(subscriptionHandle, id, fields);\n },\n\n startUniversalSubs: function () {\n var self = this;\n // Make a shallow copy of the set of universal handlers and start them. If\n // additional universal publishers start while we're running them (due to\n // yielding), they will run separately as part of Server.publish.\n var handlers = _.clone(self.server.universal_publish_handlers);\n _.each(handlers, function (handler) {\n self._startSubscription(handler);\n });\n },\n\n // Destroy this session and unregister it at the server.\n close: function () {\n var self = this;\n\n // Destroy this session, even if it's not registered at the\n // server. Stop all processing and tear everything down. If a socket\n // was attached, close it.\n\n // Already destroyed.\n if (! self.inQueue)\n return;\n\n // Drop the merge box data immediately.\n self.inQueue = null;\n self.collectionViews = {};\n\n if (self.heartbeat) {\n self.heartbeat.stop();\n self.heartbeat = null;\n }\n\n if (self.socket) {\n self.socket.close();\n self.socket._meteorSession = null;\n }\n\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"sessions\", -1);\n\n Meteor.defer(function () {\n // stop callbacks can yield, so we defer this on close.\n // sub._isDeactivated() detects that we set inQueue to null and\n // treats it as semi-deactivated (it will ignore incoming callbacks, etc).\n self._deactivateAllSubscriptions();\n\n // Defer calling the close callbacks, so that the caller closing\n // the session isn't waiting for all the callbacks to complete.\n _.each(self._closeCallbacks, function (callback) {\n callback();\n });\n });\n\n // Unregister the session.\n self.server._removeSession(self);\n },\n\n // Send a message (doing nothing if no socket is connected right now.)\n // It should be a JSON object (it will be stringified.)\n send: function (msg) {\n var self = this;\n if (self.socket) {\n if (Meteor._printSentDDP)\n Meteor._debug(\"Sent DDP\", stringifyDDP(msg));\n self.socket.send(stringifyDDP(msg));\n }\n },\n\n // Send a connection error.\n sendError: function (reason, offendingMessage) {\n var self = this;\n var msg = {msg: 'error', reason: reason};\n if (offendingMessage)\n msg.offendingMessage = offendingMessage;\n self.send(msg);\n },\n\n // Process 'msg' as an incoming message. (But as a guard against\n // race conditions during reconnection, ignore the message if\n // 'socket' is not the currently connected socket.)\n //\n // We run the messages from the client one at a time, in the order\n // given by the client. The message handler is passed an idempotent\n // function 'unblock' which it may call to allow other messages to\n // begin running in parallel in another fiber (for example, a method\n // that wants to yield.) Otherwise, it is automatically unblocked\n // when it returns.\n //\n // Actually, we don't have to 'totally order' the messages in this\n // way, but it's the easiest thing that's correct. (unsub needs to\n // be ordered against sub, methods need to be ordered against each\n // other.)\n processMessage: function (msg_in) {\n var self = this;\n if (!self.inQueue) // we have been destroyed.\n return;\n\n // Respond to ping and pong messages immediately without queuing.\n // If the negotiated DDP version is \"pre1\" which didn't support\n // pings, preserve the \"pre1\" behavior of responding with a \"bad\n // request\" for the unknown messages.\n //\n // Fibers are needed because heartbeat uses Meteor.setTimeout, which\n // needs a Fiber. We could actually use regular setTimeout and avoid\n // these new fibers, but it is easier to just make everything use\n // Meteor.setTimeout and not think too hard.\n if (self.version !== 'pre1' && msg_in.msg === 'ping') {\n if (self._respondToPings)\n self.send({msg: \"pong\", id: msg_in.id});\n if (self.heartbeat)\n Fiber(function () {\n self.heartbeat.pingReceived();\n }).run();\n return;\n }\n if (self.version !== 'pre1' && msg_in.msg === 'pong') {\n if (self.heartbeat)\n Fiber(function () {\n self.heartbeat.pongReceived();\n }).run();\n return;\n }\n\n self.inQueue.push(msg_in);\n if (self.workerRunning)\n return;\n self.workerRunning = true;\n\n var processNext = function () {\n var msg = self.inQueue && self.inQueue.shift();\n if (!msg) {\n self.workerRunning = false;\n return;\n }\n\n Fiber(function () {\n var blocked = true;\n\n var unblock = function () {\n if (!blocked)\n return; // idempotent\n blocked = false;\n processNext();\n };\n\n if (_.has(self.protocol_handlers, msg.msg))\n self.protocol_handlers[msg.msg].call(self, msg, unblock);\n else\n self.sendError('Bad request', msg);\n unblock(); // in case the handler didn't already do it\n }).run();\n };\n\n processNext();\n },\n\n protocol_handlers: {\n sub: function (msg) {\n var self = this;\n\n // reject malformed messages\n if (typeof (msg.id) !== \"string\" ||\n typeof (msg.name) !== \"string\" ||\n (('params' in msg) && !(msg.params instanceof Array))) {\n self.sendError(\"Malformed subscription\", msg);\n return;\n }\n\n if (!self.server.publish_handlers[msg.name]) {\n self.send({\n msg: 'nosub', id: msg.id,\n error: new Meteor.Error(404, \"Subscription not found\")});\n return;\n }\n\n if (_.has(self._namedSubs, msg.id))\n // subs are idempotent, or rather, they are ignored if a sub\n // with that id already exists. this is important during\n // reconnect.\n return;\n\n var handler = self.server.publish_handlers[msg.name];\n self._startSubscription(handler, msg.id, msg.params, msg.name);\n\n },\n\n unsub: function (msg) {\n var self = this;\n\n self._stopSubscription(msg.id);\n },\n\n method: function (msg, unblock) {\n var self = this;\n\n // reject malformed messages\n // For now, we silently ignore unknown attributes,\n // for forwards compatibility.\n if (typeof (msg.id) !== \"string\" ||\n typeof (msg.method) !== \"string\" ||\n (('params' in msg) && !(msg.params instanceof Array)) ||\n (('randomSeed' in msg) && (typeof msg.randomSeed !== \"string\"))) {\n self.sendError(\"Malformed method invocation\", msg);\n return;\n }\n\n var randomSeed = msg.randomSeed || null;\n\n // set up to mark the method as satisfied once all observers\n // (and subscriptions) have reacted to any writes that were\n // done.\n var fence = new DDPServer._WriteFence;\n fence.onAllCommitted(function () {\n // Retire the fence so that future writes are allowed.\n // This means that callbacks like timers are free to use\n // the fence, and if they fire before it's armed (for\n // example, because the method waits for them) their\n // writes will be included in the fence.\n fence.retire();\n self.send({\n msg: 'updated', methods: [msg.id]});\n });\n\n // find the handler\n var handler = self.server.method_handlers[msg.method];\n if (!handler) {\n self.send({\n msg: 'result', id: msg.id,\n error: new Meteor.Error(404, \"Method not found\")});\n fence.arm();\n return;\n }\n\n var setUserId = function(userId) {\n self._setUserId(userId);\n };\n\n var invocation = new MethodInvocation({\n isSimulation: false,\n userId: self.userId,\n setUserId: setUserId,\n unblock: unblock,\n connection: self.connectionHandle,\n randomSeed: randomSeed\n });\n try {\n var result = DDPServer._CurrentWriteFence.withValue(fence, function () {\n return DDP._CurrentInvocation.withValue(invocation, function () {\n return maybeAuditArgumentChecks(\n handler, invocation, msg.params, \"call to '\" + msg.method + \"'\");\n });\n });\n } catch (e) {\n var exception = e;\n }\n\n fence.arm(); // we're done adding writes to the fence\n unblock(); // unblock, if the method hasn't done it already\n\n exception = wrapInternalException(\n exception, \"while invoking method '\" + msg.method + \"'\");\n\n // send response and add to cache\n var payload =\n exception ? {error: exception} : (result !== undefined ?\n {result: result} : {});\n self.send(_.extend({msg: 'result', id: msg.id}, payload));\n }\n },\n\n _eachSub: function (f) {\n var self = this;\n _.each(self._namedSubs, f);\n _.each(self._universalSubs, f);\n },\n\n _diffCollectionViews: function (beforeCVs) {\n var self = this;\n LocalCollection._diffObjects(beforeCVs, self.collectionViews, {\n both: function (collectionName, leftValue, rightValue) {\n rightValue.diff(leftValue);\n },\n rightOnly: function (collectionName, rightValue) {\n _.each(rightValue.documents, function (docView, id) {\n self.sendAdded(collectionName, id, docView.getFields());\n });\n },\n leftOnly: function (collectionName, leftValue) {\n _.each(leftValue.documents, function (doc, id) {\n self.sendRemoved(collectionName, id);\n });\n }\n });\n },\n\n // Sets the current user id in all appropriate contexts and reruns\n // all subscriptions\n _setUserId: function(userId) {\n var self = this;\n\n if (userId !== null && typeof userId !== \"string\")\n throw new Error(\"setUserId must be called on string or null, not \" +\n typeof userId);\n\n // Prevent newly-created universal subscriptions from being added to our\n // session; they will be found below when we call startUniversalSubs.\n //\n // (We don't have to worry about named subscriptions, because we only add\n // them when we process a 'sub' message. We are currently processing a\n // 'method' message, and the method did not unblock, because it is illegal\n // to call setUserId after unblock. Thus we cannot be concurrently adding a\n // new named subscription.)\n self._dontStartNewUniversalSubs = true;\n\n // Prevent current subs from updating our collectionViews and call their\n // stop callbacks. This may yield.\n self._eachSub(function (sub) {\n sub._deactivate();\n });\n\n // All subs should now be deactivated. Stop sending messages to the client,\n // save the state of the published collections, reset to an empty view, and\n // update the userId.\n self._isSending = false;\n var beforeCVs = self.collectionViews;\n self.collectionViews = {};\n self.userId = userId;\n\n // Save the old named subs, and reset to having no subscriptions.\n var oldNamedSubs = self._namedSubs;\n self._namedSubs = {};\n self._universalSubs = [];\n\n _.each(oldNamedSubs, function (sub, subscriptionId) {\n self._namedSubs[subscriptionId] = sub._recreate();\n // nb: if the handler throws or calls this.error(), it will in fact\n // immediately send its 'nosub'. This is OK, though.\n self._namedSubs[subscriptionId]._runHandler();\n });\n\n // Allow newly-created universal subs to be started on our connection in\n // parallel with the ones we're spinning up here, and spin up universal\n // subs.\n self._dontStartNewUniversalSubs = false;\n self.startUniversalSubs();\n\n // Start sending messages again, beginning with the diff from the previous\n // state of the world to the current state. No yields are allowed during\n // this diff, so that other changes cannot interleave.\n Meteor._noYieldsAllowed(function () {\n self._isSending = true;\n self._diffCollectionViews(beforeCVs);\n if (!_.isEmpty(self._pendingReady)) {\n self.sendReady(self._pendingReady);\n self._pendingReady = [];\n }\n });\n },\n\n _startSubscription: function (handler, subId, params, name) {\n var self = this;\n\n var sub = new Subscription(\n self, handler, subId, params, name);\n if (subId)\n self._namedSubs[subId] = sub;\n else\n self._universalSubs.push(sub);\n\n sub._runHandler();\n },\n\n // tear down specified subscription\n _stopSubscription: function (subId, error) {\n var self = this;\n\n var subName = null;\n\n if (subId && self._namedSubs[subId]) {\n subName = self._namedSubs[subId]._name;\n self._namedSubs[subId]._removeAllDocuments();\n self._namedSubs[subId]._deactivate();\n delete self._namedSubs[subId];\n }\n\n var response = {msg: 'nosub', id: subId};\n\n if (error) {\n response.error = wrapInternalException(\n error,\n subName ? (\"from sub \" + subName + \" id \" + subId)\n : (\"from sub id \" + subId));\n }\n\n self.send(response);\n },\n\n // tear down all subscriptions. Note that this does NOT send removed or nosub\n // messages, since we assume the client is gone.\n _deactivateAllSubscriptions: function () {\n var self = this;\n\n _.each(self._namedSubs, function (sub, id) {\n sub._deactivate();\n });\n self._namedSubs = {};\n\n _.each(self._universalSubs, function (sub) {\n sub._deactivate();\n });\n self._universalSubs = [];\n },\n\n // Determine the remote client's IP address, based on the\n // HTTP_FORWARDED_COUNT environment variable representing how many\n // proxies the server is behind.\n _clientAddress: function () {\n var self = this;\n\n // For the reported client address for a connection to be correct,\n // the developer must set the HTTP_FORWARDED_COUNT environment\n // variable to an integer representing the number of hops they\n // expect in the `x-forwarded-for` header. E.g., set to \"1\" if the\n // server is behind one proxy.\n //\n // This could be computed once at startup instead of every time.\n var httpForwardedCount = parseInt(process.env['HTTP_FORWARDED_COUNT']) || 0;\n\n if (httpForwardedCount === 0)\n return self.socket.remoteAddress;\n\n var forwardedFor = self.socket.headers[\"x-forwarded-for\"];\n if (! _.isString(forwardedFor))\n return null;\n forwardedFor = forwardedFor.trim().split(/\\s*,\\s*/);\n\n // Typically the first value in the `x-forwarded-for` header is\n // the original IP address of the client connecting to the first\n // proxy. However, the end user can easily spoof the header, in\n // which case the first value(s) will be the fake IP address from\n // the user pretending to be a proxy reporting the original IP\n // address value. By counting HTTP_FORWARDED_COUNT back from the\n // end of the list, we ensure that we get the IP address being\n // reported by *our* first proxy.\n\n if (httpForwardedCount < 0 || httpForwardedCount > forwardedFor.length)\n return null;\n\n return forwardedFor[forwardedFor.length - httpForwardedCount];\n }\n});\n\n/******************************************************************************/\n/* Subscription */\n/******************************************************************************/\n\n// ctor for a sub handle: the input to each publish function\n\n// Instance name is this because it's usually referred to as this inside a\n// publish\n/**\n * @summary The server's side of a subscription\n * @class Subscription\n * @instanceName this\n */\nvar Subscription = function (\n session, handler, subscriptionId, params, name) {\n var self = this;\n self._session = session; // type is Session\n\n /**\n * @summary Access inside the publish function. The incoming [connection](#meteor_onconnection) for this subscription.\n * @locus Server\n * @name connection\n * @memberOf Subscription\n * @instance\n */\n self.connection = session.connectionHandle; // public API object\n\n self._handler = handler;\n\n // my subscription ID (generated by client, undefined for universal subs).\n self._subscriptionId = subscriptionId;\n // undefined for universal subs\n self._name = name;\n\n self._params = params || [];\n\n // Only named subscriptions have IDs, but we need some sort of string\n // internally to keep track of all subscriptions inside\n // SessionDocumentViews. We use this subscriptionHandle for that.\n if (self._subscriptionId) {\n self._subscriptionHandle = 'N' + self._subscriptionId;\n } else {\n self._subscriptionHandle = 'U' + Random.id();\n }\n\n // has _deactivate been called?\n self._deactivated = false;\n\n // stop callbacks to g/c this sub. called w/ zero arguments.\n self._stopCallbacks = [];\n\n // the set of (collection, documentid) that this subscription has\n // an opinion about\n self._documents = {};\n\n // remember if we are ready.\n self._ready = false;\n\n // Part of the public API: the user of this sub.\n\n /**\n * @summary Access inside the publish function. The id of the logged-in user, or `null` if no user is logged in.\n * @locus Server\n * @memberOf Subscription\n * @name userId\n * @instance\n */\n self.userId = session.userId;\n\n // For now, the id filter is going to default to\n // the to/from DDP methods on LocalCollection, to\n // specifically deal with mongo/minimongo ObjectIds.\n\n // Later, you will be able to make this be \"raw\"\n // if you want to publish a collection that you know\n // just has strings for keys and no funny business, to\n // a ddp consumer that isn't minimongo\n\n self._idFilter = {\n idStringify: LocalCollection._idStringify,\n idParse: LocalCollection._idParse\n };\n\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"subscriptions\", 1);\n};\n\n_.extend(Subscription.prototype, {\n _runHandler: function () {\n // XXX should we unblock() here? Either before running the publish\n // function, or before running _publishCursor.\n //\n // Right now, each publish function blocks all future publishes and\n // methods waiting on data from Mongo (or whatever else the function\n // blocks on). This probably slows page load in common cases.\n\n var self = this;\n try {\n var res = maybeAuditArgumentChecks(\n self._handler, self, EJSON.clone(self._params),\n // It's OK that this would look weird for universal subscriptions,\n // because they have no arguments so there can never be an\n // audit-argument-checks failure.\n \"publisher '\" + self._name + \"'\");\n } catch (e) {\n self.error(e);\n return;\n }\n\n // Did the handler call this.error or this.stop?\n if (self._isDeactivated())\n return;\n\n // SPECIAL CASE: Instead of writing their own callbacks that invoke\n // this.added/changed/ready/etc, the user can just return a collection\n // cursor or array of cursors from the publish function; we call their\n // _publishCursor method which starts observing the cursor and publishes the\n // results. Note that _publishCursor does NOT call ready().\n //\n // XXX This uses an undocumented interface which only the Mongo cursor\n // interface publishes. Should we make this interface public and encourage\n // users to implement it themselves? Arguably, it's unnecessary; users can\n // already write their own functions like\n // var publishMyReactiveThingy = function (name, handler) {\n // Meteor.publish(name, function () {\n // var reactiveThingy = handler();\n // reactiveThingy.publishMe();\n // });\n // };\n var isCursor = function (c) {\n return c && c._publishCursor;\n };\n if (isCursor(res)) {\n try {\n res._publishCursor(self);\n } catch (e) {\n self.error(e);\n return;\n }\n // _publishCursor only returns after the initial added callbacks have run.\n // mark subscription as ready.\n self.ready();\n } else if (_.isArray(res)) {\n // check all the elements are cursors\n if (! _.all(res, isCursor)) {\n self.error(new Error(\"Publish function returned an array of non-Cursors\"));\n return;\n }\n // find duplicate collection names\n // XXX we should support overlapping cursors, but that would require the\n // merge box to allow overlap within a subscription\n var collectionNames = {};\n for (var i = 0; i < res.length; ++i) {\n var collectionName = res[i]._getCollectionName();\n if (_.has(collectionNames, collectionName)) {\n self.error(new Error(\n \"Publish function returned multiple cursors for collection \" +\n collectionName));\n return;\n }\n collectionNames[collectionName] = true;\n };\n\n try {\n _.each(res, function (cur) {\n cur._publishCursor(self);\n });\n } catch (e) {\n self.error(e);\n return;\n }\n self.ready();\n } else if (res) {\n // truthy values other than cursors or arrays are probably a\n // user mistake (possible returning a Mongo document via, say,\n // `coll.findOne()`).\n self.error(new Error(\"Publish function can only return a Cursor or \"\n + \"an array of Cursors\"));\n }\n },\n\n // This calls all stop callbacks and prevents the handler from updating any\n // SessionCollectionViews further. It's used when the user unsubscribes or\n // disconnects, as well as during setUserId re-runs. It does *NOT* send\n // removed messages for the published objects; if that is necessary, call\n // _removeAllDocuments first.\n _deactivate: function() {\n var self = this;\n if (self._deactivated)\n return;\n self._deactivated = true;\n self._callStopCallbacks();\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"subscriptions\", -1);\n },\n\n _callStopCallbacks: function () {\n var self = this;\n // tell listeners, so they can clean up\n var callbacks = self._stopCallbacks;\n self._stopCallbacks = [];\n _.each(callbacks, function (callback) {\n callback();\n });\n },\n\n // Send remove messages for every document.\n _removeAllDocuments: function () {\n var self = this;\n Meteor._noYieldsAllowed(function () {\n _.each(self._documents, function(collectionDocs, collectionName) {\n // Iterate over _.keys instead of the dictionary itself, since we'll be\n // mutating it.\n _.each(_.keys(collectionDocs), function (strId) {\n self.removed(collectionName, self._idFilter.idParse(strId));\n });\n });\n });\n },\n\n // Returns a new Subscription for the same session with the same\n // initial creation parameters. This isn't a clone: it doesn't have\n // the same _documents cache, stopped state or callbacks; may have a\n // different _subscriptionHandle, and gets its userId from the\n // session, not from this object.\n _recreate: function () {\n var self = this;\n return new Subscription(\n self._session, self._handler, self._subscriptionId, self._params,\n self._name);\n },\n\n /**\n * @summary Call inside the publish function. Stops this client's subscription, triggering a call on the client to the `onStop` callback passed to [`Meteor.subscribe`](#meteor_subscribe), if any. If `error` is not a [`Meteor.Error`](#meteor_error), it will be [sanitized](#meteor_error).\n * @locus Server\n * @param {Error} error The error to pass to the client.\n * @instance\n * @memberOf Subscription\n */\n error: function (error) {\n var self = this;\n if (self._isDeactivated())\n return;\n self._session._stopSubscription(self._subscriptionId, error);\n },\n\n // Note that while our DDP client will notice that you've called stop() on the\n // server (and clean up its _subscriptions table) we don't actually provide a\n // mechanism for an app to notice this (the subscribe onError callback only\n // triggers if there is an error).\n\n /**\n * @summary Call inside the publish function. Stops this client's subscription and invokes the client's `onStop` callback with no error.\n * @locus Server\n * @instance\n * @memberOf Subscription\n */\n stop: function () {\n var self = this;\n if (self._isDeactivated())\n return;\n self._session._stopSubscription(self._subscriptionId);\n },\n\n /**\n * @summary Call inside the publish function. Registers a callback function to run when the subscription is stopped.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {Function} func The callback function\n */\n onStop: function (callback) {\n var self = this;\n if (self._isDeactivated())\n callback();\n else\n self._stopCallbacks.push(callback);\n },\n\n // This returns true if the sub has been deactivated, *OR* if the session was\n // destroyed but the deferred call to _deactivateAllSubscriptions hasn't\n // happened yet.\n _isDeactivated: function () {\n var self = this;\n return self._deactivated || self._session.inQueue === null;\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that a document has been added to the record set.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {String} collection The name of the collection that contains the new document.\n * @param {String} id The new document's ID.\n * @param {Object} fields The fields in the new document. If `_id` is present it is ignored.\n */\n added: function (collectionName, id, fields) {\n var self = this;\n if (self._isDeactivated())\n return;\n id = self._idFilter.idStringify(id);\n Meteor._ensure(self._documents, collectionName)[id] = true;\n self._session.added(self._subscriptionHandle, collectionName, id, fields);\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that a document in the record set has been modified.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {String} collection The name of the collection that contains the changed document.\n * @param {String} id The changed document's ID.\n * @param {Object} fields The fields in the document that have changed, together with their new values. If a field is not present in `fields` it was left unchanged; if it is present in `fields` and has a value of `undefined` it was removed from the document. If `_id` is present it is ignored.\n */\n changed: function (collectionName, id, fields) {\n var self = this;\n if (self._isDeactivated())\n return;\n id = self._idFilter.idStringify(id);\n self._session.changed(self._subscriptionHandle, collectionName, id, fields);\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that a document has been removed from the record set.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {String} collection The name of the collection that the document has been removed from.\n * @param {String} id The ID of the document that has been removed.\n */\n removed: function (collectionName, id) {\n var self = this;\n if (self._isDeactivated())\n return;\n id = self._idFilter.idStringify(id);\n // We don't bother to delete sets of things in a collection if the\n // collection is empty. It could break _removeAllDocuments.\n delete self._documents[collectionName][id];\n self._session.removed(self._subscriptionHandle, collectionName, id);\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that an initial, complete snapshot of the record set has been sent. This will trigger a call on the client to the `onReady` callback passed to [`Meteor.subscribe`](#meteor_subscribe), if any.\n * @locus Server\n * @memberOf Subscription\n * @instance\n */\n ready: function () {\n var self = this;\n if (self._isDeactivated())\n return;\n if (!self._subscriptionId)\n return; // unnecessary but ignored for universal sub\n if (!self._ready) {\n self._session.sendReady([self._subscriptionId]);\n self._ready = true;\n }\n }\n});\n\n/******************************************************************************/\n/* Server */\n/******************************************************************************/\n\nServer = function (options) {\n var self = this;\n\n // The default heartbeat interval is 30 seconds on the server and 35\n // seconds on the client. Since the client doesn't need to send a\n // ping as long as it is receiving pings, this means that pings\n // normally go from the server to the client.\n //\n // Note: Troposphere depends on the ability to mutate\n // Meteor.server.options.heartbeatTimeout! This is a hack, but it's life.\n self.options = _.defaults(options || {}, {\n heartbeatInterval: 30000,\n heartbeatTimeout: 15000,\n // For testing, allow responding to pings to be disabled.\n respondToPings: true\n });\n\n // Map of callbacks to call when a new connection comes in to the\n // server and completes DDP version negotiation. Use an object instead\n // of an array so we can safely remove one from the list while\n // iterating over it.\n self.onConnectionHook = new Hook({\n debugPrintExceptions: \"onConnection callback\"\n });\n\n self.publish_handlers = {};\n self.universal_publish_handlers = [];\n\n self.method_handlers = {};\n\n self.sessions = {}; // map from id to session\n\n self.stream_server = new StreamServer;\n\n self.stream_server.register(function (socket) {\n // socket implements the SockJSConnection interface\n socket._meteorSession = null;\n\n var sendError = function (reason, offendingMessage) {\n var msg = {msg: 'error', reason: reason};\n if (offendingMessage)\n msg.offendingMessage = offendingMessage;\n socket.send(stringifyDDP(msg));\n };\n\n socket.on('data', function (raw_msg) {\n if (Meteor._printReceivedDDP) {\n Meteor._debug(\"Received DDP\", raw_msg);\n }\n try {\n try {\n var msg = parseDDP(raw_msg);\n } catch (err) {\n sendError('Parse error');\n return;\n }\n if (msg === null || !msg.msg) {\n sendError('Bad request', msg);\n return;\n }\n\n if (msg.msg === 'connect') {\n if (socket._meteorSession) {\n sendError(\"Already connected\", msg);\n return;\n }\n Fiber(function () {\n self._handleConnect(socket, msg);\n }).run();\n return;\n }\n\n if (!socket._meteorSession) {\n sendError('Must connect first', msg);\n return;\n }\n socket._meteorSession.processMessage(msg);\n } catch (e) {\n // XXX print stack nicely\n Meteor._debug(\"Internal exception while processing message\", msg,\n e.message, e.stack);\n }\n });\n\n socket.on('close', function () {\n if (socket._meteorSession) {\n Fiber(function () {\n socket._meteorSession.close();\n }).run();\n }\n });\n });\n};\n\n_.extend(Server.prototype, {\n\n /**\n * @summary Register a callback to be called when a new DDP connection is made to the server.\n * @locus Server\n * @param {function} callback The function to call when a new DDP connection is established.\n * @memberOf Meteor\n */\n onConnection: function (fn) {\n var self = this;\n return self.onConnectionHook.register(fn);\n },\n\n _handleConnect: function (socket, msg) {\n var self = this;\n\n // The connect message must specify a version and an array of supported\n // versions, and it must claim to support what it is proposing.\n if (!(typeof (msg.version) === 'string' &&\n _.isArray(msg.support) &&\n _.all(msg.support, _.isString) &&\n _.contains(msg.support, msg.version))) {\n socket.send(stringifyDDP({msg: 'failed',\n version: SUPPORTED_DDP_VERSIONS[0]}));\n socket.close();\n return;\n }\n\n // In the future, handle session resumption: something like:\n // socket._meteorSession = self.sessions[msg.session]\n var version = calculateVersion(msg.support, SUPPORTED_DDP_VERSIONS);\n\n if (msg.version !== version) {\n // The best version to use (according to the client's stated preferences)\n // is not the one the client is trying to use. Inform them about the best\n // version to use.\n socket.send(stringifyDDP({msg: 'failed', version: version}));\n socket.close();\n return;\n }\n\n // Yay, version matches! Create a new session.\n // Note: Troposphere depends on the ability to mutate\n // Meteor.server.options.heartbeatTimeout! This is a hack, but it's life.\n socket._meteorSession = new Session(self, version, socket, self.options);\n self.sessions[socket._meteorSession.id] = socket._meteorSession;\n self.onConnectionHook.each(function (callback) {\n if (socket._meteorSession)\n callback(socket._meteorSession.connectionHandle);\n return true;\n });\n },\n /**\n * Register a publish handler function.\n *\n * @param name {String} identifier for query\n * @param handler {Function} publish handler\n * @param options {Object}\n *\n * Server will call handler function on each new subscription,\n * either when receiving DDP sub message for a named subscription, or on\n * DDP connect for a universal subscription.\n *\n * If name is null, this will be a subscription that is\n * automatically established and permanently on for all connected\n * client, instead of a subscription that can be turned on and off\n * with subscribe().\n *\n * options to contain:\n * - (mostly internal) is_auto: true if generated automatically\n * from an autopublish hook. this is for cosmetic purposes only\n * (it lets us determine whether to print a warning suggesting\n * that you turn off autopublish.)\n */\n\n /**\n * @summary Publish a record set.\n * @memberOf Meteor\n * @locus Server\n * @param {String} name Name of the record set. If `null`, the set has no name, and the record set is automatically sent to all connected clients.\n * @param {Function} func Function called on the server each time a client subscribes. Inside the function, `this` is the publish handler object, described below. If the client passed arguments to `subscribe`, the function is called with the same arguments.\n */\n publish: function (name, handler, options) {\n var self = this;\n\n options = options || {};\n\n if (name && name in self.publish_handlers) {\n Meteor._debug(\"Ignoring duplicate publish named '\" + name + \"'\");\n return;\n }\n\n if (Package.autopublish && !options.is_auto) {\n // They have autopublish on, yet they're trying to manually\n // picking stuff to publish. They probably should turn off\n // autopublish. (This check isn't perfect -- if you create a\n // publish before you turn on autopublish, it won't catch\n // it. But this will definitely handle the simple case where\n // you've added the autopublish package to your app, and are\n // calling publish from your app code.)\n if (!self.warned_about_autopublish) {\n self.warned_about_autopublish = true;\n Meteor._debug(\n\"** You've set up some data subscriptions with Meteor.publish(), but\\n\" +\n\"** you still have autopublish turned on. Because autopublish is still\\n\" +\n\"** on, your Meteor.publish() calls won't have much effect. All data\\n\" +\n\"** will still be sent to all clients.\\n\" +\n\"**\\n\" +\n\"** Turn off autopublish by removing the autopublish package:\\n\" +\n\"**\\n\" +\n\"** $ meteor remove autopublish\\n\" +\n\"**\\n\" +\n\"** .. and make sure you have Meteor.publish() and Meteor.subscribe() calls\\n\" +\n\"** for each collection that you want clients to see.\\n\");\n }\n }\n\n if (name)\n self.publish_handlers[name] = handler;\n else {\n self.universal_publish_handlers.push(handler);\n // Spin up the new publisher on any existing session too. Run each\n // session's subscription in a new Fiber, so that there's no change for\n // self.sessions to change while we're running this loop.\n _.each(self.sessions, function (session) {\n if (!session._dontStartNewUniversalSubs) {\n Fiber(function() {\n session._startSubscription(handler);\n }).run();\n }\n });\n }\n },\n\n _removeSession: function (session) {\n var self = this;\n if (self.sessions[session.id]) {\n delete self.sessions[session.id];\n }\n },\n\n /**\n * @summary Defines functions that can be invoked over the network by clients.\n * @locus Anywhere\n * @param {Object} methods Dictionary whose keys are method names and values are functions.\n * @memberOf Meteor\n */\n methods: function (methods) {\n var self = this;\n _.each(methods, function (func, name) {\n if (self.method_handlers[name])\n throw new Error(\"A method named '\" + name + \"' is already defined\");\n self.method_handlers[name] = func;\n });\n },\n\n call: function (name /*, arguments */) {\n // if it's a function, the last argument is the result callback,\n // not a parameter to the remote method.\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length && typeof args[args.length - 1] === \"function\")\n var callback = args.pop();\n return this.apply(name, args, callback);\n },\n\n // @param options {Optional Object}\n // @param callback {Optional Function}\n apply: function (name, args, options, callback) {\n var self = this;\n\n // We were passed 3 arguments. They may be either (name, args, options)\n // or (name, args, callback)\n if (!callback && typeof options === 'function') {\n callback = options;\n options = {};\n }\n options = options || {};\n\n if (callback)\n // It's not really necessary to do this, since we immediately\n // run the callback in this fiber before returning, but we do it\n // anyway for regularity.\n // XXX improve error message (and how we report it)\n callback = Meteor.bindEnvironment(\n callback,\n \"delivering result of invoking '\" + name + \"'\"\n );\n\n // Run the handler\n var handler = self.method_handlers[name];\n var exception;\n if (!handler) {\n exception = new Meteor.Error(404, \"Method not found\");\n } else {\n // If this is a method call from within another method, get the\n // user state from the outer method, otherwise don't allow\n // setUserId to be called\n var userId = null;\n var setUserId = function() {\n throw new Error(\"Can't call setUserId on a server initiated method call\");\n };\n var connection = null;\n var currentInvocation = DDP._CurrentInvocation.get();\n if (currentInvocation) {\n userId = currentInvocation.userId;\n setUserId = function(userId) {\n currentInvocation.setUserId(userId);\n };\n connection = currentInvocation.connection;\n }\n\n var invocation = new MethodInvocation({\n isSimulation: false,\n userId: userId,\n setUserId: setUserId,\n connection: connection,\n randomSeed: makeRpcSeed(currentInvocation, name)\n });\n try {\n var result = DDP._CurrentInvocation.withValue(invocation, function () {\n return maybeAuditArgumentChecks(\n handler, invocation, EJSON.clone(args), \"internal call to '\" +\n name + \"'\");\n });\n result = EJSON.clone(result);\n } catch (e) {\n exception = e;\n }\n }\n\n // Return the result in whichever way the caller asked for it. Note that we\n // do NOT block on the write fence in an analogous way to how the client\n // blocks on the relevant data being visible, so you are NOT guaranteed that\n // cursor observe callbacks have fired when your callback is invoked. (We\n // can change this if there's a real use case.)\n if (callback) {\n callback(exception, result);\n return undefined;\n }\n if (exception)\n throw exception;\n return result;\n },\n\n _urlForSession: function (sessionId) {\n var self = this;\n var session = self.sessions[sessionId];\n if (session)\n return session._socketUrl;\n else\n return null;\n }\n});\n\nvar calculateVersion = function (clientSupportedVersions,\n serverSupportedVersions) {\n var correctVersion = _.find(clientSupportedVersions, function (version) {\n return _.contains(serverSupportedVersions, version);\n });\n if (!correctVersion) {\n correctVersion = serverSupportedVersions[0];\n }\n return correctVersion;\n};\n\nLivedataTest.calculateVersion = calculateVersion;\n\n\n// \"blind\" exceptions other than those that were deliberately thrown to signal\n// errors to the client\nvar wrapInternalException = function (exception, context) {\n if (!exception || exception instanceof Meteor.Error)\n return exception;\n\n // tests can set the 'expected' flag on an exception so it won't go to the\n // server log\n if (!exception.expected) {\n Meteor._debug(\"Exception \" + context, exception.stack);\n if (exception.sanitizedError) {\n Meteor._debug(\"Sanitized and reported to the client as:\", exception.sanitizedError.message);\n Meteor._debug();\n }\n }\n\n // Did the error contain more details that could have been useful if caught in\n // server code (or if thrown from non-client-originated code), but also\n // provided a \"sanitized\" version with more context than 500 Internal server\n // error? Use that.\n if (exception.sanitizedError) {\n if (exception.sanitizedError instanceof Meteor.Error)\n return exception.sanitizedError;\n Meteor._debug(\"Exception \" + context + \" provides a sanitizedError that \" +\n \"is not a Meteor.Error; ignoring\");\n }\n\n return new Meteor.Error(500, \"Internal server error\");\n};\n\n\n// Audit argument checks, if the audit-argument-checks package exists (it is a\n// weak dependency of this package).\nvar maybeAuditArgumentChecks = function (f, context, args, description) {\n args = args || [];\n if (Package['audit-argument-checks']) {\n return Match._failIfArgumentsAreNotAllChecked(\n f, context, args, description);\n }\n return f.apply(context, args);\n};\n","var path = Npm.require('path');\nvar Future = Npm.require(path.join('fibers', 'future'));\n\n// A write fence collects a group of writes, and provides a callback\n// when all of the writes are fully committed and propagated (all\n// observers have been notified of the write and acknowledged it.)\n//\nDDPServer._WriteFence = function () {\n var self = this;\n\n self.armed = false;\n self.fired = false;\n self.retired = false;\n self.outstanding_writes = 0;\n self.completion_callbacks = [];\n};\n\n// The current write fence. When there is a current write fence, code\n// that writes to databases should register their writes with it using\n// beginWrite().\n//\nDDPServer._CurrentWriteFence = new Meteor.EnvironmentVariable;\n\n_.extend(DDPServer._WriteFence.prototype, {\n // Start tracking a write, and return an object to represent it. The\n // object has a single method, committed(). This method should be\n // called when the write is fully committed and propagated. You can\n // continue to add writes to the WriteFence up until it is triggered\n // (calls its callbacks because all writes have committed.)\n beginWrite: function () {\n var self = this;\n\n if (self.retired)\n return { committed: function () {} };\n\n if (self.fired)\n throw new Error(\"fence has already activated -- too late to add writes\");\n\n self.outstanding_writes++;\n var committed = false;\n return {\n committed: function () {\n if (committed)\n throw new Error(\"committed called twice on the same write\");\n committed = true;\n self.outstanding_writes--;\n self._maybeFire();\n }\n };\n },\n\n // Arm the fence. Once the fence is armed, and there are no more\n // uncommitted writes, it will activate.\n arm: function () {\n var self = this;\n if (self === DDPServer._CurrentWriteFence.get())\n throw Error(\"Can't arm the current fence\");\n self.armed = true;\n self._maybeFire();\n },\n\n // Register a function to be called when the fence fires.\n onAllCommitted: function (func) {\n var self = this;\n if (self.fired)\n throw new Error(\"fence has already activated -- too late to \" +\n \"add a callback\");\n self.completion_callbacks.push(func);\n },\n\n // Convenience function. Arms the fence, then blocks until it fires.\n armAndWait: function () {\n var self = this;\n var future = new Future;\n self.onAllCommitted(function () {\n future['return']();\n });\n self.arm();\n future.wait();\n },\n\n _maybeFire: function () {\n var self = this;\n if (self.fired)\n throw new Error(\"write fence already activated?\");\n if (self.armed && !self.outstanding_writes) {\n self.fired = true;\n _.each(self.completion_callbacks, function (f) {f(self);});\n self.completion_callbacks = [];\n }\n },\n\n // Deactivate this fence so that adding more writes has no effect.\n // The fence must have already fired.\n retire: function () {\n var self = this;\n if (! self.fired)\n throw new Error(\"Can't retire a fence that hasn't fired.\");\n self.retired = true;\n }\n});\n","// A \"crossbar\" is a class that provides structured notification registration.\n// See _match for the definition of how a notification matches a trigger.\n// All notifications and triggers must have a string key named 'collection'.\n\nDDPServer._Crossbar = function (options) {\n var self = this;\n options = options || {};\n\n self.nextId = 1;\n // map from collection name (string) -> listener id -> object. each object has\n // keys 'trigger', 'callback'.\n self.listenersByCollection = {};\n self.factPackage = options.factPackage || \"livedata\";\n self.factName = options.factName || null;\n};\n\n_.extend(DDPServer._Crossbar.prototype, {\n // Listen for notification that match 'trigger'. A notification\n // matches if it has the key-value pairs in trigger as a\n // subset. When a notification matches, call 'callback', passing\n // the actual notification.\n //\n // Returns a listen handle, which is an object with a method\n // stop(). Call stop() to stop listening.\n //\n // XXX It should be legal to call fire() from inside a listen()\n // callback?\n listen: function (trigger, callback) {\n var self = this;\n var id = self.nextId++;\n\n if (typeof(trigger.collection) !== 'string') {\n throw Error(\"Trigger lacks collection!\");\n }\n\n var collection = trigger.collection; // save in case trigger is mutated\n var record = {trigger: EJSON.clone(trigger), callback: callback};\n if (! _.has(self.listenersByCollection, collection)) {\n self.listenersByCollection[collection] = {};\n }\n self.listenersByCollection[collection][id] = record;\n\n if (self.factName && Package.facts) {\n Package.facts.Facts.incrementServerFact(\n self.factPackage, self.factName, 1);\n }\n\n return {\n stop: function () {\n if (self.factName && Package.facts) {\n Package.facts.Facts.incrementServerFact(\n self.factPackage, self.factName, -1);\n }\n delete self.listenersByCollection[collection][id];\n if (_.isEmpty(self.listenersByCollection[collection])) {\n delete self.listenersByCollection[collection];\n }\n }\n };\n },\n\n // Fire the provided 'notification' (an object whose attribute\n // values are all JSON-compatibile) -- inform all matching listeners\n // (registered with listen()).\n //\n // If fire() is called inside a write fence, then each of the\n // listener callbacks will be called inside the write fence as well.\n //\n // The listeners may be invoked in parallel, rather than serially.\n fire: function (notification) {\n var self = this;\n\n if (typeof(notification.collection) !== 'string') {\n throw Error(\"Notification lacks collection!\");\n }\n\n if (! _.has(self.listenersByCollection, notification.collection))\n return;\n\n var listenersForCollection =\n self.listenersByCollection[notification.collection];\n var callbackIds = [];\n _.each(listenersForCollection, function (l, id) {\n if (self._matches(notification, l.trigger)) {\n callbackIds.push(id);\n }\n });\n\n // Listener callbacks can yield, so we need to first find all the ones that\n // match in a single iteration over self.listenersByCollection (which can't\n // be mutated during this iteration), and then invoke the matching\n // callbacks, checking before each call to ensure they haven't stopped.\n // Note that we don't have to check that\n // self.listenersByCollection[notification.collection] still ===\n // listenersForCollection, because the only way that stops being true is if\n // listenersForCollection first gets reduced down to the empty object (and\n // then never gets increased again).\n _.each(callbackIds, function (id) {\n if (_.has(listenersForCollection, id)) {\n listenersForCollection[id].callback(notification);\n }\n });\n },\n\n // A notification matches a trigger if all keys that exist in both are equal.\n //\n // Examples:\n // N:{collection: \"C\"} matches T:{collection: \"C\"}\n // (a non-targeted write to a collection matches a\n // non-targeted query)\n // N:{collection: \"C\", id: \"X\"} matches T:{collection: \"C\"}\n // (a targeted write to a collection matches a non-targeted query)\n // N:{collection: \"C\"} matches T:{collection: \"C\", id: \"X\"}\n // (a non-targeted write to a collection matches a\n // targeted query)\n // N:{collection: \"C\", id: \"X\"} matches T:{collection: \"C\", id: \"X\"}\n // (a targeted write to a collection matches a targeted query targeted\n // at the same document)\n // N:{collection: \"C\", id: \"X\"} does not match T:{collection: \"C\", id: \"Y\"}\n // (a targeted write to a collection does not match a targeted query\n // targeted at a different document)\n _matches: function (notification, trigger) {\n // Most notifications that use the crossbar have a string `collection` and\n // maybe an `id` that is a string or ObjectID. We're already dividing up\n // triggers by collection, but let's fast-track \"nope, different ID\" (and\n // avoid the overly generic EJSON.equals). This makes a noticeable\n // performance difference; see https://github.com/meteor/meteor/pull/3697\n if (typeof(notification.id) === 'string' &&\n typeof(trigger.id) === 'string' &&\n notification.id !== trigger.id) {\n return false;\n }\n if (notification.id instanceof LocalCollection._ObjectID &&\n trigger.id instanceof LocalCollection._ObjectID &&\n ! notification.id.equals(trigger.id)) {\n return false;\n }\n\n return _.all(trigger, function (triggerValue, key) {\n return !_.has(notification, key) ||\n EJSON.equals(triggerValue, notification[key]);\n });\n }\n});\n\n// The \"invalidation crossbar\" is a specific instance used by the DDP server to\n// implement write fence notifications. Listener callbacks on this crossbar\n// should call beginWrite on the current write fence before they return, if they\n// want to delay the write fence from firing (ie, the DDP method-data-updated\n// message from being sent).\nDDPServer._InvalidationCrossbar = new DDPServer._Crossbar({\n factName: \"invalidation-crossbar-listeners\"\n});\n","// All the supported versions (for both the client and server)\n// These must be in order of preference; most favored-first\nSUPPORTED_DDP_VERSIONS = [ '1', 'pre2', 'pre1' ];\n\nLivedataTest.SUPPORTED_DDP_VERSIONS = SUPPORTED_DDP_VERSIONS;\n\n// Instance name is this because it is usually referred to as this inside a\n// method definition\n/**\n * @summary The state for a single invocation of a method, referenced by this\n * inside a method definition.\n * @param {Object} options\n * @instanceName this\n */\nMethodInvocation = function (options) {\n var self = this;\n\n // true if we're running not the actual method, but a stub (that is,\n // if we're on a client (which may be a browser, or in the future a\n // server connecting to another server) and presently running a\n // simulation of a server-side method for latency compensation\n // purposes). not currently true except in a client such as a browser,\n // since there's usually no point in running stubs unless you have a\n // zero-latency connection to the user.\n\n /**\n * @summary Access inside a method invocation. Boolean value, true if this invocation is a stub.\n * @locus Anywhere\n * @name isSimulation\n * @memberOf MethodInvocation\n * @instance\n * @type {Boolean}\n */\n this.isSimulation = options.isSimulation;\n\n // call this function to allow other method invocations (from the\n // same client) to continue running without waiting for this one to\n // complete.\n this._unblock = options.unblock || function () {};\n this._calledUnblock = false;\n\n // current user id\n\n /**\n * @summary The id of the user that made this method call, or `null` if no user was logged in.\n * @locus Anywhere\n * @name userId\n * @memberOf MethodInvocation\n * @instance\n */\n this.userId = options.userId;\n\n // sets current user id in all appropriate server contexts and\n // reruns subscriptions\n this._setUserId = options.setUserId || function () {};\n\n // On the server, the connection this method call came in on.\n\n /**\n * @summary Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call.\n * @locus Server\n * @name connection\n * @memberOf MethodInvocation\n * @instance\n */\n this.connection = options.connection;\n\n // The seed for randomStream value generation\n this.randomSeed = options.randomSeed;\n\n // This is set by RandomStream.get; and holds the random stream state\n this.randomStream = null;\n};\n\n_.extend(MethodInvocation.prototype, {\n /**\n * @summary Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber.\n * @locus Server\n * @memberOf MethodInvocation\n * @instance\n */\n unblock: function () {\n var self = this;\n self._calledUnblock = true;\n self._unblock();\n },\n\n /**\n * @summary Set the logged in user.\n * @locus Server\n * @memberOf MethodInvocation\n * @instance\n * @param {String | null} userId The value that should be returned by `userId` on this connection.\n */\n setUserId: function(userId) {\n var self = this;\n if (self._calledUnblock)\n throw new Error(\"Can't call setUserId in a method after calling unblock\");\n self.userId = userId;\n self._setUserId(userId);\n }\n});\n\nparseDDP = function (stringMessage) {\n try {\n var msg = JSON.parse(stringMessage);\n } catch (e) {\n Meteor._debug(\"Discarding message with invalid JSON\", stringMessage);\n return null;\n }\n // DDP messages must be objects.\n if (msg === null || typeof msg !== 'object') {\n Meteor._debug(\"Discarding non-object DDP message\", stringMessage);\n return null;\n }\n\n // massage msg to get it into \"abstract ddp\" rather than \"wire ddp\" format.\n\n // switch between \"cleared\" rep of unsetting fields and \"undefined\"\n // rep of same\n if (_.has(msg, 'cleared')) {\n if (!_.has(msg, 'fields'))\n msg.fields = {};\n _.each(msg.cleared, function (clearKey) {\n msg.fields[clearKey] = undefined;\n });\n delete msg.cleared;\n }\n\n _.each(['fields', 'params', 'result'], function (field) {\n if (_.has(msg, field))\n msg[field] = EJSON._adjustTypesFromJSONValue(msg[field]);\n });\n\n return msg;\n};\n\nstringifyDDP = function (msg) {\n var copy = EJSON.clone(msg);\n // swizzle 'changed' messages from 'fields undefined' rep to 'fields\n // and cleared' rep\n if (_.has(msg, 'fields')) {\n var cleared = [];\n _.each(msg.fields, function (value, key) {\n if (value === undefined) {\n cleared.push(key);\n delete copy.fields[key];\n }\n });\n if (!_.isEmpty(cleared))\n copy.cleared = cleared;\n if (_.isEmpty(copy.fields))\n delete copy.fields;\n }\n // adjust types to basic\n _.each(['fields', 'params', 'result'], function (field) {\n if (_.has(copy, field))\n copy[field] = EJSON._adjustTypesToJSONValue(copy[field]);\n });\n if (msg.id && typeof msg.id !== 'string') {\n throw new Error(\"Message id is not a string\");\n }\n return JSON.stringify(copy);\n};\n\n// This is private but it's used in a few places. accounts-base uses\n// it to get the current user. accounts-password uses it to stash SRP\n// state in the DDP session. Meteor.setTimeout and friends clear\n// it. We can probably find a better way to factor this.\nDDP._CurrentInvocation = new Meteor.EnvironmentVariable;\n","// RandomStream allows for generation of pseudo-random values, from a seed.\n//\n// We use this for consistent 'random' numbers across the client and server.\n// We want to generate probably-unique IDs on the client, and we ideally want\n// the server to generate the same IDs when it executes the method.\n//\n// For generated values to be the same, we must seed ourselves the same way,\n// and we must keep track of the current state of our pseudo-random generators.\n// We call this state the scope. By default, we use the current DDP method\n// invocation as our scope. DDP now allows the client to specify a randomSeed.\n// If a randomSeed is provided it will be used to seed our random sequences.\n// In this way, client and server method calls will generate the same values.\n//\n// We expose multiple named streams; each stream is independent\n// and is seeded differently (but predictably from the name).\n// By using multiple streams, we support reordering of requests,\n// as long as they occur on different streams.\n//\n// @param options {Optional Object}\n// seed: Array or value - Seed value(s) for the generator.\n// If an array, will be used as-is\n// If a value, will be converted to a single-value array\n// If omitted, a random array will be used as the seed.\nRandomStream = function (options) {\n var self = this;\n\n this.seed = [].concat(options.seed || randomToken());\n\n this.sequences = {};\n};\n\n// Returns a random string of sufficient length for a random seed.\n// This is a placeholder function; a similar function is planned\n// for Random itself; when that is added we should remove this function,\n// and call Random's randomToken instead.\nfunction randomToken() {\n return Random.hexString(20);\n};\n\n// Returns the random stream with the specified name, in the specified scope.\n// If scope is null (or otherwise falsey) then we will use Random, which will\n// give us as random numbers as possible, but won't produce the same\n// values across client and server.\n// However, scope will normally be the current DDP method invocation, so\n// we'll use the stream with the specified name, and we should get consistent\n// values on the client and server sides of a method call.\nRandomStream.get = function (scope, name) {\n if (!name) {\n name = \"default\";\n }\n if (!scope) {\n // There was no scope passed in;\n // the sequence won't actually be reproducible.\n return Random;\n }\n var randomStream = scope.randomStream;\n if (!randomStream) {\n scope.randomStream = randomStream = new RandomStream({\n seed: scope.randomSeed\n });\n }\n return randomStream._sequence(name);\n};\n\n// Returns the named sequence of pseudo-random values.\n// The scope will be DDP._CurrentInvocation.get(), so the stream will produce\n// consistent values for method calls on the client and server.\nDDP.randomStream = function (name) {\n var scope = DDP._CurrentInvocation.get();\n return RandomStream.get(scope, name);\n};\n\n// Creates a randomSeed for passing to a method call.\n// Note that we take enclosing as an argument,\n// though we expect it to be DDP._CurrentInvocation.get()\n// However, we often evaluate makeRpcSeed lazily, and thus the relevant\n// invocation may not be the one currently in scope.\n// If enclosing is null, we'll use Random and values won't be repeatable.\nmakeRpcSeed = function (enclosing, methodName) {\n var stream = RandomStream.get(enclosing, '/rpc/' + methodName);\n return stream.hexString(20);\n};\n\n_.extend(RandomStream.prototype, {\n // Get a random sequence with the specified name, creating it if does not exist.\n // New sequences are seeded with the seed concatenated with the name.\n // By passing a seed into Random.create, we use the Alea generator.\n _sequence: function (name) {\n var self = this;\n\n var sequence = self.sequences[name] || null;\n if (sequence === null) {\n var sequenceSeed = self.seed.concat(name);\n for (var i = 0; i < sequenceSeed.length; i++) {\n if (_.isFunction(sequenceSeed[i])) {\n sequenceSeed[i] = sequenceSeed[i]();\n }\n }\n self.sequences[name] = sequence = Random.createWithSeeds.apply(null, sequenceSeed);\n }\n return sequence;\n }\n});\n","if (Meteor.isServer) {\n var path = Npm.require('path');\n var Fiber = Npm.require('fibers');\n var Future = Npm.require(path.join('fibers', 'future'));\n}\n\n// @param url {String|Object} URL to Meteor app,\n// or an object as a test hook (see code)\n// Options:\n// reloadWithOutstanding: is it OK to reload if there are outstanding methods?\n// headers: extra headers to send on the websockets connection, for\n// server-to-server DDP only\n// _sockjsOptions: Specifies options to pass through to the sockjs client\n// onDDPNegotiationVersionFailure: callback when version negotiation fails.\n//\n// XXX There should be a way to destroy a DDP connection, causing all\n// outstanding method calls to fail.\n//\n// XXX Our current way of handling failure and reconnection is great\n// for an app (where we want to tolerate being disconnected as an\n// expect state, and keep trying forever to reconnect) but cumbersome\n// for something like a command line tool that wants to make a\n// connection, call a method, and print an error if connection\n// fails. We should have better usability in the latter case (while\n// still transparently reconnecting if it's just a transient failure\n// or the server migrating us).\nvar Connection = function (url, options) {\n var self = this;\n options = _.extend({\n onConnected: function () {},\n onDDPVersionNegotiationFailure: function (description) {\n Meteor._debug(description);\n },\n heartbeatInterval: 35000,\n heartbeatTimeout: 15000,\n // These options are only for testing.\n reloadWithOutstanding: false,\n supportedDDPVersions: SUPPORTED_DDP_VERSIONS,\n retry: true,\n respondToPings: true\n }, options);\n\n // If set, called when we reconnect, queuing method calls _before_ the\n // existing outstanding ones. This is the only data member that is part of the\n // public API!\n self.onReconnect = null;\n\n // as a test hook, allow passing a stream instead of a url.\n if (typeof url === \"object\") {\n self._stream = url;\n } else {\n self._stream = new LivedataTest.ClientStream(url, {\n retry: options.retry,\n headers: options.headers,\n _sockjsOptions: options._sockjsOptions,\n // Used to keep some tests quiet, or for other cases in which\n // the right thing to do with connection errors is to silently\n // fail (e.g. sending package usage stats). At some point we\n // should have a real API for handling client-stream-level\n // errors.\n _dontPrintErrors: options._dontPrintErrors,\n connectTimeoutMs: options.connectTimeoutMs\n });\n }\n\n self._lastSessionId = null;\n self._versionSuggestion = null; // The last proposed DDP version.\n self._version = null; // The DDP version agreed on by client and server.\n self._stores = {}; // name -> object with methods\n self._methodHandlers = {}; // name -> func\n self._nextMethodId = 1;\n self._supportedDDPVersions = options.supportedDDPVersions;\n\n self._heartbeatInterval = options.heartbeatInterval;\n self._heartbeatTimeout = options.heartbeatTimeout;\n\n // Tracks methods which the user has tried to call but which have not yet\n // called their user callback (ie, they are waiting on their result or for all\n // of their writes to be written to the local cache). Map from method ID to\n // MethodInvoker object.\n self._methodInvokers = {};\n\n // Tracks methods which the user has called but whose result messages have not\n // arrived yet.\n //\n // _outstandingMethodBlocks is an array of blocks of methods. Each block\n // represents a set of methods that can run at the same time. The first block\n // represents the methods which are currently in flight; subsequent blocks\n // must wait for previous blocks to be fully finished before they can be sent\n // to the server.\n //\n // Each block is an object with the following fields:\n // - methods: a list of MethodInvoker objects\n // - wait: a boolean; if true, this block had a single method invoked with\n // the \"wait\" option\n //\n // There will never be adjacent blocks with wait=false, because the only thing\n // that makes methods need to be serialized is a wait method.\n //\n // Methods are removed from the first block when their \"result\" is\n // received. The entire first block is only removed when all of the in-flight\n // methods have received their results (so the \"methods\" list is empty) *AND*\n // all of the data written by those methods are visible in the local cache. So\n // it is possible for the first block's methods list to be empty, if we are\n // still waiting for some objects to quiesce.\n //\n // Example:\n // _outstandingMethodBlocks = [\n // {wait: false, methods: []},\n // {wait: true, methods: [<MethodInvoker for 'login'>]},\n // {wait: false, methods: [<MethodInvoker for 'foo'>,\n // <MethodInvoker for 'bar'>]}]\n // This means that there were some methods which were sent to the server and\n // which have returned their results, but some of the data written by\n // the methods may not be visible in the local cache. Once all that data is\n // visible, we will send a 'login' method. Once the login method has returned\n // and all the data is visible (including re-running subs if userId changes),\n // we will send the 'foo' and 'bar' methods in parallel.\n self._outstandingMethodBlocks = [];\n\n // method ID -> array of objects with keys 'collection' and 'id', listing\n // documents written by a given method's stub. keys are associated with\n // methods whose stub wrote at least one document, and whose data-done message\n // has not yet been received.\n self._documentsWrittenByStub = {};\n // collection -> IdMap of \"server document\" object. A \"server document\" has:\n // - \"document\": the version of the document according the\n // server (ie, the snapshot before a stub wrote it, amended by any changes\n // received from the server)\n // It is undefined if we think the document does not exist\n // - \"writtenByStubs\": a set of method IDs whose stubs wrote to the document\n // whose \"data done\" messages have not yet been processed\n self._serverDocuments = {};\n\n // Array of callbacks to be called after the next update of the local\n // cache. Used for:\n // - Calling methodInvoker.dataVisible and sub ready callbacks after\n // the relevant data is flushed.\n // - Invoking the callbacks of \"half-finished\" methods after reconnect\n // quiescence. Specifically, methods whose result was received over the old\n // connection (so we don't re-send it) but whose data had not been made\n // visible.\n self._afterUpdateCallbacks = [];\n\n // In two contexts, we buffer all incoming data messages and then process them\n // all at once in a single update:\n // - During reconnect, we buffer all data messages until all subs that had\n // been ready before reconnect are ready again, and all methods that are\n // active have returned their \"data done message\"; then\n // - During the execution of a \"wait\" method, we buffer all data messages\n // until the wait method gets its \"data done\" message. (If the wait method\n // occurs during reconnect, it doesn't get any special handling.)\n // all data messages are processed in one update.\n //\n // The following fields are used for this \"quiescence\" process.\n\n // This buffers the messages that aren't being processed yet.\n self._messagesBufferedUntilQuiescence = [];\n // Map from method ID -> true. Methods are removed from this when their\n // \"data done\" message is received, and we will not quiesce until it is\n // empty.\n self._methodsBlockingQuiescence = {};\n // map from sub ID -> true for subs that were ready (ie, called the sub\n // ready callback) before reconnect but haven't become ready again yet\n self._subsBeingRevived = {}; // map from sub._id -> true\n // if true, the next data update should reset all stores. (set during\n // reconnect.)\n self._resetStores = false;\n\n // name -> array of updates for (yet to be created) collections\n self._updatesForUnknownStores = {};\n // if we're blocking a migration, the retry func\n self._retryMigrate = null;\n\n // metadata for subscriptions. Map from sub ID to object with keys:\n // - id\n // - name\n // - params\n // - inactive (if true, will be cleaned up if not reused in re-run)\n // - ready (has the 'ready' message been received?)\n // - readyCallback (an optional callback to call when ready)\n // - errorCallback (an optional callback to call if the sub terminates with\n // an error, XXX COMPAT WITH 1.0.3.1)\n // - stopCallback (an optional callback to call when the sub terminates\n // for any reason, with an error argument if an error triggered the stop)\n self._subscriptions = {};\n\n // Reactive userId.\n self._userId = null;\n self._userIdDeps = new Tracker.Dependency;\n\n // Block auto-reload while we're waiting for method responses.\n if (Meteor.isClient && Package.reload && !options.reloadWithOutstanding) {\n Package.reload.Reload._onMigrate(function (retry) {\n if (!self._readyToMigrate()) {\n if (self._retryMigrate)\n throw new Error(\"Two migrations in progress?\");\n self._retryMigrate = retry;\n return false;\n } else {\n return [true];\n }\n });\n }\n\n var onMessage = function (raw_msg) {\n try {\n var msg = parseDDP(raw_msg);\n } catch (e) {\n Meteor._debug(\"Exception while parsing DDP\", e);\n return;\n }\n\n if (msg === null || !msg.msg) {\n // XXX COMPAT WITH 0.6.6. ignore the old welcome message for back\n // compat. Remove this 'if' once the server stops sending welcome\n // messages (stream_server.js).\n if (! (msg && msg.server_id))\n Meteor._debug(\"discarding invalid livedata message\", msg);\n return;\n }\n\n if (msg.msg === 'connected') {\n self._version = self._versionSuggestion;\n self._livedata_connected(msg);\n options.onConnected();\n }\n else if (msg.msg == 'failed') {\n if (_.contains(self._supportedDDPVersions, msg.version)) {\n self._versionSuggestion = msg.version;\n self._stream.reconnect({_force: true});\n } else {\n var description =\n \"DDP version negotiation failed; server requested version \" + msg.version;\n self._stream.disconnect({_permanent: true, _error: description});\n options.onDDPVersionNegotiationFailure(description);\n }\n }\n else if (msg.msg === 'ping') {\n if (options.respondToPings)\n self._send({msg: \"pong\", id: msg.id});\n if (self._heartbeat)\n self._heartbeat.pingReceived();\n }\n else if (msg.msg === 'pong') {\n if (self._heartbeat) {\n self._heartbeat.pongReceived();\n }\n }\n else if (_.include(['added', 'changed', 'removed', 'ready', 'updated'], msg.msg))\n self._livedata_data(msg);\n else if (msg.msg === 'nosub')\n self._livedata_nosub(msg);\n else if (msg.msg === 'result')\n self._livedata_result(msg);\n else if (msg.msg === 'error')\n self._livedata_error(msg);\n else\n Meteor._debug(\"discarding unknown livedata message type\", msg);\n };\n\n var onReset = function () {\n // Send a connect message at the beginning of the stream.\n // NOTE: reset is called even on the first connection, so this is\n // the only place we send this message.\n var msg = {msg: 'connect'};\n if (self._lastSessionId)\n msg.session = self._lastSessionId;\n msg.version = self._versionSuggestion || self._supportedDDPVersions[0];\n self._versionSuggestion = msg.version;\n msg.support = self._supportedDDPVersions;\n self._send(msg);\n\n // Now, to minimize setup latency, go ahead and blast out all of\n // our pending methods ands subscriptions before we've even taken\n // the necessary RTT to know if we successfully reconnected. (1)\n // They're supposed to be idempotent; (2) even if we did\n // reconnect, we're not sure what messages might have gotten lost\n // (in either direction) since we were disconnected (TCP being\n // sloppy about that.)\n\n // If the current block of methods all got their results (but didn't all get\n // their data visible), discard the empty block now.\n if (! _.isEmpty(self._outstandingMethodBlocks) &&\n _.isEmpty(self._outstandingMethodBlocks[0].methods)) {\n self._outstandingMethodBlocks.shift();\n }\n\n // Mark all messages as unsent, they have not yet been sent on this\n // connection.\n _.each(self._methodInvokers, function (m) {\n m.sentMessage = false;\n });\n\n // If an `onReconnect` handler is set, call it first. Go through\n // some hoops to ensure that methods that are called from within\n // `onReconnect` get executed _before_ ones that were originally\n // outstanding (since `onReconnect` is used to re-establish auth\n // certificates)\n if (self.onReconnect)\n self._callOnReconnectAndSendAppropriateOutstandingMethods();\n else\n self._sendOutstandingMethods();\n\n // add new subscriptions at the end. this way they take effect after\n // the handlers and we don't see flicker.\n _.each(self._subscriptions, function (sub, id) {\n self._send({\n msg: 'sub',\n id: id,\n name: sub.name,\n params: sub.params\n });\n });\n };\n\n var onDisconnect = function () {\n if (self._heartbeat) {\n self._heartbeat.stop();\n self._heartbeat = null;\n }\n };\n\n if (Meteor.isServer) {\n self._stream.on('message', Meteor.bindEnvironment(onMessage, Meteor._debug));\n self._stream.on('reset', Meteor.bindEnvironment(onReset, Meteor._debug));\n self._stream.on('disconnect', Meteor.bindEnvironment(onDisconnect, Meteor._debug));\n } else {\n self._stream.on('message', onMessage);\n self._stream.on('reset', onReset);\n self._stream.on('disconnect', onDisconnect);\n }\n};\n\n// A MethodInvoker manages sending a method to the server and calling the user's\n// callbacks. On construction, it registers itself in the connection's\n// _methodInvokers map; it removes itself once the method is fully finished and\n// the callback is invoked. This occurs when it has both received a result,\n// and the data written by it is fully visible.\nvar MethodInvoker = function (options) {\n var self = this;\n\n // Public (within this file) fields.\n self.methodId = options.methodId;\n self.sentMessage = false;\n\n self._callback = options.callback;\n self._connection = options.connection;\n self._message = options.message;\n self._onResultReceived = options.onResultReceived || function () {};\n self._wait = options.wait;\n self._methodResult = null;\n self._dataVisible = false;\n\n // Register with the connection.\n self._connection._methodInvokers[self.methodId] = self;\n};\n_.extend(MethodInvoker.prototype, {\n // Sends the method message to the server. May be called additional times if\n // we lose the connection and reconnect before receiving a result.\n sendMessage: function () {\n var self = this;\n // This function is called before sending a method (including resending on\n // reconnect). We should only (re)send methods where we don't already have a\n // result!\n if (self.gotResult())\n throw new Error(\"sendingMethod is called on method with result\");\n\n // If we're re-sending it, it doesn't matter if data was written the first\n // time.\n self._dataVisible = false;\n\n self.sentMessage = true;\n\n // If this is a wait method, make all data messages be buffered until it is\n // done.\n if (self._wait)\n self._connection._methodsBlockingQuiescence[self.methodId] = true;\n\n // Actually send the message.\n self._connection._send(self._message);\n },\n // Invoke the callback, if we have both a result and know that all data has\n // been written to the local cache.\n _maybeInvokeCallback: function () {\n var self = this;\n if (self._methodResult && self._dataVisible) {\n // Call the callback. (This won't throw: the callback was wrapped with\n // bindEnvironment.)\n self._callback(self._methodResult[0], self._methodResult[1]);\n\n // Forget about this method.\n delete self._connection._methodInvokers[self.methodId];\n\n // Let the connection know that this method is finished, so it can try to\n // move on to the next block of methods.\n self._connection._outstandingMethodFinished();\n }\n },\n // Call with the result of the method from the server. Only may be called\n // once; once it is called, you should not call sendMessage again.\n // If the user provided an onResultReceived callback, call it immediately.\n // Then invoke the main callback if data is also visible.\n receiveResult: function (err, result) {\n var self = this;\n if (self.gotResult())\n throw new Error(\"Methods should only receive results once\");\n self._methodResult = [err, result];\n self._onResultReceived(err, result);\n self._maybeInvokeCallback();\n },\n // Call this when all data written by the method is visible. This means that\n // the method has returns its \"data is done\" message *AND* all server\n // documents that are buffered at that time have been written to the local\n // cache. Invokes the main callback if the result has been received.\n dataVisible: function () {\n var self = this;\n self._dataVisible = true;\n self._maybeInvokeCallback();\n },\n // True if receiveResult has been called.\n gotResult: function () {\n var self = this;\n return !!self._methodResult;\n }\n});\n\n_.extend(Connection.prototype, {\n // 'name' is the name of the data on the wire that should go in the\n // store. 'wrappedStore' should be an object with methods beginUpdate, update,\n // endUpdate, saveOriginals, retrieveOriginals. see Collection for an example.\n registerStore: function (name, wrappedStore) {\n var self = this;\n\n if (name in self._stores)\n return false;\n\n // Wrap the input object in an object which makes any store method not\n // implemented by 'store' into a no-op.\n var store = {};\n _.each(['update', 'beginUpdate', 'endUpdate', 'saveOriginals',\n 'retrieveOriginals'], function (method) {\n store[method] = function () {\n return (wrappedStore[method]\n ? wrappedStore[method].apply(wrappedStore, arguments)\n : undefined);\n };\n });\n\n self._stores[name] = store;\n\n var queued = self._updatesForUnknownStores[name];\n if (queued) {\n store.beginUpdate(queued.length, false);\n _.each(queued, function (msg) {\n store.update(msg);\n });\n store.endUpdate();\n delete self._updatesForUnknownStores[name];\n }\n\n return true;\n },\n\n /**\n * @memberOf Meteor\n * @summary Subscribe to a record set. Returns a handle that provides\n * `stop()` and `ready()` methods.\n * @locus Client\n * @param {String} name Name of the subscription. Matches the name of the\n * server's `publish()` call.\n * @param {Any} [arg1,arg2...] Optional arguments passed to publisher\n * function on server.\n * @param {Function|Object} [callbacks] Optional. May include `onStop`\n * and `onReady` callbacks. If there is an error, it is passed as an\n * argument to `onStop`. If a function is passed instead of an object, it\n * is interpreted as an `onReady` callback.\n */\n subscribe: function (name /* .. [arguments] .. (callback|callbacks) */) {\n var self = this;\n\n var params = Array.prototype.slice.call(arguments, 1);\n var callbacks = {};\n if (params.length) {\n var lastParam = params[params.length - 1];\n if (_.isFunction(lastParam)) {\n callbacks.onReady = params.pop();\n } else if (lastParam &&\n // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use\n // onStop with an error callback instead.\n _.any([lastParam.onReady, lastParam.onError, lastParam.onStop],\n _.isFunction)) {\n callbacks = params.pop();\n }\n }\n\n // Is there an existing sub with the same name and param, run in an\n // invalidated Computation? This will happen if we are rerunning an\n // existing computation.\n //\n // For example, consider a rerun of:\n //\n // Tracker.autorun(function () {\n // Meteor.subscribe(\"foo\", Session.get(\"foo\"));\n // Meteor.subscribe(\"bar\", Session.get(\"bar\"));\n // });\n //\n // If \"foo\" has changed but \"bar\" has not, we will match the \"bar\"\n // subcribe to an existing inactive subscription in order to not\n // unsub and resub the subscription unnecessarily.\n //\n // We only look for one such sub; if there are N apparently-identical subs\n // being invalidated, we will require N matching subscribe calls to keep\n // them all active.\n var existing = _.find(self._subscriptions, function (sub) {\n return sub.inactive && sub.name === name &&\n EJSON.equals(sub.params, params);\n });\n\n var id;\n if (existing) {\n id = existing.id;\n existing.inactive = false; // reactivate\n\n if (callbacks.onReady) {\n // If the sub is not already ready, replace any ready callback with the\n // one provided now. (It's not really clear what users would expect for\n // an onReady callback inside an autorun; the semantics we provide is\n // that at the time the sub first becomes ready, we call the last\n // onReady callback provided, if any.)\n if (!existing.ready)\n existing.readyCallback = callbacks.onReady;\n }\n\n // XXX COMPAT WITH 1.0.3.1 we used to have onError but now we call\n // onStop with an optional error argument\n if (callbacks.onError) {\n // Replace existing callback if any, so that errors aren't\n // double-reported.\n existing.errorCallback = callbacks.onError;\n }\n\n if (callbacks.onStop) {\n existing.stopCallback = callbacks.onStop;\n }\n } else {\n // New sub! Generate an id, save it locally, and send message.\n id = Random.id();\n self._subscriptions[id] = {\n id: id,\n name: name,\n params: EJSON.clone(params),\n inactive: false,\n ready: false,\n readyDeps: new Tracker.Dependency,\n readyCallback: callbacks.onReady,\n // XXX COMPAT WITH 1.0.3.1 #errorCallback\n errorCallback: callbacks.onError,\n stopCallback: callbacks.onStop,\n connection: self,\n remove: function() {\n delete this.connection._subscriptions[this.id];\n this.ready && this.readyDeps.changed();\n },\n stop: function() {\n this.connection._send({msg: 'unsub', id: id});\n this.remove();\n\n if (callbacks.onStop) {\n callbacks.onStop();\n }\n }\n };\n self._send({msg: 'sub', id: id, name: name, params: params});\n }\n\n // return a handle to the application.\n var handle = {\n stop: function () {\n if (!_.has(self._subscriptions, id))\n return;\n\n self._subscriptions[id].stop();\n },\n ready: function () {\n // return false if we've unsubscribed.\n if (!_.has(self._subscriptions, id))\n return false;\n var record = self._subscriptions[id];\n record.readyDeps.depend();\n return record.ready;\n },\n subscriptionId: id\n };\n\n if (Tracker.active) {\n // We're in a reactive computation, so we'd like to unsubscribe when the\n // computation is invalidated... but not if the rerun just re-subscribes\n // to the same subscription! When a rerun happens, we use onInvalidate\n // as a change to mark the subscription \"inactive\" so that it can\n // be reused from the rerun. If it isn't reused, it's killed from\n // an afterFlush.\n Tracker.onInvalidate(function (c) {\n if (_.has(self._subscriptions, id))\n self._subscriptions[id].inactive = true;\n\n Tracker.afterFlush(function () {\n if (_.has(self._subscriptions, id) &&\n self._subscriptions[id].inactive)\n handle.stop();\n });\n });\n }\n\n return handle;\n },\n\n // options:\n // - onLateError {Function(error)} called if an error was received after the ready event.\n // (errors received before ready cause an error to be thrown)\n _subscribeAndWait: function (name, args, options) {\n var self = this;\n var f = new Future();\n var ready = false;\n var handle;\n args = args || [];\n args.push({\n onReady: function () {\n ready = true;\n f['return']();\n },\n onError: function (e) {\n if (!ready)\n f['throw'](e);\n else\n options && options.onLateError && options.onLateError(e);\n }\n });\n\n handle = self.subscribe.apply(self, [name].concat(args));\n f.wait();\n return handle;\n },\n\n methods: function (methods) {\n var self = this;\n _.each(methods, function (func, name) {\n if (self._methodHandlers[name])\n throw new Error(\"A method named '\" + name + \"' is already defined\");\n self._methodHandlers[name] = func;\n });\n },\n\n /**\n * @memberOf Meteor\n * @summary Invokes a method passing any number of arguments.\n * @locus Anywhere\n * @param {String} name Name of method to invoke\n * @param {EJSONable} [arg1,arg2...] Optional method arguments\n * @param {Function} [asyncCallback] Optional callback, which is called asynchronously with the error or result after the method is complete. If not provided, the method runs synchronously if possible (see below).\n */\n call: function (name /* .. [arguments] .. callback */) {\n // if it's a function, the last argument is the result callback,\n // not a parameter to the remote method.\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length && typeof args[args.length - 1] === \"function\")\n var callback = args.pop();\n return this.apply(name, args, callback);\n },\n\n // @param options {Optional Object}\n // wait: Boolean - Should we wait to call this until all current methods\n // are fully finished, and block subsequent method calls\n // until this method is fully finished?\n // (does not affect methods called from within this method)\n // onResultReceived: Function - a callback to call as soon as the method\n // result is received. the data written by\n // the method may not yet be in the cache!\n // returnStubValue: Boolean - If true then in cases where we would have\n // otherwise discarded the stub's return value\n // and returned undefined, instead we go ahead\n // and return it. Specifically, this is any\n // time other than when (a) we are already\n // inside a stub or (b) we are in Node and no\n // callback was provided. Currently we require\n // this flag to be explicitly passed to reduce\n // the likelihood that stub return values will\n // be confused with server return values; we\n // may improve this in future.\n // @param callback {Optional Function}\n\n /**\n * @memberOf Meteor\n * @summary Invoke a method passing an array of arguments.\n * @locus Anywhere\n * @param {String} name Name of method to invoke\n * @param {EJSONable[]} args Method arguments\n * @param {Object} [options]\n * @param {Boolean} options.wait (Client only) If true, don't send this method until all previous method calls have completed, and don't send any subsequent method calls until this one is completed.\n * @param {Function} options.onResultReceived (Client only) This callback is invoked with the error or result of the method (just like `asyncCallback`) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method.\n * @param {Function} [asyncCallback] Optional callback; same semantics as in [`Meteor.call`](#meteor_call).\n */\n apply: function (name, args, options, callback) {\n var self = this;\n\n // We were passed 3 arguments. They may be either (name, args, options)\n // or (name, args, callback)\n if (!callback && typeof options === 'function') {\n callback = options;\n options = {};\n }\n options = options || {};\n\n if (callback) {\n // XXX would it be better form to do the binding in stream.on,\n // or caller, instead of here?\n // XXX improve error message (and how we report it)\n callback = Meteor.bindEnvironment(\n callback,\n \"delivering result of invoking '\" + name + \"'\"\n );\n }\n\n // Keep our args safe from mutation (eg if we don't send the message for a\n // while because of a wait method).\n args = EJSON.clone(args);\n\n // Lazily allocate method ID once we know that it'll be needed.\n var methodId = (function () {\n var id;\n return function () {\n if (id === undefined)\n id = '' + (self._nextMethodId++);\n return id;\n };\n })();\n\n var enclosing = DDP._CurrentInvocation.get();\n var alreadyInSimulation = enclosing && enclosing.isSimulation;\n\n // Lazily generate a randomSeed, only if it is requested by the stub.\n // The random streams only have utility if they're used on both the client\n // and the server; if the client doesn't generate any 'random' values\n // then we don't expect the server to generate any either.\n // Less commonly, the server may perform different actions from the client,\n // and may in fact generate values where the client did not, but we don't\n // have any client-side values to match, so even here we may as well just\n // use a random seed on the server. In that case, we don't pass the\n // randomSeed to save bandwidth, and we don't even generate it to save a\n // bit of CPU and to avoid consuming entropy.\n var randomSeed = null;\n var randomSeedGenerator = function () {\n if (randomSeed === null) {\n randomSeed = makeRpcSeed(enclosing, name);\n }\n return randomSeed;\n };\n\n // Run the stub, if we have one. The stub is supposed to make some\n // temporary writes to the database to give the user a smooth experience\n // until the actual result of executing the method comes back from the\n // server (whereupon the temporary writes to the database will be reversed\n // during the beginUpdate/endUpdate process.)\n //\n // Normally, we ignore the return value of the stub (even if it is an\n // exception), in favor of the real return value from the server. The\n // exception is if the *caller* is a stub. In that case, we're not going\n // to do a RPC, so we use the return value of the stub as our return\n // value.\n\n var stub = self._methodHandlers[name];\n if (stub) {\n var setUserId = function(userId) {\n self.setUserId(userId);\n };\n\n var invocation = new MethodInvocation({\n isSimulation: true,\n userId: self.userId(),\n setUserId: setUserId,\n randomSeed: function () { return randomSeedGenerator(); }\n });\n\n if (!alreadyInSimulation)\n self._saveOriginals();\n\n try {\n // Note that unlike in the corresponding server code, we never audit\n // that stubs check() their arguments.\n var stubReturnValue = DDP._CurrentInvocation.withValue(invocation, function () {\n if (Meteor.isServer) {\n // Because saveOriginals and retrieveOriginals aren't reentrant,\n // don't allow stubs to yield.\n return Meteor._noYieldsAllowed(function () {\n // re-clone, so that the stub can't affect our caller's values\n return stub.apply(invocation, EJSON.clone(args));\n });\n } else {\n return stub.apply(invocation, EJSON.clone(args));\n }\n });\n }\n catch (e) {\n var exception = e;\n }\n\n if (!alreadyInSimulation)\n self._retrieveAndStoreOriginals(methodId());\n }\n\n // If we're in a simulation, stop and return the result we have,\n // rather than going on to do an RPC. If there was no stub,\n // we'll end up returning undefined.\n if (alreadyInSimulation) {\n if (callback) {\n callback(exception, stubReturnValue);\n return undefined;\n }\n if (exception)\n throw exception;\n return stubReturnValue;\n }\n\n // If an exception occurred in a stub, and we're ignoring it\n // because we're doing an RPC and want to use what the server\n // returns instead, log it so the developer knows.\n //\n // Tests can set the 'expected' flag on an exception so it won't\n // go to log.\n if (exception && !exception.expected) {\n Meteor._debug(\"Exception while simulating the effect of invoking '\" +\n name + \"'\", exception, exception.stack);\n }\n\n\n // At this point we're definitely doing an RPC, and we're going to\n // return the value of the RPC to the caller.\n\n // If the caller didn't give a callback, decide what to do.\n if (!callback) {\n if (Meteor.isClient) {\n // On the client, we don't have fibers, so we can't block. The\n // only thing we can do is to return undefined and discard the\n // result of the RPC. If an error occurred then print the error\n // to the console.\n callback = function (err) {\n err && Meteor._debug(\"Error invoking Method '\" + name + \"':\",\n err.message);\n };\n } else {\n // On the server, make the function synchronous. Throw on\n // errors, return on success.\n var future = new Future;\n callback = future.resolver();\n }\n }\n // Send the RPC. Note that on the client, it is important that the\n // stub have finished before we send the RPC, so that we know we have\n // a complete list of which local documents the stub wrote.\n var message = {\n msg: 'method',\n method: name,\n params: args,\n id: methodId()\n };\n\n // Send the randomSeed only if we used it\n if (randomSeed !== null) {\n message.randomSeed = randomSeed;\n }\n\n var methodInvoker = new MethodInvoker({\n methodId: methodId(),\n callback: callback,\n connection: self,\n onResultReceived: options.onResultReceived,\n wait: !!options.wait,\n message: message\n });\n\n if (options.wait) {\n // It's a wait method! Wait methods go in their own block.\n self._outstandingMethodBlocks.push(\n {wait: true, methods: [methodInvoker]});\n } else {\n // Not a wait method. Start a new block if the previous block was a wait\n // block, and add it to the last block of methods.\n if (_.isEmpty(self._outstandingMethodBlocks) ||\n _.last(self._outstandingMethodBlocks).wait)\n self._outstandingMethodBlocks.push({wait: false, methods: []});\n _.last(self._outstandingMethodBlocks).methods.push(methodInvoker);\n }\n\n // If we added it to the first block, send it out now.\n if (self._outstandingMethodBlocks.length === 1)\n methodInvoker.sendMessage();\n\n // If we're using the default callback on the server,\n // block waiting for the result.\n if (future) {\n return future.wait();\n }\n return options.returnStubValue ? stubReturnValue : undefined;\n },\n\n // Before calling a method stub, prepare all stores to track changes and allow\n // _retrieveAndStoreOriginals to get the original versions of changed\n // documents.\n _saveOriginals: function () {\n var self = this;\n _.each(self._stores, function (s) {\n s.saveOriginals();\n });\n },\n // Retrieves the original versions of all documents modified by the stub for\n // method 'methodId' from all stores and saves them to _serverDocuments (keyed\n // by document) and _documentsWrittenByStub (keyed by method ID).\n _retrieveAndStoreOriginals: function (methodId) {\n var self = this;\n if (self._documentsWrittenByStub[methodId])\n throw new Error(\"Duplicate methodId in _retrieveAndStoreOriginals\");\n\n var docsWritten = [];\n _.each(self._stores, function (s, collection) {\n var originals = s.retrieveOriginals();\n // not all stores define retrieveOriginals\n if (!originals)\n return;\n originals.forEach(function (doc, id) {\n docsWritten.push({collection: collection, id: id});\n if (!_.has(self._serverDocuments, collection))\n self._serverDocuments[collection] = new LocalCollection._IdMap;\n var serverDoc = self._serverDocuments[collection].setDefault(id, {});\n if (serverDoc.writtenByStubs) {\n // We're not the first stub to write this doc. Just add our method ID\n // to the record.\n serverDoc.writtenByStubs[methodId] = true;\n } else {\n // First stub! Save the original value and our method ID.\n serverDoc.document = doc;\n serverDoc.flushCallbacks = [];\n serverDoc.writtenByStubs = {};\n serverDoc.writtenByStubs[methodId] = true;\n }\n });\n });\n if (!_.isEmpty(docsWritten)) {\n self._documentsWrittenByStub[methodId] = docsWritten;\n }\n },\n\n // This is very much a private function we use to make the tests\n // take up fewer server resources after they complete.\n _unsubscribeAll: function () {\n var self = this;\n _.each(_.clone(self._subscriptions), function (sub, id) {\n // Avoid killing the autoupdate subscription so that developers\n // still get hot code pushes when writing tests.\n //\n // XXX it's a hack to encode knowledge about autoupdate here,\n // but it doesn't seem worth it yet to have a special API for\n // subscriptions to preserve after unit tests.\n if (sub.name !== 'meteor_autoupdate_clientVersions') {\n self._subscriptions[id].stop();\n }\n });\n },\n\n // Sends the DDP stringification of the given message object\n _send: function (obj) {\n var self = this;\n self._stream.send(stringifyDDP(obj));\n },\n\n // We detected via DDP-level heartbeats that we've lost the\n // connection. Unlike `disconnect` or `close`, a lost connection\n // will be automatically retried.\n _lostConnection: function (error) {\n var self = this;\n self._stream._lostConnection(error);\n },\n\n /**\n * @summary Get the current connection status. A reactive data source.\n * @locus Client\n * @memberOf Meteor\n */\n status: function (/*passthrough args*/) {\n var self = this;\n return self._stream.status.apply(self._stream, arguments);\n },\n\n /**\n * @summary Force an immediate reconnection attempt if the client is not connected to the server.\n\n This method does nothing if the client is already connected.\n * @locus Client\n * @memberOf Meteor\n */\n reconnect: function (/*passthrough args*/) {\n var self = this;\n return self._stream.reconnect.apply(self._stream, arguments);\n },\n\n /**\n * @summary Disconnect the client from the server.\n * @locus Client\n * @memberOf Meteor\n */\n disconnect: function (/*passthrough args*/) {\n var self = this;\n return self._stream.disconnect.apply(self._stream, arguments);\n },\n\n close: function () {\n var self = this;\n return self._stream.disconnect({_permanent: true});\n },\n\n ///\n /// Reactive user system\n ///\n userId: function () {\n var self = this;\n if (self._userIdDeps)\n self._userIdDeps.depend();\n return self._userId;\n },\n\n setUserId: function (userId) {\n var self = this;\n // Avoid invalidating dependents if setUserId is called with current value.\n if (self._userId === userId)\n return;\n self._userId = userId;\n if (self._userIdDeps)\n self._userIdDeps.changed();\n },\n\n // Returns true if we are in a state after reconnect of waiting for subs to be\n // revived or early methods to finish their data, or we are waiting for a\n // \"wait\" method to finish.\n _waitingForQuiescence: function () {\n var self = this;\n return (! _.isEmpty(self._subsBeingRevived) ||\n ! _.isEmpty(self._methodsBlockingQuiescence));\n },\n\n // Returns true if any method whose message has been sent to the server has\n // not yet invoked its user callback.\n _anyMethodsAreOutstanding: function () {\n var self = this;\n return _.any(_.pluck(self._methodInvokers, 'sentMessage'));\n },\n\n _livedata_connected: function (msg) {\n var self = this;\n\n if (self._version !== 'pre1' && self._heartbeatInterval !== 0) {\n self._heartbeat = new Heartbeat({\n heartbeatInterval: self._heartbeatInterval,\n heartbeatTimeout: self._heartbeatTimeout,\n onTimeout: function () {\n self._lostConnection(\n new DDP.ConnectionError(\"DDP heartbeat timed out\"));\n },\n sendPing: function () {\n self._send({msg: 'ping'});\n }\n });\n self._heartbeat.start();\n }\n\n // If this is a reconnect, we'll have to reset all stores.\n if (self._lastSessionId)\n self._resetStores = true;\n\n if (typeof (msg.session) === \"string\") {\n var reconnectedToPreviousSession = (self._lastSessionId === msg.session);\n self._lastSessionId = msg.session;\n }\n\n if (reconnectedToPreviousSession) {\n // Successful reconnection -- pick up where we left off. Note that right\n // now, this never happens: the server never connects us to a previous\n // session, because DDP doesn't provide enough data for the server to know\n // what messages the client has processed. We need to improve DDP to make\n // this possible, at which point we'll probably need more code here.\n return;\n }\n\n // Server doesn't have our data any more. Re-sync a new session.\n\n // Forget about messages we were buffering for unknown collections. They'll\n // be resent if still relevant.\n self._updatesForUnknownStores = {};\n\n if (self._resetStores) {\n // Forget about the effects of stubs. We'll be resetting all collections\n // anyway.\n self._documentsWrittenByStub = {};\n self._serverDocuments = {};\n }\n\n // Clear _afterUpdateCallbacks.\n self._afterUpdateCallbacks = [];\n\n // Mark all named subscriptions which are ready (ie, we already called the\n // ready callback) as needing to be revived.\n // XXX We should also block reconnect quiescence until unnamed subscriptions\n // (eg, autopublish) are done re-publishing to avoid flicker!\n self._subsBeingRevived = {};\n _.each(self._subscriptions, function (sub, id) {\n if (sub.ready)\n self._subsBeingRevived[id] = true;\n });\n\n // Arrange for \"half-finished\" methods to have their callbacks run, and\n // track methods that were sent on this connection so that we don't\n // quiesce until they are all done.\n //\n // Start by clearing _methodsBlockingQuiescence: methods sent before\n // reconnect don't matter, and any \"wait\" methods sent on the new connection\n // that we drop here will be restored by the loop below.\n self._methodsBlockingQuiescence = {};\n if (self._resetStores) {\n _.each(self._methodInvokers, function (invoker) {\n if (invoker.gotResult()) {\n // This method already got its result, but it didn't call its callback\n // because its data didn't become visible. We did not resend the\n // method RPC. We'll call its callback when we get a full quiesce,\n // since that's as close as we'll get to \"data must be visible\".\n self._afterUpdateCallbacks.push(_.bind(invoker.dataVisible, invoker));\n } else if (invoker.sentMessage) {\n // This method has been sent on this connection (maybe as a resend\n // from the last connection, maybe from onReconnect, maybe just very\n // quickly before processing the connected message).\n //\n // We don't need to do anything special to ensure its callbacks get\n // called, but we'll count it as a method which is preventing\n // reconnect quiescence. (eg, it might be a login method that was run\n // from onReconnect, and we don't want to see flicker by seeing a\n // logged-out state.)\n self._methodsBlockingQuiescence[invoker.methodId] = true;\n }\n });\n }\n\n self._messagesBufferedUntilQuiescence = [];\n\n // If we're not waiting on any methods or subs, we can reset the stores and\n // call the callbacks immediately.\n if (!self._waitingForQuiescence()) {\n if (self._resetStores) {\n _.each(self._stores, function (s) {\n s.beginUpdate(0, true);\n s.endUpdate();\n });\n self._resetStores = false;\n }\n self._runAfterUpdateCallbacks();\n }\n },\n\n\n _processOneDataMessage: function (msg, updates) {\n var self = this;\n // Using underscore here so as not to need to capitalize.\n self['_process_' + msg.msg](msg, updates);\n },\n\n\n _livedata_data: function (msg) {\n var self = this;\n\n // collection name -> array of messages\n var updates = {};\n\n if (self._waitingForQuiescence()) {\n self._messagesBufferedUntilQuiescence.push(msg);\n\n if (msg.msg === \"nosub\")\n delete self._subsBeingRevived[msg.id];\n\n _.each(msg.subs || [], function (subId) {\n delete self._subsBeingRevived[subId];\n });\n _.each(msg.methods || [], function (methodId) {\n delete self._methodsBlockingQuiescence[methodId];\n });\n\n if (self._waitingForQuiescence())\n return;\n\n // No methods or subs are blocking quiescence!\n // We'll now process and all of our buffered messages, reset all stores,\n // and apply them all at once.\n _.each(self._messagesBufferedUntilQuiescence, function (bufferedMsg) {\n self._processOneDataMessage(bufferedMsg, updates);\n });\n self._messagesBufferedUntilQuiescence = [];\n } else {\n self._processOneDataMessage(msg, updates);\n }\n\n if (self._resetStores || !_.isEmpty(updates)) {\n // Begin a transactional update of each store.\n _.each(self._stores, function (s, storeName) {\n s.beginUpdate(_.has(updates, storeName) ? updates[storeName].length : 0,\n self._resetStores);\n });\n self._resetStores = false;\n\n _.each(updates, function (updateMessages, storeName) {\n var store = self._stores[storeName];\n if (store) {\n _.each(updateMessages, function (updateMessage) {\n store.update(updateMessage);\n });\n } else {\n // Nobody's listening for this data. Queue it up until\n // someone wants it.\n // XXX memory use will grow without bound if you forget to\n // create a collection or just don't care about it... going\n // to have to do something about that.\n if (!_.has(self._updatesForUnknownStores, storeName))\n self._updatesForUnknownStores[storeName] = [];\n Array.prototype.push.apply(self._updatesForUnknownStores[storeName],\n updateMessages);\n }\n });\n\n // End update transaction.\n _.each(self._stores, function (s) { s.endUpdate(); });\n }\n\n self._runAfterUpdateCallbacks();\n },\n\n // Call any callbacks deferred with _runWhenAllServerDocsAreFlushed whose\n // relevant docs have been flushed, as well as dataVisible callbacks at\n // reconnect-quiescence time.\n _runAfterUpdateCallbacks: function () {\n var self = this;\n var callbacks = self._afterUpdateCallbacks;\n self._afterUpdateCallbacks = [];\n _.each(callbacks, function (c) {\n c();\n });\n },\n\n _pushUpdate: function (updates, collection, msg) {\n var self = this;\n if (!_.has(updates, collection)) {\n updates[collection] = [];\n }\n updates[collection].push(msg);\n },\n\n _getServerDoc: function (collection, id) {\n var self = this;\n if (!_.has(self._serverDocuments, collection))\n return null;\n var serverDocsForCollection = self._serverDocuments[collection];\n return serverDocsForCollection.get(id) || null;\n },\n\n _process_added: function (msg, updates) {\n var self = this;\n var id = LocalCollection._idParse(msg.id);\n var serverDoc = self._getServerDoc(msg.collection, id);\n if (serverDoc) {\n // Some outstanding stub wrote here.\n if (serverDoc.document !== undefined)\n throw new Error(\"Server sent add for existing id: \" + msg.id);\n serverDoc.document = msg.fields || {};\n serverDoc.document._id = id;\n } else {\n self._pushUpdate(updates, msg.collection, msg);\n }\n },\n\n _process_changed: function (msg, updates) {\n var self = this;\n var serverDoc = self._getServerDoc(\n msg.collection, LocalCollection._idParse(msg.id));\n if (serverDoc) {\n if (serverDoc.document === undefined)\n throw new Error(\"Server sent changed for nonexisting id: \" + msg.id);\n LocalCollection._applyChanges(serverDoc.document, msg.fields);\n } else {\n self._pushUpdate(updates, msg.collection, msg);\n }\n },\n\n _process_removed: function (msg, updates) {\n var self = this;\n var serverDoc = self._getServerDoc(\n msg.collection, LocalCollection._idParse(msg.id));\n if (serverDoc) {\n // Some outstanding stub wrote here.\n if (serverDoc.document === undefined)\n throw new Error(\"Server sent removed for nonexisting id:\" + msg.id);\n serverDoc.document = undefined;\n } else {\n self._pushUpdate(updates, msg.collection, {\n msg: 'removed',\n collection: msg.collection,\n id: msg.id\n });\n }\n },\n\n _process_updated: function (msg, updates) {\n var self = this;\n // Process \"method done\" messages.\n _.each(msg.methods, function (methodId) {\n _.each(self._documentsWrittenByStub[methodId], function (written) {\n var serverDoc = self._getServerDoc(written.collection, written.id);\n if (!serverDoc)\n throw new Error(\"Lost serverDoc for \" + JSON.stringify(written));\n if (!serverDoc.writtenByStubs[methodId])\n throw new Error(\"Doc \" + JSON.stringify(written) +\n \" not written by method \" + methodId);\n delete serverDoc.writtenByStubs[methodId];\n if (_.isEmpty(serverDoc.writtenByStubs)) {\n // All methods whose stubs wrote this method have completed! We can\n // now copy the saved document to the database (reverting the stub's\n // change if the server did not write to this object, or applying the\n // server's writes if it did).\n\n // This is a fake ddp 'replace' message. It's just for talking\n // between livedata connections and minimongo. (We have to stringify\n // the ID because it's supposed to look like a wire message.)\n self._pushUpdate(updates, written.collection, {\n msg: 'replace',\n id: LocalCollection._idStringify(written.id),\n replace: serverDoc.document\n });\n // Call all flush callbacks.\n _.each(serverDoc.flushCallbacks, function (c) {\n c();\n });\n\n // Delete this completed serverDocument. Don't bother to GC empty\n // IdMaps inside self._serverDocuments, since there probably aren't\n // many collections and they'll be written repeatedly.\n self._serverDocuments[written.collection].remove(written.id);\n }\n });\n delete self._documentsWrittenByStub[methodId];\n\n // We want to call the data-written callback, but we can't do so until all\n // currently buffered messages are flushed.\n var callbackInvoker = self._methodInvokers[methodId];\n if (!callbackInvoker)\n throw new Error(\"No callback invoker for method \" + methodId);\n self._runWhenAllServerDocsAreFlushed(\n _.bind(callbackInvoker.dataVisible, callbackInvoker));\n });\n },\n\n _process_ready: function (msg, updates) {\n var self = this;\n // Process \"sub ready\" messages. \"sub ready\" messages don't take effect\n // until all current server documents have been flushed to the local\n // database. We can use a write fence to implement this.\n _.each(msg.subs, function (subId) {\n self._runWhenAllServerDocsAreFlushed(function () {\n var subRecord = self._subscriptions[subId];\n // Did we already unsubscribe?\n if (!subRecord)\n return;\n // Did we already receive a ready message? (Oops!)\n if (subRecord.ready)\n return;\n subRecord.readyCallback && subRecord.readyCallback();\n subRecord.ready = true;\n subRecord.readyDeps.changed();\n });\n });\n },\n\n // Ensures that \"f\" will be called after all documents currently in\n // _serverDocuments have been written to the local cache. f will not be called\n // if the connection is lost before then!\n _runWhenAllServerDocsAreFlushed: function (f) {\n var self = this;\n var runFAfterUpdates = function () {\n self._afterUpdateCallbacks.push(f);\n };\n var unflushedServerDocCount = 0;\n var onServerDocFlush = function () {\n --unflushedServerDocCount;\n if (unflushedServerDocCount === 0) {\n // This was the last doc to flush! Arrange to run f after the updates\n // have been applied.\n runFAfterUpdates();\n }\n };\n _.each(self._serverDocuments, function (collectionDocs) {\n collectionDocs.forEach(function (serverDoc) {\n var writtenByStubForAMethodWithSentMessage = _.any(\n serverDoc.writtenByStubs, function (dummy, methodId) {\n var invoker = self._methodInvokers[methodId];\n return invoker && invoker.sentMessage;\n });\n if (writtenByStubForAMethodWithSentMessage) {\n ++unflushedServerDocCount;\n serverDoc.flushCallbacks.push(onServerDocFlush);\n }\n });\n });\n if (unflushedServerDocCount === 0) {\n // There aren't any buffered docs --- we can call f as soon as the current\n // round of updates is applied!\n runFAfterUpdates();\n }\n },\n\n _livedata_nosub: function (msg) {\n var self = this;\n\n // First pass it through _livedata_data, which only uses it to help get\n // towards quiescence.\n self._livedata_data(msg);\n\n // Do the rest of our processing immediately, with no\n // buffering-until-quiescence.\n\n // we weren't subbed anyway, or we initiated the unsub.\n if (!_.has(self._subscriptions, msg.id))\n return;\n\n // XXX COMPAT WITH 1.0.3.1 #errorCallback\n var errorCallback = self._subscriptions[msg.id].errorCallback;\n var stopCallback = self._subscriptions[msg.id].stopCallback;\n\n self._subscriptions[msg.id].remove();\n\n var meteorErrorFromMsg = function (msgArg) {\n return msgArg && msgArg.error && new Meteor.Error(\n msgArg.error.error, msgArg.error.reason, msgArg.error.details);\n }\n\n // XXX COMPAT WITH 1.0.3.1 #errorCallback\n if (errorCallback && msg.error) {\n errorCallback(meteorErrorFromMsg(msg));\n }\n\n if (stopCallback) {\n stopCallback(meteorErrorFromMsg(msg));\n }\n },\n\n _process_nosub: function () {\n // This is called as part of the \"buffer until quiescence\" process, but\n // nosub's effect is always immediate. It only goes in the buffer at all\n // because it's possible for a nosub to be the thing that triggers\n // quiescence, if we were waiting for a sub to be revived and it dies\n // instead.\n },\n\n _livedata_result: function (msg) {\n // id, result or error. error has error (code), reason, details\n\n var self = this;\n\n // find the outstanding request\n // should be O(1) in nearly all realistic use cases\n if (_.isEmpty(self._outstandingMethodBlocks)) {\n Meteor._debug(\"Received method result but no methods outstanding\");\n return;\n }\n var currentMethodBlock = self._outstandingMethodBlocks[0].methods;\n var m;\n for (var i = 0; i < currentMethodBlock.length; i++) {\n m = currentMethodBlock[i];\n if (m.methodId === msg.id)\n break;\n }\n\n if (!m) {\n Meteor._debug(\"Can't match method response to original method call\", msg);\n return;\n }\n\n // Remove from current method block. This may leave the block empty, but we\n // don't move on to the next block until the callback has been delivered, in\n // _outstandingMethodFinished.\n currentMethodBlock.splice(i, 1);\n\n if (_.has(msg, 'error')) {\n m.receiveResult(new Meteor.Error(\n msg.error.error, msg.error.reason,\n msg.error.details));\n } else {\n // msg.result may be undefined if the method didn't return a\n // value\n m.receiveResult(undefined, msg.result);\n }\n },\n\n // Called by MethodInvoker after a method's callback is invoked. If this was\n // the last outstanding method in the current block, runs the next block. If\n // there are no more methods, consider accepting a hot code push.\n _outstandingMethodFinished: function () {\n var self = this;\n if (self._anyMethodsAreOutstanding())\n return;\n\n // No methods are outstanding. This should mean that the first block of\n // methods is empty. (Or it might not exist, if this was a method that\n // half-finished before disconnect/reconnect.)\n if (! _.isEmpty(self._outstandingMethodBlocks)) {\n var firstBlock = self._outstandingMethodBlocks.shift();\n if (! _.isEmpty(firstBlock.methods))\n throw new Error(\"No methods outstanding but nonempty block: \" +\n JSON.stringify(firstBlock));\n\n // Send the outstanding methods now in the first block.\n if (!_.isEmpty(self._outstandingMethodBlocks))\n self._sendOutstandingMethods();\n }\n\n // Maybe accept a hot code push.\n self._maybeMigrate();\n },\n\n // Sends messages for all the methods in the first block in\n // _outstandingMethodBlocks.\n _sendOutstandingMethods: function() {\n var self = this;\n if (_.isEmpty(self._outstandingMethodBlocks))\n return;\n _.each(self._outstandingMethodBlocks[0].methods, function (m) {\n m.sendMessage();\n });\n },\n\n _livedata_error: function (msg) {\n Meteor._debug(\"Received error from server: \", msg.reason);\n if (msg.offendingMessage)\n Meteor._debug(\"For: \", msg.offendingMessage);\n },\n\n _callOnReconnectAndSendAppropriateOutstandingMethods: function() {\n var self = this;\n var oldOutstandingMethodBlocks = self._outstandingMethodBlocks;\n self._outstandingMethodBlocks = [];\n\n self.onReconnect();\n\n if (_.isEmpty(oldOutstandingMethodBlocks))\n return;\n\n // We have at least one block worth of old outstanding methods to try\n // again. First: did onReconnect actually send anything? If not, we just\n // restore all outstanding methods and run the first block.\n if (_.isEmpty(self._outstandingMethodBlocks)) {\n self._outstandingMethodBlocks = oldOutstandingMethodBlocks;\n self._sendOutstandingMethods();\n return;\n }\n\n // OK, there are blocks on both sides. Special case: merge the last block of\n // the reconnect methods with the first block of the original methods, if\n // neither of them are \"wait\" blocks.\n if (!_.last(self._outstandingMethodBlocks).wait &&\n !oldOutstandingMethodBlocks[0].wait) {\n _.each(oldOutstandingMethodBlocks[0].methods, function (m) {\n _.last(self._outstandingMethodBlocks).methods.push(m);\n\n // If this \"last block\" is also the first block, send the message.\n if (self._outstandingMethodBlocks.length === 1)\n m.sendMessage();\n });\n\n oldOutstandingMethodBlocks.shift();\n }\n\n // Now add the rest of the original blocks on.\n _.each(oldOutstandingMethodBlocks, function (block) {\n self._outstandingMethodBlocks.push(block);\n });\n },\n\n // We can accept a hot code push if there are no methods in flight.\n _readyToMigrate: function() {\n var self = this;\n return _.isEmpty(self._methodInvokers);\n },\n\n // If we were blocking a migration, see if it's now possible to continue.\n // Call whenever the set of outstanding/blocked methods shrinks.\n _maybeMigrate: function () {\n var self = this;\n if (self._retryMigrate && self._readyToMigrate()) {\n self._retryMigrate();\n self._retryMigrate = null;\n }\n }\n});\n\nLivedataTest.Connection = Connection;\n\n// @param url {String} URL to Meteor app,\n// e.g.:\n// \"subdomain.meteor.com\",\n// \"http://subdomain.meteor.com\",\n// \"/\",\n// \"ddp+sockjs://ddp--****-foo.meteor.com/sockjs\"\n\n/**\n * @summary Connect to the server of a different Meteor application to subscribe to its document sets and invoke its remote methods.\n * @locus Anywhere\n * @param {String} url The URL of another Meteor application.\n */\nDDP.connect = function (url, options) {\n var ret = new Connection(url, options);\n allConnections.push(ret); // hack. see below.\n return ret;\n};\n\n// Hack for `spiderable` package: a way to see if the page is done\n// loading all the data it needs.\n//\nallConnections = [];\nDDP._allSubscriptionsReady = function () {\n return _.all(allConnections, function (conn) {\n return _.all(conn._subscriptions, function (sub) {\n return sub.ready;\n });\n });\n};\n","// Only create a server if we are in an environment with a HTTP server\n// (as opposed to, eg, a command-line tool).\n//\n// Note: this whole conditional is a total hack to get around the fact that this\n// package logically should be split into a ddp-client and ddp-server package;\n// see https://github.com/meteor/meteor/issues/3452\n//\n// Until we do that, this conditional (and the weak dependency on webapp that\n// should really be a strong dependency of the ddp-server package) allows you to\n// build projects which use `ddp` in Node without wanting to run a DDP server\n// (ie, allows you to act as if you were using the nonexistent `ddp-client`\n// server package).\nif (Package.webapp) {\n if (process.env.DDP_DEFAULT_CONNECTION_URL) {\n __meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL =\n process.env.DDP_DEFAULT_CONNECTION_URL;\n }\n\n Meteor.server = new Server;\n\n Meteor.refresh = function (notification) {\n DDPServer._InvalidationCrossbar.fire(notification);\n };\n\n // Proxy the public methods of Meteor.server so they can\n // be called directly on Meteor.\n _.each(['publish', 'methods', 'call', 'apply', 'onConnection'],\n function (name) {\n Meteor[name] = _.bind(Meteor.server[name], Meteor.server);\n });\n} else {\n // No server? Make these empty/no-ops.\n Meteor.server = null;\n Meteor.refresh = function (notification) {\n };\n\n // Make these empty/no-ops too, so that non-webapp apps can still\n // depend on/use packages that use those functions.\n _.each(['publish', 'methods', 'onConnection'],\n function (name) {\n Meteor[name] = function () { };\n });\n}\n\n// Meteor.server used to be called Meteor.default_server. Provide\n// backcompat as a courtesy even though it was never documented.\n// XXX COMPAT WITH 0.6.4\nMeteor.default_server = Meteor.server;\n"]}