@derec-alliance/react-native 0.0.5 → 0.0.7
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/README.md +33 -15
- package/android/CMakeLists.txt +1 -1
- package/android/src/main/jniLibs/arm64-v8a/libderec_library.a +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libderec_library.a +0 -0
- package/android/src/main/jniLibs/x86_64/libderec_library.a +0 -0
- package/cpp/Convert.cpp +125 -0
- package/cpp/Convert.h +35 -0
- package/cpp/DeRecInstaller.cpp +0 -12
- package/cpp/Primitives.cpp +175 -26
- package/cpp/Primitives.h +33 -0
- package/cpp/ProtocolHost.cpp +38 -50
- package/cpp/StoreCallbacks.cpp +179 -301
- package/cpp/UserSecretsJson.cpp +109 -0
- package/cpp/UserSecretsJson.h +28 -0
- package/cpp/derec_ffi.h +298 -132
- package/ios/DeRecFFI.xcframework/Info.plist +5 -5
- package/ios/DeRecFFI.xcframework/ios-arm64/Headers/derec_ffi.h +298 -132
- package/ios/DeRecFFI.xcframework/ios-arm64/libderec_library.a +0 -0
- package/ios/DeRecFFI.xcframework/ios-arm64_x86_64-simulator/Headers/derec_ffi.h +298 -132
- package/ios/DeRecFFI.xcframework/ios-arm64_x86_64-simulator/libderec_library.a +2 -2
- package/lib/errors.d.ts +16 -0
- package/lib/native.d.ts +5 -1
- package/lib/primitives.d.ts +18 -21
- package/lib/primitives.js +48 -25
- package/lib/protocol.d.ts +65 -102
- package/lib/protocol.js +98 -171
- package/lib/types.d.ts +299 -110
- package/lib/types.js +43 -13
- package/package.json +1 -1
- package/src/errors.ts +17 -0
- package/src/native.ts +2 -1
- package/src/primitives.ts +82 -25
- package/src/protocol.ts +105 -189
- package/src/types.ts +304 -99
package/cpp/StoreCallbacks.cpp
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
#include "Convert.h"
|
|
12
12
|
#include "Primitives.h"
|
|
13
|
+
#include "UserSecretsJson.h"
|
|
13
14
|
|
|
14
15
|
using namespace facebook;
|
|
15
16
|
|
|
@@ -24,9 +25,9 @@ namespace {
|
|
|
24
25
|
|
|
25
26
|
static_assert(sizeof(ChannelStoreCallbacks) == 9 * sizeof(void*),
|
|
26
27
|
"ChannelStoreCallbacks layout changed; update the bindings");
|
|
27
|
-
static_assert(sizeof(SecretStoreCallbacks) ==
|
|
28
|
+
static_assert(sizeof(SecretStoreCallbacks) == 6 * sizeof(void*),
|
|
28
29
|
"SecretStoreCallbacks layout changed; update the bindings");
|
|
29
|
-
static_assert(sizeof(ShareStoreCallbacks) ==
|
|
30
|
+
static_assert(sizeof(ShareStoreCallbacks) == 10 * sizeof(void*),
|
|
30
31
|
"ShareStoreCallbacks layout changed; update the bindings");
|
|
31
32
|
static_assert(sizeof(UserSecretStoreCallbacks) == 5 * sizeof(void*),
|
|
32
33
|
"UserSecretStoreCallbacks layout changed; update the bindings");
|
|
@@ -74,118 +75,6 @@ jsi::Value idVal(jsi::Runtime& rt, uint64_t id) {
|
|
|
74
75
|
return jsi::Value(rt, jsi::String::createFromUtf8(rt, std::to_string(id)));
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
/// Build a genuine `Uint8Array` (not a bare `ArrayBuffer`) — every store
|
|
78
|
-
/// interface types its byte payloads as `Uint8Array`, and application code
|
|
79
|
-
/// is entitled to index or slice it directly.
|
|
80
|
-
jsi::Value toUint8ArrayVal(jsi::Runtime& rt, const uint8_t* bytes, size_t len) {
|
|
81
|
-
jsi::Uint8Array arr(rt, len);
|
|
82
|
-
if (len > 0 && bytes != nullptr) {
|
|
83
|
-
jsi::ArrayBuffer buffer = arr.buffer(rt);
|
|
84
|
-
std::memcpy(buffer.data(rt), bytes, len);
|
|
85
|
-
}
|
|
86
|
-
return jsi::Value(rt, std::move(arr));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
jsi::Value toUint8ArrayVal(jsi::Runtime& rt, const std::vector<uint8_t>& bytes) {
|
|
90
|
-
return toUint8ArrayVal(rt, bytes.data(), bytes.size());
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Minimal decoders for the wire's "bare JSON array/object of unsigned
|
|
94
|
-
// integers" convention (`versions_json`, `channel_ids_json`, the `bytes`
|
|
95
|
-
// field of a `ShareRecord` / `SecretValueRecord`). These are produced by
|
|
96
|
-
// `serde_json`'s default compact, whitespace-free output over a fixed,
|
|
97
|
-
// known shape, so a substring scan is sufficient and — critically — never
|
|
98
|
-
// routes a `u64` through a JS `number`, which cannot represent every value
|
|
99
|
-
// in that range exactly.
|
|
100
|
-
|
|
101
|
-
/// Parse a bare `[n, n, ...]` array of unsigned decimal integers.
|
|
102
|
-
std::vector<uint64_t> parseUnsignedJsonArray(const uint8_t* ptr, size_t len) {
|
|
103
|
-
std::vector<uint64_t> out;
|
|
104
|
-
size_t i = 0;
|
|
105
|
-
while (i < len && ptr[i] != '[') ++i;
|
|
106
|
-
if (i >= len) return out;
|
|
107
|
-
++i;
|
|
108
|
-
while (i < len && ptr[i] != ']') {
|
|
109
|
-
if (ptr[i] < '0' || ptr[i] > '9') {
|
|
110
|
-
++i;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
uint64_t value = 0;
|
|
114
|
-
while (i < len && ptr[i] >= '0' && ptr[i] <= '9') {
|
|
115
|
-
value = value * 10 + static_cast<uint64_t>(ptr[i] - '0');
|
|
116
|
-
++i;
|
|
117
|
-
}
|
|
118
|
-
out.push_back(value);
|
|
119
|
-
}
|
|
120
|
-
return out;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/// Offset just past `"key":` in a flat, compact JSON object, or
|
|
124
|
-
/// `std::string::npos` if `key` is absent.
|
|
125
|
-
size_t fieldValueOffset(const std::string& json, const char* key) {
|
|
126
|
-
std::string needle = std::string("\"") + key + "\":";
|
|
127
|
-
size_t pos = json.find(needle);
|
|
128
|
-
return pos == std::string::npos ? std::string::npos : pos + needle.size();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
uint64_t parseUnsignedAt(const std::string& json, size_t pos) {
|
|
132
|
-
uint64_t value = 0;
|
|
133
|
-
while (pos < json.size() && json[pos] >= '0' && json[pos] <= '9') {
|
|
134
|
-
value = value * 10 + static_cast<uint64_t>(json[pos] - '0');
|
|
135
|
-
++pos;
|
|
136
|
-
}
|
|
137
|
-
return value;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
std::vector<uint8_t> parseByteArrayAt(const std::string& json, size_t pos) {
|
|
141
|
-
std::vector<uint8_t> out;
|
|
142
|
-
while (pos < json.size() && json[pos] != '[') ++pos;
|
|
143
|
-
if (pos >= json.size()) return out;
|
|
144
|
-
++pos;
|
|
145
|
-
while (pos < json.size() && json[pos] != ']') {
|
|
146
|
-
if (json[pos] < '0' || json[pos] > '9') {
|
|
147
|
-
++pos;
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
uint64_t value = 0;
|
|
151
|
-
while (pos < json.size() && json[pos] >= '0' && json[pos] <= '9') {
|
|
152
|
-
value = value * 10 + static_cast<uint64_t>(json[pos] - '0');
|
|
153
|
-
++pos;
|
|
154
|
-
}
|
|
155
|
-
out.push_back(static_cast<uint8_t>(value));
|
|
156
|
-
}
|
|
157
|
-
return out;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/// `{"kind":<n>,"bytes":[<n>,...]}` — the wire shape `SecretStore.save`
|
|
161
|
-
/// receives; `kind` is already carried as a separate FFI argument so only
|
|
162
|
-
/// `bytes` needs to be read back out.
|
|
163
|
-
std::vector<uint8_t> decodeSecretValueBytes(const uint8_t* ptr, size_t len) {
|
|
164
|
-
std::string json(reinterpret_cast<const char*>(ptr), len);
|
|
165
|
-
size_t pos = fieldValueOffset(json, "bytes");
|
|
166
|
-
if (pos == std::string::npos) return {};
|
|
167
|
-
return parseByteArrayAt(json, pos);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/// `{"secret_id":"...","version":<n>,"bytes":[<n>,...]}` — the wire shape
|
|
171
|
-
/// `ShareStore.save` receives. `secret_id` duplicates the FFI's own
|
|
172
|
-
/// `secret_id` argument, so only `version` and `bytes` are decoded.
|
|
173
|
-
struct DecodedShare {
|
|
174
|
-
uint32_t version;
|
|
175
|
-
std::vector<uint8_t> bytes;
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
DecodedShare decodeShareRecord(const uint8_t* ptr, size_t len) {
|
|
179
|
-
std::string json(reinterpret_cast<const char*>(ptr), len);
|
|
180
|
-
size_t versionPos = fieldValueOffset(json, "version");
|
|
181
|
-
size_t bytesPos = fieldValueOffset(json, "bytes");
|
|
182
|
-
DecodedShare out{};
|
|
183
|
-
out.version =
|
|
184
|
-
versionPos == std::string::npos ? 0 : static_cast<uint32_t>(parseUnsignedAt(json, versionPos));
|
|
185
|
-
out.bytes = bytesPos == std::string::npos ? std::vector<uint8_t>{} : parseByteArrayAt(json, bytesPos);
|
|
186
|
-
return out;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
78
|
/// True only for a non-empty run of ASCII decimal digits.
|
|
190
79
|
///
|
|
191
80
|
/// Every `u64` id crosses this boundary as a decimal string, and the wire
|
|
@@ -376,6 +265,37 @@ ResultConverter makeSecretLoadResult(uint32_t kind) {
|
|
|
376
265
|
};
|
|
377
266
|
}
|
|
378
267
|
|
|
268
|
+
/// `SecretStore.loadMany(): Promise<Array<Uint8Array | null | undefined>>`.
|
|
269
|
+
/// Encodes the resolved array element by element, in order, as the wire's
|
|
270
|
+
/// `Vec<Option<SecretValueRecord>>` JSON: the record `load` builds, or `null`
|
|
271
|
+
/// for a `null`/`undefined` entry. The library pairs entries with the
|
|
272
|
+
/// requested ids and decides what a missing one means.
|
|
273
|
+
///
|
|
274
|
+
/// A non-array return is a backend failure, never an empty list.
|
|
275
|
+
ResultConverter makeSecretLoadManyResult(uint32_t kind) {
|
|
276
|
+
return [kind](jsi::Runtime& rt, const jsi::Value& value) -> CallResult {
|
|
277
|
+
if (!value.isObject()) {
|
|
278
|
+
return CallResult{kBackendFailure, {}};
|
|
279
|
+
}
|
|
280
|
+
jsi::Array arr = value.asObject(rt).asArray(rt);
|
|
281
|
+
size_t n = arr.size(rt);
|
|
282
|
+
std::string text = "[";
|
|
283
|
+
for (size_t i = 0; i < n; ++i) {
|
|
284
|
+
if (i != 0) text += ',';
|
|
285
|
+
jsi::Value entry = arr.getValueAtIndex(rt, i);
|
|
286
|
+
if (entry.isNull() || entry.isUndefined()) {
|
|
287
|
+
text += "null";
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
ByteView bytes = asBytes(rt, entry);
|
|
291
|
+
std::vector<uint8_t> record = secretValueRecordJson(kind, bytes.ptr, bytes.len);
|
|
292
|
+
text.append(record.begin(), record.end());
|
|
293
|
+
}
|
|
294
|
+
text += ']';
|
|
295
|
+
return CallResult{0, std::vector<uint8_t>(text.begin(), text.end())};
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
379
299
|
/// `StateStore.loadAll(): Promise<Uint8Array[]>`. Each element is already
|
|
380
300
|
/// the exact item-JSON blob the wire expects; splice the raw bytes into an
|
|
381
301
|
/// array rather than re-parsing and re-serialising them.
|
|
@@ -400,43 +320,38 @@ CallResult toStateLoadAllResult(jsi::Runtime& rt, const jsi::Value& value) {
|
|
|
400
320
|
return CallResult{0, std::vector<uint8_t>(text.begin(), text.end())};
|
|
401
321
|
}
|
|
402
322
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
std::vector<uint8_t> jsonStringifyToUtf8(jsi::Runtime& rt, const jsi::Value& value) {
|
|
417
|
-
jsi::Object json = rt.global().getPropertyAsObject(rt, "JSON");
|
|
418
|
-
jsi::Function stringify = json.getPropertyAsFunction(rt, "stringify");
|
|
419
|
-
jsi::Value result = stringify.call(rt, value);
|
|
420
|
-
std::string text = result.asString(rt).utf8(rt);
|
|
421
|
-
return std::vector<uint8_t>(text.begin(), text.end());
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
jsi::Array bytesToNumberArray(jsi::Runtime& rt, ByteView view) {
|
|
425
|
-
jsi::Array arr(rt, view.len);
|
|
426
|
-
for (size_t i = 0; i < view.len; ++i) {
|
|
427
|
-
arr.setValueAtIndex(rt, i, jsi::Value(static_cast<int>(view.ptr[i])));
|
|
323
|
+
/// `ShareStore.keepList(): Promise<number[] | null | undefined>`. Encodes the
|
|
324
|
+
/// resolved value as the wire's `Option<Vec<u32>>` JSON: `null` for
|
|
325
|
+
/// `null`/`undefined`, otherwise the array of versions.
|
|
326
|
+
///
|
|
327
|
+
/// Anything else, or an entry that is not an integer in `0..=4294967295`, is
|
|
328
|
+
/// a backend failure: reading it as `null` would silently send an empty
|
|
329
|
+
/// keepList, so helpers would keep every version instead of the list the
|
|
330
|
+
/// application meant to send.
|
|
331
|
+
CallResult toKeepListResult(jsi::Runtime& rt, const jsi::Value& value) {
|
|
332
|
+
if (value.isNull() || value.isUndefined()) {
|
|
333
|
+
return CallResult{0, {'n', 'u', 'l', 'l'}};
|
|
428
334
|
}
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
335
|
+
if (!value.isObject() || !value.asObject(rt).isArray(rt)) {
|
|
336
|
+
return CallResult{kBackendFailure, {}};
|
|
337
|
+
}
|
|
338
|
+
jsi::Array arr = value.asObject(rt).asArray(rt);
|
|
433
339
|
size_t n = arr.size(rt);
|
|
434
|
-
std::
|
|
435
|
-
out.reserve(n);
|
|
340
|
+
std::string text = "[";
|
|
436
341
|
for (size_t i = 0; i < n; ++i) {
|
|
437
|
-
|
|
342
|
+
jsi::Value entry = arr.getValueAtIndex(rt, i);
|
|
343
|
+
if (!entry.isNumber()) {
|
|
344
|
+
return CallResult{kBackendFailure, {}};
|
|
345
|
+
}
|
|
346
|
+
double version = entry.asNumber();
|
|
347
|
+
if (!(version >= 0 && version <= 4294967295.0) || version != static_cast<double>(static_cast<uint32_t>(version))) {
|
|
348
|
+
return CallResult{kBackendFailure, {}};
|
|
349
|
+
}
|
|
350
|
+
if (i != 0) text += ',';
|
|
351
|
+
text += std::to_string(static_cast<uint32_t>(version));
|
|
438
352
|
}
|
|
439
|
-
|
|
353
|
+
text += ']';
|
|
354
|
+
return CallResult{0, std::vector<uint8_t>(text.begin(), text.end())};
|
|
440
355
|
}
|
|
441
356
|
|
|
442
357
|
/// `UserSecretStore.loadLatest(): Promise<UserSecrets | null | undefined>`.
|
|
@@ -444,61 +359,7 @@ CallResult toUserSecretsResult(jsi::Runtime& rt, const jsi::Value& value) {
|
|
|
444
359
|
if (value.isNull() || value.isUndefined()) {
|
|
445
360
|
return CallResult{1, {}};
|
|
446
361
|
}
|
|
447
|
-
|
|
448
|
-
jsi::Object wire(rt);
|
|
449
|
-
wire.setProperty(rt, "version", jsi::Value(rt, src.getProperty(rt, "version")));
|
|
450
|
-
jsi::Array srcSecrets = src.getProperty(rt, "secrets").asObject(rt).asArray(rt);
|
|
451
|
-
size_t n = srcSecrets.size(rt);
|
|
452
|
-
jsi::Array wireSecrets(rt, n);
|
|
453
|
-
for (size_t i = 0; i < n; ++i) {
|
|
454
|
-
jsi::Object entry = srcSecrets.getValueAtIndex(rt, i).asObject(rt);
|
|
455
|
-
jsi::Object wireEntry(rt);
|
|
456
|
-
wireEntry.setProperty(rt, "id", jsi::Value(rt, bytesToNumberArray(rt, asBytes(rt, entry.getProperty(rt, "id")))));
|
|
457
|
-
wireEntry.setProperty(rt, "name", jsi::Value(rt, entry.getProperty(rt, "name")));
|
|
458
|
-
wireEntry.setProperty(
|
|
459
|
-
rt, "data", jsi::Value(rt, bytesToNumberArray(rt, asBytes(rt, entry.getProperty(rt, "data")))));
|
|
460
|
-
wireSecrets.setValueAtIndex(rt, i, jsi::Value(rt, wireEntry));
|
|
461
|
-
}
|
|
462
|
-
wire.setProperty(rt, "secrets", jsi::Value(rt, wireSecrets));
|
|
463
|
-
if (src.hasProperty(rt, "description")) {
|
|
464
|
-
jsi::Value description = src.getProperty(rt, "description");
|
|
465
|
-
if (!description.isUndefined()) {
|
|
466
|
-
wire.setProperty(rt, "description", description);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return CallResult{0, jsonStringifyToUtf8(rt, jsi::Value(rt, wire))};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/// Decode `UserSecretStore.saveLatest`'s incoming wire JSON into the JS
|
|
473
|
-
/// `UserSecrets` shape `src/types.ts` declares.
|
|
474
|
-
jsi::Value buildUserSecretsFromWire(jsi::Runtime& rt, const uint8_t* ptr, size_t len) {
|
|
475
|
-
jsi::Value parsed = jsonParseUtf8(rt, ptr, len);
|
|
476
|
-
jsi::Object src = parsed.asObject(rt);
|
|
477
|
-
jsi::Object out(rt);
|
|
478
|
-
out.setProperty(rt, "version", jsi::Value(rt, src.getProperty(rt, "version")));
|
|
479
|
-
jsi::Array srcSecrets = src.getProperty(rt, "secrets").asObject(rt).asArray(rt);
|
|
480
|
-
size_t n = srcSecrets.size(rt);
|
|
481
|
-
jsi::Array outSecrets(rt, n);
|
|
482
|
-
for (size_t i = 0; i < n; ++i) {
|
|
483
|
-
jsi::Object entry = srcSecrets.getValueAtIndex(rt, i).asObject(rt);
|
|
484
|
-
jsi::Object outEntry(rt);
|
|
485
|
-
std::vector<uint8_t> idBytes =
|
|
486
|
-
numberArrayToBytes(rt, entry.getProperty(rt, "id").asObject(rt).asArray(rt));
|
|
487
|
-
std::vector<uint8_t> dataBytes =
|
|
488
|
-
numberArrayToBytes(rt, entry.getProperty(rt, "data").asObject(rt).asArray(rt));
|
|
489
|
-
outEntry.setProperty(rt, "id", toUint8ArrayVal(rt, idBytes));
|
|
490
|
-
outEntry.setProperty(rt, "name", jsi::Value(rt, entry.getProperty(rt, "name")));
|
|
491
|
-
outEntry.setProperty(rt, "data", toUint8ArrayVal(rt, dataBytes));
|
|
492
|
-
outSecrets.setValueAtIndex(rt, i, jsi::Value(rt, outEntry));
|
|
493
|
-
}
|
|
494
|
-
out.setProperty(rt, "secrets", jsi::Value(rt, outSecrets));
|
|
495
|
-
if (src.hasProperty(rt, "description")) {
|
|
496
|
-
jsi::Value description = src.getProperty(rt, "description");
|
|
497
|
-
if (!description.isUndefined()) {
|
|
498
|
-
out.setProperty(rt, "description", description);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
return jsi::Value(rt, out);
|
|
362
|
+
return CallResult{0, userSecretsToWire(rt, value.asObject(rt))};
|
|
502
363
|
}
|
|
503
364
|
|
|
504
365
|
/// Releases a buffer this binding handed to Rust through an out-parameter.
|
|
@@ -654,7 +515,7 @@ extern "C" int32_t channelStoreListReplicas(void* userData, uint64_t secretId, c
|
|
|
654
515
|
return result.code;
|
|
655
516
|
}
|
|
656
517
|
|
|
657
|
-
/// `ChannelStore.linkChannel(secretId,
|
|
518
|
+
/// `ChannelStore.linkChannel(secretId, a, b) -> void`
|
|
658
519
|
extern "C" int32_t channelStoreLinkChannel(void* userData, uint64_t secretId, uint64_t a, uint64_t b) {
|
|
659
520
|
auto* self = static_cast<StoreBindings*>(userData);
|
|
660
521
|
// Kept alive past this call so the lambda handed to `callSync` — which the
|
|
@@ -728,6 +589,39 @@ extern "C" int32_t secretStoreLoad(void* userData, uint64_t secretId, uint64_t c
|
|
|
728
589
|
return result.code;
|
|
729
590
|
}
|
|
730
591
|
|
|
592
|
+
/// `SecretStore.loadMany(secretId, channelIds, kind) -> Array<Uint8Array | null>`
|
|
593
|
+
extern "C" int32_t secretStoreLoadMany(void* userData, uint64_t secretId, const uint8_t* channelIdsJsonPtr,
|
|
594
|
+
size_t channelIdsJsonLen, uint32_t kind, uint8_t** outPtr,
|
|
595
|
+
size_t* outLen) {
|
|
596
|
+
auto* self = static_cast<StoreBindings*>(userData);
|
|
597
|
+
// Kept alive past this call so the lambda handed to `callSync` — which the
|
|
598
|
+
// JavaScript CallInvoker's queue may still be holding after this object's
|
|
599
|
+
// owner has released its own reference — has somewhere safe to run.
|
|
600
|
+
auto keepAlive = self->shared_from_this();
|
|
601
|
+
std::vector<uint64_t> channelIds = parseUnsignedJsonArray(channelIdsJsonPtr, channelIdsJsonLen);
|
|
602
|
+
CallResult result = keepAlive->bridge().callSync(
|
|
603
|
+
[keepAlive, secretId, channelIds, kind](std::function<void(CallResult)> settle) {
|
|
604
|
+
jsi::Runtime& rt = keepAlive->runtime();
|
|
605
|
+
auto store = keepAlive->secretStore();
|
|
606
|
+
auto method = store->getPropertyAsFunction(rt, "loadMany");
|
|
607
|
+
jsi::Value promise =
|
|
608
|
+
method.callWithThis(rt, *store, idVal(rt, secretId),
|
|
609
|
+
jsi::Value(rt, u64VectorToJsStringArray(rt, channelIds)),
|
|
610
|
+
jsi::Value(static_cast<int>(kind)));
|
|
611
|
+
keepAlive->settleFromPromise(rt, std::move(promise), std::move(settle),
|
|
612
|
+
makeSecretLoadManyResult(kind));
|
|
613
|
+
});
|
|
614
|
+
if (result.code == 0) {
|
|
615
|
+
if (!writeOut(result.bytes, outPtr, outLen)) {
|
|
616
|
+
return kBackendFailure;
|
|
617
|
+
}
|
|
618
|
+
} else {
|
|
619
|
+
*outPtr = nullptr;
|
|
620
|
+
*outLen = 0;
|
|
621
|
+
}
|
|
622
|
+
return result.code;
|
|
623
|
+
}
|
|
624
|
+
|
|
731
625
|
/// `SecretStore.save(secretId, channelId, kind, value) -> void`
|
|
732
626
|
extern "C" int32_t secretStoreSave(void* userData, uint64_t secretId, uint64_t channelId, uint32_t kind,
|
|
733
627
|
const uint8_t* bytes, size_t len) {
|
|
@@ -899,14 +793,22 @@ extern "C" int32_t shareStoreSave(void* userData, uint64_t secretId, uint64_t ch
|
|
|
899
793
|
// JavaScript CallInvoker's queue may still be holding after this object's
|
|
900
794
|
// owner has released its own reference — has somewhere safe to run.
|
|
901
795
|
auto keepAlive = self->shared_from_this();
|
|
902
|
-
DecodedShare
|
|
796
|
+
std::optional<DecodedShare> parsed = decodeShareRecord(shareJsonPtr, shareJsonLen);
|
|
797
|
+
if (!parsed) {
|
|
798
|
+
return kBackendFailure;
|
|
799
|
+
}
|
|
800
|
+
// `share.secretId` is the record's own `secret_id`, not the partition
|
|
801
|
+
// `secretId`: on a helper the share carries the owner's secret id, which is
|
|
802
|
+
// what tells apart several secrets held in one partition.
|
|
803
|
+
DecodedShare decoded = std::move(*parsed);
|
|
903
804
|
CallResult result = keepAlive->bridge().callSync([keepAlive, secretId, channelId,
|
|
904
805
|
decoded](std::function<void(CallResult)> settle) {
|
|
905
806
|
jsi::Runtime& rt = keepAlive->runtime();
|
|
906
807
|
auto store = keepAlive->shareStore();
|
|
907
808
|
auto method = store->getPropertyAsFunction(rt, "save");
|
|
908
809
|
jsi::Object shareObj(rt);
|
|
909
|
-
shareObj.setProperty(rt, "secretId",
|
|
810
|
+
shareObj.setProperty(rt, "secretId",
|
|
811
|
+
jsi::Value(rt, jsi::String::createFromUtf8(rt, decoded.secretId)));
|
|
910
812
|
shareObj.setProperty(rt, "version", jsi::Value(static_cast<int>(decoded.version)));
|
|
911
813
|
shareObj.setProperty(rt, "bytes", toUint8ArrayVal(rt, decoded.bytes));
|
|
912
814
|
jsi::Value promise = method.callWithThis(rt, *store, idVal(rt, secretId), idVal(rt, channelId),
|
|
@@ -934,6 +836,53 @@ extern "C" int32_t shareStoreRemoveChannel(void* userData, uint64_t secretId, ui
|
|
|
934
836
|
return result.code;
|
|
935
837
|
}
|
|
936
838
|
|
|
839
|
+
/// `ShareStore.removeVersions(secretId, channelId, versions) -> void`
|
|
840
|
+
extern "C" int32_t shareStoreRemoveVersions(void* userData, uint64_t secretId, uint64_t channelId,
|
|
841
|
+
const uint8_t* versionsJsonPtr, size_t versionsJsonLen) {
|
|
842
|
+
auto* self = static_cast<StoreBindings*>(userData);
|
|
843
|
+
// Kept alive past this call so the lambda handed to `callSync` — which the
|
|
844
|
+
// JavaScript CallInvoker's queue may still be holding after this object's
|
|
845
|
+
// owner has released its own reference — has somewhere safe to run.
|
|
846
|
+
auto keepAlive = self->shared_from_this();
|
|
847
|
+
std::vector<uint64_t> raw = parseUnsignedJsonArray(versionsJsonPtr, versionsJsonLen);
|
|
848
|
+
std::vector<uint32_t> versions(raw.begin(), raw.end());
|
|
849
|
+
CallResult result = keepAlive->bridge().callSync(
|
|
850
|
+
[keepAlive, secretId, channelId, versions](std::function<void(CallResult)> settle) {
|
|
851
|
+
jsi::Runtime& rt = keepAlive->runtime();
|
|
852
|
+
auto store = keepAlive->shareStore();
|
|
853
|
+
auto method = store->getPropertyAsFunction(rt, "removeVersions");
|
|
854
|
+
jsi::Value promise = method.callWithThis(rt, *store, idVal(rt, secretId), idVal(rt, channelId),
|
|
855
|
+
jsi::Value(rt, u32VectorToJsArray(rt, versions)));
|
|
856
|
+
keepAlive->settleFromPromise(rt, std::move(promise), std::move(settle), toVoidResult);
|
|
857
|
+
});
|
|
858
|
+
return result.code;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/// `ShareStore.keepList(secretId, version) -> number[] | null`
|
|
862
|
+
extern "C" int32_t shareStoreKeepList(void* userData, uint64_t secretId, uint32_t version, uint8_t** outPtr,
|
|
863
|
+
size_t* outLen) {
|
|
864
|
+
auto* self = static_cast<StoreBindings*>(userData);
|
|
865
|
+
auto keepAlive = self->shared_from_this();
|
|
866
|
+
CallResult result =
|
|
867
|
+
keepAlive->bridge().callSync([keepAlive, secretId, version](std::function<void(CallResult)> settle) {
|
|
868
|
+
jsi::Runtime& rt = keepAlive->runtime();
|
|
869
|
+
auto store = keepAlive->shareStore();
|
|
870
|
+
auto method = store->getPropertyAsFunction(rt, "keepList");
|
|
871
|
+
jsi::Value promise =
|
|
872
|
+
method.callWithThis(rt, *store, idVal(rt, secretId), jsi::Value(static_cast<double>(version)));
|
|
873
|
+
keepAlive->settleFromPromise(rt, std::move(promise), std::move(settle), toKeepListResult);
|
|
874
|
+
});
|
|
875
|
+
if (result.code == 0) {
|
|
876
|
+
if (!writeOut(result.bytes, outPtr, outLen)) {
|
|
877
|
+
return kBackendFailure;
|
|
878
|
+
}
|
|
879
|
+
} else {
|
|
880
|
+
*outPtr = nullptr;
|
|
881
|
+
*outLen = 0;
|
|
882
|
+
}
|
|
883
|
+
return result.code;
|
|
884
|
+
}
|
|
885
|
+
|
|
937
886
|
/// `UserSecretStore.loadLatest(secretId) -> UserSecrets | null`
|
|
938
887
|
extern "C" int32_t userSecretStoreLoadLatest(void* userData, uint64_t secretId, uint8_t** outPtr,
|
|
939
888
|
size_t* outLen) {
|
|
@@ -973,7 +922,7 @@ extern "C" int32_t userSecretStoreSaveLatest(void* userData, uint64_t secretId,
|
|
|
973
922
|
jsi::Runtime& rt = keepAlive->runtime();
|
|
974
923
|
auto store = keepAlive->userSecretStore();
|
|
975
924
|
auto method = store->getPropertyAsFunction(rt, "saveLatest");
|
|
976
|
-
jsi::Value userSecrets =
|
|
925
|
+
jsi::Value userSecrets = userSecretsFromWire(rt, payload.data(), payload.size());
|
|
977
926
|
jsi::Value promise = method.callWithThis(rt, *store, idVal(rt, secretId), std::move(userSecrets));
|
|
978
927
|
keepAlive->settleFromPromise(rt, std::move(promise), std::move(settle), toVoidResult);
|
|
979
928
|
});
|
|
@@ -1092,64 +1041,6 @@ extern "C" int32_t stateStoreLoadAll(void* userData, uint64_t secretId, uint32_t
|
|
|
1092
1041
|
return result.code;
|
|
1093
1042
|
}
|
|
1094
1043
|
|
|
1095
|
-
/// Maps the wire's `derec_proto::Protocol` discriminant to the string
|
|
1096
|
-
/// `Transport.send`'s endpoint carries. Only `HTTPS = 0` is defined today;
|
|
1097
|
-
/// the protocol is explicitly extensible, so an unrecognised discriminant
|
|
1098
|
-
/// is reported rather than guessed at.
|
|
1099
|
-
std::string protocolToString(int32_t protocol) { return protocol == 0 ? "https" : "unknown"; }
|
|
1100
|
-
|
|
1101
|
-
/// Minimal reader for an encoded `TransportProtocol`: field 1 is the URI
|
|
1102
|
-
/// (length-delimited string), field 2 the protocol discriminant (varint).
|
|
1103
|
-
/// Hand-rolled rather than linking a protobuf runtime into the JSI layer for
|
|
1104
|
-
/// two fields; unknown fields are skipped so a newer library stays readable.
|
|
1105
|
-
static std::optional<std::pair<std::string, int32_t>> decodeTransportProtocol(
|
|
1106
|
-
const uint8_t* data, size_t len) {
|
|
1107
|
-
std::string uri;
|
|
1108
|
-
int32_t protocol = 0;
|
|
1109
|
-
size_t i = 0;
|
|
1110
|
-
|
|
1111
|
-
auto readVarint = [&](uint64_t& out) -> bool {
|
|
1112
|
-
out = 0;
|
|
1113
|
-
int shift = 0;
|
|
1114
|
-
while (i < len) {
|
|
1115
|
-
uint8_t b = data[i++];
|
|
1116
|
-
out |= static_cast<uint64_t>(b & 0x7F) << shift;
|
|
1117
|
-
if ((b & 0x80) == 0) return true;
|
|
1118
|
-
shift += 7;
|
|
1119
|
-
if (shift > 63) return false;
|
|
1120
|
-
}
|
|
1121
|
-
return false;
|
|
1122
|
-
};
|
|
1123
|
-
|
|
1124
|
-
while (i < len) {
|
|
1125
|
-
uint64_t tag = 0;
|
|
1126
|
-
if (!readVarint(tag)) return std::nullopt;
|
|
1127
|
-
uint32_t field = static_cast<uint32_t>(tag >> 3);
|
|
1128
|
-
uint32_t wire = static_cast<uint32_t>(tag & 0x7);
|
|
1129
|
-
|
|
1130
|
-
if (field == 1 && wire == 2) {
|
|
1131
|
-
uint64_t size = 0;
|
|
1132
|
-
if (!readVarint(size) || i + size > len) return std::nullopt;
|
|
1133
|
-
uri.assign(reinterpret_cast<const char*>(data + i), size);
|
|
1134
|
-
i += size;
|
|
1135
|
-
} else if (field == 2 && wire == 0) {
|
|
1136
|
-
uint64_t value = 0;
|
|
1137
|
-
if (!readVarint(value)) return std::nullopt;
|
|
1138
|
-
protocol = static_cast<int32_t>(value);
|
|
1139
|
-
} else if (wire == 0) {
|
|
1140
|
-
uint64_t skip = 0;
|
|
1141
|
-
if (!readVarint(skip)) return std::nullopt;
|
|
1142
|
-
} else if (wire == 2) {
|
|
1143
|
-
uint64_t size = 0;
|
|
1144
|
-
if (!readVarint(size) || i + size > len) return std::nullopt;
|
|
1145
|
-
i += size;
|
|
1146
|
-
} else {
|
|
1147
|
-
return std::nullopt;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
return std::make_pair(uri, protocol);
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
1044
|
/// `Transport.send(endpoints, message) -> void`
|
|
1154
1045
|
///
|
|
1155
1046
|
/// `endpoints` is every address the peer advertised, in the peer's order,
|
|
@@ -1164,44 +1055,25 @@ extern "C" int32_t transportSend(void* userData, const uint8_t* endpointsPtr, si
|
|
|
1164
1055
|
// owner has released its own reference — has somewhere safe to run.
|
|
1165
1056
|
auto keepAlive = self->shared_from_this();
|
|
1166
1057
|
|
|
1167
|
-
//
|
|
1168
|
-
//
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
while (offset < endpointsLen) {
|
|
1175
|
-
uint8_t b = endpointsPtr[offset++];
|
|
1176
|
-
size |= static_cast<uint64_t>(b & 0x7F) << shift;
|
|
1177
|
-
if ((b & 0x80) == 0) break;
|
|
1178
|
-
shift += 7;
|
|
1179
|
-
}
|
|
1180
|
-
if (offset + size > endpointsLen) return -1;
|
|
1181
|
-
auto parsed = decodeTransportProtocol(endpointsPtr + offset, size);
|
|
1182
|
-
if (!parsed) return -1;
|
|
1183
|
-
endpoints.push_back(*parsed);
|
|
1184
|
-
offset += size;
|
|
1058
|
+
// The crate decodes its own endpoint framing, names each protocol, and
|
|
1059
|
+
// fails the send on a discriminant it cannot name; this layer only parses
|
|
1060
|
+
// the JSON array `Transport.send` receives.
|
|
1061
|
+
DeRecMessageJsonResult decoded = derec_transport_endpoints_json(endpointsPtr, endpointsLen);
|
|
1062
|
+
if (decoded.error.code != 0) {
|
|
1063
|
+
derec_free_error(&decoded.error);
|
|
1064
|
+
return -1;
|
|
1185
1065
|
}
|
|
1066
|
+
std::vector<uint8_t> endpointsJson = takeBuffer(decoded.bytes);
|
|
1186
1067
|
|
|
1187
1068
|
std::vector<uint8_t> message(bytes, bytes + len);
|
|
1188
|
-
CallResult result = keepAlive->bridge().callSync([keepAlive,
|
|
1069
|
+
CallResult result = keepAlive->bridge().callSync([keepAlive, endpointsJson,
|
|
1189
1070
|
message](std::function<void(CallResult)> settle) {
|
|
1190
1071
|
jsi::Runtime& rt = keepAlive->runtime();
|
|
1191
1072
|
auto store = keepAlive->transportStore();
|
|
1192
1073
|
auto method = store->getPropertyAsFunction(rt, "send");
|
|
1193
|
-
jsi::
|
|
1194
|
-
for (size_t i = 0; i < endpoints.size(); ++i) {
|
|
1195
|
-
jsi::Object endpoint(rt);
|
|
1196
|
-
endpoint.setProperty(rt, "protocol",
|
|
1197
|
-
jsi::Value(rt, jsi::String::createFromUtf8(
|
|
1198
|
-
rt, protocolToString(endpoints[i].second))));
|
|
1199
|
-
endpoint.setProperty(rt, "uri",
|
|
1200
|
-
jsi::Value(rt, jsi::String::createFromUtf8(rt, endpoints[i].first)));
|
|
1201
|
-
endpointsJs.setValueAtIndex(rt, i, endpoint);
|
|
1202
|
-
}
|
|
1074
|
+
jsi::Value endpointsJs = jsonParseUtf8(rt, endpointsJson.data(), endpointsJson.size());
|
|
1203
1075
|
jsi::Value promise =
|
|
1204
|
-
method.callWithThis(rt, *store,
|
|
1076
|
+
method.callWithThis(rt, *store, endpointsJs, toUint8ArrayVal(rt, message));
|
|
1205
1077
|
keepAlive->settleFromPromise(rt, std::move(promise), std::move(settle), toVoidResult);
|
|
1206
1078
|
});
|
|
1207
1079
|
return result.code;
|
|
@@ -1318,11 +1190,12 @@ std::shared_ptr<StoreBindings> StoreBindings::create(jsi::Runtime& rt, jsi::Obje
|
|
|
1318
1190
|
storeFreeBuffer, // free_buffer
|
|
1319
1191
|
};
|
|
1320
1192
|
bindings->secretCallbacks_ = SecretStoreCallbacks{
|
|
1321
|
-
self,
|
|
1322
|
-
secretStoreLoad,
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1193
|
+
self, // user_data
|
|
1194
|
+
secretStoreLoad, // load
|
|
1195
|
+
secretStoreLoadMany, // load_many
|
|
1196
|
+
secretStoreSave, // save
|
|
1197
|
+
secretStoreRemove, // remove
|
|
1198
|
+
storeFreeBuffer, // free_buffer
|
|
1326
1199
|
};
|
|
1327
1200
|
bindings->shareCallbacks_ = ShareStoreCallbacks{
|
|
1328
1201
|
self, // user_data
|
|
@@ -1331,8 +1204,10 @@ std::shared_ptr<StoreBindings> StoreBindings::create(jsi::Runtime& rt, jsi::Obje
|
|
|
1331
1204
|
shareStoreLoadAll, // load_all
|
|
1332
1205
|
shareStoreLatestVersion, // latest_version
|
|
1333
1206
|
shareStoreSave, // save
|
|
1334
|
-
shareStoreRemoveChannel,
|
|
1335
|
-
|
|
1207
|
+
shareStoreRemoveChannel, // remove_channel
|
|
1208
|
+
shareStoreRemoveVersions, // remove_versions
|
|
1209
|
+
shareStoreKeepList, // keep_list
|
|
1210
|
+
storeFreeBuffer, // free_buffer
|
|
1336
1211
|
};
|
|
1337
1212
|
bindings->userSecretCallbacks_ = UserSecretStoreCallbacks{
|
|
1338
1213
|
self, // user_data
|
|
@@ -1366,6 +1241,7 @@ std::shared_ptr<StoreBindings> StoreBindings::create(jsi::Runtime& rt, jsi::Obje
|
|
|
1366
1241
|
|
|
1367
1242
|
requireField(rt, bindings->secretCallbacks_.user_data, "SecretStoreCallbacks.user_data");
|
|
1368
1243
|
requireField(rt, bindings->secretCallbacks_.load, "SecretStoreCallbacks.load");
|
|
1244
|
+
requireField(rt, bindings->secretCallbacks_.load_many, "SecretStoreCallbacks.load_many");
|
|
1369
1245
|
requireField(rt, bindings->secretCallbacks_.save, "SecretStoreCallbacks.save");
|
|
1370
1246
|
requireField(rt, bindings->secretCallbacks_.remove, "SecretStoreCallbacks.remove");
|
|
1371
1247
|
requireField(rt, bindings->secretCallbacks_.free_buffer, "SecretStoreCallbacks.free_buffer");
|
|
@@ -1377,6 +1253,8 @@ std::shared_ptr<StoreBindings> StoreBindings::create(jsi::Runtime& rt, jsi::Obje
|
|
|
1377
1253
|
requireField(rt, bindings->shareCallbacks_.latest_version, "ShareStoreCallbacks.latest_version");
|
|
1378
1254
|
requireField(rt, bindings->shareCallbacks_.save, "ShareStoreCallbacks.save");
|
|
1379
1255
|
requireField(rt, bindings->shareCallbacks_.remove_channel, "ShareStoreCallbacks.remove_channel");
|
|
1256
|
+
requireField(rt, bindings->shareCallbacks_.remove_versions, "ShareStoreCallbacks.remove_versions");
|
|
1257
|
+
requireField(rt, bindings->shareCallbacks_.keep_list, "ShareStoreCallbacks.keep_list");
|
|
1380
1258
|
requireField(rt, bindings->shareCallbacks_.free_buffer, "ShareStoreCallbacks.free_buffer");
|
|
1381
1259
|
|
|
1382
1260
|
requireField(rt, bindings->userSecretCallbacks_.user_data, "UserSecretStoreCallbacks.user_data");
|