react-native-nitro-dgram 0.1.4

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 (74) hide show
  1. package/android/CMakeLists.txt +24 -0
  2. package/android/OnLoad.cpp +6 -0
  3. package/android/build.gradle +64 -0
  4. package/android/libs/arm64-v8a/librust_c_udp.so +0 -0
  5. package/android/libs/armeabi-v7a/librust_c_udp.so +0 -0
  6. package/android/libs/x86/librust_c_udp.so +0 -0
  7. package/android/libs/x86_64/librust_c_udp.so +0 -0
  8. package/android/src/main/java/com/margelo/nitro/udp/NitroUdpPackage.java +25 -0
  9. package/cpp/HybridUdpDriver.cpp +21 -0
  10. package/cpp/HybridUdpDriver.hpp +18 -0
  11. package/cpp/HybridUdpSocketDriver.cpp +252 -0
  12. package/cpp/HybridUdpSocketDriver.hpp +88 -0
  13. package/cpp/UdpBindings.hpp +83 -0
  14. package/cpp/UdpManager.hpp +108 -0
  15. package/ios/Frameworks/RustCUdp.xcframework/Info.plist +44 -0
  16. package/ios/Frameworks/RustCUdp.xcframework/ios-arm64/RustCUdp.framework/Info.plist +20 -0
  17. package/ios/Frameworks/RustCUdp.xcframework/ios-arm64/RustCUdp.framework/RustCUdp +0 -0
  18. package/ios/Frameworks/RustCUdp.xcframework/ios-arm64_x86_64-simulator/RustCUdp.framework/Info.plist +20 -0
  19. package/ios/Frameworks/RustCUdp.xcframework/ios-arm64_x86_64-simulator/RustCUdp.framework/RustCUdp +0 -0
  20. package/lib/Driver.d.ts +2 -0
  21. package/lib/Driver.js +6 -0
  22. package/lib/Driver.js.map +1 -0
  23. package/lib/Udp.nitro.d.ts +54 -0
  24. package/lib/Udp.nitro.js +3 -0
  25. package/lib/Udp.nitro.js.map +1 -0
  26. package/lib/index.d.ts +82 -0
  27. package/lib/index.js +375 -0
  28. package/lib/index.js.map +1 -0
  29. package/nitrogen/generated/.gitattributes +1 -0
  30. package/nitrogen/generated/android/RustCUdp+autolinking.cmake +84 -0
  31. package/nitrogen/generated/android/RustCUdp+autolinking.gradle +27 -0
  32. package/nitrogen/generated/android/RustCUdpOnLoad.cpp +53 -0
  33. package/nitrogen/generated/android/RustCUdpOnLoad.hpp +25 -0
  34. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  35. package/nitrogen/generated/android/c++/JFunc_void_std__shared_ptr_ArrayBuffer__std__string_double.hpp +78 -0
  36. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  37. package/nitrogen/generated/android/c++/JHybridUdpDriverSpec.cpp +57 -0
  38. package/nitrogen/generated/android/c++/JHybridUdpDriverSpec.hpp +65 -0
  39. package/nitrogen/generated/android/c++/JHybridUdpSocketDriverSpec.cpp +261 -0
  40. package/nitrogen/generated/android/c++/JHybridUdpSocketDriverSpec.hpp +97 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/udp/Func_void.kt +80 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/udp/Func_void_std__shared_ptr_ArrayBuffer__std__string_double.kt +80 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/udp/Func_void_std__string.kt +80 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/udp/HybridUdpDriverSpec.kt +57 -0
  45. package/nitrogen/generated/android/kotlin/com/margelo/nitro/udp/HybridUdpSocketDriverSpec.kt +212 -0
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/udp/RustCUdpOnLoad.kt +35 -0
  47. package/nitrogen/generated/ios/RustCUdp+autolinking.rb +60 -0
  48. package/nitrogen/generated/ios/RustCUdp-Swift-Cxx-Bridge.cpp +74 -0
  49. package/nitrogen/generated/ios/RustCUdp-Swift-Cxx-Bridge.hpp +195 -0
  50. package/nitrogen/generated/ios/RustCUdp-Swift-Cxx-Umbrella.hpp +53 -0
  51. package/nitrogen/generated/ios/RustCUdpAutolinking.mm +35 -0
  52. package/nitrogen/generated/ios/RustCUdpAutolinking.swift +12 -0
  53. package/nitrogen/generated/ios/c++/HybridUdpDriverSpecSwift.cpp +11 -0
  54. package/nitrogen/generated/ios/c++/HybridUdpDriverSpecSwift.hpp +80 -0
  55. package/nitrogen/generated/ios/c++/HybridUdpSocketDriverSpecSwift.cpp +11 -0
  56. package/nitrogen/generated/ios/c++/HybridUdpSocketDriverSpecSwift.hpp +302 -0
  57. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  58. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_ArrayBuffer__std__string_double.swift +47 -0
  59. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  60. package/nitrogen/generated/ios/swift/HybridUdpDriverSpec.swift +56 -0
  61. package/nitrogen/generated/ios/swift/HybridUdpDriverSpec_cxx.swift +141 -0
  62. package/nitrogen/generated/ios/swift/HybridUdpSocketDriverSpec.swift +84 -0
  63. package/nitrogen/generated/ios/swift/HybridUdpSocketDriverSpec_cxx.swift +527 -0
  64. package/nitrogen/generated/shared/c++/HybridNitroBufferSpec.cpp +32 -0
  65. package/nitrogen/generated/shared/c++/HybridNitroBufferSpec.hpp +74 -0
  66. package/nitrogen/generated/shared/c++/HybridUdpDriverSpec.cpp +21 -0
  67. package/nitrogen/generated/shared/c++/HybridUdpDriverSpec.hpp +66 -0
  68. package/nitrogen/generated/shared/c++/HybridUdpSocketDriverSpec.cpp +54 -0
  69. package/nitrogen/generated/shared/c++/HybridUdpSocketDriverSpec.hpp +98 -0
  70. package/package.json +59 -0
  71. package/react-native-nitro-dgram.podspec +45 -0
  72. package/src/Driver.ts +4 -0
  73. package/src/Udp.nitro.ts +55 -0
  74. package/src/index.ts +414 -0
package/src/index.ts ADDED
@@ -0,0 +1,414 @@
1
+ import { EventEmitter } from 'eventemitter3'
2
+ import { Driver } from './Driver'
3
+ import type { UdpSocketDriver, RemoteInfo } from './Udp.nitro'
4
+ import { Buffer } from 'react-native-nitro-buffer'
5
+
6
+ /**
7
+ * 100% compatible with Node.js dgram module.
8
+ */
9
+
10
+ function isIP(input: string): number {
11
+ if (/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(input)) return 4;
12
+ if (/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/.test(input)) return 6;
13
+ if (/^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$/.test(input)) return 6;
14
+ return 0;
15
+ }
16
+
17
+ export type SocketType = 'udp4' | 'udp6'
18
+
19
+ export class BlockList {
20
+ private _rules: Array<{ type: 'address' | 'range' | 'subnet', data: any }> = []
21
+
22
+ addAddress(address: string, family: 'ipv4' | 'ipv6' = 'ipv4'): void {
23
+ this._rules.push({ type: 'address', data: { address, family } })
24
+ }
25
+
26
+ addRange(start: string, end: string, family: 'ipv4' | 'ipv6' = 'ipv4'): void {
27
+ this._rules.push({ type: 'range', data: { start, end, family } })
28
+ }
29
+
30
+ addSubnet(net: string, prefix: number, family: 'ipv4' | 'ipv6' = 'ipv4'): void {
31
+ this._rules.push({ type: 'subnet', data: { net, prefix, family } })
32
+ }
33
+
34
+ check(address: string, family: 'ipv4' | 'ipv6' = 'ipv4'): boolean {
35
+ // Simple implementation for now
36
+ for (const rule of this._rules) {
37
+ if (rule.type === 'address' && rule.data.address === address) return true
38
+ // Range and subnet check would need IP calculation logic
39
+ }
40
+ return false
41
+ }
42
+ }
43
+
44
+ export interface SocketOptions {
45
+ type: SocketType
46
+ reuseAddr?: boolean
47
+ reusePort?: boolean
48
+ ipv6Only?: boolean
49
+ recvBufferSize?: number
50
+ sendBufferSize?: number
51
+ signal?: AbortSignal
52
+ lookup?: (hostname: string, options: any, callback: (err: Error | null, address: string, family: number) => void) => void
53
+ receiveBlockList?: BlockList
54
+ sendBlockList?: BlockList
55
+ }
56
+
57
+ export class Socket extends EventEmitter {
58
+ private _driver: UdpSocketDriver
59
+ private _type: SocketType
60
+ private _bound: boolean = false
61
+ private _closed: boolean = false
62
+ private _connected: boolean = false
63
+ private _signal?: AbortSignal
64
+ private _lookup?: (hostname: string, options: any, callback: (err: Error | null, address: string, family: number) => void) => void
65
+ private _receiveBlockList?: BlockList
66
+ private _sendBlockList?: BlockList
67
+
68
+ constructor(type: SocketType | SocketOptions) {
69
+ super()
70
+ const options = typeof type === 'string' ? { type } : type
71
+ this._type = options.type
72
+ this._lookup = options.lookup
73
+ this._receiveBlockList = options.receiveBlockList
74
+ this._sendBlockList = options.sendBlockList
75
+ this._driver = Driver.createSocket()
76
+ this._setupEvents()
77
+
78
+ if (options.signal) {
79
+ this._signal = options.signal
80
+ if (this._signal.aborted) {
81
+ setImmediate(() => this.close())
82
+ } else {
83
+ this._signal.addEventListener('abort', () => {
84
+ this.close()
85
+ })
86
+ }
87
+ }
88
+ }
89
+
90
+ async [Symbol.asyncDispose]() {
91
+ if (this._closed) return
92
+ return new Promise<void>((resolve) => {
93
+ this.once('close', resolve)
94
+ this.close()
95
+ })
96
+ }
97
+
98
+ private _setupEvents() {
99
+ this._driver.onMessage = (data: ArrayBuffer, address: string, port: number) => {
100
+ const family = isIP(address) === 6 ? 'IPv6' : 'IPv4'
101
+ if (this._receiveBlockList && this._receiveBlockList.check(address, family === 'IPv6' ? 'ipv6' : 'ipv4')) {
102
+ return // Discard blocked message
103
+ }
104
+
105
+ const buffer = Buffer.from(data)
106
+ const rinfo: RemoteInfo = {
107
+ address,
108
+ family,
109
+ port,
110
+ size: buffer.length
111
+ }
112
+ this.emit('message', buffer, rinfo)
113
+ }
114
+
115
+ this._driver.onConnect = () => {
116
+ this._connected = true
117
+ this.emit('connect')
118
+ }
119
+
120
+ this._driver.onError = (error: string) => {
121
+ this.emit('error', new Error(error))
122
+ }
123
+
124
+ this._driver.onClose = () => {
125
+ this._closed = true
126
+ this.emit('close')
127
+ }
128
+ }
129
+
130
+ bind(port?: number, address?: string, callback?: () => void): this;
131
+ bind(options: { port?: number, address?: string, exclusive?: boolean }, callback?: () => void): this;
132
+ bind(arg1?: any, arg2?: any, arg3?: any): this {
133
+ if (this._bound) throw new Error('Already bound');
134
+
135
+ let port = 0
136
+ let address = ''
137
+ let callback: (() => void) | undefined
138
+
139
+ if (typeof arg1 === 'object' && arg1 !== null) {
140
+ port = arg1.port || 0
141
+ address = arg1.address || ''
142
+ callback = arg2
143
+ } else {
144
+ port = arg1 || 0
145
+ address = arg2 || ''
146
+ callback = arg3
147
+ }
148
+
149
+ if (callback) this.once('listening', callback);
150
+
151
+ const ipv6Only = false // TODO: handle from options
152
+ const result = this._driver.bind(port, address, ipv6Only)
153
+
154
+ if (result === 0) {
155
+ this._bound = true
156
+ setImmediate(() => this.emit('listening'))
157
+ } else {
158
+ setImmediate(() => this.emit('error', new Error(`Bind failed with code ${result}`)))
159
+ }
160
+
161
+ return this
162
+ }
163
+
164
+ connect(port: number, address?: string, callback?: () => void): void {
165
+ if (this._closed) throw new Error('Socket is closed');
166
+
167
+ if (!this._bound) {
168
+ this.bind(0);
169
+ }
170
+
171
+ if (callback) this.once('connect', callback);
172
+
173
+ const targetAddress = address || (this._type === 'udp4' ? '127.0.0.1' : '::1');
174
+ const result = this._driver.connect(port, targetAddress);
175
+
176
+ if (result !== 0) {
177
+ setImmediate(() => this.emit('error', new Error(`Connect failed with code ${result}`)));
178
+ }
179
+ }
180
+
181
+ disconnect(): void {
182
+ if (this._closed) throw new Error('Socket is closed');
183
+ if (!this._connected) return;
184
+
185
+ const result = this._driver.disconnect();
186
+ if (result === 0) {
187
+ this._connected = false;
188
+ } else {
189
+ // Some platforms might not support disconnect directy
190
+ // If disconnect failed but node says it should work, we might need to recreate socket
191
+ // but for now we'll just emit error or ignore if it's ENOTSUP
192
+ }
193
+ }
194
+
195
+ send(msg: string | Uint8Array | ReadonlyArray<any>, port?: number, address?: string, callback?: (error: Error | null, bytes: number) => void): void;
196
+ send(msg: string | Uint8Array, offset: number, length: number, port?: number, address?: string, callback?: (error: Error | null, bytes: number) => void): void;
197
+ send(msg: any, ...args: any[]): void {
198
+ if (this._closed) throw new Error('Socket is closed');
199
+
200
+ let offset = 0
201
+ let length = 0
202
+ let port = 0
203
+ let address = ''
204
+ let callback: ((error: Error | null, bytes: number) => void) | undefined
205
+
206
+ // Node.js send() behavior:
207
+ // If connected, port and address should not be provided.
208
+ // If not connected, port and address must be provided.
209
+
210
+ if (!this._bound) {
211
+ this.bind(0);
212
+ }
213
+
214
+ if (this._connected) {
215
+ // (msg, [offset, length,] [callback])
216
+ if (typeof args[0] === 'function') {
217
+ callback = args[0];
218
+ length = msg.length;
219
+ } else if (typeof args[0] === 'number' && typeof args[1] === 'number') {
220
+ offset = args[0];
221
+ length = args[1];
222
+ callback = args[2];
223
+ } else {
224
+ length = msg.length;
225
+ }
226
+ // Use connected peer info
227
+ port = this._driver.getRemotePort();
228
+ address = this._driver.getRemoteAddress();
229
+ } else {
230
+ // (msg, [offset, length,] port [, address] [, callback])
231
+ if (args.length >= 4) {
232
+ // (msg, offset, length, port, address, callback)
233
+ offset = args[0]
234
+ length = args[1]
235
+ port = args[2]
236
+ address = args[3] || '127.0.0.1'
237
+ callback = args[4]
238
+ } else {
239
+ // (msg, port, address, callback)
240
+ port = args[0]
241
+ address = args[1] || '127.0.0.1'
242
+ callback = args[2]
243
+ length = msg.length
244
+ }
245
+ }
246
+
247
+ const handleSend = (targetAddress: string) => {
248
+ const family = isIP(targetAddress) === 6 ? 'IPv6' : 'IPv4'
249
+ if (this._sendBlockList && this._sendBlockList.check(targetAddress, family === 'IPv6' ? 'ipv6' : 'ipv4')) {
250
+ const err = new Error(`Address ${targetAddress} is blocked by sendBlockList`)
251
+ if (callback) setImmediate(() => callback!(err, 0))
252
+ else this.emit('error', err)
253
+ return
254
+ }
255
+
256
+ let bytesSent = 0
257
+ if (Array.isArray(msg)) {
258
+ const abs: ArrayBuffer[] = msg.map(m => {
259
+ const b = Buffer.from(m)
260
+ return b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength)
261
+ })
262
+ bytesSent = this._driver.sendMultiple(abs, port, targetAddress)
263
+ } else {
264
+ const buffer = Buffer.from(msg)
265
+ const finalBuffer = (offset === 0 && (length === 0 || length === buffer.length))
266
+ ? buffer
267
+ : buffer.slice(offset, offset + (length || buffer.length))
268
+
269
+ const ab = finalBuffer.buffer.slice(finalBuffer.byteOffset, finalBuffer.byteOffset + finalBuffer.byteLength)
270
+ bytesSent = this._driver.send(ab, port, targetAddress)
271
+ }
272
+
273
+ if (callback) {
274
+ if (bytesSent >= 0) {
275
+ setImmediate(() => callback!(null, bytesSent))
276
+ } else {
277
+ setImmediate(() => callback!(new Error(`Send failed with code ${bytesSent}`), 0))
278
+ }
279
+ }
280
+ }
281
+
282
+ if (this._lookup && isIP(address) === 0 && !this._connected && address !== '') {
283
+ this._lookup(address, {}, (err, resolvedAddr) => {
284
+ if (err) {
285
+ if (callback) callback(err, 0);
286
+ else this.emit('error', err);
287
+ return;
288
+ }
289
+ handleSend(resolvedAddr);
290
+ });
291
+ } else {
292
+ handleSend(address);
293
+ }
294
+ }
295
+
296
+ close(callback?: () => void): this {
297
+ if (this._closed) {
298
+ if (callback) setImmediate(callback);
299
+ return this;
300
+ }
301
+ if (callback) this.once('close', callback);
302
+
303
+ // Only bound sockets have native listener, so only they receive native onClose
304
+ if (this._bound) {
305
+ this._driver.close();
306
+ } else {
307
+ // For unbound sockets, emit close directly since native layer won't
308
+ this._closed = true;
309
+ setImmediate(() => this.emit('close'));
310
+ }
311
+
312
+ return this
313
+ }
314
+
315
+ address(): { address: string, family: string, port: number } {
316
+ return {
317
+ address: this._driver.getLocalAddress(),
318
+ family: this._type === 'udp6' ? 'IPv6' : 'IPv4',
319
+ port: this._driver.getLocalPort()
320
+ }
321
+ }
322
+
323
+ remoteAddress(): { address: string, family: string, port: number } {
324
+ if (!this._connected) throw new Error('Socket is not connected');
325
+ return {
326
+ address: this._driver.getRemoteAddress(),
327
+ family: isIP(this._driver.getRemoteAddress()) === 6 ? 'IPv6' : 'IPv4',
328
+ port: this._driver.getRemotePort()
329
+ }
330
+ }
331
+
332
+ setBroadcast(flag: boolean): void {
333
+ this._driver.setBroadcast(flag)
334
+ }
335
+
336
+ setTTL(ttl: number): void {
337
+ this._driver.setTTL(ttl)
338
+ }
339
+
340
+ setMulticastTTL(ttl: number): void {
341
+ this._driver.setMulticastTTL(ttl)
342
+ }
343
+
344
+ setMulticastLoopback(flag: boolean): void {
345
+ this._driver.setMulticastLoopback(flag)
346
+ }
347
+
348
+ setMulticastInterface(interfaceAddress: string): void {
349
+ this._driver.setMulticastInterface(interfaceAddress)
350
+ }
351
+
352
+ addMembership(multicastAddress: string, interfaceAddress?: string): void {
353
+ this._driver.addMembership(multicastAddress, interfaceAddress)
354
+ }
355
+
356
+ dropMembership(multicastAddress: string, interfaceAddress?: string): void {
357
+ this._driver.dropMembership(multicastAddress, interfaceAddress)
358
+ }
359
+
360
+ addSourceSpecificMembership(sourceAddress: string, groupAddress: string, interfaceAddress?: string): void {
361
+ this._driver.addSourceSpecificMembership(sourceAddress, groupAddress, interfaceAddress)
362
+ }
363
+
364
+ dropSourceSpecificMembership(sourceAddress: string, groupAddress: string, interfaceAddress?: string): void {
365
+ this._driver.dropSourceSpecificMembership(sourceAddress, groupAddress, interfaceAddress)
366
+ }
367
+
368
+ getRecvBufferSize(): number {
369
+ if (this._driver.id === 0) throw new Error('Socket is not bound');
370
+ return this._driver.getRecvBufferSize()
371
+ }
372
+
373
+ setRecvBufferSize(size: number): void {
374
+ if (this._driver.id === 0) throw new Error('Socket is not bound');
375
+ this._driver.setRecvBufferSize(size)
376
+ }
377
+
378
+ getSendBufferSize(): number {
379
+ if (this._driver.id === 0) throw new Error('Socket is not bound');
380
+ return this._driver.getSendBufferSize()
381
+ }
382
+
383
+ setSendBufferSize(size: number): void {
384
+ if (this._driver.id === 0) throw new Error('Socket is not bound');
385
+ this._driver.setSendBufferSize(size)
386
+ }
387
+
388
+ getSendQueueCount(): number {
389
+ if (this._driver.id === 0) return 0;
390
+ return this._driver.getSendQueueCount()
391
+ }
392
+
393
+ getSendQueueSize(): number {
394
+ if (this._driver.id === 0) return 0;
395
+ return this._driver.getSendQueueSize()
396
+ }
397
+
398
+ ref(): this { return this }
399
+ unref(): this { return this }
400
+ }
401
+
402
+ export function createSocket(type: SocketType | SocketOptions, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket {
403
+ const socket = new Socket(type)
404
+ if (callback) {
405
+ socket.on('message', callback)
406
+ }
407
+ return socket
408
+ }
409
+
410
+ export default {
411
+ createSocket,
412
+ Socket,
413
+ BlockList
414
+ }