@noverachat/sdk-web 0.0.3 → 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/src/types.ts CHANGED
@@ -37,175 +37,73 @@ export type {
37
37
  WsError, WsCloseNotice,
38
38
  };
39
39
 
40
- export interface SenderMini {
41
- userId: string;
42
- nickname?: string | null;
43
- profileImageUrl?: string | null;
44
- }
40
+ /* ------------------------------------------------------------------ *
41
+ * REST DTO 는 GENERATED 이다 — packages/protocol/schema/openapi.dev.json
42
+ * (dev 서버 OpenAPI vendor 본) 이 SSOT. camelCase 인터페이스 + `parseX(json)`
43
+ * 변환 함수는 ./generated/rest.ts 에 함께 방출된다(REST_MODELS 화이트리스트,
44
+ * scripts/gen.mjs). 손으로 고치지 말고 vendor 본을 갱신한 뒤 `pnpm gen`.
45
+ * 아래에서 그대로 re-export 하고, 기존 공개 이름은 alias 로 유지한다.
46
+ * ------------------------------------------------------------------ */
47
+ export type {
48
+ SenderMini,
49
+ ReactionEntry,
50
+ MemberPreview,
51
+ RoomUnread,
52
+ UnreadRoomItem,
53
+ UnreadSummary,
54
+ AppPolicy,
55
+ MessageListResponse,
56
+ RoomMemberOut,
57
+ RoomMemberListResponse,
58
+ BulkDeleteResult,
59
+ BulkDeleteBySenderResult,
60
+ FileOut,
61
+ FileCreateResponse,
62
+ RoomOut,
63
+ DeviceListResponse,
64
+ PushPreferencesOut,
65
+ JoinRequestOut,
66
+ JoinRequestListResponse,
67
+ JoinRequestCreateResponse,
68
+ } from "./generated/rest.js";
45
69
 
46
- export interface ReactionEntry {
47
- key: string;
48
- userIds: string[];
49
- updatedAt: number; // unix ms
50
- }
70
+ // 공개 이름 유지 — 이전 손 DTO 이름을 생성물 이름으로 alias (0.x, minor 브레이킹 허용).
71
+ export type {
72
+ AnnouncementOut as Announcement,
73
+ BlockOut as Block,
74
+ InviteTokenOut as InviteToken,
75
+ InviteAcceptResponse as AcceptInviteResult,
76
+ DeviceOut as DeviceInfo,
77
+ MessageListResponse as MessagePage,
78
+ } from "./generated/rest.js";
51
79
 
52
- export interface MessageOut {
53
- appId: string;
54
- roomId: string;
55
- messageId: MessageIdStr;
56
- sender?: SenderMini | null;
57
- senderId?: string | null;
58
- messageType: MessageType;
59
- customType?: string | null;
60
- content?: string | null;
61
- data?: Record<string, unknown> | null;
62
- meta?: Record<string, unknown> | null;
63
- /** Custom metadata attached by the sender (extracted from `data._customMeta`).
64
- * Null when the sender didn't attach anything. */
65
- customMeta?: Record<string, unknown> | null;
66
- /** Snowflake bigint (string) of the attached file, when this is a file
67
- * message. Use ``chat.getFileUrl(fileId)`` to obtain a short-lived
68
- * signed download URL, or ``chat.getFileMeta(fileId)`` for metadata
69
- * without the download. Null for text-only messages. */
70
- fileId?: MessageIdStr | null;
71
- /** Inline file metadata embedded by the server on chat_receive — lets
72
- * the UI render a placeholder (filename, size, dimensions, thumbnail
73
- * status) without an extra round-trip. May be null even when `fileId`
74
- * is set (e.g. for messages loaded via REST history before the inline
75
- * metadata feature shipped); call ``getFileMeta()`` in that case. */
80
+ import type { MessageOut as RestMessageOut, ReactionEntry } from "./generated/rest.js";
81
+
82
+ /**
83
+ * Canonical message shape surfaced to SDK callers. Structurally the
84
+ * GENERATED REST `MessageOut` PLUS two client-side enhancements the server
85
+ * doesn't send verbatim:
86
+ * - `file` / `files` parsed into the camelCase `FileInline` shape (the
87
+ * generated model leaves them free-form since the OpenAPI schema does),
88
+ * - `customMeta` lifted out of `data._customMeta`.
89
+ * Built by `toMessageOut` in features/room.ts.
90
+ */
91
+ export interface MessageOut
92
+ extends Omit<RestMessageOut, "file" | "files" | "reactions"> {
93
+ /** Inline file metadata embedded by the server on chat_receive / REST
94
+ * history lets the UI render a placeholder (filename, size, dims,
95
+ * thumbnail status) without an extra round-trip. May be null even when
96
+ * `fileId` is set (older history predating inline meta); call
97
+ * ``getFileMeta()`` in that case. */
76
98
  file?: FileInline | null;
77
- /** Multi-file bundle set when the message references multiple files
78
- * via ``data.file_ids`` (kind = "file_group"). Each entry has the
79
- * same shape as ``file``. Null for single-file / text-only messages.
80
- * UIs should check ``files?.length > 0`` first and only fall through
81
- * to the ``file`` singleton when the bundle field is absent. */
99
+ /** Multi-file bundle (kind = "file_group"). Null for single-file /
100
+ * text-only messages. Check ``files?.length`` first, then fall back to
101
+ * the ``file`` singleton. */
82
102
  files?: FileInline[] | null;
83
- replyToId?: MessageIdStr | null;
84
103
  reactions: ReactionEntry[];
85
- createdAt: string; // ISO
86
- createdAtMs: number; // unix ms
87
- updatedAt?: string | null;
88
- updatedAtMs?: number | null;
89
- deletedAt?: string | null;
90
- deletedByUserId?: string | null;
91
- deleteScope?: DeleteScope | null;
92
- editedCount: number;
93
- }
94
-
95
- export interface RoomUnread {
96
- roomId: string;
97
- unreadCount: number;
98
- lastReadMessageId: MessageIdStr | null;
99
- lastMessageId: MessageIdStr | null;
100
- }
101
-
102
- export interface Announcement {
103
- id: number;
104
- roomId: string;
105
- content: string;
106
- createdBy: string;
107
- createdAt: string;
108
- updatedAt: string;
109
- }
110
-
111
- /** One-way user block — KakaoTalk-style.
112
- *
113
- * A row in the caller's block list. The blocked user is NOT told they're
114
- * blocked; they keep sending messages that just don't reach the blocker.
115
- * See ``chat.blockUser`` / ``chat.unblockUser`` / ``chat.listBlocks``.
116
- */
117
- export interface Block {
118
- blockedUserId: string;
119
- reason: string | null;
120
- /** ISO-8601 UTC. */
121
- createdAt: string;
122
- }
123
-
124
- /** 방 초대 링크 (카톡 오픈채팅 URL 스타일). Minted by an OPERATOR via
125
- * ``room.createInvite()``; consumed by any user via ``chat.acceptInvite()``. */
126
- export interface InviteToken {
127
- /** URL-safe short id (~22 chars). Embed in the shareable URL as-is. */
128
- token: string;
129
- roomId: string;
130
- createdBy: string;
131
- /** ISO-8601 UTC. */
132
- createdAt: string;
133
- /** ISO-8601 UTC. `null` = never expires. */
134
- expiresAt: string | null;
135
- /** `null` = unlimited uses. */
136
- maxUses: number | null;
137
- /** Successful `accept` calls so far. */
138
- usedCount: number;
139
- /** ISO-8601 UTC. `null` = still active (assuming not expired / exhausted). */
140
- revokedAt: string | null;
141
- }
142
-
143
- /** Result of `chat.acceptInvite(token)`. */
144
- export interface AcceptInviteResult {
145
- roomId: string;
146
- roomName: string | null;
147
- /** True when the caller was already an active member — the call is a
148
- * no-op and does NOT bump the invite's `used_count`. Useful for
149
- * deep-link flows that may be triggered on re-open. */
150
- wasAlreadyMember: boolean;
151
- }
152
-
153
- /** One member shown in a channel-list avatar mosaic. */
154
- export interface MemberPreview {
155
- userId: string;
156
- nickname: string | null;
157
- profileImageUrl: string | null;
158
- /** Cluster-wide presence dot. `null` = not resolved (endpoint didn't
159
- * include it). Consumers rendering a green/gray dot should treat
160
- * `null` and `false` differently only if they care about the
161
- * "unknown" state. */
162
- isOnline: boolean | null;
163
- }
164
-
165
- export interface UnreadRoomItem {
166
- roomId: string;
167
- name: string | null;
168
- /** "ONE" | "GROUP" | "SUPER_GROUP" — lets the UI pick a 1-up vs mosaic avatar. */
169
- roomType: string | null;
170
- unreadCount: number;
171
- lastMessageId: MessageIdStr | null;
172
- /** One-line preview shown under the room name in the channel list.
173
- * TEXT/SYSTEM → first 200 chars, EMOTICON → "[이모티콘]", images/videos/
174
- * files → "[사진]" / "[동영상]" / "[파일] name". Null when the room has
175
- * no messages yet (or was just cleared). */
176
- lastMessagePreview: string | null;
177
- lastReadMessageId: MessageIdStr | null;
178
- /** Total active (non-KICKED) members incl. the caller — the count badge. */
179
- memberCount: number;
180
- /** Up to 4 OTHER members (caller excluded) for the avatar mosaic. */
181
- members: MemberPreview[];
182
- }
183
-
184
- export interface UnreadSummary {
185
- total: number;
186
- rooms: UnreadRoomItem[];
187
- }
188
-
189
- export interface AppPolicy {
190
- appId: string;
191
- /** When true, any non-KICKED room member can call
192
- * `room.deleteAllMessages()`. When false, only operators (or super-admin)
193
- * can. UIs should gate the destructive button on this. */
194
- allowMemberBulkDelete: boolean;
195
- /** When false, server does NOT broadcast `sync_tick` frames — hide
196
- * read-receipt badges client-side. Per-user unread counts via REST
197
- * still work (DB tracking is independent of the broadcast). */
198
- readReceiptsEnabled: boolean;
199
- /** When false, the server silently drops inbound `typing` frames and
200
- * no peer ever sees "alice is typing…". SDK callers should also
201
- * short-circuit `room.setTyping()` based on this flag so a disabled
202
- * app doesn't pay the WS frame cost per keystroke. */
203
- typingIndicatorsEnabled: boolean;
204
- /** When true, any non-KICKED room member can hard-delete another
205
- * member's message (scope=ALL). Default false — sender-or-operator
206
- * only. UIs should gate the "Delete for everyone" button on other
207
- * users' messages by this flag. */
208
- deleteAnyMessageEnabled: boolean;
104
+ /** Custom metadata attached by the sender (extracted from
105
+ * `data._customMeta`). Null when the sender didn't attach anything. */
106
+ customMeta?: Record<string, unknown> | null;
209
107
  }
210
108
 
211
109
  /** Push providers we can deliver to. Matches the backend `TokenType`. */
@@ -227,19 +125,6 @@ export interface RegisterDeviceParams {
227
125
  appVersion?: string;
228
126
  }
229
127
 
230
- export interface DeviceInfo {
231
- deviceId: string;
232
- tokenType: DeviceTokenType;
233
- /** Server returns only a short prefix of the token — it's a credential
234
- * and the caller already holds the full value. */
235
- tokenPreview: string;
236
- os: DeviceOS | null;
237
- appVersion: string | null;
238
- pushEnabled: boolean;
239
- lastActiveAt: string | null;
240
- createdAt: string;
241
- }
242
-
243
128
  export interface SendParams {
244
129
  content?: string;
245
130
  messageType?: "TEXT" | "FILE" | "EMOTICON";