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/icons/svg/index.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export function log(message: string|object = "", options: LogOptions = {}) {
|
|
14
14
|
let {
|
|
15
|
-
color
|
|
16
|
-
style = "color:
|
|
15
|
+
color,
|
|
16
|
+
style = "color:rgb(54, 165, 220); font-size: 10pt;",
|
|
17
17
|
hideInProduction = undefined,
|
|
18
18
|
startSpinner = false,
|
|
19
19
|
stopSpinner = false,
|
|
20
20
|
} = options;
|
|
21
|
+
const colors = getColors();
|
|
21
22
|
|
|
22
23
|
// Auto-detect if we should hide logs in production based on hostname
|
|
23
24
|
if (typeof hideInProduction === "undefined")
|
|
@@ -30,9 +31,17 @@ export function log(message: string|object = "", options: LogOptions = {}) {
|
|
|
30
31
|
message =
|
|
31
32
|
printJSONStructure(message) + "\n\n" + JSON.stringify(message, null, 2);
|
|
32
33
|
|
|
33
|
-
//
|
|
34
|
+
// change color: [red] to color: red if only one
|
|
35
|
+
if (Array.isArray(color) && color.length == 1) color = color[0];
|
|
36
|
+
|
|
37
|
+
//colorize in terminal (%c is only in browser but we polyfill it)
|
|
34
38
|
if (color && typeof process !== undefined)
|
|
35
|
-
message
|
|
39
|
+
if (message.includes("%c") && Array.isArray(color)) // replace each c with color[i]
|
|
40
|
+
message = message.replace(/%c/g, (match, index) => colors[color[index]] || "");
|
|
41
|
+
else if (color && typeof color === "string")
|
|
42
|
+
message = (colors[color] || "") + message + colors.reset;
|
|
43
|
+
|
|
44
|
+
|
|
36
45
|
|
|
37
46
|
// Displays an animated spinner in the terminal with the provided text.
|
|
38
47
|
var i = 0;
|
|
@@ -40,7 +49,7 @@ export function log(message: string|object = "", options: LogOptions = {}) {
|
|
|
40
49
|
if (startSpinner)
|
|
41
50
|
(global || globalThis).interval = setInterval(() => {
|
|
42
51
|
process.stdout.write(
|
|
43
|
-
(colors[color] || "") +
|
|
52
|
+
(Array.isArray(color) ? colors[color[0]] : colors[color] || "") +
|
|
44
53
|
"\r" +
|
|
45
54
|
"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏".split("")[(i = ++i % 10)] +
|
|
46
55
|
" " +
|
|
@@ -51,7 +60,7 @@ export function log(message: string|object = "", options: LogOptions = {}) {
|
|
|
51
60
|
else if (stopSpinner) {
|
|
52
61
|
clearInterval((global || globalThis).interval);
|
|
53
62
|
process.stdout.write(
|
|
54
|
-
"\r" + (message || "
|
|
63
|
+
"\r" + (message || " ") + " ".repeat(message.length + 20) + "\n"
|
|
55
64
|
);
|
|
56
65
|
} else if (typeof style === "string") {
|
|
57
66
|
// check if style is a one word color code or named color
|
|
@@ -76,7 +85,7 @@ export interface LogOptions {
|
|
|
76
85
|
/** CSS style string or array of CSS strings for browser console styling */
|
|
77
86
|
style?: string | string[];
|
|
78
87
|
/** Optional color name or code for terminal environments */
|
|
79
|
-
color?:
|
|
88
|
+
color?: ColorName | ColorName[] | string | string[] ;
|
|
80
89
|
/** If true, hides log in production (auto-detects by hostname if undefined) */
|
|
81
90
|
hideInProduction?: boolean;
|
|
82
91
|
/** Start a spinner (for CLI tools, optional) */
|
|
@@ -85,42 +94,109 @@ export interface LogOptions {
|
|
|
85
94
|
stopSpinner?: boolean;
|
|
86
95
|
}
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
bgRed
|
|
110
|
-
bgGreen
|
|
111
|
-
bgYellow
|
|
112
|
-
bgBlue
|
|
113
|
-
bgMagenta
|
|
114
|
-
bgCyan
|
|
115
|
-
bgWhite
|
|
116
|
-
bgGray
|
|
97
|
+
/**
|
|
98
|
+
* Available color names
|
|
99
|
+
*/
|
|
100
|
+
export enum ColorName {
|
|
101
|
+
RESET = 'reset',
|
|
102
|
+
BLACK = 'black',
|
|
103
|
+
RED = 'red',
|
|
104
|
+
GREEN = 'green',
|
|
105
|
+
YELLOW = 'yellow',
|
|
106
|
+
BLUE = 'blue',
|
|
107
|
+
MAGENTA = 'magenta',
|
|
108
|
+
CYAN = 'cyan',
|
|
109
|
+
WHITE = 'white',
|
|
110
|
+
GRAY = 'gray',
|
|
111
|
+
BRIGHT_RED = 'brightRed',
|
|
112
|
+
BRIGHT_GREEN = 'brightGreen',
|
|
113
|
+
BRIGHT_YELLOW = 'brightYellow',
|
|
114
|
+
BRIGHT_BLUE = 'brightBlue',
|
|
115
|
+
BRIGHT_MAGENTA = 'brightMagenta',
|
|
116
|
+
BRIGHT_CYAN = 'brightCyan',
|
|
117
|
+
BRIGHT_WHITE = 'brightWhite',
|
|
118
|
+
BG_RED = 'bgRed',
|
|
119
|
+
BG_GREEN = 'bgGreen',
|
|
120
|
+
BG_YELLOW = 'bgYellow',
|
|
121
|
+
BG_BLUE = 'bgBlue',
|
|
122
|
+
BG_MAGENTA = 'bgMagenta',
|
|
123
|
+
BG_CYAN = 'bgCyan',
|
|
124
|
+
BG_WHITE = 'bgWhite',
|
|
125
|
+
BG_GRAY = 'bgGray',
|
|
126
|
+
BG_BLACK = 'bgBlack',
|
|
127
|
+
BG_BRIGHT_RED = 'bgBrightRed',
|
|
128
|
+
BG_BRIGHT_GREEN = 'bgBrightGreen',
|
|
129
|
+
BG_BRIGHT_YELLOW = 'bgBrightYellow',
|
|
130
|
+
BG_BRIGHT_BLUE = 'bgBrightBlue',
|
|
131
|
+
BG_BRIGHT_MAGENTA = 'bgBrightMagenta',
|
|
132
|
+
BG_BRIGHT_CYAN = 'bgBrightCyan',
|
|
133
|
+
BG_BRIGHT_WHITE = 'bgBrightWhite',
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Color mapping with ANSI codes and HTML hex values
|
|
138
|
+
* @type {Record<ColorName, [number, string]>}
|
|
139
|
+
* @description Maps color names to [ansiCode, hexValue] pairs
|
|
140
|
+
* - ansiCode: ANSI escape sequence number for terminal colors
|
|
141
|
+
* - hexValue: Hex color value (without #) for HTML/CSS
|
|
142
|
+
*/
|
|
143
|
+
const colorMap: Record<ColorName, [number, string]> = {
|
|
144
|
+
[ColorName.RESET]: [0, '000000'],
|
|
145
|
+
[ColorName.BLACK]: [30, '000000'],
|
|
146
|
+
[ColorName.RED]: [31, 'ff0000'],
|
|
147
|
+
[ColorName.GREEN]: [32, '00ff00'],
|
|
148
|
+
[ColorName.YELLOW]: [33, 'ffff00'],
|
|
149
|
+
[ColorName.BLUE]: [34, '0000ff'],
|
|
150
|
+
[ColorName.MAGENTA]: [35, 'ff00ff'],
|
|
151
|
+
[ColorName.CYAN]: [36, '00ffff'],
|
|
152
|
+
[ColorName.WHITE]: [37, 'ffffff'],
|
|
153
|
+
[ColorName.GRAY]: [90, '808080'],
|
|
154
|
+
[ColorName.BRIGHT_RED]: [91, 'ff5555'],
|
|
155
|
+
[ColorName.BRIGHT_GREEN]: [92, '55ff55'],
|
|
156
|
+
[ColorName.BRIGHT_YELLOW]: [93, 'ffff55'],
|
|
157
|
+
[ColorName.BRIGHT_BLUE]: [94, '5555ff'],
|
|
158
|
+
[ColorName.BRIGHT_MAGENTA]: [95, 'ff55ff'],
|
|
159
|
+
[ColorName.BRIGHT_CYAN]: [96, '55ffff'],
|
|
160
|
+
[ColorName.BRIGHT_WHITE]: [97, 'ffffff'],
|
|
161
|
+
[ColorName.BG_BLACK]: [40, '000000'],
|
|
162
|
+
[ColorName.BG_RED]: [41, 'ff0000'],
|
|
163
|
+
[ColorName.BG_GREEN]: [42, '00ff00'],
|
|
164
|
+
[ColorName.BG_YELLOW]: [43, 'ffff00'],
|
|
165
|
+
[ColorName.BG_BLUE]: [44, '0000ff'],
|
|
166
|
+
[ColorName.BG_MAGENTA]: [45, 'ff00ff'],
|
|
167
|
+
[ColorName.BG_CYAN]: [46, '00ffff'],
|
|
168
|
+
[ColorName.BG_WHITE]: [47, 'ffffff'],
|
|
169
|
+
[ColorName.BG_GRAY]: [100, '808080'],
|
|
170
|
+
[ColorName.BG_BRIGHT_RED]: [101, 'ff8888'],
|
|
171
|
+
[ColorName.BG_BRIGHT_GREEN]: [102, '88ff88'],
|
|
172
|
+
[ColorName.BG_BRIGHT_YELLOW]: [103, 'ffff88'],
|
|
173
|
+
[ColorName.BG_BRIGHT_BLUE]: [104, '8888ff'],
|
|
174
|
+
[ColorName.BG_BRIGHT_MAGENTA]: [105, 'ff88ff'],
|
|
175
|
+
[ColorName.BG_BRIGHT_CYAN]: [106, '88ffff'],
|
|
176
|
+
[ColorName.BG_BRIGHT_WHITE]: [107, 'ffffff'],
|
|
117
177
|
};
|
|
118
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Returns color codes based on the specified format
|
|
181
|
+
* @param format - Output format for colors
|
|
182
|
+
* - 'ansi': Returns ANSI escape codes (e.g., '\x1b[31m')
|
|
183
|
+
* - 'html': Returns HTML hex colors (e.g., '#ff0000')
|
|
184
|
+
* @returns Object with color names as keys and color codes as values
|
|
185
|
+
*/
|
|
186
|
+
export function getColors(format: 'html' | 'ansi' = 'ansi'): Record<ColorName, string> {
|
|
187
|
+
const colors: Record<ColorName, string> = {} as Record<ColorName, string>;
|
|
188
|
+
for (const [name, [ansiCode, hexCode]] of Object.entries(colorMap)) {
|
|
189
|
+
colors[name] = format === 'html' ? '#' + hexCode : '\x1b[' + ansiCode + 'm';
|
|
190
|
+
}
|
|
191
|
+
return colors;
|
|
192
|
+
}
|
|
193
|
+
|
|
119
194
|
/**
|
|
120
195
|
* Determines the appropriate color code for a given value type
|
|
121
196
|
* Used for consistent color coding in the structure visualization
|
|
122
197
|
*/
|
|
123
198
|
function getColorForType(value) {
|
|
199
|
+
const colors = getColors();
|
|
124
200
|
if (typeof value === "string") return colors.yellow;
|
|
125
201
|
if (typeof value === "number") return colors.cyan;
|
|
126
202
|
if (typeof value === "boolean") return colors.magenta;
|
|
@@ -153,20 +229,30 @@ function getTypeString(value) {
|
|
|
153
229
|
* Creates a colored visualization of a JSON object's structure
|
|
154
230
|
* Shows the shape and types of the data rather than actual values
|
|
155
231
|
* Recursively processes nested objects and arrays
|
|
232
|
+
* @param {object} obj - The JSON object to visualize
|
|
233
|
+
* @param {number} indent - The number of spaces to indent the object
|
|
234
|
+
* @param {ColorFormat} colorFormat - The color format to use
|
|
235
|
+
* @returns {string} The colored visualization of the JSON object
|
|
156
236
|
*/
|
|
157
|
-
export function printJSONStructure(obj, indent = 0) {
|
|
237
|
+
export function printJSONStructure(obj, indent = 0, colorFormat: 'html' | 'ansi' = 'ansi') {
|
|
238
|
+
const colors = getColors(colorFormat);
|
|
158
239
|
const pad = " ".repeat(indent);
|
|
159
|
-
|
|
240
|
+
var result = "";
|
|
160
241
|
// Handle primitive values and null
|
|
161
242
|
if (typeof obj !== "object" || obj === null) {
|
|
162
243
|
const color = getColorForType(obj);
|
|
163
244
|
return color + getTypeString(obj) + colors.reset;
|
|
164
245
|
}
|
|
165
|
-
|
|
166
246
|
// Handle arrays with special bracket formatting
|
|
167
247
|
if (Array.isArray(obj)) {
|
|
168
|
-
|
|
248
|
+
result = colors.blue + "[" + colors.reset;
|
|
169
249
|
if (obj.length) result += "\n";
|
|
250
|
+
// if array has items all of the same type or object types, print only once
|
|
251
|
+
if (obj.every((item) => typeof item === typeof obj[0])) {
|
|
252
|
+
result += pad + " " + printJSONStructure(obj[0], indent + 1);
|
|
253
|
+
result += ",";
|
|
254
|
+
result += "\n";
|
|
255
|
+
} else {
|
|
170
256
|
obj.forEach((item, idx) => {
|
|
171
257
|
result += pad + " " + printJSONStructure(item, indent + 1);
|
|
172
258
|
if (idx < obj.length - 1) result += ",";
|
|
@@ -174,10 +260,11 @@ export function printJSONStructure(obj, indent = 0) {
|
|
|
174
260
|
});
|
|
175
261
|
result += pad + colors.blue + "]" + colors.reset;
|
|
176
262
|
return result;
|
|
263
|
+
}
|
|
177
264
|
}
|
|
178
265
|
|
|
179
266
|
// Handle objects with special brace and property formatting
|
|
180
|
-
|
|
267
|
+
result = colors.green + "{" + colors.reset;
|
|
181
268
|
const keys = Object.keys(obj);
|
|
182
269
|
if (keys.length) result += "\n";
|
|
183
270
|
keys.forEach((key, index) => {
|
|
@@ -219,91 +306,3 @@ export function printJSONStructure(obj, indent = 0) {
|
|
|
219
306
|
return result;
|
|
220
307
|
}
|
|
221
308
|
|
|
222
|
-
/**
|
|
223
|
-
* Shows message in a modal overlay with scrollable message stack
|
|
224
|
-
* and is easier to dismiss unlike alert() which blocks window.
|
|
225
|
-
* Creates a semi-transparent overlay with a white box containing the message.
|
|
226
|
-
* @param {string} msg - The message to display
|
|
227
|
-
*/
|
|
228
|
-
export function showAlert(msg) {
|
|
229
|
-
if (typeof document === "undefined") return;
|
|
230
|
-
let o = document.getElementById("alert-overlay"),
|
|
231
|
-
list;
|
|
232
|
-
|
|
233
|
-
// Create overlay and alert box if they don't exist
|
|
234
|
-
if (!o) {
|
|
235
|
-
o = document.body.appendChild(document.createElement("div"));
|
|
236
|
-
o.id = "alert-overlay";
|
|
237
|
-
o.setAttribute(
|
|
238
|
-
"style",
|
|
239
|
-
"position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center"
|
|
240
|
-
);
|
|
241
|
-
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;">
|
|
242
|
-
<button id="close-alert" style="position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;">×</button>
|
|
243
|
-
<div id="alert-list" style="overflow:auto;flex:1;"></div>
|
|
244
|
-
</div>`;
|
|
245
|
-
|
|
246
|
-
// Add click handlers to close overlay
|
|
247
|
-
o.addEventListener("click", (e) => e.target == o && o.remove());
|
|
248
|
-
document.getElementById("close-alert").onclick = () => o.remove();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
list = o.querySelector("#alert-list");
|
|
252
|
-
|
|
253
|
-
// Add new message to list
|
|
254
|
-
list.innerHTML += `<div style="border-bottom:1px solid #333; font-size:1.2em;margin:0.5em 0;">${msg}</div>`;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Sets up development tools for debugging API requests
|
|
259
|
-
* Adds a keyboard shortcut (Ctrl+I) that shows a modal with request history
|
|
260
|
-
* Each request entry shows:
|
|
261
|
-
* - Request path
|
|
262
|
-
* - Request details
|
|
263
|
-
* - Response data
|
|
264
|
-
* - Timestamp
|
|
265
|
-
*/
|
|
266
|
-
export function setupDevTools() {
|
|
267
|
-
// Keyboard shortcut (Ctrl+I) to toggle debug view
|
|
268
|
-
document.addEventListener("keydown", (e) => {
|
|
269
|
-
if (e.key === "i" && e.ctrlKey) {
|
|
270
|
-
// Create HTML of the grab.log requests
|
|
271
|
-
let html = " ";
|
|
272
|
-
for (let request of grab.log) {
|
|
273
|
-
html += `<div style="margin-bottom:1em; border-bottom:1px solid #ccc; padding-bottom:1em;">
|
|
274
|
-
<b>Path:</b> ${request.path}<br>
|
|
275
|
-
<b>Request:</b> ${request.request}<br>
|
|
276
|
-
<b>Response:</b> ${JSON.stringify(request.response, null, 2)}<br>
|
|
277
|
-
<b>Time:</b> ${new Date(request.lastFetchTime).toLocaleString()}
|
|
278
|
-
</div>`;
|
|
279
|
-
}
|
|
280
|
-
showAlert(html);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Displays an animated spinner in the terminal with the provided text.
|
|
287
|
-
* The spinner animates in-place until the returned function is called,
|
|
288
|
-
* which stops the spinner and prints a success message.
|
|
289
|
-
* @param {string} text - The text to display next to the spinner animation.
|
|
290
|
-
* @returns {(success?: string) => void} Stop function with optional message.
|
|
291
|
-
* @example
|
|
292
|
-
* const stopSpinner = showSpinnerInTerminal('Downloading...');
|
|
293
|
-
* setTimeout(() => {
|
|
294
|
-
* stopSpinner('Success!');
|
|
295
|
-
* }, 2000);
|
|
296
|
-
*/
|
|
297
|
-
export function showSpinnerInTerminal(text) {
|
|
298
|
-
let i = 0,
|
|
299
|
-
interval = setInterval(() => {
|
|
300
|
-
process.stdout.write(
|
|
301
|
-
"\r" + "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏".split("")[(i = ++i % 10)] + " " + text
|
|
302
|
-
);
|
|
303
|
-
}, 50);
|
|
304
|
-
|
|
305
|
-
return function (success = "✔ Done!") {
|
|
306
|
-
clearInterval(interval);
|
|
307
|
-
process.stdout.write("\r" + success + " ".repeat(text.length) + "\n");
|
|
308
|
-
};
|
|
309
|
-
}
|