@noverachat/sdk-web 0.1.0 → 0.2.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.cjs CHANGED
@@ -206,6 +206,30 @@ function parseBulkDeleteBySenderResult(j) {
206
206
  cleared: j["cleared"]
207
207
  };
208
208
  }
209
+ function parseJoinRequestOut(j) {
210
+ return {
211
+ userId: j["user_id"],
212
+ status: j["status"],
213
+ reason: j["reason"],
214
+ requestedAt: j["requested_at"],
215
+ processedAt: j["processed_at"],
216
+ processedByUserId: j["processed_by_user_id"]
217
+ };
218
+ }
219
+ function parseJoinRequestListResponse(j) {
220
+ return {
221
+ items: j["items"].map((e) => parseJoinRequestOut(e)),
222
+ total: j["total"],
223
+ limit: j["limit"],
224
+ offset: j["offset"]
225
+ };
226
+ }
227
+ function parseJoinRequestCreateResponse(j) {
228
+ return {
229
+ status: j["status"],
230
+ roomId: j["room_id"]
231
+ };
232
+ }
209
233
  function parseSenderMini(j) {
210
234
  return {
211
235
  userId: j["user_id"],
@@ -1246,6 +1270,81 @@ var Room = class _Room {
1246
1270
  `/api/v1/rooms/${this.roomId}/invites/${encodeURIComponent(token)}`
1247
1271
  );
1248
1272
  }
1273
+ // -------- join requests --------
1274
+ /**
1275
+ * Ask to join this room (self). Behavior depends on the room's
1276
+ * `joinPolicy`:
1277
+ * - `OPEN` → the caller is added immediately (`status: "joined"`);
1278
+ * - `APPROVAL_REQUIRED` → the request lands in the operators' inbox
1279
+ * (`status: "pending"`) until [`approveJoinRequest`] / reject.
1280
+ * Idempotent for members (`status: "already_member"`).
1281
+ *
1282
+ * @param reason Optional note shown to operators ("하은이 반 친구 엄마예요").
1283
+ */
1284
+ async requestJoin(reason) {
1285
+ const raw = await this.http.request(
1286
+ "POST",
1287
+ `/api/v1/rooms/${this.roomId}/join-request`,
1288
+ reason ? { reason } : {}
1289
+ );
1290
+ return parseJoinRequestCreateResponse(raw);
1291
+ }
1292
+ /** Cancel the caller's own pending join request. */
1293
+ async cancelJoinRequest() {
1294
+ await this.http.request(
1295
+ "DELETE",
1296
+ `/api/v1/rooms/${this.roomId}/join-request`
1297
+ );
1298
+ }
1299
+ /** OPERATOR-only: list join requests for this room (the approval inbox).
1300
+ * Defaults to pending only — pass a status to see processed history. */
1301
+ async listJoinRequests(opts) {
1302
+ const raw = await this.http.request(
1303
+ "GET",
1304
+ `/api/v1/rooms/${this.roomId}/join-requests`,
1305
+ void 0,
1306
+ {
1307
+ ...opts?.status ? { status: opts.status } : {},
1308
+ ...opts?.limit !== void 0 ? { limit: opts.limit } : {},
1309
+ ...opts?.offset !== void 0 ? { offset: opts.offset } : {}
1310
+ }
1311
+ );
1312
+ return parseJoinRequestListResponse(raw);
1313
+ }
1314
+ /** OPERATOR-only: approve a pending join request — the requester becomes
1315
+ * a MEMBER. [reason] is echoed back to them in later list calls. */
1316
+ async approveJoinRequest(userId, reason) {
1317
+ await this.http.request(
1318
+ "POST",
1319
+ `/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/approve`,
1320
+ reason ? { reason } : {}
1321
+ );
1322
+ }
1323
+ /** OPERATOR-only: reject a pending join request. */
1324
+ async rejectJoinRequest(userId, reason) {
1325
+ await this.http.request(
1326
+ "POST",
1327
+ `/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/reject`,
1328
+ reason ? { reason } : {}
1329
+ );
1330
+ }
1331
+ // -------- hide / unhide --------
1332
+ /** Hide this room from the caller's own room list (self, reversible —
1333
+ * the KakaoTalk "숨기기"). The room typically reappears via [unhide]
1334
+ * or on new activity, per server policy. Messages are untouched. */
1335
+ async hide() {
1336
+ await this.http.request(
1337
+ "POST",
1338
+ `/api/v1/rooms/${this.roomId}/hide`
1339
+ );
1340
+ }
1341
+ /** Undo [hide] — the room shows up in the caller's list again. */
1342
+ async unhide() {
1343
+ await this.http.request(
1344
+ "POST",
1345
+ `/api/v1/rooms/${this.roomId}/unhide`
1346
+ );
1347
+ }
1249
1348
  // -------- internal dispatch --------
1250
1349
  on(event, fn) {
1251
1350
  return this.bus.on(event, fn);
package/dist/index.d.cts CHANGED
@@ -320,6 +320,25 @@ interface RoomMemberOut {
320
320
  isOnline?: boolean | null;
321
321
  mutedUntil?: string | null;
322
322
  }
323
+ interface JoinRequestOut {
324
+ userId: string;
325
+ status: "PENDING" | "APPROVED" | "REJECTED" | "CANCELLED";
326
+ reason: string | null;
327
+ requestedAt: string;
328
+ processedAt: string | null;
329
+ processedByUserId: string | null;
330
+ }
331
+ interface JoinRequestListResponse {
332
+ items: JoinRequestOut[];
333
+ total: number;
334
+ limit: number;
335
+ offset: number;
336
+ }
337
+ /** Synchronous response after `POST /join-request`. `status="joined"` — OPEN room, user was immediately added. `status="pending"` — APPROVAL_REQUIRED, request is pending. `status="already_member"` — caller is already an active member; no-op. */
338
+ interface JoinRequestCreateResponse {
339
+ status: "joined" | "pending" | "already_member";
340
+ roomId: string;
341
+ }
323
342
  /** Minimal sender identity — no friend/block/profile-url fluff. */
324
343
  interface SenderMini {
325
344
  userId: string;
@@ -875,6 +894,37 @@ declare class Room {
875
894
  /** OPERATOR-only: revoke a token immediately. Idempotent — revoking
876
895
  * an already-revoked token returns 204 as well. */
877
896
  revokeInvite(token: string): Promise<void>;
897
+ /**
898
+ * Ask to join this room (self). Behavior depends on the room's
899
+ * `joinPolicy`:
900
+ * - `OPEN` → the caller is added immediately (`status: "joined"`);
901
+ * - `APPROVAL_REQUIRED` → the request lands in the operators' inbox
902
+ * (`status: "pending"`) until [`approveJoinRequest`] / reject.
903
+ * Idempotent for members (`status: "already_member"`).
904
+ *
905
+ * @param reason Optional note shown to operators ("하은이 반 친구 엄마예요").
906
+ */
907
+ requestJoin(reason?: string): Promise<JoinRequestCreateResponse>;
908
+ /** Cancel the caller's own pending join request. */
909
+ cancelJoinRequest(): Promise<void>;
910
+ /** OPERATOR-only: list join requests for this room (the approval inbox).
911
+ * Defaults to pending only — pass a status to see processed history. */
912
+ listJoinRequests(opts?: {
913
+ status?: "PENDING" | "APPROVED" | "REJECTED" | "CANCELLED";
914
+ limit?: number;
915
+ offset?: number;
916
+ }): Promise<JoinRequestListResponse>;
917
+ /** OPERATOR-only: approve a pending join request — the requester becomes
918
+ * a MEMBER. [reason] is echoed back to them in later list calls. */
919
+ approveJoinRequest(userId: string, reason?: string): Promise<void>;
920
+ /** OPERATOR-only: reject a pending join request. */
921
+ rejectJoinRequest(userId: string, reason?: string): Promise<void>;
922
+ /** Hide this room from the caller's own room list (self, reversible —
923
+ * the KakaoTalk "숨기기"). The room typically reappears via [unhide]
924
+ * or on new activity, per server policy. Messages are untouched. */
925
+ hide(): Promise<void>;
926
+ /** Undo [hide] — the room shows up in the caller's list again. */
927
+ unhide(): Promise<void>;
878
928
  on<K extends keyof RoomEvents>(event: K, fn: (p: RoomEvents[K]) => void): () => void;
879
929
  off<K extends keyof RoomEvents>(event: K, fn: (p: RoomEvents[K]) => void): void;
880
930
  _dispatchAnnouncement(msg: WsAnnouncementEvent): void;
@@ -1141,4 +1191,4 @@ declare class NoveraChatError extends Error {
1141
1191
  static fromResponse(status: number, body: unknown): NoveraChatError;
1142
1192
  }
1143
1193
 
1144
- export { type InviteAcceptResponse as AcceptInviteResult, type AnnouncementOut as Announcement, type AppPolicy, type BlockOut as Block, type ClientOptions, type DeleteScope, ErrorCode, type ErrorCodeValue, type InviteTokenOut as InviteToken, type MessageOut, type MessageType, NoveraChat, NoveraChatError, type ReactionEntry, Room, type RoomEvents, type RoomMemberOut, type RoomUnread, type SendParams, type SenderMini, type UnreadRoomItem, type UnreadSummary, type WsAnnouncementEvent, type WsInbound, type WsOutbound };
1194
+ export { type InviteAcceptResponse as AcceptInviteResult, type AnnouncementOut as Announcement, type AppPolicy, type BlockOut as Block, type ClientOptions, type DeleteScope, ErrorCode, type ErrorCodeValue, type InviteTokenOut as InviteToken, type JoinRequestCreateResponse, type JoinRequestListResponse, type JoinRequestOut, type MessageOut, type MessageType, NoveraChat, NoveraChatError, type ReactionEntry, Room, type RoomEvents, type RoomMemberOut, type RoomUnread, type SendParams, type SenderMini, type UnreadRoomItem, type UnreadSummary, type WsAnnouncementEvent, type WsInbound, type WsOutbound };
package/dist/index.d.ts CHANGED
@@ -320,6 +320,25 @@ interface RoomMemberOut {
320
320
  isOnline?: boolean | null;
321
321
  mutedUntil?: string | null;
322
322
  }
323
+ interface JoinRequestOut {
324
+ userId: string;
325
+ status: "PENDING" | "APPROVED" | "REJECTED" | "CANCELLED";
326
+ reason: string | null;
327
+ requestedAt: string;
328
+ processedAt: string | null;
329
+ processedByUserId: string | null;
330
+ }
331
+ interface JoinRequestListResponse {
332
+ items: JoinRequestOut[];
333
+ total: number;
334
+ limit: number;
335
+ offset: number;
336
+ }
337
+ /** Synchronous response after `POST /join-request`. `status="joined"` — OPEN room, user was immediately added. `status="pending"` — APPROVAL_REQUIRED, request is pending. `status="already_member"` — caller is already an active member; no-op. */
338
+ interface JoinRequestCreateResponse {
339
+ status: "joined" | "pending" | "already_member";
340
+ roomId: string;
341
+ }
323
342
  /** Minimal sender identity — no friend/block/profile-url fluff. */
324
343
  interface SenderMini {
325
344
  userId: string;
@@ -875,6 +894,37 @@ declare class Room {
875
894
  /** OPERATOR-only: revoke a token immediately. Idempotent — revoking
876
895
  * an already-revoked token returns 204 as well. */
877
896
  revokeInvite(token: string): Promise<void>;
897
+ /**
898
+ * Ask to join this room (self). Behavior depends on the room's
899
+ * `joinPolicy`:
900
+ * - `OPEN` → the caller is added immediately (`status: "joined"`);
901
+ * - `APPROVAL_REQUIRED` → the request lands in the operators' inbox
902
+ * (`status: "pending"`) until [`approveJoinRequest`] / reject.
903
+ * Idempotent for members (`status: "already_member"`).
904
+ *
905
+ * @param reason Optional note shown to operators ("하은이 반 친구 엄마예요").
906
+ */
907
+ requestJoin(reason?: string): Promise<JoinRequestCreateResponse>;
908
+ /** Cancel the caller's own pending join request. */
909
+ cancelJoinRequest(): Promise<void>;
910
+ /** OPERATOR-only: list join requests for this room (the approval inbox).
911
+ * Defaults to pending only — pass a status to see processed history. */
912
+ listJoinRequests(opts?: {
913
+ status?: "PENDING" | "APPROVED" | "REJECTED" | "CANCELLED";
914
+ limit?: number;
915
+ offset?: number;
916
+ }): Promise<JoinRequestListResponse>;
917
+ /** OPERATOR-only: approve a pending join request — the requester becomes
918
+ * a MEMBER. [reason] is echoed back to them in later list calls. */
919
+ approveJoinRequest(userId: string, reason?: string): Promise<void>;
920
+ /** OPERATOR-only: reject a pending join request. */
921
+ rejectJoinRequest(userId: string, reason?: string): Promise<void>;
922
+ /** Hide this room from the caller's own room list (self, reversible —
923
+ * the KakaoTalk "숨기기"). The room typically reappears via [unhide]
924
+ * or on new activity, per server policy. Messages are untouched. */
925
+ hide(): Promise<void>;
926
+ /** Undo [hide] — the room shows up in the caller's list again. */
927
+ unhide(): Promise<void>;
878
928
  on<K extends keyof RoomEvents>(event: K, fn: (p: RoomEvents[K]) => void): () => void;
879
929
  off<K extends keyof RoomEvents>(event: K, fn: (p: RoomEvents[K]) => void): void;
880
930
  _dispatchAnnouncement(msg: WsAnnouncementEvent): void;
@@ -1141,4 +1191,4 @@ declare class NoveraChatError extends Error {
1141
1191
  static fromResponse(status: number, body: unknown): NoveraChatError;
1142
1192
  }
1143
1193
 
1144
- export { type InviteAcceptResponse as AcceptInviteResult, type AnnouncementOut as Announcement, type AppPolicy, type BlockOut as Block, type ClientOptions, type DeleteScope, ErrorCode, type ErrorCodeValue, type InviteTokenOut as InviteToken, type MessageOut, type MessageType, NoveraChat, NoveraChatError, type ReactionEntry, Room, type RoomEvents, type RoomMemberOut, type RoomUnread, type SendParams, type SenderMini, type UnreadRoomItem, type UnreadSummary, type WsAnnouncementEvent, type WsInbound, type WsOutbound };
1194
+ export { type InviteAcceptResponse as AcceptInviteResult, type AnnouncementOut as Announcement, type AppPolicy, type BlockOut as Block, type ClientOptions, type DeleteScope, ErrorCode, type ErrorCodeValue, type InviteTokenOut as InviteToken, type JoinRequestCreateResponse, type JoinRequestListResponse, type JoinRequestOut, type MessageOut, type MessageType, NoveraChat, NoveraChatError, type ReactionEntry, Room, type RoomEvents, type RoomMemberOut, type RoomUnread, type SendParams, type SenderMini, type UnreadRoomItem, type UnreadSummary, type WsAnnouncementEvent, type WsInbound, type WsOutbound };
package/dist/index.js CHANGED
@@ -177,6 +177,30 @@ function parseBulkDeleteBySenderResult(j) {
177
177
  cleared: j["cleared"]
178
178
  };
179
179
  }
180
+ function parseJoinRequestOut(j) {
181
+ return {
182
+ userId: j["user_id"],
183
+ status: j["status"],
184
+ reason: j["reason"],
185
+ requestedAt: j["requested_at"],
186
+ processedAt: j["processed_at"],
187
+ processedByUserId: j["processed_by_user_id"]
188
+ };
189
+ }
190
+ function parseJoinRequestListResponse(j) {
191
+ return {
192
+ items: j["items"].map((e) => parseJoinRequestOut(e)),
193
+ total: j["total"],
194
+ limit: j["limit"],
195
+ offset: j["offset"]
196
+ };
197
+ }
198
+ function parseJoinRequestCreateResponse(j) {
199
+ return {
200
+ status: j["status"],
201
+ roomId: j["room_id"]
202
+ };
203
+ }
180
204
  function parseSenderMini(j) {
181
205
  return {
182
206
  userId: j["user_id"],
@@ -1217,6 +1241,81 @@ var Room = class _Room {
1217
1241
  `/api/v1/rooms/${this.roomId}/invites/${encodeURIComponent(token)}`
1218
1242
  );
1219
1243
  }
1244
+ // -------- join requests --------
1245
+ /**
1246
+ * Ask to join this room (self). Behavior depends on the room's
1247
+ * `joinPolicy`:
1248
+ * - `OPEN` → the caller is added immediately (`status: "joined"`);
1249
+ * - `APPROVAL_REQUIRED` → the request lands in the operators' inbox
1250
+ * (`status: "pending"`) until [`approveJoinRequest`] / reject.
1251
+ * Idempotent for members (`status: "already_member"`).
1252
+ *
1253
+ * @param reason Optional note shown to operators ("하은이 반 친구 엄마예요").
1254
+ */
1255
+ async requestJoin(reason) {
1256
+ const raw = await this.http.request(
1257
+ "POST",
1258
+ `/api/v1/rooms/${this.roomId}/join-request`,
1259
+ reason ? { reason } : {}
1260
+ );
1261
+ return parseJoinRequestCreateResponse(raw);
1262
+ }
1263
+ /** Cancel the caller's own pending join request. */
1264
+ async cancelJoinRequest() {
1265
+ await this.http.request(
1266
+ "DELETE",
1267
+ `/api/v1/rooms/${this.roomId}/join-request`
1268
+ );
1269
+ }
1270
+ /** OPERATOR-only: list join requests for this room (the approval inbox).
1271
+ * Defaults to pending only — pass a status to see processed history. */
1272
+ async listJoinRequests(opts) {
1273
+ const raw = await this.http.request(
1274
+ "GET",
1275
+ `/api/v1/rooms/${this.roomId}/join-requests`,
1276
+ void 0,
1277
+ {
1278
+ ...opts?.status ? { status: opts.status } : {},
1279
+ ...opts?.limit !== void 0 ? { limit: opts.limit } : {},
1280
+ ...opts?.offset !== void 0 ? { offset: opts.offset } : {}
1281
+ }
1282
+ );
1283
+ return parseJoinRequestListResponse(raw);
1284
+ }
1285
+ /** OPERATOR-only: approve a pending join request — the requester becomes
1286
+ * a MEMBER. [reason] is echoed back to them in later list calls. */
1287
+ async approveJoinRequest(userId, reason) {
1288
+ await this.http.request(
1289
+ "POST",
1290
+ `/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/approve`,
1291
+ reason ? { reason } : {}
1292
+ );
1293
+ }
1294
+ /** OPERATOR-only: reject a pending join request. */
1295
+ async rejectJoinRequest(userId, reason) {
1296
+ await this.http.request(
1297
+ "POST",
1298
+ `/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/reject`,
1299
+ reason ? { reason } : {}
1300
+ );
1301
+ }
1302
+ // -------- hide / unhide --------
1303
+ /** Hide this room from the caller's own room list (self, reversible —
1304
+ * the KakaoTalk "숨기기"). The room typically reappears via [unhide]
1305
+ * or on new activity, per server policy. Messages are untouched. */
1306
+ async hide() {
1307
+ await this.http.request(
1308
+ "POST",
1309
+ `/api/v1/rooms/${this.roomId}/hide`
1310
+ );
1311
+ }
1312
+ /** Undo [hide] — the room shows up in the caller's list again. */
1313
+ async unhide() {
1314
+ await this.http.request(
1315
+ "POST",
1316
+ `/api/v1/rooms/${this.roomId}/unhide`
1317
+ );
1318
+ }
1220
1319
  // -------- internal dispatch --------
1221
1320
  on(event, fn) {
1222
1321
  return this.bus.on(event, fn);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noverachat/sdk-web",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "NoveraChat browser/Node SDK — WebSocket + REST client with optimistic UI, reconnection and gap-fill",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -42,10 +42,16 @@ import {
42
42
  parseFileCreateResponse,
43
43
  parseFileOut,
44
44
  parseInviteTokenOut,
45
+ parseJoinRequestCreateResponse,
46
+ parseJoinRequestListResponse,
45
47
  parseMessageOut,
46
48
  parseRoomMemberOut,
47
49
  parseRoomUnread,
48
50
  } from "../generated/rest.js";
51
+ import type {
52
+ JoinRequestCreateResponse,
53
+ JoinRequestListResponse,
54
+ } from "../generated/rest.js";
49
55
  // Note: previously used `utils/debounce`, replaced with a hand-rolled
50
56
  // timer in Room so we can also expose `flushMarkRead()` — the util has
51
57
  // no flush primitive.
@@ -1010,6 +1016,87 @@ export class Room {
1010
1016
  );
1011
1017
  }
1012
1018
 
1019
+ // -------- join requests --------
1020
+
1021
+ /**
1022
+ * Ask to join this room (self). Behavior depends on the room's
1023
+ * `joinPolicy`:
1024
+ * - `OPEN` → the caller is added immediately (`status: "joined"`);
1025
+ * - `APPROVAL_REQUIRED` → the request lands in the operators' inbox
1026
+ * (`status: "pending"`) until [`approveJoinRequest`] / reject.
1027
+ * Idempotent for members (`status: "already_member"`).
1028
+ *
1029
+ * @param reason Optional note shown to operators ("하은이 반 친구 엄마예요").
1030
+ */
1031
+ async requestJoin(reason?: string): Promise<JoinRequestCreateResponse> {
1032
+ const raw = await this.http.request<unknown>(
1033
+ "POST", `/api/v1/rooms/${this.roomId}/join-request`,
1034
+ reason ? { reason } : {},
1035
+ );
1036
+ return parseJoinRequestCreateResponse(raw);
1037
+ }
1038
+
1039
+ /** Cancel the caller's own pending join request. */
1040
+ async cancelJoinRequest(): Promise<void> {
1041
+ await this.http.request<void>(
1042
+ "DELETE", `/api/v1/rooms/${this.roomId}/join-request`,
1043
+ );
1044
+ }
1045
+
1046
+ /** OPERATOR-only: list join requests for this room (the approval inbox).
1047
+ * Defaults to pending only — pass a status to see processed history. */
1048
+ async listJoinRequests(opts?: {
1049
+ status?: "PENDING" | "APPROVED" | "REJECTED" | "CANCELLED";
1050
+ limit?: number;
1051
+ offset?: number;
1052
+ }): Promise<JoinRequestListResponse> {
1053
+ const raw = await this.http.request<unknown>(
1054
+ "GET", `/api/v1/rooms/${this.roomId}/join-requests`, undefined, {
1055
+ ...(opts?.status ? { status: opts.status } : {}),
1056
+ ...(opts?.limit !== undefined ? { limit: opts.limit } : {}),
1057
+ ...(opts?.offset !== undefined ? { offset: opts.offset } : {}),
1058
+ },
1059
+ );
1060
+ return parseJoinRequestListResponse(raw);
1061
+ }
1062
+
1063
+ /** OPERATOR-only: approve a pending join request — the requester becomes
1064
+ * a MEMBER. [reason] is echoed back to them in later list calls. */
1065
+ async approveJoinRequest(userId: string, reason?: string): Promise<void> {
1066
+ await this.http.request<void>(
1067
+ "POST",
1068
+ `/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/approve`,
1069
+ reason ? { reason } : {},
1070
+ );
1071
+ }
1072
+
1073
+ /** OPERATOR-only: reject a pending join request. */
1074
+ async rejectJoinRequest(userId: string, reason?: string): Promise<void> {
1075
+ await this.http.request<void>(
1076
+ "POST",
1077
+ `/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/reject`,
1078
+ reason ? { reason } : {},
1079
+ );
1080
+ }
1081
+
1082
+ // -------- hide / unhide --------
1083
+
1084
+ /** Hide this room from the caller's own room list (self, reversible —
1085
+ * the KakaoTalk "숨기기"). The room typically reappears via [unhide]
1086
+ * or on new activity, per server policy. Messages are untouched. */
1087
+ async hide(): Promise<void> {
1088
+ await this.http.request<void>(
1089
+ "POST", `/api/v1/rooms/${this.roomId}/hide`,
1090
+ );
1091
+ }
1092
+
1093
+ /** Undo [hide] — the room shows up in the caller's list again. */
1094
+ async unhide(): Promise<void> {
1095
+ await this.http.request<void>(
1096
+ "POST", `/api/v1/rooms/${this.roomId}/unhide`,
1097
+ );
1098
+ }
1099
+
1013
1100
  // -------- internal dispatch --------
1014
1101
 
1015
1102
  on<K extends keyof RoomEvents>(event: K, fn: (p: RoomEvents[K]) => void): () => void {
@@ -440,6 +440,55 @@ export function parseBulkDeleteBySenderResult(j: any): BulkDeleteBySenderResult
440
440
  } as BulkDeleteBySenderResult;
441
441
  }
442
442
 
443
+ export interface JoinRequestOut {
444
+ userId: string;
445
+ status: "PENDING" | "APPROVED" | "REJECTED" | "CANCELLED";
446
+ reason: string | null;
447
+ requestedAt: string;
448
+ processedAt: string | null;
449
+ processedByUserId: string | null;
450
+ }
451
+
452
+ export function parseJoinRequestOut(j: any): JoinRequestOut {
453
+ return {
454
+ userId: j["user_id"],
455
+ status: j["status"],
456
+ reason: j["reason"],
457
+ requestedAt: j["requested_at"],
458
+ processedAt: j["processed_at"],
459
+ processedByUserId: j["processed_by_user_id"],
460
+ } as JoinRequestOut;
461
+ }
462
+
463
+ export interface JoinRequestListResponse {
464
+ items: JoinRequestOut[];
465
+ total: number;
466
+ limit: number;
467
+ offset: number;
468
+ }
469
+
470
+ export function parseJoinRequestListResponse(j: any): JoinRequestListResponse {
471
+ return {
472
+ items: (j["items"] as unknown[]).map((e: any) => parseJoinRequestOut(e)),
473
+ total: j["total"],
474
+ limit: j["limit"],
475
+ offset: j["offset"],
476
+ } as JoinRequestListResponse;
477
+ }
478
+
479
+ /** Synchronous response after `POST /join-request`. `status="joined"` — OPEN room, user was immediately added. `status="pending"` — APPROVAL_REQUIRED, request is pending. `status="already_member"` — caller is already an active member; no-op. */
480
+ export interface JoinRequestCreateResponse {
481
+ status: "joined" | "pending" | "already_member";
482
+ roomId: string;
483
+ }
484
+
485
+ export function parseJoinRequestCreateResponse(j: any): JoinRequestCreateResponse {
486
+ return {
487
+ status: j["status"],
488
+ roomId: j["room_id"],
489
+ } as JoinRequestCreateResponse;
490
+ }
491
+
443
492
  /** Minimal sender identity — no friend/block/profile-url fluff. */
444
493
  export interface SenderMini {
445
494
  userId: string;
package/src/index.ts CHANGED
@@ -13,6 +13,9 @@ export type {
13
13
  ClientOptions,
14
14
  DeleteScope,
15
15
  InviteToken,
16
+ JoinRequestCreateResponse,
17
+ JoinRequestListResponse,
18
+ JoinRequestOut,
16
19
  MessageOut,
17
20
  MessageType,
18
21
  ReactionEntry,
package/src/types.ts CHANGED
@@ -62,6 +62,9 @@ export type {
62
62
  RoomOut,
63
63
  DeviceListResponse,
64
64
  PushPreferencesOut,
65
+ JoinRequestOut,
66
+ JoinRequestListResponse,
67
+ JoinRequestCreateResponse,
65
68
  } from "./generated/rest.js";
66
69
 
67
70
  // 공개 이름 유지 — 이전 손 DTO 이름을 생성물 이름으로 alias (0.x, minor 브레이킹 허용).