@candypoets/nipworker 0.96.3 → 0.96.5

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 (180) hide show
  1. package/README.md +206 -73
  2. package/crates/native-ffi/README.md +14 -151
  3. package/crates/native-ffi/ios/README.md +4 -50
  4. package/crates/native-ffi/react-native/README.md +8 -7
  5. package/crates/native-ffi/react-native/android/build.gradle +1 -0
  6. package/crates/native-ffi/react-native/android/src/main/cpp/NipworkerByteRuntime.cpp +348 -0
  7. package/crates/native-ffi/react-native/android/src/main/java/com/candypoets/nipworker/reactnative/NipworkerHooks.kt +292 -0
  8. package/crates/native-ffi/react-native/android/src/main/java/com/candypoets/nipworker/reactnative/NipworkerReactNativeModule.kt +283 -89
  9. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/AddressPointer.java +75 -0
  10. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ArticleBlock.java +102 -0
  11. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ArticleBlockType.java +23 -0
  12. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ArticleEntity.java +80 -0
  13. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ArticleInline.java +85 -0
  14. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ArticleInlineType.java +24 -0
  15. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/BadgeAwardRecipient.java +61 -0
  16. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/BufferFull.java +52 -0
  17. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CacheInput.java +16 -0
  18. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CacheInputMessage.java +57 -0
  19. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CacheRequest.java +84 -0
  20. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CacheResponse.java +67 -0
  21. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CashuData.java +55 -0
  22. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ChatLimiterPipeConfig.java +74 -0
  23. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CodeData.java +61 -0
  24. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ConnectionStatus.java +68 -0
  25. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Contact.java +70 -0
  26. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ContentBlock.java +70 -0
  27. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ContentData.java +23 -0
  28. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Coordinate.java +75 -0
  29. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CountResponse.java +60 -0
  30. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/CounterPipeConfig.java +67 -0
  31. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/DLEQProof.java +69 -0
  32. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Emoji.java +62 -0
  33. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/EmojiData.java +68 -0
  34. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Eoce.java +55 -0
  35. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/EventPointer.java +74 -0
  36. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/GetPublicKey.java +43 -0
  37. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/HTLCWitness.java +64 -0
  38. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/HashtagData.java +55 -0
  39. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/HistoryTag.java +74 -0
  40. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ImageData.java +67 -0
  41. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ImetaTag.java +100 -0
  42. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind0Parsed.java +168 -0
  43. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind10002Parsed.java +59 -0
  44. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind10019Parsed.java +73 -0
  45. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind1018Parsed.java +79 -0
  46. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind1068Parsed.java +107 -0
  47. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind1111Parsed.java +159 -0
  48. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind1311Parsed.java +91 -0
  49. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind17375Parsed.java +74 -0
  50. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind17Parsed.java +81 -0
  51. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind1Parsed.java +109 -0
  52. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind20Parsed.java +107 -0
  53. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind22Parsed.java +111 -0
  54. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind30023Parsed.java +113 -0
  55. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind3Parsed.java +59 -0
  56. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind4Parsed.java +78 -0
  57. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind6Parsed.java +53 -0
  58. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind7374Parsed.java +66 -0
  59. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind7375Parsed.java +79 -0
  60. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind7376Parsed.java +100 -0
  61. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind7Parsed.java +81 -0
  62. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind8Parsed.java +78 -0
  63. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind9321Parsed.java +117 -0
  64. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Kind9735Parsed.java +119 -0
  65. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/KindFilterPipeConfig.java +60 -0
  66. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/LinkPreviewData.java +73 -0
  67. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ListParsed.java +123 -0
  68. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/LiveActivityRef.java +72 -0
  69. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/LiveChatParticipant.java +61 -0
  70. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/LiveChatThreadRef.java +61 -0
  71. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MainContent.java +20 -0
  72. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MainMessage.java +57 -0
  73. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MediaGroupData.java +58 -0
  74. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MediaItem.java +58 -0
  75. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Message.java +24 -0
  76. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MessageType.java +19 -0
  77. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MintInfo.java +65 -0
  78. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MintProofs.java +66 -0
  79. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/MuteFilterPipeConfig.java +84 -0
  80. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Nip07.java +43 -0
  81. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Nip46Bunker.java +61 -0
  82. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Nip46QR.java +61 -0
  83. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/NostrData.java +81 -0
  84. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/NostrEvent.java +95 -0
  85. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/NpubLimiterKey.java +15 -0
  86. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/NpubLimiterPipeConfig.java +64 -0
  87. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/P2PKWitness.java +57 -0
  88. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ParsePipeConfig.java +43 -0
  89. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ParsedData.java +39 -0
  90. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ParsedDataUnion.java +35 -0
  91. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ParsedEvent.java +108 -0
  92. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Pipe.java +57 -0
  93. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PipeConfig.java +23 -0
  94. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PipelineConfig.java +59 -0
  95. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PollOption.java +62 -0
  96. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PollType.java +15 -0
  97. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PreGenericParsed.java +213 -0
  98. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PreParticipant.java +73 -0
  99. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PreRefEvent.java +67 -0
  100. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/PrivateKey.java +55 -0
  101. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ProfilePointer.java +64 -0
  102. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Proof.java +90 -0
  103. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ProofVerificationPipeConfig.java +52 -0
  104. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Pubkey.java +55 -0
  105. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Publish.java +80 -0
  106. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Raw.java +55 -0
  107. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ReactionType.java +17 -0
  108. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/RelayInfo.java +63 -0
  109. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Request.java +134 -0
  110. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SaveToDbPipeConfig.java +43 -0
  111. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SerializeEventsPipeConfig.java +55 -0
  112. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SetSigner.java +57 -0
  113. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SignEvent.java +54 -0
  114. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SignedEvent.java +54 -0
  115. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SignerOp.java +19 -0
  116. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SignerRequest.java +80 -0
  117. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SignerResponse.java +64 -0
  118. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SignerType.java +18 -0
  119. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/StringVec.java +57 -0
  120. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Subscribe.java +72 -0
  121. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SubscribeKind.java +19 -0
  122. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/SubscriptionConfig.java +95 -0
  123. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Tag.java +64 -0
  124. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Template.java +74 -0
  125. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Unsubscribe.java +55 -0
  126. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ValidProofs.java +59 -0
  127. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/VideoData.java +67 -0
  128. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/VideoVariant.java +102 -0
  129. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/Witness.java +17 -0
  130. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/WitnessString.java +54 -0
  131. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/WorkerMessage.java +74 -0
  132. package/crates/native-ffi/react-native/android/src/main/java/nostr/fb/ZapRequest.java +83 -0
  133. package/crates/native-ffi/react-native/ios/NipworkerReactNativeModule.h +18 -0
  134. package/crates/native-ffi/react-native/ios/NipworkerReactNativeModule.mm +263 -8
  135. package/dist/EngineManager.d.ts +1 -0
  136. package/dist/EngineManager.d.ts.map +1 -1
  137. package/dist/EngineManager.js +143 -137
  138. package/dist/NostrManager.d.ts +1 -0
  139. package/dist/NostrManager.d.ts.map +1 -1
  140. package/dist/NostrManager.js +49 -44
  141. package/dist/connections/index.js +87 -87
  142. package/dist/engine/index.js +119 -119
  143. package/dist/engine.d.ts.map +1 -1
  144. package/dist/hooks.d.ts +1 -1
  145. package/dist/hooks.d.ts.map +1 -1
  146. package/dist/hooks.js +27 -26
  147. package/dist/index.d.ts +1 -1
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/legacy.d.ts.map +1 -1
  150. package/dist/lib/ArrayBufferReader.d.ts +2 -1
  151. package/dist/lib/ArrayBufferReader.d.ts.map +1 -1
  152. package/dist/lib/BaseBackend.d.ts +5 -3
  153. package/dist/lib/BaseBackend.d.ts.map +1 -1
  154. package/dist/manager.d.ts +2 -6
  155. package/dist/manager.d.ts.map +1 -1
  156. package/dist/manager.js +10 -28
  157. package/dist/react-native.d.ts +33 -4
  158. package/dist/react-native.d.ts.map +1 -1
  159. package/dist/react-native.js +400 -122
  160. package/dist/scheduleMicrotask.js +58 -50
  161. package/package.json +13 -11
  162. package/swift/Package.swift +36 -0
  163. package/swift/README.md +438 -0
  164. package/swift/Sources/NipworkerSwift/ArrayBufferReader.swift +136 -0
  165. package/swift/Sources/NipworkerSwift/AuthSession.swift +24 -0
  166. package/swift/Sources/NipworkerSwift/FFI.swift +94 -0
  167. package/swift/Sources/NipworkerSwift/FlatBufferHelpers.swift +273 -0
  168. package/swift/Sources/NipworkerSwift/Generated/message_generated.swift +6530 -0
  169. package/swift/Sources/NipworkerSwift/Hooks.swift +300 -0
  170. package/swift/Sources/NipworkerSwift/Models.swift +156 -0
  171. package/swift/Sources/NipworkerSwift/NostrManager.swift +594 -0
  172. package/swift/Sources/NipworkerSwift/PublishTracker.swift +40 -0
  173. package/swift/Sources/NipworkerSwift/Subscription.swift +44 -0
  174. package/swift/Sources/NipworkerSwift/WorkerMessageView.swift +145 -0
  175. package/dist/NativeBackend.d.ts +0 -53
  176. package/dist/NativeBackend.d.ts.map +0 -1
  177. package/dist/NativeBackend.js +0 -464
  178. package/dist/native.d.ts +0 -29
  179. package/dist/native.d.ts.map +0 -1
  180. package/dist/native.js +0 -273
@@ -0,0 +1,300 @@
1
+ import Foundation
2
+
3
+ public final class NipworkerHookHandle {
4
+ private let onCancel: () -> Void
5
+ private var isCancelled = false
6
+
7
+ public init(_ onCancel: @escaping () -> Void) {
8
+ self.onCancel = onCancel
9
+ }
10
+
11
+ public func cancel() {
12
+ guard !isCancelled else { return }
13
+ isCancelled = true
14
+ onCancel()
15
+ }
16
+
17
+ deinit {
18
+ cancel()
19
+ }
20
+ }
21
+
22
+ // MARK: - Callback-based subscription helpers
23
+
24
+ /// Subscribe through the React Native shared runtime. Returns an unsubscribe function.
25
+ public func useSubscription(
26
+ subscriptionId: String,
27
+ requests: [RequestObject],
28
+ callback: @escaping ([WorkerMessageView]) -> Void,
29
+ options: SubscriptionConfig = SubscriptionConfig()
30
+ ) -> () -> Void {
31
+ useSubscription(
32
+ manager: NostrManager.reactNativeShared(),
33
+ subscriptionId: subscriptionId,
34
+ requests: requests,
35
+ callback: callback,
36
+ options: options
37
+ )
38
+ }
39
+
40
+ /// Subscribe with a callback closure. Returns an unsubscribe function.
41
+ /// Mirrors the JS `useSubscription` hook pattern.
42
+ public func useSubscription(
43
+ manager: NostrManager,
44
+ subscriptionId: String,
45
+ requests: [RequestObject],
46
+ callback: @escaping ([WorkerMessageView]) -> Void,
47
+ options: SubscriptionConfig = SubscriptionConfig()
48
+ ) -> () -> Void {
49
+ var notificationToken: NSObjectProtocol?
50
+ var lastReadPosition = 4
51
+ var isActive = true
52
+ let canonicalSubscriptionId = subscriptionId
53
+
54
+ func readAvailableMessages() {
55
+ Task {
56
+ var position = lastReadPosition
57
+ var allMessages: [WorkerMessageView] = []
58
+
59
+ while true {
60
+ let result = await manager.readWorkerMessages(for: canonicalSubscriptionId, from: position)
61
+ if result.messages.isEmpty {
62
+ break
63
+ }
64
+ allMessages.append(contentsOf: result.messages)
65
+ position = result.newPosition
66
+ }
67
+
68
+ lastReadPosition = position
69
+ if isActive, !allMessages.isEmpty {
70
+ callback(allMessages)
71
+ }
72
+ }
73
+ }
74
+
75
+ Task {
76
+ notificationToken = NotificationCenter.default.addObserver(
77
+ forName: .nipworkerSubscriptionUpdated(subId: canonicalSubscriptionId),
78
+ object: nil,
79
+ queue: .main
80
+ ) { _ in
81
+ guard isActive else { return }
82
+ readAvailableMessages()
83
+ }
84
+
85
+ _ = await manager.subscribe(subscriptionId: subscriptionId, requests: requests, options: options)
86
+ readAvailableMessages()
87
+ }
88
+
89
+ return {
90
+ isActive = false
91
+ if let token = notificationToken {
92
+ NotificationCenter.default.removeObserver(token)
93
+ }
94
+ Task {
95
+ await manager.unsubscribe(subscriptionId: canonicalSubscriptionId)
96
+ }
97
+ }
98
+ }
99
+
100
+ /// Subscribe through the React Native shared runtime. Returns a cancellable handle.
101
+ public func useSubscriptionHandle(
102
+ subscriptionId: String,
103
+ requests: [RequestObject],
104
+ callback: @escaping ([WorkerMessageView]) -> Void,
105
+ options: SubscriptionConfig = SubscriptionConfig()
106
+ ) -> NipworkerHookHandle {
107
+ NipworkerHookHandle(
108
+ useSubscription(
109
+ subscriptionId: subscriptionId,
110
+ requests: requests,
111
+ callback: callback,
112
+ options: options
113
+ )
114
+ )
115
+ }
116
+
117
+ /// Subscribe with a callback closure. Returns a cancellable handle.
118
+ /// This is the native component-friendly form of the JS `useSubscription` API.
119
+ public func useSubscriptionHandle(
120
+ manager: NostrManager,
121
+ subscriptionId: String,
122
+ requests: [RequestObject],
123
+ callback: @escaping ([WorkerMessageView]) -> Void,
124
+ options: SubscriptionConfig = SubscriptionConfig()
125
+ ) -> NipworkerHookHandle {
126
+ NipworkerHookHandle(
127
+ useSubscription(
128
+ manager: manager,
129
+ subscriptionId: subscriptionId,
130
+ requests: requests,
131
+ callback: callback,
132
+ options: options
133
+ )
134
+ )
135
+ }
136
+
137
+ /// Publish through the React Native shared runtime. Returns an unsubscribe function.
138
+ public func usePublish(
139
+ publishId: String,
140
+ event: NostrEvent,
141
+ callback: @escaping ([String: PublishStatus]) -> Void,
142
+ defaultRelays: [String] = [],
143
+ optimisticSubIds: [String] = []
144
+ ) -> () -> Void {
145
+ usePublish(
146
+ manager: NostrManager.reactNativeShared(),
147
+ publishId: publishId,
148
+ event: event,
149
+ callback: callback,
150
+ defaultRelays: defaultRelays,
151
+ optimisticSubIds: optimisticSubIds
152
+ )
153
+ }
154
+
155
+ /// Publish with a callback for status updates. Returns an unsubscribe function.
156
+ /// Mirrors the JS `usePublish` hook pattern.
157
+ public func usePublish(
158
+ manager: NostrManager,
159
+ publishId: String,
160
+ event: NostrEvent,
161
+ callback: @escaping ([String: PublishStatus]) -> Void,
162
+ defaultRelays: [String] = [],
163
+ optimisticSubIds: [String] = []
164
+ ) -> () -> Void {
165
+ var notificationToken: NSObjectProtocol?
166
+ var lastReadPosition = 4
167
+ var isActive = true
168
+
169
+ Task {
170
+ _ = await manager.publish(
171
+ publishId: publishId,
172
+ event: event,
173
+ defaultRelays: defaultRelays,
174
+ optimisticSubIds: optimisticSubIds
175
+ )
176
+
177
+ notificationToken = NotificationCenter.default.addObserver(
178
+ forName: .nipworkerSubscriptionUpdated(subId: publishId),
179
+ object: nil,
180
+ queue: .main
181
+ ) { [weak manager] _ in
182
+ guard isActive,
183
+ let manager = manager else { return }
184
+
185
+ Task {
186
+ var position = lastReadPosition
187
+ var merged: [String: PublishStatus] = [:]
188
+
189
+ while true {
190
+ let result = await manager.readPublishStatuses(for: publishId, from: position)
191
+ if result.statuses.isEmpty {
192
+ break
193
+ }
194
+ for (url, status) in result.statuses {
195
+ merged[url] = status
196
+ }
197
+ position = result.newPosition
198
+ }
199
+
200
+ lastReadPosition = position
201
+ if !merged.isEmpty {
202
+ callback(merged)
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ return {
209
+ isActive = false
210
+ if let token = notificationToken {
211
+ NotificationCenter.default.removeObserver(token)
212
+ }
213
+ Task {
214
+ await manager.releasePublish(publishId: publishId)
215
+ }
216
+ }
217
+ }
218
+
219
+ /// Publish through the React Native shared runtime. Returns a cancellable handle.
220
+ public func usePublishHandle(
221
+ publishId: String,
222
+ event: NostrEvent,
223
+ callback: @escaping ([String: PublishStatus]) -> Void,
224
+ defaultRelays: [String] = [],
225
+ optimisticSubIds: [String] = []
226
+ ) -> NipworkerHookHandle {
227
+ NipworkerHookHandle(
228
+ usePublish(
229
+ publishId: publishId,
230
+ event: event,
231
+ callback: callback,
232
+ defaultRelays: defaultRelays,
233
+ optimisticSubIds: optimisticSubIds
234
+ )
235
+ )
236
+ }
237
+
238
+ /// Publish with a callback for status updates. Returns a cancellable handle.
239
+ /// This is the native component-friendly form of the JS `usePublish` API.
240
+ public func usePublishHandle(
241
+ manager: NostrManager,
242
+ publishId: String,
243
+ event: NostrEvent,
244
+ callback: @escaping ([String: PublishStatus]) -> Void,
245
+ defaultRelays: [String] = [],
246
+ optimisticSubIds: [String] = []
247
+ ) -> NipworkerHookHandle {
248
+ NipworkerHookHandle(
249
+ usePublish(
250
+ manager: manager,
251
+ publishId: publishId,
252
+ event: event,
253
+ callback: callback,
254
+ defaultRelays: defaultRelays,
255
+ optimisticSubIds: optimisticSubIds
256
+ )
257
+ )
258
+ }
259
+
260
+ /// Relay status helper using the React Native shared runtime.
261
+ public func useRelayStatus(
262
+ onStatus: @escaping (String, RelayStatus) -> Void
263
+ ) -> () -> Void {
264
+ useRelayStatus(manager: NostrManager.reactNativeShared(), onStatus: onStatus)
265
+ }
266
+
267
+ /// Relay status helper. Immediately calls handler with current statuses,
268
+ /// then subscribes to updates. Returns a stop function.
269
+ public func useRelayStatus(
270
+ manager: NostrManager,
271
+ onStatus: @escaping (String, RelayStatus) -> Void
272
+ ) -> () -> Void {
273
+ var notificationToken: NSObjectProtocol?
274
+ var isActive = true
275
+
276
+ Task {
277
+ let statuses = await manager.getRelayStatuses()
278
+ for (url, status) in statuses {
279
+ onStatus(url, status)
280
+ }
281
+
282
+ notificationToken = NotificationCenter.default.addObserver(
283
+ forName: .nipworkerRelayStatusUpdated,
284
+ object: nil,
285
+ queue: .main
286
+ ) { notification in
287
+ guard isActive,
288
+ let url = notification.userInfo?["url"] as? String,
289
+ let status = notification.userInfo?["status"] as? RelayStatus else { return }
290
+ onStatus(url, status)
291
+ }
292
+ }
293
+
294
+ return {
295
+ isActive = false
296
+ if let token = notificationToken {
297
+ NotificationCenter.default.removeObserver(token)
298
+ }
299
+ }
300
+ }
@@ -0,0 +1,156 @@
1
+ import Foundation
2
+
3
+ public struct RequestObject {
4
+ public var ids: [String]?
5
+ public var authors: [String]?
6
+ public var kinds: [UInt16]?
7
+ public var tags: [String: [String]]?
8
+ public var since: Int?
9
+ public var until: Int?
10
+ public var limit: Int?
11
+ public var search: String?
12
+ public var relays: [String]
13
+ public var closeOnEOSE: Bool?
14
+ public var cacheFirst: Bool?
15
+ public var noCache: Bool?
16
+ public var maxRelays: UInt16?
17
+
18
+ public init(
19
+ ids: [String]? = nil,
20
+ authors: [String]? = nil,
21
+ kinds: [UInt16]? = nil,
22
+ tags: [String: [String]]? = nil,
23
+ since: Int? = nil,
24
+ until: Int? = nil,
25
+ limit: Int? = nil,
26
+ search: String? = nil,
27
+ relays: [String] = [],
28
+ closeOnEOSE: Bool? = nil,
29
+ cacheFirst: Bool? = nil,
30
+ noCache: Bool? = nil,
31
+ maxRelays: UInt16? = nil
32
+ ) {
33
+ self.ids = ids
34
+ self.authors = authors
35
+ self.kinds = kinds
36
+ self.tags = tags
37
+ self.since = since
38
+ self.until = until
39
+ self.limit = limit
40
+ self.search = search
41
+ self.relays = relays
42
+ self.closeOnEOSE = closeOnEOSE
43
+ self.cacheFirst = cacheFirst
44
+ self.noCache = noCache
45
+ self.maxRelays = maxRelays
46
+ }
47
+ }
48
+
49
+ public struct PipeConfig {
50
+ public enum Kind {
51
+ case muteFilter
52
+ case parse
53
+ case saveToDb
54
+ case serializeEvents(subId: String)
55
+ case kindFilter(kinds: [UInt16])
56
+ case counter(kinds: [UInt16], pubkey: String)
57
+ case npubLimiter(kind: UInt16, limitPerNpub: UInt32, maxTotalNpubs: UInt32)
58
+ case proofVerification(maxProofs: UInt32)
59
+ }
60
+ public var kind: Kind
61
+ public init(_ kind: Kind) { self.kind = kind }
62
+ }
63
+
64
+ public struct SubscriptionConfig {
65
+ public var pipeline: [PipeConfig]?
66
+ public var closeOnEose: Bool
67
+ public var cacheFirst: Bool
68
+ public var timeoutMs: UInt64?
69
+ public var maxEvents: UInt32?
70
+ public var skipCache: Bool
71
+ public var force: Bool
72
+ public var bytesPerEvent: UInt32
73
+ public var isSlow: Bool
74
+ public var pagination: String?
75
+ public var cacheOnly: Bool
76
+
77
+ public init(
78
+ pipeline: [PipeConfig]? = nil,
79
+ closeOnEose: Bool = false,
80
+ cacheFirst: Bool = true,
81
+ timeoutMs: UInt64? = nil,
82
+ maxEvents: UInt32? = nil,
83
+ skipCache: Bool = false,
84
+ force: Bool = false,
85
+ bytesPerEvent: UInt32 = 3072,
86
+ isSlow: Bool = false,
87
+ pagination: String? = nil,
88
+ cacheOnly: Bool = false
89
+ ) {
90
+ self.pipeline = pipeline
91
+ self.closeOnEose = closeOnEose
92
+ self.cacheFirst = cacheFirst
93
+ self.timeoutMs = timeoutMs
94
+ self.maxEvents = maxEvents
95
+ self.skipCache = skipCache
96
+ self.force = force
97
+ self.bytesPerEvent = bytesPerEvent
98
+ self.isSlow = isSlow
99
+ self.pagination = pagination
100
+ self.cacheOnly = cacheOnly
101
+ }
102
+ }
103
+
104
+ public struct EventTemplate {
105
+ public var kind: UInt16
106
+ public var content: String
107
+ public var tags: [[String]]
108
+
109
+ public init(kind: UInt16, content: String, tags: [[String]] = []) {
110
+ self.kind = kind
111
+ self.content = content
112
+ self.tags = tags
113
+ }
114
+ }
115
+
116
+ public struct NostrEvent: Identifiable, Equatable, Sendable {
117
+ public var id: String
118
+ public var pubkey: String
119
+ public var kind: UInt16
120
+ public var content: String
121
+ public var tags: [[String]]
122
+ public var createdAt: Int
123
+ public var sig: String
124
+
125
+ public init(id: String, pubkey: String, kind: UInt16, content: String, tags: [[String]], createdAt: Int, sig: String) {
126
+ self.id = id
127
+ self.pubkey = pubkey
128
+ self.kind = kind
129
+ self.content = content
130
+ self.tags = tags
131
+ self.createdAt = createdAt
132
+ self.sig = sig
133
+ }
134
+ }
135
+
136
+ public struct NostrManagerConfig {
137
+ public var logLevel: String?
138
+ public init(logLevel: String? = nil) { self.logLevel = logLevel }
139
+ }
140
+
141
+ public enum SignerTypeName: Sendable {
142
+ case pubkey(String)
143
+ case privkey(String)
144
+ }
145
+
146
+ public enum RelayStatus: Equatable, Sendable {
147
+ case connected
148
+ case failed
149
+ case closed(String?)
150
+ }
151
+
152
+ public enum PublishStatus: Equatable, Sendable {
153
+ case pending
154
+ case success(String)
155
+ case failed(String)
156
+ }