sfiledl 2.0.0 → 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 +381 -132
- package/build/lib.cjs.map +1 -14
- package/build/lib.d.ts +40 -32
- package/build/lib.mjs +342 -124
- package/build/lib.mjs.map +1 -12
- package/changelog +685 -0
- package/license +661 -0
- package/package.json +6 -3
- package/readme.md +170 -0
package/build/lib.d.ts
CHANGED
|
@@ -1,37 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
interface DownloadOptions {
|
|
2
|
+
headless?: boolean;
|
|
3
|
+
debug?: boolean;
|
|
4
|
+
userAgent?: string;
|
|
5
|
+
timeout?: number;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
readonly status: LibraryStatus.UNDER_CONSTRUCTION
|
|
11
|
-
readonly isDeprecated: true
|
|
12
|
-
readonly message: 'This version represents a complete architectural refactor from CLI to framework. Not stable.'
|
|
7
|
+
interface DownloadResult {
|
|
8
|
+
filePath: string;
|
|
9
|
+
size: number;
|
|
10
|
+
method: 'direct' | 'fallback';
|
|
13
11
|
}
|
|
14
|
-
|
|
15
|
-
declare
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
|
|
13
|
+
declare function downloadSfile(url: string, saveDir: string, options?: DownloadOptions): Promise<DownloadResult>;
|
|
14
|
+
|
|
15
|
+
declare abstract class AppError extends Error {
|
|
16
|
+
readonly code: string;
|
|
17
|
+
readonly retryable: boolean;
|
|
18
|
+
readonly timestamp: string;
|
|
19
|
+
readonly context: Record<string, unknown> | undefined;
|
|
20
|
+
constructor(message: string, code: string, retryable?: boolean, context?: Record<string, unknown>);
|
|
21
|
+
toJSON(): {
|
|
22
|
+
name: string;
|
|
23
|
+
message: string;
|
|
24
|
+
code: string;
|
|
25
|
+
retryable: boolean;
|
|
26
|
+
timestamp: string;
|
|
27
|
+
context: Record<string, unknown> | undefined;
|
|
28
|
+
};
|
|
24
29
|
}
|
|
25
|
-
declare function initFramework(): never
|
|
26
|
-
declare function isStable(): boolean
|
|
27
|
-
declare function assertStable(): void
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
initFramework,
|
|
35
|
-
isDevelopmentVersion,
|
|
36
|
-
isStable,
|
|
31
|
+
declare class ValidationError extends AppError {
|
|
32
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
33
|
+
}
|
|
34
|
+
declare class NetworkError extends AppError {
|
|
35
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
37
36
|
}
|
|
37
|
+
declare class FileError extends AppError {
|
|
38
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
39
|
+
}
|
|
40
|
+
declare class BrowserError extends AppError {
|
|
41
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { AppError, BrowserError, FileError, NetworkError, ValidationError, downloadSfile };
|
|
45
|
+
export type { DownloadOptions, DownloadResult };
|
package/build/lib.mjs
CHANGED
|
@@ -1,136 +1,354 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
+
}
|
|
8
31
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
})(LibraryStatus || (LibraryStatus = {}))
|
|
15
|
-
const LIB_INFO = {
|
|
16
|
-
name: 'MySuperFramework',
|
|
17
|
-
currentVersion: '2.0.0-dev',
|
|
18
|
-
previousVersion: '1.0.2',
|
|
19
|
-
status: LibraryStatus.UNDER_CONSTRUCTION,
|
|
20
|
-
isDeprecated: true,
|
|
21
|
-
message:
|
|
22
|
-
'This version represents a complete architectural refactor from CLI to framework. Not stable.',
|
|
32
|
+
|
|
33
|
+
class ValidationError extends AppError {
|
|
34
|
+
constructor(message, context) {
|
|
35
|
+
super(message, 'VALIDATION_ERROR', false, context);
|
|
36
|
+
}
|
|
23
37
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
class NetworkError extends AppError {
|
|
39
|
+
constructor(message, context) {
|
|
40
|
+
super(message, 'NETWORK_ERROR', true, context);
|
|
41
|
+
}
|
|
27
42
|
}
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
class FileError extends AppError {
|
|
44
|
+
constructor(message, context) {
|
|
45
|
+
super(message, 'FILE_ERROR', false, context);
|
|
46
|
+
}
|
|
30
47
|
}
|
|
31
|
-
class
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
static getInstance() {
|
|
37
|
-
if (!LibStatus.instance) {
|
|
38
|
-
LibStatus.instance = new LibStatus()
|
|
39
|
-
}
|
|
40
|
-
return LibStatus.instance
|
|
41
|
-
}
|
|
42
|
-
displayDeveloperWarning() {
|
|
43
|
-
if (typeof console === 'undefined' || typeof console.warn !== 'function') {
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
if (warningDisplayed) {
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
if (
|
|
50
|
-
typeof process !== 'undefined' &&
|
|
51
|
-
process.env &&
|
|
52
|
-
process.env['MY_SUPER_FRAMEWORK_SUPPRESS_WARNING'] === 'true'
|
|
53
|
-
) {
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
const border = `${COLORS.GRAY}${'='.repeat(80)}${COLORS.RESET}`
|
|
57
|
-
const label = `${COLORS.YELLOW}${COLORS.BRIGHT}[WARNING]${COLORS.RESET}`
|
|
58
|
-
const highlight = `${COLORS.CYAN}`
|
|
59
|
-
console.warn(`\n${border}`)
|
|
60
|
-
console.warn(`${label} DEVELOPMENT VERSION IN USE`)
|
|
61
|
-
console.warn(
|
|
62
|
-
`${COLORS.GRAY}Library: ${COLORS.RESET}${highlight}${LIB_INFO.name}${COLORS.RESET}`,
|
|
63
|
-
)
|
|
64
|
-
console.warn(
|
|
65
|
-
`${COLORS.GRAY}Current Version: ${COLORS.RESET}${highlight}${LIB_INFO.currentVersion}${COLORS.RESET}`,
|
|
66
|
-
)
|
|
67
|
-
console.warn(
|
|
68
|
-
`${COLORS.GRAY}Previous Version: ${COLORS.RESET}${highlight}${LIB_INFO.previousVersion}${COLORS.RESET}`,
|
|
69
|
-
)
|
|
70
|
-
console.warn(
|
|
71
|
-
`${COLORS.GRAY}Architecture: ${COLORS.RESET}${highlight}CLI -> Framework (Full Refactor)${COLORS.RESET}`,
|
|
72
|
-
)
|
|
73
|
-
console.warn(
|
|
74
|
-
`\n${COLORS.RED}${COLORS.BRIGHT}DO NOT USE IN PRODUCTION ENVIRONMENTS${COLORS.RESET}`,
|
|
75
|
-
)
|
|
76
|
-
console.warn(
|
|
77
|
-
`${COLORS.GRAY}API stability is not guaranteed. Breaking changes may occur without notice.${COLORS.RESET}`,
|
|
78
|
-
)
|
|
79
|
-
console.warn(
|
|
80
|
-
`${COLORS.GRAY}To suppress this warning, set env MY_SUPER_FRAMEWORK_SUPPRESS_WARNING=true${COLORS.RESET}`,
|
|
81
|
-
)
|
|
82
|
-
console.warn(`${border}\n`)
|
|
83
|
-
warningDisplayed = true
|
|
84
|
-
}
|
|
85
|
-
suppressWarning() {
|
|
86
|
-
warningDisplayed = true
|
|
87
|
-
}
|
|
88
|
-
ensureStable() {
|
|
89
|
-
if (LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION) {
|
|
90
|
-
const errorMessage =
|
|
91
|
-
`${COLORS.RED}${COLORS.BRIGHT}[${LIB_INFO.name}] Stability Check Failed${COLORS.RESET}\n` +
|
|
92
|
-
`${COLORS.GRAY}Library version ${LIB_INFO.currentVersion} is under active development.${COLORS.RESET}\n` +
|
|
93
|
-
`${COLORS.GRAY}Current status: ${LIB_INFO.status}${COLORS.RESET}`
|
|
94
|
-
throw new Error(stripAnsi(errorMessage))
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
getVersion() {
|
|
98
|
-
return LIB_INFO.currentVersion
|
|
99
|
-
}
|
|
100
|
-
getInfo() {
|
|
101
|
-
return LIB_INFO
|
|
102
|
-
}
|
|
48
|
+
class BrowserError extends AppError {
|
|
49
|
+
constructor(message, context) {
|
|
50
|
+
super(message, 'BROWSER_ERROR', true, context);
|
|
51
|
+
}
|
|
103
52
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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,
|
|
60
|
+
};
|
|
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
|
+
}
|
|
111
91
|
}
|
|
112
|
-
|
|
113
|
-
|
|
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';
|
|
114
100
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
}
|
|
122
109
|
}
|
|
123
|
-
|
|
124
|
-
|
|
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}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
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 });
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
throw new NetworkError(`Navigation failed: ${err.message}`, { url });
|
|
170
|
+
}
|
|
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)
|
|
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}`);
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
this.logger.error(`Failed to save debug artifacts: ${e.message}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
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');
|
|
249
|
+
}
|
|
125
250
|
}
|
|
126
251
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
+
}
|
|
135
283
|
}
|
|
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
|
+
};
|
|
292
|
+
}
|
|
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();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export { AppError, BrowserError, FileError, NetworkError, ValidationError, downloadSfile };
|
|
136
354
|
//# sourceMappingURL=lib.mjs.map
|
package/build/lib.mjs.map
CHANGED
|
@@ -1,12 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"file": "lib.mjs",
|
|
4
|
-
"sources": [
|
|
5
|
-
"../lib/lib.ts"
|
|
6
|
-
],
|
|
7
|
-
"sourcesContent": [
|
|
8
|
-
"const COLORS = {\n\tRESET: '\\x1b[0m',\n\tBRIGHT: '\\x1b[1m',\n\tYELLOW: '\\x1b[33m',\n\tRED: '\\x1b[31m',\n\tCYAN: '\\x1b[36m',\n\tGRAY: '\\x1b[90m',\n} as const\nexport enum LibraryStatus {\n\tUNDER_CONSTRUCTION = 'UNDER_CONSTRUCTION',\n\tSTABLE = 'STABLE',\n\tDEPRECATED = 'DEPRECATED',\n}\nexport const LIB_INFO = {\n\tname: 'MySuperFramework',\n\tcurrentVersion: '2.0.0-dev',\n\tpreviousVersion: '1.0.2',\n\tstatus: LibraryStatus.UNDER_CONSTRUCTION,\n\tisDeprecated: true,\n\tmessage:\n\t\t'This version represents a complete architectural refactor from CLI to framework. Not stable.',\n} as const\nlet warningDisplayed = false\nfunction stripAnsi(text: string): string {\n\treturn text.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n}\nexport function isDevelopmentVersion(): boolean {\n\treturn LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION\n}\nexport class LibStatus {\n\tprivate static instance: LibStatus | undefined\n\tprivate constructor() {\n\t\tthis.displayDeveloperWarning()\n\t}\n\tpublic static getInstance(): LibStatus {\n\t\tif (!LibStatus.instance) {\n\t\t\tLibStatus.instance = new LibStatus()\n\t\t}\n\t\treturn LibStatus.instance\n\t}\n\tprivate displayDeveloperWarning(): void {\n\t\tif (typeof console === 'undefined' || typeof console.warn !== 'function') {\n\t\t\treturn\n\t\t}\n\t\tif (warningDisplayed) {\n\t\t\treturn\n\t\t}\n\t\tif (\n\t\t\ttypeof process !== 'undefined' &&\n\t\t\tprocess.env &&\n\t\t\tprocess.env['MY_SUPER_FRAMEWORK_SUPPRESS_WARNING'] === 'true'\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tconst border = `${COLORS.GRAY}${'='.repeat(80)}${COLORS.RESET}`\n\t\tconst label = `${COLORS.YELLOW}${COLORS.BRIGHT}[WARNING]${COLORS.RESET}`\n\t\tconst highlight = `${COLORS.CYAN}`\n\t\tconsole.warn(`\\n${border}`)\n\t\tconsole.warn(`${label} DEVELOPMENT VERSION IN USE`)\n\t\tconsole.warn(\n\t\t\t`${COLORS.GRAY}Library: ${COLORS.RESET}${highlight}${LIB_INFO.name}${COLORS.RESET}`,\n\t\t)\n\t\tconsole.warn(\n\t\t\t`${COLORS.GRAY}Current Version: ${COLORS.RESET}${highlight}${LIB_INFO.currentVersion}${COLORS.RESET}`,\n\t\t)\n\t\tconsole.warn(\n\t\t\t`${COLORS.GRAY}Previous Version: ${COLORS.RESET}${highlight}${LIB_INFO.previousVersion}${COLORS.RESET}`,\n\t\t)\n\t\tconsole.warn(\n\t\t\t`${COLORS.GRAY}Architecture: ${COLORS.RESET}${highlight}CLI -> Framework (Full Refactor)${COLORS.RESET}`,\n\t\t)\n\t\tconsole.warn(\n\t\t\t`\\n${COLORS.RED}${COLORS.BRIGHT}DO NOT USE IN PRODUCTION ENVIRONMENTS${COLORS.RESET}`,\n\t\t)\n\t\tconsole.warn(\n\t\t\t`${COLORS.GRAY}API stability is not guaranteed. Breaking changes may occur without notice.${COLORS.RESET}`,\n\t\t)\n\t\tconsole.warn(\n\t\t\t`${COLORS.GRAY}To suppress this warning, set env MY_SUPER_FRAMEWORK_SUPPRESS_WARNING=true${COLORS.RESET}`,\n\t\t)\n\t\tconsole.warn(`${border}\\n`)\n\t\twarningDisplayed = true\n\t}\n\tpublic suppressWarning(): void {\n\t\twarningDisplayed = true\n\t}\n\tpublic ensureStable(): void {\n\t\tif (LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION) {\n\t\t\tconst errorMessage =\n\t\t\t\t`${COLORS.RED}${COLORS.BRIGHT}[${LIB_INFO.name}] Stability Check Failed${COLORS.RESET}\\n` +\n\t\t\t\t`${COLORS.GRAY}Library version ${LIB_INFO.currentVersion} is under active development.${COLORS.RESET}\\n` +\n\t\t\t\t`${COLORS.GRAY}Current status: ${LIB_INFO.status}${COLORS.RESET}`\n\t\t\tthrow new Error(stripAnsi(errorMessage))\n\t\t}\n\t}\n\tpublic getVersion(): string {\n\t\treturn LIB_INFO.currentVersion\n\t}\n\tpublic getInfo(): Readonly<typeof LIB_INFO> {\n\t\treturn LIB_INFO\n\t}\n}\nexport function initFramework(): never {\n\tconst message =\n\t\t`${COLORS.RED}${COLORS.BRIGHT}[${LIB_INFO.name}] Initialization Blocked${COLORS.RESET}\\n` +\n\t\t`${COLORS.GRAY}Framework is undergoing complete architectural refactor.${COLORS.RESET}\\n` +\n\t\t`${COLORS.GRAY}Migration path: v${LIB_INFO.previousVersion} (CLI) -> v${LIB_INFO.currentVersion} (Framework)${COLORS.RESET}\\n` +\n\t\t`${COLORS.GRAY}Status: ${LIB_INFO.status}${COLORS.RESET}`\n\tthrow new Error(stripAnsi(message))\n}\nexport function isStable(): boolean {\n\treturn LIB_INFO.status !== LibraryStatus.UNDER_CONSTRUCTION\n}\nexport function assertStable(): void {\n\tif (LIB_INFO.status === LibraryStatus.UNDER_CONSTRUCTION) {\n\t\tthrow new Error(\n\t\t\t`[${LIB_INFO.name}] Cannot use unstable version ${LIB_INFO.currentVersion} in production. ` +\n\t\t\t\t`Status: ${LIB_INFO.status}`,\n\t\t)\n\t}\n}\nif (isDevelopmentVersion()) {\n\tLibStatus.getInstance()\n}\n"
|
|
9
|
-
],
|
|
10
|
-
"names": [],
|
|
11
|
-
"mappings": "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"
|
|
12
|
-
}
|
|
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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"}
|