diff --git a/veilid-flutter/build.sh b/veilid-flutter/build.sh index 1f08284e..2a765035 100755 --- a/veilid-flutter/build.sh +++ b/veilid-flutter/build.sh @@ -1,3 +1,3 @@ #!/bin/bash set -e -dart run build_runner build +dart run build_runner build --delete-conflicting-outputs diff --git a/veilid-flutter/lib/routing_context.dart b/veilid-flutter/lib/routing_context.dart index 5060e98e..e5cde3fa 100644 --- a/veilid-flutter/lib/routing_context.dart +++ b/veilid-flutter/lib/routing_context.dart @@ -123,7 +123,7 @@ class ValueData with _$ValueData { @Assert('seq >= 0', 'seq out of range') const factory ValueData({ required int seq, - @Uint8ListJsonConverter() required Uint8List data, + @Uint8ListJsonConverter.jsIsArray() required Uint8List data, required PublicKey writer, }) = _ValueData; diff --git a/veilid-flutter/lib/routing_context.freezed.dart b/veilid-flutter/lib/routing_context.freezed.dart index 140c2214..af4f4aab 100644 --- a/veilid-flutter/lib/routing_context.freezed.dart +++ b/veilid-flutter/lib/routing_context.freezed.dart @@ -969,7 +969,7 @@ ValueData _$ValueDataFromJson(Map json) { /// @nodoc mixin _$ValueData { int get seq => throw _privateConstructorUsedError; - @Uint8ListJsonConverter() + @Uint8ListJsonConverter.jsIsArray() Uint8List get data => throw _privateConstructorUsedError; FixedEncodedString43 get writer => throw _privateConstructorUsedError; @@ -986,7 +986,7 @@ abstract class $ValueDataCopyWith<$Res> { @useResult $Res call( {int seq, - @Uint8ListJsonConverter() Uint8List data, + @Uint8ListJsonConverter.jsIsArray() Uint8List data, FixedEncodedString43 writer}); } @@ -1033,7 +1033,7 @@ abstract class _$$_ValueDataCopyWith<$Res> implements $ValueDataCopyWith<$Res> { @useResult $Res call( {int seq, - @Uint8ListJsonConverter() Uint8List data, + @Uint8ListJsonConverter.jsIsArray() Uint8List data, FixedEncodedString43 writer}); } @@ -1074,7 +1074,7 @@ class __$$_ValueDataCopyWithImpl<$Res> class _$_ValueData implements _ValueData { const _$_ValueData( {required this.seq, - @Uint8ListJsonConverter() required this.data, + @Uint8ListJsonConverter.jsIsArray() required this.data, required this.writer}) : assert(seq >= 0, 'seq out of range'); @@ -1084,7 +1084,7 @@ class _$_ValueData implements _ValueData { @override final int seq; @override - @Uint8ListJsonConverter() + @Uint8ListJsonConverter.jsIsArray() final Uint8List data; @override final FixedEncodedString43 writer; @@ -1126,7 +1126,7 @@ class _$_ValueData implements _ValueData { abstract class _ValueData implements ValueData { const factory _ValueData( {required final int seq, - @Uint8ListJsonConverter() required final Uint8List data, + @Uint8ListJsonConverter.jsIsArray() required final Uint8List data, required final FixedEncodedString43 writer}) = _$_ValueData; factory _ValueData.fromJson(Map json) = @@ -1135,7 +1135,7 @@ abstract class _ValueData implements ValueData { @override int get seq; @override - @Uint8ListJsonConverter() + @Uint8ListJsonConverter.jsIsArray() Uint8List get data; @override FixedEncodedString43 get writer; diff --git a/veilid-flutter/lib/routing_context.g.dart b/veilid-flutter/lib/routing_context.g.dart index da5aa313..d76ff703 100644 --- a/veilid-flutter/lib/routing_context.g.dart +++ b/veilid-flutter/lib/routing_context.g.dart @@ -80,14 +80,14 @@ Map _$$_ValueSubkeyRangeToJson(_$_ValueSubkeyRange instance) => _$_ValueData _$$_ValueDataFromJson(Map json) => _$_ValueData( seq: json['seq'] as int, - data: const Uint8ListJsonConverter().fromJson(json['data'] as String), + data: const Uint8ListJsonConverter.jsIsArray().fromJson(json['data']), writer: FixedEncodedString43.fromJson(json['writer']), ); Map _$$_ValueDataToJson(_$_ValueData instance) => { 'seq': instance.seq, - 'data': const Uint8ListJsonConverter().toJson(instance.data), + 'data': const Uint8ListJsonConverter.jsIsArray().toJson(instance.data), 'writer': instance.writer.toJson(), }; @@ -109,7 +109,7 @@ Map _$$_SafetySpecToJson(_$_SafetySpec instance) => _$_RouteBlob _$$_RouteBlobFromJson(Map json) => _$_RouteBlob( routeId: json['route_id'] as String, - blob: const Uint8ListJsonConverter().fromJson(json['blob'] as String), + blob: const Uint8ListJsonConverter().fromJson(json['blob']), ); Map _$$_RouteBlobToJson(_$_RouteBlob instance) => diff --git a/veilid-flutter/lib/veilid_encoding.dart b/veilid-flutter/lib/veilid_encoding.dart index 7962dbc5..5022bd84 100644 --- a/veilid-flutter/lib/veilid_encoding.dart +++ b/veilid-flutter/lib/veilid_encoding.dart @@ -1,9 +1,13 @@ import 'dart:convert'; -import 'dart:typed_data'; import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'veilid_stub.dart' + if (dart.library.io) 'veilid_ffi.dart' + if (dart.library.js) 'veilid_js.dart'; + String base64UrlNoPadEncode(List bytes) { var x = base64Url.encode(bytes); while (x.endsWith('=')) { @@ -20,13 +24,20 @@ Uint8List base64UrlNoPadDecode(String source) { Uint8List base64UrlNoPadDecodeDynamic(dynamic source) => base64UrlNoPadDecode(source as String); -class Uint8ListJsonConverter implements JsonConverter { - const Uint8ListJsonConverter(); +class Uint8ListJsonConverter implements JsonConverter { + const Uint8ListJsonConverter() : _jsIsArray = false; + const Uint8ListJsonConverter.jsIsArray() : _jsIsArray = true; + + final bool _jsIsArray; @override - Uint8List fromJson(dynamic json) => base64UrlNoPadDecode(json as String); + Uint8List fromJson(dynamic json) => kIsWeb && _jsIsArray + ? convertUint8ListFromJson(json) + : base64UrlNoPadDecode(json as String); @override - String toJson(Uint8List data) => base64UrlNoPadEncode(data); + dynamic toJson(Uint8List data) => kIsWeb && _jsIsArray + ? convertUint8ListToJson(data) + : base64UrlNoPadEncode(data); } @immutable diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index cf899832..373c653e 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -246,6 +246,11 @@ const int messageStreamClose = 8; // Interface factory for high level Veilid API Veilid getVeilid() => VeilidFFI(_dylib); +// Uint8List marshaling +Uint8List convertUint8ListFromJson(dynamic json) => + base64UrlNoPadDecode(json as String); +dynamic convertUint8ListToJson(Uint8List data) => base64UrlNoPadEncode(data); + // Parse handle async returns Future processFuturePlain(Future future) async => future.then((value) { diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart index 3ef1f283..e7e3d223 100644 --- a/veilid-flutter/lib/veilid_js.dart +++ b/veilid-flutter/lib/veilid_js.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; import 'dart:js' as js; +import 'dart:js_interop' as js_interop; import 'dart:js_util' as js_util; import 'dart:typed_data'; @@ -13,14 +14,26 @@ Veilid getVeilid() => VeilidJS(); Object wasm = js_util.getProperty(html.window, 'veilid_wasm'); +Uint8List convertUint8ListFromJson(dynamic json) => Uint8List.fromList( + ((json as js_interop.JSArray).dartify()! as List) + .map((e) => e! as int) + .toList()); + +dynamic convertUint8ListToJson(Uint8List data) => data.toList().jsify(); + Future _wrapApiPromise(Object p) => js_util .promiseToFuture(p) .then((value) => value) // ignore: inference_failure_on_untyped_parameter .catchError((e) { - // Wrap all other errors in VeilidAPIExceptionInternal - throw VeilidAPIExceptionInternal(e.toString()); - }, test: (e) => e is! VeilidAPIException); + try { + final ex = VeilidAPIException.fromJson(jsonDecode(e as String)); + throw ex; + } on Exception catch (_) { + // Wrap all other errors in VeilidAPIExceptionInternal + throw VeilidAPIExceptionInternal(e.toString()); + } + }); class _Ctx { _Ctx(int id, this.js) : _id = id; @@ -142,7 +155,11 @@ class VeilidRoutingContextJS extends VeilidRoutingContext { wasm, 'routing_context_get_dht_value', [id, jsonEncode(key), subkey, forceRefresh])); - return opt == null ? null : ValueData.fromJson(jsonDecode(opt)); + if (opt == null) { + return null; + } + final jsonOpt = jsonDecode(opt); + return jsonOpt == null ? null : ValueData.fromJson(jsonOpt); } @override @@ -153,7 +170,11 @@ class VeilidRoutingContextJS extends VeilidRoutingContext { wasm, 'routing_context_set_dht_value', [id, jsonEncode(key), subkey, base64UrlNoPadEncode(data)])); - return opt == null ? null : ValueData.fromJson(jsonDecode(opt)); + if (opt == null) { + return null; + } + final jsonOpt = jsonDecode(opt); + return jsonOpt == null ? null : ValueData.fromJson(jsonOpt); } @override diff --git a/veilid-flutter/lib/veilid_state.g.dart b/veilid-flutter/lib/veilid_state.g.dart index d3797e28..8ed1eb66 100644 --- a/veilid-flutter/lib/veilid_state.g.dart +++ b/veilid-flutter/lib/veilid_state.g.dart @@ -129,8 +129,7 @@ Map _$$VeilidLogToJson(_$VeilidLog instance) => _$VeilidAppMessage _$$VeilidAppMessageFromJson(Map json) => _$VeilidAppMessage( - message: - const Uint8ListJsonConverter().fromJson(json['message'] as String), + message: const Uint8ListJsonConverter().fromJson(json['message']), sender: json['sender'] == null ? null : Typed.fromJson(json['sender']), @@ -146,8 +145,7 @@ Map _$$VeilidAppMessageToJson(_$VeilidAppMessage instance) => _$VeilidAppCall _$$VeilidAppCallFromJson(Map json) => _$VeilidAppCall( - message: - const Uint8ListJsonConverter().fromJson(json['message'] as String), + message: const Uint8ListJsonConverter().fromJson(json['message']), callId: json['call_id'] as String, sender: json['sender'] == null ? null diff --git a/veilid-flutter/lib/veilid_stub.dart b/veilid-flutter/lib/veilid_stub.dart index 1606518d..639cbe15 100644 --- a/veilid-flutter/lib/veilid_stub.dart +++ b/veilid-flutter/lib/veilid_stub.dart @@ -1,3 +1,9 @@ +import 'dart:typed_data'; + import 'veilid.dart'; Veilid getVeilid() => throw UnsupportedError('Cannot create Veilid object'); +Uint8List convertUint8ListFromJson(dynamic json) => + throw UnsupportedError('Cannot convertUint8ListFromJson'); +dynamic convertUint8ListToJson(Uint8List data) => + throw UnsupportedError('Cannot convertUint8ListToJson'); diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index 1521640c..05d7ae25 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -345,11 +345,14 @@ pub fn release_routing_context(id: u32) -> i32 { #[wasm_bindgen()] pub fn routing_context_with_privacy(id: u32) -> u32 { - let rc = (*ROUTING_CONTEXTS).borrow(); - let Some(routing_context) = rc.get(&id) else { - return 0; + let routing_context = { + let rc = (*ROUTING_CONTEXTS).borrow(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + routing_context.clone() }; - let Ok(routing_context) = routing_context.clone().with_privacy() else { + let Ok(routing_context) = routing_context.with_privacy() else { return 0; }; add_routing_context(routing_context) @@ -360,14 +363,14 @@ pub fn routing_context_with_custom_privacy(id: u32, safety_selection: String) -> let safety_selection: veilid_core::SafetySelection = veilid_core::deserialize_json(&safety_selection).unwrap(); - let rc = (*ROUTING_CONTEXTS).borrow(); - let Some(routing_context) = rc.get(&id) else { - return 0; + let routing_context = { + let rc = (*ROUTING_CONTEXTS).borrow(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + routing_context.clone() }; - let Ok(routing_context) = routing_context - .clone() - .with_custom_privacy(safety_selection) - else { + let Ok(routing_context) = routing_context.with_custom_privacy(safety_selection) else { return 0; }; add_routing_context(routing_context) @@ -377,11 +380,14 @@ pub fn routing_context_with_custom_privacy(id: u32, safety_selection: String) -> pub fn routing_context_with_sequencing(id: u32, sequencing: String) -> u32 { let sequencing: veilid_core::Sequencing = veilid_core::deserialize_json(&sequencing).unwrap(); - let rc = (*ROUTING_CONTEXTS).borrow(); - let Some(routing_context) = rc.get(&id) else { - return 0; + let routing_context = { + let rc = (*ROUTING_CONTEXTS).borrow(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + routing_context.clone() }; - let routing_context = routing_context.clone().with_sequencing(sequencing); + let routing_context = routing_context.with_sequencing(sequencing); add_routing_context(routing_context) } @@ -1544,8 +1550,8 @@ pub fn crypto_crypt_no_auth( } #[wasm_bindgen()] -pub fn now() -> u64 { - veilid_core::get_aligned_timestamp().as_u64() +pub fn now() -> String { + veilid_core::get_aligned_timestamp().as_u64().to_string() } #[wasm_bindgen()]