grab-url 1.6.2 → 1.6.4
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/grab-api.cjs.js +1 -1
- package/dist/grab-api.cjs.js.map +1 -1
- package/dist/grab-api.es.js +1 -1
- package/dist/grab-api.es.js.map +1 -1
- package/dist/grab-url-cli.cjs.js +1 -1
- package/dist/grab-url-cli.cjs.js.map +1 -1
- package/dist/grab-url-cli.es.js +1 -1
- package/dist/grab-url-cli.es.js.map +1 -1
- package/package.json +1 -1
package/dist/grab-api.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./log.cjs.js"),t=async(e,t)=>{let o;return async function(...n){clearTimeout(o),o=setTimeout(async()=>{clearTimeout(o),await e(...n)},t)}},o=e=>new Promise(t=>setTimeout(t,1e3*e||0)),n=(e,t)=>{let o=e=>t?.startsWith(e)||!1,n=e,r=t;return t?.startsWith("http:")||t?.startsWith("https:")?n="":o("/")||n.endsWith("/")?o("/")&&n.endsWith("/")&&(r=t.slice(1)):r="/"+t,{baseURL:n,path:r}};function r(e){if("undefined"==typeof document)return;let t,o=document.getElementById("alert-overlay");if(!o){o=document.body.appendChild(document.createElement("div")),o.id="alert-overlay",o.setAttribute("style","position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center"),o.innerHTML='<div id="alert-box" style="background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:220px;max-height:80vh;position:relative;display:flex;flex-direction:column;">\n <button id="close-alert" style="position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;">×</button>\n <div id="alert-list" style="overflow:auto;flex:1;"></div>\n </div>',o.addEventListener("click",e=>e.target==o&&o.remove());const e=document.getElementById("close-alert");e&&(e.onclick=()=>o.remove())}t=document.getElementById("alert-list"),t.innerHTML+=`<div style="border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;">${e}</div>`}function s(){"undefined"!=typeof document&&document.addEventListener("keydown",t=>{const o=window.grab;if(o&&o.log&&"i"===t.key&&t.ctrlKey&&t.altKey){let t=" ";for(let n of o.log)t+=`<div style="margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;">\n <b>Path:</b> ${n.path}<br>\n <b>Request:</b> ${e.printJSONStructure(n.request,0,"html")}<br>\n <b>Response:</b> ${e.printJSONStructure(n.response,0,"html")}<br> \n <b>Time:</b> ${new Date(n.lastFetchTime).toLocaleString()}\n </div>`;r(t)}})}async function i(s,a){const l=function(e){return{..."undefined"!=typeof window?window?.grab?.defaults:globalThis?.grab?.defaults||{},...e}}(a);let{headers:c,response:d,method:u=(l.post?"POST":l.put?"PUT":l.patch?"PATCH":"GET"),cache:f,timeout:p=30,baseURL:g="undefined"!=typeof process&&process.env.SERVER_API_URL||"/api/",cancelOngoingIfNew:m,cancelNewIfOngoing:y,rateLimit:b,debug:w,infiniteScroll:h,logger:v=e.log,onRequest:T,onResponse:x,onError:L,onStream:S,body:E,post:O,put:A,patch:P,...j}=l;const k=n(g,s);g=k.baseURL,s=k.path;const R=function(e){const t="function"==typeof e?e:null;return{response:!e||t?{}:e,resFunction:t}}(d);let N=R.response,$=R.resFunction;const q="undefined"!=typeof window?window.grab:globalThis.grab,F=q?.log||[];$?N=$({...N,isLoading:!0}):"object"==typeof N&&(N.isLoading=!0);try{const n=await async function(e,o,n,r){const{debounce:s=0,repeat:i=0,repeatEvery:a=null,setDefaults:l=!1}=n;if(s>0){const i=await t(async()=>{await r(e,{...o,debounce:0})},1e3*s);return await i(),n.response||{}}if(i>1){for(let t=0;t<i;t++)await r(e,{...o,repeat:0});return n.response||{}}if(a)return setInterval(async()=>{await r(e,{...o,repeat:0,repeatEvery:null})},1e3*a),n.response||{};if(l){const e="undefined"!=typeof window?window.grab:globalThis.grab;return e&&(e.defaults={...o,setDefaults:void 0}),n.response||{}}return null}(s,a||{},l,i);if(n)return n;!function(e,t,o,n){if("undefined"==typeof window)return;const{regrabOnStale:r,regrabOnFocus:s,regrabOnNetwork:i,cache:a,cacheForTime:l=60}=o,c=async()=>await n(e,{...t,cache:!1});r&&a&&setTimeout(c,1e3*l),i&&window.addEventListener("online",c),s&&(window.addEventListener("focus",c),document.addEventListener("visibilitychange",async()=>{"visible"===document.visibilityState&&await c()}))}(s,a||{},l,i);let{params:r,priorRequest:d,response:L,paramsAsText:O}=function(e,t,o,n,r,s){const{cache:i,cacheForTime:a,infiniteScroll:l}=o,[c,d]=l||[],u=JSON.stringify(c?{...t,[c]:void 0}:t);let f=s.find(t=>t.request===u&&t.path===e);if(c){let e=(f?.currentPage||0)+1||t?.[c]||1;f?f.currentPage=e:(n[d]=[],e=1),t={...t,[c]:e}}else{for(let e of Object.keys(n))n[e]=void 0;if(i&&f?.response&&(!a||f.lastFetchTime>Date.now()-1e3*a)){for(let e of Object.keys(f.response))n[e]=f.response[e];r&&(n=r(n))}}return{params:t,priorRequest:f,response:n,paramsAsText:u}}(s,j,l,N,$,F);if(j=r,N=L,function(t,o,n,r,s){if("undefined"==typeof window||!n?.length)return;const[i,,a]=n;if(void 0===a)return;let l="string"==typeof a?document.querySelector(a):a;l?(window.scrollListener&&"function"==typeof l.removeEventListener&&l.removeEventListener("scroll",window.scrollListener),window.scrollListener=e=>{const n=e.target;localStorage.setItem("scroll",JSON.stringify([n.scrollTop,n.scrollLeft,a])),n.scrollHeight-n.scrollTop<=n.clientHeight+200&&s(t,{...o,cache:!1,[i]:(r?.currentPage||0)+1})},l.addEventListener("scroll",window.scrollListener)):e.log("paginateDOM not found",{color:"red"})}(s,a,h,d,i),b>0&&d?.lastFetchTime>Date.now()-1e3*b)throw new Error(`Fetch rate limit exceeded for ${s}. Wait ${b}s between requests.`);if(d?.controller)if(m)d.controller.abort();else if(y)return{isLoading:!0};const A=new AbortController;let P=A.signal;m||"function"!=typeof AbortSignal.timeout||(P=AbortSignal.timeout(1e3*p)),F.unshift({path:s,request:O,lastFetchTime:Date.now(),controller:A});let{fetchParams:k,paramsGETRequest:R}=function(e,t,o,n,r,s){const i=["POST","PUT","PATCH"].includes(e),a={method:e,headers:{"Content-Type":"application/json",Accept:"application/json",...t},body:o||(i?JSON.stringify(n):null),redirect:"follow",cache:r?"force-cache":"no-store",signal:s};let l="";return i||(l=(Object.keys(n).length?"?":"")+new URLSearchParams(n).toString()),{fetchParams:a,paramsGETRequest:l}}(u,c,E,j,!!f,P);if("function"==typeof T){const e=T(s,N,j,k);Array.isArray(e)&&([s,N,j,k]=e)}const q=new Date,D=await async function(e,t,n,r,s,i){const a="undefined"!=typeof window?window.grab:globalThis.grab,l=a?.mock?.[t],c=JSON.stringify(s);if(l&&(!l.method||l.method===r.method)&&(!l.params||c===JSON.stringify(l.params)))return await o(l.delay||0),"function"==typeof l.response?l.response(s):l.response;const d=await fetch(e+t+n,r).catch(e=>{throw new Error(e.message)});if(!d.ok)throw new Error(`HTTP error: ${d.status} ${d.statusText}`);if(i)return await i(d.body),null;const u=d.headers.get("content-type");return await(u?u.includes("application/json")?d.json():u.includes("application/pdf")||u.includes("application/octet-stream")?d.blob():d.text():d.json()).catch(e=>{throw new Error("Error parsing response: "+e)})}(g,s,R,k,j,S);if($?N=$({...N,isLoading:void 0}):"object"==typeof N&&delete N.isLoading,"function"==typeof x){const e=x(s,N,j,k);Array.isArray(e)&&([s,N,j,k]=e)}const J=((Number(new Date)-Number(q))/1e3).toFixed(1);w&&v(`Path:${g+s+R}\n${JSON.stringify(a,null,2)}\nTime: ${J}s\nResponse: ${e.printJSONStructure(D)}`);const[,I]=h||[];return N=function(e,t,o,n){if("object"==typeof e&&null!==e){for(let o of Object.keys(e))t[o]=n===o&&Array.isArray(t[o])?[...t[o],...e[o]]:e[o];t.data=e}else o?t=o({data:e,...e}):"object"==typeof t&&(t.data=e);return t}(D,N,$,I),F[0]&&(F[0].response=N),$&&(N=$(N)),N}catch(D){if("function"==typeof L&&L(D.message,g+s,j),l.retryAttempts&&l.retryAttempts>0)return await i(s,{...a,retryAttempts:--l.retryAttempts});!D.message.includes("signal")&&w&&(v(`Error: ${D.message}\nPath:${g+s}\n`,{color:"red"}),"undefined"!=typeof document&&r(D.message)),N=N||{},N.error=D.message;const e="function"==typeof d?d:null;return e?(N.data=e({isLoading:void 0,error:D.message}),N=N.data):delete N.isLoading,N}}const a=i;a.instance=(e={})=>(t,o={})=>i(t,{...e,...o}),a.log=[],a.mock={},a.defaults={},"undefined"!=typeof window?(window.log=e.log,window.grab=a,s(),document.addEventListener("DOMContentLoaded",()=>{try{const e=localStorage.getItem("scroll");if(!e)return;const[t,o,n]=JSON.parse(e);if(!t||!n)return;const r=document.querySelector(n);r&&(r.scrollTop=t,r.scrollLeft=o)}catch(e){console.warn("Failed to restore scroll position",e)}})):"undefined"!=typeof globalThis&&(globalThis.log=e.log,globalThis.grab=a),exports.log=e.log,exports.buildUrl=n,exports.debouncer=t,exports.default=a,exports.grab=a,exports.setupDevTools=s,exports.showAlert=r,exports.wait=o;
|
|
2
2
|
//# sourceMappingURL=grab-api.cjs.js.map
|
package/dist/grab-api.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grab-api.cjs.js","sources":["../packages/grab-api/common/utils.ts","../packages/grab-api/devtools/devtools.ts","../packages/grab-api/core/core.ts","../packages/grab-api/core/flow-control.ts","../packages/grab-api/response/response-handler.ts","../packages/grab-api/core/regrab-events.ts","../packages/grab-api/core/cache-pagination.ts","../packages/grab-api/response/infinite-scroll.ts","../packages/grab-api/core/request-executor.ts","../packages/grab-api/index.ts"],"sourcesContent":["/**\r\n * @file common/utils.ts\r\n * @description Common utility functions used across the Grab API.\r\n * Includes helpers for debouncing, URL building, and asynchronous waiting.\r\n */\r\n\r\n/**\r\n * Delays execution so that future calls may override and only executes the last one.\r\n * Useful for search inputs or other high-frequency events.\r\n * \r\n * @param func - The function to debounce.\r\n * @param wait - Time to wait in milliseconds.\r\n * @returns A debounced version of the function.\r\n */\r\nexport const debouncer = async (func: Function, wait: number) => {\r\n let timeout: any;\r\n return async function executedFunction(...args: any[]) {\r\n const later = async () => {\r\n clearTimeout(timeout);\r\n await func(...args);\r\n };\r\n clearTimeout(timeout);\r\n timeout = setTimeout(later, wait);\r\n };\r\n};\r\n\r\n/**\r\n * Helper function to wait for a specified number of seconds.\r\n * \r\n * @param s - Seconds to wait.\r\n * @returns A promise that resolves after the timeout.\r\n */\r\nexport const wait = (s: number) => new Promise((res) => setTimeout(res, s * 1000 || 0));\r\n\r\n/**\r\n * Normalizes and builds a URL from a base and relative path.\r\n * \r\n * @param baseURL - The base API URL.\r\n * @param path - The specific endpoint path.\r\n * @returns An object containing the combined URL and updated baseURL/path if needed.\r\n */\r\nexport const buildUrl = (baseURL: string, path: string) => {\r\n let s = (t: string) => path?.startsWith(t) || false;\r\n let finalBaseURL = baseURL;\r\n let finalPath = path;\r\n\r\n if (path?.startsWith(\"http:\") || path?.startsWith(\"https:\")) {\r\n finalBaseURL = \"\";\r\n } else if (!s(\"/\") && !finalBaseURL.endsWith(\"/\")) {\r\n finalPath = \"/\" + path;\r\n } else if (s(\"/\") && finalBaseURL.endsWith(\"/\")) {\r\n finalPath = path.slice(1);\r\n }\r\n\r\n return { baseURL: finalBaseURL, path: finalPath };\r\n};\r\n","/**\r\n * @file ui/devtools.ts\r\n * @description Visual development tools for the Grab API.\r\n * Includes a modal overlay for inspect request history (Ctrl+Alt+I).\r\n */\r\n\r\nimport { printJSONStructure } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Shows a message in a modal overlay with a scrollable message stack.\r\n * Easier to dismiss than native alert() and does not block window execution.\r\n *\r\n * @param msg - The message or HTML content to display.\r\n */\r\nexport function showAlert(msg: string) {\r\n if (typeof document === \"undefined\") return;\r\n let o = document.getElementById(\"alert-overlay\"),\r\n list: HTMLElement;\r\n\r\n if (!o) {\r\n o = document.body.appendChild(document.createElement(\"div\"));\r\n o.id = \"alert-overlay\";\r\n o.setAttribute(\r\n \"style\",\r\n \"position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\",\r\n );\r\n o.innerHTML = `<div id=\"alert-box\" style=\"background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:220px;max-height:80vh;position:relative;display:flex;flex-direction:column;\">\r\n <button id=\"close-alert\" style=\"position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;\">×</button>\r\n <div id=\"alert-list\" style=\"overflow:auto;flex:1;\"></div>\r\n </div>`;\r\n\r\n o.addEventListener(\"click\", (e) => e.target == o && o!.remove());\r\n const closeBtn = document.getElementById(\"close-alert\");\r\n if (closeBtn) closeBtn.onclick = () => o!.remove();\r\n }\r\n\r\n list = document.getElementById(\"alert-list\")!;\r\n list.innerHTML += `<div style=\"border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;\">${msg}</div>`;\r\n}\r\n\r\n/**\r\n * Sets up development tools for debugging API requests.\r\n * Adds a keyboard shortcut (Ctrl+Alt+I) to toggle a modal showing the request history from `grab.log`.\r\n */\r\nexport function setupDevTools() {\r\n if (typeof document === \"undefined\") return;\r\n\r\n document.addEventListener(\"keydown\", (e) => {\r\n // Check for global grab on window\r\n const grab = (window as any).grab as GrabFunction;\r\n if (!grab || !grab.log) return;\r\n\r\n if (e.key === \"i\" && e.ctrlKey && e.altKey) {\r\n let html = \" \";\r\n for (let request of grab.log) {\r\n html += `<div style=\"margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;\">\r\n <b>Path:</b> ${request.path}<br>\r\n <b>Request:</b> ${printJSONStructure(request.request, 0, \"html\")}<br>\r\n <b>Response:</b> ${printJSONStructure(\r\n request.response,\r\n 0,\r\n \"html\",\r\n )}<br> \r\n <b>Time:</b> ${new Date(request.lastFetchTime).toLocaleString()}\r\n </div>`;\r\n }\r\n showAlert(html);\r\n }\r\n });\r\n}\r\n","/**\r\n * @file core/core.ts\r\n * @description Main orchestration logic for the Grab API.\r\n * Defines the primary 'grab' function and coordinates request flow,\r\n * including option merging, caching, and execution.\r\n */\r\n\r\nimport { printJSONStructure, log } from \"@grab-url/log\";\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { buildUrl } from \"../common/utils\";\r\nimport { showAlert } from \"../devtools/devtools\";\r\n\r\nimport { getMergedOptions, handleFlowControl } from \"./flow-control\";\r\nimport { handleRegrabEvents } from \"./regrab-events\";\r\nimport {\r\n initializeResponse,\r\n mapResultToResponse,\r\n} from \"../response/response-handler\";\r\nimport { setupInfiniteScroll } from \"../response/infinite-scroll\";\r\nimport { manageCacheAndPagination } from \"./cache-pagination\";\r\nimport { prepareFetchRequest, executeRequest } from \"./request-executor\";\r\n\r\n/**\r\n * ### GRAB: Generate Request to API from Browser\r\n *\r\n * The core function for making API requests. Handles JSON conversion,\r\n * loading states, caching, pagination, debouncing, and more.\r\n */\r\nexport async function grab<TResponse = any, TParams = any>(\r\n path: string,\r\n options?: GrabOptions<TResponse, TParams>,\r\n): Promise<GrabResponse<TResponse>> {\r\n const merged = getMergedOptions(options);\r\n let {\r\n headers,\r\n response: responseOption,\r\n method = merged.post\r\n ? \"POST\"\r\n : merged.put\r\n ? \"PUT\"\r\n : merged.patch\r\n ? \"PATCH\"\r\n : \"GET\",\r\n cache,\r\n timeout = 30,\r\n baseURL = (typeof process !== \"undefined\" && process.env.SERVER_API_URL) ||\r\n \"/api/\",\r\n cancelOngoingIfNew,\r\n cancelNewIfOngoing,\r\n rateLimit,\r\n debug,\r\n infiniteScroll,\r\n logger = log,\r\n onRequest,\r\n onResponse,\r\n onError,\r\n onStream,\r\n body,\r\n post,\r\n put,\r\n patch,\r\n ...params\r\n } = merged;\r\n\r\n const urlConfig = buildUrl(baseURL, path);\r\n baseURL = urlConfig.baseURL;\r\n path = urlConfig.path;\r\n\r\n const initialized = initializeResponse(responseOption);\r\n let response: any = initialized.response;\r\n let resFunction = initialized.resFunction;\r\n const target = (\r\n typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab\r\n ) as GrabFunction;\r\n const grabLog = target?.log || [];\r\n\r\n // Set loading state synchronously before any await\r\n if (resFunction) response = resFunction({ ...response, isLoading: true });\r\n else if (typeof response === \"object\") response.isLoading = true;\r\n\r\n try {\r\n const flowResult = await handleFlowControl(\r\n path,\r\n options || {},\r\n merged,\r\n grab as unknown as GrabFunction,\r\n );\r\n if (flowResult) return flowResult as any;\r\n\r\n handleRegrabEvents(path, options || {}, merged, grab as unknown as GrabFunction);\r\n\r\n let {\r\n params: updatedParams,\r\n priorRequest,\r\n response: updatedResponse,\r\n paramsAsText,\r\n } = manageCacheAndPagination(\r\n path,\r\n params,\r\n merged,\r\n response,\r\n resFunction,\r\n grabLog,\r\n );\r\n params = updatedParams;\r\n response = updatedResponse;\r\n\r\n setupInfiniteScroll(path, options, infiniteScroll, priorRequest, grab as unknown as GrabFunction);\r\n\r\n if (\r\n rateLimit > 0 &&\r\n priorRequest?.lastFetchTime > Date.now() - 1000 * rateLimit\r\n ) {\r\n throw new Error(\r\n `Fetch rate limit exceeded for ${path}. Wait ${rateLimit}s between requests.`,\r\n );\r\n }\r\n\r\n if (priorRequest?.controller) {\r\n if (cancelOngoingIfNew) priorRequest.controller.abort();\r\n else if (cancelNewIfOngoing) return { isLoading: true } as GrabResponse;\r\n }\r\n\r\n const controller = new AbortController();\r\n let signal = controller.signal;\r\n\r\n // Add timeout if requested (and signal not already used for cancellation)\r\n if (!cancelOngoingIfNew && typeof AbortSignal.timeout === \"function\") {\r\n signal = AbortSignal.timeout(timeout * 1000);\r\n }\r\n\r\n grabLog.unshift({\r\n path,\r\n request: paramsAsText,\r\n lastFetchTime: Date.now(),\r\n controller,\r\n });\r\n\r\n let { fetchParams, paramsGETRequest } = prepareFetchRequest(\r\n method,\r\n headers,\r\n body,\r\n params,\r\n !!cache,\r\n signal,\r\n );\r\n\r\n if (typeof onRequest === \"function\") {\r\n const modified = onRequest(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const startTime = new Date();\r\n const res = await executeRequest(\r\n baseURL,\r\n path,\r\n paramsGETRequest,\r\n fetchParams,\r\n params,\r\n onStream,\r\n );\r\n\r\n // Clear loading state\r\n if (resFunction)\r\n response = resFunction({ ...response, isLoading: undefined });\r\n else if (typeof response === \"object\") delete response.isLoading;\r\n\r\n if (typeof onResponse === \"function\") {\r\n const modified = onResponse(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const elapsedTime = (\r\n (Number(new Date()) - Number(startTime)) /\r\n 1000\r\n ).toFixed(1);\r\n if (debug) {\r\n logger(\r\n `Path:${baseURL + path + paramsGETRequest}\\n${JSON.stringify(options, null, 2)}\\nTime: ${elapsedTime}s\\nResponse: ${printJSONStructure(res)}`,\r\n );\r\n }\r\n\r\n const [, paginateResult] = (infiniteScroll as any) || [];\r\n response = mapResultToResponse(res, response, resFunction, paginateResult);\r\n\r\n if (grabLog[0]) grabLog[0].response = response;\r\n if (resFunction) response = resFunction(response);\r\n\r\n return response as any;\r\n } catch (error: any) {\r\n if (typeof onError === \"function\")\r\n onError(error.message, baseURL + path, params);\r\n\r\n if (merged.retryAttempts && merged.retryAttempts > 0) {\r\n return await grab(path, {\r\n ...options,\r\n retryAttempts: --merged.retryAttempts,\r\n });\r\n }\r\n\r\n if (!error.message.includes(\"signal\") && debug) {\r\n logger(`Error: ${error.message}\\nPath:${baseURL + path}\\n`, {\r\n color: \"red\",\r\n });\r\n if (typeof document !== \"undefined\") showAlert(error.message);\r\n }\r\n\r\n response = response || {};\r\n response.error = error.message;\r\n const resFn = typeof responseOption === \"function\" ? responseOption : null;\r\n if (resFn) {\r\n response.data = resFn({ isLoading: undefined, error: error.message });\r\n response = response.data;\r\n } else {\r\n delete response.isLoading;\r\n }\r\n return response as any;\r\n }\r\n}\r\n","/**\r\n * @file core/flow-control.ts\r\n * @description Logic for managing request flow and option merging.\r\n * Handles debouncing, request repetition, and default option application.\r\n */\r\n\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { debouncer } from \"../common/utils\";\r\n\r\n/**\r\n * Merges provided options with global defaults.\r\n */\r\nexport function getMergedOptions<TResponse, TParams>(\r\n options?: GrabOptions<TResponse, TParams>\r\n): GrabOptions<TResponse, TParams> & { [key: string]: any } {\r\n const defaults = (typeof window !== \"undefined\"\r\n ? window?.grab?.defaults\r\n : (globalThis as any)?.grab?.defaults || {}) as GrabOptions<TResponse, TParams>;\r\n\r\n return {\r\n ...defaults,\r\n ...options,\r\n };\r\n}\r\n\r\n/**\r\n * Handles flow control logic like debouncing and repeating requests.\r\n */\r\nexport async function handleFlowControl<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): Promise<GrabResponse<TResponse> | null> {\r\n const { debounce = 0, repeat = 0, repeatEvery = null, setDefaults = false } = mergedOptions;\r\n\r\n // Handle debounce\r\n if (debounce > 0) {\r\n const task = await debouncer(async () => {\r\n await grab(path, { ...options, debounce: 0 });\r\n }, debounce * 1000);\r\n await task();\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Handle repeat options\r\n if (repeat > 1) {\r\n for (let i = 0; i < repeat; i++) {\r\n await grab(path, { ...options, repeat: 0 });\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n if (repeatEvery) {\r\n setInterval(async () => {\r\n await grab(path, { ...options, repeat: 0, repeatEvery: null });\r\n }, repeatEvery * 1000);\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Store defaults if requested\r\n if (setDefaults) {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab);\r\n if (target) {\r\n target.defaults = { ...options, setDefaults: undefined };\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n return null;\r\n}\r\n","/**\r\n * @file response/response-handler.ts\r\n * @description Logic for handling API responses.\r\n * Includes functions for initializing response objects and mapping results.\r\n */\r\n\r\n/**\r\n * Initializes the response object and result function.\r\n */\r\nexport function initializeResponse<TResponse>(responseOption: any): {\r\n response: any;\r\n resFunction: ((data: any) => any) | null;\r\n} {\r\n const resFunction = typeof responseOption === \"function\" ? responseOption : null;\r\n let response = (!responseOption || resFunction) ? {} : responseOption;\r\n return { response, resFunction };\r\n}\r\n\r\n/**\r\n * Maps the raw result onto the response object.\r\n */\r\nexport function mapResultToResponse(res: any, response: any, resFunction: any, paginateResult: string | null): any {\r\n if (typeof res === \"object\" && res !== null) {\r\n for (let key of Object.keys(res)) {\r\n response[key] = (paginateResult === key && Array.isArray(response[key]))\r\n ? [...response[key], ...res[key]]\r\n : res[key];\r\n }\r\n response.data = res;\r\n } else {\r\n if (resFunction) {\r\n response = resFunction({ data: res, ...res });\r\n } else if (typeof response === \"object\") {\r\n response.data = res;\r\n }\r\n }\r\n return response;\r\n}\r\n","/**\r\n * @file core/regrab-events.ts\r\n * @description Event listeners for automatic data re-fetching.\r\n * Handles stale cache, window focus, and network status changes.\r\n */\r\n\r\nimport { GrabOptions, GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Sets up event listeners for regrabbing data on stale, focus, or network changes.\r\n */\r\nexport function handleRegrabEvents<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const { regrabOnStale, regrabOnFocus, regrabOnNetwork, cache, cacheForTime = 60 } = mergedOptions;\r\n const regrab = async () => await grab(path, { ...options, cache: false });\r\n\r\n if (regrabOnStale && cache) setTimeout(regrab, 1000 * cacheForTime);\r\n if (regrabOnNetwork) window.addEventListener(\"online\", regrab);\r\n if (regrabOnFocus) {\r\n window.addEventListener(\"focus\", regrab);\r\n document.addEventListener(\"visibilitychange\", async () => {\r\n if (document.visibilityState === \"visible\") await regrab();\r\n });\r\n }\r\n}\r\n","/**\r\n * @file core/cache-pagination.ts\r\n * @description Cache management and pagination state tracking.\r\n * Determines cache hits and updates current page numbers for infinite scroll.\r\n */\r\n\r\n/**\r\n * Manages cache hits and pagination state updates.\r\n */\r\nexport function manageCacheAndPagination(\r\n path: string,\r\n params: any,\r\n mergedOptions: any,\r\n response: any,\r\n resFunction: any,\r\n grabLog: any[]\r\n): { params: any; priorRequest: any; response: any; paramsAsText: string } {\r\n const { cache, cacheForTime, infiniteScroll } = mergedOptions;\r\n const [paginateKey, paginateResult] = (infiniteScroll as any) || [];\r\n\r\n const paramsAsText = JSON.stringify(\r\n paginateKey ? { ...params, [paginateKey as string]: undefined } : params\r\n );\r\n\r\n let priorRequest = grabLog.find(e => e.request === paramsAsText && e.path === path);\r\n\r\n if (!paginateKey) {\r\n // Clear response for fresh request\r\n for (let key of Object.keys(response)) response[key] = undefined;\r\n\r\n // Cache hit check\r\n if (cache && priorRequest?.response &&\r\n (!cacheForTime || priorRequest.lastFetchTime > Date.now() - 1000 * cacheForTime)) {\r\n for (let key of Object.keys(priorRequest.response)) {\r\n response[key] = priorRequest.response[key];\r\n }\r\n if (resFunction) response = resFunction(response);\r\n }\r\n } else {\r\n // Pagination logic\r\n let pageNumber = (priorRequest?.currentPage || 0) + 1 || params?.[paginateKey] || 1;\r\n if (!priorRequest) {\r\n response[paginateResult] = [];\r\n pageNumber = 1;\r\n } else {\r\n priorRequest.currentPage = pageNumber;\r\n }\r\n params = { ...params, [paginateKey]: pageNumber };\r\n }\r\n\r\n return { params, priorRequest, response, paramsAsText };\r\n}\r\n","/**\r\n * @file response/infinite-scroll.ts\r\n * @description Infinite scroll pagination logic.\r\n * Manages scroll event listeners and triggers automatic next-page fetching.\r\n */\r\n\r\nimport { log } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Configures infinite scroll listener.\r\n */\r\nexport function setupInfiniteScroll(\r\n path: string,\r\n options: any,\r\n infiniteScroll: any,\r\n priorRequest: any,\r\n grab: GrabFunction,\r\n): void {\r\n if (typeof window === \"undefined\" || !infiniteScroll?.length) return;\r\n\r\n const [paginateKey, , paginateElement] = infiniteScroll;\r\n if (typeof paginateElement === \"undefined\") return;\r\n\r\n let paginateDOM =\r\n typeof paginateElement === \"string\"\r\n ? document.querySelector(paginateElement)\r\n : paginateElement;\r\n\r\n if (!paginateDOM) {\r\n log(\"paginateDOM not found\", { color: \"red\" });\r\n return;\r\n }\r\n\r\n if (\r\n (window as any).scrollListener &&\r\n typeof (paginateDOM as any).removeEventListener === \"function\"\r\n ) {\r\n (paginateDOM as any).removeEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n }\r\n\r\n (window as any).scrollListener = (event: Event) => {\r\n const t = event.target as HTMLElement;\r\n localStorage.setItem(\r\n \"scroll\",\r\n JSON.stringify([t.scrollTop, t.scrollLeft, paginateElement]),\r\n );\r\n\r\n if (t.scrollHeight - t.scrollTop <= t.clientHeight + 200) {\r\n grab(path, {\r\n ...options,\r\n cache: false,\r\n [paginateKey as string]: (priorRequest?.currentPage || 0) + 1,\r\n });\r\n }\r\n };\r\n\r\n (paginateDOM as any).addEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n}\r\n","/**\r\n * @file core/request-executor.ts\r\n * @description Logic for preparing and executing API requests.\r\n * Handles both mock responses and real network fetch calls.\r\n */\r\n\r\nimport { GrabFunction, GrabMockHandler } from \"../common/types\";\r\nimport { wait } from \"../common/utils\";\r\n\r\n/**\r\n * Prepares the fetch parameters and URL.\r\n */\r\nexport function prepareFetchRequest(\r\n method: string,\r\n headers: any,\r\n body: any,\r\n params: any,\r\n cache: boolean,\r\n signal: AbortSignal\r\n): { fetchParams: RequestInit; paramsGETRequest: string } {\r\n const isBodyMethod = [\"POST\", \"PUT\", \"PATCH\"].includes(method);\r\n\r\n const fetchParams: RequestInit = {\r\n method,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n ...headers,\r\n },\r\n body: body || (isBodyMethod ? JSON.stringify(params) : null),\r\n redirect: \"follow\",\r\n cache: cache ? \"force-cache\" : \"no-store\",\r\n signal,\r\n };\r\n\r\n let paramsGETRequest = \"\";\r\n if (!isBodyMethod) {\r\n paramsGETRequest = (Object.keys(params).length ? \"?\" : \"\") + new URLSearchParams(params).toString();\r\n }\r\n\r\n return { fetchParams, paramsGETRequest };\r\n}\r\n\r\n/**\r\n * Executes the request, either via mock or actual fetch.\r\n */\r\nexport async function executeRequest(\r\n baseURL: string,\r\n path: string,\r\n paramsGETRequest: string,\r\n fetchParams: RequestInit,\r\n params: any,\r\n onStream: any\r\n): Promise<any> {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab) as GrabFunction;\r\n const mockHandler = target?.mock?.[path] as GrabMockHandler;\r\n const paramsAsText = JSON.stringify(params);\r\n\r\n if (mockHandler &&\r\n (!mockHandler.method || mockHandler.method === fetchParams.method) &&\r\n (!mockHandler.params || paramsAsText === JSON.stringify(mockHandler.params))) {\r\n await wait(mockHandler.delay || 0);\r\n return typeof mockHandler.response === \"function\" ? mockHandler.response(params) : mockHandler.response;\r\n }\r\n\r\n const fetchRes = await fetch(baseURL + path + paramsGETRequest, fetchParams).catch(e => {\r\n throw new Error(e.message);\r\n });\r\n\r\n if (!fetchRes.ok) throw new Error(`HTTP error: ${fetchRes.status} ${fetchRes.statusText}`);\r\n\r\n if (onStream) {\r\n await onStream(fetchRes.body);\r\n return null;\r\n }\r\n\r\n const type = fetchRes.headers.get(\"content-type\");\r\n return await (\r\n type\r\n ? type.includes(\"application/json\")\r\n ? fetchRes.json()\r\n : type.includes(\"application/pdf\") || type.includes(\"application/octet-stream\")\r\n ? fetchRes.blob()\r\n : fetchRes.text()\r\n : fetchRes.json()\r\n ).catch(e => {\r\n throw new Error(\"Error parsing response: \" + e);\r\n });\r\n}\r\n","import { grab as coreGrab } from \"./core/core\";\r\nimport { setupDevTools } from \"./devtools/devtools\";\r\nimport { GrabFunction, GrabOptions } from \"./common/types\";\r\nimport { log } from \"@grab-url/log\";\r\n\r\n// Add instance method to the core grab function\r\nconst grab: GrabFunction = coreGrab as any;\r\n\r\n/**\r\n * Creates a new instance of grab with default options\r\n * to apply to all requests made by this instance.\r\n *\r\n * @param defaults - Options for all requests made by this instance.\r\n * @returns A new grab() function using those default options.\r\n */\r\ngrab.instance = (defaults: Partial<GrabOptions> = {}) =>\r\n ((path: string, options: Partial<GrabOptions> = {}) =>\r\n coreGrab(path, { ...defaults, ...options })) as any;\r\n\r\n// Initialize global state\r\ngrab.log = [];\r\ngrab.mock = {};\r\ngrab.defaults = {};\r\n\r\n// Handle global registration for both Browser and Node.js environments\r\nif (typeof window !== \"undefined\") {\r\n // @ts-ignore\r\n window.log = log;\r\n window.grab = grab;\r\n\r\n // Setup visual dev tools\r\n setupDevTools();\r\n\r\n // Restore scroll position when page loads for infinite scroll persistence\r\n document.addEventListener(\"DOMContentLoaded\", () => {\r\n try {\r\n const scrollData = localStorage.getItem(\"scroll\");\r\n if (!scrollData) return;\r\n\r\n const [scrollTop, scrollLeft, paginateElement] = JSON.parse(scrollData);\r\n if (!scrollTop || !paginateElement) return;\r\n\r\n const el = document.querySelector(paginateElement);\r\n if (el) {\r\n el.scrollTop = scrollTop;\r\n el.scrollLeft = scrollLeft;\r\n }\r\n } catch (e) {\r\n console.warn(\"Failed to restore scroll position\", e);\r\n }\r\n });\r\n} else if (typeof globalThis !== \"undefined\") {\r\n (globalThis as any).log = log;\r\n (globalThis as any).grab = grab;\r\n}\r\n\r\n// Re-export core function and all types\r\nexport { grab };\r\nexport default grab;\r\n\r\nexport { log };\r\nexport * from \"./common/types\";\r\nexport * from \"./devtools/devtools\";\r\nexport * from \"./common/utils\";\r\n"],"names":["debouncer","async","func","wait","timeout","args","clearTimeout","setTimeout","s","Promise","res","buildUrl","baseURL","path","t","startsWith","finalBaseURL","finalPath","endsWith","slice","showAlert","msg","document","list","o","getElementById","body","appendChild","createElement","id","setAttribute","innerHTML","addEventListener","e","target","remove","closeBtn","onclick","setupDevTools","grab","window","log","key","ctrlKey","altKey","html","request","printJSONStructure","response","Date","lastFetchTime","toLocaleString","options","merged","defaults","globalThis","getMergedOptions","headers","responseOption","method","post","put","patch","cache","process","env","SERVER_API_URL","cancelOngoingIfNew","cancelNewIfOngoing","rateLimit","debug","infiniteScroll","logger","onRequest","onResponse","onError","onStream","params","urlConfig","initialized","resFunction","initializeResponse","grabLog","isLoading","flowResult","mergedOptions","debounce","repeat","repeatEvery","setDefaults","task","i","setInterval","handleFlowControl","regrabOnStale","regrabOnFocus","regrabOnNetwork","cacheForTime","regrab","visibilityState","handleRegrabEvents","updatedParams","priorRequest","updatedResponse","paramsAsText","paginateKey","paginateResult","JSON","stringify","find","pageNumber","currentPage","Object","keys","now","manageCacheAndPagination","length","paginateElement","paginateDOM","querySelector","scrollListener","removeEventListener","event","localStorage","setItem","scrollTop","scrollLeft","scrollHeight","clientHeight","color","setupInfiniteScroll","Error","controller","abort","AbortController","signal","AbortSignal","unshift","fetchParams","paramsGETRequest","isBodyMethod","includes","Accept","redirect","URLSearchParams","toString","prepareFetchRequest","modified","Array","isArray","startTime","mockHandler","mock","delay","fetchRes","fetch","catch","message","ok","status","statusText","type","get","json","blob","text","executeRequest","elapsedTime","Number","toFixed","data","mapResultToResponse","error","retryAttempts","resFn","coreGrab","instance","scrollData","getItem","parse","el","console","warn"],"mappings":"6IAcaA,EAAYC,MAAOC,EAAgBC,KAC5C,IAAIC,EACJ,OAAOH,kBAAmCI,GAKtCC,aAAaF,GACbA,EAAUG,WALIN,UACVK,aAAaF,SACPF,KAAQG,IAGUF,EAChC,GASSA,EAAQK,GAAc,IAAIC,QAASC,GAAQH,WAAWG,EAAS,IAAJF,GAAY,IASvEG,EAAW,CAACC,EAAiBC,KACtC,IAAIL,EAAKM,GAAcD,GAAME,WAAWD,KAAM,EAC1CE,EAAeJ,EACfK,EAAYJ,EAUhB,OARIA,GAAME,WAAW,UAAYF,GAAME,WAAW,UAC9CC,EAAe,GACPR,EAAE,MAASQ,EAAaE,SAAS,KAElCV,EAAE,MAAQQ,EAAaE,SAAS,OACvCD,EAAYJ,EAAKM,MAAM,IAFvBF,EAAY,IAAMJ,EAKf,CAAED,QAASI,EAAcH,KAAMI,ICvCnC,SAASG,EAAUC,GACxB,GAAwB,oBAAbC,SAA0B,OACrC,IACEC,EADEC,EAAIF,SAASG,eAAe,iBAGhC,IAAKD,EAAG,CACNA,EAAIF,SAASI,KAAKC,YAAYL,SAASM,cAAc,QACrDJ,EAAEK,GAAK,gBACPL,EAAEM,aACA,QACA,yHAEFN,EAAEO,UAAY,ybAKdP,EAAEQ,iBAAiB,QAAUC,GAAMA,EAAEC,QAAUV,GAAKA,EAAGW,UACvD,MAAMC,EAAWd,SAASG,eAAe,eACrCW,IAAUA,EAASC,QAAU,IAAMb,EAAGW,SAC5C,CAEAZ,EAAOD,SAASG,eAAe,cAC/BF,EAAKQ,WAAa,8EAA8EV,SAClG,CAMO,SAASiB,IACU,oBAAbhB,UAEXA,SAASU,iBAAiB,UAAYC,IAEpC,MAAMM,EAAQC,OAAeD,KAC7B,GAAKA,GAASA,EAAKE,KAEL,MAAVR,EAAES,KAAeT,EAAEU,SAAWV,EAAEW,OAAQ,CAC1C,IAAIC,EAAO,IACX,IAAA,IAASC,KAAWP,EAAKE,IACvBI,GAAQ,8GACSC,EAAQjC,uCACLkC,EAAAA,mBAAmBD,EAAQA,QAAS,EAAG,2CACtCC,EAAAA,mBACjBD,EAAQE,SACR,EACA,wCAEa,IAAIC,KAAKH,EAAQI,eAAeC,mCAGnD/B,EAAUyB,EACZ,GAEJ,CC1CA5C,eAAsBsC,EACpB1B,EACAuC,GAEA,MAAMC,ECpBD,SACHD,GAMA,MAAO,IAJ6B,oBAAXZ,OACnBA,QAAQD,MAAMe,SACbC,YAAoBhB,MAAMe,UAAY,CAAA,KAItCF,EAEX,CDSiBI,CAAiBJ,GAChC,IAAIK,QACFA,EACAT,SAAUU,EAAAC,OACVA,GAASN,EAAOO,KACZ,OACAP,EAAOQ,IACL,MACAR,EAAOS,MACL,QACA,OAAAC,MACRA,EAAA3D,QACAA,EAAU,GAAAQ,QACVA,EAA8B,oBAAZoD,SAA2BA,QAAQC,IAAIC,gBACvD,QAAAC,mBACFA,EAAAC,mBACAA,EAAAC,UACAA,EAAAC,MACAA,EAAAC,eACAA,EAAAC,OACAA,EAAS/B,EAAAA,IAAAA,UACTgC,EAAAC,WACAA,EAAAC,QACAA,EAAAC,SACAA,EAAAlD,KACAA,EAAAkC,KACAA,EAAAC,IACAA,EAAAC,MACAA,KACGe,GACDxB,EAEJ,MAAMyB,EAAYnE,EAASC,EAASC,GACpCD,EAAUkE,EAAUlE,QACpBC,EAAOiE,EAAUjE,KAEjB,MAAMkE,EE3DD,SAAuCrB,GAI1C,MAAMsB,EAAwC,mBAAnBtB,EAAgCA,EAAiB,KAE5E,MAAO,CAAEV,UADQU,GAAkBsB,EAAe,CAAA,EAAKtB,EACpCsB,cACvB,CFoDsBC,CAAmBvB,GACvC,IAAIV,EAAgB+B,EAAY/B,SAC5BgC,EAAcD,EAAYC,YAC9B,MAAM9C,EACc,oBAAXM,OAAyBA,OAAOD,KAAQgB,WAAmBhB,KAE9D2C,EAAUhD,GAAQO,KAAO,GAG3BuC,IAAwBA,EAAY,IAAKhC,EAAUmC,WAAW,IACrC,iBAAbnC,IAAuBA,EAASmC,WAAY,GAE5D,IACE,MAAMC,QCrDVnF,eACIY,EACAuC,EACAiC,EACA9C,GAEA,MAAM+C,SAAEA,EAAW,EAAAC,OAAGA,EAAS,cAAGC,EAAc,KAAAC,YAAMA,GAAc,GAAUJ,EAG9E,GAAIC,EAAW,EAAG,CACd,MAAMI,QAAa1F,EAAUC,gBACnBsC,EAAK1B,EAAM,IAAKuC,EAASkC,SAAU,KAC/B,IAAXA,GAEH,aADMI,IACEL,EAAcrC,UAAY,CAAA,CACtC,CAGA,GAAIuC,EAAS,EAAG,CACZ,IAAA,IAASI,EAAI,EAAGA,EAAIJ,EAAQI,UAClBpD,EAAK1B,EAAM,IAAKuC,EAASmC,OAAQ,IAE3C,OAAQF,EAAcrC,UAAY,CAAA,CACtC,CACA,GAAIwC,EAIA,OAHAI,YAAY3F,gBACFsC,EAAK1B,EAAM,IAAKuC,EAASmC,OAAQ,EAAGC,YAAa,QAC1C,IAAdA,GACKH,EAAcrC,UAAY,CAAA,EAItC,GAAIyC,EAAa,CACb,MAAMvD,EAA4B,oBAAXM,OAAyBA,OAAOD,KAAQgB,WAAmBhB,KAIlF,OAHIL,IACAA,EAAOoB,SAAW,IAAKF,EAASqC,iBAAa,IAEzCJ,EAAcrC,UAAY,CAAA,CACtC,CAEA,OAAO,IACX,CDY6B6C,CACvBhF,EACAuC,GAAW,CAAA,EACXC,EACAd,GAEF,GAAI6C,EAAY,OAAOA,GG5EpB,SACHvE,EACAuC,EACAiC,EACA9C,GAEA,GAAsB,oBAAXC,OAAwB,OAEnC,MAAMsD,cAAEA,EAAAC,cAAeA,EAAAC,gBAAeA,QAAiBjC,EAAAkC,aAAOA,EAAe,IAAOZ,EAC9Ea,EAASjG,eAAkBsC,EAAK1B,EAAM,IAAKuC,EAASW,OAAO,IAE7D+B,GAAiB/B,GAAOxD,WAAW2F,EAAQ,IAAOD,GAClDD,GAAiBxD,OAAOR,iBAAiB,SAAUkE,GACnDH,IACAvD,OAAOR,iBAAiB,QAASkE,GACjC5E,SAASU,iBAAiB,mBAAoB/B,UACT,YAA7BqB,SAAS6E,uBAAqCD,MAG9D,CH2DIE,CAAmBvF,EAAMuC,GAAW,CAAA,EAAIC,EAAQd,GAEhD,IACEsC,OAAQwB,EAAAC,aACRA,EACAtD,SAAUuD,EAAAC,aACVA,GItFC,SACH3F,EACAgE,EACAQ,EACArC,EACAgC,EACAE,GAEA,MAAMnB,MAAEA,EAAAkC,aAAOA,EAAA1B,eAAcA,GAAmBc,GACzCoB,EAAaC,GAAmBnC,GAA0B,GAE3DiC,EAAeG,KAAKC,UACtBH,EAAc,IAAK5B,EAAQ4B,CAACA,WAAsC5B,GAGtE,IAAIyB,EAAepB,EAAQ2B,KAAK5E,GAAKA,EAAEa,UAAY0D,GAAgBvE,EAAEpB,OAASA,GAE9E,GAAK4F,EAYE,CAEH,IAAIK,GAAcR,GAAcS,aAAe,GAAK,GAAKlC,IAAS4B,IAAgB,EAC7EH,EAIDA,EAAaS,YAAcD,GAH3B9D,EAAS0D,GAAkB,GAC3BI,EAAa,GAIjBjC,EAAS,IAAKA,EAAQ4B,CAACA,GAAcK,EACzC,KAtBkB,CAEd,IAAA,IAASpE,KAAOsE,OAAOC,KAAKjE,GAAWA,EAASN,QAAO,EAGvD,GAAIqB,GAASuC,GAActD,YACrBiD,GAAgBK,EAAapD,cAAgBD,KAAKiE,MAAQ,IAAOjB,GAAe,CAClF,IAAA,IAASvD,KAAOsE,OAAOC,KAAKX,EAAatD,UACrCA,EAASN,GAAO4D,EAAatD,SAASN,GAEtCsC,IAAahC,EAAWgC,EAAYhC,GAC5C,CACJ,CAYA,MAAO,CAAE6B,SAAQyB,eAActD,WAAUwD,eAC7C,CJ6CQW,CACFtG,EACAgE,EACAxB,EACAL,EACAgC,EACAE,GAOF,GALAL,EAASwB,EACTrD,EAAWuD,EK7FR,SACL1F,EACAuC,EACAmB,EACA+B,EACA/D,GAEA,GAAsB,oBAAXC,SAA2B+B,GAAgB6C,OAAQ,OAE9D,MAAOX,GAAeY,GAAmB9C,EACzC,QAA+B,IAApB8C,EAAiC,OAE5C,IAAIC,EACyB,iBAApBD,EACH/F,SAASiG,cAAcF,GACvBA,EAEDC,GAMF9E,OAAegF,gBACoC,mBAA5CF,EAAoBG,qBAE3BH,EAAoBG,oBACnB,SACCjF,OAAegF,gBAInBhF,OAAegF,eAAkBE,IAChC,MAAM5G,EAAI4G,EAAMxF,OAChByF,aAAaC,QACX,SACAjB,KAAKC,UAAU,CAAC9F,EAAE+G,UAAW/G,EAAEgH,WAAYT,KAGzCvG,EAAEiH,aAAejH,EAAE+G,WAAa/G,EAAEkH,aAAe,KACnDzF,EAAK1B,EAAM,IACNuC,EACHW,OAAO,EACP0C,CAACA,IAAyBH,GAAcS,aAAe,GAAK,KAKjEO,EAAoBtF,iBACnB,SACCQ,OAAegF,iBAhChB/E,EAAAA,IAAI,wBAAyB,CAAEwF,MAAO,OAkC1C,CL2CIC,CAAoBrH,EAAMuC,EAASmB,EAAgB+B,EAAc/D,GAG/D8B,EAAY,GACZiC,GAAcpD,cAAgBD,KAAKiE,MAAQ,IAAO7C,EAElD,MAAM,IAAI8D,MACR,iCAAiCtH,WAAcwD,wBAInD,GAAIiC,GAAc8B,WAChB,GAAIjE,EAAoBmC,EAAa8B,WAAWC,aAAA,GACvCjE,EAAoB,MAAO,CAAEe,WAAW,GAGnD,MAAMiD,EAAa,IAAIE,gBACvB,IAAIC,EAASH,EAAWG,OAGnBpE,GAAqD,mBAAxBqE,YAAYpI,UAC5CmI,EAASC,YAAYpI,QAAkB,IAAVA,IAG/B8E,EAAQuD,QAAQ,CACd5H,OACAiC,QAAS0D,EACTtD,cAAeD,KAAKiE,MACpBkB,eAGF,IAAIM,YAAEA,EAAAC,iBAAaA,GM9HhB,SACHhF,EACAF,EACA/B,EACAmD,EACAd,EACAwE,GAEA,MAAMK,EAAe,CAAC,OAAQ,MAAO,SAASC,SAASlF,GAEjD+E,EAA2B,CAC7B/E,SACAF,QAAS,CACL,eAAgB,mBAChBqF,OAAQ,sBACLrF,GAEP/B,KAAMA,IAASkH,EAAejC,KAAKC,UAAU/B,GAAU,MACvDkE,SAAU,SACVhF,MAAOA,EAAQ,cAAgB,WAC/BwE,UAGJ,IAAII,EAAmB,GAKvB,OAJKC,IACDD,GAAoB3B,OAAOC,KAAKpC,GAAQuC,OAAS,IAAM,IAAM,IAAI4B,gBAAgBnE,GAAQoE,YAGtF,CAAEP,cAAaC,mBAC1B,CNiG4CO,CACtCvF,EACAF,EACA/B,EACAmD,IACEd,EACFwE,GAGF,GAAyB,mBAAd9D,EAA0B,CACnC,MAAM0E,EAAW1E,EAAU5D,EAAMmC,EAAU6B,EAAQ6D,GAC/CU,MAAMC,QAAQF,MACftI,EAAMmC,EAAU6B,EAAQ6D,GAAeS,EAC5C,CAEA,MAAMG,MAAgBrG,KAChBvC,QM5GVT,eACIW,EACAC,EACA8H,EACAD,EACA7D,EACAD,GAEA,MAAM1C,EAA4B,oBAAXM,OAAyBA,OAAOD,KAAQgB,WAAmBhB,KAC5EgH,EAAcrH,GAAQsH,OAAO3I,GAC7B2F,EAAeG,KAAKC,UAAU/B,GAEpC,GAAI0E,KACEA,EAAY5F,QAAU4F,EAAY5F,SAAW+E,EAAY/E,WACzD4F,EAAY1E,QAAU2B,IAAiBG,KAAKC,UAAU2C,EAAY1E,SAEpE,aADM1E,EAAKoJ,EAAYE,OAAS,GACO,mBAAzBF,EAAYvG,SAA0BuG,EAAYvG,SAAS6B,GAAU0E,EAAYvG,SAGnG,MAAM0G,QAAiBC,MAAM/I,EAAUC,EAAO8H,EAAkBD,GAAakB,MAAM3H,IAC/E,MAAM,IAAIkG,MAAMlG,EAAE4H,WAGtB,IAAKH,EAASI,GAAI,MAAM,IAAI3B,MAAM,eAAeuB,EAASK,UAAUL,EAASM,cAE7E,GAAIpF,EAEA,aADMA,EAAS8E,EAAShI,MACjB,KAGX,MAAMuI,EAAOP,EAASjG,QAAQyG,IAAI,gBAClC,aACID,EACMA,EAAKpB,SAAS,oBACVa,EAASS,OACTF,EAAKpB,SAAS,oBAAsBoB,EAAKpB,SAAS,4BAC9Ca,EAASU,OACTV,EAASW,OACjBX,EAASS,QACjBP,MAAM3H,IACJ,MAAM,IAAIkG,MAAM,2BAA6BlG,IAErD,CNkEsBqI,CAChB1J,EACAC,EACA8H,EACAD,EACA7D,EACAD,GAQF,GAJII,EACFhC,EAAWgC,EAAY,IAAKhC,EAAUmC,mBACX,iBAAbnC,UAA8BA,EAASmC,UAE7B,mBAAfT,EAA2B,CACpC,MAAMyE,EAAWzE,EAAW7D,EAAMmC,EAAU6B,EAAQ6D,GAChDU,MAAMC,QAAQF,MACftI,EAAMmC,EAAU6B,EAAQ6D,GAAeS,EAC5C,CAEA,MAAMoB,IACHC,OAAO,IAAIvH,MAAUuH,OAAOlB,IAC7B,KACAmB,QAAQ,GACNnG,GACFE,EACE,QAAQ5D,EAAUC,EAAO8H,MAAqBhC,KAAKC,UAAUxD,EAAS,KAAM,aAAamH,iBAA2BxH,EAAAA,mBAAmBrC,MAI3I,MAAM,CAAGgG,GAAmBnC,GAA0B,GAMtD,OALAvB,EEpKG,SAA6BtC,EAAUsC,EAAegC,EAAkB0B,GAC3E,GAAmB,iBAARhG,GAA4B,OAARA,EAAc,CACzC,IAAA,IAASgC,KAAOsE,OAAOC,KAAKvG,GACxBsC,EAASN,GAAQgE,IAAmBhE,GAAO0G,MAAMC,QAAQrG,EAASN,IAC5D,IAAIM,EAASN,MAAShC,EAAIgC,IAC1BhC,EAAIgC,GAEdM,EAAS0H,KAAOhK,CACpB,MACQsE,EACAhC,EAAWgC,EAAY,CAAE0F,KAAMhK,KAAQA,IACZ,iBAAbsC,IACdA,EAAS0H,KAAOhK,GAGxB,OAAOsC,CACX,CFoJe2H,CAAoBjK,EAAKsC,EAAUgC,EAAa0B,GAEvDxB,EAAQ,KAAIA,EAAQ,GAAGlC,SAAWA,GAClCgC,IAAahC,EAAWgC,EAAYhC,IAEjCA,CACT,OAAS4H,GAIP,GAHuB,mBAAZjG,GACTA,EAAQiG,EAAMf,QAASjJ,EAAUC,EAAMgE,GAErCxB,EAAOwH,eAAiBxH,EAAOwH,cAAgB,EACjD,aAAatI,EAAK1B,EAAM,IACnBuC,EACHyH,gBAAiBxH,EAAOwH,iBAIvBD,EAAMf,QAAQhB,SAAS,WAAavE,IACvCE,EAAO,UAAUoG,EAAMf,iBAAiBjJ,EAAUC,MAAU,CAC1DoH,MAAO,QAEe,oBAAb3G,UAA0BF,EAAUwJ,EAAMf,UAGvD7G,EAAWA,GAAY,CAAA,EACvBA,EAAS4H,MAAQA,EAAMf,QACvB,MAAMiB,EAAkC,mBAAnBpH,EAAgCA,EAAiB,KAOtE,OANIoH,GACF9H,EAAS0H,KAAOI,EAAM,CAAE3F,iBAAsByF,MAAOA,EAAMf,UAC3D7G,EAAWA,EAAS0H,aAEb1H,EAASmC,UAEXnC,CACT,CACF,COtNA,MAAMT,EAAqBwI,EAS3BxI,EAAKyI,SAAW,CAAC1H,EAAiC,CAAA,IAAA,CAC9CzC,EAAcuC,EAAgC,CAAA,IAC9C2H,EAASlK,EAAM,IAAKyC,KAAaF,IAGrCb,EAAKE,IAAM,GACXF,EAAKiH,KAAO,CAAA,EACZjH,EAAKe,SAAW,CAAA,EAGM,oBAAXd,QAETA,OAAOC,IAAMA,EAAAA,IACbD,OAAOD,KAAOA,EAGdD,IAGAhB,SAASU,iBAAiB,mBAAoB,KAC5C,IACE,MAAMiJ,EAAatD,aAAauD,QAAQ,UACxC,IAAKD,EAAY,OAEjB,MAAOpD,EAAWC,EAAYT,GAAmBV,KAAKwE,MAAMF,GAC5D,IAAKpD,IAAcR,EAAiB,OAEpC,MAAM+D,EAAK9J,SAASiG,cAAcF,GAC9B+D,IACFA,EAAGvD,UAAYA,EACfuD,EAAGtD,WAAaA,EAEpB,OAAS7F,GACPoJ,QAAQC,KAAK,oCAAqCrJ,EACpD,KAE6B,oBAAfsB,aACfA,WAAmBd,IAAMA,EAAAA,IACzBc,WAAmBhB,KAAOA"}
|
|
1
|
+
{"version":3,"file":"grab-api.cjs.js","sources":["../packages/grab-api/common/utils.ts","../packages/grab-api/devtools/devtools.ts","../packages/grab-api/core/core.ts","../packages/grab-api/core/flow-control.ts","../packages/grab-api/response/response-handler.ts","../packages/grab-api/core/regrab-events.ts","../packages/grab-api/core/cache-pagination.ts","../packages/grab-api/response/infinite-scroll.ts","../packages/grab-api/core/request-executor.ts","../packages/grab-api/index.ts"],"sourcesContent":["/**\r\n * @file common/utils.ts\r\n * @description Common utility functions used across the Grab API.\r\n * Includes helpers for debouncing, URL building, and asynchronous waiting.\r\n */\r\n\r\n/**\r\n * Delays execution so that future calls may override and only executes the last one.\r\n * Useful for search inputs or other high-frequency events.\r\n * \r\n * @param func - The function to debounce.\r\n * @param wait - Time to wait in milliseconds.\r\n * @returns A debounced version of the function.\r\n */\r\nexport const debouncer = async (func: Function, wait: number) => {\r\n let timeout: any;\r\n return async function executedFunction(...args: any[]) {\r\n const later = async () => {\r\n clearTimeout(timeout);\r\n await func(...args);\r\n };\r\n clearTimeout(timeout);\r\n timeout = setTimeout(later, wait);\r\n };\r\n};\r\n\r\n/**\r\n * Helper function to wait for a specified number of seconds.\r\n * \r\n * @param s - Seconds to wait.\r\n * @returns A promise that resolves after the timeout.\r\n */\r\nexport const wait = (s: number) => new Promise((res) => setTimeout(res, s * 1000 || 0));\r\n\r\n/**\r\n * Normalizes and builds a URL from a base and relative path.\r\n * \r\n * @param baseURL - The base API URL.\r\n * @param path - The specific endpoint path.\r\n * @returns An object containing the combined URL and updated baseURL/path if needed.\r\n */\r\nexport const buildUrl = (baseURL: string, path: string) => {\r\n let s = (t: string) => path?.startsWith(t) || false;\r\n let finalBaseURL = baseURL;\r\n let finalPath = path;\r\n\r\n if (path?.startsWith(\"http:\") || path?.startsWith(\"https:\")) {\r\n finalBaseURL = \"\";\r\n } else if (!s(\"/\") && !finalBaseURL.endsWith(\"/\")) {\r\n finalPath = \"/\" + path;\r\n } else if (s(\"/\") && finalBaseURL.endsWith(\"/\")) {\r\n finalPath = path.slice(1);\r\n }\r\n\r\n return { baseURL: finalBaseURL, path: finalPath };\r\n};\r\n","/**\r\n * @file ui/devtools.ts\r\n * @description Visual development tools for the Grab API.\r\n * Includes a modal overlay for inspect request history (Ctrl+Alt+I).\r\n */\r\n\r\nimport { printJSONStructure } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Shows a message in a modal overlay with a scrollable message stack.\r\n * Easier to dismiss than native alert() and does not block window execution.\r\n *\r\n * @param msg - The message or HTML content to display.\r\n */\r\nexport function showAlert(msg: string) {\r\n if (typeof document === \"undefined\") return;\r\n let o = document.getElementById(\"alert-overlay\"),\r\n list: HTMLElement;\r\n\r\n if (!o) {\r\n o = document.body.appendChild(document.createElement(\"div\"));\r\n o.id = \"alert-overlay\";\r\n o.setAttribute(\r\n \"style\",\r\n \"position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\",\r\n );\r\n o.innerHTML = `<div id=\"alert-box\" style=\"background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:220px;max-height:80vh;position:relative;display:flex;flex-direction:column;\">\r\n <button id=\"close-alert\" style=\"position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;\">×</button>\r\n <div id=\"alert-list\" style=\"overflow:auto;flex:1;\"></div>\r\n </div>`;\r\n\r\n o.addEventListener(\"click\", (e) => e.target == o && o!.remove());\r\n const closeBtn = document.getElementById(\"close-alert\");\r\n if (closeBtn) closeBtn.onclick = () => o!.remove();\r\n }\r\n\r\n list = document.getElementById(\"alert-list\")!;\r\n list.innerHTML += `<div style=\"border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;\">${msg}</div>`;\r\n}\r\n\r\n/**\r\n * Sets up development tools for debugging API requests.\r\n * Adds a keyboard shortcut (Ctrl+Alt+I) to toggle a modal showing the request history from `grab.log`.\r\n */\r\nexport function setupDevTools() {\r\n if (typeof document === \"undefined\") return;\r\n\r\n document.addEventListener(\"keydown\", (e) => {\r\n // Check for global grab on window\r\n const grab = (window as any).grab as GrabFunction;\r\n if (!grab || !grab.log) return;\r\n\r\n if (e.key === \"i\" && e.ctrlKey && e.altKey) {\r\n let html = \" \";\r\n for (let request of grab.log) {\r\n html += `<div style=\"margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;\">\r\n <b>Path:</b> ${request.path}<br>\r\n <b>Request:</b> ${printJSONStructure(request.request, 0, \"html\")}<br>\r\n <b>Response:</b> ${printJSONStructure(\r\n request.response,\r\n 0,\r\n \"html\",\r\n )}<br> \r\n <b>Time:</b> ${new Date(request.lastFetchTime).toLocaleString()}\r\n </div>`;\r\n }\r\n showAlert(html);\r\n }\r\n });\r\n}\r\n","/**\r\n * @file core/core.ts\r\n * @description Main orchestration logic for the Grab API.\r\n * Defines the primary 'grab' function and coordinates request flow,\r\n * including option merging, caching, and execution.\r\n */\r\n\r\nimport { printJSONStructure, log } from \"@grab-url/log\";\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { buildUrl } from \"../common/utils\";\r\nimport { showAlert } from \"../devtools/devtools\";\r\n\r\nimport { getMergedOptions, handleFlowControl } from \"./flow-control\";\r\nimport { handleRegrabEvents } from \"./regrab-events\";\r\nimport {\r\n initializeResponse,\r\n mapResultToResponse,\r\n} from \"../response/response-handler\";\r\nimport { setupInfiniteScroll } from \"../response/infinite-scroll\";\r\nimport { manageCacheAndPagination } from \"./cache-pagination\";\r\nimport { prepareFetchRequest, executeRequest } from \"./request-executor\";\r\n\r\n/**\r\n * ### GRAB: Generate Request to API from Browser\r\n *\r\n * The core function for making API requests. Handles JSON conversion,\r\n * loading states, caching, pagination, debouncing, and more.\r\n */\r\nexport async function grab<TResponse = any, TParams = any>(\r\n path: string,\r\n options?: GrabOptions<TResponse, TParams>,\r\n): Promise<GrabResponse<TResponse>> {\r\n const merged = getMergedOptions(options);\r\n let {\r\n headers,\r\n response: responseOption,\r\n method = merged.post\r\n ? \"POST\"\r\n : merged.put\r\n ? \"PUT\"\r\n : merged.patch\r\n ? \"PATCH\"\r\n : \"GET\",\r\n cache,\r\n timeout = 30,\r\n baseURL = (typeof process !== \"undefined\" && process.env.SERVER_API_URL) ||\r\n \"/api/\",\r\n cancelOngoingIfNew,\r\n cancelNewIfOngoing,\r\n rateLimit,\r\n debug,\r\n infiniteScroll,\r\n logger = log,\r\n onRequest,\r\n onResponse,\r\n onError,\r\n onStream,\r\n body,\r\n post,\r\n put,\r\n patch,\r\n ...params\r\n } = merged;\r\n\r\n const urlConfig = buildUrl(baseURL, path);\r\n baseURL = urlConfig.baseURL;\r\n path = urlConfig.path;\r\n\r\n const initialized = initializeResponse(responseOption);\r\n let response: any = initialized.response;\r\n let resFunction = initialized.resFunction;\r\n const target = (\r\n typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab\r\n ) as GrabFunction;\r\n const grabLog = target?.log || [];\r\n\r\n // Set loading state synchronously before any await\r\n if (resFunction) response = resFunction({ ...response, isLoading: true });\r\n else if (typeof response === \"object\") response.isLoading = true;\r\n\r\n try {\r\n const flowResult = await handleFlowControl(\r\n path,\r\n options || {},\r\n merged,\r\n grab as unknown as GrabFunction,\r\n );\r\n if (flowResult) return flowResult as any;\r\n\r\n handleRegrabEvents(path, options || {}, merged, grab as unknown as GrabFunction);\r\n\r\n let {\r\n params: updatedParams,\r\n priorRequest,\r\n response: updatedResponse,\r\n paramsAsText,\r\n } = manageCacheAndPagination(\r\n path,\r\n params,\r\n merged,\r\n response,\r\n resFunction,\r\n grabLog,\r\n );\r\n params = updatedParams;\r\n response = updatedResponse;\r\n\r\n setupInfiniteScroll(path, options, infiniteScroll, priorRequest, grab as unknown as GrabFunction);\r\n\r\n if (\r\n rateLimit > 0 &&\r\n priorRequest?.lastFetchTime > Date.now() - 1000 * rateLimit\r\n ) {\r\n throw new Error(\r\n `Fetch rate limit exceeded for ${path}. Wait ${rateLimit}s between requests.`,\r\n );\r\n }\r\n\r\n if (priorRequest?.controller) {\r\n if (cancelOngoingIfNew) priorRequest.controller.abort();\r\n else if (cancelNewIfOngoing) return { isLoading: true } as GrabResponse;\r\n }\r\n\r\n const controller = new AbortController();\r\n let signal = controller.signal;\r\n\r\n // Add timeout if requested (and signal not already used for cancellation)\r\n if (!cancelOngoingIfNew && typeof AbortSignal.timeout === \"function\") {\r\n signal = AbortSignal.timeout(timeout * 1000);\r\n }\r\n\r\n grabLog.unshift({\r\n path,\r\n request: paramsAsText,\r\n lastFetchTime: Date.now(),\r\n controller,\r\n });\r\n\r\n let { fetchParams, paramsGETRequest } = prepareFetchRequest(\r\n method,\r\n headers,\r\n body,\r\n params,\r\n !!cache,\r\n signal,\r\n );\r\n\r\n if (typeof onRequest === \"function\") {\r\n const modified = onRequest(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const startTime = new Date();\r\n const res = await executeRequest(\r\n baseURL,\r\n path,\r\n paramsGETRequest,\r\n fetchParams,\r\n params,\r\n onStream,\r\n );\r\n\r\n // Clear loading state\r\n if (resFunction)\r\n response = resFunction({ ...response, isLoading: undefined });\r\n else if (typeof response === \"object\") delete response.isLoading;\r\n\r\n if (typeof onResponse === \"function\") {\r\n const modified = onResponse(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const elapsedTime = (\r\n (Number(new Date()) - Number(startTime)) /\r\n 1000\r\n ).toFixed(1);\r\n if (debug) {\r\n logger(\r\n `Path:${baseURL + path + paramsGETRequest}\\n${JSON.stringify(options, null, 2)}\\nTime: ${elapsedTime}s\\nResponse: ${printJSONStructure(res)}`,\r\n );\r\n }\r\n\r\n const [, paginateResult] = (infiniteScroll as any) || [];\r\n response = mapResultToResponse(res, response, resFunction, paginateResult);\r\n\r\n if (grabLog[0]) grabLog[0].response = response;\r\n if (resFunction) response = resFunction(response);\r\n\r\n return response as any;\r\n } catch (error: any) {\r\n if (typeof onError === \"function\")\r\n onError(error.message, baseURL + path, params);\r\n\r\n if (merged.retryAttempts && merged.retryAttempts > 0) {\r\n return await grab(path, {\r\n ...options,\r\n retryAttempts: --merged.retryAttempts,\r\n });\r\n }\r\n\r\n if (!error.message.includes(\"signal\") && debug) {\r\n logger(`Error: ${error.message}\\nPath:${baseURL + path}\\n`, {\r\n color: \"red\",\r\n });\r\n if (typeof document !== \"undefined\") showAlert(error.message);\r\n }\r\n\r\n response = response || {};\r\n response.error = error.message;\r\n const resFn = typeof responseOption === \"function\" ? responseOption : null;\r\n if (resFn) {\r\n response.data = resFn({ isLoading: undefined, error: error.message });\r\n response = response.data;\r\n } else {\r\n delete response.isLoading;\r\n }\r\n return response as any;\r\n }\r\n}\r\n","/**\r\n * @file core/flow-control.ts\r\n * @description Logic for managing request flow and option merging.\r\n * Handles debouncing, request repetition, and default option application.\r\n */\r\n\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { debouncer } from \"../common/utils\";\r\n\r\n/**\r\n * Merges provided options with global defaults.\r\n */\r\nexport function getMergedOptions<TResponse, TParams>(\r\n options?: GrabOptions<TResponse, TParams>\r\n): GrabOptions<TResponse, TParams> & { [key: string]: any } {\r\n const defaults = (typeof window !== \"undefined\"\r\n ? window?.grab?.defaults\r\n : (globalThis as any)?.grab?.defaults || {}) as GrabOptions<TResponse, TParams>;\r\n\r\n return {\r\n ...defaults,\r\n ...options,\r\n };\r\n}\r\n\r\n/**\r\n * Handles flow control logic like debouncing and repeating requests.\r\n */\r\nexport async function handleFlowControl<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): Promise<GrabResponse<TResponse> | null> {\r\n const { debounce = 0, repeat = 0, repeatEvery = null, setDefaults = false } = mergedOptions;\r\n\r\n // Handle debounce\r\n if (debounce > 0) {\r\n const task = await debouncer(async () => {\r\n await grab(path, { ...options, debounce: 0 });\r\n }, debounce * 1000);\r\n await task();\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Handle repeat options\r\n if (repeat > 1) {\r\n for (let i = 0; i < repeat; i++) {\r\n await grab(path, { ...options, repeat: 0 });\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n if (repeatEvery) {\r\n setInterval(async () => {\r\n await grab(path, { ...options, repeat: 0, repeatEvery: null });\r\n }, repeatEvery * 1000);\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Store defaults if requested\r\n if (setDefaults) {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab);\r\n if (target) {\r\n target.defaults = { ...options, setDefaults: undefined };\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n return null;\r\n}\r\n","/**\r\n * @file response/response-handler.ts\r\n * @description Logic for handling API responses.\r\n * Includes functions for initializing response objects and mapping results.\r\n */\r\n\r\n/**\r\n * Initializes the response object and result function.\r\n */\r\nexport function initializeResponse<TResponse>(responseOption: any): {\r\n response: any;\r\n resFunction: ((data: any) => any) | null;\r\n} {\r\n const resFunction = typeof responseOption === \"function\" ? responseOption : null;\r\n let response = (!responseOption || resFunction) ? {} : responseOption;\r\n return { response, resFunction };\r\n}\r\n\r\n/**\r\n * Maps the raw result onto the response object.\r\n */\r\nexport function mapResultToResponse(res: any, response: any, resFunction: any, paginateResult: string | null): any {\r\n if (typeof res === \"object\" && res !== null) {\r\n for (let key of Object.keys(res)) {\r\n response[key] = (paginateResult === key && Array.isArray(response[key]))\r\n ? [...response[key], ...res[key]]\r\n : res[key];\r\n }\r\n response.data = res;\r\n } else {\r\n if (resFunction) {\r\n response = resFunction({ data: res, ...res });\r\n } else if (typeof response === \"object\") {\r\n response.data = res;\r\n }\r\n }\r\n return response;\r\n}\r\n","/**\r\n * @file core/regrab-events.ts\r\n * @description Event listeners for automatic data re-fetching.\r\n * Handles stale cache, window focus, and network status changes.\r\n */\r\n\r\nimport { GrabOptions, GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Sets up event listeners for regrabbing data on stale, focus, or network changes.\r\n */\r\nexport function handleRegrabEvents<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const { regrabOnStale, regrabOnFocus, regrabOnNetwork, cache, cacheForTime = 60 } = mergedOptions;\r\n const regrab = async () => await grab(path, { ...options, cache: false });\r\n\r\n if (regrabOnStale && cache) setTimeout(regrab, 1000 * cacheForTime);\r\n if (regrabOnNetwork) window.addEventListener(\"online\", regrab);\r\n if (regrabOnFocus) {\r\n window.addEventListener(\"focus\", regrab);\r\n document.addEventListener(\"visibilitychange\", async () => {\r\n if (document.visibilityState === \"visible\") await regrab();\r\n });\r\n }\r\n}\r\n","/**\r\n * @file core/cache-pagination.ts\r\n * @description Cache management and pagination state tracking.\r\n * Determines cache hits and updates current page numbers for infinite scroll.\r\n */\r\n\r\n/**\r\n * Manages cache hits and pagination state updates.\r\n */\r\nexport function manageCacheAndPagination(\r\n path: string,\r\n params: any,\r\n mergedOptions: any,\r\n response: any,\r\n resFunction: any,\r\n grabLog: any[]\r\n): { params: any; priorRequest: any; response: any; paramsAsText: string } {\r\n const { cache, cacheForTime, infiniteScroll } = mergedOptions;\r\n const [paginateKey, paginateResult] = (infiniteScroll as any) || [];\r\n\r\n const paramsAsText = JSON.stringify(\r\n paginateKey ? { ...params, [paginateKey as string]: undefined } : params\r\n );\r\n\r\n let priorRequest = grabLog.find(e => e.request === paramsAsText && e.path === path);\r\n\r\n if (!paginateKey) {\r\n // Clear response for fresh request\r\n for (let key of Object.keys(response)) response[key] = undefined;\r\n\r\n // Cache hit check\r\n if (cache && priorRequest?.response &&\r\n (!cacheForTime || priorRequest.lastFetchTime > Date.now() - 1000 * cacheForTime)) {\r\n for (let key of Object.keys(priorRequest.response)) {\r\n response[key] = priorRequest.response[key];\r\n }\r\n if (resFunction) response = resFunction(response);\r\n }\r\n } else {\r\n // Pagination logic\r\n let pageNumber = (priorRequest?.currentPage || 0) + 1 || params?.[paginateKey] || 1;\r\n if (!priorRequest) {\r\n response[paginateResult] = [];\r\n pageNumber = 1;\r\n } else {\r\n priorRequest.currentPage = pageNumber;\r\n }\r\n params = { ...params, [paginateKey]: pageNumber };\r\n }\r\n\r\n return { params, priorRequest, response, paramsAsText };\r\n}\r\n","/**\r\n * @file response/infinite-scroll.ts\r\n * @description Infinite scroll pagination logic.\r\n * Manages scroll event listeners and triggers automatic next-page fetching.\r\n */\r\n\r\nimport { log } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Configures infinite scroll listener.\r\n */\r\nexport function setupInfiniteScroll(\r\n path: string,\r\n options: any,\r\n infiniteScroll: any,\r\n priorRequest: any,\r\n grab: GrabFunction,\r\n): void {\r\n if (typeof window === \"undefined\" || !infiniteScroll?.length) return;\r\n\r\n const [paginateKey, , paginateElement] = infiniteScroll;\r\n if (typeof paginateElement === \"undefined\") return;\r\n\r\n let paginateDOM =\r\n typeof paginateElement === \"string\"\r\n ? document.querySelector(paginateElement)\r\n : paginateElement;\r\n\r\n if (!paginateDOM) {\r\n log(\"paginateDOM not found\", { color: \"red\" });\r\n return;\r\n }\r\n\r\n if (\r\n (window as any).scrollListener &&\r\n typeof (paginateDOM as any).removeEventListener === \"function\"\r\n ) {\r\n (paginateDOM as any).removeEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n }\r\n\r\n (window as any).scrollListener = (event: Event) => {\r\n const t = event.target as HTMLElement;\r\n localStorage.setItem(\r\n \"scroll\",\r\n JSON.stringify([t.scrollTop, t.scrollLeft, paginateElement]),\r\n );\r\n\r\n if (t.scrollHeight - t.scrollTop <= t.clientHeight + 200) {\r\n grab(path, {\r\n ...options,\r\n cache: false,\r\n [paginateKey as string]: (priorRequest?.currentPage || 0) + 1,\r\n });\r\n }\r\n };\r\n\r\n (paginateDOM as any).addEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n}\r\n","/**\r\n * @file core/request-executor.ts\r\n * @description Logic for preparing and executing API requests.\r\n * Handles both mock responses and real network fetch calls.\r\n */\r\n\r\nimport { GrabFunction, GrabMockHandler } from \"../common/types\";\r\nimport { wait } from \"../common/utils\";\r\n\r\n/**\r\n * Prepares the fetch parameters and URL.\r\n */\r\nexport function prepareFetchRequest(\r\n method: string,\r\n headers: any,\r\n body: any,\r\n params: any,\r\n cache: boolean,\r\n signal: AbortSignal\r\n): { fetchParams: RequestInit; paramsGETRequest: string } {\r\n const isBodyMethod = [\"POST\", \"PUT\", \"PATCH\"].includes(method);\r\n\r\n const fetchParams: RequestInit = {\r\n method,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n ...headers,\r\n },\r\n body: body || (isBodyMethod ? JSON.stringify(params) : null),\r\n redirect: \"follow\",\r\n cache: cache ? \"force-cache\" : \"no-store\",\r\n signal,\r\n };\r\n\r\n let paramsGETRequest = \"\";\r\n if (!isBodyMethod) {\r\n paramsGETRequest = (Object.keys(params).length ? \"?\" : \"\") + new URLSearchParams(params).toString();\r\n }\r\n\r\n return { fetchParams, paramsGETRequest };\r\n}\r\n\r\n/**\r\n * Executes the request, either via mock or actual fetch.\r\n */\r\nexport async function executeRequest(\r\n baseURL: string,\r\n path: string,\r\n paramsGETRequest: string,\r\n fetchParams: RequestInit,\r\n params: any,\r\n onStream: any\r\n): Promise<any> {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab) as GrabFunction;\r\n const mockHandler = target?.mock?.[path] as GrabMockHandler;\r\n const paramsAsText = JSON.stringify(params);\r\n\r\n if (mockHandler &&\r\n (!mockHandler.method || mockHandler.method === fetchParams.method) &&\r\n (!mockHandler.params || paramsAsText === JSON.stringify(mockHandler.params))) {\r\n await wait(mockHandler.delay || 0);\r\n return typeof mockHandler.response === \"function\" ? mockHandler.response(params) : mockHandler.response;\r\n }\r\n\r\n const fetchRes = await fetch(baseURL + path + paramsGETRequest, fetchParams).catch(e => {\r\n throw new Error(e.message);\r\n });\r\n\r\n if (!fetchRes.ok) throw new Error(`HTTP error: ${fetchRes.status} ${fetchRes.statusText}`);\r\n\r\n if (onStream) {\r\n await onStream(fetchRes.body);\r\n return null;\r\n }\r\n\r\n const type = fetchRes.headers.get(\"content-type\");\r\n return await (\r\n type\r\n ? type.includes(\"application/json\")\r\n ? fetchRes.json()\r\n : type.includes(\"application/pdf\") || type.includes(\"application/octet-stream\")\r\n ? fetchRes.blob()\r\n : fetchRes.text()\r\n : fetchRes.json()\r\n ).catch(e => {\r\n throw new Error(\"Error parsing response: \" + e);\r\n });\r\n}\r\n","import { grab as coreGrab } from \"./core/core\";\r\nimport { setupDevTools } from \"./devtools/devtools\";\r\nimport { GrabFunction, GrabOptions } from \"./common/types\";\r\nimport { log } from \"@grab-url/log\";\r\n\r\n// Add instance method to the core grab function\r\nconst grab: GrabFunction = coreGrab as any;\r\n\r\n/**\r\n * Creates a new instance of grab with default options\r\n * to apply to all requests made by this instance.\r\n *\r\n * @param defaults - Options for all requests made by this instance.\r\n * @returns A new grab() function using those default options.\r\n */\r\ngrab.instance = (defaults: Partial<GrabOptions> = {}) =>\r\n ((path: string, options: Partial<GrabOptions> = {}) =>\r\n coreGrab(path, { ...defaults, ...options })) as any;\r\n\r\n// Initialize global state\r\ngrab.log = [];\r\ngrab.mock = {};\r\ngrab.defaults = {};\r\n\r\n// Handle global registration for both Browser and Node.js environments\r\nif (typeof window !== \"undefined\") {\r\n // @ts-ignore\r\n window.log = log;\r\n window.grab = grab;\r\n\r\n // Setup visual dev tools\r\n setupDevTools();\r\n\r\n // Restore scroll position when page loads for infinite scroll persistence\r\n document.addEventListener(\"DOMContentLoaded\", () => {\r\n try {\r\n const scrollData = localStorage.getItem(\"scroll\");\r\n if (!scrollData) return;\r\n\r\n const [scrollTop, scrollLeft, paginateElement] = JSON.parse(scrollData);\r\n if (!scrollTop || !paginateElement) return;\r\n\r\n const el = document.querySelector(paginateElement);\r\n if (el) {\r\n el.scrollTop = scrollTop;\r\n el.scrollLeft = scrollLeft;\r\n }\r\n } catch (e) {\r\n console.warn(\"Failed to restore scroll position\", e);\r\n }\r\n });\r\n} else if (typeof globalThis !== \"undefined\") {\r\n (globalThis as any).log = log;\r\n (globalThis as any).grab = grab;\r\n}\r\n\r\n// Re-export core function and all types\r\nexport { grab };\r\nexport default grab;\r\n\r\nexport { log };\r\nexport * from \"./common/types\";\r\nexport * from \"./devtools/devtools\";\r\nexport * from \"./common/utils\";\r\n"],"names":["debouncer","async","func","wait","timeout","args","clearTimeout","setTimeout","s","Promise","res","buildUrl","baseURL","path","t","startsWith","finalBaseURL","finalPath","endsWith","slice","showAlert","msg","document","list","o","getElementById","body","appendChild","createElement","id","setAttribute","innerHTML","addEventListener","e","target","remove","closeBtn","onclick","setupDevTools","grab","window","log","key","ctrlKey","altKey","html","request","printJSONStructure","response","Date","lastFetchTime","toLocaleString","options","merged","defaults","globalThis","getMergedOptions","headers","responseOption","method","post","put","patch","cache","process","env","SERVER_API_URL","cancelOngoingIfNew","cancelNewIfOngoing","rateLimit","debug","infiniteScroll","logger","onRequest","onResponse","onError","onStream","params","urlConfig","initialized","resFunction","initializeResponse","grabLog","isLoading","flowResult","mergedOptions","debounce","repeat","repeatEvery","setDefaults","task","i","setInterval","handleFlowControl","regrabOnStale","regrabOnFocus","regrabOnNetwork","cacheForTime","regrab","visibilityState","handleRegrabEvents","updatedParams","priorRequest","updatedResponse","paramsAsText","paginateKey","paginateResult","JSON","stringify","find","pageNumber","currentPage","Object","keys","now","manageCacheAndPagination","length","paginateElement","paginateDOM","querySelector","scrollListener","removeEventListener","event","localStorage","setItem","scrollTop","scrollLeft","scrollHeight","clientHeight","color","setupInfiniteScroll","Error","controller","abort","AbortController","signal","AbortSignal","unshift","fetchParams","paramsGETRequest","isBodyMethod","includes","Accept","redirect","URLSearchParams","toString","prepareFetchRequest","modified","Array","isArray","startTime","mockHandler","mock","delay","fetchRes","fetch","catch","message","ok","status","statusText","type","get","json","blob","text","executeRequest","elapsedTime","Number","toFixed","data","mapResultToResponse","error","retryAttempts","resFn","coreGrab","instance","scrollData","getItem","parse","el","console","warn"],"mappings":"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"}
|
package/dist/grab-api.es.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{printJSONStructure as e,log as t}from"
|
|
1
|
+
import{printJSONStructure as e,log as t}from"./log.es.js";const n=async(e,t)=>{let n;return async function(...o){clearTimeout(n),n=setTimeout(async()=>{clearTimeout(n),await e(...o)},t)}},o=e=>new Promise(t=>setTimeout(t,1e3*e||0)),r=(e,t)=>{let n=e=>t?.startsWith(e)||!1,o=e,r=t;return t?.startsWith("http:")||t?.startsWith("https:")?o="":n("/")||o.endsWith("/")?n("/")&&o.endsWith("/")&&(r=t.slice(1)):r="/"+t,{baseURL:o,path:r}};function i(e){if("undefined"==typeof document)return;let t,n=document.getElementById("alert-overlay");if(!n){n=document.body.appendChild(document.createElement("div")),n.id="alert-overlay",n.setAttribute("style","position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center"),n.innerHTML='<div id="alert-box" style="background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:220px;max-height:80vh;position:relative;display:flex;flex-direction:column;">\n <button id="close-alert" style="position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;">×</button>\n <div id="alert-list" style="overflow:auto;flex:1;"></div>\n </div>',n.addEventListener("click",e=>e.target==n&&n.remove());const e=document.getElementById("close-alert");e&&(e.onclick=()=>n.remove())}t=document.getElementById("alert-list"),t.innerHTML+=`<div style="border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;">${e}</div>`}function s(){"undefined"!=typeof document&&document.addEventListener("keydown",t=>{const n=window.grab;if(n&&n.log&&"i"===t.key&&t.ctrlKey&&t.altKey){let t=" ";for(let o of n.log)t+=`<div style="margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;">\n <b>Path:</b> ${o.path}<br>\n <b>Request:</b> ${e(o.request,0,"html")}<br>\n <b>Response:</b> ${e(o.response,0,"html")}<br> \n <b>Time:</b> ${new Date(o.lastFetchTime).toLocaleString()}\n </div>`;i(t)}})}async function a(s,c){const l=function(e){return{..."undefined"!=typeof window?window?.grab?.defaults:globalThis?.grab?.defaults||{},...e}}(c);let{headers:d,response:u,method:f=(l.post?"POST":l.put?"PUT":l.patch?"PATCH":"GET"),cache:p,timeout:m=30,baseURL:g="undefined"!=typeof process&&process.env.SERVER_API_URL||"/api/",cancelOngoingIfNew:y,cancelNewIfOngoing:w,rateLimit:b,debug:h,infiniteScroll:v,logger:T=t,onRequest:L,onResponse:E,onError:x,onStream:S,body:A,post:O,put:P,patch:k,...R}=l;const j=r(g,s);g=j.baseURL,s=j.path;const $=function(e){const t="function"==typeof e?e:null;return{response:!e||t?{}:e,resFunction:t}}(u);let q=$.response,F=$.resFunction;const N="undefined"!=typeof window?window.grab:globalThis.grab,D=N?.log||[];F?q=F({...q,isLoading:!0}):"object"==typeof q&&(q.isLoading=!0);try{const r=await async function(e,t,o,r){const{debounce:i=0,repeat:s=0,repeatEvery:a=null,setDefaults:c=!1}=o;if(i>0){const s=await n(async()=>{await r(e,{...t,debounce:0})},1e3*i);return await s(),o.response||{}}if(s>1){for(let n=0;n<s;n++)await r(e,{...t,repeat:0});return o.response||{}}if(a)return setInterval(async()=>{await r(e,{...t,repeat:0,repeatEvery:null})},1e3*a),o.response||{};if(c){const e="undefined"!=typeof window?window.grab:globalThis.grab;return e&&(e.defaults={...t,setDefaults:void 0}),o.response||{}}return null}(s,c||{},l,a);if(r)return r;!function(e,t,n,o){if("undefined"==typeof window)return;const{regrabOnStale:r,regrabOnFocus:i,regrabOnNetwork:s,cache:a,cacheForTime:c=60}=n,l=async()=>await o(e,{...t,cache:!1});r&&a&&setTimeout(l,1e3*c),s&&window.addEventListener("online",l),i&&(window.addEventListener("focus",l),document.addEventListener("visibilitychange",async()=>{"visible"===document.visibilityState&&await l()}))}(s,c||{},l,a);let{params:i,priorRequest:u,response:x,paramsAsText:O}=function(e,t,n,o,r,i){const{cache:s,cacheForTime:a,infiniteScroll:c}=n,[l,d]=c||[],u=JSON.stringify(l?{...t,[l]:void 0}:t);let f=i.find(t=>t.request===u&&t.path===e);if(l){let e=(f?.currentPage||0)+1||t?.[l]||1;f?f.currentPage=e:(o[d]=[],e=1),t={...t,[l]:e}}else{for(let e of Object.keys(o))o[e]=void 0;if(s&&f?.response&&(!a||f.lastFetchTime>Date.now()-1e3*a)){for(let e of Object.keys(f.response))o[e]=f.response[e];r&&(o=r(o))}}return{params:t,priorRequest:f,response:o,paramsAsText:u}}(s,R,l,q,F,D);if(R=i,q=x,function(e,n,o,r,i){if("undefined"==typeof window||!o?.length)return;const[s,,a]=o;if(void 0===a)return;let c="string"==typeof a?document.querySelector(a):a;c?(window.scrollListener&&"function"==typeof c.removeEventListener&&c.removeEventListener("scroll",window.scrollListener),window.scrollListener=t=>{const o=t.target;localStorage.setItem("scroll",JSON.stringify([o.scrollTop,o.scrollLeft,a])),o.scrollHeight-o.scrollTop<=o.clientHeight+200&&i(e,{...n,cache:!1,[s]:(r?.currentPage||0)+1})},c.addEventListener("scroll",window.scrollListener)):t("paginateDOM not found",{color:"red"})}(s,c,v,u,a),b>0&&u?.lastFetchTime>Date.now()-1e3*b)throw new Error(`Fetch rate limit exceeded for ${s}. Wait ${b}s between requests.`);if(u?.controller)if(y)u.controller.abort();else if(w)return{isLoading:!0};const P=new AbortController;let k=P.signal;y||"function"!=typeof AbortSignal.timeout||(k=AbortSignal.timeout(1e3*m)),D.unshift({path:s,request:O,lastFetchTime:Date.now(),controller:P});let{fetchParams:j,paramsGETRequest:$}=function(e,t,n,o,r,i){const s=["POST","PUT","PATCH"].includes(e),a={method:e,headers:{"Content-Type":"application/json",Accept:"application/json",...t},body:n||(s?JSON.stringify(o):null),redirect:"follow",cache:r?"force-cache":"no-store",signal:i};let c="";return s||(c=(Object.keys(o).length?"?":"")+new URLSearchParams(o).toString()),{fetchParams:a,paramsGETRequest:c}}(f,d,A,R,!!p,k);if("function"==typeof L){const e=L(s,q,R,j);Array.isArray(e)&&([s,q,R,j]=e)}const N=/* @__PURE__ */new Date,I=await async function(e,t,n,r,i,s){const a="undefined"!=typeof window?window.grab:globalThis.grab,c=a?.mock?.[t],l=JSON.stringify(i);if(c&&(!c.method||c.method===r.method)&&(!c.params||l===JSON.stringify(c.params)))return await o(c.delay||0),"function"==typeof c.response?c.response(i):c.response;const d=await fetch(e+t+n,r).catch(e=>{throw new Error(e.message)});if(!d.ok)throw new Error(`HTTP error: ${d.status} ${d.statusText}`);if(s)return await s(d.body),null;const u=d.headers.get("content-type");return await(u?u.includes("application/json")?d.json():u.includes("application/pdf")||u.includes("application/octet-stream")?d.blob():d.text():d.json()).catch(e=>{throw new Error("Error parsing response: "+e)})}(g,s,$,j,R,S);if(F?q=F({...q,isLoading:void 0}):"object"==typeof q&&delete q.isLoading,"function"==typeof E){const e=E(s,q,R,j);Array.isArray(e)&&([s,q,R,j]=e)}const H=((Number(/* @__PURE__ */new Date)-Number(N))/1e3).toFixed(1);h&&T(`Path:${g+s+$}\n${JSON.stringify(c,null,2)}\nTime: ${H}s\nResponse: ${e(I)}`);const[,J]=v||[];return q=function(e,t,n,o){if("object"==typeof e&&null!==e){for(let n of Object.keys(e))t[n]=o===n&&Array.isArray(t[n])?[...t[n],...e[n]]:e[n];t.data=e}else n?t=n({data:e,...e}):"object"==typeof t&&(t.data=e);return t}(I,q,F,J),D[0]&&(D[0].response=q),F&&(q=F(q)),q}catch(I){if("function"==typeof x&&x(I.message,g+s,R),l.retryAttempts&&l.retryAttempts>0)return await a(s,{...c,retryAttempts:--l.retryAttempts});!I.message.includes("signal")&&h&&(T(`Error: ${I.message}\nPath:${g+s}\n`,{color:"red"}),"undefined"!=typeof document&&i(I.message)),q=q||{},q.error=I.message;const e="function"==typeof u?u:null;return e?(q.data=e({isLoading:void 0,error:I.message}),q=q.data):delete q.isLoading,q}}const c=a;c.instance=(e={})=>(t,n={})=>a(t,{...e,...n}),c.log=[],c.mock={},c.defaults={},"undefined"!=typeof window?(window.log=t,window.grab=c,s(),document.addEventListener("DOMContentLoaded",()=>{try{const e=localStorage.getItem("scroll");if(!e)return;const[t,n,o]=JSON.parse(e);if(!t||!o)return;const r=document.querySelector(o);r&&(r.scrollTop=t,r.scrollLeft=n)}catch(e){console.warn("Failed to restore scroll position",e)}})):"undefined"!=typeof globalThis&&(globalThis.log=t,globalThis.grab=c);export{r as buildUrl,n as debouncer,c as default,c as grab,t as log,s as setupDevTools,i as showAlert,o as wait};
|
|
2
2
|
//# sourceMappingURL=grab-api.es.js.map
|
package/dist/grab-api.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grab-api.es.js","sources":["../packages/grab-api/common/utils.ts","../packages/grab-api/devtools/devtools.ts","../packages/grab-api/core/core.ts","../packages/grab-api/core/flow-control.ts","../packages/grab-api/response/response-handler.ts","../packages/grab-api/core/regrab-events.ts","../packages/grab-api/core/cache-pagination.ts","../packages/grab-api/response/infinite-scroll.ts","../packages/grab-api/core/request-executor.ts","../packages/grab-api/index.ts"],"sourcesContent":["/**\r\n * @file common/utils.ts\r\n * @description Common utility functions used across the Grab API.\r\n * Includes helpers for debouncing, URL building, and asynchronous waiting.\r\n */\r\n\r\n/**\r\n * Delays execution so that future calls may override and only executes the last one.\r\n * Useful for search inputs or other high-frequency events.\r\n * \r\n * @param func - The function to debounce.\r\n * @param wait - Time to wait in milliseconds.\r\n * @returns A debounced version of the function.\r\n */\r\nexport const debouncer = async (func: Function, wait: number) => {\r\n let timeout: any;\r\n return async function executedFunction(...args: any[]) {\r\n const later = async () => {\r\n clearTimeout(timeout);\r\n await func(...args);\r\n };\r\n clearTimeout(timeout);\r\n timeout = setTimeout(later, wait);\r\n };\r\n};\r\n\r\n/**\r\n * Helper function to wait for a specified number of seconds.\r\n * \r\n * @param s - Seconds to wait.\r\n * @returns A promise that resolves after the timeout.\r\n */\r\nexport const wait = (s: number) => new Promise((res) => setTimeout(res, s * 1000 || 0));\r\n\r\n/**\r\n * Normalizes and builds a URL from a base and relative path.\r\n * \r\n * @param baseURL - The base API URL.\r\n * @param path - The specific endpoint path.\r\n * @returns An object containing the combined URL and updated baseURL/path if needed.\r\n */\r\nexport const buildUrl = (baseURL: string, path: string) => {\r\n let s = (t: string) => path?.startsWith(t) || false;\r\n let finalBaseURL = baseURL;\r\n let finalPath = path;\r\n\r\n if (path?.startsWith(\"http:\") || path?.startsWith(\"https:\")) {\r\n finalBaseURL = \"\";\r\n } else if (!s(\"/\") && !finalBaseURL.endsWith(\"/\")) {\r\n finalPath = \"/\" + path;\r\n } else if (s(\"/\") && finalBaseURL.endsWith(\"/\")) {\r\n finalPath = path.slice(1);\r\n }\r\n\r\n return { baseURL: finalBaseURL, path: finalPath };\r\n};\r\n","/**\r\n * @file ui/devtools.ts\r\n * @description Visual development tools for the Grab API.\r\n * Includes a modal overlay for inspect request history (Ctrl+Alt+I).\r\n */\r\n\r\nimport { printJSONStructure } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Shows a message in a modal overlay with a scrollable message stack.\r\n * Easier to dismiss than native alert() and does not block window execution.\r\n *\r\n * @param msg - The message or HTML content to display.\r\n */\r\nexport function showAlert(msg: string) {\r\n if (typeof document === \"undefined\") return;\r\n let o = document.getElementById(\"alert-overlay\"),\r\n list: HTMLElement;\r\n\r\n if (!o) {\r\n o = document.body.appendChild(document.createElement(\"div\"));\r\n o.id = \"alert-overlay\";\r\n o.setAttribute(\r\n \"style\",\r\n \"position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\",\r\n );\r\n o.innerHTML = `<div id=\"alert-box\" style=\"background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:220px;max-height:80vh;position:relative;display:flex;flex-direction:column;\">\r\n <button id=\"close-alert\" style=\"position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;\">×</button>\r\n <div id=\"alert-list\" style=\"overflow:auto;flex:1;\"></div>\r\n </div>`;\r\n\r\n o.addEventListener(\"click\", (e) => e.target == o && o!.remove());\r\n const closeBtn = document.getElementById(\"close-alert\");\r\n if (closeBtn) closeBtn.onclick = () => o!.remove();\r\n }\r\n\r\n list = document.getElementById(\"alert-list\")!;\r\n list.innerHTML += `<div style=\"border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;\">${msg}</div>`;\r\n}\r\n\r\n/**\r\n * Sets up development tools for debugging API requests.\r\n * Adds a keyboard shortcut (Ctrl+Alt+I) to toggle a modal showing the request history from `grab.log`.\r\n */\r\nexport function setupDevTools() {\r\n if (typeof document === \"undefined\") return;\r\n\r\n document.addEventListener(\"keydown\", (e) => {\r\n // Check for global grab on window\r\n const grab = (window as any).grab as GrabFunction;\r\n if (!grab || !grab.log) return;\r\n\r\n if (e.key === \"i\" && e.ctrlKey && e.altKey) {\r\n let html = \" \";\r\n for (let request of grab.log) {\r\n html += `<div style=\"margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;\">\r\n <b>Path:</b> ${request.path}<br>\r\n <b>Request:</b> ${printJSONStructure(request.request, 0, \"html\")}<br>\r\n <b>Response:</b> ${printJSONStructure(\r\n request.response,\r\n 0,\r\n \"html\",\r\n )}<br> \r\n <b>Time:</b> ${new Date(request.lastFetchTime).toLocaleString()}\r\n </div>`;\r\n }\r\n showAlert(html);\r\n }\r\n });\r\n}\r\n","/**\r\n * @file core/core.ts\r\n * @description Main orchestration logic for the Grab API.\r\n * Defines the primary 'grab' function and coordinates request flow,\r\n * including option merging, caching, and execution.\r\n */\r\n\r\nimport { printJSONStructure, log } from \"@grab-url/log\";\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { buildUrl } from \"../common/utils\";\r\nimport { showAlert } from \"../devtools/devtools\";\r\n\r\nimport { getMergedOptions, handleFlowControl } from \"./flow-control\";\r\nimport { handleRegrabEvents } from \"./regrab-events\";\r\nimport {\r\n initializeResponse,\r\n mapResultToResponse,\r\n} from \"../response/response-handler\";\r\nimport { setupInfiniteScroll } from \"../response/infinite-scroll\";\r\nimport { manageCacheAndPagination } from \"./cache-pagination\";\r\nimport { prepareFetchRequest, executeRequest } from \"./request-executor\";\r\n\r\n/**\r\n * ### GRAB: Generate Request to API from Browser\r\n *\r\n * The core function for making API requests. Handles JSON conversion,\r\n * loading states, caching, pagination, debouncing, and more.\r\n */\r\nexport async function grab<TResponse = any, TParams = any>(\r\n path: string,\r\n options?: GrabOptions<TResponse, TParams>,\r\n): Promise<GrabResponse<TResponse>> {\r\n const merged = getMergedOptions(options);\r\n let {\r\n headers,\r\n response: responseOption,\r\n method = merged.post\r\n ? \"POST\"\r\n : merged.put\r\n ? \"PUT\"\r\n : merged.patch\r\n ? \"PATCH\"\r\n : \"GET\",\r\n cache,\r\n timeout = 30,\r\n baseURL = (typeof process !== \"undefined\" && process.env.SERVER_API_URL) ||\r\n \"/api/\",\r\n cancelOngoingIfNew,\r\n cancelNewIfOngoing,\r\n rateLimit,\r\n debug,\r\n infiniteScroll,\r\n logger = log,\r\n onRequest,\r\n onResponse,\r\n onError,\r\n onStream,\r\n body,\r\n post,\r\n put,\r\n patch,\r\n ...params\r\n } = merged;\r\n\r\n const urlConfig = buildUrl(baseURL, path);\r\n baseURL = urlConfig.baseURL;\r\n path = urlConfig.path;\r\n\r\n const initialized = initializeResponse(responseOption);\r\n let response: any = initialized.response;\r\n let resFunction = initialized.resFunction;\r\n const target = (\r\n typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab\r\n ) as GrabFunction;\r\n const grabLog = target?.log || [];\r\n\r\n // Set loading state synchronously before any await\r\n if (resFunction) response = resFunction({ ...response, isLoading: true });\r\n else if (typeof response === \"object\") response.isLoading = true;\r\n\r\n try {\r\n const flowResult = await handleFlowControl(\r\n path,\r\n options || {},\r\n merged,\r\n grab as unknown as GrabFunction,\r\n );\r\n if (flowResult) return flowResult as any;\r\n\r\n handleRegrabEvents(path, options || {}, merged, grab as unknown as GrabFunction);\r\n\r\n let {\r\n params: updatedParams,\r\n priorRequest,\r\n response: updatedResponse,\r\n paramsAsText,\r\n } = manageCacheAndPagination(\r\n path,\r\n params,\r\n merged,\r\n response,\r\n resFunction,\r\n grabLog,\r\n );\r\n params = updatedParams;\r\n response = updatedResponse;\r\n\r\n setupInfiniteScroll(path, options, infiniteScroll, priorRequest, grab as unknown as GrabFunction);\r\n\r\n if (\r\n rateLimit > 0 &&\r\n priorRequest?.lastFetchTime > Date.now() - 1000 * rateLimit\r\n ) {\r\n throw new Error(\r\n `Fetch rate limit exceeded for ${path}. Wait ${rateLimit}s between requests.`,\r\n );\r\n }\r\n\r\n if (priorRequest?.controller) {\r\n if (cancelOngoingIfNew) priorRequest.controller.abort();\r\n else if (cancelNewIfOngoing) return { isLoading: true } as GrabResponse;\r\n }\r\n\r\n const controller = new AbortController();\r\n let signal = controller.signal;\r\n\r\n // Add timeout if requested (and signal not already used for cancellation)\r\n if (!cancelOngoingIfNew && typeof AbortSignal.timeout === \"function\") {\r\n signal = AbortSignal.timeout(timeout * 1000);\r\n }\r\n\r\n grabLog.unshift({\r\n path,\r\n request: paramsAsText,\r\n lastFetchTime: Date.now(),\r\n controller,\r\n });\r\n\r\n let { fetchParams, paramsGETRequest } = prepareFetchRequest(\r\n method,\r\n headers,\r\n body,\r\n params,\r\n !!cache,\r\n signal,\r\n );\r\n\r\n if (typeof onRequest === \"function\") {\r\n const modified = onRequest(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const startTime = new Date();\r\n const res = await executeRequest(\r\n baseURL,\r\n path,\r\n paramsGETRequest,\r\n fetchParams,\r\n params,\r\n onStream,\r\n );\r\n\r\n // Clear loading state\r\n if (resFunction)\r\n response = resFunction({ ...response, isLoading: undefined });\r\n else if (typeof response === \"object\") delete response.isLoading;\r\n\r\n if (typeof onResponse === \"function\") {\r\n const modified = onResponse(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const elapsedTime = (\r\n (Number(new Date()) - Number(startTime)) /\r\n 1000\r\n ).toFixed(1);\r\n if (debug) {\r\n logger(\r\n `Path:${baseURL + path + paramsGETRequest}\\n${JSON.stringify(options, null, 2)}\\nTime: ${elapsedTime}s\\nResponse: ${printJSONStructure(res)}`,\r\n );\r\n }\r\n\r\n const [, paginateResult] = (infiniteScroll as any) || [];\r\n response = mapResultToResponse(res, response, resFunction, paginateResult);\r\n\r\n if (grabLog[0]) grabLog[0].response = response;\r\n if (resFunction) response = resFunction(response);\r\n\r\n return response as any;\r\n } catch (error: any) {\r\n if (typeof onError === \"function\")\r\n onError(error.message, baseURL + path, params);\r\n\r\n if (merged.retryAttempts && merged.retryAttempts > 0) {\r\n return await grab(path, {\r\n ...options,\r\n retryAttempts: --merged.retryAttempts,\r\n });\r\n }\r\n\r\n if (!error.message.includes(\"signal\") && debug) {\r\n logger(`Error: ${error.message}\\nPath:${baseURL + path}\\n`, {\r\n color: \"red\",\r\n });\r\n if (typeof document !== \"undefined\") showAlert(error.message);\r\n }\r\n\r\n response = response || {};\r\n response.error = error.message;\r\n const resFn = typeof responseOption === \"function\" ? responseOption : null;\r\n if (resFn) {\r\n response.data = resFn({ isLoading: undefined, error: error.message });\r\n response = response.data;\r\n } else {\r\n delete response.isLoading;\r\n }\r\n return response as any;\r\n }\r\n}\r\n","/**\r\n * @file core/flow-control.ts\r\n * @description Logic for managing request flow and option merging.\r\n * Handles debouncing, request repetition, and default option application.\r\n */\r\n\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { debouncer } from \"../common/utils\";\r\n\r\n/**\r\n * Merges provided options with global defaults.\r\n */\r\nexport function getMergedOptions<TResponse, TParams>(\r\n options?: GrabOptions<TResponse, TParams>\r\n): GrabOptions<TResponse, TParams> & { [key: string]: any } {\r\n const defaults = (typeof window !== \"undefined\"\r\n ? window?.grab?.defaults\r\n : (globalThis as any)?.grab?.defaults || {}) as GrabOptions<TResponse, TParams>;\r\n\r\n return {\r\n ...defaults,\r\n ...options,\r\n };\r\n}\r\n\r\n/**\r\n * Handles flow control logic like debouncing and repeating requests.\r\n */\r\nexport async function handleFlowControl<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): Promise<GrabResponse<TResponse> | null> {\r\n const { debounce = 0, repeat = 0, repeatEvery = null, setDefaults = false } = mergedOptions;\r\n\r\n // Handle debounce\r\n if (debounce > 0) {\r\n const task = await debouncer(async () => {\r\n await grab(path, { ...options, debounce: 0 });\r\n }, debounce * 1000);\r\n await task();\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Handle repeat options\r\n if (repeat > 1) {\r\n for (let i = 0; i < repeat; i++) {\r\n await grab(path, { ...options, repeat: 0 });\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n if (repeatEvery) {\r\n setInterval(async () => {\r\n await grab(path, { ...options, repeat: 0, repeatEvery: null });\r\n }, repeatEvery * 1000);\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Store defaults if requested\r\n if (setDefaults) {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab);\r\n if (target) {\r\n target.defaults = { ...options, setDefaults: undefined };\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n return null;\r\n}\r\n","/**\r\n * @file response/response-handler.ts\r\n * @description Logic for handling API responses.\r\n * Includes functions for initializing response objects and mapping results.\r\n */\r\n\r\n/**\r\n * Initializes the response object and result function.\r\n */\r\nexport function initializeResponse<TResponse>(responseOption: any): {\r\n response: any;\r\n resFunction: ((data: any) => any) | null;\r\n} {\r\n const resFunction = typeof responseOption === \"function\" ? responseOption : null;\r\n let response = (!responseOption || resFunction) ? {} : responseOption;\r\n return { response, resFunction };\r\n}\r\n\r\n/**\r\n * Maps the raw result onto the response object.\r\n */\r\nexport function mapResultToResponse(res: any, response: any, resFunction: any, paginateResult: string | null): any {\r\n if (typeof res === \"object\" && res !== null) {\r\n for (let key of Object.keys(res)) {\r\n response[key] = (paginateResult === key && Array.isArray(response[key]))\r\n ? [...response[key], ...res[key]]\r\n : res[key];\r\n }\r\n response.data = res;\r\n } else {\r\n if (resFunction) {\r\n response = resFunction({ data: res, ...res });\r\n } else if (typeof response === \"object\") {\r\n response.data = res;\r\n }\r\n }\r\n return response;\r\n}\r\n","/**\r\n * @file core/regrab-events.ts\r\n * @description Event listeners for automatic data re-fetching.\r\n * Handles stale cache, window focus, and network status changes.\r\n */\r\n\r\nimport { GrabOptions, GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Sets up event listeners for regrabbing data on stale, focus, or network changes.\r\n */\r\nexport function handleRegrabEvents<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const { regrabOnStale, regrabOnFocus, regrabOnNetwork, cache, cacheForTime = 60 } = mergedOptions;\r\n const regrab = async () => await grab(path, { ...options, cache: false });\r\n\r\n if (regrabOnStale && cache) setTimeout(regrab, 1000 * cacheForTime);\r\n if (regrabOnNetwork) window.addEventListener(\"online\", regrab);\r\n if (regrabOnFocus) {\r\n window.addEventListener(\"focus\", regrab);\r\n document.addEventListener(\"visibilitychange\", async () => {\r\n if (document.visibilityState === \"visible\") await regrab();\r\n });\r\n }\r\n}\r\n","/**\r\n * @file core/cache-pagination.ts\r\n * @description Cache management and pagination state tracking.\r\n * Determines cache hits and updates current page numbers for infinite scroll.\r\n */\r\n\r\n/**\r\n * Manages cache hits and pagination state updates.\r\n */\r\nexport function manageCacheAndPagination(\r\n path: string,\r\n params: any,\r\n mergedOptions: any,\r\n response: any,\r\n resFunction: any,\r\n grabLog: any[]\r\n): { params: any; priorRequest: any; response: any; paramsAsText: string } {\r\n const { cache, cacheForTime, infiniteScroll } = mergedOptions;\r\n const [paginateKey, paginateResult] = (infiniteScroll as any) || [];\r\n\r\n const paramsAsText = JSON.stringify(\r\n paginateKey ? { ...params, [paginateKey as string]: undefined } : params\r\n );\r\n\r\n let priorRequest = grabLog.find(e => e.request === paramsAsText && e.path === path);\r\n\r\n if (!paginateKey) {\r\n // Clear response for fresh request\r\n for (let key of Object.keys(response)) response[key] = undefined;\r\n\r\n // Cache hit check\r\n if (cache && priorRequest?.response &&\r\n (!cacheForTime || priorRequest.lastFetchTime > Date.now() - 1000 * cacheForTime)) {\r\n for (let key of Object.keys(priorRequest.response)) {\r\n response[key] = priorRequest.response[key];\r\n }\r\n if (resFunction) response = resFunction(response);\r\n }\r\n } else {\r\n // Pagination logic\r\n let pageNumber = (priorRequest?.currentPage || 0) + 1 || params?.[paginateKey] || 1;\r\n if (!priorRequest) {\r\n response[paginateResult] = [];\r\n pageNumber = 1;\r\n } else {\r\n priorRequest.currentPage = pageNumber;\r\n }\r\n params = { ...params, [paginateKey]: pageNumber };\r\n }\r\n\r\n return { params, priorRequest, response, paramsAsText };\r\n}\r\n","/**\r\n * @file response/infinite-scroll.ts\r\n * @description Infinite scroll pagination logic.\r\n * Manages scroll event listeners and triggers automatic next-page fetching.\r\n */\r\n\r\nimport { log } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Configures infinite scroll listener.\r\n */\r\nexport function setupInfiniteScroll(\r\n path: string,\r\n options: any,\r\n infiniteScroll: any,\r\n priorRequest: any,\r\n grab: GrabFunction,\r\n): void {\r\n if (typeof window === \"undefined\" || !infiniteScroll?.length) return;\r\n\r\n const [paginateKey, , paginateElement] = infiniteScroll;\r\n if (typeof paginateElement === \"undefined\") return;\r\n\r\n let paginateDOM =\r\n typeof paginateElement === \"string\"\r\n ? document.querySelector(paginateElement)\r\n : paginateElement;\r\n\r\n if (!paginateDOM) {\r\n log(\"paginateDOM not found\", { color: \"red\" });\r\n return;\r\n }\r\n\r\n if (\r\n (window as any).scrollListener &&\r\n typeof (paginateDOM as any).removeEventListener === \"function\"\r\n ) {\r\n (paginateDOM as any).removeEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n }\r\n\r\n (window as any).scrollListener = (event: Event) => {\r\n const t = event.target as HTMLElement;\r\n localStorage.setItem(\r\n \"scroll\",\r\n JSON.stringify([t.scrollTop, t.scrollLeft, paginateElement]),\r\n );\r\n\r\n if (t.scrollHeight - t.scrollTop <= t.clientHeight + 200) {\r\n grab(path, {\r\n ...options,\r\n cache: false,\r\n [paginateKey as string]: (priorRequest?.currentPage || 0) + 1,\r\n });\r\n }\r\n };\r\n\r\n (paginateDOM as any).addEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n}\r\n","/**\r\n * @file core/request-executor.ts\r\n * @description Logic for preparing and executing API requests.\r\n * Handles both mock responses and real network fetch calls.\r\n */\r\n\r\nimport { GrabFunction, GrabMockHandler } from \"../common/types\";\r\nimport { wait } from \"../common/utils\";\r\n\r\n/**\r\n * Prepares the fetch parameters and URL.\r\n */\r\nexport function prepareFetchRequest(\r\n method: string,\r\n headers: any,\r\n body: any,\r\n params: any,\r\n cache: boolean,\r\n signal: AbortSignal\r\n): { fetchParams: RequestInit; paramsGETRequest: string } {\r\n const isBodyMethod = [\"POST\", \"PUT\", \"PATCH\"].includes(method);\r\n\r\n const fetchParams: RequestInit = {\r\n method,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n ...headers,\r\n },\r\n body: body || (isBodyMethod ? JSON.stringify(params) : null),\r\n redirect: \"follow\",\r\n cache: cache ? \"force-cache\" : \"no-store\",\r\n signal,\r\n };\r\n\r\n let paramsGETRequest = \"\";\r\n if (!isBodyMethod) {\r\n paramsGETRequest = (Object.keys(params).length ? \"?\" : \"\") + new URLSearchParams(params).toString();\r\n }\r\n\r\n return { fetchParams, paramsGETRequest };\r\n}\r\n\r\n/**\r\n * Executes the request, either via mock or actual fetch.\r\n */\r\nexport async function executeRequest(\r\n baseURL: string,\r\n path: string,\r\n paramsGETRequest: string,\r\n fetchParams: RequestInit,\r\n params: any,\r\n onStream: any\r\n): Promise<any> {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab) as GrabFunction;\r\n const mockHandler = target?.mock?.[path] as GrabMockHandler;\r\n const paramsAsText = JSON.stringify(params);\r\n\r\n if (mockHandler &&\r\n (!mockHandler.method || mockHandler.method === fetchParams.method) &&\r\n (!mockHandler.params || paramsAsText === JSON.stringify(mockHandler.params))) {\r\n await wait(mockHandler.delay || 0);\r\n return typeof mockHandler.response === \"function\" ? mockHandler.response(params) : mockHandler.response;\r\n }\r\n\r\n const fetchRes = await fetch(baseURL + path + paramsGETRequest, fetchParams).catch(e => {\r\n throw new Error(e.message);\r\n });\r\n\r\n if (!fetchRes.ok) throw new Error(`HTTP error: ${fetchRes.status} ${fetchRes.statusText}`);\r\n\r\n if (onStream) {\r\n await onStream(fetchRes.body);\r\n return null;\r\n }\r\n\r\n const type = fetchRes.headers.get(\"content-type\");\r\n return await (\r\n type\r\n ? type.includes(\"application/json\")\r\n ? fetchRes.json()\r\n : type.includes(\"application/pdf\") || type.includes(\"application/octet-stream\")\r\n ? fetchRes.blob()\r\n : fetchRes.text()\r\n : fetchRes.json()\r\n ).catch(e => {\r\n throw new Error(\"Error parsing response: \" + e);\r\n });\r\n}\r\n","import { grab as coreGrab } from \"./core/core\";\r\nimport { setupDevTools } from \"./devtools/devtools\";\r\nimport { GrabFunction, GrabOptions } from \"./common/types\";\r\nimport { log } from \"@grab-url/log\";\r\n\r\n// Add instance method to the core grab function\r\nconst grab: GrabFunction = coreGrab as any;\r\n\r\n/**\r\n * Creates a new instance of grab with default options\r\n * to apply to all requests made by this instance.\r\n *\r\n * @param defaults - Options for all requests made by this instance.\r\n * @returns A new grab() function using those default options.\r\n */\r\ngrab.instance = (defaults: Partial<GrabOptions> = {}) =>\r\n ((path: string, options: Partial<GrabOptions> = {}) =>\r\n coreGrab(path, { ...defaults, ...options })) as any;\r\n\r\n// Initialize global state\r\ngrab.log = [];\r\ngrab.mock = {};\r\ngrab.defaults = {};\r\n\r\n// Handle global registration for both Browser and Node.js environments\r\nif (typeof window !== \"undefined\") {\r\n // @ts-ignore\r\n window.log = log;\r\n window.grab = grab;\r\n\r\n // Setup visual dev tools\r\n setupDevTools();\r\n\r\n // Restore scroll position when page loads for infinite scroll persistence\r\n document.addEventListener(\"DOMContentLoaded\", () => {\r\n try {\r\n const scrollData = localStorage.getItem(\"scroll\");\r\n if (!scrollData) return;\r\n\r\n const [scrollTop, scrollLeft, paginateElement] = JSON.parse(scrollData);\r\n if (!scrollTop || !paginateElement) return;\r\n\r\n const el = document.querySelector(paginateElement);\r\n if (el) {\r\n el.scrollTop = scrollTop;\r\n el.scrollLeft = scrollLeft;\r\n }\r\n } catch (e) {\r\n console.warn(\"Failed to restore scroll position\", e);\r\n }\r\n });\r\n} else if (typeof globalThis !== \"undefined\") {\r\n (globalThis as any).log = log;\r\n (globalThis as any).grab = grab;\r\n}\r\n\r\n// Re-export core function and all types\r\nexport { grab };\r\nexport default grab;\r\n\r\nexport { log };\r\nexport * from \"./common/types\";\r\nexport * from \"./devtools/devtools\";\r\nexport * from \"./common/utils\";\r\n"],"names":["debouncer","async","func","wait","timeout","args","clearTimeout","setTimeout","s","Promise","res","buildUrl","baseURL","path","t","startsWith","finalBaseURL","finalPath","endsWith","slice","showAlert","msg","document","list","o","getElementById","body","appendChild","createElement","id","setAttribute","innerHTML","addEventListener","e","target","remove","closeBtn","onclick","setupDevTools","grab","window","log","key","ctrlKey","altKey","html","request","printJSONStructure","response","Date","lastFetchTime","toLocaleString","options","merged","defaults","globalThis","getMergedOptions","headers","responseOption","method","post","put","patch","cache","process","env","SERVER_API_URL","cancelOngoingIfNew","cancelNewIfOngoing","rateLimit","debug","infiniteScroll","logger","onRequest","onResponse","onError","onStream","params","urlConfig","initialized","resFunction","initializeResponse","grabLog","isLoading","flowResult","mergedOptions","debounce","repeat","repeatEvery","setDefaults","task","i","setInterval","handleFlowControl","regrabOnStale","regrabOnFocus","regrabOnNetwork","cacheForTime","regrab","visibilityState","handleRegrabEvents","updatedParams","priorRequest","updatedResponse","paramsAsText","paginateKey","paginateResult","JSON","stringify","find","pageNumber","currentPage","Object","keys","now","manageCacheAndPagination","length","paginateElement","paginateDOM","querySelector","scrollListener","removeEventListener","event","localStorage","setItem","scrollTop","scrollLeft","scrollHeight","clientHeight","color","setupInfiniteScroll","Error","controller","abort","AbortController","signal","AbortSignal","unshift","fetchParams","paramsGETRequest","isBodyMethod","includes","Accept","redirect","URLSearchParams","toString","prepareFetchRequest","modified","Array","isArray","startTime","mockHandler","mock","delay","fetchRes","fetch","catch","message","ok","status","statusText","type","get","json","blob","text","executeRequest","elapsedTime","Number","toFixed","data","mapResultToResponse","error","retryAttempts","resFn","coreGrab","instance","scrollData","getItem","parse","el","console","warn"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"grab-api.es.js","sources":["../packages/grab-api/common/utils.ts","../packages/grab-api/devtools/devtools.ts","../packages/grab-api/core/core.ts","../packages/grab-api/core/flow-control.ts","../packages/grab-api/response/response-handler.ts","../packages/grab-api/core/regrab-events.ts","../packages/grab-api/core/cache-pagination.ts","../packages/grab-api/response/infinite-scroll.ts","../packages/grab-api/core/request-executor.ts","../packages/grab-api/index.ts"],"sourcesContent":["/**\r\n * @file common/utils.ts\r\n * @description Common utility functions used across the Grab API.\r\n * Includes helpers for debouncing, URL building, and asynchronous waiting.\r\n */\r\n\r\n/**\r\n * Delays execution so that future calls may override and only executes the last one.\r\n * Useful for search inputs or other high-frequency events.\r\n * \r\n * @param func - The function to debounce.\r\n * @param wait - Time to wait in milliseconds.\r\n * @returns A debounced version of the function.\r\n */\r\nexport const debouncer = async (func: Function, wait: number) => {\r\n let timeout: any;\r\n return async function executedFunction(...args: any[]) {\r\n const later = async () => {\r\n clearTimeout(timeout);\r\n await func(...args);\r\n };\r\n clearTimeout(timeout);\r\n timeout = setTimeout(later, wait);\r\n };\r\n};\r\n\r\n/**\r\n * Helper function to wait for a specified number of seconds.\r\n * \r\n * @param s - Seconds to wait.\r\n * @returns A promise that resolves after the timeout.\r\n */\r\nexport const wait = (s: number) => new Promise((res) => setTimeout(res, s * 1000 || 0));\r\n\r\n/**\r\n * Normalizes and builds a URL from a base and relative path.\r\n * \r\n * @param baseURL - The base API URL.\r\n * @param path - The specific endpoint path.\r\n * @returns An object containing the combined URL and updated baseURL/path if needed.\r\n */\r\nexport const buildUrl = (baseURL: string, path: string) => {\r\n let s = (t: string) => path?.startsWith(t) || false;\r\n let finalBaseURL = baseURL;\r\n let finalPath = path;\r\n\r\n if (path?.startsWith(\"http:\") || path?.startsWith(\"https:\")) {\r\n finalBaseURL = \"\";\r\n } else if (!s(\"/\") && !finalBaseURL.endsWith(\"/\")) {\r\n finalPath = \"/\" + path;\r\n } else if (s(\"/\") && finalBaseURL.endsWith(\"/\")) {\r\n finalPath = path.slice(1);\r\n }\r\n\r\n return { baseURL: finalBaseURL, path: finalPath };\r\n};\r\n","/**\r\n * @file ui/devtools.ts\r\n * @description Visual development tools for the Grab API.\r\n * Includes a modal overlay for inspect request history (Ctrl+Alt+I).\r\n */\r\n\r\nimport { printJSONStructure } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Shows a message in a modal overlay with a scrollable message stack.\r\n * Easier to dismiss than native alert() and does not block window execution.\r\n *\r\n * @param msg - The message or HTML content to display.\r\n */\r\nexport function showAlert(msg: string) {\r\n if (typeof document === \"undefined\") return;\r\n let o = document.getElementById(\"alert-overlay\"),\r\n list: HTMLElement;\r\n\r\n if (!o) {\r\n o = document.body.appendChild(document.createElement(\"div\"));\r\n o.id = \"alert-overlay\";\r\n o.setAttribute(\r\n \"style\",\r\n \"position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\",\r\n );\r\n o.innerHTML = `<div id=\"alert-box\" style=\"background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:220px;max-height:80vh;position:relative;display:flex;flex-direction:column;\">\r\n <button id=\"close-alert\" style=\"position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;\">×</button>\r\n <div id=\"alert-list\" style=\"overflow:auto;flex:1;\"></div>\r\n </div>`;\r\n\r\n o.addEventListener(\"click\", (e) => e.target == o && o!.remove());\r\n const closeBtn = document.getElementById(\"close-alert\");\r\n if (closeBtn) closeBtn.onclick = () => o!.remove();\r\n }\r\n\r\n list = document.getElementById(\"alert-list\")!;\r\n list.innerHTML += `<div style=\"border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;\">${msg}</div>`;\r\n}\r\n\r\n/**\r\n * Sets up development tools for debugging API requests.\r\n * Adds a keyboard shortcut (Ctrl+Alt+I) to toggle a modal showing the request history from `grab.log`.\r\n */\r\nexport function setupDevTools() {\r\n if (typeof document === \"undefined\") return;\r\n\r\n document.addEventListener(\"keydown\", (e) => {\r\n // Check for global grab on window\r\n const grab = (window as any).grab as GrabFunction;\r\n if (!grab || !grab.log) return;\r\n\r\n if (e.key === \"i\" && e.ctrlKey && e.altKey) {\r\n let html = \" \";\r\n for (let request of grab.log) {\r\n html += `<div style=\"margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;\">\r\n <b>Path:</b> ${request.path}<br>\r\n <b>Request:</b> ${printJSONStructure(request.request, 0, \"html\")}<br>\r\n <b>Response:</b> ${printJSONStructure(\r\n request.response,\r\n 0,\r\n \"html\",\r\n )}<br> \r\n <b>Time:</b> ${new Date(request.lastFetchTime).toLocaleString()}\r\n </div>`;\r\n }\r\n showAlert(html);\r\n }\r\n });\r\n}\r\n","/**\r\n * @file core/core.ts\r\n * @description Main orchestration logic for the Grab API.\r\n * Defines the primary 'grab' function and coordinates request flow,\r\n * including option merging, caching, and execution.\r\n */\r\n\r\nimport { printJSONStructure, log } from \"@grab-url/log\";\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { buildUrl } from \"../common/utils\";\r\nimport { showAlert } from \"../devtools/devtools\";\r\n\r\nimport { getMergedOptions, handleFlowControl } from \"./flow-control\";\r\nimport { handleRegrabEvents } from \"./regrab-events\";\r\nimport {\r\n initializeResponse,\r\n mapResultToResponse,\r\n} from \"../response/response-handler\";\r\nimport { setupInfiniteScroll } from \"../response/infinite-scroll\";\r\nimport { manageCacheAndPagination } from \"./cache-pagination\";\r\nimport { prepareFetchRequest, executeRequest } from \"./request-executor\";\r\n\r\n/**\r\n * ### GRAB: Generate Request to API from Browser\r\n *\r\n * The core function for making API requests. Handles JSON conversion,\r\n * loading states, caching, pagination, debouncing, and more.\r\n */\r\nexport async function grab<TResponse = any, TParams = any>(\r\n path: string,\r\n options?: GrabOptions<TResponse, TParams>,\r\n): Promise<GrabResponse<TResponse>> {\r\n const merged = getMergedOptions(options);\r\n let {\r\n headers,\r\n response: responseOption,\r\n method = merged.post\r\n ? \"POST\"\r\n : merged.put\r\n ? \"PUT\"\r\n : merged.patch\r\n ? \"PATCH\"\r\n : \"GET\",\r\n cache,\r\n timeout = 30,\r\n baseURL = (typeof process !== \"undefined\" && process.env.SERVER_API_URL) ||\r\n \"/api/\",\r\n cancelOngoingIfNew,\r\n cancelNewIfOngoing,\r\n rateLimit,\r\n debug,\r\n infiniteScroll,\r\n logger = log,\r\n onRequest,\r\n onResponse,\r\n onError,\r\n onStream,\r\n body,\r\n post,\r\n put,\r\n patch,\r\n ...params\r\n } = merged;\r\n\r\n const urlConfig = buildUrl(baseURL, path);\r\n baseURL = urlConfig.baseURL;\r\n path = urlConfig.path;\r\n\r\n const initialized = initializeResponse(responseOption);\r\n let response: any = initialized.response;\r\n let resFunction = initialized.resFunction;\r\n const target = (\r\n typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab\r\n ) as GrabFunction;\r\n const grabLog = target?.log || [];\r\n\r\n // Set loading state synchronously before any await\r\n if (resFunction) response = resFunction({ ...response, isLoading: true });\r\n else if (typeof response === \"object\") response.isLoading = true;\r\n\r\n try {\r\n const flowResult = await handleFlowControl(\r\n path,\r\n options || {},\r\n merged,\r\n grab as unknown as GrabFunction,\r\n );\r\n if (flowResult) return flowResult as any;\r\n\r\n handleRegrabEvents(path, options || {}, merged, grab as unknown as GrabFunction);\r\n\r\n let {\r\n params: updatedParams,\r\n priorRequest,\r\n response: updatedResponse,\r\n paramsAsText,\r\n } = manageCacheAndPagination(\r\n path,\r\n params,\r\n merged,\r\n response,\r\n resFunction,\r\n grabLog,\r\n );\r\n params = updatedParams;\r\n response = updatedResponse;\r\n\r\n setupInfiniteScroll(path, options, infiniteScroll, priorRequest, grab as unknown as GrabFunction);\r\n\r\n if (\r\n rateLimit > 0 &&\r\n priorRequest?.lastFetchTime > Date.now() - 1000 * rateLimit\r\n ) {\r\n throw new Error(\r\n `Fetch rate limit exceeded for ${path}. Wait ${rateLimit}s between requests.`,\r\n );\r\n }\r\n\r\n if (priorRequest?.controller) {\r\n if (cancelOngoingIfNew) priorRequest.controller.abort();\r\n else if (cancelNewIfOngoing) return { isLoading: true } as GrabResponse;\r\n }\r\n\r\n const controller = new AbortController();\r\n let signal = controller.signal;\r\n\r\n // Add timeout if requested (and signal not already used for cancellation)\r\n if (!cancelOngoingIfNew && typeof AbortSignal.timeout === \"function\") {\r\n signal = AbortSignal.timeout(timeout * 1000);\r\n }\r\n\r\n grabLog.unshift({\r\n path,\r\n request: paramsAsText,\r\n lastFetchTime: Date.now(),\r\n controller,\r\n });\r\n\r\n let { fetchParams, paramsGETRequest } = prepareFetchRequest(\r\n method,\r\n headers,\r\n body,\r\n params,\r\n !!cache,\r\n signal,\r\n );\r\n\r\n if (typeof onRequest === \"function\") {\r\n const modified = onRequest(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const startTime = new Date();\r\n const res = await executeRequest(\r\n baseURL,\r\n path,\r\n paramsGETRequest,\r\n fetchParams,\r\n params,\r\n onStream,\r\n );\r\n\r\n // Clear loading state\r\n if (resFunction)\r\n response = resFunction({ ...response, isLoading: undefined });\r\n else if (typeof response === \"object\") delete response.isLoading;\r\n\r\n if (typeof onResponse === \"function\") {\r\n const modified = onResponse(path, response, params, fetchParams);\r\n if (Array.isArray(modified))\r\n [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const elapsedTime = (\r\n (Number(new Date()) - Number(startTime)) /\r\n 1000\r\n ).toFixed(1);\r\n if (debug) {\r\n logger(\r\n `Path:${baseURL + path + paramsGETRequest}\\n${JSON.stringify(options, null, 2)}\\nTime: ${elapsedTime}s\\nResponse: ${printJSONStructure(res)}`,\r\n );\r\n }\r\n\r\n const [, paginateResult] = (infiniteScroll as any) || [];\r\n response = mapResultToResponse(res, response, resFunction, paginateResult);\r\n\r\n if (grabLog[0]) grabLog[0].response = response;\r\n if (resFunction) response = resFunction(response);\r\n\r\n return response as any;\r\n } catch (error: any) {\r\n if (typeof onError === \"function\")\r\n onError(error.message, baseURL + path, params);\r\n\r\n if (merged.retryAttempts && merged.retryAttempts > 0) {\r\n return await grab(path, {\r\n ...options,\r\n retryAttempts: --merged.retryAttempts,\r\n });\r\n }\r\n\r\n if (!error.message.includes(\"signal\") && debug) {\r\n logger(`Error: ${error.message}\\nPath:${baseURL + path}\\n`, {\r\n color: \"red\",\r\n });\r\n if (typeof document !== \"undefined\") showAlert(error.message);\r\n }\r\n\r\n response = response || {};\r\n response.error = error.message;\r\n const resFn = typeof responseOption === \"function\" ? responseOption : null;\r\n if (resFn) {\r\n response.data = resFn({ isLoading: undefined, error: error.message });\r\n response = response.data;\r\n } else {\r\n delete response.isLoading;\r\n }\r\n return response as any;\r\n }\r\n}\r\n","/**\r\n * @file core/flow-control.ts\r\n * @description Logic for managing request flow and option merging.\r\n * Handles debouncing, request repetition, and default option application.\r\n */\r\n\r\nimport { GrabOptions, GrabResponse, GrabFunction } from \"../common/types\";\r\nimport { debouncer } from \"../common/utils\";\r\n\r\n/**\r\n * Merges provided options with global defaults.\r\n */\r\nexport function getMergedOptions<TResponse, TParams>(\r\n options?: GrabOptions<TResponse, TParams>\r\n): GrabOptions<TResponse, TParams> & { [key: string]: any } {\r\n const defaults = (typeof window !== \"undefined\"\r\n ? window?.grab?.defaults\r\n : (globalThis as any)?.grab?.defaults || {}) as GrabOptions<TResponse, TParams>;\r\n\r\n return {\r\n ...defaults,\r\n ...options,\r\n };\r\n}\r\n\r\n/**\r\n * Handles flow control logic like debouncing and repeating requests.\r\n */\r\nexport async function handleFlowControl<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): Promise<GrabResponse<TResponse> | null> {\r\n const { debounce = 0, repeat = 0, repeatEvery = null, setDefaults = false } = mergedOptions;\r\n\r\n // Handle debounce\r\n if (debounce > 0) {\r\n const task = await debouncer(async () => {\r\n await grab(path, { ...options, debounce: 0 });\r\n }, debounce * 1000);\r\n await task();\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Handle repeat options\r\n if (repeat > 1) {\r\n for (let i = 0; i < repeat; i++) {\r\n await grab(path, { ...options, repeat: 0 });\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n if (repeatEvery) {\r\n setInterval(async () => {\r\n await grab(path, { ...options, repeat: 0, repeatEvery: null });\r\n }, repeatEvery * 1000);\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n // Store defaults if requested\r\n if (setDefaults) {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab);\r\n if (target) {\r\n target.defaults = { ...options, setDefaults: undefined };\r\n }\r\n return (mergedOptions.response || {}) as GrabResponse<TResponse>;\r\n }\r\n\r\n return null;\r\n}\r\n","/**\r\n * @file response/response-handler.ts\r\n * @description Logic for handling API responses.\r\n * Includes functions for initializing response objects and mapping results.\r\n */\r\n\r\n/**\r\n * Initializes the response object and result function.\r\n */\r\nexport function initializeResponse<TResponse>(responseOption: any): {\r\n response: any;\r\n resFunction: ((data: any) => any) | null;\r\n} {\r\n const resFunction = typeof responseOption === \"function\" ? responseOption : null;\r\n let response = (!responseOption || resFunction) ? {} : responseOption;\r\n return { response, resFunction };\r\n}\r\n\r\n/**\r\n * Maps the raw result onto the response object.\r\n */\r\nexport function mapResultToResponse(res: any, response: any, resFunction: any, paginateResult: string | null): any {\r\n if (typeof res === \"object\" && res !== null) {\r\n for (let key of Object.keys(res)) {\r\n response[key] = (paginateResult === key && Array.isArray(response[key]))\r\n ? [...response[key], ...res[key]]\r\n : res[key];\r\n }\r\n response.data = res;\r\n } else {\r\n if (resFunction) {\r\n response = resFunction({ data: res, ...res });\r\n } else if (typeof response === \"object\") {\r\n response.data = res;\r\n }\r\n }\r\n return response;\r\n}\r\n","/**\r\n * @file core/regrab-events.ts\r\n * @description Event listeners for automatic data re-fetching.\r\n * Handles stale cache, window focus, and network status changes.\r\n */\r\n\r\nimport { GrabOptions, GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Sets up event listeners for regrabbing data on stale, focus, or network changes.\r\n */\r\nexport function handleRegrabEvents<TResponse, TParams>(\r\n path: string,\r\n options: GrabOptions<TResponse, TParams>,\r\n mergedOptions: any,\r\n grab: GrabFunction\r\n): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const { regrabOnStale, regrabOnFocus, regrabOnNetwork, cache, cacheForTime = 60 } = mergedOptions;\r\n const regrab = async () => await grab(path, { ...options, cache: false });\r\n\r\n if (regrabOnStale && cache) setTimeout(regrab, 1000 * cacheForTime);\r\n if (regrabOnNetwork) window.addEventListener(\"online\", regrab);\r\n if (regrabOnFocus) {\r\n window.addEventListener(\"focus\", regrab);\r\n document.addEventListener(\"visibilitychange\", async () => {\r\n if (document.visibilityState === \"visible\") await regrab();\r\n });\r\n }\r\n}\r\n","/**\r\n * @file core/cache-pagination.ts\r\n * @description Cache management and pagination state tracking.\r\n * Determines cache hits and updates current page numbers for infinite scroll.\r\n */\r\n\r\n/**\r\n * Manages cache hits and pagination state updates.\r\n */\r\nexport function manageCacheAndPagination(\r\n path: string,\r\n params: any,\r\n mergedOptions: any,\r\n response: any,\r\n resFunction: any,\r\n grabLog: any[]\r\n): { params: any; priorRequest: any; response: any; paramsAsText: string } {\r\n const { cache, cacheForTime, infiniteScroll } = mergedOptions;\r\n const [paginateKey, paginateResult] = (infiniteScroll as any) || [];\r\n\r\n const paramsAsText = JSON.stringify(\r\n paginateKey ? { ...params, [paginateKey as string]: undefined } : params\r\n );\r\n\r\n let priorRequest = grabLog.find(e => e.request === paramsAsText && e.path === path);\r\n\r\n if (!paginateKey) {\r\n // Clear response for fresh request\r\n for (let key of Object.keys(response)) response[key] = undefined;\r\n\r\n // Cache hit check\r\n if (cache && priorRequest?.response &&\r\n (!cacheForTime || priorRequest.lastFetchTime > Date.now() - 1000 * cacheForTime)) {\r\n for (let key of Object.keys(priorRequest.response)) {\r\n response[key] = priorRequest.response[key];\r\n }\r\n if (resFunction) response = resFunction(response);\r\n }\r\n } else {\r\n // Pagination logic\r\n let pageNumber = (priorRequest?.currentPage || 0) + 1 || params?.[paginateKey] || 1;\r\n if (!priorRequest) {\r\n response[paginateResult] = [];\r\n pageNumber = 1;\r\n } else {\r\n priorRequest.currentPage = pageNumber;\r\n }\r\n params = { ...params, [paginateKey]: pageNumber };\r\n }\r\n\r\n return { params, priorRequest, response, paramsAsText };\r\n}\r\n","/**\r\n * @file response/infinite-scroll.ts\r\n * @description Infinite scroll pagination logic.\r\n * Manages scroll event listeners and triggers automatic next-page fetching.\r\n */\r\n\r\nimport { log } from \"@grab-url/log\";\r\nimport { GrabFunction } from \"../common/types\";\r\n\r\n/**\r\n * Configures infinite scroll listener.\r\n */\r\nexport function setupInfiniteScroll(\r\n path: string,\r\n options: any,\r\n infiniteScroll: any,\r\n priorRequest: any,\r\n grab: GrabFunction,\r\n): void {\r\n if (typeof window === \"undefined\" || !infiniteScroll?.length) return;\r\n\r\n const [paginateKey, , paginateElement] = infiniteScroll;\r\n if (typeof paginateElement === \"undefined\") return;\r\n\r\n let paginateDOM =\r\n typeof paginateElement === \"string\"\r\n ? document.querySelector(paginateElement)\r\n : paginateElement;\r\n\r\n if (!paginateDOM) {\r\n log(\"paginateDOM not found\", { color: \"red\" });\r\n return;\r\n }\r\n\r\n if (\r\n (window as any).scrollListener &&\r\n typeof (paginateDOM as any).removeEventListener === \"function\"\r\n ) {\r\n (paginateDOM as any).removeEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n }\r\n\r\n (window as any).scrollListener = (event: Event) => {\r\n const t = event.target as HTMLElement;\r\n localStorage.setItem(\r\n \"scroll\",\r\n JSON.stringify([t.scrollTop, t.scrollLeft, paginateElement]),\r\n );\r\n\r\n if (t.scrollHeight - t.scrollTop <= t.clientHeight + 200) {\r\n grab(path, {\r\n ...options,\r\n cache: false,\r\n [paginateKey as string]: (priorRequest?.currentPage || 0) + 1,\r\n });\r\n }\r\n };\r\n\r\n (paginateDOM as any).addEventListener(\r\n \"scroll\",\r\n (window as any).scrollListener,\r\n );\r\n}\r\n","/**\r\n * @file core/request-executor.ts\r\n * @description Logic for preparing and executing API requests.\r\n * Handles both mock responses and real network fetch calls.\r\n */\r\n\r\nimport { GrabFunction, GrabMockHandler } from \"../common/types\";\r\nimport { wait } from \"../common/utils\";\r\n\r\n/**\r\n * Prepares the fetch parameters and URL.\r\n */\r\nexport function prepareFetchRequest(\r\n method: string,\r\n headers: any,\r\n body: any,\r\n params: any,\r\n cache: boolean,\r\n signal: AbortSignal\r\n): { fetchParams: RequestInit; paramsGETRequest: string } {\r\n const isBodyMethod = [\"POST\", \"PUT\", \"PATCH\"].includes(method);\r\n\r\n const fetchParams: RequestInit = {\r\n method,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n ...headers,\r\n },\r\n body: body || (isBodyMethod ? JSON.stringify(params) : null),\r\n redirect: \"follow\",\r\n cache: cache ? \"force-cache\" : \"no-store\",\r\n signal,\r\n };\r\n\r\n let paramsGETRequest = \"\";\r\n if (!isBodyMethod) {\r\n paramsGETRequest = (Object.keys(params).length ? \"?\" : \"\") + new URLSearchParams(params).toString();\r\n }\r\n\r\n return { fetchParams, paramsGETRequest };\r\n}\r\n\r\n/**\r\n * Executes the request, either via mock or actual fetch.\r\n */\r\nexport async function executeRequest(\r\n baseURL: string,\r\n path: string,\r\n paramsGETRequest: string,\r\n fetchParams: RequestInit,\r\n params: any,\r\n onStream: any\r\n): Promise<any> {\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab) as GrabFunction;\r\n const mockHandler = target?.mock?.[path] as GrabMockHandler;\r\n const paramsAsText = JSON.stringify(params);\r\n\r\n if (mockHandler &&\r\n (!mockHandler.method || mockHandler.method === fetchParams.method) &&\r\n (!mockHandler.params || paramsAsText === JSON.stringify(mockHandler.params))) {\r\n await wait(mockHandler.delay || 0);\r\n return typeof mockHandler.response === \"function\" ? mockHandler.response(params) : mockHandler.response;\r\n }\r\n\r\n const fetchRes = await fetch(baseURL + path + paramsGETRequest, fetchParams).catch(e => {\r\n throw new Error(e.message);\r\n });\r\n\r\n if (!fetchRes.ok) throw new Error(`HTTP error: ${fetchRes.status} ${fetchRes.statusText}`);\r\n\r\n if (onStream) {\r\n await onStream(fetchRes.body);\r\n return null;\r\n }\r\n\r\n const type = fetchRes.headers.get(\"content-type\");\r\n return await (\r\n type\r\n ? type.includes(\"application/json\")\r\n ? fetchRes.json()\r\n : type.includes(\"application/pdf\") || type.includes(\"application/octet-stream\")\r\n ? fetchRes.blob()\r\n : fetchRes.text()\r\n : fetchRes.json()\r\n ).catch(e => {\r\n throw new Error(\"Error parsing response: \" + e);\r\n });\r\n}\r\n","import { grab as coreGrab } from \"./core/core\";\r\nimport { setupDevTools } from \"./devtools/devtools\";\r\nimport { GrabFunction, GrabOptions } from \"./common/types\";\r\nimport { log } from \"@grab-url/log\";\r\n\r\n// Add instance method to the core grab function\r\nconst grab: GrabFunction = coreGrab as any;\r\n\r\n/**\r\n * Creates a new instance of grab with default options\r\n * to apply to all requests made by this instance.\r\n *\r\n * @param defaults - Options for all requests made by this instance.\r\n * @returns A new grab() function using those default options.\r\n */\r\ngrab.instance = (defaults: Partial<GrabOptions> = {}) =>\r\n ((path: string, options: Partial<GrabOptions> = {}) =>\r\n coreGrab(path, { ...defaults, ...options })) as any;\r\n\r\n// Initialize global state\r\ngrab.log = [];\r\ngrab.mock = {};\r\ngrab.defaults = {};\r\n\r\n// Handle global registration for both Browser and Node.js environments\r\nif (typeof window !== \"undefined\") {\r\n // @ts-ignore\r\n window.log = log;\r\n window.grab = grab;\r\n\r\n // Setup visual dev tools\r\n setupDevTools();\r\n\r\n // Restore scroll position when page loads for infinite scroll persistence\r\n document.addEventListener(\"DOMContentLoaded\", () => {\r\n try {\r\n const scrollData = localStorage.getItem(\"scroll\");\r\n if (!scrollData) return;\r\n\r\n const [scrollTop, scrollLeft, paginateElement] = JSON.parse(scrollData);\r\n if (!scrollTop || !paginateElement) return;\r\n\r\n const el = document.querySelector(paginateElement);\r\n if (el) {\r\n el.scrollTop = scrollTop;\r\n el.scrollLeft = scrollLeft;\r\n }\r\n } catch (e) {\r\n console.warn(\"Failed to restore scroll position\", e);\r\n }\r\n });\r\n} else if (typeof globalThis !== \"undefined\") {\r\n (globalThis as any).log = log;\r\n (globalThis as any).grab = grab;\r\n}\r\n\r\n// Re-export core function and all types\r\nexport { grab };\r\nexport default grab;\r\n\r\nexport { log };\r\nexport * from \"./common/types\";\r\nexport * from \"./devtools/devtools\";\r\nexport * from \"./common/utils\";\r\n"],"names":["debouncer","async","func","wait","timeout","args","clearTimeout","setTimeout","s","Promise","res","buildUrl","baseURL","path","t","startsWith","finalBaseURL","finalPath","endsWith","slice","showAlert","msg","document","list","o","getElementById","body","appendChild","createElement","id","setAttribute","innerHTML","addEventListener","e","target","remove","closeBtn","onclick","setupDevTools","grab","window","log","key","ctrlKey","altKey","html","request","printJSONStructure","response","Date","lastFetchTime","toLocaleString","options","merged","defaults","globalThis","getMergedOptions","headers","responseOption","method","post","put","patch","cache","process","env","SERVER_API_URL","cancelOngoingIfNew","cancelNewIfOngoing","rateLimit","debug","infiniteScroll","logger","onRequest","onResponse","onError","onStream","params","urlConfig","initialized","resFunction","initializeResponse","grabLog","isLoading","flowResult","mergedOptions","debounce","repeat","repeatEvery","setDefaults","task","i","setInterval","handleFlowControl","regrabOnStale","regrabOnFocus","regrabOnNetwork","cacheForTime","regrab","visibilityState","handleRegrabEvents","updatedParams","priorRequest","updatedResponse","paramsAsText","paginateKey","paginateResult","JSON","stringify","find","pageNumber","currentPage","Object","keys","now","manageCacheAndPagination","length","paginateElement","paginateDOM","querySelector","scrollListener","removeEventListener","event","localStorage","setItem","scrollTop","scrollLeft","scrollHeight","clientHeight","color","setupInfiniteScroll","Error","controller","abort","AbortController","signal","AbortSignal","unshift","fetchParams","paramsGETRequest","isBodyMethod","includes","Accept","redirect","URLSearchParams","toString","prepareFetchRequest","modified","Array","isArray","startTime","mockHandler","mock","delay","fetchRes","fetch","catch","message","ok","status","statusText","type","get","json","blob","text","executeRequest","elapsedTime","Number","toFixed","data","mapResultToResponse","error","retryAttempts","resFn","coreGrab","instance","scrollData","getItem","parse","el","console","warn"],"mappings":"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"}
|