wacrawl-dashboard 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +220 -105
  2. package/apps/api/dist/demo/demoArchive.js +371 -0
  3. package/apps/api/dist/demo/demoCast.js +138 -0
  4. package/apps/api/dist/demo/demoMedia.js +108 -0
  5. package/apps/api/dist/index.js +109 -29
  6. package/apps/api/dist/lib/archiveSchema.js +57 -0
  7. package/apps/api/dist/lib/archiveSync.js +151 -0
  8. package/apps/api/dist/lib/chatKind.js +41 -0
  9. package/apps/api/dist/lib/displayName.js +20 -0
  10. package/apps/api/dist/lib/mediaFsPath.js +27 -0
  11. package/apps/api/dist/lib/percentile.js +23 -0
  12. package/apps/api/dist/lib/query.js +33 -7
  13. package/apps/api/dist/lib/rollupDb.js +253 -0
  14. package/apps/api/dist/lib/searchFilters.js +141 -0
  15. package/apps/api/dist/lib/selfIdentity.js +42 -0
  16. package/apps/api/dist/lib/statsCache.js +83 -0
  17. package/apps/api/dist/lib/timezone.js +102 -0
  18. package/apps/api/dist/lib/wordCloudUsefulStopWords.js +36 -3
  19. package/apps/api/dist/pathsResolve.js +17 -3
  20. package/apps/api/dist/routes/data.js +141 -77
  21. package/apps/api/dist/routes/settings.js +3 -0
  22. package/apps/api/dist/routes/stats/profiles.js +522 -0
  23. package/apps/api/dist/routes/stats/reports.js +555 -0
  24. package/apps/api/dist/routes/stats/shared.js +75 -0
  25. package/apps/api/dist/routes/stats.js +273 -323
  26. package/apps/api/dist/routes/sync.js +60 -0
  27. package/apps/api/dist/scripts/generateDemoArchive.js +24 -0
  28. package/apps/api/package.json +31 -0
  29. package/apps/web/dist/apple-touch-icon.png +0 -0
  30. package/apps/web/dist/assets/charts-BMGh5n-d.js +36 -0
  31. package/apps/web/dist/assets/icons-BdElrSaU.js +1 -0
  32. package/apps/web/dist/assets/index-Bb8Zj42D.css +1 -0
  33. package/apps/web/dist/assets/index-wrxENU1e.js +9 -0
  34. package/apps/web/dist/assets/react-D3RL6ob4.js +3 -0
  35. package/apps/web/dist/favicon-16x16.png +0 -0
  36. package/apps/web/dist/favicon-32x32.png +0 -0
  37. package/apps/web/dist/favicon-512x512.png +0 -0
  38. package/apps/web/dist/favicon.ico +0 -0
  39. package/apps/web/dist/favicon.svg +59 -0
  40. package/apps/web/dist/index.html +11 -2
  41. package/bin/wacrawl-dashboard.js +64 -15
  42. package/package.json +20 -4
  43. package/apps/web/dist/assets/index-CFh_Wz98.js +0 -43
  44. package/apps/web/dist/assets/index-DJiLhgJZ.css +0 -1
@@ -0,0 +1,3 @@
1
+ function dt(e,t){for(var n=0;n<t.length;n++){const r=t[n];if(typeof r!="string"&&!Array.isArray(r)){for(const a in r)if(a!=="default"&&!(a in e)){const i=Object.getOwnPropertyDescriptor(r,a);i&&Object.defineProperty(e,a,i.get?i:{enumerable:!0,get:()=>r[a]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}var Vr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function ht(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var pe={exports:{}},v={};var $e;function pt(){if($e)return v;$e=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),i=Symbol.for("react.consumer"),s=Symbol.for("react.context"),f=Symbol.for("react.forward_ref"),l=Symbol.for("react.suspense"),o=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),h=Symbol.for("react.activity"),y=Symbol.iterator;function C(u){return u===null||typeof u!="object"?null:(u=y&&u[y]||u["@@iterator"],typeof u=="function"?u:null)}var E={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,m={};function w(u,p,S){this.props=u,this.context=p,this.refs=m,this.updater=S||E}w.prototype.isReactComponent={},w.prototype.setState=function(u,p){if(typeof u!="object"&&typeof u!="function"&&u!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,u,p,"setState")},w.prototype.forceUpdate=function(u){this.updater.enqueueForceUpdate(this,u,"forceUpdate")};function _(){}_.prototype=w.prototype;function x(u,p,S){this.props=u,this.context=p,this.refs=m,this.updater=S||E}var $=x.prototype=new _;$.constructor=x,g($,w.prototype),$.isPureReactComponent=!0;var k=Array.isArray;function I(){}var b={H:null,A:null,T:null,S:null},W=Object.prototype.hasOwnProperty;function Y(u,p,S){var R=S.ref;return{$$typeof:e,type:u,key:p,ref:R!==void 0?R:null,props:S}}function X(u,p){return Y(u.type,p,u.props)}function Q(u){return typeof u=="object"&&u!==null&&u.$$typeof===e}function he(u){var p={"=":"=0",":":"=2"};return"$"+u.replace(/[=:]/g,function(S){return p[S]})}var Z=/\/+/g;function q(u,p){return typeof u=="object"&&u!==null&&u.key!=null?he(""+u.key):p.toString(36)}function B(u){switch(u.status){case"fulfilled":return u.value;case"rejected":throw u.reason;default:switch(typeof u.status=="string"?u.then(I,I):(u.status="pending",u.then(function(p){u.status==="pending"&&(u.status="fulfilled",u.value=p)},function(p){u.status==="pending"&&(u.status="rejected",u.reason=p)})),u.status){case"fulfilled":return u.value;case"rejected":throw u.reason}}throw u}function G(u,p,S,R,P){var T=typeof u;(T==="undefined"||T==="boolean")&&(u=null);var L=!1;if(u===null)L=!0;else switch(T){case"bigint":case"string":case"number":L=!0;break;case"object":switch(u.$$typeof){case e:case t:L=!0;break;case d:return L=u._init,G(L(u._payload),p,S,R,P)}}if(L)return P=P(u),L=R===""?"."+q(u,0):R,k(P)?(S="",L!=null&&(S=L.replace(Z,"$&/")+"/"),G(P,p,S,"",function(ft){return ft})):P!=null&&(Q(P)&&(P=X(P,S+(P.key==null||u&&u.key===P.key?"":(""+P.key).replace(Z,"$&/")+"/")+L)),p.push(P)),1;L=0;var j=R===""?".":R+":";if(k(u))for(var D=0;D<u.length;D++)R=u[D],T=j+q(R,D),L+=G(R,p,S,T,P);else if(D=C(u),typeof D=="function")for(u=D.call(u),D=0;!(R=u.next()).done;)R=R.value,T=j+q(R,D++),L+=G(R,p,S,T,P);else if(T==="object"){if(typeof u.then=="function")return G(B(u),p,S,R,P);throw p=String(u),Error("Objects are not valid as a React child (found: "+(p==="[object Object]"?"object with keys {"+Object.keys(u).join(", ")+"}":p)+"). If you meant to render a collection of children, use an array instead.")}return L}function ae(u,p,S){if(u==null)return u;var R=[],P=0;return G(u,R,"","",function(T){return p.call(S,T,P++)}),R}function st(u){if(u._status===-1){var p=u._result;p=p(),p.then(function(S){(u._status===0||u._status===-1)&&(u._status=1,u._result=S)},function(S){(u._status===0||u._status===-1)&&(u._status=2,u._result=S)}),u._status===-1&&(u._status=0,u._result=p)}if(u._status===1)return u._result.default;throw u._result}var ke=typeof reportError=="function"?reportError:function(u){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var p=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof u=="object"&&u!==null&&typeof u.message=="string"?String(u.message):String(u),error:u});if(!window.dispatchEvent(p))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",u);return}console.error(u)},ct={map:ae,forEach:function(u,p,S){ae(u,function(){p.apply(this,arguments)},S)},count:function(u){var p=0;return ae(u,function(){p++}),p},toArray:function(u){return ae(u,function(p){return p})||[]},only:function(u){if(!Q(u))throw Error("React.Children.only expected to receive a single React element child.");return u}};return v.Activity=h,v.Children=ct,v.Component=w,v.Fragment=n,v.Profiler=a,v.PureComponent=x,v.StrictMode=r,v.Suspense=l,v.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=b,v.__COMPILER_RUNTIME={__proto__:null,c:function(u){return b.H.useMemoCache(u)}},v.cache=function(u){return function(){return u.apply(null,arguments)}},v.cacheSignal=function(){return null},v.cloneElement=function(u,p,S){if(u==null)throw Error("The argument must be a React element, but you passed "+u+".");var R=g({},u.props),P=u.key;if(p!=null)for(T in p.key!==void 0&&(P=""+p.key),p)!W.call(p,T)||T==="key"||T==="__self"||T==="__source"||T==="ref"&&p.ref===void 0||(R[T]=p[T]);var T=arguments.length-2;if(T===1)R.children=S;else if(1<T){for(var L=Array(T),j=0;j<T;j++)L[j]=arguments[j+2];R.children=L}return Y(u.type,P,R)},v.createContext=function(u){return u={$$typeof:s,_currentValue:u,_currentValue2:u,_threadCount:0,Provider:null,Consumer:null},u.Provider=u,u.Consumer={$$typeof:i,_context:u},u},v.createElement=function(u,p,S){var R,P={},T=null;if(p!=null)for(R in p.key!==void 0&&(T=""+p.key),p)W.call(p,R)&&R!=="key"&&R!=="__self"&&R!=="__source"&&(P[R]=p[R]);var L=arguments.length-2;if(L===1)P.children=S;else if(1<L){for(var j=Array(L),D=0;D<L;D++)j[D]=arguments[D+2];P.children=j}if(u&&u.defaultProps)for(R in L=u.defaultProps,L)P[R]===void 0&&(P[R]=L[R]);return Y(u,T,P)},v.createRef=function(){return{current:null}},v.forwardRef=function(u){return{$$typeof:f,render:u}},v.isValidElement=Q,v.lazy=function(u){return{$$typeof:d,_payload:{_status:-1,_result:u},_init:st}},v.memo=function(u,p){return{$$typeof:o,type:u,compare:p===void 0?null:p}},v.startTransition=function(u){var p=b.T,S={};b.T=S;try{var R=u(),P=b.S;P!==null&&P(S,R),typeof R=="object"&&R!==null&&typeof R.then=="function"&&R.then(I,ke)}catch(T){ke(T)}finally{p!==null&&S.types!==null&&(p.types=S.types),b.T=p}},v.unstable_useCacheRefresh=function(){return b.H.useCacheRefresh()},v.use=function(u){return b.H.use(u)},v.useActionState=function(u,p,S){return b.H.useActionState(u,p,S)},v.useCallback=function(u,p){return b.H.useCallback(u,p)},v.useContext=function(u){return b.H.useContext(u)},v.useDebugValue=function(){},v.useDeferredValue=function(u,p){return b.H.useDeferredValue(u,p)},v.useEffect=function(u,p){return b.H.useEffect(u,p)},v.useEffectEvent=function(u){return b.H.useEffectEvent(u)},v.useId=function(){return b.H.useId()},v.useImperativeHandle=function(u,p,S){return b.H.useImperativeHandle(u,p,S)},v.useInsertionEffect=function(u,p){return b.H.useInsertionEffect(u,p)},v.useLayoutEffect=function(u,p){return b.H.useLayoutEffect(u,p)},v.useMemo=function(u,p){return b.H.useMemo(u,p)},v.useOptimistic=function(u,p){return b.H.useOptimistic(u,p)},v.useReducer=function(u,p,S){return b.H.useReducer(u,p,S)},v.useRef=function(u){return b.H.useRef(u)},v.useState=function(u){return b.H.useState(u)},v.useSyncExternalStore=function(u,p,S){return b.H.useSyncExternalStore(u,p,S)},v.useTransition=function(){return b.H.useTransition()},v.version="19.2.6",v}var Ae;function We(){return Ae||(Ae=1,pe.exports=pt()),pe.exports}var c=We();const mt=ht(c),Jr=dt({__proto__:null,default:mt},[c]);var me={exports:{}},A={};var De;function yt(){if(De)return A;De=1;var e=We();function t(l){var o="https://react.dev/errors/"+l;if(1<arguments.length){o+="?args[]="+encodeURIComponent(arguments[1]);for(var d=2;d<arguments.length;d++)o+="&args[]="+encodeURIComponent(arguments[d])}return"Minified React error #"+l+"; visit "+o+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function n(){}var r={d:{f:n,r:function(){throw Error(t(522))},D:n,C:n,L:n,m:n,X:n,S:n,M:n},p:0,findDOMNode:null},a=Symbol.for("react.portal");function i(l,o,d){var h=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:a,key:h==null?null:""+h,children:l,containerInfo:o,implementation:d}}var s=e.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function f(l,o){if(l==="font")return"";if(typeof o=="string")return o==="use-credentials"?o:""}return A.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=r,A.createPortal=function(l,o){var d=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!o||o.nodeType!==1&&o.nodeType!==9&&o.nodeType!==11)throw Error(t(299));return i(l,o,null,d)},A.flushSync=function(l){var o=s.T,d=r.p;try{if(s.T=null,r.p=2,l)return l()}finally{s.T=o,r.p=d,r.d.f()}},A.preconnect=function(l,o){typeof l=="string"&&(o?(o=o.crossOrigin,o=typeof o=="string"?o==="use-credentials"?o:"":void 0):o=null,r.d.C(l,o))},A.prefetchDNS=function(l){typeof l=="string"&&r.d.D(l)},A.preinit=function(l,o){if(typeof l=="string"&&o&&typeof o.as=="string"){var d=o.as,h=f(d,o.crossOrigin),y=typeof o.integrity=="string"?o.integrity:void 0,C=typeof o.fetchPriority=="string"?o.fetchPriority:void 0;d==="style"?r.d.S(l,typeof o.precedence=="string"?o.precedence:void 0,{crossOrigin:h,integrity:y,fetchPriority:C}):d==="script"&&r.d.X(l,{crossOrigin:h,integrity:y,fetchPriority:C,nonce:typeof o.nonce=="string"?o.nonce:void 0})}},A.preinitModule=function(l,o){if(typeof l=="string")if(typeof o=="object"&&o!==null){if(o.as==null||o.as==="script"){var d=f(o.as,o.crossOrigin);r.d.M(l,{crossOrigin:d,integrity:typeof o.integrity=="string"?o.integrity:void 0,nonce:typeof o.nonce=="string"?o.nonce:void 0})}}else o==null&&r.d.M(l)},A.preload=function(l,o){if(typeof l=="string"&&typeof o=="object"&&o!==null&&typeof o.as=="string"){var d=o.as,h=f(d,o.crossOrigin);r.d.L(l,d,{crossOrigin:h,integrity:typeof o.integrity=="string"?o.integrity:void 0,nonce:typeof o.nonce=="string"?o.nonce:void 0,type:typeof o.type=="string"?o.type:void 0,fetchPriority:typeof o.fetchPriority=="string"?o.fetchPriority:void 0,referrerPolicy:typeof o.referrerPolicy=="string"?o.referrerPolicy:void 0,imageSrcSet:typeof o.imageSrcSet=="string"?o.imageSrcSet:void 0,imageSizes:typeof o.imageSizes=="string"?o.imageSizes:void 0,media:typeof o.media=="string"?o.media:void 0})}},A.preloadModule=function(l,o){if(typeof l=="string")if(o){var d=f(o.as,o.crossOrigin);r.d.m(l,{as:typeof o.as=="string"&&o.as!=="script"?o.as:void 0,crossOrigin:d,integrity:typeof o.integrity=="string"?o.integrity:void 0})}else r.d.m(l)},A.requestFormReset=function(l){r.d.r(l)},A.unstable_batchedUpdates=function(l,o){return l(o)},A.useFormState=function(l,o,d){return s.H.useFormState(l,o,d)},A.useFormStatus=function(){return s.H.useHostTransitionStatus()},A.version="19.2.6",A}var Ne;function gt(){if(Ne)return me.exports;Ne=1;function e(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),me.exports=yt(),me.exports}var Ie="popstate";function Me(e){return typeof e=="object"&&e!=null&&"pathname"in e&&"search"in e&&"hash"in e&&"state"in e&&"key"in e}function vt(e={}){function t(r,a){let i=a.state?.masked,{pathname:s,search:f,hash:l}=i||r.location;return Re("",{pathname:s,search:f,hash:l},a.state&&a.state.usr||null,a.state&&a.state.key||"default",i?{pathname:r.location.pathname,search:r.location.search,hash:r.location.hash}:void 0)}function n(r,a){return typeof a=="string"?a:te(a)}return Et(t,n,null,e)}function O(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function M(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Rt(){return Math.random().toString(36).substring(2,10)}function He(e,t){return{usr:e.state,key:e.key,idx:t,masked:e.mask?{pathname:e.pathname,search:e.search,hash:e.hash}:void 0}}function Re(e,t,n=null,r,a){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?K(t):t,state:n,key:t&&t.key||r||Rt(),mask:a}}function te({pathname:e="/",search:t="",hash:n=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),n&&n!=="#"&&(e+=n.charAt(0)==="#"?n:"#"+n),e}function K(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function Et(e,t,n,r={}){let{window:a=document.defaultView,v5Compat:i=!1}=r,s=a.history,f="POP",l=null,o=d();o==null&&(o=0,s.replaceState({...s.state,idx:o},""));function d(){return(s.state||{idx:null}).idx}function h(){f="POP";let m=d(),w=m==null?null:m-o;o=m,l&&l({action:f,location:g.location,delta:w})}function y(m,w){f="PUSH";let _=Me(m)?m:Re(g.location,m,w);o=d()+1;let x=He(_,o),$=g.createHref(_.mask||_);try{s.pushState(x,"",$)}catch(k){if(k instanceof DOMException&&k.name==="DataCloneError")throw k;a.location.assign($)}i&&l&&l({action:f,location:g.location,delta:1})}function C(m,w){f="REPLACE";let _=Me(m)?m:Re(g.location,m,w);o=d();let x=He(_,o),$=g.createHref(_.mask||_);s.replaceState(x,"",$),i&&l&&l({action:f,location:g.location,delta:0})}function E(m){return wt(m)}let g={get action(){return f},get location(){return e(a,s)},listen(m){if(l)throw new Error("A history only accepts one active listener");return a.addEventListener(Ie,h),l=m,()=>{a.removeEventListener(Ie,h),l=null}},createHref(m){return t(a,m)},createURL:E,encodeLocation(m){let w=E(m);return{pathname:w.pathname,search:w.search,hash:w.hash}},push:y,replace:C,go(m){return s.go(m)}};return g}function wt(e,t=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),O(n,"No window.location.(origin|href) available to create URL");let r=typeof e=="string"?e:te(e);return r=r.replace(/ $/,"%20"),!t&&r.startsWith("//")&&(r=n+r),new URL(r,n)}function Ye(e,t,n="/"){return St(e,t,n,!1)}function St(e,t,n,r,a){let i=typeof t=="string"?K(t):t,s=z(i.pathname||"/",n);if(s==null)return null;let f=Ct(e),l=null,o=Nt(s);for(let d=0;l==null&&d<f.length;++d)l=At(f[d],o,r);return l}function Ct(e){let t=ze(e);return _t(t),t}function ze(e,t=[],n=[],r="",a=!1){let i=(s,f,l=a,o)=>{let d={relativePath:o===void 0?s.path||"":o,caseSensitive:s.caseSensitive===!0,childrenIndex:f,route:s};if(d.relativePath.startsWith("/")){if(!d.relativePath.startsWith(r)&&l)return;O(d.relativePath.startsWith(r),`Absolute route path "${d.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),d.relativePath=d.relativePath.slice(r.length)}let h=H([r,d.relativePath]),y=n.concat(d);s.children&&s.children.length>0&&(O(s.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${h}".`),ze(s.children,t,y,h,l)),!(s.path==null&&!s.index)&&t.push({path:h,score:kt(h,s.index),routesMeta:y})};return e.forEach((s,f)=>{if(s.path===""||!s.path?.includes("?"))i(s,f);else for(let l of qe(s.path))i(s,f,!0,l)}),t}function qe(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,a=n.endsWith("?"),i=n.replace(/\?$/,"");if(r.length===0)return a?[i,""]:[i];let s=qe(r.join("/")),f=[];return f.push(...s.map(l=>l===""?i:[i,l].join("/"))),a&&f.push(...s),f.map(l=>e.startsWith("/")&&l===""?"/":l)}function _t(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:$t(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}var xt=/^:[\w-]+$/,bt=3,Pt=2,Tt=1,Lt=10,Ot=-2,Ue=e=>e==="*";function kt(e,t){let n=e.split("/"),r=n.length;return n.some(Ue)&&(r+=Ot),t&&(r+=Pt),n.filter(a=>!Ue(a)).reduce((a,i)=>a+(xt.test(i)?bt:i===""?Tt:Lt),r)}function $t(e,t){return e.length===t.length&&e.slice(0,-1).every((r,a)=>r===t[a])?e[e.length-1]-t[t.length-1]:0}function At(e,t,n=!1){let{routesMeta:r}=e,a={},i="/",s=[];for(let f=0;f<r.length;++f){let l=r[f],o=f===r.length-1,d=i==="/"?t:t.slice(i.length)||"/",h=le({path:l.relativePath,caseSensitive:l.caseSensitive,end:o},d),y=l.route;if(!h&&o&&n&&!r[r.length-1].route.index&&(h=le({path:l.relativePath,caseSensitive:l.caseSensitive,end:!1},d)),!h)return null;Object.assign(a,h.params),s.push({params:a,pathname:H([i,h.pathname]),pathnameBase:Ut(H([i,h.pathnameBase])),route:y}),h.pathnameBase!=="/"&&(i=H([i,h.pathnameBase]))}return s}function le(e,t){typeof e=="string"&&(e={path:e,caseSensitive:!1,end:!0});let[n,r]=Dt(e.path,e.caseSensitive,e.end),a=t.match(n);if(!a)return null;let i=a[0],s=i.replace(/(.)\/+$/,"$1"),f=a.slice(1);return{params:r.reduce((o,{paramName:d,isOptional:h},y)=>{if(d==="*"){let E=f[y]||"";s=i.slice(0,i.length-E.length).replace(/(.)\/+$/,"$1")}const C=f[y];return h&&!C?o[d]=void 0:o[d]=(C||"").replace(/%2F/g,"/"),o},{}),pathname:i,pathnameBase:s,pattern:e}}function Dt(e,t=!1,n=!0){M(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let r=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(s,f,l,o,d)=>{if(r.push({paramName:f,isOptional:l!=null}),l){let h=d.charAt(o+s.length);return h&&h!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return e.endsWith("*")?(r.push({paramName:"*"}),a+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?a+="\\/*$":e!==""&&e!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),r]}function Nt(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return M(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function z(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}var It=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function Mt(e,t="/"){let{pathname:n,search:r="",hash:a=""}=typeof e=="string"?K(e):e,i;return n?(n=Ge(n),n.startsWith("/")?i=Fe(n.substring(1),"/"):i=Fe(n,t)):i=t,{pathname:i,search:Ft(r),hash:Bt(a)}}function Fe(e,t){let n=se(t).split("/");return e.split("/").forEach(a=>{a===".."?n.length>1&&n.pop():a!=="."&&n.push(a)}),n.length>1?n.join("/"):"/"}function ye(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`}function Ht(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Se(e){let t=Ht(e);return t.map((n,r)=>r===t.length-1?n.pathname:n.pathnameBase)}function ce(e,t,n,r=!1){let a;typeof e=="string"?a=K(e):(a={...e},O(!a.pathname||!a.pathname.includes("?"),ye("?","pathname","search",a)),O(!a.pathname||!a.pathname.includes("#"),ye("#","pathname","hash",a)),O(!a.search||!a.search.includes("#"),ye("#","search","hash",a)));let i=e===""||a.pathname==="",s=i?"/":a.pathname,f;if(s==null)f=n;else{let h=t.length-1;if(!r&&s.startsWith("..")){let y=s.split("/");for(;y[0]==="..";)y.shift(),h-=1;a.pathname=y.join("/")}f=h>=0?t[h]:"/"}let l=Mt(a,f),o=s&&s!=="/"&&s.endsWith("/"),d=(i||s===".")&&n.endsWith("/");return!l.pathname.endsWith("/")&&(o||d)&&(l.pathname+="/"),l}var Ge=e=>e.replace(/\/\/+/g,"/"),H=e=>Ge(e.join("/")),se=e=>e.replace(/\/+$/,""),Ut=e=>se(e).replace(/^\/*/,"/"),Ft=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,Bt=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e,jt=class{constructor(e,t,n,r=!1){this.status=e,this.statusText=t||"",this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function Wt(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}function Yt(e){let t=e.map(n=>n.route.path).filter(Boolean);return H(t)||"/"}var Ke=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Ve(e,t){let n=e;if(typeof n!="string"||!It.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,a=!1;if(Ke)try{let i=new URL(window.location.href),s=n.startsWith("//")?new URL(i.protocol+n):new URL(n),f=z(s.pathname,t);s.origin===i.origin&&f!=null?n=f+s.search+s.hash:a=!0}catch{M(!1,`<Link to="${n}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:a,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var Je=["POST","PUT","PATCH","DELETE"];new Set(Je);var zt=["GET",...Je];new Set(zt);var V=c.createContext(null);V.displayName="DataRouter";var fe=c.createContext(null);fe.displayName="DataRouterState";var Xe=c.createContext(!1);function qt(){return c.useContext(Xe)}var Qe=c.createContext({isTransitioning:!1});Qe.displayName="ViewTransition";var Gt=c.createContext(new Map);Gt.displayName="Fetchers";var Kt=c.createContext(null);Kt.displayName="Await";var N=c.createContext(null);N.displayName="Navigation";var re=c.createContext(null);re.displayName="Location";var U=c.createContext({outlet:null,matches:[],isDataRoute:!1});U.displayName="Route";var Ce=c.createContext(null);Ce.displayName="RouteError";var Ze="REACT_ROUTER_ERROR",Vt="REDIRECT",Jt="ROUTE_ERROR_RESPONSE";function Xt(e){if(e.startsWith(`${Ze}:${Vt}:{`))try{let t=JSON.parse(e.slice(28));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.location=="string"&&typeof t.reloadDocument=="boolean"&&typeof t.replace=="boolean")return t}catch{}}function Qt(e){if(e.startsWith(`${Ze}:${Jt}:{`))try{let t=JSON.parse(e.slice(40));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string")return new jt(t.status,t.statusText,t.data)}catch{}}function Zt(e,{relative:t}={}){O(J(),"useHref() may be used only in the context of a <Router> component.");let{basename:n,navigator:r}=c.useContext(N),{hash:a,pathname:i,search:s}=ne(e,{relative:t}),f=i;return n!=="/"&&(f=i==="/"?n:H([n,i])),r.createHref({pathname:f,search:s,hash:a})}function J(){return c.useContext(re)!=null}function F(){return O(J(),"useLocation() may be used only in the context of a <Router> component."),c.useContext(re).location}var et="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function tt(e){c.useContext(N).static||c.useLayoutEffect(e)}function _e(){let{isDataRoute:e}=c.useContext(U);return e?dr():er()}function er(){O(J(),"useNavigate() may be used only in the context of a <Router> component.");let e=c.useContext(V),{basename:t,navigator:n}=c.useContext(N),{matches:r}=c.useContext(U),{pathname:a}=F(),i=JSON.stringify(Se(r)),s=c.useRef(!1);return tt(()=>{s.current=!0}),c.useCallback((l,o={})=>{if(M(s.current,et),!s.current)return;if(typeof l=="number"){n.go(l);return}let d=ce(l,JSON.parse(i),a,o.relative==="path");e==null&&t!=="/"&&(d.pathname=d.pathname==="/"?t:H([t,d.pathname])),(o.replace?n.replace:n.push)(d,o.state,o)},[t,n,i,a,e])}c.createContext(null);function Xr(){let{matches:e}=c.useContext(U);return e[e.length-1]?.params??{}}function ne(e,{relative:t}={}){let{matches:n}=c.useContext(U),{pathname:r}=F(),a=JSON.stringify(Se(n));return c.useMemo(()=>ce(e,JSON.parse(a),r,t==="path"),[e,a,r,t])}function tr(e,t){return rt(e,t)}function rt(e,t,n){O(J(),"useRoutes() may be used only in the context of a <Router> component.");let{navigator:r}=c.useContext(N),{matches:a}=c.useContext(U),i=a[a.length-1],s=i?i.params:{},f=i?i.pathname:"/",l=i?i.pathnameBase:"/",o=i&&i.route;{let m=o&&o.path||"";at(f,!o||m.endsWith("*")||m.endsWith("*?"),`You rendered descendant <Routes> (or called \`useRoutes()\`) at "${f}" (under <Route path="${m}">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
2
+
3
+ Please change the parent <Route path="${m}"> to <Route path="${m==="/"?"*":`${m}/*`}">.`)}let d=F(),h;if(t){let m=typeof t=="string"?K(t):t;O(l==="/"||m.pathname?.startsWith(l),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${l}" but pathname "${m.pathname}" was given in the \`location\` prop.`),h=m}else h=d;let y=h.pathname||"/",C=y;if(l!=="/"){let m=l.replace(/^\//,"").split("/");C="/"+y.replace(/^\//,"").split("/").slice(m.length).join("/")}let E=n&&n.state.matches.length?n.state.matches.map(m=>Object.assign(m,{route:n.manifest[m.route.id]||m.route})):Ye(e,{pathname:C});M(o||E!=null,`No routes matched location "${h.pathname}${h.search}${h.hash}" `),M(E==null||E[E.length-1].route.element!==void 0||E[E.length-1].route.Component!==void 0||E[E.length-1].route.lazy!==void 0,`Matched leaf route at location "${h.pathname}${h.search}${h.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let g=ir(E&&E.map(m=>Object.assign({},m,{params:Object.assign({},s,m.params),pathname:H([l,r.encodeLocation?r.encodeLocation(m.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:m.pathname]),pathnameBase:m.pathnameBase==="/"?l:H([l,r.encodeLocation?r.encodeLocation(m.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:m.pathnameBase])})),a,n);return t&&g?c.createElement(re.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...h},navigationType:"POP"}},g):g}function rr(){let e=fr(),t=Wt(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:r},i={padding:"2px 4px",backgroundColor:r},s=null;return console.error("Error handled by React Router default ErrorBoundary:",e),s=c.createElement(c.Fragment,null,c.createElement("p",null,"💿 Hey developer 👋"),c.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",c.createElement("code",{style:i},"ErrorBoundary")," or"," ",c.createElement("code",{style:i},"errorElement")," prop on your route.")),c.createElement(c.Fragment,null,c.createElement("h2",null,"Unexpected Application Error!"),c.createElement("h3",{style:{fontStyle:"italic"}},t),n?c.createElement("pre",{style:a},n):null,s)}var nr=c.createElement(rr,null),nt=class extends c.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error("React Router caught the following error during render",e)}render(){let e=this.state.error;if(this.context&&typeof e=="object"&&e&&"digest"in e&&typeof e.digest=="string"){const n=Qt(e.digest);n&&(e=n)}let t=e!==void 0?c.createElement(U.Provider,{value:this.props.routeContext},c.createElement(Ce.Provider,{value:e,children:this.props.component})):this.props.children;return this.context?c.createElement(ar,{error:e},t):t}};nt.contextType=Xe;var ge=new WeakMap;function ar({children:e,error:t}){let{basename:n}=c.useContext(N);if(typeof t=="object"&&t&&"digest"in t&&typeof t.digest=="string"){let r=Xt(t.digest);if(r){let a=ge.get(t);if(a)throw a;let i=Ve(r.location,n);if(Ke&&!ge.get(t))if(i.isExternal||r.reloadDocument)window.location.href=i.absoluteURL||i.to;else{const s=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(i.to,{replace:r.replace}));throw ge.set(t,s),s}return c.createElement("meta",{httpEquiv:"refresh",content:`0;url=${i.absoluteURL||i.to}`})}}return e}function or({routeContext:e,match:t,children:n}){let r=c.useContext(V);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),c.createElement(U.Provider,{value:e},n)}function ir(e,t=[],n){let r=n?.state;if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let a=e,i=r?.errors;if(i!=null){let d=a.findIndex(h=>h.route.id&&i?.[h.route.id]!==void 0);O(d>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(i).join(",")}`),a=a.slice(0,Math.min(a.length,d+1))}let s=!1,f=-1;if(n&&r){s=r.renderFallback;for(let d=0;d<a.length;d++){let h=a[d];if((h.route.HydrateFallback||h.route.hydrateFallbackElement)&&(f=d),h.route.id){let{loaderData:y,errors:C}=r,E=h.route.loader&&!y.hasOwnProperty(h.route.id)&&(!C||C[h.route.id]===void 0);if(h.route.lazy||E){n.isStatic&&(s=!0),f>=0?a=a.slice(0,f+1):a=[a[0]];break}}}}let l=n?.onError,o=r&&l?(d,h)=>{l(d,{location:r.location,params:r.matches?.[0]?.params??{},pattern:Yt(r.matches),errorInfo:h})}:void 0;return a.reduceRight((d,h,y)=>{let C,E=!1,g=null,m=null;r&&(C=i&&h.route.id?i[h.route.id]:void 0,g=h.route.errorElement||nr,s&&(f<0&&y===0?(at("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),E=!0,m=null):f===y&&(E=!0,m=h.route.hydrateFallbackElement||null)));let w=t.concat(a.slice(0,y+1)),_=()=>{let x;return C?x=g:E?x=m:h.route.Component?x=c.createElement(h.route.Component,null):h.route.element?x=h.route.element:x=d,c.createElement(or,{match:h,routeContext:{outlet:d,matches:w,isDataRoute:r!=null},children:x})};return r&&(h.route.ErrorBoundary||h.route.errorElement||y===0)?c.createElement(nt,{location:r.location,revalidation:r.revalidation,component:g,error:C,children:_(),routeContext:{outlet:null,matches:w,isDataRoute:!0},onError:o}):_()},null)}function xe(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function ur(e){let t=c.useContext(V);return O(t,xe(e)),t}function lr(e){let t=c.useContext(fe);return O(t,xe(e)),t}function sr(e){let t=c.useContext(U);return O(t,xe(e)),t}function be(e){let t=sr(e),n=t.matches[t.matches.length-1];return O(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}function cr(){return be("useRouteId")}function fr(){let e=c.useContext(Ce),t=lr("useRouteError"),n=be("useRouteError");return e!==void 0?e:t.errors?.[n]}function dr(){let{router:e}=ur("useNavigate"),t=be("useNavigate"),n=c.useRef(!1);return tt(()=>{n.current=!0}),c.useCallback(async(a,i={})=>{M(n.current,et),n.current&&(typeof a=="number"?await e.navigate(a):await e.navigate(a,{fromRouteId:t,...i}))},[e,t])}var Be={};function at(e,t,n){!t&&!Be[e]&&(Be[e]=!0,M(!1,n))}c.memo(hr);function hr({routes:e,manifest:t,future:n,state:r,isStatic:a,onError:i}){return rt(e,void 0,{manifest:t,state:r,isStatic:a,onError:i})}function Qr({to:e,replace:t,state:n,relative:r}){O(J(),"<Navigate> may be used only in the context of a <Router> component.");let{static:a}=c.useContext(N);M(!a,"<Navigate> must not be used on the initial render in a <StaticRouter>. This is a no-op, but you should modify your code so the <Navigate> is only ever rendered in response to some user interaction or state change.");let{matches:i}=c.useContext(U),{pathname:s}=F(),f=_e(),l=ce(e,Se(i),s,r==="path"),o=JSON.stringify(l);return c.useEffect(()=>{f(JSON.parse(o),{replace:t,state:n,relative:r})},[f,o,r,t,n]),null}function pr(e){O(!1,"A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.")}function mr({basename:e="/",children:t=null,location:n,navigationType:r="POP",navigator:a,static:i=!1,useTransitions:s}){O(!J(),"You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");let f=e.replace(/^\/*/,"/"),l=c.useMemo(()=>({basename:f,navigator:a,static:i,useTransitions:s,future:{}}),[f,a,i,s]);typeof n=="string"&&(n=K(n));let{pathname:o="/",search:d="",hash:h="",state:y=null,key:C="default",mask:E}=n,g=c.useMemo(()=>{let m=z(o,f);return m==null?null:{location:{pathname:m,search:d,hash:h,state:y,key:C,mask:E},navigationType:r}},[f,o,d,h,y,C,r,E]);return M(g!=null,`<Router basename="${f}"> is not able to match the URL "${o}${d}${h}" because it does not start with the basename, so the <Router> won't render anything.`),g==null?null:c.createElement(N.Provider,{value:l},c.createElement(re.Provider,{children:t,value:g}))}function Zr({children:e,location:t}){return tr(Ee(e),t)}function Ee(e,t=[]){let n=[];return c.Children.forEach(e,(r,a)=>{if(!c.isValidElement(r))return;let i=[...t,a];if(r.type===c.Fragment){n.push.apply(n,Ee(r.props.children,i));return}O(r.type===pr,`[${typeof r.type=="string"?r.type:r.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),O(!r.props.index||!r.props.children,"An index route cannot have child routes.");let s={id:r.props.id||i.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,middleware:r.props.middleware,loader:r.props.loader,action:r.props.action,hydrateFallbackElement:r.props.hydrateFallbackElement,HydrateFallback:r.props.HydrateFallback,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.hasErrorBoundary===!0||r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(s.children=Ee(r.props.children,i)),n.push(s)}),n}var ie="get",ue="application/x-www-form-urlencoded";function de(e){return typeof HTMLElement<"u"&&e instanceof HTMLElement}function yr(e){return de(e)&&e.tagName.toLowerCase()==="button"}function gr(e){return de(e)&&e.tagName.toLowerCase()==="form"}function vr(e){return de(e)&&e.tagName.toLowerCase()==="input"}function Rr(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Er(e,t){return e.button===0&&(!t||t==="_self")&&!Rr(e)}function we(e=""){return new URLSearchParams(typeof e=="string"||Array.isArray(e)||e instanceof URLSearchParams?e:Object.keys(e).reduce((t,n)=>{let r=e[n];return t.concat(Array.isArray(r)?r.map(a=>[n,a]):[[n,r]])},[]))}function wr(e,t){let n=we(e);return t&&t.forEach((r,a)=>{n.has(a)||t.getAll(a).forEach(i=>{n.append(a,i)})}),n}var oe=null;function Sr(){if(oe===null)try{new FormData(document.createElement("form"),0),oe=!1}catch{oe=!0}return oe}var Cr=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function ve(e){return e!=null&&!Cr.has(e)?(M(!1,`"${e}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${ue}"`),null):e}function _r(e,t){let n,r,a,i,s;if(gr(e)){let f=e.getAttribute("action");r=f?z(f,t):null,n=e.getAttribute("method")||ie,a=ve(e.getAttribute("enctype"))||ue,i=new FormData(e)}else if(yr(e)||vr(e)&&(e.type==="submit"||e.type==="image")){let f=e.form;if(f==null)throw new Error('Cannot submit a <button> or <input type="submit"> without a <form>');let l=e.getAttribute("formaction")||f.getAttribute("action");if(r=l?z(l,t):null,n=e.getAttribute("formmethod")||f.getAttribute("method")||ie,a=ve(e.getAttribute("formenctype"))||ve(f.getAttribute("enctype"))||ue,i=new FormData(f,e),!Sr()){let{name:o,type:d,value:h}=e;if(d==="image"){let y=o?`${o}.`:"";i.append(`${y}x`,"0"),i.append(`${y}y`,"0")}else o&&i.append(o,h)}}else{if(de(e))throw new Error('Cannot submit element that is not <form>, <button>, or <input type="submit|image">');n=ie,r=null,a=ue,s=e}return i&&a==="text/plain"&&(s=i,i=void 0),{action:r,method:n.toLowerCase(),encType:a,formData:i,body:s}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");function Pe(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function ot(e,t,n,r){let a=typeof e=="string"?new URL(e,typeof window>"u"?"server://singlefetch/":window.location.origin):e;return n?a.pathname.endsWith("/")?a.pathname=`${a.pathname}_.${r}`:a.pathname=`${a.pathname}.${r}`:a.pathname==="/"?a.pathname=`_root.${r}`:t&&z(a.pathname,t)==="/"?a.pathname=`${se(t)}/_root.${r}`:a.pathname=`${se(a.pathname)}.${r}`,a}async function xr(e,t){if(e.id in t)return t[e.id];try{let n=await import(e.module);return t[e.id]=n,n}catch(n){return console.error(`Error loading route module \`${e.module}\`, reloading page...`),console.error(n),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise(()=>{})}}function br(e){return e==null?!1:e.href==null?e.rel==="preload"&&typeof e.imageSrcSet=="string"&&typeof e.imageSizes=="string":typeof e.rel=="string"&&typeof e.href=="string"}async function Pr(e,t,n){let r=await Promise.all(e.map(async a=>{let i=t.routes[a.route.id];if(i){let s=await xr(i,n);return s.links?s.links():[]}return[]}));return kr(r.flat(1).filter(br).filter(a=>a.rel==="stylesheet"||a.rel==="preload").map(a=>a.rel==="stylesheet"?{...a,rel:"prefetch",as:"style"}:{...a,rel:"prefetch"}))}function je(e,t,n,r,a,i){let s=(l,o)=>n[o]?l.route.id!==n[o].route.id:!0,f=(l,o)=>n[o].pathname!==l.pathname||n[o].route.path?.endsWith("*")&&n[o].params["*"]!==l.params["*"];return i==="assets"?t.filter((l,o)=>s(l,o)||f(l,o)):i==="data"?t.filter((l,o)=>{let d=r.routes[l.route.id];if(!d||!d.hasLoader)return!1;if(s(l,o)||f(l,o))return!0;if(l.route.shouldRevalidate){let h=l.route.shouldRevalidate({currentUrl:new URL(a.pathname+a.search+a.hash,window.origin),currentParams:n[0]?.params||{},nextUrl:new URL(e,window.origin),nextParams:l.params,defaultShouldRevalidate:!0});if(typeof h=="boolean")return h}return!0}):[]}function Tr(e,t,{includeHydrateFallback:n}={}){return Lr(e.map(r=>{let a=t.routes[r.route.id];if(!a)return[];let i=[a.module];return a.clientActionModule&&(i=i.concat(a.clientActionModule)),a.clientLoaderModule&&(i=i.concat(a.clientLoaderModule)),n&&a.hydrateFallbackModule&&(i=i.concat(a.hydrateFallbackModule)),a.imports&&(i=i.concat(a.imports)),i}).flat(1))}function Lr(e){return[...new Set(e)]}function Or(e){let t={},n=Object.keys(e).sort();for(let r of n)t[r]=e[r];return t}function kr(e,t){let n=new Set;return new Set(t),e.reduce((r,a)=>{let i=JSON.stringify(Or(a));return n.has(i)||(n.add(i),r.push({key:i,link:a})),r},[])}function Te(){let e=c.useContext(V);return Pe(e,"You must render this element inside a <DataRouterContext.Provider> element"),e}function $r(){let e=c.useContext(fe);return Pe(e,"You must render this element inside a <DataRouterStateContext.Provider> element"),e}var Le=c.createContext(void 0);Le.displayName="FrameworkContext";function Oe(){let e=c.useContext(Le);return Pe(e,"You must render this element inside a <HydratedRouter> element"),e}function Ar(e,t){let n=c.useContext(Le),[r,a]=c.useState(!1),[i,s]=c.useState(!1),{onFocus:f,onBlur:l,onMouseEnter:o,onMouseLeave:d,onTouchStart:h}=t,y=c.useRef(null);c.useEffect(()=>{if(e==="render"&&s(!0),e==="viewport"){let g=w=>{w.forEach(_=>{s(_.isIntersecting)})},m=new IntersectionObserver(g,{threshold:.5});return y.current&&m.observe(y.current),()=>{m.disconnect()}}},[e]),c.useEffect(()=>{if(r){let g=setTimeout(()=>{s(!0)},100);return()=>{clearTimeout(g)}}},[r]);let C=()=>{a(!0)},E=()=>{a(!1),s(!1)};return n?e!=="intent"?[i,y,{}]:[i,y,{onFocus:ee(f,C),onBlur:ee(l,E),onMouseEnter:ee(o,C),onMouseLeave:ee(d,E),onTouchStart:ee(h,C)}]:[!1,y,{}]}function ee(e,t){return n=>{e&&e(n),n.defaultPrevented||t(n)}}function Dr({page:e,...t}){let n=qt(),{router:r}=Te(),a=c.useMemo(()=>Ye(r.routes,e,r.basename),[r.routes,e,r.basename]);return a?n?c.createElement(Ir,{page:e,matches:a,...t}):c.createElement(Mr,{page:e,matches:a,...t}):null}function Nr(e){let{manifest:t,routeModules:n}=Oe(),[r,a]=c.useState([]);return c.useEffect(()=>{let i=!1;return Pr(e,t,n).then(s=>{i||a(s)}),()=>{i=!0}},[e,t,n]),r}function Ir({page:e,matches:t,...n}){let r=F(),{future:a}=Oe(),{basename:i}=Te(),s=c.useMemo(()=>{if(e===r.pathname+r.search+r.hash)return[];let f=ot(e,i,a.unstable_trailingSlashAwareDataRequests,"rsc"),l=!1,o=[];for(let d of t)typeof d.route.shouldRevalidate=="function"?l=!0:o.push(d.route.id);return l&&o.length>0&&f.searchParams.set("_routes",o.join(",")),[f.pathname+f.search]},[i,a.unstable_trailingSlashAwareDataRequests,e,r,t]);return c.createElement(c.Fragment,null,s.map(f=>c.createElement("link",{key:f,rel:"prefetch",as:"fetch",href:f,...n})))}function Mr({page:e,matches:t,...n}){let r=F(),{future:a,manifest:i,routeModules:s}=Oe(),{basename:f}=Te(),{loaderData:l,matches:o}=$r(),d=c.useMemo(()=>je(e,t,o,i,r,"data"),[e,t,o,i,r]),h=c.useMemo(()=>je(e,t,o,i,r,"assets"),[e,t,o,i,r]),y=c.useMemo(()=>{if(e===r.pathname+r.search+r.hash)return[];let g=new Set,m=!1;if(t.forEach(_=>{let x=i.routes[_.route.id];!x||!x.hasLoader||(!d.some($=>$.route.id===_.route.id)&&_.route.id in l&&s[_.route.id]?.shouldRevalidate||x.hasClientLoader?m=!0:g.add(_.route.id))}),g.size===0)return[];let w=ot(e,f,a.unstable_trailingSlashAwareDataRequests,"data");return m&&g.size>0&&w.searchParams.set("_routes",t.filter(_=>g.has(_.route.id)).map(_=>_.route.id).join(",")),[w.pathname+w.search]},[f,a.unstable_trailingSlashAwareDataRequests,l,r,i,d,t,e,s]),C=c.useMemo(()=>Tr(h,i),[h,i]),E=Nr(h);return c.createElement(c.Fragment,null,y.map(g=>c.createElement("link",{key:g,rel:"prefetch",as:"fetch",href:g,...n})),C.map(g=>c.createElement("link",{key:g,rel:"modulepreload",href:g,...n})),E.map(({key:g,link:m})=>c.createElement("link",{key:g,nonce:n.nonce,...m,crossOrigin:m.crossOrigin??n.crossOrigin})))}function Hr(...e){return t=>{e.forEach(n=>{typeof n=="function"?n(t):n!=null&&(n.current=t)})}}var Ur=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";try{Ur&&(window.__reactRouterVersion="7.15.0")}catch{}function en({basename:e,children:t,useTransitions:n,window:r}){let a=c.useRef();a.current==null&&(a.current=vt({window:r,v5Compat:!0}));let i=a.current,[s,f]=c.useState({action:i.action,location:i.location}),l=c.useCallback(o=>{n===!1?f(o):c.startTransition(()=>f(o))},[n]);return c.useLayoutEffect(()=>i.listen(l),[i,l]),c.createElement(mr,{basename:e,children:t,location:s.location,navigationType:s.action,navigator:i,useTransitions:n})}var it=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,ut=c.forwardRef(function({onClick:t,discover:n="render",prefetch:r="none",relative:a,reloadDocument:i,replace:s,mask:f,state:l,target:o,to:d,preventScrollReset:h,viewTransition:y,defaultShouldRevalidate:C,...E},g){let{basename:m,navigator:w,useTransitions:_}=c.useContext(N),x=typeof d=="string"&&it.test(d),$=Ve(d,m);d=$.to;let k=Zt(d,{relative:a}),I=F(),b=null;if(f){let B=ce(f,[],I.mask?I.mask.pathname:"/",!0);m!=="/"&&(B.pathname=B.pathname==="/"?m:H([m,B.pathname])),b=w.createHref(B)}let[W,Y,X]=Ar(r,E),Q=Wr(d,{replace:s,mask:f,state:l,target:o,preventScrollReset:h,relative:a,viewTransition:y,defaultShouldRevalidate:C,useTransitions:_});function he(B){t&&t(B),B.defaultPrevented||Q(B)}let Z=!($.isExternal||i),q=c.createElement("a",{...E,...X,href:(Z?b:void 0)||$.absoluteURL||k,onClick:Z?he:t,ref:Hr(g,Y),target:o,"data-discover":!x&&n==="render"?"true":void 0});return W&&!x?c.createElement(c.Fragment,null,q,c.createElement(Dr,{page:k})):q});ut.displayName="Link";var Fr=c.forwardRef(function({"aria-current":t="page",caseSensitive:n=!1,className:r="",end:a=!1,style:i,to:s,viewTransition:f,children:l,...o},d){let h=ne(s,{relative:o.relative}),y=F(),C=c.useContext(fe),{navigator:E,basename:g}=c.useContext(N),m=C!=null&&Kr(h)&&f===!0,w=E.encodeLocation?E.encodeLocation(h).pathname:h.pathname,_=y.pathname,x=C&&C.navigation&&C.navigation.location?C.navigation.location.pathname:null;n||(_=_.toLowerCase(),x=x?x.toLowerCase():null,w=w.toLowerCase()),x&&g&&(x=z(x,g)||x);const $=w!=="/"&&w.endsWith("/")?w.length-1:w.length;let k=_===w||!a&&_.startsWith(w)&&_.charAt($)==="/",I=x!=null&&(x===w||!a&&x.startsWith(w)&&x.charAt(w.length)==="/"),b={isActive:k,isPending:I,isTransitioning:m},W=k?t:void 0,Y;typeof r=="function"?Y=r(b):Y=[r,k?"active":null,I?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let X=typeof i=="function"?i(b):i;return c.createElement(ut,{...o,"aria-current":W,className:Y,ref:d,style:X,to:s,viewTransition:f},typeof l=="function"?l(b):l)});Fr.displayName="NavLink";var Br=c.forwardRef(({discover:e="render",fetcherKey:t,navigate:n,reloadDocument:r,replace:a,state:i,method:s=ie,action:f,onSubmit:l,relative:o,preventScrollReset:d,viewTransition:h,defaultShouldRevalidate:y,...C},E)=>{let{useTransitions:g}=c.useContext(N),m=qr(),w=Gr(f,{relative:o}),_=s.toLowerCase()==="get"?"get":"post",x=typeof f=="string"&&it.test(f),$=k=>{if(l&&l(k),k.defaultPrevented)return;k.preventDefault();let I=k.nativeEvent.submitter,b=I?.getAttribute("formmethod")||s,W=()=>m(I||k.currentTarget,{fetcherKey:t,method:b,navigate:n,replace:a,state:i,relative:o,preventScrollReset:d,viewTransition:h,defaultShouldRevalidate:y});g&&n!==!1?c.startTransition(()=>W()):W()};return c.createElement("form",{ref:E,method:_,action:w,onSubmit:r?l:$,...C,"data-discover":!x&&e==="render"?"true":void 0})});Br.displayName="Form";function jr(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function lt(e){let t=c.useContext(V);return O(t,jr(e)),t}function Wr(e,{target:t,replace:n,mask:r,state:a,preventScrollReset:i,relative:s,viewTransition:f,defaultShouldRevalidate:l,useTransitions:o}={}){let d=_e(),h=F(),y=ne(e,{relative:s});return c.useCallback(C=>{if(Er(C,t)){C.preventDefault();let E=n!==void 0?n:te(h)===te(y),g=()=>d(e,{replace:E,mask:r,state:a,preventScrollReset:i,relative:s,viewTransition:f,defaultShouldRevalidate:l});o?c.startTransition(()=>g()):g()}},[h,d,y,n,r,a,t,e,i,s,f,l,o])}function tn(e){M(typeof URLSearchParams<"u","You cannot use the `useSearchParams` hook in a browser that does not support the URLSearchParams API. If you need to support Internet Explorer 11, we recommend you load a polyfill such as https://github.com/ungap/url-search-params.");let t=c.useRef(we(e)),n=c.useRef(!1),r=F(),a=c.useMemo(()=>wr(r.search,n.current?null:t.current),[r.search]),i=_e(),s=c.useCallback((f,l)=>{const o=we(typeof f=="function"?f(new URLSearchParams(a)):f);n.current=!0,i("?"+o,l)},[i,a]);return[a,s]}var Yr=0,zr=()=>`__${String(++Yr)}__`;function qr(){let{router:e}=lt("useSubmit"),{basename:t}=c.useContext(N),n=cr(),r=e.fetch,a=e.navigate;return c.useCallback(async(i,s={})=>{let{action:f,method:l,encType:o,formData:d,body:h}=_r(i,t);if(s.navigate===!1){let y=s.fetcherKey||zr();await r(y,n,s.action||f,{defaultShouldRevalidate:s.defaultShouldRevalidate,preventScrollReset:s.preventScrollReset,formData:d,body:h,formMethod:s.method||l,formEncType:s.encType||o,flushSync:s.flushSync})}else await a(s.action||f,{defaultShouldRevalidate:s.defaultShouldRevalidate,preventScrollReset:s.preventScrollReset,formData:d,body:h,formMethod:s.method||l,formEncType:s.encType||o,replace:s.replace,state:s.state,fromRouteId:n,flushSync:s.flushSync,viewTransition:s.viewTransition})},[r,a,t,n])}function Gr(e,{relative:t}={}){let{basename:n}=c.useContext(N),r=c.useContext(U);O(r,"useFormAction must be used inside a RouteContext");let[a]=r.matches.slice(-1),i={...ne(e||".",{relative:t})},s=F();if(e==null){i.search=s.search;let f=new URLSearchParams(i.search),l=f.getAll("index");if(l.some(d=>d==="")){f.delete("index"),l.filter(h=>h).forEach(h=>f.append("index",h));let d=f.toString();i.search=d?`?${d}`:""}}return(!e||e===".")&&a.route.index&&(i.search=i.search?i.search.replace(/^\?/,"?index&"):"?index"),n!=="/"&&(i.pathname=i.pathname==="/"?n:H([n,i.pathname])),te(i)}function Kr(e,{relative:t}={}){let n=c.useContext(Qe);O(n!=null,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:r}=lt("useViewTransitionState"),a=ne(e,{relative:t});if(!n.isTransitioning)return!1;let i=z(n.currentLocation.pathname,r)||n.currentLocation.pathname,s=z(n.nextLocation.pathname,r)||n.nextLocation.pathname;return le(a.pathname,s)!=null||le(a.pathname,i)!=null}var rn=gt();export{en as B,ut as L,Fr as N,Jr as R,We as a,rn as b,Vr as c,gt as d,mt as e,_e as f,ht as g,tn as h,Xr as i,Zr as j,pr as k,Qr as l,c as r,F as u};
Binary file
Binary file
Binary file
@@ -0,0 +1,59 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="WaCrawl">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#0f172a"/>
5
+ <stop offset="100%" stop-color="#020617"/>
6
+ </linearGradient>
7
+ <linearGradient id="bar" x1="0%" y1="100%" x2="0%" y2="0%">
8
+ <stop offset="0%" stop-color="#16a34a"/>
9
+ <stop offset="55%" stop-color="#22c55e"/>
10
+ <stop offset="100%" stop-color="#4ade80"/>
11
+ </linearGradient>
12
+ <linearGradient id="trail" x1="0%" y1="50%" x2="100%" y2="50%">
13
+ <stop offset="0%" stop-color="#4ade80" stop-opacity="0"/>
14
+ <stop offset="35%" stop-color="#86efac" stop-opacity="0.95"/>
15
+ <stop offset="100%" stop-color="#bbf7d0" stop-opacity="0.35"/>
16
+ </linearGradient>
17
+ <radialGradient id="glow" cx="72%" cy="28%" r="42%">
18
+ <stop offset="0%" stop-color="#22c55e" stop-opacity="0.35"/>
19
+ <stop offset="100%" stop-color="#22c55e" stop-opacity="0"/>
20
+ </radialGradient>
21
+ <filter id="soft" x="-20%" y="-20%" width="140%" height="140%">
22
+ <feGaussianBlur stdDeviation="8"/>
23
+ </filter>
24
+ </defs>
25
+
26
+ <!-- App tile -->
27
+ <rect width="512" height="512" rx="112" fill="url(#bg)"/>
28
+ <rect width="512" height="512" rx="112" fill="url(#glow)"/>
29
+
30
+ <!-- Data trail: crawl path through the chart -->
31
+ <path
32
+ d="M108 332 C 168 300, 196 252, 248 236 S 332 188, 392 148"
33
+ fill="none"
34
+ stroke="url(#trail)"
35
+ stroke-width="18"
36
+ stroke-linecap="round"
37
+ filter="url(#soft)"
38
+ opacity="0.55"
39
+ />
40
+ <path
41
+ d="M108 332 C 168 300, 196 252, 248 236 S 332 188, 392 148"
42
+ fill="none"
43
+ stroke="#ecfdf5"
44
+ stroke-width="7"
45
+ stroke-linecap="round"
46
+ opacity="0.9"
47
+ />
48
+
49
+ <!-- Analytics bars -->
50
+ <rect x="118" y="286" width="72" height="118" rx="22" fill="url(#bar)" opacity="0.72"/>
51
+ <rect x="220" y="228" width="72" height="176" rx="22" fill="url(#bar)" opacity="0.88"/>
52
+ <rect x="322" y="162" width="72" height="242" rx="22" fill="url(#bar)"/>
53
+
54
+ <!-- Crawl nodes -->
55
+ <circle cx="154" cy="270" r="16" fill="#ecfdf5"/>
56
+ <circle cx="256" cy="212" r="16" fill="#ecfdf5"/>
57
+ <circle cx="358" cy="146" r="20" fill="#ecfdf5"/>
58
+ <circle cx="358" cy="146" r="10" fill="#16a34a"/>
59
+ </svg>
@@ -2,10 +2,19 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
+ <link rel="icon" href="/favicon.ico" sizes="any" />
6
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
7
+ <link rel="icon" href="/favicon-32x32.png" type="image/png" sizes="32x32" />
8
+ <link rel="icon" href="/favicon-16x16.png" type="image/png" sizes="16x16" />
9
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
10
+ <meta name="theme-color" content="#0f172a" />
5
11
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
12
  <title>WaCrawl Dashboard</title>
7
- <script type="module" crossorigin src="/assets/index-CFh_Wz98.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-DJiLhgJZ.css">
13
+ <script type="module" crossorigin src="/assets/index-wrxENU1e.js"></script>
14
+ <link rel="modulepreload" crossorigin href="/assets/react-D3RL6ob4.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/icons-BdElrSaU.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/charts-BMGh5n-d.js">
17
+ <link rel="stylesheet" crossorigin href="/assets/index-Bb8Zj42D.css">
9
18
  </head>
10
19
  <body>
11
20
  <div id="root"></div>
@@ -3,10 +3,13 @@
3
3
 
4
4
  const { spawn } = require('node:child_process');
5
5
  const { join } = require('node:path');
6
- const { existsSync } = require('node:fs');
6
+ const { existsSync, mkdtempSync, rmSync } = require('node:fs');
7
+ const { tmpdir } = require('node:os');
8
+ const { pathToFileURL } = require('node:url');
7
9
 
8
10
  const pkgRoot = join(__dirname, '..');
9
11
  const apiEntry = join(pkgRoot, 'apps', 'api', 'dist', 'index.js');
12
+ const demoEntry = join(pkgRoot, 'apps', 'api', 'dist', 'demo', 'demoArchive.js');
10
13
  const webDist = join(pkgRoot, 'apps', 'web', 'dist');
11
14
 
12
15
  if (!existsSync(apiEntry)) {
@@ -20,22 +23,68 @@ if (!existsSync(apiEntry)) {
20
23
  const PORT = process.env.PORT ?? '3001';
21
24
  const url = `http://127.0.0.1:${PORT}`;
22
25
 
23
- console.log(`WaCrawl Dashboard → ${url}`);
24
- if (!process.env.WACRAWL_DB) {
25
- console.log(' Database: ~/.wacrawl/wacrawl.db (override with WACRAWL_DB=<path>)');
26
+ /**
27
+ * Generate a throwaway archive and an environment pointing at nothing but it.
28
+ *
29
+ * The blank paths matter: dotenv never overrides a variable that is already set,
30
+ * so they stop an apps/api/.env in the current directory from naming a real
31
+ * archive. The API then reads no file of the user's, and the paths file being
32
+ * temporary keeps any Settings changes made during the demo out of their config.
33
+ */
34
+ async function prepareDemo() {
35
+ const dir = mkdtempSync(join(tmpdir(), 'wacrawl-demo-'));
36
+ try {
37
+ const { generateDemoArchive } = await import(pathToFileURL(demoEntry).href);
38
+ const archive = generateDemoArchive({ dir });
39
+ console.log(` Demo mode: ${archive.messages.toLocaleString('en-US')} invented messages. Your own archive is not touched.`);
40
+ return {
41
+ dir,
42
+ env: {
43
+ WACRAWL_DEMO: '1',
44
+ WACRAWL_DB: archive.dbPath,
45
+ WACRAWL_MEDIA_ROOT: archive.mediaRoot,
46
+ WACRAWL_PATHS_FILE: join(dir, 'dashboard-paths.json'),
47
+ WACRAWL_DISABLE_SYNC: '1',
48
+ WACRAWL_WHATSAPP_CONTAINER: '',
49
+ WACRAWL_CHAT_DB: '',
50
+ WACRAWL_CONTACTS_DB: '',
51
+ },
52
+ };
53
+ } catch (error) {
54
+ rmSync(dir, { recursive: true, force: true });
55
+ throw error;
56
+ }
26
57
  }
27
58
 
28
- const child = spawn(process.execPath, [apiEntry], {
29
- stdio: 'inherit',
30
- env: {
31
- ...process.env,
32
- WACRAWL_STATIC_DIR: webDist,
33
- PORT,
34
- },
35
- });
59
+ async function main() {
60
+ console.log(`WaCrawl Dashboard → ${url}`);
61
+
62
+ const demo = process.argv.includes('--demo') ? await prepareDemo() : null;
63
+ if (!demo && !process.env.WACRAWL_DB) {
64
+ console.log(' Database: ~/.wacrawl/wacrawl.db (override with WACRAWL_DB=<path>)');
65
+ }
66
+
67
+ const child = spawn(process.execPath, [apiEntry], {
68
+ stdio: 'inherit',
69
+ env: {
70
+ ...process.env,
71
+ ...demo?.env,
72
+ WACRAWL_STATIC_DIR: webDist,
73
+ PORT,
74
+ },
75
+ });
36
76
 
37
- child.on('exit', (code) => process.exit(code ?? 0));
77
+ child.on('exit', (code) => {
78
+ if (demo) rmSync(demo.dir, { recursive: true, force: true });
79
+ process.exit(code ?? 0);
80
+ });
38
81
 
39
- for (const sig of ['SIGINT', 'SIGTERM']) {
40
- process.on(sig, () => child.kill(sig));
82
+ for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
83
+ process.on(sig, () => child.kill(sig));
84
+ }
41
85
  }
86
+
87
+ main().catch((error) => {
88
+ console.error(`wacrawl-dashboard: ${error.message}`);
89
+ process.exit(1);
90
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wacrawl-dashboard",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Local WhatsApp analytics dashboard powered by a WaCrawl SQLite archive",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -26,6 +26,7 @@
26
26
  },
27
27
  "files": [
28
28
  "apps/api/dist",
29
+ "apps/api/package.json",
29
30
  "apps/web/dist",
30
31
  "bin"
31
32
  ],
@@ -38,8 +39,16 @@
38
39
  "build": "npm run build -w @wacrawl/api && npm run build -w @wacrawl/web",
39
40
  "test": "npm run test -w @wacrawl/api",
40
41
  "preview": "npm run preview -w @wacrawl/web",
41
- "typecheck": "npm run typecheck -w @wacrawl/api && npm run typecheck -w @wacrawl/web",
42
- "prepublishOnly": "npm run build",
42
+ "typecheck": "npm run typecheck -w @wacrawl/api && npm run typecheck -w @wacrawl/web && tsc -p e2e/tsconfig.json",
43
+ "lint": "eslint .",
44
+ "lint:fix": "eslint . --fix",
45
+ "verify": "npm run lint && npm run typecheck && npm test",
46
+ "verify:full": "npm run verify && npm run build && npm run test:e2e && npm run test:fresh-install",
47
+ "demo": "node bin/wacrawl-dashboard.js --demo",
48
+ "test:e2e": "playwright test --project=smoke",
49
+ "test:fresh-install": "node e2e/fresh-install.mjs",
50
+ "docs:screenshots": "playwright test --project=screenshots",
51
+ "prepublishOnly": "npm run verify && npm run build",
43
52
  "publish:verify-auth": "npm whoami",
44
53
  "publish:npm": "npm run publish:verify-auth && npm publish --access public"
45
54
  },
@@ -55,7 +64,14 @@
55
64
  "stopword": "^3.1.5"
56
65
  },
57
66
  "devDependencies": {
67
+ "@eslint/js": "^10.0.1",
68
+ "@playwright/test": "^1.64.1",
58
69
  "concurrently": "^9.2.1",
59
- "typescript": "^5.9.3"
70
+ "eslint": "^10.8.0",
71
+ "eslint-plugin-react-hooks": "^7.1.1",
72
+ "eslint-plugin-react-refresh": "^0.5.3",
73
+ "globals": "^17.9.0",
74
+ "typescript": "^5.9.3",
75
+ "typescript-eslint": "^8.66.0"
60
76
  }
61
77
  }