# Standard library v0

- **Status:** current on 2026-11-06, for version 0. The table of methods in `crates/surf-check/src/infer.rs` is the source of this page.
- **Related documents:** [language spec](https://surface.solenovis.com/docs/language.md) (sections 4, 6 and 8), [getting started](https://surface.solenovis.com/docs/getting-started.md), [limits of version 0](https://surface.solenovis.com/docs/limits.md)

The standard library of version 0 is small: the methods of the built-in types, `Int.parse` and `Float.parse`, the built-in names, and the operators. A file uses them without an import. Each method gives the same result on every target. `tests/compare.sh` compares the results of the native target, the interpreter and the web target.

A method that changes its value needs a place, for example a `state` or a `let` variable (spec 5.2). A call of such a method is a statement of its own: `todos.push(t)` is valid, but `const n = todos.push(t)` is a compile error.

---

## 1. Int and Float

`Int` is an integer from −(2^53 − 1) to 2^53 − 1 on all targets. `Float` is an IEEE 754 binary64 number.

| Method | Result | Rule |
|---|---|---|
| `i.toFloat()` | `Float` | The same value as a `Float` |
| `f.toInt()` | `Int` | Removes the fraction: `2.7` gives `2`, `-2.7` gives `-2` |
| `f.round()` | `Int` | The nearest integer. A value halfway between two integers goes away from zero: `2.5` gives `3`, `-2.5` gives `-3` |
| `f.floor()` | `Int` | The nearest integer that is not more than `f` |
| `f.ceil()` | `Int` | The nearest integer that is not less than `f` |

Read a number from a `String`:

| Function | Result | Rule |
|---|---|---|
| `Int.parse(s)` | `Int?` | An optional `+` or `-`, then the digits `0` to `9`: `Int.parse("-042")` is `-42`. Other text, and a value outside the range, give `none` |
| `Float.parse(s)` | `Float?` | An optional sign, digits with an optional `.` and fraction, and an optional exponent: `Float.parse("-.5e2")` is `-50.0`. Other text, and a value that is too large, give `none` |

- Both functions accept no white space: `Int.parse(" 4")` is `none`. Use `Int.parse(draft.trim())` for the text of an `Input`.
- The four conversions from `Float` give the runtime error "the Float is NaN or outside the Int range" for NaN, and for a result outside the `Int` range.
- An `Int` result outside the range is the runtime error "the result is outside the integer range".
- `a / b` with two `Int` values removes the fraction. `a % b` has the sign of `a`. Division by zero is a runtime error for `Int`, and gives infinity or NaN for `Float`.
- An operation cannot mix `Int` and `Float`. Write `n.toFloat() * 1.5`.
- `"{x}"` writes a `Float` with the shortest text that gives the same value again: `0.1 + 0.2` gives `0.30000000000000004`, and `2.0` gives `2`.

Math:

| Method | Result | Rule |
|---|---|---|
| `n.abs()`, `f.abs()` | `Int`, `Float` | The value without its sign. A negative literal needs parentheses: `(-3).abs()` |
| `a.min(b)`, `a.max(b)` | the type of `a` | The smaller or the larger value. For `Float`, NaN when a value is NaN |
| `x.clamp(lo, hi)` | the type of `x` | `lo.max(x.min(hi))`: `x` kept between `lo` and `hi`. When `lo` is above `hi`, the result is `lo` |
| `f.sqrt()`, `f.sin()`, `f.cos()`, `f.tan()`, `f.exp()`, `f.log()` | `Float` | `log` is the natural logarithm. The angles are in radians |
| `f.pow(e)`, `y.atan2(x)` | `Float` | `f` to the power `e`, and the angle of the point (`x`, `y`) |
| `Float.pi` | `Float` | π |
| `f.format(decimals)` | `String` | `f` with 0 to 15 digits after the point. The value times 10 to the power of `decimals` rounds half away from zero, as `round` does: `0.125.format(2)` is `"0.13"`, `1.005.format(2)` is `"1.00"`. A value that is too large at that scale, NaN and the infinities give the text of `"{f}"` |

- `sqrt`, `sin`, `cos`, `tan`, `exp`, `log`, `pow` and `atan2` use the math library of the target. The results can differ in the last bit between targets, so show them with `format`.
- `pow` follows IEEE 754 on each target: `1.0.pow(e)` is 1 for each `e`, also NaN, and `(-1.0).pow(e)` is 1 for an infinite `e`.

---

## 2. String

A `String` is a sequence of Unicode scalar values. `count`, `slice` and `chars` count scalar values on all targets, not bytes or UTF-16 units. A string has no index operator.

| Method | Result | Rule |
|---|---|---|
| `s.count()` | `Int` | The number of scalar values: `"aé😀".count()` is `3` |
| `s.isEmpty()` | `Bool` | `true` for `""` |
| `s.contains(t)` | `Bool` | `true` when `t` is in `s` |
| `s.startsWith(t)`, `s.endsWith(t)` | `Bool` | |
| `s.slice(from, to)` | `String` | The scalar values from `from` to `to`, without `to`. The method keeps each bound between 0 and `s.count()`, and `to` is at least `from`: `"abc".slice(-1, 9)` is `"abc"`, `"abc".slice(2, 1)` is `""` |
| `s.chars()` | `List<String>` | One string for each scalar value |
| `s.split(sep)` | `List<String>` | The parts between the separators: `"a,b,".split(",")` is `["a", "b", ""]`. An empty separator gives the same list as `chars()` |
| `s.trim()` | `String` | Without the white space at the start and at the end. White space is each character with the property `White_Space` of Unicode, for example U+0085 and U+3000, and not U+FEFF |
| `s.upper()`, `s.lower()` | `String` | The Unicode default case mapping, the same for every locale |
| `s.replace(old, new)` | `String` | Each `old` becomes `new`. An empty `old` changes nothing |
| `s.indexOf(t)` | `Int?` | The index of the first `t` in scalar values, or `none` |
| `s.hash()` | `Int` | The hash of the trait `Hashable` (spec 7.8): the same number on each target. A later version can give another number |

- `a + b` joins two strings. `String` has an impl of the operator trait `Add` (spec 7.8, ADR-0134). A record with an impl of an operator trait can use its operator too, for example `a + b` of two `Vector` values with an impl of `Add`.
- `"{n} items"` puts values into a string. `{...}` takes an `Int`, a `Float`, a `Bool` or a `String`. A type with an impl of the trait `Text` can go into `{...}` too (spec 5.4, ADR-0134).
- `==` compares the scalar values. `<`, `<=`, `>` and `>=` compare scalar value by scalar value on the native targets and in the interpreter. The web compares by UTF-16 code units, as JavaScript does (spec 4.4, ADR-0113). The two orders differ only for a few characters, for example most emoji. For the order of a language, use `Locale.compare` (section 6a) with `sortWith` (section 3).

---

## 3. List

`List<T>` is an ordered sequence. A literal is `[1, 2, 3]`. An empty literal needs a known type: `state todos: List<Todo> = []`.

Methods that give a value:

| Method | Result | Rule |
|---|---|---|
| `xs[i]` | `T` | The element at `i`. An index outside the list is the runtime error "the index is outside the list" |
| `xs.get(i)` | `T?` | The element at `i`, or `none` for an index outside the list, also for a negative index |
| `xs.first()`, `xs.last()` | `T?` | `none` for an empty list |
| `xs.count()` | `Int` | |
| `xs.isEmpty()` | `Bool` | |
| `xs.contains(v)` | `Bool` | `true` when an element is equal to `v` (`==`, spec 5.1) |
| `xs.slice(from, to)` | `List<T>` | The elements from `from` to `to`, without `to`, with the bounds rule of `String.slice` |
| `xs.filter(x => cond)` | `List<T>` | The elements for which `cond` is `true`, in their order |
| `xs.flatMap(x => list)` | `List<U>` | The lists of the elements, as one list (ADR-0052) |
| `xs.reversed()` | `List<T>` | A new list in the other order |
| `xs.concat(ys)`, `xs + ys` | `List<T>` | A new list: the elements of `xs`, then of `ys` |
| `xs.unique()` | `List<T>` | The first of each value, in the order of the list. For `Int`, `String` and `Bool` |
| `xs.find(x => cond)` | `T?` | The first element for which `cond` is `true` |
| `xs.map(x => value)` | `List<U>` | One value for each element |
| `xs.any(x => cond)`, `xs.all(x => cond)` | `Bool` | `true` when `cond` is `true` for one element, or for all elements. An empty list gives `false` and `true` |
| `xs.indexOf(v)` | `Int?` | The index of the first element that is equal to `v`, or `none` |
| `xs.sum()` | `Int`, `Float` | The sum of a `List<Int>` or a `List<Float>`. An `Int` sum outside the range is a runtime error |
| `xs.join(sep)` | `String` | The strings of a `List<String>` with `sep` between them |

Methods that change the list. Each is a statement of its own:

| Method | Rule |
|---|---|
| `xs.push(v)` | Adds `v` at the end |
| `xs.insert(i, v)` | Puts `v` at index `i`. `i` can be from 0 to `xs.count()`. Another index is a runtime error |
| `xs.removeAt(i)` | Removes the element at `i`. An index outside the list is a runtime error |
| `xs.remove(v)` | Removes the first element that is equal to `v`. Without such an element, the list does not change |
| `xs.clear()` | Removes all elements |
| `xs.sort()` | Orders the list by `<`. The type of the elements must have the trait `Comparable` (spec 7.8, ADR-0134): `Int`, `Int64`, `Float`, `String`, or a type with an impl of `Comparable` |
| `xs.sortBy(x => key)` | Orders the list by a key, for example `todos.sortBy(t => t.title)`. The type of the key must have the trait `Comparable` |
| `xs.sortWith((a, b) => order)` | Orders the list with a compare function that gives an `Ordering` (ADR-0132). `.less` puts `a` before `b`. For example, `names.sortWith((a, b) => Locale.compare(a, b))` sorts in the order of the locale |

- The three sorts are stable: equal elements keep their order. For `sortWith`, two elements are equal when the compare function gives `.equal`.
- Each runtime uses the same stable merge sort for the three sorts, also the web (ADR-0132). So each target gives the same order, also when the order is not consistent. An example is a compare function or an impl of `Comparable` that gives `.less` for `(a, b)` and also for `(b, a)`. Spec 4.5 gives the exact form of the merge sort.
- `sortBy` gets the key of each element one time, before the sort starts. `sortWith` calls the compare function for each comparison of the merge sort.
- `sortWith` is for an order that no key gives, for example the order of a language (ADR-0132).
- The lambda of `filter`, `find`, `map` and `sortBy` has one parameter and a value as its body. The lambda of `sortWith` has two parameters and a value as its body. Each runs before the call returns, so it can read the parameters, the local values and the reactive values around it.
- `contains`, `indexOf` and `remove` compare with `==`. A list of `dyn` values cannot use them, because a `dyn` value has no `==` (spec 5.1, ADR-0135).
- `xs[i] = v` and `xs[i].done = true` write one element.
- `for x in xs { ... }` and `for x, i in xs { ... }` go through the list as it was when the loop started (spec 6.1).

---

## 4. Map

`Map<K, V>` maps keys to values. A literal is `Map { "a": 1, "b": 2 }`. The key type must have the trait `Hashable` (spec 7.8, ADR-0134): `Int`, `String`, `Bool`, or a record or an enum whose fields all have `Hashable`. A `Float` and a `dyn` type cannot be a key. A type with an impl of `Equatable` needs an impl of `Hashable` too. Two keys are one key when their `hash` is the same and `==` gives `true`.

| Method | Result | Rule |
|---|---|---|
| `m[k]` | `V?` | The value of `k`, or `none` |
| `m.count()` | `Int` | The number of keys |
| `m.isEmpty()` | `Bool` | |
| `m.contains(k)` | `Bool` | `true` when `m` has the key `k` |
| `m.keys()` | `List<K>` | The keys, in the order in which they were first added |
| `m.values()` | `List<V>` | The values, in the order of their keys |

Changes:

| Statement | Rule |
|---|---|
| `m[k] = v` | Adds or replaces the value of `k`. A new key goes to the end. A replaced value keeps the position of its key |
| `m.remove(k)` | Removes the key `k` and its value |
| `m.clear()` | Removes all keys |

- `for k, v in m { ... }` goes through the keys in their order.
- Two maps are equal when they have the same keys with equal values. The order of the keys does not count.

`Set<T>` is a `Map<T, Bool>` (ADR-0083). `s.insert(x)` adds a value, and `Map {}` is an empty set.

---

## 4a. Bytes

`Bytes` is an immutable sequence of numbers from 0 to 255 (ADR-0037). `==` compares the content. A value cannot be a key of a `Map`, and the markup cannot show it.

| Form | Result | Rule |
|---|---|---|
| `text.bytes()` | `Bytes` | The UTF-8 bytes of a `String` |
| `Bytes.from(list)` | `Bytes` | The bytes of a `List<Int>`. A number outside 0 to 255 is a runtime error |
| `b.count()` | `Int` | The number of bytes |
| `b.isEmpty()` | `Bool` | True when there are no bytes |
| `b.get(i)` | `Int?` | The byte at the index `i`, or `none` |
| `b.slice(from, to)` | `Bytes` | The bytes from `from` to `to`, with the bounds of `xs.slice` |
| `b.text()` | `String` | The UTF-8 text. An invalid sequence becomes U+FFFD |
| `b.toList()` | `List<Int>` | The numbers |

---

## 5. Optional values and results

| Form | Rule |
|---|---|
| `none` | The empty value of each type `T?` |
| `a ?? b` | `a` when it has a value, else `b` |
| `a?.b` | `none` when `a` is `none`, else `a.b` |
| `if let x = a { ... }` | Runs the block when `a` has a value. `x` is the value |
| `#if let x = a { ... } else { ... }` | The same in markup. `else if let` continues the block |
| `a.toList()` | A list with the value of `a`, or an empty list |
| `Result<T, E>` | A built-in enum with the cases `ok(T)` and `err(E)`. Use `match` to read it |
| `r?` | In a function that gives `Result<_, E>`: returns the `err` value at once, or gives the `ok` value |

---

## 6. Built-in names

| Name | Type | Rule |
|---|---|---|
| `sleep(ms)` | `async fn (Int) -> Void` | A task that ends after `ms` milliseconds. Use it with `await` (spec 8) |
| `interval(ms)` | `async fn (Int) -> Void` | A task that ends at the next multiple of `ms` on the clock of the app. A loop with `await interval(1000)` ticks each second and does not drift. Use it in place of a timer (ADR-0026) |
| `panic(message)` | `(String) -> Void` | Stops the current batch with a runtime error. A path that ends in `panic` needs no `return` |
| `Int64.from(n)`, `Int64.parse(text)` | `Int64`, `Int64?` | A whole number with 64 bits (ADR-0084). `+`, `-`, `*`, `/`, `%` and compares work on two `Int64` values. `x.toInt()` gives an `Int` |
| `Time.now()` | `Int` | The milliseconds since 1970-01-01 UTC |
| `Time.utc(ms)`, `Time.local(ms)` | `TimeParts` | The date of `ms` in UTC or in the time zone of the machine: `year`, `month`, `day`, `hour`, `minute`, `second`, `millisecond` and `weekday` (ADR-0079). `weekday` is 1 for Monday |
| `Time.iso(ms)` | `String` | The ISO 8601 text, for example `2026-11-01T12:00:00.000Z`. A year outside 0 to 9999 has a sign and six digits: `+010000-01-01T00:00:00.000Z` |
| `Time.parse(text)` | `Int?` | The milliseconds of an ISO 8601 date. A text with no zone is in UTC. `none` for another text |
| `Theme.designWidth()`, `Theme.designHeight()` | `Float` | `scale.design` of `style.toml`, at compile time |
| `Theme.minScale()`, `Theme.maxScale()` | `Float` | `scale.min` and `scale.max` of `style.toml`, at compile time |
| `Http.event(url)` | `Task<Result<String, String>>` | The next message of a stream of server-sent events (ADR-0077). An `err` ends the stream, and the next call opens it again |
| `File.pick(accept)` | `Task<PickedFile?>` | The file dialog of the platform (ADR-0078). A `PickedFile` has `name` and `bytes`. `none` when the user closes it |
| `Storage.get(key)` | `String?` | The text of `key` in the store of the app (ADR-0074). `none` on a server |
| `Storage.set(key, value)`, `Storage.delete(key)` | nothing | Writes or deletes a value. A store that refuses a write has no effect |
| `Theme.setScale(factor)` | nothing | Scales the whole UI, from 0.5 to 3 (ADR-0085) |
| `Theme.setToken(name, value)` | nothing | Changes a token of the theme while the app runs (ADR-0082, ADR-0130). The signature is `Theme.setToken<T: Token>(name: String, value: T)`. `Token` is a built-in trait for `Color` and `Float` (ADR-0134). A `Color` changes a color in each variant. A `Float` changes a length in logical px. With a literal name, only that length token can change. With a computed name, each length token can change. An app that never gives a length has no run-time code for lengths (layout spec, section 2.1). With a literal name, a value of the wrong kind is a compile error, for example a `Float` for the color token `accent`. With a computed name, a value of the wrong kind has no effect, as an unknown name has. A length that is not finite has no effect |
| `Theme.setMode(mode)` | nothing | Chooses the theme: `.system`, `.light`, `.dark`, `.contrastLight` or `.contrastDark` of the built-in enum `ThemeMode` (ADR-0072, ADR-0103). The app stores the choice. |
| `Focus.to(id)` | nothing | Gives the keyboard focus to the element with this `id`, at the end of the batch (ADR-0102) |
| `Focus.newId()` | `String` | An id that no other call gives, for `id`, `controls` and `labelledBy` (ADR-0102) |
| `Json.embed("../x.json")` | The expected type | The value of a JSON file at compile time. The path starts at the folder of the sources, and it can leave that folder. A file that does not fit the type is a compile error. A key with no field is ignored. |
| `Random.float()` | `Float` | A random number from 0 up to, but without, 1. It is not for secrets |
| `Random.int(lo, hi)` | `Int` | A random `Int` from `lo` up to, but without, `hi`. `lo` when `hi` is not above `lo` |
| `Color.parse(text)` | `Color?` | A color from text at run time: `#rgb`, `#rrggbb`, `#rrggbbaa`, `rgb(r g b / a)` or `transparent` (ADR-0050). `none` for another text |
| `Color.rgb(r, g, b, a)` | `Color` | The color `rgb(r g b / a)` of three `Int` channels from 0 to 255 and a `Float` alpha from 0 to 1 (ADR-0160). A value outside its range takes the nearest end, as in CSS |
| `Color.mix(a, b, t)` | `Color` | The color at `t` from `a` (0.0) to `b` (1.0): `color-mix(in srgb, a, b t%)` of CSS (ADR-0160). The channels mix with their alpha, as in a transition, so a mix with `transparent` has no gray. A token stays a token, so the mix follows the theme. `t` outside 0 to 1 takes the nearest end |
| `Icon.parse(text)` | `Result<Icon, String>` | The icon of SVG text that the app gets while it runs, for example from the network. The rules are those of an imported file (ADR-0030). The error has the line. Only an app that calls it links the parser |
| `Http.get(url)` | `Task<Result<HttpResponse, String>>` | A built-in `async fn`: a `GET` request (ADR-0037). `err` means that no response came. A status of 404 is `ok` |
| `Http.send(request)` | `Task<Result<HttpResponse, String>>` | The same for an `HttpRequest`: `url`, `method` (default `"GET"`), `headers` and `body`. A request ends after 30 seconds, and with its component |
| `Json.decode(text)` | `Result<T, String>` | The value of the expected type `T` (ADR-0038): `const feed: Feed = Json.decode(text)?`. The error has a path. A key that is not a field does not matter. A field of a type `T?` can be missing |
| `Json.encode(value)` | `String` | The JSON text of a value, in the format of `JSON.stringify` |
| `scalar.text()` | `String` | The text of a `Scalar` (ADR-0045): the text itself, a number as in `{x}` of a `Float`, or `true` or `false` |
| `Window.width`, `Window.height` | `Float` | The size of the window in logical px (ADR-0106). A read has the dependencies of a state. Only a component can read it. A server gives 0 |
| `Platform.os` | `Os` | The target: `.macos`, `.windows`, `.linux`, `.android`, `.ios` or `.web`. It is a constant of the build, so the compiler removes the code of the other targets in `if Platform.os == .web { ... }` |

The theme tokens, for example `$accent` and `$text-secondary`, are also built-in names. The [layout spec](https://surface.solenovis.com/docs/layout.md), section 2.1, lists them.

---

## 6a. Locale

The module `Locale` formats values for a person in the locale of the app (ADR-0132, spec 14). A locale is a BCP 47 tag, for example `en`, `de` or `pt-BR`.

`surf.toml` lists the locales of the project: `locales = ["en", "de"]`. At the start, the app finds the first language of the system that fits an item of the list, with the lookup of RFC 4647: `de-CH` fits `de` (spec 14). Else it takes the first item of the list. If a project uses `Locale` and its `surf.toml` has no key `locales`, or an empty list, the compiler gives an error. The message gives an example line, for example `locales = ["en"]`.

| Function | Result | Rule |
|---|---|---|
| `Locale.number(x, style: NumberStyle = .decimal)` | `String` | An `Int` or a `Float` with the decimal separator and the grouping of the locale: `Locale.number(1234.5)` is `"1,234.5"` in `en` and `"1.234,5"` in `de` |
| `Locale.date(ms, style: DateStyle = .medium)` | `String` | The date of `ms` in the time zone of the machine, with the names of months and weekdays of the locale |
| `Locale.time(ms, style: DateStyle = .medium)` | `String` | The time of day of `ms` in the time zone of the machine |
| `Locale.relative(ms)` | `String` | The text of a difference of `ms` milliseconds: `Locale.relative(-3 * 86_400_000)` is `"3 days ago"` in `en`. A negative value is in the past |
| `Locale.list(items)` | `String` | The strings of a `List<String>` with the words of the locale: `["a", "b", "c"]` gives `"a, b, and c"` in `en` and `"a, b und c"` in `de` |
| `Locale.plural(n)` | `PluralCategory` | The plural category of an `Int` or a `Float` in the locale |
| `Locale.compare(a, b)` | `Ordering` | The order of two strings in the rules of the locale. `.less` when `a` comes before `b`. `xs.sortWith((a, b) => Locale.compare(a, b))` sorts a list in this order (section 3) |
| `Locale.set(tag)` | nothing | Changes the locale while the app runs. A tag that fits no item of the list gives the first item. The change is not saved. A `derived` value and a markup expression cannot call it, because it is a write |
| `Locale.current` | `String` | The tag of the current locale. It is always an item of the list |

- `NumberStyle` has the cases `decimal`, `percent` and `currency(code: String)`, with a currency code of ISO 4217: `style: .currency(code: "EUR")`. A percent shows `x` times 100.
- A decimal number has at most 3 digits after the separator, and a percent has none. A currency has the digits of the currency. The value rounds half away from zero, as `round` does.
- `DateStyle` has the cases `short`, `medium` and `long`. The locale gives the pattern of each length.
- `Locale.relative` uses the largest unit in which the amount is at least 1. The units are year (365 days), month (30 days), week, day, hour and minute. Else it uses seconds. The amount drops its fraction.
- `PluralCategory` has the categories of CLDR: `zero`, `one`, `two`, `few`, `many` and `other`. English uses only `one` and `other`.
- A read of `Locale.current` and a call of a function of `Locale` have the dependencies of a state. After `Locale.set`, each `derived` value, effect and text that uses a format updates.
- `Locale` is valid in each function, also in a function of a `.surfs` file: `fn due(t: Todo) -> String = Locale.date(t.due);`. The checker follows each call, so a component that calls such a function updates after `Locale.set` too (spec 10.2).
- A module constant cannot read `Locale`, also through a call. This is a compile error. Use a function, for example `fn title() -> String` (spec 7.3).
- A call of a function prop reads `Locale` when a function value of the type of the prop in the app reads `Locale` (spec 10.2).
- `Locale.set` does not save the locale, as `Theme.setToken` does not save a token (ADR-0082). An app can save the choice with `Storage`.
- On a SurfWave server, each request takes the first tag of its `Accept-Language` header that fits an item of the list, with the same lookup. The tags have the order of their weights. Else it takes the first item of the list. The page hydrates with the same locale. After `Locale.set`, the header `x-wave-locale` replaces `Accept-Language` ([SurfWave](https://surface.solenovis.com/docs/routes.md), section 5).
- The web uses `Intl` of the browser. The native targets and the interpreter use the ICU of the system (ADR-0132). The two can have different versions of the data, so a few results can differ (see [native differences](https://surface.solenovis.com/docs/targets.md)).
- `Locale.plural` rounds a `Float` to 3 digits after the separator first, as `Locale.number` does: `Locale.plural(1.0001)` is `.one` in `en`.
- An app that uses `Locale` loads ICU when it starts. A system without ICU stops the app at its start with an error that names the package of ICU. The `.deb` of such an app depends on ICU, and its MSIX package needs Windows 10 version 1903 (ADR-0086, ADR-0141).

---

## 7. What the library does not have

Version 0 has no functions for these tasks. The right column gives a way to do the task in version 0.

| Task | Way in version 0 |
|---|---|
| A repeated action, as `setInterval` does | A loop with `await interval(ms)` in an `async fn`, started in `on mount`. The runtime stops it with its component |
