@noverachat/sdk-web 0.5.0 → 0.5.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/dist/index.cjs +38 -2
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +38 -2
- package/package.json +1 -1
- package/src/client.ts +10 -0
- package/src/features/message-collection.ts +31 -2
package/dist/index.cjs
CHANGED
|
@@ -826,7 +826,15 @@ var MessageCollection = class {
|
|
|
826
826
|
this._messages.splice(0, this.hydratedCount);
|
|
827
827
|
this.hydratedCount = 0;
|
|
828
828
|
}
|
|
829
|
-
|
|
829
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
830
|
+
this._messages.forEach((m, i) => indexById.set(m.id, i));
|
|
831
|
+
const fresh = [];
|
|
832
|
+
for (const item of page.items.map(chatMessageFromHistory)) {
|
|
833
|
+
const at = indexById.get(item.id);
|
|
834
|
+
if (at === void 0) fresh.push(item);
|
|
835
|
+
else this._messages[at] = item;
|
|
836
|
+
}
|
|
837
|
+
this._messages.unshift(...fresh);
|
|
830
838
|
this.oldestCursor = page.nextCursor;
|
|
831
839
|
this._hasMore = page.hasMore;
|
|
832
840
|
this.emit("replaced");
|
|
@@ -997,8 +1005,26 @@ var MessageCollection = class {
|
|
|
997
1005
|
this.bus.removeAll();
|
|
998
1006
|
}
|
|
999
1007
|
// ── event handlers ──────────────────────────────────────────────
|
|
1008
|
+
/**
|
|
1009
|
+
* 수신 프레임을 컬렉션에 넣는다.
|
|
1010
|
+
*
|
|
1011
|
+
* 두 가지를 방어한다 — backfill(`chat.backfill()`)은 재연결 시 앵커
|
|
1012
|
+
* 이후를 **다시** 실어오므로, 이미 들고 있는 메시지가 섞여 오고 화면보다
|
|
1013
|
+
* 오래된 메시지도 함께 온다:
|
|
1014
|
+
* - **중복**: 같은 id가 이미 있으면 무시한다.
|
|
1015
|
+
* - **순서**: 뒤에서부터 자리를 찾아 꽂는다. 실시간 프레임은 항상 마지막
|
|
1016
|
+
* 메시지보다 새로우므로 비교 1번으로 끝나고(빠른 경로), 옛 메시지가
|
|
1017
|
+
* 섞여 올 때만 탐색이 일어난다.
|
|
1018
|
+
*/
|
|
1000
1019
|
onIncoming(f) {
|
|
1001
|
-
|
|
1020
|
+
const msg = chatMessageFromLive(f);
|
|
1021
|
+
let i = this._messages.length - 1;
|
|
1022
|
+
for (; i >= 0; i--) {
|
|
1023
|
+
const cur = this._messages[i];
|
|
1024
|
+
if (cur.id === msg.id) return;
|
|
1025
|
+
if (!idGreater(cur.id, msg.id)) break;
|
|
1026
|
+
}
|
|
1027
|
+
this._messages.splice(i + 1, 0, msg);
|
|
1002
1028
|
this.emit("inserted");
|
|
1003
1029
|
}
|
|
1004
1030
|
onUpdated(f) {
|
|
@@ -2163,6 +2189,16 @@ var NoveraChat = class {
|
|
|
2163
2189
|
readDebounceMs;
|
|
2164
2190
|
logger;
|
|
2165
2191
|
connectedOnce = false;
|
|
2192
|
+
/**
|
|
2193
|
+
* 방별 "실시간 파이프라인으로 본 최대 message_id" — 재연결 핸드셰이크의
|
|
2194
|
+
* `?since=`와 backfill 시작점.
|
|
2195
|
+
*
|
|
2196
|
+
* **의도적으로 낮게 유지한다.** 히스토리 로드(REST)는 이 값을 올리지
|
|
2197
|
+
* 않는다 — 앵커가 낮으면 backfill이 이미 가진 것까지 다시 주는 쪽으로
|
|
2198
|
+
* (안전), 높으면 못 받은 것을 영영 건너뛰는 쪽으로(치명) 실패하기
|
|
2199
|
+
* 때문이다. 중복은 수신 측(`MessageCollection.onIncoming`)의 id dedupe가
|
|
2200
|
+
* 흡수하므로, "빠뜨린 갱신"으로 보고 올리지 말 것.
|
|
2201
|
+
*/
|
|
2166
2202
|
lastMessageIdByRoom = /* @__PURE__ */ new Map();
|
|
2167
2203
|
/** Most recently dispatched presence event key, `${userId}:${at}:${online}`.
|
|
2168
2204
|
* Used to drop the N-1 shared-room copies of a single online/offline
|
package/dist/index.d.cts
CHANGED
|
@@ -912,6 +912,17 @@ declare class MessageCollection {
|
|
|
912
912
|
/** Unsubscribe from the room and flush the pending read watermark. The
|
|
913
913
|
* collection must not be used afterwards. */
|
|
914
914
|
dispose(): void;
|
|
915
|
+
/**
|
|
916
|
+
* 수신 프레임을 컬렉션에 넣는다.
|
|
917
|
+
*
|
|
918
|
+
* 두 가지를 방어한다 — backfill(`chat.backfill()`)은 재연결 시 앵커
|
|
919
|
+
* 이후를 **다시** 실어오므로, 이미 들고 있는 메시지가 섞여 오고 화면보다
|
|
920
|
+
* 오래된 메시지도 함께 온다:
|
|
921
|
+
* - **중복**: 같은 id가 이미 있으면 무시한다.
|
|
922
|
+
* - **순서**: 뒤에서부터 자리를 찾아 꽂는다. 실시간 프레임은 항상 마지막
|
|
923
|
+
* 메시지보다 새로우므로 비교 1번으로 끝나고(빠른 경로), 옛 메시지가
|
|
924
|
+
* 섞여 올 때만 탐색이 일어난다.
|
|
925
|
+
*/
|
|
915
926
|
private onIncoming;
|
|
916
927
|
private onUpdated;
|
|
917
928
|
private onDeleted;
|
|
@@ -1386,6 +1397,16 @@ declare class NoveraChat {
|
|
|
1386
1397
|
private readDebounceMs;
|
|
1387
1398
|
private logger;
|
|
1388
1399
|
private connectedOnce;
|
|
1400
|
+
/**
|
|
1401
|
+
* 방별 "실시간 파이프라인으로 본 최대 message_id" — 재연결 핸드셰이크의
|
|
1402
|
+
* `?since=`와 backfill 시작점.
|
|
1403
|
+
*
|
|
1404
|
+
* **의도적으로 낮게 유지한다.** 히스토리 로드(REST)는 이 값을 올리지
|
|
1405
|
+
* 않는다 — 앵커가 낮으면 backfill이 이미 가진 것까지 다시 주는 쪽으로
|
|
1406
|
+
* (안전), 높으면 못 받은 것을 영영 건너뛰는 쪽으로(치명) 실패하기
|
|
1407
|
+
* 때문이다. 중복은 수신 측(`MessageCollection.onIncoming`)의 id dedupe가
|
|
1408
|
+
* 흡수하므로, "빠뜨린 갱신"으로 보고 올리지 말 것.
|
|
1409
|
+
*/
|
|
1389
1410
|
private lastMessageIdByRoom;
|
|
1390
1411
|
/** Most recently dispatched presence event key, `${userId}:${at}:${online}`.
|
|
1391
1412
|
* Used to drop the N-1 shared-room copies of a single online/offline
|
package/dist/index.d.ts
CHANGED
|
@@ -912,6 +912,17 @@ declare class MessageCollection {
|
|
|
912
912
|
/** Unsubscribe from the room and flush the pending read watermark. The
|
|
913
913
|
* collection must not be used afterwards. */
|
|
914
914
|
dispose(): void;
|
|
915
|
+
/**
|
|
916
|
+
* 수신 프레임을 컬렉션에 넣는다.
|
|
917
|
+
*
|
|
918
|
+
* 두 가지를 방어한다 — backfill(`chat.backfill()`)은 재연결 시 앵커
|
|
919
|
+
* 이후를 **다시** 실어오므로, 이미 들고 있는 메시지가 섞여 오고 화면보다
|
|
920
|
+
* 오래된 메시지도 함께 온다:
|
|
921
|
+
* - **중복**: 같은 id가 이미 있으면 무시한다.
|
|
922
|
+
* - **순서**: 뒤에서부터 자리를 찾아 꽂는다. 실시간 프레임은 항상 마지막
|
|
923
|
+
* 메시지보다 새로우므로 비교 1번으로 끝나고(빠른 경로), 옛 메시지가
|
|
924
|
+
* 섞여 올 때만 탐색이 일어난다.
|
|
925
|
+
*/
|
|
915
926
|
private onIncoming;
|
|
916
927
|
private onUpdated;
|
|
917
928
|
private onDeleted;
|
|
@@ -1386,6 +1397,16 @@ declare class NoveraChat {
|
|
|
1386
1397
|
private readDebounceMs;
|
|
1387
1398
|
private logger;
|
|
1388
1399
|
private connectedOnce;
|
|
1400
|
+
/**
|
|
1401
|
+
* 방별 "실시간 파이프라인으로 본 최대 message_id" — 재연결 핸드셰이크의
|
|
1402
|
+
* `?since=`와 backfill 시작점.
|
|
1403
|
+
*
|
|
1404
|
+
* **의도적으로 낮게 유지한다.** 히스토리 로드(REST)는 이 값을 올리지
|
|
1405
|
+
* 않는다 — 앵커가 낮으면 backfill이 이미 가진 것까지 다시 주는 쪽으로
|
|
1406
|
+
* (안전), 높으면 못 받은 것을 영영 건너뛰는 쪽으로(치명) 실패하기
|
|
1407
|
+
* 때문이다. 중복은 수신 측(`MessageCollection.onIncoming`)의 id dedupe가
|
|
1408
|
+
* 흡수하므로, "빠뜨린 갱신"으로 보고 올리지 말 것.
|
|
1409
|
+
*/
|
|
1389
1410
|
private lastMessageIdByRoom;
|
|
1390
1411
|
/** Most recently dispatched presence event key, `${userId}:${at}:${online}`.
|
|
1391
1412
|
* Used to drop the N-1 shared-room copies of a single online/offline
|
package/dist/index.js
CHANGED
|
@@ -791,7 +791,15 @@ var MessageCollection = class {
|
|
|
791
791
|
this._messages.splice(0, this.hydratedCount);
|
|
792
792
|
this.hydratedCount = 0;
|
|
793
793
|
}
|
|
794
|
-
|
|
794
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
795
|
+
this._messages.forEach((m, i) => indexById.set(m.id, i));
|
|
796
|
+
const fresh = [];
|
|
797
|
+
for (const item of page.items.map(chatMessageFromHistory)) {
|
|
798
|
+
const at = indexById.get(item.id);
|
|
799
|
+
if (at === void 0) fresh.push(item);
|
|
800
|
+
else this._messages[at] = item;
|
|
801
|
+
}
|
|
802
|
+
this._messages.unshift(...fresh);
|
|
795
803
|
this.oldestCursor = page.nextCursor;
|
|
796
804
|
this._hasMore = page.hasMore;
|
|
797
805
|
this.emit("replaced");
|
|
@@ -962,8 +970,26 @@ var MessageCollection = class {
|
|
|
962
970
|
this.bus.removeAll();
|
|
963
971
|
}
|
|
964
972
|
// ── event handlers ──────────────────────────────────────────────
|
|
973
|
+
/**
|
|
974
|
+
* 수신 프레임을 컬렉션에 넣는다.
|
|
975
|
+
*
|
|
976
|
+
* 두 가지를 방어한다 — backfill(`chat.backfill()`)은 재연결 시 앵커
|
|
977
|
+
* 이후를 **다시** 실어오므로, 이미 들고 있는 메시지가 섞여 오고 화면보다
|
|
978
|
+
* 오래된 메시지도 함께 온다:
|
|
979
|
+
* - **중복**: 같은 id가 이미 있으면 무시한다.
|
|
980
|
+
* - **순서**: 뒤에서부터 자리를 찾아 꽂는다. 실시간 프레임은 항상 마지막
|
|
981
|
+
* 메시지보다 새로우므로 비교 1번으로 끝나고(빠른 경로), 옛 메시지가
|
|
982
|
+
* 섞여 올 때만 탐색이 일어난다.
|
|
983
|
+
*/
|
|
965
984
|
onIncoming(f) {
|
|
966
|
-
|
|
985
|
+
const msg = chatMessageFromLive(f);
|
|
986
|
+
let i = this._messages.length - 1;
|
|
987
|
+
for (; i >= 0; i--) {
|
|
988
|
+
const cur = this._messages[i];
|
|
989
|
+
if (cur.id === msg.id) return;
|
|
990
|
+
if (!idGreater(cur.id, msg.id)) break;
|
|
991
|
+
}
|
|
992
|
+
this._messages.splice(i + 1, 0, msg);
|
|
967
993
|
this.emit("inserted");
|
|
968
994
|
}
|
|
969
995
|
onUpdated(f) {
|
|
@@ -2128,6 +2154,16 @@ var NoveraChat = class {
|
|
|
2128
2154
|
readDebounceMs;
|
|
2129
2155
|
logger;
|
|
2130
2156
|
connectedOnce = false;
|
|
2157
|
+
/**
|
|
2158
|
+
* 방별 "실시간 파이프라인으로 본 최대 message_id" — 재연결 핸드셰이크의
|
|
2159
|
+
* `?since=`와 backfill 시작점.
|
|
2160
|
+
*
|
|
2161
|
+
* **의도적으로 낮게 유지한다.** 히스토리 로드(REST)는 이 값을 올리지
|
|
2162
|
+
* 않는다 — 앵커가 낮으면 backfill이 이미 가진 것까지 다시 주는 쪽으로
|
|
2163
|
+
* (안전), 높으면 못 받은 것을 영영 건너뛰는 쪽으로(치명) 실패하기
|
|
2164
|
+
* 때문이다. 중복은 수신 측(`MessageCollection.onIncoming`)의 id dedupe가
|
|
2165
|
+
* 흡수하므로, "빠뜨린 갱신"으로 보고 올리지 말 것.
|
|
2166
|
+
*/
|
|
2131
2167
|
lastMessageIdByRoom = /* @__PURE__ */ new Map();
|
|
2132
2168
|
/** Most recently dispatched presence event key, `${userId}:${at}:${online}`.
|
|
2133
2169
|
* Used to drop the N-1 shared-room copies of a single online/offline
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -57,6 +57,16 @@ export class NoveraChat {
|
|
|
57
57
|
private readDebounceMs: number;
|
|
58
58
|
private logger: ClientOptions["logger"];
|
|
59
59
|
private connectedOnce = false;
|
|
60
|
+
/**
|
|
61
|
+
* 방별 "실시간 파이프라인으로 본 최대 message_id" — 재연결 핸드셰이크의
|
|
62
|
+
* `?since=`와 backfill 시작점.
|
|
63
|
+
*
|
|
64
|
+
* **의도적으로 낮게 유지한다.** 히스토리 로드(REST)는 이 값을 올리지
|
|
65
|
+
* 않는다 — 앵커가 낮으면 backfill이 이미 가진 것까지 다시 주는 쪽으로
|
|
66
|
+
* (안전), 높으면 못 받은 것을 영영 건너뛰는 쪽으로(치명) 실패하기
|
|
67
|
+
* 때문이다. 중복은 수신 측(`MessageCollection.onIncoming`)의 id dedupe가
|
|
68
|
+
* 흡수하므로, "빠뜨린 갱신"으로 보고 올리지 말 것.
|
|
69
|
+
*/
|
|
60
70
|
private lastMessageIdByRoom = new Map<string, MessageIdStr>();
|
|
61
71
|
/** Most recently dispatched presence event key, `${userId}:${at}:${online}`.
|
|
62
72
|
* Used to drop the N-1 shared-room copies of a single online/offline
|
|
@@ -223,7 +223,18 @@ export class MessageCollection {
|
|
|
223
223
|
this._messages.splice(0, this.hydratedCount); // 스냅샷 통째 폐기 → 서버가 이긴다
|
|
224
224
|
this.hydratedCount = 0;
|
|
225
225
|
}
|
|
226
|
-
|
|
226
|
+
// id 기준 upsert — 이미 들고 있는 메시지는 서버 버전으로 교체하고, 없는
|
|
227
|
+
// 것만 앞에 잇는다. `load()`가 두 번 불려도(React StrictMode의 mount →
|
|
228
|
+
// cleanup → mount 사이클처럼) 같은 페이지가 겹쳐 쌓이지 않는다.
|
|
229
|
+
const indexById = new Map<string, number>();
|
|
230
|
+
this._messages.forEach((m, i) => indexById.set(m.id, i));
|
|
231
|
+
const fresh: ChatMessage[] = [];
|
|
232
|
+
for (const item of page.items.map(chatMessageFromHistory)) {
|
|
233
|
+
const at = indexById.get(item.id);
|
|
234
|
+
if (at === undefined) fresh.push(item);
|
|
235
|
+
else this._messages[at] = item; // 겹치는 구간은 서버가 이긴다
|
|
236
|
+
}
|
|
237
|
+
this._messages.unshift(...fresh);
|
|
227
238
|
this.oldestCursor = page.nextCursor;
|
|
228
239
|
this._hasMore = page.hasMore;
|
|
229
240
|
this.emit("replaced");
|
|
@@ -439,8 +450,26 @@ export class MessageCollection {
|
|
|
439
450
|
|
|
440
451
|
// ── event handlers ──────────────────────────────────────────────
|
|
441
452
|
|
|
453
|
+
/**
|
|
454
|
+
* 수신 프레임을 컬렉션에 넣는다.
|
|
455
|
+
*
|
|
456
|
+
* 두 가지를 방어한다 — backfill(`chat.backfill()`)은 재연결 시 앵커
|
|
457
|
+
* 이후를 **다시** 실어오므로, 이미 들고 있는 메시지가 섞여 오고 화면보다
|
|
458
|
+
* 오래된 메시지도 함께 온다:
|
|
459
|
+
* - **중복**: 같은 id가 이미 있으면 무시한다.
|
|
460
|
+
* - **순서**: 뒤에서부터 자리를 찾아 꽂는다. 실시간 프레임은 항상 마지막
|
|
461
|
+
* 메시지보다 새로우므로 비교 1번으로 끝나고(빠른 경로), 옛 메시지가
|
|
462
|
+
* 섞여 올 때만 탐색이 일어난다.
|
|
463
|
+
*/
|
|
442
464
|
private onIncoming(f: WsChatReceive): void {
|
|
443
|
-
|
|
465
|
+
const msg = chatMessageFromLive(f);
|
|
466
|
+
let i = this._messages.length - 1;
|
|
467
|
+
for (; i >= 0; i--) {
|
|
468
|
+
const cur = this._messages[i]!;
|
|
469
|
+
if (cur.id === msg.id) return; // 이미 있음 — 재전송분
|
|
470
|
+
if (!idGreater(cur.id, msg.id)) break; // 여기가 삽입 자리
|
|
471
|
+
}
|
|
472
|
+
this._messages.splice(i + 1, 0, msg);
|
|
444
473
|
this.emit("inserted");
|
|
445
474
|
}
|
|
446
475
|
|