js
! The host, reached generally. Web only. ! ! This is the floor below the typed world, and importing it should be visible ! in a file's first three lines. Everything here is untyped, every call can ! fail, and nothing here knows what a DOM is. std/dom is written over it, in ! ordinary Kite, and so is anything else a program needs that the standard ! library never covered. ! ! About twenty primitives, and they are general enough to reach anything the ! host has. std/dom is written over them, and so is whatever a program ! needs that nobody wrapped — in Kite, without touching the compiler, and with ! the generated glue staying a fixed size however much of the platform a ! program uses. ! ! The name is looked up when the program runs rather than fixed when it ! compiles. That costs a little speed, and it means a mistyped name is found ! at run time — which is why every call here answers with an error rather than ! throwing. ! ! ``kite ignore ! use std/js ! ! fn title() -> (str, error) { ! let document = js.global("document") ! let (value, err) = js.get(document, "title") ! check err ! return js.as_str(value) ! } ! ` ! ! **Keep JsValue out of your own public interface.** Wrap it in a struct ! whose field is not pub — outside the declaring module such a struct can be ! held and passed but not read or built ([spec §4.2]), which is the whole of ! how the untyped layer is stopped from spreading. std/dom` is the worked ! example.
SAFE_INTEGERglobalnothinggetsetatlengthcall0call1call2call3call4new0new1new2new3sameis_nothingkind_ofinstance_ofof_strof_numof_boolof_intas_stras_numas_boolas_intsettlestr_ornum_orbool_or
SAFE_INTEGER
pub fn SAFE_INTEGER() -> int
The largest whole number a JavaScript number holds exactly.
Numbers cross as f64, because that is what a JavaScript number is and an int is an i64 — every crossing would otherwise allocate a BigInt. The cost is this limit, and it is checked here rather than discovered as a value that came back different from the one that went out.
global
pub fn global(name: str) -> JsValue
A property of the global object: document, window, Math, a constructor. The root everything else is reached from.
Not fallible: reading a property that is not there is not an error in JavaScript, it is undefined. Ask [is_nothing] if that matters.
nothing
pub fn nothing() -> JsValue
null, for passing to something that wants one.
get
pub fn get(target: JsValue, name: str) -> (JsValue, error)
One property.
set
pub fn set(target: JsValue, name: str, value: JsValue) -> error
Write one property.
Returns only an error, because there is no value to return and a (unit, error) pair would make every call site destructure something meaningless.
at
pub fn at(target: JsValue, index: int) -> (JsValue, error)
One element of an array-like thing.
length
pub fn length(target: JsValue) -> (int, error)
How many elements an array-like thing has.
call0
pub fn call0(target: JsValue, name: str) -> (JsValue, error)
call1
pub fn call1(target: JsValue, name: str, a: JsValue) -> (JsValue, error)
call2
pub fn call2(target: JsValue, name: str, a: JsValue, b: JsValue) -> (JsValue, error)
call3
pub fn call3(
target: JsValue,
name: str,
a: JsValue,
b: JsValue,
c: JsValue,
) -> (JsValue, error)
call4
pub fn call4(
target: JsValue,
name: str,
a: JsValue,
b: JsValue,
c: JsValue,
d: JsValue,
) -> (JsValue, error)
new0
pub fn new0(constructor: str) -> (JsValue, error)
new Thing(…), by the constructor's name on the global object.
new1
pub fn new1(constructor: str, a: JsValue) -> (JsValue, error)
new2
pub fn new2(constructor: str, a: JsValue, b: JsValue) -> (JsValue, error)
new3
pub fn new3(constructor: str, a: JsValue, b: JsValue, c: JsValue) -> (JsValue, error)
same
pub fn same(a: JsValue, b: JsValue) -> bool
Whether two references are the same object — the host's ===.
This is what == would have been, and cannot be: an externref is outside Wasm's eq hierarchy, so there is no comparison to lower it to. Writing == on a JsValue is a compile error that points here.
is_nothing
pub fn is_nothing(v: JsValue) -> bool
Whether the value is null or undefined.
One question rather than two. The difference between them is a JavaScript distinction with no Kite meaning, and a program that acted on it would be acting on which of two ways a property failed to be there.
kind_of
pub fn kind_of(v: JsValue) -> str
The host's own answer to what this is: "string", "number", "object", "function", "undefined".
instance_of
pub fn instance_of(v: JsValue, constructor: str) -> (bool, error)
Whether the value came from this constructor.
Needed the moment an event target has to become something typed: a target may be an element, a text node or the window, and only one of those is worth wrapping.
of_str
pub fn of_str(value: str) -> JsValue
of_num
pub fn of_num(value: float) -> JsValue
of_bool
pub fn of_bool(value: bool) -> JsValue
of_int
pub fn of_int(value: int) -> (JsValue, error)
An int as a JavaScript number, refusing rather than rounding.
The check is on this side and the conversion is on the other. Doing the arithmetic here would mean an int becoming a float in Kite, which is a lossy cast the compiler rightly warns about — and the loss it warns about is exactly the one this refuses.
as_str
pub fn as_str(v: JsValue) -> (str, error)
The value as text — and an error when it is not text.
This is where JavaScript's commonest bug is removed. Reading a property that is not there yields undefined, and undefined silently becoming 0 or "" is a mistake that surfaces somewhere else entirely. Here it is a value the taint analysis will not let the caller use unchecked.
as_num
pub fn as_num(v: JsValue) -> (float, error)
as_bool
pub fn as_bool(v: JsValue) -> (bool, error)
as_int
pub fn as_int(v: JsValue) -> (int, error)
A whole number, or the reason it is not one.
"Is this number a whole one that survives the crossing" is JavaScript's own question — Number.isSafeInteger — and it is asked where the number lives rather than reconstructed here out of a truncation and a float comparison. The value only becomes an int once the answer is yes.
settle
pub fn settle(promise: JsValue, done: fn(JsValue), failed: fn(str)) -> error
Handle both halves of a promise.
Both callbacks are required, and that is the whole design. then with one callback compiles, runs, and throws a rejection away — which is exactly the failure this language spends its error design preventing everywhere else. Passing a failed that ignores its argument is still a decision somebody wrote down; a missing one is a decision nobody made.
The failure arrives as text rather than as an error, and that is deliberate. An error in Kite is either nil or a failure, so a handler taking one would have to test it for nil at every call site — for a callback that only ever runs when something went wrong. A parameter that can never be nil should not be typed as though it can. Anything wanting an error to propagate writes errors.new(why), which is all this layer would have done.
let (p, err) = js.call1(js.global("globalThis"), "fetch", js.of_str(url))
check err
check js.settle(p,
|response: JsValue| { show(response) },
|why: str| { io.error("could not load: \(why)") },
)
str_or
pub fn str_or(target: JsValue, name: str, fallback: str) -> str
A string property, or fallback when it is missing or is not a string.
num_or
pub fn num_or(target: JsValue, name: str, fallback: float) -> float
A numeric property, or fallback.
bool_or
pub fn bool_or(target: JsValue, name: str, fallback: bool) -> bool
A boolean property, or fallback.