ilha 0.10.2 → 0.11.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/dist/{index-Bi_I3qEL.d.ts → index-Cx35Xugv.d.ts} +26 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +357 -129
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/{jsx-runtime-DJ2B4fvA.d.ts → jsx-runtime-B-viVOB1.d.ts} +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/package.json +2 -1
|
@@ -242,6 +242,8 @@ type OnMountContext<TInput, TStateMap extends Record<string, unknown>, TDerivedM
|
|
|
242
242
|
input: TInput;
|
|
243
243
|
host: Element;
|
|
244
244
|
hydrated: boolean;
|
|
245
|
+
/** AbortSignal that aborts when the island unmounts. */
|
|
246
|
+
signal: AbortSignal;
|
|
245
247
|
};
|
|
246
248
|
type HandlerContext<TInput, TStateMap extends Record<string, unknown>, TDerivedMap extends Record<string, unknown> = Record<never, never>, TActionMap extends ActionMap = Record<never, never>> = {
|
|
247
249
|
state: IslandState<TStateMap>;
|
|
@@ -283,6 +285,20 @@ interface StateEntry<TInput> {
|
|
|
283
285
|
key: string;
|
|
284
286
|
init: StateInit<TInput, unknown>;
|
|
285
287
|
}
|
|
288
|
+
/** A state key fed by a generator (`.stream()`). The runtime iterates the
|
|
289
|
+
* generator and writes each yielded value into the state signal. During SSR
|
|
290
|
+
* only the first value is pulled so it can render inline; on the client the
|
|
291
|
+
* generator is consumed continuously. */
|
|
292
|
+
interface StreamEntry<TInput> {
|
|
293
|
+
key: string;
|
|
294
|
+
fn: StreamFn<TInput>;
|
|
295
|
+
}
|
|
296
|
+
type StreamFnContext<TInput> = {
|
|
297
|
+
input: TInput;
|
|
298
|
+
/** Aborts when the island unmounts. Pass through to fetch/SSE layers. */
|
|
299
|
+
signal: AbortSignal;
|
|
300
|
+
};
|
|
301
|
+
type StreamFn<TInput, V = unknown> = (ctx: StreamFnContext<TInput>) => AsyncGenerator<V> | Generator<V>;
|
|
286
302
|
interface ActionEntry<TInput, TStateMap extends Record<string, unknown>, TDerivedMap extends Record<string, unknown>> {
|
|
287
303
|
key: string;
|
|
288
304
|
fn: (props: unknown, ctx: ActionContext<TInput, TStateMap, TDerivedMap>) => unknown;
|
|
@@ -300,7 +316,7 @@ interface EffectEntry<TInput, TStateMap extends Record<string, unknown>, TDerive
|
|
|
300
316
|
/** Where the error originated. `"on"` covers sync throws and async rejections
|
|
301
317
|
* from `.on()` handlers; `"effect"` covers sync throws from `.effect()` runs
|
|
302
318
|
* (async work spawned inside an effect is not awaited by the runtime). */
|
|
303
|
-
type ErrorSource = "on" | "effect" | "mount" | "transition" | "action";
|
|
319
|
+
type ErrorSource = "on" | "effect" | "mount" | "transition" | "action" | "stream";
|
|
304
320
|
/**
|
|
305
321
|
* Register a global error handler invoked when any island reports an error
|
|
306
322
|
* (from .on, .effect, .onMount, or transitions) and has no local .onError()
|
|
@@ -339,6 +355,7 @@ interface BuilderConfig<TInput, TStateMap extends Record<string, unknown>, TDeri
|
|
|
339
355
|
/** Shallow defaults merged before props (POJO `.input({ ... })` only). */
|
|
340
356
|
defaultInput: Record<string, unknown> | null;
|
|
341
357
|
states: StateEntry<TInput>[];
|
|
358
|
+
streams: StreamEntry<TInput>[];
|
|
342
359
|
deriveds: DerivedEntry<TInput, TStateMap>[];
|
|
343
360
|
actions: ActionEntry<TInput, TStateMap, TDerivedMap>[];
|
|
344
361
|
ons: OnEntry<TInput, TStateMap, TDerivedMap, TActionMap>[];
|
|
@@ -358,6 +375,7 @@ declare class IlhaBuilder<TInput extends Record<string, unknown>, TStateMap exte
|
|
|
358
375
|
input<T extends Record<string, unknown>>(defaults: T): IlhaBuilder<T, Record<never, never>, Record<never, never>, Record<never, never>>;
|
|
359
376
|
as<Tag extends string>(tag: Tag): IlhaBuilder<TInput, TStateMap, TDerivedMap, TActionMap>;
|
|
360
377
|
state<V = undefined, K extends string = string>(key: K, init?: StateInit<TInput, V> | undefined): IlhaBuilder<TInput, MergeState<TStateMap, K, V>, TDerivedMap, TActionMap>;
|
|
378
|
+
stream<K extends string, V>(key: K, fn: StreamFn<TInput, V>): IlhaBuilder<TInput, MergeState<TStateMap, K, V>, TDerivedMap, TActionMap>;
|
|
361
379
|
derived<K extends string, V>(key: K, fn: DerivedFn<TInput, TStateMap, V>): IlhaBuilder<TInput, TStateMap, TDerivedMap & Record<K, V>, TActionMap>;
|
|
362
380
|
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>>;
|
|
363
381
|
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>;
|
|
@@ -406,6 +424,12 @@ declare function __ilhaJsxSlot({ island, props, key }: {
|
|
|
406
424
|
props: Record<string, unknown> | undefined;
|
|
407
425
|
key: string | undefined;
|
|
408
426
|
}): RawHtml;
|
|
427
|
+
/**
|
|
428
|
+
* Morph an element's children toward `html`, patching in place: surviving
|
|
429
|
+
* elements keep their identity, listeners, and focus (see the morph engine).
|
|
430
|
+
* Falls back to an innerHTML write when the root tags don't match.
|
|
431
|
+
*/
|
|
432
|
+
declare function morph(host: Element, html: string): void;
|
|
409
433
|
declare const html: typeof ilhaHtml;
|
|
410
434
|
declare const raw: typeof ilhaRaw;
|
|
411
435
|
declare const css: typeof ilhaCss;
|
|
@@ -418,4 +442,4 @@ declare const context: typeof ilhaContextFn & {
|
|
|
418
442
|
clear(): void;
|
|
419
443
|
};
|
|
420
444
|
//#endregion
|
|
421
|
-
export {
|
|
445
|
+
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, ilha as default, ilhaEffect as effect, from, html, ilhaSignal, ilhaSignal as signal, mount, onUncaughtError, raw, untrack };
|
|
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-Cx35Xugv.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, ilha as default, ilhaEffect as effect, from, html, 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;
|
|
@@ -465,16 +473,31 @@ const RAW = Symbol.for("ilha.raw");
|
|
|
465
473
|
const SIGNAL_ACCESSOR = Symbol.for("ilha.signalAccessor");
|
|
466
474
|
const ISLAND = Symbol.for("ilha.island");
|
|
467
475
|
const ISLAND_CALL = Symbol.for("ilha.islandCall");
|
|
476
|
+
const ASTRO_RENDERER_GLOBAL = Symbol.for("ilha.astroRenderer");
|
|
468
477
|
/** @internal Internal hook used by a parent's mountSlots to mount a child island and
|
|
469
478
|
* retain a handle to push updated props into it on subsequent parent
|
|
470
479
|
* re-renders. Not part of the public surface. */
|
|
471
480
|
const ISLAND_MOUNT_INTERNAL = Symbol.for("ilha.islandMountInternal");
|
|
472
481
|
const ISLAND_SLOT_TAG = Symbol.for("ilha.islandSlotTag");
|
|
482
|
+
const ISLAND_CLIENT_REF = Symbol.for("ilha.clientRef");
|
|
473
483
|
/** @internal Live mount handles keyed by host element. Lets @ilha/router adopt
|
|
474
484
|
* islands hydrated by `ilha.mount()` (whose handles it never saw) and push new
|
|
475
485
|
* loader props into them in place instead of remounting. Entries are removed
|
|
476
486
|
* on unmount. Not part of the public surface. */
|
|
477
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
|
+
}
|
|
478
501
|
const SLOT_ATTR = "data-ilha-slot";
|
|
479
502
|
/** Marks a slot host whose child island is mid leave-transition: it has been
|
|
480
503
|
* unmounted (and its slot id stripped) but stays connected so the leave
|
|
@@ -482,25 +505,63 @@ const SLOT_ATTR = "data-ilha-slot";
|
|
|
482
505
|
* or mutating them. */
|
|
483
506
|
const LEAVING_ATTR = "data-ilha-leaving";
|
|
484
507
|
const PROPS_ATTR = "data-ilha-props";
|
|
508
|
+
const CLIENT_REF_ATTR = "data-ilha-client-ref";
|
|
485
509
|
const STATE_ATTR = "data-ilha-state";
|
|
486
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";
|
|
487
516
|
function buildScopedStyle(css) {
|
|
488
517
|
const safeCss = css.replace(/<\/style/gi, "<\\/style");
|
|
489
518
|
return `<style ${CSS_ATTR}>@scope (:scope) to ([data-ilha]){${safeCss}}</style>`;
|
|
490
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
|
+
}
|
|
491
551
|
const RENDER_CTX_STACK = Symbol.for("ilha.renderCtxStack");
|
|
492
552
|
function renderCtxStack() {
|
|
493
553
|
const g = globalThis;
|
|
494
554
|
return g[RENDER_CTX_STACK] ??= [];
|
|
495
555
|
}
|
|
496
|
-
function pushRenderCtx(liveHost, asyncChildren) {
|
|
556
|
+
function pushRenderCtx(liveHost, asyncChildren, manifest) {
|
|
497
557
|
const ctx = {
|
|
498
558
|
slots: /* @__PURE__ */ new Map(),
|
|
499
559
|
positional: 0,
|
|
500
560
|
liveHost,
|
|
501
561
|
pending: asyncChildren ? /* @__PURE__ */ new Map() : void 0,
|
|
502
562
|
binds: [],
|
|
503
|
-
events: []
|
|
563
|
+
events: [],
|
|
564
|
+
manifest
|
|
504
565
|
};
|
|
505
566
|
renderCtxStack().push(ctx);
|
|
506
567
|
return ctx;
|
|
@@ -523,15 +584,16 @@ function isIslandCall(v) {
|
|
|
523
584
|
function emitIslandSlot({ island, props, key }) {
|
|
524
585
|
const ctx = currentRenderCtx();
|
|
525
586
|
let id;
|
|
526
|
-
if (key
|
|
527
|
-
else id =
|
|
587
|
+
if (key === void 0) id = ctx ? `p:${ctx.positional++}` : "p:0";
|
|
588
|
+
else id = `k:${key}`;
|
|
528
589
|
if (ctx) ctx.slots.set(id, {
|
|
529
590
|
island,
|
|
530
591
|
props
|
|
531
592
|
});
|
|
532
593
|
const slotTag = getIslandSlotTag(island);
|
|
533
|
-
const
|
|
534
|
-
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)}"` : "");
|
|
535
597
|
if (ctx?.liveHost) return wrapIslandSlotHtml({
|
|
536
598
|
tag: slotTag,
|
|
537
599
|
id,
|
|
@@ -541,9 +603,12 @@ function emitIslandSlot({ island, props, key }) {
|
|
|
541
603
|
if (ctx?.pending) {
|
|
542
604
|
popRenderCtx();
|
|
543
605
|
try {
|
|
544
|
-
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);
|
|
545
610
|
if (result instanceof Promise) {
|
|
546
|
-
ctx.pending.set(id, result.then(String));
|
|
611
|
+
ctx.pending.set(id, result.then(String).then((html) => prefix() + html));
|
|
547
612
|
return wrapIslandSlotHtml({
|
|
548
613
|
tag: slotTag,
|
|
549
614
|
id,
|
|
@@ -555,7 +620,7 @@ function emitIslandSlot({ island, props, key }) {
|
|
|
555
620
|
tag: slotTag,
|
|
556
621
|
id,
|
|
557
622
|
propsAttr,
|
|
558
|
-
inner: String(result)
|
|
623
|
+
inner: prefix() + String(result)
|
|
559
624
|
});
|
|
560
625
|
} finally {
|
|
561
626
|
renderCtxStack().push(ctx);
|
|
@@ -827,7 +892,8 @@ function ilhaHtml(strings, ...values) {
|
|
|
827
892
|
}
|
|
828
893
|
if (pendingBindSpecs !== "" || pendingEventSpecs !== "") {
|
|
829
894
|
const { index: gtIdx, quote } = findTagCloseIndex(chunk, pendingQuote);
|
|
830
|
-
if (gtIdx
|
|
895
|
+
if (gtIdx === -1) pendingQuote = quote;
|
|
896
|
+
else {
|
|
831
897
|
const head = chunk.slice(0, gtIdx).replace(/\s*\/\s*$/, "");
|
|
832
898
|
const bindAttr = pendingBindSpecs ? ` data-ilha-bind="${pendingBindSpecs}"` : "";
|
|
833
899
|
const eventAttr = pendingEventSpecs ? ` data-ilha-on="${pendingEventSpecs}"` : "";
|
|
@@ -835,7 +901,7 @@ function ilhaHtml(strings, ...values) {
|
|
|
835
901
|
pendingBindSpecs = "";
|
|
836
902
|
pendingEventSpecs = "";
|
|
837
903
|
pendingQuote = null;
|
|
838
|
-
}
|
|
904
|
+
}
|
|
839
905
|
}
|
|
840
906
|
if (i >= values.length) {
|
|
841
907
|
result += chunk;
|
|
@@ -843,13 +909,16 @@ function ilhaHtml(strings, ...values) {
|
|
|
843
909
|
}
|
|
844
910
|
const value = values[i];
|
|
845
911
|
const eventMatch = chunk.match(EVENT_PREFIX_RE);
|
|
846
|
-
|
|
912
|
+
const eventMatchStart = eventMatch ? chunk.length - eventMatch[0].length : -1;
|
|
913
|
+
const eventAttributeFragment = eventMatch !== null && chunk.slice(0, eventMatchStart).trim() === "";
|
|
914
|
+
if (eventMatch && (eventAttributeFragment || isEventAttributePosition(result + chunk, result.length + eventMatchStart))) {
|
|
847
915
|
const eventName = eventMatch[1];
|
|
848
916
|
const rawModifier = eventMatch[2];
|
|
849
917
|
const modifier = NATIVE_EVENT_MODIFIERS.has(rawModifier) ? rawModifier : void 0;
|
|
850
918
|
const openQuote = eventMatch[3] ?? null;
|
|
851
|
-
const
|
|
852
|
-
const
|
|
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;
|
|
853
922
|
result += cleanPrefix;
|
|
854
923
|
if (ctx && typeof value === "function") {
|
|
855
924
|
const index = ctx.events.length;
|
|
@@ -857,7 +926,8 @@ function ilhaHtml(strings, ...values) {
|
|
|
857
926
|
ctx.events.push({
|
|
858
927
|
type: eventName,
|
|
859
928
|
handler: value,
|
|
860
|
-
modifier
|
|
929
|
+
modifier,
|
|
930
|
+
capture
|
|
861
931
|
});
|
|
862
932
|
pendingEventSpecs += (pendingEventSpecs ? "," : "") + `${eventName}:${index}`;
|
|
863
933
|
}
|
|
@@ -1153,7 +1223,12 @@ function createDerivedProxy({ entries, state, input, derivedSnapshot }) {
|
|
|
1153
1223
|
for (const entry of entries) {
|
|
1154
1224
|
let initialEnvelope;
|
|
1155
1225
|
if (derivedSnapshot != null && entry.key in derivedSnapshot) initialEnvelope = { ...derivedSnapshot[entry.key] };
|
|
1156
|
-
else if (
|
|
1226
|
+
else if (entry.fn.constructor.name === "AsyncFunction" || entry.fn.constructor.name === "AsyncGeneratorFunction") initialEnvelope = {
|
|
1227
|
+
loading: true,
|
|
1228
|
+
value: void 0,
|
|
1229
|
+
error: void 0
|
|
1230
|
+
};
|
|
1231
|
+
else {
|
|
1157
1232
|
const ac = new AbortController();
|
|
1158
1233
|
const captureSlot = {};
|
|
1159
1234
|
const stack = queryCaptureStack();
|
|
@@ -1170,19 +1245,18 @@ function createDerivedProxy({ entries, state, input, derivedSnapshot }) {
|
|
|
1170
1245
|
value: void 0,
|
|
1171
1246
|
error: void 0
|
|
1172
1247
|
};
|
|
1173
|
-
else if (
|
|
1174
|
-
loading: false,
|
|
1175
|
-
value: result,
|
|
1176
|
-
error: void 0
|
|
1177
|
-
};
|
|
1178
|
-
else {
|
|
1248
|
+
else if (result instanceof Promise) {
|
|
1179
1249
|
initialEnvelope = {
|
|
1180
1250
|
loading: true,
|
|
1181
1251
|
value: void 0,
|
|
1182
1252
|
error: void 0
|
|
1183
1253
|
};
|
|
1184
1254
|
result.catch(() => {});
|
|
1185
|
-
}
|
|
1255
|
+
} else initialEnvelope = {
|
|
1256
|
+
loading: false,
|
|
1257
|
+
value: result,
|
|
1258
|
+
error: void 0
|
|
1259
|
+
};
|
|
1186
1260
|
} catch (err) {
|
|
1187
1261
|
globalThis[ILHA_QUERY_PENDING] = void 0;
|
|
1188
1262
|
initialEnvelope = {
|
|
@@ -1194,11 +1268,7 @@ function createDerivedProxy({ entries, state, input, derivedSnapshot }) {
|
|
|
1194
1268
|
stack.pop();
|
|
1195
1269
|
ac.abort();
|
|
1196
1270
|
}
|
|
1197
|
-
}
|
|
1198
|
-
loading: true,
|
|
1199
|
-
value: void 0,
|
|
1200
|
-
error: void 0
|
|
1201
|
-
};
|
|
1271
|
+
}
|
|
1202
1272
|
const env = signal(initialEnvelope);
|
|
1203
1273
|
envelopes.set(entry.key, env);
|
|
1204
1274
|
}
|
|
@@ -1478,7 +1548,7 @@ function applyJsxEvents({ host, records, reportError, unmountSignal, onceFired }
|
|
|
1478
1548
|
}
|
|
1479
1549
|
return () => cleanups.forEach((cleanup) => cleanup());
|
|
1480
1550
|
}
|
|
1481
|
-
function applyTemplateBindings(host, binds) {
|
|
1551
|
+
function applyTemplateBindings(host, binds, phase = "all") {
|
|
1482
1552
|
if (binds.length === 0) return () => {};
|
|
1483
1553
|
const cleanups = [];
|
|
1484
1554
|
const elements = [];
|
|
@@ -1493,14 +1563,16 @@ function applyTemplateBindings(host, binds) {
|
|
|
1493
1563
|
const { event, read, write } = resolveBindOps(el, spec.kind);
|
|
1494
1564
|
const accessor = record.accessor;
|
|
1495
1565
|
if (spec.kind === "this") {
|
|
1496
|
-
|
|
1497
|
-
|
|
1566
|
+
if (phase !== "listen") {
|
|
1567
|
+
accessor(el);
|
|
1568
|
+
cleanups.push(() => accessor(null));
|
|
1569
|
+
}
|
|
1498
1570
|
continue;
|
|
1499
1571
|
}
|
|
1500
|
-
try {
|
|
1572
|
+
if (phase !== "listen") try {
|
|
1501
1573
|
write(el, accessor());
|
|
1502
1574
|
} catch (err) {}
|
|
1503
|
-
if (event === null) continue;
|
|
1575
|
+
if (phase === "reflect" || event === null) continue;
|
|
1504
1576
|
const listener = () => {
|
|
1505
1577
|
const raw = read(el);
|
|
1506
1578
|
if (spec.kind === "group") {
|
|
@@ -1624,6 +1696,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1624
1696
|
schema,
|
|
1625
1697
|
defaultInput,
|
|
1626
1698
|
states: [],
|
|
1699
|
+
streams: [],
|
|
1627
1700
|
deriveds: [],
|
|
1628
1701
|
actions: [],
|
|
1629
1702
|
ons: [],
|
|
@@ -1651,6 +1724,21 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1651
1724
|
}]
|
|
1652
1725
|
});
|
|
1653
1726
|
}
|
|
1727
|
+
stream(key, fn) {
|
|
1728
|
+
const cfg = this._cfg;
|
|
1729
|
+
const states = cfg.states.some((entry) => entry.key === key) ? cfg.states : [...cfg.states, {
|
|
1730
|
+
key,
|
|
1731
|
+
init: void 0
|
|
1732
|
+
}];
|
|
1733
|
+
return new IlhaBuilder({
|
|
1734
|
+
...cfg,
|
|
1735
|
+
states,
|
|
1736
|
+
streams: [...cfg.streams.filter((entry) => entry.key !== key), {
|
|
1737
|
+
key,
|
|
1738
|
+
fn
|
|
1739
|
+
}]
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1654
1742
|
derived(key, fn) {
|
|
1655
1743
|
const cfg = this._cfg;
|
|
1656
1744
|
return new IlhaBuilder({
|
|
@@ -1724,8 +1812,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1724
1812
|
});
|
|
1725
1813
|
}
|
|
1726
1814
|
render(fn) {
|
|
1727
|
-
const { schema, defaultInput, states, deriveds, actions, ons, effects, onMounts, onErrors, transition, css: cssSource, as: slotAs } = this._cfg;
|
|
1728
|
-
const stylePrefix = cssSource
|
|
1815
|
+
const { schema, defaultInput, states, streams, deriveds, actions, ons, effects, onMounts, onErrors, transition, css: cssSource, as: slotAs } = this._cfg;
|
|
1816
|
+
const stylePrefix = cssSource == null ? "" : buildScopedStyle(cssSource);
|
|
1729
1817
|
const configuredSlotTag = slotAs ?? "div";
|
|
1730
1818
|
function resolveInput(props) {
|
|
1731
1819
|
const merged = {
|
|
@@ -1735,8 +1823,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1735
1823
|
if (!schema) return merged;
|
|
1736
1824
|
return validateSchema(schema, merged);
|
|
1737
1825
|
}
|
|
1738
|
-
function renderWithCtx({ render, liveHost, asyncChildren }) {
|
|
1739
|
-
const ctx = pushRenderCtx(liveHost, asyncChildren);
|
|
1826
|
+
function renderWithCtx({ render, liveHost, asyncChildren, manifest }) {
|
|
1827
|
+
const ctx = pushRenderCtx(liveHost, asyncChildren, manifest);
|
|
1740
1828
|
try {
|
|
1741
1829
|
const html = unwrapHtml(render());
|
|
1742
1830
|
const slots = ctx.slots;
|
|
@@ -1789,8 +1877,19 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1789
1877
|
}
|
|
1790
1878
|
function buildSsrActions() {
|
|
1791
1879
|
const out = {};
|
|
1880
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
1792
1881
|
for (const entry of actions) {
|
|
1793
|
-
const invoke = () => {
|
|
1882
|
+
const invoke = (...callArgs) => {
|
|
1883
|
+
const frame = captureStack[captureStack.length - 1];
|
|
1884
|
+
if (frame) {
|
|
1885
|
+
if (!frame.some((r) => r.k === entry.key)) frame.push({
|
|
1886
|
+
k: entry.key,
|
|
1887
|
+
a: callArgs
|
|
1888
|
+
});
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1892
|
+
invoke[SSR_ACTION_STUB] = true;
|
|
1794
1893
|
Object.defineProperties(invoke, {
|
|
1795
1894
|
pending: {
|
|
1796
1895
|
get: () => false,
|
|
@@ -1806,8 +1905,12 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1806
1905
|
}
|
|
1807
1906
|
});
|
|
1808
1907
|
out[entry.key] = invoke;
|
|
1908
|
+
lookup.set(invoke, entry.key);
|
|
1809
1909
|
}
|
|
1810
|
-
return
|
|
1910
|
+
return {
|
|
1911
|
+
action: out,
|
|
1912
|
+
lookup
|
|
1913
|
+
};
|
|
1811
1914
|
}
|
|
1812
1915
|
/** Detached host for SSR setup hooks; must not be `{}` (Areia/components call host.matches). */
|
|
1813
1916
|
function createSsrOnMountHost() {
|
|
@@ -1828,6 +1931,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1828
1931
|
if (onMounts.length === 0) return;
|
|
1829
1932
|
if (currentRenderCtx()) return;
|
|
1830
1933
|
const host = createSsrOnMountHost();
|
|
1934
|
+
const controller = new AbortController();
|
|
1831
1935
|
const ssrCleanups = [];
|
|
1832
1936
|
for (const entry of onMounts) {
|
|
1833
1937
|
const prevSub = setActiveSub(void 0);
|
|
@@ -1839,7 +1943,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1839
1943
|
action,
|
|
1840
1944
|
input,
|
|
1841
1945
|
host,
|
|
1842
|
-
hydrated: false
|
|
1946
|
+
hydrated: false,
|
|
1947
|
+
signal: controller.signal
|
|
1843
1948
|
});
|
|
1844
1949
|
} catch (err) {
|
|
1845
1950
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -1863,6 +1968,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1863
1968
|
}
|
|
1864
1969
|
if (typeof userCleanup === "function") ssrCleanups.push(userCleanup);
|
|
1865
1970
|
}
|
|
1971
|
+
controller.abort();
|
|
1866
1972
|
for (const teardown of ssrCleanups.reverse()) try {
|
|
1867
1973
|
teardown();
|
|
1868
1974
|
} catch (err) {
|
|
@@ -1872,11 +1978,45 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1872
1978
|
}
|
|
1873
1979
|
}
|
|
1874
1980
|
}
|
|
1875
|
-
function renderToString(props, sync = false) {
|
|
1981
|
+
function renderToString(props, sync = false, streamOut, eventsOut, stateOverride, forceAsyncChildren = false) {
|
|
1876
1982
|
const input = resolveInput(props);
|
|
1877
|
-
const state = buildSignalState(input);
|
|
1878
|
-
const action = buildSsrActions();
|
|
1879
|
-
const
|
|
1983
|
+
const state = buildSignalState(input, stateOverride);
|
|
1984
|
+
const { action, lookup: actionLookup } = buildSsrActions();
|
|
1985
|
+
const recordEventsManifest = (events) => {
|
|
1986
|
+
if (!eventsOut || events.length === 0) return;
|
|
1987
|
+
events.forEach((record, index) => {
|
|
1988
|
+
if (record.capture) {
|
|
1989
|
+
eventsOut.set(`${record.type}:${index}`, record.capture);
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
const key = actionLookup.get(record.handler);
|
|
1993
|
+
if (key) eventsOut.set(`${record.type}:${index}`, key);
|
|
1994
|
+
});
|
|
1995
|
+
};
|
|
1996
|
+
const pullStreamInitials = async (streamOut) => {
|
|
1997
|
+
for (const entry of streams) {
|
|
1998
|
+
const controller = new AbortController();
|
|
1999
|
+
try {
|
|
2000
|
+
const gen = await entry.fn({
|
|
2001
|
+
input,
|
|
2002
|
+
signal: controller.signal
|
|
2003
|
+
});
|
|
2004
|
+
const first = await gen.next();
|
|
2005
|
+
controller.abort();
|
|
2006
|
+
await gen.return?.(void 0);
|
|
2007
|
+
if (first.done) continue;
|
|
2008
|
+
if (streamOut) streamOut[entry.key] = first.value;
|
|
2009
|
+
const prevSub = setActiveSub(void 0);
|
|
2010
|
+
try {
|
|
2011
|
+
const setter = state[entry.key];
|
|
2012
|
+
setter?.(first.value);
|
|
2013
|
+
} finally {
|
|
2014
|
+
setActiveSub(prevSub);
|
|
2015
|
+
}
|
|
2016
|
+
} catch {}
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
const computeDerivedResults = () => deriveds.map((entry) => {
|
|
1880
2020
|
const prevSub = setActiveSub(void 0);
|
|
1881
2021
|
try {
|
|
1882
2022
|
return {
|
|
@@ -1896,84 +2036,95 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1896
2036
|
setActiveSub(prevSub);
|
|
1897
2037
|
}
|
|
1898
2038
|
});
|
|
1899
|
-
if (!
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
error: void 0
|
|
1910
|
-
};
|
|
1911
|
-
const derived = buildDerivedAccessors(envelopes);
|
|
1912
|
-
if (!currentRenderCtx()) runOnMountForRender({
|
|
1913
|
-
input,
|
|
1914
|
-
state,
|
|
1915
|
-
derived,
|
|
1916
|
-
action
|
|
1917
|
-
});
|
|
1918
|
-
const prevSub = setActiveSub(void 0);
|
|
1919
|
-
try {
|
|
1920
|
-
const { html } = renderWithCtx({ render: () => fn({
|
|
1921
|
-
state,
|
|
1922
|
-
derived,
|
|
1923
|
-
action,
|
|
1924
|
-
input
|
|
1925
|
-
}) });
|
|
1926
|
-
return stylePrefix + html;
|
|
1927
|
-
} finally {
|
|
1928
|
-
setActiveSub(prevSub);
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
return Promise.all(results.map(async (r) => {
|
|
1932
|
-
try {
|
|
1933
|
-
return {
|
|
1934
|
-
key: r.key,
|
|
1935
|
-
envelope: {
|
|
1936
|
-
loading: false,
|
|
1937
|
-
value: await Promise.resolve(r.result),
|
|
1938
|
-
error: void 0
|
|
1939
|
-
}
|
|
2039
|
+
if (!sync && (streams.length > 0 || forceAsyncChildren)) return pullStreamInitials(streamOut).then(() => renderToStringTail());
|
|
2040
|
+
return renderToStringTail();
|
|
2041
|
+
function renderToStringTail() {
|
|
2042
|
+
const results = computeDerivedResults();
|
|
2043
|
+
if (!(forceAsyncChildren || results.some((r) => r.result instanceof Promise)) || sync) {
|
|
2044
|
+
const envelopes = {};
|
|
2045
|
+
for (const r of results) if (r.result instanceof Promise) envelopes[r.key] = {
|
|
2046
|
+
loading: true,
|
|
2047
|
+
value: void 0,
|
|
2048
|
+
error: void 0
|
|
1940
2049
|
};
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
loading: false,
|
|
1946
|
-
value: void 0,
|
|
1947
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1948
|
-
}
|
|
2050
|
+
else envelopes[r.key] = {
|
|
2051
|
+
loading: false,
|
|
2052
|
+
value: r.result,
|
|
2053
|
+
error: void 0
|
|
1949
2054
|
};
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
input,
|
|
1957
|
-
state,
|
|
1958
|
-
derived,
|
|
1959
|
-
action
|
|
1960
|
-
});
|
|
1961
|
-
const prevSub = setActiveSub(void 0);
|
|
1962
|
-
try {
|
|
1963
|
-
const { html } = await renderWithCtx({
|
|
1964
|
-
render: () => fn({
|
|
1965
|
-
state,
|
|
1966
|
-
derived,
|
|
1967
|
-
action,
|
|
1968
|
-
input
|
|
1969
|
-
}),
|
|
1970
|
-
asyncChildren: true
|
|
2055
|
+
const derived = buildDerivedAccessors(envelopes);
|
|
2056
|
+
if (!currentRenderCtx()) runOnMountForRender({
|
|
2057
|
+
input,
|
|
2058
|
+
state,
|
|
2059
|
+
derived,
|
|
2060
|
+
action
|
|
1971
2061
|
});
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2062
|
+
const prevSub = setActiveSub(void 0);
|
|
2063
|
+
try {
|
|
2064
|
+
const { html, events } = renderWithCtx({
|
|
2065
|
+
render: () => fn({
|
|
2066
|
+
state,
|
|
2067
|
+
derived,
|
|
2068
|
+
action,
|
|
2069
|
+
input
|
|
2070
|
+
}),
|
|
2071
|
+
manifest: eventsOut !== void 0
|
|
2072
|
+
});
|
|
2073
|
+
recordEventsManifest(events);
|
|
2074
|
+
return stylePrefix + html;
|
|
2075
|
+
} finally {
|
|
2076
|
+
setActiveSub(prevSub);
|
|
2077
|
+
}
|
|
1975
2078
|
}
|
|
1976
|
-
|
|
2079
|
+
return Promise.all(results.map(async (r) => {
|
|
2080
|
+
try {
|
|
2081
|
+
return {
|
|
2082
|
+
key: r.key,
|
|
2083
|
+
envelope: {
|
|
2084
|
+
loading: false,
|
|
2085
|
+
value: await Promise.resolve(r.result),
|
|
2086
|
+
error: void 0
|
|
2087
|
+
}
|
|
2088
|
+
};
|
|
2089
|
+
} catch (err) {
|
|
2090
|
+
return {
|
|
2091
|
+
key: r.key,
|
|
2092
|
+
envelope: {
|
|
2093
|
+
loading: false,
|
|
2094
|
+
value: void 0,
|
|
2095
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2096
|
+
}
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
})).then(async (resolved) => {
|
|
2100
|
+
const envelopes = {};
|
|
2101
|
+
for (const r of resolved) envelopes[r.key] = r.envelope;
|
|
2102
|
+
const derived = buildDerivedAccessors(envelopes);
|
|
2103
|
+
if (!currentRenderCtx()) runOnMountForRender({
|
|
2104
|
+
input,
|
|
2105
|
+
state,
|
|
2106
|
+
derived,
|
|
2107
|
+
action
|
|
2108
|
+
});
|
|
2109
|
+
const prevSub = setActiveSub(void 0);
|
|
2110
|
+
try {
|
|
2111
|
+
const { html, events } = await renderWithCtx({
|
|
2112
|
+
render: () => fn({
|
|
2113
|
+
state,
|
|
2114
|
+
derived,
|
|
2115
|
+
action,
|
|
2116
|
+
input
|
|
2117
|
+
}),
|
|
2118
|
+
asyncChildren: true,
|
|
2119
|
+
manifest: eventsOut !== void 0
|
|
2120
|
+
});
|
|
2121
|
+
recordEventsManifest(events);
|
|
2122
|
+
return stylePrefix + html;
|
|
2123
|
+
} finally {
|
|
2124
|
+
setActiveSub(prevSub);
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
1977
2128
|
}
|
|
1978
2129
|
function mountIsland(host, props) {
|
|
1979
2130
|
return mountIslandInternal(host, props).unmount;
|
|
@@ -2017,7 +2168,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2017
2168
|
const parsed = safeParseSnapshot(rawState, STATE_ATTR);
|
|
2018
2169
|
if (parsed !== void 0) snapshotRaw = parsed;
|
|
2019
2170
|
}
|
|
2020
|
-
const stateSnapshot = snapshotRaw ? Object.fromEntries(Object.entries(snapshotRaw).filter(([k]) => k !== "_derived" && k !== "_skipOnMount")) : void 0;
|
|
2171
|
+
const stateSnapshot = snapshotRaw ? Object.fromEntries(Object.entries(snapshotRaw).filter(([k]) => k !== "_derived" && k !== "_skipOnMount" && k !== "_streams")) : void 0;
|
|
2021
2172
|
const derivedSnapshotRaw = snapshotRaw?._derived;
|
|
2022
2173
|
let derivedSnapshot;
|
|
2023
2174
|
if (derivedSnapshotRaw) {
|
|
@@ -2350,7 +2501,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2350
2501
|
action,
|
|
2351
2502
|
input,
|
|
2352
2503
|
host,
|
|
2353
|
-
hydrated
|
|
2504
|
+
hydrated,
|
|
2505
|
+
signal: unmountController.signal
|
|
2354
2506
|
});
|
|
2355
2507
|
} catch (err) {
|
|
2356
2508
|
reportError(err, "mount");
|
|
@@ -2382,7 +2534,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2382
2534
|
liveHost: preserveSSRDom ? host : void 0
|
|
2383
2535
|
});
|
|
2384
2536
|
if (!preserveSSRDom) host.innerHTML = stylePrefix + initial.html;
|
|
2385
|
-
|
|
2537
|
+
const stopInitialBindingReflection = applyTemplateBindings(host, initial.binds, "reflect");
|
|
2538
|
+
let stopBindings = stopInitialBindingReflection;
|
|
2386
2539
|
cleanups.push(() => stopBindings());
|
|
2387
2540
|
let stopJsxEvents = applyJsxEvents({
|
|
2388
2541
|
host,
|
|
@@ -2396,6 +2549,11 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2396
2549
|
cleanups.push(() => mountedSlots.forEach((entry) => entry.unmount()));
|
|
2397
2550
|
attachListeners();
|
|
2398
2551
|
if (!hydrated && onMounts.length > 0) invokeOnMounts();
|
|
2552
|
+
const stopInitialBindingListeners = applyTemplateBindings(host, initial.binds, "listen");
|
|
2553
|
+
stopBindings = () => {
|
|
2554
|
+
stopInitialBindingListeners();
|
|
2555
|
+
stopInitialBindingReflection();
|
|
2556
|
+
};
|
|
2399
2557
|
for (const entry of effects) {
|
|
2400
2558
|
let userCleanup;
|
|
2401
2559
|
let runController = null;
|
|
@@ -2439,6 +2597,36 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2439
2597
|
}
|
|
2440
2598
|
stopDerived = setupDerived();
|
|
2441
2599
|
cleanups.push(stopDerived);
|
|
2600
|
+
for (const entry of streams) {
|
|
2601
|
+
let streamAborted = false;
|
|
2602
|
+
const streamController = new AbortController();
|
|
2603
|
+
const runStream = async () => {
|
|
2604
|
+
try {
|
|
2605
|
+
const gen = await entry.fn({
|
|
2606
|
+
input,
|
|
2607
|
+
signal: streamController.signal
|
|
2608
|
+
});
|
|
2609
|
+
while (!streamAborted && !streamController.signal.aborted) {
|
|
2610
|
+
const { done, value } = await gen.next();
|
|
2611
|
+
if (streamAborted || streamController.signal.aborted || done) break;
|
|
2612
|
+
const prevSub = setActiveSub(void 0);
|
|
2613
|
+
try {
|
|
2614
|
+
const setter = state[entry.key];
|
|
2615
|
+
setter?.(value);
|
|
2616
|
+
} finally {
|
|
2617
|
+
setActiveSub(prevSub);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
} catch (err) {
|
|
2621
|
+
if (!streamAborted && !streamController.signal.aborted && err?.name !== "AbortError") reportError(err, "stream");
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
runStream();
|
|
2625
|
+
cleanups.push(() => {
|
|
2626
|
+
streamAborted = true;
|
|
2627
|
+
streamController.abort();
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2442
2630
|
let initialized = false;
|
|
2443
2631
|
const initialRenderedHtml = initial.html;
|
|
2444
2632
|
let lastRendered = null;
|
|
@@ -2618,6 +2806,12 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2618
2806
|
island.mount = (host, props) => mountIsland(host, props);
|
|
2619
2807
|
island[ISLAND_MOUNT_INTERNAL] = (host, props) => mountIslandInternal(host, props);
|
|
2620
2808
|
island[ISLAND_SLOT_TAG] = configuredSlotTag;
|
|
2809
|
+
island[ISLAND_RENDER_STATE] = async (props) => {
|
|
2810
|
+
const manifest = /* @__PURE__ */ new Map();
|
|
2811
|
+
const html = await renderToString(props ?? void 0, false, void 0, manifest, void 0, true);
|
|
2812
|
+
return manifestTemplatePrefix(manifest) + html;
|
|
2813
|
+
};
|
|
2814
|
+
island[ISLAND_SSR_MANIFEST] = (props, eventsOut) => renderToString(props, false, void 0, eventsOut, void 0, true);
|
|
2621
2815
|
island.key = (key) => {
|
|
2622
2816
|
if (typeof key !== "string" || key.trim().length === 0) throw new Error("island.key() requires a non-empty string.");
|
|
2623
2817
|
if (key.includes(":")) throw new Error(`island.key() key cannot contain the slot separator ":" (got "${key}").`);
|
|
@@ -2631,6 +2825,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2631
2825
|
return keyed;
|
|
2632
2826
|
};
|
|
2633
2827
|
island[ISLAND] = true;
|
|
2828
|
+
const astroRendererName = globalThis[ASTRO_RENDERER_GLOBAL];
|
|
2829
|
+
if (typeof astroRendererName === "string") island[Symbol.for("astro:renderer")] = astroRendererName;
|
|
2634
2830
|
island.define = (tagName, options) => {
|
|
2635
2831
|
if (typeof customElements === "undefined" || typeof HTMLElement === "undefined") {
|
|
2636
2832
|
`${tagName}`;
|
|
@@ -2715,7 +2911,9 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2715
2911
|
const { name, as: rawTag = "div", snapshot = false, skipOnMount: explicitSkipOnMount } = opts;
|
|
2716
2912
|
const tag = assertValidSlotTagName(rawTag);
|
|
2717
2913
|
const resolvedProps = props ?? {};
|
|
2718
|
-
const
|
|
2914
|
+
const streamValues = {};
|
|
2915
|
+
const eventsManifest = /* @__PURE__ */ new Map();
|
|
2916
|
+
const inner = await renderToString(resolvedProps, false, streamValues, eventsManifest, void 0, true);
|
|
2719
2917
|
const encodedProps = escapeHtml(JSON.stringify(resolvedProps));
|
|
2720
2918
|
let stateAttr = "";
|
|
2721
2919
|
if (snapshot !== false) {
|
|
@@ -2725,7 +2923,9 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2725
2923
|
const snapshotData = {};
|
|
2726
2924
|
const input = resolveInput(resolvedProps);
|
|
2727
2925
|
const plainState = buildPlainState(input);
|
|
2728
|
-
|
|
2926
|
+
const stateForDerived = { ...plainState };
|
|
2927
|
+
for (const [k, v] of Object.entries(streamValues)) stateForDerived[k] = () => v;
|
|
2928
|
+
if (doState) for (const entry of states) snapshotData[entry.key] = entry.key in streamValues ? streamValues[entry.key] : plainState[entry.key]();
|
|
2729
2929
|
if (doDerived) {
|
|
2730
2930
|
const derivedResults = {};
|
|
2731
2931
|
for (const entry of deriveds) {
|
|
@@ -2735,7 +2935,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2735
2935
|
let threw = false;
|
|
2736
2936
|
try {
|
|
2737
2937
|
resultPromise = entry.fn({
|
|
2738
|
-
state:
|
|
2938
|
+
state: stateForDerived,
|
|
2739
2939
|
input,
|
|
2740
2940
|
signal: new AbortController().signal
|
|
2741
2941
|
});
|
|
@@ -2771,9 +2971,12 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2771
2971
|
snapshotData["_derived"] = derivedResults;
|
|
2772
2972
|
}
|
|
2773
2973
|
if (doSkipOnMount) snapshotData["_skipOnMount"] = true;
|
|
2974
|
+
if (streams.length > 0) snapshotData["_streams"] = Object.fromEntries(streams.map((entry) => [entry.key, true]));
|
|
2774
2975
|
stateAttr = ` ${STATE_ATTR}='${escapeHtml(JSON.stringify(snapshotData))}'`;
|
|
2775
2976
|
}
|
|
2776
|
-
|
|
2977
|
+
let actionsAttr = "";
|
|
2978
|
+
if (eventsManifest.size > 0) actionsAttr = ` ${ACTIONS_ATTR}='${escapeHtml(JSON.stringify(Object.fromEntries(eventsManifest)))}'`;
|
|
2979
|
+
return `<${tag} data-ilha="${escapeHtml(name)}" ${PROPS_ATTR}='${encodedProps}'${stateAttr}${actionsAttr}>${inner}</${tag}>`;
|
|
2777
2980
|
};
|
|
2778
2981
|
return island;
|
|
2779
2982
|
}
|
|
@@ -2835,6 +3038,7 @@ const rootBuilder = new IlhaBuilder({
|
|
|
2835
3038
|
schema: null,
|
|
2836
3039
|
defaultInput: null,
|
|
2837
3040
|
states: [],
|
|
3041
|
+
streams: [],
|
|
2838
3042
|
deriveds: [],
|
|
2839
3043
|
actions: [],
|
|
2840
3044
|
ons: [],
|
|
@@ -2871,11 +3075,14 @@ const ilha = Object.assign(callableRoot, {
|
|
|
2871
3075
|
function __ilhaJsxEvent({ type, handler, modifier }) {
|
|
2872
3076
|
const ctx = currentRenderCtx();
|
|
2873
3077
|
if (!ctx) return void 0;
|
|
3078
|
+
const isActionStub = SSR_ACTION_STUB in handler;
|
|
3079
|
+
const capture = ctx.manifest && !isActionStub ? captureHandlerActions(handler) : void 0;
|
|
2874
3080
|
const index = ctx.events.length;
|
|
2875
3081
|
ctx.events.push({
|
|
2876
3082
|
type,
|
|
2877
3083
|
handler,
|
|
2878
|
-
modifier
|
|
3084
|
+
modifier,
|
|
3085
|
+
capture
|
|
2879
3086
|
});
|
|
2880
3087
|
return index;
|
|
2881
3088
|
}
|
|
@@ -2887,6 +3094,27 @@ function __ilhaJsxSlot({ island, props, key }) {
|
|
|
2887
3094
|
key
|
|
2888
3095
|
}));
|
|
2889
3096
|
}
|
|
3097
|
+
/**
|
|
3098
|
+
* Morph an element's children toward `html`, patching in place: surviving
|
|
3099
|
+
* elements keep their identity, listeners, and focus (see the morph engine).
|
|
3100
|
+
* Falls back to an innerHTML write when the root tags don't match.
|
|
3101
|
+
*/
|
|
3102
|
+
function morph(host, html) {
|
|
3103
|
+
if (typeof document === "undefined") return;
|
|
3104
|
+
const openTag = host.localName ?? "div";
|
|
3105
|
+
const tpl = document.createElement("template");
|
|
3106
|
+
tpl.innerHTML = `<${openTag}>${html}</${openTag}>`;
|
|
3107
|
+
const next = tpl.content.firstElementChild;
|
|
3108
|
+
if (!next || next.localName !== host.localName) {
|
|
3109
|
+
host.innerHTML = html;
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
try {
|
|
3113
|
+
morphInner(host, next);
|
|
3114
|
+
} catch {
|
|
3115
|
+
host.innerHTML = html;
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
2890
3118
|
const html = ilhaHtml;
|
|
2891
3119
|
const raw = ilhaRaw;
|
|
2892
3120
|
const css = ilhaCss;
|
|
@@ -2895,4 +3123,4 @@ const from = ilhaFrom;
|
|
|
2895
3123
|
const context = ilhaContext;
|
|
2896
3124
|
|
|
2897
3125
|
//#endregion
|
|
2898
|
-
export { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, __ilhaJsxEvent, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css, ilha as default, ilhaEffect as effect, from, html, ilhaSignal, ilhaSignal as signal, mount, onUncaughtError, raw, untrack };
|
|
3126
|
+
export { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, __ilhaJsxEvent, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css, ilha as default, ilhaEffect as effect, from, html, 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-B-viVOB1.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-Cx35Xugv.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-B-viVOB1.js";
|
|
2
2
|
export { Fragment, type JSX, jsx, jsxDEV, jsxs };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ilha",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "A tiny, framework-free island architecture library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework-free",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsc && tsdown",
|
|
56
|
+
"cleanup": "rimraf dist node_modules",
|
|
56
57
|
"test": "bun test"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|