stream-chat 9.43.2 → 9.44.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/README.md +1 -1
- package/dist/cjs/index.browser.js +148 -6
- package/dist/cjs/index.browser.js.map +3 -3
- package/dist/cjs/index.node.js +153 -7
- package/dist/cjs/index.node.js.map +3 -3
- package/dist/esm/index.mjs +148 -6
- package/dist/esm/index.mjs.map +3 -3
- package/dist/types/client.d.ts +32 -0
- package/dist/types/signing.d.ts +99 -5
- package/package.json +2 -1
- package/src/channel.ts +3 -1
- package/src/client.ts +56 -2
- package/src/messageComposer/messageComposer.ts +2 -0
- package/src/signing.ts +195 -7
- package/src/utils.ts +1 -1
package/README.md
CHANGED
|
@@ -151,7 +151,7 @@ yarn start
|
|
|
151
151
|
|
|
152
152
|
## 📚 More Code Examples
|
|
153
153
|
|
|
154
|
-
Read up more on [Logging](./docs/logging.md)
|
|
154
|
+
Read up more on [Logging](./docs/logging.md), [User Token](./docs/userToken.md), and [Webhooks](./docs/webhooks.md) (including compressed payloads and SQS / SNS delivery) or visit our [documentation](https://getstream.io/chat/docs/) for more examples.
|
|
155
155
|
|
|
156
156
|
## ✍️ Contributing
|
|
157
157
|
|
|
@@ -48,6 +48,12 @@ var require_crypto = __commonJS({
|
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
// (disabled):zlib
|
|
52
|
+
var require_zlib = __commonJS({
|
|
53
|
+
"(disabled):zlib"() {
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
51
57
|
// src/index.ts
|
|
52
58
|
var index_exports = {};
|
|
53
59
|
__export(index_exports, {
|
|
@@ -92,6 +98,8 @@ __export(index_exports, {
|
|
|
92
98
|
FilterBuilder: () => FilterBuilder,
|
|
93
99
|
FixedSizeQueueCache: () => FixedSizeQueueCache,
|
|
94
100
|
InsightMetrics: () => InsightMetrics,
|
|
101
|
+
InvalidWebhookError: () => InvalidWebhookError,
|
|
102
|
+
InvalidWebhookErrorMessages: () => InvalidWebhookErrorMessages,
|
|
95
103
|
JWTServerToken: () => JWTServerToken,
|
|
96
104
|
JWTUserToken: () => JWTUserToken,
|
|
97
105
|
LinkPreviewStatus: () => LinkPreviewStatus,
|
|
@@ -178,6 +186,8 @@ __export(index_exports, {
|
|
|
178
186
|
createUploadConfigCheckMiddleware: () => createUploadConfigCheckMiddleware,
|
|
179
187
|
createUploadErrorHandlerMiddleware: () => createUploadErrorHandlerMiddleware,
|
|
180
188
|
decodeBase64: () => decodeBase64,
|
|
189
|
+
decodeSnsPayload: () => decodeSnsPayload,
|
|
190
|
+
decodeSqsPayload: () => decodeSqsPayload,
|
|
181
191
|
defaultPollFieldBlurEventValidators: () => defaultPollFieldBlurEventValidators,
|
|
182
192
|
defaultPollFieldChangeEventValidators: () => defaultPollFieldChangeEventValidators,
|
|
183
193
|
encodeBase64: () => encodeBase64,
|
|
@@ -193,6 +203,7 @@ __export(index_exports, {
|
|
|
193
203
|
getRawCommandName: () => getRawCommandName,
|
|
194
204
|
getTokenizedSuggestionDisplayName: () => getTokenizedSuggestionDisplayName,
|
|
195
205
|
getTriggerCharWithToken: () => getTriggerCharWithToken,
|
|
206
|
+
gunzipPayload: () => gunzipPayload,
|
|
196
207
|
insertItemWithTrigger: () => insertItemWithTrigger,
|
|
197
208
|
isAudioAttachment: () => isAudioAttachment,
|
|
198
209
|
isBlobButNotFile: () => isBlobButNotFile,
|
|
@@ -223,6 +234,9 @@ __export(index_exports, {
|
|
|
223
234
|
logChatPromiseExecution: () => logChatPromiseExecution,
|
|
224
235
|
mapPollStateToResponse: () => mapPollStateToResponse,
|
|
225
236
|
notifyCommandDisabled: () => notifyCommandDisabled,
|
|
237
|
+
parseEvent: () => parseEvent,
|
|
238
|
+
parseSns: () => parseSns,
|
|
239
|
+
parseSqs: () => parseSqs,
|
|
226
240
|
pollCompositionStateProcessors: () => pollCompositionStateProcessors,
|
|
227
241
|
pollStateChangeValidators: () => pollStateChangeValidators,
|
|
228
242
|
postInsights: () => postInsights,
|
|
@@ -232,7 +246,9 @@ __export(index_exports, {
|
|
|
232
246
|
replaceWordWithEntity: () => replaceWordWithEntity,
|
|
233
247
|
stripCommandFromText: () => stripCommandFromText,
|
|
234
248
|
textIsEmpty: () => textIsEmpty,
|
|
235
|
-
timeLeftMs: () => timeLeftMs
|
|
249
|
+
timeLeftMs: () => timeLeftMs,
|
|
250
|
+
verifyAndParseWebhook: () => verifyAndParseWebhook,
|
|
251
|
+
verifySignature: () => verifySignature
|
|
236
252
|
});
|
|
237
253
|
module.exports = __toCommonJS(index_exports);
|
|
238
254
|
|
|
@@ -844,7 +860,7 @@ var deleteUserMessages = ({
|
|
|
844
860
|
if (message.user?.id === user.id) {
|
|
845
861
|
messages[i] = message.type === "deleted" ? message : toDeletedMessage({ message, hardDelete, deletedAt });
|
|
846
862
|
}
|
|
847
|
-
if (message.quoted_message?.user?.id === user.id) {
|
|
863
|
+
if (messages[i].quoted_message && message.quoted_message?.user?.id === user.id) {
|
|
848
864
|
messages[i].quoted_message = message.quoted_message.type === "deleted" ? message.quoted_message : toDeletedMessage({
|
|
849
865
|
message: messages[i].quoted_message,
|
|
850
866
|
hardDelete,
|
|
@@ -7444,6 +7460,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
7444
7460
|
const draft = event.draft;
|
|
7445
7461
|
if (!draft || (draft.parent_id ?? null) !== (this.threadId ?? null) || draft.channel_cid !== this.channel.cid)
|
|
7446
7462
|
return;
|
|
7463
|
+
if (this.editedMessage) return;
|
|
7447
7464
|
this.initState({ composition: draft });
|
|
7448
7465
|
}).unsubscribe;
|
|
7449
7466
|
this.subscribeDraftDeleted = () => this.client.on("draft.deleted", (event) => {
|
|
@@ -7451,6 +7468,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
7451
7468
|
if (!draft || (draft.parent_id ?? null) !== (this.threadId ?? null) || draft.channel_cid !== this.channel.cid) {
|
|
7452
7469
|
return;
|
|
7453
7470
|
}
|
|
7471
|
+
if (this.editedMessage) return;
|
|
7454
7472
|
this.logDraftUpdateTimestamp();
|
|
7455
7473
|
if (this.compositionIsEmpty) {
|
|
7456
7474
|
return;
|
|
@@ -9494,7 +9512,7 @@ var Channel = class {
|
|
|
9494
9512
|
if (Array.isArray(this.data?.own_capabilities) && !this.data?.own_capabilities.includes("read-events")) {
|
|
9495
9513
|
return false;
|
|
9496
9514
|
}
|
|
9497
|
-
if (this.
|
|
9515
|
+
if (this.getClient()._muteStatus(this.cid).muted) return false;
|
|
9498
9516
|
return true;
|
|
9499
9517
|
}
|
|
9500
9518
|
/**
|
|
@@ -11005,6 +11023,7 @@ var UploadManager = class {
|
|
|
11005
11023
|
// src/signing.ts
|
|
11006
11024
|
var import_jsonwebtoken = __toESM(require_jsonwebtoken());
|
|
11007
11025
|
var import_crypto = __toESM(require_crypto());
|
|
11026
|
+
var import_zlib = __toESM(require_zlib());
|
|
11008
11027
|
function JWTUserToken(apiSecret, userId, extraData = {}, jwtOptions = {}) {
|
|
11009
11028
|
if (typeof userId !== "string") {
|
|
11010
11029
|
throw new TypeError("userId should be a string");
|
|
@@ -11056,7 +11075,7 @@ function DevToken(userId) {
|
|
|
11056
11075
|
// hardcoded signature
|
|
11057
11076
|
].join(".");
|
|
11058
11077
|
}
|
|
11059
|
-
function
|
|
11078
|
+
function verifySignature(body, signature, secret) {
|
|
11060
11079
|
const key = Buffer.from(secret, "utf8");
|
|
11061
11080
|
const hash = import_crypto.default.createHmac("sha256", key).update(body).digest("hex");
|
|
11062
11081
|
try {
|
|
@@ -11065,6 +11084,86 @@ function CheckSignature(body, secret, signature) {
|
|
|
11065
11084
|
return false;
|
|
11066
11085
|
}
|
|
11067
11086
|
}
|
|
11087
|
+
function CheckSignature(body, secret, signature) {
|
|
11088
|
+
return verifySignature(body, signature, secret);
|
|
11089
|
+
}
|
|
11090
|
+
var InvalidWebhookErrorMessages = {
|
|
11091
|
+
signatureMismatch: "signature mismatch",
|
|
11092
|
+
invalidBase64: "invalid base64 encoding",
|
|
11093
|
+
gzipFailed: "gzip decompression failed",
|
|
11094
|
+
invalidJson: "invalid JSON payload"
|
|
11095
|
+
};
|
|
11096
|
+
var InvalidWebhookError = class extends Error {
|
|
11097
|
+
constructor(message = InvalidWebhookErrorMessages.signatureMismatch) {
|
|
11098
|
+
super(message);
|
|
11099
|
+
this.name = "InvalidWebhookError";
|
|
11100
|
+
}
|
|
11101
|
+
};
|
|
11102
|
+
function gunzipPayload(rawBody) {
|
|
11103
|
+
const GZIP_MAGIC = Buffer.from([31, 139]);
|
|
11104
|
+
const body = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody);
|
|
11105
|
+
if (body.length >= 2 && body.subarray(0, 2).equals(GZIP_MAGIC)) {
|
|
11106
|
+
try {
|
|
11107
|
+
return import_zlib.default.gunzipSync(body);
|
|
11108
|
+
} catch {
|
|
11109
|
+
throw new InvalidWebhookError(InvalidWebhookErrorMessages.gzipFailed);
|
|
11110
|
+
}
|
|
11111
|
+
}
|
|
11112
|
+
return body;
|
|
11113
|
+
}
|
|
11114
|
+
function decodeSqsPayload(body) {
|
|
11115
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(body) || body.length % 4 !== 0) {
|
|
11116
|
+
throw new InvalidWebhookError(InvalidWebhookErrorMessages.invalidBase64);
|
|
11117
|
+
}
|
|
11118
|
+
const decoded = Buffer.from(body, "base64");
|
|
11119
|
+
if (decoded.toString("base64").length !== body.length) {
|
|
11120
|
+
throw new InvalidWebhookError(InvalidWebhookErrorMessages.invalidBase64);
|
|
11121
|
+
}
|
|
11122
|
+
return gunzipPayload(decoded);
|
|
11123
|
+
}
|
|
11124
|
+
function decodeSnsPayload(notificationBody) {
|
|
11125
|
+
const inner = extractSnsMessage(notificationBody);
|
|
11126
|
+
return decodeSqsPayload(inner ?? notificationBody);
|
|
11127
|
+
}
|
|
11128
|
+
function extractSnsMessage(notificationBody) {
|
|
11129
|
+
const trimmed = notificationBody.replace(/^[\s\uFEFF]+/, "");
|
|
11130
|
+
if (!trimmed.startsWith("{")) {
|
|
11131
|
+
return null;
|
|
11132
|
+
}
|
|
11133
|
+
let parsed;
|
|
11134
|
+
try {
|
|
11135
|
+
parsed = JSON.parse(trimmed);
|
|
11136
|
+
} catch {
|
|
11137
|
+
return null;
|
|
11138
|
+
}
|
|
11139
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed) || typeof parsed.Message !== "string") {
|
|
11140
|
+
return null;
|
|
11141
|
+
}
|
|
11142
|
+
return parsed.Message;
|
|
11143
|
+
}
|
|
11144
|
+
function parseEvent(payload) {
|
|
11145
|
+
const text = Buffer.isBuffer(payload) ? payload.toString("utf8") : payload;
|
|
11146
|
+
try {
|
|
11147
|
+
return JSON.parse(text);
|
|
11148
|
+
} catch {
|
|
11149
|
+
throw new InvalidWebhookError(InvalidWebhookErrorMessages.invalidJson);
|
|
11150
|
+
}
|
|
11151
|
+
}
|
|
11152
|
+
function verifyAndParse(payload, signature, secret) {
|
|
11153
|
+
if (!verifySignature(payload, signature, secret)) {
|
|
11154
|
+
throw new InvalidWebhookError(InvalidWebhookErrorMessages.signatureMismatch);
|
|
11155
|
+
}
|
|
11156
|
+
return parseEvent(payload);
|
|
11157
|
+
}
|
|
11158
|
+
function verifyAndParseWebhook(rawBody, signature, secret) {
|
|
11159
|
+
return verifyAndParse(gunzipPayload(rawBody), signature, secret);
|
|
11160
|
+
}
|
|
11161
|
+
function parseSqs(messageBody) {
|
|
11162
|
+
return parseEvent(decodeSqsPayload(messageBody));
|
|
11163
|
+
}
|
|
11164
|
+
function parseSns(notificationBody) {
|
|
11165
|
+
return parseEvent(decodeSnsPayload(notificationBody));
|
|
11166
|
+
}
|
|
11068
11167
|
|
|
11069
11168
|
// src/token_manager.ts
|
|
11070
11169
|
var TokenManager = class {
|
|
@@ -15668,7 +15767,7 @@ var StreamChat = class _StreamChat {
|
|
|
15668
15767
|
if (this.userAgent) {
|
|
15669
15768
|
return this.userAgent;
|
|
15670
15769
|
}
|
|
15671
|
-
const version = "9.
|
|
15770
|
+
const version = "9.44.0";
|
|
15672
15771
|
const clientBundle = "browser-cjs";
|
|
15673
15772
|
let userAgentString = "";
|
|
15674
15773
|
if (this.sdkIdentifier) {
|
|
@@ -15761,7 +15860,50 @@ var StreamChat = class _StreamChat {
|
|
|
15761
15860
|
* @returns {boolean}
|
|
15762
15861
|
*/
|
|
15763
15862
|
verifyWebhook(requestBody, xSignature) {
|
|
15764
|
-
return !!this.secret &&
|
|
15863
|
+
return !!this.secret && verifySignature(requestBody, xSignature, this.secret);
|
|
15864
|
+
}
|
|
15865
|
+
/**
|
|
15866
|
+
* Verify and parse an HTTP webhook event.
|
|
15867
|
+
*
|
|
15868
|
+
* Decompresses `rawBody` when gzipped (detected from the body bytes),
|
|
15869
|
+
* verifies the `X-Signature` header against the app's API secret, and
|
|
15870
|
+
* returns the parsed `Event`. Works whether or not Stream is currently
|
|
15871
|
+
* compressing payloads for this app, and stays correct behind
|
|
15872
|
+
* middleware that auto-decompresses the request.
|
|
15873
|
+
*
|
|
15874
|
+
* @param rawBody Raw HTTP request body bytes Stream signed
|
|
15875
|
+
* @param signature Value of the `X-Signature` header
|
|
15876
|
+
* @throws {InvalidWebhookError} When the signature does not match or
|
|
15877
|
+
* the gzip envelope is malformed.
|
|
15878
|
+
*/
|
|
15879
|
+
verifyAndParseWebhook(rawBody, signature) {
|
|
15880
|
+
if (!this.secret) {
|
|
15881
|
+
throw new InvalidWebhookError(
|
|
15882
|
+
"cannot verify webhook signature without an API secret on the client"
|
|
15883
|
+
);
|
|
15884
|
+
}
|
|
15885
|
+
return verifyAndParseWebhook(rawBody, signature, this.secret);
|
|
15886
|
+
}
|
|
15887
|
+
/**
|
|
15888
|
+
* Parse an SQS firehose event: decodes the message `Body` (base64 +
|
|
15889
|
+
* optional gzip) and returns the parsed `Event`. No HMAC verification
|
|
15890
|
+
* (Stream does not sign SQS bodies).
|
|
15891
|
+
*
|
|
15892
|
+
* @param messageBody SQS message `Body` string
|
|
15893
|
+
* @throws {InvalidWebhookError} When the base64 / gzip envelope is malformed.
|
|
15894
|
+
*/
|
|
15895
|
+
parseSqs(messageBody) {
|
|
15896
|
+
return parseSqs(messageBody);
|
|
15897
|
+
}
|
|
15898
|
+
/**
|
|
15899
|
+
* Parse an SNS-delivered event (unwraps envelope JSON when needed, then
|
|
15900
|
+
* same decode path as SQS). No HMAC verification.
|
|
15901
|
+
*
|
|
15902
|
+
* @param notificationBody Raw SNS POST body or pre-extracted `Message` string
|
|
15903
|
+
* @throws {InvalidWebhookError} When the envelope cannot be decoded.
|
|
15904
|
+
*/
|
|
15905
|
+
parseSns(notificationBody) {
|
|
15906
|
+
return parseSns(notificationBody);
|
|
15765
15907
|
}
|
|
15766
15908
|
/** getPermission - gets the definition for a permission
|
|
15767
15909
|
*
|