sfiledl 2.0.1 → 2.1.1
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/build/lib.cjs +362 -90
- package/build/lib.cjs.map +1 -1
- package/build/lib.d.ts +42 -26
- package/build/lib.mjs +335 -86
- package/build/lib.mjs.map +1 -1
- package/changelog +92 -0
- package/package.json +2 -2
- package/readme.md +170 -0
- package/readme +0 -3
package/build/lib.mjs
CHANGED
|
@@ -1,105 +1,354 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import fs__default from 'fs/promises';
|
|
4
|
+
import { chromium } from 'playwright';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
|
|
7
|
+
class AppError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
retryable;
|
|
10
|
+
timestamp;
|
|
11
|
+
context;
|
|
12
|
+
constructor(message, code, retryable = false, context) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.retryable = retryable;
|
|
16
|
+
this.name = this.constructor.name;
|
|
17
|
+
this.timestamp = new Date().toISOString();
|
|
18
|
+
this.context = context ? { ...context } : undefined;
|
|
19
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
toJSON() {
|
|
22
|
+
return {
|
|
23
|
+
name: this.name,
|
|
24
|
+
message: this.message,
|
|
25
|
+
code: this.code,
|
|
26
|
+
retryable: this.retryable,
|
|
27
|
+
timestamp: this.timestamp,
|
|
28
|
+
context: this.context,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class ValidationError extends AppError {
|
|
34
|
+
constructor(message, context) {
|
|
35
|
+
super(message, 'VALIDATION_ERROR', false, context);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
class NetworkError extends AppError {
|
|
39
|
+
constructor(message, context) {
|
|
40
|
+
super(message, 'NETWORK_ERROR', true, context);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
class FileError extends AppError {
|
|
44
|
+
constructor(message, context) {
|
|
45
|
+
super(message, 'FILE_ERROR', false, context);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class BrowserError extends AppError {
|
|
49
|
+
constructor(message, context) {
|
|
50
|
+
super(message, 'BROWSER_ERROR', true, context);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const DEFAULTS = {
|
|
55
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
|
|
56
|
+
headless: true,
|
|
57
|
+
timeout: 100000,
|
|
58
|
+
downloadButtonTimeout: 100000,
|
|
59
|
+
fallbackWaitMs: 5000,
|
|
22
60
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
61
|
+
|
|
62
|
+
class SfilePageInteractions {
|
|
63
|
+
page;
|
|
64
|
+
logger;
|
|
65
|
+
constructor(page, logger) {
|
|
66
|
+
this.page = page;
|
|
67
|
+
this.logger = logger;
|
|
68
|
+
}
|
|
69
|
+
async waitForDownloadButton(timeout) {
|
|
70
|
+
this.logger.debug('Waiting for #download button to be visible');
|
|
71
|
+
const button = this.page.locator('#download');
|
|
72
|
+
await button.waitFor({ state: 'visible', timeout });
|
|
73
|
+
this.logger.debug('Waiting for button to become active (href != "#" and pointerEvents != "none")');
|
|
74
|
+
await this.page.waitForFunction(() => {
|
|
75
|
+
const btn = document.querySelector('#download');
|
|
76
|
+
if (!btn)
|
|
77
|
+
return false;
|
|
78
|
+
const href = btn.getAttribute('href');
|
|
79
|
+
const style = window.getComputedStyle(btn);
|
|
80
|
+
return href && href !== '#' && style.pointerEvents !== 'none';
|
|
81
|
+
}, { timeout });
|
|
82
|
+
}
|
|
83
|
+
async extractIntermediateUrl() {
|
|
84
|
+
this.logger.debug('Extracting href from #download');
|
|
85
|
+
const href = await this.page.$eval('#download', (el) => el.href);
|
|
86
|
+
if (!href || href === '#') {
|
|
87
|
+
throw new NetworkError('Download button href is invalid', { href });
|
|
88
|
+
}
|
|
89
|
+
return href;
|
|
90
|
+
}
|
|
26
91
|
}
|
|
27
|
-
|
|
28
|
-
|
|
92
|
+
|
|
93
|
+
function sanitizeFilename(name, replacement = '_') {
|
|
94
|
+
const sanitized = name
|
|
95
|
+
.replace(/[<>:"/\\|?*\x00-\x1F]/g, replacement)
|
|
96
|
+
.replace(new RegExp(`${replacement}+`, 'g'), replacement)
|
|
97
|
+
.trim()
|
|
98
|
+
.slice(0, 255);
|
|
99
|
+
return sanitized || 'file.bin';
|
|
29
100
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
101
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
102
|
+
function safeStringify(value) {
|
|
103
|
+
try {
|
|
104
|
+
return JSON.stringify(value);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return '[unserializable]';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class BrowserManager {
|
|
112
|
+
logger;
|
|
113
|
+
opts;
|
|
114
|
+
browser = null;
|
|
115
|
+
context = null;
|
|
116
|
+
page = null;
|
|
117
|
+
interactions = null;
|
|
118
|
+
debugDir = null;
|
|
119
|
+
constructor(logger, opts) {
|
|
120
|
+
this.logger = logger;
|
|
121
|
+
this.opts = opts;
|
|
122
|
+
}
|
|
123
|
+
async launch() {
|
|
124
|
+
try {
|
|
125
|
+
this.logger.info('Launching browser', { headless: this.opts.headless });
|
|
126
|
+
this.browser = await chromium.launch({
|
|
127
|
+
headless: this.opts.headless,
|
|
128
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
|
|
129
|
+
});
|
|
130
|
+
this.context = await this.browser.newContext({
|
|
131
|
+
userAgent: this.opts.userAgent,
|
|
132
|
+
acceptDownloads: true,
|
|
133
|
+
locale: 'en-US',
|
|
134
|
+
});
|
|
135
|
+
await this.context.addCookies([
|
|
136
|
+
{
|
|
137
|
+
name: 'safe_link_counter',
|
|
138
|
+
value: '1',
|
|
139
|
+
domain: '.sfile.co',
|
|
140
|
+
path: '/',
|
|
141
|
+
expires: Math.floor(Date.now() / 1000) + 3600,
|
|
142
|
+
},
|
|
143
|
+
]);
|
|
144
|
+
this.page = await this.context.newPage();
|
|
145
|
+
this.interactions = new SfilePageInteractions(this.page, this.logger);
|
|
146
|
+
if (this.opts.debug) {
|
|
147
|
+
this.page.on('console', (msg) => {
|
|
148
|
+
if (msg.type() === 'error')
|
|
149
|
+
this.logger.error(`[console] ${msg.text()}`);
|
|
150
|
+
else if (msg.type() === 'warning')
|
|
151
|
+
this.logger.warn(`[console] ${msg.text()}`);
|
|
152
|
+
});
|
|
153
|
+
this.page.on('pageerror', (err) => this.logger.error(`[pageerror] ${err.message}`));
|
|
154
|
+
}
|
|
155
|
+
this.logger.info('Browser ready');
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
throw new BrowserError(`Failed to launch browser: ${err.message}`);
|
|
38
159
|
}
|
|
39
|
-
return LibStatus.instance;
|
|
40
160
|
}
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
161
|
+
async goto(url, waitUntil = 'networkidle') {
|
|
162
|
+
if (!this.page)
|
|
163
|
+
throw new BrowserError('Page not initialized');
|
|
164
|
+
try {
|
|
165
|
+
this.logger.debug(`Navigating to ${url} (waitUntil=${waitUntil})`);
|
|
166
|
+
await this.page.goto(url, { waitUntil, timeout: this.opts.timeout });
|
|
44
167
|
}
|
|
45
|
-
|
|
46
|
-
|
|
168
|
+
catch (err) {
|
|
169
|
+
throw new NetworkError(`Navigation failed: ${err.message}`, { url });
|
|
47
170
|
}
|
|
48
|
-
|
|
171
|
+
}
|
|
172
|
+
async waitForDownloadButton() {
|
|
173
|
+
if (!this.interactions)
|
|
174
|
+
throw new BrowserError('Interactions not ready');
|
|
175
|
+
await this.interactions.waitForDownloadButton(DEFAULTS.downloadButtonTimeout);
|
|
176
|
+
}
|
|
177
|
+
async getIntermediateUrl() {
|
|
178
|
+
if (!this.interactions)
|
|
179
|
+
throw new BrowserError('Interactions not ready');
|
|
180
|
+
return this.interactions.extractIntermediateUrl();
|
|
181
|
+
}
|
|
182
|
+
async startDownloadAndWait(autoUrl) {
|
|
183
|
+
if (!this.page)
|
|
184
|
+
throw new BrowserError('Page not initialized');
|
|
185
|
+
const downloadPromise = this.page
|
|
186
|
+
.waitForEvent('download', { timeout: this.opts.timeout })
|
|
187
|
+
.catch((err) => {
|
|
188
|
+
this.logger.warn(`Download event wait failed: ${err.message}`);
|
|
189
|
+
return null;
|
|
190
|
+
});
|
|
191
|
+
this.logger.debug(`Navigating to auto URL: ${autoUrl}`);
|
|
192
|
+
await this.page.goto(autoUrl, { waitUntil: 'commit', timeout: this.opts.timeout });
|
|
193
|
+
const download = await downloadPromise;
|
|
194
|
+
if (download) {
|
|
195
|
+
this.logger.info('Download event captured');
|
|
196
|
+
return download;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
async fallbackCollectFileResponse() {
|
|
201
|
+
if (!this.page)
|
|
202
|
+
throw new BrowserError('Page not initialized');
|
|
203
|
+
this.logger.warn('Falling back to response interception');
|
|
204
|
+
const responses = [];
|
|
205
|
+
const handler = (res) => responses.push(res);
|
|
206
|
+
this.page.on('response', handler);
|
|
207
|
+
await sleep(DEFAULTS.fallbackWaitMs);
|
|
208
|
+
this.page.off('response', handler);
|
|
209
|
+
const fileResponse = [...responses].reverse().find((r) => {
|
|
210
|
+
const disposition = r.headers()['content-disposition'];
|
|
211
|
+
return ((disposition && disposition.includes('attachment')) ||
|
|
212
|
+
r.url().includes('/downloadfile/'));
|
|
213
|
+
});
|
|
214
|
+
if (!fileResponse)
|
|
215
|
+
return null;
|
|
216
|
+
const buffer = await fileResponse.body();
|
|
217
|
+
let filename = fileResponse.url().split('/').pop()?.split('?')[0] || 'file.bin';
|
|
218
|
+
filename = filename.replace(/[<>:"/\\|?*]/g, '_');
|
|
219
|
+
return { buffer, filename };
|
|
220
|
+
}
|
|
221
|
+
async saveDebugArtifacts(errorMessage) {
|
|
222
|
+
if (!this.page)
|
|
49
223
|
return;
|
|
224
|
+
try {
|
|
225
|
+
this.debugDir = path.join(os.tmpdir(), `sfile_debug_${Date.now()}`);
|
|
226
|
+
await fs.mkdir(this.debugDir, { recursive: true });
|
|
227
|
+
await Promise.all([
|
|
228
|
+
this.page.screenshot({
|
|
229
|
+
path: path.join(this.debugDir, 'error.png'),
|
|
230
|
+
fullPage: true,
|
|
231
|
+
}),
|
|
232
|
+
fs.writeFile(path.join(this.debugDir, 'error.html'), await this.page.content()),
|
|
233
|
+
fs.writeFile(path.join(this.debugDir, 'error.txt'), errorMessage),
|
|
234
|
+
]);
|
|
235
|
+
this.logger.info(`Debug artifacts saved to ${this.debugDir}`);
|
|
50
236
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const highlight = `${COLORS.CYAN}`;
|
|
54
|
-
console.warn(`\n${border}`);
|
|
55
|
-
console.warn(`${label} DEVELOPMENT VERSION IN USE`);
|
|
56
|
-
console.warn(`${COLORS.GRAY}Library: ${COLORS.RESET}${highlight}${LIB_INFO.name}${COLORS.RESET}`);
|
|
57
|
-
console.warn(`${COLORS.GRAY}Current Version: ${COLORS.RESET}${highlight}${LIB_INFO.currentVersion}${COLORS.RESET}`);
|
|
58
|
-
console.warn(`${COLORS.GRAY}Previous Version: ${COLORS.RESET}${highlight}${LIB_INFO.previousVersion}${COLORS.RESET}`);
|
|
59
|
-
console.warn(`${COLORS.GRAY}Architecture: ${COLORS.RESET}${highlight}CLI -> Framework (Full Refactor)${COLORS.RESET}`);
|
|
60
|
-
console.warn(`\n${COLORS.RED}${COLORS.BRIGHT}DO NOT USE IN PRODUCTION ENVIRONMENTS${COLORS.RESET}`);
|
|
61
|
-
console.warn(`${COLORS.GRAY}API stability is not guaranteed. Breaking changes may occur without notice.${COLORS.RESET}`);
|
|
62
|
-
console.warn(`${COLORS.GRAY}To suppress this warning, set env MY_SUPER_FRAMEWORK_SUPPRESS_WARNING=true${COLORS.RESET}`);
|
|
63
|
-
console.warn(`${border}\n`);
|
|
64
|
-
warningDisplayed = true;
|
|
65
|
-
}
|
|
66
|
-
suppressWarning() {
|
|
67
|
-
warningDisplayed = true;
|
|
68
|
-
}
|
|
69
|
-
ensureStable() {
|
|
70
|
-
if (LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION) {
|
|
71
|
-
const errorMessage = `${COLORS.RED}${COLORS.BRIGHT}[${LIB_INFO.name}] Stability Check Failed${COLORS.RESET}\n` +
|
|
72
|
-
`${COLORS.GRAY}Library version ${LIB_INFO.currentVersion} is under active development.${COLORS.RESET}\n` +
|
|
73
|
-
`${COLORS.GRAY}Current status: ${LIB_INFO.status}${COLORS.RESET}`;
|
|
74
|
-
throw new Error(stripAnsi(errorMessage));
|
|
237
|
+
catch (e) {
|
|
238
|
+
this.logger.error(`Failed to save debug artifacts: ${e.message}`);
|
|
75
239
|
}
|
|
76
240
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
241
|
+
async close() {
|
|
242
|
+
if (this.page)
|
|
243
|
+
await this.page.close().catch(() => { });
|
|
244
|
+
if (this.context)
|
|
245
|
+
await this.context.close().catch(() => { });
|
|
246
|
+
if (this.browser)
|
|
247
|
+
await this.browser.close().catch(() => { });
|
|
248
|
+
this.logger.debug('Browser closed');
|
|
82
249
|
}
|
|
83
250
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
251
|
+
|
|
252
|
+
class Logger {
|
|
253
|
+
minLevel;
|
|
254
|
+
constructor(debugMode = false) {
|
|
255
|
+
this.minLevel = debugMode ? 'debug' : 'info';
|
|
256
|
+
}
|
|
257
|
+
setDebug(enabled) {
|
|
258
|
+
this.minLevel = enabled ? 'debug' : 'info';
|
|
259
|
+
}
|
|
260
|
+
shouldLog(level) {
|
|
261
|
+
const levels = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
262
|
+
return levels[level] >= levels[this.minLevel];
|
|
263
|
+
}
|
|
264
|
+
log(level, message, context) {
|
|
265
|
+
if (!this.shouldLog(level))
|
|
266
|
+
return;
|
|
267
|
+
const ts = new Date().toISOString();
|
|
268
|
+
const ctx = context ? ` ${safeStringify(context)}` : '';
|
|
269
|
+
console.log(`[${ts}] ${level.toUpperCase()}: ${message}${ctx}`);
|
|
270
|
+
}
|
|
271
|
+
debug(msg, ctx) {
|
|
272
|
+
this.log('debug', msg, ctx);
|
|
273
|
+
}
|
|
274
|
+
info(msg, ctx) {
|
|
275
|
+
this.log('info', msg, ctx);
|
|
276
|
+
}
|
|
277
|
+
warn(msg, ctx) {
|
|
278
|
+
this.log('warn', msg, ctx);
|
|
279
|
+
}
|
|
280
|
+
error(msg, ctx) {
|
|
281
|
+
this.log('error', msg, ctx);
|
|
282
|
+
}
|
|
90
283
|
}
|
|
91
|
-
|
|
92
|
-
|
|
284
|
+
|
|
285
|
+
function normalizeOptions(opts) {
|
|
286
|
+
return {
|
|
287
|
+
headless: opts?.headless ?? DEFAULTS.headless,
|
|
288
|
+
debug: opts?.debug ?? false,
|
|
289
|
+
userAgent: opts?.userAgent ?? DEFAULTS.userAgent,
|
|
290
|
+
timeout: opts?.timeout ?? DEFAULTS.timeout,
|
|
291
|
+
};
|
|
93
292
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
293
|
+
|
|
294
|
+
async function downloadSfile(url, saveDir, options) {
|
|
295
|
+
const opts = normalizeOptions(options);
|
|
296
|
+
const logger = new Logger(opts.debug);
|
|
297
|
+
if (!url || !url.includes('sfile.co')) {
|
|
298
|
+
throw new ValidationError('URL must contain sfile.co', { url });
|
|
299
|
+
}
|
|
300
|
+
await fs__default.mkdir(saveDir, { recursive: true });
|
|
301
|
+
const browserMgr = new BrowserManager(logger, {
|
|
302
|
+
headless: opts.headless,
|
|
303
|
+
userAgent: opts.userAgent,
|
|
304
|
+
timeout: opts.timeout,
|
|
305
|
+
debug: opts.debug,
|
|
306
|
+
});
|
|
307
|
+
try {
|
|
308
|
+
await browserMgr.launch();
|
|
309
|
+
await browserMgr.goto(url, 'networkidle');
|
|
310
|
+
await browserMgr.waitForDownloadButton();
|
|
311
|
+
const intermediateUrl = await browserMgr.getIntermediateUrl();
|
|
312
|
+
const autoUrl = intermediateUrl.includes('?')
|
|
313
|
+
? `${intermediateUrl}&auto=1`
|
|
314
|
+
: `${intermediateUrl}?auto=1`;
|
|
315
|
+
let download = await browserMgr.startDownloadAndWait(autoUrl);
|
|
316
|
+
let finalPath;
|
|
317
|
+
let fileSize;
|
|
318
|
+
let method;
|
|
319
|
+
if (download) {
|
|
320
|
+
const suggested = download.suggestedFilename() || 'file.bin';
|
|
321
|
+
const filename = sanitizeFilename(suggested);
|
|
322
|
+
finalPath = path.join(saveDir, filename);
|
|
323
|
+
await download.saveAs(finalPath);
|
|
324
|
+
const stat = await fs__default.stat(finalPath);
|
|
325
|
+
fileSize = stat.size;
|
|
326
|
+
method = 'direct';
|
|
327
|
+
logger.info(`Saved via direct download: ${finalPath} (${fileSize} bytes)`);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
const fallback = await browserMgr.fallbackCollectFileResponse();
|
|
331
|
+
if (!fallback) {
|
|
332
|
+
throw new NetworkError('No download event and no file response found');
|
|
333
|
+
}
|
|
334
|
+
const { buffer, filename: rawName } = fallback;
|
|
335
|
+
const filename = sanitizeFilename(rawName);
|
|
336
|
+
finalPath = path.join(saveDir, filename);
|
|
337
|
+
await fs__default.writeFile(finalPath, buffer);
|
|
338
|
+
fileSize = buffer.length;
|
|
339
|
+
method = 'fallback';
|
|
340
|
+
logger.info(`Saved via fallback: ${finalPath} (${fileSize} bytes)`);
|
|
341
|
+
}
|
|
342
|
+
return { filePath: finalPath, size: fileSize, method };
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
await browserMgr.saveDebugArtifacts(err.message);
|
|
346
|
+
throw err;
|
|
347
|
+
}
|
|
348
|
+
finally {
|
|
349
|
+
await browserMgr.close();
|
|
98
350
|
}
|
|
99
|
-
}
|
|
100
|
-
if (isDevelopmentVersion()) {
|
|
101
|
-
LibStatus.getInstance();
|
|
102
351
|
}
|
|
103
352
|
|
|
104
|
-
export {
|
|
353
|
+
export { AppError, BrowserError, FileError, NetworkError, ValidationError, downloadSfile };
|
|
105
354
|
//# sourceMappingURL=lib.mjs.map
|
package/build/lib.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.mjs","sources":["../lib/lib.ts"],"sourcesContent":["/**\n * ====================================================================================================\n * WARNING: LIBRARY UNDER ACTIVE DEVELOPMENT - NOT FOR PRODUCTION USE\n * ====================================================================================================\n * \n * Status: DEPRECATED / UNSTABLE\n * Previous Version: 1.0.2 (CLI-based architecture)\n * Target Version: 2.0.1 (Full framework architecture)\n * \n * BREAKING CHANGES NOTICE:\n * - This library is undergoing a complete rewrite and architectural refactor.\n * - Legacy CLI-based implementation has been entirely removed.\n * - New framework-based architecture is currently under active development.\n * \n * PRODUCTION USE PROHIBITED:\n * - API surface is subject to change without notice.\n * - No backward compatibility guarantees.\n * - Use exclusively for development and testing purposes.\n * \n * ====================================================================================================\n */\n\n// ANSI color codes for terminal output\nconst COLORS = {\n RESET: \"\\x1b[0m\",\n BRIGHT: \"\\x1b[1m\",\n YELLOW: \"\\x1b[33m\",\n RED: \"\\x1b[31m\",\n CYAN: \"\\x1b[36m\",\n GRAY: \"\\x1b[90m\",\n} as const;\n\n/**\n * Development status of the library.\n */\nexport enum LibraryStatus {\n /** Library is under active construction, not ready for production. */\n UNDER_CONSTRUCTION = \"UNDER_CONSTRUCTION\",\n /** Library is stable and ready for production. (Future use) */\n STABLE = \"STABLE\",\n /** Library is deprecated. (Future use) */\n DEPRECATED = \"DEPRECATED\",\n}\n\n/**\n * Library metadata.\n */\nexport const LIB_INFO = {\n name: \"MySuperFramework\",\n currentVersion: \"2.0.1-dev\",\n previousVersion: \"1.0.2\",\n status: LibraryStatus.UNDER_CONSTRUCTION,\n isDeprecated: true,\n message: \"This version represents a complete architectural refactor from CLI to framework. Not stable.\",\n} as const;\n\n// Flag to ensure warning is displayed only once\nlet warningDisplayed = false;\n\n/**\n * Utility function to strip ANSI escape codes from a string.\n * Supports common ANSI sequences (colors, styles, etc.).\n * @param text - Text containing ANSI escape sequences.\n * @returns Plain text without ANSI formatting.\n */\nfunction stripAnsi(text: string): string {\n // Matches ANSI escape sequences: \\x1b[ (or \\u001b[) followed by any number of parameters and ending with a letter\n return text.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, \"\");\n}\n\n/**\n * Checks if the library is currently in development mode.\n * @returns `true` if the library status is `UNDER_CONSTRUCTION`.\n */\nexport function isDevelopmentVersion(): boolean {\n return LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION;\n}\n\n/**\n * Primary class for library status management and developer notifications.\n * Provides programmatic access to version information and stability checks.\n */\nexport class LibStatus {\n private static instance: LibStatus | undefined;\n\n private constructor() {\n this.displayDeveloperWarning();\n }\n\n /**\n * Gets the singleton instance of LibStatus.\n * @returns The LibStatus instance.\n */\n public static getInstance(): LibStatus {\n if (!LibStatus.instance) {\n LibStatus.instance = new LibStatus();\n }\n return LibStatus.instance;\n }\n\n /**\n * Outputs formatted warning message to console upon library initialization.\n * Uses ANSI color codes for enhanced visibility in terminal environments.\n * The warning is displayed only once per process, unless suppressed.\n */\n private displayDeveloperWarning(): void {\n // Skip if console is not available or warning already displayed\n if (typeof console === \"undefined\" || typeof console.warn !== \"function\") {\n return;\n }\n if (warningDisplayed) {\n return;\n }\n // Check environment variable to suppress warning (e.g., for testing)\n // Use bracket notation to avoid TypeScript index signature error\n if (typeof process !== \"undefined\" && process.env && process.env['MY_SUPER_FRAMEWORK_SUPPRESS_WARNING'] === \"true\") {\n return;\n }\n\n const border = `${COLORS.GRAY}${\"=\".repeat(80)}${COLORS.RESET}`;\n const label = `${COLORS.YELLOW}${COLORS.BRIGHT}[WARNING]${COLORS.RESET}`;\n const highlight = `${COLORS.CYAN}`;\n\n console.warn(`\\n${border}`);\n console.warn(`${label} DEVELOPMENT VERSION IN USE`);\n console.warn(`${COLORS.GRAY}Library: ${COLORS.RESET}${highlight}${LIB_INFO.name}${COLORS.RESET}`);\n console.warn(`${COLORS.GRAY}Current Version: ${COLORS.RESET}${highlight}${LIB_INFO.currentVersion}${COLORS.RESET}`);\n console.warn(`${COLORS.GRAY}Previous Version: ${COLORS.RESET}${highlight}${LIB_INFO.previousVersion}${COLORS.RESET}`);\n console.warn(`${COLORS.GRAY}Architecture: ${COLORS.RESET}${highlight}CLI -> Framework (Full Refactor)${COLORS.RESET}`);\n console.warn(`\\n${COLORS.RED}${COLORS.BRIGHT}DO NOT USE IN PRODUCTION ENVIRONMENTS${COLORS.RESET}`);\n console.warn(`${COLORS.GRAY}API stability is not guaranteed. Breaking changes may occur without notice.${COLORS.RESET}`);\n console.warn(`${COLORS.GRAY}To suppress this warning, set env MY_SUPER_FRAMEWORK_SUPPRESS_WARNING=true${COLORS.RESET}`);\n console.warn(`${border}\\n`);\n\n warningDisplayed = true;\n }\n\n /**\n * Suppresses the development warning for this instance.\n * Useful when you need to programmatically disable the warning (e.g., in tests).\n */\n public suppressWarning(): void {\n warningDisplayed = true;\n }\n\n /**\n * Validates library stability status.\n * @throws Error if library is marked as unstable or under construction.\n */\n public ensureStable(): void {\n if (LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION) {\n const errorMessage = `${COLORS.RED}${COLORS.BRIGHT}[${LIB_INFO.name}] Stability Check Failed${COLORS.RESET}\\n` +\n `${COLORS.GRAY}Library version ${LIB_INFO.currentVersion} is under active development.${COLORS.RESET}\\n` +\n `${COLORS.GRAY}Current status: ${LIB_INFO.status}${COLORS.RESET}`;\n\n throw new Error(stripAnsi(errorMessage));\n }\n }\n\n /**\n * Returns the current library version string.\n * @returns Version identifier in semver format.\n */\n public getVersion(): string {\n return LIB_INFO.currentVersion;\n }\n\n /**\n * Returns detailed library metadata.\n * @returns Readonly object containing library information.\n */\n public getInfo(): Readonly<typeof LIB_INFO> {\n return LIB_INFO;\n }\n}\n\n/**\n * Placeholder entry point for framework initialization.\n * Intentionally throws error to prevent usage during development phase.\n * @throws Error indicating framework is not ready for use.\n */\nexport function initFramework(): never {\n const message = `${COLORS.RED}${COLORS.BRIGHT}[${LIB_INFO.name}] Initialization Blocked${COLORS.RESET}\\n` +\n `${COLORS.GRAY}Framework is undergoing complete architectural refactor.${COLORS.RESET}\\n` +\n `${COLORS.GRAY}Migration path: v${LIB_INFO.previousVersion} (CLI) -> v${LIB_INFO.currentVersion} (Framework)${COLORS.RESET}\\n` +\n `${COLORS.GRAY}Status: ${LIB_INFO.status}${COLORS.RESET}`;\n\n throw new Error(stripAnsi(message));\n}\n\n/**\n * Type guard to check if current version is stable.\n * @returns `false` if library is under construction, `true` otherwise.\n */\nexport function isStable(): boolean {\n return LIB_INFO.status !== LibraryStatus.UNDER_CONSTRUCTION;\n}\n\n/**\n * Throws an error if the library is not stable.\n * Use this at the start of your application to ensure you're not using an unstable version.\n * @throws Error when library is under construction.\n */\nexport function assertStable(): void {\n if (LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION) {\n throw new Error(\n `[${LIB_INFO.name}] Cannot use unstable version ${LIB_INFO.currentVersion} in production. ` +\n `Status: ${LIB_INFO.status}`\n );\n }\n}\n\n// Immediate execution block: displays warning upon module import (only once)\n// Does not throw to allow type-checking and development workflows\nif (isDevelopmentVersion()) {\n // Trigger singleton instantiation which displays the warning\n LibStatus.getInstance();\n}"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lib.mjs","sources":["../lib/errors/base.ts","../lib/errors/errors.ts","../lib/config/defaults.ts","../lib/browser/page-interactions.ts","../lib/utils/helpers.ts","../lib/browser/browser-manager.ts","../lib/utils/logger.ts","../lib/config/schema.ts","../lib/core/downloader.ts"],"sourcesContent":["export abstract class AppError extends Error {\n\tpublic readonly timestamp: string\n\tpublic readonly context: Record<string, unknown> | undefined\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly code: string,\n\t\tpublic readonly retryable: boolean = false,\n\t\tcontext?: Record<string, unknown>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = this.constructor.name\n\t\tthis.timestamp = new Date().toISOString()\n\t\tthis.context = context ? { ...context } : undefined\n\t\tError.captureStackTrace?.(this, this.constructor)\n\t}\n\ttoJSON() {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tcode: this.code,\n\t\t\tretryable: this.retryable,\n\t\t\ttimestamp: this.timestamp,\n\t\t\tcontext: this.context,\n\t\t}\n\t}\n}\n","import { AppError } from './base.js'\nexport class ValidationError extends AppError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'VALIDATION_ERROR', false, context)\n\t}\n}\nexport class NetworkError extends AppError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'NETWORK_ERROR', true, context)\n\t}\n}\nexport class FileError extends AppError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'FILE_ERROR', false, context)\n\t}\n}\nexport class BrowserError extends AppError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'BROWSER_ERROR', true, context)\n\t}\n}\n","export const DEFAULTS = {\n\tuserAgent:\n\t\t'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',\n\theadless: true,\n\ttimeout: 100000,\n\tdownloadButtonTimeout: 100000,\n\tfallbackWaitMs: 5000,\n}\n","import { Page } from 'playwright'\nimport { Logger } from '../utils/logger.js'\nimport { NetworkError } from '../errors/index.js'\nexport class SfilePageInteractions {\n\tconstructor(\n\t\tprivate page: Page,\n\t\tprivate logger: Logger,\n\t) {}\n\tasync waitForDownloadButton(timeout: number): Promise<void> {\n\t\tthis.logger.debug('Waiting for #download button to be visible')\n\t\tconst button = this.page.locator('#download')\n\t\tawait button.waitFor({ state: 'visible', timeout })\n\t\tthis.logger.debug(\n\t\t\t'Waiting for button to become active (href != \"#\" and pointerEvents != \"none\")',\n\t\t)\n\t\tawait this.page.waitForFunction(\n\t\t\t() => {\n\t\t\t\tconst btn = document.querySelector('#download') as HTMLAnchorElement | null\n\t\t\t\tif (!btn) return false\n\t\t\t\tconst href = btn.getAttribute('href')\n\t\t\t\tconst style = window.getComputedStyle(btn)\n\t\t\t\treturn href && href !== '#' && style.pointerEvents !== 'none'\n\t\t\t},\n\t\t\t{ timeout },\n\t\t)\n\t}\n\tasync extractIntermediateUrl(): Promise<string> {\n\t\tthis.logger.debug('Extracting href from #download')\n\t\tconst href = await this.page.$eval('#download', (el) => (el as HTMLAnchorElement).href)\n\t\tif (!href || href === '#') {\n\t\t\tthrow new NetworkError('Download button href is invalid', { href })\n\t\t}\n\t\treturn href\n\t}\n}\n","export function sanitizeFilename(name: string, replacement = '_'): string {\n\tconst sanitized = name\n\t\t.replace(/[<>:\"/\\\\|?*\\x00-\\x1F]/g, replacement)\n\t\t.replace(new RegExp(`${replacement}+`, 'g'), replacement)\n\t\t.trim()\n\t\t.slice(0, 255)\n\treturn sanitized || 'file.bin'\n}\nexport const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))\nexport function safeStringify(value: unknown): string {\n\ttry {\n\t\treturn JSON.stringify(value)\n\t} catch {\n\t\treturn '[unserializable]'\n\t}\n}\n","import { chromium, Browser, BrowserContext, Page, Download, Response } from 'playwright'\nimport * as fs from 'fs/promises'\nimport path from 'path'\nimport os from 'os'\nimport { Logger } from '../utils/logger.js'\nimport { BrowserError, NetworkError } from '../errors/index.js'\nimport { DEFAULTS } from '../config/defaults.js'\nimport { SfilePageInteractions } from './page-interactions.js'\nimport { sleep } from '../utils/helpers.js'\nexport interface BrowserManagerOptions {\n\theadless: boolean\n\tuserAgent: string\n\ttimeout: number\n\tdebug: boolean\n}\nexport class BrowserManager {\n\tprivate browser: Browser | null = null\n\tprivate context: BrowserContext | null = null\n\tprivate page: Page | null = null\n\tprivate interactions: SfilePageInteractions | null = null\n\tprivate debugDir: string | null = null\n\tconstructor(\n\t\tprivate logger: Logger,\n\t\tprivate opts: BrowserManagerOptions,\n\t) {}\n\tasync launch(): Promise<void> {\n\t\ttry {\n\t\t\tthis.logger.info('Launching browser', { headless: this.opts.headless })\n\t\t\tthis.browser = await chromium.launch({\n\t\t\t\theadless: this.opts.headless,\n\t\t\t\targs: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],\n\t\t\t})\n\t\t\tthis.context = await this.browser.newContext({\n\t\t\t\tuserAgent: this.opts.userAgent,\n\t\t\t\tacceptDownloads: true,\n\t\t\t\tlocale: 'en-US',\n\t\t\t})\n\t\t\tawait this.context.addCookies([\n\t\t\t\t{\n\t\t\t\t\tname: 'safe_link_counter',\n\t\t\t\t\tvalue: '1',\n\t\t\t\t\tdomain: '.sfile.co',\n\t\t\t\t\tpath: '/',\n\t\t\t\t\texpires: Math.floor(Date.now() / 1000) + 3600,\n\t\t\t\t},\n\t\t\t])\n\t\t\tthis.page = await this.context.newPage()\n\t\t\tthis.interactions = new SfilePageInteractions(this.page, this.logger)\n\t\t\tif (this.opts.debug) {\n\t\t\t\tthis.page.on('console', (msg) => {\n\t\t\t\t\tif (msg.type() === 'error') this.logger.error(`[console] ${msg.text()}`)\n\t\t\t\t\telse if (msg.type() === 'warning') this.logger.warn(`[console] ${msg.text()}`)\n\t\t\t\t})\n\t\t\t\tthis.page.on('pageerror', (err) => this.logger.error(`[pageerror] ${err.message}`))\n\t\t\t}\n\t\t\tthis.logger.info('Browser ready')\n\t\t} catch (err: any) {\n\t\t\tthrow new BrowserError(`Failed to launch browser: ${err.message}`)\n\t\t}\n\t}\n\tasync goto(\n\t\turl: string,\n\t\twaitUntil: 'load' | 'networkidle' | 'commit' = 'networkidle',\n\t): Promise<void> {\n\t\tif (!this.page) throw new BrowserError('Page not initialized')\n\t\ttry {\n\t\t\tthis.logger.debug(`Navigating to ${url} (waitUntil=${waitUntil})`)\n\t\t\tawait this.page.goto(url, { waitUntil, timeout: this.opts.timeout })\n\t\t} catch (err: any) {\n\t\t\tthrow new NetworkError(`Navigation failed: ${err.message}`, { url })\n\t\t}\n\t}\n\tasync waitForDownloadButton(): Promise<void> {\n\t\tif (!this.interactions) throw new BrowserError('Interactions not ready')\n\t\tawait this.interactions.waitForDownloadButton(DEFAULTS.downloadButtonTimeout)\n\t}\n\tasync getIntermediateUrl(): Promise<string> {\n\t\tif (!this.interactions) throw new BrowserError('Interactions not ready')\n\t\treturn this.interactions.extractIntermediateUrl()\n\t}\n\tasync startDownloadAndWait(autoUrl: string): Promise<Download | null> {\n\t\tif (!this.page) throw new BrowserError('Page not initialized')\n\t\tconst downloadPromise = this.page\n\t\t\t.waitForEvent('download', { timeout: this.opts.timeout })\n\t\t\t.catch((err) => {\n\t\t\t\tthis.logger.warn(`Download event wait failed: ${err.message}`)\n\t\t\t\treturn null\n\t\t\t})\n\t\tthis.logger.debug(`Navigating to auto URL: ${autoUrl}`)\n\t\tawait this.page.goto(autoUrl, { waitUntil: 'commit', timeout: this.opts.timeout })\n\t\tconst download = await downloadPromise\n\t\tif (download) {\n\t\t\tthis.logger.info('Download event captured')\n\t\t\treturn download\n\t\t}\n\t\treturn null\n\t}\n\tasync fallbackCollectFileResponse(): Promise<{ buffer: Buffer; filename: string } | null> {\n\t\tif (!this.page) throw new BrowserError('Page not initialized')\n\t\tthis.logger.warn('Falling back to response interception')\n\t\tconst responses: Response[] = []\n\t\tconst handler = (res: Response) => responses.push(res)\n\t\tthis.page.on('response', handler)\n\t\tawait sleep(DEFAULTS.fallbackWaitMs)\n\t\tthis.page.off('response', handler)\n\t\tconst fileResponse = [...responses].reverse().find((r) => {\n\t\t\tconst disposition = r.headers()['content-disposition']\n\t\t\treturn (\n\t\t\t\t(disposition && disposition.includes('attachment')) ||\n\t\t\t\tr.url().includes('/downloadfile/')\n\t\t\t)\n\t\t})\n\t\tif (!fileResponse) return null\n\t\tconst buffer = await fileResponse.body()\n\t\tlet filename = fileResponse.url().split('/').pop()?.split('?')[0] || 'file.bin'\n\t\tfilename = filename.replace(/[<>:\"/\\\\|?*]/g, '_')\n\t\treturn { buffer, filename }\n\t}\n\tasync saveDebugArtifacts(errorMessage: string): Promise<void> {\n\t\tif (!this.page) return\n\t\ttry {\n\t\t\tthis.debugDir = path.join(os.tmpdir(), `sfile_debug_${Date.now()}`)\n\t\t\tawait fs.mkdir(this.debugDir, { recursive: true })\n\t\t\tawait Promise.all([\n\t\t\t\tthis.page.screenshot({\n\t\t\t\t\tpath: path.join(this.debugDir, 'error.png'),\n\t\t\t\t\tfullPage: true,\n\t\t\t\t}),\n\t\t\t\tfs.writeFile(path.join(this.debugDir, 'error.html'), await this.page.content()),\n\t\t\t\tfs.writeFile(path.join(this.debugDir, 'error.txt'), errorMessage),\n\t\t\t])\n\t\t\tthis.logger.info(`Debug artifacts saved to ${this.debugDir}`)\n\t\t} catch (e: any) {\n\t\t\tthis.logger.error(`Failed to save debug artifacts: ${e.message}`)\n\t\t}\n\t}\n\tasync close(): Promise<void> {\n\t\tif (this.page) await this.page.close().catch(() => {})\n\t\tif (this.context) await this.context.close().catch(() => {})\n\t\tif (this.browser) await this.browser.close().catch(() => {})\n\t\tthis.logger.debug('Browser closed')\n\t}\n}\n","import { safeStringify } from './helpers.js'\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error'\nexport class Logger {\n\tprivate minLevel: LogLevel\n\tconstructor(debugMode = false) {\n\t\tthis.minLevel = debugMode ? 'debug' : 'info'\n\t}\n\tsetDebug(enabled: boolean) {\n\t\tthis.minLevel = enabled ? 'debug' : 'info'\n\t}\n\tprivate shouldLog(level: LogLevel): boolean {\n\t\tconst levels: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 }\n\t\treturn levels[level] >= levels[this.minLevel]\n\t}\n\tprivate log(level: LogLevel, message: string, context?: any) {\n\t\tif (!this.shouldLog(level)) return\n\t\tconst ts = new Date().toISOString()\n\t\tconst ctx = context ? ` ${safeStringify(context)}` : ''\n\t\tconsole.log(`[${ts}] ${level.toUpperCase()}: ${message}${ctx}`)\n\t}\n\tdebug(msg: string, ctx?: any) {\n\t\tthis.log('debug', msg, ctx)\n\t}\n\tinfo(msg: string, ctx?: any) {\n\t\tthis.log('info', msg, ctx)\n\t}\n\twarn(msg: string, ctx?: any) {\n\t\tthis.log('warn', msg, ctx)\n\t}\n\terror(msg: string, ctx?: any) {\n\t\tthis.log('error', msg, ctx)\n\t}\n}\n","import { DEFAULTS } from './defaults.js'\nexport interface DownloadOptions {\n\theadless?: boolean\n\tdebug?: boolean\n\tuserAgent?: string\n\ttimeout?: number\n}\nexport interface DownloadResult {\n\tfilePath: string\n\tsize: number\n\tmethod: 'direct' | 'fallback'\n}\nexport function normalizeOptions(opts?: DownloadOptions) {\n\treturn {\n\t\theadless: opts?.headless ?? DEFAULTS.headless,\n\t\tdebug: opts?.debug ?? false,\n\t\tuserAgent: opts?.userAgent ?? DEFAULTS.userAgent,\n\t\ttimeout: opts?.timeout ?? DEFAULTS.timeout,\n\t}\n}\n","import path from 'path'\nimport fs from 'fs/promises'\nimport { BrowserManager } from '../browser/browser-manager.js'\nimport { Logger } from '../utils/logger.js'\nimport { ValidationError, NetworkError } from '../errors/index.js'\nimport { DownloadOptions, DownloadResult, normalizeOptions } from '../config/schema.js'\nimport { sanitizeFilename } from '../utils/helpers.js'\nexport async function downloadSfile(\n\turl: string,\n\tsaveDir: string,\n\toptions?: DownloadOptions,\n): Promise<DownloadResult> {\n\tconst opts = normalizeOptions(options)\n\tconst logger = new Logger(opts.debug)\n\tif (!url || !url.includes('sfile.co')) {\n\t\tthrow new ValidationError('URL must contain sfile.co', { url })\n\t}\n\tawait fs.mkdir(saveDir, { recursive: true })\n\tconst browserMgr = new BrowserManager(logger, {\n\t\theadless: opts.headless,\n\t\tuserAgent: opts.userAgent,\n\t\ttimeout: opts.timeout,\n\t\tdebug: opts.debug,\n\t})\n\ttry {\n\t\tawait browserMgr.launch()\n\t\tawait browserMgr.goto(url, 'networkidle')\n\t\tawait browserMgr.waitForDownloadButton()\n\t\tconst intermediateUrl = await browserMgr.getIntermediateUrl()\n\t\tconst autoUrl = intermediateUrl.includes('?')\n\t\t\t? `${intermediateUrl}&auto=1`\n\t\t\t: `${intermediateUrl}?auto=1`\n\t\tlet download = await browserMgr.startDownloadAndWait(autoUrl)\n\t\tlet finalPath: string\n\t\tlet fileSize: number\n\t\tlet method: 'direct' | 'fallback'\n\t\tif (download) {\n\t\t\tconst suggested = download.suggestedFilename() || 'file.bin'\n\t\t\tconst filename = sanitizeFilename(suggested)\n\t\t\tfinalPath = path.join(saveDir, filename)\n\t\t\tawait download.saveAs(finalPath)\n\t\t\tconst stat = await fs.stat(finalPath)\n\t\t\tfileSize = stat.size\n\t\t\tmethod = 'direct'\n\t\t\tlogger.info(`Saved via direct download: ${finalPath} (${fileSize} bytes)`)\n\t\t} else {\n\t\t\tconst fallback = await browserMgr.fallbackCollectFileResponse()\n\t\t\tif (!fallback) {\n\t\t\t\tthrow new NetworkError('No download event and no file response found')\n\t\t\t}\n\t\t\tconst { buffer, filename: rawName } = fallback\n\t\t\tconst filename = sanitizeFilename(rawName)\n\t\t\tfinalPath = path.join(saveDir, filename)\n\t\t\tawait fs.writeFile(finalPath, buffer)\n\t\t\tfileSize = buffer.length\n\t\t\tmethod = 'fallback'\n\t\t\tlogger.info(`Saved via fallback: ${finalPath} (${fileSize} bytes)`)\n\t\t}\n\t\treturn { filePath: finalPath, size: fileSize, method }\n\t} catch (err: any) {\n\t\tawait browserMgr.saveDebugArtifacts(err.message)\n\t\tthrow err\n\t} finally {\n\t\tawait browserMgr.close()\n\t}\n}\n"],"names":["fs"],"mappings":"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"}
|
package/changelog
CHANGED
|
@@ -1,3 +1,95 @@
|
|
|
1
|
+
commit 549aec09f8297318fcf52d69bc8262d78d769e0d
|
|
2
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
3
|
+
Date: Thu Apr 2 11:31:54 2026 +0700
|
|
4
|
+
|
|
5
|
+
chore(release): bump version to 2.1.1 for new download implementation
|
|
6
|
+
|
|
7
|
+
commit 54147f326795e40dd6e8e6f08e70fb7323ab2f25
|
|
8
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
9
|
+
Date: Thu Apr 2 11:31:54 2026 +0700
|
|
10
|
+
|
|
11
|
+
refactor(lib): simplify entry point to re-export core modules only
|
|
12
|
+
|
|
13
|
+
commit c82f51154fa17e8533b32c85c218644f46d2067a
|
|
14
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
15
|
+
Date: Thu Apr 2 11:31:50 2026 +0700
|
|
16
|
+
|
|
17
|
+
feat(core): implement downloadSfile function with direct/fallback download strategies
|
|
18
|
+
|
|
19
|
+
commit 84a9e5db96eee8cd195a46b934f792d8c94461c0
|
|
20
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
21
|
+
Date: Thu Apr 2 11:31:45 2026 +0700
|
|
22
|
+
|
|
23
|
+
feat(browser): implement BrowserManager for Playwright lifecycle and fallback handling
|
|
24
|
+
|
|
25
|
+
commit 8fb98f7b6da4717e27ddf019f1962fe25734cc45
|
|
26
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
27
|
+
Date: Thu Apr 2 11:31:44 2026 +0700
|
|
28
|
+
|
|
29
|
+
feat(browser): implement SfilePageInteractions for button wait and URL extraction
|
|
30
|
+
|
|
31
|
+
commit 0e5fa3e6fa007baaf2baa60ce6be475792ae7376
|
|
32
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
33
|
+
Date: Thu Apr 2 11:31:40 2026 +0700
|
|
34
|
+
|
|
35
|
+
feat(config): define DownloadOptions, DownloadResult types and normalizeOptions
|
|
36
|
+
|
|
37
|
+
commit 0271fcd44dc7d6ad5889e94f67af50f21afe23f6
|
|
38
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
39
|
+
Date: Thu Apr 2 11:31:39 2026 +0700
|
|
40
|
+
|
|
41
|
+
feat(config): add DEFAULTS constants for browser and timeout settings
|
|
42
|
+
|
|
43
|
+
commit 5c2b9514892096756f817d9c7302b80a4c0bdbdc
|
|
44
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
45
|
+
Date: Thu Apr 2 11:31:34 2026 +0700
|
|
46
|
+
|
|
47
|
+
feat(errors): export all error classes from index
|
|
48
|
+
|
|
49
|
+
commit 39e37b2392132c6e2f50544f363b6fef18a3693f
|
|
50
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
51
|
+
Date: Thu Apr 2 11:31:34 2026 +0700
|
|
52
|
+
|
|
53
|
+
feat(errors): implement ValidationError, NetworkError, FileError, BrowserError
|
|
54
|
+
|
|
55
|
+
commit 113aa52b8c10c3dceae6dab54fb793a713c85d48
|
|
56
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
57
|
+
Date: Thu Apr 2 11:31:34 2026 +0700
|
|
58
|
+
|
|
59
|
+
feat(errors): create abstract AppError base class with metadata
|
|
60
|
+
|
|
61
|
+
commit 632e1716941f317ef34e33131e8db22a745e812b
|
|
62
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
63
|
+
Date: Thu Apr 2 11:31:28 2026 +0700
|
|
64
|
+
|
|
65
|
+
feat(utils): add Result type and ok/err helper functions
|
|
66
|
+
|
|
67
|
+
commit 404af4c69d443e1722bb74aa9e5dbc4391dcd77e
|
|
68
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
69
|
+
Date: Thu Apr 2 11:31:28 2026 +0700
|
|
70
|
+
|
|
71
|
+
feat(utils): implement Logger class with level-based filtering
|
|
72
|
+
|
|
73
|
+
commit a590df93fe4476306a694f9475ae1ce27af74fd0
|
|
74
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
75
|
+
Date: Thu Apr 2 11:31:27 2026 +0700
|
|
76
|
+
|
|
77
|
+
feat(utils): add helper functions for filename sanitization and sleep
|
|
78
|
+
|
|
79
|
+
commit 02745291f00d47110f673b1b923f108e55e331fc
|
|
80
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
81
|
+
Date: Thu Apr 2 02:02:27 2026 +0700
|
|
82
|
+
|
|
83
|
+
remake prepublish scripts
|
|
84
|
+
|
|
85
|
+
commit a88b254acafbad32719b67c096fb3420ed813aa0
|
|
86
|
+
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
87
|
+
Date: Thu Apr 2 02:01:54 2026 +0700
|
|
88
|
+
|
|
89
|
+
release: v2.0.1
|
|
90
|
+
|
|
91
|
+
delete ci for auto publihs
|
|
92
|
+
|
|
1
93
|
commit c63b25ce873bcba391cf66dc52c3c04fb8eb9306
|
|
2
94
|
Author: neuxdotdev <neuxdev1@gmail.com>
|
|
3
95
|
Date: Thu Apr 2 01:50:16 2026 +0700
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sfiledl",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Implement and automate downloading of any file from sfile.co with javascripts library, written entirely in typescripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/lib.cjs",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"license": "AGPL-3.0-only",
|
|
10
10
|
"files": [
|
|
11
11
|
"build",
|
|
12
|
-
"readme",
|
|
12
|
+
"readme.md",
|
|
13
13
|
"license",
|
|
14
14
|
"changelog"
|
|
15
15
|
],
|