bytex-sdk 5.2.0 → 5.3.0
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 +180 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -121,6 +121,186 @@ export class BytexCloud {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
_requireKey() { if (!this.apiKey) throw new Error('API Key required!'); }
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Copy a file within the same project under a new name.
|
|
127
|
+
*/
|
|
128
|
+
async copy(sourceName, destName) {
|
|
129
|
+
return this._execute(async () => {
|
|
130
|
+
this._requireKey();
|
|
131
|
+
const src = sourceName.endsWith('.btx') ? sourceName : `${sourceName}.stream.btx`;
|
|
132
|
+
const { data: { session } } = await this.supabase.auth.getSession();
|
|
133
|
+
const res = await fetch(`${this.workerUrl}?action=view&key=${this.apiKey}&file=${encodeURIComponent(src)}&_cb=${Date.now()}`, {
|
|
134
|
+
headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
135
|
+
});
|
|
136
|
+
if (!res.ok) throw new Error(`File not found: ${sourceName}`);
|
|
137
|
+
const data = await res.arrayBuffer();
|
|
138
|
+
const fd = new FormData();
|
|
139
|
+
fd.append('file', new Blob([data]), destName);
|
|
140
|
+
const upRes = await fetch(`${this.workerUrl}?action=upload&key=${this.apiKey}`, { method: 'POST', body: fd });
|
|
141
|
+
if (!upRes.ok) throw new Error(await upRes.text());
|
|
142
|
+
return { source: sourceName, dest: destName };
|
|
143
|
+
}, 'Copy');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Move a file to a different project (API key).
|
|
148
|
+
* @param {string} name - File name to move
|
|
149
|
+
* @param {string} destApiKey - Destination project's API key
|
|
150
|
+
*/
|
|
151
|
+
async move(name, destApiKey) {
|
|
152
|
+
return this._execute(async () => {
|
|
153
|
+
this._requireKey();
|
|
154
|
+
const src = name.endsWith('.btx') ? name : `${name}.stream.btx`;
|
|
155
|
+
const { data: { session } } = await this.supabase.auth.getSession();
|
|
156
|
+
// Download from source
|
|
157
|
+
const res = await fetch(`${this.workerUrl}?action=view&key=${this.apiKey}&file=${encodeURIComponent(src)}&_cb=${Date.now()}`, {
|
|
158
|
+
headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
159
|
+
});
|
|
160
|
+
if (!res.ok) throw new Error(`File not found: ${name}`);
|
|
161
|
+
const data = await res.arrayBuffer();
|
|
162
|
+
// Upload to destination
|
|
163
|
+
const fd = new FormData();
|
|
164
|
+
fd.append('file', new Blob([data]), name);
|
|
165
|
+
await fetch(`${this.workerUrl}?action=upload&key=${destApiKey}`, { method: 'POST', body: fd });
|
|
166
|
+
// Delete from source
|
|
167
|
+
await fetch(`${this.workerUrl}?action=delete&key=${this.apiKey}&file=${encodeURIComponent(src)}`, {
|
|
168
|
+
method: 'DELETE', headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
169
|
+
});
|
|
170
|
+
return { moved: name, to: destApiKey };
|
|
171
|
+
}, 'Move');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Tag a file with one or more labels.
|
|
176
|
+
* @param {string} fileName - The file to tag
|
|
177
|
+
* @param {string[]} tags - Array of tag strings
|
|
178
|
+
*/
|
|
179
|
+
async tag(fileName, tags) {
|
|
180
|
+
return this._execute(async () => {
|
|
181
|
+
this._requireKey();
|
|
182
|
+
const existing = await this._getMeta('tags', fileName) || [];
|
|
183
|
+
const merged = [...new Set([...existing, ...tags])];
|
|
184
|
+
await this._setMeta('tags', fileName, merged);
|
|
185
|
+
return { fileName, tags: merged };
|
|
186
|
+
}, 'Tag');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async getTags(fileName) {
|
|
190
|
+
return this._execute(async () => {
|
|
191
|
+
this._requireKey();
|
|
192
|
+
return await this._getMeta('tags', fileName) || [];
|
|
193
|
+
}, 'GetTags');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async removeTag(fileName, tag) {
|
|
197
|
+
return this._execute(async () => {
|
|
198
|
+
this._requireKey();
|
|
199
|
+
const existing = await this._getMeta('tags', fileName) || [];
|
|
200
|
+
const updated = existing.filter(t => t !== tag);
|
|
201
|
+
await this._setMeta('tags', fileName, updated);
|
|
202
|
+
return { fileName, tags: updated };
|
|
203
|
+
}, 'RemoveTag');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Pin a file to protect it from bulk operations.
|
|
208
|
+
*/
|
|
209
|
+
async pin(fileName) {
|
|
210
|
+
return this._execute(async () => {
|
|
211
|
+
this._requireKey();
|
|
212
|
+
const existing = await this._getMeta('pins', '__global') || [];
|
|
213
|
+
if (!existing.includes(fileName)) {
|
|
214
|
+
await this._setMeta('pins', '__global', [...existing, fileName]);
|
|
215
|
+
}
|
|
216
|
+
return { pinned: fileName };
|
|
217
|
+
}, 'Pin');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async unpin(fileName) {
|
|
221
|
+
return this._execute(async () => {
|
|
222
|
+
this._requireKey();
|
|
223
|
+
const existing = await this._getMeta('pins', '__global') || [];
|
|
224
|
+
await this._setMeta('pins', '__global', existing.filter(f => f !== fileName));
|
|
225
|
+
return { unpinned: fileName };
|
|
226
|
+
}, 'Unpin');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async getPins() {
|
|
230
|
+
return this._execute(async () => {
|
|
231
|
+
this._requireKey();
|
|
232
|
+
return await this._getMeta('pins', '__global') || [];
|
|
233
|
+
}, 'GetPins');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Save a versioned snapshot of a file.
|
|
238
|
+
*/
|
|
239
|
+
async saveVersion(name) {
|
|
240
|
+
return this._execute(async () => {
|
|
241
|
+
this._requireKey();
|
|
242
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
|
|
243
|
+
return await this.copy(name, `__version__${ts}__${name}`);
|
|
244
|
+
}, 'SaveVersion');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* List all saved versions of a file.
|
|
249
|
+
*/
|
|
250
|
+
async listVersions(name) {
|
|
251
|
+
return this._execute(async () => {
|
|
252
|
+
this._requireKey();
|
|
253
|
+
const { data: { session } } = await this.supabase.auth.getSession();
|
|
254
|
+
const res = await fetch(`${this.workerUrl}?action=list&key=${this.apiKey}`, {
|
|
255
|
+
headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
256
|
+
});
|
|
257
|
+
const text = await res.text();
|
|
258
|
+
const files = text.trim().split('\n').filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
259
|
+
return files.filter(f => (f.file_name || '').startsWith('__version__') && (f.file_name || '').endsWith(name));
|
|
260
|
+
}, 'ListVersions');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Register a webhook URL to receive event notifications.
|
|
265
|
+
* Note: webhooks are stored locally/in Supabase. Your server must listen at the endpoint.
|
|
266
|
+
*/
|
|
267
|
+
async addWebhook(url, events = ['upload', 'delete']) {
|
|
268
|
+
return this._execute(async () => {
|
|
269
|
+
this._requireKey();
|
|
270
|
+
const existing = await this._getMeta('webhooks', '__global') || [];
|
|
271
|
+
const updated = [...existing.filter(w => w.url !== url), { url, events, createdAt: new Date().toISOString() }];
|
|
272
|
+
await this._setMeta('webhooks', '__global', updated);
|
|
273
|
+
return { url, events };
|
|
274
|
+
}, 'AddWebhook');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async getWebhooks() {
|
|
278
|
+
return this._execute(async () => {
|
|
279
|
+
this._requireKey();
|
|
280
|
+
return await this._getMeta('webhooks', '__global') || [];
|
|
281
|
+
}, 'GetWebhooks');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async removeWebhook(url) {
|
|
285
|
+
return this._execute(async () => {
|
|
286
|
+
this._requireKey();
|
|
287
|
+
const existing = await this._getMeta('webhooks', '__global') || [];
|
|
288
|
+
await this._setMeta('webhooks', '__global', existing.filter(w => w.url !== url));
|
|
289
|
+
return { removed: url };
|
|
290
|
+
}, 'RemoveWebhook');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Internal metadata helper using Supabase
|
|
294
|
+
async _getMeta(type, key) {
|
|
295
|
+
try {
|
|
296
|
+
const { data } = await this.supabase.from('bytex_metadata').select('value').eq('api_key', this.apiKey).eq('meta_type', type).eq('meta_key', key).single();
|
|
297
|
+
return data?.value || null;
|
|
298
|
+
} catch { return null; }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async _setMeta(type, key, value) {
|
|
302
|
+
await this.supabase.from('bytex_metadata').upsert({ api_key: this.apiKey, meta_type: type, meta_key: key, value }, { onConflict: 'api_key,meta_type,meta_key' });
|
|
303
|
+
}
|
|
124
304
|
}
|
|
125
305
|
|
|
126
306
|
/**
|