@alixex/ranview 0.1.0-alpha.0 → 0.2.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 +100 -26
- package/dist/builder.d.ts +128 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/core.d.ts +5 -116
- package/dist/core.d.ts.map +1 -1
- package/dist/events.d.ts +2 -63
- package/dist/events.d.ts.map +1 -1
- package/dist/factories-DYUNLmkS.js +203 -0
- package/dist/factories-DYUNLmkS.js.map +1 -0
- package/dist/factories.d.ts +22 -0
- package/dist/factories.d.ts.map +1 -0
- package/dist/factory.d.ts +1 -32
- package/dist/factory.d.ts.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +99 -248
- package/dist/index.js.map +1 -1
- package/dist/selectors.d.ts +1 -1
- package/dist/static.d.ts +18 -0
- package/dist/static.d.ts.map +1 -0
- package/dist/static.js +25 -0
- package/dist/static.js.map +1 -0
- package/package.json +15 -12
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/mocks-DvJQ7jGP.cjs +0 -2
- package/dist/mocks-DvJQ7jGP.cjs.map +0 -1
- package/dist/mocks.cjs +0 -1
- package/dist/signal.cjs +0 -2
- package/dist/signal.cjs.map +0 -1
package/dist/static.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, t as a } from "./factories-DYUNLmkS.js";
|
|
2
|
+
import { n as o, r as s, t as c } from "./mocks-Bxv4c1qp.js";
|
|
3
|
+
//#region src/static.ts
|
|
4
|
+
var l = () => ({ current: null });
|
|
5
|
+
function u(e) {
|
|
6
|
+
return () => (e.each() ?? []).map((t, n) => e.render(t, () => n));
|
|
7
|
+
}
|
|
8
|
+
function d(e) {
|
|
9
|
+
return () => (e.each() ?? []).map((t, n) => e.render(() => t, n));
|
|
10
|
+
}
|
|
11
|
+
function f(e) {
|
|
12
|
+
return () => e.when() ? e.children(() => e.when()) : e.fallback?.() ?? null;
|
|
13
|
+
}
|
|
14
|
+
var p = (e) => e;
|
|
15
|
+
function m(e) {
|
|
16
|
+
return () => {
|
|
17
|
+
let t = e.children.find((e) => !!e.when());
|
|
18
|
+
return t ? t.children(() => t.when()) : e.fallback?.() ?? null;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
var { View: h, Div: g, Svg: _, Span: v, Slot: y, ButtonBuilder: b, InputBuilder: x, Style: S, Label: C, Ul: w, Li: T, Section: E, Article: D, Nav: O, Header: k, Footer: A, Main: j, DeclarativeShadow: M } = a(n);
|
|
22
|
+
//#endregion
|
|
23
|
+
export { D as Article, b as ButtonBuilder, M as DeclarativeShadow, g as Div, c as DocumentFragmentMock, n as ElementBuilder, e as EventManager, A as Footer, u as For, o as HTMLElementMock, k as Header, d as Index, x as InputBuilder, C as Label, T as Li, j as Main, p as Match, O as Nav, i as SVG_NAMESPACE, E as Section, t as ShadowBuilder, s as ShadowRootMock, f as Show, y as Slot, v as Span, S as Style, _ as Svg, m as Switch, w as Ul, h as View, l as createRef, r as isSSR };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=static.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static.js","names":[],"sources":["../src/static.ts"],"sourcesContent":["/** Static DOM/mock construction. Getters render once; no signal runtime is imported. */\nimport { ElementBuilder } from './builder';\nimport { createFactories } from './factories';\nimport type { Child, ForOptions, IndexOptions, ShowOptions, SwitchOptions, MatchClause } from './core';\nexport { ElementBuilder, ShadowBuilder, SVG_NAMESPACE } from './builder';\nexport { isSSR } from './env';\nexport { EventManager } from './events';\nexport { DocumentFragmentMock, HTMLElementMock, ShadowRootMock } from './mocks';\nexport type { Child, Ref, ForOptions, IndexOptions, ShowOptions, SwitchOptions, MatchClause } from './core';\nexport const createRef = <T extends HTMLElement = HTMLElement>() => ({ current: null as T | null });\nexport function For<T>(options: ForOptions<T>): () => Child {\n return () => (options.each() ?? []).map((item, i) => options.render(item, () => i));\n}\nexport function Index<T>(options: IndexOptions<T>): () => Child {\n return () => (options.each() ?? []).map((item, i) => options.render(() => item, i));\n}\nexport function Show<T>(options: ShowOptions<T>): () => Child {\n return () =>\n options.when() ? options.children(() => options.when() as NonNullable<T>) : (options.fallback?.() ?? null);\n}\nexport const Match = <T>(clause: MatchClause<T>): MatchClause<T> => clause;\nexport function Switch(options: SwitchOptions): () => Child {\n return () => {\n const clause = options.children.find((c) => Boolean(c.when()));\n return clause ? clause.children(() => clause.when() as NonNullable<unknown>) : (options.fallback?.() ?? null);\n };\n}\nexport const {\n View,\n Div,\n Svg,\n Span,\n Slot,\n ButtonBuilder,\n InputBuilder,\n Style,\n Label,\n Ul,\n Li,\n Section,\n Article,\n Nav,\n Header,\n Footer,\n Main,\n DeclarativeShadow,\n} = createFactories(ElementBuilder);\n"],"mappings":";;;AASA,IAAa,WAAwD,EAAE,SAAS,KAAiB;AACjG,SAAgB,EAAO,GAAqC;CAC1D,cAAc,EAAQ,KAAK,KAAK,CAAC,EAAA,CAAG,KAAK,GAAM,MAAM,EAAQ,OAAO,SAAY,CAAC,CAAC;AACpF;AACA,SAAgB,EAAS,GAAuC;CAC9D,cAAc,EAAQ,KAAK,KAAK,CAAC,EAAA,CAAG,KAAK,GAAM,MAAM,EAAQ,aAAa,GAAM,CAAC,CAAC;AACpF;AACA,SAAgB,EAAQ,GAAsC;CAC5D,aACE,EAAQ,KAAK,IAAI,EAAQ,eAAe,EAAQ,KAAK,CAAmB,IAAK,EAAQ,WAAW,KAAK;AACzG;AACA,IAAa,KAAY,MAA2C;AACpE,SAAgB,EAAO,GAAqC;CAC1D,aAAa;EACX,IAAM,IAAS,EAAQ,SAAS,MAAM,MAAM,EAAQ,EAAE,KAAK,CAAE;EAC7D,OAAO,IAAS,EAAO,eAAe,EAAO,KAAK,CAAyB,IAAK,EAAQ,WAAW,KAAK;CAC1G;AACF;AACA,IAAa,EACX,SACA,QACA,QACA,SACA,SACA,kBACA,iBACA,UACA,UACA,OACA,OACA,YACA,YACA,QACA,WACA,WACA,SACA,yBACE,EAAgB,CAAc"}
|
package/package.json
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alixex/ranview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Framework-free DOM construction with fine-grained reactivity — build once, update in place, in a browser or to a string",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.js"
|
|
10
|
-
"require": "./dist/index.cjs"
|
|
9
|
+
"import": "./dist/index.js"
|
|
11
10
|
},
|
|
12
11
|
"./signal": {
|
|
13
12
|
"types": "./dist/signal.d.ts",
|
|
14
|
-
"import": "./dist/signal.js"
|
|
15
|
-
"require": "./dist/index.cjs"
|
|
13
|
+
"import": "./dist/signal.js"
|
|
16
14
|
},
|
|
17
15
|
"./mocks": {
|
|
18
16
|
"types": "./dist/mocks.d.ts",
|
|
19
|
-
"import": "./dist/mocks.js"
|
|
20
|
-
|
|
17
|
+
"import": "./dist/mocks.js"
|
|
18
|
+
},
|
|
19
|
+
"./static": {
|
|
20
|
+
"types": "./dist/static.d.ts",
|
|
21
|
+
"import": "./dist/static.js"
|
|
21
22
|
}
|
|
22
23
|
},
|
|
23
24
|
"repository": {
|
|
@@ -31,14 +32,15 @@
|
|
|
31
32
|
"author": "chaxus",
|
|
32
33
|
"license": "MIT",
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"ranuts": "0.
|
|
35
|
+
"ranuts": "0.5.0"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@types/node": "26.1.2",
|
|
38
39
|
"jsdom": "30.0.1",
|
|
39
40
|
"typescript": "7.0.2",
|
|
40
41
|
"vite": "8.2.1",
|
|
41
|
-
"vitest": "4.1.11"
|
|
42
|
+
"vitest": "4.1.11",
|
|
43
|
+
"tsx": "4.23.10"
|
|
42
44
|
},
|
|
43
45
|
"keywords": [
|
|
44
46
|
"dom",
|
|
@@ -58,12 +60,13 @@
|
|
|
58
60
|
"access": "public"
|
|
59
61
|
},
|
|
60
62
|
"scripts": {
|
|
61
|
-
"build": "vite build && tsc -p tsconfig.build.json",
|
|
63
|
+
"build": "vite build && tsc -p tsconfig.build.json && node ../../bin/prepare-declarations.mjs dist . && node bin/verify-static-build.mjs",
|
|
62
64
|
"tsc": "tsc --noEmit",
|
|
63
65
|
"test": "vitest run",
|
|
64
|
-
"test:watch": "vitest"
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"bench:ssr": "tsx ./bench/ssr.ts"
|
|
65
68
|
},
|
|
66
|
-
"main": "./dist/index.
|
|
69
|
+
"main": "./dist/index.js",
|
|
67
70
|
"module": "./dist/index.js",
|
|
68
71
|
"types": "./dist/index.d.ts"
|
|
69
72
|
}
|
package/dist/index.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./signal.cjs"),t=require("./mocks-DvJQ7jGP.cjs");let n=require("ranuts/utils");var r=typeof document>`u`,i=class{ac=null;get signal(){return this.ac??=new AbortController,this.ac.signal}on(e,t,n,r){return e.addEventListener(t,n,{...r,signal:this.signal}),this}delegate(e,t,n,r,i){return e.addEventListener(n,n=>{let i=n.target?.closest(t);i&&e.contains(i)&&r(n,i)},{...i,signal:this.signal}),this}abort(){this.ac?.abort(),this.ac=null}},a=()=>({current:null}),o=e=>e.reduce((e,t)=>Array.isArray(t)?e.concat(o(t)):e.concat(t),[]),s=e=>e==null?null:e instanceof D?e.build():typeof e==`string`?r?e:document.createTextNode(e):e,c=(t,n)=>{if(r){C(t,[n()]);return}let i=document.createComment(``),a=document.createComment(``);t.appendChild(i),t.appendChild(a);let o=null,s=()=>{o?.(),o=null;let e=i.nextSibling;for(;e&&e!==a;){let t=e.nextSibling;e.parentNode?.removeChild(e),e=t}};e.createEffect(()=>{let t=n();s();let r=document.createDocumentFragment();o=e.createRoot(e=>(C(r,[t]),e)),a.parentNode?.insertBefore(r,a)}),e.onCleanup(()=>o?.())},l=`__ranFor`;function u(e){return{...e,[l]:!0}}var d=e=>typeof e==`object`&&!!e&&e[l]===!0;function f(t){let n=e.computed(()=>!!t.when()),r=()=>t.when();return()=>n()?t.children(r):t.fallback?.()??null}function p(e){return e}function m(t){let n=t.children,r=e.computed(()=>n.findIndex(e=>!!e.when()));return()=>{let e=r();if(e<0)return t.fallback?.()??null;let i=n[e];return i.children(()=>i.when())}}var h=(t,n,r)=>{let[i,a]=e.signal(n),o=null,c=e.createRoot(e=>{let n=s(r(t,i));return o=typeof n==`string`?document.createTextNode(n):n,e});return{node:o,index:a,dispose:c}},g=(e,t)=>{let n=e.parentNode;if(!n)return;let r=e;for(let e of t)r.nextSibling!==e&&n.insertBefore(e,r.nextSibling),r=e},_=(t,n)=>{if(r){(n.each()??[]).forEach((e,r)=>{let i=s(n.render(e,()=>r));i!=null&&t.appendChild(i)});return}let i=document.createComment(``);t.appendChild(i);let a=new Map;e.onCleanup(()=>{for(let e of a.values())e.dispose();a.clear()}),e.createEffect(()=>{let e=n.each()??[],t=new Map,r=[];e.forEach((e,i)=>{let o=n.key(e,i);if(t.has(o)){E()&&console.error(`[ranview For] duplicate key "${String(o)}" — keys must be unique; ignoring the duplicate item.`);return}let s=a.get(o);s?(a.delete(o),s.index(i)):s=h(e,i,n.render),t.set(o,s),s.node&&r.push(s.node)});for(let e of a.values())e.node?.parentNode?.removeChild(e.node),e.dispose();g(i,r),a=t})},v=`__ranIndex`;function y(e){return{...e,[v]:!0}}var b=e=>typeof e==`object`&&!!e&&e[v]===!0,x=(t,n,r)=>{let[i,a]=e.signal(t),o=null,c=e.createRoot(e=>{let t=s(r(i,n));return o=typeof t==`string`?document.createTextNode(t):t,e});return{node:o,setItem:a,dispose:c}},S=(t,n)=>{if(r){(n.each()??[]).forEach((e,r)=>{let i=s(n.render(()=>e,r));i!=null&&t.appendChild(i)});return}let i=document.createComment(``);t.appendChild(i);let a=[];e.onCleanup(()=>{for(let e of a)e.dispose();a=[]}),e.createEffect(()=>{let e=n.each()??[];for(let t=0;t<e.length;t++)t<a.length?a[t].setItem(e[t]):a.push(x(e[t],t,n.render));for(let t=a.length-1;t>=e.length;t--){let{node:e}=a[t];e?.parentNode?.removeChild(e),a[t].dispose(),a.pop()}g(i,a.map(e=>e.node).filter(e=>e!=null))})},C=(e,t)=>{o(t).forEach(t=>{if(d(t)){_(e,t);return}if(b(t)){S(e,t);return}if(typeof t==`function`){c(e,t);return}let n=s(t);n!=null&&e.appendChild(n)})},w=`http://www.w3.org/2000/svg`,T=new Set(`svg.circle.clipPath.defs.desc.ellipse.feBlend.feColorMatrix.feComposite.feDropShadow.feFlood.feGaussianBlur.feMerge.feMergeNode.feOffset.filter.foreignObject.g.image.line.linearGradient.marker.mask.path.pattern.polygon.polyline.radialGradient.rect.stop.switch.symbol.text.textPath.tspan.use`.split(`.`)),E=()=>!1,D=class{el;constructor(e,n){let i=n??(T.has(e)?`http://www.w3.org/2000/svg`:void 0);this.el=r?new t.n(e):i?document.createElementNS(i,e):document.createElement(e)}id(e){return this.el.setAttribute(`id`,e),this}bind(t,n){typeof t==`function`?e.createEffect(()=>n(t())):n(t)}class(e){return this.bind(e,e=>{r?this.el.attributes.set(`class`,e):this.el.setAttribute(`class`,e)}),this}addClass(...e){return e.forEach(e=>this.el.classList.add(e)),this}removeClass(...e){return e.forEach(e=>this.el.classList.remove(e)),this}attr(e,t){return this.bind(t,t=>this.el.setAttribute(e,t)),this}attrs(e){return Object.entries(e).forEach(([e,t])=>{t!=null&&this.el.setAttribute(e,String(t))}),this}boolAttr(e,t,n=``){return this.bind(t,t=>{t?this.el.setAttribute(e,n):this.el.removeAttribute(e)}),this}part(e){return this.attr(`part`,e)}data(e,t){return this.attr(`data-${e}`,t)}style(e,t){return typeof e==`string`?this.bind(t??``,t=>this.el.style.setProperty(e,t)):Object.entries(e).forEach(([e,t])=>this.el.style.setProperty(e,t)),this}cssVar(e,t){let n=e.startsWith(`--`)?e:`--${e}`;return this.style(n,t)}aria(e,t){return this.attr(`aria-${e}`,t)}role(e){return this.attr(`role`,e)}tabIndex(e){return this.attr(`tabindex`,String(e))}label(e){return this.aria(`label`,e)}labelledBy(e){return this.aria(`labelledby`,e)}describedBy(e){return this.aria(`describedby`,e)}ariaHidden(e=!0){return this.aria(`hidden`,String(e))}on(e,t,n){return this.el.addEventListener(e,t,n),this}listen(e,t,n,r){return e.on(this.el,t,n,r),this}delegate(e,t,n,r,i){return e.delegate(this.el,t,n,r,i),this}children(...e){return C(this.el,e),this}replaceChildren(...e){if(r){let e=this.el;e.childrenList=[]}else this.el.replaceChildren();return this.children(...e)}text(e){return this.bind(e,e=>{this.el.textContent=e}),this}unsafeHtml(e){return this.bind(e,e=>{this.el.innerHTML=e}),this}ref(e){return e.current=this.el,this}shadow(e={mode:`closed`}){let t=this.el.attachShadow(e);return new O(this.el,t,e)}build(){return this.el}serialize(){if(r)return this.el.serialize();let e=document.createElement(`div`);return e.appendChild(this.el.cloneNode(!0)),e.innerHTML}},O=class{root;hostEl;constructor(e,t,n){this.hostEl=e,this.root=t}children(...e){return C(this.root,e),this}adoptSheet(...e){return this.root.adoptedStyleSheets=[...this.root.adoptedStyleSheets,...e],this}css(e){if(r)return this.root.adoptedStyleSheets.push(e),this;if(typeof CSSStyleSheet<`u`)try{let t=new CSSStyleSheet;return t.replaceSync(e),this.root.adoptedStyleSheets=[...this.root.adoptedStyleSheets,t],this}catch{}let t=document.createElement(`style`);return t.textContent=e,this.root.appendChild(t),this}done(){return{host:this.hostEl,shadow:this.root}}serialize(){return r?this.root.serialize():this.root.innerHTML}},k=e=>new D(e),A=()=>k(`div`),j=e=>new D(e,w),M=()=>k(`span`),N=()=>k(`slot`),P=()=>k(`button`),F=()=>k(`input`),I=()=>k(`style`),L=()=>k(`label`),R=()=>k(`ul`),z=()=>k(`li`),B=()=>k(`section`),V=()=>k(`article`),H=()=>k(`nav`),U=()=>k(`header`),W=()=>k(`footer`),G=()=>k(`main`),K=(e=`open`,t=!1)=>{let n=k(`template`);return n.attr(`shadowrootmode`,e),t&&n.attr(`shadowrootdelegatesfocus`,``),n};exports.Article=V,exports.ButtonBuilder=P,exports.DeclarativeShadow=K,exports.Div=A,exports.DocumentFragmentMock=t.t,exports.ElementBuilder=D,exports.EventManager=i,exports.Footer=W,exports.For=u,exports.HTMLElementMock=t.n,exports.Header=U,exports.Index=y,exports.InputBuilder=F,exports.Label=L,exports.Li=z,exports.Main=G,exports.Match=p,exports.Nav=H,exports.SVG_NAMESPACE=w,exports.Section=B,exports.ShadowBuilder=O,exports.ShadowRootMock=t.r,exports.Show=f,exports.Slot=N,exports.Span=M,exports.Style=I,exports.Svg=j,exports.Switch=m,exports.Ul=R,exports.View=k,exports.batch=e.batch,exports.collectMatches=t.i,exports.computed=e.computed,exports.createEffect=e.createEffect,exports.createRef=a,exports.createRoot=e.createRoot,exports.escapeHtml=n.escapeHtml,exports.escapeHtmlAttribute=t.o,exports.getOwner=e.getOwner,exports.isSSR=r,exports.matchSelector=t.a,exports.onCleanup=e.onCleanup,exports.runWithOwner=e.runWithOwner,exports.signal=e.signal,exports.untrack=e.untrack;
|
|
2
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/env.ts","../src/events.ts","../src/core.ts","../src/factory.ts"],"sourcesContent":["export const isSSR = typeof document === 'undefined';\n","/**\n * EventManager — lifecycle-scoped event registry backed by AbortController.\n *\n * Web Component usage:\n *\n * private _events = new EventManager();\n *\n * connectedCallback() {\n * this._events\n * .on(this._input, 'input', this.handleInput)\n * .on(this, 'click', this.handleClick, { capture: true });\n * }\n *\n * disconnectedCallback() {\n * this._events.abort(); // removes all listeners, resets for next connect\n * }\n *\n * Page development usage:\n *\n * function initSection(container: HTMLElement) {\n * const scope = new EventManager();\n *\n * scope\n * .on(input, 'input', handleSearch)\n * .delegate(container, '[data-action]', 'click', (ev, target) => {\n * handleAction(target.getAttribute('data-action'));\n * });\n *\n * return () => scope.abort(); // call on section teardown\n * }\n */\nexport class EventManager {\n /**\n * Created on first use, and dropped again by {@link abort}.\n *\n * No component registers a listener in its constructor — they all wait for\n * `connectedCallback` — and some managers, like the drag-scoped one in `r-progress`,\n * only ever register during an interaction that may not happen. Allocating eagerly\n * charged every construction, and every `abort()`, for a controller that might go unused:\n * about 30% of a mount/unmount cycle for `r-progress`.\n */\n private ac: AbortController | null = null;\n\n /** The underlying AbortSignal — pass to addEventListener options directly if needed. */\n get signal(): AbortSignal {\n this.ac ??= new AbortController();\n return this.ac.signal;\n }\n\n on<K extends keyof HTMLElementEventMap>(\n target: HTMLElement,\n type: K,\n handler: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,\n options?: Omit<AddEventListenerOptions, 'signal'>,\n ): this;\n on(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: Omit<AddEventListenerOptions, 'signal'>,\n ): this;\n on(target: EventTarget, type: string, handler: any, options?: Omit<AddEventListenerOptions, 'signal'>): this {\n target.addEventListener(type, handler, { ...options, signal: this.signal });\n return this;\n }\n\n /**\n * Event delegation — attach one listener to `parent`, fire `handler` only when\n * the event originates from a descendant matching `selector`.\n *\n * The handler receives the original event and the matched element as arguments.\n *\n * scope.delegate(list, '.item', 'click', (ev, item) => {\n * console.log(item.dataset.id);\n * });\n */\n delegate<K extends keyof HTMLElementEventMap>(\n parent: HTMLElement,\n selector: string,\n type: K,\n handler: (ev: HTMLElementEventMap[K], target: Element) => void,\n options?: Omit<AddEventListenerOptions, 'signal'>,\n ): this {\n parent.addEventListener(\n type,\n (ev) => {\n const target = (ev.target as Element | null)?.closest(selector);\n if (target && parent.contains(target)) handler(ev as HTMLElementEventMap[K], target);\n },\n { ...options, signal: this.signal },\n );\n return this;\n }\n\n /**\n * Remove all registered listeners and drop the controller.\n * Safe to call multiple times; the next on() / delegate() starts a fresh one.\n */\n abort(): void {\n this.ac?.abort();\n this.ac = null;\n }\n}\n","import { isSSR } from './env';\nimport { DocumentFragmentMock, HTMLElementMock, ShadowRootMock } from './mocks';\nimport type { EventManager } from './events';\nimport { computed, createEffect, createRoot, onCleanup, signal, type Getter } from './signal';\n\nexport interface Ref<T extends HTMLElement = HTMLElement> {\n current: T | null;\n}\n\nexport const createRef = <T extends HTMLElement = HTMLElement>(): Ref<T> => ({ current: null });\n\n/** A single directly-appendable child: element, text, nested builder, or nothing.\n * Internal helper — the public, general type is {@link Child}. */\ntype StaticChild = HTMLElement | string | ElementBuilder<any> | undefined | null;\n\n/**\n * The one type every `children()` / `replaceChildren()` argument accepts —\n * fully composable (recursive): a node, text, nested builder, `null`/`undefined`,\n * a (nested) array, a {@link For}/{@link Index} handle for a keyed list, or a\n * getter `() => Child` marking a reactive region. A getter (or `Show`/`Switch`\n * branch) may itself return any `Child`, including a `For`/`Index` or another\n * getter — control-flow nests freely. On SSR getters / `For` / `Index` render once.\n */\nexport type Child = StaticChild | ForHandle | IndexHandle | Child[] | (() => Child);\n\nconst flattenChildren = (arr: unknown[]): unknown[] =>\n arr.reduce<unknown[]>((acc, val) => (Array.isArray(val) ? acc.concat(flattenChildren(val)) : acc.concat(val)), []);\n\n/** Resolve one static child to a node (client) / string (SSR); `null` when skippable. */\nconst toChildNode = (item: StaticChild): Node | string | null => {\n if (item == null) return null;\n if (item instanceof ElementBuilder) return item.build() as unknown as Node;\n if (typeof item === 'string') return isSSR ? item : document.createTextNode(item);\n return item as Node;\n};\n\n/**\n * Mount a reactive child region into `parent`. The getter may return **any**\n * `Child` — static nodes, arrays, a `For`/`Index` handle, or another getter —\n * so `Show`/`Switch` branches nest control flow freely.\n *\n * Client: a `start`/`end` comment pair brackets the region. Each run disposes the\n * previous run's scope (tearing down any nested `For`/`Index`/getter effects),\n * clears the bracketed nodes, then mounts the new output under a fresh\n * `createRoot` via `appendChildren` (uniform dispatch). SSR: evaluate once.\n */\nconst mountReactiveChildren = (parent: Node, getter: () => Child): void => {\n if (isSSR) {\n appendChildren(parent, [getter()]);\n return;\n }\n const start = document.createComment('');\n const end = document.createComment('');\n parent.appendChild(start);\n parent.appendChild(end);\n let disposeInner: (() => void) | null = null;\n const clear = (): void => {\n disposeInner?.();\n disposeInner = null;\n let n = start.nextSibling;\n while (n && n !== end) {\n const nextN = n.nextSibling;\n n.parentNode?.removeChild(n);\n n = nextN;\n }\n };\n createEffect(() => {\n const out = getter();\n clear();\n const frag = document.createDocumentFragment();\n disposeInner = createRoot((dispose) => {\n appendChildren(frag, [out]);\n return dispose;\n });\n end.parentNode?.insertBefore(frag, end);\n });\n onCleanup(() => disposeInner?.());\n};\n\n// ── For — keyed list reconciliation ──────────────────────────────────────────\n\nconst FOR_BRAND = '__ranFor';\n\n/** Options for {@link For}. */\nexport interface ForOptions<T> {\n /** Reactive source array. Read inside an effect, so the list updates on change. */\n each: () => readonly T[] | null | undefined;\n /** Stable identity per item — **must be unique** within the list. Reused across\n * updates to match old nodes to new items (that is what preserves DOM state). */\n key: (item: T, index: number) => string | number;\n /** Render one item to a single node. `index` is a **getter** (reactive): it\n * reflects the item's current position even after the list reorders. Runs once\n * per item (not on every list change) — drive per-item updates with signals. */\n render: (item: T, index: Getter<number>) => StaticChild;\n}\n\ninterface ForSpec<T> extends ForOptions<T> {\n [FOR_BRAND]: true;\n}\n\n/** Opaque handle returned by {@link For}; pass it straight to `children()`. */\nexport type ForHandle = { readonly [FOR_BRAND]: true };\n\n/**\n * Keyed list for `children()`. Unlike a plain getter child (which rebuilds the\n * whole region on every change), `For` matches items by `key` and **reuses their\n * DOM nodes** — only added/removed/moved items touch the DOM, so focus, scroll,\n * input values and in-flight transitions inside surviving rows are preserved.\n *\n * Ul().children(\n * For({\n * each: () => rows(),\n * key: (r) => r.id,\n * render: (r, i) => Li().text(() => `${i()}. ${r.title}`),\n * }),\n * );\n *\n * On SSR the list is rendered once as a static snapshot. Must be built inside a\n * `createRoot` so per-item scopes are disposed with the page.\n */\nexport function For<T>(options: ForOptions<T>): ForHandle {\n return { ...options, [FOR_BRAND]: true } as ForSpec<T>;\n}\n\nconst isForSpec = (v: unknown): v is ForSpec<unknown> =>\n typeof v === 'object' && v !== null && (v as Record<string, unknown>)[FOR_BRAND] === true;\n\n// ── Show — fine-grained conditional ──────────────────────────────────────────\n\n/** Options for {@link Show}. */\nexport interface ShowOptions<T> {\n /** Condition source. Truthy → `children`, falsy → `fallback`. */\n when: () => T;\n /** Built when `when` is truthy. May return any {@link Child} — a `For`/`Index`\n * list, a nested `Show`, etc. Receives an accessor to the narrowed value —\n * read it inside a binding (`.text(() => v())`) to update without rebuilding. */\n children: (value: () => NonNullable<T>) => Child;\n /** Built when `when` is falsy. Omitted → nothing is rendered. */\n fallback?: () => Child;\n}\n\n/**\n * Fine-grained conditional child. Unlike a raw getter child (which re-runs on\n * **every** change it reads), `Show` rebuilds the branch **only when the\n * truthiness of `when` flips** — the condition is memoized. Content inside a\n * branch updates through its own bindings, not by re-running `Show`. This is the\n * SwiftUI/Solid model: build once, toggle only when the branch actually changes.\n *\n * Show({\n * when: () => user(), // reads a signal\n * children: (u) => Div().text(() => u().name),\n * fallback: () => Div().text('Signed out'),\n * })\n *\n * `Show` returns a getter, so it is accepted anywhere `children()` takes a child.\n * Must be created inside a `createRoot` (it owns a memo + the branch effect).\n */\nexport function Show<T>(options: ShowOptions<T>): () => Child {\n const on = computed(() => Boolean(options.when()));\n const value = (): NonNullable<T> => options.when() as NonNullable<T>;\n return () => (on() ? options.children(value) : (options.fallback?.() ?? null));\n}\n\n// ── Switch / Match — fine-grained multi-branch ───────────────────────────────\n\n/** One branch of a {@link Switch}; build with {@link Match}. */\nexport interface MatchClause<T> {\n when: () => T;\n children: (value: () => NonNullable<T>) => Child;\n}\n\n/** Declare one `Switch` branch (identity helper — gives per-clause type inference). */\nexport function Match<T>(clause: MatchClause<T>): MatchClause<T> {\n return clause;\n}\n\n/** Options for {@link Switch}. */\nexport interface SwitchOptions {\n /** Branches, tried in order; the first with a truthy `when` renders. Clauses are\n * heterogeneous (each `Match<T>` carries its own `T`), hence `any` here. */\n children: MatchClause<any>[];\n /** Rendered when no branch matches. Omitted → nothing. */\n fallback?: () => Child;\n}\n\n/**\n * Fine-grained multi-branch conditional (the n-way `Show`). Renders the first\n * `Match` whose `when` is truthy, else `fallback`. Only the **index of the\n * winning branch** is memoized, so it rebuilds only when the active branch\n * changes — not on every change a `when` reads. Evaluation short-circuits at the\n * first match (later branches aren't subscribed while an earlier one wins).\n *\n * Switch({\n * fallback: () => Span().text('idle'),\n * children: [\n * Match({ when: () => status() === 'loading', children: () => Spinner() }),\n * Match({ when: () => error(), children: (e) => ErrorView(e) }),\n * ],\n * })\n */\nexport function Switch(options: SwitchOptions): () => Child {\n const clauses = options.children;\n const winner = computed(() => clauses.findIndex((c) => Boolean(c.when())));\n return () => {\n const i = winner();\n if (i < 0) return options.fallback?.() ?? null;\n const clause = clauses[i];\n return clause.children(() => clause.when() as NonNullable<unknown>);\n };\n}\n\ninterface ItemScope {\n node: Node | null;\n index: (value: number) => void;\n dispose: () => void;\n}\n\n/** Create one item's node inside its own reactive scope (independently disposable). */\nconst createItemScope = <T>(item: T, i: number, render: ForSpec<T>['render']): ItemScope => {\n const [index, setIndex] = signal(i);\n let node: Node | null = null;\n const dispose = createRoot((d) => {\n const built = toChildNode(render(item, index) as StaticChild);\n node = typeof built === 'string' ? document.createTextNode(built) : built;\n return d;\n });\n return { node, index: setIndex, dispose };\n};\n\n/** Reorder `nodes` to sit, in order, immediately after `anchor` — moving only\n * those out of place (reused nodes keep their identity, hence their state). */\nconst placeAfterAnchor = (anchor: ChildNode, nodes: Node[]): void => {\n const parent = anchor.parentNode;\n if (!parent) return;\n let ref: ChildNode = anchor;\n for (const node of nodes) {\n if (ref.nextSibling !== node) parent.insertBefore(node, ref.nextSibling);\n ref = node as ChildNode;\n }\n};\n\n/** Mount a keyed list: SSR renders once; client reconciles by key on each change. */\nconst mountKeyedList = <T>(parent: Node, spec: ForSpec<T>): void => {\n if (isSSR) {\n (spec.each() ?? []).forEach((item, i) => {\n const node = toChildNode(spec.render(item, () => i) as StaticChild);\n if (node != null) (parent as unknown as { appendChild(n: unknown): unknown }).appendChild(node);\n });\n return;\n }\n\n const anchor = document.createComment('');\n parent.appendChild(anchor);\n let scopes = new Map<string | number, ItemScope>();\n onCleanup(() => {\n for (const scope of scopes.values()) scope.dispose();\n scopes.clear();\n });\n\n createEffect(() => {\n const items = spec.each() ?? [];\n const next = new Map<string | number, ItemScope>();\n const ordered: Node[] = [];\n\n items.forEach((item, i) => {\n const k = spec.key(item, i);\n // Duplicate key: a Map can hold one node per key, so honouring the second\n // would orphan the first (untracked → never removed/disposed). Ignore the\n // duplicate — deterministic and leak-free — and warn in dev.\n if (next.has(k)) {\n if (isDev()) {\n console.error(\n `[ranview For] duplicate key \"${String(k)}\" — keys must be unique; ignoring the duplicate item.`,\n );\n }\n return;\n }\n let scope = scopes.get(k);\n if (scope) {\n scopes.delete(k); // claimed → leftovers below are removals\n scope.index(i); // keep the reactive index in sync after moves\n } else {\n scope = createItemScope(item, i, spec.render);\n }\n next.set(k, scope);\n if (scope.node) ordered.push(scope.node);\n });\n\n // Remove + dispose items no longer present.\n for (const scope of scopes.values()) {\n scope.node?.parentNode?.removeChild(scope.node);\n scope.dispose();\n }\n placeAfterAnchor(anchor, ordered);\n scopes = next;\n });\n};\n\n// ── Index — position-keyed list ──────────────────────────────────────────────\n\nconst INDEX_BRAND = '__ranIndex';\n\n/** Options for {@link Index}. */\nexport interface IndexOptions<T> {\n /** Reactive source array. */\n each: () => readonly T[] | null | undefined;\n /** Render the slot at a position. `item` is a **getter** (a signal): when the\n * value at this index changes, it updates in place — the node is not rebuilt.\n * `index` is a fixed number (the position never moves). */\n render: (item: () => T, index: number) => StaticChild;\n}\n\ninterface IndexSpec<T> extends IndexOptions<T> {\n [INDEX_BRAND]: true;\n}\n\n/** Opaque handle returned by {@link Index}; pass it straight to `children()`. */\nexport type IndexHandle = { readonly [INDEX_BRAND]: true };\n\n/**\n * Position-keyed list. The node at index `i` is **reused** across updates and its\n * `item` signal is updated in place — nodes never move. Use it when position is\n * the identity (primitive arrays, fixed-length rows). Use {@link For} instead\n * when items have a stable id and can reorder. SSR renders once.\n *\n * Ul().children(\n * Index({ each: () => nums(), render: (n, i) => Li().text(() => `${i}: ${n()}`) }),\n * );\n */\nexport function Index<T>(options: IndexOptions<T>): IndexHandle {\n return { ...options, [INDEX_BRAND]: true } as IndexSpec<T>;\n}\n\nconst isIndexSpec = (v: unknown): v is IndexSpec<unknown> =>\n typeof v === 'object' && v !== null && (v as Record<string, unknown>)[INDEX_BRAND] === true;\n\ninterface IndexSlot<T> {\n node: Node | null;\n setItem: (value: T) => void;\n dispose: () => void;\n}\n\nconst createIndexSlot = <T>(item: T, i: number, render: IndexSpec<T>['render']): IndexSlot<T> => {\n const [get, setItem] = signal(item);\n let node: Node | null = null;\n const dispose = createRoot((d) => {\n const built = toChildNode(render(get, i) as StaticChild);\n node = typeof built === 'string' ? document.createTextNode(built) : built;\n return d;\n });\n return { node, setItem, dispose };\n};\n\n/** Mount a position-keyed list: SSR renders once; client reuses nodes per index. */\nconst mountIndexList = <T>(parent: Node, spec: IndexSpec<T>): void => {\n if (isSSR) {\n (spec.each() ?? []).forEach((item, i) => {\n const node = toChildNode(spec.render(() => item, i) as StaticChild);\n if (node != null) (parent as unknown as { appendChild(n: unknown): unknown }).appendChild(node);\n });\n return;\n }\n\n const anchor = document.createComment('');\n parent.appendChild(anchor);\n let slots: IndexSlot<T>[] = [];\n onCleanup(() => {\n for (const s of slots) s.dispose();\n slots = [];\n });\n\n createEffect(() => {\n const items = spec.each() ?? [];\n // Reuse slot 0..n: update its signal (no-op if unchanged) — node stays put.\n for (let i = 0; i < items.length; i++) {\n if (i < slots.length) slots[i].setItem(items[i]);\n else slots.push(createIndexSlot(items[i], i, spec.render));\n }\n // Trailing slots (list shrank) → remove + dispose.\n for (let i = slots.length - 1; i >= items.length; i--) {\n const { node } = slots[i];\n node?.parentNode?.removeChild(node);\n slots[i].dispose();\n slots.pop();\n }\n placeAfterAnchor(\n anchor,\n slots.map((s) => s.node).filter((n): n is Node => n != null),\n );\n });\n};\n\n/** Append mixed static / array / reactive-getter / keyed-list children to a parent node. */\nconst appendChildren = (parent: Node, items: Child[]): void => {\n flattenChildren(items).forEach((item) => {\n if (isForSpec(item)) {\n mountKeyedList(parent, item);\n return;\n }\n if (isIndexSpec(item)) {\n mountIndexList(parent, item);\n return;\n }\n if (typeof item === 'function') {\n mountReactiveChildren(parent, item as () => Child);\n return;\n }\n const node = toChildNode(item as StaticChild);\n if (node != null) (parent as unknown as { appendChild(n: unknown): unknown }).appendChild(node);\n });\n};\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\n/**\n * Tags that exist only in SVG, so a builder can create them correctly without\n * being told which namespace they belong to.\n *\n * Deliberately excludes the tags SVG shares with HTML -- `a`, `script`,\n * `style`, `title` -- since guessing wrong there would silently produce the\n * wrong kind of element for the far more common HTML case. Reach for `Svg()`\n * when one of those is meant as SVG.\n */\nconst SVG_ONLY_TAGS = new Set([\n 'svg',\n 'circle',\n 'clipPath',\n 'defs',\n 'desc',\n 'ellipse',\n 'feBlend',\n 'feColorMatrix',\n 'feComposite',\n 'feDropShadow',\n 'feFlood',\n 'feGaussianBlur',\n 'feMerge',\n 'feMergeNode',\n 'feOffset',\n 'filter',\n 'foreignObject',\n 'g',\n 'image',\n 'line',\n 'linearGradient',\n 'marker',\n 'mask',\n 'path',\n 'pattern',\n 'polygon',\n 'polyline',\n 'radialGradient',\n 'rect',\n 'stop',\n 'switch',\n 'symbol',\n 'text',\n 'textPath',\n 'tspan',\n 'use',\n]);\n\n/**\n * True in a dev build, without needing `vite/client` in the consumer's tsconfig.\n *\n * This package exports TypeScript source to workspace consumers, so its types are checked\n * in *their* project. A bare `import.meta.env.DEV` therefore fails to compile anywhere\n * that has not declared Vite's ambient types — which is every consumer that does not\n * happen to use Vite.\n */\nconst isDev = (): boolean => (import.meta as { env?: { DEV?: boolean } }).env?.DEV === true;\n\nexport class ElementBuilder<T extends HTMLElement = HTMLElement> {\n private el: T;\n\n /**\n * @param tag element name\n * @param namespace element namespace; inferred for SVG-only tags, and forced\n * by `Svg()` for the tags SVG shares with HTML.\n *\n * The namespace matters more than it looks. `document.createElement('svg')`\n * yields an `HTMLUnknownElement`: the browser does not render it as SVG, and\n * -- because HTML lowercases attribute names -- `viewBox` lands as `viewbox`,\n * which SVG reads case-sensitively and therefore ignores. An icon built that\n * way is invisible, with markup that looks right in a serialized page.\n */\n constructor(tag: string, namespace?: string) {\n const ns = namespace ?? (SVG_ONLY_TAGS.has(tag) ? SVG_NAMESPACE : undefined);\n this.el = (isSSR\n ? new HTMLElementMock(tag)\n : ns\n ? document.createElementNS(ns, tag)\n : document.createElement(tag)) as unknown as T;\n }\n\n id(value: string): this {\n this.el.setAttribute('id', value);\n return this;\n }\n\n /**\n * Apply a value now, or bind it reactively when a getter is passed.\n * A getter creates an effect owned by the current reactive scope (createRoot),\n * so the binding is disposed automatically when that scope is torn down.\n */\n private bind<V>(value: V | Getter<V>, apply: (v: V) => void): void {\n if (typeof value === 'function') createEffect(() => apply((value as Getter<V>)()));\n else apply(value);\n }\n\n /**\n * Sets the element's class.\n *\n * Written through `setAttribute` rather than `className`. On an SVG element `className`\n * is a read-only `SVGAnimatedString`, so assigning to it throws\n * `Cannot set property className of [object SVGElement] which has only a getter` — which,\n * happening inside a custom element's constructor, leaves the element un-upgraded with\n * no shadow root rather than reporting a styling problem. `setAttribute` is equivalent\n * for HTML elements and correct for both.\n */\n class(name: string | Getter<string>): this {\n this.bind(name, (n) => {\n if (isSSR) (this.el as unknown as HTMLElementMock).attributes.set('class', n);\n else this.el.setAttribute('class', n);\n });\n return this;\n }\n\n addClass(...names: string[]): this {\n names.forEach((n) => this.el.classList.add(n));\n return this;\n }\n\n removeClass(...names: string[]): this {\n names.forEach((n) => this.el.classList.remove(n));\n return this;\n }\n\n attr(name: string, value: string | Getter<string>): this {\n this.bind(value, (v) => this.el.setAttribute(name, v));\n return this;\n }\n\n attrs(values: Record<string, string | number | boolean | null | undefined>): this {\n Object.entries(values).forEach(([name, value]) => {\n if (value == null) return;\n this.el.setAttribute(name, String(value));\n });\n return this;\n }\n\n boolAttr(name: string, value: boolean | Getter<boolean>, enabledValue = ''): this {\n this.bind(value, (v) => {\n if (v) this.el.setAttribute(name, enabledValue);\n else this.el.removeAttribute(name);\n });\n return this;\n }\n\n part(value: string | Getter<string>): this {\n return this.attr('part', value);\n }\n\n data(key: string, value: string | Getter<string>): this {\n return this.attr(`data-${key}`, value);\n }\n\n style(keyOrMap: string | Record<string, string>, value?: string | Getter<string>): this {\n if (typeof keyOrMap === 'string') {\n this.bind(value ?? '', (v) => this.el.style.setProperty(keyOrMap, v));\n } else {\n Object.entries(keyOrMap).forEach(([k, v]) => this.el.style.setProperty(k, v));\n }\n return this;\n }\n\n cssVar(name: string, value: string | Getter<string>): this {\n const property = name.startsWith('--') ? name : `--${name}`;\n return this.style(property, value);\n }\n\n aria(key: string, value: string | Getter<string>): this {\n return this.attr(`aria-${key}`, value);\n }\n\n role(value: string | Getter<string>): this {\n return this.attr('role', value);\n }\n\n tabIndex(value: number): this {\n return this.attr('tabindex', String(value));\n }\n\n label(value: string | Getter<string>): this {\n return this.aria('label', value);\n }\n\n labelledBy(id: string | Getter<string>): this {\n return this.aria('labelledby', id);\n }\n\n describedBy(id: string | Getter<string>): this {\n return this.aria('describedby', id);\n }\n\n ariaHidden(hidden = true): this {\n return this.aria('hidden', String(hidden));\n }\n\n /**\n * Permanent build-time listener — tied to the element's lifetime.\n * Use for internal shadow DOM elements created in the constructor.\n */\n on<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: T, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ): this {\n this.el.addEventListener(type, listener as EventListener, options);\n return this;\n }\n\n /**\n * Lifecycle-managed listener — registered into an EventManager.\n * Use in connectedCallback when building elements that need cleanup on disconnect.\n * Call manager.abort() in disconnectedCallback to remove all listeners at once.\n */\n listen<K extends keyof HTMLElementEventMap>(\n manager: EventManager,\n type: K,\n handler: (this: T, ev: HTMLElementEventMap[K]) => any,\n options?: Omit<AddEventListenerOptions, 'signal'>,\n ): this {\n manager.on(this.el, type, handler as EventListener, options);\n return this;\n }\n\n /**\n * Delegated listener — the built element acts as the parent container.\n * Fires handler only when the event originates from a descendant matching selector.\n * Registered into an EventManager so it is cleaned up with manager.abort().\n *\n * Div().class('list')\n * .children(...)\n * .delegate(scope, '.item', 'click', (ev, item) => handleItem(item))\n * .build();\n */\n delegate<K extends keyof HTMLElementEventMap>(\n manager: EventManager,\n selector: string,\n type: K,\n handler: (ev: HTMLElementEventMap[K], target: Element) => void,\n options?: Omit<AddEventListenerOptions, 'signal'>,\n ): this {\n manager.delegate(this.el, selector, type, handler, options);\n return this;\n }\n\n children(...items: Child[]): this {\n appendChildren(this.el, items);\n return this;\n }\n\n replaceChildren(...items: Child[]): this {\n if (isSSR) {\n const mock = this.el as unknown as HTMLElementMock | DocumentFragmentMock;\n mock.childrenList = [];\n } else {\n this.el.replaceChildren();\n }\n return this.children(...items);\n }\n\n text(value: string | Getter<string>): this {\n this.bind(value, (v) => {\n this.el.textContent = v;\n });\n return this;\n }\n\n /**\n * Set the element's content from a string of markup, without escaping it.\n *\n * The counterpart to `text()`, and named for the difference: `text()` escapes,\n * this does not. Everything handed here is parsed as HTML, so anything that\n * came from outside the program must be sanitized before it arrives.\n *\n * It exists because some content simply *is* HTML, and a builder without this\n * cannot express it at all: markdown rendered for a page, a fragment assembled\n * somewhere else, a translated string that carries its own emphasis\n * (`Local-first · <b>open source</b>`). That gap is why a static-site\n * generator with markdown in it could not be written against this API and had\n * to concatenate strings instead.\n *\n * Like `textContent`, this replaces whatever content the element had --\n * including anything set by `text()` or `children()` before it.\n */\n unsafeHtml(value: string | Getter<string>): this {\n this.bind(value, (v) => {\n this.el.innerHTML = v;\n });\n return this;\n }\n\n ref(holder: Ref<T>): this {\n holder.current = this.el;\n return this;\n }\n\n shadow(options: ShadowRootInit = { mode: 'closed' }): ShadowBuilder<T> {\n const root = this.el.attachShadow(options);\n return new ShadowBuilder<T>(this.el, root, options);\n }\n\n build(): T {\n return this.el;\n }\n\n serialize(): string {\n if (isSSR) return (this.el as unknown as HTMLElementMock).serialize();\n const outer = document.createElement('div');\n outer.appendChild(this.el.cloneNode(true));\n return outer.innerHTML;\n }\n}\n\nexport class ShadowBuilder<T extends HTMLElement = HTMLElement> {\n private root: ShadowRoot;\n private hostEl: T;\n\n /**\n * `options` is taken and discarded: `attachShadow` has already consumed it by the time\n * a builder exists, and nothing ever read the copy this class used to keep. The\n * parameter stays so the call signature is unchanged.\n */\n constructor(host: T, root: ShadowRoot, _options: ShadowRootInit) {\n this.hostEl = host;\n this.root = root;\n }\n\n children(...items: Child[]): this {\n appendChildren(this.root, items);\n return this;\n }\n\n adoptSheet(...sheets: CSSStyleSheet[]): this {\n this.root.adoptedStyleSheets = [...this.root.adoptedStyleSheets, ...sheets];\n return this;\n }\n\n css(cssText: string): this {\n if (isSSR) {\n (this.root as unknown as ShadowRootMock).adoptedStyleSheets.push(cssText);\n return this;\n }\n if (typeof CSSStyleSheet !== 'undefined') {\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(cssText);\n this.root.adoptedStyleSheets = [...this.root.adoptedStyleSheets, sheet];\n return this;\n } catch {\n // Fallback to style injection when adoptedStyleSheets is unavailable.\n }\n }\n const style = document.createElement('style');\n style.textContent = cssText;\n this.root.appendChild(style);\n return this;\n }\n\n done(): { host: T; shadow: ShadowRoot } {\n return { host: this.hostEl, shadow: this.root };\n }\n\n serialize(): string {\n if (isSSR) return (this.root as unknown as ShadowRootMock).serialize();\n return (this.root as ShadowRoot).innerHTML;\n }\n}\n","import { ElementBuilder, SVG_NAMESPACE } from './core';\n\nexport const View = <T extends HTMLElement = HTMLElement>(tag: string): ElementBuilder<T> => new ElementBuilder<T>(tag);\nexport const Div = (): ElementBuilder<HTMLDivElement> => View<HTMLDivElement>('div');\n\n/**\n * An element in the SVG namespace.\n *\n * `View()` already infers it for tags that only exist in SVG (`path`, `circle`,\n * `g`, …), so this is for the ones SVG shares with HTML -- `a`, `script`,\n * `style`, `title` -- where guessing would break the commoner HTML case.\n *\n * ```ts\n * View('svg').attrs({ viewBox: '0 0 16 16' }).children(\n * Svg('a').attr('href', '#').children(View('path').attr('d', 'M1 1').build()).build(),\n * );\n * ```\n */\nexport const Svg = <T extends HTMLElement = HTMLElement>(tag: string): ElementBuilder<T> =>\n new ElementBuilder<T>(tag, SVG_NAMESPACE);\nexport const Span = (): ElementBuilder<HTMLSpanElement> => View<HTMLSpanElement>('span');\nexport const Slot = (): ElementBuilder<HTMLSlotElement> => View<HTMLSlotElement>('slot');\nexport const ButtonBuilder = (): ElementBuilder<HTMLButtonElement> => View<HTMLButtonElement>('button');\nexport const InputBuilder = (): ElementBuilder<HTMLInputElement> => View<HTMLInputElement>('input');\nexport const Style = (): ElementBuilder<HTMLStyleElement> => View<HTMLStyleElement>('style');\nexport const Label = (): ElementBuilder<HTMLLabelElement> => View<HTMLLabelElement>('label');\nexport const Ul = (): ElementBuilder<HTMLUListElement> => View<HTMLUListElement>('ul');\nexport const Li = (): ElementBuilder<HTMLLIElement> => View<HTMLLIElement>('li');\nexport const Section = (): ElementBuilder<HTMLElement> => View<HTMLElement>('section');\nexport const Article = (): ElementBuilder<HTMLElement> => View<HTMLElement>('article');\nexport const Nav = (): ElementBuilder<HTMLElement> => View<HTMLElement>('nav');\nexport const Header = (): ElementBuilder<HTMLElement> => View<HTMLElement>('header');\nexport const Footer = (): ElementBuilder<HTMLElement> => View<HTMLElement>('footer');\nexport const Main = (): ElementBuilder<HTMLElement> => View<HTMLElement>('main');\n\nexport const DeclarativeShadow = (\n mode: 'open' | 'closed' = 'open',\n delegatesFocus = false,\n): ElementBuilder<HTMLTemplateElement> => {\n const tpl = View<HTMLTemplateElement>('template');\n tpl.attr('shadowrootmode', mode);\n if (delegatesFocus) tpl.attr('shadowrootdelegatesfocus', '');\n return tpl;\n};\n"],"mappings":"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"}
|
package/dist/mocks-DvJQ7jGP.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
let e=require("ranuts/utils");function t(t){return(0,e.escapeHtml)(t)}var n=e=>typeof e!=`string`&&`tagName`in e&&`childrenList`in e,r=`.#[`;function i(e){let t=[],n=0,i=/^[a-zA-Z][\w-]*/.exec(e);for(i!==null&&(t.push(i[0]),n=i[0].length);n<e.length;){let i=e[n];if(i===`[`){let r=e.indexOf(`]`,n+1);if(r===-1)return null;t.push(e.slice(n,r+1)),n=r+1;continue}if(i!==`.`&&i!==`#`)return null;let a=n+1;for(;a<e.length&&!r.includes(e[a]);)a+=1;if(a===n+1)return null;t.push(e.slice(n,a)),n=a}return t.length>0?t:null}var a=(e,t)=>{let n=t;if(n.startsWith(`.`))return e.classList.contains(n.slice(1));if(n.startsWith(`#`))return e.getAttribute(`id`)===n.slice(1);if(n.startsWith(`[`)&&n.endsWith(`]`)){let[t,r]=n.slice(1,-1).trim().split(`=`),i=(t||``).trim();if(!i)return!1;if(r==null)return e.hasAttribute(i);let a=r.trim().replace(/^['"]|['"]$/g,``);return e.getAttribute(i)===a}return e.tagName===n.toLowerCase()},o=(e,t)=>{let n=t.trim();if(!n)return!1;let r=i(n);return r!==null&&r.every(t=>a(e,t))},s=(e,t,r)=>{for(let i of e)n(i)&&(o(i,t)&&r.push(i),s(i.childrenList,t,r))},c=class{childrenList=[];appendChild(e){return this.childrenList.push(e),e}serialize(){return this.childrenList.map(t=>typeof t==`string`?(0,e.escapeHtml)(t):t.serialize()).join(``)}querySelector(e){let t=this.querySelectorAll(e);return t.length>0?t[0]:null}querySelectorAll(e){let t=[];return s(this.childrenList,e,t),t}},l=new Map,u=class{tagName;attributes=new Map;_inlineStyles=null;get inlineStyles(){return this._inlineStyles??l}childrenList=[];shadowRoot=null;_textContent=null;get textContent(){return this._textContent}set textContent(e){this._textContent=e}content;eventListeners=null;_style=null;_classList=null;get style(){return this._style??={setProperty:(e,t)=>{let n=e.startsWith(`--`)?e:e.replace(/([A-Z])/g,`-$1`).toLowerCase();(this._inlineStyles??=new Map).set(n,t)},removeProperty:e=>this._inlineStyles?.delete(e)??!1}}get classList(){return this._classList??={add:(...e)=>{let t=this.attributes.get(`class`)||``,n=new Set([...t.split(` `).filter(Boolean),...e]);this.attributes.set(`class`,Array.from(n).join(` `))},remove:(...e)=>{let t=(this.attributes.get(`class`)||``).split(` `).filter(t=>!e.includes(t));this.attributes.set(`class`,t.join(` `))},toggle:e=>{this.classList.contains(e)?this.classList.remove(e):this.classList.add(e)},contains:e=>(this.attributes.get(`class`)||``).split(` `).includes(e)}}_innerHTML=``;get innerHTML(){return this._textContent===null?this._innerHTML:(0,e.escapeHtml)(this._textContent)}set innerHTML(e){this._innerHTML=e,this._textContent=null,this.tagName===`template`&&this.content&&(this.content.childrenList=[e])}constructor(e=`div`){this.tagName=e.toLowerCase(),this.tagName===`template`&&(this.content=new c)}get hidden(){return this.hasAttribute(`hidden`)}set hidden(e){e?this.setAttribute(`hidden`,``):this.removeAttribute(`hidden`)}setAttribute(e,t){this.attributes.set(e,t)}getAttribute(e){return this.attributes.get(e)||null}removeAttribute(e){this.attributes.delete(e)}hasAttribute(e){return this.attributes.has(e)}appendChild(e){return this.childrenList.push(e),e}attachShadow(e){return this.shadowRoot=new d(this,e),this.shadowRoot}querySelector(e){let t=this.querySelectorAll(e);return t.length>0?t[0]:null}querySelectorAll(e){let t=[];return s(this.childrenList,e,t),t}addEventListener(e,t){let n=this.eventListeners??=new Map,r=n.get(e)||new Set;r.add(t),n.set(e,r)}removeEventListener(e,t){let n=this.eventListeners?.get(e);n&&(n.delete(t),n.size===0&&this.eventListeners?.delete(e))}dispatchEvent(e){let t=this.eventListeners?.get(e.type);if(!t||t.size===0)return!0;for(let n of t)typeof n==`function`?n.call(this,e):n.handleEvent(e);return!e.defaultPrevented}serialize(n){typeof this._preSerialize==`function`&&this._preSerialize();let r=n||this._ssrTag||this.tagName,i=Array.from(this.attributes.entries()).map(([e,n])=>` ${e}="${t(n)}"`).join(``),a=this._inlineStyles?Array.from(this._inlineStyles.entries()).map(([e,t])=>`${e}:${t}`).join(`;`):``,o=a?` style="${t(a)}"`:``,s=``;return this.shadowRoot&&(s+=this.shadowRoot.serialize()),this.textContent===null?this._innerHTML?s+=this._innerHTML:s+=this.childrenList.map(t=>typeof t==`string`?(0,e.escapeHtml)(t):(t instanceof c,t.serialize())).join(``):s+=(0,e.escapeHtml)(this.textContent),[`area`,`base`,`br`,`col`,`embed`,`hr`,`img`,`input`,`link`,`meta`,`param`,`source`,`track`,`wbr`].includes(r)&&!s?`<${r}${i}${o} />`:`<${r}${i}${o}>${s}</${r}>`}},d=class{host;options;childrenList=[];adoptedStyleSheets=[];constructor(e,t){this.host=e,this.options=t}appendChild(e){return this.childrenList.push(e),e}querySelector(e){let t=this.querySelectorAll(e);return t.length>0?t[0]:null}querySelectorAll(e){let t=[];return s(this.childrenList,e,t),t}serialize(){return`<template shadowrootmode="${this.options.mode}"${this.options.delegatesFocus?` shadowrootdelegatesfocus`:``}>${this.adoptedStyleSheets.map(t=>`<style>${(0,e.escapeHtml)(t)}</style>`).join(``)}${this.childrenList.map(t=>typeof t==`string`?(0,e.escapeHtml)(t):(t instanceof c,t.serialize())).join(``)}</template>`}};Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return c}});
|
|
2
|
-
//# sourceMappingURL=mocks-DvJQ7jGP.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mocks-DvJQ7jGP.cjs","names":[],"sources":["../src/escape.ts","../src/selectors.ts","../src/mocks.ts"],"sourcesContent":["// HTML 转义。实现住在 ranuts —— 它先用一个正则探测有没有特殊字符,没有就原样返回,\n// 省掉了「无论如何都跑五次 replace」的分配开销;ranview 这边只保留导出名。\n//\n// 一处可见差异:单引号现在转义成 `'` 而不是 `'`。两者是同一个字符的\n// 等价实体,渲染结果相同,但 SSR 序列化出来的 HTML 字节会变,比对快照时会看到。\nimport { escapeHtml } from 'ranuts/utils';\n\nexport { escapeHtml };\n\n/**\n * 属性值转义。当前与 `escapeHtml` 完全一致:单双引号都会被转义,所以带引号、\n * 不带引号的属性值都安全。保留独立的名字是为了在调用点标出意图。\n */\nexport function escapeHtmlAttribute(unsafe: string): string {\n return escapeHtml(unsafe);\n}\n","import type { DocumentFragmentMock, HTMLElementMock } from './mocks';\n\nexport type MockNode = HTMLElementMock | DocumentFragmentMock | string;\n\nconst isHTMLElementMockNode = (node: MockNode): node is HTMLElementMock => {\n if (typeof node === 'string') return false;\n return 'tagName' in node && 'childrenList' in node;\n};\n\n/** Characters that begin a new qualifier, and so end the one before it. */\nconst QUALIFIER_START = '.#[';\n\n/**\n * Splits a compound selector into its parts.\n *\n * Hand-written rather than a regular expression: the obvious pattern for the attribute\n * form, `\\[[^\\]]*\\]`, backtracks across every position on an input like `[[[[[`, which is\n * polynomial in the selector's length. This walks the string once. A selector is not\n * usually attacker-controlled, but `querySelector` is a library entry point and the caller\n * chooses what reaches it.\n *\n * @param selector The trimmed, non-empty selector.\n * @returns Its parts, or null when the selector uses syntax this engine does not implement.\n */\nfunction splitCompound(selector: string): string[] | null {\n const parts: string[] = [];\n let at = 0;\n\n const tag = /^[a-zA-Z][\\w-]*/.exec(selector);\n if (tag !== null) {\n parts.push(tag[0]);\n at = tag[0].length;\n }\n\n while (at < selector.length) {\n const start = selector[at];\n if (start === '[') {\n const close = selector.indexOf(']', at + 1);\n if (close === -1) return null;\n parts.push(selector.slice(at, close + 1));\n at = close + 1;\n continue;\n }\n if (start !== '.' && start !== '#') return null;\n let end = at + 1;\n while (end < selector.length && !QUALIFIER_START.includes(selector[end])) end += 1;\n // A bare `.` or `#` qualifies nothing.\n if (end === at + 1) return null;\n parts.push(selector.slice(at, end));\n at = end;\n }\n\n return parts.length > 0 ? parts : null;\n}\n\n/**\n * Matches one simple selector — a single tag, class, id, or attribute test.\n *\n * @param node The node to test.\n * @param selector One simple selector, already trimmed and non-empty.\n * @returns Whether the node matches.\n */\nconst matchSimple = (node: HTMLElementMock, selector: string): boolean => {\n const trimmed = selector;\n\n if (trimmed.startsWith('.')) {\n return node.classList.contains(trimmed.slice(1));\n }\n if (trimmed.startsWith('#')) {\n return node.getAttribute('id') === trimmed.slice(1);\n }\n if (trimmed.startsWith('[') && trimmed.endsWith(']')) {\n const content = trimmed.slice(1, -1).trim();\n const [rawKey, rawValue] = content.split('=');\n const key = (rawKey || '').trim();\n if (!key) return false;\n if (rawValue == null) return node.hasAttribute(key);\n const value = rawValue.trim().replace(/^['\"]|['\"]$/g, '');\n return node.getAttribute(key) === value;\n }\n return node.tagName === trimmed.toLowerCase();\n};\n\n/**\n * Matches a compound selector such as `slot[name=\"footer\"]` or `div.row#first`.\n *\n * Every qualifier must match. Supporting only one at a time meant a selector like\n * `slot[name=\"footer\"]` fell through to the tag comparison and matched nothing, which broke\n * server rendering for any component that located a node that way — silently, because a\n * `querySelector` returning null there is usually asserted away with `!`.\n *\n * Anything the tokenizer cannot consume in full — a descendant combinator, `>`, a\n * pseudo-class — returns false rather than matching on the part it understood. A selector\n * this engine does not implement should find nothing, not something arbitrary.\n *\n * @param node The node to test.\n * @param selector The selector to match.\n * @returns Whether the node matches every part of the selector.\n */\nexport const matchSelector = (node: HTMLElementMock, selector: string): boolean => {\n const trimmed = selector.trim();\n if (!trimmed) return false;\n const parts = splitCompound(trimmed);\n if (parts === null) return false;\n return parts.every((part) => matchSimple(node, part));\n};\n\nexport const collectMatches = (nodes: MockNode[], selector: string, result: HTMLElementMock[]): void => {\n for (const child of nodes) {\n if (!isHTMLElementMockNode(child)) continue;\n if (matchSelector(child, selector)) {\n result.push(child);\n }\n collectMatches(child.childrenList as MockNode[], selector, result);\n }\n};\n","import { escapeHtml, escapeHtmlAttribute } from './escape';\nimport type { MockNode } from './selectors';\nimport { collectMatches } from './selectors';\n\n/**\n * A minimal mock of DocumentFragment for SSR environments.\n */\nexport class DocumentFragmentMock {\n public childrenList: (HTMLElementMock | string)[] = [];\n\n appendChild(node: any): any {\n this.childrenList.push(node);\n return node;\n }\n\n serialize(): string {\n return this.childrenList\n .map((child) => (typeof child === 'string' ? escapeHtml(child) : child.serialize()))\n .join('');\n }\n\n querySelector(selector: string): HTMLElementMock | null {\n const list = this.querySelectorAll(selector);\n return list.length > 0 ? list[0] : null;\n }\n\n querySelectorAll(selector: string): HTMLElementMock[] {\n const result: HTMLElementMock[] = [];\n collectMatches(this.childrenList as MockNode[], selector, result);\n return result;\n }\n}\n\n/**\n * A robust mock of HTMLElement for SSR environments.\n */\n/** Handed back by `inlineStyles` when nothing was ever set. Never mutated. */\nconst EMPTY_STRING_MAP: ReadonlyMap<string, string> = new Map<string, string>();\n\nexport class HTMLElementMock {\n public tagName: string;\n public attributes: Map<string, string> = new Map<string, string>();\n /**\n * Created on first write, like the two façades below.\n *\n * Only `style.setProperty` fills this, and `serialize()` is the only reader, so a node\n * that never sets an inline style never needs the Map.\n */\n private _inlineStyles: Map<string, string> | null = null;\n\n /**\n * The styles actually set, or an empty map — reading never allocates.\n *\n * Read-only because the shared empty map is handed to every element that never set a\n * style; writing through it would leak one element's styles into all of them. Set\n * styles with `style.setProperty`, which is what a browser requires anyway.\n */\n get inlineStyles(): ReadonlyMap<string, string> {\n return this._inlineStyles ?? EMPTY_STRING_MAP;\n }\n public childrenList: (HTMLElementMock | string | DocumentFragmentMock)[] = [];\n public shadowRoot: ShadowRootMock | null = null;\n private _textContent: string | null = null;\n\n /**\n * Setting either `textContent` or `innerHTML` replaces the element's content\n * in a real DOM, so the last one written wins. This mock used to keep both\n * and let `serialize()` prefer the text, which meant\n * `el.textContent = 'a'; el.innerHTML = '<b/>'` rendered `a` here and `<b/>`\n * in a browser -- the kind of difference that only surfaces once the same\n * template is rendered in both, which is exactly what a static-site generator\n * under vitest does.\n */\n get textContent(): string | null {\n return this._textContent;\n }\n\n set textContent(value: string | null) {\n this._textContent = value;\n }\n public content?: DocumentFragmentMock;\n private eventListeners: Map<string, Set<EventListenerOrEventListenerObject>> | null = null;\n\n /*\n * `style` and `classList` are built on first access, not in the constructor.\n *\n * They were instance fields holding object literals of arrow functions. An arrow\n * function closes over `this`, so it cannot live on the prototype — every element\n * allocated two objects and six closures whether or not anything ever touched them,\n * and nearly nothing does: a generated page is overwhelmingly elements with a class\n * attribute and a text child.\n *\n * Measured on `new HTMLElementMock('div')` plus two attributes: 1.466 µs before,\n * 0.043 µs after. That is 97% of the cost of building a node, and it was being paid\n * for capabilities the node never used. A getter is transparent to every reader —\n * `el.style.setProperty(…)` is unchanged — and nothing enumerates an element's own\n * properties, which is the one thing that would have noticed the difference.\n */\n private _style: HTMLElementMock['style'] | null = null;\n private _classList: HTMLElementMock['classList'] | null = null;\n\n get style(): { setProperty(k: string, v: string): void; removeProperty(k: string): boolean } {\n return (this._style ??= {\n setProperty: (k: string, v: string): void => {\n const prop = k.startsWith('--') ? k : k.replace(/([A-Z])/g, '-$1').toLowerCase();\n (this._inlineStyles ??= new Map<string, string>()).set(prop, v);\n },\n removeProperty: (k: string): boolean => this._inlineStyles?.delete(k) ?? false,\n });\n }\n\n get classList(): {\n add(...names: string[]): void;\n remove(...names: string[]): void;\n toggle(name: string): void;\n contains(name: string): boolean;\n } {\n return (this._classList ??= {\n add: (...names: string[]): void => {\n const existing = this.attributes.get('class') || '';\n const list = new Set([...existing.split(' ').filter(Boolean), ...names]);\n this.attributes.set('class', Array.from(list).join(' '));\n },\n remove: (...names: string[]): void => {\n const existing = this.attributes.get('class') || '';\n const list = existing.split(' ').filter((c) => !names.includes(c));\n this.attributes.set('class', list.join(' '));\n },\n toggle: (name: string): void => {\n if (this.classList.contains(name)) this.classList.remove(name);\n else this.classList.add(name);\n },\n contains: (name: string): boolean => (this.attributes.get('class') || '').split(' ').includes(name),\n });\n }\n\n private _innerHTML: string = '';\n\n get innerHTML(): string {\n // Reading it back reflects whichever was written last, as a browser does:\n // after `el.textContent = 'a'`, `el.innerHTML` is `a`, not the markup that\n // was there before.\n if (this._textContent !== null) return escapeHtml(this._textContent);\n return this._innerHTML;\n }\n\n set innerHTML(value: string) {\n this._innerHTML = value;\n this._textContent = null;\n if (this.tagName === 'template' && this.content) {\n this.content.childrenList = [value];\n }\n }\n\n constructor(tag: string = 'div') {\n this.tagName = tag.toLowerCase();\n if (this.tagName === 'template') {\n this.content = new DocumentFragmentMock();\n }\n }\n\n get hidden(): boolean {\n return this.hasAttribute('hidden');\n }\n\n set hidden(val: boolean) {\n if (val) this.setAttribute('hidden', '');\n else this.removeAttribute('hidden');\n }\n\n setAttribute(k: string, v: string): void {\n this.attributes.set(k, v);\n }\n\n getAttribute(k: string): string | null {\n return this.attributes.get(k) || null;\n }\n\n removeAttribute(k: string): void {\n this.attributes.delete(k);\n }\n\n hasAttribute(k: string): boolean {\n return this.attributes.has(k);\n }\n\n appendChild(node: any): any {\n this.childrenList.push(node);\n return node;\n }\n\n attachShadow(options: ShadowRootInit): ShadowRoot {\n this.shadowRoot = new ShadowRootMock(this, options);\n return this.shadowRoot as unknown as ShadowRoot;\n }\n\n querySelector(selector: string): HTMLElementMock | null {\n const result = this.querySelectorAll(selector);\n return result.length > 0 ? result[0] : null;\n }\n\n querySelectorAll(selector: string): HTMLElementMock[] {\n const result: HTMLElementMock[] = [];\n collectMatches(this.childrenList as MockNode[], selector, result);\n return result;\n }\n\n addEventListener(type: string, listener: EventListenerOrEventListenerObject): void {\n const listeners = (this.eventListeners ??= new Map<string, Set<EventListenerOrEventListenerObject>>());\n const bucket = listeners.get(type) || new Set<EventListenerOrEventListenerObject>();\n bucket.add(listener);\n listeners.set(type, bucket);\n }\n\n removeEventListener(type: string, listener: EventListenerOrEventListenerObject): void {\n const bucket = this.eventListeners?.get(type);\n if (!bucket) return;\n bucket.delete(listener);\n if (bucket.size === 0) {\n this.eventListeners?.delete(type);\n }\n }\n\n dispatchEvent(event: Event): boolean {\n const listeners = this.eventListeners?.get(event.type);\n if (!listeners || listeners.size === 0) return true;\n for (const listener of listeners) {\n if (typeof listener === 'function') {\n listener.call(this, event);\n } else {\n listener.handleEvent(event);\n }\n }\n return !event.defaultPrevented;\n }\n\n serialize(tagNameOverride?: string): string {\n if (typeof (this as any)._preSerialize === 'function') {\n (this as any)._preSerialize();\n }\n const tagName =\n tagNameOverride || ((this as Record<string, unknown>)._ssrTag as string | undefined) || this.tagName;\n const attrs = Array.from(this.attributes.entries())\n .map(([k, v]) => ` ${k}=\"${escapeHtmlAttribute(v)}\"`)\n .join('');\n\n const styleString = this._inlineStyles\n ? Array.from(this._inlineStyles.entries())\n .map(([k, v]) => `${k}:${v}`)\n .join(';')\n : '';\n\n const styleAttr = styleString ? ` style=\"${escapeHtmlAttribute(styleString)}\"` : '';\n\n let innerHTML = '';\n if (this.shadowRoot) innerHTML += this.shadowRoot.serialize();\n\n if (this.textContent !== null) {\n innerHTML += escapeHtml(this.textContent);\n } else if (this._innerHTML) {\n innerHTML += this._innerHTML;\n } else {\n innerHTML += this.childrenList\n .map((child) => {\n if (typeof child === 'string') return escapeHtml(child);\n if (child instanceof DocumentFragmentMock) return child.serialize();\n return child.serialize();\n })\n .join('');\n }\n\n const selfClosing = [\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n ];\n if (selfClosing.includes(tagName) && !innerHTML) {\n return `<${tagName}${attrs}${styleAttr} />`;\n }\n\n return `<${tagName}${attrs}${styleAttr}>${innerHTML}</${tagName}>`;\n }\n}\n\nexport class ShadowRootMock {\n public host: HTMLElementMock;\n public options: ShadowRootInit;\n public childrenList: (HTMLElementMock | string | DocumentFragmentMock)[] = [];\n public adoptedStyleSheets: string[] = [];\n\n constructor(host: HTMLElementMock, options: ShadowRootInit) {\n this.host = host;\n this.options = options;\n }\n\n appendChild(node: any): any {\n this.childrenList.push(node);\n return node;\n }\n\n querySelector(selector: string): HTMLElementMock | null {\n const result = this.querySelectorAll(selector);\n return result.length > 0 ? result[0] : null;\n }\n\n querySelectorAll(selector: string): HTMLElementMock[] {\n const result: HTMLElementMock[] = [];\n collectMatches(this.childrenList as MockNode[], selector, result);\n return result;\n }\n\n serialize(): string {\n const mode = this.options.mode;\n const delegatesFocus = this.options.delegatesFocus ? ' shadowrootdelegatesfocus' : '';\n const styles = this.adoptedStyleSheets.map((s) => `<style>${escapeHtml(s)}</style>`).join('');\n const content = this.childrenList\n .map((child) => {\n if (typeof child === 'string') return escapeHtml(child);\n if (child instanceof DocumentFragmentMock) return child.serialize();\n return child.serialize();\n })\n .join('');\n\n return `<template shadowrootmode=\"${mode}\"${delegatesFocus}>${styles}${content}</template>`;\n }\n}\n"],"mappings":"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"}
|
package/dist/mocks.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./mocks-DvJQ7jGP.cjs");exports.DocumentFragmentMock=e.t,exports.HTMLElementMock=e.n,exports.ShadowRootMock=e.r;
|
package/dist/signal.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=0,t=1,n=2,r=null,i=null,a=0,o=new Set,s=!1;function c(e,t){let r=t?.equals??Object.is,i={value:e,observers:new Set};return[()=>(y(i),i.value),e=>{let t=typeof e==`function`?e(i.value):e;if(!r(i.value,t)){i.value=t;for(let e of i.observers)b(e,n);a===0&&x()}}]}function l(e,t){let n=_(e,!1,t?.equals);return v(n),()=>n.disposed?n.value:(S(n),y(n),n.value)}function u(e){let t=_(e,!0);return v(t),C(t),()=>T(t)}function d(e){a++;try{e()}finally{a--,a===0&&x()}}function f(e){let t=r;r=null;try{return e()}finally{r=t}}function p(e){let t={owned:[],cleanups:[],owner:i},n=i,a=r;i=t,r=null;try{return e(()=>E(t))}finally{i=n,r=a}}function m(e){i&&i.cleanups.push(e)}function h(){return i}function g(e,t){let n=i,a=r;i=e,r=null;try{return t()}finally{i=n,r=a}}function _(e,t,r){return{fn:e,sources:new Set,isEffect:t,running:!1,disposed:!1,state:n,observers:new Set,value:void 0,equals:r??Object.is,owned:[],cleanups:[],owner:i}}function v(e){i&&i.owned.push(e)}function y(e){r&&(r.sources.add(e),e.observers.add(r))}function b(n,r){if(n.disposed||n.state>=r)return;let i=n.state===e;if(n.state=r,n.isEffect)o.add(n);else if(i)for(let e of n.observers)b(e,t)}function x(){if(!(a>0||s)){s=!0;try{for(;o.size>0;){let e=[...o];o.clear();for(let t of e)t.disposed||S(t)}}finally{s=!1}}}function S(r){if(!(r.disposed||r.state===e)){if(r.state===t)for(let e of r.sources)`fn`in e&&S(e);r.state===n&&C(r),r.state=e}}function C(t){if(t.running)throw Error(`ranview reactivity: cyclic dependency detected (a computation triggered itself).`);w(t);let a=r,o=i;r=t,i=t,t.running=!0,t.state=e;try{let e=t.fn();if(t.isEffect)typeof e==`function`&&t.cleanups.push(e);else{let r=t.value;if(t.value=e,t.observers.size>0&&!t.equals(r,e))for(let e of t.observers)b(e,n)}}finally{t.running=!1,r=a,i=o}}function w(e){for(let t of e.owned)T(t);e.owned.length=0;for(let t of e.cleanups)t();e.cleanups.length=0;for(let t of e.sources)t.observers.delete(e);e.sources.clear()}function T(e){e.disposed||(e.disposed=!0,w(e),e.observers.clear())}function E(e){for(let t of e.owned)T(t);e.owned.length=0;for(let t of e.cleanups)t();e.cleanups.length=0}exports.batch=d,exports.computed=l,exports.createEffect=u,exports.createRoot=p,exports.getOwner=h,exports.onCleanup=m,exports.runWithOwner=g,exports.signal=c,exports.untrack=f;
|
|
2
|
-
//# sourceMappingURL=signal.cjs.map
|
package/dist/signal.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signal.cjs","names":[],"sources":["../src/signal.ts"],"sourcesContent":["/**\n * Reactive primitives — signal / computed / createEffect / batch, plus an\n * ownership layer (createRoot / onCleanup / getOwner / runWithOwner) and untrack.\n *\n * Design reference: SwiftUI @Observable + Solid.js fine-grained reactivity.\n *\n * View = f(State)\n * signal — reactive source (@State)\n * computed — derived, LAZY + memoized (SwiftUI computed property / Solid memo)\n * createEffect— side effect that re-runs when its reads change (≈ body)\n * ownership — effects/memos form an owner tree; disposing a scope disposes\n * everything created under it (critical for MPA: tear down a page\n * and every effect it spawned is cleaned up in one call).\n *\n * Model (push-pull scheduling + lazy memos):\n * - Reading a source inside a computation links them both ways.\n * - Writing a signal marks direct observers DIRTY and their transitive memo\n * observers CHECK (\"maybe dirty\"), scheduling any dependent effects. Effects\n * flush once (deduped — diamond dependencies run an effect a single time).\n * - Memos recompute lazily on read, and only re-notify their observers when\n * their derived value actually changes (via `equals`). A CHECK computation\n * first pulls its sources up to date; if none changed value, it is skipped —\n * so a memo whose output is stable never wakes the effects behind it.\n * - Every computation is owned; re-running or disposing it first disposes its\n * owned children and runs its cleanups (no leaked nested effects).\n */\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport type Getter<T> = () => T;\nexport type Setter<T> = (value: T | ((prev: T) => T)) => void;\n\nexport interface SignalOptions<T> {\n /** Custom equality. Defaults to Object.is. Return true to skip the update. */\n equals?: (prev: T, next: T) => boolean;\n}\n\n/** Anything a computation can subscribe to (a signal, or a memo). */\ninterface SourceLike {\n observers: Set<Computation>;\n}\n\n/** Staleness of a computation. CLEAN → up to date; CHECK → a memo source may\n * have changed (verify before recomputing); DIRTY → definitely recompute. */\nconst CLEAN = 0;\nconst CHECK = 1;\nconst DIRTY = 2;\ntype State = typeof CLEAN | typeof CHECK | typeof DIRTY;\n\n/** An effect or a memo — a node that runs a function and tracks its reads. */\ninterface Computation extends Owner {\n fn: () => unknown;\n /** Sources (signals / memos) read during the last run. */\n sources: Set<SourceLike>;\n isEffect: boolean;\n running: boolean;\n disposed: boolean;\n state: State;\n // Memo-only fields:\n observers: Set<Computation>; // downstream that read this memo\n value: unknown;\n equals: (a: unknown, b: unknown) => boolean; // gates observer notification\n}\n\n/** A node that can own child computations + cleanups (effect, memo, or root). */\nexport interface Owner {\n owned: Computation[];\n cleanups: Array<() => void>;\n owner: Owner | null;\n}\n\n// ── Global tracking state ────────────────────────────────────────────────────\n\n/** The computation currently running — reads link to it. Null = untracked. */\nlet currentObserver: Computation | null = null;\n/** The owner scope currently active — new computations/cleanups attach here. */\nlet currentOwner: Owner | null = null;\n/** >0 while inside batch(): effect flushing is deferred. */\nlet batchDepth = 0;\n/** Effects queued for the next flush (a Set → each runs once per flush). */\nconst scheduled = new Set<Computation>();\n/** Guard against re-entrant flushes (an effect writing a signal mid-flush). */\nlet flushing = false;\n\n// ── signal ───────────────────────────────────────────────────────────────────\n\n/**\n * Create a reactive value. Returns a `[getter, setter]` tuple.\n *\n * const [count, setCount] = signal(0);\n * count(); // read — auto-tracked inside computed / createEffect\n * setCount(1); // write — notifies dependents; skipped if unchanged\n * setCount((n) => n + 1) // updater form\n */\nexport function signal<T>(initial: T, options?: SignalOptions<T>): [Getter<T>, Setter<T>] {\n const equals = options?.equals ?? Object.is;\n const node: SourceLike & { value: T } = { value: initial, observers: new Set() };\n\n const get: Getter<T> = () => {\n track(node);\n return node.value;\n };\n\n const set: Setter<T> = (next) => {\n const value = typeof next === 'function' ? (next as (prev: T) => T)(node.value) : next;\n if (equals(node.value, value)) return;\n node.value = value;\n // markStale only flags/schedules — it never mutates node.observers mid-loop.\n for (const observer of node.observers) markStale(observer, DIRTY);\n if (batchDepth === 0) flush();\n };\n\n return [get, set];\n}\n\n// ── computed (lazy memo) ─────────────────────────────────────────────────────\n\n/**\n * Derived, read-only, LAZY + memoized value. Recomputes only when read after a\n * dependency changed; unread memos never recompute. When a recompute produces an\n * equal value (default `Object.is`, override via `options.equals`) the memo does\n * NOT notify its observers — so effects behind a value-stable memo stay asleep.\n * Owned by the current scope, so it is disposed when that scope is.\n *\n * const fullName = computed(() => `${first()} ${last()}`);\n * fullName(); // computes on first read, caches until a dep's value changes\n */\nexport function computed<T>(fn: () => T, options?: SignalOptions<T>): Getter<T> {\n const node = makeComputation(fn, false, options?.equals as ((a: unknown, b: unknown) => boolean) | undefined);\n adopt(node);\n\n return () => {\n if (node.disposed) return node.value as T;\n updateIfNecessary(node); // recompute only if actually stale\n track(node); // link the reader to this memo\n return node.value as T;\n };\n}\n\n// ── createEffect ──────────────────────────────────────────────────────────────\n\n/**\n * Run `fn` immediately, then re-run it whenever a signal/memo it read changes.\n * Owned by the current scope. `fn` may return a cleanup, called before each\n * re-run and on dispose. Returns a dispose function.\n *\n * const dispose = createEffect(() => {\n * el.textContent = `Count: ${count()}`;\n * return () => {}; // optional cleanup\n * });\n */\nexport function createEffect(fn: () => void | (() => void)): () => void {\n const node = makeComputation(fn, true);\n adopt(node);\n runComputation(node);\n return () => disposeComputation(node);\n}\n\n// ── batch ─────────────────────────────────────────────────────────────────────\n\n/**\n * Coalesce multiple writes into one flush. Effects run once after `fn` returns,\n * deduplicated. Nested batches are absorbed by the outermost one.\n */\nexport function batch(fn: () => void): void {\n batchDepth++;\n try {\n fn();\n } finally {\n batchDepth--;\n if (batchDepth === 0) flush();\n }\n}\n\n// ── untrack ────────────────────────────────────────────────────────────────────\n\n/** Read signals without subscribing the current computation to them. */\nexport function untrack<T>(fn: () => T): T {\n const prev = currentObserver;\n currentObserver = null;\n try {\n return fn();\n } finally {\n currentObserver = prev;\n }\n}\n\n// ── Ownership ───────────────────────────────────────────────────────────────\n\n/**\n * Create a disposable reactive scope. Effects/memos created inside are owned by\n * it; calling the passed `dispose` tears them all down. Use one per page/route\n * in an MPA/SPA so navigating away cleans up every effect that page spawned.\n *\n * const dispose = createRoot((dispose) => { renderPage(); return dispose; });\n * // ...later, on navigation:\n * dispose();\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n const root: Owner = { owned: [], cleanups: [], owner: currentOwner };\n const prevOwner = currentOwner;\n const prevObserver = currentObserver;\n currentOwner = root;\n currentObserver = null; // roots do not track\n try {\n return fn(() => disposeOwner(root));\n } finally {\n currentOwner = prevOwner;\n currentObserver = prevObserver;\n }\n}\n\n/** Register a cleanup on the current scope, run when it re-runs or is disposed. */\nexport function onCleanup(fn: () => void): void {\n if (currentOwner) currentOwner.cleanups.push(fn);\n}\n\n/** The current owner scope (for advanced integrations, e.g. a router). */\nexport function getOwner(): Owner | null {\n return currentOwner;\n}\n\n/** Run `fn` under a specific owner scope (pairs with getOwner). */\nexport function runWithOwner<T>(owner: Owner | null, fn: () => T): T {\n const prevOwner = currentOwner;\n const prevObserver = currentObserver;\n currentOwner = owner;\n currentObserver = null;\n try {\n return fn();\n } finally {\n currentOwner = prevOwner;\n currentObserver = prevObserver;\n }\n}\n\n// ── Internals ────────────────────────────────────────────────────────────────\n\nfunction makeComputation(\n fn: () => unknown,\n isEffect: boolean,\n equals?: (a: unknown, b: unknown) => boolean,\n): Computation {\n return {\n fn,\n sources: new Set(),\n isEffect,\n running: false,\n disposed: false,\n state: DIRTY, // never run yet\n observers: new Set(),\n value: undefined,\n equals: equals ?? Object.is,\n owned: [],\n cleanups: [],\n owner: currentOwner,\n };\n}\n\n/** Attach a computation to the current owner so it is disposed with the scope. */\nfunction adopt(node: Computation): void {\n if (currentOwner) currentOwner.owned.push(node);\n}\n\nfunction track(source: SourceLike): void {\n if (currentObserver) {\n currentObserver.sources.add(source);\n source.observers.add(currentObserver);\n }\n}\n\n/**\n * Propagate a change. `state` is DIRTY when `node` directly read the changed\n * source, or CHECK when a memo upstream of it may have changed. Effects are\n * scheduled; a memo, the first time it leaves CLEAN, marks its own observers\n * CHECK so the staleness reaches the effects behind it. The `state >= incoming`\n * guard makes this idempotent (a DIRTY effect is not re-scheduled by a later\n * CHECK) and terminates diamonds.\n */\nfunction markStale(node: Computation, state: State): void {\n if (node.disposed || node.state >= state) return;\n const wasClean = node.state === CLEAN;\n node.state = state;\n if (node.isEffect) {\n scheduled.add(node);\n } else if (wasClean) {\n // A clean memo becoming stale: its observers are now \"maybe dirty\".\n // Recursion touches each observer's own observers, never this node's.\n for (const observer of node.observers) markStale(observer, CHECK);\n }\n}\n\nfunction flush(): void {\n if (batchDepth > 0 || flushing) return; // re-entrant writes drain via the loop below\n flushing = true;\n try {\n // Loop: an effect may schedule more effects as it runs.\n while (scheduled.size > 0) {\n const batchOfEffects = [...scheduled];\n scheduled.clear();\n for (const effect of batchOfEffects) {\n if (!effect.disposed) updateIfNecessary(effect);\n }\n }\n } finally {\n flushing = false;\n }\n}\n\n/**\n * Bring `node` up to date, recomputing only if it is genuinely dirty. A CHECK\n * node first pulls every memo source current: a source whose value changed marks\n * `node` DIRTY (via runComputation → markStale). If none did, `node` stays CHECK\n * and is skipped — this is what keeps a value-stable memo from waking effects.\n * All sources are refreshed (no early break) so none recompute mid-run and\n * spuriously re-schedule the node.\n */\nfunction updateIfNecessary(node: Computation): void {\n if (node.disposed || node.state === CLEAN) return;\n if (node.state === CHECK) {\n // Refreshing a source recomputes only that memo; it never mutates node.sources.\n for (const source of node.sources) {\n if ('fn' in source) updateIfNecessary(source as Computation); // only memos have `fn`\n }\n }\n if (node.state === DIRTY) runComputation(node);\n node.state = CLEAN;\n}\n\nfunction runComputation(node: Computation): void {\n if (node.running) {\n throw new Error('ranview reactivity: cyclic dependency detected (a computation triggered itself).');\n }\n cleanNode(node);\n\n const prevObserver = currentObserver;\n const prevOwner = currentOwner;\n currentObserver = node;\n currentOwner = node;\n node.running = true;\n // Mark clean up front: a genuine re-entrant write inside fn (a cycle) re-marks\n // and re-enters, tripping the `running` guard above. Memo sources are already\n // current here (updateIfNecessary refreshed them), so no benign re-mark occurs.\n node.state = CLEAN;\n try {\n const result = node.fn();\n if (node.isEffect) {\n if (typeof result === 'function') node.cleanups.push(result as () => void);\n } else {\n // memo: store value; notify observers only when the value actually changed.\n const prev = node.value;\n node.value = result;\n // Notify only when there are observers AND the value changed. Skipping the\n // empty-observer case also means `equals` never sees the initial `undefined`\n // sentinel (the first run always precedes any observer). markStale only\n // flags/schedules — it never mutates node.observers here.\n if (node.observers.size > 0 && !node.equals(prev, result)) {\n for (const observer of node.observers) markStale(observer, DIRTY);\n }\n }\n } finally {\n node.running = false;\n currentObserver = prevObserver;\n currentOwner = prevOwner;\n }\n}\n\n/** Dispose owned children, run cleanups, and unlink from all sources. */\nfunction cleanNode(node: Computation): void {\n for (const child of node.owned) disposeComputation(child);\n node.owned.length = 0;\n\n for (const cleanup of node.cleanups) cleanup();\n node.cleanups.length = 0;\n\n for (const source of node.sources) source.observers.delete(node);\n node.sources.clear();\n}\n\nfunction disposeComputation(node: Computation): void {\n if (node.disposed) return;\n node.disposed = true;\n cleanNode(node);\n node.observers.clear();\n}\n\nfunction disposeOwner(owner: Owner): void {\n for (const child of owner.owned) disposeComputation(child);\n owner.owned.length = 0;\n for (const cleanup of owner.cleanups) cleanup();\n owner.cleanups.length = 0;\n}\n"],"mappings":"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"}
|