ping-openmls-sdk-react-native-macos 0.2.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.
- package/Frameworks/.gitkeep +0 -0
- package/Frameworks/libping_ffi.a +0 -0
- package/README.md +64 -0
- package/ios/.gitkeep +0 -0
- package/ios/Generated.swift +3239 -0
- package/ios/JSObserverBridge.swift +35 -0
- package/ios/JSStorageBridge.swift +135 -0
- package/ios/JSTransportBridge.swift +126 -0
- package/ios/PingNativeModule.m +143 -0
- package/ios/PingNativeModule.swift +656 -0
- package/ios/TypeBridge.swift +297 -0
- package/ios/module.modulemap +4 -0
- package/ios/pingFFI.h +990 -0
- package/package.json +34 -0
- package/ping-openmls-sdk-react-native-macos.podspec +54 -0
- package/src/MessagingClient.ts +488 -0
- package/src/NativePing.ts +162 -0
- package/src/WebSocketTransport.ts +118 -0
- package/src/clipboard.ts +37 -0
- package/src/index.ts +112 -0
- package/src/storage-bridge.ts +124 -0
- package/src/transport-bridge.ts +89 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// Type encoding/decoding helpers between UniFFI Swift types and JS-friendly JSON.
|
|
2
|
+
//
|
|
3
|
+
// RN's bridge marshals only primitives, NSDictionary, NSArray, NSString, NSNumber, and
|
|
4
|
+
// NSData. UniFFI types (`MessageEnvelope`, `ConversationId`, `DeviceId`, `Hlc`, etc.) are
|
|
5
|
+
// pure Swift structs/classes — they need to be projected into the bridge's vocabulary
|
|
6
|
+
// before they can cross to JS, and reconstructed from it on the way back.
|
|
7
|
+
//
|
|
8
|
+
// Encoding rule: byte fields cross as `[Int]` arrays, matching the `{_bytes: [...]}`
|
|
9
|
+
// projection that the existing web/relay code already uses (just unwrapped one level
|
|
10
|
+
// because RN doesn't need the wrapper). All structs become `[String: Any]` dictionaries
|
|
11
|
+
// keyed by snake_case field names — JS code that received CBOR from the relay gets the
|
|
12
|
+
// same shape.
|
|
13
|
+
//
|
|
14
|
+
// Stage 4a establishes the encoders/decoders. Stage 4b uses them in `MessagingClient.init`.
|
|
15
|
+
// Stage 4c+ uses them in every method that takes/returns UniFFI types.
|
|
16
|
+
//
|
|
17
|
+
// Spec: docs/RN_NATIVE_BINDINGS.md (stage 4).
|
|
18
|
+
|
|
19
|
+
import Foundation
|
|
20
|
+
|
|
21
|
+
enum TypeBridge {
|
|
22
|
+
|
|
23
|
+
// MARK: - Bytes
|
|
24
|
+
|
|
25
|
+
static func encodeBytes(_ data: Data) -> [Int] {
|
|
26
|
+
return data.map { Int($0) }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static func decodeBytes(_ value: Any?) -> Data? {
|
|
30
|
+
guard let value, !(value is NSNull) else { return nil }
|
|
31
|
+
if let arr = value as? [NSNumber] {
|
|
32
|
+
var bytes = [UInt8](); bytes.reserveCapacity(arr.count)
|
|
33
|
+
for n in arr { bytes.append(n.uint8Value) }
|
|
34
|
+
return Data(bytes)
|
|
35
|
+
}
|
|
36
|
+
if let arr = value as? [Int] {
|
|
37
|
+
return Data(arr.map { UInt8(truncatingIfNeeded: $0) })
|
|
38
|
+
}
|
|
39
|
+
// Relay JSON projection wraps byte fields as `{_bytes: [...]}` for browser
|
|
40
|
+
// DevTools inspectability — unwrap and recurse on the inner array.
|
|
41
|
+
if let dict = value as? [String: Any], let inner = dict["_bytes"] {
|
|
42
|
+
return decodeBytes(inner)
|
|
43
|
+
}
|
|
44
|
+
return nil
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static func decodeBytesOrThrow(_ value: Any?, field: String) throws -> Data {
|
|
48
|
+
guard let data = decodeBytes(value) else {
|
|
49
|
+
throw BridgeError.decodeFailure("expected bytes for field `\(field)`, got \(type(of: value))")
|
|
50
|
+
}
|
|
51
|
+
return data
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// MARK: - ConversationId / DeviceId / UserId
|
|
55
|
+
//
|
|
56
|
+
// The UniFFI surface declares these as dictionaries containing a `value: bytes` field.
|
|
57
|
+
// We project them to/from JS as bare `[Int]` arrays for ergonomics — the JS user
|
|
58
|
+
// doesn't need to deal with the dictionary wrapping.
|
|
59
|
+
|
|
60
|
+
static func encodeConversationId(_ id: ConversationId) -> [Int] {
|
|
61
|
+
return encodeBytes(id.value)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static func decodeConversationId(_ value: Any?) throws -> ConversationId {
|
|
65
|
+
let data = try decodeBytesOrThrow(value, field: "conversation_id")
|
|
66
|
+
return ConversationId(value: data)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static func encodeDeviceId(_ id: DeviceId) -> [Int] {
|
|
70
|
+
return encodeBytes(id.value)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static func decodeDeviceId(_ value: Any?) throws -> DeviceId {
|
|
74
|
+
let data = try decodeBytesOrThrow(value, field: "device_id")
|
|
75
|
+
return DeviceId(value: data)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static func encodeUserId(_ id: UserId) -> [Int] {
|
|
79
|
+
return encodeBytes(id.value)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static func decodeUserId(_ value: Any?) throws -> UserId {
|
|
83
|
+
let data = try decodeBytesOrThrow(value, field: "user_id")
|
|
84
|
+
return UserId(value: data)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// MARK: - Hlc
|
|
88
|
+
|
|
89
|
+
static func encodeHlc(_ hlc: Hlc) -> [String: Any] {
|
|
90
|
+
return ["wall_ms": Int(hlc.wallMs), "logical": Int(hlc.logical)]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static func decodeHlc(_ value: Any?) throws -> Hlc {
|
|
94
|
+
guard let dict = value as? [String: Any] else {
|
|
95
|
+
throw BridgeError.decodeFailure("expected hlc dict")
|
|
96
|
+
}
|
|
97
|
+
let wallMs = (dict["wall_ms"] as? NSNumber)?.uint64Value
|
|
98
|
+
?? UInt64(dict["wall_ms"] as? Int ?? 0)
|
|
99
|
+
let logical = (dict["logical"] as? NSNumber)?.uint32Value
|
|
100
|
+
?? UInt32(dict["logical"] as? Int ?? 0)
|
|
101
|
+
return Hlc(wallMs: wallMs, logical: logical)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// MARK: - MessageKind
|
|
105
|
+
|
|
106
|
+
static func encodeMessageKind(_ kind: MessageKind) -> String {
|
|
107
|
+
switch kind {
|
|
108
|
+
case .application: return "Application"
|
|
109
|
+
case .commit: return "Commit"
|
|
110
|
+
case .welcome: return "Welcome"
|
|
111
|
+
case .proposal: return "Proposal"
|
|
112
|
+
case .keyPackage: return "KeyPackage"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static func decodeMessageKind(_ value: Any?) throws -> MessageKind {
|
|
117
|
+
guard let s = value as? String else {
|
|
118
|
+
throw BridgeError.decodeFailure("expected MessageKind string")
|
|
119
|
+
}
|
|
120
|
+
switch s {
|
|
121
|
+
case "Application": return .application
|
|
122
|
+
case "Commit": return .commit
|
|
123
|
+
case "Welcome": return .welcome
|
|
124
|
+
case "Proposal": return .proposal
|
|
125
|
+
case "KeyPackage": return .keyPackage
|
|
126
|
+
default:
|
|
127
|
+
throw BridgeError.decodeFailure("unknown MessageKind: \(s)")
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// MARK: - MessageEnvelope
|
|
132
|
+
|
|
133
|
+
static func encodeEnvelope(_ env: MessageEnvelope) -> [String: Any] {
|
|
134
|
+
return [
|
|
135
|
+
"v": Int(env.v),
|
|
136
|
+
"conversation_id": encodeConversationId(env.conversationId),
|
|
137
|
+
"epoch": Int(env.epoch),
|
|
138
|
+
"kind": encodeMessageKind(env.kind),
|
|
139
|
+
"sender_device": encodeDeviceId(env.senderDevice),
|
|
140
|
+
"seq": Int(env.seq),
|
|
141
|
+
"hlc": encodeHlc(env.hlc),
|
|
142
|
+
"payload": encodeBytes(env.payload),
|
|
143
|
+
"content_hash": encodeBytes(env.contentHash),
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static func decodeEnvelope(_ value: Any?) throws -> MessageEnvelope {
|
|
148
|
+
guard let dict = value as? [String: Any] else {
|
|
149
|
+
throw BridgeError.decodeFailure("expected envelope dict")
|
|
150
|
+
}
|
|
151
|
+
let v = UInt8((dict["v"] as? NSNumber)?.intValue ?? (dict["v"] as? Int ?? 1))
|
|
152
|
+
let convId = try decodeConversationId(dict["conversation_id"])
|
|
153
|
+
let epoch = (dict["epoch"] as? NSNumber)?.uint64Value
|
|
154
|
+
?? UInt64(dict["epoch"] as? Int ?? 0)
|
|
155
|
+
let kind = try decodeMessageKind(dict["kind"])
|
|
156
|
+
let senderDevice = try decodeDeviceId(dict["sender_device"])
|
|
157
|
+
let seq = (dict["seq"] as? NSNumber)?.uint64Value
|
|
158
|
+
?? UInt64(dict["seq"] as? Int ?? 0)
|
|
159
|
+
let hlc = try decodeHlc(dict["hlc"])
|
|
160
|
+
let payload = try decodeBytesOrThrow(dict["payload"], field: "payload")
|
|
161
|
+
let contentHash = try decodeBytesOrThrow(dict["content_hash"], field: "content_hash")
|
|
162
|
+
return MessageEnvelope(
|
|
163
|
+
v: v,
|
|
164
|
+
conversationId: convId,
|
|
165
|
+
epoch: epoch,
|
|
166
|
+
kind: kind,
|
|
167
|
+
senderDevice: senderDevice,
|
|
168
|
+
seq: seq,
|
|
169
|
+
hlc: hlc,
|
|
170
|
+
payload: payload,
|
|
171
|
+
contentHash: contentHash
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static func encodeEnvelopeArray(_ envs: [MessageEnvelope]) -> [[String: Any]] {
|
|
176
|
+
return envs.map { encodeEnvelope($0) }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static func decodeEnvelopeArray(_ value: Any?) throws -> [MessageEnvelope] {
|
|
180
|
+
guard let arr = value as? [[String: Any]] else {
|
|
181
|
+
throw BridgeError.decodeFailure("expected envelope array")
|
|
182
|
+
}
|
|
183
|
+
return try arr.map { try decodeEnvelope($0) }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// MARK: - IncomingMessage (output-only; we encode but never decode)
|
|
187
|
+
|
|
188
|
+
static func encodeIncomingMessage(_ msg: IncomingMessage) -> [String: Any] {
|
|
189
|
+
return [
|
|
190
|
+
"conversation_id": encodeConversationId(msg.conversationId),
|
|
191
|
+
"sender_device": encodeDeviceId(msg.senderDevice),
|
|
192
|
+
"epoch": Int(msg.epoch),
|
|
193
|
+
"hlc": encodeHlc(msg.hlc),
|
|
194
|
+
"plaintext": encodeBytes(msg.plaintext),
|
|
195
|
+
"content_hash": encodeBytes(msg.contentHash),
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
static func encodeIncomingMessageArray(_ msgs: [IncomingMessage]) -> [[String: Any]] {
|
|
200
|
+
return msgs.map { encodeIncomingMessage($0) }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// MARK: - ConversationMeta
|
|
204
|
+
|
|
205
|
+
static func encodeConversationMeta(_ m: ConversationMeta) -> [String: Any] {
|
|
206
|
+
return [
|
|
207
|
+
"id": encodeConversationId(m.id),
|
|
208
|
+
"name": m.name as Any? ?? NSNull(),
|
|
209
|
+
"epoch": Int(m.epoch),
|
|
210
|
+
"member_count": Int(m.memberCount),
|
|
211
|
+
"is_device_group": m.isDeviceGroup,
|
|
212
|
+
"created_at_ms": Int(m.createdAtMs),
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static func encodeConversationMetaArray(_ ms: [ConversationMeta]) -> [[String: Any]] {
|
|
217
|
+
return ms.map { encodeConversationMeta($0) }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// MARK: - DeviceInfo
|
|
221
|
+
|
|
222
|
+
static func encodeDeviceInfo(_ d: DeviceInfo) -> [String: Any] {
|
|
223
|
+
return [
|
|
224
|
+
"device_id": encodeDeviceId(d.deviceId),
|
|
225
|
+
"user_id": encodeUserId(d.userId),
|
|
226
|
+
"label": d.label,
|
|
227
|
+
"created_at_ms": Int(d.createdAtMs),
|
|
228
|
+
"last_seen_ms": Int(d.lastSeenMs),
|
|
229
|
+
"revoked": d.revoked,
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static func encodeDeviceInfoArray(_ ds: [DeviceInfo]) -> [[String: Any]] {
|
|
234
|
+
return ds.map { encodeDeviceInfo($0) }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// MARK: - DiscoveredDevice
|
|
238
|
+
|
|
239
|
+
static func encodeDiscoveredDevice(_ d: DiscoveredDevice) -> [String: Any] {
|
|
240
|
+
return [
|
|
241
|
+
"device_id": encodeDeviceId(d.deviceId),
|
|
242
|
+
"key_package": encodeBytes(d.keyPackage),
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static func decodeDiscoveredDevice(_ value: Any?) throws -> DiscoveredDevice {
|
|
247
|
+
guard let dict = value as? [String: Any] else {
|
|
248
|
+
throw BridgeError.decodeFailure("expected DiscoveredDevice dict")
|
|
249
|
+
}
|
|
250
|
+
let deviceId = try decodeDeviceId(dict["device_id"])
|
|
251
|
+
let keyPackage = try decodeBytesOrThrow(dict["key_package"], field: "key_package")
|
|
252
|
+
return DiscoveredDevice(deviceId: deviceId, keyPackage: keyPackage)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
static func decodeDiscoveredDeviceArray(_ value: Any?) throws -> [DiscoveredDevice] {
|
|
256
|
+
guard let arr = value as? [[String: Any]] else {
|
|
257
|
+
throw BridgeError.decodeFailure("expected DiscoveredDevice array")
|
|
258
|
+
}
|
|
259
|
+
return try arr.map { try decodeDiscoveredDevice($0) }
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// MARK: - LinkingTicket
|
|
263
|
+
|
|
264
|
+
static func encodeLinkingTicket(_ t: LinkingTicket) -> [String: Any] {
|
|
265
|
+
return [
|
|
266
|
+
"v": Int(t.v),
|
|
267
|
+
"user_id": encodeUserId(t.userId),
|
|
268
|
+
"user_pubkey": encodeBytes(t.userPubkey),
|
|
269
|
+
"new_device_id": encodeDeviceId(t.newDeviceId),
|
|
270
|
+
"device_binding_sig": encodeBytes(t.deviceBindingSig),
|
|
271
|
+
"device_group_welcome": encodeBytes(t.deviceGroupWelcome),
|
|
272
|
+
"catchup_snapshot": encodeBytes(t.catchupSnapshot),
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
static func decodeLinkingTicket(_ value: Any?) throws -> LinkingTicket {
|
|
277
|
+
guard let dict = value as? [String: Any] else {
|
|
278
|
+
throw BridgeError.decodeFailure("expected LinkingTicket dict")
|
|
279
|
+
}
|
|
280
|
+
let v = UInt8((dict["v"] as? NSNumber)?.intValue ?? (dict["v"] as? Int ?? 1))
|
|
281
|
+
let userId = try decodeUserId(dict["user_id"])
|
|
282
|
+
let userPubkey = try decodeBytesOrThrow(dict["user_pubkey"], field: "user_pubkey")
|
|
283
|
+
let newDeviceId = try decodeDeviceId(dict["new_device_id"])
|
|
284
|
+
let bindingSig = try decodeBytesOrThrow(dict["device_binding_sig"], field: "device_binding_sig")
|
|
285
|
+
let groupWelcome = try decodeBytesOrThrow(dict["device_group_welcome"], field: "device_group_welcome")
|
|
286
|
+
let snapshot = try decodeBytesOrThrow(dict["catchup_snapshot"], field: "catchup_snapshot")
|
|
287
|
+
return LinkingTicket(
|
|
288
|
+
v: v,
|
|
289
|
+
userId: userId,
|
|
290
|
+
userPubkey: userPubkey,
|
|
291
|
+
newDeviceId: newDeviceId,
|
|
292
|
+
deviceBindingSig: bindingSig,
|
|
293
|
+
deviceGroupWelcome: groupWelcome,
|
|
294
|
+
catchupSnapshot: snapshot
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
}
|