# `nupp.host`
`nupp.host` asks whatever is running the program for something and waits for
the answer: the page's JavaScript in a browser, or the C or Rust application
that embeds the runtime natively. Reach for it when a library needs a facility
the host has and Nupp does not, such as a canvas, a clipboard or an engine's
asset loader, and should not care which host it is running under.
A request names a *kind*, a dotted name the host answers, and carries a flat
list of values. Declare the kind's signature once as a function type and bind
it; callers then get named arguments and a checked call:
```nupp:fragment
local host = nupp.host
--- Returns the host's id for the decoded image, and its width and height.
local type DecodeImage = function(url: string, format: string?): (integer, integer, integer)
local type ReleaseImage = function(id: integer): nil
local releaseImage = host.bindPost("tecs.image.release")
local decodeImage = host.bind("tecs.image.decode",
late = function(id: any, ...: any): nil releaseImage(id) end)
local id, width, height = decodeImage(url = "sprites.png")
```
A call suspends through `nupp.suspension` until the host answers, so it
composes with [task scopes](../../../learn/runtime/concurrency/task-scopes/index.html),
deadlines and cancellation. A host failure is raised in the caller with the
host's message. `bind` allocates its closure once; a call through it passes its
arguments straight through rather than gathering them into a table.
## Values
Arguments and results are each a count of flat values: `nil`, booleans,
numbers, strings and byte spans. Every position is kept, so interior and
trailing `nil`s arrive as written. A number must be finite and a string must be
UTF-8 text; arbitrary bytes are a `nupp.mem.span` of bytes, never a string
holding binary. Tables and functions are refused, naming the argument's
position. Structured data is encoded into bytes by the library that owns it.
A byte span is a borrow, so it crosses through a bound kind whose signature
says so; `call` and `post` take their arguments as `any`, which a borrow cannot
cross, and carry the other values only:
```nupp:fragment
local host = nupp.host
local span = nupp.mem.span
local type Digest = function(borrows bytes: span.ByteSpan): (integer, span.ByteSpan)
local digest = host.bind("app.digest")
const text = "hello"
local length, echoed = digest(bytes = span.fromString(text))
```
A byte result arrives as a byte span the caller owns. A string or byte value
has a size limit: a string at most 64 KiB, bytes sent to the host at most
64 MiB, and bytes answered at most 8 MiB. A host names its own resources with
integers it assigns.
## Results nobody is waiting for
A call cancelled after it reached the host may still be answered. When the
results name a host resource, declare `late`: it receives those results, runs
where no task is waiting, and must not suspend, so it releases through `post`,
which queues a request without waiting for it. A kind without `late` drops a
late answer.
`post` and `bindPost` never suspend. The host runs the request like any call;
its answer is discarded and its failure reported rather than raised.
## Streams
A stream carries messages one way, without answers. `send` appends one to a
kind's outbound stream, which ships with the frame it was sent in; `bindSend`
binds a kind to a typed function as `bind` does, which is how bytes are sent.
`route` delivers a kind's inbound messages as `nupp.events` on a bus the
program owns, at an address it picks, with each message's values filling the
event's fields in declaration order:
```nupp:fragment
local host = nupp.host
local events = nupp.events
@derive(events.Event)
local struct PointerMove
x: number
y: number
end
const WINDOW = 1
local input: events.MessageBus = events.newMessageBus()
input:observe(WINDOW, PointerMove, |move| -> print(move.x, move.y), "cursor")
local route = host.route("app.pointer.move", PointerMove, input, WINDOW, "dropOldest", 64)
```
Messages arrive at a turn boundary, never in the middle of the code that runs
between them, and an event nothing observes is never built. A stream that
fills applies its policy: `block` suspends the sender until the frame ships,
`latest` keeps only the newest message, and `dropOldest` drops the oldest past
the limit and counts it. An inbound message carries scalars and strings, which
is what an event's fields hold; bytes travel on a call.
Natively a send reaches the application's handler at once, so nothing waits
to ship and a policy never applies.
## Kinds
A kind is two or more dot-separated segments of letters, digits, `_` and `-`,
such as `tecs.image.decode`. Names under `nupp.` are reserved for the runtime.
::: seealso
- [Suspension](../../../learn/runtime/concurrency/suspension/index.html) for what a wait
does under a handler
- [Embedding Nupp](../../../learn/projects/embedding/index.html) for answering requests from C
:::
## Submodules
| Module | Description |
| --- | --- |
| `nupp.host.pump` | nupp.host.pump runs one operation at a time for an application that embeds the runtime and owns its own loop. |
| `nupp.host.spi` | The interface a nupp.host implementation provides. |
## Types
### `Late` _type_
```nupp
type Late = function(...: any): nil
```
Called with the results of a call whose caller stopped waiting.
### `Policy` _type_
```nupp
type Policy = "block" | "latest" | "dropOldest"
```
What a full stream does with one more message.
### `Route` _record_
```nupp
record host.Route ...
```
An inbound stream's delivery to a bus; close it to stop.
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`stop`](#nupp.host.Route.stop) | method | |
| [`droppedCount`](#nupp.host.Route.droppedCount) | method | |
| [`close`](#nupp.host.Route.close) | method | Stops delivering; messages already on their way are discarded. |
| [`dropped`](#nupp.host.Route.dropped) | method | How many inbound messages this route has dropped under its policy. |
#### `stop` _method_
```nupp
stop: function(): nil
```
`@private`
##### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### `droppedCount` _method_
```nupp
droppedCount: function(): integer
```
`@private`
##### Returns
| Type | Description |
| --- | --- |
| `integer` | |
#### `close` _method_
```nupp
close: function close(self: host.Route): nil
```
Stops delivering; messages already on their way are discarded.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `host.Route` | |
##### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### `dropped` _method_
```nupp
dropped: function dropped(self: host.Route): integer
```
How many inbound messages this route has dropped under its policy.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `host.Route` | |
##### Returns
| Type | Description |
| --- | --- |
| `integer` | |
## Functions
### `host.attached` _function_
```nupp
function host.attached(): boolean
```
Reports whether a host is attached to answer requests.
Natively that means an embedding application; a program run on its own has
no host. In a browser the page is always attached, though it may answer a
kind it does not know with a failure.
#### Returns
| Type | Description |
| --- | --- |
| `boolean` | |
### `host.bind` _function_
```nupp
function host.bind(kind: string, late: contracts.Late?): F
```
Binds `kind` to a function of type `F` that calls it.
`F` supplies the parameter names and result types callers see, so a bound
kind takes named arguments and checks them. Nothing checks that the host's
answer matches `F`'s results.
#### Type parameters
| Name | Description |
| --- | --- |
| `F` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | the dotted name the host answers |
| `late` | `contracts.Late?` | receives the results of a call answered after its caller was cancelled; it must not suspend |
#### Returns
| Type | Description |
| --- | --- |
| `F` | a function calling `kind` with its arguments |
### `host.bindPost` _function_
```nupp
function host.bindPost(kind: string): F
```
Binds `kind` to a function of type `F` that posts it.
#### Type parameters
| Name | Description |
| --- | --- |
| `F` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | the dotted name the host answers |
#### Returns
| Type | Description |
| --- | --- |
| `F` | a function posting `kind` with its arguments |
### `host.bindSend` _function_
```nupp
function host.bindSend(kind: string, policy: contracts.Policy?, limit: integer?): F
```
Binds `kind`'s outbound stream to a function of type `F` that sends on it.
#### Type parameters
| Name | Description |
| --- | --- |
| `F` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | the dotted name the host answers |
| `policy` | `contracts.Policy?` | what a full stream does with one more message, `block` unless named |
| `limit` | `integer?` | how many unshipped messages make the stream full, 1024 unless named |
#### Returns
| Type | Description |
| --- | --- |
| `F` | a function sending on `kind` with its arguments |
### `host.call` _function_
```nupp
function host.call(kind: string, ...: any): ...any
```
Sends a request of `kind` and waits for the host to answer it.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | the dotted name the host answers |
| `...` | `any` | |
#### Returns
| Type | Description |
| --- | --- |
| `...any` | the host's results, each position kept |
#### Raises
| Type | Condition |
| --- | --- |
| `string` | when a value cannot cross, no host answers the kind, or the host fails the request |
### `host.post` _function_
```nupp
function host.post(kind: string, ...: any): nil
```
Queues a request of `kind` and returns without waiting for its answer.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | the dotted name the host answers |
| `...` | `any` | |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
| Type | Condition |
| --- | --- |
| `string` | when a value cannot cross or too many posts are pending |
### `host.route` _function_
```nupp
function host.route(kind: string, event: Type, bus: events.MessageBus, address: integer, policy: contracts.Policy?, limit: integer?): host.Route
```
Delivers `kind`'s inbound messages to `bus` as `event`s at `address`.
Addresses are integers, the ids a host gives its windows, pads and devices.
#### Type parameters
| Name | Description |
| --- | --- |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | the dotted name the host pushes |
| `event` | `Type\` | the event each message fills, its fields in declaration order |
| `bus` | `events.MessageBus\` | |
| `address` | `integer` | |
| `policy` | `contracts.Policy?` | `latest` or `dropOldest`; `dropOldest` unless named |
| `limit` | `integer?` | how many messages one turn delivers, 1024 unless named |
#### Returns
| Type | Description |
| --- | --- |
| `host.Route` | the route, which stops when closed |
#### Raises
| Type | Condition |
| --- | --- |
| `string` | when `policy` is `block`, which an inbound stream cannot do |
### `host.send` _function_
```nupp
function host.send(kind: string, ...: any): nil
```
Sends a message on `kind`'s outbound stream.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | the dotted name the host answers |
| `...` | `any` | |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
| Type | Condition |
| --- | --- |
| `string` | when a value cannot cross |