ilha 0.15.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +102 -0
- package/LICENSE +21 -0
- package/MIGRATIONS.json +96 -0
- package/MIGRATIONS.md +198 -0
- package/README.md +9 -26
- package/dist/debug.d.ts +45 -0
- package/dist/debug.js +217 -0
- package/dist/index.d.ts +30 -50
- package/dist/index.js +105 -586
- package/dist/jsx-dev-runtime.d.ts +2 -2
- package/dist/jsx-dev-runtime.js +1 -1
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.js +1 -1
- package/dist/mount-3Ggg67T0.d.ts +14 -0
- package/dist/mount-BwuJsiDH.js +585 -0
- package/dist/paint-core-D1n4lbpF.js +350 -0
- package/dist/renderer.d.ts +30 -66
- package/dist/renderer.js +18 -50
- package/dist/{runtime-Baq7MtkE.d.ts → runtime-BCdKBqYp.d.ts} +29 -35
- package/dist/shared-vpgYXytd.js +36 -0
- package/dist/snapshot-BDC74QwI.d.ts +9 -0
- package/dist/start-B-e1dG5U.js +515 -0
- package/dist/test.d.ts +66 -0
- package/dist/test.js +179 -0
- package/dist/{types-DsdtZMhG.d.ts → types-NhEiBvwy.d.ts} +39 -46
- package/dist/vnode-DJ25Jn3D.js +22 -0
- package/dist/{vnode-CpSQZnLM.d.ts → vnode-DskooLPD.d.ts} +10 -2
- package/llms.txt +37 -0
- package/oxlint.cjs +596 -0
- package/package.json +20 -14
- package/dist/define.d.ts +0 -4
- package/dist/define.js +0 -18
- package/dist/mount-DKY_jSsG.js +0 -513
- package/dist/paint-core-B7jENZwS.js +0 -1693
- package/dist/vnode-BHUY61Rm.js +0 -100
package/AGENTS.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ilha 0.16.0 — agent guide
|
|
2
|
+
|
|
3
|
+
Generated from `src/index.ts` by `scripts/agent-docs.ts`. Do not edit by hand.
|
|
4
|
+
|
|
5
|
+
ilha is a tiny, isomorphic islands UI library on Effect 4 (`effect` is the only peer dependency). A component is a function (sync, async, or generator) that returns JSX. State lives in order-based `atom()` slots. There is no virtual DOM and no compiler.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { atom, mount } from "ilha";
|
|
9
|
+
|
|
10
|
+
const Counter = () => {
|
|
11
|
+
const count = atom(0);
|
|
12
|
+
return (
|
|
13
|
+
<button type="button" onclick={() => count.update((n) => n + 1)}>
|
|
14
|
+
Count: {count}
|
|
15
|
+
</button>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
mount(document.getElementById("app")!, Counter);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Pick exactly one API per task
|
|
23
|
+
|
|
24
|
+
| Task | Use |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| Local state | `const n = atom(0)` |
|
|
27
|
+
| Derived value | `atom(Atom.map(source.atom, fn))` or `atom(Atom.transform(...))` |
|
|
28
|
+
| One-time init | `atom.lazy(() => expensive())` |
|
|
29
|
+
| React to a change | `watch(source, fn)` |
|
|
30
|
+
| Mount-only side effect | `watch.once(fn)` (return a cleanup) |
|
|
31
|
+
| Async data | `atom(Effect)`, read as `AsyncResult` — see /guide/recipes/data-fetching |
|
|
32
|
+
| Live data | `yield` an Effect `Stream` from a generator component |
|
|
33
|
+
| Per-emission interruptible flow | `yield* when(stream, function* (v) {...})` |
|
|
34
|
+
| Share values down the tree | `createContext()` + `context(ctx)` |
|
|
35
|
+
| Handle failures | `<ErrorBoundary fallback={...}>` |
|
|
36
|
+
| Server render | `await renderToString(Component)` |
|
|
37
|
+
| Client render | `mount(host, Component)`, or `mount(host, Component, { hydrate: true })` over server HTML |
|
|
38
|
+
| Raw trusted HTML | `unsafe(html)` |
|
|
39
|
+
|
|
40
|
+
Full table: https://ilha.build/guide/getting-started/choose-an-api
|
|
41
|
+
|
|
42
|
+
## Never do
|
|
43
|
+
|
|
44
|
+
- Never call `atom()`, `watch()`, or `when()` conditionally, in loops, or after an early return: slots are order-based.
|
|
45
|
+
- Never read an atom with `.value` or `.get()`: call `count()`, or pass `{count}` in JSX.
|
|
46
|
+
- Never wrap Effect Atom APIs (`Atom.map`, `Atom.transform`, `Atom.fn`): pass `handle.atom` to them and wrap the result in `atom()`.
|
|
47
|
+
- Never use `useState`, `useEffect`, or other React hooks: there is no hook layer.
|
|
48
|
+
- Never pass untrusted input to `unsafe()`.
|
|
49
|
+
- Never import removed APIs (`resource`, `invalidate`, `fromEventSource`, `ilha/define`, `structuralEqual`): see MIGRATIONS.md.
|
|
50
|
+
- Never use an atom handle from another island: it throws ILHA003.
|
|
51
|
+
|
|
52
|
+
## Public API (`ilha`)
|
|
53
|
+
|
|
54
|
+
- `atom` — Declare reactive state in a component: `atom(0)`, `atom(effect | stream)` (handle holds an `AsyncResult`), `atom(Atom.map(...))` (derived), `atom.lazy(() => init)` (one-time init). Read `count()`, write `count.set(v)` / `count.update(fn)`.
|
|
55
|
+
- `batch` — Group several atom writes into one rerender (re-export of Effect `Atom.batch`).
|
|
56
|
+
- `context` — Read a context value as an atom handle.
|
|
57
|
+
- `createContext` — `createContext(default)` returns a context whose `Provider` (`<Ctx.Provider value={v}>`) shares a value down the tree; read it with `context(ctx)`.
|
|
58
|
+
- `ErrorBoundary` — Catch subtree failures and paint a fallback.
|
|
59
|
+
- `Fragment` — Group children without a wrapper element (`<>...</>`).
|
|
60
|
+
- `h` — Build a vnode by hand; JSX compiles to this.
|
|
61
|
+
- `IlhaError` — Error thrown for misuse, with `code` (ILHA001..), `fix`, and `docs` fields.
|
|
62
|
+
- `mount` — `mount(host, Component, { hydrate?, onError?, onIdle? })` renders in the browser and returns a dispose function. With `{ hydrate: true }` it adopts server HTML.
|
|
63
|
+
- `renderToString` — `await renderToString(Component, { timeout?, snapshot?, markers? })` renders HTML on the server, awaiting async components and streams (first emission).
|
|
64
|
+
- `unsafe` — Mark a string as pre-rendered HTML and paint it without escaping.
|
|
65
|
+
- `untrack` — Read atoms inside `fn` without subscribing the active render.
|
|
66
|
+
- `watch` — `watch(source, ({ signal }) => ...)` reacts to an atom, Effect atom, or Stream; `watch.once(fn)` runs once on mount and may return a cleanup. Both return `Instruction<void>` — call at the top level of the component, in the same order every render.
|
|
67
|
+
- `when` — Rerun `body` for every value of `stream`, replacing the previous run.
|
|
68
|
+
|
|
69
|
+
Types: `AtomEquality`, `AtomHandle`, `AtomLazyOptions`, `AtomOptions`, `Component`, `ContextProviderProps`, `ErrorBoundaryProps`, `ErrorFallback`, `ErrorFallbackProps`, `IlhaCode`, `IlhaContext`, `MountOptions`, `RenderToStringOptions`, `UnsafeHtml`, `View`, `WatchCallback`, `WatchContext`, `WatchOnceCleanup`.
|
|
70
|
+
|
|
71
|
+
## Subpaths
|
|
72
|
+
|
|
73
|
+
- `ilha/jsx-runtime` — JSX factory. Set `jsxImportSource: "ilha"` in tsconfig; also `ilha/jsx-dev-runtime`.
|
|
74
|
+
- `ilha/renderer` — `createRenderer`/`PaintOps` seam for hosts that are not a DOM (native, test harness).
|
|
75
|
+
- `ilha/test` — `render(component)` harness for unit tests — see /guide/recipes/testing.
|
|
76
|
+
- `ilha/debug` — `inspect()` returns JSON-serializable trees of live islands; `roots()`, `onChange()` — see /guide/devtools.
|
|
77
|
+
- `ilha/oxlint` — Oxlint JS plugin enforcing ilha rules — see /guide/recipes/oxlint.
|
|
78
|
+
- `@ilha/devtools` — Separate package: in-browser inspector built on `ilha/debug` — see /guide/devtools.
|
|
79
|
+
|
|
80
|
+
## Errors
|
|
81
|
+
|
|
82
|
+
Every diagnostic is an `IlhaError` (or a console warning) with a stable code, a `fix` line, and a docs link.
|
|
83
|
+
|
|
84
|
+
| Code | Meaning |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| [ILHA001](https://ilha.build/errors/ILHA001) | Primitive called with no active component |
|
|
87
|
+
| [ILHA002](https://ilha.build/errors/ILHA002) | Atom/watch slot count changed between renders (warning) |
|
|
88
|
+
| [ILHA003](https://ilha.build/errors/ILHA003) | Atom handle used from a different island |
|
|
89
|
+
| [ILHA004](https://ilha.build/errors/ILHA004) | Generator resumed while an instruction is in flight |
|
|
90
|
+
| [ILHA005](https://ilha.build/errors/ILHA005) | Duplicate key among sibling components (warning) |
|
|
91
|
+
| [ILHA006](https://ilha.build/errors/ILHA006) | Hydration mismatch (warning) |
|
|
92
|
+
| [ILHA007](https://ilha.build/errors/ILHA007) | Unsafe raw text in <script>/<style> during renderToString |
|
|
93
|
+
|
|
94
|
+
Details and fixes: https://ilha.build/errors
|
|
95
|
+
|
|
96
|
+
## Where to read more
|
|
97
|
+
|
|
98
|
+
- Docs index for LLMs: https://ilha.build/llms.txt
|
|
99
|
+
- Removed or changed APIs: `MIGRATIONS.md` / `MIGRATIONS.json` in this package
|
|
100
|
+
- Testing: https://ilha.build/guide/recipes/testing
|
|
101
|
+
- Devtools and `ilha/debug`: https://ilha.build/guide/devtools
|
|
102
|
+
- Lint rules: https://ilha.build/guide/recipes/oxlint
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryuz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/MIGRATIONS.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package": "ilha",
|
|
3
|
+
"docs": "https://ilha.build/llms.txt",
|
|
4
|
+
"changes": [
|
|
5
|
+
{
|
|
6
|
+
"id": "resource",
|
|
7
|
+
"kind": "removed",
|
|
8
|
+
"api": "resource, invalidate",
|
|
9
|
+
"import": "ilha",
|
|
10
|
+
"replacement": "An Effect Cache in a module, read through atom(Effect).",
|
|
11
|
+
"docs": "https://ilha.build/guide/recipes/data-fetching",
|
|
12
|
+
"before": "const user = resource(\"user\", (id, { signal }) => fetchUser(id, signal));\nuser.data();",
|
|
13
|
+
"after": "const users = Effect.runSync(Cache.make({ capacity: 100, lookup: loadUser, timeToLive: \"1 minute\" }));\nconst user = atom(Cache.get(users, id)); // AsyncResult"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "from-event-source",
|
|
17
|
+
"kind": "removed",
|
|
18
|
+
"api": "fromEventSource",
|
|
19
|
+
"import": "ilha",
|
|
20
|
+
"replacement": "Stream.callback over EventSource with Stream.retry.",
|
|
21
|
+
"docs": "https://ilha.build/guide/recipes/server-sent-events",
|
|
22
|
+
"before": "const feed = fromEventSource(\"/events\");\nyield Stream.map(feed.stream, (m) => <p>{m}</p>);",
|
|
23
|
+
"after": "const events = Stream.callback<string>((queue) => { /* see recipe */ });\nyield Stream.map(events, (m) => <p>{m}</p>);"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "define",
|
|
27
|
+
"kind": "removed",
|
|
28
|
+
"api": "define",
|
|
29
|
+
"import": "ilha/define",
|
|
30
|
+
"replacement": "Copy the define() helper from the custom-elements recipe; it calls mount() in connectedCallback.",
|
|
31
|
+
"docs": "https://ilha.build/guide/recipes/custom-elements",
|
|
32
|
+
"before": "import { define } from \"ilha/define\";\ndefine(\"ilha-counter\", Counter);",
|
|
33
|
+
"after": "import { define } from \"./define\"; // helper from the recipe\ndefine(\"ilha-counter\", Counter);"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "structural-equal",
|
|
37
|
+
"kind": "removed",
|
|
38
|
+
"api": "structuralEqual",
|
|
39
|
+
"import": "ilha",
|
|
40
|
+
"replacement": "Equal.equals from effect, or the equals: \"structural\" atom option.",
|
|
41
|
+
"docs": "https://ilha.build/guide/ui/state",
|
|
42
|
+
"before": "atom(value, { equals: structuralEqual });",
|
|
43
|
+
"after": "atom(value, { equals: \"structural\" });\n// or: import * as Equal from \"effect/Equal\"; Equal.equals(a, b)"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "capture-actions",
|
|
47
|
+
"kind": "removed",
|
|
48
|
+
"api": "renderToString option captureActions",
|
|
49
|
+
"import": "ilha",
|
|
50
|
+
"replacement": "Remove the option. SSR no longer captures actions; hydration restores state from the embedded snapshot.",
|
|
51
|
+
"docs": "https://ilha.build/guide/ui/render",
|
|
52
|
+
"before": "await renderToString(Component, { captureActions: true });",
|
|
53
|
+
"after": "await renderToString(Component);"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "island-mount-hook",
|
|
57
|
+
"kind": "removed",
|
|
58
|
+
"api": "internal island mount hook (ISLAND_MOUNT_INTERNAL)",
|
|
59
|
+
"import": "ilha",
|
|
60
|
+
"replacement": "None. It was internal. Use mount(host, Component) for every root.",
|
|
61
|
+
"docs": "https://ilha.build/guide/ui/render",
|
|
62
|
+
"before": "",
|
|
63
|
+
"after": "mount(host, Component, { hydrate: true });"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "watch-when-instruction",
|
|
67
|
+
"kind": "changed",
|
|
68
|
+
"api": "watch, watch.once, when",
|
|
69
|
+
"import": "ilha",
|
|
70
|
+
"replacement": "They return Instruction<void>. Call them at the top level of the component (yield* is optional in generators); do not read a return value.",
|
|
71
|
+
"docs": "https://ilha.build/guide/ui/state",
|
|
72
|
+
"before": "const result = yield* watch(count, fn); // loosely typed result",
|
|
73
|
+
"after": "watch(count, fn);"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": "atom-async-result",
|
|
77
|
+
"kind": "changed",
|
|
78
|
+
"api": "atom(effect | stream)",
|
|
79
|
+
"import": "ilha",
|
|
80
|
+
"replacement": "The handle is typed AtomHandle<AsyncResult<A, E>>. Branch on Initial, Success, and Failure instead of reading the value directly. Any atom handle is a valid View.",
|
|
81
|
+
"docs": "https://ilha.build/guide/recipes/data-fetching",
|
|
82
|
+
"before": "const user = atom(fetchUser); // typed as the value\nuser().name",
|
|
83
|
+
"after": "const user = atom(fetchUser); // AsyncResult<User, Error>\nAsyncResult.isSuccess(user()) && user().value.name"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "renderer-paint-ops",
|
|
87
|
+
"kind": "changed",
|
|
88
|
+
"api": "PaintOps",
|
|
89
|
+
"import": "ilha/renderer",
|
|
90
|
+
"replacement": "appendRoot, asRoot, asNode, asElement, createSlotHost, and disconnect are removed. reuseNode, committed, setStyle, setFormControl, and bindEvents are optional. morph is added.",
|
|
91
|
+
"docs": "https://ilha.build/reference/ilha",
|
|
92
|
+
"before": "const ops: PaintOps<Node, El> = { createElement, createText, createRaw, placeChildren, appendRoot, asRoot, asNode, asElement, createSlotHost, disconnect, /* ... */ };",
|
|
93
|
+
"after": "const ops: PaintOps<Node, El> = { createElement, createText, createRaw, placeChildren, morph /* optional: reuseNode, committed, setStyle, setFormControl, bindEvents */ };"
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
package/MIGRATIONS.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# ilha migrations
|
|
2
|
+
|
|
3
|
+
Generated from `MIGRATIONS.json` by `scripts/agent-docs.ts`. Do not edit by hand.
|
|
4
|
+
|
|
5
|
+
Removed and changed APIs, each with its replacement. Machine-readable form: `MIGRATIONS.json`. Index for LLMs: https://ilha.build/llms.txt
|
|
6
|
+
|
|
7
|
+
## resource, invalidate
|
|
8
|
+
|
|
9
|
+
- Status: removed
|
|
10
|
+
- Import: `ilha`
|
|
11
|
+
- Replacement: An Effect Cache in a module, read through atom(Effect).
|
|
12
|
+
- Docs: https://ilha.build/guide/recipes/data-fetching
|
|
13
|
+
|
|
14
|
+
Before:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
const user = resource("user", (id, { signal }) => fetchUser(id, signal));
|
|
18
|
+
user.data();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const users = Effect.runSync(
|
|
25
|
+
Cache.make({ capacity: 100, lookup: loadUser, timeToLive: "1 minute" })
|
|
26
|
+
);
|
|
27
|
+
const user = atom(Cache.get(users, id)); // AsyncResult
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## fromEventSource
|
|
31
|
+
|
|
32
|
+
- Status: removed
|
|
33
|
+
- Import: `ilha`
|
|
34
|
+
- Replacement: Stream.callback over EventSource with Stream.retry.
|
|
35
|
+
- Docs: https://ilha.build/guide/recipes/server-sent-events
|
|
36
|
+
|
|
37
|
+
Before:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const feed = fromEventSource("/events");
|
|
41
|
+
yield Stream.map(feed.stream, (m) => <p>{m}</p>);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
After:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const events = Stream.callback<string>((queue) => { /* see recipe */ });
|
|
48
|
+
yield Stream.map(events, (m) => <p>{m}</p>);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## define
|
|
52
|
+
|
|
53
|
+
- Status: removed
|
|
54
|
+
- Import: `ilha/define`
|
|
55
|
+
- Replacement: Copy the define() helper from the custom-elements recipe; it calls mount() in connectedCallback.
|
|
56
|
+
- Docs: https://ilha.build/guide/recipes/custom-elements
|
|
57
|
+
|
|
58
|
+
Before:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { define } from "ilha/define";
|
|
62
|
+
define("ilha-counter", Counter);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
After:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { define } from "./define"; // helper from the recipe
|
|
69
|
+
define("ilha-counter", Counter);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## structuralEqual
|
|
73
|
+
|
|
74
|
+
- Status: removed
|
|
75
|
+
- Import: `ilha`
|
|
76
|
+
- Replacement: Equal.equals from effect, or the equals: "structural" atom option.
|
|
77
|
+
- Docs: https://ilha.build/guide/ui/state
|
|
78
|
+
|
|
79
|
+
Before:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
atom(value, { equals: structuralEqual });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
After:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
atom(value, { equals: "structural" });
|
|
89
|
+
// or: import * as Equal from "effect/Equal"; Equal.equals(a, b)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## renderToString option captureActions
|
|
93
|
+
|
|
94
|
+
- Status: removed
|
|
95
|
+
- Import: `ilha`
|
|
96
|
+
- Replacement: Remove the option. SSR no longer captures actions; hydration restores state from the embedded snapshot.
|
|
97
|
+
- Docs: https://ilha.build/guide/ui/render
|
|
98
|
+
|
|
99
|
+
Before:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
await renderToString(Component, { captureActions: true });
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
After:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await renderToString(Component);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## internal island mount hook (ISLAND_MOUNT_INTERNAL)
|
|
112
|
+
|
|
113
|
+
- Status: removed
|
|
114
|
+
- Import: `ilha`
|
|
115
|
+
- Replacement: None. It was internal. Use mount(host, Component) for every root.
|
|
116
|
+
- Docs: https://ilha.build/guide/ui/render
|
|
117
|
+
|
|
118
|
+
After:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
mount(host, Component, { hydrate: true });
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## watch, watch.once, when
|
|
125
|
+
|
|
126
|
+
- Status: changed
|
|
127
|
+
- Import: `ilha`
|
|
128
|
+
- Replacement: They return Instruction<void>. Call them at the top level of the component (yield* is optional in generators); do not read a return value.
|
|
129
|
+
- Docs: https://ilha.build/guide/ui/state
|
|
130
|
+
|
|
131
|
+
Before:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const result = yield * watch(count, fn); // loosely typed result
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
After:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
watch(count, fn);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## atom(effect | stream)
|
|
144
|
+
|
|
145
|
+
- Status: changed
|
|
146
|
+
- Import: `ilha`
|
|
147
|
+
- Replacement: The handle is typed AtomHandle<AsyncResult<A, E>>. Branch on Initial, Success, and Failure instead of reading the value directly. Any atom handle is a valid View.
|
|
148
|
+
- Docs: https://ilha.build/guide/recipes/data-fetching
|
|
149
|
+
|
|
150
|
+
Before:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const user = atom(fetchUser); // typed as the value
|
|
154
|
+
user().name;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
After:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const user = atom(fetchUser); // AsyncResult<User, Error>
|
|
161
|
+
AsyncResult.isSuccess(user()) && user().value.name;
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## PaintOps
|
|
165
|
+
|
|
166
|
+
- Status: changed
|
|
167
|
+
- Import: `ilha/renderer`
|
|
168
|
+
- Replacement: appendRoot, asRoot, asNode, asElement, createSlotHost, and disconnect are removed. reuseNode, committed, setStyle, setFormControl, and bindEvents are optional. morph is added.
|
|
169
|
+
- Docs: https://ilha.build/reference/ilha
|
|
170
|
+
|
|
171
|
+
Before:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const ops: PaintOps<Node, El> = {
|
|
175
|
+
createElement,
|
|
176
|
+
createText,
|
|
177
|
+
createRaw,
|
|
178
|
+
placeChildren,
|
|
179
|
+
appendRoot,
|
|
180
|
+
asRoot,
|
|
181
|
+
asNode,
|
|
182
|
+
asElement,
|
|
183
|
+
createSlotHost,
|
|
184
|
+
disconnect /* ... */,
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
After:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const ops: PaintOps<Node, El> = {
|
|
192
|
+
createElement,
|
|
193
|
+
createText,
|
|
194
|
+
createRaw,
|
|
195
|
+
placeChildren,
|
|
196
|
+
morph /* optional: reuseNode, committed, setStyle, setFormControl, bindEvents */,
|
|
197
|
+
};
|
|
198
|
+
```
|
package/README.md
CHANGED
|
@@ -112,12 +112,11 @@ const host = document.querySelector("[data-ilha]");
|
|
|
112
112
|
if (host) mount(host, Counter, { hydrate: true });
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
| Option
|
|
116
|
-
|
|
|
117
|
-
| `snapshot`
|
|
118
|
-
| `markers`
|
|
119
|
-
| `timeout`
|
|
120
|
-
| `captureActions` | `false` | Probe handlers for server RPCs |
|
|
115
|
+
| Option | Default | Meaning |
|
|
116
|
+
| ---------- | ------- | ---------------------------- |
|
|
117
|
+
| `snapshot` | `true` | Embed atom values |
|
|
118
|
+
| `markers` | `true` | Wrap in `<div data-ilha>` |
|
|
119
|
+
| `timeout` | none | Serialize after this many ms |
|
|
121
120
|
|
|
122
121
|
`renderToString` builds HTML without a DOM — no polyfill in Node, Bun, or edge runtimes.
|
|
123
122
|
|
|
@@ -138,31 +137,15 @@ Event props are lowercase (`onclick`, `onchange`). Use `class`, not `className`.
|
|
|
138
137
|
|
|
139
138
|
---
|
|
140
139
|
|
|
141
|
-
##
|
|
140
|
+
## Recipes
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
import { atom } from "ilha";
|
|
145
|
-
import { define } from "ilha/define";
|
|
146
|
-
|
|
147
|
-
define("ilha-counter", () => {
|
|
148
|
-
const count = atom(0);
|
|
149
|
-
return (
|
|
150
|
-
<button type="button" onclick={() => count.update((n: number) => n + 1)}>
|
|
151
|
-
{count}
|
|
152
|
-
</button>
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
```html
|
|
158
|
-
<ilha-counter data-ilha></ilha-counter>
|
|
159
|
-
```
|
|
142
|
+
Data fetching, server-sent events, and custom elements are short Effect recipes rather than exports: see [ilha.build/guide/recipes](https://ilha.build/guide/recipes/data-fetching).
|
|
160
143
|
|
|
161
144
|
---
|
|
162
145
|
|
|
163
|
-
##
|
|
146
|
+
## Void and Astro
|
|
164
147
|
|
|
165
|
-
- [`@ilha/
|
|
148
|
+
- [`@ilha/void`](https://github.com/ilhajs/ilha/tree/main/packages/void) — Void pages, layouts, forms, and islands.
|
|
166
149
|
- [`@ilha/astro`](https://github.com/ilhajs/ilha/tree/main/packages/astro) — Astro renderer (`renderToString` + `mount`).
|
|
167
150
|
|
|
168
151
|
Docs: [ilha.build](https://ilha.build)
|
package/dist/debug.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { t as SnapshotValue } from "./snapshot-BDC74QwI.js";
|
|
2
|
+
//#region src/debug.d.ts
|
|
3
|
+
/** An atom value made JSON-safe: functions and Effects become `<…>` strings. */
|
|
4
|
+
export type InspectValue = SnapshotValue | string;
|
|
5
|
+
export interface IlhaInspectAtom {
|
|
6
|
+
/** Position among the component's `atom()` slots, in declaration order. */
|
|
7
|
+
readonly index: number;
|
|
8
|
+
readonly value: InspectValue;
|
|
9
|
+
}
|
|
10
|
+
export interface IlhaInspectNode {
|
|
11
|
+
/** Stable while the component lives; pass it to `setAtom` and `element`. */
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly component: string;
|
|
14
|
+
/** CSS-like description of the host element; absent for non-DOM roots. */
|
|
15
|
+
readonly host?: string;
|
|
16
|
+
readonly atoms: readonly IlhaInspectAtom[];
|
|
17
|
+
readonly children: readonly IlhaInspectNode[];
|
|
18
|
+
}
|
|
19
|
+
export type SetAtomResult = {
|
|
20
|
+
ok: true;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
reason: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* JSON-serializable trees of the live roots: component names, atom values,
|
|
27
|
+
* and child components. Pass `host` to inspect one root or component host.
|
|
28
|
+
*/
|
|
29
|
+
export declare const inspect: (host?: Element) => IlhaInspectNode[];
|
|
30
|
+
/** Host elements of the live DOM roots. SSR and custom-renderer roots have none. */
|
|
31
|
+
export declare const roots: () => Element[];
|
|
32
|
+
/** The element an inspected component paints into, to highlight or measure. */
|
|
33
|
+
export declare const element: (id: string) => Element | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Replace atom `index` of component `id` with a JSON `value`. Only writable
|
|
36
|
+
* state atoms accept it; derived atoms and Effect atoms report why they cannot.
|
|
37
|
+
*/
|
|
38
|
+
export declare const setAtom: <T extends SnapshotValue>(id: string, index: number, value: T) => SetAtomResult;
|
|
39
|
+
/**
|
|
40
|
+
* Call `listener` after any root settles, mounts, or unmounts. The listener
|
|
41
|
+
* runs synchronously, so coalesce in it if you re-read the whole tree.
|
|
42
|
+
* Returns an unsubscribe function.
|
|
43
|
+
*/
|
|
44
|
+
export declare const onChange: (listener: () => void) => (() => void);
|
|
45
|
+
//#endregion
|