grab-url 1.0.7 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/download.cjs.js +3 -0
- package/dist/download.cjs.js.map +1 -0
- package/dist/download.d.ts +1 -0
- package/dist/download.es.js +3715 -0
- package/dist/download.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 +63 -54
- package/dist/grab-api.es.js +244 -354
- package/dist/grab-api.es.js.map +1 -1
- package/dist/grab-api.umd.js +2 -0
- package/dist/grab-api.umd.js.map +1 -0
- package/dist/icons.cjs.js +1 -1
- package/dist/icons.cjs.js.map +1 -1
- package/dist/icons.d.ts +0 -0
- package/dist/icons.es.js +3 -3
- package/dist/icons.es.js.map +1 -1
- package/dist/log.cjs.js +2 -0
- package/dist/log.cjs.js.map +1 -0
- package/dist/log.d.ts +123 -0
- package/dist/log.es.js +299 -0
- package/dist/log.es.js.map +1 -0
- package/package.json +17 -13
- package/readme.md +7 -7
- package/src/grab-api.ts +150 -112
- package/src/grab-url.js +9 -40
- package/src/icons/cli/spinners.js +818 -0
- package/src/icons/svg/index.ts +0 -0
- package/src/icons/svg/loading-bouncy-ball.svg +0 -0
- package/src/icons/svg/loading-double-ring.svg +0 -0
- package/src/icons/svg/loading-eclipse.svg +0 -0
- package/src/icons/svg/loading-ellipsis.svg +0 -0
- package/src/icons/svg/loading-floating-search.svg +0 -0
- package/src/icons/svg/loading-gears.svg +0 -0
- package/src/icons/svg/loading-infinity.svg +0 -0
- package/src/icons/svg/loading-orbital.svg +0 -0
- package/src/icons/svg/loading-pacman.svg +0 -0
- package/src/icons/svg/loading-pulse-bars.svg +0 -0
- package/src/icons/svg/loading-red-blue-ball.svg +0 -0
- package/src/icons/svg/loading-reload-arrow.svg +0 -0
- package/src/icons/svg/loading-ring.svg +0 -0
- package/src/icons/svg/loading-ripple.svg +0 -0
- package/src/icons/svg/loading-spinner-oval.svg +0 -0
- package/src/icons/svg/loading-spinner.svg +0 -0
- package/src/icons/svg/loading-square-blocks.svg +0 -0
- package/src/{log.ts → log-json.ts} +128 -129
- package/src/icons/cli/spinners.json +0 -1074
package/src/grab-api.ts
CHANGED
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
printJSONStructure,
|
|
3
3
|
log,
|
|
4
|
-
showAlert,
|
|
5
|
-
setupDevTools,
|
|
6
4
|
type LogOptions,
|
|
7
|
-
} from "./log";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* TODO
|
|
11
|
-
* - react tests
|
|
12
|
-
* - grab error popup and dev tool
|
|
13
|
-
* - show net log in alert
|
|
14
|
-
* - progress
|
|
15
|
-
* - pagination working
|
|
16
|
-
* - tests in stackblitz
|
|
17
|
-
* - loading icons
|
|
18
|
-
* - cache revalidation
|
|
19
|
-
*/
|
|
5
|
+
} from "./log-json";
|
|
20
6
|
|
|
21
7
|
/**
|
|
22
8
|
* ### GRAB: Generate Request to API from Browser
|
|
23
|
-
* 
|
|
24
10
|
*
|
|
25
11
|
* 1. **GRAB is the FBEST Request Manager: Functionally Brilliant, Elegantly Simple Tool**: One Function, no dependencies,
|
|
26
|
-
* minimalist syntax, [more features than alternatives](https://grab.js.org/
|
|
12
|
+
* minimalist syntax, [more features than alternatives](https://grab.js.org/docs/Comparisons)
|
|
27
13
|
* 2. **Auto-JSON Convert**: Pass parameters and get response or error in JSON, handling other data types as is.
|
|
28
14
|
* 3. **isLoading Status**: Sets `.isLoading=true` on the pre-initialized response object so you can show a "Loading..." in any framework
|
|
29
15
|
* 4. **Debug Logging**: Adds global `log()` and prints colored JSON structure, response, timing for requests in test.
|
|
@@ -39,7 +25,7 @@ import {
|
|
|
39
25
|
* 14. **Framework Agnostic**: Alternatives like TanStack work only in component initialization and depend on React & others.
|
|
40
26
|
* 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`
|
|
41
27
|
* 16. **TypeScript Tooltips**: Developers can hover over option names and autocomplete TypeScript.
|
|
42
|
-
* 17. **Request Stategies**: [🎯 Examples](https://grab.js.org/
|
|
28
|
+
* 17. **Request Stategies**: [🎯 Examples](https://grab.js.org/docs/Examples) show common stategies like debounce, repeat, proxy, unit tests, interceptors, file upload, etc
|
|
43
29
|
* 18. **Rate Limiting**: Built-in rate limiting to prevent multi-click cascading responses, require to wait seconds between requests.
|
|
44
30
|
* 19. **Repeat**: Repeat request this many times, or repeat every X seconds to poll for updates.
|
|
45
31
|
* 20. **Loading Icons**: Import from `grab-url/icons` to get enhanced animated loading icons.
|
|
@@ -77,33 +63,27 @@ import {
|
|
|
77
63
|
* @param {any} [...params] All other params become GET params, POST body, and other methods.
|
|
78
64
|
* @returns {Promise<Object>} The response object with resulting data or .error if error.
|
|
79
65
|
* @author [vtempest (2025)](https://github.com/vtempest/GRAB-URL)
|
|
80
|
-
* @see
|
|
81
|
-
* @example import grab from 'grab-url';
|
|
82
|
-
* let res = {};
|
|
83
|
-
* await grab('search', {
|
|
84
|
-
* response: res,
|
|
85
|
-
* query: "search words"
|
|
86
|
-
* })
|
|
66
|
+
* @see [🎯 Examples](https://grab.js.org/docs/Examples) [📑 Docs](https://grab.js.org)
|
|
87
67
|
*/
|
|
88
68
|
export default async function grab<TResponse = any, TParams = any>(
|
|
89
69
|
path: string,
|
|
90
70
|
options: GrabOptions<TResponse, TParams>
|
|
91
71
|
): Promise<GrabResponse<TResponse>> {
|
|
92
|
-
|
|
72
|
+
var {
|
|
93
73
|
headers,
|
|
94
74
|
response = {} as any, // Pre-initialized object to set the response in. isLoading and error are also set on this object.
|
|
95
75
|
method = options.post // set post: true for POST, omit for GET
|
|
96
76
|
? "POST"
|
|
97
77
|
: options.put
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
78
|
+
? "PUT"
|
|
79
|
+
: options.patch
|
|
80
|
+
? "PATCH"
|
|
81
|
+
: "GET",
|
|
102
82
|
cache = false, // Enable/disable frontend caching
|
|
103
83
|
cacheForTime = 60, // Seconds to consider data stale and invalidate cache
|
|
104
84
|
timeout = 30, // Request timeout in seconds
|
|
105
85
|
baseURL = (typeof process !== "undefined" && process.env.SERVER_API_URL) ||
|
|
106
|
-
|
|
86
|
+
"/api/", // Use env var or default to /api/
|
|
107
87
|
cancelOngoingIfNew = false, // Cancel previous request for same path
|
|
108
88
|
cancelNewIfOngoing = false, // Don't make new request if one is ongoing
|
|
109
89
|
rateLimit = 0, // Minimum seconds between requests
|
|
@@ -127,7 +107,7 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
127
107
|
put = false,
|
|
128
108
|
patch = false,
|
|
129
109
|
body = null,
|
|
130
|
-
...params
|
|
110
|
+
...params // All other params become request params/query
|
|
131
111
|
} = {
|
|
132
112
|
// Destructure options with defaults, merging with any globally set defaults
|
|
133
113
|
...(typeof window !== "undefined"
|
|
@@ -143,7 +123,7 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
143
123
|
else if (!s("/") && !baseURL.endsWith("/")) path = "/" + path;
|
|
144
124
|
else if (s("/") && baseURL.endsWith("/")) path = path.slice(1);
|
|
145
125
|
|
|
146
|
-
|
|
126
|
+
try {
|
|
147
127
|
//handle debounce
|
|
148
128
|
if (debounce > 0)
|
|
149
129
|
return (await debouncer(async () => {
|
|
@@ -181,7 +161,7 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
181
161
|
}
|
|
182
162
|
|
|
183
163
|
// regrab on stale, on window refocus, on network
|
|
184
|
-
if (typeof window !== undefined) {
|
|
164
|
+
if (typeof window !== "undefined") {
|
|
185
165
|
const regrab = async () => await grab(path, { ...options, cache: false });
|
|
186
166
|
if (regrabOnStale && cache) setTimeout(regrab, 1000 * cacheForTime);
|
|
187
167
|
if (regrabOnNetwork) window.addEventListener("online", regrab);
|
|
@@ -203,21 +183,22 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
203
183
|
// Configure infinite scroll behavior if enabled
|
|
204
184
|
// Attaches scroll listener to specified element that triggers next page load
|
|
205
185
|
if (infiniteScroll?.length && typeof paginateElement !== "undefined"
|
|
206
|
-
|
|
186
|
+
&& typeof window !== "undefined") {
|
|
207
187
|
let paginateDOM =
|
|
208
188
|
typeof paginateElement === "string"
|
|
209
189
|
? document.querySelector(paginateElement)
|
|
210
190
|
: paginateElement;
|
|
211
191
|
|
|
212
192
|
if (!paginateDOM) log("paginateDOM not found", { color: "red" });
|
|
213
|
-
|
|
214
|
-
|
|
193
|
+
else if (window.scrollListener
|
|
194
|
+
&& typeof paginateDOM !== "undefined"
|
|
195
|
+
&& typeof paginateDOM.removeEventListener === "function")
|
|
215
196
|
paginateDOM.removeEventListener("scroll", window.scrollListener);
|
|
216
197
|
|
|
217
198
|
// Your modified scroll listener with position saving
|
|
218
199
|
window.scrollListener = async (event) => {
|
|
219
200
|
const t = event.target as HTMLElement;
|
|
220
|
-
|
|
201
|
+
|
|
221
202
|
// Save scroll position whenever user scrolls
|
|
222
203
|
localStorage.setItem(
|
|
223
204
|
"scroll",
|
|
@@ -233,8 +214,8 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
233
214
|
}
|
|
234
215
|
};
|
|
235
216
|
|
|
236
|
-
if (paginateDOM)
|
|
237
|
-
|
|
217
|
+
if (paginateDOM)
|
|
218
|
+
paginateDOM.addEventListener("scroll", window.scrollListener);
|
|
238
219
|
}
|
|
239
220
|
|
|
240
221
|
// Check request history for a previous request with same path/params
|
|
@@ -280,8 +261,7 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
280
261
|
|
|
281
262
|
// Update page tracking
|
|
282
263
|
if (priorRequest) priorRequest.currentPage = pageNumber;
|
|
283
|
-
|
|
284
|
-
params[paginateKey] = pageNumber;
|
|
264
|
+
params = { ...params, [paginateKey]: pageNumber };
|
|
285
265
|
}
|
|
286
266
|
|
|
287
267
|
// Set loading state on response object
|
|
@@ -377,7 +357,7 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
377
357
|
// Make actual API request and handle response based on content type
|
|
378
358
|
res = await fetch(baseURL + path + paramsGETRequest, fetchParams).catch(
|
|
379
359
|
(e) => {
|
|
380
|
-
throw new Error(e);
|
|
360
|
+
throw new Error(e.message);
|
|
381
361
|
}
|
|
382
362
|
);
|
|
383
363
|
|
|
@@ -394,8 +374,8 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
394
374
|
? res && res.json()
|
|
395
375
|
: type.includes("application/pdf") ||
|
|
396
376
|
type.includes("application/octet-stream")
|
|
397
|
-
|
|
398
|
-
|
|
377
|
+
? res.blob()
|
|
378
|
+
: res.text()
|
|
399
379
|
: res.json()
|
|
400
380
|
).catch((e) => {
|
|
401
381
|
throw new Error("Error parsing response: " + e);
|
|
@@ -427,15 +407,15 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
427
407
|
if (debug) {
|
|
428
408
|
logger(
|
|
429
409
|
"Path:" +
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
410
|
+
baseURL +
|
|
411
|
+
path +
|
|
412
|
+
paramsGETRequest +
|
|
413
|
+
"\n" +
|
|
414
|
+
JSON.stringify(options, null, 2) +
|
|
415
|
+
"\nTime: " +
|
|
416
|
+
elapsedTime +
|
|
417
|
+
"s\nResponse: " +
|
|
418
|
+
printJSONStructure(res)
|
|
439
419
|
);
|
|
440
420
|
// console.log(res);
|
|
441
421
|
}
|
|
@@ -467,46 +447,47 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
467
447
|
if (resFunction) response = resFunction(response);
|
|
468
448
|
|
|
469
449
|
return response;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
450
|
+
} catch (error) {
|
|
451
|
+
// Handle any errors that occurred during request processing
|
|
452
|
+
let errorMessage =
|
|
453
|
+
"Error: " + error.message + "\nPath:" + baseURL + path + "\n";
|
|
454
|
+
// JSON.stringify(params);
|
|
455
|
+
|
|
456
|
+
// if onError hook is passed
|
|
457
|
+
if (typeof onError === "function")
|
|
458
|
+
onError(error.message, baseURL + path, params);
|
|
459
|
+
|
|
460
|
+
// Retry request if retries are configured and attempts remain
|
|
461
|
+
if (options.retryAttempts > 0)
|
|
462
|
+
return await grab(path, {
|
|
463
|
+
...options,
|
|
464
|
+
retryAttempts: --options.retryAttempts,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Update error state in response object
|
|
468
|
+
// Do not show errors for duplicate aborted requests
|
|
469
|
+
if (!error.message.includes("signal") && options.debug) {
|
|
470
|
+
logger(errorMessage, { color: "red" });
|
|
471
|
+
if (debug && typeof document !== "undefined") showAlert(errorMessage);
|
|
472
|
+
}
|
|
473
|
+
response.error = error.message;
|
|
474
|
+
if (typeof response === "function") {
|
|
475
|
+
response.data = response({ isLoading: undefined, error: error.message });
|
|
476
|
+
response = response.data;
|
|
477
|
+
} else delete response?.isLoading;
|
|
478
|
+
|
|
479
|
+
// Log error in request history
|
|
480
|
+
if (typeof grab.log != "undefined")
|
|
481
|
+
grab.log?.unshift({
|
|
482
|
+
path,
|
|
483
|
+
request: JSON.stringify(params),
|
|
484
|
+
error: error.message,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// if (typeof options.response === "function")
|
|
488
|
+
// response = options.response(response);
|
|
489
|
+
return response;
|
|
490
|
+
}
|
|
510
491
|
}
|
|
511
492
|
|
|
512
493
|
/**
|
|
@@ -517,8 +498,8 @@ export default async function grab<TResponse = any, TParams = any>(
|
|
|
517
498
|
*/
|
|
518
499
|
grab.instance =
|
|
519
500
|
(defaults = {}) =>
|
|
520
|
-
|
|
521
|
-
|
|
501
|
+
(path, options = {}) =>
|
|
502
|
+
grab(path, { ...defaults, ...options });
|
|
522
503
|
|
|
523
504
|
// delays execution so that future calls may override and only executes last one
|
|
524
505
|
const debouncer = async (func, wait) => {
|
|
@@ -536,7 +517,6 @@ const debouncer = async (func, wait) => {
|
|
|
536
517
|
// Add globals to window in browser, or global in Node.js
|
|
537
518
|
if (typeof window !== "undefined") {
|
|
538
519
|
window.log = log;
|
|
539
|
-
// @ts-ignore
|
|
540
520
|
window.grab = grab;
|
|
541
521
|
|
|
542
522
|
window.grab.log = [];
|
|
@@ -568,6 +548,73 @@ if (typeof window !== "undefined") {
|
|
|
568
548
|
globalThis.grab = grab.instance();
|
|
569
549
|
}
|
|
570
550
|
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Shows message in a modal overlay with scrollable message stack
|
|
555
|
+
* and is easier to dismiss unlike alert() which blocks window.
|
|
556
|
+
* Creates a semi-transparent overlay with a white box containing the message.
|
|
557
|
+
* @param {string} msg - The message to display
|
|
558
|
+
*/
|
|
559
|
+
export function showAlert(msg) {
|
|
560
|
+
if (typeof document === "undefined") return;
|
|
561
|
+
let o = document.getElementById("alert-overlay"),
|
|
562
|
+
list;
|
|
563
|
+
|
|
564
|
+
// Create overlay and alert box if they don't exist
|
|
565
|
+
if (!o) {
|
|
566
|
+
o = document.body.appendChild(document.createElement("div"));
|
|
567
|
+
o.id = "alert-overlay";
|
|
568
|
+
o.setAttribute(
|
|
569
|
+
"style",
|
|
570
|
+
"position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center"
|
|
571
|
+
);
|
|
572
|
+
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;">
|
|
573
|
+
<button id="close-alert" style="position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;">×</button>
|
|
574
|
+
<div id="alert-list" style="overflow:auto;flex:1;"></div>
|
|
575
|
+
</div>`;
|
|
576
|
+
|
|
577
|
+
// Add click handlers to close overlay
|
|
578
|
+
o.addEventListener("click", (e) => e.target == o && o.remove());
|
|
579
|
+
document.getElementById("close-alert").onclick = () => o.remove();
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
list = o.querySelector("#alert-list");
|
|
583
|
+
|
|
584
|
+
// Add new message to list
|
|
585
|
+
list.innerHTML += `<div style="border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;">${msg}</div>`;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Sets up development tools for debugging API requests
|
|
590
|
+
* Adds a keyboard shortcut (Ctrl+Alt+I) that shows a modal with request history
|
|
591
|
+
* Each request entry shows:
|
|
592
|
+
* - Request path
|
|
593
|
+
* - Request details
|
|
594
|
+
* - Response data
|
|
595
|
+
* - Timestamp
|
|
596
|
+
*/
|
|
597
|
+
export function setupDevTools() {
|
|
598
|
+
// Keyboard shortcut (Ctrl+Alt+I) to toggle debug view
|
|
599
|
+
document.addEventListener("keydown", (e) => {
|
|
600
|
+
if (e.key === "i" && e.ctrlKey && e.altKey) {
|
|
601
|
+
// Create HTML of the grab.log requests
|
|
602
|
+
let html = " ";
|
|
603
|
+
for (let request of grab.log) {
|
|
604
|
+
html += `<div style="margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;">
|
|
605
|
+
<b>Path:</b> ${request.path}<br>
|
|
606
|
+
<b>Request:</b> ${printJSONStructure(request.request, 0, 'html')}<br>
|
|
607
|
+
<b>Response:</b> ${printJSONStructure(request.response, 0, 'html')}<br>
|
|
608
|
+
<b>Time:</b> ${new Date(request.lastFetchTime).toLocaleString()}
|
|
609
|
+
</div>`;
|
|
610
|
+
}
|
|
611
|
+
showAlert(html);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
|
|
571
618
|
/***************** TYPESCRIPT INTERFACES *****************/
|
|
572
619
|
|
|
573
620
|
// Core response object that gets populated with API response data
|
|
@@ -693,23 +740,23 @@ export interface GrabGlobal {
|
|
|
693
740
|
export interface GrabFunction {
|
|
694
741
|
/**
|
|
695
742
|
* ### GRAB: Generate Request to API from Browser
|
|
696
|
-
* 
|
|
697
744
|
* Make API request with path
|
|
698
745
|
* @returns {Promise<Object>} The response object with resulting data or .error if error.
|
|
699
746
|
* @author [vtempest (2025)](https://github.com/vtempest/GRAB-URL)
|
|
700
|
-
* @see [🎯 Examples](https://grab.js.org/
|
|
747
|
+
* @see [🎯 Examples](https://grab.js.org/docs/Examples) [📑 Docs](https://grab.js.org/lib)
|
|
701
748
|
*/
|
|
702
|
-
<TResponse = any, TParams = Record<string, any>>(path: string): Promise<
|
|
749
|
+
<TResponse = any, TParams = Record<string, any>>(path: string, options?: GrabOptions<TResponse, TParams>): Promise<
|
|
703
750
|
GrabResponse<TResponse>
|
|
704
751
|
>;
|
|
705
752
|
|
|
706
753
|
/**
|
|
707
754
|
* ### GRAB: Generate Request to API from Browser
|
|
708
|
-
* 
|
|
709
756
|
* Make API request with path and options/parameters
|
|
710
757
|
* @returns {Promise<Object>} The response object with resulting data or .error if error.
|
|
711
758
|
* @author [vtempest (2025)](https://github.com/vtempest/GRAB-URL)
|
|
712
|
-
* @see [🎯 Examples](https://grab.js.org/
|
|
759
|
+
* @see [🎯 Examples](https://grab.js.org/docs/Examples) [📑 Docs](https://grab.js.org/lib)
|
|
713
760
|
*/
|
|
714
761
|
<TResponse = any, TParams = Record<string, any>>(
|
|
715
762
|
path: string,
|
|
@@ -748,15 +795,6 @@ export interface printJSONStructureFunction {
|
|
|
748
795
|
(obj: any): string;
|
|
749
796
|
}
|
|
750
797
|
|
|
751
|
-
// Helper type for creating typed API clients
|
|
752
|
-
// export type TypedGrabFunction = <
|
|
753
|
-
// TResponse = any,
|
|
754
|
-
// TParams = Record<string, any>
|
|
755
|
-
// >(
|
|
756
|
-
// path: string,
|
|
757
|
-
// config?: GrabOptions<TResponse, TParams>
|
|
758
|
-
// ) => Promise<GrabResponse<TResponse>>;
|
|
759
|
-
|
|
760
798
|
declare global {
|
|
761
799
|
// Browser globals
|
|
762
800
|
interface Window {
|
|
@@ -778,4 +816,4 @@ declare global {
|
|
|
778
816
|
var grab: GrabFunction;
|
|
779
817
|
}
|
|
780
818
|
|
|
781
|
-
export { grab, log,
|
|
819
|
+
export { grab, log, printJSONStructure };
|
package/src/grab-url.js
CHANGED
|
@@ -4,48 +4,17 @@ import fs from 'fs';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { pipeline } from 'stream/promises';
|
|
6
6
|
import { Readable } from 'stream';
|
|
7
|
-
import cliProgress from 'cli-progress';
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
import ora from 'ora';
|
|
10
|
-
import Table from 'cli-table3';
|
|
11
|
-
import grab, { log } from '../dist/grab-api.es.js';
|
|
12
7
|
import readline from 'readline';
|
|
13
8
|
import { readFileSync } from 'fs';
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Try the icons/cli path first (where it actually exists)
|
|
24
|
-
spinners = JSON.parse(readFileSync(join(__dirname, 'icons', 'cli', 'spinners.json'), 'utf8'));
|
|
25
|
-
} catch (error) {
|
|
26
|
-
try {
|
|
27
|
-
// Try the local path
|
|
28
|
-
spinners = JSON.parse(readFileSync(join(__dirname, 'spinners.json'), 'utf8'));
|
|
29
|
-
} catch (error2) {
|
|
30
|
-
try {
|
|
31
|
-
// Try the parent directory (src)
|
|
32
|
-
spinners = JSON.parse(readFileSync(join(__dirname, '..', 'spinners.json'), 'utf8'));
|
|
33
|
-
} catch (error3) {
|
|
34
|
-
try {
|
|
35
|
-
// Try the current working directory
|
|
36
|
-
spinners = JSON.parse(readFileSync(join(process.cwd(), 'src', 'spinners.json'), 'utf8'));
|
|
37
|
-
} catch (error4) {
|
|
38
|
-
// Fallback to default spinners if file not found
|
|
39
|
-
console.warn('Could not load spinners.json, using defaults');
|
|
40
|
-
spinners = {
|
|
41
|
-
dots: { frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] },
|
|
42
|
-
line: { frames: ['-', '\\', '|', '/'] },
|
|
43
|
-
arrow: { frames: ['←', '↖', '↑', '↗', '→', '↘', '↓', '↙'] }
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import spinners from './icons/cli/spinners.js';
|
|
11
|
+
import grab, { log } from './grab-api';
|
|
12
|
+
import { pathToFileURL } from 'url';
|
|
13
|
+
|
|
14
|
+
import cliProgress from 'cli-progress';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
// Use cli-spinners for spinner animations
|
|
17
|
+
const __dirname = dirname(import.meta.url);
|
|
49
18
|
|
|
50
19
|
// --- ArgParser from grab-cli.js ---
|
|
51
20
|
class ArgParser {
|