react-native-nitro-sse 0.1.0-beta.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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/NitroSse.podspec +30 -0
  3. package/README.md +121 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +120 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  8. package/android/src/main/java/com/margelo/nitro/nitrosse/NitroSse.kt +279 -0
  9. package/android/src/main/java/com/margelo/nitro/nitrosse/NitroSsePackage.kt +22 -0
  10. package/ios/NitroSse.swift +311 -0
  11. package/lib/module/NitroSse.nitro.js +4 -0
  12. package/lib/module/NitroSse.nitro.js.map +1 -0
  13. package/lib/module/SseInterface.js +2 -0
  14. package/lib/module/SseInterface.js.map +1 -0
  15. package/lib/module/index.js +22 -0
  16. package/lib/module/index.js.map +1 -0
  17. package/lib/module/package.json +1 -0
  18. package/lib/typescript/package.json +1 -0
  19. package/lib/typescript/src/NitroSse.nitro.d.ts +34 -0
  20. package/lib/typescript/src/NitroSse.nitro.d.ts.map +1 -0
  21. package/lib/typescript/src/SseInterface.d.ts +25 -0
  22. package/lib/typescript/src/SseInterface.d.ts.map +1 -0
  23. package/lib/typescript/src/index.d.ts +5 -0
  24. package/lib/typescript/src/index.d.ts.map +1 -0
  25. package/nitro.json +17 -0
  26. package/nitrogen/generated/android/c++/JFunc_void_std__vector_SseEvent_.hpp +100 -0
  27. package/nitrogen/generated/android/c++/JHttpMethod.hpp +58 -0
  28. package/nitrogen/generated/android/c++/JHybridNitroSseSpec.cpp +110 -0
  29. package/nitrogen/generated/android/c++/JHybridNitroSseSpec.hpp +71 -0
  30. package/nitrogen/generated/android/c++/JSseConfig.hpp +98 -0
  31. package/nitrogen/generated/android/c++/JSseEvent.hpp +76 -0
  32. package/nitrogen/generated/android/c++/JSseEventType.hpp +67 -0
  33. package/nitrogen/generated/android/c++/JSseStats.hpp +70 -0
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/Func_void_std__vector_SseEvent_.kt +80 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/HttpMethod.kt +23 -0
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/HybridNitroSseSpec.kt +82 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/SseConfig.kt +56 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/SseEvent.kt +50 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/SseEventType.kt +26 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/SseStats.kt +47 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrosse/nitrosseOnLoad.kt +35 -0
  42. package/nitrogen/generated/android/nitrosse+autolinking.cmake +81 -0
  43. package/nitrogen/generated/android/nitrosse+autolinking.gradle +27 -0
  44. package/nitrogen/generated/android/nitrosseOnLoad.cpp +46 -0
  45. package/nitrogen/generated/android/nitrosseOnLoad.hpp +25 -0
  46. package/nitrogen/generated/ios/NitroSse+autolinking.rb +60 -0
  47. package/nitrogen/generated/ios/NitroSse-Swift-Cxx-Bridge.cpp +41 -0
  48. package/nitrogen/generated/ios/NitroSse-Swift-Cxx-Bridge.hpp +210 -0
  49. package/nitrogen/generated/ios/NitroSse-Swift-Cxx-Umbrella.hpp +63 -0
  50. package/nitrogen/generated/ios/NitroSseAutolinking.mm +33 -0
  51. package/nitrogen/generated/ios/NitroSseAutolinking.swift +26 -0
  52. package/nitrogen/generated/ios/c++/HybridNitroSseSpecSwift.cpp +11 -0
  53. package/nitrogen/generated/ios/c++/HybridNitroSseSpecSwift.hpp +130 -0
  54. package/nitrogen/generated/ios/swift/Func_void_std__vector_SseEvent_.swift +46 -0
  55. package/nitrogen/generated/ios/swift/HttpMethod.swift +40 -0
  56. package/nitrogen/generated/ios/swift/HybridNitroSseSpec.swift +60 -0
  57. package/nitrogen/generated/ios/swift/HybridNitroSseSpec_cxx.swift +212 -0
  58. package/nitrogen/generated/ios/swift/SseConfig.swift +130 -0
  59. package/nitrogen/generated/ios/swift/SseEvent.swift +101 -0
  60. package/nitrogen/generated/ios/swift/SseEventType.swift +52 -0
  61. package/nitrogen/generated/ios/swift/SseStats.swift +63 -0
  62. package/nitrogen/generated/shared/c++/HttpMethod.hpp +76 -0
  63. package/nitrogen/generated/shared/c++/HybridNitroSseSpec.cpp +26 -0
  64. package/nitrogen/generated/shared/c++/HybridNitroSseSpec.hpp +78 -0
  65. package/nitrogen/generated/shared/c++/SseConfig.hpp +111 -0
  66. package/nitrogen/generated/shared/c++/SseEvent.hpp +102 -0
  67. package/nitrogen/generated/shared/c++/SseEventType.hpp +88 -0
  68. package/nitrogen/generated/shared/c++/SseStats.hpp +96 -0
  69. package/package.json +179 -0
  70. package/src/NitroSse.nitro.ts +37 -0
  71. package/src/SseInterface.ts +28 -0
  72. package/src/index.tsx +26 -0
@@ -0,0 +1,311 @@
1
+ import Foundation
2
+ import NitroModules
3
+ import LDSwiftEventSource
4
+
5
+ /**
6
+ * NitroSse implements a high-performance SSE client for iOS using LDSwiftEventSource.
7
+ *
8
+ * ARCHITECTURE DECISIONS:
9
+ * 1. Threading Serialization: All operations are strictly serialized on a dedicated background queue (sseQueue).
10
+ * This ensures thread-safety for internal states (buffer, backoff) and prevents blocking the JS/Main threads.
11
+ * 2. Mobile Survival Logic: Implements a "Hibernation" pattern. When the app enters the background,
12
+ * we flush remaining events and stop the socket to preserve battery and follow Apple's background policies.
13
+ * The connection is automatically resumed from the last known ID when the app returns to foreground.
14
+ * 3. Batching: Reduces JSI bridge overhead by accumulating events and dispatching them
15
+ * as a single array after a configurable interval.
16
+ */
17
+ class NitroSse: HybridNitroSseSpec, EventHandler {
18
+ private var eventSource: EventSource?
19
+ private var config: SseConfig?
20
+ private var onEventsCallback: ((_ events: [SseEvent]) -> Void)?
21
+ private var isRunning: Bool = false
22
+
23
+ private var eventBuffer: [SseEvent] = []
24
+ private var isFlushPending: Bool = false
25
+
26
+ private var backoffCounter: Int = 0
27
+ private var lastProcessedId: String? = nil
28
+
29
+ private var totalBytesReceived: Double = 0
30
+ private var reconnectCount: Double = 0
31
+ private var lastErrorTime: Double? = nil
32
+ private var lastErrorCode: String? = nil
33
+
34
+ private let defaultRetryDelay: TimeInterval = 3.0
35
+ private let baseBackoffDelay: TimeInterval = 2.0
36
+ private let maxBackoffDelay: TimeInterval = 30.0
37
+
38
+ private let sseQueue = DispatchQueue(label: "com.margelo.nitro.sse", qos: .utility)
39
+ private var backgroundTaskIdentifier: UIBackgroundTaskIdentifier = .invalid
40
+ private var wasRunningBeforeHibernation: Bool = false
41
+
42
+ func setup(config: SseConfig, onEvent: @escaping ((_ events: [SseEvent]) -> Void)) throws {
43
+ self.config = config
44
+ self.onEventsCallback = onEvent
45
+
46
+ NotificationCenter.default.addObserver(self, selector: #selector(handleAppDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
47
+ NotificationCenter.default.addObserver(self, selector: #selector(handleAppWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
48
+ }
49
+
50
+ @objc private func handleAppDidEnterBackground() {
51
+ sseQueue.async {
52
+ guard self.isRunning else { return }
53
+
54
+ self.wasRunningBeforeHibernation = true
55
+ print("[NitroSse] App backgrounded. Hibernating connection.")
56
+
57
+ self.backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(withName: "NitroSse-GracefulHibernate") {
58
+ self.cleanupBackgroundTask()
59
+ }
60
+
61
+ self.flushEventsToJs()
62
+
63
+ self.eventSource?.stop()
64
+ self.eventSource = nil
65
+ self.isRunning = false
66
+
67
+ self.cleanupBackgroundTask()
68
+ }
69
+ }
70
+
71
+ @objc private func handleAppWillEnterForeground() {
72
+ sseQueue.async {
73
+ self.cleanupBackgroundTask()
74
+ if self.wasRunningBeforeHibernation {
75
+ print("[NitroSse] App foregrounded. Resuming stream.")
76
+ self.wasRunningBeforeHibernation = false
77
+ try? self.start()
78
+ }
79
+ }
80
+ }
81
+
82
+ private func cleanupBackgroundTask() {
83
+ if self.backgroundTaskIdentifier != .invalid {
84
+ UIApplication.shared.endBackgroundTask(self.backgroundTaskIdentifier)
85
+ self.backgroundTaskIdentifier = .invalid
86
+ }
87
+ }
88
+
89
+ private func pushEventToBuffer(_ event: SseEvent) {
90
+ dispatchPrecondition(condition: .onQueue(sseQueue))
91
+
92
+ let batchIntervalMs = config?.batchingIntervalMs ?? 0
93
+ let bufferCapacity = Int(config?.maxBufferSize ?? 1000)
94
+
95
+ while eventBuffer.count >= bufferCapacity {
96
+ eventBuffer.removeFirst()
97
+ }
98
+ eventBuffer.append(event)
99
+
100
+ if batchIntervalMs <= 0 {
101
+ flushEventsToJs()
102
+ } else if !isFlushPending {
103
+ isFlushPending = true
104
+ sseQueue.asyncAfter(deadline: .now() + (Double(batchIntervalMs) / 1000.0)) { [weak self] in
105
+ self?.flushEventsToJs()
106
+ }
107
+ }
108
+ }
109
+
110
+ private func flushEventsToJs() {
111
+ dispatchPrecondition(condition: .onQueue(sseQueue))
112
+ guard !eventBuffer.isEmpty else {
113
+ isFlushPending = false
114
+ return
115
+ }
116
+
117
+ let batch = eventBuffer
118
+ eventBuffer.removeAll()
119
+ isFlushPending = false
120
+ onEventsCallback?(batch)
121
+ }
122
+
123
+ func setLastProcessedId(id: String) {
124
+ sseQueue.async {
125
+ self.lastProcessedId = id
126
+ }
127
+ }
128
+
129
+ func updateHeaders(headers: [String: String]) throws {
130
+ sseQueue.async {
131
+ if let currentConfig = self.config {
132
+ self.config = SseConfig(
133
+ url: currentConfig.url,
134
+ method: currentConfig.method,
135
+ headers: headers,
136
+ body: currentConfig.body,
137
+ backgroundExecution: currentConfig.backgroundExecution,
138
+ batchingIntervalMs: currentConfig.batchingIntervalMs,
139
+ maxBufferSize: currentConfig.maxBufferSize
140
+ )
141
+ }
142
+ }
143
+ }
144
+
145
+ func getStats() throws -> SseStats {
146
+ return sseQueue.sync {
147
+ return SseStats(
148
+ totalBytesReceived: totalBytesReceived,
149
+ reconnectCount: reconnectCount,
150
+ lastErrorTime: lastErrorTime,
151
+ lastErrorCode: lastErrorCode
152
+ )
153
+ }
154
+ }
155
+
156
+ func start() throws {
157
+ sseQueue.async {
158
+ guard !self.isRunning else { return }
159
+ self.isRunning = true
160
+ self.backoffCounter = 0
161
+ self.establishConnection()
162
+ }
163
+ }
164
+
165
+ private func establishConnection() {
166
+ dispatchPrecondition(condition: .onQueue(sseQueue))
167
+ guard isRunning, let config = config, let url = URL(string: config.url) else { return }
168
+
169
+ var esConfig = EventSource.Config(handler: self, url: url)
170
+ esConfig.headers = config.headers ?? [:]
171
+
172
+ if let lastId = self.lastProcessedId, !lastId.isEmpty {
173
+ esConfig.headers["Last-Event-ID"] = lastId
174
+ }
175
+
176
+ esConfig.idleTimeout = 35.0
177
+ esConfig.method = config.method?.stringValue.uppercased() ?? "GET"
178
+ esConfig.body = config.body?.data(using: .utf8)
179
+
180
+ eventSource = EventSource(config: esConfig)
181
+ eventSource?.start()
182
+ }
183
+
184
+ func stop() {
185
+ sseQueue.async {
186
+ self.isRunning = false
187
+ self.eventSource?.stop()
188
+ self.eventSource = nil
189
+ self.backoffCounter = 0
190
+ self.eventBuffer.removeAll()
191
+ self.isFlushPending = false
192
+ self.cleanupBackgroundTask()
193
+ }
194
+ }
195
+
196
+ func onOpened() {
197
+ sseQueue.async {
198
+ self.backoffCounter = 0
199
+ self.pushEventToBuffer(SseEvent(type: .open, data: nil, id: nil, event: nil, message: nil))
200
+ }
201
+ }
202
+
203
+ func onClosed() {
204
+ sseQueue.async {
205
+ if self.isRunning {
206
+ self.scheduleAutomaticReconnect(isError: false)
207
+ }
208
+ }
209
+ }
210
+
211
+ func onMessage(eventType: String, messageEvent: MessageEvent) {
212
+ sseQueue.async {
213
+ let encodedDataSize = Double(messageEvent.data.utf8.count)
214
+ let metadataSize = Double(eventType.utf8.count) + Double((messageEvent.lastEventId).utf8.count)
215
+ self.totalBytesReceived += encodedDataSize + metadataSize
216
+
217
+ self.pushEventToBuffer(SseEvent(type: .message, data: messageEvent.data, id: messageEvent.lastEventId, event: eventType, message: nil))
218
+ }
219
+ }
220
+
221
+ func onComment(comment: String) {
222
+ sseQueue.async {
223
+ self.totalBytesReceived += Double(comment.utf8.count)
224
+ self.pushEventToBuffer(SseEvent(type: .heartbeat, data: nil, id: nil, event: nil, message: comment))
225
+ }
226
+ }
227
+
228
+ private func extractRetryAfterSeconds(error: Error) -> TimeInterval? {
229
+ let nsError = error as NSError
230
+ guard let response = nsError.userInfo["response"] as? HTTPURLResponse else { return nil }
231
+ guard let retryAfterHeader = response.allHeaderFields["Retry-After"] as? String else { return nil }
232
+
233
+ if let seconds = Double(retryAfterHeader) {
234
+ return seconds
235
+ }
236
+
237
+ let rfc1123Formatter = DateFormatter()
238
+ rfc1123Formatter.locale = Locale(identifier: "en_US_POSIX")
239
+ rfc1123Formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z"
240
+ if let date = rfc1123Formatter.date(from: retryAfterHeader) {
241
+ let timeUntilDate = date.timeIntervalSinceNow
242
+ return timeUntilDate > 0 ? timeUntilDate : nil
243
+ }
244
+ return nil
245
+ }
246
+
247
+ func onError(error: Error) {
248
+ sseQueue.async {
249
+ guard self.isRunning else { return }
250
+ let nsError = error as NSError
251
+ let statusCode = nsError.code
252
+
253
+ self.reconnectCount += 1
254
+ self.lastErrorTime = Date().timeIntervalSince1970 * 1000
255
+ self.lastErrorCode = "\(nsError.domain)(\(statusCode))"
256
+
257
+ let isFatalError = (statusCode == 401 || statusCode == 403 || statusCode == 400)
258
+ if isFatalError {
259
+ self.pushEventToBuffer(SseEvent(type: .error, data: nil, id: nil, event: nil, message: "Fatal Error (\(statusCode)). Stopping."))
260
+ self.stop()
261
+ return
262
+ }
263
+
264
+ let retryAfterSeconds = self.extractRetryAfterSeconds(error: error)
265
+ if (statusCode == 429 || statusCode == 503), let delay = retryAfterSeconds {
266
+ let jitter = Double.random(in: 0.5...2.0)
267
+ let totalDelay = delay + jitter
268
+ self.pushEventToBuffer(SseEvent(type: .error, data: nil, id: nil, event: nil, message: "Retry-After received (\(statusCode))"))
269
+ self.scheduleAutomaticReconnectWithFixedDelay(totalDelay)
270
+ return
271
+ }
272
+
273
+ if statusCode == 429 {
274
+ self.pushEventToBuffer(SseEvent(type: .error, data: nil, id: nil, event: nil, message: "Rate Limited (429) without Retry-After. Stopping."))
275
+ self.stop()
276
+ return
277
+ }
278
+
279
+ self.pushEventToBuffer(SseEvent(type: .error, data: nil, id: nil, event: nil, message: error.localizedDescription))
280
+ self.scheduleAutomaticReconnect(isError: true)
281
+ }
282
+ }
283
+
284
+ private func scheduleAutomaticReconnectWithFixedDelay(_ delay: TimeInterval) {
285
+ dispatchPrecondition(condition: .onQueue(sseQueue))
286
+ eventSource?.stop()
287
+ sseQueue.asyncAfter(deadline: .now() + delay) { [weak self] in
288
+ guard let self = self, self.isRunning else { return }
289
+ self.establishConnection()
290
+ }
291
+ }
292
+
293
+ private func scheduleAutomaticReconnect(isError: Bool) {
294
+ dispatchPrecondition(condition: .onQueue(sseQueue))
295
+ eventSource?.stop()
296
+ var delay: TimeInterval = defaultRetryDelay
297
+ if isError {
298
+ let exponent = Double(backoffCounter)
299
+ let base = min(baseBackoffDelay * pow(2.0, exponent), maxBackoffDelay)
300
+ backoffCounter += 1
301
+ delay = base * (0.5 + Double.random(in: 0...1))
302
+ } else {
303
+ delay = defaultRetryDelay * (0.8 + Double.random(in: 0...0.4))
304
+ }
305
+ let safeDelay = max(delay, 2.0)
306
+ sseQueue.asyncAfter(deadline: .now() + safeDelay) { [weak self] in
307
+ guard let self = self, self.isRunning else { return }
308
+ self.establishConnection()
309
+ }
310
+ }
311
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=NitroSse.nitro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../src","sources":["NitroSse.nitro.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=SseInterface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../src","sources":["SseInterface.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ import { NitroModules } from 'react-native-nitro-modules';
4
+ export * from "./SseInterface.js";
5
+ export * from "./NitroSse.nitro.js";
6
+
7
+ // Load the Hybrid Object from Native
8
+ let realNitroSse;
9
+ try {
10
+ realNitroSse = NitroModules.createHybridObject('NitroSse');
11
+ } catch {
12
+ console.debug('Native NitroSse not found. This might be a test environment or web.');
13
+ }
14
+
15
+ /**
16
+ * Public interface to use SSE.
17
+ */
18
+ if (!realNitroSse || !realNitroSse.setup) {
19
+ throw new Error('NitroSse: Native module not found. Ensure you have linked the library and built the app for iOS/Android.');
20
+ }
21
+ export const NitroSseModule = realNitroSse;
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NitroModules","realNitroSse","createHybridObject","console","debug","setup","Error","NitroSseModule"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAGzD,cAAc,mBAAgB;AAC9B,cAAc,qBAAkB;;AAEhC;AACA,IAAIC,YAAkC;AACtC,IAAI;EACFA,YAAY,GAAGD,YAAY,CAACE,kBAAkB,CAAW,UAAU,CAAC;AACtE,CAAC,CAAC,MAAM;EACNC,OAAO,CAACC,KAAK,CACX,qEACF,CAAC;AACH;;AAEA;AACA;AACA;AACA,IAAI,CAACH,YAAY,IAAI,CAAEA,YAAY,CAASI,KAAK,EAAE;EACjD,MAAM,IAAIC,KAAK,CACb,0GACF,CAAC;AACH;AAEA,OAAO,MAAMC,cAAwB,GAAGN,YAAY","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,34 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ import type { SseConfig, SseEvent, SseStats } from './SseInterface';
3
+ export interface NitroSse extends HybridObject<{
4
+ ios: 'swift';
5
+ android: 'kotlin';
6
+ }> {
7
+ /**
8
+ * Configure SSE and setup event callback.
9
+ */
10
+ setup(config: SseConfig, onEvent: (events: SseEvent[]) => void): void;
11
+ /**
12
+ * Start the SSE connection.
13
+ */
14
+ start(): void;
15
+ /**
16
+ * Stop the SSE connection.
17
+ */
18
+ stop(): void;
19
+ /**
20
+ * Set the last processed event ID.
21
+ * Native will use this ID to resume connection if interrupted.
22
+ */
23
+ setLastProcessedId(id: string): void;
24
+ /**
25
+ * Update HTTP headers dynamically (e.g., when token expires).
26
+ * These headers will be used for subsequent connection/reconnection attempts.
27
+ */
28
+ updateHeaders(headers: Record<string, string>): void;
29
+ /**
30
+ * Get connection statistics.
31
+ */
32
+ getStats(): SseStats;
33
+ }
34
+ //# sourceMappingURL=NitroSse.nitro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NitroSse.nitro.d.ts","sourceRoot":"","sources":["../../../src/NitroSse.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEpE,MAAM,WAAW,QACf,SAAQ,YAAY,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;IACzD;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAEtE;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;OAEG;IACH,IAAI,IAAI,IAAI,CAAC;IAEb;;;OAGG;IACH,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAErC;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAErD;;OAEG;IACH,QAAQ,IAAI,QAAQ,CAAC;CACtB"}
@@ -0,0 +1,25 @@
1
+ export type HttpMethod = 'get' | 'post';
2
+ export type SseEventType = 'open' | 'message' | 'error' | 'close' | 'heartbeat';
3
+ export interface SseConfig {
4
+ url: string;
5
+ method?: HttpMethod;
6
+ headers?: Record<string, string>;
7
+ body?: string;
8
+ backgroundExecution?: boolean;
9
+ batchingIntervalMs?: number;
10
+ maxBufferSize?: number;
11
+ }
12
+ export interface SseEvent {
13
+ type: SseEventType;
14
+ data?: string;
15
+ id?: string;
16
+ event?: string;
17
+ message?: string;
18
+ }
19
+ export interface SseStats {
20
+ totalBytesReceived: number;
21
+ reconnectCount: number;
22
+ lastErrorTime?: number;
23
+ lastErrorCode?: string;
24
+ }
25
+ //# sourceMappingURL=SseInterface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SseInterface.d.ts","sourceRoot":"","sources":["../../../src/SseInterface.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;AAExC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,CAAC;AAEhF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
@@ -0,0 +1,5 @@
1
+ import type { NitroSse } from './NitroSse.nitro';
2
+ export * from './SseInterface';
3
+ export * from './NitroSse.nitro';
4
+ export declare const NitroSseModule: NitroSse;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAqBjC,eAAO,MAAM,cAAc,EAAE,QAAuB,CAAC"}
package/nitro.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "cxxNamespace": ["nitrosse"],
3
+ "ios": {
4
+ "iosModuleName": "NitroSse"
5
+ },
6
+ "android": {
7
+ "androidNamespace": ["nitrosse"],
8
+ "androidCxxLibName": "nitrosse"
9
+ },
10
+ "autolinking": {
11
+ "NitroSse": {
12
+ "swift": "NitroSse",
13
+ "kotlin": "NitroSse"
14
+ }
15
+ },
16
+ "ignorePaths": ["node_modules"]
17
+ }
@@ -0,0 +1,100 @@
1
+ ///
2
+ /// JFunc_void_std__vector_SseEvent_.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #include <fbjni/fbjni.h>
11
+ #include <functional>
12
+
13
+ #include "SseEvent.hpp"
14
+ #include <vector>
15
+ #include <functional>
16
+ #include <NitroModules/JNICallable.hpp>
17
+ #include "JSseEvent.hpp"
18
+ #include "SseEventType.hpp"
19
+ #include "JSseEventType.hpp"
20
+ #include <string>
21
+ #include <optional>
22
+
23
+ namespace margelo::nitro::nitrosse {
24
+
25
+ using namespace facebook;
26
+
27
+ /**
28
+ * Represents the Java/Kotlin callback `(events: Array<SseEvent>) -> Unit`.
29
+ * This can be passed around between C++ and Java/Kotlin.
30
+ */
31
+ struct JFunc_void_std__vector_SseEvent_: public jni::JavaClass<JFunc_void_std__vector_SseEvent_> {
32
+ public:
33
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/nitrosse/Func_void_std__vector_SseEvent_;";
34
+
35
+ public:
36
+ /**
37
+ * Invokes the function this `JFunc_void_std__vector_SseEvent_` instance holds through JNI.
38
+ */
39
+ void invoke(const std::vector<SseEvent>& events) const {
40
+ static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JArrayClass<JSseEvent>> /* events */)>("invoke");
41
+ method(self(), [&]() {
42
+ size_t __size = events.size();
43
+ jni::local_ref<jni::JArrayClass<JSseEvent>> __array = jni::JArrayClass<JSseEvent>::newArray(__size);
44
+ for (size_t __i = 0; __i < __size; __i++) {
45
+ const auto& __element = events[__i];
46
+ auto __elementJni = JSseEvent::fromCpp(__element);
47
+ __array->setElement(__i, *__elementJni);
48
+ }
49
+ return __array;
50
+ }());
51
+ }
52
+ };
53
+
54
+ /**
55
+ * An implementation of Func_void_std__vector_SseEvent_ that is backed by a C++ implementation (using `std::function<...>`)
56
+ */
57
+ class JFunc_void_std__vector_SseEvent__cxx final: public jni::HybridClass<JFunc_void_std__vector_SseEvent__cxx, JFunc_void_std__vector_SseEvent_> {
58
+ public:
59
+ static jni::local_ref<JFunc_void_std__vector_SseEvent_::javaobject> fromCpp(const std::function<void(const std::vector<SseEvent>& /* events */)>& func) {
60
+ return JFunc_void_std__vector_SseEvent__cxx::newObjectCxxArgs(func);
61
+ }
62
+
63
+ public:
64
+ /**
65
+ * Invokes the C++ `std::function<...>` this `JFunc_void_std__vector_SseEvent__cxx` instance holds.
66
+ */
67
+ void invoke_cxx(jni::alias_ref<jni::JArrayClass<JSseEvent>> events) {
68
+ _func([&]() {
69
+ size_t __size = events->size();
70
+ std::vector<SseEvent> __vector;
71
+ __vector.reserve(__size);
72
+ for (size_t __i = 0; __i < __size; __i++) {
73
+ auto __element = events->getElement(__i);
74
+ __vector.push_back(__element->toCpp());
75
+ }
76
+ return __vector;
77
+ }());
78
+ }
79
+
80
+ public:
81
+ [[nodiscard]]
82
+ inline const std::function<void(const std::vector<SseEvent>& /* events */)>& getFunction() const {
83
+ return _func;
84
+ }
85
+
86
+ public:
87
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/nitrosse/Func_void_std__vector_SseEvent__cxx;";
88
+ static void registerNatives() {
89
+ registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_std__vector_SseEvent__cxx::invoke_cxx)});
90
+ }
91
+
92
+ private:
93
+ explicit JFunc_void_std__vector_SseEvent__cxx(const std::function<void(const std::vector<SseEvent>& /* events */)>& func): _func(func) { }
94
+
95
+ private:
96
+ friend HybridBase;
97
+ std::function<void(const std::vector<SseEvent>& /* events */)> _func;
98
+ };
99
+
100
+ } // namespace margelo::nitro::nitrosse
@@ -0,0 +1,58 @@
1
+ ///
2
+ /// JHttpMethod.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #include <fbjni/fbjni.h>
11
+ #include "HttpMethod.hpp"
12
+
13
+ namespace margelo::nitro::nitrosse {
14
+
15
+ using namespace facebook;
16
+
17
+ /**
18
+ * The C++ JNI bridge between the C++ enum "HttpMethod" and the the Kotlin enum "HttpMethod".
19
+ */
20
+ struct JHttpMethod final: public jni::JavaClass<JHttpMethod> {
21
+ public:
22
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/nitrosse/HttpMethod;";
23
+
24
+ public:
25
+ /**
26
+ * Convert this Java/Kotlin-based enum to the C++ enum HttpMethod.
27
+ */
28
+ [[maybe_unused]]
29
+ [[nodiscard]]
30
+ HttpMethod toCpp() const {
31
+ static const auto clazz = javaClassStatic();
32
+ static const auto fieldOrdinal = clazz->getField<int>("value");
33
+ int ordinal = this->getFieldValue(fieldOrdinal);
34
+ return static_cast<HttpMethod>(ordinal);
35
+ }
36
+
37
+ public:
38
+ /**
39
+ * Create a Java/Kotlin-based enum with the given C++ enum's value.
40
+ */
41
+ [[maybe_unused]]
42
+ static jni::alias_ref<JHttpMethod> fromCpp(HttpMethod value) {
43
+ static const auto clazz = javaClassStatic();
44
+ switch (value) {
45
+ case HttpMethod::GET:
46
+ static const auto fieldGET = clazz->getStaticField<JHttpMethod>("GET");
47
+ return clazz->getStaticFieldValue(fieldGET);
48
+ case HttpMethod::POST:
49
+ static const auto fieldPOST = clazz->getStaticField<JHttpMethod>("POST");
50
+ return clazz->getStaticFieldValue(fieldPOST);
51
+ default:
52
+ std::string stringValue = std::to_string(static_cast<int>(value));
53
+ throw std::invalid_argument("Invalid enum value (" + stringValue + "!");
54
+ }
55
+ }
56
+ };
57
+
58
+ } // namespace margelo::nitro::nitrosse