# `nupp.runtime.hotreload`
The development-only half of Nupp hot reload.
Generated watch modules keep their public function values stable and dispatch
through one array of implementation functions. A patch is loaded while `stage`
owns the registry: `define` validates every candidate and joins its authored
upvalues to the cells held by the live implementation. Nothing in a live slot
changes until `commit` has a complete candidate vector.
The state is process-wide so a host and generated modules may require this
module through different loader paths without splitting the registry. Ordinary
generated programs never require it.
See [Hot reload](../../../../learn/projects/hot-reload/index.html) for what a watch build does with this, and
[NEP 6: Hot reload](../../../../reference/neps/0006-hot-reload/index.html) for why a patch is refused rather than applied
partially.
## Types
### `Candidate` _type_
```nupp
type hotreload.Candidate = {
--- The registration record the slot belongs to, which is private to this module.
module: any,
index: integer,
implementation: hotreload.Implementation,
--- The implementation's own upvalue numbering. Committing publishes it into the
--- definition, because the next patch joins against this implementation and Lua
--- numbers upvalues by first reference, which a changed body reorders.
captures: hotreload.Captures
}
```
One slot a prepared patch will fill, and the implementation to fill it with.
### `Captures` _type_
```nupp
type hotreload.Captures = {
byName: {[string]: integer},
order: {string}
}
```
Where a function's captured names sit in its own upvalue list.
`order` is sorted, so two implementations of the same function can be compared name
by name without either side's upvalue numbering mattering.
### `Definition` _type_
```nupp
type hotreload.Definition = {
id: string,
signature: string,
selfName: string?,
captures: hotreload.Captures
}
```
What a live slot is known by, beside the implementation filling it.
A candidate has to match all four before it may be joined: a changed identity,
signature, recursive binding or capture set is a change the running process cannot
take, and each is refused by name.
### `Implementation` _type_
```nupp
type hotreload.Implementation = function(...: any): any
```
One function body a slot can hold.
Generated code writes these and generated code calls them, so no two slots need
agree about what a call takes or answers. What this module does with one is join
its upvalues and store it, neither of which reads a parameter.
### `Slots` _type_
```nupp
type hotreload.Slots = {hotreload.Implementation}
```
The implementation array a generated watch module dispatches through.
Naming it is what keeps the three functions that pass one around agreeing about
what they are passing, and what lets `bySlots` be keyed by it.
### `Staging` _type_
```nupp
type hotreload.Staging = {
baseGeneration: integer,
candidates: {hotreload.Candidate},
--- Module name and slot index, so one patch cannot fill a slot twice.
byKey: {[string]: hotreload.Candidate},
policies: {[any]: {[string]: boolean}}
}
```
What `stage` accumulates while the patch chunk runs.
It is the registry `define` writes into: private to the staging call, discarded
whether the chunk succeeds or raises, and turned into a transaction only once the
whole chunk has run without one.
### `Transaction` _type_
```nupp
type hotreload.Transaction = {
baseGeneration: integer,
generation: integer,
serial: integer,
candidates: {hotreload.Candidate},
--- Dynamic-custody policies per registration record, keyed the way `candidates`
--- names them.
policies: {[any]: {[string]: boolean}},
committed: boolean
}
```
A validated patch that has changed nothing yet.
Everything fallible has already happened by the time one of these exists, which is
what lets `commit` publish the whole vector with no failure point in the middle.
`serial` distinguishes two transactions prepared from the same generation.
## Functions
### `hotreload.abort` _function_
```nupp
function hotreload.abort(name: string): nil
```
Discards an incomplete registration after its initial chunk fails.
A module that already sealed is left alone, so this is safe to call from an
error path that cannot tell how far the chunk got.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | the watch module's name |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
### `hotreload.commit` _function_
```nupp
function hotreload.commit(prepared: hotreload.Transaction): integer?, string?
```
Atomically publishes a prepared candidate vector.
Lua cannot run authored code between the direct array writes below, so no
dispatch can observe half a patch.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `prepared` | `hotreload.Transaction` | what `stage` answered |
#### Returns
| Type | Description |
| --- | --- |
| `integer?` | the committed generation, or nil when this transaction is stale |
| `string?` | why it is stale, when it is |
### `hotreload.define` _function_
```nupp
function hotreload.define(slots: hotreload.Slots, index: integer, id: string, signature: string, selfName: string?, implementation: hotreload.Implementation): nil
```
Registers an initial implementation, or one candidate in the active stage.
A candidate must match the live definition's identity, signature, recursive
binding and capture set. Each of those is a change the running process cannot
take, and each is refused by name.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `slots` | `hotreload.Slots` | the module's slot array |
| `index` | `integer` | which slot this fills |
| `id` | `string` | the declaration identity the slot was registered under |
| `signature` | `string` | the declaration's signature |
| `selfName` | `string?` | the recursive binding's name, when the function has one |
| `implementation` | `hotreload.Implementation` | the function body to install |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
- when identity, signature, or captured lexical cells are incompatible
### `hotreload.generation` _function_
```nupp
function hotreload.generation(): integer
```
Answers the currently published implementation generation.
#### Returns
| Type | Description |
| --- | --- |
| `integer` | |
### `hotreload.loaded` _function_
```nupp
function hotreload.loaded(name: string): boolean
```
Answers whether a module has completed its initial watch execution.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | |
#### Returns
| Type | Description |
| --- | --- |
| `boolean` | |
### `hotreload.module` _function_
```nupp
function hotreload.module(name: string): hotreload.Slots
```
Answers the direct implementation array for one loaded watch module.
Outside staging, naming a module that has not executed registers it, because
that is a module about to run its own initial chunk. Inside staging it is a
patch naming something that was never loaded, which is refused.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | the watch module's name |
#### Returns
| Type | Description |
| --- | --- |
| `hotreload.Slots` | the slot array that module dispatches through |
#### Raises
- when a patch names a module that has not executed yet
### `hotreload.policy` _function_
```nupp
function hotreload.policy(slots: hotreload.Slots, key: string): nil
```
Records one managed-cell cleanup policy a generated module uses.
During staging the candidate set stays private until every live managed cell
admits the change, so a policy that still owns a payload cannot be changed out
from under it.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `slots` | `hotreload.Slots` | the module's slot array |
| `key` | `string` | the stable policy key |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
### `hotreload.resetForTesting` _function_
```nupp
function hotreload.resetForTesting(): nil
```
Clears process state. Intended for compiler/runtime tests only.
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
### `hotreload.seal` _function_
```nupp
function hotreload.seal(name: string): nil
```
Marks a module's initial registration complete after its chunk returns.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | the watch module's name |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
- when the module is unknown or has already been sealed
### `hotreload.stage` _function_
```nupp
function hotreload.stage(source: string, baseGeneration: integer): hotreload.Transaction?, string?
```
Loads and validates a patch without changing any live implementation.
Everything fallible happens here, so a transaction that comes back can be
committed without a failure point in the middle of the vector.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `source` | `string` | the patch chunk |
| `baseGeneration` | `integer` | the generation the patch was built against |
#### Returns
| Type | Description |
| --- | --- |
| `hotreload.Transaction?` | the prepared transaction, or nil when it was rejected |
| `string?` | why it was rejected, when it was |