@borealise/api 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,571 @@
1
+ // src/Api.ts
2
+ import axios from "axios";
3
+
4
+ // src/Logger.ts
5
+ var Logger = class _Logger {
6
+ constructor(name, enabled = true) {
7
+ this.name = name;
8
+ this.enabled = enabled;
9
+ }
10
+ static create(name) {
11
+ return new _Logger(name);
12
+ }
13
+ enable() {
14
+ this.enabled = true;
15
+ }
16
+ disable() {
17
+ this.enabled = false;
18
+ }
19
+ log(level, message, ...args) {
20
+ if (!this.enabled) return;
21
+ const prefix = `[${this.name}]`;
22
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
23
+ switch (level) {
24
+ case "debug":
25
+ console.debug(`${timestamp} ${prefix} ${message}`, ...args);
26
+ break;
27
+ case "info":
28
+ console.info(`${timestamp} ${prefix} ${message}`, ...args);
29
+ break;
30
+ case "success":
31
+ console.log(`${timestamp} ${prefix} \u2713 ${message}`, ...args);
32
+ break;
33
+ case "error":
34
+ console.error(`${timestamp} ${prefix} \u2717 ${message}`, ...args);
35
+ break;
36
+ }
37
+ }
38
+ debug(message, ...args) {
39
+ this.log("debug", message, ...args);
40
+ }
41
+ info(message, ...args) {
42
+ this.log("info", message, ...args);
43
+ }
44
+ success(message, ...args) {
45
+ this.log("success", message, ...args);
46
+ }
47
+ error(message, ...args) {
48
+ this.log("error", message, ...args);
49
+ }
50
+ };
51
+
52
+ // src/Api.ts
53
+ var ApiError = class extends Error {
54
+ constructor(message, status, code, response) {
55
+ super(message);
56
+ this.name = "ApiError";
57
+ this.status = status;
58
+ this.code = code;
59
+ this.response = response;
60
+ }
61
+ };
62
+ var _Api = class _Api {
63
+ constructor(config) {
64
+ var _a;
65
+ this.config = config;
66
+ this.logger = Logger.create("Api");
67
+ if (config.logging === false) {
68
+ this.logger.disable();
69
+ }
70
+ this.axios = axios.create({
71
+ baseURL: config.baseURL,
72
+ timeout: (_a = config.timeout) != null ? _a : 3e4,
73
+ headers: {
74
+ "Content-Type": "application/json",
75
+ ...config.headers
76
+ }
77
+ });
78
+ this.setupInterceptors();
79
+ this.logger.success(`initialized (baseURL: ${config.baseURL})`);
80
+ }
81
+ static getInstance(config) {
82
+ if (!_Api.instance) {
83
+ if (!config) {
84
+ throw new Error('Api must be initialized with config first. Call Api.getInstance({ baseURL: "..." }) once before using resources.');
85
+ }
86
+ _Api.instance = new _Api(config);
87
+ }
88
+ return _Api.instance;
89
+ }
90
+ /** Reset the singleton (useful for testing or re-initializing with a new config) */
91
+ static reset() {
92
+ _Api.instance = null;
93
+ }
94
+ setupInterceptors() {
95
+ this.axios.interceptors.request.use(
96
+ (config) => {
97
+ var _a;
98
+ this.logger.debug(`\u2192 ${(_a = config.method) == null ? void 0 : _a.toUpperCase()} ${config.url}`);
99
+ return config;
100
+ },
101
+ (error) => {
102
+ this.logger.error("Request error", error);
103
+ return Promise.reject(error);
104
+ }
105
+ );
106
+ this.axios.interceptors.response.use(
107
+ (response) => {
108
+ this.logger.debug(`\u2190 ${response.status} ${response.config.url}`);
109
+ return response;
110
+ },
111
+ (error) => {
112
+ var _a, _b, _c;
113
+ const apiError = this.parseError(error);
114
+ const status = (_b = (_a = apiError.status) != null ? _a : apiError.code) != null ? _b : "ERR";
115
+ this.logger.error(`\u2190 ${status} ${(_c = error.config) == null ? void 0 : _c.url}: ${apiError.message}`);
116
+ return Promise.reject(apiError);
117
+ }
118
+ );
119
+ }
120
+ parseError(error) {
121
+ if (error.response) {
122
+ const responseData = error.response.data;
123
+ let message;
124
+ let backendResponse;
125
+ if (responseData && "error" in responseData && responseData.error) {
126
+ message = responseData.error;
127
+ backendResponse = responseData;
128
+ } else if (responseData && "message" in responseData && responseData.message) {
129
+ message = responseData.message;
130
+ } else {
131
+ message = error.message;
132
+ }
133
+ return new ApiError(message, error.response.status, error.code, backendResponse);
134
+ }
135
+ if (error.request) {
136
+ return new ApiError("No response received from server", void 0, "NETWORK_ERROR");
137
+ }
138
+ return new ApiError(error.message, void 0, error.code);
139
+ }
140
+ setAuthToken(token) {
141
+ if (token) {
142
+ this.axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
143
+ this.logger.debug("Auth token set");
144
+ } else {
145
+ delete this.axios.defaults.headers.common["Authorization"];
146
+ this.logger.debug("Auth token cleared");
147
+ }
148
+ }
149
+ setHeader(key, value) {
150
+ this.axios.defaults.headers.common[key] = value;
151
+ }
152
+ removeHeader(key) {
153
+ delete this.axios.defaults.headers.common[key];
154
+ }
155
+ async get(url, config) {
156
+ const response = await this.axios.get(url, config);
157
+ return this.wrapResponse(response);
158
+ }
159
+ async post(url, data, config) {
160
+ const response = await this.axios.post(url, data, config);
161
+ return this.wrapResponse(response);
162
+ }
163
+ async put(url, data, config) {
164
+ const response = await this.axios.put(url, data, config);
165
+ return this.wrapResponse(response);
166
+ }
167
+ async patch(url, data, config) {
168
+ const response = await this.axios.patch(url, data, config);
169
+ return this.wrapResponse(response);
170
+ }
171
+ async delete(url, config) {
172
+ const response = await this.axios.delete(url, config);
173
+ return this.wrapResponse(response);
174
+ }
175
+ wrapResponse(response) {
176
+ return {
177
+ data: response.data,
178
+ status: response.status,
179
+ headers: response.headers
180
+ };
181
+ }
182
+ get axiosInstance() {
183
+ return this.axios;
184
+ }
185
+ };
186
+ _Api.instance = null;
187
+ var Api = _Api;
188
+
189
+ // src/ApiResource.ts
190
+ var ApiResource = class {
191
+ constructor() {
192
+ this._logger = null;
193
+ }
194
+ get api() {
195
+ return Api.getInstance();
196
+ }
197
+ get logger() {
198
+ if (!this._logger) {
199
+ this._logger = Logger.create(this.constructor.name);
200
+ }
201
+ return this._logger;
202
+ }
203
+ buildUrl(path) {
204
+ if (path !== void 0) {
205
+ return `${this.endpoint}/${path}`;
206
+ }
207
+ return this.endpoint;
208
+ }
209
+ buildConfig(options) {
210
+ return {
211
+ params: options == null ? void 0 : options.params,
212
+ headers: options == null ? void 0 : options.headers
213
+ };
214
+ }
215
+ async index(options) {
216
+ this.logger.debug("index");
217
+ return this.api.get(this.endpoint, this.buildConfig(options));
218
+ }
219
+ async paginate(page = 1, perPage = 15, options) {
220
+ this.logger.debug(`paginate (page: ${page}, perPage: ${perPage})`);
221
+ return this.api.get(this.endpoint, {
222
+ ...this.buildConfig(options),
223
+ params: { page, per_page: perPage, ...options == null ? void 0 : options.params }
224
+ });
225
+ }
226
+ async show(id, options) {
227
+ this.logger.debug(`show (id: ${id})`);
228
+ return this.api.get(this.buildUrl(id), this.buildConfig(options));
229
+ }
230
+ async store(data, options) {
231
+ this.logger.debug("store");
232
+ return this.api.post(this.endpoint, data, this.buildConfig(options));
233
+ }
234
+ async update(id, data, options) {
235
+ this.logger.debug(`update (id: ${id})`);
236
+ return this.api.put(this.buildUrl(id), data, this.buildConfig(options));
237
+ }
238
+ async patch(id, data, options) {
239
+ this.logger.debug(`patch (id: ${id})`);
240
+ return this.api.patch(this.buildUrl(id), data, this.buildConfig(options));
241
+ }
242
+ async destroy(id, options) {
243
+ this.logger.debug(`destroy (id: ${id})`);
244
+ return this.api.delete(this.buildUrl(id), this.buildConfig(options));
245
+ }
246
+ async get(path, options) {
247
+ return this.api.get(`${this.endpoint}/${path}`, this.buildConfig(options));
248
+ }
249
+ async post(path, data, options) {
250
+ return this.api.post(`${this.endpoint}/${path}`, data, this.buildConfig(options));
251
+ }
252
+ async put(path, data, options) {
253
+ return this.api.put(`${this.endpoint}/${path}`, data, this.buildConfig(options));
254
+ }
255
+ async delete(path, options) {
256
+ return this.api.delete(`${this.endpoint}/${path}`, this.buildConfig(options));
257
+ }
258
+ };
259
+
260
+ // src/resources/AuthResource.ts
261
+ var AuthResource = class extends ApiResource {
262
+ constructor() {
263
+ super(...arguments);
264
+ this.endpoint = "/auth";
265
+ }
266
+ // POST /api/auth/login
267
+ async login(credentials) {
268
+ return this.post("login", credentials);
269
+ }
270
+ // POST /api/auth/register
271
+ async register(data) {
272
+ return this.post("register", data);
273
+ }
274
+ // POST /api/auth/refresh
275
+ async refresh(refreshToken) {
276
+ return this.post("refresh", { refreshToken });
277
+ }
278
+ // POST /api/auth/logout
279
+ async logout() {
280
+ return this.post("logout");
281
+ }
282
+ // GET /api/auth/me
283
+ async me() {
284
+ return this.get("me");
285
+ }
286
+ };
287
+ var authResource = new AuthResource();
288
+
289
+ // src/resources/UserResource.ts
290
+ var UserResource = class extends ApiResource {
291
+ constructor() {
292
+ super(...arguments);
293
+ this.endpoint = "/users";
294
+ }
295
+ // GET /api/users/:id
296
+ async getById(id) {
297
+ return this.show(id);
298
+ }
299
+ // GET /api/users/username/:username
300
+ async getByUsername(username) {
301
+ return this.get(`username/${username}`);
302
+ }
303
+ // PATCH /api/users/me
304
+ async updateProfile(data) {
305
+ return this.api.patch(`${this.endpoint}/me`, data);
306
+ }
307
+ // DELETE /api/users/me
308
+ async deleteAccount() {
309
+ return this.api.delete(`${this.endpoint}/me`);
310
+ }
311
+ };
312
+ var userResource = new UserResource();
313
+
314
+ // src/resources/RoomResource.ts
315
+ var RoomResource = class extends ApiResource {
316
+ constructor() {
317
+ super(...arguments);
318
+ this.endpoint = "/rooms";
319
+ }
320
+ // GET /api/rooms - List all rooms
321
+ async list() {
322
+ return this.get("");
323
+ }
324
+ // GET /api/rooms/featured - Get featured rooms
325
+ async featured() {
326
+ return this.get("featured");
327
+ }
328
+ // GET /api/rooms/:slug - Get room by slug
329
+ async getBySlug(slug) {
330
+ return this.get(slug);
331
+ }
332
+ // POST /api/rooms - Create a new room
333
+ async create(data) {
334
+ return this.post("", data);
335
+ }
336
+ // PATCH /api/rooms/:slug - Update room
337
+ async updateRoom(slug, data) {
338
+ return this.api.patch(`${this.endpoint}/${slug}`, data);
339
+ }
340
+ // DELETE /api/rooms/:slug - Delete room
341
+ async deleteRoom(slug) {
342
+ return this.api.delete(`${this.endpoint}/${slug}`);
343
+ }
344
+ // GET /api/rooms/:slug/staff - Get room staff
345
+ async getStaff(slug) {
346
+ return this.get(`${slug}/staff`);
347
+ }
348
+ // PUT /api/rooms/:slug/staff/:userId - Update staff role
349
+ async updateStaffRole(slug, userId, role) {
350
+ return this.api.put(`${this.endpoint}/${slug}/staff/${userId}`, { role });
351
+ }
352
+ // DELETE /api/rooms/:slug/staff/:userId - Remove staff
353
+ async removeStaff(slug, userId) {
354
+ return this.api.delete(`${this.endpoint}/${slug}/staff/${userId}`);
355
+ }
356
+ // POST /api/rooms/:slug/join - Join a room (session-based)
357
+ async join(slug) {
358
+ return this.post(`${slug}/join`);
359
+ }
360
+ // POST /api/rooms/:slug/leave - Leave a room
361
+ async leave(slug) {
362
+ return this.post(`${slug}/leave`);
363
+ }
364
+ // ============================================
365
+ // Waitlist methods
366
+ // ============================================
367
+ // GET /api/rooms/:slug/waitlist
368
+ async getWaitlist(slug) {
369
+ return this.get(`${slug}/waitlist`);
370
+ }
371
+ // POST /api/rooms/:slug/waitlist/join
372
+ async joinWaitlist(slug) {
373
+ return this.post(`${slug}/waitlist/join`);
374
+ }
375
+ // POST /api/rooms/:slug/waitlist/leave
376
+ async leaveWaitlist(slug) {
377
+ return this.post(`${slug}/waitlist/leave`);
378
+ }
379
+ // PATCH /api/rooms/:slug/waitlist - Move user in waitlist
380
+ async moveInWaitlist(slug, userId, position) {
381
+ return this.api.patch(`${this.endpoint}/${slug}/waitlist`, { userId, position });
382
+ }
383
+ // DELETE /api/rooms/:slug/waitlist/:userId - Remove user from waitlist
384
+ async removeFromWaitlist(slug, userId) {
385
+ return this.api.delete(`${this.endpoint}/${slug}/waitlist/${userId}`);
386
+ }
387
+ // POST /api/rooms/:slug/waitlist/lock
388
+ async lockWaitlist(slug) {
389
+ return this.post(`${slug}/waitlist/lock`);
390
+ }
391
+ // POST /api/rooms/:slug/waitlist/unlock
392
+ async unlockWaitlist(slug) {
393
+ return this.post(`${slug}/waitlist/unlock`);
394
+ }
395
+ // ============================================
396
+ // Booth methods
397
+ // ============================================
398
+ // GET /api/rooms/:slug/booth
399
+ async getBooth(slug) {
400
+ return this.get(`${slug}/booth`);
401
+ }
402
+ // POST /api/rooms/:slug/booth/skip
403
+ async skipTrack(slug, options) {
404
+ return this.post(`${slug}/booth/skip`, options || {});
405
+ }
406
+ // POST /api/rooms/:slug/vote
407
+ async vote(slug, type) {
408
+ return this.post(`${slug}/vote`, { type });
409
+ }
410
+ // POST /api/rooms/:slug/grab
411
+ async grabTrack(slug, playlistId) {
412
+ return this.post(`${slug}/grab`, playlistId ? { playlistId } : {});
413
+ }
414
+ };
415
+ var roomResource = new RoomResource();
416
+
417
+ // src/resources/ChatResource.ts
418
+ var ChatResource = class extends ApiResource {
419
+ constructor() {
420
+ super(...arguments);
421
+ this.endpoint = "/rooms";
422
+ }
423
+ // POST /api/rooms/:slug/chat - Send a chat message
424
+ async sendMessage(slug, data) {
425
+ return this.api.post(`${this.endpoint}/${slug}/chat`, data);
426
+ }
427
+ // GET /api/rooms/:slug/chat - Get chat history
428
+ async getMessages(slug, before, limit = 50) {
429
+ const params = { limit };
430
+ if (before) {
431
+ params.before = before;
432
+ }
433
+ return this.api.get(`${this.endpoint}/${slug}/chat`, { params });
434
+ }
435
+ // DELETE /api/rooms/:slug/chat/:messageId - Delete a chat message (moderation)
436
+ async deleteMessage(slug, messageId) {
437
+ return this.api.delete(`${this.endpoint}/${slug}/chat/${messageId}`);
438
+ }
439
+ };
440
+ var chatResource = new ChatResource();
441
+
442
+ // src/resources/PlaylistResource.ts
443
+ var PlaylistResource = class extends ApiResource {
444
+ constructor() {
445
+ super(...arguments);
446
+ this.endpoint = "/playlists";
447
+ }
448
+ // GET /api/playlists - Get all user's playlists
449
+ async getAll() {
450
+ return this.api.get(this.endpoint);
451
+ }
452
+ // GET /api/playlists/:id - Get playlist with items
453
+ async getById(playlistId) {
454
+ return this.api.get(`${this.endpoint}/${playlistId}`);
455
+ }
456
+ // POST /api/playlists - Create playlist
457
+ async create(name) {
458
+ return this.api.post(this.endpoint, { name });
459
+ }
460
+ // PATCH /api/playlists/:id - Update playlist
461
+ async rename(playlistId, name) {
462
+ return this.api.patch(`${this.endpoint}/${playlistId}`, { name });
463
+ }
464
+ // DELETE /api/playlists/:id - Delete playlist
465
+ async remove(playlistId) {
466
+ return this.api.delete(`${this.endpoint}/${playlistId}`);
467
+ }
468
+ // POST /api/playlists/:id/activate - Activate playlist
469
+ async activate(playlistId) {
470
+ return this.api.post(`${this.endpoint}/${playlistId}/activate`);
471
+ }
472
+ // POST /api/playlists/:id/shuffle - Shuffle playlist
473
+ async shuffle(playlistId) {
474
+ return this.api.post(`${this.endpoint}/${playlistId}/shuffle`);
475
+ }
476
+ // POST /api/playlists/:id/items - Add item to playlist
477
+ async addItem(playlistId, data) {
478
+ return this.api.post(`${this.endpoint}/${playlistId}/items`, data);
479
+ }
480
+ // DELETE /api/playlists/:id/items/:itemId - Remove item from playlist
481
+ async removeItem(playlistId, itemId) {
482
+ return this.api.delete(`${this.endpoint}/${playlistId}/items/${itemId}`);
483
+ }
484
+ // PATCH /api/playlists/:id/items/:itemId/move - Move item in playlist
485
+ async moveItem(playlistId, itemId, position) {
486
+ return this.api.patch(`${this.endpoint}/${playlistId}/items/${itemId}/move`, { position });
487
+ }
488
+ // POST /api/playlists/:id/import - Import from YouTube/SoundCloud playlist
489
+ async importPlaylist(playlistId, data) {
490
+ return this.api.post(`${this.endpoint}/${playlistId}/import`, data);
491
+ }
492
+ };
493
+ var playlistResource = new PlaylistResource();
494
+
495
+ // src/resources/SourceResource.ts
496
+ var SourceResource = class extends ApiResource {
497
+ constructor() {
498
+ super(...arguments);
499
+ this.endpoint = "/sources";
500
+ }
501
+ // ============================================
502
+ // YouTube
503
+ // ============================================
504
+ // GET /api/sources/youtube/search?q=...&limit=...
505
+ async searchYouTube(query, limit = 10) {
506
+ return this.api.get(`${this.endpoint}/youtube/search`, {
507
+ params: { q: query, limit }
508
+ });
509
+ }
510
+ // GET /api/sources/youtube/videos/:videoId
511
+ async getYouTubeVideo(videoId) {
512
+ return this.api.get(`${this.endpoint}/youtube/videos/${videoId}`);
513
+ }
514
+ // ============================================
515
+ // SoundCloud
516
+ // ============================================
517
+ // GET /api/sources/soundcloud/search?q=...&limit=...
518
+ async searchSoundCloud(query, limit = 10) {
519
+ return this.api.get(`${this.endpoint}/soundcloud/search`, {
520
+ params: { q: query, limit }
521
+ });
522
+ }
523
+ // GET /api/sources/soundcloud/tracks/:trackId
524
+ async getSoundCloudTrack(trackId) {
525
+ return this.api.get(`${this.endpoint}/soundcloud/tracks/${trackId}`);
526
+ }
527
+ // GET /api/sources/soundcloud/resolve?url=...
528
+ async resolveSoundCloudUrl(url) {
529
+ return this.api.get(`${this.endpoint}/soundcloud/resolve`, {
530
+ params: { url }
531
+ });
532
+ }
533
+ // ============================================
534
+ // Combined search (helper)
535
+ // ============================================
536
+ // Search both YouTube and SoundCloud
537
+ async searchAll(query, limit = 10) {
538
+ var _a, _b, _c, _d;
539
+ const [youtube, soundcloud] = await Promise.allSettled([
540
+ this.searchYouTube(query, limit),
541
+ this.searchSoundCloud(query, limit)
542
+ ]);
543
+ const results = [];
544
+ if (youtube.status === "fulfilled" && ((_b = (_a = youtube.value.data) == null ? void 0 : _a.data) == null ? void 0 : _b.results)) {
545
+ results.push(...youtube.value.data.data.results);
546
+ }
547
+ if (soundcloud.status === "fulfilled" && ((_d = (_c = soundcloud.value.data) == null ? void 0 : _c.data) == null ? void 0 : _d.results)) {
548
+ results.push(...soundcloud.value.data.data.results);
549
+ }
550
+ return results;
551
+ }
552
+ };
553
+ var sourceResource = new SourceResource();
554
+ export {
555
+ Api,
556
+ ApiError,
557
+ ApiResource,
558
+ AuthResource,
559
+ ChatResource,
560
+ Logger,
561
+ PlaylistResource,
562
+ RoomResource,
563
+ SourceResource,
564
+ UserResource,
565
+ authResource,
566
+ chatResource,
567
+ playlistResource,
568
+ roomResource,
569
+ sourceResource,
570
+ userResource
571
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@borealise/api",
3
+ "version": "1.0.0",
4
+ "description": "Official API client for Borealise",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
21
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
22
+ "lint": "tsc --noEmit",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "borealise",
27
+ "api",
28
+ "client",
29
+ "music",
30
+ "rooms"
31
+ ],
32
+ "author": "Borealise",
33
+ "license": "MIT",
34
+ "homepage": "https://borealise.com",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/borealise/api-client"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/borealise/api-client/issues"
41
+ },
42
+ "peerDependencies": {
43
+ "axios": "^1.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^20.0.0",
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^5.0.0",
49
+ "axios": "^1.6.0"
50
+ }
51
+ }