///|
/// Convert BiDi remote value to JS literal for function arguments
fn bidi_shared_node_lookup_js(shared_id : String) -> String {
"((globalThis.__bidiSharedNodeStore && globalThis.__bidiSharedNodeStore.get(\"" +
escape_js_string(shared_id) +
"\")) ?? undefined)"
}
///|
fn bidi_handle_lookup_js(handle_id : String) -> String {
"((globalThis.__bidiHandleStore && globalThis.__bidiHandleStore.get(\"" +
escape_js_string(handle_id) +
"\")) ?? undefined)"
}
///|
fn bidi_value_to_js_literal(value : Json) -> String {
match value {
Object(map) =>
match map.get("sharedId") {
Some(String(shared_id)) => bidi_shared_node_lookup_js(shared_id)
_ =>
match map.get("handle") {
Some(String(handle_id)) => bidi_handle_lookup_js(handle_id)
_ =>
match map.get("type") {
Some(String("undefined")) => "undefined"
Some(String("null")) => "null"
Some(String("boolean")) =>
match map.get("value") {
Some(True) => "true"
Some(False) => "false"
_ => "false"
}
Some(String("number")) =>
match map.get("value") {
Some(Number(n, ..)) => n.to_string()
Some(String(s)) => s // NaN, Infinity, etc.
_ => "0"
}
Some(String("string")) =>
match map.get("value") {
Some(String(s)) => "\"" + escape_js_string(s) + "\""
_ => "\"\""
}
Some(String("bigint")) =>
match map.get("value") {
Some(String(s)) => s + "n"
_ => "0n"
}
Some(String("channel")) =>
match map.get("value") {
Some(Object(channel_map)) => {
let channel_name = match channel_map.get("channel") {
Some(String(s)) => s
_ => "channel"
}
let ownership = match channel_map.get("ownership") {
Some(String(s)) => s
_ => "none"
}
let max_object_depth = match
channel_map.get("serializationOptions") {
Some(Object(ser_opts)) => {
let normalized_ser_opts = canonicalize_serialization_options_map(
ser_opts,
)
match normalized_ser_opts.get("maxObjectDepth") {
Some(Number(n, ..)) => n.to_string()
_ => "-1"
}
}
_ => "-1"
}
"((...__args) => {" +
"if (!globalThis.__bidiChannelMessages) globalThis.__bidiChannelMessages = [];" +
"globalThis.__bidiChannelMessages.push({" +
"channel: \"" +
escape_js_string(channel_name) +
"\"," +
"data: (__args.length > 0 ? __args[0] : undefined)," +
"ownership: \"" +
escape_js_string(ownership) +
"\"," +
"maxObjectDepth: " +
max_object_depth +
"});" +
"})"
}
_ => "(() => {})"
}
Some(String("date")) =>
match map.get("value") {
Some(String(s)) =>
"new Date(\"" + escape_js_string(s) + "\")"
_ => "new Date(0)"
}
Some(String("regexp")) =>
match map.get("value") {
Some(Object(regexp_map)) => {
let pattern = match regexp_map.get("pattern") {
Some(String(s)) => "\"" + escape_js_string(s) + "\""
_ => "\"\""
}
let flags = match regexp_map.get("flags") {
Some(String(s)) => "\"" + escape_js_string(s) + "\""
_ => "\"\""
}
"new RegExp(" + pattern + ", " + flags + ")"
}
_ => "new RegExp(\"\", \"\")"
}
Some(String("array")) =>
match map.get("value") {
Some(Array(items)) => {
let parts : Array[String] = []
for item in items {
parts.push(bidi_value_to_js_literal(item))
}
"[" + parts.join(", ") + "]"
}
_ => "[]"
}
Some(String("map")) =>
match map.get("value") {
Some(Array(entries)) => {
let parts : Array[String] = []
for entry in entries {
match entry {
Array(kv) if kv.length() >= 2 => {
let key = bidi_value_to_js_literal(kv[0])
let val = bidi_value_to_js_literal(kv[1])
parts.push("[" + key + ", " + val + "]")
}
_ => ()
}
}
"new Map([" + parts.join(", ") + "])"
}
_ => "new Map()"
}
Some(String("set")) =>
match map.get("value") {
Some(Array(items)) => {
let parts : Array[String] = []
for item in items {
parts.push(bidi_value_to_js_literal(item))
}
"new Set([" + parts.join(", ") + "])"
}
_ => "new Set()"
}
Some(String("object")) =>
match map.get("value") {
Some(Array(entries)) => {
let parts : Array[String] = []
for entry in entries {
match entry {
Array(kv) if kv.length() >= 2 => {
let key = match kv[0] {
String(s) => "\"" + escape_js_string(s) + "\""
_ => bidi_value_to_js_literal(kv[0])
}
let val = bidi_value_to_js_literal(kv[1])
parts.push(key + ": " + val)
}
_ => ()
}
}
"{" + parts.join(", ") + "}"
}
_ => "{}"
}
_ => "undefined"
}
}
}
String(s) => "\"" + escape_js_string(s) + "\""
Number(n, ..) => n.to_string()
True => "true"
False => "false"
Null => "null"
_ => "undefined"
}
}
///|
/// Escape special characters in JS string
fn escape_js_string(s : String) -> String {
let buf = StringBuilder::new()
for c in s.iter() {
match c {
'\\' => buf.write_string("\\\\")
'"' => buf.write_string("\\\"")
'\n' => buf.write_string("\\n")
'\r' => buf.write_string("\\r")
'\t' => buf.write_string("\\t")
_ => buf.write_char(c)
}
}
buf.to_string()
}