UNPKG

userdo

Version:

A Durable Object base class for building applications on Cloudflare Workers.

20 lines (17 loc) 15.6 kB
/*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */var G=function(z,j){return G=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(B,J){B.__proto__=J}||function(B,J){for(var I in J)if(J.hasOwnProperty(I))B[I]=J[I]},G(z,j)};function M(z,j){G(z,j);function B(){this.constructor=z}z.prototype=j===null?Object.create(j):(B.prototype=j.prototype,new B)}function O(z){var j=typeof Symbol==="function"&&z[Symbol.iterator],B=0;if(j)return j.call(z);return{next:function(){if(z&&B>=z.length)z=void 0;return{value:z&&z[B++],done:!z}}}}function A(z,j){var B=typeof Symbol==="function"&&z[Symbol.iterator];if(!B)return z;var J=B.call(z),I,N=[],Q;try{while((j===void 0||j-- >0)&&!(I=J.next()).done)N.push(I.value)}catch(Y){Q={error:Y}}finally{try{if(I&&!I.done&&(B=J.return))B.call(J)}finally{if(Q)throw Q.error}}return N}function C(){for(var z=[],j=0;j<arguments.length;j++)z=z.concat(A(arguments[j]));return z}var V=function(){function z(j,B){this.target=B,this.type=j}return z}(),L=function(z){M(j,z);function j(B,J){var I=z.call(this,"error",J)||this;return I.message=B.message,I.error=B,I}return j}(V),f=function(z){M(j,z);function j(B,J,I){if(B===void 0)B=1000;if(J===void 0)J="";var N=z.call(this,"close",I)||this;return N.wasClean=!0,N.code=B,N.reason=J,N}return j}(V);/*! * Reconnecting WebSocket * by Pedro Ladaria <pedro.ladaria@gmail.com> * https://github.com/pladaria/reconnecting-websocket * License MIT */var F=function(){if(typeof WebSocket!=="undefined")return WebSocket},T=function(z){return typeof z!=="undefined"&&!!z&&z.CLOSING===2},Z={maxReconnectionDelay:1e4,minReconnectionDelay:1000+Math.random()*4000,minUptime:5000,reconnectionDelayGrowFactor:1.3,connectionTimeout:4000,maxRetries:1/0,maxEnqueuedMessages:1/0,startClosed:!1,debug:!1},x=function(){function z(j,B,J){var I=this;if(J===void 0)J={};if(this._listeners={error:[],message:[],open:[],close:[]},this._retryCount=-1,this._shouldReconnect=!0,this._connectLock=!1,this._binaryType="blob",this._closeCalled=!1,this._messageQueue=[],this.onclose=null,this.onerror=null,this.onmessage=null,this.onopen=null,this._handleOpen=function(N){I._debug("open event");var Q=I._options.minUptime,Y=Q===void 0?Z.minUptime:Q;if(clearTimeout(I._connectTimeout),I._uptimeTimeout=setTimeout(function(){return I._acceptOpen()},Y),I._ws.binaryType=I._binaryType,I._messageQueue.forEach(function(X){return I._ws.send(X)}),I._messageQueue=[],I.onopen)I.onopen(N);I._listeners.open.forEach(function(X){return I._callEventListener(N,X)})},this._handleMessage=function(N){if(I._debug("message event"),I.onmessage)I.onmessage(N);I._listeners.message.forEach(function(Q){return I._callEventListener(N,Q)})},this._handleError=function(N){if(I._debug("error event",N.message),I._disconnect(void 0,N.message==="TIMEOUT"?"timeout":void 0),I.onerror)I.onerror(N);I._debug("exec error listeners"),I._listeners.error.forEach(function(Q){return I._callEventListener(N,Q)}),I._connect()},this._handleClose=function(N){if(I._debug("close event"),I._clearTimeouts(),I._shouldReconnect)I._connect();if(I.onclose)I.onclose(N);I._listeners.close.forEach(function(Q){return I._callEventListener(N,Q)})},this._url=j,this._protocols=B,this._options=J,this._options.startClosed)this._shouldReconnect=!1;this._connect()}return Object.defineProperty(z,"CONNECTING",{get:function(){return 0},enumerable:!0,configurable:!0}),Object.defineProperty(z,"OPEN",{get:function(){return 1},enumerable:!0,configurable:!0}),Object.defineProperty(z,"CLOSING",{get:function(){return 2},enumerable:!0,configurable:!0}),Object.defineProperty(z,"CLOSED",{get:function(){return 3},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"CONNECTING",{get:function(){return z.CONNECTING},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"OPEN",{get:function(){return z.OPEN},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"CLOSING",{get:function(){return z.CLOSING},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"CLOSED",{get:function(){return z.CLOSED},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"binaryType",{get:function(){return this._ws?this._ws.binaryType:this._binaryType},set:function(j){if(this._binaryType=j,this._ws)this._ws.binaryType=j},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"retryCount",{get:function(){return Math.max(this._retryCount,0)},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"bufferedAmount",{get:function(){var j=this._messageQueue.reduce(function(B,J){if(typeof J==="string")B+=J.length;else if(J instanceof Blob)B+=J.size;else B+=J.byteLength;return B},0);return j+(this._ws?this._ws.bufferedAmount:0)},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"extensions",{get:function(){return this._ws?this._ws.extensions:""},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"protocol",{get:function(){return this._ws?this._ws.protocol:""},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"readyState",{get:function(){if(this._ws)return this._ws.readyState;return this._options.startClosed?z.CLOSED:z.CONNECTING},enumerable:!0,configurable:!0}),Object.defineProperty(z.prototype,"url",{get:function(){return this._ws?this._ws.url:""},enumerable:!0,configurable:!0}),z.prototype.close=function(j,B){if(j===void 0)j=1000;if(this._closeCalled=!0,this._shouldReconnect=!1,this._clearTimeouts(),!this._ws){this._debug("close enqueued: no ws instance");return}if(this._ws.readyState===this.CLOSED){this._debug("close: already closed");return}this._ws.close(j,B)},z.prototype.reconnect=function(j,B){if(this._shouldReconnect=!0,this._closeCalled=!1,this._retryCount=-1,!this._ws||this._ws.readyState===this.CLOSED)this._connect();else this._disconnect(j,B),this._connect()},z.prototype.send=function(j){if(this._ws&&this._ws.readyState===this.OPEN)this._debug("send",j),this._ws.send(j);else{var B=this._options.maxEnqueuedMessages,J=B===void 0?Z.maxEnqueuedMessages:B;if(this._messageQueue.length<J)this._debug("enqueue",j),this._messageQueue.push(j)}},z.prototype.addEventListener=function(j,B){if(this._listeners[j])this._listeners[j].push(B)},z.prototype.dispatchEvent=function(j){var B,J,I=this._listeners[j.type];if(I)try{for(var N=O(I),Q=N.next();!Q.done;Q=N.next()){var Y=Q.value;this._callEventListener(j,Y)}}catch(X){B={error:X}}finally{try{if(Q&&!Q.done&&(J=N.return))J.call(N)}finally{if(B)throw B.error}}return!0},z.prototype.removeEventListener=function(j,B){if(this._listeners[j])this._listeners[j]=this._listeners[j].filter(function(J){return J!==B})},z.prototype._debug=function(){var j=[];for(var B=0;B<arguments.length;B++)j[B]=arguments[B];if(this._options.debug)console.log.apply(console,C(["RWS>"],j))},z.prototype._getNextDelay=function(){var j=this._options,B=j.reconnectionDelayGrowFactor,J=B===void 0?Z.reconnectionDelayGrowFactor:B,I=j.minReconnectionDelay,N=I===void 0?Z.minReconnectionDelay:I,Q=j.maxReconnectionDelay,Y=Q===void 0?Z.maxReconnectionDelay:Q,X=0;if(this._retryCount>0){if(X=N*Math.pow(J,this._retryCount-1),X>Y)X=Y}return this._debug("next delay",X),X},z.prototype._wait=function(){var j=this;return new Promise(function(B){setTimeout(B,j._getNextDelay())})},z.prototype._getNextUrl=function(j){if(typeof j==="string")return Promise.resolve(j);if(typeof j==="function"){var B=j();if(typeof B==="string")return Promise.resolve(B);if(B.then)return B}throw Error("Invalid URL")},z.prototype._connect=function(){var j=this;if(this._connectLock||!this._shouldReconnect)return;this._connectLock=!0;var B=this._options,J=B.maxRetries,I=J===void 0?Z.maxRetries:J,N=B.connectionTimeout,Q=N===void 0?Z.connectionTimeout:N,Y=B.WebSocket,X=Y===void 0?F():Y;if(this._retryCount>=I){this._debug("max retries reached",this._retryCount,">=",I);return}if(this._retryCount++,this._debug("connect",this._retryCount),this._removeListeners(),!T(X))throw Error("No valid WebSocket class provided");this._wait().then(function(){return j._getNextUrl(j._url)}).then(function($){if(j._closeCalled)return;j._debug("connect",{url:$,protocols:j._protocols}),j._ws=j._protocols?new X($,j._protocols):new X($),j._ws.binaryType=j._binaryType,j._connectLock=!1,j._addListeners(),j._connectTimeout=setTimeout(function(){return j._handleTimeout()},Q)})},z.prototype._handleTimeout=function(){this._debug("timeout event"),this._handleError(new L(Error("TIMEOUT"),this))},z.prototype._disconnect=function(j,B){if(j===void 0)j=1000;if(this._clearTimeouts(),!this._ws)return;this._removeListeners();try{this._ws.close(j,B),this._handleClose(new f(j,B,this))}catch(J){}},z.prototype._acceptOpen=function(){this._debug("accept open"),this._retryCount=0},z.prototype._callEventListener=function(j,B){if("handleEvent"in B)B.handleEvent(j);else B(j)},z.prototype._removeListeners=function(){if(!this._ws)return;this._debug("removeListeners"),this._ws.removeEventListener("open",this._handleOpen),this._ws.removeEventListener("close",this._handleClose),this._ws.removeEventListener("message",this._handleMessage),this._ws.removeEventListener("error",this._handleError)},z.prototype._addListeners=function(){if(!this._ws)return;this._debug("addListeners"),this._ws.addEventListener("open",this._handleOpen),this._ws.addEventListener("close",this._handleClose),this._ws.addEventListener("message",this._handleMessage),this._ws.addEventListener("error",this._handleError)},z.prototype._clearTimeouts=function(){clearTimeout(this._connectTimeout),clearTimeout(this._uptimeTimeout)},z}(),q=x;class H{baseUrl;user=null;authListeners=new Set;ws=null;changeListeners=new Map;options;constructor(z,j={}){this.baseUrl=z;this.options=j;let B=this.autoDetectCookieDomain();if(B)console.log("\uD83C\uDF6A UserDO will use cookie domain:",B);this.checkAuthStatus()}get headers(){let z={"Content-Type":"application/json"},j=this.autoDetectCookieDomain();if(j)z["X-Cookie-Domain"]=j;return z}autoDetectCookieDomain(){try{if(!this.baseUrl.startsWith("http"))return null;let z=new URL(this.baseUrl),j=window.location.hostname,B=z.hostname;if(j===B)return null;let J=j.split("."),I=B.split(".");if(J.length<2||I.length<2)return null;let N=J.slice(-2).join("."),Q=I.slice(-2).join(".");if(N===Q)return`.${N}`;return null}catch(z){return console.warn("Failed to auto-detect cookie domain:",z),null}}async checkAuthStatus(){try{let z=`${this.baseUrl}/me`,j=await fetch(z,{credentials:"include"});if(j.ok){let B=await j.json();this.user=B.user}else this.user=null}catch(z){console.error("Auth check error:",z),this.user=null}this.emitAuthChange()}emitAuthChange(){if(this.authListeners.forEach((z)=>z(this.user)),console.log("\uD83D\uDD10 Auth state changed:",{user:this.user?this.user.email:"none"}),this.user&&!this.ws)console.log("\uD83D\uDD0C Triggering WebSocket connection..."),this.connectWebSocket();else if(!this.user&&this.ws)console.log("\uD83D\uDD0C Disconnecting WebSocket (user logged out)..."),this.disconnectWebSocket()}buildWebSocketUrl(){if(this.options.websocketUrl)return this.options.websocketUrl;return`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}${this.baseUrl}/ws`}connectWebSocket(){if(this.ws)return;let z=this.buildWebSocketUrl();console.log("\uD83D\uDD0C Connecting to WebSocket:",z),this.ws=new q(z),this.ws.onopen=()=>{console.log("\uD83D\uDD0C WebSocket connected")},this.ws.onmessage=(j)=>{try{let B=JSON.parse(j.data);this.handleRealtimeMessage(B)}catch(B){console.error("WebSocket message error:",B)}},this.ws.onclose=()=>{console.log("\uD83D\uDD0C WebSocket disconnected")},this.ws.onerror=(j)=>{console.error("WebSocket error:",j)}}disconnectWebSocket(){if(this.ws)this.ws.close(),this.ws=null}handleRealtimeMessage(z){let j=this.changeListeners.get(z.event);if(j)j.forEach((B)=>{try{B(z.data)}catch(J){console.error("Change listener error:",J)}})}onAuthStateChanged(z){this.authListeners.add(z),z(this.user)}offAuthStateChanged(z){this.authListeners.delete(z)}async signup(z,j){let B=await fetch(`${this.baseUrl}/signup`,{method:"POST",headers:this.headers,credentials:"include",body:JSON.stringify({email:z,password:j})});if(!B.ok)throw new Error(await B.text());let J=await B.json();return this.user=J.user,this.emitAuthChange(),J}async login(z,j){let B=await fetch(`${this.baseUrl}/login`,{method:"POST",headers:this.headers,credentials:"include",body:JSON.stringify({email:z,password:j})});if(!B.ok)throw new Error(await B.text());let J=await B.json();return this.user=J.user,this.emitAuthChange(),J}async logout(){await fetch(`${this.baseUrl}/logout`,{method:"POST",headers:this.headers,credentials:"include"}),this.user=null,this.disconnectWebSocket(),this.emitAuthChange()}async get(z){let j=await fetch(`${this.baseUrl.replace("/api","")}/data?key=${encodeURIComponent(z)}`,{headers:this.headers,credentials:"include"});if(!j.ok)throw new Error(await j.text());return(await j.json()).data}async set(z,j){let B=await fetch(`${this.baseUrl.replace("/api","")}/data`,{method:"POST",headers:this.headers,credentials:"include",body:JSON.stringify({key:z,value:j})});if(!B.ok)throw new Error(await B.text());return{ok:!0}}onChange(z,j){let B=`kv:${z}`;if(!this.changeListeners.has(B))this.changeListeners.set(B,new Set);return this.changeListeners.get(B).add(j),console.log(`\uD83D\uDD0C Watching KV key: ${z}`),()=>{let J=this.changeListeners.get(B);if(J){if(J.delete(j),J.size===0)this.changeListeners.delete(B)}console.log(`\uD83D\uDD0C Stopped watching KV key: ${z}`)}}collection(z){let j=`${this.baseUrl}/${z}`,B=this;return{async create(J){let I=await fetch(j,{method:"POST",headers:B.headers,credentials:"include",body:JSON.stringify(J)});if(!I.ok)throw new Error(await I.text());return I.json()},async findById(J){let I=await fetch(`${j}/${J}`,{headers:B.headers,credentials:"include"});if(!I.ok)throw new Error(await I.text());return I.json()},async update(J,I){let N=await fetch(`${j}/${J}`,{method:"PUT",headers:B.headers,credentials:"include",body:JSON.stringify(I)});if(!N.ok)throw new Error(await N.text());return N.json()},async delete(J){await fetch(`${j}/${J}`,{method:"DELETE",headers:B.headers,credentials:"include"})},onChange(J){let I=`table:${z}`;if(!B.changeListeners.has(I))B.changeListeners.set(I,new Set);return B.changeListeners.get(I).add(J),console.log(`\uD83D\uDD0C Watching collection: ${z}`),()=>{let N=B.changeListeners.get(I);if(N){if(N.delete(J),N.size===0)B.changeListeners.delete(I)}console.log(`\uD83D\uDD0C Stopped watching collection: ${z}`)}},query(){let J={};return{where(I,N,Q){return J.where=JSON.stringify([I,N,Q]),this},orderBy(I,N="asc"){return J.order=`${I}:${N}`,this},limit(I){return J.limit=I,this},async get(){let I=new URLSearchParams(J).toString(),N=await fetch(`${j}?${I}`,{headers:B.headers,credentials:"include"});if(!N.ok)throw new Error(await N.text());return N.json()}}}}}}var U=H;export{U as default,H as UserDOClient};