bytex-sdk 1.8.1 → 1.9.5
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/index.js +73 -90
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -6,50 +6,38 @@ const WORKER_URL = 'https://api.bytex.work/';
|
|
|
6
6
|
|
|
7
7
|
export class BytexCloud {
|
|
8
8
|
constructor(config = {}) {
|
|
9
|
+
this.config = config;
|
|
9
10
|
this.apiKey = config.apiKey || null;
|
|
10
11
|
this.workerUrl = config.workerUrl || WORKER_URL;
|
|
11
|
-
this.provider = config.dbProvider || 'supabase';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
auth: {
|
|
19
|
-
persistSession: true,
|
|
20
|
-
autoRefreshToken: true,
|
|
21
|
-
detectSessionInUrl: false
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
if (config.storage) {
|
|
25
|
-
supabaseConfig.auth.storage = config.storage;
|
|
13
|
+
const supabaseConfig = {
|
|
14
|
+
auth: {
|
|
15
|
+
persistSession: true,
|
|
16
|
+
autoRefreshToken: true,
|
|
17
|
+
detectSessionInUrl: false
|
|
26
18
|
}
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
};
|
|
20
|
+
if (config.storage) {
|
|
21
|
+
supabaseConfig.auth.storage = config.storage;
|
|
29
22
|
}
|
|
30
|
-
|
|
23
|
+
|
|
24
|
+
this.supabase = createClient(config.supabaseUrl || SUPABASE_URL, config.supabaseKey || SUPABASE_ANON_KEY, supabaseConfig);
|
|
31
25
|
this._listeners = {};
|
|
32
26
|
}
|
|
33
27
|
|
|
28
|
+
// --- CORE METHODS ---
|
|
34
29
|
async login(email, password) {
|
|
35
30
|
const { data, error } = await this.supabase.auth.signInWithPassword({ email, password });
|
|
36
|
-
if (error) throw new Error(
|
|
37
|
-
this.user = data.user;
|
|
38
|
-
this._emit('login', this.user);
|
|
31
|
+
if (error) throw new Error(error.message);
|
|
39
32
|
return data.user;
|
|
40
33
|
}
|
|
41
34
|
|
|
42
35
|
async upload(name, data) {
|
|
43
36
|
this._requireKey();
|
|
44
37
|
const fd = new FormData();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const res = await fetch(`${this.workerUrl}?action=upload&key=${this.apiKey}`, {
|
|
49
|
-
method: 'POST',
|
|
50
|
-
body: fd
|
|
51
|
-
});
|
|
52
|
-
if (!res.ok) throw new Error(`Upload failed: ${await res.text()}`);
|
|
38
|
+
fd.append('file', data instanceof Blob ? data : new Blob([data]), name);
|
|
39
|
+
const res = await fetch(`${this.workerUrl}?action=upload&key=${this.apiKey}`, { method: 'POST', body: fd });
|
|
40
|
+
if (!res.ok) throw new Error(`Upload Failed: ${res.statusText}`);
|
|
53
41
|
return await res.json();
|
|
54
42
|
}
|
|
55
43
|
|
|
@@ -57,22 +45,11 @@ export class BytexCloud {
|
|
|
57
45
|
this._requireKey();
|
|
58
46
|
const streamName = name.endsWith('.stream.btx') ? name : `${name}.stream.btx`;
|
|
59
47
|
const url = `${this.workerUrl}?action=view&key=${this.apiKey}&file=${encodeURIComponent(streamName)}&_cb=${Date.now()}`;
|
|
60
|
-
|
|
61
48
|
const { data: { session } } = await this.supabase.auth.getSession();
|
|
62
|
-
const res = await fetch(url, {
|
|
63
|
-
|
|
64
|
-
'Authorization': `Bearer ${session?.access_token || ''}`,
|
|
65
|
-
'Cache-Control': 'no-cache'
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
if (!res.ok) throw new Error(`Download failed: ${res.status}`);
|
|
49
|
+
const res = await fetch(url, { headers: { 'Authorization': `Bearer ${session?.access_token || ''}` } });
|
|
50
|
+
if (!res.ok) throw new Error(`Download Failed: ${res.status}`);
|
|
69
51
|
const text = await res.text();
|
|
70
|
-
try {
|
|
71
|
-
return JSON.parse(text);
|
|
72
|
-
} catch (e) {
|
|
73
|
-
// Handle NDJSON
|
|
74
|
-
return text.split('\n').filter(Boolean).map(line => JSON.parse(line));
|
|
75
|
-
}
|
|
52
|
+
try { return JSON.parse(text); } catch (e) { return text.split('\n').filter(Boolean).map(line => JSON.parse(line)); }
|
|
76
53
|
}
|
|
77
54
|
|
|
78
55
|
async delete(name) {
|
|
@@ -81,75 +58,81 @@ export class BytexCloud {
|
|
|
81
58
|
const { data: { session } } = await this.supabase.auth.getSession();
|
|
82
59
|
const res = await fetch(`${this.workerUrl}?action=delete&key=${this.apiKey}&file=${encodeURIComponent(streamName)}`, {
|
|
83
60
|
method: 'DELETE',
|
|
84
|
-
headers: {
|
|
85
|
-
'Authorization': `Bearer ${session?.access_token || ''}`
|
|
86
|
-
}
|
|
61
|
+
headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
87
62
|
});
|
|
88
|
-
if (!res.ok) throw new Error(`Delete
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async listFiles() {
|
|
93
|
-
this._requireKey();
|
|
94
|
-
const res = await fetch(`${this.workerUrl}?action=list&key=${this.apiKey}&cb=${Date.now()}`);
|
|
95
|
-
if (!res.ok) throw new Error(`List failed: ${res.status}`);
|
|
96
|
-
const text = await res.text();
|
|
97
|
-
return text.split('\n').filter(Boolean).map(line => JSON.parse(line));
|
|
63
|
+
if (!res.ok) throw new Error(`Delete Failed: ${res.status}`);
|
|
64
|
+
return true;
|
|
98
65
|
}
|
|
99
66
|
|
|
100
67
|
/**
|
|
101
|
-
* ByteX X-RAY
|
|
68
|
+
* ByteX X-RAY v2.1 (The Professional)
|
|
69
|
+
* Detailed diagnostic for Upload, Delete, and Streaming capabilities.
|
|
102
70
|
*/
|
|
103
71
|
async xray() {
|
|
104
|
-
console.log('[ByteX X-RAY] 🔍
|
|
72
|
+
console.log('[ByteX X-RAY] 🔍 Running professional system diagnostics...');
|
|
105
73
|
const report = {
|
|
106
74
|
timestamp: new Date().toISOString(),
|
|
107
|
-
sdk_version: '1.
|
|
108
|
-
|
|
75
|
+
sdk_version: '1.9.5',
|
|
76
|
+
health_score: 100,
|
|
77
|
+
checks: {},
|
|
78
|
+
advice: []
|
|
109
79
|
};
|
|
110
80
|
|
|
81
|
+
// 1. Auth & Session Integrity
|
|
82
|
+
const { data: { session } } = await this.supabase.auth.getSession();
|
|
83
|
+
report.checks.auth = { status: session ? 'OK' : 'GUEST', user: session?.user?.email || 'unauthenticated' };
|
|
84
|
+
if (!session) report.advice.push('User is not logged in. Most management operations (delete, protected view) will fail.');
|
|
85
|
+
|
|
86
|
+
// 2. Upload/Write Permission Check
|
|
111
87
|
try {
|
|
112
88
|
this._requireKey();
|
|
113
|
-
|
|
89
|
+
const testFd = new FormData();
|
|
90
|
+
testFd.append('file', new Blob(['test']), 'xray_probe.txt');
|
|
91
|
+
const upRes = await fetch(`${this.workerUrl}?action=upload&key=${this.apiKey}&probe=true`, { method: 'POST', body: testFd });
|
|
92
|
+
report.checks.upload_capability = upRes.ok ? 'OK' : 'RESTRICTED';
|
|
93
|
+
if (!upRes.ok) report.advice.push('Upload restricted. Your API Key might be READ-ONLY or the server rejected the probe.');
|
|
114
94
|
} catch (e) {
|
|
115
|
-
report.checks.
|
|
95
|
+
report.checks.upload_capability = 'ERROR';
|
|
96
|
+
report.advice.push(`Upload Check Failed: ${e.message}`);
|
|
116
97
|
}
|
|
117
98
|
|
|
118
|
-
|
|
119
|
-
report.checks.auth = {
|
|
120
|
-
status: session ? 'LOGGED_IN' : 'GUEST',
|
|
121
|
-
user: session?.user?.email || 'none'
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const start = Date.now();
|
|
99
|
+
// 3. Delete/Management Permission Check
|
|
125
100
|
try {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
101
|
+
const delRes = await fetch(`${this.workerUrl}?action=delete&key=${this.apiKey}&file=non_existent_probe`, {
|
|
102
|
+
method: 'DELETE',
|
|
103
|
+
headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
104
|
+
});
|
|
105
|
+
// 404 is actually GOOD here because it means the DELETE method was accepted by the worker
|
|
106
|
+
report.checks.delete_capability = (delRes.status === 404 || delRes.ok) ? 'OK' : 'FORBIDDEN';
|
|
107
|
+
if (delRes.status === 403 || delRes.status === 401) {
|
|
108
|
+
report.advice.push('Delete forbidden. Ensure your session is valid and you have owner permissions.');
|
|
109
|
+
}
|
|
131
110
|
} catch (e) {
|
|
132
|
-
report.checks.
|
|
111
|
+
report.checks.delete_capability = 'UNSUPPORTED';
|
|
133
112
|
}
|
|
134
113
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
114
|
+
// 4. Streaming & Range Support
|
|
115
|
+
try {
|
|
116
|
+
const streamRes = await fetch(`${this.workerUrl}?action=view&key=${this.apiKey}&file=probe`, {
|
|
117
|
+
headers: { 'Range': 'bytes=0-1' }
|
|
118
|
+
});
|
|
119
|
+
report.checks.streaming = streamRes.headers.get('Accept-Ranges') ? 'OPTIMIZED' : 'STANDARD';
|
|
120
|
+
if (report.checks.streaming === 'STANDARD') {
|
|
121
|
+
report.advice.push('Server does not support partial content (Ranges). Large video seeking might be slow.');
|
|
122
|
+
}
|
|
123
|
+
} catch (e) { report.checks.streaming = 'UNKNOWN'; }
|
|
144
124
|
|
|
145
|
-
|
|
146
|
-
if (this.
|
|
147
|
-
|
|
125
|
+
// 5. Code Implementation Audit
|
|
126
|
+
if (!this.config.storage && typeof navigator !== 'undefined') {
|
|
127
|
+
report.checks.implementation = 'INCOMPLETE';
|
|
128
|
+
report.advice.push('React Native detected but "storage" engine is missing in constructor. Sessions will NOT persist across app restarts.');
|
|
148
129
|
}
|
|
149
|
-
}
|
|
150
130
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
131
|
+
const issues = report.advice.length;
|
|
132
|
+
report.final_status = issues === 0 ? 'HEALTHY' : (issues < 3 ? 'WARNING' : 'CRITICAL');
|
|
133
|
+
|
|
134
|
+
return report;
|
|
154
135
|
}
|
|
136
|
+
|
|
137
|
+
_requireKey() { if (!this.apiKey) throw new Error('API Key required! Use setApiKey().'); }
|
|
155
138
|
}
|