streetui 1.0.0 → 1.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/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- import { a as Signal, b as Subscriber, U as Unsubscribe, R as ReadonlySignal, A as ApplicationId, D as DiagnosticCollector, c as ApplicationGraph, G as GraphNode, C as CompiledApplication, d as SemanticNodeType, P as PageDSL, e as ContainerDSL, f as SignalKind } from './compile-B0q07Hzq.cjs';
2
- export { g as A11yOptions, h as AppBuilder, i as AppDSL, j as AppOptions, k as ApplicationGraphOptions, B as BaseNode, l as Bindable, m as BindableText, n as BoundInputOptions, o as ButtonOptions, p as CompileOptions, q as ContainerBuilder, r as ContainerBuilderImpl, s as ContainerOptions, t as ContentDSL, u as ControlledInputOptions, v as DerivedSignal, w as Diagnostic, x as DiagnosticError, y as DiagnosticLocation, z as DiagnosticSeverity, E as ErrorBoundaryOptions, F as ErrorFallbackBuilder, H as ErrorSource, I as EventDescriptor, J as FormBuilder, K as FormBuilderImpl, L as FormDSL, M as FormOptions, N as GraphNodeData, O as HandlerFn, Q as HeadingOptions, T as ImageOptions, W as InputOptions, X as InputOptionsBase, Y as LinkOptions, Z as ListBuilder, _ as ListBuilderImpl, $ as ListDSL, a0 as ListOptions, a1 as NodeId, a2 as NodeMetadata, a3 as PageBuilder, a4 as PageBuilderImpl, a5 as PropValue, a6 as Props, a7 as ReactiveConsumer, a8 as ReactiveSource, a9 as SectionBuilder, aa as SectionBuilderImpl, ab as SectionDSL, ac as SectionOptions, ad as SerializedGraph, ae as SerializedNode, af as StateRef, S as StreetApp, ag as StreetUI, ah as TextOptions, ai as TextValue, V as VERSION, aj as batch, ak as compile, al as compileGraph, am as createNodeId, an as derived, ao as effect, ap as formatDiagnostic, aq as generateApplicationId, ar as generateNodeId, as as isBatching, at as nextId, au as nodeIdPrefix, av as observerCount, aw as reactiveListItemKey, ax as reactiveListItemSignature, ay as resetIdCounter, az as signal, aA as signalKind, aB as streetui } from './compile-B0q07Hzq.cjs';
3
- import { R as RenderHandle, S as StreetRenderer, a as HydrationDiagnosticSink } from './hydration-diagnostics-BE6xVWD1.cjs';
4
- export { H as HydrationDiagnostic, b as HydrationMismatchType, c as consoleHydrationDiagnosticSink, d as createHydrationDiagnosticCollector, f as formatHydrationDiagnostic } from './hydration-diagnostics-BE6xVWD1.cjs';
5
- import { D as DOMAdapter } from './server-84Rz4g8W.cjs';
6
- export { R as RenderToStringOptions, S as STATE_MARKER_ATTR, a as ServerDOMAdapter, r as readState, b as renderToString, s as serializeState, c as serverDOMAdapter } from './server-84Rz4g8W.cjs';
1
+ import { a as Signal, b as Subscriber, U as Unsubscribe, R as ReadonlySignal, A as ApplicationId, D as DiagnosticCollector, c as ApplicationGraph, G as GraphNode, C as CompiledApplication, d as SemanticNodeType, P as PageDSL, e as ContainerDSL, f as SignalKind } from './compile-DsNJm9IJ.cjs';
2
+ export { g as A11yOptions, h as AppBuilder, i as AppDSL, j as AppOptions, k as ApplicationGraphOptions, B as BaseNode, l as Bindable, m as BindableText, n as BoundInputOptions, o as ButtonOptions, p as CompileOptions, q as ContainerBuilder, r as ContainerBuilderImpl, s as ContainerOptions, t as ContentDSL, u as ControlledInputOptions, v as DerivedSignal, w as Diagnostic, x as DiagnosticError, y as DiagnosticLocation, z as DiagnosticSeverity, E as ErrorBoundaryOptions, F as ErrorFallbackBuilder, H as ErrorSource, I as EventDescriptor, J as FormBuilder, K as FormBuilderImpl, L as FormDSL, M as FormOptions, N as GraphNodeData, O as HandlerFn, Q as HeadingOptions, T as ImageOptions, W as InputOptions, X as InputOptionsBase, Y as LinkOptions, Z as ListBuilder, _ as ListBuilderImpl, $ as ListDSL, a0 as ListOptions, a1 as ListPlanEntry, a2 as NodeId, a3 as NodeMetadata, a4 as PageBuilder, a5 as PageBuilderImpl, a6 as PropValue, a7 as Props, a8 as ReactiveConsumer, a9 as ReactiveSource, aa as SectionBuilder, ab as SectionBuilderImpl, ac as SectionDSL, ad as SectionOptions, ae as SerializedGraph, af as SerializedNode, ag as StateRef, S as StreetApp, ah as StreetUI, ai as TextOptions, aj as TextValue, V as VERSION, ak as batch, al as compile, am as compileGraph, an as createNodeId, ao as derived, ap as effect, aq as formatDiagnostic, ar as generateApplicationId, as as generateNodeId, at as isBatching, au as nextId, av as nodeIdPrefix, aw as observerCount, ax as reactiveListItemKey, ay as reactiveListItemSignature, az as resetIdCounter, aA as signal, aB as signalKind, aC as streetui } from './compile-DsNJm9IJ.cjs';
3
+ import { R as RenderHandle, S as StreetRenderer, a as HydrationDiagnosticSink } from './hydration-diagnostics-OFIDPD15.cjs';
4
+ export { H as HydrationDiagnostic, b as HydrationMismatchType, c as consoleHydrationDiagnosticSink, d as createHydrationDiagnosticCollector, f as formatHydrationDiagnostic } from './hydration-diagnostics-OFIDPD15.cjs';
5
+ import { D as DOMAdapter } from './server-BYXgNCQK.cjs';
6
+ export { R as RenderToStringOptions, S as STATE_MARKER_ATTR, a as ServerDOMAdapter, r as readState, b as renderToString, s as serializeState, c as serverDOMAdapter } from './server-BYXgNCQK.cjs';
7
7
 
8
8
  /**
9
9
  * A simple reactive store built on top of signals.
@@ -747,8 +747,10 @@ declare function applyNodeProps(ctx: RenderContext, graphNode: GraphNode, el: El
747
747
  declare function wireSignalBindings(ctx: RenderContext, graphNode: GraphNode, instance: NodeInstance, onUpdate: (propKey: string, value: unknown) => void): void;
748
748
  /**
749
749
  * Subscribe a reactive-list instance to its driving signal. On each change the
750
- * DSL-registered build factory produces the desired child graph nodes, which
751
- * are reconciled against the live DOM with the keyed reconciler.
750
+ * DSL-registered plan factory produces lightweight per-row descriptors, which
751
+ * are reconciled against the live DOM with the keyed, minimal-move reconciler
752
+ * (spec §15). A `conditional` node has no plan handler and falls back to the
753
+ * eager build factory (it only ever renders 0..1 branch, so eager is fine).
752
754
  */
753
755
  declare function wireReactiveList(ctx: RenderContext, graphNode: GraphNode, instance: NodeInstance, el: Element): void;
754
756
 
@@ -785,6 +787,24 @@ interface ReconcileResult {
785
787
  instances: NodeInstance[];
786
788
  /** Instances that were removed and must be disposed. */
787
789
  removed: NodeInstance[];
790
+ /**
791
+ * GraphNodes freshly materialised during this reconcile (new rows + rebuilt
792
+ * changed rows). The caller detaches any of these that were not adopted as a
793
+ * live instance's graph node, so no orphan subtree lingers in the graph index.
794
+ */
795
+ built?: GraphNode[];
796
+ }
797
+ /**
798
+ * A lazy reconciliation descriptor for one reactive-list row (mirrors the DSL's
799
+ * `ListPlanEntry`). `sig()` and `build()` are only invoked for rows that are
800
+ * genuinely new or whose source reference changed — the whole point of the
801
+ * plan path (spec §15).
802
+ */
803
+ interface PlanEntry {
804
+ readonly key: string;
805
+ readonly item: unknown;
806
+ readonly sig: () => string;
807
+ readonly build: () => GraphNode;
788
808
  }
789
809
  /**
790
810
  * Reconcile children of a container element against a new list of graph nodes.
@@ -796,6 +816,25 @@ interface ReconcileResult {
796
816
  * @param mountFn Factory to create a new NodeInstance for a graph node
797
817
  */
798
818
  declare function reconcileChildren(ctx: RenderContext, parentDom: Element, oldInstances: NodeInstance[], newNodes: readonly GraphNode[], mountFn: MountFn): ReconcileResult;
819
+ /**
820
+ * Plan-based keyed reconciliation (spec §15 — the optimised reactive-list path).
821
+ *
822
+ * Identical observable result to {@link reconcileChildren}, but driven by lazy
823
+ * {@link PlanEntry} descriptors instead of a pre-built array of GraphNodes:
824
+ *
825
+ * - a reused row whose `item` reference is unchanged does **zero** work — no
826
+ * signature hash, no subtree build, no prop patch (the common case for
827
+ * append / prepend / remove / reorder / reverse, where existing item objects
828
+ * keep their identity);
829
+ * - a reused row whose reference changed hashes lazily and, only on a real
830
+ * signature change, materialises a fresh subtree for a targeted in-place
831
+ * content update;
832
+ * - a genuinely new key builds + mounts exactly one subtree.
833
+ *
834
+ * DOM reordering uses a longest-increasing-subsequence pass so the number of
835
+ * moves is minimal (e.g. a prepend into a 10k list moves 1 node, not 10k).
836
+ */
837
+ declare function reconcileChildrenByPlan(ctx: RenderContext, parentDom: Element, oldInstances: NodeInstance[], plan: readonly PlanEntry[], mountFn: MountFn): ReconcileResult;
799
838
 
800
839
  /**
801
840
  * StreetUI Renderer — framework-owned DOM renderer.
@@ -1382,6 +1421,15 @@ interface PerfSnapshot {
1382
1421
  readonly distinctSignals: number;
1383
1422
  /** Largest single-node child count (a proxy for the biggest list/section). */
1384
1423
  readonly largestChildCount: number;
1424
+ /**
1425
+ * Number of reactive keyed-list sites driven by the optimised lazy-plan
1426
+ * reconciler (v1.1 §15/§25). Counted from the graph's `__listplan__<id>`
1427
+ * handler registrations — one per `listOf(...)` bound to a signal. These are
1428
+ * the nodes whose updates take the identity-short-circuit + LIS minimal-move
1429
+ * path, so surfacing the count lets a panel or CI check see how much of an app
1430
+ * benefits from the keyed-list engine without measuring anything at runtime.
1431
+ */
1432
+ readonly reactiveLists: number;
1385
1433
  }
1386
1434
  /**
1387
1435
  * The complete read-only snapshot of a compiled application. Everything here is
@@ -1756,4 +1804,4 @@ interface ResolvedConfig {
1756
1804
  /** Identity helper that gives `streetui.config.ts` full type-checking + inference. */
1757
1805
  declare function defineConfig(config: StreetUIConfig): StreetUIConfig;
1758
1806
 
1759
- export { type A11yIds, Application, ApplicationGraph, ApplicationId, type ApplicationIdentity, type ApplicationInspection, type ApplicationOptions, type ApplicationPanel, BrowserDOMAdapter, CleanupRegistry, CompiledApplication, ContainerDSL, type Context, type ContextInspection, type ContextLike, DEFAULT_PERF_THRESHOLDS, DOMAdapter, type DevToolsOptions, type DevToolsSession, type DevToolsSnapshot, type DevToolsSources, DiagnosticCollector, type DiagnosticContext, type DiagnosticSink, type DiagnosticsSummary, type DomBinding, DomEventRegistry, Environment, type EnvironmentCapabilities, type EnvironmentKind, EventBus, type EventHandler, FOCUSABLE_SELECTOR, type Field, type Form, type FormConfig, type FormInspection, type FormLike, type FormValidators, type FormValues, GraphNode, HydrationDiagnosticSink, type I18n, type I18nConfig, type I18nInspection, type I18nLike, type InspectFormOptions, type InspectI18nOptions, type InspectResourceOptions, type InspectSignalOptions, type InspectedNode, type InspectedPage, type InterpolationParams, type IsActiveOptions, type Job, Lifecycle, type LifecycleHook, type LifecyclePhase, type MatchResult, type MessageMap, type MountFn, type MountRouterOptions, type MountedApplication, type MountedRouter, type NavigateOptions, NodeInstance, type NodeInstanceOptions, PageDSL, type PerfDiagnostic, type PerfDiagnosticCode, type PerfSnapshot, type PerfThresholds, type PerformancePanel, type Priority, ROUTER_OUTLET_ID, ReadonlySignal, type ReconcileResult, type RenderContext, RenderHandle, type ResolvedConfig, type Resource, type ResourceInspection, type ResourceLike, type ResourceLoader, type ResourceLoaderContext, type ResourceOptions, type ResourceStatus, type RouteBuilder, type RouteContext, type RouteDefinition, type RouteMatch, type RouteMatchLike, type Router, type RouterHistory, type RouterInspection, type RouterLike, type RouterLocation, type RouterOptions, Runtime, RuntimeNodeInstance, type RuntimeOptions, Scheduler, type SchedulerDiagnostics, SemanticNodeType, ServerComment, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, type ShellBuilder, Signal, type SignalInspection, SignalKind, type SignalsPanel, Store, type StoreState, type StreetEvent, type StreetEventType, StreetFrameworkError, StreetRenderHandle, StreetRenderer, StreetRendererImpl, type StreetRendererOptions, type StreetUIConfig, type SubmitStatus, Subscriber, Unsubscribe, type Validator, a11yIds, applyNodeProps, applyProp, bindDomEvent, browserDOMAdapter, buttonUpdate, consoleDiagnosticSink, createApplication, createBrowserHistory, createContext, createDevTools, createForm, createI18n, createMemoryHistory, createRenderContext, createRenderer, createRouter, createRuntime, createStore, createStreetEvent, defineConfig, diagnosePerformance, email, environment, escapeHtmlAttr, escapeHtmlText, flushSync, focusById, focusFirst, formatDiagnosticContext, frameworkError, globalEventBus, headingUpdate, hydrateGraph, inputUpdate, inspectApplication, inspectContext, inspectForm, inspectGraph, inspectI18n, inspectResource, inspectRouter, inspectSignal, interpolate, matchPattern, matchRoutes, maxLength, minLength, mountGraph, mountNode, mountRouter, nodeTypeStats, normalizePath, patchNode, patchProp, pattern, printDiagnostics, printGraph, reconcileChildren, reportDiagnostic, required, resolveTag, resource, routerOutlet, runValidators, scheduleImmediate, scheduleUpdate, scheduler, serializeChildren, serializeServerNode, splitTarget, textUpdate, toIdToken, transformGraph, validateGraph, wireEvents, wireReactiveList, wireSignalBindings };
1807
+ export { type A11yIds, Application, ApplicationGraph, ApplicationId, type ApplicationIdentity, type ApplicationInspection, type ApplicationOptions, type ApplicationPanel, BrowserDOMAdapter, CleanupRegistry, CompiledApplication, ContainerDSL, type Context, type ContextInspection, type ContextLike, DEFAULT_PERF_THRESHOLDS, DOMAdapter, type DevToolsOptions, type DevToolsSession, type DevToolsSnapshot, type DevToolsSources, DiagnosticCollector, type DiagnosticContext, type DiagnosticSink, type DiagnosticsSummary, type DomBinding, DomEventRegistry, Environment, type EnvironmentCapabilities, type EnvironmentKind, EventBus, type EventHandler, FOCUSABLE_SELECTOR, type Field, type Form, type FormConfig, type FormInspection, type FormLike, type FormValidators, type FormValues, GraphNode, HydrationDiagnosticSink, type I18n, type I18nConfig, type I18nInspection, type I18nLike, type InspectFormOptions, type InspectI18nOptions, type InspectResourceOptions, type InspectSignalOptions, type InspectedNode, type InspectedPage, type InterpolationParams, type IsActiveOptions, type Job, Lifecycle, type LifecycleHook, type LifecyclePhase, type MatchResult, type MessageMap, type MountFn, type MountRouterOptions, type MountedApplication, type MountedRouter, type NavigateOptions, NodeInstance, type NodeInstanceOptions, PageDSL, type PerfDiagnostic, type PerfDiagnosticCode, type PerfSnapshot, type PerfThresholds, type PerformancePanel, type PlanEntry, type Priority, ROUTER_OUTLET_ID, ReadonlySignal, type ReconcileResult, type RenderContext, RenderHandle, type ResolvedConfig, type Resource, type ResourceInspection, type ResourceLike, type ResourceLoader, type ResourceLoaderContext, type ResourceOptions, type ResourceStatus, type RouteBuilder, type RouteContext, type RouteDefinition, type RouteMatch, type RouteMatchLike, type Router, type RouterHistory, type RouterInspection, type RouterLike, type RouterLocation, type RouterOptions, Runtime, RuntimeNodeInstance, type RuntimeOptions, Scheduler, type SchedulerDiagnostics, SemanticNodeType, ServerComment, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, type ShellBuilder, Signal, type SignalInspection, SignalKind, type SignalsPanel, Store, type StoreState, type StreetEvent, type StreetEventType, StreetFrameworkError, StreetRenderHandle, StreetRenderer, StreetRendererImpl, type StreetRendererOptions, type StreetUIConfig, type SubmitStatus, Subscriber, Unsubscribe, type Validator, a11yIds, applyNodeProps, applyProp, bindDomEvent, browserDOMAdapter, buttonUpdate, consoleDiagnosticSink, createApplication, createBrowserHistory, createContext, createDevTools, createForm, createI18n, createMemoryHistory, createRenderContext, createRenderer, createRouter, createRuntime, createStore, createStreetEvent, defineConfig, diagnosePerformance, email, environment, escapeHtmlAttr, escapeHtmlText, flushSync, focusById, focusFirst, formatDiagnosticContext, frameworkError, globalEventBus, headingUpdate, hydrateGraph, inputUpdate, inspectApplication, inspectContext, inspectForm, inspectGraph, inspectI18n, inspectResource, inspectRouter, inspectSignal, interpolate, matchPattern, matchRoutes, maxLength, minLength, mountGraph, mountNode, mountRouter, nodeTypeStats, normalizePath, patchNode, patchProp, pattern, printDiagnostics, printGraph, reconcileChildren, reconcileChildrenByPlan, reportDiagnostic, required, resolveTag, resource, routerOutlet, runValidators, scheduleImmediate, scheduleUpdate, scheduler, serializeChildren, serializeServerNode, splitTarget, textUpdate, toIdToken, transformGraph, validateGraph, wireEvents, wireReactiveList, wireSignalBindings };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { a as Signal, b as Subscriber, U as Unsubscribe, R as ReadonlySignal, A as ApplicationId, D as DiagnosticCollector, c as ApplicationGraph, G as GraphNode, C as CompiledApplication, d as SemanticNodeType, P as PageDSL, e as ContainerDSL, f as SignalKind } from './compile-B0q07Hzq.js';
2
- export { g as A11yOptions, h as AppBuilder, i as AppDSL, j as AppOptions, k as ApplicationGraphOptions, B as BaseNode, l as Bindable, m as BindableText, n as BoundInputOptions, o as ButtonOptions, p as CompileOptions, q as ContainerBuilder, r as ContainerBuilderImpl, s as ContainerOptions, t as ContentDSL, u as ControlledInputOptions, v as DerivedSignal, w as Diagnostic, x as DiagnosticError, y as DiagnosticLocation, z as DiagnosticSeverity, E as ErrorBoundaryOptions, F as ErrorFallbackBuilder, H as ErrorSource, I as EventDescriptor, J as FormBuilder, K as FormBuilderImpl, L as FormDSL, M as FormOptions, N as GraphNodeData, O as HandlerFn, Q as HeadingOptions, T as ImageOptions, W as InputOptions, X as InputOptionsBase, Y as LinkOptions, Z as ListBuilder, _ as ListBuilderImpl, $ as ListDSL, a0 as ListOptions, a1 as NodeId, a2 as NodeMetadata, a3 as PageBuilder, a4 as PageBuilderImpl, a5 as PropValue, a6 as Props, a7 as ReactiveConsumer, a8 as ReactiveSource, a9 as SectionBuilder, aa as SectionBuilderImpl, ab as SectionDSL, ac as SectionOptions, ad as SerializedGraph, ae as SerializedNode, af as StateRef, S as StreetApp, ag as StreetUI, ah as TextOptions, ai as TextValue, V as VERSION, aj as batch, ak as compile, al as compileGraph, am as createNodeId, an as derived, ao as effect, ap as formatDiagnostic, aq as generateApplicationId, ar as generateNodeId, as as isBatching, at as nextId, au as nodeIdPrefix, av as observerCount, aw as reactiveListItemKey, ax as reactiveListItemSignature, ay as resetIdCounter, az as signal, aA as signalKind, aB as streetui } from './compile-B0q07Hzq.js';
3
- import { R as RenderHandle, S as StreetRenderer, a as HydrationDiagnosticSink } from './hydration-diagnostics-Bck5dMbz.js';
4
- export { H as HydrationDiagnostic, b as HydrationMismatchType, c as consoleHydrationDiagnosticSink, d as createHydrationDiagnosticCollector, f as formatHydrationDiagnostic } from './hydration-diagnostics-Bck5dMbz.js';
5
- import { D as DOMAdapter } from './server-D9GPmB49.js';
6
- export { R as RenderToStringOptions, S as STATE_MARKER_ATTR, a as ServerDOMAdapter, r as readState, b as renderToString, s as serializeState, c as serverDOMAdapter } from './server-D9GPmB49.js';
1
+ import { a as Signal, b as Subscriber, U as Unsubscribe, R as ReadonlySignal, A as ApplicationId, D as DiagnosticCollector, c as ApplicationGraph, G as GraphNode, C as CompiledApplication, d as SemanticNodeType, P as PageDSL, e as ContainerDSL, f as SignalKind } from './compile-DsNJm9IJ.js';
2
+ export { g as A11yOptions, h as AppBuilder, i as AppDSL, j as AppOptions, k as ApplicationGraphOptions, B as BaseNode, l as Bindable, m as BindableText, n as BoundInputOptions, o as ButtonOptions, p as CompileOptions, q as ContainerBuilder, r as ContainerBuilderImpl, s as ContainerOptions, t as ContentDSL, u as ControlledInputOptions, v as DerivedSignal, w as Diagnostic, x as DiagnosticError, y as DiagnosticLocation, z as DiagnosticSeverity, E as ErrorBoundaryOptions, F as ErrorFallbackBuilder, H as ErrorSource, I as EventDescriptor, J as FormBuilder, K as FormBuilderImpl, L as FormDSL, M as FormOptions, N as GraphNodeData, O as HandlerFn, Q as HeadingOptions, T as ImageOptions, W as InputOptions, X as InputOptionsBase, Y as LinkOptions, Z as ListBuilder, _ as ListBuilderImpl, $ as ListDSL, a0 as ListOptions, a1 as ListPlanEntry, a2 as NodeId, a3 as NodeMetadata, a4 as PageBuilder, a5 as PageBuilderImpl, a6 as PropValue, a7 as Props, a8 as ReactiveConsumer, a9 as ReactiveSource, aa as SectionBuilder, ab as SectionBuilderImpl, ac as SectionDSL, ad as SectionOptions, ae as SerializedGraph, af as SerializedNode, ag as StateRef, S as StreetApp, ah as StreetUI, ai as TextOptions, aj as TextValue, V as VERSION, ak as batch, al as compile, am as compileGraph, an as createNodeId, ao as derived, ap as effect, aq as formatDiagnostic, ar as generateApplicationId, as as generateNodeId, at as isBatching, au as nextId, av as nodeIdPrefix, aw as observerCount, ax as reactiveListItemKey, ay as reactiveListItemSignature, az as resetIdCounter, aA as signal, aB as signalKind, aC as streetui } from './compile-DsNJm9IJ.js';
3
+ import { R as RenderHandle, S as StreetRenderer, a as HydrationDiagnosticSink } from './hydration-diagnostics-BN1S-fbq.js';
4
+ export { H as HydrationDiagnostic, b as HydrationMismatchType, c as consoleHydrationDiagnosticSink, d as createHydrationDiagnosticCollector, f as formatHydrationDiagnostic } from './hydration-diagnostics-BN1S-fbq.js';
5
+ import { D as DOMAdapter } from './server-BYdG0sP_.js';
6
+ export { R as RenderToStringOptions, S as STATE_MARKER_ATTR, a as ServerDOMAdapter, r as readState, b as renderToString, s as serializeState, c as serverDOMAdapter } from './server-BYdG0sP_.js';
7
7
 
8
8
  /**
9
9
  * A simple reactive store built on top of signals.
@@ -747,8 +747,10 @@ declare function applyNodeProps(ctx: RenderContext, graphNode: GraphNode, el: El
747
747
  declare function wireSignalBindings(ctx: RenderContext, graphNode: GraphNode, instance: NodeInstance, onUpdate: (propKey: string, value: unknown) => void): void;
748
748
  /**
749
749
  * Subscribe a reactive-list instance to its driving signal. On each change the
750
- * DSL-registered build factory produces the desired child graph nodes, which
751
- * are reconciled against the live DOM with the keyed reconciler.
750
+ * DSL-registered plan factory produces lightweight per-row descriptors, which
751
+ * are reconciled against the live DOM with the keyed, minimal-move reconciler
752
+ * (spec §15). A `conditional` node has no plan handler and falls back to the
753
+ * eager build factory (it only ever renders 0..1 branch, so eager is fine).
752
754
  */
753
755
  declare function wireReactiveList(ctx: RenderContext, graphNode: GraphNode, instance: NodeInstance, el: Element): void;
754
756
 
@@ -785,6 +787,24 @@ interface ReconcileResult {
785
787
  instances: NodeInstance[];
786
788
  /** Instances that were removed and must be disposed. */
787
789
  removed: NodeInstance[];
790
+ /**
791
+ * GraphNodes freshly materialised during this reconcile (new rows + rebuilt
792
+ * changed rows). The caller detaches any of these that were not adopted as a
793
+ * live instance's graph node, so no orphan subtree lingers in the graph index.
794
+ */
795
+ built?: GraphNode[];
796
+ }
797
+ /**
798
+ * A lazy reconciliation descriptor for one reactive-list row (mirrors the DSL's
799
+ * `ListPlanEntry`). `sig()` and `build()` are only invoked for rows that are
800
+ * genuinely new or whose source reference changed — the whole point of the
801
+ * plan path (spec §15).
802
+ */
803
+ interface PlanEntry {
804
+ readonly key: string;
805
+ readonly item: unknown;
806
+ readonly sig: () => string;
807
+ readonly build: () => GraphNode;
788
808
  }
789
809
  /**
790
810
  * Reconcile children of a container element against a new list of graph nodes.
@@ -796,6 +816,25 @@ interface ReconcileResult {
796
816
  * @param mountFn Factory to create a new NodeInstance for a graph node
797
817
  */
798
818
  declare function reconcileChildren(ctx: RenderContext, parentDom: Element, oldInstances: NodeInstance[], newNodes: readonly GraphNode[], mountFn: MountFn): ReconcileResult;
819
+ /**
820
+ * Plan-based keyed reconciliation (spec §15 — the optimised reactive-list path).
821
+ *
822
+ * Identical observable result to {@link reconcileChildren}, but driven by lazy
823
+ * {@link PlanEntry} descriptors instead of a pre-built array of GraphNodes:
824
+ *
825
+ * - a reused row whose `item` reference is unchanged does **zero** work — no
826
+ * signature hash, no subtree build, no prop patch (the common case for
827
+ * append / prepend / remove / reorder / reverse, where existing item objects
828
+ * keep their identity);
829
+ * - a reused row whose reference changed hashes lazily and, only on a real
830
+ * signature change, materialises a fresh subtree for a targeted in-place
831
+ * content update;
832
+ * - a genuinely new key builds + mounts exactly one subtree.
833
+ *
834
+ * DOM reordering uses a longest-increasing-subsequence pass so the number of
835
+ * moves is minimal (e.g. a prepend into a 10k list moves 1 node, not 10k).
836
+ */
837
+ declare function reconcileChildrenByPlan(ctx: RenderContext, parentDom: Element, oldInstances: NodeInstance[], plan: readonly PlanEntry[], mountFn: MountFn): ReconcileResult;
799
838
 
800
839
  /**
801
840
  * StreetUI Renderer — framework-owned DOM renderer.
@@ -1382,6 +1421,15 @@ interface PerfSnapshot {
1382
1421
  readonly distinctSignals: number;
1383
1422
  /** Largest single-node child count (a proxy for the biggest list/section). */
1384
1423
  readonly largestChildCount: number;
1424
+ /**
1425
+ * Number of reactive keyed-list sites driven by the optimised lazy-plan
1426
+ * reconciler (v1.1 §15/§25). Counted from the graph's `__listplan__<id>`
1427
+ * handler registrations — one per `listOf(...)` bound to a signal. These are
1428
+ * the nodes whose updates take the identity-short-circuit + LIS minimal-move
1429
+ * path, so surfacing the count lets a panel or CI check see how much of an app
1430
+ * benefits from the keyed-list engine without measuring anything at runtime.
1431
+ */
1432
+ readonly reactiveLists: number;
1385
1433
  }
1386
1434
  /**
1387
1435
  * The complete read-only snapshot of a compiled application. Everything here is
@@ -1756,4 +1804,4 @@ interface ResolvedConfig {
1756
1804
  /** Identity helper that gives `streetui.config.ts` full type-checking + inference. */
1757
1805
  declare function defineConfig(config: StreetUIConfig): StreetUIConfig;
1758
1806
 
1759
- export { type A11yIds, Application, ApplicationGraph, ApplicationId, type ApplicationIdentity, type ApplicationInspection, type ApplicationOptions, type ApplicationPanel, BrowserDOMAdapter, CleanupRegistry, CompiledApplication, ContainerDSL, type Context, type ContextInspection, type ContextLike, DEFAULT_PERF_THRESHOLDS, DOMAdapter, type DevToolsOptions, type DevToolsSession, type DevToolsSnapshot, type DevToolsSources, DiagnosticCollector, type DiagnosticContext, type DiagnosticSink, type DiagnosticsSummary, type DomBinding, DomEventRegistry, Environment, type EnvironmentCapabilities, type EnvironmentKind, EventBus, type EventHandler, FOCUSABLE_SELECTOR, type Field, type Form, type FormConfig, type FormInspection, type FormLike, type FormValidators, type FormValues, GraphNode, HydrationDiagnosticSink, type I18n, type I18nConfig, type I18nInspection, type I18nLike, type InspectFormOptions, type InspectI18nOptions, type InspectResourceOptions, type InspectSignalOptions, type InspectedNode, type InspectedPage, type InterpolationParams, type IsActiveOptions, type Job, Lifecycle, type LifecycleHook, type LifecyclePhase, type MatchResult, type MessageMap, type MountFn, type MountRouterOptions, type MountedApplication, type MountedRouter, type NavigateOptions, NodeInstance, type NodeInstanceOptions, PageDSL, type PerfDiagnostic, type PerfDiagnosticCode, type PerfSnapshot, type PerfThresholds, type PerformancePanel, type Priority, ROUTER_OUTLET_ID, ReadonlySignal, type ReconcileResult, type RenderContext, RenderHandle, type ResolvedConfig, type Resource, type ResourceInspection, type ResourceLike, type ResourceLoader, type ResourceLoaderContext, type ResourceOptions, type ResourceStatus, type RouteBuilder, type RouteContext, type RouteDefinition, type RouteMatch, type RouteMatchLike, type Router, type RouterHistory, type RouterInspection, type RouterLike, type RouterLocation, type RouterOptions, Runtime, RuntimeNodeInstance, type RuntimeOptions, Scheduler, type SchedulerDiagnostics, SemanticNodeType, ServerComment, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, type ShellBuilder, Signal, type SignalInspection, SignalKind, type SignalsPanel, Store, type StoreState, type StreetEvent, type StreetEventType, StreetFrameworkError, StreetRenderHandle, StreetRenderer, StreetRendererImpl, type StreetRendererOptions, type StreetUIConfig, type SubmitStatus, Subscriber, Unsubscribe, type Validator, a11yIds, applyNodeProps, applyProp, bindDomEvent, browserDOMAdapter, buttonUpdate, consoleDiagnosticSink, createApplication, createBrowserHistory, createContext, createDevTools, createForm, createI18n, createMemoryHistory, createRenderContext, createRenderer, createRouter, createRuntime, createStore, createStreetEvent, defineConfig, diagnosePerformance, email, environment, escapeHtmlAttr, escapeHtmlText, flushSync, focusById, focusFirst, formatDiagnosticContext, frameworkError, globalEventBus, headingUpdate, hydrateGraph, inputUpdate, inspectApplication, inspectContext, inspectForm, inspectGraph, inspectI18n, inspectResource, inspectRouter, inspectSignal, interpolate, matchPattern, matchRoutes, maxLength, minLength, mountGraph, mountNode, mountRouter, nodeTypeStats, normalizePath, patchNode, patchProp, pattern, printDiagnostics, printGraph, reconcileChildren, reportDiagnostic, required, resolveTag, resource, routerOutlet, runValidators, scheduleImmediate, scheduleUpdate, scheduler, serializeChildren, serializeServerNode, splitTarget, textUpdate, toIdToken, transformGraph, validateGraph, wireEvents, wireReactiveList, wireSignalBindings };
1807
+ export { type A11yIds, Application, ApplicationGraph, ApplicationId, type ApplicationIdentity, type ApplicationInspection, type ApplicationOptions, type ApplicationPanel, BrowserDOMAdapter, CleanupRegistry, CompiledApplication, ContainerDSL, type Context, type ContextInspection, type ContextLike, DEFAULT_PERF_THRESHOLDS, DOMAdapter, type DevToolsOptions, type DevToolsSession, type DevToolsSnapshot, type DevToolsSources, DiagnosticCollector, type DiagnosticContext, type DiagnosticSink, type DiagnosticsSummary, type DomBinding, DomEventRegistry, Environment, type EnvironmentCapabilities, type EnvironmentKind, EventBus, type EventHandler, FOCUSABLE_SELECTOR, type Field, type Form, type FormConfig, type FormInspection, type FormLike, type FormValidators, type FormValues, GraphNode, HydrationDiagnosticSink, type I18n, type I18nConfig, type I18nInspection, type I18nLike, type InspectFormOptions, type InspectI18nOptions, type InspectResourceOptions, type InspectSignalOptions, type InspectedNode, type InspectedPage, type InterpolationParams, type IsActiveOptions, type Job, Lifecycle, type LifecycleHook, type LifecyclePhase, type MatchResult, type MessageMap, type MountFn, type MountRouterOptions, type MountedApplication, type MountedRouter, type NavigateOptions, NodeInstance, type NodeInstanceOptions, PageDSL, type PerfDiagnostic, type PerfDiagnosticCode, type PerfSnapshot, type PerfThresholds, type PerformancePanel, type PlanEntry, type Priority, ROUTER_OUTLET_ID, ReadonlySignal, type ReconcileResult, type RenderContext, RenderHandle, type ResolvedConfig, type Resource, type ResourceInspection, type ResourceLike, type ResourceLoader, type ResourceLoaderContext, type ResourceOptions, type ResourceStatus, type RouteBuilder, type RouteContext, type RouteDefinition, type RouteMatch, type RouteMatchLike, type Router, type RouterHistory, type RouterInspection, type RouterLike, type RouterLocation, type RouterOptions, Runtime, RuntimeNodeInstance, type RuntimeOptions, Scheduler, type SchedulerDiagnostics, SemanticNodeType, ServerComment, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, type ShellBuilder, Signal, type SignalInspection, SignalKind, type SignalsPanel, Store, type StoreState, type StreetEvent, type StreetEventType, StreetFrameworkError, StreetRenderHandle, StreetRenderer, StreetRendererImpl, type StreetRendererOptions, type StreetUIConfig, type SubmitStatus, Subscriber, Unsubscribe, type Validator, a11yIds, applyNodeProps, applyProp, bindDomEvent, browserDOMAdapter, buttonUpdate, consoleDiagnosticSink, createApplication, createBrowserHistory, createContext, createDevTools, createForm, createI18n, createMemoryHistory, createRenderContext, createRenderer, createRouter, createRuntime, createStore, createStreetEvent, defineConfig, diagnosePerformance, email, environment, escapeHtmlAttr, escapeHtmlText, flushSync, focusById, focusFirst, formatDiagnosticContext, frameworkError, globalEventBus, headingUpdate, hydrateGraph, inputUpdate, inspectApplication, inspectContext, inspectForm, inspectGraph, inspectI18n, inspectResource, inspectRouter, inspectSignal, interpolate, matchPattern, matchRoutes, maxLength, minLength, mountGraph, mountNode, mountRouter, nodeTypeStats, normalizePath, patchNode, patchProp, pattern, printDiagnostics, printGraph, reconcileChildren, reconcileChildrenByPlan, reportDiagnostic, required, resolveTag, resource, routerOutlet, runValidators, scheduleImmediate, scheduleUpdate, scheduler, serializeChildren, serializeServerNode, splitTarget, textUpdate, toIdToken, transformGraph, validateGraph, wireEvents, wireReactiveList, wireSignalBindings };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "1.0.0";
2
+ var VERSION = "1.2.0";
3
3
 
4
4
  // ../state/src/signal.ts
5
5
  var _activeConsumer = null;
@@ -801,6 +801,7 @@ var ApplicationGraph = class {
801
801
  this.handlers.delete(`__signal__${ref.signalId}`);
802
802
  }
803
803
  this.handlers.delete(`__listbuild__${node.id}`);
804
+ this.handlers.delete(`__listplan__${node.id}`);
804
805
  }
805
806
  // ── Handler registry ──────────────────────────────────────────────────────
806
807
  registerHandler(key, fn) {
@@ -1096,16 +1097,38 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
1096
1097
  const itemKey = reactiveListItemKey(item, index);
1097
1098
  const itemNode = graph.createNode("list-item", {
1098
1099
  key: itemKey,
1099
- props: { key: itemKey, _sig: reactiveListItemSignature(item) }
1100
+ // `_item` records the source item *reference* so the reconciler can
1101
+ // short-circuit unchanged rows by identity (no signature hashing); `_sig`
1102
+ // is the content signature used to detect an in-place data change when the
1103
+ // reference differs. Both are internal metadata (leading `_`) and never
1104
+ // reach the DOM.
1105
+ props: {
1106
+ key: itemKey,
1107
+ _sig: reactiveListItemSignature(item),
1108
+ // The item reference is stored as opaque internal metadata (never
1109
+ // rendered); cast through `unknown` since `T` is not a `PropValue`.
1110
+ _item: item
1111
+ }
1100
1112
  });
1101
1113
  renderItem(item, index, new ContainerBuilderImpl(itemNode, graph));
1102
1114
  return itemNode;
1103
1115
  };
1104
- const buildAll = (raw) => {
1116
+ const buildPlan = (raw) => {
1105
1117
  const arr = Array.isArray(raw) ? raw : [];
1106
- return arr.map((item, i) => buildItem(item, i));
1118
+ const plan = new Array(arr.length);
1119
+ for (let i = 0; i < arr.length; i++) {
1120
+ const item = arr[i];
1121
+ const index = i;
1122
+ plan[i] = {
1123
+ key: reactiveListItemKey(item, index),
1124
+ item,
1125
+ sig: () => reactiveListItemSignature(item),
1126
+ build: () => buildItem(item, index)
1127
+ };
1128
+ }
1129
+ return plan;
1107
1130
  };
1108
- graph.registerHandler(`__listbuild__${node.id}`, buildAll);
1131
+ graph.registerHandler(`__listplan__${node.id}`, buildPlan);
1109
1132
  const current = isSignal(items) ? items.peek() : items;
1110
1133
  const initial = Array.isArray(current) ? current : [];
1111
1134
  initial.forEach((item, i) => {
@@ -2250,6 +2273,7 @@ function patchProp(dom, element, name, oldValue, newValue) {
2250
2273
 
2251
2274
  // ../renderer/src/events.ts
2252
2275
  function wireEvents(dom, graph, node, element, instance) {
2276
+ if (node.events.length === 0) return;
2253
2277
  for (const eventDesc of node.events) {
2254
2278
  const handler = graph.getHandler(eventDesc.handlerKey);
2255
2279
  if (handler === void 0) continue;
@@ -2379,6 +2403,109 @@ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
2379
2403
  reorderDom(ctx, parentDom, newInstances);
2380
2404
  return { instances: newInstances, removed };
2381
2405
  }
2406
+ function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
2407
+ const oldByKey = /* @__PURE__ */ new Map();
2408
+ for (const inst of oldInstances) {
2409
+ oldByKey.set(inst.graphNode.key ?? inst.graphNode.id, inst);
2410
+ }
2411
+ const newInstances = [];
2412
+ const usedKeys = /* @__PURE__ */ new Set();
2413
+ const built = [];
2414
+ for (const entry of plan) {
2415
+ const existing = oldByKey.get(entry.key);
2416
+ if (existing !== void 0) {
2417
+ usedKeys.add(entry.key);
2418
+ const oldItem = existing.graphNode.getProp("_item");
2419
+ if (!Object.is(oldItem, entry.item)) {
2420
+ const newSig = entry.sig();
2421
+ const oldSig = existing.graphNode.getProp("_sig");
2422
+ if (!Object.is(oldSig, newSig)) {
2423
+ const freshNode = entry.build();
2424
+ built.push(freshNode);
2425
+ patchExistingInstance(ctx, existing, freshNode);
2426
+ reconcileItemChildren(ctx, existing, freshNode, mountFn);
2427
+ existing.graphNode.setProp("_sig", newSig);
2428
+ }
2429
+ existing.graphNode.setProp("_item", entry.item);
2430
+ }
2431
+ newInstances.push(existing);
2432
+ } else {
2433
+ const freshNode = entry.build();
2434
+ built.push(freshNode);
2435
+ const inst = mountFn(freshNode, parentDom);
2436
+ newInstances.push(inst);
2437
+ }
2438
+ }
2439
+ const removed = [];
2440
+ for (const inst of oldInstances) {
2441
+ const key = inst.graphNode.key ?? inst.graphNode.id;
2442
+ if (!usedKeys.has(key)) removed.push(inst);
2443
+ }
2444
+ for (const inst of removed) {
2445
+ const parent = ctx.dom.parentNode(inst.domNode);
2446
+ if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
2447
+ inst.dispose();
2448
+ }
2449
+ reorderDomMinimal(ctx, parentDom, oldInstances, newInstances);
2450
+ return { instances: newInstances, removed, built };
2451
+ }
2452
+ function reorderDomMinimal(ctx, parentDom, oldInstances, newInstances) {
2453
+ const n = newInstances.length;
2454
+ if (n === 0) return;
2455
+ const oldIndexOf = /* @__PURE__ */ new Map();
2456
+ for (let i = 0; i < oldInstances.length; i++) oldIndexOf.set(oldInstances[i], i);
2457
+ const source = new Array(n);
2458
+ let moved = false;
2459
+ let lastSeen = -1;
2460
+ for (let i = 0; i < n; i++) {
2461
+ const oi = oldIndexOf.get(newInstances[i]);
2462
+ if (oi === void 0) {
2463
+ source[i] = -1;
2464
+ moved = true;
2465
+ } else {
2466
+ source[i] = oi;
2467
+ if (oi < lastSeen) moved = true;
2468
+ else lastSeen = oi;
2469
+ }
2470
+ }
2471
+ if (!moved) return;
2472
+ const keep = longestIncreasingSubsequence(source);
2473
+ let refNode = null;
2474
+ for (let i = n - 1; i >= 0; i--) {
2475
+ const domNode = newInstances[i].domNode;
2476
+ if (source[i] === -1 || !keep.has(i)) {
2477
+ if (ctx.dom.nextSibling(domNode) !== refNode) {
2478
+ ctx.dom.insertBefore(parentDom, domNode, refNode);
2479
+ }
2480
+ }
2481
+ refNode = domNode;
2482
+ }
2483
+ }
2484
+ function longestIncreasingSubsequence(source) {
2485
+ const keep = /* @__PURE__ */ new Set();
2486
+ const n = source.length;
2487
+ const tails = [];
2488
+ const prev = new Array(n).fill(-1);
2489
+ for (let i = 0; i < n; i++) {
2490
+ const v = source[i];
2491
+ if (v < 0) continue;
2492
+ let lo = 0;
2493
+ let hi = tails.length;
2494
+ while (lo < hi) {
2495
+ const mid = lo + hi >> 1;
2496
+ if (source[tails[mid]] < v) lo = mid + 1;
2497
+ else hi = mid;
2498
+ }
2499
+ if (lo > 0) prev[i] = tails[lo - 1];
2500
+ tails[lo] = i;
2501
+ }
2502
+ let idx = tails.length > 0 ? tails[tails.length - 1] : -1;
2503
+ while (idx >= 0) {
2504
+ keep.add(idx);
2505
+ idx = prev[idx];
2506
+ }
2507
+ return keep;
2508
+ }
2382
2509
  function reconcileItemChildren(ctx, itemInstance, newItemNode, mountFn) {
2383
2510
  const el = itemInstance.domNode;
2384
2511
  if (!ctx.dom.isElement(el)) return;
@@ -2480,10 +2607,12 @@ function mountNode(ctx, graphNode, parentDom) {
2480
2607
  const textNode = dom.createTextNode(text);
2481
2608
  dom.appendChild(el2, textNode);
2482
2609
  applyNodeProps(ctx, graphNode, el2);
2483
- wireEvents(dom, graph, graphNode, el2, new NodeInstance(graphNode, el2));
2484
2610
  const instance2 = new NodeInstance(graphNode, el2);
2485
2611
  ctx.instances.set(graphNode.id, instance2);
2486
- wireSignalBindings(ctx, graphNode, instance2, textUpdate(dom, el2, textNode));
2612
+ wireEvents(dom, graph, graphNode, el2, instance2);
2613
+ if (graphNode.stateRefs.length !== 0) {
2614
+ wireSignalBindings(ctx, graphNode, instance2, textUpdate(dom, el2, textNode));
2615
+ }
2487
2616
  dom.appendChild(parentDom, el2);
2488
2617
  return instance2;
2489
2618
  }
@@ -2497,7 +2626,9 @@ function mountNode(ctx, graphNode, parentDom) {
2497
2626
  const instance2 = new NodeInstance(graphNode, el2);
2498
2627
  ctx.instances.set(graphNode.id, instance2);
2499
2628
  wireEvents(dom, graph, graphNode, el2, instance2);
2500
- wireSignalBindings(ctx, graphNode, instance2, headingUpdate(dom, el2));
2629
+ if (graphNode.stateRefs.length !== 0) {
2630
+ wireSignalBindings(ctx, graphNode, instance2, headingUpdate(dom, el2));
2631
+ }
2501
2632
  dom.appendChild(parentDom, el2);
2502
2633
  return instance2;
2503
2634
  }
@@ -2513,7 +2644,9 @@ function mountNode(ctx, graphNode, parentDom) {
2513
2644
  const instance2 = new NodeInstance(graphNode, el2);
2514
2645
  ctx.instances.set(graphNode.id, instance2);
2515
2646
  wireEvents(dom, graph, graphNode, el2, instance2);
2516
- wireSignalBindings(ctx, graphNode, instance2, inputUpdate(dom, el2));
2647
+ if (graphNode.stateRefs.length !== 0) {
2648
+ wireSignalBindings(ctx, graphNode, instance2, inputUpdate(dom, el2));
2649
+ }
2517
2650
  dom.appendChild(parentDom, el2);
2518
2651
  return instance2;
2519
2652
  }
@@ -2561,7 +2694,9 @@ function mountNode(ctx, graphNode, parentDom) {
2561
2694
  const instance2 = new NodeInstance(graphNode, el2);
2562
2695
  ctx.instances.set(graphNode.id, instance2);
2563
2696
  wireEvents(dom, graph, graphNode, el2, instance2);
2564
- wireSignalBindings(ctx, graphNode, instance2, buttonUpdate(dom, el2));
2697
+ if (graphNode.stateRefs.length !== 0) {
2698
+ wireSignalBindings(ctx, graphNode, instance2, buttonUpdate(dom, el2));
2699
+ }
2565
2700
  dom.appendChild(parentDom, el2);
2566
2701
  return instance2;
2567
2702
  }
@@ -2588,11 +2723,11 @@ function mountNode(ctx, graphNode, parentDom) {
2588
2723
  dom.setAttribute(el, "data-streetui-key", String(itemKey));
2589
2724
  }
2590
2725
  }
2591
- if (graphNode.type === "form") {
2592
- wireEvents(dom, graph, graphNode, el, new NodeInstance(graphNode, el));
2593
- }
2594
2726
  const instance = new NodeInstance(graphNode, el);
2595
2727
  ctx.instances.set(graphNode.id, instance);
2728
+ if (graphNode.type === "form") {
2729
+ wireEvents(dom, graph, graphNode, el, instance);
2730
+ }
2596
2731
  for (const child of graphNode.children) {
2597
2732
  const childInstance = mountNode(ctx, child, el);
2598
2733
  instance.addChild(childInstance);
@@ -2643,12 +2778,15 @@ function buttonUpdate(dom, el) {
2643
2778
  };
2644
2779
  }
2645
2780
  function applyNodeProps(ctx, graphNode, el) {
2646
- for (const [key, value] of Object.entries(graphNode.props)) {
2781
+ const props = graphNode.props;
2782
+ for (const key in props) {
2783
+ if (!Object.hasOwn(props, key)) continue;
2647
2784
  if (SKIP_PROP_KEYS.has(key)) continue;
2648
- applyProp(ctx.dom, el, key, value);
2785
+ applyProp(ctx.dom, el, key, props[key]);
2649
2786
  }
2650
2787
  }
2651
2788
  function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
2789
+ if (graphNode.stateRefs.length === 0) return;
2652
2790
  for (const stateRef of graphNode.stateRefs) {
2653
2791
  const signalKey = `__signal__${stateRef.signalId}`;
2654
2792
  const maybeSig = ctx.graph.getHandler(signalKey);
@@ -2660,18 +2798,45 @@ function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
2660
2798
  }
2661
2799
  }
2662
2800
  function wireReactiveList(ctx, graphNode, instance, el) {
2801
+ const plan = ctx.graph.getHandler(`__listplan__${graphNode.id}`);
2663
2802
  const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
2664
- if (build === void 0) return;
2803
+ if (plan === void 0 && build === void 0) return;
2665
2804
  for (const stateRef of graphNode.stateRefs) {
2666
2805
  if (stateRef.propKey !== "items") continue;
2667
2806
  const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
2668
2807
  if (sig === void 0 || typeof sig.subscribe !== "function") continue;
2669
2808
  const unsub = sig.subscribe((value) => {
2670
- reconcileReactiveList(ctx, graphNode, instance, el, build(value));
2809
+ if (plan !== void 0) {
2810
+ reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value));
2811
+ } else {
2812
+ reconcileReactiveList(ctx, graphNode, instance, el, build(value));
2813
+ }
2671
2814
  });
2672
2815
  instance.trackCleanup(unsub);
2673
2816
  }
2674
2817
  }
2818
+ function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan) {
2819
+ const oldInstances = [...listInstance.children];
2820
+ const result = reconcileChildrenByPlan(
2821
+ ctx,
2822
+ listEl,
2823
+ oldInstances,
2824
+ plan,
2825
+ (node, parent) => mountNode(ctx, node, parent)
2826
+ );
2827
+ listInstance.children.length = 0;
2828
+ for (const inst of result.instances) listInstance.children.push(inst);
2829
+ for (const removed of result.removed) {
2830
+ forgetInstance(ctx, removed);
2831
+ ctx.graph.detachNode(removed.graphNode);
2832
+ }
2833
+ const adopted = new Set(result.instances.map((i) => i.graphNode));
2834
+ for (const node of result.built ?? []) {
2835
+ if (!adopted.has(node)) ctx.graph.detachNode(node);
2836
+ }
2837
+ for (const child of [...listNode.children]) listNode.removeChild(child);
2838
+ for (const inst of result.instances) listNode.appendChild(inst.graphNode);
2839
+ }
2675
2840
  function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
2676
2841
  const oldInstances = [...listInstance.children];
2677
2842
  const result = reconcileChildren(
@@ -2751,14 +2916,18 @@ function hydrateNode(ctx, graphNode, domNode, path) {
2751
2916
  const instance = new NodeInstance(graphNode, domNode);
2752
2917
  ctx.instances.set(graphNode.id, instance);
2753
2918
  wireEvents(dom, graph, graphNode, domNode, instance);
2754
- wireSignalBindings(ctx, graphNode, instance, textUpdate(dom, domNode, textNode));
2919
+ if (graphNode.stateRefs.length !== 0) {
2920
+ wireSignalBindings(ctx, graphNode, instance, textUpdate(dom, domNode, textNode));
2921
+ }
2755
2922
  return instance;
2756
2923
  }
2757
2924
  case "heading": {
2758
2925
  const instance = new NodeInstance(graphNode, domNode);
2759
2926
  ctx.instances.set(graphNode.id, instance);
2760
2927
  wireEvents(dom, graph, graphNode, domNode, instance);
2761
- wireSignalBindings(ctx, graphNode, instance, headingUpdate(dom, domNode));
2928
+ if (graphNode.stateRefs.length !== 0) {
2929
+ wireSignalBindings(ctx, graphNode, instance, headingUpdate(dom, domNode));
2930
+ }
2762
2931
  return instance;
2763
2932
  }
2764
2933
  case "input": {
@@ -2767,14 +2936,18 @@ function hydrateNode(ctx, graphNode, domNode, path) {
2767
2936
  const value = graphNode.getProp("value");
2768
2937
  if (value !== void 0) dom.setProperty(domNode, "value", String(value));
2769
2938
  wireEvents(dom, graph, graphNode, domNode, instance);
2770
- wireSignalBindings(ctx, graphNode, instance, inputUpdate(dom, domNode));
2939
+ if (graphNode.stateRefs.length !== 0) {
2940
+ wireSignalBindings(ctx, graphNode, instance, inputUpdate(dom, domNode));
2941
+ }
2771
2942
  return instance;
2772
2943
  }
2773
2944
  case "button": {
2774
2945
  const instance = new NodeInstance(graphNode, domNode);
2775
2946
  ctx.instances.set(graphNode.id, instance);
2776
2947
  wireEvents(dom, graph, graphNode, domNode, instance);
2777
- wireSignalBindings(ctx, graphNode, instance, buttonUpdate(dom, domNode));
2948
+ if (graphNode.stateRefs.length !== 0) {
2949
+ wireSignalBindings(ctx, graphNode, instance, buttonUpdate(dom, domNode));
2950
+ }
2778
2951
  return instance;
2779
2952
  }
2780
2953
  case "image":
@@ -2810,10 +2983,11 @@ function hydrateChildren(ctx, parentGraphNode, parentInstance, parentDom, parent
2810
2983
  const expected = parentGraphNode.children;
2811
2984
  const actual = elementChildren(ctx, parentDom);
2812
2985
  let cursor = 0;
2986
+ const diag = ctx.hydrationDiagnostics !== void 0;
2813
2987
  for (let i = 0; i < expected.length; i++) {
2814
2988
  const childNode = expected[i];
2815
2989
  const want = expectedTag(ctx, childNode);
2816
- const childPath = `${parentPath} / ${childNode.type}[${i}]`;
2990
+ const childPath = diag ? `${parentPath} / ${childNode.type}[${i}]` : parentPath;
2817
2991
  const actualEl = actual[cursor];
2818
2992
  if (actualEl !== void 0 && ctx.dom.isElement(actualEl) && ctx.dom.tagName(actualEl) === want) {
2819
2993
  const inst = hydrateNode(ctx, childNode, actualEl, childPath);
@@ -3665,6 +3839,10 @@ function inspectApplication(compiled) {
3665
3839
  const errors = diags.filter((d) => d.severity === "error").length;
3666
3840
  const perfAcc = { totalNodes: 0, maxDepth: 0, eventHandlers: 0, stateBindings: 0, largestChildCount: 0 };
3667
3841
  collectPerf(graph, signals.size, perfAcc);
3842
+ let reactiveLists = 0;
3843
+ for (const key of compiled.graph.handlers.keys()) {
3844
+ if (key.startsWith("__listplan__")) reactiveLists += 1;
3845
+ }
3668
3846
  return {
3669
3847
  identity: {
3670
3848
  name: compiled.name,
@@ -3686,7 +3864,8 @@ function inspectApplication(compiled) {
3686
3864
  eventHandlers: perfAcc.eventHandlers,
3687
3865
  stateBindings: perfAcc.stateBindings,
3688
3866
  distinctSignals: signals.size,
3689
- largestChildCount: perfAcc.largestChildCount
3867
+ largestChildCount: perfAcc.largestChildCount,
3868
+ reactiveLists
3690
3869
  }
3691
3870
  };
3692
3871
  }
@@ -4084,6 +4263,7 @@ export {
4084
4263
  reactiveListItemSignature,
4085
4264
  readState,
4086
4265
  reconcileChildren,
4266
+ reconcileChildrenByPlan,
4087
4267
  renderToString,
4088
4268
  reportDiagnostic,
4089
4269
  required,