ilha 0.11.0 → 0.12.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/README.md +181 -784
- package/dist/index-DZI0fEmR.d.ts +374 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1667 -1816
- package/dist/internal.d.ts +61 -0
- package/dist/internal.js +39 -0
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/{jsx-runtime-B-viVOB1.d.ts → jsx-runtime-BCGuHwTg.d.ts} +1 -3
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +11 -28
- package/package.json +7 -2
- package/dist/index-Cx35Xugv.d.ts +0 -445
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, serializeServerManifest, setJsxRuntimeBridge } from "./internal.js";
|
|
2
|
+
import { effect as effect$1, endBatch, setActiveSub, signal, startBatch } from "alien-signals";
|
|
2
3
|
|
|
3
4
|
//#region src/index.ts
|
|
5
|
+
const __DEV__ = false;
|
|
4
6
|
const MAX_SNAPSHOT_CHARS = 262144;
|
|
5
7
|
const MAX_SNAPSHOT_DEPTH = 32;
|
|
6
8
|
function exceedsMaxDepth(value, depth) {
|
|
@@ -94,8 +96,8 @@ function extractSlotPropsAttr(root, slotId) {
|
|
|
94
96
|
const SLOT_TAG_NAME_RE = /^[a-z][a-z0-9-]*$/i;
|
|
95
97
|
function assertValidSlotTagName(tag) {
|
|
96
98
|
const trimmed = tag.trim();
|
|
97
|
-
if (trimmed.length === 0) throw new Error("
|
|
98
|
-
if (!SLOT_TAG_NAME_RE.test(trimmed)) throw new Error(`
|
|
99
|
+
if (trimmed.length === 0) throw new Error("The { as } option requires a non-empty HTML tag name.");
|
|
100
|
+
if (!SLOT_TAG_NAME_RE.test(trimmed)) throw new Error(`The { as } option tag must be a valid HTML element name (got "${tag}"). Use names like "span", "div", or "li".`);
|
|
99
101
|
return trimmed.toLowerCase();
|
|
100
102
|
}
|
|
101
103
|
function getIslandSlotTag(island) {
|
|
@@ -134,11 +136,18 @@ function slotPropsForAttr(props, captureActions = false) {
|
|
|
134
136
|
if (key === "children") continue;
|
|
135
137
|
const value = props[key];
|
|
136
138
|
if (typeof value === "function") {
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
const id = captureActions && !isRawHtmlValue(value) ? actionManifestId(value) : void 0;
|
|
140
|
+
if (id) {
|
|
141
|
+
const a = actionBoundArgs(value);
|
|
142
|
+
(out ??= {})[key] = Array.isArray(a) ? {
|
|
143
|
+
__ilha: "action",
|
|
144
|
+
k: id,
|
|
145
|
+
a
|
|
146
|
+
} : {
|
|
147
|
+
__ilha: "action",
|
|
148
|
+
k: id
|
|
149
|
+
};
|
|
150
|
+
}
|
|
142
151
|
continue;
|
|
143
152
|
}
|
|
144
153
|
if (typeof value === "symbol") continue;
|
|
@@ -188,32 +197,10 @@ function reviveSlotPropValue(value) {
|
|
|
188
197
|
for (const key of Object.keys(obj)) out[key] = reviveSlotPropValue(obj[key]);
|
|
189
198
|
return out;
|
|
190
199
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Exact legacy shape produced when RawHtml lost its brand under JSON.stringify:
|
|
193
|
-
* a plain object with a single own key `value` that is a string.
|
|
194
|
-
*/
|
|
195
|
-
function isLegacyRawHtmlBlob(value) {
|
|
196
|
-
if (!value || typeof value !== "object" || isRawHtmlValue(value)) return false;
|
|
197
|
-
if (Object.getPrototypeOf(value) !== Object.prototype) return false;
|
|
198
|
-
const keys = Object.keys(value);
|
|
199
|
-
return keys.length === 1 && keys[0] === "value" && typeof value.value === "string";
|
|
200
|
-
}
|
|
201
|
-
/** Revive attr-parsed props (RawHtml tags). Children stay as plain data if present. */
|
|
200
|
+
/** Revive attr-parsed props, restoring tagged `__ilha:raw` RawHtml values. */
|
|
202
201
|
function reviveSlotProps(props) {
|
|
203
202
|
if (typeof props !== "object" || props === null || Array.isArray(props)) return {};
|
|
204
|
-
|
|
205
|
-
if ("children" in out) out.children = reviveLegacyChildren(out.children);
|
|
206
|
-
return out;
|
|
207
|
-
}
|
|
208
|
-
function reviveLegacyChildren(children) {
|
|
209
|
-
if (!Array.isArray(children)) {
|
|
210
|
-
if (isLegacyRawHtmlBlob(children)) return makeRawHtml(children.value);
|
|
211
|
-
return reviveSlotPropValue(children);
|
|
212
|
-
}
|
|
213
|
-
return children.map((child) => {
|
|
214
|
-
if (isLegacyRawHtmlBlob(child)) return makeRawHtml(child.value);
|
|
215
|
-
return reviveSlotPropValue(child);
|
|
216
|
-
});
|
|
203
|
+
return reviveSlotPropValue(props);
|
|
217
204
|
}
|
|
218
205
|
/** Serialized form of slot props, matching the decoded `data-ilha-props` attr. */
|
|
219
206
|
function serializeSlotProps(props) {
|
|
@@ -328,7 +315,7 @@ function morphChildren(fromParent, toParent) {
|
|
|
328
315
|
fromParent.replaceChild(toEl.cloneNode(true), fromEl);
|
|
329
316
|
continue;
|
|
330
317
|
}
|
|
331
|
-
if (fromEl.hasAttribute(
|
|
318
|
+
if (fromEl.hasAttribute(SLOT_ATTR) && !fromEl.isConnected) {
|
|
332
319
|
fromParent.replaceChild(toEl.cloneNode(true), fromEl);
|
|
333
320
|
continue;
|
|
334
321
|
}
|
|
@@ -460,6 +447,70 @@ const ESC = {
|
|
|
460
447
|
function escapeHtml(value) {
|
|
461
448
|
return String(value).replace(/[&<>"']/g, (c) => ESC[c]);
|
|
462
449
|
}
|
|
450
|
+
const SAFE_CSS_PROP_RE = /^(-{2}[a-zA-Z][a-zA-Z0-9-]*|-?[a-zA-Z][a-zA-Z0-9-]*)$/;
|
|
451
|
+
const URL_ATTRS = /* @__PURE__ */ new Set([
|
|
452
|
+
"href",
|
|
453
|
+
"src",
|
|
454
|
+
"srcset",
|
|
455
|
+
"imagesrcset",
|
|
456
|
+
"action",
|
|
457
|
+
"formaction",
|
|
458
|
+
"cite",
|
|
459
|
+
"data",
|
|
460
|
+
"poster"
|
|
461
|
+
]);
|
|
462
|
+
const SAFE_DATA_IMAGE_RE = /^data:image\/(png|jpe?g|gif|webp|avif)[;,]/i;
|
|
463
|
+
const UNSAFE_SCHEME_RE = /^(?:javascript|vbscript):/i;
|
|
464
|
+
const DATA_IMAGE_CONTEXTS = /* @__PURE__ */ new Set([
|
|
465
|
+
"img:src",
|
|
466
|
+
"source:srcset",
|
|
467
|
+
"link:imagesrcset"
|
|
468
|
+
]);
|
|
469
|
+
function normalizeUrl(value) {
|
|
470
|
+
return value.replace(/[\u0000-\u0020]/g, "");
|
|
471
|
+
}
|
|
472
|
+
function checkUrl(normalized, allowDataImage) {
|
|
473
|
+
if (UNSAFE_SCHEME_RE.test(normalized)) return false;
|
|
474
|
+
if (/^data:/i.test(normalized)) return allowDataImage && SAFE_DATA_IMAGE_RE.test(normalized);
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
/** Strict single-URL check — all data: URLs rejected. */
|
|
478
|
+
function isSafeUrl(value) {
|
|
479
|
+
return checkUrl(normalizeUrl(value), false);
|
|
480
|
+
}
|
|
481
|
+
/** Element- and attribute-aware URL policy for rendered attributes.
|
|
482
|
+
* `srcset`/`imagesrcset` validate each comma-separated candidate URL. */
|
|
483
|
+
function isSafeUrlAttrValue(tagName, attrName, value) {
|
|
484
|
+
const attr = attrName.toLowerCase();
|
|
485
|
+
const allowDataImage = DATA_IMAGE_CONTEXTS.has(`${tagName.toLowerCase()}:${attr}`) && !attr.startsWith("srcset") && attr !== "imagesrcset";
|
|
486
|
+
if (/^(?:srcset|imagesrcset)$/i.test(attr)) return value.split(",").every((candidate) => {
|
|
487
|
+
const url = candidate.trim().split(/\s+/)[0] ?? "";
|
|
488
|
+
return url === "" || checkUrl(normalizeUrl(url), false);
|
|
489
|
+
});
|
|
490
|
+
return checkUrl(normalizeUrl(value), allowDataImage);
|
|
491
|
+
}
|
|
492
|
+
function isUrlAttributeName(name) {
|
|
493
|
+
const lower = name.toLowerCase();
|
|
494
|
+
return URL_ATTRS.has(lower) || /:(href|src|srcset|imagesrcset|action|formaction|cite|data|poster)$/.test(lower);
|
|
495
|
+
}
|
|
496
|
+
function isPlainObject(v) {
|
|
497
|
+
return v !== null && typeof v === "object" && Object.getPrototypeOf(v) === Object.prototype;
|
|
498
|
+
}
|
|
499
|
+
function serializeStyle(value) {
|
|
500
|
+
return Object.entries(value).map(([k, v]) => {
|
|
501
|
+
if (!SAFE_CSS_PROP_RE.test(k)) return "";
|
|
502
|
+
const prop = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
503
|
+
const str = String(v);
|
|
504
|
+
if (/[<>{};]/.test(str) || /expression\(/i.test(str) || /javascript:/i.test(str)) return "";
|
|
505
|
+
return `${prop}:${str}`;
|
|
506
|
+
}).filter(Boolean).join(";");
|
|
507
|
+
}
|
|
508
|
+
function serializeScriptJson(value) {
|
|
509
|
+
return JSON.stringify(value).replace(/</g, "\\u003C");
|
|
510
|
+
}
|
|
511
|
+
function escapeStyleText(text) {
|
|
512
|
+
return text.replace(/</g, "\\3C ");
|
|
513
|
+
}
|
|
463
514
|
function dedentString(str) {
|
|
464
515
|
if (str.length === 0 || str[0] !== "\n") return str;
|
|
465
516
|
const lines = str.split("\n");
|
|
@@ -474,17 +525,8 @@ const SIGNAL_ACCESSOR = Symbol.for("ilha.signalAccessor");
|
|
|
474
525
|
const ISLAND = Symbol.for("ilha.island");
|
|
475
526
|
const ISLAND_CALL = Symbol.for("ilha.islandCall");
|
|
476
527
|
const ASTRO_RENDERER_GLOBAL = Symbol.for("ilha.astroRenderer");
|
|
477
|
-
/** @internal Internal hook used by a parent's mountSlots to mount a child island and
|
|
478
|
-
* retain a handle to push updated props into it on subsequent parent
|
|
479
|
-
* re-renders. Not part of the public surface. */
|
|
480
|
-
const ISLAND_MOUNT_INTERNAL = Symbol.for("ilha.islandMountInternal");
|
|
481
528
|
const ISLAND_SLOT_TAG = Symbol.for("ilha.islandSlotTag");
|
|
482
529
|
const ISLAND_CLIENT_REF = Symbol.for("ilha.clientRef");
|
|
483
|
-
/** @internal Live mount handles keyed by host element. Lets @ilha/router adopt
|
|
484
|
-
* islands hydrated by `ilha.mount()` (whose handles it never saw) and push new
|
|
485
|
-
* loader props into them in place instead of remounting. Entries are removed
|
|
486
|
-
* on unmount. Not part of the public surface. */
|
|
487
|
-
const ISLAND_MOUNT_HANDLES = /* @__PURE__ */ new WeakMap();
|
|
488
530
|
/** @internal Sync re-render of an island from a state snapshot. Attached by
|
|
489
531
|
* render(); used by server-frame endpoints to produce HTML for streamed
|
|
490
532
|
* updates without a live mount. Not part of the public surface. */
|
|
@@ -493,60 +535,140 @@ const ISLAND_RENDER_STATE = Symbol.for("ilha.renderState");
|
|
|
493
535
|
* provided map. Used by emitIslandSlot so nested server islands ship their
|
|
494
536
|
* interactive surface alongside their inlined HTML. */
|
|
495
537
|
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
|
-
}
|
|
501
538
|
const SLOT_ATTR = "data-ilha-slot";
|
|
502
|
-
/** Marks a slot host whose child island is mid leave-transition: it has been
|
|
503
|
-
* unmounted (and its slot id stripped) but stays connected so the leave
|
|
504
|
-
* animation can paint. The morph replaces such elements instead of matching
|
|
505
|
-
* or mutating them. */
|
|
506
|
-
const LEAVING_ATTR = "data-ilha-leaving";
|
|
507
539
|
const PROPS_ATTR = "data-ilha-props";
|
|
508
540
|
const CLIENT_REF_ATTR = "data-ilha-client-ref";
|
|
509
541
|
const STATE_ATTR = "data-ilha-state";
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
542
|
+
function normalizeElseOutput(value) {
|
|
543
|
+
if (value == null || value === false) return value;
|
|
544
|
+
return Array.isArray(value) ? value : [value];
|
|
545
|
+
}
|
|
546
|
+
function createEachResult(items, mapFn) {
|
|
547
|
+
const mapped = items.length === 0 ? [] : items.map(mapFn);
|
|
548
|
+
const result = mapped;
|
|
549
|
+
Object.defineProperty(result, "else", {
|
|
550
|
+
value(fallback) {
|
|
551
|
+
if (items.length === 0) return normalizeElseOutput(typeof fallback === "function" ? fallback(items) : fallback);
|
|
552
|
+
return mapped;
|
|
553
|
+
},
|
|
554
|
+
enumerable: false
|
|
555
|
+
});
|
|
556
|
+
return result;
|
|
557
|
+
}
|
|
558
|
+
function createEachBuilder(items) {
|
|
559
|
+
return {
|
|
560
|
+
key(keyFn) {
|
|
561
|
+
return { as(mapFn) {
|
|
562
|
+
return createEachResult(items, (item, index) => mapFn(item, index, keyFn(item, index)));
|
|
563
|
+
} };
|
|
564
|
+
},
|
|
565
|
+
as(mapFn) {
|
|
566
|
+
return createEachResult(items, mapFn);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
}
|
|
524
570
|
/**
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
571
|
+
* Svelte-style `{#each}` helper for mapping collections to rendered output
|
|
572
|
+
* with an optional empty fallback.
|
|
573
|
+
*
|
|
574
|
+
* @example — plain list (empty → [])
|
|
575
|
+
* each(items).as((item) => html`<li>${item.name}</li>`)
|
|
528
576
|
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
*
|
|
577
|
+
* @example — keyed list feeding island.key(), with empty fallback
|
|
578
|
+
* each(items)
|
|
579
|
+
* .key((item) => item.id)
|
|
580
|
+
* .as((item, _i, id) => Row.key(id)({ item }))
|
|
581
|
+
* .else(() => html`<EmptyState />`)
|
|
534
582
|
*/
|
|
535
|
-
function
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
583
|
+
function each(items) {
|
|
584
|
+
if (typeof items === "function") throw new TypeError("[ilha] each() expected an array but received a function. Call accessors first (each(state.items())) or pass a snapshot from a reactive render.");
|
|
585
|
+
return createEachBuilder(Array.isArray(items) ? items : []);
|
|
586
|
+
}
|
|
587
|
+
/** Marks SSR action stubs so event registration can recognize action
|
|
588
|
+
* references without ever invoking them (executing a handler body during
|
|
589
|
+
* render would run its side effects server-side). Symbol.for brands keep
|
|
590
|
+
* this recognizable across duplicate ilha copies and external action
|
|
591
|
+
* implementations (e.g. oxidejs) in the same realm. */
|
|
592
|
+
const SSR_ACTION_STUB = Symbol.for("ilha.ssrActionStub");
|
|
593
|
+
const ACTION_MANIFEST_ID = Symbol.for("ilha.actionManifestId");
|
|
594
|
+
const ACTION_BOUND_ARGS = Symbol.for("ilha.actionBoundArgs");
|
|
595
|
+
/** Serialized size ceiling for `.withArgs()` payloads (JSON chars). */
|
|
596
|
+
const MAX_BOUND_ARGS_CHARS = 8192;
|
|
597
|
+
/** Manifest id for a brand-recognized action reference, else undefined.
|
|
598
|
+
* Anything that isn't a branded action stub is rejected outright — event
|
|
599
|
+
* handlers are never executed during SSR to discover what they call. */
|
|
600
|
+
function actionManifestId(fn) {
|
|
601
|
+
if (typeof fn !== "function" || !(SSR_ACTION_STUB in fn)) return void 0;
|
|
602
|
+
return fn[ACTION_MANIFEST_ID];
|
|
603
|
+
}
|
|
604
|
+
/** Bound args recorded by `.withArgs()` on an action reference, else undefined. */
|
|
605
|
+
function actionBoundArgs(fn) {
|
|
606
|
+
if (typeof fn !== "function" || !(ACTION_BOUND_ARGS in fn)) return void 0;
|
|
607
|
+
const args = fn[ACTION_BOUND_ARGS];
|
|
608
|
+
return Array.isArray(args) ? args : void 0;
|
|
609
|
+
}
|
|
610
|
+
/** Attach `.withArgs(...args)` to an action accessor. Returns an explicit,
|
|
611
|
+
* serializable action REFERENCE — never a closure to inspect or probe.
|
|
612
|
+
* Arguments are validated JSON-safe at bind time; the hydration manifest
|
|
613
|
+
* stores `{ actionId, args }` directly and the client RPC path sends them
|
|
614
|
+
* only after a real user event. On the client, calling the bound reference
|
|
615
|
+
* invokes the accessor with the bound payload. */
|
|
616
|
+
/** Keys that must never appear in a bound payload — they become dangerous
|
|
617
|
+
* if an RPC handler or downstream library merges the object into another. */
|
|
618
|
+
const UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
619
|
+
"__proto__",
|
|
620
|
+
"constructor",
|
|
621
|
+
"prototype"
|
|
622
|
+
]);
|
|
623
|
+
/** Strict JSON-safety validator: accept, or throw — never transform.
|
|
624
|
+
* Rejects undefined, functions, symbols, bigint, non-finite numbers,
|
|
625
|
+
* Dates/custom prototypes/Map/Set, circular values, and unsafe keys so a
|
|
626
|
+
* bound payload is byte-identical to what the author wrote. */
|
|
627
|
+
function assertJsonSafe(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
628
|
+
if (value === null) return;
|
|
629
|
+
switch (typeof value) {
|
|
630
|
+
case "string":
|
|
631
|
+
case "boolean": return;
|
|
632
|
+
case "number":
|
|
633
|
+
if (Number.isFinite(value)) return;
|
|
634
|
+
throw new TypeError("withArgs() rejects non-finite numbers.");
|
|
635
|
+
case "object": break;
|
|
636
|
+
default: throw new TypeError(`withArgs() rejects ${typeof value} values.`);
|
|
637
|
+
}
|
|
638
|
+
if (seen.has(value)) throw new TypeError("withArgs() rejects circular values.");
|
|
639
|
+
seen.add(value);
|
|
640
|
+
if (Array.isArray(value)) {
|
|
641
|
+
for (const item of value) assertJsonSafe(item, seen);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) throw new TypeError("withArgs() accepts plain objects only.");
|
|
645
|
+
for (const [key, child] of Object.entries(value)) {
|
|
646
|
+
if (UNSAFE_KEYS.has(key)) throw new TypeError(`withArgs() rejects unsafe key "${key}".`);
|
|
647
|
+
assertJsonSafe(child, seen);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/** Deep-clone an already-validated JSON-safe value. */
|
|
651
|
+
function cloneJsonSafe(value) {
|
|
652
|
+
return structuredClone(value);
|
|
653
|
+
}
|
|
654
|
+
function attachWithArgs(accessor) {
|
|
655
|
+
Object.defineProperty(accessor, "withArgs", {
|
|
656
|
+
enumerable: false,
|
|
657
|
+
value: (...args) => {
|
|
658
|
+
for (const arg of args) assertJsonSafe(arg);
|
|
659
|
+
const serialized = cloneJsonSafe(args);
|
|
660
|
+
if (JSON.stringify(serialized).length > MAX_BOUND_ARGS_CHARS) throw new RangeError(`withArgs() payload exceeds ${MAX_BOUND_ARGS_CHARS} JSON characters.`);
|
|
661
|
+
const manifestId = actionManifestId(accessor);
|
|
662
|
+
const bound = (...runtimeArgs) => {
|
|
663
|
+
return accessor(serialized.length > 0 ? serialized.length === 1 ? serialized[0] : serialized : runtimeArgs.length === 1 ? runtimeArgs[0] : runtimeArgs);
|
|
664
|
+
};
|
|
665
|
+
const brand = bound;
|
|
666
|
+
brand[SSR_ACTION_STUB] = true;
|
|
667
|
+
brand[ACTION_MANIFEST_ID] = manifestId;
|
|
668
|
+
brand[ACTION_BOUND_ARGS] = serialized;
|
|
669
|
+
return bound;
|
|
670
|
+
}
|
|
671
|
+
});
|
|
550
672
|
}
|
|
551
673
|
const RENDER_CTX_STACK = Symbol.for("ilha.renderCtxStack");
|
|
552
674
|
function renderCtxStack() {
|
|
@@ -605,8 +727,8 @@ function emitIslandSlot({ island, props, key }) {
|
|
|
605
727
|
try {
|
|
606
728
|
const manifest = /* @__PURE__ */ new Map();
|
|
607
729
|
const ssrWithManifest = island[ISLAND_SSR_MANIFEST];
|
|
608
|
-
const result = ssrWithManifest ? ssrWithManifest(props, manifest) : island(props);
|
|
609
|
-
const prefix = () =>
|
|
730
|
+
const result = ssrWithManifest ? ssrWithManifest(props, manifest) : island.toStringAsync(props);
|
|
731
|
+
const prefix = () => serializeServerManifest(manifest);
|
|
610
732
|
if (result instanceof Promise) {
|
|
611
733
|
ctx.pending.set(id, result.then(String).then((html) => prefix() + html));
|
|
612
734
|
return wrapIslandSlotHtml({
|
|
@@ -656,7 +778,7 @@ function resolveSignalSetter(read, value) {
|
|
|
656
778
|
function markSignalAccessor(fn) {
|
|
657
779
|
fn[SIGNAL_ACCESSOR] = true;
|
|
658
780
|
const accessor = fn;
|
|
659
|
-
accessor.select = (
|
|
781
|
+
accessor.select = ((...args) => createSelectAccessor(accessor, args));
|
|
660
782
|
return accessor;
|
|
661
783
|
}
|
|
662
784
|
function isSignalAccessor(v) {
|
|
@@ -731,22 +853,27 @@ function trackSelectPath(rootState, selector) {
|
|
|
731
853
|
selector(track(rootState));
|
|
732
854
|
return path;
|
|
733
855
|
}
|
|
734
|
-
function createSelectAccessor(root,
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
856
|
+
function createSelectAccessor(root, input) {
|
|
857
|
+
const selector = typeof input[0] === "function" ? input[0] : void 0;
|
|
858
|
+
let path;
|
|
859
|
+
if (selector) {
|
|
860
|
+
path = trackSelectPath(root(), selector);
|
|
861
|
+
const selected = selector(root());
|
|
862
|
+
const resolved = path.length === 0 ? root() : getAtPath(root(), path);
|
|
863
|
+
if (!Object.is(selected, resolved)) throw new Error("select(): selector must only traverse nested properties or array indexes — derived or transformed values are not supported.");
|
|
864
|
+
} else path = input;
|
|
865
|
+
const read = (from) => selector ? path.length === 0 ? selector(from) : getAtPath(from, path) : getAtPath(from, path);
|
|
739
866
|
return markSignalAccessor((...args) => {
|
|
740
|
-
if (args.length === 0) return
|
|
867
|
+
if (args.length === 0) return read(root());
|
|
741
868
|
const previousRoot = root();
|
|
742
|
-
const previousSelected =
|
|
869
|
+
const previousSelected = read(previousRoot);
|
|
743
870
|
const value = resolveSignalSetter(() => previousSelected, args[0]);
|
|
744
|
-
const next = path.length === 0 ? value : setAtPath({
|
|
871
|
+
const next = path.length === 0 && selector ? value : setAtPath({
|
|
745
872
|
object: previousRoot,
|
|
746
873
|
path,
|
|
747
874
|
value
|
|
748
875
|
});
|
|
749
|
-
if (!Object.is(previousRoot, next)) root((
|
|
876
|
+
if (!Object.is(previousRoot, next)) root(() => next);
|
|
750
877
|
});
|
|
751
878
|
}
|
|
752
879
|
function ilhaRaw(value) {
|
|
@@ -755,14 +882,37 @@ function ilhaRaw(value) {
|
|
|
755
882
|
value
|
|
756
883
|
};
|
|
757
884
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
885
|
+
/**
|
|
886
|
+
* Serialize a JSON-safe value into executable `<script>` content safely. `<`
|
|
887
|
+
* is escaped as `\u003C`, so a value can never close the script block or open
|
|
888
|
+
* the `<!--` escape hatch. Use it for script-element data:
|
|
889
|
+
*
|
|
890
|
+
* ```ts
|
|
891
|
+
* html`<script>const d = ${json(payload)};<\/script>`
|
|
892
|
+
* ```
|
|
893
|
+
*
|
|
894
|
+
* Like `raw()`, `json()` is for element *content* only — interpolating it into
|
|
895
|
+
* an attribute is author error. It does not protect against values that are
|
|
896
|
+
* not valid JSON (functions, symbols, undefined, cyclic objects are dropped
|
|
897
|
+
* or throw, matching `JSON.stringify`).
|
|
898
|
+
*/
|
|
899
|
+
function json(value) {
|
|
900
|
+
return ilhaRaw(serializeScriptJson(value));
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Escape text for a `<style>` element body. `<` becomes the CSS hex escape
|
|
904
|
+
* `\3C `, so the value can never close the style block or open the `<!--`
|
|
905
|
+
* escape hatch while surviving CSS parsing:
|
|
906
|
+
*
|
|
907
|
+
* ```ts
|
|
908
|
+
* html`<style>${css(cssSource)}</style>`
|
|
909
|
+
* ```
|
|
910
|
+
*
|
|
911
|
+
* Like `raw()`, `css()` is for element *content* only — interpolating it into
|
|
912
|
+
* an attribute is author error.
|
|
913
|
+
*/
|
|
914
|
+
function css(value) {
|
|
915
|
+
return ilhaRaw(escapeStyleText(value));
|
|
766
916
|
}
|
|
767
917
|
function interpolateValue(v) {
|
|
768
918
|
if (v == null || v === true || v === false) return "";
|
|
@@ -788,21 +938,31 @@ function interpolateValue(v) {
|
|
|
788
938
|
const BIND_VALID_KINDS = /* @__PURE__ */ new Set([
|
|
789
939
|
"value",
|
|
790
940
|
"checked",
|
|
791
|
-
"valueAsNumber",
|
|
792
|
-
"valueAsDate",
|
|
793
941
|
"files",
|
|
794
942
|
"open",
|
|
795
943
|
"group",
|
|
796
944
|
"this"
|
|
797
945
|
]);
|
|
798
946
|
const BIND_PREFIX_RE = /\bbind:([a-zA-Z]+)\s*=\s*("|')?$/;
|
|
799
|
-
const EVENT_PREFIX_RE = /(?:^|\s)on([a-
|
|
947
|
+
const EVENT_PREFIX_RE = /(?:^|\s)on([a-zA-Z][a-zA-Z0-9-]*)(?::([a-zA-Z][a-zA-Z0-9-]*))?\s*=\s*("|')?$/;
|
|
800
948
|
const NATIVE_EVENT_MODIFIERS = /* @__PURE__ */ new Set([
|
|
801
949
|
"abortable",
|
|
802
950
|
"once",
|
|
803
951
|
"capture",
|
|
804
952
|
"passive"
|
|
805
953
|
]);
|
|
954
|
+
const FULL_VALUE_ATTR_RE = /(?:^|\s)([a-zA-Z_:][a-zA-Z0-9:._-]*)\s*=\s*("|')?$/;
|
|
955
|
+
const UNQUOTED_HAZARD_RE = /[\s=]|`/;
|
|
956
|
+
function resolveTemplateValue(v) {
|
|
957
|
+
if (isSignalAccessor(v)) return v();
|
|
958
|
+
if (typeof v === "function") return v();
|
|
959
|
+
return v;
|
|
960
|
+
}
|
|
961
|
+
function openTagName(source, index) {
|
|
962
|
+
const open = source.lastIndexOf("<", index);
|
|
963
|
+
if (open === -1) return "element";
|
|
964
|
+
return (source.slice(open).match(/^<\s*([a-zA-Z][a-zA-Z0-9:._-]*)/)?.[1] ?? "element").toLowerCase();
|
|
965
|
+
}
|
|
806
966
|
function isEventAttributePosition(source, index) {
|
|
807
967
|
let open = -1;
|
|
808
968
|
let quote = null;
|
|
@@ -837,14 +997,6 @@ function findTagCloseIndex(chunk, initialQuote) {
|
|
|
837
997
|
quote
|
|
838
998
|
};
|
|
839
999
|
}
|
|
840
|
-
function formatDateForInput(d) {
|
|
841
|
-
if (d instanceof Date) {
|
|
842
|
-
if (isNaN(d.getTime())) return "";
|
|
843
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
844
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
845
|
-
}
|
|
846
|
-
return "";
|
|
847
|
-
}
|
|
848
1000
|
function extractElementStaticValue(prefix) {
|
|
849
1001
|
const lastOpen = prefix.lastIndexOf("<");
|
|
850
1002
|
if (lastOpen === -1) return null;
|
|
@@ -864,8 +1016,6 @@ function emitBindSSR({ kind, index, accessor, prefixForStaticPeek }) {
|
|
|
864
1016
|
}
|
|
865
1017
|
switch (kind) {
|
|
866
1018
|
case "value": return [` value="${escapeHtml(v ?? "")}"`, spec];
|
|
867
|
-
case "valueAsNumber": return [` value="${escapeHtml(v == null ? "" : String(v))}"`, spec];
|
|
868
|
-
case "valueAsDate": return [` value="${escapeHtml(formatDateForInput(v))}"`, spec];
|
|
869
1019
|
case "checked": return [v ? ` checked` : ``, spec];
|
|
870
1020
|
case "open": return [v ? ` open` : ``, spec];
|
|
871
1021
|
case "files": return [``, spec];
|
|
@@ -910,24 +1060,29 @@ function ilhaHtml(strings, ...values) {
|
|
|
910
1060
|
const value = values[i];
|
|
911
1061
|
const eventMatch = chunk.match(EVENT_PREFIX_RE);
|
|
912
1062
|
const eventMatchStart = eventMatch ? chunk.length - eventMatch[0].length : -1;
|
|
1063
|
+
if (!eventMatch && typeof value === "function") {
|
|
1064
|
+
const ambiguous = chunk.match(/(?:^|\s)on([a-zA-Z][a-zA-Z0-9-]*)(?::[a-zA-Z][a-zA-Z0-9-]*)?\s*=\s*(?:(?:"[^"]*)|(?:'[^']*)|(?:[^\s"'=<>`]+))$/);
|
|
1065
|
+
const start = ambiguous ? chunk.length - ambiguous[0].length : -1;
|
|
1066
|
+
if (ambiguous && isEventAttributePosition(result + chunk, result.length + start)) throw new Error(`[ilha] on${ambiguous[1]} handler must occupy the entire attribute value. Write on${ambiguous[1]}=\${handler}.`);
|
|
1067
|
+
}
|
|
913
1068
|
const eventAttributeFragment = eventMatch !== null && chunk.slice(0, eventMatchStart).trim() === "";
|
|
914
1069
|
if (eventMatch && (eventAttributeFragment || isEventAttributePosition(result + chunk, result.length + eventMatchStart))) {
|
|
915
|
-
const eventName = eventMatch[1];
|
|
916
|
-
const rawModifier = eventMatch[2];
|
|
917
|
-
const modifier = NATIVE_EVENT_MODIFIERS.has(rawModifier) ? rawModifier : void 0;
|
|
1070
|
+
const eventName = eventMatch[1].toLowerCase();
|
|
1071
|
+
const rawModifier = eventMatch[2]?.toLowerCase();
|
|
1072
|
+
const modifier = rawModifier !== void 0 && NATIVE_EVENT_MODIFIERS.has(rawModifier) ? rawModifier : void 0;
|
|
918
1073
|
const openQuote = eventMatch[3] ?? null;
|
|
1074
|
+
const nextChunk = strings[i + 1] ?? "";
|
|
1075
|
+
if (!(openQuote ? nextChunk.startsWith(openQuote) : /^(?:\s|\/?>|$)/.test(nextChunk))) throw new Error(`[ilha] on${eventName} handler must occupy the entire attribute value. Write on${eventName}=\${handler}.`);
|
|
919
1076
|
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;
|
|
922
1077
|
result += cleanPrefix;
|
|
1078
|
+
typeof value === "function" && SSR_ACTION_STUB in value;
|
|
923
1079
|
if (ctx && typeof value === "function") {
|
|
924
1080
|
const index = ctx.events.length;
|
|
925
1081
|
if (rawModifier && !modifier) {} else {
|
|
926
1082
|
ctx.events.push({
|
|
927
1083
|
type: eventName,
|
|
928
1084
|
handler: value,
|
|
929
|
-
modifier
|
|
930
|
-
capture
|
|
1085
|
+
modifier
|
|
931
1086
|
});
|
|
932
1087
|
pendingEventSpecs += (pendingEventSpecs ? "," : "") + `${eventName}:${index}`;
|
|
933
1088
|
}
|
|
@@ -968,6 +1123,72 @@ function ilhaHtml(strings, ...values) {
|
|
|
968
1123
|
if (openQuote) stripLeadingQuote = openQuote;
|
|
969
1124
|
continue;
|
|
970
1125
|
}
|
|
1126
|
+
const attrMatch = chunk.match(FULL_VALUE_ATTR_RE);
|
|
1127
|
+
if (attrMatch && isEventAttributePosition(result + chunk, result.length + attrMatch.index)) {
|
|
1128
|
+
const attrName = attrMatch[1];
|
|
1129
|
+
const openQuote = attrMatch[2] ?? null;
|
|
1130
|
+
if (openQuote) stripLeadingQuote = openQuote;
|
|
1131
|
+
const cleanPrefix = chunk.slice(0, attrMatch.index).replace(/\s+$/, "");
|
|
1132
|
+
if (attrName.length > 2 && /^on/i.test(attrName)) {
|
|
1133
|
+
result += cleanPrefix;
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (attrName.toLowerCase() === "srcdoc") {
|
|
1137
|
+
result += cleanPrefix;
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
if (isIsland(value) || isIslandCall(value)) {
|
|
1141
|
+
stripLeadingQuote = null;
|
|
1142
|
+
result += chunk + interpolateValue(value);
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (isUrlAttributeName(attrName)) {
|
|
1146
|
+
if (isRawHtml(value)) {
|
|
1147
|
+
result += cleanPrefix + ` ${attrName}="${value.value}"`;
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
const resolved = resolveTemplateValue(value);
|
|
1151
|
+
if (resolved == null || resolved === false) {
|
|
1152
|
+
result += cleanPrefix;
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
const url = String(resolved);
|
|
1156
|
+
if (!isSafeUrlAttrValue(openTagName(result + chunk, result.length + attrMatch.index), attrName, url)) {
|
|
1157
|
+
result += cleanPrefix;
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
const quotedUrl = openQuote !== null || UNQUOTED_HAZARD_RE.test(url);
|
|
1161
|
+
result += cleanPrefix + ` ${attrName}=` + (quotedUrl ? `"${escapeHtml(url)}"` : escapeHtml(url));
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
if (attrName.toLowerCase() === "style") {
|
|
1165
|
+
if (!isRawHtml(value)) {
|
|
1166
|
+
const resolved = resolveTemplateValue(value);
|
|
1167
|
+
if (isPlainObject(resolved)) {
|
|
1168
|
+
result += cleanPrefix + ` style="${escapeHtml(serializeStyle(resolved))}"`;
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
if (isRawHtml(value)) {
|
|
1174
|
+
stripLeadingQuote = null;
|
|
1175
|
+
result += chunk + interpolateValue(value);
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
const resolved = resolveTemplateValue(value);
|
|
1179
|
+
if (resolved == null || resolved === false) {
|
|
1180
|
+
result += cleanPrefix;
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
if (resolved === true) {
|
|
1184
|
+
result += cleanPrefix + ` ${attrName}`;
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
const generic = String(resolved);
|
|
1188
|
+
if (openQuote !== null || UNQUOTED_HAZARD_RE.test(generic)) result += cleanPrefix + ` ${attrName}="${escapeHtml(generic)}"`;
|
|
1189
|
+
else result += cleanPrefix + ` ${attrName}=${escapeHtml(generic)}`;
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
971
1192
|
result += chunk + interpolateValue(value);
|
|
972
1193
|
}
|
|
973
1194
|
if (pendingBindSpecs !== "") result += ` data-ilha-bind="${pendingBindSpecs}"`;
|
|
@@ -988,10 +1209,10 @@ const contextRegistry = /* @__PURE__ */ new Map();
|
|
|
988
1209
|
function ilhaContextFn(key, initial) {
|
|
989
1210
|
if (contextRegistry.has(key)) return contextRegistry.get(key);
|
|
990
1211
|
const s = signal(initial);
|
|
991
|
-
const accessor = (...args) => {
|
|
1212
|
+
const accessor = markSignalAccessor((...args) => {
|
|
992
1213
|
if (args.length === 0) return s();
|
|
993
1214
|
s(resolveSignalSetter(() => s(), args[0]));
|
|
994
|
-
};
|
|
1215
|
+
});
|
|
995
1216
|
contextRegistry.set(key, accessor);
|
|
996
1217
|
return accessor;
|
|
997
1218
|
}
|
|
@@ -1007,16 +1228,11 @@ const ilhaContext = Object.assign(ilhaContextFn, {
|
|
|
1007
1228
|
});
|
|
1008
1229
|
/**
|
|
1009
1230
|
* Create a free-standing reactive signal that lives outside any island.
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
* template syntax.
|
|
1013
|
-
*
|
|
1014
|
-
* The returned accessor is a getter when called with no arguments and a
|
|
1015
|
-
* setter when called with one. Reading it inside a `.derived()`, `.effect()`,
|
|
1016
|
-
* or `.render()` automatically subscribes the surrounding reactive scope —
|
|
1017
|
-
* so when the signal changes, dependents re-run as if it were local state.
|
|
1231
|
+
* Internal: used by state()'s slot-drift fallback. The public way to create
|
|
1232
|
+
* free-standing shared state is `context(key, initial)`.
|
|
1018
1233
|
*/
|
|
1019
1234
|
function ilhaSignal(initial) {
|
|
1235
|
+
if (currentFrame()?.creating && __DEV__);
|
|
1020
1236
|
const s = signal(initial);
|
|
1021
1237
|
return markSignalAccessor((...args) => {
|
|
1022
1238
|
if (args.length === 0) return s();
|
|
@@ -1024,25 +1240,6 @@ function ilhaSignal(initial) {
|
|
|
1024
1240
|
});
|
|
1025
1241
|
}
|
|
1026
1242
|
/**
|
|
1027
|
-
* Create a free-standing read-only reactive value derived from other signals.
|
|
1028
|
-
* The computation is lazy and cached: `fn` re-runs only when a signal it read
|
|
1029
|
-
* changed and the computed is read again. Reading it inside a `.derived()`,
|
|
1030
|
-
* `.effect()`, `.render()`, or top-level `effect()` subscribes that scope —
|
|
1031
|
-
* dependents re-run when the computed's value changes.
|
|
1032
|
-
*
|
|
1033
|
-
* ```ts
|
|
1034
|
-
* const items = ilha.signal([1, 2, 3]);
|
|
1035
|
-
* const total = ilha.computed(() => items().reduce((a, b) => a + b, 0));
|
|
1036
|
-
* ```
|
|
1037
|
-
*/
|
|
1038
|
-
function ilhaComputed(fn) {
|
|
1039
|
-
const c = computed(fn);
|
|
1040
|
-
return markSignalAccessor((...args) => {
|
|
1041
|
-
if (args.length > 0) return;
|
|
1042
|
-
return c();
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
1243
|
* Run a free-standing reactive effect outside any island. `fn` runs once
|
|
1047
1244
|
* immediately and again whenever a signal it read changes. It may return a
|
|
1048
1245
|
* cleanup function, invoked before each re-run and on stop. Signal writes
|
|
@@ -1067,7 +1264,7 @@ function ilhaEffect(fn) {
|
|
|
1067
1264
|
cleanup = void 0;
|
|
1068
1265
|
}
|
|
1069
1266
|
};
|
|
1070
|
-
const stop = effect(() => {
|
|
1267
|
+
const stop = effect$1(() => {
|
|
1071
1268
|
runCleanup();
|
|
1072
1269
|
startBatch();
|
|
1073
1270
|
try {
|
|
@@ -1115,6 +1312,74 @@ function batch(fn) {
|
|
|
1115
1312
|
endBatch();
|
|
1116
1313
|
}
|
|
1117
1314
|
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Keep a standalone signal in sync with persistent storage:
|
|
1317
|
+
*
|
|
1318
|
+
* 1. On call, reads `key` and writes the stored value into the signal.
|
|
1319
|
+
* 2. Subscribes to the signal and writes its value back on every change.
|
|
1320
|
+
* 3. Optionally mirrors writes from other tabs (`storage` events).
|
|
1321
|
+
*
|
|
1322
|
+
* No-op on the server (returns an inert unsubscribe). Call the returned
|
|
1323
|
+
* unsubscribe to stop syncing.
|
|
1324
|
+
*
|
|
1325
|
+
* ```ts
|
|
1326
|
+
* const cart = signal([] as string[]);
|
|
1327
|
+
* persist(cart, "cart");
|
|
1328
|
+
* ```
|
|
1329
|
+
*/
|
|
1330
|
+
function persist(accessor, key, options = {}) {
|
|
1331
|
+
if (typeof window === "undefined") return () => {};
|
|
1332
|
+
const storage = options.storage ?? window.localStorage;
|
|
1333
|
+
const serialize = options.serialize ?? JSON.stringify;
|
|
1334
|
+
const deserialize = options.deserialize ?? ((raw) => {
|
|
1335
|
+
try {
|
|
1336
|
+
return JSON.parse(raw);
|
|
1337
|
+
} catch {
|
|
1338
|
+
return null;
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
const label = `[ilha] persist("${key}")`;
|
|
1342
|
+
const applyRaw = (raw) => {
|
|
1343
|
+
if (raw == null) return;
|
|
1344
|
+
try {
|
|
1345
|
+
const value = deserialize(raw);
|
|
1346
|
+
if (value !== null) {
|
|
1347
|
+
const prevSub = setActiveSub(void 0);
|
|
1348
|
+
try {
|
|
1349
|
+
accessor(() => value);
|
|
1350
|
+
} finally {
|
|
1351
|
+
setActiveSub(prevSub);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
} catch (err) {
|
|
1355
|
+
console.error(`${label}: failed to restore`, err);
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
try {
|
|
1359
|
+
applyRaw(storage.getItem(key));
|
|
1360
|
+
} catch (err) {
|
|
1361
|
+
console.error(`${label}: failed to read storage`, err);
|
|
1362
|
+
}
|
|
1363
|
+
const stopEffect = ilhaEffect(() => {
|
|
1364
|
+
try {
|
|
1365
|
+
storage.setItem(key, serialize(accessor()));
|
|
1366
|
+
} catch (err) {
|
|
1367
|
+
console.error(`${label}: failed to write storage`, err);
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
let onStorage = null;
|
|
1371
|
+
if (options.crossTab !== false && storage === window.localStorage) {
|
|
1372
|
+
onStorage = (e) => {
|
|
1373
|
+
if (e.key !== key) return;
|
|
1374
|
+
applyRaw(e.newValue);
|
|
1375
|
+
};
|
|
1376
|
+
window.addEventListener("storage", onStorage);
|
|
1377
|
+
}
|
|
1378
|
+
return () => {
|
|
1379
|
+
stopEffect();
|
|
1380
|
+
if (onStorage) window.removeEventListener("storage", onStorage);
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1118
1383
|
function createDerivedAccessor(read, write) {
|
|
1119
1384
|
const accessor = markSignalAccessor((...args) => {
|
|
1120
1385
|
if (args.length > 0) {
|
|
@@ -1135,342 +1400,69 @@ function defaultDerivedAccessor() {
|
|
|
1135
1400
|
error: void 0
|
|
1136
1401
|
}));
|
|
1137
1402
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
const ILHA_QUERY = Symbol.for("ilha.store.query");
|
|
1151
|
-
const ILHA_QUERY_RUN = Symbol.for("ilha.store.query.run");
|
|
1152
|
-
const ILHA_QUERY_PENDING = Symbol.for("ilha.store.query.pending");
|
|
1153
|
-
const ILHA_QUERY_STACK = Symbol.for("ilha.store.query.stack");
|
|
1154
|
-
function queryCaptureStack() {
|
|
1155
|
-
const g = globalThis;
|
|
1156
|
-
let stack = g[ILHA_QUERY_STACK];
|
|
1157
|
-
if (!stack) {
|
|
1158
|
-
stack = [];
|
|
1159
|
-
g[ILHA_QUERY_STACK] = stack;
|
|
1160
|
-
}
|
|
1161
|
-
return stack;
|
|
1162
|
-
}
|
|
1163
|
-
function isQueryCall(value) {
|
|
1164
|
-
return value !== null && typeof value === "object" && ILHA_QUERY in value;
|
|
1165
|
-
}
|
|
1166
|
-
function resolveQueryCall(result, captureSlot) {
|
|
1167
|
-
const pending = globalThis[ILHA_QUERY_PENDING];
|
|
1168
|
-
globalThis[ILHA_QUERY_PENDING] = void 0;
|
|
1169
|
-
return captureSlot.queryCall ?? pending ?? (isQueryCall(result) ? result : void 0);
|
|
1170
|
-
}
|
|
1171
|
-
function runBrandedQuery(queryCall, abortSignal, env) {
|
|
1172
|
-
const writeEnv = (next) => {
|
|
1173
|
-
if (abortSignal.aborted) return;
|
|
1174
|
-
const prevSub = setActiveSub(void 0);
|
|
1175
|
-
env(next);
|
|
1176
|
-
setActiveSub(prevSub);
|
|
1177
|
-
};
|
|
1178
|
-
const run = queryCall[ILHA_QUERY_RUN];
|
|
1179
|
-
if (typeof run !== "function") {
|
|
1180
|
-
writeEnv({
|
|
1181
|
-
loading: false,
|
|
1182
|
-
value: void 0,
|
|
1183
|
-
error: /* @__PURE__ */ new Error("[@ilha] branded query is missing a callable runner")
|
|
1184
|
-
});
|
|
1185
|
-
return;
|
|
1186
|
-
}
|
|
1187
|
-
try {
|
|
1188
|
-
run.call(queryCall, abortSignal, {
|
|
1189
|
-
onResult: (value) => {
|
|
1190
|
-
writeEnv({
|
|
1191
|
-
loading: false,
|
|
1192
|
-
value,
|
|
1193
|
-
error: void 0
|
|
1194
|
-
});
|
|
1195
|
-
},
|
|
1196
|
-
onError: (err) => {
|
|
1197
|
-
writeEnv({
|
|
1198
|
-
loading: false,
|
|
1199
|
-
value: void 0,
|
|
1200
|
-
error: err
|
|
1201
|
-
});
|
|
1202
|
-
},
|
|
1203
|
-
onFetchStart: () => {
|
|
1204
|
-
const prevSub = setActiveSub(void 0);
|
|
1205
|
-
env({
|
|
1206
|
-
loading: true,
|
|
1207
|
-
value: env().value,
|
|
1208
|
-
error: void 0
|
|
1209
|
-
});
|
|
1210
|
-
setActiveSub(prevSub);
|
|
1211
|
-
}
|
|
1212
|
-
});
|
|
1213
|
-
} catch (err) {
|
|
1214
|
-
writeEnv({
|
|
1215
|
-
loading: false,
|
|
1216
|
-
value: void 0,
|
|
1217
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1403
|
+
const BIND_SENTINEL_ATTR = "data-ilha-bind";
|
|
1404
|
+
const EVENT_SENTINEL_ATTR = "data-ilha-on";
|
|
1405
|
+
function parseBindSentinel(value) {
|
|
1406
|
+
const out = [];
|
|
1407
|
+
for (const part of value.split(",")) {
|
|
1408
|
+
const [kind, idx] = part.split(":");
|
|
1409
|
+
if (!kind || !idx) continue;
|
|
1410
|
+
const i = Number(idx);
|
|
1411
|
+
if (!Number.isInteger(i) || i < 0) continue;
|
|
1412
|
+
out.push({
|
|
1413
|
+
kind,
|
|
1414
|
+
index: i
|
|
1218
1415
|
});
|
|
1219
1416
|
}
|
|
1417
|
+
return out;
|
|
1220
1418
|
}
|
|
1221
|
-
function
|
|
1222
|
-
const
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
value: void 0,
|
|
1229
|
-
error: void 0
|
|
1419
|
+
function resolveBindOps(el, kind) {
|
|
1420
|
+
const input = el;
|
|
1421
|
+
switch (kind) {
|
|
1422
|
+
case "value": return {
|
|
1423
|
+
event: el.tagName === "SELECT" ? "change" : "input",
|
|
1424
|
+
read: (el) => el.value,
|
|
1425
|
+
write: (el, v) => el.value = v == null ? "" : String(v)
|
|
1230
1426
|
};
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1427
|
+
case "checked": return {
|
|
1428
|
+
event: "change",
|
|
1429
|
+
read: (el) => el.checked,
|
|
1430
|
+
write: (el, v) => el.checked = Boolean(v)
|
|
1431
|
+
};
|
|
1432
|
+
case "files": return {
|
|
1433
|
+
event: "change",
|
|
1434
|
+
read: (el) => el.files,
|
|
1435
|
+
write: () => {}
|
|
1436
|
+
};
|
|
1437
|
+
case "open": return {
|
|
1438
|
+
event: "toggle",
|
|
1439
|
+
read: (el) => el.open,
|
|
1440
|
+
write: (el, v) => el.open = Boolean(v)
|
|
1441
|
+
};
|
|
1442
|
+
case "this": return {
|
|
1443
|
+
event: null,
|
|
1444
|
+
read: () => void 0,
|
|
1445
|
+
write: () => {}
|
|
1446
|
+
};
|
|
1447
|
+
case "group": {
|
|
1448
|
+
const isCheckbox = input.type === "checkbox";
|
|
1449
|
+
return {
|
|
1450
|
+
event: "change",
|
|
1451
|
+
read: (el) => {
|
|
1452
|
+
const i = el;
|
|
1453
|
+
if (isCheckbox) return {
|
|
1454
|
+
__ilhaGroup: true,
|
|
1455
|
+
value: i.value,
|
|
1456
|
+
checked: i.checked
|
|
1253
1457
|
};
|
|
1254
|
-
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
initialEnvelope = {
|
|
1263
|
-
loading: false,
|
|
1264
|
-
value: void 0,
|
|
1265
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1266
|
-
};
|
|
1267
|
-
} finally {
|
|
1268
|
-
stack.pop();
|
|
1269
|
-
ac.abort();
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
const env = signal(initialEnvelope);
|
|
1273
|
-
envelopes.set(entry.key, env);
|
|
1274
|
-
}
|
|
1275
|
-
const accessors = /* @__PURE__ */ new Map();
|
|
1276
|
-
for (const entry of entries) {
|
|
1277
|
-
const env = envelopes.get(entry.key);
|
|
1278
|
-
accessors.set(entry.key, createDerivedAccessor(() => env(), (value) => {
|
|
1279
|
-
const prevSub = setActiveSub(void 0);
|
|
1280
|
-
env({
|
|
1281
|
-
loading: false,
|
|
1282
|
-
value,
|
|
1283
|
-
error: void 0
|
|
1284
|
-
});
|
|
1285
|
-
setActiveSub(prevSub);
|
|
1286
|
-
}));
|
|
1287
|
-
}
|
|
1288
|
-
const proxy = new Proxy({}, { get(_, key) {
|
|
1289
|
-
if (typeof key !== "string") return void 0;
|
|
1290
|
-
return accessors.get(key) ?? defaultDerivedAccessor();
|
|
1291
|
-
} });
|
|
1292
|
-
const setup = () => {
|
|
1293
|
-
const stops = [];
|
|
1294
|
-
for (const entry of entries) {
|
|
1295
|
-
const env = envelopes.get(entry.key);
|
|
1296
|
-
let ac = new AbortController();
|
|
1297
|
-
let skipFirst = derivedSnapshot != null && entry.key in derivedSnapshot;
|
|
1298
|
-
const stopEffect = effect(() => {
|
|
1299
|
-
ac.abort();
|
|
1300
|
-
ac = new AbortController();
|
|
1301
|
-
const currentAc = ac;
|
|
1302
|
-
let result;
|
|
1303
|
-
const captureSlot = {};
|
|
1304
|
-
const stack = queryCaptureStack();
|
|
1305
|
-
globalThis[ILHA_QUERY_PENDING] = void 0;
|
|
1306
|
-
stack.push(captureSlot);
|
|
1307
|
-
try {
|
|
1308
|
-
result = entry.fn({
|
|
1309
|
-
state,
|
|
1310
|
-
input,
|
|
1311
|
-
signal: currentAc.signal
|
|
1312
|
-
});
|
|
1313
|
-
} catch (err) {
|
|
1314
|
-
globalThis[ILHA_QUERY_PENDING] = void 0;
|
|
1315
|
-
if (skipFirst) {
|
|
1316
|
-
skipFirst = false;
|
|
1317
|
-
return;
|
|
1318
|
-
}
|
|
1319
|
-
const prevSub = setActiveSub(void 0);
|
|
1320
|
-
env({
|
|
1321
|
-
loading: false,
|
|
1322
|
-
value: void 0,
|
|
1323
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1324
|
-
});
|
|
1325
|
-
setActiveSub(prevSub);
|
|
1326
|
-
return;
|
|
1327
|
-
} finally {
|
|
1328
|
-
stack.pop();
|
|
1329
|
-
}
|
|
1330
|
-
const queryCall = resolveQueryCall(result, captureSlot);
|
|
1331
|
-
if (skipFirst) {
|
|
1332
|
-
skipFirst = false;
|
|
1333
|
-
if (result instanceof Promise) result.catch(() => {});
|
|
1334
|
-
return;
|
|
1335
|
-
}
|
|
1336
|
-
if (queryCall) {
|
|
1337
|
-
runBrandedQuery(queryCall, currentAc.signal, env);
|
|
1338
|
-
return;
|
|
1339
|
-
}
|
|
1340
|
-
if (!(result instanceof Promise)) {
|
|
1341
|
-
const prevSub = setActiveSub(void 0);
|
|
1342
|
-
env({
|
|
1343
|
-
loading: false,
|
|
1344
|
-
value: result,
|
|
1345
|
-
error: void 0
|
|
1346
|
-
});
|
|
1347
|
-
setActiveSub(prevSub);
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
const prevSub = setActiveSub(void 0);
|
|
1351
|
-
const prevVal = env();
|
|
1352
|
-
env({
|
|
1353
|
-
loading: true,
|
|
1354
|
-
value: prevVal.value,
|
|
1355
|
-
error: void 0
|
|
1356
|
-
});
|
|
1357
|
-
setActiveSub(prevSub);
|
|
1358
|
-
result.then((value) => {
|
|
1359
|
-
if (currentAc.signal.aborted) return;
|
|
1360
|
-
if (isQueryCall(value)) {
|
|
1361
|
-
runBrandedQuery(value, currentAc.signal, env);
|
|
1362
|
-
return;
|
|
1363
|
-
}
|
|
1364
|
-
const latePending = globalThis[ILHA_QUERY_PENDING];
|
|
1365
|
-
globalThis[ILHA_QUERY_PENDING] = void 0;
|
|
1366
|
-
if (latePending) {
|
|
1367
|
-
runBrandedQuery(latePending, currentAc.signal, env);
|
|
1368
|
-
return;
|
|
1369
|
-
}
|
|
1370
|
-
env({
|
|
1371
|
-
loading: false,
|
|
1372
|
-
value,
|
|
1373
|
-
error: void 0
|
|
1374
|
-
});
|
|
1375
|
-
}).catch((err) => {
|
|
1376
|
-
if (currentAc.signal.aborted) return;
|
|
1377
|
-
env({
|
|
1378
|
-
loading: false,
|
|
1379
|
-
value: void 0,
|
|
1380
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1381
|
-
});
|
|
1382
|
-
});
|
|
1383
|
-
});
|
|
1384
|
-
stops.push(() => {
|
|
1385
|
-
stopEffect();
|
|
1386
|
-
ac.abort();
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
return () => stops.forEach((s) => s());
|
|
1390
|
-
};
|
|
1391
|
-
return {
|
|
1392
|
-
proxy,
|
|
1393
|
-
setup
|
|
1394
|
-
};
|
|
1395
|
-
}
|
|
1396
|
-
const BIND_SENTINEL_ATTR = "data-ilha-bind";
|
|
1397
|
-
const EVENT_SENTINEL_ATTR = "data-ilha-on";
|
|
1398
|
-
function parseBindSentinel(value) {
|
|
1399
|
-
const out = [];
|
|
1400
|
-
for (const part of value.split(",")) {
|
|
1401
|
-
const [kind, idx] = part.split(":");
|
|
1402
|
-
if (!kind || !idx) continue;
|
|
1403
|
-
const i = Number(idx);
|
|
1404
|
-
if (!Number.isInteger(i) || i < 0) continue;
|
|
1405
|
-
out.push({
|
|
1406
|
-
kind,
|
|
1407
|
-
index: i
|
|
1408
|
-
});
|
|
1409
|
-
}
|
|
1410
|
-
return out;
|
|
1411
|
-
}
|
|
1412
|
-
function resolveBindOps(el, kind) {
|
|
1413
|
-
const input = el;
|
|
1414
|
-
switch (kind) {
|
|
1415
|
-
case "value": return {
|
|
1416
|
-
event: el.tagName === "SELECT" ? "change" : "input",
|
|
1417
|
-
read: (el) => el.value,
|
|
1418
|
-
write: (el, v) => el.value = v == null ? "" : String(v)
|
|
1419
|
-
};
|
|
1420
|
-
case "valueAsNumber": return {
|
|
1421
|
-
event: "input",
|
|
1422
|
-
read: (el) => {
|
|
1423
|
-
const n = el.valueAsNumber;
|
|
1424
|
-
return Number.isNaN(n) ? null : n;
|
|
1425
|
-
},
|
|
1426
|
-
write: (el, v) => el.value = v == null || Number.isNaN(v) ? "" : String(v)
|
|
1427
|
-
};
|
|
1428
|
-
case "valueAsDate": return {
|
|
1429
|
-
event: "input",
|
|
1430
|
-
read: (el) => el.valueAsDate,
|
|
1431
|
-
write: (el, v) => {
|
|
1432
|
-
el.value = formatDateForInput(v);
|
|
1433
|
-
}
|
|
1434
|
-
};
|
|
1435
|
-
case "checked": return {
|
|
1436
|
-
event: "change",
|
|
1437
|
-
read: (el) => el.checked,
|
|
1438
|
-
write: (el, v) => el.checked = Boolean(v)
|
|
1439
|
-
};
|
|
1440
|
-
case "files": return {
|
|
1441
|
-
event: "change",
|
|
1442
|
-
read: (el) => el.files,
|
|
1443
|
-
write: () => {}
|
|
1444
|
-
};
|
|
1445
|
-
case "open": return {
|
|
1446
|
-
event: "toggle",
|
|
1447
|
-
read: (el) => el.open,
|
|
1448
|
-
write: (el, v) => el.open = Boolean(v)
|
|
1449
|
-
};
|
|
1450
|
-
case "this": return {
|
|
1451
|
-
event: null,
|
|
1452
|
-
read: () => void 0,
|
|
1453
|
-
write: () => {}
|
|
1454
|
-
};
|
|
1455
|
-
case "group": {
|
|
1456
|
-
const isCheckbox = input.type === "checkbox";
|
|
1457
|
-
return {
|
|
1458
|
-
event: "change",
|
|
1459
|
-
read: (el) => {
|
|
1460
|
-
const i = el;
|
|
1461
|
-
if (isCheckbox) return {
|
|
1462
|
-
__ilhaGroup: true,
|
|
1463
|
-
value: i.value,
|
|
1464
|
-
checked: i.checked
|
|
1465
|
-
};
|
|
1466
|
-
return i.checked ? i.value : void 0;
|
|
1467
|
-
},
|
|
1468
|
-
write: (el, v) => {
|
|
1469
|
-
const i = el;
|
|
1470
|
-
if (isCheckbox) i.checked = (Array.isArray(v) ? v.map(String) : []).includes(i.value);
|
|
1471
|
-
else i.checked = v != null && String(v) === i.value;
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1458
|
+
return i.checked ? i.value : void 0;
|
|
1459
|
+
},
|
|
1460
|
+
write: (el, v) => {
|
|
1461
|
+
const i = el;
|
|
1462
|
+
if (isCheckbox) i.checked = (Array.isArray(v) ? v.map(String) : []).includes(i.value);
|
|
1463
|
+
else i.checked = v != null && String(v) === i.value;
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1474
1466
|
}
|
|
1475
1467
|
}
|
|
1476
1468
|
}
|
|
@@ -1520,7 +1512,7 @@ function applyJsxEvents({ host, records, reportError, unmountSignal, onceFired }
|
|
|
1520
1512
|
invocationController?.abort();
|
|
1521
1513
|
invocationController = new AbortController();
|
|
1522
1514
|
}
|
|
1523
|
-
const signal = invocationController ?
|
|
1515
|
+
const signal = invocationController ? AbortSignal.any([listenerController.signal, invocationController.signal]) : listenerController.signal;
|
|
1524
1516
|
let result;
|
|
1525
1517
|
startBatch();
|
|
1526
1518
|
try {
|
|
@@ -1619,47 +1611,6 @@ function applyTemplateBindings(host, binds, phase = "all") {
|
|
|
1619
1611
|
}
|
|
1620
1612
|
return () => cleanups.forEach((c) => c());
|
|
1621
1613
|
}
|
|
1622
|
-
function parseOnArgs(selectorOrCombined) {
|
|
1623
|
-
const atIdx = selectorOrCombined.lastIndexOf("@");
|
|
1624
|
-
const selector = atIdx === -1 ? "" : selectorOrCombined.slice(0, atIdx);
|
|
1625
|
-
const parts = (atIdx === -1 ? selectorOrCombined : selectorOrCombined.slice(atIdx + 1)).split(":");
|
|
1626
|
-
const eventType = parts[0];
|
|
1627
|
-
const modifiers = new Set(parts.slice(1));
|
|
1628
|
-
return {
|
|
1629
|
-
selector,
|
|
1630
|
-
eventType,
|
|
1631
|
-
options: {
|
|
1632
|
-
once: modifiers.has("once"),
|
|
1633
|
-
capture: modifiers.has("capture"),
|
|
1634
|
-
passive: modifiers.has("passive")
|
|
1635
|
-
},
|
|
1636
|
-
abortable: modifiers.has("abortable")
|
|
1637
|
-
};
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Combine multiple AbortSignals into one that aborts when any input aborts.
|
|
1641
|
-
* Uses native AbortSignal.any when available, falls back to a manual chain
|
|
1642
|
-
* otherwise (for older runtimes).
|
|
1643
|
-
*/
|
|
1644
|
-
function anySignal(signals) {
|
|
1645
|
-
if (typeof AbortSignal.any === "function") return AbortSignal.any(signals);
|
|
1646
|
-
const controller = new AbortController();
|
|
1647
|
-
const cleanups = [];
|
|
1648
|
-
for (const s of signals) {
|
|
1649
|
-
if (s.aborted) {
|
|
1650
|
-
controller.abort(s.reason);
|
|
1651
|
-
cleanups.forEach((c) => c());
|
|
1652
|
-
return controller.signal;
|
|
1653
|
-
}
|
|
1654
|
-
const handler = () => controller.abort(s.reason);
|
|
1655
|
-
s.addEventListener("abort", handler, { once: true });
|
|
1656
|
-
cleanups.push(() => s.removeEventListener("abort", handler));
|
|
1657
|
-
}
|
|
1658
|
-
if (!controller.signal.aborted) controller.signal.addEventListener("abort", () => {
|
|
1659
|
-
cleanups.forEach((c) => c());
|
|
1660
|
-
});
|
|
1661
|
-
return controller.signal;
|
|
1662
|
-
}
|
|
1663
1614
|
const globalErrorHandlers = /* @__PURE__ */ new Set();
|
|
1664
1615
|
function reportToGlobal(error, source) {
|
|
1665
1616
|
if (globalErrorHandlers.size === 0) return false;
|
|
@@ -1672,9 +1623,7 @@ function reportToGlobal(error, source) {
|
|
|
1672
1623
|
}
|
|
1673
1624
|
/**
|
|
1674
1625
|
* Register a global error handler invoked when any island reports an error
|
|
1675
|
-
*
|
|
1676
|
-
* handler. Returns an unsubscribe function. Islands with their own .onError()
|
|
1677
|
-
* are handled locally and do not reach the global sink.
|
|
1626
|
+
* and has no local onError() handler. Returns an unsubscribe function.
|
|
1678
1627
|
*/
|
|
1679
1628
|
function onUncaughtError(fn) {
|
|
1680
1629
|
globalErrorHandlers.add(fn);
|
|
@@ -1682,1313 +1631,1256 @@ function onUncaughtError(fn) {
|
|
|
1682
1631
|
globalErrorHandlers.delete(fn);
|
|
1683
1632
|
};
|
|
1684
1633
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
deriveds: [],
|
|
1701
|
-
actions: [],
|
|
1702
|
-
ons: [],
|
|
1703
|
-
effects: [],
|
|
1704
|
-
onMounts: [],
|
|
1705
|
-
onErrors: [],
|
|
1706
|
-
transition: null,
|
|
1707
|
-
css: null,
|
|
1708
|
-
as: null
|
|
1709
|
-
});
|
|
1710
|
-
}
|
|
1711
|
-
as(tag) {
|
|
1712
|
-
return new IlhaBuilder({
|
|
1713
|
-
...this._cfg,
|
|
1714
|
-
as: assertValidSlotTagName(tag)
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
state(key, init) {
|
|
1718
|
-
const cfg = this._cfg;
|
|
1719
|
-
return new IlhaBuilder({
|
|
1720
|
-
...cfg,
|
|
1721
|
-
states: [...cfg.states, {
|
|
1722
|
-
key,
|
|
1723
|
-
init
|
|
1724
|
-
}]
|
|
1725
|
-
});
|
|
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
|
-
}
|
|
1742
|
-
derived(key, fn) {
|
|
1743
|
-
const cfg = this._cfg;
|
|
1744
|
-
return new IlhaBuilder({
|
|
1745
|
-
...cfg,
|
|
1746
|
-
deriveds: [...cfg.deriveds, {
|
|
1747
|
-
key,
|
|
1748
|
-
fn
|
|
1749
|
-
}]
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
action(key, fn) {
|
|
1753
|
-
return new IlhaBuilder({
|
|
1754
|
-
...this._cfg,
|
|
1755
|
-
actions: [...this._cfg.actions, {
|
|
1756
|
-
key,
|
|
1757
|
-
fn
|
|
1758
|
-
}]
|
|
1759
|
-
});
|
|
1760
|
-
}
|
|
1761
|
-
on(selectorOrCombined, handler) {
|
|
1762
|
-
const parsed = parseOnArgs(selectorOrCombined);
|
|
1763
|
-
return new IlhaBuilder({
|
|
1764
|
-
...this._cfg,
|
|
1765
|
-
ons: [...this._cfg.ons, {
|
|
1766
|
-
selector: parsed.selector,
|
|
1767
|
-
event: parsed.eventType,
|
|
1768
|
-
options: parsed.options,
|
|
1769
|
-
abortable: parsed.abortable,
|
|
1770
|
-
handler
|
|
1771
|
-
}]
|
|
1772
|
-
});
|
|
1773
|
-
}
|
|
1774
|
-
effect(fn) {
|
|
1775
|
-
return new IlhaBuilder({
|
|
1776
|
-
...this._cfg,
|
|
1777
|
-
effects: [...this._cfg.effects, { fn }]
|
|
1778
|
-
});
|
|
1779
|
-
}
|
|
1780
|
-
onMount(fn) {
|
|
1781
|
-
return new IlhaBuilder({
|
|
1782
|
-
...this._cfg,
|
|
1783
|
-
onMounts: [...this._cfg.onMounts, { fn }]
|
|
1784
|
-
});
|
|
1785
|
-
}
|
|
1786
|
-
onError(fn) {
|
|
1787
|
-
return new IlhaBuilder({
|
|
1788
|
-
...this._cfg,
|
|
1789
|
-
onErrors: [...this._cfg.onErrors, { fn }]
|
|
1790
|
-
});
|
|
1791
|
-
}
|
|
1792
|
-
transition(opts) {
|
|
1793
|
-
return new IlhaBuilder({
|
|
1794
|
-
...this._cfg,
|
|
1795
|
-
transition: opts
|
|
1796
|
-
});
|
|
1797
|
-
}
|
|
1798
|
-
css(strings, ...values) {
|
|
1799
|
-
let source;
|
|
1800
|
-
if (typeof strings === "string") source = strings;
|
|
1801
|
-
else {
|
|
1802
|
-
let result = "";
|
|
1803
|
-
for (let i = 0; i < strings.length; i++) {
|
|
1804
|
-
result += strings[i];
|
|
1805
|
-
if (i < values.length) result += String(values[i]);
|
|
1806
|
-
}
|
|
1807
|
-
source = result;
|
|
1808
|
-
}
|
|
1809
|
-
return new IlhaBuilder({
|
|
1810
|
-
...this._cfg,
|
|
1811
|
-
css: source
|
|
1812
|
-
});
|
|
1634
|
+
const FRAME_STACK = Symbol.for("ilha.primitiveFrameStack");
|
|
1635
|
+
function frameStack() {
|
|
1636
|
+
const g = globalThis;
|
|
1637
|
+
return g[FRAME_STACK] ??= [];
|
|
1638
|
+
}
|
|
1639
|
+
function currentFrame() {
|
|
1640
|
+
const stack = frameStack();
|
|
1641
|
+
return stack[stack.length - 1];
|
|
1642
|
+
}
|
|
1643
|
+
function withFrame(frame, fn) {
|
|
1644
|
+
frameStack().push(frame);
|
|
1645
|
+
try {
|
|
1646
|
+
return fn();
|
|
1647
|
+
} finally {
|
|
1648
|
+
frameStack().pop();
|
|
1813
1649
|
}
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1650
|
+
}
|
|
1651
|
+
function freshFrame(options) {
|
|
1652
|
+
return {
|
|
1653
|
+
slots: [],
|
|
1654
|
+
cursor: 0,
|
|
1655
|
+
creating: true,
|
|
1656
|
+
ssr: options?.ssr ?? false,
|
|
1657
|
+
instance: options?.instance ?? null,
|
|
1658
|
+
stateIndex: 0,
|
|
1659
|
+
derivedIndex: 0,
|
|
1660
|
+
actionIndex: 0,
|
|
1661
|
+
pendingDerived: options?.ssr ? [] : null,
|
|
1662
|
+
actionManifest: options?.ssr ? /* @__PURE__ */ new Map() : null
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
const PRIMITIVE_NAMES = {
|
|
1666
|
+
state: "state",
|
|
1667
|
+
derived: "derived",
|
|
1668
|
+
action: "action",
|
|
1669
|
+
effect: "effect",
|
|
1670
|
+
once: "effect.once",
|
|
1671
|
+
error: "onError"
|
|
1672
|
+
};
|
|
1673
|
+
/** Reserve the next slot for a primitive call. Throws outside an island
|
|
1674
|
+
* render; warns in dev on hook count/kind drift during re-renders. */
|
|
1675
|
+
function acquireSlot(kind) {
|
|
1676
|
+
const frame = currentFrame();
|
|
1677
|
+
if (!frame) throw new Error(`[ilha] ${PRIMITIVE_NAMES[kind]}() called outside an island render. Primitives are only valid while an island component (or a plain component owned by one) is rendering.`);
|
|
1678
|
+
const index = frame.cursor++;
|
|
1679
|
+
if (frame.creating) return void 0;
|
|
1680
|
+
const existing = frame.slots[index];
|
|
1681
|
+
return existing && existing.kind === kind ? existing : void 0;
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Declare island-local reactive state at this call position. The initializer
|
|
1685
|
+
* applies only when the instance is created — later renders reuse the same
|
|
1686
|
+
* underlying signal, so prop-driven initializers never reset user state.
|
|
1687
|
+
*
|
|
1688
|
+
* A function argument is treated as a lazy initializer:
|
|
1689
|
+
* const count = state(() => expensiveInitialValue());
|
|
1690
|
+
* To store a function VALUE, return it from the updater wrapper on write:
|
|
1691
|
+
* setCallback(() => nextCallback);
|
|
1692
|
+
*/
|
|
1693
|
+
function state(init) {
|
|
1694
|
+
const frame = currentFrame();
|
|
1695
|
+
const existing = acquireSlot("state");
|
|
1696
|
+
if (existing) return existing.acc;
|
|
1697
|
+
const f = frame;
|
|
1698
|
+
if (!f.creating) return ilhaSignal(void 0);
|
|
1699
|
+
const index = f.stateIndex++;
|
|
1700
|
+
const snap = f.ssr ? void 0 : f.instance?.stateSnapshot?.s?.[index];
|
|
1701
|
+
const initial = snap === void 0 ? typeof init === "function" ? init() : init : snap;
|
|
1702
|
+
const s = signal(initial);
|
|
1703
|
+
const acc = markSignalAccessor((...args) => {
|
|
1704
|
+
if (args.length === 0) return s();
|
|
1705
|
+
const next = resolveSignalSetter(() => s(), args[0]);
|
|
1706
|
+
s(next);
|
|
1707
|
+
});
|
|
1708
|
+
f.slots[f.cursor - 1] = {
|
|
1709
|
+
kind: "state",
|
|
1710
|
+
acc
|
|
1711
|
+
};
|
|
1712
|
+
return acc;
|
|
1713
|
+
}
|
|
1714
|
+
/** True for async iterables (incl. async generators). */
|
|
1715
|
+
function isAsyncIterable(value) {
|
|
1716
|
+
return value !== null && typeof value[Symbol.asyncIterator] === "function";
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Declare derived state at this call position: synchronous computations,
|
|
1720
|
+
* Promises, or async generators. Reads track their dependencies; async work
|
|
1721
|
+
* races latest-run-wins and aborts stale runs through `ctx.signal`.
|
|
1722
|
+
*/
|
|
1723
|
+
function derived(fn) {
|
|
1724
|
+
const frame = currentFrame();
|
|
1725
|
+
const existing = acquireSlot("derived");
|
|
1726
|
+
const f = frame;
|
|
1727
|
+
if (existing) {
|
|
1728
|
+
const slot = existing;
|
|
1729
|
+
slot.fn = fn;
|
|
1730
|
+
if (f.ssr && !f.creating && !slot.ssrAsync) {
|
|
1731
|
+
const ac = new AbortController();
|
|
1732
|
+
const prevSub = setActiveSub(void 0);
|
|
1828
1733
|
try {
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
const pending = ctx.pending;
|
|
1835
|
-
return (async () => {
|
|
1836
|
-
return {
|
|
1837
|
-
html: await resolveAsyncChildren(html, pending),
|
|
1838
|
-
slots,
|
|
1839
|
-
binds,
|
|
1840
|
-
events
|
|
1841
|
-
};
|
|
1842
|
-
})();
|
|
1843
|
-
}
|
|
1844
|
-
return {
|
|
1845
|
-
html,
|
|
1846
|
-
slots,
|
|
1847
|
-
binds,
|
|
1848
|
-
events
|
|
1849
|
-
};
|
|
1850
|
-
} finally {
|
|
1851
|
-
popRenderCtx();
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
function buildPlainState(input) {
|
|
1855
|
-
const state = {};
|
|
1856
|
-
for (const entry of states) {
|
|
1857
|
-
const value = typeof entry.init === "function" ? entry.init(input) : entry.init;
|
|
1858
|
-
const accessor = markSignalAccessor((...args) => {
|
|
1859
|
-
if (args.length === 0) return value;
|
|
1734
|
+
const result = slot.fn({ signal: ac.signal });
|
|
1735
|
+
if (!(result instanceof Promise) && !isAsyncIterable(result)) slot.env({
|
|
1736
|
+
loading: false,
|
|
1737
|
+
value: result,
|
|
1738
|
+
error: void 0
|
|
1860
1739
|
});
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
const state = {};
|
|
1867
|
-
for (const entry of states) {
|
|
1868
|
-
const initial = snapshot && entry.key in snapshot ? snapshot[entry.key] : typeof entry.init === "function" ? entry.init(input) : entry.init;
|
|
1869
|
-
const s = signal(initial);
|
|
1870
|
-
const accessor = markSignalAccessor((...args) => {
|
|
1871
|
-
if (args.length === 0) return s();
|
|
1872
|
-
s(resolveSignalSetter(() => s(), args[0]));
|
|
1740
|
+
} catch (err) {
|
|
1741
|
+
slot.env({
|
|
1742
|
+
loading: false,
|
|
1743
|
+
value: void 0,
|
|
1744
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1873
1745
|
});
|
|
1874
|
-
|
|
1746
|
+
} finally {
|
|
1747
|
+
setActiveSub(prevSub);
|
|
1875
1748
|
}
|
|
1876
|
-
return state;
|
|
1877
1749
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
Object.defineProperties(invoke, {
|
|
1894
|
-
pending: {
|
|
1895
|
-
get: () => false,
|
|
1896
|
-
enumerable: true
|
|
1897
|
-
},
|
|
1898
|
-
data: {
|
|
1899
|
-
get: () => void 0,
|
|
1900
|
-
enumerable: true
|
|
1901
|
-
},
|
|
1902
|
-
error: {
|
|
1903
|
-
get: () => void 0,
|
|
1904
|
-
enumerable: true
|
|
1905
|
-
}
|
|
1906
|
-
});
|
|
1907
|
-
out[entry.key] = invoke;
|
|
1908
|
-
lookup.set(invoke, entry.key);
|
|
1909
|
-
}
|
|
1910
|
-
return {
|
|
1911
|
-
action: out,
|
|
1912
|
-
lookup
|
|
1913
|
-
};
|
|
1750
|
+
return slot.acc;
|
|
1751
|
+
}
|
|
1752
|
+
if (!f.creating) return defaultDerivedAccessor();
|
|
1753
|
+
const index = f.derivedIndex++;
|
|
1754
|
+
const env = signal({
|
|
1755
|
+
loading: false,
|
|
1756
|
+
value: void 0,
|
|
1757
|
+
error: void 0
|
|
1758
|
+
});
|
|
1759
|
+
let fromSnapshot = false;
|
|
1760
|
+
if (!f.ssr) {
|
|
1761
|
+
const snap = f.instance?.stateSnapshot?.d?.[index];
|
|
1762
|
+
if (snap) {
|
|
1763
|
+
env({ ...snap });
|
|
1764
|
+
fromSnapshot = true;
|
|
1914
1765
|
}
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
};
|
|
1927
|
-
return stub;
|
|
1766
|
+
}
|
|
1767
|
+
const accessor = createDerivedAccessor(() => env(), (value) => {
|
|
1768
|
+
const prevSub = setActiveSub(void 0);
|
|
1769
|
+
try {
|
|
1770
|
+
env({
|
|
1771
|
+
loading: false,
|
|
1772
|
+
value,
|
|
1773
|
+
error: void 0
|
|
1774
|
+
});
|
|
1775
|
+
} finally {
|
|
1776
|
+
setActiveSub(prevSub);
|
|
1928
1777
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1778
|
+
});
|
|
1779
|
+
const slot = {
|
|
1780
|
+
kind: "derived",
|
|
1781
|
+
env,
|
|
1782
|
+
acc: accessor,
|
|
1783
|
+
fn,
|
|
1784
|
+
ssrAsync: false,
|
|
1785
|
+
fromSnapshot
|
|
1786
|
+
};
|
|
1787
|
+
if (f.ssr) {
|
|
1788
|
+
const ac = new AbortController();
|
|
1789
|
+
const prevSub = setActiveSub(void 0);
|
|
1790
|
+
let result;
|
|
1791
|
+
let threw = false;
|
|
1792
|
+
let syncError;
|
|
1793
|
+
try {
|
|
1794
|
+
result = slot.fn({ signal: ac.signal });
|
|
1795
|
+
} catch (err) {
|
|
1796
|
+
threw = true;
|
|
1797
|
+
syncError = err;
|
|
1798
|
+
} finally {
|
|
1799
|
+
setActiveSub(prevSub);
|
|
1800
|
+
}
|
|
1801
|
+
let pull = null;
|
|
1802
|
+
if (threw) env({
|
|
1803
|
+
loading: false,
|
|
1804
|
+
value: void 0,
|
|
1805
|
+
error: syncError instanceof Error ? syncError : new Error(String(syncError))
|
|
1806
|
+
});
|
|
1807
|
+
else if (result instanceof Promise) {
|
|
1808
|
+
env({
|
|
1809
|
+
loading: true,
|
|
1810
|
+
value: void 0,
|
|
1811
|
+
error: void 0
|
|
1812
|
+
});
|
|
1813
|
+
pull = result.then((value) => env({
|
|
1814
|
+
loading: false,
|
|
1815
|
+
value,
|
|
1816
|
+
error: void 0
|
|
1817
|
+
}), (err) => env({
|
|
1818
|
+
loading: false,
|
|
1819
|
+
value: void 0,
|
|
1820
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1821
|
+
}));
|
|
1822
|
+
result.catch(() => {});
|
|
1823
|
+
} else if (isAsyncIterable(result)) {
|
|
1824
|
+
env({
|
|
1825
|
+
loading: true,
|
|
1826
|
+
value: void 0,
|
|
1827
|
+
error: void 0
|
|
1828
|
+
});
|
|
1829
|
+
const iterable = result;
|
|
1830
|
+
pull = (async () => {
|
|
1831
|
+
const it = iterable[Symbol.asyncIterator]();
|
|
1832
|
+
try {
|
|
1833
|
+
const first = await it.next();
|
|
1834
|
+
env({
|
|
1835
|
+
loading: false,
|
|
1836
|
+
value: first.done ? void 0 : first.value,
|
|
1837
|
+
error: void 0
|
|
1948
1838
|
});
|
|
1949
|
-
} catch (err) {
|
|
1950
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1951
|
-
if (onErrors.length === 0) {
|
|
1952
|
-
if (!reportToGlobal(error, "mount")) console.error(error);
|
|
1953
|
-
} else for (const oe of onErrors) try {
|
|
1954
|
-
oe.fn({
|
|
1955
|
-
error,
|
|
1956
|
-
source: "mount",
|
|
1957
|
-
state,
|
|
1958
|
-
derived,
|
|
1959
|
-
action,
|
|
1960
|
-
input,
|
|
1961
|
-
host
|
|
1962
|
-
});
|
|
1963
|
-
} catch (handlerErr) {
|
|
1964
|
-
console.error(handlerErr);
|
|
1965
|
-
}
|
|
1966
1839
|
} finally {
|
|
1967
|
-
|
|
1968
|
-
}
|
|
1969
|
-
if (typeof userCleanup === "function") ssrCleanups.push(userCleanup);
|
|
1970
|
-
}
|
|
1971
|
-
controller.abort();
|
|
1972
|
-
for (const teardown of ssrCleanups.reverse()) try {
|
|
1973
|
-
teardown();
|
|
1974
|
-
} catch (err) {
|
|
1975
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1976
|
-
if (onErrors.length === 0) {
|
|
1977
|
-
if (!reportToGlobal(error, "mount")) console.error(error);
|
|
1840
|
+
await it.return?.(void 0);
|
|
1978
1841
|
}
|
|
1842
|
+
})();
|
|
1843
|
+
} else env({
|
|
1844
|
+
loading: false,
|
|
1845
|
+
value: result,
|
|
1846
|
+
error: void 0
|
|
1847
|
+
});
|
|
1848
|
+
slot.ssrAsync = pull !== null;
|
|
1849
|
+
if (pull) f.pendingDerived.push({
|
|
1850
|
+
index,
|
|
1851
|
+
promise: pull.catch(() => {})
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
f.slots[f.cursor - 1] = slot;
|
|
1855
|
+
return accessor;
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Declare a reactive operation at this call position. Use plain functions
|
|
1859
|
+
* for ordinary operations; action() adds pending/data/error tracking,
|
|
1860
|
+
* concurrent-invocation bookkeeping, and lifecycle cancellation.
|
|
1861
|
+
*/
|
|
1862
|
+
function action(fn) {
|
|
1863
|
+
const frame = currentFrame();
|
|
1864
|
+
const existing = acquireSlot("action");
|
|
1865
|
+
const f = frame;
|
|
1866
|
+
if (existing) return existing.acc;
|
|
1867
|
+
if (!f.creating) {
|
|
1868
|
+
const noop = (() => {});
|
|
1869
|
+
Object.defineProperties(noop, {
|
|
1870
|
+
pending: { get: () => false },
|
|
1871
|
+
data: { get: () => void 0 },
|
|
1872
|
+
error: { get: () => void 0 }
|
|
1873
|
+
});
|
|
1874
|
+
attachWithArgs(noop);
|
|
1875
|
+
return noop;
|
|
1876
|
+
}
|
|
1877
|
+
const manifestId = `a${f.actionIndex++}`;
|
|
1878
|
+
if (f.ssr) {
|
|
1879
|
+
const invoke = (..._callArgs) => {};
|
|
1880
|
+
invoke[SSR_ACTION_STUB] = true;
|
|
1881
|
+
invoke[ACTION_MANIFEST_ID] = manifestId;
|
|
1882
|
+
Object.defineProperties(invoke, {
|
|
1883
|
+
pending: {
|
|
1884
|
+
get: () => false,
|
|
1885
|
+
enumerable: true
|
|
1886
|
+
},
|
|
1887
|
+
data: {
|
|
1888
|
+
get: () => void 0,
|
|
1889
|
+
enumerable: true
|
|
1890
|
+
},
|
|
1891
|
+
error: {
|
|
1892
|
+
get: () => void 0,
|
|
1893
|
+
enumerable: true
|
|
1979
1894
|
}
|
|
1895
|
+
});
|
|
1896
|
+
f.actionManifest.set(invoke, manifestId);
|
|
1897
|
+
f.slots[f.cursor - 1] = {
|
|
1898
|
+
kind: "action",
|
|
1899
|
+
acc: invoke,
|
|
1900
|
+
fn
|
|
1901
|
+
};
|
|
1902
|
+
const ssrAccessor = invoke;
|
|
1903
|
+
attachWithArgs(ssrAccessor);
|
|
1904
|
+
return ssrAccessor;
|
|
1905
|
+
}
|
|
1906
|
+
const instance = f.instance;
|
|
1907
|
+
const envelope = signal({
|
|
1908
|
+
pending: 0,
|
|
1909
|
+
data: void 0,
|
|
1910
|
+
error: void 0
|
|
1911
|
+
});
|
|
1912
|
+
const readEnvelope = () => untrack(() => envelope());
|
|
1913
|
+
let latestRun = 0;
|
|
1914
|
+
const invoke = (payload) => {
|
|
1915
|
+
if (currentRenderCtx()) return;
|
|
1916
|
+
if (instance.disposed) return;
|
|
1917
|
+
const run = ++latestRun;
|
|
1918
|
+
const runSignal = instance.unmountController.signal;
|
|
1919
|
+
let result;
|
|
1920
|
+
let isAsync = false;
|
|
1921
|
+
startBatch();
|
|
1922
|
+
try {
|
|
1923
|
+
result = slot.fn(payload, { signal: runSignal });
|
|
1924
|
+
isAsync = result != null && typeof result.then === "function";
|
|
1925
|
+
const current = readEnvelope();
|
|
1926
|
+
envelope(isAsync ? {
|
|
1927
|
+
pending: current.pending + 1,
|
|
1928
|
+
data: current.data,
|
|
1929
|
+
error: void 0
|
|
1930
|
+
} : {
|
|
1931
|
+
pending: current.pending,
|
|
1932
|
+
data: result,
|
|
1933
|
+
error: void 0
|
|
1934
|
+
});
|
|
1935
|
+
} catch (error) {
|
|
1936
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
1937
|
+
envelope({
|
|
1938
|
+
pending: readEnvelope().pending,
|
|
1939
|
+
data: void 0,
|
|
1940
|
+
error: normalized
|
|
1941
|
+
});
|
|
1942
|
+
instance.reportError(normalized, "action");
|
|
1943
|
+
return;
|
|
1944
|
+
} finally {
|
|
1945
|
+
endBatch();
|
|
1980
1946
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
const
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1947
|
+
if (!isAsync) return;
|
|
1948
|
+
Promise.resolve(result).then((data) => {
|
|
1949
|
+
if (instance.disposed || runSignal.aborted || run !== latestRun) return;
|
|
1950
|
+
const value = readEnvelope();
|
|
1951
|
+
envelope({
|
|
1952
|
+
pending: value.pending,
|
|
1953
|
+
data,
|
|
1954
|
+
error: void 0
|
|
1955
|
+
});
|
|
1956
|
+
}).catch((error) => {
|
|
1957
|
+
if (instance.disposed || runSignal.aborted || error?.name === "AbortError") return;
|
|
1958
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
1959
|
+
if (run === latestRun) {
|
|
1960
|
+
const value = readEnvelope();
|
|
1961
|
+
envelope({
|
|
1962
|
+
pending: value.pending,
|
|
1963
|
+
data: void 0,
|
|
1964
|
+
error: normalized
|
|
1994
1965
|
});
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
1966
|
+
}
|
|
1967
|
+
instance.reportError(normalized, "action");
|
|
1968
|
+
}).finally(() => {
|
|
1969
|
+
if (instance.disposed) return;
|
|
1970
|
+
const value = readEnvelope();
|
|
1971
|
+
envelope({
|
|
1972
|
+
...value,
|
|
1973
|
+
pending: Math.max(0, value.pending - 1)
|
|
1974
|
+
});
|
|
1975
|
+
});
|
|
1976
|
+
};
|
|
1977
|
+
const slot = {
|
|
1978
|
+
kind: "action",
|
|
1979
|
+
acc: invoke,
|
|
1980
|
+
fn
|
|
1981
|
+
};
|
|
1982
|
+
Object.defineProperties(invoke, {
|
|
1983
|
+
pending: {
|
|
1984
|
+
get: () => envelope().pending > 0,
|
|
1985
|
+
enumerable: true
|
|
1986
|
+
},
|
|
1987
|
+
data: {
|
|
1988
|
+
get: () => envelope().data,
|
|
1989
|
+
enumerable: true
|
|
1990
|
+
},
|
|
1991
|
+
error: {
|
|
1992
|
+
get: () => envelope().error,
|
|
1993
|
+
enumerable: true
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
f.slots[f.cursor - 1] = slot;
|
|
1997
|
+
instance.actionIds.set(slot.acc, manifestId);
|
|
1998
|
+
const clientAccessor = invoke;
|
|
1999
|
+
clientAccessor[ACTION_MANIFEST_ID] = manifestId;
|
|
2000
|
+
attachWithArgs(clientAccessor);
|
|
2001
|
+
return clientAccessor;
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Reactive side effect. Inside an island render, registers an ordered effect
|
|
2005
|
+
* slot that tracks signals read in the body, reruns on change, cleans up
|
|
2006
|
+
* before rerun and on unmount, and is client-only. Outside an island render,
|
|
2007
|
+
* behaves as the standalone reactive effect and returns a stop function.
|
|
2008
|
+
*
|
|
2009
|
+
* @returns Inside an island render, `void` (an effect slot is registered and
|
|
2010
|
+
* cleaned up automatically). At module scope, a stop function that disposes
|
|
2011
|
+
* the effect and runs its final cleanup. The union return type is deliberate:
|
|
2012
|
+
* the same call shape cannot be discriminated at the call site.
|
|
2013
|
+
*/
|
|
2014
|
+
const effect = Object.assign((fn) => {
|
|
2015
|
+
const frame = currentFrame();
|
|
2016
|
+
if (!frame) {
|
|
2017
|
+
let cleanup;
|
|
2018
|
+
let controller = null;
|
|
2019
|
+
const runCleanup = () => {
|
|
2020
|
+
if (typeof cleanup === "function") {
|
|
2021
2021
|
try {
|
|
2022
|
-
|
|
2023
|
-
key: entry.key,
|
|
2024
|
-
result: entry.fn({
|
|
2025
|
-
state,
|
|
2026
|
-
input,
|
|
2027
|
-
signal: new AbortController().signal
|
|
2028
|
-
})
|
|
2029
|
-
};
|
|
2022
|
+
cleanup();
|
|
2030
2023
|
} catch (err) {
|
|
2031
|
-
|
|
2032
|
-
key: entry.key,
|
|
2033
|
-
result: Promise.reject(err)
|
|
2034
|
-
};
|
|
2035
|
-
} finally {
|
|
2036
|
-
setActiveSub(prevSub);
|
|
2024
|
+
console.error(err);
|
|
2037
2025
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2026
|
+
cleanup = void 0;
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
const stop = ilhaEffect(() => {
|
|
2030
|
+
runCleanup();
|
|
2031
|
+
if (controller) controller.abort();
|
|
2032
|
+
controller = new AbortController();
|
|
2033
|
+
startBatch();
|
|
2034
|
+
try {
|
|
2035
|
+
cleanup = fn({ signal: controller.signal });
|
|
2036
|
+
} catch (err) {
|
|
2037
|
+
console.error(err);
|
|
2038
|
+
} finally {
|
|
2039
|
+
endBatch();
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
2042
|
+
return () => {
|
|
2043
|
+
stop();
|
|
2044
|
+
runCleanup();
|
|
2045
|
+
controller?.abort();
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
const existing = acquireSlot("effect");
|
|
2049
|
+
if (existing) {
|
|
2050
|
+
existing.fn = fn;
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
if (!frame.creating) return;
|
|
2054
|
+
frame.slots[frame.cursor - 1] = {
|
|
2055
|
+
kind: "effect",
|
|
2056
|
+
fn
|
|
2057
|
+
};
|
|
2058
|
+
}, { once(fn) {
|
|
2059
|
+
if (acquireSlot("once")) return;
|
|
2060
|
+
const f = currentFrame();
|
|
2061
|
+
if (!f) throw new Error("[ilha] effect.once() called outside an island render. It registers per-instance setup and is only valid while an island component is rendering.");
|
|
2062
|
+
if (!f.creating) return;
|
|
2063
|
+
f.slots[f.cursor - 1] = {
|
|
2064
|
+
kind: "once",
|
|
2065
|
+
fn
|
|
2066
|
+
};
|
|
2067
|
+
} });
|
|
2068
|
+
/**
|
|
2069
|
+
* Register an error handler slot for this island. Handlers run in
|
|
2070
|
+
* declaration order; state, derived values, actions, and props should be
|
|
2071
|
+
* accessed through lexical closure rather than the context object.
|
|
2072
|
+
*/
|
|
2073
|
+
function onError(fn) {
|
|
2074
|
+
if (!currentFrame()) throw new Error("[ilha] onError() called outside an island render. Register it while an island component is rendering.");
|
|
2075
|
+
const existing = acquireSlot("error");
|
|
2076
|
+
if (existing) {
|
|
2077
|
+
existing.fn = fn;
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
const f = currentFrame();
|
|
2081
|
+
if (!f || !f.creating) return;
|
|
2082
|
+
f.slots[f.cursor - 1] = {
|
|
2083
|
+
kind: "error",
|
|
2084
|
+
fn
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
function parseStateSnapshot(raw) {
|
|
2088
|
+
const parsed = safeParseSnapshot(raw, STATE_ATTR);
|
|
2089
|
+
if (parsed === void 0) return void 0;
|
|
2090
|
+
const rec = parsed;
|
|
2091
|
+
if (rec["v"] !== 2) return;
|
|
2092
|
+
const out = { skipOnce: rec["_skipOnMount"] === true };
|
|
2093
|
+
if (Array.isArray(rec["s"])) {
|
|
2094
|
+
stripUnsafeKeys(rec["s"]);
|
|
2095
|
+
out.s = rec["s"];
|
|
2096
|
+
}
|
|
2097
|
+
if (Array.isArray(rec["d"])) {
|
|
2098
|
+
stripUnsafeKeys(rec["d"]);
|
|
2099
|
+
out.d = rec["d"].map((entry) => {
|
|
2100
|
+
const e = entry ?? {};
|
|
2101
|
+
const errorValue = e.error;
|
|
2102
|
+
return {
|
|
2103
|
+
loading: e.loading === true,
|
|
2104
|
+
value: e.value,
|
|
2105
|
+
error: errorValue == null ? void 0 : errorValue instanceof Error ? errorValue : new Error(String(errorValue))
|
|
2106
|
+
};
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
return out;
|
|
2110
|
+
}
|
|
2111
|
+
const ilha = ((...args) => {
|
|
2112
|
+
let schema = null;
|
|
2113
|
+
let component;
|
|
2114
|
+
let options;
|
|
2115
|
+
const first = args[0];
|
|
2116
|
+
const second = args[1];
|
|
2117
|
+
if (isStandardSchema(first)) {
|
|
2118
|
+
schema = first;
|
|
2119
|
+
component = second;
|
|
2120
|
+
options = args[2];
|
|
2121
|
+
} else {
|
|
2122
|
+
component = first;
|
|
2123
|
+
options = second;
|
|
2124
|
+
}
|
|
2125
|
+
if (typeof component !== "function") throw new Error("[ilha] ilha() requires a component function.");
|
|
2126
|
+
const configuredSlotTag = options?.as === void 0 ? "div" : assertValidSlotTagName(options.as);
|
|
2127
|
+
function resolveInput(props) {
|
|
2128
|
+
const merged = { ...props };
|
|
2129
|
+
if (!schema) return merged;
|
|
2130
|
+
return validateSchema(schema, merged);
|
|
2131
|
+
}
|
|
2132
|
+
function renderWithCtx({ render, liveHost, asyncChildren, manifest }) {
|
|
2133
|
+
const ctx = pushRenderCtx(liveHost, asyncChildren, manifest);
|
|
2134
|
+
try {
|
|
2135
|
+
const html = unwrapHtml(render());
|
|
2136
|
+
const slots = ctx.slots;
|
|
2137
|
+
const binds = ctx.binds;
|
|
2138
|
+
const events = ctx.events;
|
|
2139
|
+
if (ctx.pending && ctx.pending.size > 0) {
|
|
2140
|
+
const pending = ctx.pending;
|
|
2141
|
+
return (async () => {
|
|
2142
|
+
return {
|
|
2143
|
+
html: await resolveAsyncChildren(html, pending),
|
|
2144
|
+
slots,
|
|
2145
|
+
binds,
|
|
2146
|
+
events
|
|
2054
2147
|
};
|
|
2055
|
-
|
|
2056
|
-
if (!currentRenderCtx()) runOnMountForRender({
|
|
2057
|
-
input,
|
|
2058
|
-
state,
|
|
2059
|
-
derived,
|
|
2060
|
-
action
|
|
2061
|
-
});
|
|
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
|
-
}
|
|
2078
|
-
}
|
|
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
|
-
});
|
|
2148
|
+
})();
|
|
2127
2149
|
}
|
|
2150
|
+
return {
|
|
2151
|
+
html,
|
|
2152
|
+
slots,
|
|
2153
|
+
binds,
|
|
2154
|
+
events
|
|
2155
|
+
};
|
|
2156
|
+
} finally {
|
|
2157
|
+
popRenderCtx();
|
|
2128
2158
|
}
|
|
2129
|
-
|
|
2130
|
-
|
|
2159
|
+
}
|
|
2160
|
+
/** Invoke the component inside the given frame + render context. */
|
|
2161
|
+
function runInFrame(frame, props) {
|
|
2162
|
+
return () => withFrame(frame, () => component(props));
|
|
2163
|
+
}
|
|
2164
|
+
function recordEventsManifest(eventsOut, events, frame) {
|
|
2165
|
+
if (!eventsOut || events.length === 0) return;
|
|
2166
|
+
events.forEach((record, index) => {
|
|
2167
|
+
const id = frame.actionManifest?.get(record.handler) ?? actionManifestId(record.handler);
|
|
2168
|
+
if (!id) return;
|
|
2169
|
+
const args = actionBoundArgs(record.handler);
|
|
2170
|
+
eventsOut.set(`${record.type}:${index}`, Array.isArray(args) ? {
|
|
2171
|
+
k: id,
|
|
2172
|
+
a: args
|
|
2173
|
+
} : id);
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
function renderToString(props, sync = false, eventsOut, forceAsyncChildren = false) {
|
|
2177
|
+
const input = resolveInput(props);
|
|
2178
|
+
const frame = freshFrame({ ssr: true });
|
|
2179
|
+
const first = forceAsyncChildren ? renderWithCtx({
|
|
2180
|
+
render: runInFrame(frame, input),
|
|
2181
|
+
asyncChildren: true,
|
|
2182
|
+
manifest: eventsOut !== void 0
|
|
2183
|
+
}) : renderWithCtx({
|
|
2184
|
+
render: runInFrame(frame, input),
|
|
2185
|
+
manifest: eventsOut !== void 0
|
|
2186
|
+
});
|
|
2187
|
+
const finish = (firstHtml) => firstHtml.html;
|
|
2188
|
+
const needsAsync = forceAsyncChildren || (frame.pendingDerived?.length ?? 0) > 0;
|
|
2189
|
+
if (first instanceof Promise) return first.then((out) => {
|
|
2190
|
+
recordEventsManifest(eventsOut, out.events, frame);
|
|
2191
|
+
return needsAsync ? awaitPending(out) : out.html;
|
|
2192
|
+
});
|
|
2193
|
+
recordEventsManifest(eventsOut, first.events, frame);
|
|
2194
|
+
if (!needsAsync || sync) {
|
|
2195
|
+
if (sync && (frame.pendingDerived?.length ?? 0) > 0 && __DEV__);
|
|
2196
|
+
return finish(first);
|
|
2131
2197
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
try {
|
|
2151
|
-
return Reflect.ownKeys(inputSignal());
|
|
2152
|
-
} finally {
|
|
2153
|
-
setActiveSub(prevSub);
|
|
2154
|
-
}
|
|
2155
|
-
},
|
|
2156
|
-
getOwnPropertyDescriptor(_t, key) {
|
|
2157
|
-
const prevSub = setActiveSub(void 0);
|
|
2158
|
-
try {
|
|
2159
|
-
return Reflect.getOwnPropertyDescriptor(inputSignal(), key);
|
|
2160
|
-
} finally {
|
|
2161
|
-
setActiveSub(prevSub);
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
});
|
|
2165
|
-
let snapshotRaw;
|
|
2166
|
-
const rawState = host.getAttribute(STATE_ATTR);
|
|
2167
|
-
if (rawState) {
|
|
2168
|
-
const parsed = safeParseSnapshot(rawState, STATE_ATTR);
|
|
2169
|
-
if (parsed !== void 0) snapshotRaw = parsed;
|
|
2170
|
-
}
|
|
2171
|
-
const stateSnapshot = snapshotRaw ? Object.fromEntries(Object.entries(snapshotRaw).filter(([k]) => k !== "_derived" && k !== "_skipOnMount" && k !== "_streams")) : void 0;
|
|
2172
|
-
const derivedSnapshotRaw = snapshotRaw?._derived;
|
|
2173
|
-
let derivedSnapshot;
|
|
2174
|
-
if (derivedSnapshotRaw) {
|
|
2175
|
-
derivedSnapshot = {};
|
|
2176
|
-
for (const [k, v] of Object.entries(derivedSnapshotRaw)) if (v.error && !(v.error instanceof Error)) derivedSnapshot[k] = {
|
|
2177
|
-
...v,
|
|
2178
|
-
error: new Error(String(v.error))
|
|
2198
|
+
return awaitPending(first);
|
|
2199
|
+
function awaitPending(out) {
|
|
2200
|
+
const pendings = frame.pendingDerived ?? [];
|
|
2201
|
+
if (pendings.length === 0) return Promise.resolve(out.html);
|
|
2202
|
+
return Promise.all(pendings.map((p) => p.promise)).then(() => {
|
|
2203
|
+
frame.creating = false;
|
|
2204
|
+
frame.cursor = 0;
|
|
2205
|
+
const second = forceAsyncChildren ? renderWithCtx({
|
|
2206
|
+
render: runInFrame(frame, input),
|
|
2207
|
+
asyncChildren: true,
|
|
2208
|
+
manifest: eventsOut !== void 0
|
|
2209
|
+
}) : renderWithCtx({
|
|
2210
|
+
render: runInFrame(frame, input),
|
|
2211
|
+
manifest: eventsOut !== void 0
|
|
2212
|
+
});
|
|
2213
|
+
const done = (o) => {
|
|
2214
|
+
recordEventsManifest(eventsOut, o.events, frame);
|
|
2215
|
+
return o.html;
|
|
2179
2216
|
};
|
|
2180
|
-
|
|
2181
|
-
}
|
|
2182
|
-
const hydrated = snapshotRaw != null;
|
|
2183
|
-
const snapshotHasIslandState = stateSnapshot != null && Object.keys(stateSnapshot).length > 0;
|
|
2184
|
-
const shouldSkipOnMount = hydrated && snapshotRaw?.["_skipOnMount"] === true && snapshotHasIslandState;
|
|
2185
|
-
const state = buildSignalState(input, stateSnapshot);
|
|
2186
|
-
const cleanups = [];
|
|
2187
|
-
const unmountController = new AbortController();
|
|
2188
|
-
cleanups.push(() => unmountController.abort());
|
|
2189
|
-
const { proxy: derived, setup: setupDerived } = createDerivedProxy({
|
|
2190
|
-
entries: deriveds,
|
|
2191
|
-
state,
|
|
2192
|
-
input,
|
|
2193
|
-
derivedSnapshot
|
|
2217
|
+
return second instanceof Promise ? second.then(done) : done(second);
|
|
2194
2218
|
});
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
function mountIsland(host, props) {
|
|
2222
|
+
return mountIslandInternal(host, props).unmount;
|
|
2223
|
+
}
|
|
2224
|
+
function mountIslandInternal(host, props) {
|
|
2225
|
+
if (props === void 0) {
|
|
2226
|
+
const rawProps = host.getAttribute(PROPS_ATTR);
|
|
2227
|
+
if (rawProps) {
|
|
2228
|
+
const parsed = safeParseSnapshot(rawProps, PROPS_ATTR);
|
|
2229
|
+
if (parsed !== void 0) props = reviveSlotProps(parsed);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
const inputSignal = signal(resolveInput(props));
|
|
2233
|
+
const derivedPropsVersion = signal(0);
|
|
2234
|
+
const input = new Proxy({}, {
|
|
2235
|
+
get(_t, key) {
|
|
2236
|
+
return inputSignal()[key];
|
|
2237
|
+
},
|
|
2238
|
+
has(_t, key) {
|
|
2239
|
+
return key in inputSignal();
|
|
2240
|
+
},
|
|
2241
|
+
ownKeys() {
|
|
2242
|
+
const prevSub = setActiveSub(void 0);
|
|
2243
|
+
try {
|
|
2244
|
+
return Reflect.ownKeys(inputSignal());
|
|
2245
|
+
} finally {
|
|
2246
|
+
setActiveSub(prevSub);
|
|
2202
2247
|
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
input,
|
|
2211
|
-
host
|
|
2212
|
-
});
|
|
2213
|
-
} catch (handlerErr) {
|
|
2214
|
-
console.error(handlerErr);
|
|
2248
|
+
},
|
|
2249
|
+
getOwnPropertyDescriptor(_t, key) {
|
|
2250
|
+
const prevSub = setActiveSub(void 0);
|
|
2251
|
+
try {
|
|
2252
|
+
return Reflect.getOwnPropertyDescriptor(inputSignal(), key);
|
|
2253
|
+
} finally {
|
|
2254
|
+
setActiveSub(prevSub);
|
|
2215
2255
|
}
|
|
2216
2256
|
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
const invoke = (props) => {
|
|
2232
|
-
if (currentRenderCtx()) return;
|
|
2233
|
-
if (disposed) return;
|
|
2234
|
-
const run = ++latestRun;
|
|
2235
|
-
const runSignal = unmountController.signal;
|
|
2236
|
-
const ctx = {
|
|
2237
|
-
state,
|
|
2238
|
-
derived,
|
|
2239
|
-
input,
|
|
2240
|
-
host,
|
|
2241
|
-
signal: runSignal
|
|
2242
|
-
};
|
|
2243
|
-
let result;
|
|
2244
|
-
let isAsync = false;
|
|
2245
|
-
startBatch();
|
|
2246
|
-
try {
|
|
2247
|
-
result = entry.fn(props, ctx);
|
|
2248
|
-
isAsync = result != null && typeof result.then === "function";
|
|
2249
|
-
const current = readEnvelope();
|
|
2250
|
-
envelope(isAsync ? {
|
|
2251
|
-
pending: current.pending + 1,
|
|
2252
|
-
data: current.data,
|
|
2253
|
-
error: void 0
|
|
2254
|
-
} : {
|
|
2255
|
-
pending: current.pending,
|
|
2256
|
-
data: result,
|
|
2257
|
-
error: void 0
|
|
2258
|
-
});
|
|
2259
|
-
} catch (error) {
|
|
2260
|
-
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
2261
|
-
envelope({
|
|
2262
|
-
pending: readEnvelope().pending,
|
|
2263
|
-
data: void 0,
|
|
2264
|
-
error: normalized
|
|
2265
|
-
});
|
|
2266
|
-
reportError(normalized, "action");
|
|
2267
|
-
return;
|
|
2268
|
-
} finally {
|
|
2269
|
-
endBatch();
|
|
2270
|
-
}
|
|
2271
|
-
if (!isAsync) return;
|
|
2272
|
-
Promise.resolve(result).then((data) => {
|
|
2273
|
-
if (disposed || runSignal.aborted || run !== latestRun) return;
|
|
2274
|
-
const value = readEnvelope();
|
|
2275
|
-
envelope({
|
|
2276
|
-
pending: value.pending,
|
|
2277
|
-
data,
|
|
2278
|
-
error: void 0
|
|
2279
|
-
});
|
|
2280
|
-
}).catch((error) => {
|
|
2281
|
-
if (disposed || runSignal.aborted || error?.name === "AbortError") return;
|
|
2282
|
-
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
2283
|
-
if (run === latestRun) {
|
|
2284
|
-
const value = readEnvelope();
|
|
2285
|
-
envelope({
|
|
2286
|
-
pending: value.pending,
|
|
2287
|
-
data: void 0,
|
|
2288
|
-
error: normalized
|
|
2289
|
-
});
|
|
2290
|
-
}
|
|
2291
|
-
reportError(normalized, "action");
|
|
2292
|
-
}).finally(() => {
|
|
2293
|
-
if (disposed) return;
|
|
2294
|
-
const value = readEnvelope();
|
|
2295
|
-
envelope({
|
|
2296
|
-
...value,
|
|
2297
|
-
pending: Math.max(0, value.pending - 1)
|
|
2298
|
-
});
|
|
2299
|
-
});
|
|
2300
|
-
};
|
|
2301
|
-
Object.defineProperties(invoke, {
|
|
2302
|
-
pending: {
|
|
2303
|
-
get: () => envelope().pending > 0,
|
|
2304
|
-
enumerable: true
|
|
2305
|
-
},
|
|
2306
|
-
data: {
|
|
2307
|
-
get: () => envelope().data,
|
|
2308
|
-
enumerable: true
|
|
2309
|
-
},
|
|
2310
|
-
error: {
|
|
2311
|
-
get: () => envelope().error,
|
|
2312
|
-
enumerable: true
|
|
2313
|
-
}
|
|
2314
|
-
});
|
|
2315
|
-
actionObject[entry.key] = invoke;
|
|
2257
|
+
});
|
|
2258
|
+
const snapshotRaw = host.getAttribute(STATE_ATTR);
|
|
2259
|
+
const snapshot = snapshotRaw ? parseStateSnapshot(snapshotRaw) : void 0;
|
|
2260
|
+
const hydrated = snapshot != null;
|
|
2261
|
+
const shouldSkipOnce = snapshot?.skipOnce === true;
|
|
2262
|
+
const cleanups = [];
|
|
2263
|
+
const unmountController = new AbortController();
|
|
2264
|
+
cleanups.push(() => unmountController.abort());
|
|
2265
|
+
const errorSlots = [];
|
|
2266
|
+
const reportError = (err, source) => {
|
|
2267
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
2268
|
+
if (errorSlots.length === 0) {
|
|
2269
|
+
if (!reportToGlobal(error, source)) console.error(error);
|
|
2270
|
+
return;
|
|
2316
2271
|
}
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2272
|
+
for (const slot of errorSlots) try {
|
|
2273
|
+
slot.fn({
|
|
2274
|
+
error,
|
|
2275
|
+
source,
|
|
2276
|
+
host
|
|
2277
|
+
});
|
|
2278
|
+
} catch (handlerErr) {
|
|
2279
|
+
console.error(handlerErr);
|
|
2323
2280
|
}
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2281
|
+
};
|
|
2282
|
+
const instance = {
|
|
2283
|
+
host,
|
|
2284
|
+
inputSignal,
|
|
2285
|
+
input,
|
|
2286
|
+
unmountController,
|
|
2287
|
+
cleanups,
|
|
2288
|
+
reportError,
|
|
2289
|
+
actionIds: /* @__PURE__ */ new Map(),
|
|
2290
|
+
stateSnapshot: snapshot ? {
|
|
2291
|
+
s: snapshot.s,
|
|
2292
|
+
d: snapshot.d
|
|
2293
|
+
} : void 0,
|
|
2294
|
+
hydrated,
|
|
2295
|
+
disposed: false,
|
|
2296
|
+
frame: null
|
|
2297
|
+
};
|
|
2298
|
+
cleanups.push(() => {
|
|
2299
|
+
instance.disposed = true;
|
|
2300
|
+
});
|
|
2301
|
+
const frame = freshFrame({
|
|
2302
|
+
ssr: false,
|
|
2303
|
+
instance
|
|
2304
|
+
});
|
|
2305
|
+
instance.frame = frame;
|
|
2306
|
+
const mountedSlots = /* @__PURE__ */ new Map();
|
|
2307
|
+
function teardownMountedSlot(id, entry) {
|
|
2308
|
+
mountedSlots.delete(id);
|
|
2309
|
+
entry.el.remove();
|
|
2310
|
+
entry.unmount();
|
|
2311
|
+
}
|
|
2312
|
+
function slotBelongsToHost(candidate) {
|
|
2313
|
+
let el = candidate.parentElement;
|
|
2314
|
+
while (el && el !== host) {
|
|
2315
|
+
if (el.hasAttribute(SLOT_ATTR) || el.hasAttribute("data-ilha")) return false;
|
|
2316
|
+
el = el.parentElement;
|
|
2347
2317
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2318
|
+
return el === host;
|
|
2319
|
+
}
|
|
2320
|
+
function buildSlotIndex() {
|
|
2321
|
+
const index = /* @__PURE__ */ new Map();
|
|
2322
|
+
for (const candidate of host.querySelectorAll(`[${SLOT_ATTR}]`)) {
|
|
2323
|
+
const id = candidate.getAttribute(SLOT_ATTR);
|
|
2324
|
+
if (id === null || index.has(id)) continue;
|
|
2325
|
+
if (slotBelongsToHost(candidate)) index.set(id, candidate);
|
|
2355
2326
|
}
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
if (id === null || index.has(id)) continue;
|
|
2363
|
-
if (slotBelongsToHost(candidate)) index.set(id, candidate);
|
|
2364
|
-
}
|
|
2365
|
-
return index;
|
|
2327
|
+
return index;
|
|
2328
|
+
}
|
|
2329
|
+
function pushUpdatedProps(nextSlots) {
|
|
2330
|
+
for (const [id, entry] of mountedSlots) {
|
|
2331
|
+
const next = nextSlots.get(id);
|
|
2332
|
+
if (next) entry.updateProps(next.props);
|
|
2366
2333
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2334
|
+
}
|
|
2335
|
+
function mountSlots(slotMap) {
|
|
2336
|
+
for (const [id, entry] of mountedSlots) if (!slotMap.has(id)) teardownMountedSlot(id, entry);
|
|
2337
|
+
for (const [id, entry] of mountedSlots) {
|
|
2338
|
+
const next = slotMap.get(id);
|
|
2339
|
+
if (next && next.island !== entry.island) teardownMountedSlot(id, entry);
|
|
2373
2340
|
}
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2341
|
+
let slotIndex = null;
|
|
2342
|
+
for (const [id, { island: childIsland, props: childProps }] of slotMap) {
|
|
2343
|
+
const existing = mountedSlots.get(id);
|
|
2344
|
+
if (existing) {
|
|
2345
|
+
existing.updateProps(childProps);
|
|
2346
|
+
continue;
|
|
2379
2347
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
if (!slotEl) continue;
|
|
2390
|
-
let slotProps = props;
|
|
2391
|
-
if (slotProps === void 0) {
|
|
2392
|
-
const rawProps = slotEl.getAttribute(PROPS_ATTR) ?? slotEl.getAttribute("data-props");
|
|
2393
|
-
if (rawProps) {
|
|
2394
|
-
const parsed = safeParseSnapshot(rawProps, `props on [${SLOT_ATTR}="${id}"]`);
|
|
2395
|
-
if (parsed !== void 0) slotProps = reviveSlotProps(parsed);
|
|
2396
|
-
}
|
|
2348
|
+
if (slotIndex === null) slotIndex = buildSlotIndex();
|
|
2349
|
+
const slotEl = slotIndex.get(id) ?? null;
|
|
2350
|
+
if (!slotEl) continue;
|
|
2351
|
+
let slotProps = childProps;
|
|
2352
|
+
if (slotProps === void 0) {
|
|
2353
|
+
const rawProps = slotEl.getAttribute(PROPS_ATTR) ?? slotEl.getAttribute("data-props");
|
|
2354
|
+
if (rawProps) {
|
|
2355
|
+
const parsed = safeParseSnapshot(rawProps, `props on [${SLOT_ATTR}="${id}"]`);
|
|
2356
|
+
if (parsed !== void 0) slotProps = reviveSlotProps(parsed);
|
|
2397
2357
|
}
|
|
2398
|
-
const prevSub = setActiveSub(void 0);
|
|
2399
|
-
let handle;
|
|
2400
|
-
try {
|
|
2401
|
-
const internal = childIsland[ISLAND_MOUNT_INTERNAL];
|
|
2402
|
-
handle = internal ? internal(slotEl, slotProps) : {
|
|
2403
|
-
unmount: childIsland.mount(slotEl, slotProps),
|
|
2404
|
-
updateProps: () => {}
|
|
2405
|
-
};
|
|
2406
|
-
} finally {
|
|
2407
|
-
setActiveSub(prevSub);
|
|
2408
|
-
}
|
|
2409
|
-
mountedSlots.set(id, {
|
|
2410
|
-
el: slotEl,
|
|
2411
|
-
island: childIsland,
|
|
2412
|
-
...handle
|
|
2413
|
-
});
|
|
2414
2358
|
}
|
|
2359
|
+
const prevSub = setActiveSub(void 0);
|
|
2360
|
+
let handle;
|
|
2361
|
+
try {
|
|
2362
|
+
const internal = childIsland[ISLAND_MOUNT_INTERNAL];
|
|
2363
|
+
handle = internal ? internal(slotEl, slotProps) : {
|
|
2364
|
+
unmount: childIsland.mount(slotEl, slotProps),
|
|
2365
|
+
updateProps: () => {}
|
|
2366
|
+
};
|
|
2367
|
+
} finally {
|
|
2368
|
+
setActiveSub(prevSub);
|
|
2369
|
+
}
|
|
2370
|
+
mountedSlots.set(id, {
|
|
2371
|
+
el: slotEl,
|
|
2372
|
+
island: childIsland,
|
|
2373
|
+
...handle
|
|
2374
|
+
});
|
|
2415
2375
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2376
|
+
}
|
|
2377
|
+
const nativeOnceFired = /* @__PURE__ */ new WeakMap();
|
|
2378
|
+
let stopJsxEvents = () => {};
|
|
2379
|
+
let stopBindings = () => {};
|
|
2380
|
+
/** Register derived scopes before the render effect so source updates
|
|
2381
|
+
* settle derived envelopes before a render can consume them. */
|
|
2382
|
+
function startDerivedScopes() {
|
|
2383
|
+
for (const slot of frame.slots) if (slot.kind === "derived") startDerivedScope(slot);
|
|
2384
|
+
}
|
|
2385
|
+
/** Register side-effecting scopes after the render effect so prop-change
|
|
2386
|
+
* renders refresh their closures before those scopes execute. */
|
|
2387
|
+
function startEffectScopes() {
|
|
2388
|
+
for (const slot of frame.slots) if (slot.kind === "effect") startEffectScope(slot);
|
|
2389
|
+
else if (slot.kind === "once") startOnceScope(slot);
|
|
2390
|
+
else if (slot.kind === "error") errorSlots.push(slot);
|
|
2391
|
+
}
|
|
2392
|
+
function startDerivedScope(slot) {
|
|
2393
|
+
let ac = new AbortController();
|
|
2394
|
+
let skipFirst = slot.fromSnapshot;
|
|
2395
|
+
const stopScope = effect$1(() => {
|
|
2396
|
+
ac.abort();
|
|
2397
|
+
ac = new AbortController();
|
|
2398
|
+
const currentAc = ac;
|
|
2399
|
+
derivedPropsVersion();
|
|
2400
|
+
let result;
|
|
2401
|
+
try {
|
|
2402
|
+
result = slot.fn({ signal: currentAc.signal });
|
|
2403
|
+
} catch (err) {
|
|
2404
|
+
if (skipFirst) {
|
|
2405
|
+
skipFirst = false;
|
|
2406
|
+
return;
|
|
2427
2407
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
for (const l of listeners.filter((l) => l.entry === entry)) l.target.removeEventListener(l.type, l.fn, l.options);
|
|
2433
|
-
listeners.splice(0, listeners.length, ...listeners.filter((l) => l.entry !== entry));
|
|
2434
|
-
}
|
|
2435
|
-
const eventTarget = event.target instanceof Element ? event.target : listenerTarget;
|
|
2436
|
-
const handlerTarget = entry.selector === "" ? eventTarget : listenerTarget;
|
|
2437
|
-
let handlerSignal;
|
|
2438
|
-
if (entry.abortable) {
|
|
2439
|
-
let entryMap = invocationControllers.get(entry);
|
|
2440
|
-
if (!entryMap) {
|
|
2441
|
-
entryMap = /* @__PURE__ */ new WeakMap();
|
|
2442
|
-
invocationControllers.set(entry, entryMap);
|
|
2443
|
-
}
|
|
2444
|
-
const prev = entryMap.get(listenerTarget);
|
|
2445
|
-
if (prev) prev.abort();
|
|
2446
|
-
const invocationController = new AbortController();
|
|
2447
|
-
entryMap.set(listenerTarget, invocationController);
|
|
2448
|
-
handlerSignal = anySignal([unmountController.signal, invocationController.signal]);
|
|
2449
|
-
} else handlerSignal = unmountController.signal;
|
|
2450
|
-
let result;
|
|
2451
|
-
startBatch();
|
|
2452
|
-
try {
|
|
2453
|
-
result = entry.handler({
|
|
2454
|
-
state,
|
|
2455
|
-
derived,
|
|
2456
|
-
action,
|
|
2457
|
-
input,
|
|
2458
|
-
host,
|
|
2459
|
-
target: handlerTarget,
|
|
2460
|
-
event,
|
|
2461
|
-
signal: handlerSignal
|
|
2462
|
-
});
|
|
2463
|
-
} catch (err) {
|
|
2464
|
-
reportError(err, "on");
|
|
2465
|
-
endBatch();
|
|
2466
|
-
return;
|
|
2467
|
-
}
|
|
2468
|
-
endBatch();
|
|
2469
|
-
if (result instanceof Promise) result.catch((err) => {
|
|
2470
|
-
if (err && err.name === "AbortError") return;
|
|
2471
|
-
reportError(err, "on");
|
|
2472
|
-
});
|
|
2473
|
-
};
|
|
2474
|
-
const opts = {
|
|
2475
|
-
...entry.options,
|
|
2476
|
-
once: false
|
|
2477
|
-
};
|
|
2478
|
-
listenerTarget.addEventListener(entry.event, listener, opts);
|
|
2479
|
-
listeners.push({
|
|
2480
|
-
target: listenerTarget,
|
|
2481
|
-
type: entry.event,
|
|
2482
|
-
fn: listener,
|
|
2483
|
-
options: opts,
|
|
2484
|
-
entry
|
|
2485
|
-
});
|
|
2408
|
+
writeEnvelope(currentAc, {
|
|
2409
|
+
loading: false,
|
|
2410
|
+
value: void 0,
|
|
2411
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2486
2412
|
});
|
|
2413
|
+
return;
|
|
2487
2414
|
}
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
derived,
|
|
2501
|
-
action,
|
|
2502
|
-
input,
|
|
2503
|
-
host,
|
|
2504
|
-
hydrated,
|
|
2505
|
-
signal: unmountController.signal
|
|
2415
|
+
if (skipFirst) {
|
|
2416
|
+
skipFirst = false;
|
|
2417
|
+
if (result instanceof Promise) result.catch(() => {});
|
|
2418
|
+
return;
|
|
2419
|
+
}
|
|
2420
|
+
if (!(result instanceof Promise)) {
|
|
2421
|
+
if (isAsyncIterable(result)) {
|
|
2422
|
+
const iterable = result;
|
|
2423
|
+
writeEnvelope(currentAc, {
|
|
2424
|
+
loading: true,
|
|
2425
|
+
value: readEnv().value,
|
|
2426
|
+
error: void 0
|
|
2506
2427
|
});
|
|
2507
|
-
|
|
2508
|
-
reportError(err, "mount");
|
|
2509
|
-
} finally {
|
|
2510
|
-
setActiveSub(prevSub);
|
|
2511
|
-
}
|
|
2512
|
-
if (userCleanup) {
|
|
2513
|
-
const teardown = userCleanup;
|
|
2514
|
-
cleanups.push(() => {
|
|
2428
|
+
(async () => {
|
|
2515
2429
|
try {
|
|
2516
|
-
|
|
2430
|
+
for await (const value of iterable) if (writeEnvelope(currentAc, {
|
|
2431
|
+
loading: false,
|
|
2432
|
+
value,
|
|
2433
|
+
error: void 0
|
|
2434
|
+
})) break;
|
|
2517
2435
|
} catch (err) {
|
|
2518
|
-
|
|
2436
|
+
writeEnvelope(currentAc, {
|
|
2437
|
+
loading: false,
|
|
2438
|
+
value: void 0,
|
|
2439
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2440
|
+
});
|
|
2519
2441
|
}
|
|
2520
|
-
});
|
|
2442
|
+
})();
|
|
2443
|
+
return;
|
|
2521
2444
|
}
|
|
2445
|
+
writeEnvelope(currentAc, {
|
|
2446
|
+
loading: false,
|
|
2447
|
+
value: result,
|
|
2448
|
+
error: void 0
|
|
2449
|
+
});
|
|
2450
|
+
return;
|
|
2522
2451
|
}
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2452
|
+
writeEnvelope(currentAc, {
|
|
2453
|
+
loading: true,
|
|
2454
|
+
value: readEnv().value,
|
|
2455
|
+
error: void 0
|
|
2456
|
+
});
|
|
2457
|
+
result.then((value) => {
|
|
2458
|
+
writeEnvelope(currentAc, {
|
|
2459
|
+
loading: false,
|
|
2460
|
+
value,
|
|
2461
|
+
error: void 0
|
|
2462
|
+
});
|
|
2463
|
+
}).catch((err) => {
|
|
2464
|
+
writeEnvelope(currentAc, {
|
|
2465
|
+
loading: false,
|
|
2466
|
+
value: void 0,
|
|
2467
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2468
|
+
});
|
|
2469
|
+
});
|
|
2535
2470
|
});
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
cleanups.push(() => stopBindings());
|
|
2540
|
-
let stopJsxEvents = applyJsxEvents({
|
|
2541
|
-
host,
|
|
2542
|
-
records: initial.events,
|
|
2543
|
-
reportError: (error) => reportError(error, "on"),
|
|
2544
|
-
unmountSignal: unmountController.signal,
|
|
2545
|
-
onceFired: nativeOnceFired
|
|
2471
|
+
cleanups.push(() => {
|
|
2472
|
+
stopScope();
|
|
2473
|
+
ac.abort();
|
|
2546
2474
|
});
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
const runSignal = anySignal([unmountController.signal, runController.signal]);
|
|
2572
|
-
startBatch();
|
|
2475
|
+
function readEnv() {
|
|
2476
|
+
const prevSub = setActiveSub(void 0);
|
|
2477
|
+
try {
|
|
2478
|
+
return slot.env();
|
|
2479
|
+
} finally {
|
|
2480
|
+
setActiveSub(prevSub);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
function writeEnvelope(ac, patch) {
|
|
2484
|
+
if (ac.signal.aborted) return true;
|
|
2485
|
+
const prevSub = setActiveSub(void 0);
|
|
2486
|
+
try {
|
|
2487
|
+
slot.env(patch);
|
|
2488
|
+
} finally {
|
|
2489
|
+
setActiveSub(prevSub);
|
|
2490
|
+
}
|
|
2491
|
+
return false;
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
function startEffectScope(slot) {
|
|
2495
|
+
let userCleanup;
|
|
2496
|
+
let runController = null;
|
|
2497
|
+
const stopScope = effect$1(() => {
|
|
2498
|
+
if (userCleanup) {
|
|
2573
2499
|
try {
|
|
2574
|
-
userCleanup = entry.fn({
|
|
2575
|
-
state,
|
|
2576
|
-
derived,
|
|
2577
|
-
action,
|
|
2578
|
-
input,
|
|
2579
|
-
host,
|
|
2580
|
-
signal: runSignal
|
|
2581
|
-
});
|
|
2582
|
-
} catch (err) {
|
|
2583
|
-
reportError(err, "effect");
|
|
2584
|
-
} finally {
|
|
2585
|
-
endBatch();
|
|
2586
|
-
}
|
|
2587
|
-
});
|
|
2588
|
-
cleanups.push(() => {
|
|
2589
|
-
stopEffect();
|
|
2590
|
-
if (userCleanup) try {
|
|
2591
2500
|
userCleanup();
|
|
2592
2501
|
} catch (err) {
|
|
2593
2502
|
reportError(err, "effect");
|
|
2594
2503
|
}
|
|
2595
|
-
|
|
2504
|
+
userCleanup = void 0;
|
|
2505
|
+
}
|
|
2506
|
+
if (runController) runController.abort();
|
|
2507
|
+
runController = new AbortController();
|
|
2508
|
+
const runSignal = AbortSignal.any([unmountController.signal, runController.signal]);
|
|
2509
|
+
inputSignal();
|
|
2510
|
+
startBatch();
|
|
2511
|
+
try {
|
|
2512
|
+
userCleanup = slot.fn({ signal: runSignal });
|
|
2513
|
+
} catch (err) {
|
|
2514
|
+
reportError(err, "effect");
|
|
2515
|
+
} finally {
|
|
2516
|
+
endBatch();
|
|
2517
|
+
}
|
|
2518
|
+
});
|
|
2519
|
+
cleanups.push(() => {
|
|
2520
|
+
stopScope();
|
|
2521
|
+
if (userCleanup) try {
|
|
2522
|
+
userCleanup();
|
|
2523
|
+
} catch (err) {
|
|
2524
|
+
reportError(err, "effect");
|
|
2525
|
+
}
|
|
2526
|
+
if (runController) runController.abort();
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
function startOnceScope(slot) {
|
|
2530
|
+
if (shouldSkipOnce) return;
|
|
2531
|
+
const prevSub = setActiveSub(void 0);
|
|
2532
|
+
let userCleanup;
|
|
2533
|
+
try {
|
|
2534
|
+
userCleanup = slot.fn({
|
|
2535
|
+
host,
|
|
2536
|
+
hydrated,
|
|
2537
|
+
signal: unmountController.signal
|
|
2596
2538
|
});
|
|
2539
|
+
} catch (err) {
|
|
2540
|
+
reportError(err, "once");
|
|
2541
|
+
} finally {
|
|
2542
|
+
setActiveSub(prevSub);
|
|
2597
2543
|
}
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
let streamAborted = false;
|
|
2602
|
-
const streamController = new AbortController();
|
|
2603
|
-
const runStream = async () => {
|
|
2544
|
+
if (userCleanup) {
|
|
2545
|
+
const teardown = userCleanup;
|
|
2546
|
+
cleanups.push(() => {
|
|
2604
2547
|
try {
|
|
2605
|
-
|
|
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
|
-
}
|
|
2548
|
+
teardown();
|
|
2620
2549
|
} catch (err) {
|
|
2621
|
-
|
|
2550
|
+
reportError(err, "once");
|
|
2622
2551
|
}
|
|
2623
|
-
};
|
|
2624
|
-
runStream();
|
|
2625
|
-
cleanups.push(() => {
|
|
2626
|
-
streamAborted = true;
|
|
2627
|
-
streamController.abort();
|
|
2628
2552
|
});
|
|
2629
2553
|
}
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2554
|
+
}
|
|
2555
|
+
const preserveSSRDom = hydrated && host.childNodes.length > 0;
|
|
2556
|
+
const initial = renderWithCtx({
|
|
2557
|
+
render: runInFrame(frame, input),
|
|
2558
|
+
liveHost: preserveSSRDom ? host : void 0
|
|
2559
|
+
});
|
|
2560
|
+
if (!preserveSSRDom) host.innerHTML = initial.html;
|
|
2561
|
+
const stopInitialBindingReflection = applyTemplateBindings(host, initial.binds, "reflect");
|
|
2562
|
+
let stopInitialBindingListeners = null;
|
|
2563
|
+
stopBindings = stopInitialBindingReflection;
|
|
2564
|
+
cleanups.push(() => stopBindings());
|
|
2565
|
+
stopJsxEvents = applyJsxEvents({
|
|
2566
|
+
host,
|
|
2567
|
+
records: initial.events,
|
|
2568
|
+
reportError: (error) => reportError(error, "event"),
|
|
2569
|
+
unmountSignal: unmountController.signal,
|
|
2570
|
+
onceFired: nativeOnceFired
|
|
2571
|
+
});
|
|
2572
|
+
cleanups.push(() => stopJsxEvents());
|
|
2573
|
+
mountSlots(initial.slots);
|
|
2574
|
+
cleanups.push(() => mountedSlots.forEach((entry) => entry.unmount()));
|
|
2575
|
+
stopInitialBindingListeners = applyTemplateBindings(host, initial.binds, "listen");
|
|
2576
|
+
stopBindings = () => {
|
|
2577
|
+
stopInitialBindingListeners();
|
|
2578
|
+
stopInitialBindingReflection();
|
|
2579
|
+
};
|
|
2580
|
+
let initialized = false;
|
|
2581
|
+
const initialRenderedHtml = initial.html;
|
|
2582
|
+
let lastRendered = null;
|
|
2583
|
+
let renderEpoch = 0;
|
|
2584
|
+
const renderReactive = () => {
|
|
2585
|
+
const epoch = ++renderEpoch;
|
|
2586
|
+
frame.creating = false;
|
|
2587
|
+
frame.cursor = 0;
|
|
2588
|
+
const { html: rendered, slots: newSlotMap, binds: newBinds, events: newEvents } = renderWithCtx({
|
|
2589
|
+
render: runInFrame(frame, input),
|
|
2590
|
+
liveHost: host
|
|
2591
|
+
});
|
|
2592
|
+
stopJsxEvents();
|
|
2593
|
+
stopJsxEvents = applyJsxEvents({
|
|
2594
|
+
host,
|
|
2595
|
+
records: newEvents,
|
|
2596
|
+
reportError: (error) => reportError(error, "event"),
|
|
2597
|
+
unmountSignal: unmountController.signal,
|
|
2598
|
+
onceFired: nativeOnceFired
|
|
2599
|
+
});
|
|
2600
|
+
if (!initialized) {
|
|
2601
|
+
initialized = true;
|
|
2602
|
+
if (preserveSSRDom) {
|
|
2603
|
+
pushUpdatedProps(newSlotMap);
|
|
2663
2604
|
if (rendered === initialRenderedHtml) {
|
|
2664
2605
|
lastRendered = rendered;
|
|
2665
2606
|
return;
|
|
2666
2607
|
}
|
|
2667
|
-
if (mountedSlots.size > 0 && mountedSlots.size === newSlotMap.size && [...newSlotMap.keys()].every((id) => mountedSlots.has(id)) && isStableInlineSlotMount({
|
|
2668
|
-
initialHtml: initialRenderedHtml,
|
|
2669
|
-
renderedHtml: rendered,
|
|
2670
|
-
slotIds: newSlotMap.keys()
|
|
2671
|
-
})) {
|
|
2672
|
-
pushUpdatedProps(newSlotMap);
|
|
2673
|
-
return;
|
|
2674
|
-
}
|
|
2675
2608
|
}
|
|
2676
|
-
if (rendered ===
|
|
2677
|
-
|
|
2609
|
+
if (rendered === initialRenderedHtml) {
|
|
2610
|
+
lastRendered = rendered;
|
|
2678
2611
|
return;
|
|
2679
2612
|
}
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
if (r instanceof Promise) leavingPromises.push(r);
|
|
2688
|
-
}
|
|
2689
|
-
for (const [id, entry] of mountedSlots) {
|
|
2690
|
-
const next = newSlotMap.get(id);
|
|
2691
|
-
if (next && next.island !== entry.island) {
|
|
2692
|
-
const r = teardownMountedSlot(id, entry);
|
|
2693
|
-
if (r instanceof Promise) leavingPromises.push(r);
|
|
2694
|
-
}
|
|
2613
|
+
if (mountedSlots.size > 0 && mountedSlots.size === newSlotMap.size && [...newSlotMap.keys()].every((id) => mountedSlots.has(id)) && isStableInlineSlotMount({
|
|
2614
|
+
initialHtml: initialRenderedHtml,
|
|
2615
|
+
renderedHtml: rendered,
|
|
2616
|
+
slotIds: newSlotMap.keys()
|
|
2617
|
+
})) {
|
|
2618
|
+
pushUpdatedProps(newSlotMap);
|
|
2619
|
+
return;
|
|
2695
2620
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2621
|
+
}
|
|
2622
|
+
if (rendered === lastRendered && mountedSlots.size === newSlotMap.size && [...newSlotMap].every(([id, next]) => mountedSlots.get(id)?.island === next.island)) {
|
|
2623
|
+
pushUpdatedProps(newSlotMap);
|
|
2624
|
+
return;
|
|
2625
|
+
}
|
|
2626
|
+
stopBindings();
|
|
2627
|
+
stopJsxEvents();
|
|
2628
|
+
const prevMountedCount = mountedSlots.size;
|
|
2629
|
+
for (const [id, entry] of mountedSlots) if (!newSlotMap.has(id)) teardownMountedSlot(id, entry);
|
|
2630
|
+
for (const [id, entry] of mountedSlots) {
|
|
2631
|
+
const next = newSlotMap.get(id);
|
|
2632
|
+
if (next && next.island !== entry.island) teardownMountedSlot(id, entry);
|
|
2633
|
+
}
|
|
2634
|
+
const applyMorph = () => {
|
|
2635
|
+
if (epoch !== renderEpoch) return;
|
|
2636
|
+
if (newSlotMap.size < prevMountedCount) {
|
|
2637
|
+
let divergeFrom = Infinity;
|
|
2638
|
+
for (const [id, entry] of mountedSlots) {
|
|
2639
|
+
if (!id.startsWith("p:")) continue;
|
|
2640
|
+
const next = newSlotMap.get(id);
|
|
2641
|
+
if (!next) continue;
|
|
2642
|
+
const index = Number(id.slice(2));
|
|
2643
|
+
if (Number.isNaN(index) || index >= divergeFrom) continue;
|
|
2644
|
+
if (serializeSlotProps(next.props) !== (entry.el.getAttribute(PROPS_ATTR) ?? "")) divergeFrom = index;
|
|
2713
2645
|
}
|
|
2714
|
-
const tpl = document.createElement("template");
|
|
2715
|
-
const morphRootTag = host.tagName.toLowerCase();
|
|
2716
|
-
tpl.innerHTML = `<${morphRootTag}>${html}</${morphRootTag}>`;
|
|
2717
|
-
morphInner(host, tpl.content.firstElementChild);
|
|
2718
|
-
lastRendered = rendered;
|
|
2719
|
-
let rehomeIndex = null;
|
|
2720
2646
|
for (const [id, entry] of mountedSlots) {
|
|
2721
|
-
if (!newSlotMap.has(id)
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
if (stub && stub !== entry.el) stub.replaceWith(entry.el);
|
|
2647
|
+
if (!newSlotMap.has(id)) continue;
|
|
2648
|
+
if (!entry.el.isConnected) continue;
|
|
2649
|
+
if (id.startsWith("p:") && Number(id.slice(2)) >= divergeFrom) teardownMountedSlot(id, entry);
|
|
2725
2650
|
}
|
|
2726
|
-
attachListeners();
|
|
2727
|
-
stopBindings = applyTemplateBindings(host, newBinds);
|
|
2728
|
-
stopJsxEvents = applyJsxEvents({
|
|
2729
|
-
host,
|
|
2730
|
-
records: newEvents,
|
|
2731
|
-
reportError: (error) => reportError(error, "on"),
|
|
2732
|
-
unmountSignal: unmountController.signal,
|
|
2733
|
-
onceFired: nativeOnceFired
|
|
2734
|
-
});
|
|
2735
|
-
mountSlots(newSlotMap);
|
|
2736
|
-
};
|
|
2737
|
-
if (leavingPromises.length > 0) Promise.allSettled(leavingPromises).then((results) => {
|
|
2738
|
-
for (const r of results) if (r.status === "rejected") console.error(r.reason);
|
|
2739
|
-
applyMorph();
|
|
2740
|
-
});
|
|
2741
|
-
else applyMorph();
|
|
2742
|
-
});
|
|
2743
|
-
let tornDown = false;
|
|
2744
|
-
const unmount = () => {
|
|
2745
|
-
if (tornDown) return;
|
|
2746
|
-
tornDown = true;
|
|
2747
|
-
disposed = true;
|
|
2748
|
-
unmountController.abort();
|
|
2749
|
-
ISLAND_MOUNT_HANDLES.delete(host);
|
|
2750
|
-
stopRender();
|
|
2751
|
-
detachListeners();
|
|
2752
|
-
const pending = [];
|
|
2753
|
-
for (const [, entry] of mountedSlots) {
|
|
2754
|
-
const result = entry.unmount();
|
|
2755
|
-
if (result instanceof Promise) pending.push(result);
|
|
2756
2651
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2652
|
+
const tpl = document.createElement("template");
|
|
2653
|
+
const morphRootTag = host.tagName.toLowerCase();
|
|
2654
|
+
tpl.innerHTML = `<${morphRootTag}>${rendered}</${morphRootTag}>`;
|
|
2655
|
+
morphInner(host, tpl.content.firstElementChild);
|
|
2656
|
+
lastRendered = rendered;
|
|
2657
|
+
let rehomeIndex = null;
|
|
2658
|
+
for (const [id, entry] of mountedSlots) {
|
|
2659
|
+
if (!newSlotMap.has(id) || entry.el.isConnected) continue;
|
|
2660
|
+
rehomeIndex ??= buildSlotIndex();
|
|
2661
|
+
const stub = rehomeIndex.get(id);
|
|
2662
|
+
if (stub && stub !== entry.el) stub.replaceWith(entry.el);
|
|
2762
2663
|
}
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
reportError(
|
|
2768
|
-
|
|
2664
|
+
stopBindings = applyTemplateBindings(host, newBinds);
|
|
2665
|
+
stopJsxEvents = applyJsxEvents({
|
|
2666
|
+
host,
|
|
2667
|
+
records: newEvents,
|
|
2668
|
+
reportError: (error) => reportError(error, "event"),
|
|
2669
|
+
unmountSignal: unmountController.signal,
|
|
2670
|
+
onceFired: nativeOnceFired
|
|
2769
2671
|
});
|
|
2770
|
-
|
|
2771
|
-
};
|
|
2772
|
-
const updateProps = (nextProps) => {
|
|
2773
|
-
if (tornDown) return;
|
|
2774
|
-
const next = resolveInput(nextProps);
|
|
2775
|
-
if (shallowEqualInput(inputSignal(), next)) return;
|
|
2776
|
-
inputSignal(next);
|
|
2777
|
-
};
|
|
2778
|
-
const handle = {
|
|
2779
|
-
unmount,
|
|
2780
|
-
updateProps
|
|
2781
|
-
};
|
|
2782
|
-
ISLAND_MOUNT_HANDLES.set(host, handle);
|
|
2783
|
-
return handle;
|
|
2784
|
-
}
|
|
2785
|
-
/**
|
|
2786
|
-
* Dual-mode island invocation:
|
|
2787
|
-
* - When called inside an `html` interpolation (i.e. {@link currentRenderCtx}
|
|
2788
|
-
* is active), returns an {@link IslandCall} carrying props to be emitted
|
|
2789
|
-
* as a child slot marker; {@link interpolateValue} consumes this marker.
|
|
2790
|
-
* - When called at the top level with no active render context, returns
|
|
2791
|
-
* SSR HTML via {@link renderToString} (backward-compatible public API).
|
|
2792
|
-
*
|
|
2793
|
-
* The declared return type omits {@link IslandCall} because TypeScript
|
|
2794
|
-
* cannot narrow based on runtime stack state.
|
|
2795
|
-
*/
|
|
2796
|
-
const island = ((props) => {
|
|
2797
|
-
if (currentRenderCtx()) return {
|
|
2798
|
-
[ISLAND_CALL]: true,
|
|
2799
|
-
island,
|
|
2800
|
-
props,
|
|
2801
|
-
key: void 0
|
|
2672
|
+
mountSlots(newSlotMap);
|
|
2802
2673
|
};
|
|
2803
|
-
|
|
2804
|
-
});
|
|
2805
|
-
island.toString = (props) => renderToString(props, true);
|
|
2806
|
-
island.mount = (host, props) => mountIsland(host, props);
|
|
2807
|
-
island[ISLAND_MOUNT_INTERNAL] = (host, props) => mountIslandInternal(host, props);
|
|
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;
|
|
2674
|
+
applyMorph();
|
|
2813
2675
|
};
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2676
|
+
let stopRender = effect$1(renderReactive);
|
|
2677
|
+
startDerivedScopes();
|
|
2678
|
+
stopRender();
|
|
2679
|
+
stopRender = effect$1(renderReactive);
|
|
2680
|
+
startEffectScopes();
|
|
2681
|
+
let tornDown = false;
|
|
2682
|
+
const unmount = () => {
|
|
2683
|
+
if (tornDown) return;
|
|
2684
|
+
tornDown = true;
|
|
2685
|
+
instance.disposed = true;
|
|
2686
|
+
unmountController.abort();
|
|
2687
|
+
ISLAND_MOUNT_HANDLES.delete(host);
|
|
2688
|
+
stopRender();
|
|
2689
|
+
for (const [, entry] of mountedSlots) entry.unmount();
|
|
2690
|
+
for (const c of cleanups) c();
|
|
2826
2691
|
};
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2692
|
+
const updateProps = (nextProps) => {
|
|
2693
|
+
if (tornDown) return;
|
|
2694
|
+
const next = resolveInput(nextProps);
|
|
2695
|
+
if (shallowEqualInput(inputSignal(), next)) return;
|
|
2696
|
+
inputSignal(next);
|
|
2697
|
+
derivedPropsVersion(derivedPropsVersion() + 1);
|
|
2698
|
+
};
|
|
2699
|
+
const handle = {
|
|
2700
|
+
unmount,
|
|
2701
|
+
updateProps
|
|
2702
|
+
};
|
|
2703
|
+
ISLAND_MOUNT_HANDLES.set(host, handle);
|
|
2704
|
+
return handle;
|
|
2705
|
+
}
|
|
2706
|
+
const island = ((props) => ({
|
|
2707
|
+
[ISLAND_CALL]: true,
|
|
2708
|
+
island,
|
|
2709
|
+
props,
|
|
2710
|
+
key: void 0
|
|
2711
|
+
}));
|
|
2712
|
+
island.toString = (props) => renderToString(props, true);
|
|
2713
|
+
island.toStringAsync = (props) => Promise.resolve(renderToString(props, false));
|
|
2714
|
+
island.mount = (host, props) => mountIsland(host, props);
|
|
2715
|
+
island[ISLAND_MOUNT_INTERNAL] = (host, props) => mountIslandInternal(host, props);
|
|
2716
|
+
island[ISLAND_SLOT_TAG] = configuredSlotTag;
|
|
2717
|
+
island[ISLAND_RENDER_STATE] = async (props) => {
|
|
2718
|
+
const manifest = /* @__PURE__ */ new Map();
|
|
2719
|
+
const html = await renderToString(props ?? void 0, false, manifest, true);
|
|
2720
|
+
return serializeServerManifest(manifest) + html;
|
|
2721
|
+
};
|
|
2722
|
+
island[ISLAND_SSR_MANIFEST] = (props, eventsOut) => renderToString(props, false, eventsOut, true);
|
|
2723
|
+
island.key = (key) => {
|
|
2724
|
+
if (typeof key !== "string" || key.trim().length === 0) throw new Error("island.key() requires a non-empty string.");
|
|
2725
|
+
if (key.includes(":")) throw new Error(`island.key() key cannot contain the slot separator ":" (got "${key}").`);
|
|
2726
|
+
const keyed = ((props) => ({
|
|
2727
|
+
[ISLAND_CALL]: true,
|
|
2728
|
+
island,
|
|
2729
|
+
props,
|
|
2730
|
+
key
|
|
2731
|
+
}));
|
|
2732
|
+
keyed[ISLAND_CALL] = true;
|
|
2733
|
+
return keyed;
|
|
2734
|
+
};
|
|
2735
|
+
island[ISLAND] = true;
|
|
2736
|
+
const astroRendererName = globalThis[ASTRO_RENDERER_GLOBAL];
|
|
2737
|
+
if (typeof astroRendererName === "string") island[Symbol.for("astro:renderer")] = astroRendererName;
|
|
2738
|
+
island.define = (tagName, options) => {
|
|
2739
|
+
if (typeof customElements === "undefined" || typeof HTMLElement === "undefined") {
|
|
2740
|
+
`${tagName}`;
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2743
|
+
const CE_RESERVED = /* @__PURE__ */ new Set([
|
|
2744
|
+
"annotation-xml",
|
|
2745
|
+
"color-profile",
|
|
2746
|
+
"font-face",
|
|
2747
|
+
"font-face-src",
|
|
2748
|
+
"font-face-uri",
|
|
2749
|
+
"font-face-format",
|
|
2750
|
+
"font-face-name",
|
|
2751
|
+
"missing-glyph"
|
|
2752
|
+
]);
|
|
2753
|
+
if (typeof tagName !== "string" || !/^[a-z][a-z0-9._-]*-[a-z0-9._-]*$/.test(tagName) || CE_RESERVED.has(tagName)) {
|
|
2754
|
+
`${tagName}`;
|
|
2755
|
+
return;
|
|
2756
|
+
}
|
|
2757
|
+
if (customElements.get(tagName)) {
|
|
2758
|
+
`${tagName}`;
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
const observe = options?.observe ?? [];
|
|
2762
|
+
class IlhaIslandElement extends HTMLElement {
|
|
2763
|
+
static observedAttributes = observe;
|
|
2764
|
+
_handle = null;
|
|
2765
|
+
_props;
|
|
2766
|
+
_unmounting = false;
|
|
2767
|
+
_reconnect = false;
|
|
2768
|
+
get props() {
|
|
2769
|
+
return this._props;
|
|
2848
2770
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2771
|
+
set props(p) {
|
|
2772
|
+
this._props = p;
|
|
2773
|
+
if (this._handle && !this._unmounting) this._handle.updateProps(this._mergedProps());
|
|
2852
2774
|
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2775
|
+
_mergedProps() {
|
|
2776
|
+
const attrProps = {};
|
|
2777
|
+
let hasAttrs = false;
|
|
2778
|
+
for (const name of observe) {
|
|
2779
|
+
const v = this.getAttribute(name);
|
|
2780
|
+
if (v !== null) {
|
|
2781
|
+
attrProps[name] = v;
|
|
2782
|
+
hasAttrs = true;
|
|
2783
|
+
}
|
|
2862
2784
|
}
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2785
|
+
if (!hasAttrs && this._props === void 0) return void 0;
|
|
2786
|
+
return {
|
|
2787
|
+
...attrProps,
|
|
2788
|
+
...this._props
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
connectedCallback() {
|
|
2792
|
+
if (this._unmounting) {
|
|
2793
|
+
this._reconnect = true;
|
|
2794
|
+
return;
|
|
2866
2795
|
}
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2796
|
+
if (this._handle) return;
|
|
2797
|
+
this._handle = mountIslandInternal(this, this._mergedProps());
|
|
2798
|
+
}
|
|
2799
|
+
disconnectedCallback() {
|
|
2800
|
+
if (!this._handle || this._unmounting) return;
|
|
2801
|
+
this._unmounting = true;
|
|
2802
|
+
this._reconnect = false;
|
|
2803
|
+
Promise.resolve(this._handle.unmount()).finally(() => {
|
|
2804
|
+
this._handle = null;
|
|
2805
|
+
this._unmounting = false;
|
|
2806
|
+
if (this._reconnect) {
|
|
2807
|
+
this._reconnect = false;
|
|
2808
|
+
if (this.isConnected) this._handle = mountIslandInternal(this, this._mergedProps());
|
|
2876
2809
|
}
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
attributeChangedCallback() {
|
|
2813
|
+
if (this._handle && !this._unmounting) this._handle.updateProps(this._mergedProps());
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
customElements.define(tagName, IlhaIslandElement);
|
|
2817
|
+
};
|
|
2818
|
+
island.hydratable = async (props, opts) => {
|
|
2819
|
+
const { name, as: rawTag = "div", snapshot = false, skipOnMount: explicitSkipOnMount } = opts;
|
|
2820
|
+
const tag = assertValidSlotTagName(rawTag);
|
|
2821
|
+
const resolvedProps = props ?? {};
|
|
2822
|
+
const innerPromise = renderToString(resolvedProps, false, void 0, true);
|
|
2823
|
+
let stateAttr = "";
|
|
2824
|
+
if (snapshot !== false) {
|
|
2825
|
+
const doState = snapshot === true || snapshot.state !== false;
|
|
2826
|
+
const doDerived = snapshot === true || snapshot.derived !== false;
|
|
2827
|
+
const doSkipOnMount = explicitSkipOnMount ?? (doState || doDerived);
|
|
2828
|
+
const snapshotData = { v: 2 };
|
|
2829
|
+
const input = resolveInput(resolvedProps);
|
|
2830
|
+
const probeFrame = freshFrame({ ssr: true });
|
|
2831
|
+
withFrame(probeFrame, () => {
|
|
2832
|
+
component(input);
|
|
2833
|
+
});
|
|
2834
|
+
if (doState) {
|
|
2835
|
+
const states = [];
|
|
2836
|
+
for (const slot of probeFrame.slots) {
|
|
2837
|
+
if (slot.kind !== "state") continue;
|
|
2838
|
+
const prevSub = setActiveSub(void 0);
|
|
2839
|
+
try {
|
|
2840
|
+
states.push(slot.acc());
|
|
2841
|
+
} finally {
|
|
2842
|
+
setActiveSub(prevSub);
|
|
2887
2843
|
}
|
|
2888
|
-
if (this._handle) return;
|
|
2889
|
-
this._handle = mountIslandInternal(this, this._mergedProps());
|
|
2890
2844
|
}
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
}
|
|
2845
|
+
snapshotData["s"] = states;
|
|
2846
|
+
}
|
|
2847
|
+
if (doDerived) {
|
|
2848
|
+
const probePendings = probeFrame.pendingDerived ?? [];
|
|
2849
|
+
if (probePendings.length > 0) {
|
|
2850
|
+
await Promise.all(probePendings.map((p) => p.promise));
|
|
2851
|
+
probeFrame.creating = false;
|
|
2852
|
+
probeFrame.cursor = 0;
|
|
2853
|
+
withFrame(probeFrame, () => {
|
|
2854
|
+
component(input);
|
|
2902
2855
|
});
|
|
2903
2856
|
}
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
const resolvedProps = props ?? {};
|
|
2914
|
-
const streamValues = {};
|
|
2915
|
-
const eventsManifest = /* @__PURE__ */ new Map();
|
|
2916
|
-
const inner = await renderToString(resolvedProps, false, streamValues, eventsManifest, void 0, true);
|
|
2917
|
-
const encodedProps = escapeHtml(JSON.stringify(resolvedProps));
|
|
2918
|
-
let stateAttr = "";
|
|
2919
|
-
if (snapshot !== false) {
|
|
2920
|
-
const doState = snapshot === true || snapshot.state !== false;
|
|
2921
|
-
const doDerived = snapshot === true || snapshot.derived !== false;
|
|
2922
|
-
const doSkipOnMount = explicitSkipOnMount ?? (doState || doDerived);
|
|
2923
|
-
const snapshotData = {};
|
|
2924
|
-
const input = resolveInput(resolvedProps);
|
|
2925
|
-
const plainState = buildPlainState(input);
|
|
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]();
|
|
2929
|
-
if (doDerived) {
|
|
2930
|
-
const derivedResults = {};
|
|
2931
|
-
for (const entry of deriveds) {
|
|
2932
|
-
const prevSub = setActiveSub(void 0);
|
|
2933
|
-
let resultPromise;
|
|
2934
|
-
let syncError;
|
|
2935
|
-
let threw = false;
|
|
2936
|
-
try {
|
|
2937
|
-
resultPromise = entry.fn({
|
|
2938
|
-
state: stateForDerived,
|
|
2939
|
-
input,
|
|
2940
|
-
signal: new AbortController().signal
|
|
2941
|
-
});
|
|
2942
|
-
} catch (err) {
|
|
2943
|
-
threw = true;
|
|
2944
|
-
syncError = err;
|
|
2945
|
-
} finally {
|
|
2946
|
-
setActiveSub(prevSub);
|
|
2947
|
-
}
|
|
2948
|
-
if (threw) {
|
|
2949
|
-
derivedResults[entry.key] = {
|
|
2950
|
-
loading: false,
|
|
2951
|
-
value: void 0,
|
|
2952
|
-
error: syncError instanceof Error ? syncError.message : String(syncError)
|
|
2953
|
-
};
|
|
2954
|
-
continue;
|
|
2955
|
-
}
|
|
2956
|
-
try {
|
|
2957
|
-
const result = await Promise.resolve(resultPromise);
|
|
2958
|
-
derivedResults[entry.key] = {
|
|
2959
|
-
loading: false,
|
|
2960
|
-
value: result,
|
|
2961
|
-
error: void 0
|
|
2962
|
-
};
|
|
2963
|
-
} catch (err) {
|
|
2964
|
-
derivedResults[entry.key] = {
|
|
2965
|
-
loading: false,
|
|
2966
|
-
value: void 0,
|
|
2967
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2968
|
-
};
|
|
2969
|
-
}
|
|
2857
|
+
const derivedEntries = [];
|
|
2858
|
+
for (const slot of probeFrame.slots) {
|
|
2859
|
+
if (slot.kind !== "derived") continue;
|
|
2860
|
+
const prevSub = setActiveSub(void 0);
|
|
2861
|
+
let envelope;
|
|
2862
|
+
try {
|
|
2863
|
+
envelope = { ...slot.env() };
|
|
2864
|
+
} finally {
|
|
2865
|
+
setActiveSub(prevSub);
|
|
2970
2866
|
}
|
|
2971
|
-
|
|
2867
|
+
const entry = {
|
|
2868
|
+
loading: false,
|
|
2869
|
+
value: envelope.value
|
|
2870
|
+
};
|
|
2871
|
+
if (envelope.error) entry["error"] = envelope.error.message;
|
|
2872
|
+
derivedEntries.push(entry);
|
|
2972
2873
|
}
|
|
2973
|
-
|
|
2974
|
-
if (streams.length > 0) snapshotData["_streams"] = Object.fromEntries(streams.map((entry) => [entry.key, true]));
|
|
2975
|
-
stateAttr = ` ${STATE_ATTR}='${escapeHtml(JSON.stringify(snapshotData))}'`;
|
|
2874
|
+
snapshotData["d"] = derivedEntries;
|
|
2976
2875
|
}
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
return
|
|
2982
|
-
}
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
const host = typeof selector === "string" ? document.querySelector(selector) : selector;
|
|
2986
|
-
if (!host) {
|
|
2987
|
-
console.warn(`[ilha] from(): element not found: ${selector}`);
|
|
2988
|
-
return null;
|
|
2989
|
-
}
|
|
2990
|
-
return island.mount(host, props);
|
|
2991
|
-
}
|
|
2876
|
+
if (doSkipOnMount) snapshotData["_skipOnMount"] = true;
|
|
2877
|
+
stateAttr = ` ${STATE_ATTR}='${escapeHtml(JSON.stringify(snapshotData))}'`;
|
|
2878
|
+
}
|
|
2879
|
+
const inner = await innerPromise;
|
|
2880
|
+
return `<${tag} data-ilha="${escapeHtml(name)}" ${PROPS_ATTR}='${escapeHtml(JSON.stringify(resolvedProps))}'${stateAttr}>${inner}</${tag}>`;
|
|
2881
|
+
};
|
|
2882
|
+
return island;
|
|
2883
|
+
});
|
|
2992
2884
|
function mountAll(registry, options = {}) {
|
|
2993
2885
|
const root = options.root ?? document.body;
|
|
2994
2886
|
const lazy = options.lazy ?? false;
|
|
@@ -3034,66 +2926,27 @@ function mountAll(registry, options = {}) {
|
|
|
3034
2926
|
if (pending.length > 0) return Promise.all(pending).then(() => {});
|
|
3035
2927
|
} };
|
|
3036
2928
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
}
|
|
3057
|
-
for (const key of Object.getOwnPropertyNames(IlhaBuilder.prototype)) {
|
|
3058
|
-
if (key === "constructor") continue;
|
|
3059
|
-
const descriptor = Object.getOwnPropertyDescriptor(IlhaBuilder.prototype, key);
|
|
3060
|
-
if (descriptor) Object.defineProperty(callableRoot, key, descriptor);
|
|
3061
|
-
}
|
|
3062
|
-
const ilha = Object.assign(callableRoot, {
|
|
3063
|
-
html: ilhaHtml,
|
|
3064
|
-
raw: ilhaRaw,
|
|
3065
|
-
mount: mountAll,
|
|
3066
|
-
from: ilhaFrom,
|
|
3067
|
-
context: ilhaContext,
|
|
3068
|
-
signal: ilhaSignal,
|
|
3069
|
-
computed: ilhaComputed,
|
|
3070
|
-
batch,
|
|
3071
|
-
untrack,
|
|
3072
|
-
onUncaughtError
|
|
2929
|
+
setJsxRuntimeBridge({
|
|
2930
|
+
registerEvent({ type, handler, modifier }) {
|
|
2931
|
+
const ctx = currentRenderCtx();
|
|
2932
|
+
if (!ctx) return void 0;
|
|
2933
|
+
SSR_ACTION_STUB in handler;
|
|
2934
|
+
const index = ctx.events.length;
|
|
2935
|
+
ctx.events.push({
|
|
2936
|
+
type,
|
|
2937
|
+
handler,
|
|
2938
|
+
modifier
|
|
2939
|
+
});
|
|
2940
|
+
return index;
|
|
2941
|
+
},
|
|
2942
|
+
slot({ island, props, key }) {
|
|
2943
|
+
return ilhaRaw(emitIslandSlot({
|
|
2944
|
+
island,
|
|
2945
|
+
props,
|
|
2946
|
+
key
|
|
2947
|
+
}));
|
|
2948
|
+
}
|
|
3073
2949
|
});
|
|
3074
|
-
/** @internal Used by the separate JSX runtime to register a native event handler. */
|
|
3075
|
-
function __ilhaJsxEvent({ type, handler, modifier }) {
|
|
3076
|
-
const ctx = currentRenderCtx();
|
|
3077
|
-
if (!ctx) return void 0;
|
|
3078
|
-
const isActionStub = SSR_ACTION_STUB in handler;
|
|
3079
|
-
const capture = ctx.manifest && !isActionStub ? captureHandlerActions(handler) : void 0;
|
|
3080
|
-
const index = ctx.events.length;
|
|
3081
|
-
ctx.events.push({
|
|
3082
|
-
type,
|
|
3083
|
-
handler,
|
|
3084
|
-
modifier,
|
|
3085
|
-
capture
|
|
3086
|
-
});
|
|
3087
|
-
return index;
|
|
3088
|
-
}
|
|
3089
|
-
/** @internal Used by the separate JSX runtime entry to preserve island slot composition. */
|
|
3090
|
-
function __ilhaJsxSlot({ island, props, key }) {
|
|
3091
|
-
return ilhaRaw(emitIslandSlot({
|
|
3092
|
-
island,
|
|
3093
|
-
props,
|
|
3094
|
-
key
|
|
3095
|
-
}));
|
|
3096
|
-
}
|
|
3097
2950
|
/**
|
|
3098
2951
|
* Morph an element's children toward `html`, patching in place: surviving
|
|
3099
2952
|
* elements keep their identity, listeners, and focus (see the morph engine).
|
|
@@ -3117,10 +2970,8 @@ function morph(host, html) {
|
|
|
3117
2970
|
}
|
|
3118
2971
|
const html = ilhaHtml;
|
|
3119
2972
|
const raw = ilhaRaw;
|
|
3120
|
-
const css = ilhaCss;
|
|
3121
2973
|
const mount = mountAll;
|
|
3122
|
-
const from = ilhaFrom;
|
|
3123
2974
|
const context = ilhaContext;
|
|
3124
2975
|
|
|
3125
2976
|
//#endregion
|
|
3126
|
-
export {
|
|
2977
|
+
export { action, batch, context, css, derived, each, effect, html, ilha, ilhaSignal, isSafeUrl, isSafeUrlAttrValue, isUrlAttributeName, json, morph, mount, onError, onUncaughtError, persist, raw, serializeStyle, state, untrack };
|