(feat): redesign WASM module, add TypeScript SDK, migrate to pnpm
Some checks failed
CI / rustfmt (push) Successful in 17s
CI / wasm build (push) Successful in 1m16s
CI / clippy (push) Successful in 1m28s
CI / test (push) Successful in 1m48s
CI / example (push) Successful in 1m31s
CI / duplicate code (push) Failing after 33s
CI / web client (push) Failing after 34s
CI / cargo-machete (push) Successful in 1m18s
CI / cargo-deny (push) Failing after 3m2s
Some checks failed
CI / rustfmt (push) Successful in 17s
CI / wasm build (push) Successful in 1m16s
CI / clippy (push) Successful in 1m28s
CI / test (push) Successful in 1m48s
CI / example (push) Successful in 1m31s
CI / duplicate code (push) Failing after 33s
CI / web client (push) Failing after 34s
CI / cargo-machete (push) Successful in 1m18s
CI / cargo-deny (push) Failing after 3m2s
This commit is contained in:
parent
89a20044a5
commit
5caa1c9d5f
49 changed files with 3717 additions and 1501 deletions
|
|
@ -13,22 +13,7 @@ crate-type = ["cdylib"]
|
|||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
js-sys = "0.3"
|
||||
web-sys = { version = "0.3", features = [
|
||||
"console",
|
||||
"WebTransport",
|
||||
"WebTransportOptions",
|
||||
"WebTransportHash",
|
||||
"WebTransportBidirectionalStream",
|
||||
"WebTransportCloseInfo",
|
||||
"WebTransportDatagramDuplexStream",
|
||||
"WebTransportError",
|
||||
"WebTransportReceiveStream",
|
||||
"WebTransportSendStream",
|
||||
"ReadableStream",
|
||||
"ReadableStreamDefaultReader",
|
||||
"WritableStream",
|
||||
"WritableStreamDefaultWriter",
|
||||
] }
|
||||
futures-channel = "0.3"
|
||||
console_error_panic_hook = "0.1"
|
||||
|
||||
hex = "0.4"
|
||||
|
|
|
|||
94
wasm/pkg/mtp_wasm.d.ts
vendored
94
wasm/pkg/mtp_wasm.d.ts
vendored
|
|
@ -1,6 +1,17 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export interface ParsedFrame {
|
||||
id?: number;
|
||||
type: string;
|
||||
sender?: bigint;
|
||||
receiver?: bigint;
|
||||
data: Record<string, unknown>;
|
||||
raw: Uint8Array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class ConnectionConfig {
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
|
|
@ -66,7 +77,12 @@ export class WasmClient {
|
|||
disconnect(): void;
|
||||
static is_supported(): boolean;
|
||||
constructor(on_state_change: Function, on_message: Function, on_error: Function);
|
||||
request(frame: Uint8Array, response_type?: string | null): Promise<any>;
|
||||
send(frame: Uint8Array): Promise<void>;
|
||||
start_protocol_pings(interval_ms: number, client_id: bigint): void;
|
||||
stop_protocol_pings(): void;
|
||||
subscribe(message_type: string, callback: Function): number;
|
||||
unsubscribe(id: number): boolean;
|
||||
readonly state: number;
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +121,15 @@ export class WasmKeyring {
|
|||
to_bytes(): Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log severity used by the public SDK when translating raw WASM events.
|
||||
*/
|
||||
export enum WasmLogHint {
|
||||
Info = 0,
|
||||
Warning = 1,
|
||||
Error = 2,
|
||||
}
|
||||
|
||||
export class WasmPublicKeyBundle {
|
||||
private constructor();
|
||||
free(): void;
|
||||
|
|
@ -117,25 +142,27 @@ export class WasmPublicKeyBundle {
|
|||
}
|
||||
|
||||
/**
|
||||
* Build a demo Ping frame with encrypted and signed containers
|
||||
* (mirrors the Rust client example but uses only reserved data types).
|
||||
* Minimal message router used by higher-level SDK subscription code.
|
||||
*/
|
||||
export function build_demo_message(client_id: bigint, keyring_bytes: Uint8Array, host_bundle_bytes: Uint8Array): Uint8Array;
|
||||
export class WasmSubscriptionRouter {
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
dispatch(message_type: string, message: any): boolean;
|
||||
constructor();
|
||||
subscribe(message_type: string, callback: Function): void;
|
||||
unsubscribe(message_type: string): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a simple Ping frame with description, timestamp, and optional data.
|
||||
* Build a typed MTP frame using generated communication/data type names.
|
||||
*/
|
||||
export function build_frame(message_type: string, data: any, options: any): Uint8Array;
|
||||
|
||||
/**
|
||||
* Build a protocol-level Ping frame with description, timestamp, and optional data.
|
||||
*/
|
||||
export function build_ping_frame(client_id: bigint, description: string, timestamp: bigint, data: Uint8Array): Uint8Array;
|
||||
|
||||
/**
|
||||
* Build a request frame with the given communication type name, request ID, and JSON data.
|
||||
*
|
||||
* - `comm_type`: communication type name (e.g. "get_model", "rank_models") or PascalCase
|
||||
* - `id`: request ID for response correlation
|
||||
* - `json_data`: JSON-stringified request payload
|
||||
*/
|
||||
export function build_request_frame(comm_type: string, id: number, json_data: string): Uint8Array;
|
||||
|
||||
/**
|
||||
* Generate a fresh Ed25519 keypair.
|
||||
*
|
||||
|
|
@ -169,9 +196,9 @@ export function main(): void;
|
|||
export function parse_auth_response(response: Uint8Array): any;
|
||||
|
||||
/**
|
||||
* Parse a response frame into a JSON string containing `_id`, `_type`, and data fields.
|
||||
* Parse any MTP frame into structured JavaScript data.
|
||||
*/
|
||||
export function parse_response_frame(frame: Uint8Array): string;
|
||||
export function parse_frame(frame: Uint8Array): ParsedFrame;
|
||||
|
||||
/**
|
||||
* Derive a 32-byte encryption key from `ikm` with `salt` and `context`.
|
||||
|
|
@ -197,6 +224,11 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
|
|||
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly build_frame: (a: number, b: number, c: any, d: any) => [number, number, number, number];
|
||||
readonly build_ping_frame: (a: bigint, b: number, c: number, d: bigint, e: number, f: number) => [number, number, number, number];
|
||||
readonly format_frame: (a: number, b: number) => [number, number, number, number];
|
||||
readonly parse_auth_response: (a: number, b: number) => [number, number, number];
|
||||
readonly parse_frame: (a: number, b: number) => [number, number, number];
|
||||
readonly __wbg_wasmchacha20poly1305_free: (a: number, b: number) => void;
|
||||
readonly __wbg_wasmed25519signer_free: (a: number, b: number) => void;
|
||||
readonly __wbg_wasmkeyring_free: (a: number, b: number) => void;
|
||||
|
|
@ -222,32 +254,36 @@ export interface InitOutput {
|
|||
readonly wasmpublickeybundle_sig_cl_public_key: (a: number) => [number, number];
|
||||
readonly wasmpublickeybundle_sig_pq_public_key: (a: number) => [number, number];
|
||||
readonly wasmpublickeybundle_to_bytes: (a: number) => [number, number];
|
||||
readonly build_demo_message: (a: bigint, b: number, c: number, d: number, e: number) => [number, number, number, number];
|
||||
readonly build_ping_frame: (a: bigint, b: number, c: number, d: bigint, e: number, f: number) => [number, number, number, number];
|
||||
readonly build_request_frame: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
|
||||
readonly format_frame: (a: number, b: number) => [number, number, number, number];
|
||||
readonly parse_auth_response: (a: number, b: number) => [number, number, number];
|
||||
readonly parse_response_frame: (a: number, b: number) => [number, number, number, number];
|
||||
readonly __wbg_connectionconfig_free: (a: number, b: number) => void;
|
||||
readonly __wbg_wasmclient_free: (a: number, b: number) => void;
|
||||
readonly connectionconfig_client_id: (a: number) => bigint;
|
||||
readonly connectionconfig_new: (a: number, b: number) => number;
|
||||
readonly connectionconfig_set_client_id: (a: number, b: bigint) => void;
|
||||
readonly connectionconfig_set_server_certificate_hashes: (a: number, b: number, c: number) => void;
|
||||
readonly connectionconfig_url: (a: number) => [number, number];
|
||||
readonly wasmclient_auth_connect: (a: number, b: number, c: number, d: number, e: number, f: number, g: bigint) => any;
|
||||
readonly wasmclient_auth_register: (a: number, b: number, c: number, d: number, e: number, f: number) => any;
|
||||
readonly wasmclient_connect: (a: number, b: number) => any;
|
||||
readonly wasmclient_disconnect: (a: number) => void;
|
||||
readonly wasmclient_is_supported: () => number;
|
||||
readonly wasmclient_new: (a: any, b: any, c: any) => number;
|
||||
readonly wasmclient_request: (a: number, b: number, c: number, d: number, e: number) => any;
|
||||
readonly wasmclient_send: (a: number, b: number, c: number) => any;
|
||||
readonly wasmclient_start_protocol_pings: (a: number, b: number, c: bigint) => [number, number];
|
||||
readonly wasmclient_state: (a: number) => number;
|
||||
readonly wasmclient_stop_protocol_pings: (a: number) => void;
|
||||
readonly wasmclient_subscribe: (a: number, b: number, c: number, d: any) => number;
|
||||
readonly wasmclient_unsubscribe: (a: number, b: number) => number;
|
||||
readonly __wbg_wasmsubscriptionrouter_free: (a: number, b: number) => void;
|
||||
readonly main: () => void;
|
||||
readonly wasmsubscriptionrouter_dispatch: (a: number, b: number, c: number, d: any) => number;
|
||||
readonly wasmsubscriptionrouter_new: () => number;
|
||||
readonly wasmsubscriptionrouter_subscribe: (a: number, b: number, c: number, d: any) => void;
|
||||
readonly wasmsubscriptionrouter_unsubscribe: (a: number, b: number, c: number) => number;
|
||||
readonly __wbg_connectionconfig_free: (a: number, b: number) => void;
|
||||
readonly connectionconfig_client_id: (a: number) => bigint;
|
||||
readonly connectionconfig_new: (a: number, b: number) => number;
|
||||
readonly connectionconfig_set_client_id: (a: number, b: bigint) => void;
|
||||
readonly connectionconfig_set_server_certificate_hashes: (a: number, b: number, c: number) => void;
|
||||
readonly connectionconfig_url: (a: number) => [number, number];
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584: (a: number, b: number, c: any) => [number, number];
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f: (a: number, b: number, c: any) => [number, number];
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2: (a: number, b: number, c: any) => [number, number];
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h4bf2427f775cf424: (a: number, b: number, c: any, d: any) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h3c511b580d027299: (a: number, b: number, c: any) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h99ead69e8d7f5bae: (a: number, b: number) => void;
|
||||
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
readonly __wbindgen_exn_store: (a: number) => void;
|
||||
|
|
|
|||
|
|
@ -227,6 +227,19 @@ export class WasmClient {
|
|||
WasmClientFinalization.register(this, this.__wbg_ptr, this);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @param {Uint8Array} frame
|
||||
* @param {string | null} [response_type]
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
request(frame, response_type) {
|
||||
const ptr0 = passArray8ToWasm0(frame, wasm.__wbindgen_malloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
var ptr1 = isLikeNone(response_type) ? 0 : passStringToWasm0(response_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len1 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.wasmclient_request(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* @param {Uint8Array} frame
|
||||
* @returns {Promise<void>}
|
||||
|
|
@ -237,6 +250,16 @@ export class WasmClient {
|
|||
const ret = wasm.wasmclient_send(this.__wbg_ptr, ptr0, len0);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* @param {number} interval_ms
|
||||
* @param {bigint} client_id
|
||||
*/
|
||||
start_protocol_pings(interval_ms, client_id) {
|
||||
const ret = wasm.wasmclient_start_protocol_pings(this.__wbg_ptr, interval_ms, client_id);
|
||||
if (ret[1]) {
|
||||
throw takeFromExternrefTable0(ret[0]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
|
|
@ -244,6 +267,28 @@ export class WasmClient {
|
|||
const ret = wasm.wasmclient_state(this.__wbg_ptr);
|
||||
return ret;
|
||||
}
|
||||
stop_protocol_pings() {
|
||||
wasm.wasmclient_stop_protocol_pings(this.__wbg_ptr);
|
||||
}
|
||||
/**
|
||||
* @param {string} message_type
|
||||
* @param {Function} callback
|
||||
* @returns {number}
|
||||
*/
|
||||
subscribe(message_type, callback) {
|
||||
const ptr0 = passStringToWasm0(message_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.wasmclient_subscribe(this.__wbg_ptr, ptr0, len0, callback);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* @param {number} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
unsubscribe(id) {
|
||||
const ret = wasm.wasmclient_unsubscribe(this.__wbg_ptr, id);
|
||||
return ret !== 0;
|
||||
}
|
||||
}
|
||||
if (Symbol.dispose) WasmClient.prototype[Symbol.dispose] = WasmClient.prototype.free;
|
||||
|
||||
|
|
@ -365,6 +410,16 @@ export class WasmKeyring {
|
|||
}
|
||||
if (Symbol.dispose) WasmKeyring.prototype[Symbol.dispose] = WasmKeyring.prototype.free;
|
||||
|
||||
/**
|
||||
* Log severity used by the public SDK when translating raw WASM events.
|
||||
* @enum {0 | 1 | 2}
|
||||
*/
|
||||
export const WasmLogHint = Object.freeze({
|
||||
Info: 0, "0": "Info",
|
||||
Warning: 1, "1": "Warning",
|
||||
Error: 2, "2": "Error",
|
||||
});
|
||||
|
||||
export class WasmPublicKeyBundle {
|
||||
static __wrap(ptr) {
|
||||
const obj = Object.create(WasmPublicKeyBundle.prototype);
|
||||
|
|
@ -435,29 +490,79 @@ export class WasmPublicKeyBundle {
|
|||
if (Symbol.dispose) WasmPublicKeyBundle.prototype[Symbol.dispose] = WasmPublicKeyBundle.prototype.free;
|
||||
|
||||
/**
|
||||
* Build a demo Ping frame with encrypted and signed containers
|
||||
* (mirrors the Rust client example but uses only reserved data types).
|
||||
* @param {bigint} client_id
|
||||
* @param {Uint8Array} keyring_bytes
|
||||
* @param {Uint8Array} host_bundle_bytes
|
||||
* Minimal message router used by higher-level SDK subscription code.
|
||||
*/
|
||||
export class WasmSubscriptionRouter {
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.__wbg_ptr;
|
||||
this.__wbg_ptr = 0;
|
||||
WasmSubscriptionRouterFinalization.unregister(this);
|
||||
return ptr;
|
||||
}
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_wasmsubscriptionrouter_free(ptr, 0);
|
||||
}
|
||||
/**
|
||||
* @param {string} message_type
|
||||
* @param {any} message
|
||||
* @returns {boolean}
|
||||
*/
|
||||
dispatch(message_type, message) {
|
||||
const ptr0 = passStringToWasm0(message_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.wasmsubscriptionrouter_dispatch(this.__wbg_ptr, ptr0, len0, message);
|
||||
return ret !== 0;
|
||||
}
|
||||
constructor() {
|
||||
const ret = wasm.wasmsubscriptionrouter_new();
|
||||
this.__wbg_ptr = ret;
|
||||
WasmSubscriptionRouterFinalization.register(this, this.__wbg_ptr, this);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @param {string} message_type
|
||||
* @param {Function} callback
|
||||
*/
|
||||
subscribe(message_type, callback) {
|
||||
const ptr0 = passStringToWasm0(message_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.wasmsubscriptionrouter_subscribe(this.__wbg_ptr, ptr0, len0, callback);
|
||||
}
|
||||
/**
|
||||
* @param {string} message_type
|
||||
* @returns {boolean}
|
||||
*/
|
||||
unsubscribe(message_type) {
|
||||
const ptr0 = passStringToWasm0(message_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.wasmsubscriptionrouter_unsubscribe(this.__wbg_ptr, ptr0, len0);
|
||||
return ret !== 0;
|
||||
}
|
||||
}
|
||||
if (Symbol.dispose) WasmSubscriptionRouter.prototype[Symbol.dispose] = WasmSubscriptionRouter.prototype.free;
|
||||
|
||||
/**
|
||||
* Build a typed MTP frame using generated communication/data type names.
|
||||
* @param {string} message_type
|
||||
* @param {any} data
|
||||
* @param {any} options
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function build_demo_message(client_id, keyring_bytes, host_bundle_bytes) {
|
||||
const ptr0 = passArray8ToWasm0(keyring_bytes, wasm.__wbindgen_malloc);
|
||||
export function build_frame(message_type, data, options) {
|
||||
const ptr0 = passStringToWasm0(message_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passArray8ToWasm0(host_bundle_bytes, wasm.__wbindgen_malloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.build_demo_message(client_id, ptr0, len0, ptr1, len1);
|
||||
const ret = wasm.build_frame(ptr0, len0, data, options);
|
||||
if (ret[3]) {
|
||||
throw takeFromExternrefTable0(ret[2]);
|
||||
}
|
||||
var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
||||
var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
||||
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
||||
return v3;
|
||||
return v2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a simple Ping frame with description, timestamp, and optional data.
|
||||
* Build a protocol-level Ping frame with description, timestamp, and optional data.
|
||||
* @param {bigint} client_id
|
||||
* @param {string} description
|
||||
* @param {bigint} timestamp
|
||||
|
|
@ -478,31 +583,6 @@ export function build_ping_frame(client_id, description, timestamp, data) {
|
|||
return v3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a request frame with the given communication type name, request ID, and JSON data.
|
||||
*
|
||||
* - `comm_type`: communication type name (e.g. "get_model", "rank_models") or PascalCase
|
||||
* - `id`: request ID for response correlation
|
||||
* - `json_data`: JSON-stringified request payload
|
||||
* @param {string} comm_type
|
||||
* @param {number} id
|
||||
* @param {string} json_data
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function build_request_frame(comm_type, id, json_data) {
|
||||
const ptr0 = passStringToWasm0(comm_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(json_data, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.build_request_frame(ptr0, len0, id, ptr1, len1);
|
||||
if (ret[3]) {
|
||||
throw takeFromExternrefTable0(ret[2]);
|
||||
}
|
||||
var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
||||
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
||||
return v3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a fresh Ed25519 keypair.
|
||||
*
|
||||
|
|
@ -605,29 +685,18 @@ export function parse_auth_response(response) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parse a response frame into a JSON string containing `_id`, `_type`, and data fields.
|
||||
* Parse any MTP frame into structured JavaScript data.
|
||||
* @param {Uint8Array} frame
|
||||
* @returns {string}
|
||||
* @returns {ParsedFrame}
|
||||
*/
|
||||
export function parse_response_frame(frame) {
|
||||
let deferred3_0;
|
||||
let deferred3_1;
|
||||
try {
|
||||
const ptr0 = passArray8ToWasm0(frame, wasm.__wbindgen_malloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.parse_response_frame(ptr0, len0);
|
||||
var ptr2 = ret[0];
|
||||
var len2 = ret[1];
|
||||
if (ret[3]) {
|
||||
ptr2 = 0; len2 = 0;
|
||||
throw takeFromExternrefTable0(ret[2]);
|
||||
}
|
||||
deferred3_0 = ptr2;
|
||||
deferred3_1 = len2;
|
||||
return getStringFromWasm0(ptr2, len2);
|
||||
} finally {
|
||||
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
||||
export function parse_frame(frame) {
|
||||
const ptr0 = passArray8ToWasm0(frame, wasm.__wbindgen_malloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.parse_frame(ptr0, len0);
|
||||
if (ret[2]) {
|
||||
throw takeFromExternrefTable0(ret[1]);
|
||||
}
|
||||
return takeFromExternrefTable0(ret[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -707,6 +776,10 @@ export function wasm_sha256_double(data) {
|
|||
function __wbg_get_imports() {
|
||||
const import0 = {
|
||||
__proto__: null,
|
||||
__wbg_BigInt_ff69cca7a537413a: function(arg0, arg1) {
|
||||
const ret = BigInt(getStringFromWasm0(arg0, arg1));
|
||||
return ret;
|
||||
},
|
||||
__wbg___wbindgen_boolean_get_fa956cfa2d1bd751: function(arg0) {
|
||||
const v = arg0;
|
||||
const ret = typeof(v) === 'boolean' ? v : undefined;
|
||||
|
|
@ -740,6 +813,12 @@ function __wbg_get_imports() {
|
|||
const ret = arg0 === undefined;
|
||||
return ret;
|
||||
},
|
||||
__wbg___wbindgen_number_get_394265ed1e1b84ee: function(arg0, arg1) {
|
||||
const obj = arg1;
|
||||
const ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true);
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
|
||||
},
|
||||
__wbg___wbindgen_string_get_b0ca35b86a603356: function(arg0, arg1) {
|
||||
const obj = arg1;
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
|
|
@ -751,6 +830,10 @@ function __wbg_get_imports() {
|
|||
__wbg___wbindgen_throw_344f42d3211c4765: function(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
},
|
||||
__wbg___wbindgen_typeof_b1bf2ff71f77b13e: function(arg0) {
|
||||
const ret = typeof arg0;
|
||||
return ret;
|
||||
},
|
||||
__wbg__wbg_cb_unref_fffb441def202758: function(arg0) {
|
||||
arg0._wbg_cb_unref();
|
||||
},
|
||||
|
|
@ -762,21 +845,18 @@ function __wbg_get_imports() {
|
|||
const ret = arg0.call(arg1, arg2);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_close_23b01d38b065688a: function(arg0, arg1) {
|
||||
arg0.close(arg1);
|
||||
},
|
||||
__wbg_createUnidirectionalStream_9ff0e4127f40ed0d: function(arg0) {
|
||||
const ret = arg0.createUnidirectionalStream();
|
||||
__wbg_call_e3b662382210db98: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = arg0.call(arg1, arg2, arg3);
|
||||
return ret;
|
||||
},
|
||||
}, arguments); },
|
||||
__wbg_construct_4e1a16de27aea5b9: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = Reflect.construct(arg0, arg1);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_crypto_38df2bab126b63dc: function(arg0) {
|
||||
const ret = arg0.crypto;
|
||||
return ret;
|
||||
},
|
||||
__wbg_entries_015dc610cd81ede0: function(arg0) {
|
||||
const ret = Object.entries(arg0);
|
||||
return ret;
|
||||
},
|
||||
__wbg_error_a6fa202b58aa1cd3: function(arg0, arg1) {
|
||||
let deferred0_0;
|
||||
let deferred0_1;
|
||||
|
|
@ -788,32 +868,56 @@ function __wbg_get_imports() {
|
|||
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
||||
}
|
||||
},
|
||||
__wbg_from_13e323c65fc8f464: function(arg0) {
|
||||
const ret = Array.from(arg0);
|
||||
return ret;
|
||||
},
|
||||
__wbg_getRandomValues_c44a50d8cfdaebeb: function() { return handleError(function (arg0, arg1) {
|
||||
arg0.getRandomValues(arg1);
|
||||
}, arguments); },
|
||||
__wbg_getRandomValues_cc7f052a444bb2ce: function() { return handleError(function (arg0, arg1) {
|
||||
globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
|
||||
}, arguments); },
|
||||
__wbg_get_507a50627bffa49b: function(arg0, arg1) {
|
||||
const ret = arg0[arg1 >>> 0];
|
||||
return ret;
|
||||
},
|
||||
__wbg_get_78f252d074a84d0b: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = Reflect.get(arg0, arg1);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_get_unchecked_6e0ad6d2a41b06f6: function(arg0, arg1) {
|
||||
const ret = arg0[arg1 >>> 0];
|
||||
return ret;
|
||||
},
|
||||
__wbg_has_8374cf06984d8bfc: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = Reflect.has(arg0, arg1);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_incomingUnidirectionalStreams_505a67efefd669d3: function(arg0) {
|
||||
const ret = arg0.incomingUnidirectionalStreams;
|
||||
__wbg_instanceof_Promise_4cb210c0b8f8c959: function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
result = arg0 instanceof Promise;
|
||||
} catch (_) {
|
||||
result = false;
|
||||
}
|
||||
const ret = result;
|
||||
return ret;
|
||||
},
|
||||
__wbg_instanceof_Uint8Array_309b927aaf7a3fc7: function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
result = arg0 instanceof Uint8Array;
|
||||
} catch (_) {
|
||||
result = false;
|
||||
}
|
||||
const ret = result;
|
||||
return ret;
|
||||
},
|
||||
__wbg_isArray_0677c962b281d01a: function(arg0) {
|
||||
const ret = Array.isArray(arg0);
|
||||
return ret;
|
||||
},
|
||||
__wbg_keys_58421f8f96795607: function(arg0) {
|
||||
const ret = Object.keys(arg0);
|
||||
return ret;
|
||||
},
|
||||
__wbg_length_1f0964f4a5e2c6d8: function(arg0) {
|
||||
const ret = arg0.length;
|
||||
return ret;
|
||||
|
|
@ -822,9 +926,6 @@ function __wbg_get_imports() {
|
|||
const ret = arg0.length;
|
||||
return ret;
|
||||
},
|
||||
__wbg_log_d267660666346fb3: function(arg0) {
|
||||
console.log(arg0);
|
||||
},
|
||||
__wbg_msCrypto_bd5a034af96bcba6: function(arg0) {
|
||||
const ret = arg0.msCrypto;
|
||||
return ret;
|
||||
|
|
@ -833,10 +934,10 @@ function __wbg_get_imports() {
|
|||
const ret = new Error();
|
||||
return ret;
|
||||
},
|
||||
__wbg_new_a2778e1fb014b494: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = new WebTransport(getStringFromWasm0(arg0, arg1));
|
||||
__wbg_new_32b398fb48b6d94a: function() {
|
||||
const ret = new Array();
|
||||
return ret;
|
||||
}, arguments); },
|
||||
},
|
||||
__wbg_new_cd45aabdf6073e84: function(arg0) {
|
||||
const ret = new Uint8Array(arg0);
|
||||
return ret;
|
||||
|
|
@ -871,10 +972,6 @@ function __wbg_get_imports() {
|
|||
const ret = new Uint8Array(arg0 >>> 0);
|
||||
return ret;
|
||||
},
|
||||
__wbg_new_with_options_5b1cc213336d0b4c: function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = new WebTransport(getStringFromWasm0(arg0, arg1), arg2);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_node_84ea875411254db1: function(arg0) {
|
||||
const ret = arg0.node;
|
||||
return ret;
|
||||
|
|
@ -883,10 +980,6 @@ function __wbg_get_imports() {
|
|||
const ret = Date.now();
|
||||
return ret;
|
||||
},
|
||||
__wbg_parse_1c0d8a8656d7e016: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_process_44c7a14e11e9f69e: function(arg0) {
|
||||
const ret = arg0.process;
|
||||
return ret;
|
||||
|
|
@ -894,6 +987,10 @@ function __wbg_get_imports() {
|
|||
__wbg_prototypesetcall_4770620bbe4688a0: function(arg0, arg1, arg2) {
|
||||
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
|
||||
},
|
||||
__wbg_push_d2ae3af0c1217ae6: function(arg0, arg1) {
|
||||
const ret = arg0.push(arg1);
|
||||
return ret;
|
||||
},
|
||||
__wbg_queueMicrotask_0ab5b2d2393e99b9: function(arg0) {
|
||||
const ret = arg0.queueMicrotask;
|
||||
return ret;
|
||||
|
|
@ -904,10 +1001,6 @@ function __wbg_get_imports() {
|
|||
__wbg_randomFillSync_6c25eac9869eb53c: function() { return handleError(function (arg0, arg1) {
|
||||
arg0.randomFillSync(arg1);
|
||||
}, arguments); },
|
||||
__wbg_ready_e4dad560377c42e6: function(arg0) {
|
||||
const ret = arg0.ready;
|
||||
return ret;
|
||||
},
|
||||
__wbg_require_b4edbdcf3e2a1ef0: function() { return handleError(function () {
|
||||
const ret = module.require;
|
||||
return ret;
|
||||
|
|
@ -920,15 +1013,6 @@ function __wbg_get_imports() {
|
|||
const ret = Reflect.set(arg0, arg1, arg2);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_set_algorithm_4884633ae550b091: function(arg0, arg1, arg2) {
|
||||
arg0.algorithm = getStringFromWasm0(arg1, arg2);
|
||||
},
|
||||
__wbg_set_server_certificate_hashes_c6f10c7638672baf: function(arg0, arg1, arg2) {
|
||||
arg0.serverCertificateHashes = getArrayJsValueViewFromWasm0(arg1, arg2);
|
||||
},
|
||||
__wbg_set_value_u8_array_38f1c892603c4916: function(arg0, arg1) {
|
||||
arg0.value = arg1;
|
||||
},
|
||||
__wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
|
||||
const ret = arg1.stack;
|
||||
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
|
@ -952,10 +1036,6 @@ function __wbg_get_imports() {
|
|||
const ret = typeof window === 'undefined' ? null : window;
|
||||
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||
},
|
||||
__wbg_stringify_b54333f60f1e4dad: function() { return handleError(function (arg0) {
|
||||
const ret = JSON.stringify(arg0);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_subarray_3ed232c8a6baee09: function(arg0, arg1, arg2) {
|
||||
const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0);
|
||||
return ret;
|
||||
|
|
@ -968,6 +1048,10 @@ function __wbg_get_imports() {
|
|||
const ret = arg0.then(arg1);
|
||||
return ret;
|
||||
},
|
||||
__wbg_toString_34387d7c1df9ca1e: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = arg0.toString(arg1);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_versions_276b2795b1c6a219: function(arg0) {
|
||||
const ret = arg0.versions;
|
||||
return ret;
|
||||
|
|
@ -977,18 +1061,18 @@ function __wbg_get_imports() {
|
|||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 135, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 128, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("WebTransportSendStream")], shim_idx: 64, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 65, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h3c511b580d027299);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("undefined")], shim_idx: 64, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 63, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h99ead69e8d7f5bae);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000004: function(arg0) {
|
||||
|
|
@ -1027,6 +1111,14 @@ function __wbg_get_imports() {
|
|||
};
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__h99ead69e8d7f5bae(arg0, arg1) {
|
||||
wasm.wasm_bindgen__convert__closures_____invoke__h99ead69e8d7f5bae(arg0, arg1);
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__h3c511b580d027299(arg0, arg1, arg2) {
|
||||
wasm.wasm_bindgen__convert__closures_____invoke__h3c511b580d027299(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584(arg0, arg1, arg2) {
|
||||
const ret = wasm.wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584(arg0, arg1, arg2);
|
||||
if (ret[1]) {
|
||||
|
|
@ -1034,20 +1126,6 @@ function wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584(arg0, arg
|
|||
}
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f(arg0, arg1, arg2) {
|
||||
const ret = wasm.wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f(arg0, arg1, arg2);
|
||||
if (ret[1]) {
|
||||
throw takeFromExternrefTable0(ret[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2(arg0, arg1, arg2) {
|
||||
const ret = wasm.wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2(arg0, arg1, arg2);
|
||||
if (ret[1]) {
|
||||
throw takeFromExternrefTable0(ret[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__h4bf2427f775cf424(arg0, arg1, arg2, arg3) {
|
||||
wasm.wasm_bindgen__convert__closures_____invoke__h4bf2427f775cf424(arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
|
@ -1070,6 +1148,9 @@ const WasmKeyringFinalization = (typeof FinalizationRegistry === 'undefined')
|
|||
const WasmPublicKeyBundleFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||
? { register: () => {}, unregister: () => {} }
|
||||
: new FinalizationRegistry(ptr => wasm.__wbg_wasmpublickeybundle_free(ptr, 1));
|
||||
const WasmSubscriptionRouterFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||
? { register: () => {}, unregister: () => {} }
|
||||
: new FinalizationRegistry(ptr => wasm.__wbg_wasmsubscriptionrouter_free(ptr, 1));
|
||||
|
||||
function addToExternrefTable0(obj) {
|
||||
const idx = wasm.__externref_table_alloc();
|
||||
|
|
@ -1152,16 +1233,6 @@ function debugString(val) {
|
|||
return className;
|
||||
}
|
||||
|
||||
function getArrayJsValueViewFromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
const mem = getDataViewMemory0();
|
||||
const result = [];
|
||||
for (let i = ptr; i < ptr + 4 * len; i += 4) {
|
||||
result.push(wasm.__wbindgen_externrefs.get(mem.getUint32(i, true)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
|
|
|
|||
Binary file not shown.
37
wasm/pkg/mtp_wasm_bg.wasm.d.ts
vendored
37
wasm/pkg/mtp_wasm_bg.wasm.d.ts
vendored
|
|
@ -1,6 +1,11 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export const build_frame: (a: number, b: number, c: any, d: any) => [number, number, number, number];
|
||||
export const build_ping_frame: (a: bigint, b: number, c: number, d: bigint, e: number, f: number) => [number, number, number, number];
|
||||
export const format_frame: (a: number, b: number) => [number, number, number, number];
|
||||
export const parse_auth_response: (a: number, b: number) => [number, number, number];
|
||||
export const parse_frame: (a: number, b: number) => [number, number, number];
|
||||
export const __wbg_wasmchacha20poly1305_free: (a: number, b: number) => void;
|
||||
export const __wbg_wasmed25519signer_free: (a: number, b: number) => void;
|
||||
export const __wbg_wasmkeyring_free: (a: number, b: number) => void;
|
||||
|
|
@ -26,32 +31,36 @@ export const wasmpublickeybundle_kem_public_key: (a: number) => [number, number]
|
|||
export const wasmpublickeybundle_sig_cl_public_key: (a: number) => [number, number];
|
||||
export const wasmpublickeybundle_sig_pq_public_key: (a: number) => [number, number];
|
||||
export const wasmpublickeybundle_to_bytes: (a: number) => [number, number];
|
||||
export const build_demo_message: (a: bigint, b: number, c: number, d: number, e: number) => [number, number, number, number];
|
||||
export const build_ping_frame: (a: bigint, b: number, c: number, d: bigint, e: number, f: number) => [number, number, number, number];
|
||||
export const build_request_frame: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
|
||||
export const format_frame: (a: number, b: number) => [number, number, number, number];
|
||||
export const parse_auth_response: (a: number, b: number) => [number, number, number];
|
||||
export const parse_response_frame: (a: number, b: number) => [number, number, number, number];
|
||||
export const __wbg_connectionconfig_free: (a: number, b: number) => void;
|
||||
export const __wbg_wasmclient_free: (a: number, b: number) => void;
|
||||
export const connectionconfig_client_id: (a: number) => bigint;
|
||||
export const connectionconfig_new: (a: number, b: number) => number;
|
||||
export const connectionconfig_set_client_id: (a: number, b: bigint) => void;
|
||||
export const connectionconfig_set_server_certificate_hashes: (a: number, b: number, c: number) => void;
|
||||
export const connectionconfig_url: (a: number) => [number, number];
|
||||
export const wasmclient_auth_connect: (a: number, b: number, c: number, d: number, e: number, f: number, g: bigint) => any;
|
||||
export const wasmclient_auth_register: (a: number, b: number, c: number, d: number, e: number, f: number) => any;
|
||||
export const wasmclient_connect: (a: number, b: number) => any;
|
||||
export const wasmclient_disconnect: (a: number) => void;
|
||||
export const wasmclient_is_supported: () => number;
|
||||
export const wasmclient_new: (a: any, b: any, c: any) => number;
|
||||
export const wasmclient_request: (a: number, b: number, c: number, d: number, e: number) => any;
|
||||
export const wasmclient_send: (a: number, b: number, c: number) => any;
|
||||
export const wasmclient_start_protocol_pings: (a: number, b: number, c: bigint) => [number, number];
|
||||
export const wasmclient_state: (a: number) => number;
|
||||
export const wasmclient_stop_protocol_pings: (a: number) => void;
|
||||
export const wasmclient_subscribe: (a: number, b: number, c: number, d: any) => number;
|
||||
export const wasmclient_unsubscribe: (a: number, b: number) => number;
|
||||
export const __wbg_wasmsubscriptionrouter_free: (a: number, b: number) => void;
|
||||
export const main: () => void;
|
||||
export const wasmsubscriptionrouter_dispatch: (a: number, b: number, c: number, d: any) => number;
|
||||
export const wasmsubscriptionrouter_new: () => number;
|
||||
export const wasmsubscriptionrouter_subscribe: (a: number, b: number, c: number, d: any) => void;
|
||||
export const wasmsubscriptionrouter_unsubscribe: (a: number, b: number, c: number) => number;
|
||||
export const __wbg_connectionconfig_free: (a: number, b: number) => void;
|
||||
export const connectionconfig_client_id: (a: number) => bigint;
|
||||
export const connectionconfig_new: (a: number, b: number) => number;
|
||||
export const connectionconfig_set_client_id: (a: number, b: bigint) => void;
|
||||
export const connectionconfig_set_server_certificate_hashes: (a: number, b: number, c: number) => void;
|
||||
export const connectionconfig_url: (a: number) => [number, number];
|
||||
export const wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584: (a: number, b: number, c: any) => [number, number];
|
||||
export const wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f: (a: number, b: number, c: any) => [number, number];
|
||||
export const wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2: (a: number, b: number, c: any) => [number, number];
|
||||
export const wasm_bindgen__convert__closures_____invoke__h4bf2427f775cf424: (a: number, b: number, c: any, d: any) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__h3c511b580d027299: (a: number, b: number, c: any) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__h99ead69e8d7f5bae: (a: number, b: number) => void;
|
||||
export const __wbindgen_malloc: (a: number, b: number) => number;
|
||||
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
export const __wbindgen_exn_store: (a: number) => void;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use std::cell::Cell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures_channel::oneshot;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataValue, PROTOCOL_VERSION};
|
||||
|
|
@ -8,9 +11,116 @@ use mtp_type_map::CommunicationTypeId;
|
|||
|
||||
use mtp_crypto::SignatureScheme;
|
||||
|
||||
use crate::config::ConnectionConfig;
|
||||
use crate::error::js_error;
|
||||
use crate::transport::WasmTransport;
|
||||
|
||||
struct PendingRequest {
|
||||
response_type: Option<String>,
|
||||
sender: oneshot::Sender<Result<JsValue, JsValue>>,
|
||||
}
|
||||
|
||||
struct PingTimer {
|
||||
id: i32,
|
||||
closure: Closure<dyn FnMut()>,
|
||||
}
|
||||
|
||||
fn frame_property(frame: &JsValue, key: &str) -> Option<JsValue> {
|
||||
js_sys::Reflect::get(frame, &JsValue::from_str(key))
|
||||
.ok()
|
||||
.filter(|value| !value.is_null() && !value.is_undefined())
|
||||
}
|
||||
|
||||
fn frame_id(frame: &JsValue) -> Option<u32> {
|
||||
frame_property(frame, "id")
|
||||
.and_then(|value| value.as_f64())
|
||||
.map(|value| value as u32)
|
||||
}
|
||||
|
||||
fn frame_type(frame: &JsValue) -> Option<String> {
|
||||
frame_property(frame, "type").and_then(|value| value.as_string())
|
||||
}
|
||||
|
||||
fn route_incoming_frame(
|
||||
frame: &JsValue,
|
||||
on_message: &js_sys::Function,
|
||||
subscriptions: &Rc<RefCell<HashMap<u32, (String, js_sys::Function)>>>,
|
||||
pending_requests: &Rc<RefCell<HashMap<u32, PendingRequest>>>,
|
||||
) {
|
||||
let message_type = frame_type(frame);
|
||||
|
||||
if let Some(request_id) = frame_id(frame) {
|
||||
let pending = pending_requests.borrow_mut().remove(&request_id);
|
||||
if let Some(pending) = pending {
|
||||
let type_matches = pending
|
||||
.response_type
|
||||
.as_ref()
|
||||
.zip(message_type.as_ref())
|
||||
.map(|(expected, actual)| expected == actual)
|
||||
.unwrap_or(true);
|
||||
if type_matches {
|
||||
let _ = pending.sender.send(Ok(frame.clone()));
|
||||
} else {
|
||||
let actual = message_type.clone().unwrap_or_else(|| "unknown".into());
|
||||
let _ = pending.sender.send(Err(js_error(&format!(
|
||||
"unexpected response type: expected {}, got {}",
|
||||
pending.response_type.unwrap_or_else(|| "unknown".into()),
|
||||
actual
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(message_type) = message_type.as_ref() {
|
||||
let matching_id = pending_requests
|
||||
.borrow()
|
||||
.iter()
|
||||
.find_map(|(id, pending)| match pending.response_type.as_ref() {
|
||||
Some(response_type) if response_type == message_type => Some(*id),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(id) = matching_id {
|
||||
if let Some(pending) = pending_requests.borrow_mut().remove(&id) {
|
||||
let _ = pending.sender.send(Ok(frame.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = on_message.call1(&JsValue::NULL, frame);
|
||||
|
||||
let Some(message_type) = message_type else {
|
||||
return;
|
||||
};
|
||||
for (_, (subscription_type, callback)) in subscriptions.borrow().iter() {
|
||||
if subscription_type == &message_type {
|
||||
let _ = callback.call1(&JsValue::NULL, frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_ping_timer(ping_timer: &Rc<RefCell<Option<PingTimer>>>) {
|
||||
let Some(timer) = ping_timer.borrow_mut().take() else {
|
||||
return;
|
||||
};
|
||||
if let Ok(clear_interval) =
|
||||
js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("clearInterval"))
|
||||
.and_then(|value| value.dyn_into::<js_sys::Function>().map_err(Into::into))
|
||||
{
|
||||
let _ = clear_interval.call1(&JsValue::NULL, &JsValue::from_f64(timer.id as f64));
|
||||
}
|
||||
drop(timer.closure);
|
||||
}
|
||||
|
||||
fn reject_pending_requests(
|
||||
pending_requests: &Rc<RefCell<HashMap<u32, PendingRequest>>>,
|
||||
message: &str,
|
||||
) {
|
||||
let pending = std::mem::take(&mut *pending_requests.borrow_mut());
|
||||
for (_, pending) in pending {
|
||||
let _ = pending.sender.send(Err(js_error(message)));
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_frame_preview(bytes: &[u8]) -> String {
|
||||
let shown = bytes.len().min(256);
|
||||
let mut preview = hex::encode(&bytes[..shown]);
|
||||
|
|
@ -139,45 +249,6 @@ pub enum ConnectionState {
|
|||
Failed = 3,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct ConnectionConfig {
|
||||
url: String,
|
||||
server_certificate_hashes: Option<Vec<String>>,
|
||||
client_id: u64,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl ConnectionConfig {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(url: String) -> Self {
|
||||
Self {
|
||||
url,
|
||||
server_certificate_hashes: None,
|
||||
client_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn url(&self) -> String {
|
||||
self.url.clone()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_client_id(&mut self, id: u64) {
|
||||
self.client_id = id;
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn client_id(&self) -> u64 {
|
||||
self.client_id
|
||||
}
|
||||
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_server_certificate_hashes(&mut self, hashes: Vec<String>) {
|
||||
self.server_certificate_hashes = Some(hashes);
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct WasmClient {
|
||||
transport: Option<WasmTransport>,
|
||||
|
|
@ -185,6 +256,10 @@ pub struct WasmClient {
|
|||
on_state_change: js_sys::Function,
|
||||
pub(crate) on_message: js_sys::Function,
|
||||
pub(crate) on_error: js_sys::Function,
|
||||
subscriptions: Rc<RefCell<HashMap<u32, (String, js_sys::Function)>>>,
|
||||
next_subscription_id: Rc<Cell<u32>>,
|
||||
pending_requests: Rc<RefCell<HashMap<u32, PendingRequest>>>,
|
||||
ping_timer: Rc<RefCell<Option<PingTimer>>>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
|
@ -201,6 +276,10 @@ impl WasmClient {
|
|||
on_state_change: on_state_change.clone(),
|
||||
on_message: on_message.clone(),
|
||||
on_error: on_error.clone(),
|
||||
subscriptions: Rc::new(RefCell::new(HashMap::new())),
|
||||
next_subscription_id: Rc::new(Cell::new(1)),
|
||||
pending_requests: Rc::new(RefCell::new(HashMap::new())),
|
||||
ping_timer: Rc::new(RefCell::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -455,12 +534,134 @@ impl WasmClient {
|
|||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn request(
|
||||
&self,
|
||||
frame: Vec<u8>,
|
||||
response_type: Option<String>,
|
||||
) -> Result<JsValue, JsValue> {
|
||||
let request = CommunicationValue::from_bytes(&frame)
|
||||
.map_err(|e| js_error(&format!("parse request: {}", e)))?;
|
||||
let request_id = request.get_id();
|
||||
if request_id == 0 {
|
||||
return Err(js_error("request frame must have a non-zero id"));
|
||||
}
|
||||
|
||||
let Some(transport) = self.transport.clone() else {
|
||||
return Err(js_error("not connected"));
|
||||
};
|
||||
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.pending_requests.borrow_mut().insert(
|
||||
request_id,
|
||||
PendingRequest {
|
||||
response_type,
|
||||
sender,
|
||||
},
|
||||
);
|
||||
|
||||
if let Err(error) = transport.send_frame(&frame).await {
|
||||
self.pending_requests.borrow_mut().remove(&request_id);
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
match receiver.await {
|
||||
Ok(result) => result,
|
||||
Err(_) => Err(js_error("request cancelled")),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn subscribe(&self, message_type: String, callback: js_sys::Function) -> u32 {
|
||||
let id = self.next_subscription_id.get();
|
||||
self.next_subscription_id.set(id.wrapping_add(1).max(1));
|
||||
self.subscriptions
|
||||
.borrow_mut()
|
||||
.insert(id, (message_type, callback));
|
||||
id
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn unsubscribe(&self, id: u32) -> bool {
|
||||
self.subscriptions.borrow_mut().remove(&id).is_some()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn start_protocol_pings(&self, interval_ms: u32, client_id: u64) -> Result<(), JsValue> {
|
||||
self.stop_protocol_pings();
|
||||
let Some(transport) = self.transport.clone() else {
|
||||
return Err(js_error("not connected"));
|
||||
};
|
||||
let interval_ms = interval_ms.max(1_000) as i32;
|
||||
let on_error = self.on_error.clone();
|
||||
let closure = Closure::wrap(Box::new(move || {
|
||||
let transport = transport.clone();
|
||||
let on_error = on_error.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let timestamp = js_sys::Date::now() as u64;
|
||||
let frame = CommunicationValue::new(CommunicationType::Ping)
|
||||
.add_typed_default(
|
||||
DataType::Description,
|
||||
DataValue::Str("protocol ping".into()),
|
||||
)
|
||||
.add_typed_default(
|
||||
DataType::Timestamp,
|
||||
DataValue::UnsignedNumber(timestamp as u128),
|
||||
)
|
||||
.with_sender(client_id)
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode ping failed: {}", e)));
|
||||
match frame {
|
||||
Ok(frame) => {
|
||||
if let Err(error) = transport.send_frame(&frame).await {
|
||||
let _ = on_error.call1(&JsValue::NULL, &error);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
let _ = on_error.call1(&JsValue::NULL, &error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
let set_interval =
|
||||
js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("setInterval"))?
|
||||
.dyn_into::<js_sys::Function>()?;
|
||||
let id = set_interval
|
||||
.call2(
|
||||
&JsValue::NULL,
|
||||
closure.as_ref().unchecked_ref(),
|
||||
&JsValue::from_f64(interval_ms as f64),
|
||||
)?
|
||||
.as_f64()
|
||||
.ok_or_else(|| js_error("setInterval did not return an id"))? as i32;
|
||||
*self.ping_timer.borrow_mut() = Some(PingTimer { id, closure });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn stop_protocol_pings(&self) {
|
||||
let Some(timer) = self.ping_timer.borrow_mut().take() else {
|
||||
return;
|
||||
};
|
||||
if let Ok(clear_interval) =
|
||||
js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("clearInterval"))
|
||||
.and_then(|value| value.dyn_into::<js_sys::Function>().map_err(Into::into))
|
||||
{
|
||||
let _ = clear_interval.call1(&JsValue::NULL, &JsValue::from_f64(timer.id as f64));
|
||||
}
|
||||
drop(timer.closure);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn disconnect(&mut self) {
|
||||
self.stop_protocol_pings();
|
||||
if let Some(t) = &self.transport {
|
||||
t.close();
|
||||
}
|
||||
self.transport = None;
|
||||
self.subscriptions.borrow_mut().clear();
|
||||
self.reject_pending_requests("disconnected");
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
}
|
||||
|
||||
|
|
@ -479,12 +680,34 @@ impl WasmClient {
|
|||
let state = self.state.clone();
|
||||
let on_msg = self.on_message.clone();
|
||||
let on_err = self.on_error.clone();
|
||||
let subscriptions = self.subscriptions.clone();
|
||||
let pending_requests = self.pending_requests.clone();
|
||||
let loop_pending_requests = pending_requests.clone();
|
||||
let ping_timer = self.ping_timer.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
loop_transport.receive_loop(on_msg, on_err).await;
|
||||
let route_frame = Closure::wrap(Box::new(move |frame: JsValue| {
|
||||
route_incoming_frame(&frame, &on_msg, &subscriptions, &loop_pending_requests);
|
||||
}) as Box<dyn FnMut(JsValue)>);
|
||||
loop_transport
|
||||
.receive_loop(
|
||||
route_frame
|
||||
.as_ref()
|
||||
.unchecked_ref::<js_sys::Function>()
|
||||
.clone(),
|
||||
on_err.clone(),
|
||||
)
|
||||
.await;
|
||||
drop(route_frame);
|
||||
state.set(ConnectionState::Disconnected);
|
||||
stop_ping_timer(&ping_timer);
|
||||
reject_pending_requests(&pending_requests, "disconnected");
|
||||
});
|
||||
}
|
||||
|
||||
fn reject_pending_requests(&self, message: &str) {
|
||||
reject_pending_requests(&self.pending_requests, message);
|
||||
}
|
||||
|
||||
async fn read_verified_challenge(
|
||||
&self,
|
||||
transport: &WasmTransport,
|
||||
|
|
|
|||
40
wasm/src/config.rs
Normal file
40
wasm/src/config.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct ConnectionConfig {
|
||||
pub(crate) url: String,
|
||||
pub(crate) server_certificate_hashes: Option<Vec<String>>,
|
||||
pub(crate) client_id: u64,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl ConnectionConfig {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(url: String) -> Self {
|
||||
Self {
|
||||
url,
|
||||
server_certificate_hashes: None,
|
||||
client_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn url(&self) -> String {
|
||||
self.url.clone()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_client_id(&mut self, id: u64) {
|
||||
self.client_id = id;
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn client_id(&self) -> u64 {
|
||||
self.client_id
|
||||
}
|
||||
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_server_certificate_hashes(&mut self, hashes: Vec<String>) {
|
||||
self.server_certificate_hashes = Some(hashes);
|
||||
}
|
||||
}
|
||||
479
wasm/src/frame.rs
Normal file
479
wasm/src/frame.rs
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
use wasm_bindgen::{JsCast, prelude::*};
|
||||
|
||||
use mtp_codec::{
|
||||
CommunicationType, CommunicationValue, DataType, DataValue, communication_type_name,
|
||||
data_type_name,
|
||||
};
|
||||
use mtp_type_map::TypeMap;
|
||||
|
||||
use crate::error::js_error;
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const PARSED_FRAME_TS: &'static str = r#"
|
||||
export interface ParsedFrame {
|
||||
id?: number;
|
||||
type: string;
|
||||
sender?: bigint;
|
||||
receiver?: bigint;
|
||||
data: Record<string, unknown>;
|
||||
raw: Uint8Array;
|
||||
}
|
||||
"#;
|
||||
|
||||
fn set_prop(obj: &js_sys::Object, key: &str, value: &JsValue) -> Result<(), JsValue> {
|
||||
js_sys::Reflect::set(obj, &JsValue::from_str(key), value).map(|_| ())
|
||||
}
|
||||
|
||||
fn integer_value(value: &str) -> JsValue {
|
||||
if let Ok(number) = value.parse::<f64>() {
|
||||
if number.fract() == 0.0 && number.abs() <= 9_007_199_254_740_991.0 {
|
||||
return JsValue::from_f64(number);
|
||||
}
|
||||
}
|
||||
JsValue::from_str(value)
|
||||
}
|
||||
|
||||
fn data_value_to_js(value: &DataValue) -> Result<JsValue, JsValue> {
|
||||
match value {
|
||||
DataValue::BoolTrue => Ok(JsValue::TRUE),
|
||||
DataValue::BoolFalse => Ok(JsValue::FALSE),
|
||||
DataValue::Bool(v) => Ok(JsValue::from_bool(*v)),
|
||||
DataValue::SignedNumber(n) => Ok(integer_value(&n.to_string())),
|
||||
DataValue::UnsignedNumber(n) => Ok(integer_value(&n.to_string())),
|
||||
DataValue::Float(exp, mant) => {
|
||||
Ok(JsValue::from_f64((*mant as f64) * 10f64.powi(*exp as i32)))
|
||||
}
|
||||
DataValue::Str(s) => Ok(JsValue::from_str(s)),
|
||||
DataValue::Bytes(bytes) => Ok(js_sys::Uint8Array::from(&bytes[..]).into()),
|
||||
DataValue::Array(values) => {
|
||||
let arr = js_sys::Array::new();
|
||||
for value in values {
|
||||
arr.push(&data_value_to_js(value)?);
|
||||
}
|
||||
Ok(arr.into())
|
||||
}
|
||||
DataValue::Container(entries) => {
|
||||
let obj = js_sys::Object::new();
|
||||
for (key, value) in entries {
|
||||
let name = data_type_name(key.0)
|
||||
.map(str::to_string)
|
||||
.unwrap_or_else(|| key.0.to_string());
|
||||
set_prop(&obj, &name, &data_value_to_js(value)?)?;
|
||||
}
|
||||
Ok(obj.into())
|
||||
}
|
||||
DataValue::EncryptedContainer(bytes)
|
||||
| DataValue::SignedContainer(bytes)
|
||||
| DataValue::SignedEncryptedContainer(bytes) => {
|
||||
Ok(js_sys::Uint8Array::from(&bytes[..]).into())
|
||||
}
|
||||
DataValue::Null => Ok(JsValue::NULL),
|
||||
}
|
||||
}
|
||||
|
||||
fn js_to_data_value(value: &JsValue) -> Result<DataValue, JsValue> {
|
||||
if value.is_null() || value.is_undefined() {
|
||||
return Ok(DataValue::Null);
|
||||
}
|
||||
if let Some(v) = value.as_bool() {
|
||||
return Ok(DataValue::Bool(v));
|
||||
}
|
||||
if let Some(v) = value.as_string() {
|
||||
return Ok(DataValue::Str(v));
|
||||
}
|
||||
if js_sys::Uint8Array::instanceof(value) {
|
||||
return Ok(DataValue::Bytes(js_sys::Uint8Array::new(value).to_vec()));
|
||||
}
|
||||
if js_sys::Array::is_array(value) {
|
||||
let array = js_sys::Array::from(value);
|
||||
let mut values = Vec::with_capacity(array.length() as usize);
|
||||
for item in array.iter() {
|
||||
values.push(js_to_data_value(&item)?);
|
||||
}
|
||||
return Ok(DataValue::Array(values));
|
||||
}
|
||||
if let Some(v) = value.as_f64() {
|
||||
if v.fract() == 0.0 {
|
||||
if v >= 0.0 {
|
||||
return Ok(DataValue::UnsignedNumber(v as u128));
|
||||
}
|
||||
return Ok(DataValue::SignedNumber(v as i128));
|
||||
}
|
||||
let mantissa = (v * 1_000_000.0).round() as u32;
|
||||
return Ok(DataValue::Float(246, mantissa));
|
||||
}
|
||||
|
||||
let type_name = value.js_typeof().as_string().unwrap_or_default();
|
||||
if type_name == "bigint" {
|
||||
let bigint = value.clone().unchecked_into::<js_sys::BigInt>();
|
||||
let as_string = bigint
|
||||
.to_string(10)?
|
||||
.as_string()
|
||||
.ok_or_else(|| js_error("failed to stringify bigint"))?;
|
||||
if let Some(unsigned) = as_string.strip_prefix('-') {
|
||||
let n = unsigned
|
||||
.parse::<i128>()
|
||||
.map_err(|_| js_error("bigint out of range"))?;
|
||||
return Ok(DataValue::SignedNumber(-n));
|
||||
}
|
||||
let n = as_string
|
||||
.parse::<u128>()
|
||||
.map_err(|_| js_error("bigint out of range"))?;
|
||||
return Ok(DataValue::UnsignedNumber(n));
|
||||
}
|
||||
|
||||
if value.is_object() {
|
||||
let object = js_sys::Object::from(value.clone());
|
||||
let keys = js_sys::Object::keys(&object);
|
||||
let mut entries = Vec::with_capacity(keys.length() as usize);
|
||||
for key in keys.iter() {
|
||||
let key = key
|
||||
.as_string()
|
||||
.ok_or_else(|| js_error("object key must be a string"))?;
|
||||
let data_type = DataType::from_name(&key)
|
||||
.ok_or_else(|| js_error(&format!("unknown data type: {key}")))?;
|
||||
let value = js_sys::Reflect::get(&object, &JsValue::from_str(&key))?;
|
||||
entries.push((
|
||||
data_type.to_id(&TypeMap::latest()),
|
||||
js_to_data_value(&value)?,
|
||||
));
|
||||
}
|
||||
return Ok(DataValue::Container(entries));
|
||||
}
|
||||
|
||||
Err(js_error("unsupported data value"))
|
||||
}
|
||||
|
||||
fn option_u32(options: &JsValue, key: &str) -> Result<Option<u32>, JsValue> {
|
||||
let value = js_sys::Reflect::get(options, &JsValue::from_str(key))?;
|
||||
if value.is_null() || value.is_undefined() {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(n) = value.as_f64() else {
|
||||
return Err(js_error(&format!("{key} must be a number")));
|
||||
};
|
||||
Ok(Some(n as u32))
|
||||
}
|
||||
|
||||
fn option_u64(options: &JsValue, key: &str) -> Result<Option<u64>, JsValue> {
|
||||
let value = js_sys::Reflect::get(options, &JsValue::from_str(key))?;
|
||||
if value.is_null() || value.is_undefined() {
|
||||
return Ok(None);
|
||||
}
|
||||
if let Some(n) = value.as_f64() {
|
||||
return Ok(Some(n as u64));
|
||||
}
|
||||
let type_name = value.js_typeof().as_string().unwrap_or_default();
|
||||
if type_name == "bigint" {
|
||||
let bigint = value.clone().unchecked_into::<js_sys::BigInt>();
|
||||
let as_string = bigint
|
||||
.to_string(10)?
|
||||
.as_string()
|
||||
.ok_or_else(|| js_error("failed to stringify bigint"))?;
|
||||
return as_string
|
||||
.parse::<u64>()
|
||||
.map(Some)
|
||||
.map_err(|_| js_error(&format!("{key} out of range")));
|
||||
}
|
||||
Err(js_error(&format!("{key} must be a number or bigint")))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_frame_value(frame: &[u8]) -> Result<JsValue, JsValue> {
|
||||
let comm = CommunicationValue::from_bytes(frame)
|
||||
.map_err(|e| js_error(&format!("parse failed: {}", e)))?;
|
||||
let obj = js_sys::Object::new();
|
||||
let data = js_sys::Object::new();
|
||||
|
||||
if comm.get_id() != 0 {
|
||||
set_prop(&obj, "id", &JsValue::from_f64(comm.get_id() as f64))?;
|
||||
}
|
||||
|
||||
let frame_type = communication_type_name(comm.get_type().0)
|
||||
.map(str::to_string)
|
||||
.unwrap_or_else(|| comm.get_type().0.to_string());
|
||||
set_prop(&obj, "type", &JsValue::from_str(&frame_type))?;
|
||||
|
||||
if comm.get_sender() != 0 {
|
||||
set_prop(
|
||||
&obj,
|
||||
"sender",
|
||||
&JsValue::bigint_from_str(&comm.get_sender().to_string()),
|
||||
)?;
|
||||
}
|
||||
if comm.get_receiver() != 0 {
|
||||
set_prop(
|
||||
&obj,
|
||||
"receiver",
|
||||
&JsValue::bigint_from_str(&comm.get_receiver().to_string()),
|
||||
)?;
|
||||
}
|
||||
|
||||
for (key, value) in comm.data() {
|
||||
let name = data_type_name(key.0)
|
||||
.map(str::to_string)
|
||||
.unwrap_or_else(|| key.0.to_string());
|
||||
set_prop(&data, &name, &data_value_to_js(value)?)?;
|
||||
}
|
||||
set_prop(&obj, "data", &data.into())?;
|
||||
set_prop(&obj, "raw", &js_sys::Uint8Array::from(frame).into())?;
|
||||
|
||||
Ok(obj.into())
|
||||
}
|
||||
|
||||
/// Build a protocol-level Ping frame with description, timestamp, and optional data.
|
||||
#[wasm_bindgen]
|
||||
pub fn build_ping_frame(
|
||||
client_id: u64,
|
||||
description: &str,
|
||||
timestamp: u64,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, JsValue> {
|
||||
let mut msg = CommunicationValue::new(CommunicationType::Ping)
|
||||
.add_typed_default(
|
||||
DataType::Description,
|
||||
DataValue::Str(description.to_string()),
|
||||
)
|
||||
.add_typed_default(
|
||||
DataType::Timestamp,
|
||||
DataValue::UnsignedNumber(timestamp as u128),
|
||||
)
|
||||
.with_sender(client_id);
|
||||
|
||||
if !data.is_empty() {
|
||||
msg = msg.add_typed_default(DataType::Id, DataValue::Bytes(data.to_vec()));
|
||||
}
|
||||
|
||||
msg.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))
|
||||
}
|
||||
|
||||
/// Parse an auth response frame into a JS object.
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_auth_response(response: &[u8]) -> Result<JsValue, JsValue> {
|
||||
let comm = CommunicationValue::from_bytes(response)
|
||||
.map_err(|e| js_error(&format!("parse failed: {}", e)))?;
|
||||
|
||||
let connected = matches!(
|
||||
comm.get_data(DataType::Connected.to_id(&TypeMap::latest())),
|
||||
DataValue::BoolTrue
|
||||
);
|
||||
|
||||
let client_nonce = match comm.get_data(DataType::ClientNonce.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let assigned_id = match comm.get_data(DataType::Id.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n as u64),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let timestamp = match comm.get_data(DataType::Timestamp.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let signature = match comm.get_data(DataType::Signature.to_id(&TypeMap::latest())) {
|
||||
DataValue::Bytes(b) => Some(b.clone()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let obj = js_sys::Object::new();
|
||||
js_sys::Reflect::set(&obj, &"connected".into(), &JsValue::from(connected)).ok();
|
||||
if let Some(n) = client_nonce {
|
||||
let arr = js_sys::Uint8Array::from(&n.to_be_bytes()[..]);
|
||||
js_sys::Reflect::set(&obj, &"clientNonce".into(), &arr).ok();
|
||||
}
|
||||
if let Some(id) = assigned_id {
|
||||
js_sys::Reflect::set(&obj, &"assignedId".into(), &JsValue::from(id as f64)).ok();
|
||||
}
|
||||
if let Some(ts) = timestamp {
|
||||
js_sys::Reflect::set(&obj, &"timestamp".into(), &JsValue::from(ts as f64)).ok();
|
||||
}
|
||||
if let Some(sig) = signature {
|
||||
let arr = js_sys::Uint8Array::from(&sig[..]);
|
||||
js_sys::Reflect::set(&obj, &"signature".into(), &arr).ok();
|
||||
}
|
||||
|
||||
Ok(obj.into())
|
||||
}
|
||||
|
||||
/// Parse any MTP frame into the human-readable CommunicationValue display form.
|
||||
#[wasm_bindgen]
|
||||
pub fn format_frame(frame: &[u8]) -> Result<String, JsValue> {
|
||||
let comm = CommunicationValue::from_bytes(frame)
|
||||
.map_err(|e| js_error(&format!("parse failed: {}", e)))?;
|
||||
Ok(comm.to_string())
|
||||
}
|
||||
|
||||
/// Parse any MTP frame into structured JavaScript data.
|
||||
#[wasm_bindgen(unchecked_return_type = "ParsedFrame")]
|
||||
pub fn parse_frame(frame: &[u8]) -> Result<JsValue, JsValue> {
|
||||
parse_frame_value(frame)
|
||||
}
|
||||
|
||||
/// Build a typed MTP frame using generated communication/data type names.
|
||||
#[wasm_bindgen]
|
||||
pub fn build_frame(
|
||||
message_type: &str,
|
||||
data: JsValue,
|
||||
options: JsValue,
|
||||
) -> Result<Vec<u8>, JsValue> {
|
||||
let comm_type = CommunicationType::from_name(message_type)
|
||||
.ok_or_else(|| js_error(&format!("unknown communication type: {message_type}")))?;
|
||||
let mut msg = CommunicationValue::new(comm_type);
|
||||
|
||||
if !options.is_null() && !options.is_undefined() {
|
||||
if let Some(id) = option_u32(&options, "id")? {
|
||||
msg = msg.with_id(id);
|
||||
}
|
||||
if let Some(sender) = option_u64(&options, "sender")? {
|
||||
msg = msg.with_sender(sender);
|
||||
}
|
||||
if let Some(receiver) = option_u64(&options, "receiver")? {
|
||||
msg = msg.with_receiver(receiver);
|
||||
}
|
||||
}
|
||||
|
||||
if data.is_object() && !js_sys::Uint8Array::instanceof(&data) && !js_sys::Array::is_array(&data)
|
||||
{
|
||||
let object = js_sys::Object::from(data);
|
||||
let keys = js_sys::Object::keys(&object);
|
||||
for key in keys.iter() {
|
||||
let key = key
|
||||
.as_string()
|
||||
.ok_or_else(|| js_error("object key must be a string"))?;
|
||||
let data_type = DataType::from_name(&key)
|
||||
.ok_or_else(|| js_error(&format!("unknown data type: {key}")))?;
|
||||
let value = js_sys::Reflect::get(&object, &JsValue::from_str(&key))?;
|
||||
msg = msg.add_data(
|
||||
data_type.to_id(&TypeMap::latest()),
|
||||
js_to_data_value(&value)?,
|
||||
);
|
||||
}
|
||||
} else if !data.is_null() && !data.is_undefined() {
|
||||
return Err(js_error(
|
||||
"frame data must be an object keyed by MTP data type",
|
||||
));
|
||||
}
|
||||
|
||||
msg.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_ping_frame_roundtrip() {
|
||||
let bytes = build_ping_frame(42, "test-ping", 1234567890, &[]).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 42);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("test-ping".into())
|
||||
);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Timestamp.to_id(&tm)),
|
||||
&DataValue::UnsignedNumber(1234567890)
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_ping_frame_with_data() {
|
||||
let payload = b"attachment-data";
|
||||
let bytes = build_ping_frame(99, "with-data", 555, payload).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 99);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("with-data".into())
|
||||
);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Timestamp.to_id(&tm)),
|
||||
&DataValue::UnsignedNumber(555)
|
||||
);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Id.to_id(&tm)),
|
||||
&DataValue::Bytes(payload.to_vec())
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_ping_frame_client_id_zero() {
|
||||
let bytes = build_ping_frame(0, "zero-id", 0, &[]).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
assert_eq!(cv.get_sender(), 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_success() {
|
||||
let resp = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolTrue)
|
||||
.add_typed_default(DataType::ClientNonce, DataValue::UnsignedNumber(999))
|
||||
.add_typed_default(DataType::Id, DataValue::UnsignedNumber(42))
|
||||
.add_typed_default(DataType::Timestamp, DataValue::UnsignedNumber(12345))
|
||||
.to_bytes()
|
||||
.expect("encode failed");
|
||||
|
||||
let result = parse_auth_response(&resp).expect("parse failed");
|
||||
|
||||
let connected = js_sys::Reflect::get(&result, &"connected".into())
|
||||
.ok()
|
||||
.and_then(|v| v.as_bool());
|
||||
assert_eq!(connected, Some(true));
|
||||
|
||||
let id = js_sys::Reflect::get(&result, &"assignedId".into())
|
||||
.ok()
|
||||
.and_then(|v| v.as_f64());
|
||||
assert_eq!(id, Some(42.0));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_rejected() {
|
||||
let resp = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolFalse)
|
||||
.to_bytes()
|
||||
.expect("encode failed");
|
||||
|
||||
let result = parse_auth_response(&resp).expect("parse failed");
|
||||
|
||||
let connected = js_sys::Reflect::get(&result, &"connected".into())
|
||||
.ok()
|
||||
.and_then(|v| v.as_bool());
|
||||
assert_eq!(connected, Some(false));
|
||||
|
||||
let has_id = js_sys::Reflect::has(&result, &"assignedId".into()).unwrap_or(false);
|
||||
assert!(!has_id);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_with_signature() {
|
||||
let sig_bytes = vec![0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe];
|
||||
let resp = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolTrue)
|
||||
.add_typed_default(DataType::Signature, DataValue::Bytes(sig_bytes.clone()))
|
||||
.to_bytes()
|
||||
.expect("encode failed");
|
||||
|
||||
let result = parse_auth_response(&resp).expect("parse failed");
|
||||
|
||||
let has_sig = js_sys::Reflect::has(&result, &"signature".into()).unwrap_or(false);
|
||||
assert!(has_sig);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_invalid_frame() {
|
||||
let result = parse_auth_response(b"garbage-data");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod crypto;
|
||||
pub mod error;
|
||||
pub mod message;
|
||||
pub mod frame;
|
||||
pub mod logging;
|
||||
pub mod subscription;
|
||||
pub mod transport;
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
|
@ -11,5 +14,4 @@ use wasm_bindgen::prelude::*;
|
|||
#[wasm_bindgen(start)]
|
||||
pub fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
web_sys::console::log_1(&"mtp-wasm: module loaded".into());
|
||||
}
|
||||
|
|
|
|||
10
wasm/src/logging.rs
Normal file
10
wasm/src/logging.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
/// Log severity used by the public SDK when translating raw WASM events.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WasmLogHint {
|
||||
Info = 0,
|
||||
Warning = 1,
|
||||
Error = 2,
|
||||
}
|
||||
|
|
@ -1,411 +0,0 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataTypeId, DataValue};
|
||||
use mtp_crypto::{Ed25519Signer, EncryptionType, Keyring, PublicKeyBundle, SigAlgorithm};
|
||||
use mtp_type_map::{TypeMap, communication_type_name};
|
||||
|
||||
use crate::error::js_error;
|
||||
|
||||
/// Build a simple Ping frame with description, timestamp, and optional data.
|
||||
#[wasm_bindgen]
|
||||
pub fn build_ping_frame(
|
||||
client_id: u64,
|
||||
description: &str,
|
||||
timestamp: u64,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, JsValue> {
|
||||
let mut msg = CommunicationValue::new(CommunicationType::Ping)
|
||||
.add_typed_default(
|
||||
DataType::Description,
|
||||
DataValue::Str(description.to_string()),
|
||||
)
|
||||
.add_typed_default(
|
||||
DataType::Timestamp,
|
||||
DataValue::UnsignedNumber(timestamp as u128),
|
||||
)
|
||||
.with_sender(client_id);
|
||||
|
||||
if !data.is_empty() {
|
||||
msg = msg.add_typed_default(DataType::Id, DataValue::Bytes(data.to_vec()));
|
||||
}
|
||||
|
||||
msg.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))
|
||||
}
|
||||
|
||||
/// Build a demo Ping frame with encrypted and signed containers
|
||||
/// (mirrors the Rust client example but uses only reserved data types).
|
||||
#[wasm_bindgen]
|
||||
pub fn build_demo_message(
|
||||
client_id: u64,
|
||||
keyring_bytes: &[u8],
|
||||
host_bundle_bytes: &[u8],
|
||||
) -> Result<Vec<u8>, JsValue> {
|
||||
let keyring = Keyring::from_bytes(keyring_bytes)
|
||||
.map_err(|e| js_error(&format!("invalid keyring: {}", e)))?;
|
||||
|
||||
// Encrypt to the server's KEM public key; the server decrypts with its keyring.
|
||||
// (The client keyring only needs the Ed25519 signing key for this demo.)
|
||||
let recipient = PublicKeyBundle::from_bytes(host_bundle_bytes)
|
||||
.map_err(|e| js_error(&format!("invalid host bundle: {}", e)))?;
|
||||
let enc_type = EncryptionType::MlKemChaCha20Poly1305;
|
||||
let signer = Ed25519Signer::new(&keyring.sig_cl_secret_key)
|
||||
.map_err(|e| js_error(&format!("signer creation failed: {}", e)))?;
|
||||
|
||||
// Encrypted container
|
||||
let inner_enc = DataValue::Container(vec![
|
||||
(
|
||||
DataType::Version.to_id(&TypeMap::latest()),
|
||||
DataValue::Str("secret inner data".into()),
|
||||
),
|
||||
(
|
||||
DataType::Id.to_id(&TypeMap::latest()),
|
||||
DataValue::UnsignedNumber(42),
|
||||
),
|
||||
]);
|
||||
let mut dv_enc = inner_enc;
|
||||
dv_enc
|
||||
.encrypt_container(enc_type, &recipient, b"demo-aad")
|
||||
.ok_or_else(|| js_error("encryption failed"))?;
|
||||
|
||||
// Signed container
|
||||
let inner_sig = DataValue::Container(vec![
|
||||
(
|
||||
DataType::Version.to_id(&TypeMap::latest()),
|
||||
DataValue::Str("signed by client".into()),
|
||||
),
|
||||
(
|
||||
DataType::Id.to_id(&TypeMap::latest()),
|
||||
DataValue::UnsignedNumber(99),
|
||||
),
|
||||
]);
|
||||
let mut dv_sig = inner_sig;
|
||||
dv_sig
|
||||
.sign_container(SigAlgorithm::ED25519, &signer)
|
||||
.ok_or_else(|| js_error("signing failed"))?;
|
||||
|
||||
// Signed + encrypted container
|
||||
let inner_sec = DataValue::Container(vec![
|
||||
(
|
||||
DataType::Version.to_id(&TypeMap::latest()),
|
||||
DataValue::Str("signed+encrypted payload".into()),
|
||||
),
|
||||
(
|
||||
DataType::Id.to_id(&TypeMap::latest()),
|
||||
DataValue::UnsignedNumber(7),
|
||||
),
|
||||
]);
|
||||
let mut dv_sec = inner_sec;
|
||||
dv_sec
|
||||
.sign_and_encrypt_container(
|
||||
SigAlgorithm::ED25519,
|
||||
&signer,
|
||||
enc_type,
|
||||
&recipient,
|
||||
b"demo-aad",
|
||||
)
|
||||
.ok_or_else(|| js_error("sign+encrypt failed"))?;
|
||||
|
||||
let timestamp = js_sys::Date::now() as u64;
|
||||
|
||||
let msg = CommunicationValue::new(CommunicationType::Ping)
|
||||
.add_typed_default(
|
||||
DataType::Description,
|
||||
DataValue::Str("MTP WASM Demo".into()),
|
||||
)
|
||||
.add_typed_default(
|
||||
DataType::Timestamp,
|
||||
DataValue::UnsignedNumber(timestamp as u128),
|
||||
)
|
||||
.add_typed_default(DataType::Version, DataValue::Str("demo-wasm".into()))
|
||||
.with_sender(client_id);
|
||||
|
||||
msg.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))
|
||||
}
|
||||
|
||||
/// Parse an auth response frame into a JS object.
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_auth_response(response: &[u8]) -> Result<JsValue, JsValue> {
|
||||
let comm = CommunicationValue::from_bytes(response)
|
||||
.map_err(|e| js_error(&format!("parse failed: {}", e)))?;
|
||||
|
||||
let connected = matches!(
|
||||
comm.get_data(DataType::Connected.to_id(&TypeMap::latest())),
|
||||
DataValue::BoolTrue
|
||||
);
|
||||
|
||||
let client_nonce = match comm.get_data(DataType::ClientNonce.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let assigned_id = match comm.get_data(DataType::Id.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n as u64),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let timestamp = match comm.get_data(DataType::Timestamp.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let signature = match comm.get_data(DataType::Signature.to_id(&TypeMap::latest())) {
|
||||
DataValue::Bytes(b) => Some(b.clone()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let obj = js_sys::Object::new();
|
||||
js_sys::Reflect::set(&obj, &"connected".into(), &JsValue::from(connected)).ok();
|
||||
if let Some(n) = client_nonce {
|
||||
let arr = js_sys::Uint8Array::from(&n.to_be_bytes()[..]);
|
||||
js_sys::Reflect::set(&obj, &"clientNonce".into(), &arr).ok();
|
||||
}
|
||||
if let Some(id) = assigned_id {
|
||||
js_sys::Reflect::set(&obj, &"assignedId".into(), &JsValue::from(id as f64)).ok();
|
||||
}
|
||||
if let Some(ts) = timestamp {
|
||||
js_sys::Reflect::set(&obj, &"timestamp".into(), &JsValue::from(ts as f64)).ok();
|
||||
}
|
||||
if let Some(sig) = signature {
|
||||
let arr = js_sys::Uint8Array::from(&sig[..]);
|
||||
js_sys::Reflect::set(&obj, &"signature".into(), &arr).ok();
|
||||
}
|
||||
|
||||
Ok(obj.into())
|
||||
}
|
||||
|
||||
/// Build a request frame with the given communication type name, request ID, and JSON data.
|
||||
///
|
||||
/// - `comm_type`: communication type name (e.g. "get_model", "rank_models") or PascalCase
|
||||
/// - `id`: request ID for response correlation
|
||||
/// - `json_data`: JSON-stringified request payload
|
||||
#[wasm_bindgen]
|
||||
pub fn build_request_frame(comm_type: &str, id: u32, json_data: &str) -> Result<Vec<u8>, JsValue> {
|
||||
let comm_type_enum = CommunicationType::from_name(comm_type)
|
||||
.or_else(|| {
|
||||
let pascal = comm_type
|
||||
.split('_')
|
||||
.map(|s| {
|
||||
let mut c = s.chars();
|
||||
match c.next() {
|
||||
None => String::new(),
|
||||
Some(f) => f.to_uppercase().to_string() + c.as_str(),
|
||||
}
|
||||
})
|
||||
.collect::<String>();
|
||||
CommunicationType::from_name(&pascal)
|
||||
})
|
||||
.ok_or_else(|| js_error(&format!("unknown communication type: {}", comm_type)))?;
|
||||
|
||||
let frame = CommunicationValue::new(comm_type_enum)
|
||||
.with_id(id)
|
||||
.add_data(DataTypeId(32), DataValue::Str(json_data.to_string()))
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
/// Parse a response frame into a JSON string containing `_id`, `_type`, and data fields.
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_response_frame(frame: &[u8]) -> Result<String, JsValue> {
|
||||
let comm = CommunicationValue::from_bytes(frame)
|
||||
.map_err(|e| js_error(&format!("parse failed: {}", e)))?;
|
||||
|
||||
let obj = js_sys::Object::new();
|
||||
|
||||
let _ = js_sys::Reflect::set(
|
||||
&obj,
|
||||
&JsValue::from_str("_id"),
|
||||
&JsValue::from(comm.get_id()),
|
||||
);
|
||||
|
||||
let type_name = communication_type_name(comm.get_type().0).unwrap_or("Unknown");
|
||||
let _ = js_sys::Reflect::set(
|
||||
&obj,
|
||||
&JsValue::from_str("_type"),
|
||||
&JsValue::from_str(&type_name),
|
||||
);
|
||||
|
||||
if let DataValue::Str(s) = comm.get_data(DataTypeId(32)) {
|
||||
if let Ok(parsed) = js_sys::JSON::parse(s) {
|
||||
let parsed_obj: &js_sys::Object = parsed.unchecked_ref();
|
||||
let entries = js_sys::Object::entries(parsed_obj);
|
||||
let len = entries.length();
|
||||
for i in 0..len {
|
||||
let entry = js_sys::Array::get(&entries, i);
|
||||
if let Some(entry_arr) = entry.dyn_ref::<js_sys::Array>() {
|
||||
if let Some(key) = entry_arr.get(0).as_string() {
|
||||
let val = entry_arr.get(1);
|
||||
let _ = js_sys::Reflect::set(&obj, &JsValue::from_str(&key), &val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let stringified =
|
||||
js_sys::JSON::stringify(&obj).map_err(|_| js_error("JSON stringify failed"))?;
|
||||
stringified
|
||||
.as_string()
|
||||
.ok_or_else(|| js_error("JSON stringify result not a string"))
|
||||
}
|
||||
|
||||
/// Parse any MTP frame into the human-readable CommunicationValue display form.
|
||||
#[wasm_bindgen]
|
||||
pub fn format_frame(frame: &[u8]) -> Result<String, JsValue> {
|
||||
let comm = CommunicationValue::from_bytes(frame)
|
||||
.map_err(|e| js_error(&format!("parse failed: {}", e)))?;
|
||||
Ok(comm.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_ping_frame_roundtrip() {
|
||||
let bytes = build_ping_frame(42, "test-ping", 1234567890, &[]).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 42);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("test-ping".into())
|
||||
);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Timestamp.to_id(&tm)),
|
||||
&DataValue::UnsignedNumber(1234567890)
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_ping_frame_with_data() {
|
||||
let payload = b"attachment-data";
|
||||
let bytes = build_ping_frame(99, "with-data", 555, payload).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 99);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("with-data".into())
|
||||
);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Timestamp.to_id(&tm)),
|
||||
&DataValue::UnsignedNumber(555)
|
||||
);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Id.to_id(&tm)),
|
||||
&DataValue::Bytes(payload.to_vec())
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_ping_frame_client_id_zero() {
|
||||
let bytes = build_ping_frame(0, "zero-id", 0, &[]).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
assert_eq!(cv.get_sender(), 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_demo_message_roundtrip() {
|
||||
// The demo KEM-encrypts to the host's bundle, so a real host keypair is
|
||||
// required; the client keyring only needs its Ed25519 signing key.
|
||||
let keyring = Keyring::generate();
|
||||
let keyring_bytes = keyring.to_bytes();
|
||||
let host_bundle = Keyring::generate().public_key_bundle().as_bytes();
|
||||
|
||||
let result = build_demo_message(7, &keyring_bytes, &host_bundle);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let bytes = result.unwrap();
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 7);
|
||||
assert_eq!(
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("MTP WASM Demo".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn build_demo_message_invalid_keyring() {
|
||||
let host_bundle = Keyring::generate().public_key_bundle().as_bytes();
|
||||
let result = build_demo_message(1, b"not-a-valid-keyring", &host_bundle);
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
assert!(err.as_string().unwrap().contains("invalid keyring"));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_success() {
|
||||
let resp = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolTrue)
|
||||
.add_typed_default(DataType::ClientNonce, DataValue::UnsignedNumber(999))
|
||||
.add_typed_default(DataType::Id, DataValue::UnsignedNumber(42))
|
||||
.add_typed_default(DataType::Timestamp, DataValue::UnsignedNumber(12345))
|
||||
.to_bytes()
|
||||
.expect("encode failed");
|
||||
|
||||
let result = parse_auth_response(&resp).expect("parse failed");
|
||||
|
||||
let connected = js_sys::Reflect::get(&result, &"connected".into())
|
||||
.ok()
|
||||
.and_then(|v| v.as_bool());
|
||||
assert_eq!(connected, Some(true));
|
||||
|
||||
let id = js_sys::Reflect::get(&result, &"assignedId".into())
|
||||
.ok()
|
||||
.and_then(|v| v.as_f64());
|
||||
assert_eq!(id, Some(42.0));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_rejected() {
|
||||
let resp = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolFalse)
|
||||
.to_bytes()
|
||||
.expect("encode failed");
|
||||
|
||||
let result = parse_auth_response(&resp).expect("parse failed");
|
||||
|
||||
let connected = js_sys::Reflect::get(&result, &"connected".into())
|
||||
.ok()
|
||||
.and_then(|v| v.as_bool());
|
||||
assert_eq!(connected, Some(false));
|
||||
|
||||
// rejected should have no assignedId
|
||||
let has_id = js_sys::Reflect::has(&result, &"assignedId".into()).unwrap_or(false);
|
||||
assert!(!has_id);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_with_signature() {
|
||||
let sig_bytes = vec![0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe];
|
||||
let resp = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolTrue)
|
||||
.add_typed_default(DataType::Signature, DataValue::Bytes(sig_bytes.clone()))
|
||||
.to_bytes()
|
||||
.expect("encode failed");
|
||||
|
||||
let result = parse_auth_response(&resp).expect("parse failed");
|
||||
|
||||
let has_sig = js_sys::Reflect::has(&result, &"signature".into()).unwrap_or(false);
|
||||
assert!(has_sig);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn parse_auth_response_invalid_frame() {
|
||||
let result = parse_auth_response(b"garbage-data");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
44
wasm/src/subscription.rs
Normal file
44
wasm/src/subscription.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
/// Minimal message router used by higher-level SDK subscription code.
|
||||
#[wasm_bindgen]
|
||||
pub struct WasmSubscriptionRouter {
|
||||
handlers: HashMap<String, js_sys::Function>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl WasmSubscriptionRouter {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
handlers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn subscribe(&mut self, message_type: String, callback: js_sys::Function) {
|
||||
self.handlers.insert(message_type, callback);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn unsubscribe(&mut self, message_type: &str) -> bool {
|
||||
self.handlers.remove(message_type).is_some()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn dispatch(&self, message_type: &str, message: JsValue) -> bool {
|
||||
let Some(callback) = self.handlers.get(message_type) else {
|
||||
return false;
|
||||
};
|
||||
let _ = callback.call1(&JsValue::NULL, &message);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WasmSubscriptionRouter {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ use std::rc::Rc;
|
|||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{WebTransport, WebTransportHash, WebTransportOptions};
|
||||
|
||||
use crate::error::js_error;
|
||||
use crate::frame::parse_frame_value;
|
||||
|
||||
const CLOSE_FRAME_LEN: u32 = u32::MAX;
|
||||
|
||||
|
|
@ -40,42 +40,6 @@ enum FrameOutcome {
|
|||
Ended,
|
||||
}
|
||||
|
||||
/*
|
||||
* Debug-log the exact bytes about to be written to the WebTransport stream.
|
||||
*
|
||||
* Wire layout (note the DOUBLE length prefix):
|
||||
* [0..4] outer_len u32 BE - added by send_frame (= inner frame length)
|
||||
* [4..8] inner_len u32 BE - added by CommunicationValue::to_bytes
|
||||
* [8..10] comm_type u16 BE - e.g. Identification
|
||||
* [10] flags u8
|
||||
* [11..] id/sender/receiver/signature/data, gated by `flags`
|
||||
*/
|
||||
fn log_frame_bytes(wire: &[u8]) {
|
||||
let hex: String = wire
|
||||
.iter()
|
||||
.map(|b| format!("{b:02x}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
|
||||
let outer_len = wire
|
||||
.get(0..4)
|
||||
.map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]]));
|
||||
let inner_len = wire
|
||||
.get(4..8)
|
||||
.map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]]));
|
||||
let comm_type = wire.get(8..10).map(|b| u16::from_be_bytes([b[0], b[1]]));
|
||||
let flags = wire.get(10).copied();
|
||||
|
||||
web_sys::console::log_1(
|
||||
&format!(
|
||||
"mtp-wasm send_frame: {len} bytes | outer_len={outer_len:?} inner_len={inner_len:?} \
|
||||
comm_type={comm_type:?} flags={flags:?}\n{hex}",
|
||||
len = wire.len(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* WebTransport client transport.
|
||||
*
|
||||
|
|
@ -90,7 +54,7 @@ fn log_frame_bytes(wire: &[u8]) {
|
|||
*/
|
||||
#[derive(Clone)]
|
||||
pub struct WasmTransport {
|
||||
inner: WebTransport,
|
||||
inner: JsValue,
|
||||
/// Reader over `incoming_unidirectional_streams()` (a singleton stream of streams).
|
||||
streams_reader: Rc<RefCell<Option<JsValue>>>,
|
||||
/// Reader over the host's current uni-directional stream, if one is open.
|
||||
|
|
@ -101,28 +65,48 @@ pub struct WasmTransport {
|
|||
|
||||
impl WasmTransport {
|
||||
pub async fn connect(url: &str, cert_hashes: Option<Vec<String>>) -> Result<Self, JsValue> {
|
||||
let transport = match cert_hashes {
|
||||
Some(hashes) => {
|
||||
let opts = WebTransportOptions::new();
|
||||
let mut wt_hashes = Vec::new();
|
||||
for h in hashes {
|
||||
if let Some((algo, hex_val)) = h.split_once(':') {
|
||||
if let Ok(bytes) = hex::decode(hex_val) {
|
||||
let hash = WebTransportHash::new();
|
||||
hash.set_algorithm(algo);
|
||||
hash.set_value_u8_array(&js_sys::Uint8Array::from(&bytes[..]));
|
||||
wt_hashes.push(hash);
|
||||
}
|
||||
let ctor = js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("WebTransport"))?
|
||||
.dyn_into::<js_sys::Function>()
|
||||
.map_err(|_| js_error("WebTransport not available"))?;
|
||||
let args = js_sys::Array::new();
|
||||
args.push(&JsValue::from_str(url));
|
||||
|
||||
if let Some(hashes) = cert_hashes {
|
||||
let wt_hashes = js_sys::Array::new();
|
||||
for h in hashes {
|
||||
if let Some((algo, hex_val)) = h.split_once(':') {
|
||||
if let Ok(bytes) = hex::decode(hex_val) {
|
||||
let hash = js_sys::Object::new();
|
||||
js_sys::Reflect::set(
|
||||
&hash,
|
||||
&JsValue::from_str("algorithm"),
|
||||
&JsValue::from_str(algo),
|
||||
)?;
|
||||
js_sys::Reflect::set(
|
||||
&hash,
|
||||
&JsValue::from_str("value"),
|
||||
&js_sys::Uint8Array::from(&bytes[..]),
|
||||
)?;
|
||||
wt_hashes.push(&hash);
|
||||
}
|
||||
}
|
||||
if !wt_hashes.is_empty() {
|
||||
opts.set_server_certificate_hashes(&wt_hashes);
|
||||
}
|
||||
WebTransport::new_with_options(url, &opts)?
|
||||
}
|
||||
None => WebTransport::new(url)?,
|
||||
if wt_hashes.length() > 0 {
|
||||
let opts = js_sys::Object::new();
|
||||
js_sys::Reflect::set(
|
||||
&opts,
|
||||
&JsValue::from_str("serverCertificateHashes"),
|
||||
&wt_hashes,
|
||||
)?;
|
||||
args.push(&opts);
|
||||
}
|
||||
};
|
||||
JsFuture::from(transport.ready())
|
||||
|
||||
let transport = js_sys::Reflect::construct(&ctor, &args)?;
|
||||
let ready = js_sys::Reflect::get(&transport, &JsValue::from_str("ready"))?
|
||||
.dyn_into::<js_sys::Promise>()
|
||||
.map_err(|_| js_error("WebTransport.ready is not a Promise"))?;
|
||||
JsFuture::from(ready)
|
||||
.await
|
||||
.map_err(|e| js_error(&format!("WebTransport ready failed: {:?}", e)))?;
|
||||
Ok(Self {
|
||||
|
|
@ -133,12 +117,21 @@ impl WasmTransport {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &WebTransport {
|
||||
pub fn inner(&self) -> &JsValue {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub async fn send_frame(&self, frame: &[u8]) -> Result<(), JsValue> {
|
||||
let stream_promise = self.inner.create_unidirectional_stream();
|
||||
let create_stream = js_sys::Reflect::get(
|
||||
&self.inner,
|
||||
&JsValue::from_str("createUnidirectionalStream"),
|
||||
)?
|
||||
.dyn_into::<js_sys::Function>()
|
||||
.map_err(|_| js_error("createUnidirectionalStream not a function"))?;
|
||||
let stream_promise = create_stream
|
||||
.call0(&self.inner)?
|
||||
.dyn_into::<js_sys::Promise>()
|
||||
.map_err(|_| js_error("createUnidirectionalStream did not return a Promise"))?;
|
||||
let stream = JsFuture::from(stream_promise).await?;
|
||||
|
||||
let writable_or_stream = resolve_stream_writable(&stream)?;
|
||||
|
|
@ -155,8 +148,6 @@ impl WasmTransport {
|
|||
wire.extend_from_slice(&len.to_be_bytes());
|
||||
wire.extend_from_slice(frame);
|
||||
|
||||
log_frame_bytes(&wire);
|
||||
|
||||
let chunk = js_sys::Uint8Array::from(&wire[..]);
|
||||
|
||||
let write_fn = js_sys::Reflect::get(&writer_val, &JsValue::from_str("write"))
|
||||
|
|
@ -185,7 +176,10 @@ impl WasmTransport {
|
|||
if let Some(reader) = self.streams_reader.borrow().clone() {
|
||||
return Ok(reader);
|
||||
}
|
||||
let incoming = self.inner.incoming_unidirectional_streams();
|
||||
let incoming = js_sys::Reflect::get(
|
||||
&self.inner,
|
||||
&JsValue::from_str("incomingUnidirectionalStreams"),
|
||||
)?;
|
||||
let reader = js_sys::Reflect::get(&incoming, &JsValue::from_str("getReader"))
|
||||
.map_err(|_| js_error("missing getReader"))?
|
||||
.dyn_into::<js_sys::Function>()
|
||||
|
|
@ -341,10 +335,15 @@ impl WasmTransport {
|
|||
pub async fn receive_loop(&self, on_message: js_sys::Function, on_error: js_sys::Function) {
|
||||
loop {
|
||||
match self.next_frame().await {
|
||||
Ok(FrameOutcome::Frame(frame)) => {
|
||||
let arr = js_sys::Uint8Array::from(&frame[..]);
|
||||
let _ = on_message.call1(&JsValue::NULL, &arr);
|
||||
}
|
||||
Ok(FrameOutcome::Frame(frame)) => match parse_frame_value(&frame) {
|
||||
Ok(parsed) => {
|
||||
let _ = on_message.call1(&JsValue::NULL, &parsed);
|
||||
}
|
||||
Err(e) => {
|
||||
let message = e.as_string().unwrap_or_else(|| format!("{:?}", e));
|
||||
let _ = on_error.call1(&JsValue::NULL, &JsValue::from_str(&message));
|
||||
}
|
||||
},
|
||||
Ok(FrameOutcome::Closed) | Ok(FrameOutcome::Ended) => break,
|
||||
Err(e) => {
|
||||
let _ = on_error.call1(&JsValue::NULL, &e);
|
||||
|
|
@ -355,7 +354,10 @@ impl WasmTransport {
|
|||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
let info = web_sys::WebTransportCloseInfo::new();
|
||||
let _ = self.inner.close_with_close_info(&info);
|
||||
if let Ok(close) = js_sys::Reflect::get(&self.inner, &JsValue::from_str("close"))
|
||||
.and_then(|value| value.dyn_into::<js_sys::Function>().map_err(Into::into))
|
||||
{
|
||||
let _ = close.call1(&self.inner, &js_sys::Object::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
173
wasm/types/mtp_wasm.d.ts
vendored
Normal file
173
wasm/types/mtp_wasm.d.ts
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
||||
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
}
|
||||
|
||||
export interface DisposableWasmObject {
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
}
|
||||
|
||||
export type StateChangeCallback = (state: ConnectionState) => void;
|
||||
export type MessageCallback = (frame: ParsedFrame) => void;
|
||||
export type ErrorCallback = (error: string) => void;
|
||||
|
||||
export interface Ed25519GenerateResult {
|
||||
signer: WasmEd25519Signer;
|
||||
secretKey: Uint8Array;
|
||||
publicKey: Uint8Array;
|
||||
}
|
||||
|
||||
export interface AuthResponse {
|
||||
connected: boolean;
|
||||
clientNonce?: Uint8Array;
|
||||
assignedId?: number;
|
||||
timestamp?: number;
|
||||
signature?: Uint8Array;
|
||||
}
|
||||
|
||||
export interface ParsedResponse {
|
||||
_id?: number;
|
||||
_type: string;
|
||||
[field: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ParsedFrame {
|
||||
id?: number;
|
||||
type: string;
|
||||
sender?: bigint;
|
||||
receiver?: bigint;
|
||||
data: Record<string, unknown>;
|
||||
raw: Uint8Array;
|
||||
}
|
||||
|
||||
export class ConnectionConfig implements DisposableWasmObject {
|
||||
constructor(url: string);
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
client_id: bigint;
|
||||
server_certificate_hashes: string[];
|
||||
readonly url: string;
|
||||
}
|
||||
|
||||
export enum ConnectionState {
|
||||
Disconnected = 0,
|
||||
Connecting = 1,
|
||||
Connected = 2,
|
||||
Failed = 3,
|
||||
}
|
||||
|
||||
export enum WasmLogHint {
|
||||
Info = 0,
|
||||
Warning = 1,
|
||||
Error = 2,
|
||||
}
|
||||
|
||||
export class WasmChaCha20Poly1305 implements DisposableWasmObject {
|
||||
constructor(key: Uint8Array);
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
decrypt(ciphertext: Uint8Array, aad: Uint8Array): Uint8Array;
|
||||
encrypt(plaintext: Uint8Array, aad: Uint8Array): Uint8Array;
|
||||
}
|
||||
|
||||
export class WasmClient implements DisposableWasmObject {
|
||||
constructor(
|
||||
on_state_change: StateChangeCallback,
|
||||
on_message: MessageCallback,
|
||||
on_error: ErrorCallback,
|
||||
);
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
auth_connect(
|
||||
config: ConnectionConfig,
|
||||
host_public_key_bytes: Uint8Array,
|
||||
keyring_bytes: Uint8Array,
|
||||
client_id: bigint,
|
||||
): Promise<bigint>;
|
||||
auth_register(
|
||||
config: ConnectionConfig,
|
||||
host_public_key_bytes: Uint8Array,
|
||||
keyring_bytes: Uint8Array,
|
||||
): Promise<bigint>;
|
||||
connect(config: ConnectionConfig): Promise<void>;
|
||||
disconnect(): void;
|
||||
request(frame: Uint8Array, response_type?: string | null): Promise<ParsedFrame>;
|
||||
send(frame: Uint8Array): Promise<void>;
|
||||
start_protocol_pings(interval_ms: number, client_id: bigint): void;
|
||||
stop_protocol_pings(): void;
|
||||
subscribe(message_type: string, callback: MessageCallback): number;
|
||||
unsubscribe(id: number): boolean;
|
||||
static is_supported(): boolean;
|
||||
readonly state: ConnectionState;
|
||||
}
|
||||
|
||||
export class WasmEd25519Signer implements DisposableWasmObject {
|
||||
constructor(secret_key: Uint8Array);
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
sign(message: Uint8Array): Uint8Array;
|
||||
verify(message: Uint8Array, signature: Uint8Array): void;
|
||||
}
|
||||
|
||||
export class WasmKeyring implements DisposableWasmObject {
|
||||
private constructor();
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
static from_bytes(bytes: Uint8Array): WasmKeyring;
|
||||
public_key_bundle(): WasmPublicKeyBundle;
|
||||
to_bytes(): Uint8Array;
|
||||
}
|
||||
|
||||
export class WasmPublicKeyBundle implements DisposableWasmObject {
|
||||
private constructor();
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
static from_bytes(bytes: Uint8Array): WasmPublicKeyBundle;
|
||||
to_bytes(): Uint8Array;
|
||||
readonly kem_public_key: Uint8Array;
|
||||
readonly sig_cl_public_key: Uint8Array;
|
||||
readonly sig_pq_public_key: Uint8Array;
|
||||
}
|
||||
|
||||
export class WasmSubscriptionRouter implements DisposableWasmObject {
|
||||
constructor();
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
dispatch(message_type: string, message: unknown): boolean;
|
||||
subscribe(message_type: string, callback: (message: unknown) => void): void;
|
||||
unsubscribe(message_type: string): boolean;
|
||||
}
|
||||
|
||||
export function build_ping_frame(
|
||||
client_id: bigint,
|
||||
description: string,
|
||||
timestamp: bigint,
|
||||
data: Uint8Array,
|
||||
): Uint8Array;
|
||||
|
||||
export function build_frame(message_type: string, data: Record<string, unknown>, options?: {
|
||||
id?: number;
|
||||
sender?: bigint | number;
|
||||
receiver?: bigint | number;
|
||||
}): Uint8Array;
|
||||
|
||||
export function ed25519_generate(): Ed25519GenerateResult;
|
||||
export function ed25519_verify(public_key: Uint8Array, message: Uint8Array, signature: Uint8Array): void;
|
||||
export function format_frame(frame: Uint8Array): string;
|
||||
export function keyring_from_ed25519(secret_key: Uint8Array, public_key: Uint8Array): Uint8Array;
|
||||
export function main(): void;
|
||||
export function parse_auth_response(response: Uint8Array): AuthResponse;
|
||||
export function parse_frame(frame: Uint8Array): ParsedFrame;
|
||||
export function wasm_derive_encryption_key(ikm: Uint8Array, salt: Uint8Array, context: Uint8Array): Uint8Array;
|
||||
export function wasm_hkdf_expand(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, len: number): Uint8Array;
|
||||
export function wasm_sha256(data: Uint8Array): Uint8Array;
|
||||
export function wasm_sha256_double(data: Uint8Array): Uint8Array;
|
||||
|
||||
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
|
||||
|
||||
export default function init(
|
||||
module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>,
|
||||
): Promise<InitOutput>;
|
||||
Loading…
Reference in a new issue