stream-chat-react-native 9.8.0 → 9.8.1-beta.2
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/android/build.gradle
CHANGED
|
@@ -73,8 +73,10 @@ android {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
compileOptions {
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
// Must match the Kotlin jvmTarget below. AGP 9 (React Native 0.87) fails the build on an
|
|
77
|
+
// inconsistent JVM-target between the Java and Kotlin compilation tasks.
|
|
78
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
79
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
kotlinOptions {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
# Fallbacks used only when the consuming app's root project does not define these (an app created
|
|
2
|
+
# from the React Native template always does). Kept in step with React Native 0.87's requirements:
|
|
3
|
+
# libraries must build against compileSdk >= 34 (minCompileSdk), and Kotlin >= 2.0 is required.
|
|
4
|
+
ImageResizer_kotlinVersion=2.2.0
|
|
5
|
+
ImageResizer_minSdkVersion=24
|
|
6
|
+
ImageResizer_targetSdkVersion=36
|
|
7
|
+
ImageResizer_compileSdkVersion=34
|
|
8
|
+
ImageResizer_ndkversion=27.1.12297006
|
|
@@ -5,10 +5,48 @@ private enum StreamMultipartBodyElement {
|
|
|
5
5
|
case file(URL)
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/// Carries a body-production failure out of band.
|
|
9
|
+
///
|
|
10
|
+
/// A bound stream pair has no error channel: the only way the producer can signal failure is to
|
|
11
|
+
/// close the write end, which the server sees as a truncated body. Recording the real error here
|
|
12
|
+
/// lets the upload manager surface it instead of the generic transport error.
|
|
13
|
+
final class StreamMultipartBodyErrorBox: @unchecked Sendable {
|
|
14
|
+
private let lock = NSLock()
|
|
15
|
+
private var storedError: Error?
|
|
16
|
+
|
|
17
|
+
var error: Error? {
|
|
18
|
+
lock.lock()
|
|
19
|
+
defer { lock.unlock() }
|
|
20
|
+
return storedError
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func record(_ error: Error) {
|
|
24
|
+
lock.lock()
|
|
25
|
+
defer { lock.unlock() }
|
|
26
|
+
if storedError == nil {
|
|
27
|
+
storedError = error
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
8
32
|
final class StreamMultipartUploadBodyStreamFactory {
|
|
9
33
|
let boundary: String
|
|
10
34
|
let contentLength: Int64?
|
|
11
35
|
|
|
36
|
+
/// Set when the body of the **most recent** attempt could not be produced in full.
|
|
37
|
+
///
|
|
38
|
+
/// `URLSession` can ask for a fresh body stream (redirect, auth retry) via
|
|
39
|
+
/// `needNewBodyStream`. Each attempt therefore gets its own box and `makeStream()` installs it
|
|
40
|
+
/// as the current one, so a failure recorded by an abandoned attempt — e.g. its reader going
|
|
41
|
+
/// away because URLSession decided to retry — can never fail a later attempt that succeeds.
|
|
42
|
+
var bodyError: Error? {
|
|
43
|
+
boxLock.lock()
|
|
44
|
+
defer { boxLock.unlock() }
|
|
45
|
+
return currentErrorBox.error
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private let boxLock = NSLock()
|
|
49
|
+
private var currentErrorBox = StreamMultipartBodyErrorBox()
|
|
12
50
|
private let elements: [StreamMultipartBodyElement]
|
|
13
51
|
|
|
14
52
|
private init(
|
|
@@ -63,7 +101,37 @@ final class StreamMultipartUploadBodyStreamFactory {
|
|
|
63
101
|
}
|
|
64
102
|
|
|
65
103
|
func makeStream() -> InputStream {
|
|
66
|
-
|
|
104
|
+
var readStream: Unmanaged<CFReadStream>?
|
|
105
|
+
var writeStream: Unmanaged<CFWriteStream>?
|
|
106
|
+
|
|
107
|
+
CFStreamCreateBoundPair(
|
|
108
|
+
kCFAllocatorDefault,
|
|
109
|
+
&readStream,
|
|
110
|
+
&writeStream,
|
|
111
|
+
CFIndex(StreamMultipartBodyProducer.transferBufferSize)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
// A fresh box per attempt; see `bodyError`.
|
|
115
|
+
let errorBox = StreamMultipartBodyErrorBox()
|
|
116
|
+
boxLock.lock()
|
|
117
|
+
currentErrorBox = errorBox
|
|
118
|
+
boxLock.unlock()
|
|
119
|
+
|
|
120
|
+
guard
|
|
121
|
+
let input = readStream?.takeRetainedValue() as InputStream?,
|
|
122
|
+
let output = writeStream?.takeRetainedValue()
|
|
123
|
+
else {
|
|
124
|
+
errorBox.record(StreamMultipartUploadError.invalidRequest("Could not create a request body stream"))
|
|
125
|
+
return InputStream(data: Data())
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
StreamMultipartBodyProducer(
|
|
129
|
+
elements: elements,
|
|
130
|
+
output: output,
|
|
131
|
+
errorBox: errorBox
|
|
132
|
+
).start()
|
|
133
|
+
|
|
134
|
+
return input
|
|
67
135
|
}
|
|
68
136
|
|
|
69
137
|
private static func multipartTextData(boundary: String, part: StreamMultipartTextPart) -> Data {
|
|
@@ -102,153 +170,263 @@ final class StreamMultipartUploadBodyStreamFactory {
|
|
|
102
170
|
}
|
|
103
171
|
}
|
|
104
172
|
|
|
105
|
-
|
|
173
|
+
/// Feeds the write end of a `CFStreamCreateBoundPair` from the multipart element list.
|
|
174
|
+
///
|
|
175
|
+
/// The body is handed to `URLSession` as the *read* end of a Core Foundation bound stream pair
|
|
176
|
+
/// rather than as a hand-rolled `InputStream` subclass. That matters: CFNetwork drives an HTTP/1.1
|
|
177
|
+
/// request body through the `CFReadStream` client-callback machinery, and a plain `InputStream`
|
|
178
|
+
/// subclass cannot participate in it — it can never report end-of-stream. CFNetwork stops reading
|
|
179
|
+
/// as soon as `Content-Length` is satisfied, so with a subclass it never observed the end of the
|
|
180
|
+
/// body, never considered the request finished, and the task sat idle until it timed out (the
|
|
181
|
+
/// server had already answered 201). A real bound pair reports every event, and closing the write
|
|
182
|
+
/// end is what tells CFNetwork the body is complete.
|
|
183
|
+
///
|
|
184
|
+
/// The write end is driven by **GCD** (`CFWriteStreamSetDispatchQueue`) rather than a run loop, so
|
|
185
|
+
/// this owns no thread and cannot outlive its work.
|
|
186
|
+
private final class StreamMultipartBodyProducer {
|
|
187
|
+
static let transferBufferSize = 64 * 1024
|
|
188
|
+
|
|
189
|
+
private enum Refill {
|
|
190
|
+
case filled
|
|
191
|
+
case drained
|
|
192
|
+
case failed(Error)
|
|
193
|
+
}
|
|
194
|
+
|
|
106
195
|
private let elements: [StreamMultipartBodyElement]
|
|
196
|
+
private let output: CFWriteStream
|
|
197
|
+
private let errorBox: StreamMultipartBodyErrorBox
|
|
198
|
+
private let queue: DispatchQueue
|
|
199
|
+
private let buffer = UnsafeMutablePointer<UInt8>.allocate(
|
|
200
|
+
capacity: StreamMultipartBodyProducer.transferBufferSize
|
|
201
|
+
)
|
|
202
|
+
|
|
107
203
|
private var currentIndex = 0
|
|
108
204
|
private var currentStream: InputStream?
|
|
109
|
-
private
|
|
110
|
-
private var
|
|
111
|
-
private var
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
205
|
+
private var bufferOffset = 0
|
|
206
|
+
private var bufferLength = 0
|
|
207
|
+
private var isFinished = false
|
|
208
|
+
/// Keeps the producer alive while the stream client holds an unretained pointer to it.
|
|
209
|
+
private var selfRetain: StreamMultipartBodyProducer?
|
|
210
|
+
|
|
211
|
+
init(
|
|
212
|
+
elements: [StreamMultipartBodyElement],
|
|
213
|
+
output: CFWriteStream,
|
|
214
|
+
errorBox: StreamMultipartBodyErrorBox
|
|
215
|
+
) {
|
|
115
216
|
self.elements = elements
|
|
116
|
-
|
|
217
|
+
self.output = output
|
|
218
|
+
self.errorBox = errorBox
|
|
219
|
+
queue = DispatchQueue(
|
|
220
|
+
label: "io.getstream.chat.multipart-upload-body",
|
|
221
|
+
qos: .userInitiated
|
|
222
|
+
)
|
|
117
223
|
}
|
|
118
224
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
internalDelegate
|
|
122
|
-
}
|
|
123
|
-
set {
|
|
124
|
-
internalDelegate = newValue
|
|
125
|
-
currentStream?.delegate = newValue
|
|
126
|
-
}
|
|
225
|
+
deinit {
|
|
226
|
+
buffer.deallocate()
|
|
127
227
|
}
|
|
128
228
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return false
|
|
132
|
-
}
|
|
229
|
+
func start() {
|
|
230
|
+
selfRetain = self
|
|
133
231
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
232
|
+
var context = CFStreamClientContext(
|
|
233
|
+
version: 0,
|
|
234
|
+
info: Unmanaged.passUnretained(self).toOpaque(),
|
|
235
|
+
retain: nil,
|
|
236
|
+
release: nil,
|
|
237
|
+
copyDescription: nil
|
|
238
|
+
)
|
|
140
239
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
240
|
+
let events: CFOptionFlags = CFStreamEventType.canAcceptBytes.rawValue
|
|
241
|
+
| CFStreamEventType.errorOccurred.rawValue
|
|
242
|
+
| CFStreamEventType.endEncountered.rawValue
|
|
144
243
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
244
|
+
let didSetClient = CFWriteStreamSetClient(
|
|
245
|
+
output,
|
|
246
|
+
events,
|
|
247
|
+
{ _, event, info in
|
|
248
|
+
guard let info else {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
Unmanaged<StreamMultipartBodyProducer>.fromOpaque(info)
|
|
252
|
+
.takeUnretainedValue()
|
|
253
|
+
.handle(event)
|
|
254
|
+
},
|
|
255
|
+
&context
|
|
256
|
+
)
|
|
148
257
|
|
|
149
|
-
|
|
150
|
-
|
|
258
|
+
guard didSetClient else {
|
|
259
|
+
// No callbacks will ever arrive; finishing here is safe because the dispatch queue has not
|
|
260
|
+
// been attached yet, so nothing else can be running.
|
|
261
|
+
finish(error: writeStreamError() ?? StreamMultipartUploadError.invalidRequest(
|
|
262
|
+
"Could not observe the request body stream"
|
|
263
|
+
))
|
|
151
264
|
return
|
|
152
265
|
}
|
|
153
266
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
267
|
+
// Deliver client callbacks on our serial queue instead of scheduling on a run loop, so the
|
|
268
|
+
// producer needs no thread of its own and GCD owns its lifetime.
|
|
269
|
+
CFWriteStreamSetDispatchQueue(output, queue)
|
|
270
|
+
|
|
271
|
+
guard CFWriteStreamOpen(output) else {
|
|
272
|
+
// The queue is attached now, so tear down on it to stay single-threaded.
|
|
273
|
+
queue.async { [self] in
|
|
274
|
+
finish(error: writeStreamError() ?? StreamMultipartUploadError.invalidRequest(
|
|
275
|
+
"Could not open the request body stream"
|
|
276
|
+
))
|
|
277
|
+
}
|
|
157
278
|
return
|
|
158
279
|
}
|
|
159
|
-
internalStatus = currentStream == nil ? .atEnd : .open
|
|
160
280
|
}
|
|
161
281
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
internalStatus = .closed
|
|
282
|
+
/// The write end's own error, when Core Foundation has one to give.
|
|
283
|
+
private func writeStreamError() -> Error? {
|
|
284
|
+
CFWriteStreamCopyError(output) as Error?
|
|
166
285
|
}
|
|
167
286
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
287
|
+
// MARK: - Callbacks (always on `queue`)
|
|
288
|
+
|
|
289
|
+
private func handle(_ event: CFStreamEventType) {
|
|
290
|
+
switch event {
|
|
291
|
+
case .canAcceptBytes:
|
|
292
|
+
pump()
|
|
293
|
+
case .errorOccurred:
|
|
294
|
+
// Usually the reader going away (a cancelled upload), but it can be a genuine write-side
|
|
295
|
+
// failure — record whatever CF gives us. Cancellation still wins in the manager, which
|
|
296
|
+
// checks `NSURLErrorCancelled` first.
|
|
297
|
+
finish(error: writeStreamError())
|
|
298
|
+
case .endEncountered:
|
|
299
|
+
finish(error: nil)
|
|
300
|
+
default:
|
|
301
|
+
break
|
|
302
|
+
}
|
|
171
303
|
}
|
|
172
304
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
305
|
+
private func pump() {
|
|
306
|
+
while !isFinished, CFWriteStreamCanAcceptBytes(output) {
|
|
307
|
+
if bufferOffset >= bufferLength {
|
|
308
|
+
switch refill() {
|
|
309
|
+
case .filled:
|
|
310
|
+
break
|
|
311
|
+
case .drained:
|
|
312
|
+
finish(error: nil)
|
|
313
|
+
return
|
|
314
|
+
case .failed(let error):
|
|
315
|
+
finish(error: error)
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
}
|
|
177
319
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
320
|
+
let written = CFWriteStreamWrite(
|
|
321
|
+
output,
|
|
322
|
+
buffer + bufferOffset,
|
|
323
|
+
bufferLength - bufferOffset
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
if written > 0 {
|
|
327
|
+
bufferOffset += written
|
|
328
|
+
continue
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if written == 0 {
|
|
332
|
+
// Backpressure, NOT end of body: `CFWriteStreamCanAcceptBytes` may answer true without
|
|
333
|
+
// knowing, and the pair reports 0 when it is full. The unwritten remainder stays in
|
|
334
|
+
// `buffer` at `bufferOffset`, so the next `.canAcceptBytes` resumes exactly here.
|
|
335
|
+
// Closing the stream here would silently truncate the body.
|
|
336
|
+
return
|
|
337
|
+
}
|
|
182
338
|
|
|
183
|
-
|
|
184
|
-
|
|
339
|
+
// A negative write is itself the failure signal — do not depend on CF having an error
|
|
340
|
+
// object, or the failure degrades into the clean stream close this refactor exists to
|
|
341
|
+
// disambiguate.
|
|
342
|
+
finish(error: writeStreamError() ?? StreamMultipartUploadError.invalidRequest(
|
|
343
|
+
"Could not write the request body stream"
|
|
344
|
+
))
|
|
345
|
+
return
|
|
185
346
|
}
|
|
347
|
+
}
|
|
186
348
|
|
|
349
|
+
/// Fills `buffer` from the next available element.
|
|
350
|
+
private func refill() -> Refill {
|
|
187
351
|
while true {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return
|
|
352
|
+
if currentStream == nil {
|
|
353
|
+
guard currentIndex < elements.count else {
|
|
354
|
+
return .drained
|
|
191
355
|
}
|
|
192
356
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
357
|
+
let element = elements[currentIndex]
|
|
358
|
+
currentIndex += 1
|
|
359
|
+
|
|
360
|
+
switch element {
|
|
361
|
+
case .data(let data):
|
|
362
|
+
currentStream = InputStream(data: data)
|
|
363
|
+
case .file(let url):
|
|
364
|
+
guard let stream = InputStream(url: url) else {
|
|
365
|
+
return .failed(StreamMultipartUploadError.unreadableFile(url.path))
|
|
366
|
+
}
|
|
367
|
+
currentStream = stream
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
guard let stream = currentStream else {
|
|
371
|
+
return .drained
|
|
372
|
+
}
|
|
196
373
|
|
|
197
|
-
|
|
374
|
+
stream.open()
|
|
198
375
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
376
|
+
if stream.streamStatus == .error {
|
|
377
|
+
return .failed(stream.streamError ?? StreamMultipartUploadError.unreadableFile(elementPath()))
|
|
378
|
+
}
|
|
202
379
|
}
|
|
203
380
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
internalStatus = .error
|
|
207
|
-
return -1
|
|
381
|
+
guard let stream = currentStream else {
|
|
382
|
+
return .drained
|
|
208
383
|
}
|
|
209
384
|
|
|
210
|
-
|
|
211
|
-
self.currentStream = nil
|
|
212
|
-
advanceStreamIfNeeded()
|
|
385
|
+
let read = stream.read(buffer, maxLength: StreamMultipartBodyProducer.transferBufferSize)
|
|
213
386
|
|
|
214
|
-
if
|
|
215
|
-
|
|
216
|
-
|
|
387
|
+
if read > 0 {
|
|
388
|
+
bufferOffset = 0
|
|
389
|
+
bufferLength = read
|
|
390
|
+
return .filled
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
let readError = read < 0 ? (stream.streamError ?? StreamMultipartUploadError.unreadableFile(elementPath())) : nil
|
|
394
|
+
stream.close()
|
|
395
|
+
currentStream = nil
|
|
396
|
+
|
|
397
|
+
if let readError {
|
|
398
|
+
return .failed(readError)
|
|
217
399
|
}
|
|
218
400
|
}
|
|
219
401
|
}
|
|
220
402
|
|
|
221
|
-
private func
|
|
222
|
-
guard
|
|
403
|
+
private func elementPath() -> String {
|
|
404
|
+
guard currentIndex > 0, case .file(let url) = elements[currentIndex - 1] else {
|
|
405
|
+
return ""
|
|
406
|
+
}
|
|
407
|
+
return url.path
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private func finish(error: Error?) {
|
|
411
|
+
guard !isFinished else {
|
|
223
412
|
return
|
|
224
413
|
}
|
|
225
414
|
|
|
226
|
-
|
|
227
|
-
let nextElement = elements[currentIndex]
|
|
228
|
-
currentIndex += 1
|
|
229
|
-
|
|
230
|
-
let nextStream: InputStream?
|
|
231
|
-
switch nextElement {
|
|
232
|
-
case .data(let data):
|
|
233
|
-
nextStream = InputStream(data: data)
|
|
234
|
-
case .file(let url):
|
|
235
|
-
nextStream = InputStream(url: url)
|
|
236
|
-
if nextStream == nil {
|
|
237
|
-
internalError = StreamMultipartUploadError.unreadableFile(url.path)
|
|
238
|
-
internalStatus = .error
|
|
239
|
-
return
|
|
240
|
-
}
|
|
241
|
-
}
|
|
415
|
+
isFinished = true
|
|
242
416
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
for scheduled in scheduledRunLoops {
|
|
246
|
-
nextStream.schedule(in: scheduled.runLoop, forMode: scheduled.mode)
|
|
247
|
-
}
|
|
248
|
-
nextStream.open()
|
|
249
|
-
currentStream = nextStream
|
|
250
|
-
return
|
|
251
|
-
}
|
|
417
|
+
if let error {
|
|
418
|
+
errorBox.record(error)
|
|
252
419
|
}
|
|
420
|
+
|
|
421
|
+
currentStream?.close()
|
|
422
|
+
currentStream = nil
|
|
423
|
+
|
|
424
|
+
// Unregister before dropping the retain so no callback can arrive against a dead pointer.
|
|
425
|
+
CFWriteStreamSetClient(output, 0, nil, nil)
|
|
426
|
+
CFWriteStreamSetDispatchQueue(output, nil)
|
|
427
|
+
// Closing the write end is what surfaces end-of-stream on the read end.
|
|
428
|
+
CFWriteStreamClose(output)
|
|
429
|
+
|
|
430
|
+
selfRetain = nil
|
|
253
431
|
}
|
|
254
432
|
}
|
|
@@ -390,6 +390,11 @@ extension StreamMultipartUploadManager: URLSessionDataDelegate, URLSessionTaskDe
|
|
|
390
390
|
|
|
391
391
|
if nsError.domain == NSURLErrorDomain, nsError.code == NSURLErrorCancelled {
|
|
392
392
|
state.completion?(.failure(StreamMultipartUploadError.cancelled))
|
|
393
|
+
} else if let bodyError = state.bodyFactory.bodyError {
|
|
394
|
+
// The request body could not be produced in full. A bound stream pair has no error
|
|
395
|
+
// channel — the reader only sees a truncated body — so prefer the recorded cause over
|
|
396
|
+
// the transport error it surfaces as.
|
|
397
|
+
state.completion?(.failure(bodyError))
|
|
393
398
|
} else {
|
|
394
399
|
state.completion?(.failure(nsError))
|
|
395
400
|
}
|
|
@@ -397,6 +402,16 @@ extension StreamMultipartUploadManager: URLSessionDataDelegate, URLSessionTaskDe
|
|
|
397
402
|
return
|
|
398
403
|
}
|
|
399
404
|
|
|
405
|
+
// The task completed without a transport error, but the body may still not have been produced
|
|
406
|
+
// in full. A bound stream pair has no error channel, so a producer failure closes the write end
|
|
407
|
+
// and the reader sees a clean EOF — with no `Content-Length` (chunked) that is a well-formed
|
|
408
|
+
// short body the server can happily accept. Never report a truncated upload as a success.
|
|
409
|
+
if let bodyError = state.bodyFactory.bodyError {
|
|
410
|
+
state.completion?(.failure(bodyError))
|
|
411
|
+
state.completion = nil
|
|
412
|
+
return
|
|
413
|
+
}
|
|
414
|
+
|
|
400
415
|
guard let response = state.response else {
|
|
401
416
|
state.completion?(.failure(StreamMultipartUploadError.missingHTTPResponse))
|
|
402
417
|
state.completion = nil
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat-react-native",
|
|
3
3
|
"description": "The official React Native SDK for Stream Chat, a service for building chat applications",
|
|
4
|
-
"version": "9.8.
|
|
4
|
+
"version": "9.8.1-beta.2",
|
|
5
5
|
"homepage": "https://www.npmjs.com/package/stream-chat-react-native",
|
|
6
6
|
"author": {
|
|
7
7
|
"company": "Stream.io Inc",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"es6-symbol": "^3.1.3",
|
|
32
32
|
"mime": "^4.0.7",
|
|
33
|
-
"stream-chat-react-native-core": "9.8.
|
|
33
|
+
"stream-chat-react-native-core": "9.8.1-beta.2"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@react-native-camera-roll/camera-roll": ">=7.9.0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@stream-io/typescript-config": "workspace:^",
|
|
87
|
-
"react-native": "0.
|
|
87
|
+
"react-native": "0.87.0",
|
|
88
88
|
"typescript": "6.0.3"
|
|
89
89
|
},
|
|
90
90
|
"codegenConfig": {
|