@opensite/hooks 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/opensite-hooks.umd.cjs +1 -1
- package/dist/browser/opensite-hooks.umd.js +1 -1
- package/dist/browser/opensite-hooks.umd.js.map +1 -1
- package/dist/core/usePlatformFromUrl.cjs +125 -1
- package/dist/core/usePlatformFromUrl.d.ts +1 -1
- package/dist/core/usePlatformFromUrl.js +125 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).OpensiteHooks={},e.React)}(this,function(e,t){"use strict";const n="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;function r(e,r,o={}){const u=t.useRef(e),i=t.useRef(null),c=t.useRef(null),a=t.useRef(null),s=o.leading??!1,l=o.trailing??!0,d=o.maxWait,f=Math.max(0,r);n(()=>{u.current=e},[e]);const m=t.useCallback(()=>{i.current&&(clearTimeout(i.current),i.current=null),c.current&&(clearTimeout(c.current),c.current=null)},[]),p=t.useCallback(()=>{if(!a.current)return;const e=a.current;a.current=null,u.current(...e)},[]),y=t.useCallback((...e)=>{a.current=e;if(s&&null===i.current&&null===c.current&&p(),i.current&&clearTimeout(i.current),l&&(i.current=setTimeout(()=>{i.current=null,a.current&&p(),c.current&&(clearTimeout(c.current),c.current=null)},f)),null!=d&&l&&!c.current){const e=Math.max(0,d);c.current=setTimeout(()=>{c.current=null,i.current&&(clearTimeout(i.current),i.current=null),a.current&&p()},e)}},[p,s,l,d,f]),w=t.useCallback(()=>{m(),a.current=null},[m]),g=t.useCallback(()=>{a.current&&(m(),p())},[m,p]);return t.useEffect(()=>()=>w(),[w]),{debouncedCallback:y,cancel:w,flush:g}}function o(e,n,o){const[u,i]=t.useState(e),{debouncedCallback:c,cancel:a}=r(e=>{i(e)},n,o);return t.useEffect(()=>{c(e)},[c,e]),t.useEffect(()=>()=>a(),[a]),u}const u=new Map([["instagram.com","instagram"],["www.instagram.com","instagram"],["instagr.am","instagram"],["www.instagr.am","instagram"],["linkedin.com","linkedin"],["www.linkedin.com","linkedin"],["ca.linkedin.com","linkedin"],["uk.linkedin.com","linkedin"],["in.linkedin.com","linkedin"],["lnkd.in","linkedin"],["google.com","google"],["www.google.com","google"],["maps.google.com","google"],["goo.gl","google"],["maps.app.goo.gl","google"],["g.co","google"],["facebook.com","facebook"],["www.facebook.com","facebook"],["m.facebook.com","facebook"],["fb.com","facebook"],["fb.me","facebook"],["on.fb.me","facebook"],["tiktok.com","tiktok"],["www.tiktok.com","tiktok"],["m.tiktok.com","tiktok"],["vm.tiktok.com","tiktok"],["vt.tiktok.com","tiktok"],["youtube.com","youtube"],["www.youtube.com","youtube"],["m.youtube.com","youtube"],["youtu.be","youtube"],["yelp.com","yelp"],["www.yelp.com","yelp"],["m.yelp.com","yelp"],["spotify.com","spotify"],["www.spotify.com","spotify"],["open.spotify.com","spotify"],["play.spotify.com","spotify"],["spoti.fi","spotify"],["spotify.link","spotify"],["apple.com","apple"],["www.apple.com","apple"],["music.apple.com","apple"],["podcasts.apple.com","apple"],["apps.apple.com","apple"],["itunes.apple.com","apple"],["x.com","x"],["www.x.com","x"],["twitter.com","x"],["www.twitter.com","x"],["t.co","x"]]);function i(e,r,o,u){const i=t.useRef(r);n(()=>{i.current=r},[r]),t.useEffect(()=>{const t="undefined"!=typeof Window&&o instanceof Window,n="undefined"!=typeof Document&&o instanceof Document,r=void 0===o?"undefined"!=typeof window?window:null:t||n||"undefined"!=typeof HTMLElement&&o instanceof HTMLElement?o:(c=o)&&"object"==typeof c&&"current"in c?o.current:null;var c;if(!(null==r?void 0:r.addEventListener))return;const a=e=>{const t=i.current;"function"==typeof t?t(e):t.handleEvent(e)};return r.addEventListener(e,a,u),()=>{r.removeEventListener(e,a,u)}},[e,o,u])}function c(){const[e,n]=t.useState(!1);return t.useEffect(()=>{n(!0)},[]),e}function a(e){const[r,o]=t.useState(()=>e instanceof Map||Array.isArray(e)?new Map(e):new Map),u=t.useRef(r);n(()=>{u.current=r},[r]);const i=t.useMemo(()=>({set:(e,t)=>{o(n=>{const r=new Map(n);return r.set(e,t),r})},setAll:e=>{o((Map,new Map(e)))},remove:e=>{o(t=>{const n=new Map(t);return n.delete(e),n})},clear:()=>o(new Map),get:e=>u.current.get(e),has:e=>u.current.has(e)}),[]);return[r,i]}const s="https://octane.buzz";function l(e){const t=function(e){return e.replace(/\/+$/,"")}(e.baseUrl??s),n=new URLSearchParams;return e.apiKey&&n.set("api_key",e.apiKey),n.set("url",e.url),`${t}/api/v1/extract/${e.endpoint}?${n.toString()}`}async function d(e){var t;if(!e.url||0===e.url.trim().length)return{ok:!1,error:{message:"URL is required."}};const n=l(e);try{const t=await fetch(n,{method:"GET",signal:e.signal});let r=null;try{r=await t.json()}catch{r=null}if(!t.ok){const e=r;return{ok:!1,error:{message:(null==e?void 0:e.error)??`Request failed with status ${t.status}.`,status:(null==e?void 0:e.status)??t.status,raw:r}}}return{ok:!0,response:r}}catch(r){return(null==(t=e.signal)?void 0:t.aborted)?{ok:!1,error:{message:"Request aborted."}}:{ok:!1,error:{message:r instanceof Error?r.message:"Request failed unexpectedly.",raw:r}}}}function f(e){const{endpoint:n,options:u,selectData:i,shouldSkip:l}=e,f=c(),[m,p]=t.useState({loading:!1}),[,y]=a(),w=u.cache??true,g=u.enabled??true,v=u.debounceMs??250,b=u.refreshDebounceMs??150,k=o(t.useMemo(()=>{var e;return(null==(e=u.url)?void 0:e.trim())??""},[u.url]),v),h=t.useRef(0),E=t.useRef(0),[C,T]=t.useState(0),{debouncedCallback:x,cancel:S}=r(()=>{h.current+=1,T(h.current)},b),M=t.useCallback(()=>{x()},[x]);t.useEffect(()=>()=>S(),[S]);const R=t.useMemo(()=>{if(!k)return"";const e=u.baseUrl??s,t=u.apiKey??"";return`${n}:${e}:${t}:${k}`},[n,u.apiKey,u.baseUrl,k]),L=t.useRef(null);return t.useEffect(()=>{var e;if(!f)return;if(!g||!k)return void p({loading:!1});if(null==l?void 0:l(k))return void p({loading:!1});const t=C!==E.current;if(t&&(E.current=C),w&&!t){const e=y.get(R);if(e)return void p({loading:!1,data:e.data,raw:e.raw,meta:e.meta})}null==(e=L.current)||e.abort();const r=new AbortController;return L.current=r,p(e=>({...e,loading:!0,error:void 0})),d({endpoint:n,url:k,apiKey:u.apiKey,baseUrl:u.baseUrl,signal:r.signal}).then(e=>{if(r.signal.aborted)return;if(!e.ok)return void p(t=>({...t,loading:!1,error:e.error}));const t=e.response,n=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:u,contentType:i,fetchedAt:c,bodyBytes:a,bodyTruncated:s,maxBodyBytes:l,cache:d}=e;return{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:u,contentType:i,fetchedAt:c,bodyBytes:a,bodyTruncated:s,maxBodyBytes:l,cache:d}}(t),o=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:u,contentType:i,fetchedAt:c,bodyBytes:a,bodyTruncated:s,maxBodyBytes:l,cache:d,...f}=e;return f}(t),u=i(o,t,n);w&&y.set(R,{data:u,raw:t,meta:n}),p({loading:!1,data:u,raw:t,meta:n})}).catch(e=>{r.signal.aborted||p(t=>({...t,loading:!1,error:{message:e instanceof Error?e.message:"Request failed unexpectedly.",raw:e}}))}),()=>{r.abort()}},[y,w,k,g,n,f,u.apiKey,u.baseUrl,R,C,i,l]),t.useMemo(()=>({...m,refresh:M}),[M,m])}const m=[/search\.google\.com\/local\/reviews/i,/google\.com\/maps\/place/i,/maps\.google\.com/i,/opentable\.com/i],p=(...e)=>{for(const t of e)if("string"==typeof t&&t.trim().length>0)return t},y=e=>{if(e)try{return new URL(e).hostname}catch{return}};e.buildWebsiteExtractorUrl=l,e.fetchWebsiteExtractor=d,e.useBoolean=function(e=!1){const[n,r]=t.useState(e),o=t.useCallback(()=>r(!0),[]),u=t.useCallback(()=>r(!1),[]),i=t.useCallback(()=>r(e=>!e),[]);return t.useMemo(()=>({value:n,setValue:r,setTrue:o,setFalse:u,toggle:i}),[n,r,o,u,i])},e.useCopyToClipboard=function(e={}){const n=e.resetDelay??2e3,r=t.useRef(null),[o,u]=t.useState(null),i=t.useMemo(()=>!("undefined"==typeof navigator||!navigator.clipboard)||"undefined"!=typeof document&&("function"==typeof document.queryCommandSupported&&document.queryCommandSupported("copy")),[]),c=t.useCallback(()=>{r.current&&clearTimeout(r.current),r.current=setTimeout(()=>{u(null)},n)},[n]),a=t.useCallback(async e=>{if(!i)return!1;const t="undefined"!=typeof navigator&&!!navigator.clipboard;try{if(t)await navigator.clipboard.writeText(e);else if("undefined"!=typeof document){const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="fixed",t.style.left="-9999px",t.style.top="0",document.body.appendChild(t),t.focus(),t.select();const n=document.execCommand("copy");if(document.body.removeChild(t),!n)return!1}return u(e),c(),!0}catch{return!1}},[i,c]);return t.useEffect(()=>()=>{r.current&&clearTimeout(r.current)},[]),{copy:a,copiedText:o,isSupported:i}},e.useDebounceCallback=r,e.useDebounceValue=o,e.useEventListener=i,e.useHover=function(e){const[n,r]=t.useState(!1),o=t.useCallback(()=>{r(!0)},[]),u=t.useCallback(()=>{r(!1)},[]);return i("pointerenter",o,e),i("pointerleave",u,e),n},e.useIsClient=c,e.useIsomorphicLayoutEffect=n,e.useLocalStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:u=JSON.stringify,deserialize:i=JSON.parse,listenToStorageChanges:c=!0}=r,a=t.useRef(n),s=t.useCallback(()=>{if("undefined"==typeof window)return a.current;if(!o)return a.current;try{const t=window.localStorage.getItem(e);return t?i(t):a.current}catch{return a.current}},[i,o,e]),[l,d]=t.useState(()=>s()),f=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.localStorage.setItem(e,u(r))}catch{}return r})},[e,u]);return t.useEffect(()=>{d(s())},[s]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(i(t.newValue))}catch{d(a.current)}else d(a.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[i,e,c]),[l,f]},e.useMap=a,e.useMediaQuery=function(e,n={}){const[r,o]=t.useState(()=>"undefined"==typeof window||"function"!=typeof window.matchMedia?n.defaultValue??!1:window.matchMedia(e).matches);return t.useEffect(()=>{if("undefined"==typeof window||"function"!=typeof window.matchMedia)return;const t=window.matchMedia(e),n=e=>{o(e.matches)};return o(t.matches),t.addEventListener?(t.addEventListener("change",n),()=>t.removeEventListener("change",n)):(t.addListener(n),()=>t.removeListener(n))},[e]),r},e.useOnClickOutside=function(e,r,o="mousedown",u){const i=t.useRef(r);n(()=>{i.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof document)return;const t=Array.isArray(e)?e:[e],n=e=>{const n=e.target;if("undefined"==typeof Node||!(n instanceof Node))return;t.some(e=>{const t=e.current;return!!t&&t.contains(n)})||i.current(e)};return document.addEventListener(o,n,u),()=>{document.removeEventListener(o,n,u)}},[o,u,e])},e.useOpenGraphExtractor=function(e){const n=t.useMemo(()=>e.skipPatterns??m,[e.skipPatterns]),r=t.useCallback(e=>n.some(t=>t.test(e)),[n]),o=t.useCallback((e,t,n)=>{var r,o,u;const{openGraph:i,htmlInferred:c,hybridGraph:a}=e,s=p(null==i?void 0:i.description,null==a?void 0:a.description,null==c?void 0:c.description),l=p(null==i?void 0:i.title,null==a?void 0:a.title,null==c?void 0:c.title),d=p(null==i?void 0:i.site_name,null==a?void 0:a.site_name,null==c?void 0:c.site_name),f=p(null==a?void 0:a.favicon,null==c?void 0:c.favicon),m=p((null==(r=null==i?void 0:i.image)?void 0:r.url)??void 0,(null==a?void 0:a.image)??void 0,(null==c?void 0:c.image)??void 0,null==(o=null==c?void 0:c.images)?void 0:o[0]),w=p((null==(u=null==i?void 0:i.video)?void 0:u.url)??void 0,(null==a?void 0:a.video)??void 0),g=p(null==a?void 0:a.videoType,null==c?void 0:c.videoType),v=p(n.url,(null==i?void 0:i.url)??void 0,(null==a?void 0:a.url)??void 0,(null==c?void 0:c.url)??void 0,n.finalUrl,n.normalizedUrl,n.requestedUrl)??"";return{description:s,favicon:f,image:m,video:w,videoType:g,siteName:d,title:l,url:v,siteHost:y(v)}},[]);return f({endpoint:"open-graph",options:e,selectData:o,shouldSkip:r})},e.usePlatformFromUrl=function(e){return t.useMemo(()=>{if(!e||"string"!=typeof e)return"unknown";const t=e.trim();if(!t)return"unknown";try{const e=new URL(t).hostname.toLowerCase();return u.get(e)??"unknown"}catch{return"unknown"}},[e])},e.usePrevious=function(e){const r=t.useRef();return n(()=>{r.current=e},[e]),r.current},e.useResizeObserver=function(e,r,o){const u=t.useRef(r),i=t.useRef(null),[c,a]=t.useState(null);return n(()=>{u.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof ResizeObserver)return;const t="undefined"!=typeof Element&&e instanceof Element?e:(n=e)&&"object"==typeof n&&"current"in n?e.current:null;var n;if(!t)return;const r=new ResizeObserver(e=>{const t=e[0];i.current=t,u.current?u.current(t):a(t)});return r.observe(t,o),()=>r.disconnect()},[o,e]),u.current?i.current:c},e.useSessionStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:u=JSON.stringify,deserialize:i=JSON.parse,listenToStorageChanges:c=!1}=r,a=t.useRef(n),s=t.useCallback(()=>{if("undefined"==typeof window)return a.current;if(!o)return a.current;try{const t=window.sessionStorage.getItem(e);return t?i(t):a.current}catch{return a.current}},[i,o,e]),[l,d]=t.useState(()=>s()),f=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.sessionStorage.setItem(e,u(r))}catch{}return r})},[e,u]);return t.useEffect(()=>{d(s())},[s]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(i(t.newValue))}catch{d(a.current)}else d(a.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[i,e,c]),[l,f]},e.useThrottle=function(e,n,r={}){const o=r.leading??!0,u=r.trailing??!0,i=Math.max(0,n),[c,a]=t.useState(e),s=t.useRef(0),l=t.useRef(null),d=t.useRef(null);return t.useEffect(()=>{if(0===i)return void a(e);const t=Date.now();if(0===s.current)return s.current=t,o?void a(e):void(u&&!l.current&&(d.current=e,l.current=setTimeout(()=>{l.current=null,null!==d.current&&(a(d.current),d.current=null,s.current=Date.now())},i)));const n=t-s.current;if(n>=i&&o)return a(e),s.current=t,void(d.current=null);if(u&&(d.current=e,!l.current)){const e=Math.max(i-n,0);l.current=setTimeout(()=>{l.current=null,null!==d.current&&(a(d.current),d.current=null,s.current=Date.now())},e)}},[o,u,e,i]),t.useEffect(()=>()=>{l.current&&clearTimeout(l.current)},[]),c},e.useWebsiteLinksExtractor=function(e){return f({endpoint:"links",options:e,selectData:t.useCallback(e=>({totalLinks:e.totalLinks??0,uniqueDomains:e.uniqueDomains??0,links:e.links??[]}),[])})},e.useWebsiteMetaExtractor=function(e){return f({endpoint:"meta",options:e,selectData:t.useCallback(e=>({title:e.title??void 0,description:e.description??void 0,language:e.language??void 0,canonicalUrl:e.canonicalUrl??void 0,feedUrl:e.feedUrl??null,textContentLength:e.textContentLength??void 0,metaTags:e.metaTags??{}}),[])})},e.useWebsiteRssExtractor=function(e){return f({endpoint:"rss",options:e,selectData:t.useCallback(e=>({feedUrl:e.feedUrl??null,feeds:e.feeds??[]}),[])})},e.useWebsiteSchemaExtractor=function(e){return f({endpoint:"schema",options:e,selectData:t.useCallback(e=>({schema:e.schema??[],schemaTypes:e.schemaTypes??[]}),[])})},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).OpensiteHooks={},e.React)}(this,function(e,t){"use strict";const n="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;function r(e,r,o={}){const i=t.useRef(e),u=t.useRef(null),c=t.useRef(null),s=t.useRef(null),a=o.leading??!1,l=o.trailing??!0,d=o.maxWait,m=Math.max(0,r);n(()=>{i.current=e},[e]);const f=t.useCallback(()=>{u.current&&(clearTimeout(u.current),u.current=null),c.current&&(clearTimeout(c.current),c.current=null)},[]),p=t.useCallback(()=>{if(!s.current)return;const e=s.current;s.current=null,i.current(...e)},[]),w=t.useCallback((...e)=>{s.current=e;if(a&&null===u.current&&null===c.current&&p(),u.current&&clearTimeout(u.current),l&&(u.current=setTimeout(()=>{u.current=null,s.current&&p(),c.current&&(clearTimeout(c.current),c.current=null)},m)),null!=d&&l&&!c.current){const e=Math.max(0,d);c.current=setTimeout(()=>{c.current=null,u.current&&(clearTimeout(u.current),u.current=null),s.current&&p()},e)}},[p,a,l,d,m]),g=t.useCallback(()=>{f(),s.current=null},[f]),b=t.useCallback(()=>{s.current&&(f(),p())},[f,p]);return t.useEffect(()=>()=>g(),[g]),{debouncedCallback:w,cancel:g,flush:b}}function o(e,n,o){const[i,u]=t.useState(e),{debouncedCallback:c,cancel:s}=r(e=>{u(e)},n,o);return t.useEffect(()=>{c(e)},[c,e]),t.useEffect(()=>()=>s(),[s]),i}const i=new Map([["instagram.com","instagram"],["www.instagram.com","instagram"],["instagr.am","instagram"],["www.instagr.am","instagram"],["linkedin.com","linkedin"],["www.linkedin.com","linkedin"],["ca.linkedin.com","linkedin"],["uk.linkedin.com","linkedin"],["in.linkedin.com","linkedin"],["lnkd.in","linkedin"],["google.com","google"],["www.google.com","google"],["maps.google.com","google"],["goo.gl","google"],["maps.app.goo.gl","google"],["g.co","google"],["facebook.com","facebook"],["www.facebook.com","facebook"],["m.facebook.com","facebook"],["fb.com","facebook"],["fb.me","facebook"],["on.fb.me","facebook"],["tiktok.com","tiktok"],["www.tiktok.com","tiktok"],["m.tiktok.com","tiktok"],["vm.tiktok.com","tiktok"],["vt.tiktok.com","tiktok"],["youtube.com","youtube"],["www.youtube.com","youtube"],["m.youtube.com","youtube"],["youtu.be","youtube"],["yelp.com","yelp"],["www.yelp.com","yelp"],["m.yelp.com","yelp"],["spotify.com","spotify"],["www.spotify.com","spotify"],["open.spotify.com","spotify"],["play.spotify.com","spotify"],["spoti.fi","spotify"],["spotify.link","spotify"],["apple.com","apple"],["www.apple.com","apple"],["music.apple.com","apple"],["podcasts.apple.com","apple"],["apps.apple.com","apple"],["itunes.apple.com","apple"],["x.com","x"],["www.x.com","x"],["twitter.com","x"],["www.twitter.com","x"],["t.co","x"],["github.com","github"],["www.github.com","github"],["gist.github.com","github"],["raw.githubusercontent.com","github"],["github.io","github"],["discord.com","discord"],["www.discord.com","discord"],["discord.gg","discord"],["discordapp.com","discord"],["discordapp.net","discord"],["discord.new","discord"],["discord.gift","discord"],["discord.gifts","discord"],["dis.gd","discord"],["snapchat.com","snapchat"],["www.snapchat.com","snapchat"],["snap.com","snapchat"],["www.snap.com","snapchat"],["story.snapchat.com","snapchat"],["web.snapchat.com","snapchat"],["dev.to","dev"],["www.dev.to","dev"],["substack.com","substack"],["www.substack.com","substack"],["reddit.com","reddit"],["www.reddit.com","reddit"],["old.reddit.com","reddit"],["new.reddit.com","reddit"],["i.redd.it","reddit"],["v.redd.it","reddit"],["redd.it","reddit"],["preview.redd.it","reddit"],["pinterest.com","pinterest"],["www.pinterest.com","pinterest"],["pin.it","pinterest"],["in.pinterest.com","pinterest"],["br.pinterest.com","pinterest"],["uk.pinterest.com","pinterest"],["threads.net","threads"],["www.threads.net","threads"],["threads.com","threads"],["www.threads.com","threads"],["twitch.tv","twitch"],["www.twitch.tv","twitch"],["m.twitch.tv","twitch"],["whatsapp.com","whatsapp"],["www.whatsapp.com","whatsapp"],["wa.me","whatsapp"],["web.whatsapp.com","whatsapp"],["telegram.org","telegram"],["www.telegram.org","telegram"],["t.me","telegram"],["telegram.me","telegram"],["telegram.dog","telegram"],["medium.com","medium"],["www.medium.com","medium"],["patreon.com","patreon"],["www.patreon.com","patreon"],["onlyfans.com","onlyfans"],["www.onlyfans.com","onlyfans"],["eventbrite.com","eventbrite"],["www.eventbrite.com","eventbrite"],["eventbrite.co.uk","eventbrite"],["eventbrite.com.au","eventbrite"],["eventbrite.ca","eventbrite"],["eventbrite.de","eventbrite"],["eventbrite.fr","eventbrite"],["eventbrite.es","eventbrite"],["eventbrite.it","eventbrite"],["eventbrite.ie","eventbrite"],["eventbrite.nl","eventbrite"],["eventbrite.co.nz","eventbrite"],["eventbriteapi.com","eventbrite"],["evbuc.com","eventbrite"],["npmjs.com","npmjs"],["www.npmjs.com","npmjs"],["npmjs.org","npmjs"],["www.npmjs.org","npmjs"],["registry.npmjs.org","npmjs"],["registry.npmjs.com","npmjs"],["replicate.npmjs.com","npmjs"],["crates.io","crates"],["www.crates.io","crates"],["rubygems.org","rubygems"],["www.rubygems.org","rubygems"]]);function u(e,r,o,i){const u=t.useRef(r);n(()=>{u.current=r},[r]),t.useEffect(()=>{const t="undefined"!=typeof Window&&o instanceof Window,n="undefined"!=typeof Document&&o instanceof Document,r=void 0===o?"undefined"!=typeof window?window:null:t||n||"undefined"!=typeof HTMLElement&&o instanceof HTMLElement?o:(c=o)&&"object"==typeof c&&"current"in c?o.current:null;var c;if(!(null==r?void 0:r.addEventListener))return;const s=e=>{const t=u.current;"function"==typeof t?t(e):t.handleEvent(e)};return r.addEventListener(e,s,i),()=>{r.removeEventListener(e,s,i)}},[e,o,i])}function c(){const[e,n]=t.useState(!1);return t.useEffect(()=>{n(!0)},[]),e}function s(e){const[r,o]=t.useState(()=>e instanceof Map||Array.isArray(e)?new Map(e):new Map),i=t.useRef(r);n(()=>{i.current=r},[r]);const u=t.useMemo(()=>({set:(e,t)=>{o(n=>{const r=new Map(n);return r.set(e,t),r})},setAll:e=>{o((Map,new Map(e)))},remove:e=>{o(t=>{const n=new Map(t);return n.delete(e),n})},clear:()=>o(new Map),get:e=>i.current.get(e),has:e=>i.current.has(e)}),[]);return[r,u]}const a="https://octane.buzz";function l(e){const t=function(e){return e.replace(/\/+$/,"")}(e.baseUrl??a),n=new URLSearchParams;return e.apiKey&&n.set("api_key",e.apiKey),n.set("url",e.url),`${t}/api/v1/extract/${e.endpoint}?${n.toString()}`}async function d(e){var t;if(!e.url||0===e.url.trim().length)return{ok:!1,error:{message:"URL is required."}};const n=l(e);try{const t=await fetch(n,{method:"GET",signal:e.signal});let r=null;try{r=await t.json()}catch{r=null}if(!t.ok){const e=r;return{ok:!1,error:{message:(null==e?void 0:e.error)??`Request failed with status ${t.status}.`,status:(null==e?void 0:e.status)??t.status,raw:r}}}return{ok:!0,response:r}}catch(r){return(null==(t=e.signal)?void 0:t.aborted)?{ok:!1,error:{message:"Request aborted."}}:{ok:!1,error:{message:r instanceof Error?r.message:"Request failed unexpectedly.",raw:r}}}}function m(e){const{endpoint:n,options:i,selectData:u,shouldSkip:l}=e,m=c(),[f,p]=t.useState({loading:!1}),[,w]=s(),g=i.cache??true,b=i.enabled??true,v=i.debounceMs??250,y=i.refreshDebounceMs??150,h=o(t.useMemo(()=>{var e;return(null==(e=i.url)?void 0:e.trim())??""},[i.url]),v),k=t.useRef(0),E=t.useRef(0),[C,T]=t.useState(0),{debouncedCallback:x,cancel:S}=r(()=>{k.current+=1,T(k.current)},y),M=t.useCallback(()=>{x()},[x]);t.useEffect(()=>()=>S(),[S]);const R=t.useMemo(()=>{if(!h)return"";const e=i.baseUrl??a,t=i.apiKey??"";return`${n}:${e}:${t}:${h}`},[n,i.apiKey,i.baseUrl,h]),L=t.useRef(null);return t.useEffect(()=>{var e;if(!m)return;if(!b||!h)return void p({loading:!1});if(null==l?void 0:l(h))return void p({loading:!1});const t=C!==E.current;if(t&&(E.current=C),g&&!t){const e=w.get(R);if(e)return void p({loading:!1,data:e.data,raw:e.raw,meta:e.meta})}null==(e=L.current)||e.abort();const r=new AbortController;return L.current=r,p(e=>({...e,loading:!0,error:void 0})),d({endpoint:n,url:h,apiKey:i.apiKey,baseUrl:i.baseUrl,signal:r.signal}).then(e=>{if(r.signal.aborted)return;if(!e.ok)return void p(t=>({...t,loading:!1,error:e.error}));const t=e.response,n=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:i,contentType:u,fetchedAt:c,bodyBytes:s,bodyTruncated:a,maxBodyBytes:l,cache:d}=e;return{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:i,contentType:u,fetchedAt:c,bodyBytes:s,bodyTruncated:a,maxBodyBytes:l,cache:d}}(t),o=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:i,contentType:u,fetchedAt:c,bodyBytes:s,bodyTruncated:a,maxBodyBytes:l,cache:d,...m}=e;return m}(t),i=u(o,t,n);g&&w.set(R,{data:i,raw:t,meta:n}),p({loading:!1,data:i,raw:t,meta:n})}).catch(e=>{r.signal.aborted||p(t=>({...t,loading:!1,error:{message:e instanceof Error?e.message:"Request failed unexpectedly.",raw:e}}))}),()=>{r.abort()}},[w,g,h,b,n,m,i.apiKey,i.baseUrl,R,C,u,l]),t.useMemo(()=>({...f,refresh:M}),[M,f])}const f=[/search\.google\.com\/local\/reviews/i,/google\.com\/maps\/place/i,/maps\.google\.com/i,/opentable\.com/i],p=(...e)=>{for(const t of e)if("string"==typeof t&&t.trim().length>0)return t},w=e=>{if(e)try{return new URL(e).hostname}catch{return}};e.buildWebsiteExtractorUrl=l,e.fetchWebsiteExtractor=d,e.useBoolean=function(e=!1){const[n,r]=t.useState(e),o=t.useCallback(()=>r(!0),[]),i=t.useCallback(()=>r(!1),[]),u=t.useCallback(()=>r(e=>!e),[]);return t.useMemo(()=>({value:n,setValue:r,setTrue:o,setFalse:i,toggle:u}),[n,r,o,i,u])},e.useCopyToClipboard=function(e={}){const n=e.resetDelay??2e3,r=t.useRef(null),[o,i]=t.useState(null),u=t.useMemo(()=>!("undefined"==typeof navigator||!navigator.clipboard)||"undefined"!=typeof document&&("function"==typeof document.queryCommandSupported&&document.queryCommandSupported("copy")),[]),c=t.useCallback(()=>{r.current&&clearTimeout(r.current),r.current=setTimeout(()=>{i(null)},n)},[n]),s=t.useCallback(async e=>{if(!u)return!1;const t="undefined"!=typeof navigator&&!!navigator.clipboard;try{if(t)await navigator.clipboard.writeText(e);else if("undefined"!=typeof document){const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="fixed",t.style.left="-9999px",t.style.top="0",document.body.appendChild(t),t.focus(),t.select();const n=document.execCommand("copy");if(document.body.removeChild(t),!n)return!1}return i(e),c(),!0}catch{return!1}},[u,c]);return t.useEffect(()=>()=>{r.current&&clearTimeout(r.current)},[]),{copy:s,copiedText:o,isSupported:u}},e.useDebounceCallback=r,e.useDebounceValue=o,e.useEventListener=u,e.useHover=function(e){const[n,r]=t.useState(!1),o=t.useCallback(()=>{r(!0)},[]),i=t.useCallback(()=>{r(!1)},[]);return u("pointerenter",o,e),u("pointerleave",i,e),n},e.useIsClient=c,e.useIsomorphicLayoutEffect=n,e.useLocalStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:i=JSON.stringify,deserialize:u=JSON.parse,listenToStorageChanges:c=!0}=r,s=t.useRef(n),a=t.useCallback(()=>{if("undefined"==typeof window)return s.current;if(!o)return s.current;try{const t=window.localStorage.getItem(e);return t?u(t):s.current}catch{return s.current}},[u,o,e]),[l,d]=t.useState(()=>a()),m=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.localStorage.setItem(e,i(r))}catch{}return r})},[e,i]);return t.useEffect(()=>{d(a())},[a]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(u(t.newValue))}catch{d(s.current)}else d(s.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[u,e,c]),[l,m]},e.useMap=s,e.useMediaQuery=function(e,n={}){const[r,o]=t.useState(()=>"undefined"==typeof window||"function"!=typeof window.matchMedia?n.defaultValue??!1:window.matchMedia(e).matches);return t.useEffect(()=>{if("undefined"==typeof window||"function"!=typeof window.matchMedia)return;const t=window.matchMedia(e),n=e=>{o(e.matches)};return o(t.matches),t.addEventListener?(t.addEventListener("change",n),()=>t.removeEventListener("change",n)):(t.addListener(n),()=>t.removeListener(n))},[e]),r},e.useOnClickOutside=function(e,r,o="mousedown",i){const u=t.useRef(r);n(()=>{u.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof document)return;const t=Array.isArray(e)?e:[e],n=e=>{const n=e.target;if("undefined"==typeof Node||!(n instanceof Node))return;t.some(e=>{const t=e.current;return!!t&&t.contains(n)})||u.current(e)};return document.addEventListener(o,n,i),()=>{document.removeEventListener(o,n,i)}},[o,i,e])},e.useOpenGraphExtractor=function(e){const n=t.useMemo(()=>e.skipPatterns??f,[e.skipPatterns]),r=t.useCallback(e=>n.some(t=>t.test(e)),[n]),o=t.useCallback((e,t,n)=>{var r,o,i;const{openGraph:u,htmlInferred:c,hybridGraph:s}=e,a=p(null==u?void 0:u.description,null==s?void 0:s.description,null==c?void 0:c.description),l=p(null==u?void 0:u.title,null==s?void 0:s.title,null==c?void 0:c.title),d=p(null==u?void 0:u.site_name,null==s?void 0:s.site_name,null==c?void 0:c.site_name),m=p(null==s?void 0:s.favicon,null==c?void 0:c.favicon),f=p((null==(r=null==u?void 0:u.image)?void 0:r.url)??void 0,(null==s?void 0:s.image)??void 0,(null==c?void 0:c.image)??void 0,null==(o=null==c?void 0:c.images)?void 0:o[0]),g=p((null==(i=null==u?void 0:u.video)?void 0:i.url)??void 0,(null==s?void 0:s.video)??void 0),b=p(null==s?void 0:s.videoType,null==c?void 0:c.videoType),v=p(n.url,(null==u?void 0:u.url)??void 0,(null==s?void 0:s.url)??void 0,(null==c?void 0:c.url)??void 0,n.finalUrl,n.normalizedUrl,n.requestedUrl)??"";return{description:a,favicon:m,image:f,video:g,videoType:b,siteName:d,title:l,url:v,siteHost:w(v)}},[]);return m({endpoint:"open-graph",options:e,selectData:o,shouldSkip:r})},e.usePlatformFromUrl=function(e){return t.useMemo(()=>{if(!e||"string"!=typeof e)return"unknown";const t=e.trim();if(!t)return"unknown";try{const e=new URL(t).hostname.toLowerCase(),n=i.get(e);return n||(e.endsWith(".substack.com")?"substack":e.endsWith(".github.io")?"github":e.includes("pinterest.com")?"pinterest":e.includes("eventbrite.")?"eventbrite":e.endsWith(".medium.com")?"medium":"unknown")}catch{return"unknown"}},[e])},e.usePrevious=function(e){const r=t.useRef();return n(()=>{r.current=e},[e]),r.current},e.useResizeObserver=function(e,r,o){const i=t.useRef(r),u=t.useRef(null),[c,s]=t.useState(null);return n(()=>{i.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof ResizeObserver)return;const t="undefined"!=typeof Element&&e instanceof Element?e:(n=e)&&"object"==typeof n&&"current"in n?e.current:null;var n;if(!t)return;const r=new ResizeObserver(e=>{const t=e[0];u.current=t,i.current?i.current(t):s(t)});return r.observe(t,o),()=>r.disconnect()},[o,e]),i.current?u.current:c},e.useSessionStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:i=JSON.stringify,deserialize:u=JSON.parse,listenToStorageChanges:c=!1}=r,s=t.useRef(n),a=t.useCallback(()=>{if("undefined"==typeof window)return s.current;if(!o)return s.current;try{const t=window.sessionStorage.getItem(e);return t?u(t):s.current}catch{return s.current}},[u,o,e]),[l,d]=t.useState(()=>a()),m=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.sessionStorage.setItem(e,i(r))}catch{}return r})},[e,i]);return t.useEffect(()=>{d(a())},[a]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(u(t.newValue))}catch{d(s.current)}else d(s.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[u,e,c]),[l,m]},e.useThrottle=function(e,n,r={}){const o=r.leading??!0,i=r.trailing??!0,u=Math.max(0,n),[c,s]=t.useState(e),a=t.useRef(0),l=t.useRef(null),d=t.useRef(null);return t.useEffect(()=>{if(0===u)return void s(e);const t=Date.now();if(0===a.current)return a.current=t,o?void s(e):void(i&&!l.current&&(d.current=e,l.current=setTimeout(()=>{l.current=null,null!==d.current&&(s(d.current),d.current=null,a.current=Date.now())},u)));const n=t-a.current;if(n>=u&&o)return s(e),a.current=t,void(d.current=null);if(i&&(d.current=e,!l.current)){const e=Math.max(u-n,0);l.current=setTimeout(()=>{l.current=null,null!==d.current&&(s(d.current),d.current=null,a.current=Date.now())},e)}},[o,i,e,u]),t.useEffect(()=>()=>{l.current&&clearTimeout(l.current)},[]),c},e.useWebsiteLinksExtractor=function(e){return m({endpoint:"links",options:e,selectData:t.useCallback(e=>({totalLinks:e.totalLinks??0,uniqueDomains:e.uniqueDomains??0,links:e.links??[]}),[])})},e.useWebsiteMetaExtractor=function(e){return m({endpoint:"meta",options:e,selectData:t.useCallback(e=>({title:e.title??void 0,description:e.description??void 0,language:e.language??void 0,canonicalUrl:e.canonicalUrl??void 0,feedUrl:e.feedUrl??null,textContentLength:e.textContentLength??void 0,metaTags:e.metaTags??{}}),[])})},e.useWebsiteRssExtractor=function(e){return m({endpoint:"rss",options:e,selectData:t.useCallback(e=>({feedUrl:e.feedUrl??null,feeds:e.feeds??[]}),[])})},e.useWebsiteSchemaExtractor=function(e){return m({endpoint:"schema",options:e,selectData:t.useCallback(e=>({schema:e.schema??[],schemaTypes:e.schemaTypes??[]}),[])})},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
2
2
|
//# sourceMappingURL=opensite-hooks.umd.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).OpensiteHooks={},e.React)}(this,function(e,t){"use strict";const n="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;function r(e,r,o={}){const u=t.useRef(e),i=t.useRef(null),c=t.useRef(null),a=t.useRef(null),s=o.leading??!1,l=o.trailing??!0,d=o.maxWait,f=Math.max(0,r);n(()=>{u.current=e},[e]);const m=t.useCallback(()=>{i.current&&(clearTimeout(i.current),i.current=null),c.current&&(clearTimeout(c.current),c.current=null)},[]),p=t.useCallback(()=>{if(!a.current)return;const e=a.current;a.current=null,u.current(...e)},[]),y=t.useCallback((...e)=>{a.current=e;if(s&&null===i.current&&null===c.current&&p(),i.current&&clearTimeout(i.current),l&&(i.current=setTimeout(()=>{i.current=null,a.current&&p(),c.current&&(clearTimeout(c.current),c.current=null)},f)),null!=d&&l&&!c.current){const e=Math.max(0,d);c.current=setTimeout(()=>{c.current=null,i.current&&(clearTimeout(i.current),i.current=null),a.current&&p()},e)}},[p,s,l,d,f]),w=t.useCallback(()=>{m(),a.current=null},[m]),g=t.useCallback(()=>{a.current&&(m(),p())},[m,p]);return t.useEffect(()=>()=>w(),[w]),{debouncedCallback:y,cancel:w,flush:g}}function o(e,n,o){const[u,i]=t.useState(e),{debouncedCallback:c,cancel:a}=r(e=>{i(e)},n,o);return t.useEffect(()=>{c(e)},[c,e]),t.useEffect(()=>()=>a(),[a]),u}const u=new Map([["instagram.com","instagram"],["www.instagram.com","instagram"],["instagr.am","instagram"],["www.instagr.am","instagram"],["linkedin.com","linkedin"],["www.linkedin.com","linkedin"],["ca.linkedin.com","linkedin"],["uk.linkedin.com","linkedin"],["in.linkedin.com","linkedin"],["lnkd.in","linkedin"],["google.com","google"],["www.google.com","google"],["maps.google.com","google"],["goo.gl","google"],["maps.app.goo.gl","google"],["g.co","google"],["facebook.com","facebook"],["www.facebook.com","facebook"],["m.facebook.com","facebook"],["fb.com","facebook"],["fb.me","facebook"],["on.fb.me","facebook"],["tiktok.com","tiktok"],["www.tiktok.com","tiktok"],["m.tiktok.com","tiktok"],["vm.tiktok.com","tiktok"],["vt.tiktok.com","tiktok"],["youtube.com","youtube"],["www.youtube.com","youtube"],["m.youtube.com","youtube"],["youtu.be","youtube"],["yelp.com","yelp"],["www.yelp.com","yelp"],["m.yelp.com","yelp"],["spotify.com","spotify"],["www.spotify.com","spotify"],["open.spotify.com","spotify"],["play.spotify.com","spotify"],["spoti.fi","spotify"],["spotify.link","spotify"],["apple.com","apple"],["www.apple.com","apple"],["music.apple.com","apple"],["podcasts.apple.com","apple"],["apps.apple.com","apple"],["itunes.apple.com","apple"],["x.com","x"],["www.x.com","x"],["twitter.com","x"],["www.twitter.com","x"],["t.co","x"]]);function i(e,r,o,u){const i=t.useRef(r);n(()=>{i.current=r},[r]),t.useEffect(()=>{const t="undefined"!=typeof Window&&o instanceof Window,n="undefined"!=typeof Document&&o instanceof Document,r=void 0===o?"undefined"!=typeof window?window:null:t||n||"undefined"!=typeof HTMLElement&&o instanceof HTMLElement?o:(c=o)&&"object"==typeof c&&"current"in c?o.current:null;var c;if(!(null==r?void 0:r.addEventListener))return;const a=e=>{const t=i.current;"function"==typeof t?t(e):t.handleEvent(e)};return r.addEventListener(e,a,u),()=>{r.removeEventListener(e,a,u)}},[e,o,u])}function c(){const[e,n]=t.useState(!1);return t.useEffect(()=>{n(!0)},[]),e}function a(e){const[r,o]=t.useState(()=>e instanceof Map||Array.isArray(e)?new Map(e):new Map),u=t.useRef(r);n(()=>{u.current=r},[r]);const i=t.useMemo(()=>({set:(e,t)=>{o(n=>{const r=new Map(n);return r.set(e,t),r})},setAll:e=>{o((Map,new Map(e)))},remove:e=>{o(t=>{const n=new Map(t);return n.delete(e),n})},clear:()=>o(new Map),get:e=>u.current.get(e),has:e=>u.current.has(e)}),[]);return[r,i]}const s="https://octane.buzz";function l(e){const t=function(e){return e.replace(/\/+$/,"")}(e.baseUrl??s),n=new URLSearchParams;return e.apiKey&&n.set("api_key",e.apiKey),n.set("url",e.url),`${t}/api/v1/extract/${e.endpoint}?${n.toString()}`}async function d(e){var t;if(!e.url||0===e.url.trim().length)return{ok:!1,error:{message:"URL is required."}};const n=l(e);try{const t=await fetch(n,{method:"GET",signal:e.signal});let r=null;try{r=await t.json()}catch{r=null}if(!t.ok){const e=r;return{ok:!1,error:{message:(null==e?void 0:e.error)??`Request failed with status ${t.status}.`,status:(null==e?void 0:e.status)??t.status,raw:r}}}return{ok:!0,response:r}}catch(r){return(null==(t=e.signal)?void 0:t.aborted)?{ok:!1,error:{message:"Request aborted."}}:{ok:!1,error:{message:r instanceof Error?r.message:"Request failed unexpectedly.",raw:r}}}}function f(e){const{endpoint:n,options:u,selectData:i,shouldSkip:l}=e,f=c(),[m,p]=t.useState({loading:!1}),[,y]=a(),w=u.cache??true,g=u.enabled??true,v=u.debounceMs??250,b=u.refreshDebounceMs??150,k=o(t.useMemo(()=>{var e;return(null==(e=u.url)?void 0:e.trim())??""},[u.url]),v),h=t.useRef(0),E=t.useRef(0),[C,T]=t.useState(0),{debouncedCallback:x,cancel:S}=r(()=>{h.current+=1,T(h.current)},b),M=t.useCallback(()=>{x()},[x]);t.useEffect(()=>()=>S(),[S]);const R=t.useMemo(()=>{if(!k)return"";const e=u.baseUrl??s,t=u.apiKey??"";return`${n}:${e}:${t}:${k}`},[n,u.apiKey,u.baseUrl,k]),L=t.useRef(null);return t.useEffect(()=>{var e;if(!f)return;if(!g||!k)return void p({loading:!1});if(null==l?void 0:l(k))return void p({loading:!1});const t=C!==E.current;if(t&&(E.current=C),w&&!t){const e=y.get(R);if(e)return void p({loading:!1,data:e.data,raw:e.raw,meta:e.meta})}null==(e=L.current)||e.abort();const r=new AbortController;return L.current=r,p(e=>({...e,loading:!0,error:void 0})),d({endpoint:n,url:k,apiKey:u.apiKey,baseUrl:u.baseUrl,signal:r.signal}).then(e=>{if(r.signal.aborted)return;if(!e.ok)return void p(t=>({...t,loading:!1,error:e.error}));const t=e.response,n=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:u,contentType:i,fetchedAt:c,bodyBytes:a,bodyTruncated:s,maxBodyBytes:l,cache:d}=e;return{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:u,contentType:i,fetchedAt:c,bodyBytes:a,bodyTruncated:s,maxBodyBytes:l,cache:d}}(t),o=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:u,contentType:i,fetchedAt:c,bodyBytes:a,bodyTruncated:s,maxBodyBytes:l,cache:d,...f}=e;return f}(t),u=i(o,t,n);w&&y.set(R,{data:u,raw:t,meta:n}),p({loading:!1,data:u,raw:t,meta:n})}).catch(e=>{r.signal.aborted||p(t=>({...t,loading:!1,error:{message:e instanceof Error?e.message:"Request failed unexpectedly.",raw:e}}))}),()=>{r.abort()}},[y,w,k,g,n,f,u.apiKey,u.baseUrl,R,C,i,l]),t.useMemo(()=>({...m,refresh:M}),[M,m])}const m=[/search\.google\.com\/local\/reviews/i,/google\.com\/maps\/place/i,/maps\.google\.com/i,/opentable\.com/i],p=(...e)=>{for(const t of e)if("string"==typeof t&&t.trim().length>0)return t},y=e=>{if(e)try{return new URL(e).hostname}catch{return}};e.buildWebsiteExtractorUrl=l,e.fetchWebsiteExtractor=d,e.useBoolean=function(e=!1){const[n,r]=t.useState(e),o=t.useCallback(()=>r(!0),[]),u=t.useCallback(()=>r(!1),[]),i=t.useCallback(()=>r(e=>!e),[]);return t.useMemo(()=>({value:n,setValue:r,setTrue:o,setFalse:u,toggle:i}),[n,r,o,u,i])},e.useCopyToClipboard=function(e={}){const n=e.resetDelay??2e3,r=t.useRef(null),[o,u]=t.useState(null),i=t.useMemo(()=>!("undefined"==typeof navigator||!navigator.clipboard)||"undefined"!=typeof document&&("function"==typeof document.queryCommandSupported&&document.queryCommandSupported("copy")),[]),c=t.useCallback(()=>{r.current&&clearTimeout(r.current),r.current=setTimeout(()=>{u(null)},n)},[n]),a=t.useCallback(async e=>{if(!i)return!1;const t="undefined"!=typeof navigator&&!!navigator.clipboard;try{if(t)await navigator.clipboard.writeText(e);else if("undefined"!=typeof document){const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="fixed",t.style.left="-9999px",t.style.top="0",document.body.appendChild(t),t.focus(),t.select();const n=document.execCommand("copy");if(document.body.removeChild(t),!n)return!1}return u(e),c(),!0}catch{return!1}},[i,c]);return t.useEffect(()=>()=>{r.current&&clearTimeout(r.current)},[]),{copy:a,copiedText:o,isSupported:i}},e.useDebounceCallback=r,e.useDebounceValue=o,e.useEventListener=i,e.useHover=function(e){const[n,r]=t.useState(!1),o=t.useCallback(()=>{r(!0)},[]),u=t.useCallback(()=>{r(!1)},[]);return i("pointerenter",o,e),i("pointerleave",u,e),n},e.useIsClient=c,e.useIsomorphicLayoutEffect=n,e.useLocalStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:u=JSON.stringify,deserialize:i=JSON.parse,listenToStorageChanges:c=!0}=r,a=t.useRef(n),s=t.useCallback(()=>{if("undefined"==typeof window)return a.current;if(!o)return a.current;try{const t=window.localStorage.getItem(e);return t?i(t):a.current}catch{return a.current}},[i,o,e]),[l,d]=t.useState(()=>s()),f=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.localStorage.setItem(e,u(r))}catch{}return r})},[e,u]);return t.useEffect(()=>{d(s())},[s]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(i(t.newValue))}catch{d(a.current)}else d(a.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[i,e,c]),[l,f]},e.useMap=a,e.useMediaQuery=function(e,n={}){const[r,o]=t.useState(()=>"undefined"==typeof window||"function"!=typeof window.matchMedia?n.defaultValue??!1:window.matchMedia(e).matches);return t.useEffect(()=>{if("undefined"==typeof window||"function"!=typeof window.matchMedia)return;const t=window.matchMedia(e),n=e=>{o(e.matches)};return o(t.matches),t.addEventListener?(t.addEventListener("change",n),()=>t.removeEventListener("change",n)):(t.addListener(n),()=>t.removeListener(n))},[e]),r},e.useOnClickOutside=function(e,r,o="mousedown",u){const i=t.useRef(r);n(()=>{i.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof document)return;const t=Array.isArray(e)?e:[e],n=e=>{const n=e.target;if("undefined"==typeof Node||!(n instanceof Node))return;t.some(e=>{const t=e.current;return!!t&&t.contains(n)})||i.current(e)};return document.addEventListener(o,n,u),()=>{document.removeEventListener(o,n,u)}},[o,u,e])},e.useOpenGraphExtractor=function(e){const n=t.useMemo(()=>e.skipPatterns??m,[e.skipPatterns]),r=t.useCallback(e=>n.some(t=>t.test(e)),[n]),o=t.useCallback((e,t,n)=>{var r,o,u;const{openGraph:i,htmlInferred:c,hybridGraph:a}=e,s=p(null==i?void 0:i.description,null==a?void 0:a.description,null==c?void 0:c.description),l=p(null==i?void 0:i.title,null==a?void 0:a.title,null==c?void 0:c.title),d=p(null==i?void 0:i.site_name,null==a?void 0:a.site_name,null==c?void 0:c.site_name),f=p(null==a?void 0:a.favicon,null==c?void 0:c.favicon),m=p((null==(r=null==i?void 0:i.image)?void 0:r.url)??void 0,(null==a?void 0:a.image)??void 0,(null==c?void 0:c.image)??void 0,null==(o=null==c?void 0:c.images)?void 0:o[0]),w=p((null==(u=null==i?void 0:i.video)?void 0:u.url)??void 0,(null==a?void 0:a.video)??void 0),g=p(null==a?void 0:a.videoType,null==c?void 0:c.videoType),v=p(n.url,(null==i?void 0:i.url)??void 0,(null==a?void 0:a.url)??void 0,(null==c?void 0:c.url)??void 0,n.finalUrl,n.normalizedUrl,n.requestedUrl)??"";return{description:s,favicon:f,image:m,video:w,videoType:g,siteName:d,title:l,url:v,siteHost:y(v)}},[]);return f({endpoint:"open-graph",options:e,selectData:o,shouldSkip:r})},e.usePlatformFromUrl=function(e){return t.useMemo(()=>{if(!e||"string"!=typeof e)return"unknown";const t=e.trim();if(!t)return"unknown";try{const e=new URL(t).hostname.toLowerCase();return u.get(e)??"unknown"}catch{return"unknown"}},[e])},e.usePrevious=function(e){const r=t.useRef();return n(()=>{r.current=e},[e]),r.current},e.useResizeObserver=function(e,r,o){const u=t.useRef(r),i=t.useRef(null),[c,a]=t.useState(null);return n(()=>{u.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof ResizeObserver)return;const t="undefined"!=typeof Element&&e instanceof Element?e:(n=e)&&"object"==typeof n&&"current"in n?e.current:null;var n;if(!t)return;const r=new ResizeObserver(e=>{const t=e[0];i.current=t,u.current?u.current(t):a(t)});return r.observe(t,o),()=>r.disconnect()},[o,e]),u.current?i.current:c},e.useSessionStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:u=JSON.stringify,deserialize:i=JSON.parse,listenToStorageChanges:c=!1}=r,a=t.useRef(n),s=t.useCallback(()=>{if("undefined"==typeof window)return a.current;if(!o)return a.current;try{const t=window.sessionStorage.getItem(e);return t?i(t):a.current}catch{return a.current}},[i,o,e]),[l,d]=t.useState(()=>s()),f=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.sessionStorage.setItem(e,u(r))}catch{}return r})},[e,u]);return t.useEffect(()=>{d(s())},[s]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(i(t.newValue))}catch{d(a.current)}else d(a.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[i,e,c]),[l,f]},e.useThrottle=function(e,n,r={}){const o=r.leading??!0,u=r.trailing??!0,i=Math.max(0,n),[c,a]=t.useState(e),s=t.useRef(0),l=t.useRef(null),d=t.useRef(null);return t.useEffect(()=>{if(0===i)return void a(e);const t=Date.now();if(0===s.current)return s.current=t,o?void a(e):void(u&&!l.current&&(d.current=e,l.current=setTimeout(()=>{l.current=null,null!==d.current&&(a(d.current),d.current=null,s.current=Date.now())},i)));const n=t-s.current;if(n>=i&&o)return a(e),s.current=t,void(d.current=null);if(u&&(d.current=e,!l.current)){const e=Math.max(i-n,0);l.current=setTimeout(()=>{l.current=null,null!==d.current&&(a(d.current),d.current=null,s.current=Date.now())},e)}},[o,u,e,i]),t.useEffect(()=>()=>{l.current&&clearTimeout(l.current)},[]),c},e.useWebsiteLinksExtractor=function(e){return f({endpoint:"links",options:e,selectData:t.useCallback(e=>({totalLinks:e.totalLinks??0,uniqueDomains:e.uniqueDomains??0,links:e.links??[]}),[])})},e.useWebsiteMetaExtractor=function(e){return f({endpoint:"meta",options:e,selectData:t.useCallback(e=>({title:e.title??void 0,description:e.description??void 0,language:e.language??void 0,canonicalUrl:e.canonicalUrl??void 0,feedUrl:e.feedUrl??null,textContentLength:e.textContentLength??void 0,metaTags:e.metaTags??{}}),[])})},e.useWebsiteRssExtractor=function(e){return f({endpoint:"rss",options:e,selectData:t.useCallback(e=>({feedUrl:e.feedUrl??null,feeds:e.feeds??[]}),[])})},e.useWebsiteSchemaExtractor=function(e){return f({endpoint:"schema",options:e,selectData:t.useCallback(e=>({schema:e.schema??[],schemaTypes:e.schemaTypes??[]}),[])})},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).OpensiteHooks={},e.React)}(this,function(e,t){"use strict";const n="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;function r(e,r,o={}){const i=t.useRef(e),u=t.useRef(null),c=t.useRef(null),s=t.useRef(null),a=o.leading??!1,l=o.trailing??!0,d=o.maxWait,m=Math.max(0,r);n(()=>{i.current=e},[e]);const f=t.useCallback(()=>{u.current&&(clearTimeout(u.current),u.current=null),c.current&&(clearTimeout(c.current),c.current=null)},[]),p=t.useCallback(()=>{if(!s.current)return;const e=s.current;s.current=null,i.current(...e)},[]),w=t.useCallback((...e)=>{s.current=e;if(a&&null===u.current&&null===c.current&&p(),u.current&&clearTimeout(u.current),l&&(u.current=setTimeout(()=>{u.current=null,s.current&&p(),c.current&&(clearTimeout(c.current),c.current=null)},m)),null!=d&&l&&!c.current){const e=Math.max(0,d);c.current=setTimeout(()=>{c.current=null,u.current&&(clearTimeout(u.current),u.current=null),s.current&&p()},e)}},[p,a,l,d,m]),g=t.useCallback(()=>{f(),s.current=null},[f]),b=t.useCallback(()=>{s.current&&(f(),p())},[f,p]);return t.useEffect(()=>()=>g(),[g]),{debouncedCallback:w,cancel:g,flush:b}}function o(e,n,o){const[i,u]=t.useState(e),{debouncedCallback:c,cancel:s}=r(e=>{u(e)},n,o);return t.useEffect(()=>{c(e)},[c,e]),t.useEffect(()=>()=>s(),[s]),i}const i=new Map([["instagram.com","instagram"],["www.instagram.com","instagram"],["instagr.am","instagram"],["www.instagr.am","instagram"],["linkedin.com","linkedin"],["www.linkedin.com","linkedin"],["ca.linkedin.com","linkedin"],["uk.linkedin.com","linkedin"],["in.linkedin.com","linkedin"],["lnkd.in","linkedin"],["google.com","google"],["www.google.com","google"],["maps.google.com","google"],["goo.gl","google"],["maps.app.goo.gl","google"],["g.co","google"],["facebook.com","facebook"],["www.facebook.com","facebook"],["m.facebook.com","facebook"],["fb.com","facebook"],["fb.me","facebook"],["on.fb.me","facebook"],["tiktok.com","tiktok"],["www.tiktok.com","tiktok"],["m.tiktok.com","tiktok"],["vm.tiktok.com","tiktok"],["vt.tiktok.com","tiktok"],["youtube.com","youtube"],["www.youtube.com","youtube"],["m.youtube.com","youtube"],["youtu.be","youtube"],["yelp.com","yelp"],["www.yelp.com","yelp"],["m.yelp.com","yelp"],["spotify.com","spotify"],["www.spotify.com","spotify"],["open.spotify.com","spotify"],["play.spotify.com","spotify"],["spoti.fi","spotify"],["spotify.link","spotify"],["apple.com","apple"],["www.apple.com","apple"],["music.apple.com","apple"],["podcasts.apple.com","apple"],["apps.apple.com","apple"],["itunes.apple.com","apple"],["x.com","x"],["www.x.com","x"],["twitter.com","x"],["www.twitter.com","x"],["t.co","x"],["github.com","github"],["www.github.com","github"],["gist.github.com","github"],["raw.githubusercontent.com","github"],["github.io","github"],["discord.com","discord"],["www.discord.com","discord"],["discord.gg","discord"],["discordapp.com","discord"],["discordapp.net","discord"],["discord.new","discord"],["discord.gift","discord"],["discord.gifts","discord"],["dis.gd","discord"],["snapchat.com","snapchat"],["www.snapchat.com","snapchat"],["snap.com","snapchat"],["www.snap.com","snapchat"],["story.snapchat.com","snapchat"],["web.snapchat.com","snapchat"],["dev.to","dev"],["www.dev.to","dev"],["substack.com","substack"],["www.substack.com","substack"],["reddit.com","reddit"],["www.reddit.com","reddit"],["old.reddit.com","reddit"],["new.reddit.com","reddit"],["i.redd.it","reddit"],["v.redd.it","reddit"],["redd.it","reddit"],["preview.redd.it","reddit"],["pinterest.com","pinterest"],["www.pinterest.com","pinterest"],["pin.it","pinterest"],["in.pinterest.com","pinterest"],["br.pinterest.com","pinterest"],["uk.pinterest.com","pinterest"],["threads.net","threads"],["www.threads.net","threads"],["threads.com","threads"],["www.threads.com","threads"],["twitch.tv","twitch"],["www.twitch.tv","twitch"],["m.twitch.tv","twitch"],["whatsapp.com","whatsapp"],["www.whatsapp.com","whatsapp"],["wa.me","whatsapp"],["web.whatsapp.com","whatsapp"],["telegram.org","telegram"],["www.telegram.org","telegram"],["t.me","telegram"],["telegram.me","telegram"],["telegram.dog","telegram"],["medium.com","medium"],["www.medium.com","medium"],["patreon.com","patreon"],["www.patreon.com","patreon"],["onlyfans.com","onlyfans"],["www.onlyfans.com","onlyfans"],["eventbrite.com","eventbrite"],["www.eventbrite.com","eventbrite"],["eventbrite.co.uk","eventbrite"],["eventbrite.com.au","eventbrite"],["eventbrite.ca","eventbrite"],["eventbrite.de","eventbrite"],["eventbrite.fr","eventbrite"],["eventbrite.es","eventbrite"],["eventbrite.it","eventbrite"],["eventbrite.ie","eventbrite"],["eventbrite.nl","eventbrite"],["eventbrite.co.nz","eventbrite"],["eventbriteapi.com","eventbrite"],["evbuc.com","eventbrite"],["npmjs.com","npmjs"],["www.npmjs.com","npmjs"],["npmjs.org","npmjs"],["www.npmjs.org","npmjs"],["registry.npmjs.org","npmjs"],["registry.npmjs.com","npmjs"],["replicate.npmjs.com","npmjs"],["crates.io","crates"],["www.crates.io","crates"],["rubygems.org","rubygems"],["www.rubygems.org","rubygems"]]);function u(e,r,o,i){const u=t.useRef(r);n(()=>{u.current=r},[r]),t.useEffect(()=>{const t="undefined"!=typeof Window&&o instanceof Window,n="undefined"!=typeof Document&&o instanceof Document,r=void 0===o?"undefined"!=typeof window?window:null:t||n||"undefined"!=typeof HTMLElement&&o instanceof HTMLElement?o:(c=o)&&"object"==typeof c&&"current"in c?o.current:null;var c;if(!(null==r?void 0:r.addEventListener))return;const s=e=>{const t=u.current;"function"==typeof t?t(e):t.handleEvent(e)};return r.addEventListener(e,s,i),()=>{r.removeEventListener(e,s,i)}},[e,o,i])}function c(){const[e,n]=t.useState(!1);return t.useEffect(()=>{n(!0)},[]),e}function s(e){const[r,o]=t.useState(()=>e instanceof Map||Array.isArray(e)?new Map(e):new Map),i=t.useRef(r);n(()=>{i.current=r},[r]);const u=t.useMemo(()=>({set:(e,t)=>{o(n=>{const r=new Map(n);return r.set(e,t),r})},setAll:e=>{o((Map,new Map(e)))},remove:e=>{o(t=>{const n=new Map(t);return n.delete(e),n})},clear:()=>o(new Map),get:e=>i.current.get(e),has:e=>i.current.has(e)}),[]);return[r,u]}const a="https://octane.buzz";function l(e){const t=function(e){return e.replace(/\/+$/,"")}(e.baseUrl??a),n=new URLSearchParams;return e.apiKey&&n.set("api_key",e.apiKey),n.set("url",e.url),`${t}/api/v1/extract/${e.endpoint}?${n.toString()}`}async function d(e){var t;if(!e.url||0===e.url.trim().length)return{ok:!1,error:{message:"URL is required."}};const n=l(e);try{const t=await fetch(n,{method:"GET",signal:e.signal});let r=null;try{r=await t.json()}catch{r=null}if(!t.ok){const e=r;return{ok:!1,error:{message:(null==e?void 0:e.error)??`Request failed with status ${t.status}.`,status:(null==e?void 0:e.status)??t.status,raw:r}}}return{ok:!0,response:r}}catch(r){return(null==(t=e.signal)?void 0:t.aborted)?{ok:!1,error:{message:"Request aborted."}}:{ok:!1,error:{message:r instanceof Error?r.message:"Request failed unexpectedly.",raw:r}}}}function m(e){const{endpoint:n,options:i,selectData:u,shouldSkip:l}=e,m=c(),[f,p]=t.useState({loading:!1}),[,w]=s(),g=i.cache??true,b=i.enabled??true,v=i.debounceMs??250,y=i.refreshDebounceMs??150,h=o(t.useMemo(()=>{var e;return(null==(e=i.url)?void 0:e.trim())??""},[i.url]),v),k=t.useRef(0),E=t.useRef(0),[C,T]=t.useState(0),{debouncedCallback:x,cancel:S}=r(()=>{k.current+=1,T(k.current)},y),M=t.useCallback(()=>{x()},[x]);t.useEffect(()=>()=>S(),[S]);const R=t.useMemo(()=>{if(!h)return"";const e=i.baseUrl??a,t=i.apiKey??"";return`${n}:${e}:${t}:${h}`},[n,i.apiKey,i.baseUrl,h]),L=t.useRef(null);return t.useEffect(()=>{var e;if(!m)return;if(!b||!h)return void p({loading:!1});if(null==l?void 0:l(h))return void p({loading:!1});const t=C!==E.current;if(t&&(E.current=C),g&&!t){const e=w.get(R);if(e)return void p({loading:!1,data:e.data,raw:e.raw,meta:e.meta})}null==(e=L.current)||e.abort();const r=new AbortController;return L.current=r,p(e=>({...e,loading:!0,error:void 0})),d({endpoint:n,url:h,apiKey:i.apiKey,baseUrl:i.baseUrl,signal:r.signal}).then(e=>{if(r.signal.aborted)return;if(!e.ok)return void p(t=>({...t,loading:!1,error:e.error}));const t=e.response,n=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:i,contentType:u,fetchedAt:c,bodyBytes:s,bodyTruncated:a,maxBodyBytes:l,cache:d}=e;return{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:i,contentType:u,fetchedAt:c,bodyBytes:s,bodyTruncated:a,maxBodyBytes:l,cache:d}}(t),o=function(e){const{requestedUrl:t,finalUrl:n,url:r,normalizedUrl:o,status:i,contentType:u,fetchedAt:c,bodyBytes:s,bodyTruncated:a,maxBodyBytes:l,cache:d,...m}=e;return m}(t),i=u(o,t,n);g&&w.set(R,{data:i,raw:t,meta:n}),p({loading:!1,data:i,raw:t,meta:n})}).catch(e=>{r.signal.aborted||p(t=>({...t,loading:!1,error:{message:e instanceof Error?e.message:"Request failed unexpectedly.",raw:e}}))}),()=>{r.abort()}},[w,g,h,b,n,m,i.apiKey,i.baseUrl,R,C,u,l]),t.useMemo(()=>({...f,refresh:M}),[M,f])}const f=[/search\.google\.com\/local\/reviews/i,/google\.com\/maps\/place/i,/maps\.google\.com/i,/opentable\.com/i],p=(...e)=>{for(const t of e)if("string"==typeof t&&t.trim().length>0)return t},w=e=>{if(e)try{return new URL(e).hostname}catch{return}};e.buildWebsiteExtractorUrl=l,e.fetchWebsiteExtractor=d,e.useBoolean=function(e=!1){const[n,r]=t.useState(e),o=t.useCallback(()=>r(!0),[]),i=t.useCallback(()=>r(!1),[]),u=t.useCallback(()=>r(e=>!e),[]);return t.useMemo(()=>({value:n,setValue:r,setTrue:o,setFalse:i,toggle:u}),[n,r,o,i,u])},e.useCopyToClipboard=function(e={}){const n=e.resetDelay??2e3,r=t.useRef(null),[o,i]=t.useState(null),u=t.useMemo(()=>!("undefined"==typeof navigator||!navigator.clipboard)||"undefined"!=typeof document&&("function"==typeof document.queryCommandSupported&&document.queryCommandSupported("copy")),[]),c=t.useCallback(()=>{r.current&&clearTimeout(r.current),r.current=setTimeout(()=>{i(null)},n)},[n]),s=t.useCallback(async e=>{if(!u)return!1;const t="undefined"!=typeof navigator&&!!navigator.clipboard;try{if(t)await navigator.clipboard.writeText(e);else if("undefined"!=typeof document){const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="fixed",t.style.left="-9999px",t.style.top="0",document.body.appendChild(t),t.focus(),t.select();const n=document.execCommand("copy");if(document.body.removeChild(t),!n)return!1}return i(e),c(),!0}catch{return!1}},[u,c]);return t.useEffect(()=>()=>{r.current&&clearTimeout(r.current)},[]),{copy:s,copiedText:o,isSupported:u}},e.useDebounceCallback=r,e.useDebounceValue=o,e.useEventListener=u,e.useHover=function(e){const[n,r]=t.useState(!1),o=t.useCallback(()=>{r(!0)},[]),i=t.useCallback(()=>{r(!1)},[]);return u("pointerenter",o,e),u("pointerleave",i,e),n},e.useIsClient=c,e.useIsomorphicLayoutEffect=n,e.useLocalStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:i=JSON.stringify,deserialize:u=JSON.parse,listenToStorageChanges:c=!0}=r,s=t.useRef(n),a=t.useCallback(()=>{if("undefined"==typeof window)return s.current;if(!o)return s.current;try{const t=window.localStorage.getItem(e);return t?u(t):s.current}catch{return s.current}},[u,o,e]),[l,d]=t.useState(()=>a()),m=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.localStorage.setItem(e,i(r))}catch{}return r})},[e,i]);return t.useEffect(()=>{d(a())},[a]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(u(t.newValue))}catch{d(s.current)}else d(s.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[u,e,c]),[l,m]},e.useMap=s,e.useMediaQuery=function(e,n={}){const[r,o]=t.useState(()=>"undefined"==typeof window||"function"!=typeof window.matchMedia?n.defaultValue??!1:window.matchMedia(e).matches);return t.useEffect(()=>{if("undefined"==typeof window||"function"!=typeof window.matchMedia)return;const t=window.matchMedia(e),n=e=>{o(e.matches)};return o(t.matches),t.addEventListener?(t.addEventListener("change",n),()=>t.removeEventListener("change",n)):(t.addListener(n),()=>t.removeListener(n))},[e]),r},e.useOnClickOutside=function(e,r,o="mousedown",i){const u=t.useRef(r);n(()=>{u.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof document)return;const t=Array.isArray(e)?e:[e],n=e=>{const n=e.target;if("undefined"==typeof Node||!(n instanceof Node))return;t.some(e=>{const t=e.current;return!!t&&t.contains(n)})||u.current(e)};return document.addEventListener(o,n,i),()=>{document.removeEventListener(o,n,i)}},[o,i,e])},e.useOpenGraphExtractor=function(e){const n=t.useMemo(()=>e.skipPatterns??f,[e.skipPatterns]),r=t.useCallback(e=>n.some(t=>t.test(e)),[n]),o=t.useCallback((e,t,n)=>{var r,o,i;const{openGraph:u,htmlInferred:c,hybridGraph:s}=e,a=p(null==u?void 0:u.description,null==s?void 0:s.description,null==c?void 0:c.description),l=p(null==u?void 0:u.title,null==s?void 0:s.title,null==c?void 0:c.title),d=p(null==u?void 0:u.site_name,null==s?void 0:s.site_name,null==c?void 0:c.site_name),m=p(null==s?void 0:s.favicon,null==c?void 0:c.favicon),f=p((null==(r=null==u?void 0:u.image)?void 0:r.url)??void 0,(null==s?void 0:s.image)??void 0,(null==c?void 0:c.image)??void 0,null==(o=null==c?void 0:c.images)?void 0:o[0]),g=p((null==(i=null==u?void 0:u.video)?void 0:i.url)??void 0,(null==s?void 0:s.video)??void 0),b=p(null==s?void 0:s.videoType,null==c?void 0:c.videoType),v=p(n.url,(null==u?void 0:u.url)??void 0,(null==s?void 0:s.url)??void 0,(null==c?void 0:c.url)??void 0,n.finalUrl,n.normalizedUrl,n.requestedUrl)??"";return{description:a,favicon:m,image:f,video:g,videoType:b,siteName:d,title:l,url:v,siteHost:w(v)}},[]);return m({endpoint:"open-graph",options:e,selectData:o,shouldSkip:r})},e.usePlatformFromUrl=function(e){return t.useMemo(()=>{if(!e||"string"!=typeof e)return"unknown";const t=e.trim();if(!t)return"unknown";try{const e=new URL(t).hostname.toLowerCase(),n=i.get(e);return n||(e.endsWith(".substack.com")?"substack":e.endsWith(".github.io")?"github":e.includes("pinterest.com")?"pinterest":e.includes("eventbrite.")?"eventbrite":e.endsWith(".medium.com")?"medium":"unknown")}catch{return"unknown"}},[e])},e.usePrevious=function(e){const r=t.useRef();return n(()=>{r.current=e},[e]),r.current},e.useResizeObserver=function(e,r,o){const i=t.useRef(r),u=t.useRef(null),[c,s]=t.useState(null);return n(()=>{i.current=r},[r]),t.useEffect(()=>{if("undefined"==typeof ResizeObserver)return;const t="undefined"!=typeof Element&&e instanceof Element?e:(n=e)&&"object"==typeof n&&"current"in n?e.current:null;var n;if(!t)return;const r=new ResizeObserver(e=>{const t=e[0];u.current=t,i.current?i.current(t):s(t)});return r.observe(t,o),()=>r.disconnect()},[o,e]),i.current?u.current:c},e.useSessionStorage=function(e,n,r={}){const{initializeWithValue:o=!0,serialize:i=JSON.stringify,deserialize:u=JSON.parse,listenToStorageChanges:c=!1}=r,s=t.useRef(n),a=t.useCallback(()=>{if("undefined"==typeof window)return s.current;if(!o)return s.current;try{const t=window.sessionStorage.getItem(e);return t?u(t):s.current}catch{return s.current}},[u,o,e]),[l,d]=t.useState(()=>a()),m=t.useCallback(t=>{d(n=>{const r="function"==typeof t?t(n):t;if("undefined"!=typeof window)try{window.sessionStorage.setItem(e,i(r))}catch{}return r})},[e,i]);return t.useEffect(()=>{d(a())},[a]),t.useEffect(()=>{if("undefined"==typeof window||!c)return;const t=t=>{if(t.key===e)if(null!==t.newValue)try{d(u(t.newValue))}catch{d(s.current)}else d(s.current)};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[u,e,c]),[l,m]},e.useThrottle=function(e,n,r={}){const o=r.leading??!0,i=r.trailing??!0,u=Math.max(0,n),[c,s]=t.useState(e),a=t.useRef(0),l=t.useRef(null),d=t.useRef(null);return t.useEffect(()=>{if(0===u)return void s(e);const t=Date.now();if(0===a.current)return a.current=t,o?void s(e):void(i&&!l.current&&(d.current=e,l.current=setTimeout(()=>{l.current=null,null!==d.current&&(s(d.current),d.current=null,a.current=Date.now())},u)));const n=t-a.current;if(n>=u&&o)return s(e),a.current=t,void(d.current=null);if(i&&(d.current=e,!l.current)){const e=Math.max(u-n,0);l.current=setTimeout(()=>{l.current=null,null!==d.current&&(s(d.current),d.current=null,a.current=Date.now())},e)}},[o,i,e,u]),t.useEffect(()=>()=>{l.current&&clearTimeout(l.current)},[]),c},e.useWebsiteLinksExtractor=function(e){return m({endpoint:"links",options:e,selectData:t.useCallback(e=>({totalLinks:e.totalLinks??0,uniqueDomains:e.uniqueDomains??0,links:e.links??[]}),[])})},e.useWebsiteMetaExtractor=function(e){return m({endpoint:"meta",options:e,selectData:t.useCallback(e=>({title:e.title??void 0,description:e.description??void 0,language:e.language??void 0,canonicalUrl:e.canonicalUrl??void 0,feedUrl:e.feedUrl??null,textContentLength:e.textContentLength??void 0,metaTags:e.metaTags??{}}),[])})},e.useWebsiteRssExtractor=function(e){return m({endpoint:"rss",options:e,selectData:t.useCallback(e=>({feedUrl:e.feedUrl??null,feeds:e.feeds??[]}),[])})},e.useWebsiteSchemaExtractor=function(e){return m({endpoint:"schema",options:e,selectData:t.useCallback(e=>({schema:e.schema??[],schemaTypes:e.schemaTypes??[]}),[])})},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
2
2
|
//# sourceMappingURL=opensite-hooks.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opensite-hooks.umd.js","sources":["../../src/core/useIsomorphicLayoutEffect.ts","../../src/core/useDebounceCallback.ts","../../src/core/useDebounceValue.ts","../../src/core/usePlatformFromUrl.ts","../../src/core/useEventListener.ts","../../src/core/useIsClient.ts","../../src/core/useMap.ts","../../src/core/websiteExtractorTypes.ts","../../src/core/websiteExtractorService.ts","../../src/core/useWebsiteExtractorBase.ts","../../src/core/useOpenGraphExtractor.ts","../../src/core/useBoolean.ts","../../src/core/useCopyToClipboard.ts","../../src/core/useHover.ts","../../src/core/useLocalStorage.ts","../../src/core/useMediaQuery.ts","../../src/core/useOnClickOutside.ts","../../src/core/usePrevious.ts","../../src/core/useResizeObserver.ts","../../src/core/useSessionStorage.ts","../../src/core/useThrottle.ts","../../src/core/useWebsiteLinksExtractor.ts","../../src/core/useWebsiteMetaExtractor.ts","../../src/core/useWebsiteRssExtractor.ts","../../src/core/useWebsiteSchemaExtractor.ts"],"sourcesContent":["import { useEffect, useLayoutEffect } from \"react\";\n\nexport const useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\nexport interface DebounceOptions {\n leading?: boolean;\n trailing?: boolean;\n maxWait?: number;\n}\n\nexport interface DebouncedCallback<T extends (...args: any[]) => void> {\n debouncedCallback: (...args: Parameters<T>) => void;\n cancel: () => void;\n flush: () => void;\n}\n\nexport function useDebounceCallback<T extends (...args: any[]) => void>(\n callback: T,\n delay: number,\n options: DebounceOptions = {}\n): DebouncedCallback<T> {\n const callbackRef = useRef(callback);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const maxTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastArgsRef = useRef<Parameters<T> | null>(null);\n\n const leading = options.leading ?? false;\n const trailing = options.trailing ?? true;\n const maxWait = options.maxWait;\n const wait = Math.max(0, delay);\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n\n const clearTimers = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n if (maxTimeoutRef.current) {\n clearTimeout(maxTimeoutRef.current);\n maxTimeoutRef.current = null;\n }\n }, []);\n\n const invoke = useCallback(() => {\n if (!lastArgsRef.current) {\n return;\n }\n const args = lastArgsRef.current;\n lastArgsRef.current = null;\n callbackRef.current(...args);\n }, []);\n\n const debouncedCallback = useCallback(\n (...args: Parameters<T>) => {\n lastArgsRef.current = args;\n\n const shouldInvokeLeading =\n leading && timeoutRef.current === null && maxTimeoutRef.current === null;\n if (shouldInvokeLeading) {\n invoke();\n }\n\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n if (trailing) {\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n if (lastArgsRef.current) {\n invoke();\n }\n if (maxTimeoutRef.current) {\n clearTimeout(maxTimeoutRef.current);\n maxTimeoutRef.current = null;\n }\n }, wait);\n }\n\n if (maxWait !== undefined && maxWait !== null && trailing) {\n if (!maxTimeoutRef.current) {\n const maxDelay = Math.max(0, maxWait);\n maxTimeoutRef.current = setTimeout(() => {\n maxTimeoutRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n if (lastArgsRef.current) {\n invoke();\n }\n }, maxDelay);\n }\n }\n },\n [invoke, leading, trailing, maxWait, wait]\n );\n\n const cancel = useCallback(() => {\n clearTimers();\n lastArgsRef.current = null;\n }, [clearTimers]);\n\n const flush = useCallback(() => {\n if (!lastArgsRef.current) {\n return;\n }\n clearTimers();\n invoke();\n }, [clearTimers, invoke]);\n\n useEffect(() => () => cancel(), [cancel]);\n\n return { debouncedCallback, cancel, flush };\n}\n","import { useEffect, useState } from \"react\";\nimport { DebounceOptions, useDebounceCallback } from \"./useDebounceCallback.js\";\n\nexport function useDebounceValue<T>(\n value: T,\n delay: number,\n options?: DebounceOptions\n): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n const { debouncedCallback, cancel } = useDebounceCallback(\n (next: T) => {\n setDebouncedValue(next);\n },\n delay,\n options\n );\n\n useEffect(() => {\n debouncedCallback(value);\n }, [debouncedCallback, value]);\n\n useEffect(() => () => cancel(), [cancel]);\n\n return debouncedValue;\n}\n","import { useMemo } from \"react\";\n\n/**\n * Supported social platform names\n */\nexport type SocialPlatformName =\n | \"instagram\"\n | \"linkedin\"\n | \"google\"\n | \"facebook\"\n | \"tiktok\"\n | \"youtube\"\n | \"yelp\"\n | \"spotify\"\n | \"apple\"\n | \"x\"\n | \"unknown\";\n\n/**\n * Platform hostname mappings for O(1) lookup performance.\n * Includes standard domains and known URL variants/shorteners.\n */\nconst PLATFORM_HOSTNAME_MAP = new Map<string, SocialPlatformName>([\n // Instagram\n [\"instagram.com\", \"instagram\"],\n [\"www.instagram.com\", \"instagram\"],\n [\"instagr.am\", \"instagram\"],\n [\"www.instagr.am\", \"instagram\"],\n\n // LinkedIn\n [\"linkedin.com\", \"linkedin\"],\n [\"www.linkedin.com\", \"linkedin\"],\n [\"ca.linkedin.com\", \"linkedin\"],\n [\"uk.linkedin.com\", \"linkedin\"],\n [\"in.linkedin.com\", \"linkedin\"],\n [\"lnkd.in\", \"linkedin\"],\n\n // Google (including Maps variants)\n [\"google.com\", \"google\"],\n [\"www.google.com\", \"google\"],\n [\"maps.google.com\", \"google\"],\n [\"goo.gl\", \"google\"],\n [\"maps.app.goo.gl\", \"google\"],\n [\"g.co\", \"google\"],\n\n // Facebook\n [\"facebook.com\", \"facebook\"],\n [\"www.facebook.com\", \"facebook\"],\n [\"m.facebook.com\", \"facebook\"],\n [\"fb.com\", \"facebook\"],\n [\"fb.me\", \"facebook\"],\n [\"on.fb.me\", \"facebook\"],\n\n // TikTok\n [\"tiktok.com\", \"tiktok\"],\n [\"www.tiktok.com\", \"tiktok\"],\n [\"m.tiktok.com\", \"tiktok\"],\n [\"vm.tiktok.com\", \"tiktok\"],\n [\"vt.tiktok.com\", \"tiktok\"],\n\n // YouTube\n [\"youtube.com\", \"youtube\"],\n [\"www.youtube.com\", \"youtube\"],\n [\"m.youtube.com\", \"youtube\"],\n [\"youtu.be\", \"youtube\"],\n\n // Yelp\n [\"yelp.com\", \"yelp\"],\n [\"www.yelp.com\", \"yelp\"],\n [\"m.yelp.com\", \"yelp\"],\n\n // Spotify\n [\"spotify.com\", \"spotify\"],\n [\"www.spotify.com\", \"spotify\"],\n [\"open.spotify.com\", \"spotify\"],\n [\"play.spotify.com\", \"spotify\"],\n [\"spoti.fi\", \"spotify\"],\n [\"spotify.link\", \"spotify\"],\n\n // Apple\n [\"apple.com\", \"apple\"],\n [\"www.apple.com\", \"apple\"],\n [\"music.apple.com\", \"apple\"],\n [\"podcasts.apple.com\", \"apple\"],\n [\"apps.apple.com\", \"apple\"],\n [\"itunes.apple.com\", \"apple\"],\n\n // X (formerly Twitter)\n [\"x.com\", \"x\"],\n [\"www.x.com\", \"x\"],\n [\"twitter.com\", \"x\"],\n [\"www.twitter.com\", \"x\"],\n [\"t.co\", \"x\"],\n]);\n\n/**\n * Extracts the social platform name from a URL string.\n * Uses the native URL API for validation and hostname extraction,\n * then performs O(1) Map lookup for platform identification.\n *\n * @param url - The URL string to analyze\n * @returns The identified platform name or \"unknown\" if not recognized\n *\n * @example\n * ```tsx\n * const platform = usePlatformFromUrl(\"https://www.youtube.com/@iamthedelo\");\n * // Returns: \"youtube\"\n *\n * const platform = usePlatformFromUrl(\"https://maps.app.goo.gl/XDuog3V5fTuPcWCH7\");\n * // Returns: \"google\"\n *\n * const platform = usePlatformFromUrl(\"https://twitter.com/jordanhudgens\");\n * // Returns: \"x\"\n *\n * const platform = usePlatformFromUrl(\"not-a-url\");\n * // Returns: \"unknown\"\n * ```\n */\nexport function usePlatformFromUrl(url: string): SocialPlatformName {\n return useMemo(() => {\n if (!url || typeof url !== \"string\") {\n return \"unknown\";\n }\n\n const trimmedUrl = url.trim();\n if (!trimmedUrl) {\n return \"unknown\";\n }\n\n try {\n const urlObj = new URL(trimmedUrl);\n const hostname = urlObj.hostname.toLowerCase();\n\n const platform = PLATFORM_HOSTNAME_MAP.get(hostname);\n return platform ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n }, [url]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\ntype EventTargetLike = Window | Document | HTMLElement | null;\ntype ElementRef = React.RefObject<HTMLElement>;\n\nconst isRefObject = (value: unknown): value is ElementRef =>\n !!value && typeof value === \"object\" && \"current\" in value;\n\nexport function useEventListener<K extends keyof WindowEventMap>(\n eventName: K,\n handler: (event: WindowEventMap[K]) => void,\n element?: Window,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener<K extends keyof DocumentEventMap>(\n eventName: K,\n handler: (event: DocumentEventMap[K]) => void,\n element: Document,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener<K extends keyof HTMLElementEventMap>(\n eventName: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n element: ElementRef,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener<K extends keyof HTMLElementEventMap>(\n eventName: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n element: HTMLElement,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener(\n eventName: string,\n handler: EventListenerOrEventListenerObject,\n element?: EventTargetLike | ElementRef,\n options?: AddEventListenerOptions | boolean\n): void {\n const savedHandler = useRef(handler);\n\n useIsomorphicLayoutEffect(() => {\n savedHandler.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const isWindow =\n typeof Window !== \"undefined\" && element instanceof Window;\n const isDocument =\n typeof Document !== \"undefined\" && element instanceof Document;\n\n const target: EventTargetLike | null =\n element === undefined\n ? typeof window !== \"undefined\"\n ? window\n : null\n : isWindow || isDocument\n ? element\n : typeof HTMLElement !== \"undefined\" && element instanceof HTMLElement\n ? element\n : isRefObject(element)\n ? element.current\n : null;\n\n if (!target?.addEventListener) {\n return;\n }\n\n const listener = (event: Event) => {\n const currentHandler = savedHandler.current;\n if (typeof currentHandler === \"function\") {\n currentHandler(event);\n } else {\n currentHandler.handleEvent(event);\n }\n };\n\n target.addEventListener(eventName, listener, options);\n\n return () => {\n target.removeEventListener(eventName, listener, options);\n };\n }, [eventName, element, options]);\n}\n","import { useEffect, useState } from \"react\";\n\nexport function useIsClient(): boolean {\n const [isClient, setIsClient] = useState(false);\n\n useEffect(() => {\n setIsClient(true);\n }, []);\n\n return isClient;\n}\n","import { useMemo, useRef, useState } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\nexport interface MapActions<K, V> {\n set: (key: K, value: V) => void;\n setAll: (entries: Map<K, V> | [K, V][]) => void;\n remove: (key: K) => void;\n clear: () => void;\n get: (key: K) => V | undefined;\n has: (key: K) => boolean;\n}\n\nexport function useMap<K, V>(\n initialState?: Map<K, V> | [K, V][]\n): [Map<K, V>, MapActions<K, V>] {\n const [map, setMap] = useState<Map<K, V>>(() => {\n if (initialState instanceof Map) {\n return new Map(initialState);\n }\n if (Array.isArray(initialState)) {\n return new Map(initialState);\n }\n return new Map();\n });\n\n const mapRef = useRef(map);\n\n // Use useIsomorphicLayoutEffect to update mapRef synchronously with state\n // This ensures get() and has() methods always read the current map state\n // without a timing window where they could return stale values\n useIsomorphicLayoutEffect(() => {\n mapRef.current = map;\n }, [map]);\n\n const actions = useMemo<MapActions<K, V>>(\n () => ({\n set: (key: K, value: V) => {\n setMap((prev) => {\n const next = new Map(prev);\n next.set(key, value);\n return next;\n });\n },\n setAll: (entries: Map<K, V> | [K, V][]) => {\n setMap(entries instanceof Map ? new Map(entries) : new Map(entries));\n },\n remove: (key: K) => {\n setMap((prev) => {\n const next = new Map(prev);\n next.delete(key);\n return next;\n });\n },\n clear: () => setMap(new Map()),\n get: (key: K) => mapRef.current.get(key),\n has: (key: K) => mapRef.current.has(key),\n }),\n []\n );\n\n return [map, actions];\n}\n","export interface WebsiteExtractCacheMeta {\n hit: boolean;\n ageSeconds: number;\n ttlSeconds: number;\n staleWhileRevalidateSeconds: number;\n}\n\nexport interface WebsiteExtractMeta {\n requestedUrl: string;\n finalUrl: string;\n url: string;\n normalizedUrl: string;\n status: number;\n contentType: string;\n fetchedAt: string;\n bodyBytes: number;\n bodyTruncated: boolean;\n maxBodyBytes: number;\n cache: WebsiteExtractCacheMeta;\n}\n\nexport interface WebsiteExtractorError {\n message: string;\n status?: number;\n raw?: unknown;\n}\n\nexport interface WebsiteExtractorOptions {\n url?: string;\n apiKey?: string;\n baseUrl?: string;\n debounceMs?: number;\n refreshDebounceMs?: number;\n enabled?: boolean;\n cache?: boolean;\n}\n\nexport interface WebsiteExtractorState<TData, TRaw = TData> {\n loading: boolean;\n data?: TData;\n raw?: TRaw;\n meta?: WebsiteExtractMeta;\n error?: WebsiteExtractorError;\n}\n\nexport interface WebsiteExtractorResult<TData, TRaw = TData>\n extends WebsiteExtractorState<TData, TRaw> {\n refresh: () => void;\n}\n\nexport type WebsiteExtractorResponse<TPayload> = WebsiteExtractMeta & TPayload;\n\nexport interface WebsiteExtractorRequest {\n endpoint: string;\n url: string;\n apiKey?: string;\n baseUrl?: string;\n signal?: AbortSignal;\n}\n\nexport type WebsiteExtractorClientResult<T> =\n | { ok: true; response: T }\n | { ok: false; error: WebsiteExtractorError };\n\nexport const DEFAULT_WEBSITE_EXTRACTOR_BASE_URL = \"https://octane.buzz\";\n\nexport const DEFAULT_EXTRACTOR_DEBOUNCE_MS = 250;\n\nexport const DEFAULT_EXTRACTOR_REFRESH_DEBOUNCE_MS = 150;\n\nexport const DEFAULT_EXTRACTOR_ENABLED = true;\n\nexport const DEFAULT_EXTRACTOR_CACHE = true;\n\nexport function extractWebsiteMeta(response: WebsiteExtractMeta): WebsiteExtractMeta {\n const {\n requestedUrl,\n finalUrl,\n url,\n normalizedUrl,\n status,\n contentType,\n fetchedAt,\n bodyBytes,\n bodyTruncated,\n maxBodyBytes,\n cache,\n } = response;\n\n return {\n requestedUrl,\n finalUrl,\n url,\n normalizedUrl,\n status,\n contentType,\n fetchedAt,\n bodyBytes,\n bodyTruncated,\n maxBodyBytes,\n cache,\n };\n}\n\nexport function stripWebsiteMeta<TResponse extends WebsiteExtractMeta>(\n response: TResponse\n): Omit<TResponse, keyof WebsiteExtractMeta> {\n const {\n requestedUrl: _requestedUrl,\n finalUrl: _finalUrl,\n url: _url,\n normalizedUrl: _normalizedUrl,\n status: _status,\n contentType: _contentType,\n fetchedAt: _fetchedAt,\n bodyBytes: _bodyBytes,\n bodyTruncated: _bodyTruncated,\n maxBodyBytes: _maxBodyBytes,\n cache: _cache,\n ...payload\n } = response;\n\n return payload;\n}\n","import {\n DEFAULT_WEBSITE_EXTRACTOR_BASE_URL,\n type WebsiteExtractorClientResult,\n type WebsiteExtractorError,\n type WebsiteExtractorRequest,\n} from \"./websiteExtractorTypes.js\";\n\nconst EXTRACTOR_PATH = \"/api/v1/extract\";\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\nexport function buildWebsiteExtractorUrl(request: WebsiteExtractorRequest): string {\n const baseUrl = normalizeBaseUrl(\n request.baseUrl ?? DEFAULT_WEBSITE_EXTRACTOR_BASE_URL\n );\n const params = new URLSearchParams();\n\n if (request.apiKey) {\n params.set(\"api_key\", request.apiKey);\n }\n\n params.set(\"url\", request.url);\n\n return `${baseUrl}${EXTRACTOR_PATH}/${request.endpoint}?${params.toString()}`;\n}\n\nexport async function fetchWebsiteExtractor<TResponse>(\n request: WebsiteExtractorRequest\n): Promise<WebsiteExtractorClientResult<TResponse>> {\n if (!request.url || request.url.trim().length === 0) {\n return {\n ok: false,\n error: {\n message: \"URL is required.\",\n },\n };\n }\n\n const endpoint = buildWebsiteExtractorUrl(request);\n\n try {\n const response = await fetch(endpoint, {\n method: \"GET\",\n signal: request.signal,\n });\n\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n\n if (!response.ok) {\n const errorPayload = payload as { error?: string; status?: number } | null;\n const error: WebsiteExtractorError = {\n message:\n errorPayload?.error ??\n `Request failed with status ${response.status}.`,\n status: errorPayload?.status ?? response.status,\n raw: payload,\n };\n\n return { ok: false, error };\n }\n\n return { ok: true, response: payload as TResponse };\n } catch (error) {\n if (request.signal?.aborted) {\n return {\n ok: false,\n error: {\n message: \"Request aborted.\",\n },\n };\n }\n\n return {\n ok: false,\n error: {\n message:\n error instanceof Error ? error.message : \"Request failed unexpectedly.\",\n raw: error,\n },\n };\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useDebounceCallback } from \"./useDebounceCallback.js\";\nimport { useDebounceValue } from \"./useDebounceValue.js\";\nimport { useIsClient } from \"./useIsClient.js\";\nimport { useMap } from \"./useMap.js\";\nimport { fetchWebsiteExtractor } from \"./websiteExtractorService.js\";\nimport {\n DEFAULT_EXTRACTOR_CACHE,\n DEFAULT_EXTRACTOR_DEBOUNCE_MS,\n DEFAULT_EXTRACTOR_ENABLED,\n DEFAULT_EXTRACTOR_REFRESH_DEBOUNCE_MS,\n DEFAULT_WEBSITE_EXTRACTOR_BASE_URL,\n extractWebsiteMeta,\n stripWebsiteMeta,\n type WebsiteExtractorOptions,\n type WebsiteExtractorResult,\n type WebsiteExtractorState,\n type WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\ninterface CachedExtractorEntry<TData, TRaw extends WebsiteExtractMeta> {\n data: TData;\n raw: TRaw;\n meta: WebsiteExtractMeta;\n}\n\ninterface UseWebsiteExtractorBaseConfig<\n TResponse extends WebsiteExtractMeta,\n TData,\n> {\n endpoint: string;\n options: WebsiteExtractorOptions;\n selectData: (\n payload: Omit<TResponse, keyof WebsiteExtractMeta>,\n raw: TResponse,\n meta: WebsiteExtractMeta\n ) => TData;\n shouldSkip?: (url: string) => boolean;\n}\n\nexport function useWebsiteExtractorBase<\n TResponse extends WebsiteExtractMeta,\n TData,\n>(config: UseWebsiteExtractorBaseConfig<TResponse, TData>): WebsiteExtractorResult<\n TData,\n TResponse\n> {\n const { endpoint, options, selectData, shouldSkip } = config;\n const isClient = useIsClient();\n const [state, setState] = useState<WebsiteExtractorState<TData, TResponse>>({\n loading: false,\n });\n\n const [, cacheActions] = useMap<\n string,\n CachedExtractorEntry<TData, TResponse>\n >();\n const cacheEnabled = options.cache ?? DEFAULT_EXTRACTOR_CACHE;\n const enabled = options.enabled ?? DEFAULT_EXTRACTOR_ENABLED;\n const debounceMs = options.debounceMs ?? DEFAULT_EXTRACTOR_DEBOUNCE_MS;\n const refreshDebounceMs =\n options.refreshDebounceMs ?? DEFAULT_EXTRACTOR_REFRESH_DEBOUNCE_MS;\n\n const normalizedUrl = useMemo(() => {\n return options.url?.trim() ?? \"\";\n }, [options.url]);\n\n const debouncedUrl = useDebounceValue(normalizedUrl, debounceMs);\n\n const refreshCounterRef = useRef(0);\n const lastRefreshHandledRef = useRef(0);\n const [refreshToken, setRefreshToken] = useState(0);\n\n const { debouncedCallback: scheduleRefresh, cancel: cancelRefresh } =\n useDebounceCallback(() => {\n refreshCounterRef.current += 1;\n setRefreshToken(refreshCounterRef.current);\n }, refreshDebounceMs);\n\n const refresh = useCallback(() => {\n scheduleRefresh();\n }, [scheduleRefresh]);\n\n useEffect(() => () => cancelRefresh(), [cancelRefresh]);\n\n const requestKey = useMemo(() => {\n if (!debouncedUrl) {\n return \"\";\n }\n\n const baseUrl = options.baseUrl ?? DEFAULT_WEBSITE_EXTRACTOR_BASE_URL;\n const apiKey = options.apiKey ?? \"\";\n return `${endpoint}:${baseUrl}:${apiKey}:${debouncedUrl}`;\n }, [\n endpoint,\n options.apiKey,\n options.baseUrl,\n debouncedUrl,\n ]);\n\n const inFlightControllerRef = useRef<AbortController | null>(null);\n\n useEffect(() => {\n if (!isClient) {\n return;\n }\n\n if (!enabled || !debouncedUrl) {\n setState({ loading: false });\n return;\n }\n\n if (shouldSkip?.(debouncedUrl)) {\n setState({ loading: false });\n return;\n }\n\n const forceRefresh = refreshToken !== lastRefreshHandledRef.current;\n if (forceRefresh) {\n lastRefreshHandledRef.current = refreshToken;\n }\n\n if (cacheEnabled && !forceRefresh) {\n const cached = cacheActions.get(requestKey);\n if (cached) {\n setState({\n loading: false,\n data: cached.data,\n raw: cached.raw,\n meta: cached.meta,\n });\n return;\n }\n }\n\n inFlightControllerRef.current?.abort();\n const controller = new AbortController();\n inFlightControllerRef.current = controller;\n\n setState((prev) => ({\n ...prev,\n loading: true,\n error: undefined,\n }));\n\n fetchWebsiteExtractor<TResponse>({\n endpoint,\n url: debouncedUrl,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: controller.signal,\n })\n .then((result) => {\n if (controller.signal.aborted) {\n return;\n }\n\n if (!result.ok) {\n setState((prev) => ({\n ...prev,\n loading: false,\n error: result.error,\n }));\n return;\n }\n\n const raw = result.response;\n const meta = extractWebsiteMeta(raw);\n const payload = stripWebsiteMeta(raw);\n const data = selectData(payload, raw, meta);\n\n if (cacheEnabled) {\n cacheActions.set(requestKey, { data, raw, meta });\n }\n\n setState({\n loading: false,\n data,\n raw,\n meta,\n });\n })\n .catch((error) => {\n if (controller.signal.aborted) {\n return;\n }\n\n setState((prev) => ({\n ...prev,\n loading: false,\n error: {\n message:\n error instanceof Error\n ? error.message\n : \"Request failed unexpectedly.\",\n raw: error,\n },\n }));\n });\n\n return () => {\n controller.abort();\n };\n }, [\n cacheActions,\n cacheEnabled,\n debouncedUrl,\n enabled,\n endpoint,\n isClient,\n options.apiKey,\n options.baseUrl,\n requestKey,\n refreshToken,\n selectData,\n shouldSkip,\n ]);\n\n return useMemo(\n () => ({\n ...state,\n refresh,\n }),\n [refresh, state]\n );\n}\n","import { useCallback, useMemo } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface OpenGraphImage {\n url?: string | null;\n height?: string | null;\n width?: string | null;\n}\n\nexport interface OpenGraphVideo {\n url?: string | null;\n height?: string | null;\n width?: string | null;\n}\n\nexport interface OpenGraphData {\n description?: string | null;\n title?: string | null;\n site_name?: string | null;\n image?: OpenGraphImage | null;\n video?: OpenGraphVideo | null;\n url?: string | null;\n ogType?: string | null;\n}\n\nexport interface OpenGraphHtmlInferredData {\n description?: string | null;\n title?: string | null;\n type?: string | null;\n videoType?: string | null;\n url?: string | null;\n favicon?: string | null;\n images?: string[] | null;\n image?: string | null;\n site_name?: string | null;\n}\n\nexport interface OpenGraphHybridData {\n description?: string | null;\n title?: string | null;\n type?: string | null;\n image?: string | null;\n video?: string | null;\n videoType?: string | null;\n favicon?: string | null;\n site_name?: string | null;\n url?: string | null;\n videoWidth?: number | null;\n videoHeight?: number | null;\n}\n\nexport type OpenGraphResponse = WebsiteExtractorResponse<{\n openGraph: OpenGraphData;\n htmlInferred: OpenGraphHtmlInferredData;\n hybridGraph: OpenGraphHybridData;\n}>;\n\nexport interface OpenGraphSummary {\n description?: string;\n favicon?: string;\n image?: string;\n video?: string;\n videoType?: string;\n siteName?: string;\n title?: string;\n url: string;\n siteHost?: string;\n}\n\nexport interface OpenGraphExtractorOptions extends WebsiteExtractorOptions {\n skipPatterns?: RegExp[];\n}\n\nconst DEFAULT_SKIP_PATTERNS = [\n /search\\.google\\.com\\/local\\/reviews/i,\n /google\\.com\\/maps\\/place/i,\n /maps\\.google\\.com/i,\n /opentable\\.com/i,\n];\n\nconst pickFirstString = (\n ...values: Array<string | null | undefined>\n): string | undefined => {\n for (const value of values) {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value;\n }\n }\n return undefined;\n};\n\nconst safeHost = (value: string | undefined): string | undefined => {\n if (!value) {\n return undefined;\n }\n\n try {\n return new URL(value).hostname;\n } catch {\n return undefined;\n }\n};\n\nexport function useOpenGraphExtractor(\n options: OpenGraphExtractorOptions\n): WebsiteExtractorResult<OpenGraphSummary, OpenGraphResponse> {\n const skipPatterns = useMemo(\n () => options.skipPatterns ?? DEFAULT_SKIP_PATTERNS,\n [options.skipPatterns]\n );\n\n const shouldSkip = useCallback(\n (url: string) => skipPatterns.some((pattern) => pattern.test(url)),\n [skipPatterns]\n );\n\n const selectData = useCallback(\n (\n payload: Omit<OpenGraphResponse, keyof WebsiteExtractMeta>,\n _raw: OpenGraphResponse,\n meta: WebsiteExtractMeta\n ): OpenGraphSummary => {\n const { openGraph, htmlInferred, hybridGraph } = payload;\n\n const description = pickFirstString(\n openGraph?.description,\n hybridGraph?.description,\n htmlInferred?.description\n );\n const title = pickFirstString(\n openGraph?.title,\n hybridGraph?.title,\n htmlInferred?.title\n );\n const siteName = pickFirstString(\n openGraph?.site_name,\n hybridGraph?.site_name,\n htmlInferred?.site_name\n );\n const favicon = pickFirstString(\n hybridGraph?.favicon,\n htmlInferred?.favicon\n );\n const image = pickFirstString(\n openGraph?.image?.url ?? undefined,\n hybridGraph?.image ?? undefined,\n htmlInferred?.image ?? undefined,\n htmlInferred?.images?.[0]\n );\n const video = pickFirstString(\n openGraph?.video?.url ?? undefined,\n hybridGraph?.video ?? undefined\n );\n const videoType = pickFirstString(\n hybridGraph?.videoType,\n htmlInferred?.videoType\n );\n const resolvedUrl =\n pickFirstString(\n meta.url,\n openGraph?.url ?? undefined,\n hybridGraph?.url ?? undefined,\n htmlInferred?.url ?? undefined,\n meta.finalUrl,\n meta.normalizedUrl,\n meta.requestedUrl\n ) ?? \"\";\n\n return {\n description,\n favicon,\n image,\n video,\n videoType,\n siteName,\n title,\n url: resolvedUrl,\n siteHost: safeHost(resolvedUrl),\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<OpenGraphResponse, OpenGraphSummary>({\n endpoint: \"open-graph\",\n options,\n selectData,\n shouldSkip,\n });\n}\n","import { useCallback, useMemo, useState } from \"react\";\n\nexport interface UseBooleanResult {\n value: boolean;\n setValue: React.Dispatch<React.SetStateAction<boolean>>;\n setTrue: () => void;\n setFalse: () => void;\n toggle: () => void;\n}\n\nexport function useBoolean(defaultValue = false): UseBooleanResult {\n const [value, setValue] = useState<boolean>(defaultValue);\n\n const setTrue = useCallback(() => setValue(true), []);\n const setFalse = useCallback(() => setValue(false), []);\n const toggle = useCallback(() => setValue((current) => !current), []);\n\n // Memoize the return object to prevent unnecessary re-renders in consumers\n // that use the result object in dependency arrays\n return useMemo(\n () => ({ value, setValue, setTrue, setFalse, toggle }),\n [value, setValue, setTrue, setFalse, toggle]\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nexport interface UseCopyToClipboardOptions {\n resetDelay?: number;\n}\n\nexport interface CopyToClipboardResult {\n copy: (text: string) => Promise<boolean>;\n copiedText: string | null;\n isSupported: boolean;\n}\n\nexport function useCopyToClipboard(\n options: UseCopyToClipboardOptions = {}\n): CopyToClipboardResult {\n const resetDelay = options.resetDelay ?? 2000;\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const [copiedText, setCopiedText] = useState<string | null>(null);\n\n const isSupported = useMemo(() => {\n if (typeof navigator !== \"undefined\" && navigator.clipboard) {\n return true;\n }\n if (typeof document === \"undefined\") {\n return false;\n }\n if (typeof document.queryCommandSupported !== \"function\") {\n return false;\n }\n return document.queryCommandSupported(\"copy\");\n }, []);\n\n const resetCopied = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = setTimeout(() => {\n setCopiedText(null);\n }, resetDelay);\n }, [resetDelay]);\n\n const copy = useCallback(\n async (text: string) => {\n if (!isSupported) {\n return false;\n }\n\n const shouldUseClipboardApi =\n typeof navigator !== \"undefined\" && !!navigator.clipboard;\n\n try {\n if (shouldUseClipboardApi) {\n await navigator.clipboard.writeText(text);\n } else if (typeof document !== \"undefined\") {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n textarea.style.top = \"0\";\n document.body.appendChild(textarea);\n textarea.focus();\n textarea.select();\n const success = document.execCommand(\"copy\");\n document.body.removeChild(textarea);\n if (!success) {\n return false;\n }\n }\n\n setCopiedText(text);\n resetCopied();\n return true;\n } catch {\n return false;\n }\n },\n [isSupported, resetCopied]\n );\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return { copy, copiedText, isSupported };\n}\n","import { useCallback, useState } from \"react\";\nimport { useEventListener } from \"./useEventListener.js\";\n\nexport function useHover<T extends HTMLElement>(\n ref: React.RefObject<T>\n): boolean {\n const [isHovered, setIsHovered] = useState(false);\n\n const handleEnter = useCallback(() => {\n setIsHovered(true);\n }, []);\n\n const handleLeave = useCallback(() => {\n setIsHovered(false);\n }, []);\n\n useEventListener(\"pointerenter\", handleEnter, ref);\n useEventListener(\"pointerleave\", handleLeave, ref);\n\n return isHovered;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface StorageOptions<T> {\n initializeWithValue?: boolean;\n serialize?: (value: T) => string;\n deserialize?: (value: string) => T;\n listenToStorageChanges?: boolean;\n}\n\ntype StoredSetter<T> = (value: T | ((current: T) => T)) => void;\n\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T,\n options: StorageOptions<T> = {}\n): [T, StoredSetter<T>] {\n const {\n initializeWithValue = true,\n serialize = JSON.stringify,\n deserialize = JSON.parse as (value: string) => T,\n listenToStorageChanges = true,\n } = options;\n\n const initialValueRef = useRef(initialValue);\n\n const readValue = useCallback(() => {\n if (typeof window === \"undefined\") {\n return initialValueRef.current;\n }\n if (!initializeWithValue) {\n return initialValueRef.current;\n }\n try {\n const item = window.localStorage.getItem(key);\n return item ? deserialize(item) : initialValueRef.current;\n } catch {\n return initialValueRef.current;\n }\n }, [deserialize, initializeWithValue, key]);\n\n const [storedValue, setStoredValue] = useState<T>(() => readValue());\n\n const setValue: StoredSetter<T> = useCallback(\n (value) => {\n setStoredValue((current) => {\n const valueToStore =\n typeof value === \"function\"\n ? (value as (current: T) => T)(current)\n : value;\n if (typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(key, serialize(valueToStore));\n } catch {\n // Ignore write errors (quota/security)\n }\n }\n return valueToStore;\n });\n },\n [key, serialize]\n );\n\n useEffect(() => {\n setStoredValue(readValue());\n }, [readValue]);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !listenToStorageChanges) {\n return;\n }\n\n const handleStorageChange = (event: StorageEvent) => {\n if (event.key !== key) {\n return;\n }\n if (event.newValue === null) {\n setStoredValue(initialValueRef.current);\n return;\n }\n try {\n setStoredValue(deserialize(event.newValue));\n } catch {\n setStoredValue(initialValueRef.current);\n }\n };\n\n window.addEventListener(\"storage\", handleStorageChange);\n return () => window.removeEventListener(\"storage\", handleStorageChange);\n }, [deserialize, key, listenToStorageChanges]);\n\n return [storedValue, setValue];\n}\n","import { useEffect, useState } from \"react\";\n\nexport interface UseMediaQueryOptions {\n defaultValue?: boolean;\n}\n\nexport function useMediaQuery(\n query: string,\n options: UseMediaQueryOptions = {}\n): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return options.defaultValue ?? false;\n }\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n\n const mediaQueryList = window.matchMedia(query);\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n setMatches(mediaQueryList.matches);\n\n if (mediaQueryList.addEventListener) {\n mediaQueryList.addEventListener(\"change\", handler);\n return () => mediaQueryList.removeEventListener(\"change\", handler);\n }\n\n mediaQueryList.addListener(handler);\n return () => mediaQueryList.removeListener(handler);\n }, [query]);\n\n return matches;\n}\n","import { useEffect, useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\ntype PossibleRef<T extends HTMLElement> = React.RefObject<T>;\n\nexport function useOnClickOutside<T extends HTMLElement>(\n ref: PossibleRef<T> | Array<PossibleRef<T>>,\n handler: (event: MouseEvent | TouchEvent | PointerEvent) => void,\n eventType: \"mousedown\" | \"mouseup\" | \"click\" | \"touchstart\" | \"pointerdown\" =\n \"mousedown\",\n options?: AddEventListenerOptions | boolean\n): void {\n const handlerRef = useRef(handler);\n\n // Use useIsomorphicLayoutEffect to update handler ref synchronously\n // This prevents stale closures in rapid click scenarios where the event\n // listener (registered below) might fire before the ref is updated\n useIsomorphicLayoutEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const refs = Array.isArray(ref) ? ref : [ref];\n const listener = (event: MouseEvent | TouchEvent | PointerEvent) => {\n const target = event.target;\n if (typeof Node === \"undefined\" || !(target instanceof Node)) {\n return;\n }\n\n const clickedInside = refs.some((currentRef) => {\n const node = currentRef.current;\n return node ? node.contains(target) : false;\n });\n\n if (!clickedInside) {\n handlerRef.current(event);\n }\n };\n\n document.addEventListener(eventType, listener, options);\n return () => {\n document.removeEventListener(eventType, listener, options);\n };\n }, [eventType, options, ref]);\n}\n","import { useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\nexport function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T>();\n\n // Use useIsomorphicLayoutEffect to capture the previous value synchronously\n // BEFORE paint. This ensures that during render, ref.current holds the actual\n // previous value (from the last render), not the current value.\n // Using useEffect would update AFTER paint, making comparisons incorrect.\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref.current;\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\ntype TargetElement<T extends Element> = React.RefObject<T> | T | null;\n\nconst isRefObject = <T extends Element>(\n value: unknown\n): value is React.RefObject<T> => !!value && typeof value === \"object\" && \"current\" in value;\n\nexport function useResizeObserver<T extends Element>(\n target: TargetElement<T>,\n onResize?: (entry: ResizeObserverEntry) => void,\n options?: ResizeObserverOptions\n): ResizeObserverEntry | null {\n const callbackRef = useRef(onResize);\n const entryRef = useRef<ResizeObserverEntry | null>(null);\n const [entry, setEntry] = useState<ResizeObserverEntry | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = onResize;\n }, [onResize]);\n\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") {\n return;\n }\n\n const element =\n typeof Element !== \"undefined\" && target instanceof Element\n ? target\n : isRefObject<T>(target)\n ? target.current\n : null;\n if (!element) {\n return;\n }\n\n const observer = new ResizeObserver((entries) => {\n const firstEntry = entries[0];\n entryRef.current = firstEntry;\n if (callbackRef.current) {\n callbackRef.current(firstEntry);\n } else {\n setEntry(firstEntry);\n }\n });\n\n observer.observe(element, options);\n return () => observer.disconnect();\n }, [options, target]);\n\n return callbackRef.current ? entryRef.current : entry;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface SessionStorageOptions<T> {\n initializeWithValue?: boolean;\n serialize?: (value: T) => string;\n deserialize?: (value: string) => T;\n listenToStorageChanges?: boolean;\n}\n\ntype StoredSetter<T> = (value: T | ((current: T) => T)) => void;\n\nexport function useSessionStorage<T>(\n key: string,\n initialValue: T,\n options: SessionStorageOptions<T> = {}\n): [T, StoredSetter<T>] {\n const {\n initializeWithValue = true,\n serialize = JSON.stringify,\n deserialize = JSON.parse as (value: string) => T,\n listenToStorageChanges = false,\n } = options;\n\n const initialValueRef = useRef(initialValue);\n\n const readValue = useCallback(() => {\n if (typeof window === \"undefined\") {\n return initialValueRef.current;\n }\n if (!initializeWithValue) {\n return initialValueRef.current;\n }\n try {\n const item = window.sessionStorage.getItem(key);\n return item ? deserialize(item) : initialValueRef.current;\n } catch {\n return initialValueRef.current;\n }\n }, [deserialize, initializeWithValue, key]);\n\n const [storedValue, setStoredValue] = useState<T>(() => readValue());\n\n const setValue: StoredSetter<T> = useCallback(\n (value) => {\n setStoredValue((current) => {\n const valueToStore =\n typeof value === \"function\"\n ? (value as (current: T) => T)(current)\n : value;\n if (typeof window !== \"undefined\") {\n try {\n window.sessionStorage.setItem(key, serialize(valueToStore));\n } catch {\n // Ignore write errors (quota/security)\n }\n }\n return valueToStore;\n });\n },\n [key, serialize]\n );\n\n useEffect(() => {\n setStoredValue(readValue());\n }, [readValue]);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !listenToStorageChanges) {\n return;\n }\n\n const handleStorageChange = (event: StorageEvent) => {\n if (event.key !== key) {\n return;\n }\n if (event.newValue === null) {\n setStoredValue(initialValueRef.current);\n return;\n }\n try {\n setStoredValue(deserialize(event.newValue));\n } catch {\n setStoredValue(initialValueRef.current);\n }\n };\n\n window.addEventListener(\"storage\", handleStorageChange);\n return () => window.removeEventListener(\"storage\", handleStorageChange);\n }, [deserialize, key, listenToStorageChanges]);\n\n return [storedValue, setValue];\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nexport interface ThrottleOptions {\n leading?: boolean;\n trailing?: boolean;\n}\n\nexport function useThrottle<T>(\n value: T,\n interval: number,\n options: ThrottleOptions = {}\n): T {\n const leading = options.leading ?? true;\n const trailing = options.trailing ?? true;\n const wait = Math.max(0, interval);\n\n const [throttledValue, setThrottledValue] = useState<T>(value);\n const lastExecutedRef = useRef<number>(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValueRef = useRef<T | null>(null);\n\n useEffect(() => {\n if (wait === 0) {\n setThrottledValue(value);\n return;\n }\n\n const now = Date.now();\n\n if (lastExecutedRef.current === 0) {\n lastExecutedRef.current = now;\n if (leading) {\n setThrottledValue(value);\n return;\n }\n if (trailing && !timeoutRef.current) {\n pendingValueRef.current = value;\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n if (pendingValueRef.current !== null) {\n setThrottledValue(pendingValueRef.current);\n pendingValueRef.current = null;\n lastExecutedRef.current = Date.now();\n }\n }, wait);\n }\n return;\n }\n\n const elapsed = now - lastExecutedRef.current;\n\n if (elapsed >= wait && leading) {\n setThrottledValue(value);\n lastExecutedRef.current = now;\n pendingValueRef.current = null;\n return;\n }\n\n if (trailing) {\n pendingValueRef.current = value;\n if (!timeoutRef.current) {\n const remaining = Math.max(wait - elapsed, 0);\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n if (pendingValueRef.current !== null) {\n setThrottledValue(pendingValueRef.current);\n pendingValueRef.current = null;\n lastExecutedRef.current = Date.now();\n }\n }, remaining);\n }\n }\n }, [leading, trailing, value, wait]);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return throttledValue;\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteLinkRecord {\n url: string;\n text: string;\n isExternal: boolean;\n domain?: string | null;\n}\n\nexport interface WebsiteLinksPayload {\n totalLinks: number;\n uniqueDomains: number;\n links: WebsiteLinkRecord[];\n}\n\nexport type WebsiteLinksResponse = WebsiteExtractorResponse<WebsiteLinksPayload>;\n\nexport function useWebsiteLinksExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteLinksPayload, WebsiteLinksResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteLinksResponse, keyof WebsiteExtractMeta>\n ): WebsiteLinksPayload => {\n return {\n totalLinks: payload.totalLinks ?? 0,\n uniqueDomains: payload.uniqueDomains ?? 0,\n links: payload.links ?? [],\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteLinksResponse, WebsiteLinksPayload>({\n endpoint: \"links\",\n options,\n selectData,\n });\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteMetaPayload {\n title?: string | null;\n description?: string | null;\n language?: string | null;\n canonicalUrl?: string | null;\n feedUrl?: string | null;\n textContentLength?: number | null;\n metaTags: Record<string, string>;\n}\n\nexport type WebsiteMetaResponse = WebsiteExtractorResponse<WebsiteMetaPayload>;\n\nexport function useWebsiteMetaExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteMetaPayload, WebsiteMetaResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteMetaResponse, keyof WebsiteExtractMeta>\n ): WebsiteMetaPayload => {\n return {\n title: payload.title ?? undefined,\n description: payload.description ?? undefined,\n language: payload.language ?? undefined,\n canonicalUrl: payload.canonicalUrl ?? undefined,\n feedUrl: payload.feedUrl ?? null,\n textContentLength: payload.textContentLength ?? undefined,\n metaTags: payload.metaTags ?? {},\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteMetaResponse, WebsiteMetaPayload>({\n endpoint: \"meta\",\n options,\n selectData,\n });\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteRssFeed {\n url: string;\n feedType: string;\n title?: string | null;\n}\n\nexport interface WebsiteRssPayload {\n feedUrl?: string | null;\n feeds: WebsiteRssFeed[];\n}\n\nexport type WebsiteRssResponse = WebsiteExtractorResponse<WebsiteRssPayload>;\n\nexport function useWebsiteRssExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteRssPayload, WebsiteRssResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteRssResponse, keyof WebsiteExtractMeta>\n ): WebsiteRssPayload => {\n return {\n feedUrl: payload.feedUrl ?? null,\n feeds: payload.feeds ?? [],\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteRssResponse, WebsiteRssPayload>({\n endpoint: \"rss\",\n options,\n selectData,\n });\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteSchemaRecord {\n schema_type: string;\n value: Record<string, unknown>;\n}\n\nexport interface WebsiteSchemaPayload {\n schema: WebsiteSchemaRecord[];\n schemaTypes: string[];\n}\n\nexport type WebsiteSchemaResponse = WebsiteExtractorResponse<WebsiteSchemaPayload>;\n\nexport function useWebsiteSchemaExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteSchemaPayload, WebsiteSchemaResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteSchemaResponse, keyof WebsiteExtractMeta>\n ): WebsiteSchemaPayload => {\n return {\n schema: payload.schema ?? [],\n schemaTypes: payload.schemaTypes ?? [],\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteSchemaResponse, WebsiteSchemaPayload>({\n endpoint: \"schema\",\n options,\n selectData,\n });\n}\n"],"names":["useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","useDebounceCallback","callback","delay","options","callbackRef","useRef","timeoutRef","maxTimeoutRef","lastArgsRef","leading","trailing","maxWait","wait","Math","max","current","clearTimers","useCallback","clearTimeout","invoke","args","debouncedCallback","setTimeout","maxDelay","cancel","flush","useDebounceValue","value","debouncedValue","setDebouncedValue","useState","next","PLATFORM_HOSTNAME_MAP","Map","useEventListener","eventName","handler","element","savedHandler","isWindow","Window","isDocument","Document","target","HTMLElement","addEventListener","listener","event","currentHandler","handleEvent","removeEventListener","useIsClient","isClient","setIsClient","useMap","initialState","map","setMap","Array","isArray","mapRef","actions","useMemo","set","key","prev","setAll","entries","remove","delete","clear","get","has","DEFAULT_WEBSITE_EXTRACTOR_BASE_URL","buildWebsiteExtractorUrl","request","baseUrl","replace","normalizeBaseUrl","params","URLSearchParams","apiKey","url","endpoint","toString","async","fetchWebsiteExtractor","trim","length","ok","error","message","response","fetch","method","signal","payload","json","errorPayload","status","raw","_a","aborted","Error","useWebsiteExtractorBase","config","selectData","shouldSkip","state","setState","loading","cacheActions","cacheEnabled","cache","enabled","debounceMs","refreshDebounceMs","debouncedUrl","refreshCounterRef","lastRefreshHandledRef","refreshToken","setRefreshToken","scheduleRefresh","cancelRefresh","refresh","requestKey","inFlightControllerRef","forceRefresh","cached","data","meta","abort","controller","AbortController","then","result","requestedUrl","finalUrl","normalizedUrl","contentType","fetchedAt","bodyBytes","bodyTruncated","maxBodyBytes","extractWebsiteMeta","_requestedUrl","_finalUrl","_url","_normalizedUrl","_status","_contentType","_fetchedAt","_bodyBytes","_bodyTruncated","_maxBodyBytes","_cache","stripWebsiteMeta","catch","DEFAULT_SKIP_PATTERNS","pickFirstString","values","safeHost","URL","hostname","defaultValue","setValue","setTrue","setFalse","toggle","resetDelay","copiedText","setCopiedText","isSupported","navigator","clipboard","document","queryCommandSupported","resetCopied","copy","text","shouldUseClipboardApi","writeText","textarea","createElement","setAttribute","style","position","left","top","body","appendChild","focus","select","success","execCommand","removeChild","ref","isHovered","setIsHovered","handleEnter","handleLeave","initialValue","initializeWithValue","serialize","JSON","stringify","deserialize","parse","listenToStorageChanges","initialValueRef","readValue","item","localStorage","getItem","storedValue","setStoredValue","valueToStore","setItem","handleStorageChange","newValue","query","matches","setMatches","matchMedia","mediaQueryList","addListener","removeListener","eventType","handlerRef","refs","Node","some","currentRef","node","contains","skipPatterns","pattern","test","_raw","openGraph","htmlInferred","hybridGraph","description","title","siteName","site_name","favicon","image","_b","images","video","_c","videoType","resolvedUrl","siteHost","trimmedUrl","toLowerCase","onResize","entryRef","entry","setEntry","ResizeObserver","Element","observer","firstEntry","observe","disconnect","sessionStorage","interval","throttledValue","setThrottledValue","lastExecutedRef","pendingValueRef","now","Date","elapsed","remaining","totalLinks","uniqueDomains","links","language","canonicalUrl","feedUrl","textContentLength","metaTags","feeds","schema","schemaTypes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"opensite-hooks.umd.js","sources":["../../src/core/useIsomorphicLayoutEffect.ts","../../src/core/useDebounceCallback.ts","../../src/core/useDebounceValue.ts","../../src/core/usePlatformFromUrl.ts","../../src/core/useEventListener.ts","../../src/core/useIsClient.ts","../../src/core/useMap.ts","../../src/core/websiteExtractorTypes.ts","../../src/core/websiteExtractorService.ts","../../src/core/useWebsiteExtractorBase.ts","../../src/core/useOpenGraphExtractor.ts","../../src/core/useBoolean.ts","../../src/core/useCopyToClipboard.ts","../../src/core/useHover.ts","../../src/core/useLocalStorage.ts","../../src/core/useMediaQuery.ts","../../src/core/useOnClickOutside.ts","../../src/core/usePrevious.ts","../../src/core/useResizeObserver.ts","../../src/core/useSessionStorage.ts","../../src/core/useThrottle.ts","../../src/core/useWebsiteLinksExtractor.ts","../../src/core/useWebsiteMetaExtractor.ts","../../src/core/useWebsiteRssExtractor.ts","../../src/core/useWebsiteSchemaExtractor.ts"],"sourcesContent":["import { useEffect, useLayoutEffect } from \"react\";\n\nexport const useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\nexport interface DebounceOptions {\n leading?: boolean;\n trailing?: boolean;\n maxWait?: number;\n}\n\nexport interface DebouncedCallback<T extends (...args: any[]) => void> {\n debouncedCallback: (...args: Parameters<T>) => void;\n cancel: () => void;\n flush: () => void;\n}\n\nexport function useDebounceCallback<T extends (...args: any[]) => void>(\n callback: T,\n delay: number,\n options: DebounceOptions = {}\n): DebouncedCallback<T> {\n const callbackRef = useRef(callback);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const maxTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastArgsRef = useRef<Parameters<T> | null>(null);\n\n const leading = options.leading ?? false;\n const trailing = options.trailing ?? true;\n const maxWait = options.maxWait;\n const wait = Math.max(0, delay);\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n\n const clearTimers = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n if (maxTimeoutRef.current) {\n clearTimeout(maxTimeoutRef.current);\n maxTimeoutRef.current = null;\n }\n }, []);\n\n const invoke = useCallback(() => {\n if (!lastArgsRef.current) {\n return;\n }\n const args = lastArgsRef.current;\n lastArgsRef.current = null;\n callbackRef.current(...args);\n }, []);\n\n const debouncedCallback = useCallback(\n (...args: Parameters<T>) => {\n lastArgsRef.current = args;\n\n const shouldInvokeLeading =\n leading && timeoutRef.current === null && maxTimeoutRef.current === null;\n if (shouldInvokeLeading) {\n invoke();\n }\n\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n if (trailing) {\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n if (lastArgsRef.current) {\n invoke();\n }\n if (maxTimeoutRef.current) {\n clearTimeout(maxTimeoutRef.current);\n maxTimeoutRef.current = null;\n }\n }, wait);\n }\n\n if (maxWait !== undefined && maxWait !== null && trailing) {\n if (!maxTimeoutRef.current) {\n const maxDelay = Math.max(0, maxWait);\n maxTimeoutRef.current = setTimeout(() => {\n maxTimeoutRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n if (lastArgsRef.current) {\n invoke();\n }\n }, maxDelay);\n }\n }\n },\n [invoke, leading, trailing, maxWait, wait]\n );\n\n const cancel = useCallback(() => {\n clearTimers();\n lastArgsRef.current = null;\n }, [clearTimers]);\n\n const flush = useCallback(() => {\n if (!lastArgsRef.current) {\n return;\n }\n clearTimers();\n invoke();\n }, [clearTimers, invoke]);\n\n useEffect(() => () => cancel(), [cancel]);\n\n return { debouncedCallback, cancel, flush };\n}\n","import { useEffect, useState } from \"react\";\nimport { DebounceOptions, useDebounceCallback } from \"./useDebounceCallback.js\";\n\nexport function useDebounceValue<T>(\n value: T,\n delay: number,\n options?: DebounceOptions\n): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n const { debouncedCallback, cancel } = useDebounceCallback(\n (next: T) => {\n setDebouncedValue(next);\n },\n delay,\n options\n );\n\n useEffect(() => {\n debouncedCallback(value);\n }, [debouncedCallback, value]);\n\n useEffect(() => () => cancel(), [cancel]);\n\n return debouncedValue;\n}\n","import { useMemo } from \"react\";\n\n/**\n * Supported social platform names\n */\nexport type SocialPlatformName =\n | \"instagram\"\n | \"linkedin\"\n | \"google\"\n | \"facebook\"\n | \"tiktok\"\n | \"youtube\"\n | \"yelp\"\n | \"spotify\"\n | \"apple\"\n | \"x\"\n | \"github\"\n | \"discord\"\n | \"snapchat\"\n | \"dev\"\n | \"substack\"\n | \"reddit\"\n | \"pinterest\"\n | \"threads\"\n | \"twitch\"\n | \"whatsapp\"\n | \"telegram\"\n | \"medium\"\n | \"patreon\"\n | \"onlyfans\"\n | \"eventbrite\"\n | \"npmjs\"\n | \"crates\"\n | \"rubygems\"\n | \"unknown\";\n\n/**\n * Platform hostname mappings for O(1) lookup performance.\n * Includes standard domains and known URL variants/shorteners.\n */\nconst PLATFORM_HOSTNAME_MAP = new Map<string, SocialPlatformName>([\n // Instagram\n [\"instagram.com\", \"instagram\"],\n [\"www.instagram.com\", \"instagram\"],\n [\"instagr.am\", \"instagram\"],\n [\"www.instagr.am\", \"instagram\"],\n\n // LinkedIn\n [\"linkedin.com\", \"linkedin\"],\n [\"www.linkedin.com\", \"linkedin\"],\n [\"ca.linkedin.com\", \"linkedin\"],\n [\"uk.linkedin.com\", \"linkedin\"],\n [\"in.linkedin.com\", \"linkedin\"],\n [\"lnkd.in\", \"linkedin\"],\n\n // Google (including Maps variants)\n [\"google.com\", \"google\"],\n [\"www.google.com\", \"google\"],\n [\"maps.google.com\", \"google\"],\n [\"goo.gl\", \"google\"],\n [\"maps.app.goo.gl\", \"google\"],\n [\"g.co\", \"google\"],\n\n // Facebook\n [\"facebook.com\", \"facebook\"],\n [\"www.facebook.com\", \"facebook\"],\n [\"m.facebook.com\", \"facebook\"],\n [\"fb.com\", \"facebook\"],\n [\"fb.me\", \"facebook\"],\n [\"on.fb.me\", \"facebook\"],\n\n // TikTok\n [\"tiktok.com\", \"tiktok\"],\n [\"www.tiktok.com\", \"tiktok\"],\n [\"m.tiktok.com\", \"tiktok\"],\n [\"vm.tiktok.com\", \"tiktok\"],\n [\"vt.tiktok.com\", \"tiktok\"],\n\n // YouTube\n [\"youtube.com\", \"youtube\"],\n [\"www.youtube.com\", \"youtube\"],\n [\"m.youtube.com\", \"youtube\"],\n [\"youtu.be\", \"youtube\"],\n\n // Yelp\n [\"yelp.com\", \"yelp\"],\n [\"www.yelp.com\", \"yelp\"],\n [\"m.yelp.com\", \"yelp\"],\n\n // Spotify\n [\"spotify.com\", \"spotify\"],\n [\"www.spotify.com\", \"spotify\"],\n [\"open.spotify.com\", \"spotify\"],\n [\"play.spotify.com\", \"spotify\"],\n [\"spoti.fi\", \"spotify\"],\n [\"spotify.link\", \"spotify\"],\n\n // Apple\n [\"apple.com\", \"apple\"],\n [\"www.apple.com\", \"apple\"],\n [\"music.apple.com\", \"apple\"],\n [\"podcasts.apple.com\", \"apple\"],\n [\"apps.apple.com\", \"apple\"],\n [\"itunes.apple.com\", \"apple\"],\n\n // X (formerly Twitter)\n [\"x.com\", \"x\"],\n [\"www.x.com\", \"x\"],\n [\"twitter.com\", \"x\"],\n [\"www.twitter.com\", \"x\"],\n [\"t.co\", \"x\"],\n\n // GitHub\n [\"github.com\", \"github\"],\n [\"www.github.com\", \"github\"],\n [\"gist.github.com\", \"github\"],\n [\"raw.githubusercontent.com\", \"github\"],\n [\"github.io\", \"github\"],\n\n // Discord\n [\"discord.com\", \"discord\"],\n [\"www.discord.com\", \"discord\"],\n [\"discord.gg\", \"discord\"],\n [\"discordapp.com\", \"discord\"],\n [\"discordapp.net\", \"discord\"],\n [\"discord.new\", \"discord\"],\n [\"discord.gift\", \"discord\"],\n [\"discord.gifts\", \"discord\"],\n [\"dis.gd\", \"discord\"],\n\n // Snapchat\n [\"snapchat.com\", \"snapchat\"],\n [\"www.snapchat.com\", \"snapchat\"],\n [\"snap.com\", \"snapchat\"],\n [\"www.snap.com\", \"snapchat\"],\n [\"story.snapchat.com\", \"snapchat\"],\n [\"web.snapchat.com\", \"snapchat\"],\n\n // Dev (Dev.to)\n [\"dev.to\", \"dev\"],\n [\"www.dev.to\", \"dev\"],\n\n // Substack\n [\"substack.com\", \"substack\"],\n [\"www.substack.com\", \"substack\"],\n\n // Reddit\n [\"reddit.com\", \"reddit\"],\n [\"www.reddit.com\", \"reddit\"],\n [\"old.reddit.com\", \"reddit\"],\n [\"new.reddit.com\", \"reddit\"],\n [\"i.redd.it\", \"reddit\"],\n [\"v.redd.it\", \"reddit\"],\n [\"redd.it\", \"reddit\"],\n [\"preview.redd.it\", \"reddit\"],\n\n // Pinterest\n [\"pinterest.com\", \"pinterest\"],\n [\"www.pinterest.com\", \"pinterest\"],\n [\"pin.it\", \"pinterest\"],\n [\"in.pinterest.com\", \"pinterest\"],\n [\"br.pinterest.com\", \"pinterest\"],\n [\"uk.pinterest.com\", \"pinterest\"],\n\n // Threads (Meta)\n [\"threads.net\", \"threads\"],\n [\"www.threads.net\", \"threads\"],\n [\"threads.com\", \"threads\"],\n [\"www.threads.com\", \"threads\"],\n\n // Twitch\n [\"twitch.tv\", \"twitch\"],\n [\"www.twitch.tv\", \"twitch\"],\n [\"m.twitch.tv\", \"twitch\"],\n\n // WhatsApp\n [\"whatsapp.com\", \"whatsapp\"],\n [\"www.whatsapp.com\", \"whatsapp\"],\n [\"wa.me\", \"whatsapp\"],\n [\"web.whatsapp.com\", \"whatsapp\"],\n\n // Telegram\n [\"telegram.org\", \"telegram\"],\n [\"www.telegram.org\", \"telegram\"],\n [\"t.me\", \"telegram\"],\n [\"telegram.me\", \"telegram\"],\n [\"telegram.dog\", \"telegram\"],\n\n // Medium\n [\"medium.com\", \"medium\"],\n [\"www.medium.com\", \"medium\"],\n // Note: Custom Medium domains would need additional logic\n\n // Patreon\n [\"patreon.com\", \"patreon\"],\n [\"www.patreon.com\", \"patreon\"],\n\n // OnlyFans\n [\"onlyfans.com\", \"onlyfans\"],\n [\"www.onlyfans.com\", \"onlyfans\"],\n\n // Eventbrite\n [\"eventbrite.com\", \"eventbrite\"],\n [\"www.eventbrite.com\", \"eventbrite\"],\n [\"eventbrite.co.uk\", \"eventbrite\"],\n [\"eventbrite.com.au\", \"eventbrite\"],\n [\"eventbrite.ca\", \"eventbrite\"],\n [\"eventbrite.de\", \"eventbrite\"],\n [\"eventbrite.fr\", \"eventbrite\"],\n [\"eventbrite.es\", \"eventbrite\"],\n [\"eventbrite.it\", \"eventbrite\"],\n [\"eventbrite.ie\", \"eventbrite\"],\n [\"eventbrite.nl\", \"eventbrite\"],\n [\"eventbrite.co.nz\", \"eventbrite\"],\n [\"eventbriteapi.com\", \"eventbrite\"],\n [\"evbuc.com\", \"eventbrite\"],\n\n // npm\n [\"npmjs.com\", \"npmjs\"],\n [\"www.npmjs.com\", \"npmjs\"],\n [\"npmjs.org\", \"npmjs\"],\n [\"www.npmjs.org\", \"npmjs\"],\n [\"registry.npmjs.org\", \"npmjs\"],\n [\"registry.npmjs.com\", \"npmjs\"],\n [\"replicate.npmjs.com\", \"npmjs\"],\n\n // Crates.io (Rust)\n [\"crates.io\", \"crates\"],\n [\"www.crates.io\", \"crates\"],\n\n // RubyGems\n [\"rubygems.org\", \"rubygems\"],\n [\"www.rubygems.org\", \"rubygems\"],\n]);\n\n/**\n * Extracts the social platform name from a URL string.\n * Uses the native URL API for validation and hostname extraction,\n * then performs O(1) Map lookup for platform identification.\n *\n * @param url - The URL string to analyze\n * @returns The identified platform name or \"unknown\" if not recognized\n *\n * @example\n * ```tsx\n * const platform = usePlatformFromUrl(\"https://www.youtube.com/@iamthedelo\");\n * // Returns: \"youtube\"\n *\n * const platform = usePlatformFromUrl(\"https://maps.app.goo.gl/XDuog3V5fTuPcWCH7\");\n * // Returns: \"google\"\n *\n * const platform = usePlatformFromUrl(\"https://twitter.com/jordanhudgens\");\n * // Returns: \"x\"\n *\n * const platform = usePlatformFromUrl(\"not-a-url\");\n * // Returns: \"unknown\"\n * ```\n */\nexport function usePlatformFromUrl(url: string): SocialPlatformName {\n return useMemo(() => {\n if (!url || typeof url !== \"string\") {\n return \"unknown\";\n }\n\n const trimmedUrl = url.trim();\n if (!trimmedUrl) {\n return \"unknown\";\n }\n\n try {\n const urlObj = new URL(trimmedUrl);\n const hostname = urlObj.hostname.toLowerCase();\n\n // O(1) Map lookup first (fastest)\n const platform = PLATFORM_HOSTNAME_MAP.get(hostname);\n\n if (platform) {\n return platform;\n }\n\n // Fallback pattern matching for custom domains (slower but more comprehensive)\n if (hostname.endsWith(\".substack.com\")) {\n return \"substack\";\n }\n if (hostname.endsWith(\".github.io\")) {\n return \"github\";\n }\n if (hostname.includes(\"pinterest.com\")) {\n return \"pinterest\";\n }\n if (hostname.includes(\"eventbrite.\")) {\n return \"eventbrite\";\n }\n if (hostname.endsWith(\".medium.com\")) {\n return \"medium\";\n }\n\n return \"unknown\";\n } catch {\n return \"unknown\";\n }\n }, [url]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\ntype EventTargetLike = Window | Document | HTMLElement | null;\ntype ElementRef = React.RefObject<HTMLElement>;\n\nconst isRefObject = (value: unknown): value is ElementRef =>\n !!value && typeof value === \"object\" && \"current\" in value;\n\nexport function useEventListener<K extends keyof WindowEventMap>(\n eventName: K,\n handler: (event: WindowEventMap[K]) => void,\n element?: Window,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener<K extends keyof DocumentEventMap>(\n eventName: K,\n handler: (event: DocumentEventMap[K]) => void,\n element: Document,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener<K extends keyof HTMLElementEventMap>(\n eventName: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n element: ElementRef,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener<K extends keyof HTMLElementEventMap>(\n eventName: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n element: HTMLElement,\n options?: AddEventListenerOptions | boolean\n): void;\nexport function useEventListener(\n eventName: string,\n handler: EventListenerOrEventListenerObject,\n element?: EventTargetLike | ElementRef,\n options?: AddEventListenerOptions | boolean\n): void {\n const savedHandler = useRef(handler);\n\n useIsomorphicLayoutEffect(() => {\n savedHandler.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const isWindow =\n typeof Window !== \"undefined\" && element instanceof Window;\n const isDocument =\n typeof Document !== \"undefined\" && element instanceof Document;\n\n const target: EventTargetLike | null =\n element === undefined\n ? typeof window !== \"undefined\"\n ? window\n : null\n : isWindow || isDocument\n ? element\n : typeof HTMLElement !== \"undefined\" && element instanceof HTMLElement\n ? element\n : isRefObject(element)\n ? element.current\n : null;\n\n if (!target?.addEventListener) {\n return;\n }\n\n const listener = (event: Event) => {\n const currentHandler = savedHandler.current;\n if (typeof currentHandler === \"function\") {\n currentHandler(event);\n } else {\n currentHandler.handleEvent(event);\n }\n };\n\n target.addEventListener(eventName, listener, options);\n\n return () => {\n target.removeEventListener(eventName, listener, options);\n };\n }, [eventName, element, options]);\n}\n","import { useEffect, useState } from \"react\";\n\nexport function useIsClient(): boolean {\n const [isClient, setIsClient] = useState(false);\n\n useEffect(() => {\n setIsClient(true);\n }, []);\n\n return isClient;\n}\n","import { useMemo, useRef, useState } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\nexport interface MapActions<K, V> {\n set: (key: K, value: V) => void;\n setAll: (entries: Map<K, V> | [K, V][]) => void;\n remove: (key: K) => void;\n clear: () => void;\n get: (key: K) => V | undefined;\n has: (key: K) => boolean;\n}\n\nexport function useMap<K, V>(\n initialState?: Map<K, V> | [K, V][]\n): [Map<K, V>, MapActions<K, V>] {\n const [map, setMap] = useState<Map<K, V>>(() => {\n if (initialState instanceof Map) {\n return new Map(initialState);\n }\n if (Array.isArray(initialState)) {\n return new Map(initialState);\n }\n return new Map();\n });\n\n const mapRef = useRef(map);\n\n // Use useIsomorphicLayoutEffect to update mapRef synchronously with state\n // This ensures get() and has() methods always read the current map state\n // without a timing window where they could return stale values\n useIsomorphicLayoutEffect(() => {\n mapRef.current = map;\n }, [map]);\n\n const actions = useMemo<MapActions<K, V>>(\n () => ({\n set: (key: K, value: V) => {\n setMap((prev) => {\n const next = new Map(prev);\n next.set(key, value);\n return next;\n });\n },\n setAll: (entries: Map<K, V> | [K, V][]) => {\n setMap(entries instanceof Map ? new Map(entries) : new Map(entries));\n },\n remove: (key: K) => {\n setMap((prev) => {\n const next = new Map(prev);\n next.delete(key);\n return next;\n });\n },\n clear: () => setMap(new Map()),\n get: (key: K) => mapRef.current.get(key),\n has: (key: K) => mapRef.current.has(key),\n }),\n []\n );\n\n return [map, actions];\n}\n","export interface WebsiteExtractCacheMeta {\n hit: boolean;\n ageSeconds: number;\n ttlSeconds: number;\n staleWhileRevalidateSeconds: number;\n}\n\nexport interface WebsiteExtractMeta {\n requestedUrl: string;\n finalUrl: string;\n url: string;\n normalizedUrl: string;\n status: number;\n contentType: string;\n fetchedAt: string;\n bodyBytes: number;\n bodyTruncated: boolean;\n maxBodyBytes: number;\n cache: WebsiteExtractCacheMeta;\n}\n\nexport interface WebsiteExtractorError {\n message: string;\n status?: number;\n raw?: unknown;\n}\n\nexport interface WebsiteExtractorOptions {\n url?: string;\n apiKey?: string;\n baseUrl?: string;\n debounceMs?: number;\n refreshDebounceMs?: number;\n enabled?: boolean;\n cache?: boolean;\n}\n\nexport interface WebsiteExtractorState<TData, TRaw = TData> {\n loading: boolean;\n data?: TData;\n raw?: TRaw;\n meta?: WebsiteExtractMeta;\n error?: WebsiteExtractorError;\n}\n\nexport interface WebsiteExtractorResult<TData, TRaw = TData>\n extends WebsiteExtractorState<TData, TRaw> {\n refresh: () => void;\n}\n\nexport type WebsiteExtractorResponse<TPayload> = WebsiteExtractMeta & TPayload;\n\nexport interface WebsiteExtractorRequest {\n endpoint: string;\n url: string;\n apiKey?: string;\n baseUrl?: string;\n signal?: AbortSignal;\n}\n\nexport type WebsiteExtractorClientResult<T> =\n | { ok: true; response: T }\n | { ok: false; error: WebsiteExtractorError };\n\nexport const DEFAULT_WEBSITE_EXTRACTOR_BASE_URL = \"https://octane.buzz\";\n\nexport const DEFAULT_EXTRACTOR_DEBOUNCE_MS = 250;\n\nexport const DEFAULT_EXTRACTOR_REFRESH_DEBOUNCE_MS = 150;\n\nexport const DEFAULT_EXTRACTOR_ENABLED = true;\n\nexport const DEFAULT_EXTRACTOR_CACHE = true;\n\nexport function extractWebsiteMeta(response: WebsiteExtractMeta): WebsiteExtractMeta {\n const {\n requestedUrl,\n finalUrl,\n url,\n normalizedUrl,\n status,\n contentType,\n fetchedAt,\n bodyBytes,\n bodyTruncated,\n maxBodyBytes,\n cache,\n } = response;\n\n return {\n requestedUrl,\n finalUrl,\n url,\n normalizedUrl,\n status,\n contentType,\n fetchedAt,\n bodyBytes,\n bodyTruncated,\n maxBodyBytes,\n cache,\n };\n}\n\nexport function stripWebsiteMeta<TResponse extends WebsiteExtractMeta>(\n response: TResponse\n): Omit<TResponse, keyof WebsiteExtractMeta> {\n const {\n requestedUrl: _requestedUrl,\n finalUrl: _finalUrl,\n url: _url,\n normalizedUrl: _normalizedUrl,\n status: _status,\n contentType: _contentType,\n fetchedAt: _fetchedAt,\n bodyBytes: _bodyBytes,\n bodyTruncated: _bodyTruncated,\n maxBodyBytes: _maxBodyBytes,\n cache: _cache,\n ...payload\n } = response;\n\n return payload;\n}\n","import {\n DEFAULT_WEBSITE_EXTRACTOR_BASE_URL,\n type WebsiteExtractorClientResult,\n type WebsiteExtractorError,\n type WebsiteExtractorRequest,\n} from \"./websiteExtractorTypes.js\";\n\nconst EXTRACTOR_PATH = \"/api/v1/extract\";\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\nexport function buildWebsiteExtractorUrl(request: WebsiteExtractorRequest): string {\n const baseUrl = normalizeBaseUrl(\n request.baseUrl ?? DEFAULT_WEBSITE_EXTRACTOR_BASE_URL\n );\n const params = new URLSearchParams();\n\n if (request.apiKey) {\n params.set(\"api_key\", request.apiKey);\n }\n\n params.set(\"url\", request.url);\n\n return `${baseUrl}${EXTRACTOR_PATH}/${request.endpoint}?${params.toString()}`;\n}\n\nexport async function fetchWebsiteExtractor<TResponse>(\n request: WebsiteExtractorRequest\n): Promise<WebsiteExtractorClientResult<TResponse>> {\n if (!request.url || request.url.trim().length === 0) {\n return {\n ok: false,\n error: {\n message: \"URL is required.\",\n },\n };\n }\n\n const endpoint = buildWebsiteExtractorUrl(request);\n\n try {\n const response = await fetch(endpoint, {\n method: \"GET\",\n signal: request.signal,\n });\n\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n\n if (!response.ok) {\n const errorPayload = payload as { error?: string; status?: number } | null;\n const error: WebsiteExtractorError = {\n message:\n errorPayload?.error ??\n `Request failed with status ${response.status}.`,\n status: errorPayload?.status ?? response.status,\n raw: payload,\n };\n\n return { ok: false, error };\n }\n\n return { ok: true, response: payload as TResponse };\n } catch (error) {\n if (request.signal?.aborted) {\n return {\n ok: false,\n error: {\n message: \"Request aborted.\",\n },\n };\n }\n\n return {\n ok: false,\n error: {\n message:\n error instanceof Error ? error.message : \"Request failed unexpectedly.\",\n raw: error,\n },\n };\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useDebounceCallback } from \"./useDebounceCallback.js\";\nimport { useDebounceValue } from \"./useDebounceValue.js\";\nimport { useIsClient } from \"./useIsClient.js\";\nimport { useMap } from \"./useMap.js\";\nimport { fetchWebsiteExtractor } from \"./websiteExtractorService.js\";\nimport {\n DEFAULT_EXTRACTOR_CACHE,\n DEFAULT_EXTRACTOR_DEBOUNCE_MS,\n DEFAULT_EXTRACTOR_ENABLED,\n DEFAULT_EXTRACTOR_REFRESH_DEBOUNCE_MS,\n DEFAULT_WEBSITE_EXTRACTOR_BASE_URL,\n extractWebsiteMeta,\n stripWebsiteMeta,\n type WebsiteExtractorOptions,\n type WebsiteExtractorResult,\n type WebsiteExtractorState,\n type WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\ninterface CachedExtractorEntry<TData, TRaw extends WebsiteExtractMeta> {\n data: TData;\n raw: TRaw;\n meta: WebsiteExtractMeta;\n}\n\ninterface UseWebsiteExtractorBaseConfig<\n TResponse extends WebsiteExtractMeta,\n TData,\n> {\n endpoint: string;\n options: WebsiteExtractorOptions;\n selectData: (\n payload: Omit<TResponse, keyof WebsiteExtractMeta>,\n raw: TResponse,\n meta: WebsiteExtractMeta\n ) => TData;\n shouldSkip?: (url: string) => boolean;\n}\n\nexport function useWebsiteExtractorBase<\n TResponse extends WebsiteExtractMeta,\n TData,\n>(config: UseWebsiteExtractorBaseConfig<TResponse, TData>): WebsiteExtractorResult<\n TData,\n TResponse\n> {\n const { endpoint, options, selectData, shouldSkip } = config;\n const isClient = useIsClient();\n const [state, setState] = useState<WebsiteExtractorState<TData, TResponse>>({\n loading: false,\n });\n\n const [, cacheActions] = useMap<\n string,\n CachedExtractorEntry<TData, TResponse>\n >();\n const cacheEnabled = options.cache ?? DEFAULT_EXTRACTOR_CACHE;\n const enabled = options.enabled ?? DEFAULT_EXTRACTOR_ENABLED;\n const debounceMs = options.debounceMs ?? DEFAULT_EXTRACTOR_DEBOUNCE_MS;\n const refreshDebounceMs =\n options.refreshDebounceMs ?? DEFAULT_EXTRACTOR_REFRESH_DEBOUNCE_MS;\n\n const normalizedUrl = useMemo(() => {\n return options.url?.trim() ?? \"\";\n }, [options.url]);\n\n const debouncedUrl = useDebounceValue(normalizedUrl, debounceMs);\n\n const refreshCounterRef = useRef(0);\n const lastRefreshHandledRef = useRef(0);\n const [refreshToken, setRefreshToken] = useState(0);\n\n const { debouncedCallback: scheduleRefresh, cancel: cancelRefresh } =\n useDebounceCallback(() => {\n refreshCounterRef.current += 1;\n setRefreshToken(refreshCounterRef.current);\n }, refreshDebounceMs);\n\n const refresh = useCallback(() => {\n scheduleRefresh();\n }, [scheduleRefresh]);\n\n useEffect(() => () => cancelRefresh(), [cancelRefresh]);\n\n const requestKey = useMemo(() => {\n if (!debouncedUrl) {\n return \"\";\n }\n\n const baseUrl = options.baseUrl ?? DEFAULT_WEBSITE_EXTRACTOR_BASE_URL;\n const apiKey = options.apiKey ?? \"\";\n return `${endpoint}:${baseUrl}:${apiKey}:${debouncedUrl}`;\n }, [\n endpoint,\n options.apiKey,\n options.baseUrl,\n debouncedUrl,\n ]);\n\n const inFlightControllerRef = useRef<AbortController | null>(null);\n\n useEffect(() => {\n if (!isClient) {\n return;\n }\n\n if (!enabled || !debouncedUrl) {\n setState({ loading: false });\n return;\n }\n\n if (shouldSkip?.(debouncedUrl)) {\n setState({ loading: false });\n return;\n }\n\n const forceRefresh = refreshToken !== lastRefreshHandledRef.current;\n if (forceRefresh) {\n lastRefreshHandledRef.current = refreshToken;\n }\n\n if (cacheEnabled && !forceRefresh) {\n const cached = cacheActions.get(requestKey);\n if (cached) {\n setState({\n loading: false,\n data: cached.data,\n raw: cached.raw,\n meta: cached.meta,\n });\n return;\n }\n }\n\n inFlightControllerRef.current?.abort();\n const controller = new AbortController();\n inFlightControllerRef.current = controller;\n\n setState((prev) => ({\n ...prev,\n loading: true,\n error: undefined,\n }));\n\n fetchWebsiteExtractor<TResponse>({\n endpoint,\n url: debouncedUrl,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: controller.signal,\n })\n .then((result) => {\n if (controller.signal.aborted) {\n return;\n }\n\n if (!result.ok) {\n setState((prev) => ({\n ...prev,\n loading: false,\n error: result.error,\n }));\n return;\n }\n\n const raw = result.response;\n const meta = extractWebsiteMeta(raw);\n const payload = stripWebsiteMeta(raw);\n const data = selectData(payload, raw, meta);\n\n if (cacheEnabled) {\n cacheActions.set(requestKey, { data, raw, meta });\n }\n\n setState({\n loading: false,\n data,\n raw,\n meta,\n });\n })\n .catch((error) => {\n if (controller.signal.aborted) {\n return;\n }\n\n setState((prev) => ({\n ...prev,\n loading: false,\n error: {\n message:\n error instanceof Error\n ? error.message\n : \"Request failed unexpectedly.\",\n raw: error,\n },\n }));\n });\n\n return () => {\n controller.abort();\n };\n }, [\n cacheActions,\n cacheEnabled,\n debouncedUrl,\n enabled,\n endpoint,\n isClient,\n options.apiKey,\n options.baseUrl,\n requestKey,\n refreshToken,\n selectData,\n shouldSkip,\n ]);\n\n return useMemo(\n () => ({\n ...state,\n refresh,\n }),\n [refresh, state]\n );\n}\n","import { useCallback, useMemo } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface OpenGraphImage {\n url?: string | null;\n height?: string | null;\n width?: string | null;\n}\n\nexport interface OpenGraphVideo {\n url?: string | null;\n height?: string | null;\n width?: string | null;\n}\n\nexport interface OpenGraphData {\n description?: string | null;\n title?: string | null;\n site_name?: string | null;\n image?: OpenGraphImage | null;\n video?: OpenGraphVideo | null;\n url?: string | null;\n ogType?: string | null;\n}\n\nexport interface OpenGraphHtmlInferredData {\n description?: string | null;\n title?: string | null;\n type?: string | null;\n videoType?: string | null;\n url?: string | null;\n favicon?: string | null;\n images?: string[] | null;\n image?: string | null;\n site_name?: string | null;\n}\n\nexport interface OpenGraphHybridData {\n description?: string | null;\n title?: string | null;\n type?: string | null;\n image?: string | null;\n video?: string | null;\n videoType?: string | null;\n favicon?: string | null;\n site_name?: string | null;\n url?: string | null;\n videoWidth?: number | null;\n videoHeight?: number | null;\n}\n\nexport type OpenGraphResponse = WebsiteExtractorResponse<{\n openGraph: OpenGraphData;\n htmlInferred: OpenGraphHtmlInferredData;\n hybridGraph: OpenGraphHybridData;\n}>;\n\nexport interface OpenGraphSummary {\n description?: string;\n favicon?: string;\n image?: string;\n video?: string;\n videoType?: string;\n siteName?: string;\n title?: string;\n url: string;\n siteHost?: string;\n}\n\nexport interface OpenGraphExtractorOptions extends WebsiteExtractorOptions {\n skipPatterns?: RegExp[];\n}\n\nconst DEFAULT_SKIP_PATTERNS = [\n /search\\.google\\.com\\/local\\/reviews/i,\n /google\\.com\\/maps\\/place/i,\n /maps\\.google\\.com/i,\n /opentable\\.com/i,\n];\n\nconst pickFirstString = (\n ...values: Array<string | null | undefined>\n): string | undefined => {\n for (const value of values) {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value;\n }\n }\n return undefined;\n};\n\nconst safeHost = (value: string | undefined): string | undefined => {\n if (!value) {\n return undefined;\n }\n\n try {\n return new URL(value).hostname;\n } catch {\n return undefined;\n }\n};\n\nexport function useOpenGraphExtractor(\n options: OpenGraphExtractorOptions\n): WebsiteExtractorResult<OpenGraphSummary, OpenGraphResponse> {\n const skipPatterns = useMemo(\n () => options.skipPatterns ?? DEFAULT_SKIP_PATTERNS,\n [options.skipPatterns]\n );\n\n const shouldSkip = useCallback(\n (url: string) => skipPatterns.some((pattern) => pattern.test(url)),\n [skipPatterns]\n );\n\n const selectData = useCallback(\n (\n payload: Omit<OpenGraphResponse, keyof WebsiteExtractMeta>,\n _raw: OpenGraphResponse,\n meta: WebsiteExtractMeta\n ): OpenGraphSummary => {\n const { openGraph, htmlInferred, hybridGraph } = payload;\n\n const description = pickFirstString(\n openGraph?.description,\n hybridGraph?.description,\n htmlInferred?.description\n );\n const title = pickFirstString(\n openGraph?.title,\n hybridGraph?.title,\n htmlInferred?.title\n );\n const siteName = pickFirstString(\n openGraph?.site_name,\n hybridGraph?.site_name,\n htmlInferred?.site_name\n );\n const favicon = pickFirstString(\n hybridGraph?.favicon,\n htmlInferred?.favicon\n );\n const image = pickFirstString(\n openGraph?.image?.url ?? undefined,\n hybridGraph?.image ?? undefined,\n htmlInferred?.image ?? undefined,\n htmlInferred?.images?.[0]\n );\n const video = pickFirstString(\n openGraph?.video?.url ?? undefined,\n hybridGraph?.video ?? undefined\n );\n const videoType = pickFirstString(\n hybridGraph?.videoType,\n htmlInferred?.videoType\n );\n const resolvedUrl =\n pickFirstString(\n meta.url,\n openGraph?.url ?? undefined,\n hybridGraph?.url ?? undefined,\n htmlInferred?.url ?? undefined,\n meta.finalUrl,\n meta.normalizedUrl,\n meta.requestedUrl\n ) ?? \"\";\n\n return {\n description,\n favicon,\n image,\n video,\n videoType,\n siteName,\n title,\n url: resolvedUrl,\n siteHost: safeHost(resolvedUrl),\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<OpenGraphResponse, OpenGraphSummary>({\n endpoint: \"open-graph\",\n options,\n selectData,\n shouldSkip,\n });\n}\n","import { useCallback, useMemo, useState } from \"react\";\n\nexport interface UseBooleanResult {\n value: boolean;\n setValue: React.Dispatch<React.SetStateAction<boolean>>;\n setTrue: () => void;\n setFalse: () => void;\n toggle: () => void;\n}\n\nexport function useBoolean(defaultValue = false): UseBooleanResult {\n const [value, setValue] = useState<boolean>(defaultValue);\n\n const setTrue = useCallback(() => setValue(true), []);\n const setFalse = useCallback(() => setValue(false), []);\n const toggle = useCallback(() => setValue((current) => !current), []);\n\n // Memoize the return object to prevent unnecessary re-renders in consumers\n // that use the result object in dependency arrays\n return useMemo(\n () => ({ value, setValue, setTrue, setFalse, toggle }),\n [value, setValue, setTrue, setFalse, toggle]\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nexport interface UseCopyToClipboardOptions {\n resetDelay?: number;\n}\n\nexport interface CopyToClipboardResult {\n copy: (text: string) => Promise<boolean>;\n copiedText: string | null;\n isSupported: boolean;\n}\n\nexport function useCopyToClipboard(\n options: UseCopyToClipboardOptions = {}\n): CopyToClipboardResult {\n const resetDelay = options.resetDelay ?? 2000;\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const [copiedText, setCopiedText] = useState<string | null>(null);\n\n const isSupported = useMemo(() => {\n if (typeof navigator !== \"undefined\" && navigator.clipboard) {\n return true;\n }\n if (typeof document === \"undefined\") {\n return false;\n }\n if (typeof document.queryCommandSupported !== \"function\") {\n return false;\n }\n return document.queryCommandSupported(\"copy\");\n }, []);\n\n const resetCopied = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = setTimeout(() => {\n setCopiedText(null);\n }, resetDelay);\n }, [resetDelay]);\n\n const copy = useCallback(\n async (text: string) => {\n if (!isSupported) {\n return false;\n }\n\n const shouldUseClipboardApi =\n typeof navigator !== \"undefined\" && !!navigator.clipboard;\n\n try {\n if (shouldUseClipboardApi) {\n await navigator.clipboard.writeText(text);\n } else if (typeof document !== \"undefined\") {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n textarea.style.top = \"0\";\n document.body.appendChild(textarea);\n textarea.focus();\n textarea.select();\n const success = document.execCommand(\"copy\");\n document.body.removeChild(textarea);\n if (!success) {\n return false;\n }\n }\n\n setCopiedText(text);\n resetCopied();\n return true;\n } catch {\n return false;\n }\n },\n [isSupported, resetCopied]\n );\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return { copy, copiedText, isSupported };\n}\n","import { useCallback, useState } from \"react\";\nimport { useEventListener } from \"./useEventListener.js\";\n\nexport function useHover<T extends HTMLElement>(\n ref: React.RefObject<T>\n): boolean {\n const [isHovered, setIsHovered] = useState(false);\n\n const handleEnter = useCallback(() => {\n setIsHovered(true);\n }, []);\n\n const handleLeave = useCallback(() => {\n setIsHovered(false);\n }, []);\n\n useEventListener(\"pointerenter\", handleEnter, ref);\n useEventListener(\"pointerleave\", handleLeave, ref);\n\n return isHovered;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface StorageOptions<T> {\n initializeWithValue?: boolean;\n serialize?: (value: T) => string;\n deserialize?: (value: string) => T;\n listenToStorageChanges?: boolean;\n}\n\ntype StoredSetter<T> = (value: T | ((current: T) => T)) => void;\n\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T,\n options: StorageOptions<T> = {}\n): [T, StoredSetter<T>] {\n const {\n initializeWithValue = true,\n serialize = JSON.stringify,\n deserialize = JSON.parse as (value: string) => T,\n listenToStorageChanges = true,\n } = options;\n\n const initialValueRef = useRef(initialValue);\n\n const readValue = useCallback(() => {\n if (typeof window === \"undefined\") {\n return initialValueRef.current;\n }\n if (!initializeWithValue) {\n return initialValueRef.current;\n }\n try {\n const item = window.localStorage.getItem(key);\n return item ? deserialize(item) : initialValueRef.current;\n } catch {\n return initialValueRef.current;\n }\n }, [deserialize, initializeWithValue, key]);\n\n const [storedValue, setStoredValue] = useState<T>(() => readValue());\n\n const setValue: StoredSetter<T> = useCallback(\n (value) => {\n setStoredValue((current) => {\n const valueToStore =\n typeof value === \"function\"\n ? (value as (current: T) => T)(current)\n : value;\n if (typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(key, serialize(valueToStore));\n } catch {\n // Ignore write errors (quota/security)\n }\n }\n return valueToStore;\n });\n },\n [key, serialize]\n );\n\n useEffect(() => {\n setStoredValue(readValue());\n }, [readValue]);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !listenToStorageChanges) {\n return;\n }\n\n const handleStorageChange = (event: StorageEvent) => {\n if (event.key !== key) {\n return;\n }\n if (event.newValue === null) {\n setStoredValue(initialValueRef.current);\n return;\n }\n try {\n setStoredValue(deserialize(event.newValue));\n } catch {\n setStoredValue(initialValueRef.current);\n }\n };\n\n window.addEventListener(\"storage\", handleStorageChange);\n return () => window.removeEventListener(\"storage\", handleStorageChange);\n }, [deserialize, key, listenToStorageChanges]);\n\n return [storedValue, setValue];\n}\n","import { useEffect, useState } from \"react\";\n\nexport interface UseMediaQueryOptions {\n defaultValue?: boolean;\n}\n\nexport function useMediaQuery(\n query: string,\n options: UseMediaQueryOptions = {}\n): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return options.defaultValue ?? false;\n }\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n\n const mediaQueryList = window.matchMedia(query);\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n setMatches(mediaQueryList.matches);\n\n if (mediaQueryList.addEventListener) {\n mediaQueryList.addEventListener(\"change\", handler);\n return () => mediaQueryList.removeEventListener(\"change\", handler);\n }\n\n mediaQueryList.addListener(handler);\n return () => mediaQueryList.removeListener(handler);\n }, [query]);\n\n return matches;\n}\n","import { useEffect, useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\ntype PossibleRef<T extends HTMLElement> = React.RefObject<T>;\n\nexport function useOnClickOutside<T extends HTMLElement>(\n ref: PossibleRef<T> | Array<PossibleRef<T>>,\n handler: (event: MouseEvent | TouchEvent | PointerEvent) => void,\n eventType: \"mousedown\" | \"mouseup\" | \"click\" | \"touchstart\" | \"pointerdown\" =\n \"mousedown\",\n options?: AddEventListenerOptions | boolean\n): void {\n const handlerRef = useRef(handler);\n\n // Use useIsomorphicLayoutEffect to update handler ref synchronously\n // This prevents stale closures in rapid click scenarios where the event\n // listener (registered below) might fire before the ref is updated\n useIsomorphicLayoutEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const refs = Array.isArray(ref) ? ref : [ref];\n const listener = (event: MouseEvent | TouchEvent | PointerEvent) => {\n const target = event.target;\n if (typeof Node === \"undefined\" || !(target instanceof Node)) {\n return;\n }\n\n const clickedInside = refs.some((currentRef) => {\n const node = currentRef.current;\n return node ? node.contains(target) : false;\n });\n\n if (!clickedInside) {\n handlerRef.current(event);\n }\n };\n\n document.addEventListener(eventType, listener, options);\n return () => {\n document.removeEventListener(eventType, listener, options);\n };\n }, [eventType, options, ref]);\n}\n","import { useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\nexport function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T>();\n\n // Use useIsomorphicLayoutEffect to capture the previous value synchronously\n // BEFORE paint. This ensures that during render, ref.current holds the actual\n // previous value (from the last render), not the current value.\n // Using useEffect would update AFTER paint, making comparisons incorrect.\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref.current;\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\ntype TargetElement<T extends Element> = React.RefObject<T> | T | null;\n\nconst isRefObject = <T extends Element>(\n value: unknown\n): value is React.RefObject<T> => !!value && typeof value === \"object\" && \"current\" in value;\n\nexport function useResizeObserver<T extends Element>(\n target: TargetElement<T>,\n onResize?: (entry: ResizeObserverEntry) => void,\n options?: ResizeObserverOptions\n): ResizeObserverEntry | null {\n const callbackRef = useRef(onResize);\n const entryRef = useRef<ResizeObserverEntry | null>(null);\n const [entry, setEntry] = useState<ResizeObserverEntry | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = onResize;\n }, [onResize]);\n\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") {\n return;\n }\n\n const element =\n typeof Element !== \"undefined\" && target instanceof Element\n ? target\n : isRefObject<T>(target)\n ? target.current\n : null;\n if (!element) {\n return;\n }\n\n const observer = new ResizeObserver((entries) => {\n const firstEntry = entries[0];\n entryRef.current = firstEntry;\n if (callbackRef.current) {\n callbackRef.current(firstEntry);\n } else {\n setEntry(firstEntry);\n }\n });\n\n observer.observe(element, options);\n return () => observer.disconnect();\n }, [options, target]);\n\n return callbackRef.current ? entryRef.current : entry;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface SessionStorageOptions<T> {\n initializeWithValue?: boolean;\n serialize?: (value: T) => string;\n deserialize?: (value: string) => T;\n listenToStorageChanges?: boolean;\n}\n\ntype StoredSetter<T> = (value: T | ((current: T) => T)) => void;\n\nexport function useSessionStorage<T>(\n key: string,\n initialValue: T,\n options: SessionStorageOptions<T> = {}\n): [T, StoredSetter<T>] {\n const {\n initializeWithValue = true,\n serialize = JSON.stringify,\n deserialize = JSON.parse as (value: string) => T,\n listenToStorageChanges = false,\n } = options;\n\n const initialValueRef = useRef(initialValue);\n\n const readValue = useCallback(() => {\n if (typeof window === \"undefined\") {\n return initialValueRef.current;\n }\n if (!initializeWithValue) {\n return initialValueRef.current;\n }\n try {\n const item = window.sessionStorage.getItem(key);\n return item ? deserialize(item) : initialValueRef.current;\n } catch {\n return initialValueRef.current;\n }\n }, [deserialize, initializeWithValue, key]);\n\n const [storedValue, setStoredValue] = useState<T>(() => readValue());\n\n const setValue: StoredSetter<T> = useCallback(\n (value) => {\n setStoredValue((current) => {\n const valueToStore =\n typeof value === \"function\"\n ? (value as (current: T) => T)(current)\n : value;\n if (typeof window !== \"undefined\") {\n try {\n window.sessionStorage.setItem(key, serialize(valueToStore));\n } catch {\n // Ignore write errors (quota/security)\n }\n }\n return valueToStore;\n });\n },\n [key, serialize]\n );\n\n useEffect(() => {\n setStoredValue(readValue());\n }, [readValue]);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !listenToStorageChanges) {\n return;\n }\n\n const handleStorageChange = (event: StorageEvent) => {\n if (event.key !== key) {\n return;\n }\n if (event.newValue === null) {\n setStoredValue(initialValueRef.current);\n return;\n }\n try {\n setStoredValue(deserialize(event.newValue));\n } catch {\n setStoredValue(initialValueRef.current);\n }\n };\n\n window.addEventListener(\"storage\", handleStorageChange);\n return () => window.removeEventListener(\"storage\", handleStorageChange);\n }, [deserialize, key, listenToStorageChanges]);\n\n return [storedValue, setValue];\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nexport interface ThrottleOptions {\n leading?: boolean;\n trailing?: boolean;\n}\n\nexport function useThrottle<T>(\n value: T,\n interval: number,\n options: ThrottleOptions = {}\n): T {\n const leading = options.leading ?? true;\n const trailing = options.trailing ?? true;\n const wait = Math.max(0, interval);\n\n const [throttledValue, setThrottledValue] = useState<T>(value);\n const lastExecutedRef = useRef<number>(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValueRef = useRef<T | null>(null);\n\n useEffect(() => {\n if (wait === 0) {\n setThrottledValue(value);\n return;\n }\n\n const now = Date.now();\n\n if (lastExecutedRef.current === 0) {\n lastExecutedRef.current = now;\n if (leading) {\n setThrottledValue(value);\n return;\n }\n if (trailing && !timeoutRef.current) {\n pendingValueRef.current = value;\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n if (pendingValueRef.current !== null) {\n setThrottledValue(pendingValueRef.current);\n pendingValueRef.current = null;\n lastExecutedRef.current = Date.now();\n }\n }, wait);\n }\n return;\n }\n\n const elapsed = now - lastExecutedRef.current;\n\n if (elapsed >= wait && leading) {\n setThrottledValue(value);\n lastExecutedRef.current = now;\n pendingValueRef.current = null;\n return;\n }\n\n if (trailing) {\n pendingValueRef.current = value;\n if (!timeoutRef.current) {\n const remaining = Math.max(wait - elapsed, 0);\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n if (pendingValueRef.current !== null) {\n setThrottledValue(pendingValueRef.current);\n pendingValueRef.current = null;\n lastExecutedRef.current = Date.now();\n }\n }, remaining);\n }\n }\n }, [leading, trailing, value, wait]);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return throttledValue;\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteLinkRecord {\n url: string;\n text: string;\n isExternal: boolean;\n domain?: string | null;\n}\n\nexport interface WebsiteLinksPayload {\n totalLinks: number;\n uniqueDomains: number;\n links: WebsiteLinkRecord[];\n}\n\nexport type WebsiteLinksResponse = WebsiteExtractorResponse<WebsiteLinksPayload>;\n\nexport function useWebsiteLinksExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteLinksPayload, WebsiteLinksResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteLinksResponse, keyof WebsiteExtractMeta>\n ): WebsiteLinksPayload => {\n return {\n totalLinks: payload.totalLinks ?? 0,\n uniqueDomains: payload.uniqueDomains ?? 0,\n links: payload.links ?? [],\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteLinksResponse, WebsiteLinksPayload>({\n endpoint: \"links\",\n options,\n selectData,\n });\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteMetaPayload {\n title?: string | null;\n description?: string | null;\n language?: string | null;\n canonicalUrl?: string | null;\n feedUrl?: string | null;\n textContentLength?: number | null;\n metaTags: Record<string, string>;\n}\n\nexport type WebsiteMetaResponse = WebsiteExtractorResponse<WebsiteMetaPayload>;\n\nexport function useWebsiteMetaExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteMetaPayload, WebsiteMetaResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteMetaResponse, keyof WebsiteExtractMeta>\n ): WebsiteMetaPayload => {\n return {\n title: payload.title ?? undefined,\n description: payload.description ?? undefined,\n language: payload.language ?? undefined,\n canonicalUrl: payload.canonicalUrl ?? undefined,\n feedUrl: payload.feedUrl ?? null,\n textContentLength: payload.textContentLength ?? undefined,\n metaTags: payload.metaTags ?? {},\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteMetaResponse, WebsiteMetaPayload>({\n endpoint: \"meta\",\n options,\n selectData,\n });\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteRssFeed {\n url: string;\n feedType: string;\n title?: string | null;\n}\n\nexport interface WebsiteRssPayload {\n feedUrl?: string | null;\n feeds: WebsiteRssFeed[];\n}\n\nexport type WebsiteRssResponse = WebsiteExtractorResponse<WebsiteRssPayload>;\n\nexport function useWebsiteRssExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteRssPayload, WebsiteRssResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteRssResponse, keyof WebsiteExtractMeta>\n ): WebsiteRssPayload => {\n return {\n feedUrl: payload.feedUrl ?? null,\n feeds: payload.feeds ?? [],\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteRssResponse, WebsiteRssPayload>({\n endpoint: \"rss\",\n options,\n selectData,\n });\n}\n","import { useCallback } from \"react\";\nimport { useWebsiteExtractorBase } from \"./useWebsiteExtractorBase.js\";\nimport type {\n WebsiteExtractorOptions,\n WebsiteExtractorResponse,\n WebsiteExtractorResult,\n WebsiteExtractMeta,\n} from \"./websiteExtractorTypes.js\";\n\nexport interface WebsiteSchemaRecord {\n schema_type: string;\n value: Record<string, unknown>;\n}\n\nexport interface WebsiteSchemaPayload {\n schema: WebsiteSchemaRecord[];\n schemaTypes: string[];\n}\n\nexport type WebsiteSchemaResponse = WebsiteExtractorResponse<WebsiteSchemaPayload>;\n\nexport function useWebsiteSchemaExtractor(\n options: WebsiteExtractorOptions\n): WebsiteExtractorResult<WebsiteSchemaPayload, WebsiteSchemaResponse> {\n const selectData = useCallback(\n (\n payload: Omit<WebsiteSchemaResponse, keyof WebsiteExtractMeta>\n ): WebsiteSchemaPayload => {\n return {\n schema: payload.schema ?? [],\n schemaTypes: payload.schemaTypes ?? [],\n };\n },\n []\n );\n\n return useWebsiteExtractorBase<WebsiteSchemaResponse, WebsiteSchemaPayload>({\n endpoint: \"schema\",\n options,\n selectData,\n });\n}\n"],"names":["useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","useDebounceCallback","callback","delay","options","callbackRef","useRef","timeoutRef","maxTimeoutRef","lastArgsRef","leading","trailing","maxWait","wait","Math","max","current","clearTimers","useCallback","clearTimeout","invoke","args","debouncedCallback","setTimeout","maxDelay","cancel","flush","useDebounceValue","value","debouncedValue","setDebouncedValue","useState","next","PLATFORM_HOSTNAME_MAP","Map","useEventListener","eventName","handler","element","savedHandler","isWindow","Window","isDocument","Document","target","HTMLElement","addEventListener","listener","event","currentHandler","handleEvent","removeEventListener","useIsClient","isClient","setIsClient","useMap","initialState","map","setMap","Array","isArray","mapRef","actions","useMemo","set","key","prev","setAll","entries","remove","delete","clear","get","has","DEFAULT_WEBSITE_EXTRACTOR_BASE_URL","buildWebsiteExtractorUrl","request","baseUrl","replace","normalizeBaseUrl","params","URLSearchParams","apiKey","url","endpoint","toString","async","fetchWebsiteExtractor","trim","length","ok","error","message","response","fetch","method","signal","payload","json","errorPayload","status","raw","_a","aborted","Error","useWebsiteExtractorBase","config","selectData","shouldSkip","state","setState","loading","cacheActions","cacheEnabled","cache","enabled","debounceMs","refreshDebounceMs","debouncedUrl","refreshCounterRef","lastRefreshHandledRef","refreshToken","setRefreshToken","scheduleRefresh","cancelRefresh","refresh","requestKey","inFlightControllerRef","forceRefresh","cached","data","meta","abort","controller","AbortController","then","result","requestedUrl","finalUrl","normalizedUrl","contentType","fetchedAt","bodyBytes","bodyTruncated","maxBodyBytes","extractWebsiteMeta","_requestedUrl","_finalUrl","_url","_normalizedUrl","_status","_contentType","_fetchedAt","_bodyBytes","_bodyTruncated","_maxBodyBytes","_cache","stripWebsiteMeta","catch","DEFAULT_SKIP_PATTERNS","pickFirstString","values","safeHost","URL","hostname","defaultValue","setValue","setTrue","setFalse","toggle","resetDelay","copiedText","setCopiedText","isSupported","navigator","clipboard","document","queryCommandSupported","resetCopied","copy","text","shouldUseClipboardApi","writeText","textarea","createElement","setAttribute","style","position","left","top","body","appendChild","focus","select","success","execCommand","removeChild","ref","isHovered","setIsHovered","handleEnter","handleLeave","initialValue","initializeWithValue","serialize","JSON","stringify","deserialize","parse","listenToStorageChanges","initialValueRef","readValue","item","localStorage","getItem","storedValue","setStoredValue","valueToStore","setItem","handleStorageChange","newValue","query","matches","setMatches","matchMedia","mediaQueryList","addListener","removeListener","eventType","handlerRef","refs","Node","some","currentRef","node","contains","skipPatterns","pattern","test","_raw","openGraph","htmlInferred","hybridGraph","description","title","siteName","site_name","favicon","image","_b","images","video","_c","videoType","resolvedUrl","siteHost","trimmedUrl","toLowerCase","platform","endsWith","includes","onResize","entryRef","entry","setEntry","ResizeObserver","Element","observer","firstEntry","observe","disconnect","sessionStorage","interval","throttledValue","setThrottledValue","lastExecutedRef","pendingValueRef","now","Date","elapsed","remaining","totalLinks","uniqueDomains","links","language","canonicalUrl","feedUrl","textContentLength","metaTags","feeds","schema","schemaTypes"],"mappings":"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"}
|
|
@@ -65,6 +65,110 @@ const PLATFORM_HOSTNAME_MAP = new Map([
|
|
|
65
65
|
["twitter.com", "x"],
|
|
66
66
|
["www.twitter.com", "x"],
|
|
67
67
|
["t.co", "x"],
|
|
68
|
+
// GitHub
|
|
69
|
+
["github.com", "github"],
|
|
70
|
+
["www.github.com", "github"],
|
|
71
|
+
["gist.github.com", "github"],
|
|
72
|
+
["raw.githubusercontent.com", "github"],
|
|
73
|
+
["github.io", "github"],
|
|
74
|
+
// Discord
|
|
75
|
+
["discord.com", "discord"],
|
|
76
|
+
["www.discord.com", "discord"],
|
|
77
|
+
["discord.gg", "discord"],
|
|
78
|
+
["discordapp.com", "discord"],
|
|
79
|
+
["discordapp.net", "discord"],
|
|
80
|
+
["discord.new", "discord"],
|
|
81
|
+
["discord.gift", "discord"],
|
|
82
|
+
["discord.gifts", "discord"],
|
|
83
|
+
["dis.gd", "discord"],
|
|
84
|
+
// Snapchat
|
|
85
|
+
["snapchat.com", "snapchat"],
|
|
86
|
+
["www.snapchat.com", "snapchat"],
|
|
87
|
+
["snap.com", "snapchat"],
|
|
88
|
+
["www.snap.com", "snapchat"],
|
|
89
|
+
["story.snapchat.com", "snapchat"],
|
|
90
|
+
["web.snapchat.com", "snapchat"],
|
|
91
|
+
// Dev (Dev.to)
|
|
92
|
+
["dev.to", "dev"],
|
|
93
|
+
["www.dev.to", "dev"],
|
|
94
|
+
// Substack
|
|
95
|
+
["substack.com", "substack"],
|
|
96
|
+
["www.substack.com", "substack"],
|
|
97
|
+
// Reddit
|
|
98
|
+
["reddit.com", "reddit"],
|
|
99
|
+
["www.reddit.com", "reddit"],
|
|
100
|
+
["old.reddit.com", "reddit"],
|
|
101
|
+
["new.reddit.com", "reddit"],
|
|
102
|
+
["i.redd.it", "reddit"],
|
|
103
|
+
["v.redd.it", "reddit"],
|
|
104
|
+
["redd.it", "reddit"],
|
|
105
|
+
["preview.redd.it", "reddit"],
|
|
106
|
+
// Pinterest
|
|
107
|
+
["pinterest.com", "pinterest"],
|
|
108
|
+
["www.pinterest.com", "pinterest"],
|
|
109
|
+
["pin.it", "pinterest"],
|
|
110
|
+
["in.pinterest.com", "pinterest"],
|
|
111
|
+
["br.pinterest.com", "pinterest"],
|
|
112
|
+
["uk.pinterest.com", "pinterest"],
|
|
113
|
+
// Threads (Meta)
|
|
114
|
+
["threads.net", "threads"],
|
|
115
|
+
["www.threads.net", "threads"],
|
|
116
|
+
["threads.com", "threads"],
|
|
117
|
+
["www.threads.com", "threads"],
|
|
118
|
+
// Twitch
|
|
119
|
+
["twitch.tv", "twitch"],
|
|
120
|
+
["www.twitch.tv", "twitch"],
|
|
121
|
+
["m.twitch.tv", "twitch"],
|
|
122
|
+
// WhatsApp
|
|
123
|
+
["whatsapp.com", "whatsapp"],
|
|
124
|
+
["www.whatsapp.com", "whatsapp"],
|
|
125
|
+
["wa.me", "whatsapp"],
|
|
126
|
+
["web.whatsapp.com", "whatsapp"],
|
|
127
|
+
// Telegram
|
|
128
|
+
["telegram.org", "telegram"],
|
|
129
|
+
["www.telegram.org", "telegram"],
|
|
130
|
+
["t.me", "telegram"],
|
|
131
|
+
["telegram.me", "telegram"],
|
|
132
|
+
["telegram.dog", "telegram"],
|
|
133
|
+
// Medium
|
|
134
|
+
["medium.com", "medium"],
|
|
135
|
+
["www.medium.com", "medium"],
|
|
136
|
+
// Note: Custom Medium domains would need additional logic
|
|
137
|
+
// Patreon
|
|
138
|
+
["patreon.com", "patreon"],
|
|
139
|
+
["www.patreon.com", "patreon"],
|
|
140
|
+
// OnlyFans
|
|
141
|
+
["onlyfans.com", "onlyfans"],
|
|
142
|
+
["www.onlyfans.com", "onlyfans"],
|
|
143
|
+
// Eventbrite
|
|
144
|
+
["eventbrite.com", "eventbrite"],
|
|
145
|
+
["www.eventbrite.com", "eventbrite"],
|
|
146
|
+
["eventbrite.co.uk", "eventbrite"],
|
|
147
|
+
["eventbrite.com.au", "eventbrite"],
|
|
148
|
+
["eventbrite.ca", "eventbrite"],
|
|
149
|
+
["eventbrite.de", "eventbrite"],
|
|
150
|
+
["eventbrite.fr", "eventbrite"],
|
|
151
|
+
["eventbrite.es", "eventbrite"],
|
|
152
|
+
["eventbrite.it", "eventbrite"],
|
|
153
|
+
["eventbrite.ie", "eventbrite"],
|
|
154
|
+
["eventbrite.nl", "eventbrite"],
|
|
155
|
+
["eventbrite.co.nz", "eventbrite"],
|
|
156
|
+
["eventbriteapi.com", "eventbrite"],
|
|
157
|
+
["evbuc.com", "eventbrite"],
|
|
158
|
+
// npm
|
|
159
|
+
["npmjs.com", "npmjs"],
|
|
160
|
+
["www.npmjs.com", "npmjs"],
|
|
161
|
+
["npmjs.org", "npmjs"],
|
|
162
|
+
["www.npmjs.org", "npmjs"],
|
|
163
|
+
["registry.npmjs.org", "npmjs"],
|
|
164
|
+
["registry.npmjs.com", "npmjs"],
|
|
165
|
+
["replicate.npmjs.com", "npmjs"],
|
|
166
|
+
// Crates.io (Rust)
|
|
167
|
+
["crates.io", "crates"],
|
|
168
|
+
["www.crates.io", "crates"],
|
|
169
|
+
// RubyGems
|
|
170
|
+
["rubygems.org", "rubygems"],
|
|
171
|
+
["www.rubygems.org", "rubygems"],
|
|
68
172
|
]);
|
|
69
173
|
/**
|
|
70
174
|
* Extracts the social platform name from a URL string.
|
|
@@ -101,8 +205,28 @@ export function usePlatformFromUrl(url) {
|
|
|
101
205
|
try {
|
|
102
206
|
const urlObj = new URL(trimmedUrl);
|
|
103
207
|
const hostname = urlObj.hostname.toLowerCase();
|
|
208
|
+
// O(1) Map lookup first (fastest)
|
|
104
209
|
const platform = PLATFORM_HOSTNAME_MAP.get(hostname);
|
|
105
|
-
|
|
210
|
+
if (platform) {
|
|
211
|
+
return platform;
|
|
212
|
+
}
|
|
213
|
+
// Fallback pattern matching for custom domains (slower but more comprehensive)
|
|
214
|
+
if (hostname.endsWith(".substack.com")) {
|
|
215
|
+
return "substack";
|
|
216
|
+
}
|
|
217
|
+
if (hostname.endsWith(".github.io")) {
|
|
218
|
+
return "github";
|
|
219
|
+
}
|
|
220
|
+
if (hostname.includes("pinterest.com")) {
|
|
221
|
+
return "pinterest";
|
|
222
|
+
}
|
|
223
|
+
if (hostname.includes("eventbrite.")) {
|
|
224
|
+
return "eventbrite";
|
|
225
|
+
}
|
|
226
|
+
if (hostname.endsWith(".medium.com")) {
|
|
227
|
+
return "medium";
|
|
228
|
+
}
|
|
229
|
+
return "unknown";
|
|
106
230
|
}
|
|
107
231
|
catch {
|
|
108
232
|
return "unknown";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Supported social platform names
|
|
3
3
|
*/
|
|
4
|
-
export type SocialPlatformName = "instagram" | "linkedin" | "google" | "facebook" | "tiktok" | "youtube" | "yelp" | "spotify" | "apple" | "x" | "unknown";
|
|
4
|
+
export type SocialPlatformName = "instagram" | "linkedin" | "google" | "facebook" | "tiktok" | "youtube" | "yelp" | "spotify" | "apple" | "x" | "github" | "discord" | "snapchat" | "dev" | "substack" | "reddit" | "pinterest" | "threads" | "twitch" | "whatsapp" | "telegram" | "medium" | "patreon" | "onlyfans" | "eventbrite" | "npmjs" | "crates" | "rubygems" | "unknown";
|
|
5
5
|
/**
|
|
6
6
|
* Extracts the social platform name from a URL string.
|
|
7
7
|
* Uses the native URL API for validation and hostname extraction,
|
|
@@ -65,6 +65,110 @@ const PLATFORM_HOSTNAME_MAP = new Map([
|
|
|
65
65
|
["twitter.com", "x"],
|
|
66
66
|
["www.twitter.com", "x"],
|
|
67
67
|
["t.co", "x"],
|
|
68
|
+
// GitHub
|
|
69
|
+
["github.com", "github"],
|
|
70
|
+
["www.github.com", "github"],
|
|
71
|
+
["gist.github.com", "github"],
|
|
72
|
+
["raw.githubusercontent.com", "github"],
|
|
73
|
+
["github.io", "github"],
|
|
74
|
+
// Discord
|
|
75
|
+
["discord.com", "discord"],
|
|
76
|
+
["www.discord.com", "discord"],
|
|
77
|
+
["discord.gg", "discord"],
|
|
78
|
+
["discordapp.com", "discord"],
|
|
79
|
+
["discordapp.net", "discord"],
|
|
80
|
+
["discord.new", "discord"],
|
|
81
|
+
["discord.gift", "discord"],
|
|
82
|
+
["discord.gifts", "discord"],
|
|
83
|
+
["dis.gd", "discord"],
|
|
84
|
+
// Snapchat
|
|
85
|
+
["snapchat.com", "snapchat"],
|
|
86
|
+
["www.snapchat.com", "snapchat"],
|
|
87
|
+
["snap.com", "snapchat"],
|
|
88
|
+
["www.snap.com", "snapchat"],
|
|
89
|
+
["story.snapchat.com", "snapchat"],
|
|
90
|
+
["web.snapchat.com", "snapchat"],
|
|
91
|
+
// Dev (Dev.to)
|
|
92
|
+
["dev.to", "dev"],
|
|
93
|
+
["www.dev.to", "dev"],
|
|
94
|
+
// Substack
|
|
95
|
+
["substack.com", "substack"],
|
|
96
|
+
["www.substack.com", "substack"],
|
|
97
|
+
// Reddit
|
|
98
|
+
["reddit.com", "reddit"],
|
|
99
|
+
["www.reddit.com", "reddit"],
|
|
100
|
+
["old.reddit.com", "reddit"],
|
|
101
|
+
["new.reddit.com", "reddit"],
|
|
102
|
+
["i.redd.it", "reddit"],
|
|
103
|
+
["v.redd.it", "reddit"],
|
|
104
|
+
["redd.it", "reddit"],
|
|
105
|
+
["preview.redd.it", "reddit"],
|
|
106
|
+
// Pinterest
|
|
107
|
+
["pinterest.com", "pinterest"],
|
|
108
|
+
["www.pinterest.com", "pinterest"],
|
|
109
|
+
["pin.it", "pinterest"],
|
|
110
|
+
["in.pinterest.com", "pinterest"],
|
|
111
|
+
["br.pinterest.com", "pinterest"],
|
|
112
|
+
["uk.pinterest.com", "pinterest"],
|
|
113
|
+
// Threads (Meta)
|
|
114
|
+
["threads.net", "threads"],
|
|
115
|
+
["www.threads.net", "threads"],
|
|
116
|
+
["threads.com", "threads"],
|
|
117
|
+
["www.threads.com", "threads"],
|
|
118
|
+
// Twitch
|
|
119
|
+
["twitch.tv", "twitch"],
|
|
120
|
+
["www.twitch.tv", "twitch"],
|
|
121
|
+
["m.twitch.tv", "twitch"],
|
|
122
|
+
// WhatsApp
|
|
123
|
+
["whatsapp.com", "whatsapp"],
|
|
124
|
+
["www.whatsapp.com", "whatsapp"],
|
|
125
|
+
["wa.me", "whatsapp"],
|
|
126
|
+
["web.whatsapp.com", "whatsapp"],
|
|
127
|
+
// Telegram
|
|
128
|
+
["telegram.org", "telegram"],
|
|
129
|
+
["www.telegram.org", "telegram"],
|
|
130
|
+
["t.me", "telegram"],
|
|
131
|
+
["telegram.me", "telegram"],
|
|
132
|
+
["telegram.dog", "telegram"],
|
|
133
|
+
// Medium
|
|
134
|
+
["medium.com", "medium"],
|
|
135
|
+
["www.medium.com", "medium"],
|
|
136
|
+
// Note: Custom Medium domains would need additional logic
|
|
137
|
+
// Patreon
|
|
138
|
+
["patreon.com", "patreon"],
|
|
139
|
+
["www.patreon.com", "patreon"],
|
|
140
|
+
// OnlyFans
|
|
141
|
+
["onlyfans.com", "onlyfans"],
|
|
142
|
+
["www.onlyfans.com", "onlyfans"],
|
|
143
|
+
// Eventbrite
|
|
144
|
+
["eventbrite.com", "eventbrite"],
|
|
145
|
+
["www.eventbrite.com", "eventbrite"],
|
|
146
|
+
["eventbrite.co.uk", "eventbrite"],
|
|
147
|
+
["eventbrite.com.au", "eventbrite"],
|
|
148
|
+
["eventbrite.ca", "eventbrite"],
|
|
149
|
+
["eventbrite.de", "eventbrite"],
|
|
150
|
+
["eventbrite.fr", "eventbrite"],
|
|
151
|
+
["eventbrite.es", "eventbrite"],
|
|
152
|
+
["eventbrite.it", "eventbrite"],
|
|
153
|
+
["eventbrite.ie", "eventbrite"],
|
|
154
|
+
["eventbrite.nl", "eventbrite"],
|
|
155
|
+
["eventbrite.co.nz", "eventbrite"],
|
|
156
|
+
["eventbriteapi.com", "eventbrite"],
|
|
157
|
+
["evbuc.com", "eventbrite"],
|
|
158
|
+
// npm
|
|
159
|
+
["npmjs.com", "npmjs"],
|
|
160
|
+
["www.npmjs.com", "npmjs"],
|
|
161
|
+
["npmjs.org", "npmjs"],
|
|
162
|
+
["www.npmjs.org", "npmjs"],
|
|
163
|
+
["registry.npmjs.org", "npmjs"],
|
|
164
|
+
["registry.npmjs.com", "npmjs"],
|
|
165
|
+
["replicate.npmjs.com", "npmjs"],
|
|
166
|
+
// Crates.io (Rust)
|
|
167
|
+
["crates.io", "crates"],
|
|
168
|
+
["www.crates.io", "crates"],
|
|
169
|
+
// RubyGems
|
|
170
|
+
["rubygems.org", "rubygems"],
|
|
171
|
+
["www.rubygems.org", "rubygems"],
|
|
68
172
|
]);
|
|
69
173
|
/**
|
|
70
174
|
* Extracts the social platform name from a URL string.
|
|
@@ -101,8 +205,28 @@ export function usePlatformFromUrl(url) {
|
|
|
101
205
|
try {
|
|
102
206
|
const urlObj = new URL(trimmedUrl);
|
|
103
207
|
const hostname = urlObj.hostname.toLowerCase();
|
|
208
|
+
// O(1) Map lookup first (fastest)
|
|
104
209
|
const platform = PLATFORM_HOSTNAME_MAP.get(hostname);
|
|
105
|
-
|
|
210
|
+
if (platform) {
|
|
211
|
+
return platform;
|
|
212
|
+
}
|
|
213
|
+
// Fallback pattern matching for custom domains (slower but more comprehensive)
|
|
214
|
+
if (hostname.endsWith(".substack.com")) {
|
|
215
|
+
return "substack";
|
|
216
|
+
}
|
|
217
|
+
if (hostname.endsWith(".github.io")) {
|
|
218
|
+
return "github";
|
|
219
|
+
}
|
|
220
|
+
if (hostname.includes("pinterest.com")) {
|
|
221
|
+
return "pinterest";
|
|
222
|
+
}
|
|
223
|
+
if (hostname.includes("eventbrite.")) {
|
|
224
|
+
return "eventbrite";
|
|
225
|
+
}
|
|
226
|
+
if (hostname.endsWith(".medium.com")) {
|
|
227
|
+
return "medium";
|
|
228
|
+
}
|
|
229
|
+
return "unknown";
|
|
106
230
|
}
|
|
107
231
|
catch {
|
|
108
232
|
return "unknown";
|