@borealise/api 2.0.0-alpha.9 → 2.1.0-alpha.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/README.md CHANGED
@@ -5,15 +5,13 @@ Official JavaScript/TypeScript API client for [Borealise](https://borealise.com)
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @borealise/api axios
8
+ npm install @borealise/api
9
9
  # or
10
- yarn add @borealise/api axios
10
+ yarn add @borealise/api
11
11
  # or
12
- pnpm add @borealise/api axios
12
+ pnpm add @borealise/api
13
13
  ```
14
14
 
15
- > `axios` is a peer dependency — you need to install it alongside this package.
16
-
17
15
  ---
18
16
 
19
17
  ## Quick Start
@@ -26,7 +24,7 @@ Create a client once, then use resources:
26
24
  import { createApiClient } from '@borealise/api'
27
25
 
28
26
  const client = createApiClient({
29
- baseURL: 'https://prod.borealise.com',
27
+ baseURL: 'https://prod.borealise.com/api',
30
28
  })
31
29
 
32
30
  // Login
@@ -45,7 +43,7 @@ If you need more control, create the API and resources separately:
45
43
  import { createApi, createAuthResource, createRoomResource } from '@borealise/api'
46
44
 
47
45
  const api = createApi({
48
- baseURL: 'https://prod.borealise.com',
46
+ baseURL: 'https://prod.borealise.com/api',
49
47
  timeout: 15000,
50
48
  logging: false,
51
49
  })
@@ -63,7 +61,7 @@ await auth.login({ login: 'you@example.com', password: 'secret' })
63
61
 
64
62
  ```ts
65
63
  createApiClient({
66
- baseURL: 'https://prod.borealise.com', // required
64
+ baseURL: 'https://prod.borealise.com/api', // required
67
65
  timeout: 15000, // optional, default: 30000ms
68
66
  logging: false, // optional, disable all console output
69
67
  headers: { 'X-Custom-Header': 'value' } // optional, custom headers
@@ -217,7 +215,7 @@ try {
217
215
  if (err instanceof ApiError) {
218
216
  console.log(err.message) // Human-readable message from backend
219
217
  console.log(err.status) // HTTP status code, e.g. 401
220
- console.log(err.code) // Axios error code, e.g. 'NETWORK_ERROR'
218
+ console.log(err.code) // Error code, e.g. 'NETWORK_ERROR', 'TIMEOUT_ERROR', 'HTTP_401'
221
219
  console.log(err.response) // Raw backend error response body
222
220
  }
223
221
  }
@@ -241,6 +239,12 @@ client.api.setAuthToken(null)
241
239
 
242
240
  The underlying API instance is accessible via `client.api`. Use it for token management and custom headers:
243
241
 
242
+ ## Breaking Changes in 2.1.0-alpha.1
243
+
244
+ - The `axiosInstance` getter was removed from `Api`.
245
+ - The HTTP transport is now handled internally with Ky.
246
+ - If you previously accessed raw Axios internals, migrate to `Api` public methods (`get`, `post`, `put`, `patch`, `delete`, `setAuthToken`, `setHeader`, `removeHeader`).
247
+
244
248
  ---
245
249
 
246
250
  ## Tree-Shaking
package/dist/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
- import { AxiosRequestConfig, AxiosInstance } from 'axios';
1
+ import { URLSearchParams } from 'node:url';
2
2
 
3
3
  interface ApiConfig {
4
4
  baseURL: string;
5
5
  timeout?: number;
6
6
  headers?: Record<string, string>;
7
+ withCredentials?: boolean;
7
8
  /** Set to false to suppress all log output. Defaults to true. */
8
9
  logging?: boolean;
9
10
  }
@@ -12,34 +13,35 @@ interface ApiResponse<T = unknown> {
12
13
  status: number;
13
14
  headers: Record<string, string>;
14
15
  }
16
+ type ApiQueryParams = URLSearchParams | Record<string, unknown>;
17
+ interface ApiRequestConfig {
18
+ headers?: Record<string, string>;
19
+ params?: ApiQueryParams;
20
+ timeout?: number;
21
+ credentials?: 'omit' | 'same-origin' | 'include';
22
+ }
15
23
  interface BackendErrorResponse {
16
24
  success: false;
17
25
  error: string;
18
26
  }
27
+ interface Api {
28
+ setAuthToken(token: string | null): void;
29
+ setHeader(key: string, value: string): void;
30
+ removeHeader(key: string): void;
31
+ get<T = unknown>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
32
+ post<T = unknown>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
33
+ put<T = unknown>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
34
+ patch<T = unknown>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
35
+ delete<T = unknown>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
36
+ }
37
+
19
38
  declare class ApiError extends Error {
20
39
  readonly status?: number;
21
40
  readonly code?: string;
22
41
  readonly response?: BackendErrorResponse;
23
42
  constructor(message: string, status?: number, code?: string, response?: BackendErrorResponse);
24
43
  }
25
- declare class Api {
26
- private readonly axios;
27
- private readonly logger;
28
- private readonly config;
29
- constructor(config: ApiConfig);
30
- private setupInterceptors;
31
- private parseError;
32
- setAuthToken(token: string | null): void;
33
- setHeader(key: string, value: string): void;
34
- removeHeader(key: string): void;
35
- get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
36
- post<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
37
- put<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
38
- patch<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
39
- delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
40
- private wrapResponse;
41
- get axiosInstance(): AxiosInstance;
42
- }
44
+
43
45
  declare const createApi: (config: ApiConfig) => Api;
44
46
 
45
47
  declare class Logger {
@@ -111,7 +113,7 @@ interface MeResponse {
111
113
  interface AuthResource {
112
114
  login(credentials: LoginCredentials): Promise<ApiResponse<AuthResponse>>;
113
115
  register(data: RegisterData): Promise<ApiResponse<AuthResponse>>;
114
- refresh(refreshToken: string): Promise<ApiResponse<RefreshResponse>>;
116
+ refresh(refreshToken?: string): Promise<ApiResponse<RefreshResponse>>;
115
117
  logout(): Promise<ApiResponse<{
116
118
  success: boolean;
117
119
  message: string;
@@ -128,6 +130,34 @@ declare const createAuthResource: (api: Api) => AuthResource;
128
130
 
129
131
  interface User extends AuthUser {
130
132
  }
133
+ /**
134
+ * Public user profile — never contains private fields (email, flags, etc.)
135
+ */
136
+ interface PublicUser {
137
+ id: number;
138
+ username: string;
139
+ displayName: string | null;
140
+ avatarId: string;
141
+ bio: string | null;
142
+ globalRole: string;
143
+ xp: number;
144
+ level: number;
145
+ subscriptionType?: string | null;
146
+ subscriptionMonths?: number | null;
147
+ currentRoomSlug?: string | null;
148
+ createdAt?: string;
149
+ friendsCount: number;
150
+ }
151
+ /**
152
+ * A friend entry as shown on a public profile.
153
+ */
154
+ interface PublicProfileFriend {
155
+ id: number;
156
+ username: string;
157
+ displayName: string | null;
158
+ avatarId: string;
159
+ level: number;
160
+ }
131
161
  interface UpdateProfileData {
132
162
  displayName?: string;
133
163
  bio?: string;
@@ -136,7 +166,8 @@ type GlobalRole = 'user' | 'moderator' | 'admin' | 'owner';
136
166
  interface UserResponse {
137
167
  success: boolean;
138
168
  data: {
139
- user: User;
169
+ user: PublicUser;
170
+ friends: PublicProfileFriend[];
140
171
  };
141
172
  }
142
173
  interface MyViolation {
@@ -187,6 +218,22 @@ interface Room {
187
218
  population: number;
188
219
  totalPlays: number;
189
220
  isFeatured: boolean;
221
+ verified: boolean;
222
+ currentDJ?: {
223
+ id: number;
224
+ username: string;
225
+ displayName: string | null;
226
+ avatarId: string;
227
+ } | null;
228
+ currentMedia?: {
229
+ id: number;
230
+ source: 'youtube' | 'soundcloud';
231
+ sourceId: string;
232
+ title: string;
233
+ artist: string | null;
234
+ thumbnail: string | null;
235
+ duration: number;
236
+ } | null;
190
237
  createdAt: string;
191
238
  updatedAt: string;
192
239
  }
@@ -484,6 +531,7 @@ interface DashboardActivityResponse {
484
531
  }
485
532
  interface RoomResource {
486
533
  list(): Promise<ApiResponse<RoomsResponse>>;
534
+ mine(limit?: number): Promise<ApiResponse<RoomsResponse>>;
487
535
  featured(): Promise<ApiResponse<FeaturedRoomsResponse>>;
488
536
  getBySlug(slug: string): Promise<ApiResponse<RoomResponse>>;
489
537
  create(data: CreateRoomData): Promise<ApiResponse<RoomResponse>>;
@@ -579,6 +627,7 @@ interface ChatMessage {
579
627
  user_id?: number;
580
628
  username?: string;
581
629
  display_name?: string | null;
630
+ avatar_id?: string | null;
582
631
  role?: RoomRole;
583
632
  global_role?: string | null;
584
633
  subscription_type?: string | null;
@@ -586,7 +635,11 @@ interface ChatMessage {
586
635
  content: string;
587
636
  timestamp: number;
588
637
  type?: 'user' | 'system';
638
+ edited_at?: number | null;
639
+ edited_by?: number | null;
589
640
  deleted?: boolean;
641
+ deleted_at?: number | null;
642
+ deleted_by?: number | null;
590
643
  }
591
644
  interface SendMessageData {
592
645
  content: string;
@@ -606,6 +659,7 @@ interface ChatMessageResponse {
606
659
  interface ChatResource {
607
660
  sendMessage(slug: string, data: SendMessageData): Promise<ApiResponse<ChatMessageResponse>>;
608
661
  getMessages(slug: string, before?: string, limit?: number): Promise<ApiResponse<ChatMessagesResponse>>;
662
+ editMessage(slug: string, messageId: string, data: SendMessageData): Promise<ApiResponse<ChatMessageResponse>>;
609
663
  deleteMessage(slug: string, messageId: string): Promise<ApiResponse<{
610
664
  success: boolean;
611
665
  data: null;
@@ -802,6 +856,42 @@ interface PortalResponse {
802
856
  url: string;
803
857
  };
804
858
  }
859
+ interface CreateGiftCheckoutResponse {
860
+ success: boolean;
861
+ data: {
862
+ checkoutUrl: string;
863
+ sessionId: string;
864
+ };
865
+ }
866
+ interface GiftHistoryEntry {
867
+ giftItemId: number;
868
+ purchaseId: number;
869
+ status: string;
870
+ startsAt: string;
871
+ endsAt: string;
872
+ createdAt: string;
873
+ isAnonymous: boolean;
874
+ recipientUsername: string | null;
875
+ purchaserUsername: string | null;
876
+ }
877
+ interface GiftsHistoryResponse {
878
+ success: boolean;
879
+ data: {
880
+ purchased: GiftHistoryEntry[];
881
+ received: GiftHistoryEntry[];
882
+ failedPurchases: Array<{
883
+ purchaseId: number;
884
+ recipientUsername: string;
885
+ quantity: number;
886
+ createdAt: string;
887
+ }>;
888
+ supporterBadge: {
889
+ tier: 'none' | 'bronze' | 'silver' | 'gold';
890
+ giftedCount: number;
891
+ nextTierAt: number | null;
892
+ };
893
+ };
894
+ }
805
895
  interface SubscriptionResource {
806
896
  getStatus(): Promise<ApiResponse<{
807
897
  success: boolean;
@@ -812,6 +902,14 @@ interface SubscriptionResource {
812
902
  success: boolean;
813
903
  }>>;
814
904
  createPortal(): Promise<ApiResponse<PortalResponse>>;
905
+ createGiftCheckout(recipientUsername: string, quantity: 1 | 5 | 10 | 20, isAnonymous?: boolean): Promise<ApiResponse<CreateGiftCheckoutResponse>>;
906
+ getGiftsHistory(): Promise<ApiResponse<GiftsHistoryResponse>>;
907
+ retryGiftAssignment(purchaseId: number, recipientUsername?: string): Promise<ApiResponse<{
908
+ success: boolean;
909
+ data: {
910
+ ok: true;
911
+ };
912
+ }>>;
815
913
  }
816
914
  declare const createSubscriptionResource: (api: Api) => SubscriptionResource;
817
915
 
@@ -823,6 +921,7 @@ interface FriendEntry {
823
921
  displayName: string | null;
824
922
  avatarId: string;
825
923
  level: number;
924
+ currentRoomSlug?: string | null;
826
925
  status: 'pending' | 'accepted' | 'blocked';
827
926
  isSender: boolean;
828
927
  }
@@ -977,4 +1076,4 @@ interface ApiClient {
977
1076
  }
978
1077
  declare const createApiClient: (config: ApiConfig) => ApiClient;
979
1078
 
980
- export { type AccountViolation, type AddMediaData, type AddViolationResponse, type AdminListUsersParams, type AdminResource, type AdminStatsResponse, type AdminStatsRoom, type AdminUserEntry, type AdminUsersResponse, Api, type ApiClient, type ApiConfig, ApiError, type ApiResponse, type AuditAction, type AuthResource, type AuthUser, type AvatarCatalogItem, type AvatarCatalogResponse, type AvatarUnlockType, type BackendErrorResponse, type BanResponse, type BoothDJ, type BoothMedia, type BoothResponse, type BoothState, type ChatMessage, type ChatMessageResponse, type ChatMessagesResponse, type ChatResource, type CreateIntentResponse, type CreateRoomData, type DashboardActivityItem, type DashboardActivityResponse, type DashboardActivityType, type EquipAvatarData, type FeaturedRoomsResponse, type FriendActionResponse, type FriendEntry, type FriendList, type FriendListResponse, type FriendResource, type FriendStatusResponse, type FriendshipStatus, type GlobalRole, type GrabResponse, type ImportPlaylistData, type ImportPlaylistResponse, type ImportResult, type JoinMuteInfo, type JoinRoomResponse, Logger, type MediaItem, type MediaItemResponse, type MediaSearchResult, type MediaSource, type ModerateUserData, type MuteResponse, type MyViolation, type MyViolationsResponse, type PaginationMeta, type PlayHistoryItem, type Playlist, type PlaylistResource, type PlaylistResponse, type PlaylistsResponse, type PortalResponse, type Room, type RoomAuditLog, type RoomBan, type RoomBansResponse, type RoomHistoryResponse, type RoomMember, type RoomMute, type RoomMutesResponse, type RoomResource, type RoomResponse, type RoomRole, type RoomStaffResponse, type RoomUserState, type RoomsResponse, type SendMessageData, type ShopResource, type ShuffleResponse, type SoundCloudSearchResponse, type SoundCloudTrackResponse, type SourceResource, type SubscriptionPlan, type SubscriptionResource, type SubscriptionStatus, type UpdateProfileData, type UpdateRoomData, type User, type UserResource, type UserResponse, type UserViolationsResponse, type VoteResponse, type WaitlistResponse, type WaitlistUser, type YouTubeSearchResponse, type YouTubeVideoResponse, createAdminResource, createApi, createApiClient, createAuthResource, createChatResource, createFriendResource, createPlaylistResource, createRoomResource, createShopResource, createSourceResource, createSubscriptionResource, createUserResource };
1079
+ export { type AccountViolation, type AddMediaData, type AddViolationResponse, type AdminListUsersParams, type AdminResource, type AdminStatsResponse, type AdminStatsRoom, type AdminUserEntry, type AdminUsersResponse, type Api, type ApiClient, type ApiConfig, ApiError, type ApiQueryParams, type ApiRequestConfig, type ApiResponse, type AuditAction, type AuthResource, type AuthUser, type AvatarCatalogItem, type AvatarCatalogResponse, type AvatarUnlockType, type BackendErrorResponse, type BanResponse, type BoothDJ, type BoothMedia, type BoothResponse, type BoothState, type ChatMessage, type ChatMessageResponse, type ChatMessagesResponse, type ChatResource, type CreateIntentResponse, type CreateRoomData, type DashboardActivityItem, type DashboardActivityResponse, type DashboardActivityType, type EquipAvatarData, type FeaturedRoomsResponse, type FriendActionResponse, type FriendEntry, type FriendList, type FriendListResponse, type FriendResource, type FriendStatusResponse, type FriendshipStatus, type GlobalRole, type GrabResponse, type ImportPlaylistData, type ImportPlaylistResponse, type ImportResult, type JoinMuteInfo, type JoinRoomResponse, Logger, type MediaItem, type MediaItemResponse, type MediaSearchResult, type MediaSource, type ModerateUserData, type MuteResponse, type MyViolation, type MyViolationsResponse, type PaginationMeta, type PlayHistoryItem, type Playlist, type PlaylistResource, type PlaylistResponse, type PlaylistsResponse, type PortalResponse, type PublicProfileFriend, type PublicUser, type Room, type RoomAuditLog, type RoomBan, type RoomBansResponse, type RoomHistoryResponse, type RoomMember, type RoomMute, type RoomMutesResponse, type RoomResource, type RoomResponse, type RoomRole, type RoomStaffResponse, type RoomUserState, type RoomsResponse, type SendMessageData, type ShopResource, type ShuffleResponse, type SoundCloudSearchResponse, type SoundCloudTrackResponse, type SourceResource, type SubscriptionPlan, type SubscriptionResource, type SubscriptionStatus, type UpdateProfileData, type UpdateRoomData, type User, type UserResource, type UserResponse, type UserViolationsResponse, type VoteResponse, type WaitlistResponse, type WaitlistUser, type YouTubeSearchResponse, type YouTubeVideoResponse, createAdminResource, createApi, createApiClient, createAuthResource, createChatResource, createFriendResource, createPlaylistResource, createRoomResource, createShopResource, createSourceResource, createSubscriptionResource, createUserResource };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { AxiosRequestConfig, AxiosInstance } from 'axios';
1
+ import { URLSearchParams } from 'node:url';
2
2
 
3
3
  interface ApiConfig {
4
4
  baseURL: string;
5
5
  timeout?: number;
6
6
  headers?: Record<string, string>;
7
+ withCredentials?: boolean;
7
8
  /** Set to false to suppress all log output. Defaults to true. */
8
9
  logging?: boolean;
9
10
  }
@@ -12,34 +13,35 @@ interface ApiResponse<T = unknown> {
12
13
  status: number;
13
14
  headers: Record<string, string>;
14
15
  }
16
+ type ApiQueryParams = URLSearchParams | Record<string, unknown>;
17
+ interface ApiRequestConfig {
18
+ headers?: Record<string, string>;
19
+ params?: ApiQueryParams;
20
+ timeout?: number;
21
+ credentials?: 'omit' | 'same-origin' | 'include';
22
+ }
15
23
  interface BackendErrorResponse {
16
24
  success: false;
17
25
  error: string;
18
26
  }
27
+ interface Api {
28
+ setAuthToken(token: string | null): void;
29
+ setHeader(key: string, value: string): void;
30
+ removeHeader(key: string): void;
31
+ get<T = unknown>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
32
+ post<T = unknown>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
33
+ put<T = unknown>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
34
+ patch<T = unknown>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
35
+ delete<T = unknown>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
36
+ }
37
+
19
38
  declare class ApiError extends Error {
20
39
  readonly status?: number;
21
40
  readonly code?: string;
22
41
  readonly response?: BackendErrorResponse;
23
42
  constructor(message: string, status?: number, code?: string, response?: BackendErrorResponse);
24
43
  }
25
- declare class Api {
26
- private readonly axios;
27
- private readonly logger;
28
- private readonly config;
29
- constructor(config: ApiConfig);
30
- private setupInterceptors;
31
- private parseError;
32
- setAuthToken(token: string | null): void;
33
- setHeader(key: string, value: string): void;
34
- removeHeader(key: string): void;
35
- get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
36
- post<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
37
- put<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
38
- patch<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
39
- delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>>;
40
- private wrapResponse;
41
- get axiosInstance(): AxiosInstance;
42
- }
44
+
43
45
  declare const createApi: (config: ApiConfig) => Api;
44
46
 
45
47
  declare class Logger {
@@ -111,7 +113,7 @@ interface MeResponse {
111
113
  interface AuthResource {
112
114
  login(credentials: LoginCredentials): Promise<ApiResponse<AuthResponse>>;
113
115
  register(data: RegisterData): Promise<ApiResponse<AuthResponse>>;
114
- refresh(refreshToken: string): Promise<ApiResponse<RefreshResponse>>;
116
+ refresh(refreshToken?: string): Promise<ApiResponse<RefreshResponse>>;
115
117
  logout(): Promise<ApiResponse<{
116
118
  success: boolean;
117
119
  message: string;
@@ -128,6 +130,34 @@ declare const createAuthResource: (api: Api) => AuthResource;
128
130
 
129
131
  interface User extends AuthUser {
130
132
  }
133
+ /**
134
+ * Public user profile — never contains private fields (email, flags, etc.)
135
+ */
136
+ interface PublicUser {
137
+ id: number;
138
+ username: string;
139
+ displayName: string | null;
140
+ avatarId: string;
141
+ bio: string | null;
142
+ globalRole: string;
143
+ xp: number;
144
+ level: number;
145
+ subscriptionType?: string | null;
146
+ subscriptionMonths?: number | null;
147
+ currentRoomSlug?: string | null;
148
+ createdAt?: string;
149
+ friendsCount: number;
150
+ }
151
+ /**
152
+ * A friend entry as shown on a public profile.
153
+ */
154
+ interface PublicProfileFriend {
155
+ id: number;
156
+ username: string;
157
+ displayName: string | null;
158
+ avatarId: string;
159
+ level: number;
160
+ }
131
161
  interface UpdateProfileData {
132
162
  displayName?: string;
133
163
  bio?: string;
@@ -136,7 +166,8 @@ type GlobalRole = 'user' | 'moderator' | 'admin' | 'owner';
136
166
  interface UserResponse {
137
167
  success: boolean;
138
168
  data: {
139
- user: User;
169
+ user: PublicUser;
170
+ friends: PublicProfileFriend[];
140
171
  };
141
172
  }
142
173
  interface MyViolation {
@@ -187,6 +218,22 @@ interface Room {
187
218
  population: number;
188
219
  totalPlays: number;
189
220
  isFeatured: boolean;
221
+ verified: boolean;
222
+ currentDJ?: {
223
+ id: number;
224
+ username: string;
225
+ displayName: string | null;
226
+ avatarId: string;
227
+ } | null;
228
+ currentMedia?: {
229
+ id: number;
230
+ source: 'youtube' | 'soundcloud';
231
+ sourceId: string;
232
+ title: string;
233
+ artist: string | null;
234
+ thumbnail: string | null;
235
+ duration: number;
236
+ } | null;
190
237
  createdAt: string;
191
238
  updatedAt: string;
192
239
  }
@@ -484,6 +531,7 @@ interface DashboardActivityResponse {
484
531
  }
485
532
  interface RoomResource {
486
533
  list(): Promise<ApiResponse<RoomsResponse>>;
534
+ mine(limit?: number): Promise<ApiResponse<RoomsResponse>>;
487
535
  featured(): Promise<ApiResponse<FeaturedRoomsResponse>>;
488
536
  getBySlug(slug: string): Promise<ApiResponse<RoomResponse>>;
489
537
  create(data: CreateRoomData): Promise<ApiResponse<RoomResponse>>;
@@ -579,6 +627,7 @@ interface ChatMessage {
579
627
  user_id?: number;
580
628
  username?: string;
581
629
  display_name?: string | null;
630
+ avatar_id?: string | null;
582
631
  role?: RoomRole;
583
632
  global_role?: string | null;
584
633
  subscription_type?: string | null;
@@ -586,7 +635,11 @@ interface ChatMessage {
586
635
  content: string;
587
636
  timestamp: number;
588
637
  type?: 'user' | 'system';
638
+ edited_at?: number | null;
639
+ edited_by?: number | null;
589
640
  deleted?: boolean;
641
+ deleted_at?: number | null;
642
+ deleted_by?: number | null;
590
643
  }
591
644
  interface SendMessageData {
592
645
  content: string;
@@ -606,6 +659,7 @@ interface ChatMessageResponse {
606
659
  interface ChatResource {
607
660
  sendMessage(slug: string, data: SendMessageData): Promise<ApiResponse<ChatMessageResponse>>;
608
661
  getMessages(slug: string, before?: string, limit?: number): Promise<ApiResponse<ChatMessagesResponse>>;
662
+ editMessage(slug: string, messageId: string, data: SendMessageData): Promise<ApiResponse<ChatMessageResponse>>;
609
663
  deleteMessage(slug: string, messageId: string): Promise<ApiResponse<{
610
664
  success: boolean;
611
665
  data: null;
@@ -802,6 +856,42 @@ interface PortalResponse {
802
856
  url: string;
803
857
  };
804
858
  }
859
+ interface CreateGiftCheckoutResponse {
860
+ success: boolean;
861
+ data: {
862
+ checkoutUrl: string;
863
+ sessionId: string;
864
+ };
865
+ }
866
+ interface GiftHistoryEntry {
867
+ giftItemId: number;
868
+ purchaseId: number;
869
+ status: string;
870
+ startsAt: string;
871
+ endsAt: string;
872
+ createdAt: string;
873
+ isAnonymous: boolean;
874
+ recipientUsername: string | null;
875
+ purchaserUsername: string | null;
876
+ }
877
+ interface GiftsHistoryResponse {
878
+ success: boolean;
879
+ data: {
880
+ purchased: GiftHistoryEntry[];
881
+ received: GiftHistoryEntry[];
882
+ failedPurchases: Array<{
883
+ purchaseId: number;
884
+ recipientUsername: string;
885
+ quantity: number;
886
+ createdAt: string;
887
+ }>;
888
+ supporterBadge: {
889
+ tier: 'none' | 'bronze' | 'silver' | 'gold';
890
+ giftedCount: number;
891
+ nextTierAt: number | null;
892
+ };
893
+ };
894
+ }
805
895
  interface SubscriptionResource {
806
896
  getStatus(): Promise<ApiResponse<{
807
897
  success: boolean;
@@ -812,6 +902,14 @@ interface SubscriptionResource {
812
902
  success: boolean;
813
903
  }>>;
814
904
  createPortal(): Promise<ApiResponse<PortalResponse>>;
905
+ createGiftCheckout(recipientUsername: string, quantity: 1 | 5 | 10 | 20, isAnonymous?: boolean): Promise<ApiResponse<CreateGiftCheckoutResponse>>;
906
+ getGiftsHistory(): Promise<ApiResponse<GiftsHistoryResponse>>;
907
+ retryGiftAssignment(purchaseId: number, recipientUsername?: string): Promise<ApiResponse<{
908
+ success: boolean;
909
+ data: {
910
+ ok: true;
911
+ };
912
+ }>>;
815
913
  }
816
914
  declare const createSubscriptionResource: (api: Api) => SubscriptionResource;
817
915
 
@@ -823,6 +921,7 @@ interface FriendEntry {
823
921
  displayName: string | null;
824
922
  avatarId: string;
825
923
  level: number;
924
+ currentRoomSlug?: string | null;
826
925
  status: 'pending' | 'accepted' | 'blocked';
827
926
  isSender: boolean;
828
927
  }
@@ -977,4 +1076,4 @@ interface ApiClient {
977
1076
  }
978
1077
  declare const createApiClient: (config: ApiConfig) => ApiClient;
979
1078
 
980
- export { type AccountViolation, type AddMediaData, type AddViolationResponse, type AdminListUsersParams, type AdminResource, type AdminStatsResponse, type AdminStatsRoom, type AdminUserEntry, type AdminUsersResponse, Api, type ApiClient, type ApiConfig, ApiError, type ApiResponse, type AuditAction, type AuthResource, type AuthUser, type AvatarCatalogItem, type AvatarCatalogResponse, type AvatarUnlockType, type BackendErrorResponse, type BanResponse, type BoothDJ, type BoothMedia, type BoothResponse, type BoothState, type ChatMessage, type ChatMessageResponse, type ChatMessagesResponse, type ChatResource, type CreateIntentResponse, type CreateRoomData, type DashboardActivityItem, type DashboardActivityResponse, type DashboardActivityType, type EquipAvatarData, type FeaturedRoomsResponse, type FriendActionResponse, type FriendEntry, type FriendList, type FriendListResponse, type FriendResource, type FriendStatusResponse, type FriendshipStatus, type GlobalRole, type GrabResponse, type ImportPlaylistData, type ImportPlaylistResponse, type ImportResult, type JoinMuteInfo, type JoinRoomResponse, Logger, type MediaItem, type MediaItemResponse, type MediaSearchResult, type MediaSource, type ModerateUserData, type MuteResponse, type MyViolation, type MyViolationsResponse, type PaginationMeta, type PlayHistoryItem, type Playlist, type PlaylistResource, type PlaylistResponse, type PlaylistsResponse, type PortalResponse, type Room, type RoomAuditLog, type RoomBan, type RoomBansResponse, type RoomHistoryResponse, type RoomMember, type RoomMute, type RoomMutesResponse, type RoomResource, type RoomResponse, type RoomRole, type RoomStaffResponse, type RoomUserState, type RoomsResponse, type SendMessageData, type ShopResource, type ShuffleResponse, type SoundCloudSearchResponse, type SoundCloudTrackResponse, type SourceResource, type SubscriptionPlan, type SubscriptionResource, type SubscriptionStatus, type UpdateProfileData, type UpdateRoomData, type User, type UserResource, type UserResponse, type UserViolationsResponse, type VoteResponse, type WaitlistResponse, type WaitlistUser, type YouTubeSearchResponse, type YouTubeVideoResponse, createAdminResource, createApi, createApiClient, createAuthResource, createChatResource, createFriendResource, createPlaylistResource, createRoomResource, createShopResource, createSourceResource, createSubscriptionResource, createUserResource };
1079
+ export { type AccountViolation, type AddMediaData, type AddViolationResponse, type AdminListUsersParams, type AdminResource, type AdminStatsResponse, type AdminStatsRoom, type AdminUserEntry, type AdminUsersResponse, type Api, type ApiClient, type ApiConfig, ApiError, type ApiQueryParams, type ApiRequestConfig, type ApiResponse, type AuditAction, type AuthResource, type AuthUser, type AvatarCatalogItem, type AvatarCatalogResponse, type AvatarUnlockType, type BackendErrorResponse, type BanResponse, type BoothDJ, type BoothMedia, type BoothResponse, type BoothState, type ChatMessage, type ChatMessageResponse, type ChatMessagesResponse, type ChatResource, type CreateIntentResponse, type CreateRoomData, type DashboardActivityItem, type DashboardActivityResponse, type DashboardActivityType, type EquipAvatarData, type FeaturedRoomsResponse, type FriendActionResponse, type FriendEntry, type FriendList, type FriendListResponse, type FriendResource, type FriendStatusResponse, type FriendshipStatus, type GlobalRole, type GrabResponse, type ImportPlaylistData, type ImportPlaylistResponse, type ImportResult, type JoinMuteInfo, type JoinRoomResponse, Logger, type MediaItem, type MediaItemResponse, type MediaSearchResult, type MediaSource, type ModerateUserData, type MuteResponse, type MyViolation, type MyViolationsResponse, type PaginationMeta, type PlayHistoryItem, type Playlist, type PlaylistResource, type PlaylistResponse, type PlaylistsResponse, type PortalResponse, type PublicProfileFriend, type PublicUser, type Room, type RoomAuditLog, type RoomBan, type RoomBansResponse, type RoomHistoryResponse, type RoomMember, type RoomMute, type RoomMutesResponse, type RoomResource, type RoomResponse, type RoomRole, type RoomStaffResponse, type RoomUserState, type RoomsResponse, type SendMessageData, type ShopResource, type ShuffleResponse, type SoundCloudSearchResponse, type SoundCloudTrackResponse, type SourceResource, type SubscriptionPlan, type SubscriptionResource, type SubscriptionStatus, type UpdateProfileData, type UpdateRoomData, type User, type UserResource, type UserResponse, type UserViolationsResponse, type VoteResponse, type WaitlistResponse, type WaitlistUser, type YouTubeSearchResponse, type YouTubeVideoResponse, createAdminResource, createApi, createApiClient, createAuthResource, createChatResource, createFriendResource, createPlaylistResource, createRoomResource, createShopResource, createSourceResource, createSubscriptionResource, createUserResource };
package/dist/index.js CHANGED
@@ -30,7 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Api: () => Api,
34
33
  ApiError: () => ApiError,
35
34
  Logger: () => Logger,
36
35
  createAdminResource: () => createAdminResource,
@@ -48,8 +47,55 @@ __export(index_exports, {
48
47
  });
49
48
  module.exports = __toCommonJS(index_exports);
50
49
 
51
- // src/Api.ts
52
- var import_axios = __toESM(require("axios"));
50
+ // src/api/errors.ts
51
+ var import_ky = require("ky");
52
+ var ApiError = class extends Error {
53
+ constructor(message, status, code, response) {
54
+ super(message);
55
+ this.name = "ApiError";
56
+ this.status = status;
57
+ this.code = code;
58
+ this.response = response;
59
+ }
60
+ };
61
+ var tryParseErrorResponse = async (response) => {
62
+ const contentType = response.headers.get("content-type") ?? "";
63
+ if (contentType.includes("application/json")) {
64
+ return await response.json();
65
+ }
66
+ const text = await response.text();
67
+ return text || void 0;
68
+ };
69
+ var parseApiError = async (error) => {
70
+ if (error instanceof import_ky.HTTPError) {
71
+ const responseData = await tryParseErrorResponse(error.response);
72
+ const isObjectResponse = typeof responseData === "object" && responseData !== null;
73
+ let message;
74
+ let backendResponse;
75
+ if (isObjectResponse && "error" in responseData && responseData.error) {
76
+ message = responseData.error;
77
+ backendResponse = responseData;
78
+ } else if (isObjectResponse && "message" in responseData && responseData.message) {
79
+ message = responseData.message;
80
+ } else if (typeof responseData === "string" && responseData) {
81
+ message = responseData;
82
+ } else {
83
+ message = error.message;
84
+ }
85
+ return new ApiError(message, error.response.status, `HTTP_${error.response.status}`, backendResponse);
86
+ }
87
+ if (error instanceof import_ky.TimeoutError) {
88
+ return new ApiError("Request timed out", void 0, "TIMEOUT_ERROR");
89
+ }
90
+ if (error instanceof Error) {
91
+ return new ApiError(error.message, void 0, "NETWORK_ERROR");
92
+ }
93
+ return new ApiError("Unknown request error", void 0, "UNKNOWN_ERROR");
94
+ };
95
+
96
+ // src/api/client.ts
97
+ var import_ky2 = __toESM(require("ky"));
98
+ var import_node_url = require("url");
53
99
 
54
100
  // src/Logger.ts
55
101
  var Logger = class _Logger {
@@ -99,132 +145,114 @@ var Logger = class _Logger {
99
145
  }
100
146
  };
101
147
 
102
- // src/Api.ts
103
- var ApiError = class extends Error {
104
- constructor(message, status, code, response) {
105
- super(message);
106
- this.name = "ApiError";
107
- this.status = status;
108
- this.code = code;
109
- this.response = response;
148
+ // src/api/client.ts
149
+ var resolveUrl = (baseURL, url) => new import_node_url.URL(url, baseURL).toString();
150
+ var parseQueryParams = (params) => {
151
+ if (!params) {
152
+ return void 0;
110
153
  }
111
- };
112
- var Api = class {
113
- constructor(config) {
114
- this.config = config;
115
- this.logger = new Logger("Api");
116
- if (config.logging === false) {
117
- this.logger.disable();
118
- }
119
- this.axios = import_axios.default.create({
120
- baseURL: config.baseURL,
121
- timeout: config.timeout ?? 3e4,
122
- headers: {
123
- "Content-Type": "application/json",
124
- ...config.headers
125
- }
126
- });
127
- this.setupInterceptors();
128
- this.logger.success(`initialized (baseURL: ${config.baseURL})`);
154
+ if (params instanceof import_node_url.URLSearchParams) {
155
+ return params;
129
156
  }
130
- setupInterceptors() {
131
- this.axios.interceptors.request.use(
132
- (config) => {
133
- this.logger.debug(`\u2192 ${config.method?.toUpperCase()} ${config.url}`);
134
- return config;
135
- },
136
- (error) => {
137
- this.logger.error("Request error", error);
138
- return Promise.reject(error);
139
- }
140
- );
141
- this.axios.interceptors.response.use(
142
- (response) => {
143
- this.logger.debug(`\u2190 ${response.status} ${response.config.url}`);
144
- return response;
145
- },
146
- (error) => {
147
- const apiError = this.parseError(error);
148
- const status = apiError.status ?? apiError.code ?? "ERR";
149
- this.logger.error(`\u2190 ${status} ${error.config?.url}: ${apiError.message}`);
150
- return Promise.reject(apiError);
151
- }
152
- );
153
- }
154
- parseError(error) {
155
- if (error.response) {
156
- const responseData = error.response.data;
157
- let message;
158
- let backendResponse;
159
- if (responseData && "error" in responseData && responseData.error) {
160
- message = responseData.error;
161
- backendResponse = responseData;
162
- } else if (responseData && "message" in responseData && responseData.message) {
163
- message = responseData.message;
164
- } else {
165
- message = error.message;
166
- }
167
- return new ApiError(message, error.response.status, error.code, backendResponse);
168
- }
169
- if (error.request) {
170
- return new ApiError("No response received from server", void 0, "NETWORK_ERROR");
157
+ const searchParams = new import_node_url.URLSearchParams();
158
+ for (const [key, value] of Object.entries(params)) {
159
+ if (value === void 0 || value === null) {
160
+ continue;
171
161
  }
172
- return new ApiError(error.message, void 0, error.code);
173
- }
174
- setAuthToken(token) {
175
- if (token) {
176
- this.axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
177
- this.logger.debug("Auth token set");
178
- } else {
179
- delete this.axios.defaults.headers.common["Authorization"];
180
- this.logger.debug("Auth token cleared");
181
- }
182
- }
183
- setHeader(key, value) {
184
- this.axios.defaults.headers.common[key] = value;
162
+ searchParams.set(key, String(value));
185
163
  }
186
- removeHeader(key) {
187
- delete this.axios.defaults.headers.common[key];
188
- }
189
- async get(url, config) {
190
- const response = await this.axios.get(url, config);
191
- return this.wrapResponse(response);
192
- }
193
- async post(url, data, config) {
194
- const response = await this.axios.post(url, data, config);
195
- return this.wrapResponse(response);
196
- }
197
- async put(url, data, config) {
198
- const response = await this.axios.put(url, data, config);
199
- return this.wrapResponse(response);
200
- }
201
- async patch(url, data, config) {
202
- const response = await this.axios.patch(url, data, config);
203
- return this.wrapResponse(response);
204
- }
205
- async delete(url, config) {
206
- const response = await this.axios.delete(url, config);
207
- return this.wrapResponse(response);
208
- }
209
- wrapResponse(response) {
210
- return {
211
- data: response.data,
212
- status: response.status,
213
- headers: response.headers
214
- };
164
+ return searchParams;
165
+ };
166
+ var wrapResponse = async (response) => {
167
+ const contentType = response.headers.get("content-type") ?? "";
168
+ let data;
169
+ if (response.status === 204) {
170
+ data = void 0;
171
+ } else if (contentType.includes("application/json")) {
172
+ data = await response.json();
173
+ } else {
174
+ data = await response.text();
215
175
  }
216
- get axiosInstance() {
217
- return this.axios;
176
+ return {
177
+ data,
178
+ status: response.status,
179
+ headers: Object.fromEntries(response.headers.entries())
180
+ };
181
+ };
182
+ var buildOptions = (defaultHeaders, config) => ({
183
+ headers: {
184
+ ...defaultHeaders,
185
+ ...config?.headers
186
+ },
187
+ searchParams: parseQueryParams(config?.params),
188
+ timeout: config?.timeout,
189
+ credentials: config?.credentials
190
+ });
191
+ var createApi = (config) => {
192
+ const logger = new Logger("Api");
193
+ if (config.logging === false) {
194
+ logger.disable();
218
195
  }
196
+ const defaultHeaders = {
197
+ "Content-Type": "application/json",
198
+ ...config.headers
199
+ };
200
+ const client = import_ky2.default.create({
201
+ timeout: config.timeout ?? 3e4,
202
+ credentials: config.withCredentials ? "include" : "same-origin",
203
+ retry: 0,
204
+ throwHttpErrors: true
205
+ });
206
+ const request = async (method, url, data, requestConfig) => {
207
+ logger.debug(`\u2192 ${method.toUpperCase()} ${url}`);
208
+ try {
209
+ const options = buildOptions(defaultHeaders, requestConfig);
210
+ if (data !== void 0) {
211
+ options.json = data;
212
+ }
213
+ const response = await client(resolveUrl(config.baseURL, url), {
214
+ ...options,
215
+ method
216
+ });
217
+ logger.debug(`\u2190 ${response.status} ${url}`);
218
+ return wrapResponse(response);
219
+ } catch (error) {
220
+ const apiError = await parseApiError(error);
221
+ const status = apiError.status ?? apiError.code ?? "ERR";
222
+ logger.error(`\u2190 ${status} ${url}: ${apiError.message}`);
223
+ throw apiError;
224
+ }
225
+ };
226
+ return {
227
+ setAuthToken: (token) => {
228
+ if (token) {
229
+ defaultHeaders["Authorization"] = `Bearer ${token}`;
230
+ logger.debug("Auth token set");
231
+ } else {
232
+ delete defaultHeaders["Authorization"];
233
+ logger.debug("Auth token cleared");
234
+ }
235
+ },
236
+ setHeader: (key, value) => {
237
+ defaultHeaders[key] = value;
238
+ },
239
+ removeHeader: (key) => {
240
+ delete defaultHeaders[key];
241
+ },
242
+ get: (url, requestConfig) => request("get", url, void 0, requestConfig),
243
+ post: (url, data, requestConfig) => request("post", url, data, requestConfig),
244
+ put: (url, data, requestConfig) => request("put", url, data, requestConfig),
245
+ patch: (url, data, requestConfig) => request("patch", url, data, requestConfig),
246
+ delete: (url, requestConfig) => request("delete", url, void 0, requestConfig)
247
+ };
219
248
  };
220
- var createApi = (config) => new Api(config);
221
249
 
222
250
  // src/resources/auth.ts
223
251
  var endpoint = "/auth";
224
252
  var createAuthResource = (api) => ({
225
253
  login: (credentials) => api.post(`${endpoint}/login`, credentials),
226
254
  register: (data) => api.post(`${endpoint}/register`, data),
227
- refresh: (refreshToken) => api.post(`${endpoint}/refresh`, { refreshToken }),
255
+ refresh: (refreshToken) => api.post(`${endpoint}/refresh`, refreshToken ? { refreshToken } : {}),
228
256
  logout: () => api.post(`${endpoint}/logout`),
229
257
  me: () => api.get(`${endpoint}/me`),
230
258
  forgotPassword: (email) => api.post(`${endpoint}/forgot-password`, { email }),
@@ -247,6 +275,7 @@ var createUserResource = (api) => ({
247
275
  var endpoint3 = "/rooms";
248
276
  var createRoomResource = (api) => ({
249
277
  list: () => api.get(endpoint3),
278
+ mine: (limit = 20) => api.get(`${endpoint3}/mine`, { params: { limit } }),
250
279
  featured: () => api.get(`${endpoint3}/featured`),
251
280
  getBySlug: (slug) => api.get(`${endpoint3}/${slug}`),
252
281
  create: (data) => api.post(endpoint3, data),
@@ -299,6 +328,7 @@ var createChatResource = (api) => ({
299
328
  }
300
329
  return api.get(`${endpoint4}/${slug}/chat`, { params });
301
330
  },
331
+ editMessage: (slug, messageId, data) => api.patch(`${endpoint4}/${slug}/chat/${messageId}`, data),
302
332
  deleteMessage: (slug, messageId) => api.delete(`${endpoint4}/${slug}/chat/${messageId}`)
303
333
  });
304
334
 
@@ -365,7 +395,17 @@ var createSubscriptionResource = (api) => ({
365
395
  getStatus: () => api.get(`${endpoint8}/status`),
366
396
  createIntent: (plan) => api.post(`${endpoint8}/create-intent`, { plan }),
367
397
  cancelIntent: (subscriptionId) => api.post(`${endpoint8}/cancel-intent`, { subscriptionId }),
368
- createPortal: () => api.post(`${endpoint8}/portal`)
398
+ createPortal: () => api.post(`${endpoint8}/portal`),
399
+ createGiftCheckout: (recipientUsername, quantity, isAnonymous = false) => api.post(`${endpoint8}/create-gift-checkout`, {
400
+ recipientUsername,
401
+ quantity,
402
+ isAnonymous
403
+ }),
404
+ getGiftsHistory: () => api.get(`${endpoint8}/gifts-history`),
405
+ retryGiftAssignment: (purchaseId, recipientUsername) => api.post(`${endpoint8}/retry-gift-assignment`, {
406
+ purchaseId,
407
+ recipientUsername
408
+ })
369
409
  });
370
410
 
371
411
  // src/resources/friend.ts
@@ -425,7 +465,6 @@ var createApiClient = (config) => {
425
465
  };
426
466
  // Annotate the CommonJS export names for ESM import in node:
427
467
  0 && (module.exports = {
428
- Api,
429
468
  ApiError,
430
469
  Logger,
431
470
  createAdminResource,
package/dist/index.mjs CHANGED
@@ -1,5 +1,52 @@
1
- // src/Api.ts
2
- import axios from "axios";
1
+ // src/api/errors.ts
2
+ import { HTTPError, TimeoutError } from "ky";
3
+ var ApiError = class extends Error {
4
+ constructor(message, status, code, response) {
5
+ super(message);
6
+ this.name = "ApiError";
7
+ this.status = status;
8
+ this.code = code;
9
+ this.response = response;
10
+ }
11
+ };
12
+ var tryParseErrorResponse = async (response) => {
13
+ const contentType = response.headers.get("content-type") ?? "";
14
+ if (contentType.includes("application/json")) {
15
+ return await response.json();
16
+ }
17
+ const text = await response.text();
18
+ return text || void 0;
19
+ };
20
+ var parseApiError = async (error) => {
21
+ if (error instanceof HTTPError) {
22
+ const responseData = await tryParseErrorResponse(error.response);
23
+ const isObjectResponse = typeof responseData === "object" && responseData !== null;
24
+ let message;
25
+ let backendResponse;
26
+ if (isObjectResponse && "error" in responseData && responseData.error) {
27
+ message = responseData.error;
28
+ backendResponse = responseData;
29
+ } else if (isObjectResponse && "message" in responseData && responseData.message) {
30
+ message = responseData.message;
31
+ } else if (typeof responseData === "string" && responseData) {
32
+ message = responseData;
33
+ } else {
34
+ message = error.message;
35
+ }
36
+ return new ApiError(message, error.response.status, `HTTP_${error.response.status}`, backendResponse);
37
+ }
38
+ if (error instanceof TimeoutError) {
39
+ return new ApiError("Request timed out", void 0, "TIMEOUT_ERROR");
40
+ }
41
+ if (error instanceof Error) {
42
+ return new ApiError(error.message, void 0, "NETWORK_ERROR");
43
+ }
44
+ return new ApiError("Unknown request error", void 0, "UNKNOWN_ERROR");
45
+ };
46
+
47
+ // src/api/client.ts
48
+ import ky from "ky";
49
+ import { URL, URLSearchParams as URLSearchParams2 } from "url";
3
50
 
4
51
  // src/Logger.ts
5
52
  var Logger = class _Logger {
@@ -49,132 +96,114 @@ var Logger = class _Logger {
49
96
  }
50
97
  };
51
98
 
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;
99
+ // src/api/client.ts
100
+ var resolveUrl = (baseURL, url) => new URL(url, baseURL).toString();
101
+ var parseQueryParams = (params) => {
102
+ if (!params) {
103
+ return void 0;
60
104
  }
61
- };
62
- var Api = class {
63
- constructor(config) {
64
- this.config = config;
65
- this.logger = new Logger("Api");
66
- if (config.logging === false) {
67
- this.logger.disable();
68
- }
69
- this.axios = axios.create({
70
- baseURL: config.baseURL,
71
- timeout: config.timeout ?? 3e4,
72
- headers: {
73
- "Content-Type": "application/json",
74
- ...config.headers
75
- }
76
- });
77
- this.setupInterceptors();
78
- this.logger.success(`initialized (baseURL: ${config.baseURL})`);
105
+ if (params instanceof URLSearchParams2) {
106
+ return params;
79
107
  }
80
- setupInterceptors() {
81
- this.axios.interceptors.request.use(
82
- (config) => {
83
- this.logger.debug(`\u2192 ${config.method?.toUpperCase()} ${config.url}`);
84
- return config;
85
- },
86
- (error) => {
87
- this.logger.error("Request error", error);
88
- return Promise.reject(error);
89
- }
90
- );
91
- this.axios.interceptors.response.use(
92
- (response) => {
93
- this.logger.debug(`\u2190 ${response.status} ${response.config.url}`);
94
- return response;
95
- },
96
- (error) => {
97
- const apiError = this.parseError(error);
98
- const status = apiError.status ?? apiError.code ?? "ERR";
99
- this.logger.error(`\u2190 ${status} ${error.config?.url}: ${apiError.message}`);
100
- return Promise.reject(apiError);
101
- }
102
- );
103
- }
104
- parseError(error) {
105
- if (error.response) {
106
- const responseData = error.response.data;
107
- let message;
108
- let backendResponse;
109
- if (responseData && "error" in responseData && responseData.error) {
110
- message = responseData.error;
111
- backendResponse = responseData;
112
- } else if (responseData && "message" in responseData && responseData.message) {
113
- message = responseData.message;
114
- } else {
115
- message = error.message;
116
- }
117
- return new ApiError(message, error.response.status, error.code, backendResponse);
118
- }
119
- if (error.request) {
120
- return new ApiError("No response received from server", void 0, "NETWORK_ERROR");
108
+ const searchParams = new URLSearchParams2();
109
+ for (const [key, value] of Object.entries(params)) {
110
+ if (value === void 0 || value === null) {
111
+ continue;
121
112
  }
122
- return new ApiError(error.message, void 0, error.code);
123
- }
124
- setAuthToken(token) {
125
- if (token) {
126
- this.axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
127
- this.logger.debug("Auth token set");
128
- } else {
129
- delete this.axios.defaults.headers.common["Authorization"];
130
- this.logger.debug("Auth token cleared");
131
- }
132
- }
133
- setHeader(key, value) {
134
- this.axios.defaults.headers.common[key] = value;
113
+ searchParams.set(key, String(value));
135
114
  }
136
- removeHeader(key) {
137
- delete this.axios.defaults.headers.common[key];
138
- }
139
- async get(url, config) {
140
- const response = await this.axios.get(url, config);
141
- return this.wrapResponse(response);
142
- }
143
- async post(url, data, config) {
144
- const response = await this.axios.post(url, data, config);
145
- return this.wrapResponse(response);
146
- }
147
- async put(url, data, config) {
148
- const response = await this.axios.put(url, data, config);
149
- return this.wrapResponse(response);
150
- }
151
- async patch(url, data, config) {
152
- const response = await this.axios.patch(url, data, config);
153
- return this.wrapResponse(response);
154
- }
155
- async delete(url, config) {
156
- const response = await this.axios.delete(url, config);
157
- return this.wrapResponse(response);
158
- }
159
- wrapResponse(response) {
160
- return {
161
- data: response.data,
162
- status: response.status,
163
- headers: response.headers
164
- };
115
+ return searchParams;
116
+ };
117
+ var wrapResponse = async (response) => {
118
+ const contentType = response.headers.get("content-type") ?? "";
119
+ let data;
120
+ if (response.status === 204) {
121
+ data = void 0;
122
+ } else if (contentType.includes("application/json")) {
123
+ data = await response.json();
124
+ } else {
125
+ data = await response.text();
165
126
  }
166
- get axiosInstance() {
167
- return this.axios;
127
+ return {
128
+ data,
129
+ status: response.status,
130
+ headers: Object.fromEntries(response.headers.entries())
131
+ };
132
+ };
133
+ var buildOptions = (defaultHeaders, config) => ({
134
+ headers: {
135
+ ...defaultHeaders,
136
+ ...config?.headers
137
+ },
138
+ searchParams: parseQueryParams(config?.params),
139
+ timeout: config?.timeout,
140
+ credentials: config?.credentials
141
+ });
142
+ var createApi = (config) => {
143
+ const logger = new Logger("Api");
144
+ if (config.logging === false) {
145
+ logger.disable();
168
146
  }
147
+ const defaultHeaders = {
148
+ "Content-Type": "application/json",
149
+ ...config.headers
150
+ };
151
+ const client = ky.create({
152
+ timeout: config.timeout ?? 3e4,
153
+ credentials: config.withCredentials ? "include" : "same-origin",
154
+ retry: 0,
155
+ throwHttpErrors: true
156
+ });
157
+ const request = async (method, url, data, requestConfig) => {
158
+ logger.debug(`\u2192 ${method.toUpperCase()} ${url}`);
159
+ try {
160
+ const options = buildOptions(defaultHeaders, requestConfig);
161
+ if (data !== void 0) {
162
+ options.json = data;
163
+ }
164
+ const response = await client(resolveUrl(config.baseURL, url), {
165
+ ...options,
166
+ method
167
+ });
168
+ logger.debug(`\u2190 ${response.status} ${url}`);
169
+ return wrapResponse(response);
170
+ } catch (error) {
171
+ const apiError = await parseApiError(error);
172
+ const status = apiError.status ?? apiError.code ?? "ERR";
173
+ logger.error(`\u2190 ${status} ${url}: ${apiError.message}`);
174
+ throw apiError;
175
+ }
176
+ };
177
+ return {
178
+ setAuthToken: (token) => {
179
+ if (token) {
180
+ defaultHeaders["Authorization"] = `Bearer ${token}`;
181
+ logger.debug("Auth token set");
182
+ } else {
183
+ delete defaultHeaders["Authorization"];
184
+ logger.debug("Auth token cleared");
185
+ }
186
+ },
187
+ setHeader: (key, value) => {
188
+ defaultHeaders[key] = value;
189
+ },
190
+ removeHeader: (key) => {
191
+ delete defaultHeaders[key];
192
+ },
193
+ get: (url, requestConfig) => request("get", url, void 0, requestConfig),
194
+ post: (url, data, requestConfig) => request("post", url, data, requestConfig),
195
+ put: (url, data, requestConfig) => request("put", url, data, requestConfig),
196
+ patch: (url, data, requestConfig) => request("patch", url, data, requestConfig),
197
+ delete: (url, requestConfig) => request("delete", url, void 0, requestConfig)
198
+ };
169
199
  };
170
- var createApi = (config) => new Api(config);
171
200
 
172
201
  // src/resources/auth.ts
173
202
  var endpoint = "/auth";
174
203
  var createAuthResource = (api) => ({
175
204
  login: (credentials) => api.post(`${endpoint}/login`, credentials),
176
205
  register: (data) => api.post(`${endpoint}/register`, data),
177
- refresh: (refreshToken) => api.post(`${endpoint}/refresh`, { refreshToken }),
206
+ refresh: (refreshToken) => api.post(`${endpoint}/refresh`, refreshToken ? { refreshToken } : {}),
178
207
  logout: () => api.post(`${endpoint}/logout`),
179
208
  me: () => api.get(`${endpoint}/me`),
180
209
  forgotPassword: (email) => api.post(`${endpoint}/forgot-password`, { email }),
@@ -197,6 +226,7 @@ var createUserResource = (api) => ({
197
226
  var endpoint3 = "/rooms";
198
227
  var createRoomResource = (api) => ({
199
228
  list: () => api.get(endpoint3),
229
+ mine: (limit = 20) => api.get(`${endpoint3}/mine`, { params: { limit } }),
200
230
  featured: () => api.get(`${endpoint3}/featured`),
201
231
  getBySlug: (slug) => api.get(`${endpoint3}/${slug}`),
202
232
  create: (data) => api.post(endpoint3, data),
@@ -249,6 +279,7 @@ var createChatResource = (api) => ({
249
279
  }
250
280
  return api.get(`${endpoint4}/${slug}/chat`, { params });
251
281
  },
282
+ editMessage: (slug, messageId, data) => api.patch(`${endpoint4}/${slug}/chat/${messageId}`, data),
252
283
  deleteMessage: (slug, messageId) => api.delete(`${endpoint4}/${slug}/chat/${messageId}`)
253
284
  });
254
285
 
@@ -315,7 +346,17 @@ var createSubscriptionResource = (api) => ({
315
346
  getStatus: () => api.get(`${endpoint8}/status`),
316
347
  createIntent: (plan) => api.post(`${endpoint8}/create-intent`, { plan }),
317
348
  cancelIntent: (subscriptionId) => api.post(`${endpoint8}/cancel-intent`, { subscriptionId }),
318
- createPortal: () => api.post(`${endpoint8}/portal`)
349
+ createPortal: () => api.post(`${endpoint8}/portal`),
350
+ createGiftCheckout: (recipientUsername, quantity, isAnonymous = false) => api.post(`${endpoint8}/create-gift-checkout`, {
351
+ recipientUsername,
352
+ quantity,
353
+ isAnonymous
354
+ }),
355
+ getGiftsHistory: () => api.get(`${endpoint8}/gifts-history`),
356
+ retryGiftAssignment: (purchaseId, recipientUsername) => api.post(`${endpoint8}/retry-gift-assignment`, {
357
+ purchaseId,
358
+ recipientUsername
359
+ })
319
360
  });
320
361
 
321
362
  // src/resources/friend.ts
@@ -374,7 +415,6 @@ var createApiClient = (config) => {
374
415
  };
375
416
  };
376
417
  export {
377
- Api,
378
418
  ApiError,
379
419
  Logger,
380
420
  createAdminResource,
package/package.json CHANGED
@@ -1,51 +1,50 @@
1
- {
2
- "name": "@borealise/api",
3
- "version": "2.0.0-alpha.9",
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-Platform/api"
38
- },
39
- "bugs": {
40
- "url": "https://github.com/Borealise-Platform/api/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
- }
1
+ {
2
+ "name": "@borealise/api",
3
+ "version": "2.1.0-alpha.1",
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-Platform/api"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/Borealise-Platform/api/issues"
41
+ },
42
+ "dependencies": {
43
+ "ky": "^1.7.5"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^20.0.0",
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^5.0.0"
49
+ }
50
+ }