@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
@@ -8,9 +8,18 @@
8
8
  #include <vector>
9
9
 
10
10
  extern "C" void nipworker_handle_message(void* handle, const uint8_t* ptr, size_t len);
11
+ extern "C" bool nipworker_subscribe_message(void* handle, const uint8_t* ptr, size_t len);
12
+ extern "C" bool nipworker_publish_message(void* handle, const uint8_t* ptr, size_t len);
11
13
  extern "C" void nipworker_set_private_key(void* handle, const char* ptr);
12
14
  extern "C" void nipworker_wake(void* handle);
13
15
  extern "C" void nipworker_deinit(void* handle);
16
+ extern "C" bool nipworker_register_subscription(void* handle, const char* sub_id, size_t buffer_size);
17
+ extern "C" bool nipworker_register_publish_buffer(void* handle, const char* publish_id, size_t buffer_size);
18
+ extern "C" bool nipworker_retain_subscription(void* handle, const char* sub_id);
19
+ extern "C" void nipworker_release_subscription(void* handle, const char* sub_id);
20
+ extern "C" uint8_t* nipworker_subscription_buffer_ptr(void* handle, const char* sub_id);
21
+ extern "C" size_t nipworker_subscription_buffer_len(void* handle, const char* sub_id);
22
+ extern "C" void nipworker_cleanup_subscriptions(void* handle);
14
23
 
15
24
  namespace {
16
25
  using facebook::jsi::Array;
@@ -39,6 +48,23 @@ private:
39
48
  std::vector<uint8_t> bytes_;
40
49
  };
41
50
 
51
+ class ExternalMutableBuffer final : public MutableBuffer {
52
+ public:
53
+ ExternalMutableBuffer(uint8_t* ptr, size_t len) : ptr_(ptr), len_(len) {}
54
+
55
+ size_t size() const override {
56
+ return len_;
57
+ }
58
+
59
+ uint8_t* data() override {
60
+ return ptr_;
61
+ }
62
+
63
+ private:
64
+ uint8_t* ptr_;
65
+ size_t len_;
66
+ };
67
+
42
68
  std::mutex gQueueMutex;
43
69
  std::vector<std::vector<uint8_t>> gQueuedPackets;
44
70
  bool gInstalled = false;
@@ -78,6 +104,153 @@ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeQueue
78
104
  return JNI_TRUE;
79
105
  }
80
106
 
107
+ extern "C" JNIEXPORT jobject JNICALL
108
+ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeSubscribeMessage(
109
+ JNIEnv* env,
110
+ jclass,
111
+ jlong handle,
112
+ jbyteArray bytes,
113
+ jstring subId
114
+ ) {
115
+ if (handle == 0 || bytes == nullptr || subId == nullptr) return nullptr;
116
+ jsize len = env->GetArrayLength(bytes);
117
+ jbyte* ptr = env->GetByteArrayElements(bytes, nullptr);
118
+ if (ptr == nullptr) return nullptr;
119
+ auto* engineHandle = reinterpret_cast<void*>(handle);
120
+ bool ok = nipworker_subscribe_message(
121
+ engineHandle,
122
+ reinterpret_cast<const uint8_t*>(ptr),
123
+ static_cast<size_t>(len)
124
+ );
125
+ env->ReleaseByteArrayElements(bytes, ptr, JNI_ABORT);
126
+ if (!ok) return nullptr;
127
+
128
+ const char* subIdChars = env->GetStringUTFChars(subId, nullptr);
129
+ if (subIdChars == nullptr) return nullptr;
130
+ uint8_t* bufferPtr = nipworker_subscription_buffer_ptr(engineHandle, subIdChars);
131
+ size_t bufferLen = nipworker_subscription_buffer_len(engineHandle, subIdChars);
132
+ env->ReleaseStringUTFChars(subId, subIdChars);
133
+ if (bufferPtr == nullptr || bufferLen == 0) return nullptr;
134
+ return env->NewDirectByteBuffer(bufferPtr, static_cast<jlong>(bufferLen));
135
+ }
136
+
137
+ extern "C" JNIEXPORT jobject JNICALL
138
+ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativePublishMessage(
139
+ JNIEnv* env,
140
+ jclass,
141
+ jlong handle,
142
+ jbyteArray bytes,
143
+ jstring publishId
144
+ ) {
145
+ if (handle == 0 || bytes == nullptr || publishId == nullptr) return nullptr;
146
+ jsize len = env->GetArrayLength(bytes);
147
+ jbyte* ptr = env->GetByteArrayElements(bytes, nullptr);
148
+ if (ptr == nullptr) return nullptr;
149
+ auto* engineHandle = reinterpret_cast<void*>(handle);
150
+ bool ok = nipworker_publish_message(
151
+ engineHandle,
152
+ reinterpret_cast<const uint8_t*>(ptr),
153
+ static_cast<size_t>(len)
154
+ );
155
+ env->ReleaseByteArrayElements(bytes, ptr, JNI_ABORT);
156
+ if (!ok) return nullptr;
157
+
158
+ const char* publishIdChars = env->GetStringUTFChars(publishId, nullptr);
159
+ if (publishIdChars == nullptr) return nullptr;
160
+ uint8_t* bufferPtr = nipworker_subscription_buffer_ptr(engineHandle, publishIdChars);
161
+ size_t bufferLen = nipworker_subscription_buffer_len(engineHandle, publishIdChars);
162
+ env->ReleaseStringUTFChars(publishId, publishIdChars);
163
+ if (bufferPtr == nullptr || bufferLen == 0) return nullptr;
164
+ return env->NewDirectByteBuffer(bufferPtr, static_cast<jlong>(bufferLen));
165
+ }
166
+
167
+ extern "C" JNIEXPORT jboolean JNICALL
168
+ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeRegisterSubscription(
169
+ JNIEnv* env,
170
+ jclass,
171
+ jlong handle,
172
+ jstring subId,
173
+ jint bufferSize
174
+ ) {
175
+ if (handle == 0 || subId == nullptr || bufferSize <= 0) return JNI_FALSE;
176
+ const char* subIdChars = env->GetStringUTFChars(subId, nullptr);
177
+ if (subIdChars == nullptr) return JNI_FALSE;
178
+ bool ok = nipworker_register_subscription(
179
+ reinterpret_cast<void*>(handle),
180
+ subIdChars,
181
+ static_cast<size_t>(bufferSize)
182
+ );
183
+ env->ReleaseStringUTFChars(subId, subIdChars);
184
+ return ok ? JNI_TRUE : JNI_FALSE;
185
+ }
186
+
187
+ extern "C" JNIEXPORT jboolean JNICALL
188
+ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeRegisterPublishBuffer(
189
+ JNIEnv* env,
190
+ jclass,
191
+ jlong handle,
192
+ jstring publishId,
193
+ jint bufferSize
194
+ ) {
195
+ if (handle == 0 || publishId == nullptr || bufferSize <= 0) return JNI_FALSE;
196
+ const char* publishIdChars = env->GetStringUTFChars(publishId, nullptr);
197
+ if (publishIdChars == nullptr) return JNI_FALSE;
198
+ bool ok = nipworker_register_publish_buffer(
199
+ reinterpret_cast<void*>(handle),
200
+ publishIdChars,
201
+ static_cast<size_t>(bufferSize)
202
+ );
203
+ env->ReleaseStringUTFChars(publishId, publishIdChars);
204
+ return ok ? JNI_TRUE : JNI_FALSE;
205
+ }
206
+
207
+ extern "C" JNIEXPORT jboolean JNICALL
208
+ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeRetainSubscription(
209
+ JNIEnv* env,
210
+ jclass,
211
+ jlong handle,
212
+ jstring subId
213
+ ) {
214
+ if (handle == 0 || subId == nullptr) return JNI_FALSE;
215
+ const char* subIdChars = env->GetStringUTFChars(subId, nullptr);
216
+ if (subIdChars == nullptr) return JNI_FALSE;
217
+ bool ok = nipworker_retain_subscription(reinterpret_cast<void*>(handle), subIdChars);
218
+ env->ReleaseStringUTFChars(subId, subIdChars);
219
+ return ok ? JNI_TRUE : JNI_FALSE;
220
+ }
221
+
222
+ extern "C" JNIEXPORT void JNICALL
223
+ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeReleaseSubscription(
224
+ JNIEnv* env,
225
+ jclass,
226
+ jlong handle,
227
+ jstring subId
228
+ ) {
229
+ if (handle == 0 || subId == nullptr) return;
230
+ const char* subIdChars = env->GetStringUTFChars(subId, nullptr);
231
+ if (subIdChars == nullptr) return;
232
+ nipworker_release_subscription(reinterpret_cast<void*>(handle), subIdChars);
233
+ env->ReleaseStringUTFChars(subId, subIdChars);
234
+ }
235
+
236
+ extern "C" JNIEXPORT jobject JNICALL
237
+ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeGetSubscriptionBuffer(
238
+ JNIEnv* env,
239
+ jclass,
240
+ jlong handle,
241
+ jstring subId
242
+ ) {
243
+ if (handle == 0 || subId == nullptr) return nullptr;
244
+ const char* subIdChars = env->GetStringUTFChars(subId, nullptr);
245
+ if (subIdChars == nullptr) return nullptr;
246
+ auto* engineHandle = reinterpret_cast<void*>(handle);
247
+ uint8_t* ptr = nipworker_subscription_buffer_ptr(engineHandle, subIdChars);
248
+ size_t len = nipworker_subscription_buffer_len(engineHandle, subIdChars);
249
+ env->ReleaseStringUTFChars(subId, subIdChars);
250
+ if (ptr == nullptr || len == 0) return nullptr;
251
+ return env->NewDirectByteBuffer(ptr, static_cast<jlong>(len));
252
+ }
253
+
81
254
  extern "C" JNIEXPORT jboolean JNICALL
82
255
  Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeIsByteRuntimeInstalled(
83
256
  JNIEnv*,
@@ -158,6 +331,181 @@ Java_com_candypoets_nipworker_reactnative_NipworkerReactNativeModule_nativeInsta
158
331
  )
159
332
  );
160
333
 
334
+ byteRuntime.setProperty(
335
+ runtime,
336
+ "registerSubscription",
337
+ Function::createFromHostFunction(
338
+ runtime,
339
+ PropNameID::forAscii(runtime, "registerSubscription"),
340
+ 2,
341
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
342
+ if (count < 2 || !args[0].isString() || !args[1].isNumber()) return Value(false);
343
+ std::string subId = args[0].asString(runtime).utf8(runtime);
344
+ auto bufferSize = static_cast<size_t>(args[1].asNumber());
345
+ return Value(nipworker_register_subscription(engineHandle, subId.c_str(), bufferSize));
346
+ }
347
+ )
348
+ );
349
+
350
+ byteRuntime.setProperty(
351
+ runtime,
352
+ "subscribe",
353
+ Function::createFromHostFunction(
354
+ runtime,
355
+ PropNameID::forAscii(runtime, "subscribe"),
356
+ 2,
357
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
358
+ if (count < 2 || !isArrayBuffer(runtime, args[0]) || !args[1].isString()) {
359
+ return Value::undefined();
360
+ }
361
+ ArrayBuffer message = args[0].asObject(runtime).getArrayBuffer(runtime);
362
+ std::string subId = args[1].asString(runtime).utf8(runtime);
363
+ if (!nipworker_subscribe_message(engineHandle, message.data(runtime), message.size(runtime))) {
364
+ return Value::undefined();
365
+ }
366
+ uint8_t* ptr = nipworker_subscription_buffer_ptr(engineHandle, subId.c_str());
367
+ size_t len = nipworker_subscription_buffer_len(engineHandle, subId.c_str());
368
+ if (ptr == nullptr || len == 0) return Value::undefined();
369
+ auto nativeBuffer = std::make_shared<ExternalMutableBuffer>(ptr, len);
370
+ ArrayBuffer buffer(runtime, std::move(nativeBuffer));
371
+ return Value(runtime, std::move(buffer));
372
+ }
373
+ )
374
+ );
375
+
376
+ byteRuntime.setProperty(
377
+ runtime,
378
+ "registerPublishBuffer",
379
+ Function::createFromHostFunction(
380
+ runtime,
381
+ PropNameID::forAscii(runtime, "registerPublishBuffer"),
382
+ 2,
383
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
384
+ if (count < 2 || !args[0].isString() || !args[1].isNumber()) return Value(false);
385
+ std::string publishId = args[0].asString(runtime).utf8(runtime);
386
+ auto bufferSize = static_cast<size_t>(args[1].asNumber());
387
+ return Value(nipworker_register_publish_buffer(engineHandle, publishId.c_str(), bufferSize));
388
+ }
389
+ )
390
+ );
391
+
392
+ byteRuntime.setProperty(
393
+ runtime,
394
+ "publish",
395
+ Function::createFromHostFunction(
396
+ runtime,
397
+ PropNameID::forAscii(runtime, "publish"),
398
+ 2,
399
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
400
+ if (count < 2 || !isArrayBuffer(runtime, args[0]) || !args[1].isString()) {
401
+ return Value::undefined();
402
+ }
403
+ ArrayBuffer message = args[0].asObject(runtime).getArrayBuffer(runtime);
404
+ std::string publishId = args[1].asString(runtime).utf8(runtime);
405
+ if (!nipworker_publish_message(engineHandle, message.data(runtime), message.size(runtime))) {
406
+ return Value::undefined();
407
+ }
408
+ uint8_t* ptr = nipworker_subscription_buffer_ptr(engineHandle, publishId.c_str());
409
+ size_t len = nipworker_subscription_buffer_len(engineHandle, publishId.c_str());
410
+ if (ptr == nullptr || len == 0) return Value::undefined();
411
+ auto nativeBuffer = std::make_shared<ExternalMutableBuffer>(ptr, len);
412
+ ArrayBuffer buffer(runtime, std::move(nativeBuffer));
413
+ return Value(runtime, std::move(buffer));
414
+ }
415
+ )
416
+ );
417
+
418
+ byteRuntime.setProperty(
419
+ runtime,
420
+ "retainSubscription",
421
+ Function::createFromHostFunction(
422
+ runtime,
423
+ PropNameID::forAscii(runtime, "retainSubscription"),
424
+ 1,
425
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
426
+ if (count < 1 || !args[0].isString()) return Value(false);
427
+ std::string subId = args[0].asString(runtime).utf8(runtime);
428
+ return Value(nipworker_retain_subscription(engineHandle, subId.c_str()));
429
+ }
430
+ )
431
+ );
432
+
433
+ byteRuntime.setProperty(
434
+ runtime,
435
+ "retainSubscriptionBuffer",
436
+ Function::createFromHostFunction(
437
+ runtime,
438
+ PropNameID::forAscii(runtime, "retainSubscriptionBuffer"),
439
+ 1,
440
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
441
+ if (count < 1 || !args[0].isString()) return Value::undefined();
442
+ std::string subId = args[0].asString(runtime).utf8(runtime);
443
+ if (!nipworker_retain_subscription(engineHandle, subId.c_str())) {
444
+ return Value::undefined();
445
+ }
446
+ uint8_t* ptr = nipworker_subscription_buffer_ptr(engineHandle, subId.c_str());
447
+ size_t len = nipworker_subscription_buffer_len(engineHandle, subId.c_str());
448
+ if (ptr == nullptr || len == 0) {
449
+ nipworker_release_subscription(engineHandle, subId.c_str());
450
+ return Value::undefined();
451
+ }
452
+ auto nativeBuffer = std::make_shared<ExternalMutableBuffer>(ptr, len);
453
+ ArrayBuffer buffer(runtime, std::move(nativeBuffer));
454
+ return Value(runtime, std::move(buffer));
455
+ }
456
+ )
457
+ );
458
+
459
+ byteRuntime.setProperty(
460
+ runtime,
461
+ "releaseSubscription",
462
+ Function::createFromHostFunction(
463
+ runtime,
464
+ PropNameID::forAscii(runtime, "releaseSubscription"),
465
+ 1,
466
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
467
+ if (count < 1 || !args[0].isString()) return Value::undefined();
468
+ std::string subId = args[0].asString(runtime).utf8(runtime);
469
+ nipworker_release_subscription(engineHandle, subId.c_str());
470
+ return Value::undefined();
471
+ }
472
+ )
473
+ );
474
+
475
+ byteRuntime.setProperty(
476
+ runtime,
477
+ "getSubscriptionBuffer",
478
+ Function::createFromHostFunction(
479
+ runtime,
480
+ PropNameID::forAscii(runtime, "getSubscriptionBuffer"),
481
+ 1,
482
+ [engineHandle](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value {
483
+ if (count < 1 || !args[0].isString()) return Value::undefined();
484
+ std::string subId = args[0].asString(runtime).utf8(runtime);
485
+ uint8_t* ptr = nipworker_subscription_buffer_ptr(engineHandle, subId.c_str());
486
+ size_t len = nipworker_subscription_buffer_len(engineHandle, subId.c_str());
487
+ if (ptr == nullptr || len == 0) return Value::undefined();
488
+ auto nativeBuffer = std::make_shared<ExternalMutableBuffer>(ptr, len);
489
+ ArrayBuffer buffer(runtime, std::move(nativeBuffer));
490
+ return Value(runtime, std::move(buffer));
491
+ }
492
+ )
493
+ );
494
+
495
+ byteRuntime.setProperty(
496
+ runtime,
497
+ "cleanupSubscriptions",
498
+ Function::createFromHostFunction(
499
+ runtime,
500
+ PropNameID::forAscii(runtime, "cleanupSubscriptions"),
501
+ 0,
502
+ [engineHandle](Runtime&, const Value&, const Value*, size_t) -> Value {
503
+ nipworker_cleanup_subscriptions(engineHandle);
504
+ return Value::undefined();
505
+ }
506
+ )
507
+ );
508
+
161
509
  byteRuntime.setProperty(
162
510
  runtime,
163
511
  "deinit",
@@ -0,0 +1,292 @@
1
+ package com.candypoets.nipworker.reactnative
2
+
3
+ import com.google.flatbuffers.FlatBufferBuilder
4
+ import java.io.Closeable
5
+ import java.nio.ByteBuffer
6
+ import java.nio.ByteOrder
7
+ import nostr.fb.MainContent
8
+ import nostr.fb.MainMessage
9
+ import nostr.fb.MessageType
10
+ import nostr.fb.MuteFilterPipeConfig
11
+ import nostr.fb.ParsePipeConfig
12
+ import nostr.fb.Pipe
13
+ import nostr.fb.PipeConfig
14
+ import nostr.fb.PipelineConfig
15
+ import nostr.fb.Publish
16
+ import nostr.fb.Request
17
+ import nostr.fb.SaveToDbPipeConfig
18
+ import nostr.fb.SerializeEventsPipeConfig
19
+ import nostr.fb.StringVec
20
+ import nostr.fb.Subscribe
21
+ import nostr.fb.SubscriptionConfig
22
+ import nostr.fb.Template
23
+ import nostr.fb.WorkerMessage
24
+
25
+ data class NipworkerRequest(
26
+ val ids: List<String> = emptyList(),
27
+ val authors: List<String> = emptyList(),
28
+ val kinds: List<Int> = emptyList(),
29
+ val tags: Map<String, List<String>> = emptyMap(),
30
+ val since: Int = 0,
31
+ val until: Int = 0,
32
+ val limit: Int = 0,
33
+ val search: String? = null,
34
+ val relays: List<String> = emptyList(),
35
+ val closeOnEose: Boolean = false,
36
+ val cacheFirst: Boolean = true,
37
+ val noCache: Boolean = false,
38
+ val maxRelays: Int = 0,
39
+ )
40
+
41
+ data class NipworkerSubscriptionOptions(
42
+ val closeOnEose: Boolean = false,
43
+ val cacheFirst: Boolean = true,
44
+ val timeoutMs: Long = 0,
45
+ val maxEvents: Long = 0,
46
+ val skipCache: Boolean = false,
47
+ val force: Boolean = false,
48
+ val bytesPerEvent: Long = 3072,
49
+ val isSlow: Boolean = false,
50
+ val pagination: String? = null,
51
+ val cacheOnly: Boolean = false,
52
+ )
53
+
54
+ data class NipworkerEventTemplate(
55
+ val kind: Int,
56
+ val content: String,
57
+ val tags: List<List<String>> = emptyList(),
58
+ val createdAt: Int = 0,
59
+ )
60
+
61
+ class NipworkerWorkerMessage(private val bytes: ByteArray) {
62
+ private val buffer: ByteBuffer =
63
+ ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)
64
+
65
+ val message: WorkerMessage = WorkerMessage.getRootAsWorkerMessage(buffer)
66
+ val subId: String? get() = message.subId()
67
+ val url: String? get() = message.url()
68
+ val type: Long get() = message.type()
69
+ val contentType: Byte get() = message.contentType()
70
+
71
+ fun bytes(): ByteArray = bytes
72
+ }
73
+
74
+ class NipworkerHookHandle internal constructor(private val cancelAction: () -> Unit) : Closeable {
75
+ @Volatile
76
+ private var closed = false
77
+
78
+ override fun close() {
79
+ if (!closed) {
80
+ closed = true
81
+ cancelAction()
82
+ }
83
+ }
84
+
85
+ fun cancel() = close()
86
+ }
87
+
88
+ fun NipworkerRuntime.useSubscription(
89
+ subscriptionId: String,
90
+ requests: List<NipworkerRequest>,
91
+ options: NipworkerSubscriptionOptions = NipworkerSubscriptionOptions(),
92
+ onMessages: (List<NipworkerWorkerMessage>) -> Unit,
93
+ ): NipworkerHookHandle {
94
+ val buffer = retainSubscriptionBuffer(subscriptionId)
95
+ ?: subscribe(buildSubscribeMessage(subscriptionId, requests, options), subscriptionId)
96
+ var lastReadPosition = 4
97
+
98
+ fun drain() {
99
+ val messages = buffer?.readWorkerMessages(lastReadPosition).orEmpty()
100
+ if (messages.isNotEmpty()) {
101
+ lastReadPosition += messages.sumOf { it.bytes().size + 4 }
102
+ onMessages(messages)
103
+ }
104
+ }
105
+
106
+ val listener = addListener(subscriptionId) {
107
+ drain()
108
+ }
109
+
110
+ drain()
111
+
112
+ return NipworkerHookHandle {
113
+ listener()
114
+ releaseSubscription(subscriptionId)
115
+ }
116
+ }
117
+
118
+ fun NipworkerRuntime.usePublish(
119
+ publishId: String,
120
+ event: NipworkerEventTemplate,
121
+ defaultRelays: List<String> = emptyList(),
122
+ optimisticSubIds: List<String> = emptyList(),
123
+ onStatus: (NipworkerWorkerMessage) -> Unit,
124
+ ): NipworkerHookHandle {
125
+ val buffer = publish(buildPublishMessage(publishId, event, defaultRelays, optimisticSubIds), publishId)
126
+ var lastReadPosition = 4
127
+
128
+ fun drain() {
129
+ val messages = buffer?.readWorkerMessages(lastReadPosition).orEmpty()
130
+ if (messages.isNotEmpty()) {
131
+ lastReadPosition += messages.sumOf { it.bytes().size + 4 }
132
+ messages
133
+ .filter { it.type != MessageType.ParsedNostrEvent }
134
+ .forEach(onStatus)
135
+ }
136
+ }
137
+
138
+ val listener = addListener(publishId) {
139
+ drain()
140
+ }
141
+
142
+ drain()
143
+
144
+ return NipworkerHookHandle {
145
+ listener()
146
+ releaseSubscription(publishId)
147
+ }
148
+ }
149
+
150
+ private fun ByteBuffer.readWorkerMessages(fromPosition: Int): List<NipworkerWorkerMessage> {
151
+ val source = duplicate().order(ByteOrder.LITTLE_ENDIAN)
152
+ if (source.capacity() < 4) return emptyList()
153
+ val writePosition = source.getInt(0).coerceAtMost(source.capacity())
154
+ var position = fromPosition
155
+ val messages = mutableListOf<NipworkerWorkerMessage>()
156
+ while (position + 4 <= writePosition) {
157
+ val messageLength = source.getInt(position)
158
+ if (messageLength <= 0 || position + 4 + messageLength > writePosition) break
159
+ val bytes = ByteArray(messageLength)
160
+ source.position(position + 4)
161
+ source.get(bytes)
162
+ messages.add(NipworkerWorkerMessage(bytes))
163
+ position += 4 + messageLength
164
+ }
165
+ return messages
166
+ }
167
+
168
+ private fun buildSubscribeMessage(
169
+ subId: String,
170
+ requests: List<NipworkerRequest>,
171
+ options: NipworkerSubscriptionOptions,
172
+ ): ByteArray {
173
+ val builder = FlatBufferBuilder(2048)
174
+ val requestOffsets = requests.map { request -> buildRequest(builder, request, options) }.toIntArray()
175
+ val requestsOffset = Subscribe.createRequestsVector(builder, requestOffsets)
176
+ val pipelineOffset = buildDefaultPipeline(builder, subId)
177
+ val paginationOffset = options.pagination?.let { builder.createString(it) } ?: 0
178
+ val configOffset = SubscriptionConfig.createSubscriptionConfig(
179
+ builder,
180
+ pipelineOffset,
181
+ options.closeOnEose,
182
+ options.cacheFirst,
183
+ options.timeoutMs,
184
+ options.maxEvents,
185
+ options.skipCache,
186
+ options.force,
187
+ options.bytesPerEvent,
188
+ options.isSlow,
189
+ paginationOffset,
190
+ options.cacheOnly,
191
+ )
192
+ val subscribeOffset = Subscribe.createSubscribe(
193
+ builder,
194
+ builder.createString(subId),
195
+ requestsOffset,
196
+ configOffset,
197
+ )
198
+ val root = MainMessage.createMainMessage(builder, MainContent.Subscribe, subscribeOffset)
199
+ builder.finish(root)
200
+ return builder.sizedByteArray()
201
+ }
202
+
203
+ private fun buildRequest(
204
+ builder: FlatBufferBuilder,
205
+ request: NipworkerRequest,
206
+ options: NipworkerSubscriptionOptions,
207
+ ): Int {
208
+ val idsOffset = stringVector(builder, request.ids)
209
+ val authorsOffset = stringVector(builder, request.authors)
210
+ val kindsOffset = if (request.kinds.isEmpty()) 0 else Request.createKindsVector(builder, request.kinds.toIntArray())
211
+ val tagOffsets = request.tags.map { (key, values) ->
212
+ StringVec.createStringVec(builder, stringVector(builder, listOf(key) + values))
213
+ }.toIntArray()
214
+ val tagsOffset = if (tagOffsets.isEmpty()) 0 else Request.createTagsVector(builder, tagOffsets)
215
+ val searchOffset = request.search?.let { builder.createString(it) } ?: 0
216
+ val relaysOffset = stringVector(builder, request.relays)
217
+ return Request.createRequest(
218
+ builder,
219
+ idsOffset,
220
+ authorsOffset,
221
+ kindsOffset,
222
+ tagsOffset,
223
+ request.limit,
224
+ request.since,
225
+ request.until,
226
+ searchOffset,
227
+ relaysOffset,
228
+ request.closeOnEose,
229
+ request.cacheFirst,
230
+ request.noCache,
231
+ request.maxRelays,
232
+ options.cacheOnly,
233
+ )
234
+ }
235
+
236
+ private fun buildDefaultPipeline(builder: FlatBufferBuilder, subId: String): Int {
237
+ val muteStart = MuteFilterPipeConfig.startMuteFilterPipeConfig(builder)
238
+ val muteConfig = MuteFilterPipeConfig.endMuteFilterPipeConfig(builder)
239
+ val mutePipe = Pipe.createPipe(builder, PipeConfig.MuteFilterPipeConfig, muteConfig)
240
+
241
+ val parseStart = ParsePipeConfig.startParsePipeConfig(builder)
242
+ val parseConfig = ParsePipeConfig.endParsePipeConfig(builder)
243
+ val parsePipe = Pipe.createPipe(builder, PipeConfig.ParsePipeConfig, parseConfig)
244
+
245
+ val saveStart = SaveToDbPipeConfig.startSaveToDbPipeConfig(builder)
246
+ val saveConfig = SaveToDbPipeConfig.endSaveToDbPipeConfig(builder)
247
+ val savePipe = Pipe.createPipe(builder, PipeConfig.SaveToDbPipeConfig, saveConfig)
248
+
249
+ val serializeConfig = SerializeEventsPipeConfig.createSerializeEventsPipeConfig(builder, builder.createString(subId))
250
+ val serializePipe = Pipe.createPipe(builder, PipeConfig.SerializeEventsPipeConfig, serializeConfig)
251
+
252
+ return PipelineConfig.createPipelineConfig(
253
+ builder,
254
+ PipelineConfig.createPipesVector(builder, intArrayOf(mutePipe, parsePipe, savePipe, serializePipe)),
255
+ )
256
+ }
257
+
258
+ private fun buildPublishMessage(
259
+ publishId: String,
260
+ event: NipworkerEventTemplate,
261
+ defaultRelays: List<String>,
262
+ optimisticSubIds: List<String>,
263
+ ): ByteArray {
264
+ val builder = FlatBufferBuilder(1024)
265
+ val tags = event.tags.map { tag ->
266
+ StringVec.createStringVec(builder, stringVector(builder, tag))
267
+ }.toIntArray()
268
+ val tagsOffset = Template.createTagsVector(builder, tags)
269
+ val templateOffset = Template.createTemplate(
270
+ builder,
271
+ event.kind,
272
+ event.createdAt,
273
+ builder.createString(event.content),
274
+ tagsOffset,
275
+ )
276
+ val publishOffset = Publish.createPublish(
277
+ builder,
278
+ builder.createString(publishId),
279
+ templateOffset,
280
+ stringVector(builder, defaultRelays),
281
+ stringVector(builder, optimisticSubIds),
282
+ )
283
+ val root = MainMessage.createMainMessage(builder, MainContent.Publish, publishOffset)
284
+ builder.finish(root)
285
+ return builder.sizedByteArray()
286
+ }
287
+
288
+ private fun stringVector(builder: FlatBufferBuilder, values: List<String>): Int {
289
+ if (values.isEmpty()) return 0
290
+ val offsets = values.map { builder.createString(it) }.toIntArray()
291
+ return builder.createVectorOfTables(offsets)
292
+ }