grab-url 1.5.5 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{readme.md → README.md} +19 -14
- package/dist/archiver-web.cjs.js +2 -0
- package/dist/archiver-web.cjs.js.map +1 -0
- package/dist/archiver-web.d.ts +83 -0
- package/dist/archiver-web.es.js +2 -0
- package/dist/archiver-web.es.js.map +1 -0
- package/dist/bin-compress.cjs.js +3 -0
- package/dist/bin-compress.cjs.js.map +1 -0
- package/dist/bin-compress.es.js +3 -0
- package/dist/bin-compress.es.js.map +1 -0
- package/dist/bin-extract.cjs.js +3 -0
- package/dist/bin-extract.cjs.js.map +1 -0
- package/dist/bin-extract.d.ts +1 -0
- package/dist/bin-extract.es.js +3 -0
- package/dist/bin-extract.es.js.map +1 -0
- package/dist/grab-api.cjs.js +1 -1
- package/dist/grab-api.cjs.js.map +1 -1
- package/dist/grab-api.d.ts +2 -2
- 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.d.ts +225 -0
- package/dist/grab-url-cli.es.js +1 -1
- package/dist/grab-url-cli.es.js.map +1 -1
- package/dist/icons.cjs.js.map +1 -1
- package/dist/icons.es.js.map +1 -1
- package/dist/log.cjs.js.map +1 -1
- package/dist/log.es.js.map +1 -1
- package/package.json +20 -12
- package/dist/download.cjs.js +0 -3
- package/dist/download.cjs.js.map +0 -1
- package/dist/download.es.js +0 -3
- package/dist/download.es.js.map +0 -1
- package/dist/grab-url-CI2Oab5Q.js +0 -2
- package/dist/grab-url-CI2Oab5Q.js.map +0 -1
- package/dist/grab-url-CTh-Ji-R.cjs +0 -2
- package/dist/grab-url-CTh-Ji-R.cjs.map +0 -1
- package/dist/grab-url-DZJbPMmb.js +0 -2
- package/dist/grab-url-DZJbPMmb.js.map +0 -1
- package/dist/index-B2gz_l62.js +0 -2
- package/dist/index-B2gz_l62.js.map +0 -1
- package/dist/index-CReq3tEO.js +0 -2
- package/dist/index-CReq3tEO.js.map +0 -1
- package/dist/index-CoSpC0Zl.cjs +0 -2
- package/dist/index-CoSpC0Zl.cjs.map +0 -1
- package/dist/index-DJSiTAmP.js +0 -2
- package/dist/index-DJSiTAmP.js.map +0 -1
- package/dist/index-DNNYnR_-.cjs +0 -2
- package/dist/index-DNNYnR_-.cjs.map +0 -1
- package/dist/index-DQ_hgZsj.js +0 -2
- package/dist/index-DQ_hgZsj.js.map +0 -1
- package/dist/index-DdRkKpiZ.cjs +0 -2
- package/dist/index-DdRkKpiZ.cjs.map +0 -1
- package/dist/index-DeMphuuT.cjs +0 -2
- package/dist/index-DeMphuuT.cjs.map +0 -1
- package/dist/index-DjU37R-q.js +0 -2
- package/dist/index-DjU37R-q.js.map +0 -1
- package/dist/index-Dz8RTMHl.cjs +0 -2
- package/dist/index-Dz8RTMHl.cjs.map +0 -1
- package/dist/index-wUR1o2Bg.js +0 -2
- package/dist/index-wUR1o2Bg.js.map +0 -1
- package/dist/spinners-eoupe-P4.js +0 -2
- package/dist/spinners-eoupe-P4.js.map +0 -1
- package/src/grab-api/common/types.ts +0 -226
- package/src/grab-api/common/utils.ts +0 -56
- package/src/grab-api/core/cache-pagination.ts +0 -52
- package/src/grab-api/core/core.ts +0 -142
- package/src/grab-api/core/flow-control.ts +0 -70
- package/src/grab-api/core/regrab-events.ts +0 -31
- package/src/grab-api/core/request-executor.ts +0 -89
- package/src/grab-api/index.ts +0 -64
- package/src/grab-api/logging/colors.ts +0 -94
- package/src/grab-api/logging/log-json.ts +0 -93
- package/src/grab-api/logging/structure.ts +0 -112
- package/src/grab-api/response/infinite-scroll.ts +0 -52
- package/src/grab-api/response/response-handler.ts +0 -38
- package/src/grab-api/ui/devtools.ts +0 -71
- package/src/grab-url-cli/arg-parser.ts +0 -135
- package/src/grab-url-cli/download-format.ts +0 -168
- package/src/grab-url-cli/download-keyboard.ts +0 -131
- package/src/grab-url-cli/download-multi.ts +0 -402
- package/src/grab-url-cli/download-single.ts +0 -222
- package/src/grab-url-cli/download-spinners.ts +0 -119
- package/src/grab-url-cli/download-state.ts +0 -156
- package/src/grab-url-cli/downloader.ts +0 -206
- package/src/grab-url-cli/index.ts +0 -173
- package/src/icons/cli/spinners.js +0 -818
- package/src/icons/svg/index.ts +0 -313
- package/src/icons/svg/loading-bouncy-ball.svg +0 -3
- package/src/icons/svg/loading-double-ring.svg +0 -6
- package/src/icons/svg/loading-eclipse.svg +0 -3
- package/src/icons/svg/loading-ellipsis.svg +0 -16
- package/src/icons/svg/loading-floating-search.svg +0 -11
- package/src/icons/svg/loading-gears.svg +0 -3
- package/src/icons/svg/loading-infinity.svg +0 -3
- package/src/icons/svg/loading-orbital.svg +0 -8
- package/src/icons/svg/loading-pacman.svg +0 -22
- package/src/icons/svg/loading-pulse-bars.svg +0 -12
- package/src/icons/svg/loading-red-blue-ball.svg +0 -10
- package/src/icons/svg/loading-reload-arrow.svg +0 -5
- package/src/icons/svg/loading-ring.svg +0 -3
- package/src/icons/svg/loading-ripple.svg +0 -7
- package/src/icons/svg/loading-spinner-oval.svg +0 -49
- package/src/icons/svg/loading-spinner.svg +0 -57
- package/src/icons/svg/loading-square-blocks.svg +0 -17
- /package/dist/{download.d.ts → bin-compress.d.ts} +0 -0
package/{readme.md → README.md}
RENAMED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img width="400px" src="https://i.imgur.com/
|
|
2
|
+
<img width="400px" src="https://i.imgur.com/RH80JGZ.png" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
|
|
7
|
+
<a href="https://discord.gg/SJdBqBz3tV"><img src="https://img.shields.io/discord/1110227955554209923.svg?label=Chat&logo=Discord&colorB=7289da&style=flat" alt="Join Discord" /></a>
|
|
8
|
+
<a href="https://github.com/vtempest/GRAB-URL/actions/workflows/tests.yml">
|
|
9
|
+
<img src="https://github.com/vtempest/GRAB-URL/actions/workflows/tests.yml/badge.svg" alt="Build Status" />
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://github.com/vtempest/GRAB-URL/discussions">
|
|
12
|
+
<img alt="GitHub Stars" src="https://img.shields.io/github/stars/vtempest/GRAB-URL" /></a>
|
|
13
|
+
<a href="https://npmjs.org/package/grab-url"><img alt="NPM Version" src="https://img.shields.io/npm/v/grab-url" /></a>
|
|
14
|
+
<a href="https://github.com/vtempest/GRAB-URL/discussions"><img alt="GitHub Discussions"
|
|
12
15
|
src="https://img.shields.io/github/discussions/vtempest/GRAB-URL" /></a>
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
<a href="https://github.blog/developer-skills/github/beginners-guide-to-github-creating-a-pull-request/"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"/></a>
|
|
17
|
+
<a href="https://codespaces.new/vtempest/GRAB-URL"><img src="https://github.com/codespaces/badge.svg" width="150" height="20"/></a>
|
|
18
|
+
<a href="https://bundlephobia.com/package/grab-url"><img src="https://img.shields.io/bundlephobia/minzip/grab-url" /></a>
|
|
19
|
+
|
|
15
20
|
</p>
|
|
16
21
|
<h3 align="center">
|
|
17
|
-
<a href="https://grab
|
|
18
|
-
<a href="https://grab
|
|
22
|
+
<a href="https://grab.js.org"> 📑 Docs </a>
|
|
23
|
+
<a href="https://grab.js.org/docs/examples"> 🎯 Example Strategies </a>
|
|
19
24
|
</h3>
|
|
20
25
|
|
|
21
26
|
```bash
|
|
@@ -31,7 +36,7 @@ npx grab-url https://releases.ubuntu.com/24.04.2/ubuntu-24.04.2-live-server-amd6
|
|
|
31
36
|
1. **GRAB is the FBEST Request Manager: Functionally Brilliant, Elegantly Simple Tool**: One Function, no dependencies, minimalist syntax, [more features than alternatives](https://grab.js.org/docs/Comparisons)
|
|
32
37
|
2. **Auto-JSON Convert**: Pass parameters and get response or error in JSON, handling other data types as is.
|
|
33
38
|
3. **isLoading Status**: Sets `.isLoading=true` on the pre-initialized response object so you can show a "Loading..." in any framework
|
|
34
|
-
4. **
|
|
39
|
+
4. **[Claude Skill](https://grab.js.org/docs/claude-skill)** Copy this link as markdown and paste into ~/.claude/skills/use-grab-request/SKILL.md or similar for Antigravity, Cursor, Gemini, Codex, etc...
|
|
35
40
|
5. **Mock Server Support**: Configure `window.grab.mock` for development and testing environments
|
|
36
41
|
6. **Cancel Duplicates**: Prevent this request if one is ongoing to same path & params, or cancel the ongoing request.
|
|
37
42
|
7. **Timeout & Retry**: Customizable request timeout, default 30s, and auto-retry on error
|
|
@@ -43,8 +48,8 @@ npx grab-url https://releases.ubuntu.com/24.04.2/ubuntu-24.04.2-live-server-amd6
|
|
|
43
48
|
13. **Regrab On Error**: Regrab on timeout error, or on window refocus, or on network change, or on stale data.
|
|
44
49
|
14. **Framework Agnostic**: Alternatives like TanStack work only in component initialization and depend on React & others.
|
|
45
50
|
15. **Globals**: Adds to window in browser or global in Node.js so you only import once: `grab()`, `log()`, `grab.log`, `grab.mock`, `grab.defaults`
|
|
46
|
-
16. **
|
|
47
|
-
17. **Request Stategies**: [🎯 Examples](https://grab.js.org/docs/examples
|
|
51
|
+
16. **Debug Logging**: Adds global `log()` and prints colored JSON structure, response, timing for requests in test.
|
|
52
|
+
17. **Request Stategies**: [🎯 Examples](https://grab.js.org/docs/examples) show common stategies like debounce, repeat, proxy, unit tests, interceptors, file upload, etc
|
|
48
53
|
18. **Rate Limiting**: Built-in rate limiting to prevent multi-click cascading responses, require to wait seconds between requests.
|
|
49
54
|
19. **Repeat**: Repeat request this many times, or repeat every X seconds to poll for updates.
|
|
50
55
|
20. **Loading Icons**: Import from `grab-url/icons` to get enhanced animated loading icons.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.create,Object.defineProperty,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.prototype.hasOwnProperty;Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let e=null;async function t(){if(e)return e;if("undefined"!=typeof process&&process?.versions?.node){if(!(await async function(){if("undefined"!=typeof navigator&&"Cloudflare-Workers"===navigator.userAgent)return!1;try{return require.resolve("libarchive.js"),!0}catch{}try{const{spawn:e}=await import("node:child_process");return await new Promise(t=>{const r=e("npm",["i","-g","libarchive.js"],{stdio:"ignore"});r.on("close",()=>t()),r.on("error",()=>t())}),!0}catch{return!1}}()))throw console.error("libarchive.js not available (install failed). Use browser fallback or manual npm i -g libarchive.js."),new Error("libarchive.js Node.js unavailable");const t=await import("libarchive.js/dist/libarchive-node.mjs");e=t.Archive}else{const t=await import("libarchive.js/main.js");e=t.Archive,e.init({workerUrl:"libarchive.js/dist/worker-bundle.js"})}return e}exports._setArchiveClass=function(t){e=t},exports.compress=async function(e){const{files:r,outputName:o,format:a="USTAR",compression:i="NONE",compressionLevel:n=6,passphrase:s}=e,c=r.map(({path:e,content:t})=>{let r;if("string"==typeof t)r=new Blob([t]);else if(t instanceof Uint8Array)r=new Blob([t]);else if(t instanceof ArrayBuffer)r=new Blob([t]);else{if(!(t instanceof Blob))throw new Error(`Unsupported content type for ${e}`);r=t}return{file:r,pathname:e}}),l=await t();return{blob:await l.write({files:c,outputFileName:o,compression:i,format:a,compressionLevel:n,passphrase:s||null}),mime:"application/zip",downloadName:o}},exports.extract=async function(e){const{archiveBuffer:r,folderPath:o="",password:a}=e;if(!r)throw new Error("Must provide archiveBuffer");const i=new Blob([new Uint8Array(r)]),n=await t(),s=await n.open(i);a&&await s.usePassword(a);const c=await s.getFilesObject(),l=[],p=async(e,t="")=>{for(const[r,a]of Object.entries(e)){const e=t?`${t}/${r}`:r;if(e.startsWith(o)&&!e.endsWith("/")){const t=e.slice(o.length).replace(/^\//,""),r=await a.extract();let i;try{i=await r.text()}catch{const e=await r.arrayBuffer();i=btoa(String.fromCharCode(...new Uint8Array(e)))}l.push({path:t,size:r.size,content:i,mime:r.type||"application/octet-stream"})}"object"!=typeof a||a?.extract||await p(a,e)}};return await p(c),l};
|
|
2
|
+
//# sourceMappingURL=archiver-web.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archiver-web.cjs.js","sources":["../packages/archiver-web/src/index.ts"],"sourcesContent":["/**\r\n * Universal Archive Extractor & Creator using libarchive.js (WASM).\r\n * Supports: ZIP/7z/RAR4/5/TAR + GZIP/BZIP2/LZMA. Node.js/browser.\r\n * No streaming extract (full download first). Create archives from files.\r\n * @module archiveUtils\r\n * @example\r\n * const files = await extractFolder({ archiveUrl: 'https://ex.zip', folderPath: 'src/' });\r\n * const archiveBlob = await createArchive({ files: [...], outputName: 'out.tar.gz' });\r\n */\r\n\r\n\r\nlet ArchiveClass: any = null;\r\n\r\n/** @internal For testing: inject a mock Archive class. */\r\nexport function _setArchiveClass(cls: any) {\r\n ArchiveClass = cls;\r\n}\r\n\r\nasync function ensureLibarchiveNode(): Promise<boolean> {\r\n // Skip in Cloudflare Workers\r\n if (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') {\r\n return false;\r\n }\r\n\r\n // Check if already installed (silent resolve)\r\n try {\r\n require.resolve('libarchive.js');\r\n return true;\r\n } catch {}\r\n\r\n // Lazy load child_process and install globally\r\n let spawn: typeof import('child_process').spawn;\r\n try {\r\n const { spawn } = await import('node:child_process');\r\n await new Promise<void>((resolve) => {\r\n const proc = spawn('npm', ['i', '-g', 'libarchive.js'], { stdio: 'ignore' });\r\n proc.on('close', () => resolve());\r\n proc.on('error', () => resolve()); // npm unavailable: silent fail\r\n });\r\n return true;\r\n } catch {\r\n return false; // Silent fail if spawn/import fails\r\n }\r\n}\r\n\r\nasync function getArchive() {\r\n if (ArchiveClass) return ArchiveClass;\r\n\r\n if (typeof process !== 'undefined' && process?.versions?.node) {\r\n const installed = await ensureLibarchiveNode();\r\n if (!installed) {\r\n console.error('libarchive.js not available (install failed). Use browser fallback or manual npm i -g libarchive.js.');\r\n throw new Error('libarchive.js Node.js unavailable');\r\n }\r\n const mod = await import('libarchive.js/dist/libarchive-node.mjs');\r\n ArchiveClass = mod.Archive;\r\n } else {\r\n const mod = await import('libarchive.js/main.js');\r\n ArchiveClass = mod.Archive;\r\n ArchiveClass.init({\r\n workerUrl: 'libarchive.js/dist/worker-bundle.js'\r\n });\r\n }\r\n return ArchiveClass;\r\n}\r\n\r\n\r\nimport type { ExtractEvent, \r\n ArchiveCompression,\r\n ArchiveFormat,\r\n CreateOptions, \r\n ArchiveFile } from \"./types.js\";\r\n\r\n/**\r\n * Extract files from an archive ArrayBuffer.\r\n * @param options - Extract configuration\r\n * @param options.archiveBuffer - The archive to extract (ArrayBuffer)\r\n * @param options.folderPath - Folder to extract (e.g., 'src/'), empty=root\r\n * @param options.password - Optional password\r\n * @returns Array of extracted files\r\n * @throws Error on unsupported format\r\n */\r\nexport async function extract(options: {\r\n archiveBuffer: ArrayBuffer;\r\n folderPath?: string;\r\n password?: string;\r\n}): Promise<ExtractEvent[]> {\r\n const { archiveBuffer, folderPath = \"\", password } = options;\r\n\r\n if (!archiveBuffer) {\r\n throw new Error(\"Must provide archiveBuffer\");\r\n }\r\n const blob = new Blob([new Uint8Array(archiveBuffer)]);\r\n\r\n /** Open with libarchive.js */\r\n const Archive = await getArchive();\r\n const archive = await Archive.open(blob as File);\r\n if (password) await archive.usePassword(password);\r\n\r\n /** Get file tree */\r\n const filesObj = await archive.getFilesObject();\r\n const files: ExtractEvent[] = [];\r\n\r\n /**\r\n * Recursively walk file tree, filter by folderPath, extract matching files.\r\n * @param obj - Nested file/dir object\r\n * @param prefix - Current path prefix\r\n */\r\n const walker = async (obj: any, prefix: string = \"\"): Promise<void> => {\r\n for (const [name, entry] of Object.entries(obj)) {\r\n const fullPath = prefix ? `${prefix}/${name}` : name;\r\n if (fullPath.startsWith(folderPath) && !fullPath.endsWith(\"/\")) {\r\n const relativePath = fullPath\r\n .slice(folderPath.length)\r\n .replace(/^\\//, \"\");\r\n const fileBlob = await (entry as any).extract();\r\n let content: string;\r\n try {\r\n content = await fileBlob.text();\r\n } catch {\r\n const buffer = await fileBlob.arrayBuffer();\r\n content = btoa(String.fromCharCode(...new Uint8Array(buffer))); // Base64 binary\r\n }\r\n files.push({\r\n path: relativePath,\r\n size: fileBlob.size,\r\n content,\r\n mime: fileBlob.type || \"application/octet-stream\",\r\n });\r\n }\r\n if (typeof entry === 'object' && !(entry as any)?.extract) {\r\n // Subdir (directories don't have .extract, files do)\r\n await walker(entry, fullPath);\r\n }\r\n }\r\n };\r\n\r\n await walker(filesObj);\r\n return files;\r\n}\r\n\r\n\r\n\r\n/**\r\n * Create archive from files array.\r\n * @param options - Create configuration\r\n * @param options.files - Array of {path: string, content: string|Uint8Array|ArrayBuffer|Blob}\r\n * @param options.outputName - Archive filename (e.g., 'out.tar.gz')\r\n * @param options.format - Archive format (defaults USTAR)\r\n * @param options.compression - Compression (defaults NONE)\r\n * @param options.compressionLevel - 1-9 (defaults 6, format-dependent)\r\n * @param options.passphrase - Optional password\r\n * @returns Blob of archive (download/save as)\r\n * @throws Error on invalid options\r\n */\r\nexport async function compress(\r\n options: CreateOptions,\r\n): Promise<ArchiveFile> {\r\n const {\r\n files,\r\n outputName,\r\n format = \"USTAR\",\r\n compression = \"NONE\",\r\n compressionLevel = 6,\r\n passphrase,\r\n } = options;\r\n\r\n /** Convert files to Blobs with pathnames */\r\n const archiveFiles: { file: Blob; pathname: string }[] = files.map(\r\n ({ path, content }) => {\r\n let blobContent: Blob;\r\n if (typeof content === \"string\") {\r\n blobContent = new Blob([content]);\r\n } else if (content instanceof Uint8Array) {\r\n blobContent = new Blob([content as BlobPart]);\r\n } else if (content instanceof ArrayBuffer) {\r\n blobContent = new Blob([content]);\r\n } else if (content instanceof Blob) {\r\n blobContent = content;\r\n } else {\r\n throw new Error(`Unsupported content type for ${path}`);\r\n }\r\n return { file: blobContent, pathname: path };\r\n },\r\n );\r\n\r\n /** Create archive */\r\n const Archive = await getArchive();\r\n //@ts-ignore\r\n const archiveBlob = await Archive.write({\r\n files: archiveFiles,\r\n outputFileName: outputName,\r\n compression,\r\n format,\r\n compressionLevel, // Passed if supported\r\n passphrase: passphrase || null,\r\n });\r\n\r\n return {\r\n blob: archiveBlob,\r\n mime: \"application/zip\", // Adjust per format\r\n downloadName: outputName,\r\n };\r\n}\r\n\r\nexport type { ArchiveFormat, ArchiveCompression };\r\nexport type { ExtractEvent, CreateOptions, ArchiveFile };\r\n"],"names":["ArchiveClass","async","getArchive","process","versions","node","navigator","userAgent","require","resolve","spawn","import","Promise","proc","stdio","on","ensureLibarchiveNode","console","error","Error","mod","Archive","init","workerUrl","cls","options","files","outputName","format","compression","compressionLevel","passphrase","archiveFiles","map","path","content","blobContent","Blob","Uint8Array","ArrayBuffer","file","pathname","blob","write","outputFileName","mime","downloadName","archiveBuffer","folderPath","password","archive","open","usePassword","filesObj","getFilesObject","walker","obj","prefix","name","entry","Object","entries","fullPath","startsWith","endsWith","relativePath","slice","length","replace","fileBlob","extract","text","buffer","arrayBuffer","btoa","String","fromCharCode","push","size","type"],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export declare type ArchiveCompression = "NONE" | "GZIP" | "BZIP2" | "LZMA" | "XZ";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Created archive result.
|
|
5
|
+
*/
|
|
6
|
+
export declare interface ArchiveFile {
|
|
7
|
+
/** Archive Blob */
|
|
8
|
+
blob: Blob;
|
|
9
|
+
/** MIME for response */
|
|
10
|
+
mime: string;
|
|
11
|
+
/** Suggested download name */
|
|
12
|
+
downloadName: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export declare type ArchiveFormat = "ZIP" | "USTAR" | "_7ZIP" | "RAW" | "XAR" | "CPIO_NEWC";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create archive from files array.
|
|
19
|
+
* @param options - Create configuration
|
|
20
|
+
* @param options.files - Array of {path: string, content: string|Uint8Array|ArrayBuffer|Blob}
|
|
21
|
+
* @param options.outputName - Archive filename (e.g., 'out.tar.gz')
|
|
22
|
+
* @param options.format - Archive format (defaults USTAR)
|
|
23
|
+
* @param options.compression - Compression (defaults NONE)
|
|
24
|
+
* @param options.compressionLevel - 1-9 (defaults 6, format-dependent)
|
|
25
|
+
* @param options.passphrase - Optional password
|
|
26
|
+
* @returns Blob of archive (download/save as)
|
|
27
|
+
* @throws Error on invalid options
|
|
28
|
+
*/
|
|
29
|
+
export declare function compress(options: CreateOptions): Promise<ArchiveFile>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Options for createArchive.
|
|
33
|
+
*/
|
|
34
|
+
export declare interface CreateOptions {
|
|
35
|
+
/** Files to pack: path/content pairs */
|
|
36
|
+
files: Array<{
|
|
37
|
+
path: string;
|
|
38
|
+
content: string | Uint8Array | ArrayBuffer | Blob;
|
|
39
|
+
}>;
|
|
40
|
+
/** Output filename (.tar.gz etc.) */
|
|
41
|
+
outputName: string;
|
|
42
|
+
/** Archive format */
|
|
43
|
+
format?: ArchiveFormat;
|
|
44
|
+
/** Compression type */
|
|
45
|
+
compression?: ArchiveCompression;
|
|
46
|
+
/** Compression level 1-9 */
|
|
47
|
+
compressionLevel?: number;
|
|
48
|
+
/** Password */
|
|
49
|
+
passphrase?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extract files from an archive ArrayBuffer.
|
|
54
|
+
* @param options - Extract configuration
|
|
55
|
+
* @param options.archiveBuffer - The archive to extract (ArrayBuffer)
|
|
56
|
+
* @param options.folderPath - Folder to extract (e.g., 'src/'), empty=root
|
|
57
|
+
* @param options.password - Optional password
|
|
58
|
+
* @returns Array of extracted files
|
|
59
|
+
* @throws Error on unsupported format
|
|
60
|
+
*/
|
|
61
|
+
export declare function extract(options: {
|
|
62
|
+
archiveBuffer: ArrayBuffer;
|
|
63
|
+
folderPath?: string;
|
|
64
|
+
password?: string;
|
|
65
|
+
}): Promise<ExtractEvent[]>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extracted file info.
|
|
69
|
+
*/
|
|
70
|
+
export declare interface ExtractEvent {
|
|
71
|
+
/** Relative path */
|
|
72
|
+
path: string;
|
|
73
|
+
/** Size in bytes */
|
|
74
|
+
size: number;
|
|
75
|
+
/** Content (text or base64 binary) */
|
|
76
|
+
content: string;
|
|
77
|
+
/** MIME type */
|
|
78
|
+
mime: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Excluded from this release type: _setArchiveClass */
|
|
82
|
+
|
|
83
|
+
export { }
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=null;function t(t){e=t}async function r(){if(e)return e;if("undefined"!=typeof process&&process?.versions?.node){if(!(await async function(){if("undefined"!=typeof navigator&&"Cloudflare-Workers"===navigator.userAgent)return!1;try{return require.resolve("libarchive.js"),!0}catch{}try{const{spawn:e}=await import("node:child_process");return await new Promise(t=>{const r=e("npm",["i","-g","libarchive.js"],{stdio:"ignore"});r.on("close",()=>t()),r.on("error",()=>t())}),!0}catch{return!1}}()))throw console.error("libarchive.js not available (install failed). Use browser fallback or manual npm i -g libarchive.js."),new Error("libarchive.js Node.js unavailable");const t=await import("libarchive.js/dist/libarchive-node.mjs");e=t.Archive}else{const t=await import("libarchive.js/main.js");e=t.Archive,e.init({workerUrl:"libarchive.js/dist/worker-bundle.js"})}return e}async function i(e){const{archiveBuffer:t,folderPath:i="",password:a}=e;if(!t)throw new Error("Must provide archiveBuffer");const n=new Blob([new Uint8Array(t)]),o=await r(),s=await o.open(n);a&&await s.usePassword(a);const c=await s.getFilesObject(),l=[],f=async(e,t="")=>{for(const[r,a]of Object.entries(e)){const e=t?`${t}/${r}`:r;if(e.startsWith(i)&&!e.endsWith("/")){const t=e.slice(i.length).replace(/^\//,""),r=await a.extract();let n;try{n=await r.text()}catch{const e=await r.arrayBuffer();n=btoa(String.fromCharCode(...new Uint8Array(e)))}l.push({path:t,size:r.size,content:n,mime:r.type||"application/octet-stream"})}"object"!=typeof a||a?.extract||await f(a,e)}};return await f(c),l}async function a(e){const{files:t,outputName:i,format:a="USTAR",compression:n="NONE",compressionLevel:o=6,passphrase:s}=e,c=t.map(({path:e,content:t})=>{let r;if("string"==typeof t)r=new Blob([t]);else if(t instanceof Uint8Array)r=new Blob([t]);else if(t instanceof ArrayBuffer)r=new Blob([t]);else{if(!(t instanceof Blob))throw new Error(`Unsupported content type for ${e}`);r=t}return{file:r,pathname:e}}),l=await r();return{blob:await l.write({files:c,outputFileName:i,compression:n,format:a,compressionLevel:o,passphrase:s||null}),mime:"application/zip",downloadName:i}}export{t as _setArchiveClass,a as compress,i as extract};
|
|
2
|
+
//# sourceMappingURL=archiver-web.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archiver-web.es.js","sources":["../packages/archiver-web/src/index.ts"],"sourcesContent":["/**\r\n * Universal Archive Extractor & Creator using libarchive.js (WASM).\r\n * Supports: ZIP/7z/RAR4/5/TAR + GZIP/BZIP2/LZMA. Node.js/browser.\r\n * No streaming extract (full download first). Create archives from files.\r\n * @module archiveUtils\r\n * @example\r\n * const files = await extractFolder({ archiveUrl: 'https://ex.zip', folderPath: 'src/' });\r\n * const archiveBlob = await createArchive({ files: [...], outputName: 'out.tar.gz' });\r\n */\r\n\r\n\r\nlet ArchiveClass: any = null;\r\n\r\n/** @internal For testing: inject a mock Archive class. */\r\nexport function _setArchiveClass(cls: any) {\r\n ArchiveClass = cls;\r\n}\r\n\r\nasync function ensureLibarchiveNode(): Promise<boolean> {\r\n // Skip in Cloudflare Workers\r\n if (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') {\r\n return false;\r\n }\r\n\r\n // Check if already installed (silent resolve)\r\n try {\r\n require.resolve('libarchive.js');\r\n return true;\r\n } catch {}\r\n\r\n // Lazy load child_process and install globally\r\n let spawn: typeof import('child_process').spawn;\r\n try {\r\n const { spawn } = await import('node:child_process');\r\n await new Promise<void>((resolve) => {\r\n const proc = spawn('npm', ['i', '-g', 'libarchive.js'], { stdio: 'ignore' });\r\n proc.on('close', () => resolve());\r\n proc.on('error', () => resolve()); // npm unavailable: silent fail\r\n });\r\n return true;\r\n } catch {\r\n return false; // Silent fail if spawn/import fails\r\n }\r\n}\r\n\r\nasync function getArchive() {\r\n if (ArchiveClass) return ArchiveClass;\r\n\r\n if (typeof process !== 'undefined' && process?.versions?.node) {\r\n const installed = await ensureLibarchiveNode();\r\n if (!installed) {\r\n console.error('libarchive.js not available (install failed). Use browser fallback or manual npm i -g libarchive.js.');\r\n throw new Error('libarchive.js Node.js unavailable');\r\n }\r\n const mod = await import('libarchive.js/dist/libarchive-node.mjs');\r\n ArchiveClass = mod.Archive;\r\n } else {\r\n const mod = await import('libarchive.js/main.js');\r\n ArchiveClass = mod.Archive;\r\n ArchiveClass.init({\r\n workerUrl: 'libarchive.js/dist/worker-bundle.js'\r\n });\r\n }\r\n return ArchiveClass;\r\n}\r\n\r\n\r\nimport type { ExtractEvent, \r\n ArchiveCompression,\r\n ArchiveFormat,\r\n CreateOptions, \r\n ArchiveFile } from \"./types.js\";\r\n\r\n/**\r\n * Extract files from an archive ArrayBuffer.\r\n * @param options - Extract configuration\r\n * @param options.archiveBuffer - The archive to extract (ArrayBuffer)\r\n * @param options.folderPath - Folder to extract (e.g., 'src/'), empty=root\r\n * @param options.password - Optional password\r\n * @returns Array of extracted files\r\n * @throws Error on unsupported format\r\n */\r\nexport async function extract(options: {\r\n archiveBuffer: ArrayBuffer;\r\n folderPath?: string;\r\n password?: string;\r\n}): Promise<ExtractEvent[]> {\r\n const { archiveBuffer, folderPath = \"\", password } = options;\r\n\r\n if (!archiveBuffer) {\r\n throw new Error(\"Must provide archiveBuffer\");\r\n }\r\n const blob = new Blob([new Uint8Array(archiveBuffer)]);\r\n\r\n /** Open with libarchive.js */\r\n const Archive = await getArchive();\r\n const archive = await Archive.open(blob as File);\r\n if (password) await archive.usePassword(password);\r\n\r\n /** Get file tree */\r\n const filesObj = await archive.getFilesObject();\r\n const files: ExtractEvent[] = [];\r\n\r\n /**\r\n * Recursively walk file tree, filter by folderPath, extract matching files.\r\n * @param obj - Nested file/dir object\r\n * @param prefix - Current path prefix\r\n */\r\n const walker = async (obj: any, prefix: string = \"\"): Promise<void> => {\r\n for (const [name, entry] of Object.entries(obj)) {\r\n const fullPath = prefix ? `${prefix}/${name}` : name;\r\n if (fullPath.startsWith(folderPath) && !fullPath.endsWith(\"/\")) {\r\n const relativePath = fullPath\r\n .slice(folderPath.length)\r\n .replace(/^\\//, \"\");\r\n const fileBlob = await (entry as any).extract();\r\n let content: string;\r\n try {\r\n content = await fileBlob.text();\r\n } catch {\r\n const buffer = await fileBlob.arrayBuffer();\r\n content = btoa(String.fromCharCode(...new Uint8Array(buffer))); // Base64 binary\r\n }\r\n files.push({\r\n path: relativePath,\r\n size: fileBlob.size,\r\n content,\r\n mime: fileBlob.type || \"application/octet-stream\",\r\n });\r\n }\r\n if (typeof entry === 'object' && !(entry as any)?.extract) {\r\n // Subdir (directories don't have .extract, files do)\r\n await walker(entry, fullPath);\r\n }\r\n }\r\n };\r\n\r\n await walker(filesObj);\r\n return files;\r\n}\r\n\r\n\r\n\r\n/**\r\n * Create archive from files array.\r\n * @param options - Create configuration\r\n * @param options.files - Array of {path: string, content: string|Uint8Array|ArrayBuffer|Blob}\r\n * @param options.outputName - Archive filename (e.g., 'out.tar.gz')\r\n * @param options.format - Archive format (defaults USTAR)\r\n * @param options.compression - Compression (defaults NONE)\r\n * @param options.compressionLevel - 1-9 (defaults 6, format-dependent)\r\n * @param options.passphrase - Optional password\r\n * @returns Blob of archive (download/save as)\r\n * @throws Error on invalid options\r\n */\r\nexport async function compress(\r\n options: CreateOptions,\r\n): Promise<ArchiveFile> {\r\n const {\r\n files,\r\n outputName,\r\n format = \"USTAR\",\r\n compression = \"NONE\",\r\n compressionLevel = 6,\r\n passphrase,\r\n } = options;\r\n\r\n /** Convert files to Blobs with pathnames */\r\n const archiveFiles: { file: Blob; pathname: string }[] = files.map(\r\n ({ path, content }) => {\r\n let blobContent: Blob;\r\n if (typeof content === \"string\") {\r\n blobContent = new Blob([content]);\r\n } else if (content instanceof Uint8Array) {\r\n blobContent = new Blob([content as BlobPart]);\r\n } else if (content instanceof ArrayBuffer) {\r\n blobContent = new Blob([content]);\r\n } else if (content instanceof Blob) {\r\n blobContent = content;\r\n } else {\r\n throw new Error(`Unsupported content type for ${path}`);\r\n }\r\n return { file: blobContent, pathname: path };\r\n },\r\n );\r\n\r\n /** Create archive */\r\n const Archive = await getArchive();\r\n //@ts-ignore\r\n const archiveBlob = await Archive.write({\r\n files: archiveFiles,\r\n outputFileName: outputName,\r\n compression,\r\n format,\r\n compressionLevel, // Passed if supported\r\n passphrase: passphrase || null,\r\n });\r\n\r\n return {\r\n blob: archiveBlob,\r\n mime: \"application/zip\", // Adjust per format\r\n downloadName: outputName,\r\n };\r\n}\r\n\r\nexport type { ArchiveFormat, ArchiveCompression };\r\nexport type { ExtractEvent, CreateOptions, ArchiveFile };\r\n"],"names":["ArchiveClass","_setArchiveClass","cls","async","getArchive","process","versions","node","navigator","userAgent","require","resolve","spawn","import","Promise","proc","stdio","on","ensureLibarchiveNode","console","error","Error","mod","Archive","init","workerUrl","extract","options","archiveBuffer","folderPath","password","blob","Blob","Uint8Array","archive","open","usePassword","filesObj","getFilesObject","files","walker","obj","prefix","name","entry","Object","entries","fullPath","startsWith","endsWith","relativePath","slice","length","replace","fileBlob","content","text","buffer","arrayBuffer","btoa","String","fromCharCode","push","path","size","mime","type","compress","outputName","format","compression","compressionLevel","passphrase","archiveFiles","map","blobContent","ArrayBuffer","file","pathname","write","outputFileName","downloadName"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";const e=require("util"),o=require("fs"),r=require("path"),t=require("./archiver-web.cjs.js");function s(e){const o=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const r in e)if("default"!==r){const t=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(o,r,t.get?t:{enumerable:!0,get:()=>e[r]})}return o.default=e,Object.freeze(o)}const n=s(o),i=s(r);(async function(){const{values:o,positionals:r}=e.parseArgs({args:process.argv.slice(2),options:{format:{type:"string",short:"f",default:"ZIP"},compression:{type:"string",short:"c",default:"NONE"},out:{type:"string",short:"o"},password:{type:"string",short:"p"},help:{type:"boolean",short:"h"}},allowPositionals:!0});o.help&&(console.error("Usage: compress [files...] [options]"),console.error("If [files...] are omitted, reads from stdin."),console.error("Options:"),console.error(" -o, --out <file> Output file. If absent and not TTY, outputs to stdout."),console.error(" -f, --format <format> Format for archiving (default: ZIP)."),console.error(" -c, --compression <c> Compression type."),console.error(" -p, --password <pwd> Password for the archive."),process.exit(0));const s=[];if(r.length>0)for(let e=0;e<r.length;e++){const o=r[e];n.statSync(o).isDirectory()&&(console.error("Directory archiving not implemented for CLI basic args yet ("+o+")"),process.exit(1));const t=n.readFileSync(o);s.push({path:i.basename(o),content:new Blob([new Uint8Array(t)])})}else{process.stdin.isTTY&&(console.error("Error: Must provide files to archive or pipe input via stdin."),process.exit(1));const e=await async function(e){const o=[];for await(const r of e)o.push(Buffer.from(r));return Buffer.concat(o)}(process.stdin);s.push({path:"piped-content",content:new Blob([new Uint8Array(e)])})}const c=await t.compress({files:s,outputName:o.out?i.basename(o.out):"archive",format:o.format,compression:o.compression,passphrase:o.password}),a=Buffer.from(await c.blob.arrayBuffer());o.out?(n.writeFileSync(i.resolve(o.out),a),console.error("Created archive at "+o.out)):process.stdout.write(a)})().catch(e=>{console.error("Compress Error:",e),process.exit(1)});
|
|
3
|
+
//# sourceMappingURL=bin-compress.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-compress.cjs.js","sources":["../packages/archiver-web/src/bin-compress.ts"],"sourcesContent":["\r\nimport { parseArgs } from \"util\";\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport { compress } from \"./index.js\";\r\nimport { ArchiveFormat, ArchiveCompression } from \"./types.js\";\r\n\r\nasync function readStreamToBuffer(stream: NodeJS.ReadStream): Promise<Buffer> {\r\n const chunks: Buffer[] = [];\r\n for await (const chunk of stream) {\r\n chunks.push(Buffer.from(chunk));\r\n }\r\n return Buffer.concat(chunks);\r\n}\r\n\r\nasync function main() {\r\n const options = {\r\n format: { type: \"string\" as const, short: \"f\", default: \"ZIP\" },\r\n compression: { type: \"string\" as const, short: \"c\", default: \"NONE\" },\r\n out: { type: \"string\" as const, short: \"o\" },\r\n password: { type: \"string\" as const, short: \"p\" },\r\n help: { type: \"boolean\" as const, short: \"h\" }\r\n };\r\n\r\n const { values, positionals } = parseArgs({\r\n args: process.argv.slice(2),\r\n options,\r\n allowPositionals: true\r\n });\r\n\r\n if (values.help) {\r\n console.error(\"Usage: compress [files...] [options]\");\r\n console.error(\"If [files...] are omitted, reads from stdin.\");\r\n console.error(\"Options:\");\r\n console.error(\" -o, --out <file> Output file. If absent and not TTY, outputs to stdout.\");\r\n console.error(\" -f, --format <format> Format for archiving (default: ZIP).\");\r\n console.error(\" -c, --compression <c> Compression type.\");\r\n console.error(\" -p, --password <pwd> Password for the archive.\");\r\n process.exit(0);\r\n }\r\n\r\n const filesToArchive: Array<{ path: string; content: Blob | string }> = [];\r\n\r\n // If filenames are provided\r\n if (positionals.length > 0) {\r\n for (let i = 0; i < positionals.length; i++) {\r\n const filePath = positionals[i];\r\n const stat = fs.statSync(filePath);\r\n if (stat.isDirectory()) {\r\n console.error(\"Directory archiving not implemented for CLI basic args yet (\" + filePath + \")\");\r\n process.exit(1);\r\n }\r\n const buf = fs.readFileSync(filePath);\r\n filesToArchive.push({\r\n path: path.basename(filePath),\r\n content: new Blob([new Uint8Array(buf)])\r\n });\r\n }\r\n } else {\r\n // Read from stdin\r\n if (process.stdin.isTTY) {\r\n console.error(\"Error: Must provide files to archive or pipe input via stdin.\");\r\n process.exit(1);\r\n }\r\n const stdinBuffer = await readStreamToBuffer(process.stdin);\r\n filesToArchive.push({\r\n path: \"piped-content\", // generic name for piped data\r\n content: new Blob([new Uint8Array(stdinBuffer)])\r\n });\r\n }\r\n\r\n const archive = await compress({\r\n files: filesToArchive,\r\n outputName: values.out ? path.basename(values.out) : \"archive\",\r\n format: values.format as ArchiveFormat,\r\n compression: values.compression as ArchiveCompression,\r\n passphrase: values.password\r\n });\r\n\r\n const archiveBuffer = Buffer.from(await archive.blob.arrayBuffer());\r\n\r\n if (values.out) {\r\n fs.writeFileSync(path.resolve(values.out), archiveBuffer);\r\n console.error(\"Created archive at \" + values.out);\r\n } else {\r\n process.stdout.write(archiveBuffer);\r\n }\r\n}\r\n\r\nmain().catch((err) => {\r\n console.error(\"Compress Error:\", err);\r\n process.exit(1);\r\n});\r\n"],"names":["async","values","positionals","parseArgs","args","process","argv","slice","options","format","type","short","default","compression","out","password","help","allowPositionals","console","error","exit","filesToArchive","length","i","filePath","fs","statSync","isDirectory","buf","readFileSync","push","path","basename","content","Blob","Uint8Array","stdin","isTTY","stdinBuffer","stream","chunks","chunk","Buffer","from","concat","readStreamToBuffer","archive","compress","files","outputName","passphrase","archiveBuffer","blob","arrayBuffer","writeFileSync","resolve","stdout","write","main","catch","err"],"mappings":";0YAeAA,iBACE,MAQMC,OAAEA,EAAAC,YAAQA,GAAgBC,YAAU,CACxCC,KAAMC,QAAQC,KAAKC,MAAM,GACzBC,QAVc,CACdC,OAAQ,CAAEC,KAAM,SAAmBC,MAAO,IAAKC,QAAS,OACxDC,YAAa,CAAEH,KAAM,SAAmBC,MAAO,IAAKC,QAAS,QAC7DE,IAAK,CAAEJ,KAAM,SAAmBC,MAAO,KACvCI,SAAU,CAAEL,KAAM,SAAmBC,MAAO,KAC5CK,KAAM,CAAEN,KAAM,UAAoBC,MAAO,MAMzCM,kBAAkB,IAGhBhB,EAAOe,OACTE,QAAQC,MAAM,wCACdD,QAAQC,MAAM,gDACdD,QAAQC,MAAM,YACdD,QAAQC,MAAM,oFACdD,QAAQC,MAAM,kEACdD,QAAQC,MAAM,+CACdD,QAAQC,MAAM,uDACdd,QAAQe,KAAK,IAGf,MAAMC,EAAkE,GAGxE,GAAInB,EAAYoB,OAAS,EACvB,IAAA,IAASC,EAAI,EAAGA,EAAIrB,EAAYoB,OAAQC,IAAK,CACzC,MAAMC,EAAWtB,EAAYqB,GAChBE,EAAGC,SAASF,GAChBG,gBACLT,QAAQC,MAAM,+DAAiEK,EAAW,KAC1FnB,QAAQe,KAAK,IAEjB,MAAMQ,EAAMH,EAAGI,aAAaL,GAC5BH,EAAeS,KAAK,CAClBC,KAAMA,EAAKC,SAASR,GACpBS,QAAS,IAAIC,KAAK,CAAC,IAAIC,WAAWP,MAExC,KACK,CAEDvB,QAAQ+B,MAAMC,QAChBnB,QAAQC,MAAM,iEACdd,QAAQe,KAAK,IAEf,MAAMkB,QAzDVtC,eAAkCuC,GAChC,MAAMC,EAAmB,GACzB,UAAA,MAAiBC,KAASF,EACxBC,EAAOV,KAAKY,OAAOC,KAAKF,IAE1B,OAAOC,OAAOE,OAAOJ,EACvB,CAmD8BK,CAAmBxC,QAAQ+B,OACrDf,EAAeS,KAAK,CAClBC,KAAM,gBACNE,QAAS,IAAIC,KAAK,CAAC,IAAIC,WAAWG,MAEtC,CAEA,MAAMQ,QAAgBC,WAAS,CAC7BC,MAAO3B,EACP4B,WAAYhD,EAAOa,IAAMiB,EAAKC,SAAS/B,EAAOa,KAAO,UACrDL,OAAQR,EAAOQ,OACfI,YAAaZ,EAAOY,YACpBqC,WAAYjD,EAAOc,WAGfoC,EAAgBT,OAAOC,WAAWG,EAAQM,KAAKC,eAEjDpD,EAAOa,KACTW,EAAG6B,cAAcvB,EAAKwB,QAAQtD,EAAOa,KAAMqC,GAC3CjC,QAAQC,MAAM,sBAAwBlB,EAAOa,MAE7CT,QAAQmD,OAAOC,MAAMN,EAEzB,EAEAO,GAAOC,MAAOC,IACZ1C,QAAQC,MAAM,kBAAmByC,GACjCvD,QAAQe,KAAK"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{parseArgs as o}from"util";import*as r from"fs";import*as e from"path";import{compress as s}from"./archiver-web.es.js";(async function(){const{values:t,positionals:n}=o({args:process.argv.slice(2),options:{format:{type:"string",short:"f",default:"ZIP"},compression:{type:"string",short:"c",default:"NONE"},out:{type:"string",short:"o"},password:{type:"string",short:"p"},help:{type:"boolean",short:"h"}},allowPositionals:!0});t.help&&(console.error("Usage: compress [files...] [options]"),console.error("If [files...] are omitted, reads from stdin."),console.error("Options:"),console.error(" -o, --out <file> Output file. If absent and not TTY, outputs to stdout."),console.error(" -f, --format <format> Format for archiving (default: ZIP)."),console.error(" -c, --compression <c> Compression type."),console.error(" -p, --password <pwd> Password for the archive."),process.exit(0));const i=[];if(n.length>0)for(let o=0;o<n.length;o++){const s=n[o];r.statSync(s).isDirectory()&&(console.error("Directory archiving not implemented for CLI basic args yet ("+s+")"),process.exit(1));const t=r.readFileSync(s);i.push({path:e.basename(s),content:new Blob([new Uint8Array(t)])})}else{process.stdin.isTTY&&(console.error("Error: Must provide files to archive or pipe input via stdin."),process.exit(1));const o=await async function(o){const r=[];for await(const e of o)r.push(Buffer.from(e));return Buffer.concat(r)}(process.stdin);i.push({path:"piped-content",content:new Blob([new Uint8Array(o)])})}const a=await s({files:i,outputName:t.out?e.basename(t.out):"archive",format:t.format,compression:t.compression,passphrase:t.password}),c=Buffer.from(await a.blob.arrayBuffer());t.out?(r.writeFileSync(e.resolve(t.out),c),console.error("Created archive at "+t.out)):process.stdout.write(c)})().catch(o=>{console.error("Compress Error:",o),process.exit(1)});
|
|
3
|
+
//# sourceMappingURL=bin-compress.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-compress.es.js","sources":["../packages/archiver-web/src/bin-compress.ts"],"sourcesContent":["\r\nimport { parseArgs } from \"util\";\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport { compress } from \"./index.js\";\r\nimport { ArchiveFormat, ArchiveCompression } from \"./types.js\";\r\n\r\nasync function readStreamToBuffer(stream: NodeJS.ReadStream): Promise<Buffer> {\r\n const chunks: Buffer[] = [];\r\n for await (const chunk of stream) {\r\n chunks.push(Buffer.from(chunk));\r\n }\r\n return Buffer.concat(chunks);\r\n}\r\n\r\nasync function main() {\r\n const options = {\r\n format: { type: \"string\" as const, short: \"f\", default: \"ZIP\" },\r\n compression: { type: \"string\" as const, short: \"c\", default: \"NONE\" },\r\n out: { type: \"string\" as const, short: \"o\" },\r\n password: { type: \"string\" as const, short: \"p\" },\r\n help: { type: \"boolean\" as const, short: \"h\" }\r\n };\r\n\r\n const { values, positionals } = parseArgs({\r\n args: process.argv.slice(2),\r\n options,\r\n allowPositionals: true\r\n });\r\n\r\n if (values.help) {\r\n console.error(\"Usage: compress [files...] [options]\");\r\n console.error(\"If [files...] are omitted, reads from stdin.\");\r\n console.error(\"Options:\");\r\n console.error(\" -o, --out <file> Output file. If absent and not TTY, outputs to stdout.\");\r\n console.error(\" -f, --format <format> Format for archiving (default: ZIP).\");\r\n console.error(\" -c, --compression <c> Compression type.\");\r\n console.error(\" -p, --password <pwd> Password for the archive.\");\r\n process.exit(0);\r\n }\r\n\r\n const filesToArchive: Array<{ path: string; content: Blob | string }> = [];\r\n\r\n // If filenames are provided\r\n if (positionals.length > 0) {\r\n for (let i = 0; i < positionals.length; i++) {\r\n const filePath = positionals[i];\r\n const stat = fs.statSync(filePath);\r\n if (stat.isDirectory()) {\r\n console.error(\"Directory archiving not implemented for CLI basic args yet (\" + filePath + \")\");\r\n process.exit(1);\r\n }\r\n const buf = fs.readFileSync(filePath);\r\n filesToArchive.push({\r\n path: path.basename(filePath),\r\n content: new Blob([new Uint8Array(buf)])\r\n });\r\n }\r\n } else {\r\n // Read from stdin\r\n if (process.stdin.isTTY) {\r\n console.error(\"Error: Must provide files to archive or pipe input via stdin.\");\r\n process.exit(1);\r\n }\r\n const stdinBuffer = await readStreamToBuffer(process.stdin);\r\n filesToArchive.push({\r\n path: \"piped-content\", // generic name for piped data\r\n content: new Blob([new Uint8Array(stdinBuffer)])\r\n });\r\n }\r\n\r\n const archive = await compress({\r\n files: filesToArchive,\r\n outputName: values.out ? path.basename(values.out) : \"archive\",\r\n format: values.format as ArchiveFormat,\r\n compression: values.compression as ArchiveCompression,\r\n passphrase: values.password\r\n });\r\n\r\n const archiveBuffer = Buffer.from(await archive.blob.arrayBuffer());\r\n\r\n if (values.out) {\r\n fs.writeFileSync(path.resolve(values.out), archiveBuffer);\r\n console.error(\"Created archive at \" + values.out);\r\n } else {\r\n process.stdout.write(archiveBuffer);\r\n }\r\n}\r\n\r\nmain().catch((err) => {\r\n console.error(\"Compress Error:\", err);\r\n process.exit(1);\r\n});\r\n"],"names":["async","values","positionals","parseArgs","args","process","argv","slice","options","format","type","short","default","compression","out","password","help","allowPositionals","console","error","exit","filesToArchive","length","i","filePath","fs","statSync","isDirectory","buf","readFileSync","push","path","basename","content","Blob","Uint8Array","stdin","isTTY","stdinBuffer","stream","chunks","chunk","Buffer","from","concat","readStreamToBuffer","archive","compress","files","outputName","passphrase","archiveBuffer","blob","arrayBuffer","writeFileSync","resolve","stdout","write","main","catch","err"],"mappings":";8HAeAA,iBACE,MAQMC,OAAEA,EAAAC,YAAQA,GAAgBC,EAAU,CACxCC,KAAMC,QAAQC,KAAKC,MAAM,GACzBC,QAVc,CACdC,OAAQ,CAAEC,KAAM,SAAmBC,MAAO,IAAKC,QAAS,OACxDC,YAAa,CAAEH,KAAM,SAAmBC,MAAO,IAAKC,QAAS,QAC7DE,IAAK,CAAEJ,KAAM,SAAmBC,MAAO,KACvCI,SAAU,CAAEL,KAAM,SAAmBC,MAAO,KAC5CK,KAAM,CAAEN,KAAM,UAAoBC,MAAO,MAMzCM,kBAAkB,IAGhBhB,EAAOe,OACTE,QAAQC,MAAM,wCACdD,QAAQC,MAAM,gDACdD,QAAQC,MAAM,YACdD,QAAQC,MAAM,oFACdD,QAAQC,MAAM,kEACdD,QAAQC,MAAM,+CACdD,QAAQC,MAAM,uDACdd,QAAQe,KAAK,IAGf,MAAMC,EAAkE,GAGxE,GAAInB,EAAYoB,OAAS,EACvB,IAAA,IAASC,EAAI,EAAGA,EAAIrB,EAAYoB,OAAQC,IAAK,CACzC,MAAMC,EAAWtB,EAAYqB,GAChBE,EAAGC,SAASF,GAChBG,gBACLT,QAAQC,MAAM,+DAAiEK,EAAW,KAC1FnB,QAAQe,KAAK,IAEjB,MAAMQ,EAAMH,EAAGI,aAAaL,GAC5BH,EAAeS,KAAK,CAClBC,KAAMA,EAAKC,SAASR,GACpBS,QAAS,IAAIC,KAAK,CAAC,IAAIC,WAAWP,MAExC,KACK,CAEDvB,QAAQ+B,MAAMC,QAChBnB,QAAQC,MAAM,iEACdd,QAAQe,KAAK,IAEf,MAAMkB,QAzDVtC,eAAkCuC,GAChC,MAAMC,EAAmB,GACzB,UAAA,MAAiBC,KAASF,EACxBC,EAAOV,KAAKY,OAAOC,KAAKF,IAE1B,OAAOC,OAAOE,OAAOJ,EACvB,CAmD8BK,CAAmBxC,QAAQ+B,OACrDf,EAAeS,KAAK,CAClBC,KAAM,gBACNE,QAAS,IAAIC,KAAK,CAAC,IAAIC,WAAWG,MAEtC,CAEA,MAAMQ,QAAgBC,EAAS,CAC7BC,MAAO3B,EACP4B,WAAYhD,EAAOa,IAAMiB,EAAKC,SAAS/B,EAAOa,KAAO,UACrDL,OAAQR,EAAOQ,OACfI,YAAaZ,EAAOY,YACpBqC,WAAYjD,EAAOc,WAGfoC,EAAgBT,OAAOC,WAAWG,EAAQM,KAAKC,eAEjDpD,EAAOa,KACTW,EAAG6B,cAAcvB,EAAKwB,QAAQtD,EAAOa,KAAMqC,GAC3CjC,QAAQC,MAAM,sBAAwBlB,EAAOa,MAE7CT,QAAQmD,OAAOC,MAAMN,EAEzB,EAEAO,GAAOC,MAAOC,IACZ1C,QAAQC,MAAM,kBAAmByC,GACjCvD,QAAQe,KAAK"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";const e=require("util"),t=require("fs"),r=require("path"),o=require("./archiver-web.cjs.js");function s(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const r in e)if("default"!==r){const o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:()=>e[r]})}return t.default=e,Object.freeze(t)}const n=s(t),c=s(r);(async function(){const{values:t,positionals:r}=e.parseArgs({args:process.argv.slice(2),options:{out:{type:"string",short:"o"},password:{type:"string",short:"p"},folder:{type:"string",short:"d",default:""},help:{type:"boolean",short:"h"}},allowPositionals:!0});let s;if(t.help&&(console.error("Usage: extract [archive-file] [options]"),console.error("If [archive-file] is omitted, reads from stdin."),console.error("Options:"),console.error(" -o, --out <dir> Output directory. If absent, outputs to stdout (only works if 1 file)."),console.error(" -p, --password <pwd> Password for the archive."),console.error(" -d, --folder <dir> Folder to extract from archive."),process.exit(0)),r.length>0){const e=r[0];s=n.readFileSync(c.resolve(e))}else process.stdin.isTTY&&(console.error("Error: Must provide an archive file or pipe input via stdin."),process.exit(1)),s=await async function(e){const t=[];for await(const r of e)t.push(Buffer.from(r));return Buffer.concat(t)}(process.stdin);const i=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength),f=await o.extract({archiveBuffer:i,folderPath:t.folder,password:t.password});if(t.out){for(const e of f){const r=c.join(t.out,e.path);let o;if(n.mkdirSync(c.dirname(r),{recursive:!0}),e.content.startsWith("data:")||e.content.match(/^[A-Za-z0-9+/=]+$/))try{o=Buffer.from(e.content,"base64")}catch{o=Buffer.from(e.content,"utf8")}else o=Buffer.from(e.content,"utf8");n.writeFileSync(r,o)}console.error("Extracted "+f.length+" files to "+t.out)}else if(1===f.length){let e;e=f[0].content.match(/^[A-Za-z0-9+/=]*$/)&&f[0].content.length>50?Buffer.from(f[0].content,"base64"):Buffer.from(f[0].content,"utf8"),process.stdout.write(e)}else console.error("Archive contains "+f.length+" files. Please specify -o/--out directory to extract them."),process.exit(1)})().catch(e=>{console.error("Extract Error:",e),process.exit(1)});
|
|
3
|
+
//# sourceMappingURL=bin-extract.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-extract.cjs.js","sources":["../packages/archiver-web/src/bin-extract.ts"],"sourcesContent":["\r\nimport { parseArgs } from \"util\";\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport { extract } from \"./index.js\";\r\n\r\nasync function readStreamToBuffer(stream: NodeJS.ReadStream): Promise<Buffer> {\r\n const chunks: Buffer[] = [];\r\n for await (const chunk of stream) {\r\n chunks.push(Buffer.from(chunk));\r\n }\r\n return Buffer.concat(chunks);\r\n}\r\n\r\nasync function main() {\r\n const options = {\r\n out: { type: \"string\" as const, short: \"o\" },\r\n password: { type: \"string\" as const, short: \"p\" },\r\n folder: { type: \"string\" as const, short: \"d\", default: \"\" },\r\n help: { type: \"boolean\" as const, short: \"h\" }\r\n };\r\n\r\n const { values, positionals } = parseArgs({\r\n args: process.argv.slice(2),\r\n options,\r\n allowPositionals: true\r\n });\r\n\r\n if (values.help) {\r\n console.error(\"Usage: extract [archive-file] [options]\");\r\n console.error(\"If [archive-file] is omitted, reads from stdin.\");\r\n console.error(\"Options:\");\r\n console.error(\" -o, --out <dir> Output directory. If absent, outputs to stdout (only works if 1 file).\");\r\n console.error(\" -p, --password <pwd> Password for the archive.\");\r\n console.error(\" -d, --folder <dir> Folder to extract from archive.\");\r\n process.exit(0);\r\n }\r\n\r\n let archiveBuffer: Buffer;\r\n\r\n if (positionals.length > 0) {\r\n const inputFile = positionals[0];\r\n archiveBuffer = fs.readFileSync(path.resolve(inputFile));\r\n } else {\r\n // Read from stdin\r\n if (process.stdin.isTTY) {\r\n console.error(\"Error: Must provide an archive file or pipe input via stdin.\");\r\n process.exit(1);\r\n }\r\n archiveBuffer = await readStreamToBuffer(process.stdin);\r\n }\r\n\r\n const arrayBuf = archiveBuffer.buffer.slice(\r\n archiveBuffer.byteOffset,\r\n archiveBuffer.byteOffset + archiveBuffer.byteLength\r\n ) as ArrayBuffer;\r\n\r\n const files = await extract({\r\n archiveBuffer: arrayBuf,\r\n folderPath: values.folder,\r\n password: values.password\r\n });\r\n\r\n if (values.out) {\r\n for (const file of files) {\r\n const outPath = path.join(values.out, file.path);\r\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\r\n let buf: Buffer;\r\n if (file.content.startsWith(\"data:\") || file.content.match(/^[A-Za-z0-9+/=]+$/)) {\r\n try {\r\n buf = Buffer.from(file.content, \"base64\");\r\n } catch {\r\n buf = Buffer.from(file.content, \"utf8\");\r\n }\r\n } else {\r\n buf = Buffer.from(file.content, \"utf8\");\r\n }\r\n fs.writeFileSync(outPath, buf);\r\n }\r\n console.error(\"Extracted \" + files.length + \" files to \" + values.out);\r\n } else {\r\n // Write to stdout\r\n if (files.length === 1) {\r\n let buf: Buffer;\r\n if (files[0].content.match(/^[A-Za-z0-9+/=]*$/) && files[0].content.length > 50) {\r\n buf = Buffer.from(files[0].content, \"base64\");\r\n } else {\r\n buf = Buffer.from(files[0].content, \"utf8\");\r\n }\r\n process.stdout.write(buf);\r\n } else {\r\n console.error(\"Archive contains \" + files.length + \" files. Please specify -o/--out directory to extract them.\");\r\n process.exit(1);\r\n }\r\n }\r\n}\r\n\r\nmain().catch((err) => {\r\n console.error(\"Extract Error:\", err);\r\n process.exit(1);\r\n});\r\n"],"names":["async","values","positionals","parseArgs","args","process","argv","slice","options","out","type","short","password","folder","default","help","allowPositionals","archiveBuffer","console","error","exit","length","inputFile","fs","readFileSync","path","resolve","stdin","isTTY","stream","chunks","chunk","push","Buffer","from","concat","readStreamToBuffer","arrayBuf","buffer","byteOffset","byteLength","files","extract","folderPath","file","outPath","join","buf","mkdirSync","dirname","recursive","content","startsWith","match","writeFileSync","stdout","write","main","catch","err"],"mappings":";0YAcAA,iBACE,MAOMC,OAAEA,EAAAC,YAAQA,GAAgBC,YAAU,CACxCC,KAAMC,QAAQC,KAAKC,MAAM,GACzBC,QATc,CACdC,IAAK,CAAEC,KAAM,SAAmBC,MAAO,KACvCC,SAAU,CAAEF,KAAM,SAAmBC,MAAO,KAC5CE,OAAQ,CAAEH,KAAM,SAAmBC,MAAO,IAAKG,QAAS,IACxDC,KAAM,CAAEL,KAAM,UAAoBC,MAAO,MAMzCK,kBAAkB,IAapB,IAAIC,EAEJ,GAZIhB,EAAOc,OACTG,QAAQC,MAAM,2CACdD,QAAQC,MAAM,mDACdD,QAAQC,MAAM,YACdD,QAAQC,MAAM,oGACdD,QAAQC,MAAM,uDACdD,QAAQC,MAAM,6DACdd,QAAQe,KAAK,IAKXlB,EAAYmB,OAAS,EAAG,CAC1B,MAAMC,EAAYpB,EAAY,GAC9Be,EAAgBM,EAAGC,aAAaC,EAAKC,QAAQJ,GAC/C,MAEMjB,QAAQsB,MAAMC,QAChBV,QAAQC,MAAM,gEACdd,QAAQe,KAAK,IAEfH,QA3CJjB,eAAkC6B,GAChC,MAAMC,EAAmB,GACzB,UAAA,MAAiBC,KAASF,EACxBC,EAAOE,KAAKC,OAAOC,KAAKH,IAE1B,OAAOE,OAAOE,OAAOL,EACvB,CAqC0BM,CAAmB/B,QAAQsB,OAGnD,MAAMU,EAAWpB,EAAcqB,OAAO/B,MACpCU,EAAcsB,WACdtB,EAAcsB,WAAatB,EAAcuB,YAGrCC,QAAcC,UAAQ,CAC1BzB,cAAeoB,EACfM,WAAY1C,EAAOY,OACnBD,SAAUX,EAAOW,WAGnB,GAAIX,EAAOQ,IAAK,CACd,IAAA,MAAWmC,KAAQH,EAAO,CACxB,MAAMI,EAAUpB,EAAKqB,KAAK7C,EAAOQ,IAAKmC,EAAKnB,MAE3C,IAAIsB,EACJ,GAFAxB,EAAGyB,UAAUvB,EAAKwB,QAAQJ,GAAU,CAAEK,WAAW,IAE7CN,EAAKO,QAAQC,WAAW,UAAYR,EAAKO,QAAQE,MAAM,qBACzD,IACGN,EAAMd,OAAOC,KAAKU,EAAKO,QAAS,SACnC,CAAA,MACGJ,EAAMd,OAAOC,KAAKU,EAAKO,QAAS,OACnC,MAEAJ,EAAMd,OAAOC,KAAKU,EAAKO,QAAS,QAElC5B,EAAG+B,cAAcT,EAASE,EAC5B,CACA7B,QAAQC,MAAM,aAAesB,EAAMpB,OAAS,aAAepB,EAAOQ,IACpE,MAEE,GAAqB,IAAjBgC,EAAMpB,OAAc,CACtB,IAAI0B,EAEDA,EADCN,EAAM,GAAGU,QAAQE,MAAM,sBAAwBZ,EAAM,GAAGU,QAAQ9B,OAAS,GACpEY,OAAOC,KAAKO,EAAM,GAAGU,QAAS,UAE9BlB,OAAOC,KAAKO,EAAM,GAAGU,QAAS,QAEvC9C,QAAQkD,OAAOC,MAAMT,EACvB,MACE7B,QAAQC,MAAM,oBAAsBsB,EAAMpB,OAAS,8DACnDhB,QAAQe,KAAK,EAGnB,EAEAqC,GAAOC,MAAOC,IACZzC,QAAQC,MAAM,iBAAkBwC,GAChCtD,QAAQe,KAAK"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { }
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{parseArgs as e}from"util";import*as o from"fs";import*as t from"path";import{extract as r}from"./archiver-web.es.js";(async function(){const{values:s,positionals:n}=e({args:process.argv.slice(2),options:{out:{type:"string",short:"o"},password:{type:"string",short:"p"},folder:{type:"string",short:"d",default:""},help:{type:"boolean",short:"h"}},allowPositionals:!0});let i;if(s.help&&(console.error("Usage: extract [archive-file] [options]"),console.error("If [archive-file] is omitted, reads from stdin."),console.error("Options:"),console.error(" -o, --out <dir> Output directory. If absent, outputs to stdout (only works if 1 file)."),console.error(" -p, --password <pwd> Password for the archive."),console.error(" -d, --folder <dir> Folder to extract from archive."),process.exit(0)),n.length>0){const e=n[0];i=o.readFileSync(t.resolve(e))}else process.stdin.isTTY&&(console.error("Error: Must provide an archive file or pipe input via stdin."),process.exit(1)),i=await async function(e){const o=[];for await(const t of e)o.push(Buffer.from(t));return Buffer.concat(o)}(process.stdin);const c=i.buffer.slice(i.byteOffset,i.byteOffset+i.byteLength),f=await r({archiveBuffer:c,folderPath:s.folder,password:s.password});if(s.out){for(const e of f){const r=t.join(s.out,e.path);let n;if(o.mkdirSync(t.dirname(r),{recursive:!0}),e.content.startsWith("data:")||e.content.match(/^[A-Za-z0-9+/=]+$/))try{n=Buffer.from(e.content,"base64")}catch{n=Buffer.from(e.content,"utf8")}else n=Buffer.from(e.content,"utf8");o.writeFileSync(r,n)}console.error("Extracted "+f.length+" files to "+s.out)}else if(1===f.length){let e;e=f[0].content.match(/^[A-Za-z0-9+/=]*$/)&&f[0].content.length>50?Buffer.from(f[0].content,"base64"):Buffer.from(f[0].content,"utf8"),process.stdout.write(e)}else console.error("Archive contains "+f.length+" files. Please specify -o/--out directory to extract them."),process.exit(1)})().catch(e=>{console.error("Extract Error:",e),process.exit(1)});
|
|
3
|
+
//# sourceMappingURL=bin-extract.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-extract.es.js","sources":["../packages/archiver-web/src/bin-extract.ts"],"sourcesContent":["\r\nimport { parseArgs } from \"util\";\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport { extract } from \"./index.js\";\r\n\r\nasync function readStreamToBuffer(stream: NodeJS.ReadStream): Promise<Buffer> {\r\n const chunks: Buffer[] = [];\r\n for await (const chunk of stream) {\r\n chunks.push(Buffer.from(chunk));\r\n }\r\n return Buffer.concat(chunks);\r\n}\r\n\r\nasync function main() {\r\n const options = {\r\n out: { type: \"string\" as const, short: \"o\" },\r\n password: { type: \"string\" as const, short: \"p\" },\r\n folder: { type: \"string\" as const, short: \"d\", default: \"\" },\r\n help: { type: \"boolean\" as const, short: \"h\" }\r\n };\r\n\r\n const { values, positionals } = parseArgs({\r\n args: process.argv.slice(2),\r\n options,\r\n allowPositionals: true\r\n });\r\n\r\n if (values.help) {\r\n console.error(\"Usage: extract [archive-file] [options]\");\r\n console.error(\"If [archive-file] is omitted, reads from stdin.\");\r\n console.error(\"Options:\");\r\n console.error(\" -o, --out <dir> Output directory. If absent, outputs to stdout (only works if 1 file).\");\r\n console.error(\" -p, --password <pwd> Password for the archive.\");\r\n console.error(\" -d, --folder <dir> Folder to extract from archive.\");\r\n process.exit(0);\r\n }\r\n\r\n let archiveBuffer: Buffer;\r\n\r\n if (positionals.length > 0) {\r\n const inputFile = positionals[0];\r\n archiveBuffer = fs.readFileSync(path.resolve(inputFile));\r\n } else {\r\n // Read from stdin\r\n if (process.stdin.isTTY) {\r\n console.error(\"Error: Must provide an archive file or pipe input via stdin.\");\r\n process.exit(1);\r\n }\r\n archiveBuffer = await readStreamToBuffer(process.stdin);\r\n }\r\n\r\n const arrayBuf = archiveBuffer.buffer.slice(\r\n archiveBuffer.byteOffset,\r\n archiveBuffer.byteOffset + archiveBuffer.byteLength\r\n ) as ArrayBuffer;\r\n\r\n const files = await extract({\r\n archiveBuffer: arrayBuf,\r\n folderPath: values.folder,\r\n password: values.password\r\n });\r\n\r\n if (values.out) {\r\n for (const file of files) {\r\n const outPath = path.join(values.out, file.path);\r\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\r\n let buf: Buffer;\r\n if (file.content.startsWith(\"data:\") || file.content.match(/^[A-Za-z0-9+/=]+$/)) {\r\n try {\r\n buf = Buffer.from(file.content, \"base64\");\r\n } catch {\r\n buf = Buffer.from(file.content, \"utf8\");\r\n }\r\n } else {\r\n buf = Buffer.from(file.content, \"utf8\");\r\n }\r\n fs.writeFileSync(outPath, buf);\r\n }\r\n console.error(\"Extracted \" + files.length + \" files to \" + values.out);\r\n } else {\r\n // Write to stdout\r\n if (files.length === 1) {\r\n let buf: Buffer;\r\n if (files[0].content.match(/^[A-Za-z0-9+/=]*$/) && files[0].content.length > 50) {\r\n buf = Buffer.from(files[0].content, \"base64\");\r\n } else {\r\n buf = Buffer.from(files[0].content, \"utf8\");\r\n }\r\n process.stdout.write(buf);\r\n } else {\r\n console.error(\"Archive contains \" + files.length + \" files. Please specify -o/--out directory to extract them.\");\r\n process.exit(1);\r\n }\r\n }\r\n}\r\n\r\nmain().catch((err) => {\r\n console.error(\"Extract Error:\", err);\r\n process.exit(1);\r\n});\r\n"],"names":["async","values","positionals","parseArgs","args","process","argv","slice","options","out","type","short","password","folder","default","help","allowPositionals","archiveBuffer","console","error","exit","length","inputFile","fs","readFileSync","path","resolve","stdin","isTTY","stream","chunks","chunk","push","Buffer","from","concat","readStreamToBuffer","arrayBuf","buffer","byteOffset","byteLength","files","extract","folderPath","file","outPath","join","buf","mkdirSync","dirname","recursive","content","startsWith","match","writeFileSync","stdout","write","main","catch","err"],"mappings":";6HAcAA,iBACE,MAOMC,OAAEA,EAAAC,YAAQA,GAAgBC,EAAU,CACxCC,KAAMC,QAAQC,KAAKC,MAAM,GACzBC,QATc,CACdC,IAAK,CAAEC,KAAM,SAAmBC,MAAO,KACvCC,SAAU,CAAEF,KAAM,SAAmBC,MAAO,KAC5CE,OAAQ,CAAEH,KAAM,SAAmBC,MAAO,IAAKG,QAAS,IACxDC,KAAM,CAAEL,KAAM,UAAoBC,MAAO,MAMzCK,kBAAkB,IAapB,IAAIC,EAEJ,GAZIhB,EAAOc,OACTG,QAAQC,MAAM,2CACdD,QAAQC,MAAM,mDACdD,QAAQC,MAAM,YACdD,QAAQC,MAAM,oGACdD,QAAQC,MAAM,uDACdD,QAAQC,MAAM,6DACdd,QAAQe,KAAK,IAKXlB,EAAYmB,OAAS,EAAG,CAC1B,MAAMC,EAAYpB,EAAY,GAC9Be,EAAgBM,EAAGC,aAAaC,EAAKC,QAAQJ,GAC/C,MAEMjB,QAAQsB,MAAMC,QAChBV,QAAQC,MAAM,gEACdd,QAAQe,KAAK,IAEfH,QA3CJjB,eAAkC6B,GAChC,MAAMC,EAAmB,GACzB,UAAA,MAAiBC,KAASF,EACxBC,EAAOE,KAAKC,OAAOC,KAAKH,IAE1B,OAAOE,OAAOE,OAAOL,EACvB,CAqC0BM,CAAmB/B,QAAQsB,OAGnD,MAAMU,EAAWpB,EAAcqB,OAAO/B,MACpCU,EAAcsB,WACdtB,EAAcsB,WAAatB,EAAcuB,YAGrCC,QAAcC,EAAQ,CAC1BzB,cAAeoB,EACfM,WAAY1C,EAAOY,OACnBD,SAAUX,EAAOW,WAGnB,GAAIX,EAAOQ,IAAK,CACd,IAAA,MAAWmC,KAAQH,EAAO,CACxB,MAAMI,EAAUpB,EAAKqB,KAAK7C,EAAOQ,IAAKmC,EAAKnB,MAE3C,IAAIsB,EACJ,GAFAxB,EAAGyB,UAAUvB,EAAKwB,QAAQJ,GAAU,CAAEK,WAAW,IAE7CN,EAAKO,QAAQC,WAAW,UAAYR,EAAKO,QAAQE,MAAM,qBACzD,IACGN,EAAMd,OAAOC,KAAKU,EAAKO,QAAS,SACnC,CAAA,MACGJ,EAAMd,OAAOC,KAAKU,EAAKO,QAAS,OACnC,MAEAJ,EAAMd,OAAOC,KAAKU,EAAKO,QAAS,QAElC5B,EAAG+B,cAAcT,EAASE,EAC5B,CACA7B,QAAQC,MAAM,aAAesB,EAAMpB,OAAS,aAAepB,EAAOQ,IACpE,MAEE,GAAqB,IAAjBgC,EAAMpB,OAAc,CACtB,IAAI0B,EAEDA,EADCN,EAAM,GAAGU,QAAQE,MAAM,sBAAwBZ,EAAM,GAAGU,QAAQ9B,OAAS,GACpEY,OAAOC,KAAKO,EAAM,GAAGU,QAAS,UAE9BlB,OAAOC,KAAKO,EAAM,GAAGU,QAAS,QAEvC9C,QAAQkD,OAAOC,MAAMT,EACvB,MACE7B,QAAQC,MAAM,oBAAsBsB,EAAMpB,OAAS,8DACnDhB,QAAQe,KAAK,EAGnB,EAEAqC,GAAOC,MAAOC,IACZzC,QAAQC,MAAM,iBAAkBwC,GAChCtD,QAAQe,KAAK"}
|
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("@grab-url/log"),t=async(e,t)=>{let n;return async function(...o){clearTimeout(n),n=setTimeout(async()=>{clearTimeout(n),await e(...o)},t)}},n=e=>new Promise(t=>setTimeout(t,1e3*e||0)),o=(e,t)=>{let n=e=>t.startsWith(e),o=e,r=t;return n("http:")||n("https:")?o="":n("/")||o.endsWith("/")?n("/")&&o.endsWith("/")&&(r=t.slice(1)):r="/"+t,{baseURL:o,path:r}};function r(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 i(){"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.printJSONStructure(o.request,0,"html")}<br>\n <b>Response:</b> ${e.printJSONStructure(o.response,0,"html")}<br> \n <b>Time:</b> ${new Date(o.lastFetchTime).toLocaleString()}\n </div>`;r(t)}})}async function s(i,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=o(g,i);g=k.baseURL,i=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 o=await async function(e,n,o,r){const{debounce:i=0,repeat:s=0,repeatEvery:a=null,setDefaults:l=!1}=o;if(i>0){const s=await t(async()=>{await r(e,{...n,debounce:0})},1e3*i);return await s(),o.response||{}}if(s>1){for(let t=0;t<s;t++)await r(e,{...n,repeat:0});return o.response||{}}if(a)return setInterval(async()=>{await r(e,{...n,repeat:0,repeatEvery:null})},1e3*a),o.response||{};if(l){const e="undefined"!=typeof window?window.grab:globalThis.grab;return e&&(e.defaults={...n,setDefaults:void 0}),o.response||{}}return null}(i,a||{},l,s);if(o)return o;!function(e,t,n,o){if("undefined"==typeof window)return;const{regrabOnStale:r,regrabOnFocus:i,regrabOnNetwork:s,cache:a,cacheForTime:l=60}=n,c=async()=>await o(e,{...t,cache:!1});r&&a&&setTimeout(c,1e3*l),s&&window.addEventListener("online",c),i&&(window.addEventListener("focus",c),document.addEventListener("visibilitychange",async()=>{"visible"===document.visibilityState&&await c()}))}(i,a||{},l,s);let{params:r,priorRequest:d,response:L,paramsAsText:O}=function(e,t,n,o,r,i){const{cache:s,cacheForTime:a,infiniteScroll:l}=n,[c,d]=l||[],u=JSON.stringify(c?{...t,[c]:void 0}:t);let f=i.find(t=>t.request===u&&t.path===e);if(c){let e=(f?.currentPage||0)+1||t?.[c]||1;f?f.currentPage=e:(o[d]=[],e=1),t={...t,[c]: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}}(i,j,l,N,$,F);if(j=r,N=L,function(t,n,o,r,i){if("undefined"==typeof window||!o?.length)return;const[s,,a]=o;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 o=e.target;localStorage.setItem("scroll",JSON.stringify([o.scrollTop,o.scrollLeft,a])),o.scrollHeight-o.scrollTop<=o.clientHeight+200&&i(t,{...n,cache:!1,[s]:(r?.currentPage||0)+1})},l.addEventListener("scroll",window.scrollListener)):e.log("paginateDOM not found",{color:"red"})}(i,a,h,d,s),b>0&&d?.lastFetchTime>Date.now()-1e3*b)throw new Error(`Fetch rate limit exceeded for ${i}. 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:i,request:O,lastFetchTime:Date.now(),controller:A});let{fetchParams:k,paramsGETRequest:R}=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 l="";return s||(l=(Object.keys(o).length?"?":"")+new URLSearchParams(o).toString()),{fetchParams:a,paramsGETRequest:l}}(u,c,E,j,!!f,P);if("function"==typeof T){const e=T(i,N,j,k);Array.isArray(e)&&([i,N,j,k]=e)}const q=new Date,D=await async function(e,t,o,r,i,s){const a="undefined"!=typeof window?window.grab:globalThis.grab,l=a?.mock?.[t],c=JSON.stringify(i);if(l&&(!l.method||l.method===r.method)&&(!l.params||c===JSON.stringify(l.params)))return await n(l.delay||0),"function"==typeof l.response?l.response(i):l.response;const d=await fetch(e+t+o,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,i,R,k,j,S);if($?N=$({...N,isLoading:void 0}):"object"==typeof N&&delete N.isLoading,"function"==typeof x){const e=x(i,N,j,k);Array.isArray(e)&&([i,N,j,k]=e)}const J=((Number(new Date)-Number(q))/1e3).toFixed(1);w&&v(`Path:${g+i+R}\n${JSON.stringify(a,null,2)}\nTime: ${J}s\nResponse: ${e.printJSONStructure(D)}`);const[,I]=h||[];return N=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}(D,N,$,I),F[0]&&(F[0].response=N),$&&(N=$(N)),N}catch(D){if("function"==typeof L&&L(D.message,g+i,j),l.retryAttempts&&l.retryAttempts>0)return await s(i,{...a,retryAttempts:--l.retryAttempts});!D.message.includes("signal")&&w&&(v(`Error: ${D.message}\nPath:${g+i}\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=s;a.instance=(e={})=>(t,n={})=>s(t,{...e,...n}),a.log=[],a.mock={},a.defaults={},"undefined"!=typeof window?(window.log=e.log,window.grab=a,i(),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=e.log,globalThis.grab=a),Object.defineProperty(exports,"log",{enumerable:!0,get:()=>e.log}),exports.buildUrl=o,exports.debouncer=t,exports.default=a,exports.grab=a,exports.setupDevTools=i,exports.showAlert=r,exports.wait=n;
|
|
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":["../src/grab-api/common/utils.ts","../src/grab-api/ui/devtools.ts","../src/grab-api/core/core.ts","../src/grab-api/core/flow-control.ts","../src/grab-api/core/regrab-events.ts","../src/grab-api/response/response-handler.ts","../src/grab-api/core/cache-pagination.ts","../src/grab-api/response/infinite-scroll.ts","../src/grab-api/core/request-executor.ts","../src/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);\r\n let finalBaseURL = baseURL;\r\n let finalPath = path;\r\n\r\n if (s(\"http:\") || s(\"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 \"../logging/log-json\";\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 \"../logging/log-json\";\r\nimport {\r\n GrabOptions,\r\n GrabResponse,\r\n GrabFunction\r\n} from \"../common/types\";\r\nimport { buildUrl } from \"../common/utils\";\r\nimport { showAlert } from \"../ui/devtools\";\r\n\r\nimport { getMergedOptions, handleFlowControl } from \"./flow-control\";\r\nimport { handleRegrabEvents } from \"./regrab-events\";\r\nimport { initializeResponse, mapResultToResponse } 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, response: responseOption, method = merged.post ? \"POST\" : merged.put ? \"PUT\" : merged.patch ? \"PATCH\" : \"GET\",\r\n cache, timeout = 30, baseURL = (typeof process !== \"undefined\" && process.env.SERVER_API_URL) || \"/api/\",\r\n cancelOngoingIfNew, cancelNewIfOngoing, rateLimit, debug, infiniteScroll, logger = log,\r\n onRequest, onResponse, onError, onStream, body, ...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 let response: any = {};\r\n let resFunction: ((data: any) => any) | null = null;\r\n const target = (typeof window !== \"undefined\" ? window.grab : (globalThis as any).grab) as GrabFunction;\r\n const grabLog = target?.log || [];\r\n\r\n try {\r\n const flowResult = await handleFlowControl(path, options || {}, merged, grab);\r\n if (flowResult) return flowResult as any;\r\n\r\n handleRegrabEvents(path, options || {}, merged, grab);\r\n\r\n ({ response, resFunction } = initializeResponse(responseOption));\r\n\r\n let { params: updatedParams, priorRequest, response: updatedResponse, paramsAsText } = manageCacheAndPagination(path, params, merged, response, resFunction, grabLog);\r\n params = updatedParams;\r\n response = updatedResponse;\r\n\r\n setupInfiniteScroll(path, options, infiniteScroll, priorRequest, grab);\r\n\r\n // Set loading state\r\n if (resFunction) response = resFunction({ ...response, isLoading: true });\r\n else if (typeof response === \"object\") response.isLoading = true;\r\n\r\n if (rateLimit > 0 && priorRequest?.lastFetchTime > Date.now() - 1000 * rateLimit) {\r\n throw new Error(`Fetch rate limit exceeded for ${path}. Wait ${rateLimit}s between requests.`);\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({ path, request: paramsAsText, lastFetchTime: Date.now(), controller });\r\n\r\n let { fetchParams, paramsGETRequest } = prepareFetchRequest(method, headers, body, params, !!cache, signal);\r\n\r\n if (typeof onRequest === \"function\") {\r\n const modified = onRequest(path, response, params, fetchParams);\r\n if (Array.isArray(modified)) [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const startTime = new Date();\r\n const res = await executeRequest(baseURL, path, paramsGETRequest, fetchParams, params, onStream);\r\n\r\n // Clear loading state\r\n if (resFunction) 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)) [path, response, params, fetchParams] = modified;\r\n }\r\n\r\n const elapsedTime = ((Number(new Date()) - Number(startTime)) / 1000).toFixed(1);\r\n if (debug) {\r\n logger(`Path:${baseURL + path + paramsGETRequest}\\n${JSON.stringify(options, null, 2)}\\nTime: ${elapsedTime}s\\nResponse: ${printJSONStructure(res)}`);\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\r\n } catch (error: any) {\r\n if (typeof onError === \"function\") onError(error.message, baseURL + path, params);\r\n\r\n if (merged.retryAttempts && merged.retryAttempts > 0) {\r\n return await grab(path, { ...options, retryAttempts: --merged.retryAttempts });\r\n }\r\n\r\n if (!error.message.includes(\"signal\") && debug) {\r\n logger(`Error: ${error.message}\\nPath:${baseURL + path}\\n`, { color: \"red\" });\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 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 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/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 \"../logging/log-json\";\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 = 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 ((window as any).scrollListener && typeof (paginateDOM as any).removeEventListener === \"function\") {\r\n (paginateDOM as any).removeEventListener(\"scroll\", (window as any).scrollListener);\r\n }\r\n\r\n (window as any).scrollListener = (event: Event) => {\r\n const t = event.target as HTMLElement;\r\n localStorage.setItem(\"scroll\", JSON.stringify([t.scrollTop, t.scrollLeft, paginateElement]));\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(\"scroll\", (window as any).scrollListener);\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 \"./ui/devtools\";\r\nimport { GrabFunction, GrabOptions } from \"./common/types\";\r\nimport { log } from \"./logging/log-json\";\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 } from \"./logging/log-json\";\r\nexport * from \"./common/types\";\r\nexport * from \"./ui/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","resFunction","grabLog","flowResult","mergedOptions","debounce","repeat","repeatEvery","setDefaults","task","i","setInterval","handleFlowControl","regrabOnStale","regrabOnFocus","regrabOnNetwork","cacheForTime","regrab","visibilityState","handleRegrabEvents","initializeResponse","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","isLoading","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.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);\r\n let finalBaseURL = baseURL;\r\n let finalPath = path;\r\n\r\n if (s(\"http:\") || s(\"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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"}
|