2023-07-26 18:20:17 +00:00
|
|
|
import 'dart:async';
|
|
|
|
import 'dart:convert';
|
2022-03-17 14:31:10 +00:00
|
|
|
import 'dart:html' as html;
|
|
|
|
import 'dart:js' as js;
|
|
|
|
import 'dart:js_util' as js_util;
|
2022-10-01 02:37:55 +00:00
|
|
|
import 'dart:typed_data';
|
2022-02-07 02:18:42 +00:00
|
|
|
|
2023-07-26 18:20:17 +00:00
|
|
|
import 'veilid.dart';
|
2023-01-04 22:02:45 +00:00
|
|
|
|
2022-02-07 02:18:42 +00:00
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Veilid getVeilid() => VeilidJS();
|
|
|
|
|
2023-07-26 18:20:17 +00:00
|
|
|
Object wasm = js_util.getProperty(html.window, 'veilid_wasm');
|
2022-03-16 03:02:24 +00:00
|
|
|
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<T> _wrapApiPromise<T>(Object p) => js_util.promiseToFuture(p).then((value) => value as T).catchError(
|
2022-03-17 14:31:10 +00:00
|
|
|
(error) => Future<T>.error(
|
|
|
|
VeilidAPIException.fromJson(jsonDecode(error as String))));
|
2022-03-16 03:02:24 +00:00
|
|
|
|
2022-12-03 03:52:03 +00:00
|
|
|
class _Ctx {
|
2023-07-26 18:20:17 +00:00
|
|
|
_Ctx(int this.id, this.js);
|
2023-07-19 14:07:51 +00:00
|
|
|
int? id;
|
2022-12-03 03:52:03 +00:00
|
|
|
final VeilidJS js;
|
2023-07-19 14:07:51 +00:00
|
|
|
void ensureValid() {
|
|
|
|
if (id == null) {
|
|
|
|
throw VeilidAPIExceptionNotInitialized();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void close() {
|
|
|
|
if (id != null) {
|
2023-07-26 18:20:17 +00:00
|
|
|
js_util.callMethod(wasm, 'release_routing_context', [id!]);
|
2023-07-19 14:07:51 +00:00
|
|
|
id = null;
|
|
|
|
}
|
|
|
|
}
|
2022-12-03 03:52:03 +00:00
|
|
|
}
|
|
|
|
|
2022-11-26 19:16:02 +00:00
|
|
|
// JS implementation of VeilidRoutingContext
|
2023-07-19 14:07:51 +00:00
|
|
|
class VeilidRoutingContextJS extends VeilidRoutingContext {
|
2022-12-03 03:52:03 +00:00
|
|
|
|
|
|
|
VeilidRoutingContextJS._(this._ctx) {
|
|
|
|
_finalizer.attach(this, _ctx, detach: this);
|
|
|
|
}
|
2023-07-26 18:20:17 +00:00
|
|
|
final _Ctx _ctx;
|
|
|
|
static final Finalizer<_Ctx> _finalizer = Finalizer((ctx) => ctx.close());
|
2022-11-26 19:16:02 +00:00
|
|
|
|
2023-07-19 14:07:51 +00:00
|
|
|
@override
|
|
|
|
void close() {
|
|
|
|
_ctx.close();
|
|
|
|
}
|
|
|
|
|
2022-11-26 19:16:02 +00:00
|
|
|
@override
|
2022-12-03 03:52:03 +00:00
|
|
|
VeilidRoutingContextJS withPrivacy() {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-07-26 18:20:17 +00:00
|
|
|
final int newId =
|
|
|
|
js_util.callMethod(wasm, 'routing_context_with_privacy', [_ctx.id!]);
|
2022-12-03 03:52:03 +00:00
|
|
|
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-06-28 15:40:02 +00:00
|
|
|
VeilidRoutingContextJS withCustomPrivacy(SafetySelection safetySelection) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2022-12-03 03:52:03 +00:00
|
|
|
final newId = js_util.callMethod(
|
2023-05-29 19:24:57 +00:00
|
|
|
wasm,
|
2023-07-26 18:20:17 +00:00
|
|
|
'routing_context_with_custom_privacy',
|
2023-07-19 14:07:51 +00:00
|
|
|
[_ctx.id!, jsonEncode(safetySelection)]);
|
2022-12-03 03:52:03 +00:00
|
|
|
|
|
|
|
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2022-12-03 03:52:03 +00:00
|
|
|
VeilidRoutingContextJS withSequencing(Sequencing sequencing) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-07-26 18:20:17 +00:00
|
|
|
final newId = js_util.callMethod(wasm, 'routing_context_with_sequencing',
|
2023-07-19 14:07:51 +00:00
|
|
|
[_ctx.id!, jsonEncode(sequencing)]);
|
2022-12-03 03:52:03 +00:00
|
|
|
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<Uint8List> appCall(String target, Uint8List request) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-07-26 18:20:17 +00:00
|
|
|
final encodedRequest = base64UrlNoPadEncode(request);
|
2022-11-26 19:16:02 +00:00
|
|
|
|
2023-01-04 22:02:45 +00:00
|
|
|
return base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod(
|
2023-07-26 18:20:17 +00:00
|
|
|
wasm, 'routing_context_app_call', [_ctx.id!, target, encodedRequest])));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2022-12-29 03:53:58 +00:00
|
|
|
Future<void> appMessage(String target, Uint8List message) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-07-26 18:20:17 +00:00
|
|
|
final encodedMessage = base64UrlNoPadEncode(message);
|
2022-11-26 19:16:02 +00:00
|
|
|
|
2023-03-01 20:50:30 +00:00
|
|
|
return _wrapApiPromise(js_util.callMethod(wasm,
|
2023-07-26 18:20:17 +00:00
|
|
|
'routing_context_app_message', [_ctx.id!, target, encodedMessage]));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-09 02:50:44 +00:00
|
|
|
Future<DHTRecordDescriptor> createDHTRecord(DHTSchema schema,
|
|
|
|
{CryptoKind kind = 0}) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
2023-07-26 18:20:17 +00:00
|
|
|
.callMethod(wasm, 'routing_context_create_dht_record',
|
2023-07-19 14:07:51 +00:00
|
|
|
[_ctx.id!, jsonEncode(schema), kind]))));
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<DHTRecordDescriptor> openDHTRecord(
|
|
|
|
TypedKey key, KeyPair? writer) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
2023-07-26 18:20:17 +00:00
|
|
|
.callMethod(wasm, 'routing_context_open_dht_record', [
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.id!,
|
2023-05-29 19:24:57 +00:00
|
|
|
jsonEncode(key),
|
2023-07-26 18:20:17 +00:00
|
|
|
if (writer != null) jsonEncode(writer) else null
|
2023-05-29 19:24:57 +00:00
|
|
|
]))));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> closeDHTRecord(TypedKey key) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
return _wrapApiPromise(js_util.callMethod(
|
2023-07-26 18:20:17 +00:00
|
|
|
wasm, 'routing_context_close_dht_record', [_ctx.id!, jsonEncode(key)]));
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> deleteDHTRecord(TypedKey key) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
|
|
|
return _wrapApiPromise(js_util.callMethod(wasm,
|
2023-07-26 18:20:17 +00:00
|
|
|
'routing_context_delete_dht_record', [_ctx.id!, jsonEncode(key)]));
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<ValueData?> getDHTValue(
|
|
|
|
TypedKey key, int subkey, bool forceRefresh) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
final opt = await _wrapApiPromise(js_util.callMethod(
|
|
|
|
wasm,
|
2023-07-26 18:20:17 +00:00
|
|
|
'routing_context_get_dht_value',
|
2023-07-19 14:07:51 +00:00
|
|
|
[_ctx.id!, jsonEncode(key), subkey, forceRefresh]));
|
2023-05-29 19:24:57 +00:00
|
|
|
return opt == null ? null : ValueData.fromJson(jsonDecode(opt));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<ValueData?> setDHTValue(
|
|
|
|
TypedKey key, int subkey, Uint8List data) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
final opt = await _wrapApiPromise(js_util.callMethod(
|
|
|
|
wasm,
|
2023-07-26 18:20:17 +00:00
|
|
|
'routing_context_set_dht_value',
|
2023-07-19 14:07:51 +00:00
|
|
|
[_ctx.id!, jsonEncode(key), subkey, base64UrlNoPadEncode(data)]));
|
2023-05-29 19:24:57 +00:00
|
|
|
return opt == null ? null : ValueData.fromJson(jsonDecode(opt));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-06-27 22:26:53 +00:00
|
|
|
Future<Timestamp> watchDHTValues(TypedKey key, List<ValueSubkeyRange> subkeys,
|
2023-05-29 19:24:57 +00:00
|
|
|
Timestamp expiration, int count) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
final ts = await _wrapApiPromise(js_util.callMethod(
|
2023-07-26 18:20:17 +00:00
|
|
|
wasm, 'routing_context_watch_dht_values', [
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.id!,
|
2023-05-29 19:24:57 +00:00
|
|
|
jsonEncode(key),
|
|
|
|
jsonEncode(subkeys),
|
|
|
|
expiration.toString(),
|
|
|
|
count
|
|
|
|
]));
|
|
|
|
return Timestamp.fromString(ts);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-06-27 22:26:53 +00:00
|
|
|
Future<bool> cancelDHTWatch(TypedKey key, List<ValueSubkeyRange> subkeys) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_ctx.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
return _wrapApiPromise(js_util.callMethod(
|
|
|
|
wasm,
|
2023-07-26 18:20:17 +00:00
|
|
|
'routing_context_cancel_dht_watch',
|
2023-07-19 14:07:51 +00:00
|
|
|
[_ctx.id!, jsonEncode(key), jsonEncode(subkeys)]));
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// JS implementation of VeilidCryptoSystem
|
2023-07-19 14:07:51 +00:00
|
|
|
class VeilidCryptoSystemJS extends VeilidCryptoSystem {
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
VeilidCryptoSystemJS._(this._js, this._kind) {
|
|
|
|
// Keep the reference
|
|
|
|
_js;
|
|
|
|
}
|
2023-07-26 18:20:17 +00:00
|
|
|
final CryptoKind _kind;
|
|
|
|
final VeilidJS _js;
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
CryptoKind kind() => _kind;
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<SharedSecret> cachedDH(PublicKey key, SecretKey secret) async => SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
|
|
|
.callMethod(wasm, 'crypto_cached_dh',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, jsonEncode(key), jsonEncode(secret)]))));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<SharedSecret> computeDH(PublicKey key, SecretKey secret) async => SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
|
|
|
.callMethod(wasm, 'crypto_compute_dh',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, jsonEncode(key), jsonEncode(secret)]))));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<Uint8List> randomBytes(int len) async => base64UrlNoPadDecode(await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'crypto_random_bytes', [_kind, len])));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<int> defaultSaltLength() => _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'crypto_default_salt_length', [_kind]));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<String> hashPassword(Uint8List password, Uint8List salt) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_hash_password',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, base64UrlNoPadEncode(password), base64UrlNoPadEncode(salt)]));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<bool> verifyPassword(Uint8List password, String passwordHash) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_verify_password',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, base64UrlNoPadEncode(password), passwordHash]));
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<SharedSecret> deriveSharedSecret(
|
2023-07-26 18:20:17 +00:00
|
|
|
Uint8List password, Uint8List salt) async => SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
|
|
|
.callMethod(wasm, 'crypto_derive_shared_secret', [
|
2023-05-29 19:24:57 +00:00
|
|
|
_kind,
|
|
|
|
base64UrlNoPadEncode(password),
|
|
|
|
base64UrlNoPadEncode(salt)
|
|
|
|
]))));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<Nonce> randomNonce() async => Nonce.fromJson(jsonDecode(await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'crypto_random_nonce', [_kind]))));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<SharedSecret> randomSharedSecret() async => SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'crypto_random_shared_secret', [_kind]))));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<KeyPair> generateKeyPair() async => KeyPair.fromJson(jsonDecode(await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'crypto_generate_key_pair', [_kind]))));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<HashDigest> generateHash(Uint8List data) async => HashDigest.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
|
|
|
.callMethod(wasm, 'crypto_generate_hash',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, base64UrlNoPadEncode(data)]))));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<bool> validateKeyPair(PublicKey key, SecretKey secret) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_validate_key_pair',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, jsonEncode(key), jsonEncode(secret)]));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<bool> validateHash(Uint8List data, HashDigest hash) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_validate_hash',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, base64UrlNoPadEncode(data), jsonEncode(hash)]));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<CryptoKeyDistance> distance(CryptoKey key1, CryptoKey key2) async => CryptoKeyDistance.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
|
|
|
.callMethod(wasm, 'crypto_distance',
|
2023-05-29 19:24:57 +00:00
|
|
|
[_kind, jsonEncode(key1), jsonEncode(key2)]))));
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<Signature> sign(
|
2023-07-26 18:20:17 +00:00
|
|
|
PublicKey key, SecretKey secret, Uint8List data) async => Signature.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
|
|
|
.callMethod(wasm, 'crypto_sign', [
|
2023-05-29 19:24:57 +00:00
|
|
|
_kind,
|
|
|
|
jsonEncode(key),
|
|
|
|
jsonEncode(secret),
|
|
|
|
base64UrlNoPadEncode(data)
|
|
|
|
]))));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<void> verify(PublicKey key, Uint8List data, Signature signature) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_verify', [
|
2023-05-29 19:24:57 +00:00
|
|
|
_kind,
|
|
|
|
jsonEncode(key),
|
|
|
|
base64UrlNoPadEncode(data),
|
|
|
|
jsonEncode(signature),
|
|
|
|
]));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<int> aeadOverhead() => _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'crypto_aead_overhead', [_kind]));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
Future<Uint8List> decryptAead(Uint8List body, Nonce nonce,
|
2023-07-26 18:20:17 +00:00
|
|
|
SharedSecret sharedSecret, Uint8List? associatedData) async => base64UrlNoPadDecode(
|
|
|
|
await _wrapApiPromise(js_util.callMethod(wasm, 'crypto_decrypt_aead', [
|
2023-05-29 19:24:57 +00:00
|
|
|
_kind,
|
|
|
|
base64UrlNoPadEncode(body),
|
|
|
|
jsonEncode(nonce),
|
|
|
|
jsonEncode(sharedSecret),
|
2023-07-26 18:20:17 +00:00
|
|
|
if (associatedData != null) base64UrlNoPadEncode(associatedData) else null
|
2023-05-29 19:24:57 +00:00
|
|
|
])));
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<Uint8List> encryptAead(Uint8List body, Nonce nonce,
|
2023-07-26 18:20:17 +00:00
|
|
|
SharedSecret sharedSecret, Uint8List? associatedData) async => base64UrlNoPadDecode(
|
|
|
|
await _wrapApiPromise(js_util.callMethod(wasm, 'crypto_encrypt_aead', [
|
2023-05-29 19:24:57 +00:00
|
|
|
_kind,
|
|
|
|
base64UrlNoPadEncode(body),
|
|
|
|
jsonEncode(nonce),
|
|
|
|
jsonEncode(sharedSecret),
|
2023-07-26 18:20:17 +00:00
|
|
|
if (associatedData != null) base64UrlNoPadEncode(associatedData) else null
|
2023-05-29 19:24:57 +00:00
|
|
|
])));
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<Uint8List> cryptNoAuth(
|
2023-07-26 18:20:17 +00:00
|
|
|
Uint8List body, Nonce nonce, SharedSecret sharedSecret) async => base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod(
|
|
|
|
wasm, 'crypto_crypt_no_auth', [
|
2023-05-29 19:24:57 +00:00
|
|
|
_kind,
|
|
|
|
base64UrlNoPadEncode(body),
|
|
|
|
jsonEncode(nonce),
|
|
|
|
jsonEncode(sharedSecret)
|
|
|
|
])));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
2022-12-29 03:53:58 +00:00
|
|
|
class _TDBT {
|
2023-07-26 18:20:17 +00:00
|
|
|
|
|
|
|
_TDBT(this.id, this.tdbjs, this.js);
|
2023-07-19 14:07:51 +00:00
|
|
|
int? id;
|
|
|
|
final VeilidTableDBJS tdbjs;
|
|
|
|
final VeilidJS js;
|
|
|
|
void ensureValid() {
|
|
|
|
if (id == null) {
|
|
|
|
throw VeilidAPIExceptionNotInitialized();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void close() {
|
|
|
|
if (id != null) {
|
2023-07-26 18:20:17 +00:00
|
|
|
js_util.callMethod(wasm, 'release_table_db_transaction', [id!]);
|
2023-07-19 14:07:51 +00:00
|
|
|
id = null;
|
|
|
|
}
|
|
|
|
}
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// JS implementation of VeilidTableDBTransaction
|
|
|
|
class VeilidTableDBTransactionJS extends VeilidTableDBTransaction {
|
|
|
|
|
|
|
|
VeilidTableDBTransactionJS._(this._tdbt) {
|
|
|
|
_finalizer.attach(this, _tdbt, detach: this);
|
|
|
|
}
|
2023-07-26 18:20:17 +00:00
|
|
|
final _TDBT _tdbt;
|
|
|
|
static final Finalizer<_TDBT> _finalizer = Finalizer((tdbt) => tdbt.close());
|
2022-12-29 03:53:58 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
bool isDone() => _tdbt.id == null;
|
2022-12-29 03:53:58 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-19 14:07:51 +00:00
|
|
|
Future<void> commit() async {
|
|
|
|
_tdbt.ensureValid();
|
|
|
|
await _wrapApiPromise(
|
2023-07-26 18:20:17 +00:00
|
|
|
js_util.callMethod(wasm, 'table_db_transaction_commit', [_tdbt.id!]));
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdbt.close();
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-07-19 14:07:51 +00:00
|
|
|
Future<void> rollback() async {
|
|
|
|
_tdbt.ensureValid();
|
|
|
|
await _wrapApiPromise(
|
2023-07-26 18:20:17 +00:00
|
|
|
js_util.callMethod(wasm, 'table_db_transaction_rollback', [_tdbt.id!]));
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdbt.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> store(int col, Uint8List key, Uint8List value) async {
|
|
|
|
_tdbt.ensureValid();
|
2023-01-04 22:02:45 +00:00
|
|
|
final encodedKey = base64UrlNoPadEncode(key);
|
|
|
|
final encodedValue = base64UrlNoPadEncode(value);
|
2022-12-29 03:53:58 +00:00
|
|
|
|
2023-07-26 18:20:17 +00:00
|
|
|
await _wrapApiPromise(js_util.callMethod(wasm, 'table_db_transaction_store',
|
2023-07-19 14:07:51 +00:00
|
|
|
[_tdbt.id!, col, encodedKey, encodedValue]));
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-07-19 14:07:51 +00:00
|
|
|
Future<void> delete(int col, Uint8List key) async {
|
|
|
|
_tdbt.ensureValid();
|
2023-01-04 22:02:45 +00:00
|
|
|
final encodedKey = base64UrlNoPadEncode(key);
|
2022-12-29 03:53:58 +00:00
|
|
|
|
2023-07-19 14:07:51 +00:00
|
|
|
await _wrapApiPromise(js_util.callMethod(
|
2023-07-26 18:20:17 +00:00
|
|
|
wasm, 'table_db_transaction_delete', [_tdbt.id!, col, encodedKey]));
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _TDB {
|
|
|
|
|
2023-07-19 14:07:51 +00:00
|
|
|
_TDB(int this.id, this.js);
|
2023-07-26 18:20:17 +00:00
|
|
|
int? id;
|
|
|
|
final VeilidJS js;
|
2023-07-19 14:07:51 +00:00
|
|
|
void ensureValid() {
|
|
|
|
if (id == null) {
|
|
|
|
throw VeilidAPIExceptionNotInitialized();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void close() {
|
|
|
|
if (id != null) {
|
2023-07-26 18:20:17 +00:00
|
|
|
js_util.callMethod(wasm, 'release_table_db', [id!]);
|
2023-07-19 14:07:51 +00:00
|
|
|
id = null;
|
|
|
|
}
|
|
|
|
}
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// JS implementation of VeilidTableDB
|
|
|
|
class VeilidTableDBJS extends VeilidTableDB {
|
|
|
|
|
|
|
|
VeilidTableDBJS._(this._tdb) {
|
|
|
|
_finalizer.attach(this, _tdb, detach: this);
|
|
|
|
}
|
2023-07-26 18:20:17 +00:00
|
|
|
final _TDB _tdb;
|
|
|
|
static final Finalizer<_TDB> _finalizer = Finalizer((tdb) => tdb.close());
|
2022-12-29 03:53:58 +00:00
|
|
|
|
2023-07-19 14:07:51 +00:00
|
|
|
@override
|
|
|
|
void close() {
|
|
|
|
_tdb.close();
|
|
|
|
}
|
|
|
|
|
2022-12-29 03:53:58 +00:00
|
|
|
@override
|
|
|
|
int getColumnCount() {
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdb.ensureValid();
|
2023-07-26 18:20:17 +00:00
|
|
|
return js_util.callMethod(wasm, 'table_db_get_column_count', [_tdb.id!]);
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-05-29 19:24:57 +00:00
|
|
|
Future<List<Uint8List>> getKeys(int col) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdb.ensureValid();
|
2023-05-29 19:24:57 +00:00
|
|
|
return jsonListConstructor(base64UrlNoPadDecodeDynamic)(jsonDecode(
|
2023-07-26 18:20:17 +00:00
|
|
|
await js_util.callMethod(wasm, 'table_db_get_keys', [_tdb.id!, col])));
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
VeilidTableDBTransaction transact() {
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdb.ensureValid();
|
2023-07-26 18:20:17 +00:00
|
|
|
final id = js_util.callMethod(wasm, 'table_db_transact', [_tdb.id!]);
|
2022-12-29 03:53:58 +00:00
|
|
|
|
|
|
|
return VeilidTableDBTransactionJS._(_TDBT(id, this, _tdb.js));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> store(int col, Uint8List key, Uint8List value) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdb.ensureValid();
|
2023-01-04 22:02:45 +00:00
|
|
|
final encodedKey = base64UrlNoPadEncode(key);
|
|
|
|
final encodedValue = base64UrlNoPadEncode(value);
|
2022-12-29 03:53:58 +00:00
|
|
|
|
|
|
|
return _wrapApiPromise(js_util.callMethod(
|
2023-07-26 18:20:17 +00:00
|
|
|
wasm, 'table_db_store', [_tdb.id!, col, encodedKey, encodedValue]));
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<Uint8List?> load(int col, Uint8List key) async {
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdb.ensureValid();
|
2023-01-04 22:02:45 +00:00
|
|
|
final encodedKey = base64UrlNoPadEncode(key);
|
2022-12-29 03:53:58 +00:00
|
|
|
|
2023-07-26 18:20:17 +00:00
|
|
|
final out = await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'table_db_load', [_tdb.id!, col, encodedKey]));
|
2022-12-29 03:53:58 +00:00
|
|
|
if (out == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2023-01-04 22:02:45 +00:00
|
|
|
return base64UrlNoPadDecode(out);
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-05-29 19:24:57 +00:00
|
|
|
Future<Uint8List?> delete(int col, Uint8List key) {
|
2023-07-19 14:07:51 +00:00
|
|
|
_tdb.ensureValid();
|
2023-01-04 22:02:45 +00:00
|
|
|
final encodedKey = base64UrlNoPadEncode(key);
|
2022-12-29 03:53:58 +00:00
|
|
|
|
2023-01-04 22:02:45 +00:00
|
|
|
return _wrapApiPromise(js_util
|
2023-07-26 18:20:17 +00:00
|
|
|
.callMethod(wasm, 'table_db_delete', [_tdb.id!, col, encodedKey]));
|
2022-12-29 03:53:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-26 19:16:02 +00:00
|
|
|
// JS implementation of high level Veilid API
|
|
|
|
|
2023-07-19 14:07:51 +00:00
|
|
|
class VeilidJS extends Veilid {
|
2022-06-16 01:51:38 +00:00
|
|
|
@override
|
2022-07-01 20:20:43 +00:00
|
|
|
void initializeVeilidCore(Map<String, dynamic> platformConfigJson) {
|
2023-07-26 18:20:17 +00:00
|
|
|
final platformConfigJsonString = jsonEncode(platformConfigJson);
|
2022-07-01 20:20:43 +00:00
|
|
|
js_util
|
2023-07-26 18:20:17 +00:00
|
|
|
.callMethod(wasm, 'initialize_veilid_core', [platformConfigJsonString]);
|
2022-06-16 01:51:38 +00:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:13:52 +00:00
|
|
|
@override
|
|
|
|
void changeLogLevel(String layer, VeilidConfigLogLevel logLevel) {
|
2023-07-26 18:20:17 +00:00
|
|
|
final logLevelJsonString = jsonEncode(logLevel);
|
|
|
|
js_util.callMethod(wasm, 'change_log_level', [layer, logLevelJsonString]);
|
2022-07-01 16:13:52 +00:00
|
|
|
}
|
|
|
|
|
2022-03-04 01:45:39 +00:00
|
|
|
@override
|
2022-09-09 20:27:13 +00:00
|
|
|
Future<Stream<VeilidUpdate>> startupVeilidCore(VeilidConfig config) async {
|
2023-07-26 18:20:17 +00:00
|
|
|
final streamController = StreamController<VeilidUpdate>();
|
2022-03-17 14:31:10 +00:00
|
|
|
updateCallback(String update) {
|
2023-07-26 18:20:17 +00:00
|
|
|
final updateJson = jsonDecode(update);
|
|
|
|
if (updateJson['kind'] == 'Shutdown') {
|
2022-03-17 14:31:10 +00:00
|
|
|
streamController.close();
|
|
|
|
} else {
|
2023-07-26 18:20:17 +00:00
|
|
|
final update = VeilidUpdate.fromJson(updateJson);
|
2022-03-17 14:31:10 +00:00
|
|
|
streamController.add(update);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-26 18:20:17 +00:00
|
|
|
await _wrapApiPromise(js_util.callMethod(wasm, 'startup_veilid_core',
|
2023-05-29 19:24:57 +00:00
|
|
|
[js.allowInterop(updateCallback), jsonEncode(config)]));
|
2022-09-09 20:27:13 +00:00
|
|
|
|
|
|
|
return streamController.stream;
|
2022-02-07 02:18:42 +00:00
|
|
|
}
|
|
|
|
|
2022-03-04 01:45:39 +00:00
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<VeilidState> getVeilidState() async => VeilidState.fromJson(jsonDecode(await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'get_veilid_state', []))));
|
2022-02-07 02:18:42 +00:00
|
|
|
|
2022-09-06 22:59:41 +00:00
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<void> attach() => _wrapApiPromise(js_util.callMethod(wasm, 'attach', []));
|
2022-09-06 22:59:41 +00:00
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<void> detach() => _wrapApiPromise(js_util.callMethod(wasm, 'detach', []));
|
2022-09-06 22:59:41 +00:00
|
|
|
|
2022-03-04 01:45:39 +00:00
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<void> shutdownVeilidCore() => _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'shutdown_veilid_core', []));
|
2022-02-07 02:18:42 +00:00
|
|
|
|
2023-05-29 19:24:57 +00:00
|
|
|
@override
|
|
|
|
List<CryptoKind> validCryptoKinds() {
|
2023-07-26 18:20:17 +00:00
|
|
|
final vck = jsonDecode(js_util.callMethod(wasm, 'valid_crypto_kinds', []))
|
2023-07-25 05:04:22 +00:00
|
|
|
as List<dynamic>;
|
|
|
|
return vck.map((v) => v as CryptoKind).toList();
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<VeilidCryptoSystem> getCryptoSystem(CryptoKind kind) async {
|
|
|
|
if (!validCryptoKinds().contains(kind)) {
|
2023-07-26 18:20:17 +00:00
|
|
|
throw const VeilidAPIExceptionGeneric('unsupported cryptosystem');
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
return VeilidCryptoSystemJS._(this, kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<VeilidCryptoSystem> bestCryptoSystem() async => VeilidCryptoSystemJS._(
|
|
|
|
this, js_util.callMethod(wasm, 'best_crypto_kind', []));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
Future<List<TypedKey>> verifySignatures(List<TypedKey> nodeIds,
|
2023-07-26 18:20:17 +00:00
|
|
|
Uint8List data, List<TypedSignature> signatures) async => jsonListConstructor(TypedKey.fromJson)(jsonDecode(
|
|
|
|
await _wrapApiPromise(js_util.callMethod(wasm, 'verify_signatures', [
|
2023-05-29 19:24:57 +00:00
|
|
|
jsonEncode(nodeIds),
|
|
|
|
base64UrlNoPadEncode(data),
|
|
|
|
jsonEncode(signatures)
|
|
|
|
]))));
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<List<TypedSignature>> generateSignatures(
|
2023-07-26 18:20:17 +00:00
|
|
|
Uint8List data, List<TypedKeyPair> keyPairs) async => jsonListConstructor(TypedSignature.fromJson)(jsonDecode(
|
|
|
|
await _wrapApiPromise(js_util.callMethod(wasm, 'generate_signatures',
|
2023-05-29 19:24:57 +00:00
|
|
|
[base64UrlNoPadEncode(data), jsonEncode(keyPairs)]))));
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<TypedKeyPair> generateKeyPair(CryptoKind kind) async => TypedKeyPair.fromJson(jsonDecode(await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'generate_key_pair', [kind]))));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
2022-03-04 01:45:39 +00:00
|
|
|
@override
|
2022-11-26 19:16:02 +00:00
|
|
|
Future<VeilidRoutingContext> routingContext() async {
|
2023-07-26 18:20:17 +00:00
|
|
|
final var id =
|
|
|
|
await _wrapApiPromise(js_util.callMethod(wasm, 'routing_context', []));
|
2022-12-03 03:52:03 +00:00
|
|
|
return VeilidRoutingContextJS._(_Ctx(id, this));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<RouteBlob> newPrivateRoute() async => RouteBlob.fromJson(jsonDecode(await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'new_private_route', []))));
|
2022-11-26 19:16:02 +00:00
|
|
|
|
|
|
|
@override
|
2023-03-01 20:50:30 +00:00
|
|
|
Future<RouteBlob> newCustomPrivateRoute(
|
2022-11-26 19:16:02 +00:00
|
|
|
Stability stability, Sequencing sequencing) async {
|
2023-07-26 18:20:17 +00:00
|
|
|
final stabilityString = jsonEncode(stability);
|
|
|
|
final sequencingString = jsonEncode(sequencing);
|
2022-11-26 19:16:02 +00:00
|
|
|
|
2023-05-29 19:24:57 +00:00
|
|
|
return RouteBlob.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
2022-12-03 03:52:03 +00:00
|
|
|
.callMethod(
|
2023-07-26 18:20:17 +00:00
|
|
|
wasm, 'new_private_route', [stabilityString, sequencingString]))));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2022-12-29 03:53:58 +00:00
|
|
|
Future<String> importRemotePrivateRoute(Uint8List blob) {
|
2023-07-26 18:20:17 +00:00
|
|
|
final encodedBlob = base64UrlNoPadEncode(blob);
|
2022-11-26 19:16:02 +00:00
|
|
|
return _wrapApiPromise(
|
2023-07-26 18:20:17 +00:00
|
|
|
js_util.callMethod(wasm, 'import_remote_private_route', [encodedBlob]));
|
2022-11-26 19:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<void> releasePrivateRoute(String key) => _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'release_private_route', [key]));
|
2022-03-04 01:45:39 +00:00
|
|
|
|
2022-10-01 02:37:55 +00:00
|
|
|
@override
|
2023-06-16 15:57:55 +00:00
|
|
|
Future<void> appCallReply(String callId, Uint8List message) {
|
2023-07-26 18:20:17 +00:00
|
|
|
final encodedMessage = base64UrlNoPadEncode(message);
|
2022-10-01 02:37:55 +00:00
|
|
|
return _wrapApiPromise(
|
2023-07-26 18:20:17 +00:00
|
|
|
js_util.callMethod(wasm, 'app_call_reply', [callId, encodedMessage]));
|
2022-10-01 02:37:55 +00:00
|
|
|
}
|
2022-12-03 03:52:03 +00:00
|
|
|
|
2022-12-29 03:53:58 +00:00
|
|
|
@override
|
|
|
|
Future<VeilidTableDB> openTableDB(String name, int columnCount) async {
|
2023-07-26 18:20:17 +00:00
|
|
|
final id = await _wrapApiPromise(
|
|
|
|
js_util.callMethod(wasm, 'open_table_db', [name, columnCount]));
|
2022-12-29 03:53:58 +00:00
|
|
|
return VeilidTableDBJS._(_TDB(id, this));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<bool> deleteTableDB(String name) => _wrapApiPromise(js_util.callMethod(wasm, 'delete_table_db', [name]));
|
2022-12-29 03:53:58 +00:00
|
|
|
|
2023-05-29 19:24:57 +00:00
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Timestamp now() => Timestamp.fromString(js_util.callMethod(wasm, 'now', []));
|
2023-05-29 19:24:57 +00:00
|
|
|
|
2022-11-26 19:16:02 +00:00
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
Future<String> debug(String command) async => await _wrapApiPromise(js_util.callMethod(wasm, 'debug', [command]));
|
2022-10-01 02:37:55 +00:00
|
|
|
|
2022-03-04 01:45:39 +00:00
|
|
|
@override
|
2023-07-26 18:20:17 +00:00
|
|
|
String veilidVersionString() => js_util.callMethod(wasm, 'veilid_version_string', []);
|
2022-02-07 02:18:42 +00:00
|
|
|
|
2022-03-04 01:45:39 +00:00
|
|
|
@override
|
2022-03-17 14:31:10 +00:00
|
|
|
VeilidVersion veilidVersion() {
|
2022-12-03 03:52:03 +00:00
|
|
|
Map<String, dynamic> jsonVersion =
|
2023-07-26 18:20:17 +00:00
|
|
|
jsonDecode(js_util.callMethod(wasm, 'veilid_version', []));
|
2022-03-16 03:02:24 +00:00
|
|
|
return VeilidVersion(
|
2023-07-26 18:20:17 +00:00
|
|
|
jsonVersion['major'], jsonVersion['minor'], jsonVersion['patch']);
|
2022-02-07 02:18:42 +00:00
|
|
|
}
|
|
|
|
}
|