ilha 0.10.3 → 0.11.1
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/README.md +5 -2
- package/dist/{index-BDM797j2.d.ts → index-DnoM0M8S.d.ts} +31 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +302 -165
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/{jsx-runtime-C6R6IiOD.d.ts → jsx-runtime-DMkplizR.d.ts} +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -331,7 +331,7 @@ Both the user-supplied cleanup function (if any) and the signal abort fire when
|
|
|
331
331
|
|
|
332
332
|
### `.onMount(fn)`
|
|
333
333
|
|
|
334
|
-
Runs once after the island is mounted into the DOM. Receives `{ state, derived, input, host, hydrated }` where `hydrated` is `true` when the island was mounted over existing SSR content. Optionally returns a cleanup function called on unmount.
|
|
334
|
+
Runs once after the island is mounted into the DOM. **Client-only** — SSR never invokes it (matching `.on()` and `.effect()`), so server-rendered markup must not depend on onMount side effects. Receives `{ state, derived, input, host, hydrated }` where `hydrated` is `true` when the island was mounted over existing SSR content. Optionally returns a cleanup function called on unmount.
|
|
335
335
|
|
|
336
336
|
```ts
|
|
337
337
|
ilha
|
|
@@ -538,7 +538,8 @@ Use `await island(props)` instead when asynchronous derived values must settle b
|
|
|
538
538
|
```ts
|
|
539
539
|
MyIsland.toString(); // always sync
|
|
540
540
|
MyIsland.toString({ name: "Ilha" }); // with props
|
|
541
|
-
await MyIsland({ name: "Ilha" }); // async — awaits derived
|
|
541
|
+
await MyIsland.toStringAsync({ name: "Ilha" }); // async SSR — awaits async derived values
|
|
542
|
+
await MyIsland({ name: "Ilha" }); // callable async form (also used for JSX/html slot composition)
|
|
542
543
|
```
|
|
543
544
|
|
|
544
545
|
---
|
|
@@ -666,6 +667,8 @@ theme("dark"); // → sets to "dark"
|
|
|
666
667
|
Safe to call in both SSR and browser environments.
|
|
667
668
|
|
|
668
669
|
> **`signal()` vs `context()`** — both return the same accessor shape and can be used with `bind:` template syntax. Use `signal()` for one-off shared state where you'd hold the reference yourself; use `context()` when you want a name-keyed registry so the same signal can be looked up from anywhere by string key.
|
|
670
|
+
>
|
|
671
|
+
> **SSR caveat** — `context()` is process-global, not request-scoped. It is safe to read in SSR **only for app-wide singletons** (theme, locale, feature flags). Per-request state (current user, session) must not live in `context()`: concurrent SSR requests would leak it across requests. Pass per-request data through island props / loaders or use `@ilha/router`'s request-scope instead.
|
|
669
672
|
|
|
670
673
|
---
|
|
671
674
|
|
|
@@ -178,6 +178,12 @@ interface HydratableOptions {
|
|
|
178
178
|
interface Island<TInput = Record<string, unknown>, _TStateMap extends Record<string, unknown> = Record<string, unknown>> {
|
|
179
179
|
(props?: Partial<TInput>): string | Promise<string>;
|
|
180
180
|
toString(props?: Partial<TInput>): string;
|
|
181
|
+
/**
|
|
182
|
+
* Async SSR: renders the island and awaits async `.derived()` values before
|
|
183
|
+
* returning the HTML string. Always returns a Promise — prefer this over
|
|
184
|
+
* `await island(props)` when you need the async SSR form by name.
|
|
185
|
+
*/
|
|
186
|
+
toStringAsync(props?: Partial<TInput>): Promise<string>;
|
|
181
187
|
mount(host: Element, props?: Partial<TInput>): () => void;
|
|
182
188
|
hydratable(props: Partial<TInput>, options: HydratableOptions): Promise<string>;
|
|
183
189
|
key(key: string): KeyedIsland<TInput>;
|
|
@@ -285,6 +291,20 @@ interface StateEntry<TInput> {
|
|
|
285
291
|
key: string;
|
|
286
292
|
init: StateInit<TInput, unknown>;
|
|
287
293
|
}
|
|
294
|
+
/** A state key fed by a generator (`.stream()`). The runtime iterates the
|
|
295
|
+
* generator and writes each yielded value into the state signal. During SSR
|
|
296
|
+
* only the first value is pulled so it can render inline; on the client the
|
|
297
|
+
* generator is consumed continuously. */
|
|
298
|
+
interface StreamEntry<TInput> {
|
|
299
|
+
key: string;
|
|
300
|
+
fn: StreamFn<TInput>;
|
|
301
|
+
}
|
|
302
|
+
type StreamFnContext<TInput> = {
|
|
303
|
+
input: TInput;
|
|
304
|
+
/** Aborts when the island unmounts. Pass through to fetch/SSE layers. */
|
|
305
|
+
signal: AbortSignal;
|
|
306
|
+
};
|
|
307
|
+
type StreamFn<TInput, V = unknown> = (ctx: StreamFnContext<TInput>) => AsyncGenerator<V> | Generator<V>;
|
|
288
308
|
interface ActionEntry<TInput, TStateMap extends Record<string, unknown>, TDerivedMap extends Record<string, unknown>> {
|
|
289
309
|
key: string;
|
|
290
310
|
fn: (props: unknown, ctx: ActionContext<TInput, TStateMap, TDerivedMap>) => unknown;
|
|
@@ -302,7 +322,7 @@ interface EffectEntry<TInput, TStateMap extends Record<string, unknown>, TDerive
|
|
|
302
322
|
/** Where the error originated. `"on"` covers sync throws and async rejections
|
|
303
323
|
* from `.on()` handlers; `"effect"` covers sync throws from `.effect()` runs
|
|
304
324
|
* (async work spawned inside an effect is not awaited by the runtime). */
|
|
305
|
-
type ErrorSource = "on" | "effect" | "mount" | "transition" | "action";
|
|
325
|
+
type ErrorSource = "on" | "effect" | "mount" | "transition" | "action" | "stream";
|
|
306
326
|
/**
|
|
307
327
|
* Register a global error handler invoked when any island reports an error
|
|
308
328
|
* (from .on, .effect, .onMount, or transitions) and has no local .onError()
|
|
@@ -341,6 +361,7 @@ interface BuilderConfig<TInput, TStateMap extends Record<string, unknown>, TDeri
|
|
|
341
361
|
/** Shallow defaults merged before props (POJO `.input({ ... })` only). */
|
|
342
362
|
defaultInput: Record<string, unknown> | null;
|
|
343
363
|
states: StateEntry<TInput>[];
|
|
364
|
+
streams: StreamEntry<TInput>[];
|
|
344
365
|
deriveds: DerivedEntry<TInput, TStateMap>[];
|
|
345
366
|
actions: ActionEntry<TInput, TStateMap, TDerivedMap>[];
|
|
346
367
|
ons: OnEntry<TInput, TStateMap, TDerivedMap, TActionMap>[];
|
|
@@ -360,6 +381,7 @@ declare class IlhaBuilder<TInput extends Record<string, unknown>, TStateMap exte
|
|
|
360
381
|
input<T extends Record<string, unknown>>(defaults: T): IlhaBuilder<T, Record<never, never>, Record<never, never>, Record<never, never>>;
|
|
361
382
|
as<Tag extends string>(tag: Tag): IlhaBuilder<TInput, TStateMap, TDerivedMap, TActionMap>;
|
|
362
383
|
state<V = undefined, K extends string = string>(key: K, init?: StateInit<TInput, V> | undefined): IlhaBuilder<TInput, MergeState<TStateMap, K, V>, TDerivedMap, TActionMap>;
|
|
384
|
+
stream<K extends string, V>(key: K, fn: StreamFn<TInput, V>): IlhaBuilder<TInput, MergeState<TStateMap, K, V>, TDerivedMap, TActionMap>;
|
|
363
385
|
derived<K extends string, V>(key: K, fn: DerivedFn<TInput, TStateMap, V>): IlhaBuilder<TInput, TStateMap, TDerivedMap & Record<K, V>, TActionMap>;
|
|
364
386
|
action<K extends string, P = undefined, R = void>(key: K, fn: (props: P, ctx: ActionContext<TInput, TStateMap, TDerivedMap>) => R): IlhaBuilder<TInput, TStateMap, TDerivedMap, TActionMap & Record<K, typeof fn>>;
|
|
365
387
|
on<S extends string>(selectorOrCombined: S, handler: (ctx: S extends `${string}@${infer E}:${string}` ? HandlerContextFor<TInput, TStateMap, E, TDerivedMap, TActionMap> : S extends `${string}@${infer E}` ? HandlerContextFor<TInput, TStateMap, E, TDerivedMap, TActionMap> : HandlerContext<TInput, TStateMap, TDerivedMap, TActionMap>) => void | Promise<void>): IlhaBuilder<TInput, TStateMap, TDerivedMap, TActionMap>;
|
|
@@ -392,6 +414,7 @@ declare const ilha: RootBuilder & DirectIslandFactory & {
|
|
|
392
414
|
};
|
|
393
415
|
signal: typeof ilhaSignal;
|
|
394
416
|
computed: typeof ilhaComputed;
|
|
417
|
+
morph: typeof morph;
|
|
395
418
|
batch: typeof batch;
|
|
396
419
|
untrack: typeof untrack;
|
|
397
420
|
onUncaughtError: typeof onUncaughtError;
|
|
@@ -408,6 +431,12 @@ declare function __ilhaJsxSlot({ island, props, key }: {
|
|
|
408
431
|
props: Record<string, unknown> | undefined;
|
|
409
432
|
key: string | undefined;
|
|
410
433
|
}): RawHtml;
|
|
434
|
+
/**
|
|
435
|
+
* Morph an element's children toward `html`, patching in place: surviving
|
|
436
|
+
* elements keep their identity, listeners, and focus (see the morph engine).
|
|
437
|
+
* Falls back to an innerHTML write when the root tags don't match.
|
|
438
|
+
*/
|
|
439
|
+
declare function morph(host: Element, html: string): void;
|
|
411
440
|
declare const html: typeof ilhaHtml;
|
|
412
441
|
declare const raw: typeof ilhaRaw;
|
|
413
442
|
declare const css: typeof ilhaCss;
|
|
@@ -420,4 +449,4 @@ declare const context: typeof ilhaContextFn & {
|
|
|
420
449
|
clear(): void;
|
|
421
450
|
};
|
|
422
451
|
//#endregion
|
|
423
|
-
export {
|
|
452
|
+
export { StreamFnContext as A, ilhaEffect as B, NativeEventModifier as C, SignalSetter as D, SignalAccessor as E, css as F, raw as G, morph as H, from as I, untrack as K, html as L, __ilhaJsxSlot as M, batch as N, SignalWriter as O, context as P, ilha as R, NativeEventHandler as S, RawHtml as T, mount as U, ilhaSignal as V, onUncaughtError as W, IslandState as _, EffectContext as a, MountResult as b, ExternalSignal as c, HydratableOptions as d, ISLAND_MOUNT_HANDLES as f, IslandDerived as g, IslandActions as h, DerivedValue as i, __ilhaJsxEvent as j, StreamFn as k, HandlerContext as l, Island as m, ActionContext as n, ErrorContext as o, ISLAND_MOUNT_INTERNAL as p, DerivedAccessor as r, ErrorSource as s, ActionAccessor as t, HandlerContextFor as u, KeyedIsland as v, OnMountContext as w, NativeEventContext as x, MountOptions as y, ilhaComputed as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { ActionAccessor, ActionContext, DerivedAccessor, DerivedValue, EffectContext, ErrorContext, ErrorSource, ExternalSignal, HandlerContext, HandlerContextFor, HydratableOptions, ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, Island, IslandActions, IslandDerived, IslandState, KeyedIsland, MountOptions, MountResult, NativeEventContext, NativeEventHandler, NativeEventModifier, OnMountContext, RawHtml, SignalAccessor, SignalSetter, SignalWriter, __ilhaJsxEvent, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css,
|
|
1
|
+
import { A as StreamFnContext, B as ilhaEffect, C as NativeEventModifier, D as SignalSetter, E as SignalAccessor, F as css, G as raw, H as morph, I as from, K as untrack, L as html, M as __ilhaJsxSlot, N as batch, O as SignalWriter, P as context, R as ilha, S as NativeEventHandler, T as RawHtml, U as mount, V as ilhaSignal, W as onUncaughtError, _ as IslandState, a as EffectContext, b as MountResult, c as ExternalSignal, d as HydratableOptions, f as ISLAND_MOUNT_HANDLES, g as IslandDerived, h as IslandActions, i as DerivedValue, j as __ilhaJsxEvent, k as StreamFn, l as HandlerContext, m as Island, n as ActionContext, o as ErrorContext, p as ISLAND_MOUNT_INTERNAL, r as DerivedAccessor, s as ErrorSource, t as ActionAccessor, u as HandlerContextFor, v as KeyedIsland, w as OnMountContext, x as NativeEventContext, y as MountOptions, z as ilhaComputed } from "./index-DnoM0M8S.js";
|
|
2
|
+
export { ActionAccessor, ActionContext, DerivedAccessor, DerivedValue, EffectContext, ErrorContext, ErrorSource, ExternalSignal, HandlerContext, HandlerContextFor, HydratableOptions, ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, Island, IslandActions, IslandDerived, IslandState, KeyedIsland, MountOptions, MountResult, NativeEventContext, NativeEventHandler, NativeEventModifier, OnMountContext, RawHtml, SignalAccessor, SignalSetter, SignalWriter, StreamFn, StreamFnContext, __ilhaJsxEvent, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css, ilhaEffect as effect, from, html, ilha, ilhaSignal, ilhaSignal as signal, morph, mount, onUncaughtError, raw, untrack };
|
package/dist/index.js
CHANGED
|
@@ -127,13 +127,21 @@ function isStableInlineSlotMount({ initialHtml, renderedHtml, slotIds }) {
|
|
|
127
127
|
* map (client) and/or the inlined SSR subtree — never depend on the attr for them.
|
|
128
128
|
* RawHtml is tagged so a rare attr-only mount can revive `Symbol.for("ilha.raw")`.
|
|
129
129
|
*/
|
|
130
|
-
function slotPropsForAttr(props) {
|
|
130
|
+
function slotPropsForAttr(props, captureActions = false) {
|
|
131
131
|
if (props === void 0) return void 0;
|
|
132
132
|
let out;
|
|
133
133
|
for (const key of Object.keys(props)) {
|
|
134
134
|
if (key === "children") continue;
|
|
135
135
|
const value = props[key];
|
|
136
|
-
if (typeof value === "function"
|
|
136
|
+
if (typeof value === "function") {
|
|
137
|
+
const action = captureActions ? captureHandlerActions(value) : void 0;
|
|
138
|
+
if (action) (out ??= {})[key] = {
|
|
139
|
+
__ilha: "action",
|
|
140
|
+
...action
|
|
141
|
+
};
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (typeof value === "symbol") continue;
|
|
137
145
|
const encoded = encodeSlotPropValue(value);
|
|
138
146
|
if (encoded === void 0 && value !== void 0 && value !== null) continue;
|
|
139
147
|
(out ??= {})[key] = encoded;
|
|
@@ -471,11 +479,25 @@ const ASTRO_RENDERER_GLOBAL = Symbol.for("ilha.astroRenderer");
|
|
|
471
479
|
* re-renders. Not part of the public surface. */
|
|
472
480
|
const ISLAND_MOUNT_INTERNAL = Symbol.for("ilha.islandMountInternal");
|
|
473
481
|
const ISLAND_SLOT_TAG = Symbol.for("ilha.islandSlotTag");
|
|
482
|
+
const ISLAND_CLIENT_REF = Symbol.for("ilha.clientRef");
|
|
474
483
|
/** @internal Live mount handles keyed by host element. Lets @ilha/router adopt
|
|
475
484
|
* islands hydrated by `ilha.mount()` (whose handles it never saw) and push new
|
|
476
485
|
* loader props into them in place instead of remounting. Entries are removed
|
|
477
486
|
* on unmount. Not part of the public surface. */
|
|
478
487
|
const ISLAND_MOUNT_HANDLES = /* @__PURE__ */ new WeakMap();
|
|
488
|
+
/** @internal Sync re-render of an island from a state snapshot. Attached by
|
|
489
|
+
* render(); used by server-frame endpoints to produce HTML for streamed
|
|
490
|
+
* updates without a live mount. Not part of the public surface. */
|
|
491
|
+
const ISLAND_RENDER_STATE = Symbol.for("ilha.renderState");
|
|
492
|
+
/** @internal Async SSR render that records the event→action manifest into the
|
|
493
|
+
* provided map. Used by emitIslandSlot so nested server islands ship their
|
|
494
|
+
* interactive surface alongside their inlined HTML. */
|
|
495
|
+
const ISLAND_SSR_MANIFEST = Symbol.for("ilha.ssrManifest");
|
|
496
|
+
function manifestTemplatePrefix(manifest) {
|
|
497
|
+
if (manifest.size === 0) return "";
|
|
498
|
+
const json = escapeHtml(JSON.stringify(Object.fromEntries(manifest)));
|
|
499
|
+
return `<template ${ACTIONS_ATTR}='${json}'></template>`;
|
|
500
|
+
}
|
|
479
501
|
const SLOT_ATTR = "data-ilha-slot";
|
|
480
502
|
/** Marks a slot host whose child island is mid leave-transition: it has been
|
|
481
503
|
* unmounted (and its slot id stripped) but stays connected so the leave
|
|
@@ -483,25 +505,63 @@ const SLOT_ATTR = "data-ilha-slot";
|
|
|
483
505
|
* or mutating them. */
|
|
484
506
|
const LEAVING_ATTR = "data-ilha-leaving";
|
|
485
507
|
const PROPS_ATTR = "data-ilha-props";
|
|
508
|
+
const CLIENT_REF_ATTR = "data-ilha-client-ref";
|
|
486
509
|
const STATE_ATTR = "data-ilha-state";
|
|
487
510
|
const CSS_ATTR = "data-ilha-css";
|
|
511
|
+
/** Hydration manifest mapping `${type}:${index}` event-sentinel pairs to the
|
|
512
|
+
* action keys that own them. Emitted by hydratable(); lets server-owned
|
|
513
|
+
* islands (whose render fn never ships to the client) reconnect their
|
|
514
|
+
* interactive surface. */
|
|
515
|
+
const ACTIONS_ATTR = "data-ilha-actions";
|
|
488
516
|
function buildScopedStyle(css) {
|
|
489
517
|
const safeCss = css.replace(/<\/style/gi, "<\\/style");
|
|
490
518
|
return `<style ${CSS_ATTR}>@scope (:scope) to ([data-ilha]){${safeCss}}</style>`;
|
|
491
519
|
}
|
|
520
|
+
/** Marks SSR action stubs so event registration never capture-invokes them
|
|
521
|
+
* (invoking the real action body during render would run its side effects). */
|
|
522
|
+
const SSR_ACTION_STUB = Symbol.for("ilha.ssrActionStub");
|
|
523
|
+
const captureStack = [];
|
|
524
|
+
/**
|
|
525
|
+
* Invoke a forwarding closure once against sentinel arguments so any
|
|
526
|
+
* `action.x(...)` call inside it is RECORDED, not executed. Returns the first
|
|
527
|
+
* recorded call, or undefined when the closure throws or calls no action.
|
|
528
|
+
*
|
|
529
|
+
* ponytail: the closure body really executes once per manifest render (SSR
|
|
530
|
+
* and every frame) — only the action call is intercepted. Handlers must be
|
|
531
|
+
* pure "call one action" thunks; side effects before/around the action call
|
|
532
|
+
* run server-side on every frame. Upgrade path: static-scan the closure
|
|
533
|
+
* instead of invoking it.
|
|
534
|
+
*/
|
|
535
|
+
function captureHandlerActions(handler) {
|
|
536
|
+
const frame = [];
|
|
537
|
+
captureStack.push(frame);
|
|
538
|
+
try {
|
|
539
|
+
const result = handler({
|
|
540
|
+
preventDefault: () => {},
|
|
541
|
+
stopPropagation: () => {},
|
|
542
|
+
stopImmediatePropagation: () => {},
|
|
543
|
+
persist: () => {}
|
|
544
|
+
}, { signal: new AbortController().signal });
|
|
545
|
+
Promise.resolve(result).catch(() => {});
|
|
546
|
+
} catch {} finally {
|
|
547
|
+
captureStack.pop();
|
|
548
|
+
}
|
|
549
|
+
return frame[0];
|
|
550
|
+
}
|
|
492
551
|
const RENDER_CTX_STACK = Symbol.for("ilha.renderCtxStack");
|
|
493
552
|
function renderCtxStack() {
|
|
494
553
|
const g = globalThis;
|
|
495
554
|
return g[RENDER_CTX_STACK] ??= [];
|
|
496
555
|
}
|
|
497
|
-
function pushRenderCtx(liveHost, asyncChildren) {
|
|
556
|
+
function pushRenderCtx(liveHost, asyncChildren, manifest) {
|
|
498
557
|
const ctx = {
|
|
499
558
|
slots: /* @__PURE__ */ new Map(),
|
|
500
559
|
positional: 0,
|
|
501
560
|
liveHost,
|
|
502
561
|
pending: asyncChildren ? /* @__PURE__ */ new Map() : void 0,
|
|
503
562
|
binds: [],
|
|
504
|
-
events: []
|
|
563
|
+
events: [],
|
|
564
|
+
manifest
|
|
505
565
|
};
|
|
506
566
|
renderCtxStack().push(ctx);
|
|
507
567
|
return ctx;
|
|
@@ -531,8 +591,9 @@ function emitIslandSlot({ island, props, key }) {
|
|
|
531
591
|
props
|
|
532
592
|
});
|
|
533
593
|
const slotTag = getIslandSlotTag(island);
|
|
534
|
-
const
|
|
535
|
-
const
|
|
594
|
+
const clientRef = island[ISLAND_CLIENT_REF];
|
|
595
|
+
const attrProps = slotPropsForAttr(props, typeof clientRef === "string" && ctx?.manifest === true);
|
|
596
|
+
const propsAttr = (attrProps ? ` ${PROPS_ATTR}='${escapeHtml(JSON.stringify(attrProps))}'` : "") + (typeof clientRef === "string" ? ` ${CLIENT_REF_ATTR}="${escapeHtml(clientRef)}"` : "");
|
|
536
597
|
if (ctx?.liveHost) return wrapIslandSlotHtml({
|
|
537
598
|
tag: slotTag,
|
|
538
599
|
id,
|
|
@@ -542,9 +603,12 @@ function emitIslandSlot({ island, props, key }) {
|
|
|
542
603
|
if (ctx?.pending) {
|
|
543
604
|
popRenderCtx();
|
|
544
605
|
try {
|
|
545
|
-
const
|
|
606
|
+
const manifest = /* @__PURE__ */ new Map();
|
|
607
|
+
const ssrWithManifest = island[ISLAND_SSR_MANIFEST];
|
|
608
|
+
const result = ssrWithManifest ? ssrWithManifest(props, manifest) : island(props);
|
|
609
|
+
const prefix = () => manifestTemplatePrefix(manifest);
|
|
546
610
|
if (result instanceof Promise) {
|
|
547
|
-
ctx.pending.set(id, result.then(String));
|
|
611
|
+
ctx.pending.set(id, result.then(String).then((html) => prefix() + html));
|
|
548
612
|
return wrapIslandSlotHtml({
|
|
549
613
|
tag: slotTag,
|
|
550
614
|
id,
|
|
@@ -556,7 +620,7 @@ function emitIslandSlot({ island, props, key }) {
|
|
|
556
620
|
tag: slotTag,
|
|
557
621
|
id,
|
|
558
622
|
propsAttr,
|
|
559
|
-
inner: String(result)
|
|
623
|
+
inner: prefix() + String(result)
|
|
560
624
|
});
|
|
561
625
|
} finally {
|
|
562
626
|
renderCtxStack().push(ctx);
|
|
@@ -853,6 +917,8 @@ function ilhaHtml(strings, ...values) {
|
|
|
853
917
|
const modifier = NATIVE_EVENT_MODIFIERS.has(rawModifier) ? rawModifier : void 0;
|
|
854
918
|
const openQuote = eventMatch[3] ?? null;
|
|
855
919
|
const cleanPrefix = chunk.slice(0, eventMatchStart).replace(/\s+$/, "");
|
|
920
|
+
const isActionStub = typeof value === "function" && SSR_ACTION_STUB in value;
|
|
921
|
+
const capture = ctx?.manifest && typeof value === "function" && !isActionStub ? captureHandlerActions(value) : void 0;
|
|
856
922
|
result += cleanPrefix;
|
|
857
923
|
if (ctx && typeof value === "function") {
|
|
858
924
|
const index = ctx.events.length;
|
|
@@ -860,7 +926,8 @@ function ilhaHtml(strings, ...values) {
|
|
|
860
926
|
ctx.events.push({
|
|
861
927
|
type: eventName,
|
|
862
928
|
handler: value,
|
|
863
|
-
modifier
|
|
929
|
+
modifier,
|
|
930
|
+
capture
|
|
864
931
|
});
|
|
865
932
|
pendingEventSpecs += (pendingEventSpecs ? "," : "") + `${eventName}:${index}`;
|
|
866
933
|
}
|
|
@@ -1623,12 +1690,15 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1623
1690
|
input(initialOrSchema) {
|
|
1624
1691
|
let schema = null;
|
|
1625
1692
|
let defaultInput = null;
|
|
1626
|
-
if (initialOrSchema !== void 0)
|
|
1627
|
-
|
|
1693
|
+
if (initialOrSchema !== void 0) {
|
|
1694
|
+
if (isStandardSchema(initialOrSchema)) schema = initialOrSchema;
|
|
1695
|
+
else defaultInput = initialOrSchema;
|
|
1696
|
+
}
|
|
1628
1697
|
return new IlhaBuilder({
|
|
1629
1698
|
schema,
|
|
1630
1699
|
defaultInput,
|
|
1631
1700
|
states: [],
|
|
1701
|
+
streams: [],
|
|
1632
1702
|
deriveds: [],
|
|
1633
1703
|
actions: [],
|
|
1634
1704
|
ons: [],
|
|
@@ -1656,6 +1726,21 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1656
1726
|
}]
|
|
1657
1727
|
});
|
|
1658
1728
|
}
|
|
1729
|
+
stream(key, fn) {
|
|
1730
|
+
const cfg = this._cfg;
|
|
1731
|
+
const states = cfg.states.some((entry) => entry.key === key) ? cfg.states : [...cfg.states, {
|
|
1732
|
+
key,
|
|
1733
|
+
init: void 0
|
|
1734
|
+
}];
|
|
1735
|
+
return new IlhaBuilder({
|
|
1736
|
+
...cfg,
|
|
1737
|
+
states,
|
|
1738
|
+
streams: [...cfg.streams.filter((entry) => entry.key !== key), {
|
|
1739
|
+
key,
|
|
1740
|
+
fn
|
|
1741
|
+
}]
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1659
1744
|
derived(key, fn) {
|
|
1660
1745
|
const cfg = this._cfg;
|
|
1661
1746
|
return new IlhaBuilder({
|
|
@@ -1729,7 +1814,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1729
1814
|
});
|
|
1730
1815
|
}
|
|
1731
1816
|
render(fn) {
|
|
1732
|
-
const { schema, defaultInput, states, deriveds, actions, ons, effects, onMounts, onErrors, transition, css: cssSource, as: slotAs } = this._cfg;
|
|
1817
|
+
const { schema, defaultInput, states, streams, deriveds, actions, ons, effects, onMounts, onErrors, transition, css: cssSource, as: slotAs } = this._cfg;
|
|
1733
1818
|
const stylePrefix = cssSource == null ? "" : buildScopedStyle(cssSource);
|
|
1734
1819
|
const configuredSlotTag = slotAs ?? "div";
|
|
1735
1820
|
function resolveInput(props) {
|
|
@@ -1740,8 +1825,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1740
1825
|
if (!schema) return merged;
|
|
1741
1826
|
return validateSchema(schema, merged);
|
|
1742
1827
|
}
|
|
1743
|
-
function renderWithCtx({ render, liveHost, asyncChildren }) {
|
|
1744
|
-
const ctx = pushRenderCtx(liveHost, asyncChildren);
|
|
1828
|
+
function renderWithCtx({ render, liveHost, asyncChildren, manifest }) {
|
|
1829
|
+
const ctx = pushRenderCtx(liveHost, asyncChildren, manifest);
|
|
1745
1830
|
try {
|
|
1746
1831
|
const html = unwrapHtml(render());
|
|
1747
1832
|
const slots = ctx.slots;
|
|
@@ -1794,8 +1879,19 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1794
1879
|
}
|
|
1795
1880
|
function buildSsrActions() {
|
|
1796
1881
|
const out = {};
|
|
1882
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
1797
1883
|
for (const entry of actions) {
|
|
1798
|
-
const invoke = () => {
|
|
1884
|
+
const invoke = (...callArgs) => {
|
|
1885
|
+
const frame = captureStack[captureStack.length - 1];
|
|
1886
|
+
if (frame) {
|
|
1887
|
+
if (!frame.some((r) => r.k === entry.key)) frame.push({
|
|
1888
|
+
k: entry.key,
|
|
1889
|
+
a: callArgs
|
|
1890
|
+
});
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
invoke[SSR_ACTION_STUB] = true;
|
|
1799
1895
|
Object.defineProperties(invoke, {
|
|
1800
1896
|
pending: {
|
|
1801
1897
|
get: () => false,
|
|
@@ -1811,80 +1907,52 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1811
1907
|
}
|
|
1812
1908
|
});
|
|
1813
1909
|
out[entry.key] = invoke;
|
|
1910
|
+
lookup.set(invoke, entry.key);
|
|
1814
1911
|
}
|
|
1815
|
-
return
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
function createSsrOnMountHost() {
|
|
1819
|
-
if (typeof document !== "undefined") return document.createElement("div");
|
|
1820
|
-
const stub = {
|
|
1821
|
-
matches: () => false,
|
|
1822
|
-
querySelector: () => null,
|
|
1823
|
-
querySelectorAll: () => [],
|
|
1824
|
-
setAttribute: () => {},
|
|
1825
|
-
getAttribute: () => null,
|
|
1826
|
-
removeAttribute: () => {},
|
|
1827
|
-
appendChild: () => stub
|
|
1912
|
+
return {
|
|
1913
|
+
action: out,
|
|
1914
|
+
lookup
|
|
1828
1915
|
};
|
|
1829
|
-
return stub;
|
|
1830
1916
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
} catch (err) {
|
|
1852
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1853
|
-
if (onErrors.length === 0) {
|
|
1854
|
-
if (!reportToGlobal(error, "mount")) console.error(error);
|
|
1855
|
-
} else for (const oe of onErrors) try {
|
|
1856
|
-
oe.fn({
|
|
1857
|
-
error,
|
|
1858
|
-
source: "mount",
|
|
1859
|
-
state,
|
|
1860
|
-
derived,
|
|
1861
|
-
action,
|
|
1917
|
+
function renderToString(props, sync = false, streamOut, eventsOut, stateOverride, forceAsyncChildren = false) {
|
|
1918
|
+
const input = resolveInput(props);
|
|
1919
|
+
const state = buildSignalState(input, stateOverride);
|
|
1920
|
+
const { action, lookup: actionLookup } = buildSsrActions();
|
|
1921
|
+
const recordEventsManifest = (events) => {
|
|
1922
|
+
if (!eventsOut || events.length === 0) return;
|
|
1923
|
+
events.forEach((record, index) => {
|
|
1924
|
+
if (record.capture) {
|
|
1925
|
+
eventsOut.set(`${record.type}:${index}`, record.capture);
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
const key = actionLookup.get(record.handler);
|
|
1929
|
+
if (key) eventsOut.set(`${record.type}:${index}`, key);
|
|
1930
|
+
});
|
|
1931
|
+
};
|
|
1932
|
+
const pullStreamInitials = async (streamOut) => {
|
|
1933
|
+
for (const entry of streams) {
|
|
1934
|
+
const controller = new AbortController();
|
|
1935
|
+
try {
|
|
1936
|
+
const gen = await entry.fn({
|
|
1862
1937
|
input,
|
|
1863
|
-
|
|
1938
|
+
signal: controller.signal
|
|
1864
1939
|
});
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
if (onErrors.length === 0) {
|
|
1879
|
-
if (!reportToGlobal(error, "mount")) console.error(error);
|
|
1940
|
+
const first = await gen.next();
|
|
1941
|
+
controller.abort();
|
|
1942
|
+
await gen.return?.(void 0);
|
|
1943
|
+
if (first.done) continue;
|
|
1944
|
+
if (streamOut) streamOut[entry.key] = first.value;
|
|
1945
|
+
const prevSub = setActiveSub(void 0);
|
|
1946
|
+
try {
|
|
1947
|
+
const setter = state[entry.key];
|
|
1948
|
+
setter?.(first.value);
|
|
1949
|
+
} finally {
|
|
1950
|
+
setActiveSub(prevSub);
|
|
1951
|
+
}
|
|
1952
|
+
} catch {}
|
|
1880
1953
|
}
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
function renderToString(props, sync = false) {
|
|
1884
|
-
const input = resolveInput(props);
|
|
1885
|
-
const state = buildSignalState(input);
|
|
1886
|
-
const action = buildSsrActions();
|
|
1887
|
-
const results = deriveds.map((entry) => {
|
|
1954
|
+
};
|
|
1955
|
+
const computeDerivedResults = () => deriveds.map((entry) => {
|
|
1888
1956
|
const prevSub = setActiveSub(void 0);
|
|
1889
1957
|
try {
|
|
1890
1958
|
return {
|
|
@@ -1904,84 +1972,83 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1904
1972
|
setActiveSub(prevSub);
|
|
1905
1973
|
}
|
|
1906
1974
|
});
|
|
1907
|
-
if (!
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
error: void 0
|
|
1918
|
-
};
|
|
1919
|
-
const derived = buildDerivedAccessors(envelopes);
|
|
1920
|
-
if (!currentRenderCtx()) runOnMountForRender({
|
|
1921
|
-
input,
|
|
1922
|
-
state,
|
|
1923
|
-
derived,
|
|
1924
|
-
action
|
|
1925
|
-
});
|
|
1926
|
-
const prevSub = setActiveSub(void 0);
|
|
1927
|
-
try {
|
|
1928
|
-
const { html } = renderWithCtx({ render: () => fn({
|
|
1929
|
-
state,
|
|
1930
|
-
derived,
|
|
1931
|
-
action,
|
|
1932
|
-
input
|
|
1933
|
-
}) });
|
|
1934
|
-
return stylePrefix + html;
|
|
1935
|
-
} finally {
|
|
1936
|
-
setActiveSub(prevSub);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
return Promise.all(results.map(async (r) => {
|
|
1940
|
-
try {
|
|
1941
|
-
return {
|
|
1942
|
-
key: r.key,
|
|
1943
|
-
envelope: {
|
|
1944
|
-
loading: false,
|
|
1945
|
-
value: await Promise.resolve(r.result),
|
|
1946
|
-
error: void 0
|
|
1947
|
-
}
|
|
1975
|
+
if (!sync && (streams.length > 0 || forceAsyncChildren)) return pullStreamInitials(streamOut).then(() => renderToStringTail());
|
|
1976
|
+
return renderToStringTail();
|
|
1977
|
+
function renderToStringTail() {
|
|
1978
|
+
const results = computeDerivedResults();
|
|
1979
|
+
if (!(forceAsyncChildren || results.some((r) => r.result instanceof Promise)) || sync) {
|
|
1980
|
+
const envelopes = {};
|
|
1981
|
+
for (const r of results) if (r.result instanceof Promise) envelopes[r.key] = {
|
|
1982
|
+
loading: true,
|
|
1983
|
+
value: void 0,
|
|
1984
|
+
error: void 0
|
|
1948
1985
|
};
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
loading: false,
|
|
1954
|
-
value: void 0,
|
|
1955
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1956
|
-
}
|
|
1986
|
+
else envelopes[r.key] = {
|
|
1987
|
+
loading: false,
|
|
1988
|
+
value: r.result,
|
|
1989
|
+
error: void 0
|
|
1957
1990
|
};
|
|
1991
|
+
const derived = buildDerivedAccessors(envelopes);
|
|
1992
|
+
const prevSub = setActiveSub(void 0);
|
|
1993
|
+
try {
|
|
1994
|
+
const { html, events } = renderWithCtx({
|
|
1995
|
+
render: () => fn({
|
|
1996
|
+
state,
|
|
1997
|
+
derived,
|
|
1998
|
+
action,
|
|
1999
|
+
input
|
|
2000
|
+
}),
|
|
2001
|
+
manifest: eventsOut !== void 0
|
|
2002
|
+
});
|
|
2003
|
+
recordEventsManifest(events);
|
|
2004
|
+
return stylePrefix + html;
|
|
2005
|
+
} finally {
|
|
2006
|
+
setActiveSub(prevSub);
|
|
2007
|
+
}
|
|
1958
2008
|
}
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2009
|
+
return Promise.all(results.map(async (r) => {
|
|
2010
|
+
try {
|
|
2011
|
+
return {
|
|
2012
|
+
key: r.key,
|
|
2013
|
+
envelope: {
|
|
2014
|
+
loading: false,
|
|
2015
|
+
value: await Promise.resolve(r.result),
|
|
2016
|
+
error: void 0
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
} catch (err) {
|
|
2020
|
+
return {
|
|
2021
|
+
key: r.key,
|
|
2022
|
+
envelope: {
|
|
2023
|
+
loading: false,
|
|
2024
|
+
value: void 0,
|
|
2025
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
})).then(async (resolved) => {
|
|
2030
|
+
const envelopes = {};
|
|
2031
|
+
for (const r of resolved) envelopes[r.key] = r.envelope;
|
|
2032
|
+
const derived = buildDerivedAccessors(envelopes);
|
|
2033
|
+
const prevSub = setActiveSub(void 0);
|
|
2034
|
+
try {
|
|
2035
|
+
const { html, events } = await renderWithCtx({
|
|
2036
|
+
render: () => fn({
|
|
2037
|
+
state,
|
|
2038
|
+
derived,
|
|
2039
|
+
action,
|
|
2040
|
+
input
|
|
2041
|
+
}),
|
|
2042
|
+
asyncChildren: true,
|
|
2043
|
+
manifest: eventsOut !== void 0
|
|
2044
|
+
});
|
|
2045
|
+
recordEventsManifest(events);
|
|
2046
|
+
return stylePrefix + html;
|
|
2047
|
+
} finally {
|
|
2048
|
+
setActiveSub(prevSub);
|
|
2049
|
+
}
|
|
1968
2050
|
});
|
|
1969
|
-
|
|
1970
|
-
try {
|
|
1971
|
-
const { html } = await renderWithCtx({
|
|
1972
|
-
render: () => fn({
|
|
1973
|
-
state,
|
|
1974
|
-
derived,
|
|
1975
|
-
action,
|
|
1976
|
-
input
|
|
1977
|
-
}),
|
|
1978
|
-
asyncChildren: true
|
|
1979
|
-
});
|
|
1980
|
-
return stylePrefix + html;
|
|
1981
|
-
} finally {
|
|
1982
|
-
setActiveSub(prevSub);
|
|
1983
|
-
}
|
|
1984
|
-
});
|
|
2051
|
+
}
|
|
1985
2052
|
}
|
|
1986
2053
|
function mountIsland(host, props) {
|
|
1987
2054
|
return mountIslandInternal(host, props).unmount;
|
|
@@ -2025,7 +2092,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2025
2092
|
const parsed = safeParseSnapshot(rawState, STATE_ATTR);
|
|
2026
2093
|
if (parsed !== void 0) snapshotRaw = parsed;
|
|
2027
2094
|
}
|
|
2028
|
-
const stateSnapshot = snapshotRaw ? Object.fromEntries(Object.entries(snapshotRaw).filter(([k]) => k !== "_derived" && k !== "_skipOnMount")) : void 0;
|
|
2095
|
+
const stateSnapshot = snapshotRaw ? Object.fromEntries(Object.entries(snapshotRaw).filter(([k]) => k !== "_derived" && k !== "_skipOnMount" && k !== "_streams")) : void 0;
|
|
2029
2096
|
const derivedSnapshotRaw = snapshotRaw?._derived;
|
|
2030
2097
|
let derivedSnapshot;
|
|
2031
2098
|
if (derivedSnapshotRaw) {
|
|
@@ -2454,6 +2521,36 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2454
2521
|
}
|
|
2455
2522
|
stopDerived = setupDerived();
|
|
2456
2523
|
cleanups.push(stopDerived);
|
|
2524
|
+
for (const entry of streams) {
|
|
2525
|
+
let streamAborted = false;
|
|
2526
|
+
const streamController = new AbortController();
|
|
2527
|
+
const runStream = async () => {
|
|
2528
|
+
try {
|
|
2529
|
+
const gen = await entry.fn({
|
|
2530
|
+
input,
|
|
2531
|
+
signal: streamController.signal
|
|
2532
|
+
});
|
|
2533
|
+
while (!streamAborted && !streamController.signal.aborted) {
|
|
2534
|
+
const { done, value } = await gen.next();
|
|
2535
|
+
if (streamAborted || streamController.signal.aborted || done) break;
|
|
2536
|
+
const prevSub = setActiveSub(void 0);
|
|
2537
|
+
try {
|
|
2538
|
+
const setter = state[entry.key];
|
|
2539
|
+
setter?.(value);
|
|
2540
|
+
} finally {
|
|
2541
|
+
setActiveSub(prevSub);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
} catch (err) {
|
|
2545
|
+
if (!streamAborted && !streamController.signal.aborted && err?.name !== "AbortError") reportError(err, "stream");
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2548
|
+
runStream();
|
|
2549
|
+
cleanups.push(() => {
|
|
2550
|
+
streamAborted = true;
|
|
2551
|
+
streamController.abort();
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2457
2554
|
let initialized = false;
|
|
2458
2555
|
const initialRenderedHtml = initial.html;
|
|
2459
2556
|
let lastRendered = null;
|
|
@@ -2630,9 +2727,16 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2630
2727
|
return renderToString(props);
|
|
2631
2728
|
});
|
|
2632
2729
|
island.toString = (props) => renderToString(props, true);
|
|
2730
|
+
island.toStringAsync = (props) => Promise.resolve(renderToString(props, false));
|
|
2633
2731
|
island.mount = (host, props) => mountIsland(host, props);
|
|
2634
2732
|
island[ISLAND_MOUNT_INTERNAL] = (host, props) => mountIslandInternal(host, props);
|
|
2635
2733
|
island[ISLAND_SLOT_TAG] = configuredSlotTag;
|
|
2734
|
+
island[ISLAND_RENDER_STATE] = async (props) => {
|
|
2735
|
+
const manifest = /* @__PURE__ */ new Map();
|
|
2736
|
+
const html = await renderToString(props ?? void 0, false, void 0, manifest, void 0, true);
|
|
2737
|
+
return manifestTemplatePrefix(manifest) + html;
|
|
2738
|
+
};
|
|
2739
|
+
island[ISLAND_SSR_MANIFEST] = (props, eventsOut) => renderToString(props, false, void 0, eventsOut, void 0, true);
|
|
2636
2740
|
island.key = (key) => {
|
|
2637
2741
|
if (typeof key !== "string" || key.trim().length === 0) throw new Error("island.key() requires a non-empty string.");
|
|
2638
2742
|
if (key.includes(":")) throw new Error(`island.key() key cannot contain the slot separator ":" (got "${key}").`);
|
|
@@ -2732,7 +2836,9 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2732
2836
|
const { name, as: rawTag = "div", snapshot = false, skipOnMount: explicitSkipOnMount } = opts;
|
|
2733
2837
|
const tag = assertValidSlotTagName(rawTag);
|
|
2734
2838
|
const resolvedProps = props ?? {};
|
|
2735
|
-
const
|
|
2839
|
+
const streamValues = {};
|
|
2840
|
+
const eventsManifest = /* @__PURE__ */ new Map();
|
|
2841
|
+
const inner = await renderToString(resolvedProps, false, streamValues, eventsManifest, void 0, true);
|
|
2736
2842
|
const encodedProps = escapeHtml(JSON.stringify(resolvedProps));
|
|
2737
2843
|
let stateAttr = "";
|
|
2738
2844
|
if (snapshot !== false) {
|
|
@@ -2742,7 +2848,9 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2742
2848
|
const snapshotData = {};
|
|
2743
2849
|
const input = resolveInput(resolvedProps);
|
|
2744
2850
|
const plainState = buildPlainState(input);
|
|
2745
|
-
|
|
2851
|
+
const stateForDerived = { ...plainState };
|
|
2852
|
+
for (const [k, v] of Object.entries(streamValues)) stateForDerived[k] = () => v;
|
|
2853
|
+
if (doState) for (const entry of states) snapshotData[entry.key] = entry.key in streamValues ? streamValues[entry.key] : plainState[entry.key]();
|
|
2746
2854
|
if (doDerived) {
|
|
2747
2855
|
const derivedResults = {};
|
|
2748
2856
|
for (const entry of deriveds) {
|
|
@@ -2752,7 +2860,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2752
2860
|
let threw = false;
|
|
2753
2861
|
try {
|
|
2754
2862
|
resultPromise = entry.fn({
|
|
2755
|
-
state:
|
|
2863
|
+
state: stateForDerived,
|
|
2756
2864
|
input,
|
|
2757
2865
|
signal: new AbortController().signal
|
|
2758
2866
|
});
|
|
@@ -2788,9 +2896,12 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2788
2896
|
snapshotData["_derived"] = derivedResults;
|
|
2789
2897
|
}
|
|
2790
2898
|
if (doSkipOnMount) snapshotData["_skipOnMount"] = true;
|
|
2899
|
+
if (streams.length > 0) snapshotData["_streams"] = Object.fromEntries(streams.map((entry) => [entry.key, true]));
|
|
2791
2900
|
stateAttr = ` ${STATE_ATTR}='${escapeHtml(JSON.stringify(snapshotData))}'`;
|
|
2792
2901
|
}
|
|
2793
|
-
|
|
2902
|
+
let actionsAttr = "";
|
|
2903
|
+
if (eventsManifest.size > 0) actionsAttr = ` ${ACTIONS_ATTR}='${escapeHtml(JSON.stringify(Object.fromEntries(eventsManifest)))}'`;
|
|
2904
|
+
return `<${tag} data-ilha="${escapeHtml(name)}" ${PROPS_ATTR}='${encodedProps}'${stateAttr}${actionsAttr}>${inner}</${tag}>`;
|
|
2794
2905
|
};
|
|
2795
2906
|
return island;
|
|
2796
2907
|
}
|
|
@@ -2852,6 +2963,7 @@ const rootBuilder = new IlhaBuilder({
|
|
|
2852
2963
|
schema: null,
|
|
2853
2964
|
defaultInput: null,
|
|
2854
2965
|
states: [],
|
|
2966
|
+
streams: [],
|
|
2855
2967
|
deriveds: [],
|
|
2856
2968
|
actions: [],
|
|
2857
2969
|
ons: [],
|
|
@@ -2880,6 +2992,7 @@ const ilha = Object.assign(callableRoot, {
|
|
|
2880
2992
|
context: ilhaContext,
|
|
2881
2993
|
signal: ilhaSignal,
|
|
2882
2994
|
computed: ilhaComputed,
|
|
2995
|
+
morph,
|
|
2883
2996
|
batch,
|
|
2884
2997
|
untrack,
|
|
2885
2998
|
onUncaughtError
|
|
@@ -2888,11 +3001,14 @@ const ilha = Object.assign(callableRoot, {
|
|
|
2888
3001
|
function __ilhaJsxEvent({ type, handler, modifier }) {
|
|
2889
3002
|
const ctx = currentRenderCtx();
|
|
2890
3003
|
if (!ctx) return void 0;
|
|
3004
|
+
const isActionStub = SSR_ACTION_STUB in handler;
|
|
3005
|
+
const capture = ctx.manifest && !isActionStub ? captureHandlerActions(handler) : void 0;
|
|
2891
3006
|
const index = ctx.events.length;
|
|
2892
3007
|
ctx.events.push({
|
|
2893
3008
|
type,
|
|
2894
3009
|
handler,
|
|
2895
|
-
modifier
|
|
3010
|
+
modifier,
|
|
3011
|
+
capture
|
|
2896
3012
|
});
|
|
2897
3013
|
return index;
|
|
2898
3014
|
}
|
|
@@ -2904,6 +3020,27 @@ function __ilhaJsxSlot({ island, props, key }) {
|
|
|
2904
3020
|
key
|
|
2905
3021
|
}));
|
|
2906
3022
|
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Morph an element's children toward `html`, patching in place: surviving
|
|
3025
|
+
* elements keep their identity, listeners, and focus (see the morph engine).
|
|
3026
|
+
* Falls back to an innerHTML write when the root tags don't match.
|
|
3027
|
+
*/
|
|
3028
|
+
function morph(host, html) {
|
|
3029
|
+
if (typeof document === "undefined") return;
|
|
3030
|
+
const openTag = host.localName ?? "div";
|
|
3031
|
+
const tpl = document.createElement("template");
|
|
3032
|
+
tpl.innerHTML = `<${openTag}>${html}</${openTag}>`;
|
|
3033
|
+
const next = tpl.content.firstElementChild;
|
|
3034
|
+
if (!next || next.localName !== host.localName) {
|
|
3035
|
+
host.innerHTML = html;
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
try {
|
|
3039
|
+
morphInner(host, next);
|
|
3040
|
+
} catch {
|
|
3041
|
+
host.innerHTML = html;
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
2907
3044
|
const html = ilhaHtml;
|
|
2908
3045
|
const raw = ilhaRaw;
|
|
2909
3046
|
const css = ilhaCss;
|
|
@@ -2912,4 +3049,4 @@ const from = ilhaFrom;
|
|
|
2912
3049
|
const context = ilhaContext;
|
|
2913
3050
|
|
|
2914
3051
|
//#endregion
|
|
2915
|
-
export { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, __ilhaJsxEvent, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css,
|
|
3052
|
+
export { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, __ilhaJsxEvent, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css, ilhaEffect as effect, from, html, ilha, ilhaSignal, ilhaSignal as signal, morph, mount, onUncaughtError, raw, untrack };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as JSX, r as jsxDEV, t as Fragment } from "./jsx-runtime-
|
|
1
|
+
import { a as JSX, r as jsxDEV, t as Fragment } from "./jsx-runtime-DMkplizR.js";
|
|
2
2
|
export { Fragment, type JSX, jsxDEV };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as NativeEventModifier, E as SignalAccessor, O as SignalWriter, S as NativeEventHandler, T as RawHtml } from "./index-
|
|
1
|
+
import { C as NativeEventModifier, E as SignalAccessor, O as SignalWriter, S as NativeEventHandler, T as RawHtml } from "./index-DnoM0M8S.js";
|
|
2
2
|
//#region src/jsx-types.d.ts
|
|
3
3
|
declare namespace JSX {
|
|
4
4
|
export type Child = unknown;
|
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as JSX, i as jsxs, n as jsx, r as jsxDEV, t as Fragment } from "./jsx-runtime-
|
|
1
|
+
import { a as JSX, i as jsxs, n as jsx, r as jsxDEV, t as Fragment } from "./jsx-runtime-DMkplizR.js";
|
|
2
2
|
export { Fragment, type JSX, jsx, jsxDEV, jsxs };
|