streetui 1.9.0 → 2.0.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.ts CHANGED
@@ -1,9 +1,8 @@
1
- import { c as Signal, d as Subscriber, U as Unsubscribe, R as ReadonlySignal, A as ApplicationId, D as DiagnosticCollector, e as ApplicationGraph, G as GraphNode, C as CompiledApplication, f as SemanticNodeType, P as PageDSL, T as TransitionConfig, g as ContainerDSL, h as SignalKind } from './compile-DqsMQVJk.js';
2
- export { i as A11yOptions, j as AppBuilder, k as AppDSL, l as AppOptions, m as ApplicationGraphOptions, B as BaseNode, n as Bindable, o as BindableText, p as BoundInputOptions, q as ButtonOptions, r as CompileOptions, b as ComponentChildren, s as ComponentContext, a as ComponentDefinition, t as ComponentRender, u as ComponentSetup, b as ContainerBuilder, v as ContainerBuilderImpl, w as ContainerOptions, x as ContentDSL, y as ControlledInputOptions, z as DerivedSignal, E as Diagnostic, F as DiagnosticError, H as DiagnosticLocation, I as DiagnosticSeverity, J as ErrorBoundaryOptions, K as ErrorFallbackBuilder, L as ErrorSource, M as EventDescriptor, N as FormBuilder, O as FormBuilderImpl, Q as FormDSL, W as FormOptions, X as GraphNodeData, Y as HandlerFn, Z as HeadingOptions, _ as ImageOptions, $ as InputOptions, a0 as InputOptionsBase, a1 as LinkOptions, a2 as ListBuilder, a3 as ListBuilderImpl, a4 as ListDSL, a5 as ListOptions, a6 as ListPlanEntry, a7 as NodeId, a8 as NodeMetadata, a9 as OverlayOptions, aa as PageBuilder, ab as PageBuilderImpl, ac as PortalOptions, ad as PropValue, ae as Props, af as ReactiveConsumer, ag as ReactiveSource, ah as ResolvedTransition, ai as SectionBuilder, aj as SectionBuilderImpl, ak as SectionDSL, al as SectionOptions, am as SerializedGraph, an as SerializedNode, ao as StateRef, S as StreetApp, ap as StreetUI, aq as TextOptions, ar as TextValue, V as VERSION, as as WhenOptions, at as batch, au as compile, av as compileGraph, aw as component, ax as createNodeId, ay as derived, az as effect, aA as formatDiagnostic, aB as generateApplicationId, aC as generateNodeId, aD as isBatching, aE as isComponentDefinition, aF as isTransitionConfig, aG as nextId, aH as nodeIdPrefix, aI as observerCount, aJ as reactiveListItemKey, aK as reactiveListItemSignature, aL as resetIdCounter, aM as resolveTransition, aN as signal, aO as signalKind, aP as streetui } from './compile-DqsMQVJk.js';
3
- import { R as RenderHandle, S as StreetRenderer, a as HydrationDiagnosticSink } from './hydration-diagnostics-B2FmY05M.js';
4
- export { H as HydrationDiagnostic, b as HydrationMismatchType, c as consoleHydrationDiagnosticSink, d as createHydrationDiagnosticCollector, f as formatHydrationDiagnostic } from './hydration-diagnostics-B2FmY05M.js';
5
- import { D as DOMAdapter } from './server-BYnmb_hR.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-BYnmb_hR.js';
1
+ import { c as Signal, d as Subscriber, U as Unsubscribe, R as ReadonlySignal, e as ResourceStatus, f as ResourceOptions, g as Resource, h as ResourceLoaderContext, A as ApplicationId, D as DiagnosticCollector, i as ApplicationGraph, G as GraphNode, C as CompiledApplication, j as HydrationDiagnosticSink, k as SemanticNodeType, P as PageDSL, T as TransitionConfig, l as ContainerDSL, m as SignalKind } from './hydration-diagnostics-CFYDXBlm.js';
2
+ export { n as A11yOptions, o as AppBuilder, p as AppDSL, q as AppOptions, r as ApplicationGraphOptions, s as AsyncBoundaryBranches, B as BaseNode, t as Bindable, u as BindableString, v as BindableText, w as BoundInputOptions, x as ButtonOptions, y as CompileOptions, b as ComponentChildren, z as ComponentContext, a as ComponentDefinition, E as ComponentRender, F as ComponentSetup, b as ContainerBuilder, I as ContainerBuilderImpl, J as ContainerOptions, K as ContentDSL, L as ControlledInputOptions, M as DerivedSignal, N as Diagnostic, O as DiagnosticError, Q as DiagnosticLocation, W as DiagnosticSeverity, X as ErrorBoundaryOptions, Y as ErrorFallbackBuilder, Z as ErrorSource, _ as EventDescriptor, $ as FormBuilder, a0 as FormBuilderImpl, a1 as FormDSL, a2 as FormOptions, a3 as GraphNodeData, a4 as HandlerFn, a5 as HeadContribution, a6 as HeadEntry, a7 as HeadMetadata, a8 as HeadingOptions, H as HydrationDiagnostic, a9 as HydrationMismatchType, aa as ImageOptions, ab as InputOptions, ac as InputOptionsBase, ad as LinkDescriptor, ae as LinkOptions, af as ListBuilder, ag as ListBuilderImpl, ah as ListDSL, ai as ListOptions, aj as ListPlanEntry, ak as MetaDescriptor, al as NodeId, am as NodeMetadata, an as OverlayOptions, ao as PageBuilder, ap as PageBuilderImpl, aq as PortalOptions, ar as PropValue, as as Props, at as ReactiveConsumer, au as ReactiveSource, av as ResolvedTransition, aw as ResourceLoader, ax as SectionBuilder, ay as SectionBuilderImpl, az as SectionDSL, aA as SectionOptions, aB as SerializedGraph, aC as SerializedNode, aD as StateRef, S as StreetApp, aE as StreetUI, aF as TextOptions, aG as TextValue, V as VERSION, aH as WhenOptions, aI as batch, aJ as compile, aK as compileGraph, aL as component, aM as consoleHydrationDiagnosticSink, aN as createHydrationDiagnosticCollector, aO as createNodeId, aP as derived, aQ as effect, aR as formatDiagnostic, aS as formatHydrationDiagnostic, aT as generateApplicationId, aU as generateNodeId, aV as isBatching, aW as isComponentDefinition, aX as isHeadContribution, aY as isTransitionConfig, aZ as nextId, a_ as nodeIdPrefix, a$ as observerCount, b0 as reactiveListItemKey, b1 as reactiveListItemSignature, b2 as resetIdCounter, b3 as resolveHead, b4 as resolveTransition, b5 as resource, b6 as signal, b7 as signalKind, b8 as streetui } from './hydration-diagnostics-CFYDXBlm.js';
3
+ import { L as Lifecycle, C as CleanupRegistry, D as DOMAdapter, N as NodeInstance, R as RenderContext } from './server-C0PvFBzD.js';
4
+ export { H as HeadManager, a as LifecycleHook, b as LifecyclePhase, c as RenderToStringOptions, S as STATE_MARKER_ATTR, d as ServerDOMAdapter, e as createRenderContext, r as readState, f as renderHead, g as renderToString, s as serializeState, h as serverDOMAdapter, w as wireHeadBehavior } from './server-C0PvFBzD.js';
5
+ import { R as RenderHandle, S as StreetRenderer } from './renderer-interface-B_-hWnKk.js';
7
6
 
8
7
  /**
9
8
  * A simple reactive store built on top of signals.
@@ -23,84 +22,229 @@ declare class Store<T extends StoreState> {
23
22
  declare function createStore<T extends StoreState>(initial: T): Store<T>;
24
23
 
25
24
  /**
26
- * StreetUI async resources — framework-native asynchronous data.
25
+ * StreetUI mutations — framework-native asynchronous *writes*.
27
26
  *
28
- * A `resource` wraps a Promise-returning loader and exposes its lifecycle as
29
- * ordinary StreetUI signals (status / data / error), so it composes with
30
- * `derived`, `effect`, `when()`, `listOf` and the renderer with no second
31
- * reactive system.
27
+ * A `resource` models a read: a loader that runs on creation / on demand and
28
+ * whose value the UI observes. A `mutation` is its write-side counterpart: an
29
+ * explicit, argument-taking async action (create / update / delete, a form
30
+ * submit, a "mark as done" click) whose lifecycle is exposed as ordinary
31
+ * StreetUI signals so it composes with `derived`, `when()`, and the renderer
32
+ * with NO second reactive system.
32
33
  *
33
- * State machine:
34
+ * State machine (mirrors `resource`, but only ever advances on an explicit
35
+ * `mutate()` — a mutation never runs on its own):
34
36
  *
35
- * idle ──(load)──▶ loading ──(resolve)──▶ success
36
- * │
37
- * └────(reject)──────▶ error
37
+ * idle ──(mutate)──▶ loading ──(resolve)──▶ success
38
+ * │
39
+ * └────(reject)──────▶ error
38
40
  *
39
- * Refetch keeps the previously-loaded `data` visible while `status` is
40
- * `'loading'` again (see `isRefetching`) — there is no separate `'refetching'`
41
- * status; it is expressed through `status === 'loading'` with `data` still set.
42
- *
43
- * The resource is transport-agnostic: the loader is any function returning a
44
- * value or a Promise. When it accepts the provided `AbortSignal`, in-flight
45
- * work is cancelled on `dispose()` or when a newer request supersedes it.
41
+ * There is deliberately NO global cache and NO automatic invalidation registry
42
+ * (that would be a second state system with its own lifetime and coherency
43
+ * rules). Invalidation is explicit and local: pass an `onSuccess` that calls the
44
+ * `refetch()` of whichever resources the write affected. This keeps data flow
45
+ * one-directional and readable — the write says exactly what it invalidates.
46
46
  */
47
47
 
48
- type ResourceStatus = 'idle' | 'loading' | 'success' | 'error';
49
- /** Context handed to the loader; carries an AbortSignal for cancellation. */
50
- interface ResourceLoaderContext {
51
- readonly signal: AbortSignal;
52
- }
53
- /** Any value-or-Promise producing function. Receives an abort-aware context. */
54
- type ResourceLoader<T> = (ctx: ResourceLoaderContext) => Promise<T> | T;
55
- interface ResourceOptions<T = unknown> {
56
- /** Load immediately on creation. Defaults to `true`. When `false`, stays `idle` until `refetch()`. */
57
- readonly immediate?: boolean;
48
+ /** A mutation shares the resource status vocabulary (idle/loading/success/error). */
49
+ type MutationStatus = ResourceStatus;
50
+ /** The async action a mutation runs. Receives the caller's argument. */
51
+ type Mutator<TArgs, TResult> = (args: TArgs) => Promise<TResult> | TResult;
52
+ interface MutationOptions<TArgs, TResult> {
58
53
  /**
59
- * Explicit reactive dependencies. When any listed signal changes, the
60
- * resource refetches. Dependencies are explicit (not auto-tracked from the
61
- * loader body) so there is no risk of an accidental infinite refetch loop.
54
+ * Run after a successful mutation, before `mutate()`'s promise resolves. The
55
+ * natural place to invalidate reads: call the affected resources' `refetch()`.
56
+ * May be async; its completion is awaited so callers can rely on reads being
57
+ * up to date once `mutate()` resolves.
62
58
  */
63
- readonly watch?: ReadonlyArray<ReadonlySignal<unknown>>;
59
+ readonly onSuccess?: (result: TResult, args: TArgs) => void | Promise<void>;
60
+ /** Run after a failed mutation (the thrown value is passed through). */
61
+ readonly onError?: (error: unknown, args: TArgs) => void | Promise<void>;
62
+ /** Run after success OR error, once the lifecycle has settled. */
63
+ readonly onSettled?: (args: TArgs) => void | Promise<void>;
64
64
  /**
65
- * Optional teardown registrar (e.g. a route's `ctx.onCleanup`). When given,
66
- * the resource registers its own `dispose` so it is cleaned up automatically
67
- * when its owner is removed.
65
+ * Optional teardown registrar (e.g. a component's `ctx.onCleanup`). When
66
+ * given, the mutation registers its own `dispose` so late results from an
67
+ * in-flight `mutate()` are ignored once the owner is removed.
68
68
  */
69
69
  readonly onCleanup?: (fn: () => void) => void;
70
+ }
71
+ interface Mutation<TArgs, TResult> {
72
+ /** Reactive lifecycle status. */
73
+ readonly status: ReadonlySignal<MutationStatus>;
74
+ /** The most recent successful result, or `undefined` before first success. */
75
+ readonly data: ReadonlySignal<TResult | undefined>;
76
+ /** The most recent error, or `undefined` when there is none. Typed `unknown`. */
77
+ readonly error: ReadonlySignal<unknown>;
78
+ /** Convenience: `status === 'loading'` (an in-flight write). */
79
+ readonly pending: ReadonlySignal<boolean>;
70
80
  /**
71
- * Server-provided initial value for hydration. When present the resource
72
- * starts in `'success'` with this data already visible, and the initial
73
- * auto-load is skipped (so the client does not refetch data the server
74
- * already resolved). This is the client half of SSR resource transfer; the
75
- * server side awaits `refetch()` before serializing. Set `immediate: true`
76
- * explicitly to force a client refetch anyway.
81
+ * Run the mutation. Resolves with the result on success. On failure the
82
+ * rejection is surfaced through `error`/`status` AND re-thrown, so a caller
83
+ * that wants to react imperatively can `try/catch`; a caller that only wants
84
+ * the reactive state can ignore the returned promise. Superseded/disposed
85
+ * runs never write state (race guard), matching `resource`.
77
86
  */
78
- readonly initialData?: T;
79
- /** Server-provided initial error for hydration (mirrors `initialData`). */
80
- readonly initialError?: unknown;
87
+ mutate(args: TArgs): Promise<TResult>;
88
+ /** Reset back to `idle` with no data/error. */
89
+ reset(): void;
90
+ /** Ignore any in-flight result and mark the mutation inert. Idempotent. */
91
+ dispose(): void;
92
+ }
93
+ /**
94
+ * Create a {@link Mutation}. The zero-argument form is written
95
+ * `mutation<void, T>(() => …)` and invoked as `mutate(undefined)`.
96
+ */
97
+ declare function mutation<TArgs, TResult>(mutator: Mutator<TArgs, TResult>, options?: MutationOptions<TArgs, TResult>): Mutation<TArgs, TResult>;
98
+
99
+ /**
100
+ * Optional HTTP data client (2.0 §17) — the integration path between StreetUI's
101
+ * transport-agnostic `resource`/`mutation` primitives and a real backend (a
102
+ * StreetJS server, or any HTTP/JSON API).
103
+ *
104
+ * This is deliberately OPTIONAL and dependency-free: it imports nothing from
105
+ * StreetJS (or any server framework), so StreetUI's core stays independent — an
106
+ * app that never calls `createClient` never pays for it, and StreetUI does not
107
+ * take on a backend dependency. It is a thin, honest convenience over the
108
+ * standard `fetch`: URL joining, JSON encode/decode, header merging, abort
109
+ * propagation, and a typed error. All state still flows through the existing
110
+ * `resource`/`mutation` signals — there is no cache and no second data system.
111
+ *
112
+ * ```ts
113
+ * const api = createClient({ baseUrl: '/api' });
114
+ * const users = api.resource<User[]>('/users'); // a read
115
+ * const create = api.mutation<NewUser, User>('POST', '/users', {
116
+ * onSuccess: () => users.refetch(), // explicit invalidation
117
+ * });
118
+ * ```
119
+ */
120
+
121
+ /** A `fetch`-compatible function. Injectable for tests / non-browser runtimes. */
122
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
123
+ interface ClientConfig {
124
+ /** Prefix joined to every request path (e.g. `/api` or `https://x/api`). */
125
+ readonly baseUrl?: string;
126
+ /** Headers merged into every request (per-request headers win). */
127
+ readonly headers?: Readonly<Record<string, string>>;
128
+ /**
129
+ * The fetch implementation to use. Defaults to the global `fetch`. Injecting
130
+ * one keeps the client testable and usable where no global fetch exists.
131
+ */
132
+ readonly fetch?: FetchLike;
133
+ }
134
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
135
+ interface RequestConfig {
136
+ /** Extra headers for this request (merged over the client's). */
137
+ readonly headers?: Readonly<Record<string, string>>;
138
+ /** Abort signal — pass a resource/mutation loader's `ctx.signal` for cancellation. */
139
+ readonly signal?: AbortSignal;
140
+ /** Query parameters appended to the URL. */
141
+ readonly query?: Readonly<Record<string, string | number | boolean>>;
142
+ }
143
+ /**
144
+ * A failed HTTP response (non-2xx). Carries the status and the parsed body when
145
+ * one was returned. NOTE: the body may contain server-supplied detail; the §7
146
+ * error reporter never enumerates an error's own-properties, so `HttpError.body`
147
+ * never leaks into a diagnostics report unless an app deliberately reads it.
148
+ */
149
+ declare class HttpError extends Error {
150
+ readonly status: number;
151
+ readonly statusText: string;
152
+ readonly url: string;
153
+ readonly body: unknown;
154
+ constructor(status: number, statusText: string, url: string, body: unknown);
155
+ }
156
+ interface Client {
157
+ /** Issue a request and return the parsed JSON body (throws `HttpError` on non-2xx). */
158
+ request<T>(method: HttpMethod, path: string, body?: unknown, config?: RequestConfig): Promise<T>;
159
+ get<T>(path: string, config?: RequestConfig): Promise<T>;
160
+ post<T>(path: string, body?: unknown, config?: RequestConfig): Promise<T>;
161
+ put<T>(path: string, body?: unknown, config?: RequestConfig): Promise<T>;
162
+ patch<T>(path: string, body?: unknown, config?: RequestConfig): Promise<T>;
163
+ del<T>(path: string, config?: RequestConfig): Promise<T>;
81
164
  /**
82
- * Explicit initial status override. Rarely needed — inferred as `'success'`
83
- * from `initialData` or `'error'` from `initialError`.
165
+ * A GET-backed {@link Resource}. The loader forwards the resource's abort
166
+ * signal, so `dispose()`/supersede cancels the request.
84
167
  */
85
- readonly initialStatus?: ResourceStatus;
168
+ resource<T>(path: string, options?: ResourceOptions<T> & {
169
+ readonly query?: RequestConfig['query'];
170
+ }): Resource<T>;
171
+ /**
172
+ * A {@link Mutation} that issues `method path` with the mutate() argument as
173
+ * the JSON body. Pair with `onSuccess` to refetch affected resources.
174
+ */
175
+ mutation<TArgs, TResult>(method: HttpMethod, path: string, options?: MutationOptions<TArgs, TResult>): Mutation<TArgs, TResult>;
176
+ }
177
+ /** Create an optional HTTP data client. Uses global `fetch` unless one is injected. */
178
+ declare function createClient(config?: ClientConfig): Client;
179
+
180
+ /**
181
+ * Optional auth session primitive (2.0 §19) — reactive authentication state for
182
+ * building sign-in UIs and protected routes, composed entirely from the existing
183
+ * `resource` (the "who am I" read) and `mutation` (logout / refresh writes).
184
+ *
185
+ * There is no new auth framework here and no credential handling: the app
186
+ * supplies a `loadUser` function (however it authenticates — a StreetJS session
187
+ * cookie, a bearer token, anything) that returns the current user or `null`. The
188
+ * primitive turns that into the states a UI switches on — `loading`,
189
+ * `authenticated`, `unauthenticated`, `error` — plus `refresh()` and `logout()`.
190
+ *
191
+ * How the pieces the spec names fit together (all EXISTING seams, no new ones):
192
+ * - loading / unauth / auth → switch UI with `when(session.authenticated, …)`
193
+ * etc. (the renderer's existing conditional).
194
+ * - refresh / logout → `session.refresh()` / `session.logout()`.
195
+ * - protected route → in a route's setup, read `session.status`; when
196
+ * `unauthenticated`, navigate to the login route (router). Throwing inside a
197
+ * guarded builder is caught by `errorBoundary` for an error fallback. This
198
+ * module stays router-agnostic so core has no router dependency — the app
199
+ * wires the navigation, exactly as with any other signal.
200
+ */
201
+
202
+ /** The four states an auth-aware UI switches on. */
203
+ type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated' | 'error';
204
+ /** Load the current user, or `null`/`undefined` when nobody is signed in. */
205
+ type LoadUser<TUser> = (ctx: ResourceLoaderContext) => Promise<TUser | null | undefined> | TUser | null | undefined;
206
+ interface AuthSessionConfig<TUser> {
207
+ /** Resolve the current user (or null when unauthenticated). Abort-aware. */
208
+ readonly loadUser: LoadUser<TUser>;
209
+ /**
210
+ * Perform the server-side logout (clear the cookie/token). Optional — when
211
+ * omitted, `logout()` just re-checks the session. After it resolves the
212
+ * session refreshes, so `loadUser` should then return null.
213
+ */
214
+ readonly logout?: () => Promise<void> | void;
215
+ /** Skip the initial load; stays `loading` until the first `refresh()`. */
216
+ readonly immediate?: boolean;
217
+ /** Teardown registrar (e.g. a component's `ctx.onCleanup`). */
218
+ readonly onCleanup?: (fn: () => void) => void;
86
219
  }
87
- interface Resource<T> {
88
- /** Reactive lifecycle status. */
89
- readonly status: ReadonlySignal<ResourceStatus>;
90
- /** The last successfully-loaded value, or `undefined` before first success. */
91
- readonly data: ReadonlySignal<T | undefined>;
92
- /** The most recent error, or `undefined` when there is none. Typed `unknown` — never `any`. */
220
+ interface AuthSession<TUser> {
221
+ /** The current coarse auth state. */
222
+ readonly status: ReadonlySignal<AuthStatus>;
223
+ /** The signed-in user, or `undefined` when not authenticated. */
224
+ readonly user: ReadonlySignal<TUser | undefined>;
225
+ /** The most recent load/logout error, or `undefined`. */
93
226
  readonly error: ReadonlySignal<unknown>;
94
- /** Convenience: `status === 'loading'`. */
227
+ /** `status === 'authenticated'`. */
228
+ readonly authenticated: ReadonlySignal<boolean>;
229
+ /** `status === 'unauthenticated'`. */
230
+ readonly unauthenticated: ReadonlySignal<boolean>;
231
+ /** `status === 'loading'` (the initial who-am-I is still in flight). */
95
232
  readonly loading: ReadonlySignal<boolean>;
96
- /** Convenience: loading while previously-loaded data is still present (a refetch). */
97
- readonly isRefetching: ReadonlySignal<boolean>;
98
- /** Trigger a new request. Resolves when the request settles (or is superseded). */
99
- refetch(): Promise<void>;
100
- /** Cancel in-flight work, drop watchers, and ignore any late results. Idempotent. */
233
+ /** True while a `logout()` is in flight. */
234
+ readonly loggingOut: ReadonlySignal<boolean>;
235
+ /** Re-run `loadUser` (e.g. after a token refresh or a focus regain). */
236
+ refresh(): Promise<void>;
237
+ /** Run the configured server logout, then refresh (→ unauthenticated). */
238
+ logout(): Promise<void>;
239
+ /** Cancel in-flight work and detach. Idempotent. */
101
240
  dispose(): void;
102
241
  }
103
- declare function resource<T>(loader: ResourceLoader<T>, options?: ResourceOptions<T>): Resource<T>;
242
+ /**
243
+ * Create an {@link AuthSession}. The session starts in `loading` and resolves to
244
+ * `authenticated`/`unauthenticated` once `loadUser` settles (unless
245
+ * `immediate: false`).
246
+ */
247
+ declare function createAuthSession<TUser>(config: AuthSessionConfig<TUser>): AuthSession<TUser>;
104
248
 
105
249
  /**
106
250
  * Deterministic accessibility id helpers.
@@ -162,33 +306,6 @@ interface A11yIds {
162
306
  */
163
307
  declare function a11yIds(base: string): A11yIds;
164
308
 
165
- /**
166
- * Application and component lifecycle primitives.
167
- *
168
- * Lifecycle phases:
169
- * created → mounted → active ⇄ updating → unmounting → destroyed
170
- */
171
- type LifecyclePhase = 'created' | 'mounted' | 'active' | 'updating' | 'unmounting' | 'destroyed';
172
- type LifecycleHook = () => void | Promise<void>;
173
- declare class Lifecycle {
174
- private _phase;
175
- private readonly _hooks;
176
- get phase(): LifecyclePhase;
177
- get isMounted(): boolean;
178
- get isDestroyed(): boolean;
179
- on(phase: LifecyclePhase, hook: LifecycleHook): () => void;
180
- transition(to: LifecyclePhase): Promise<void>;
181
- onMount(hook: LifecycleHook): () => void;
182
- onUnmount(hook: LifecycleHook): () => void;
183
- onDestroy(hook: LifecycleHook): () => void;
184
- }
185
- /** A simple cleanup registry — collect teardown functions and run them all at once. */
186
- declare class CleanupRegistry {
187
- private readonly _fns;
188
- add(fn: () => void): void;
189
- run(): void;
190
- }
191
-
192
309
  /**
193
310
  * Environment detection and capability flags.
194
311
  * The framework behaves slightly differently in browser vs. server vs. test.
@@ -272,6 +389,30 @@ interface DiagnosticContext {
272
389
  readonly route?: string;
273
390
  /** A resource identifier involved, when applicable. */
274
391
  readonly resource?: string;
392
+ /**
393
+ * The component name involved (from a `component()` definition's `name`), when
394
+ * the diagnostic arises inside a component. A stable identifier, never a value.
395
+ */
396
+ readonly component?: string;
397
+ /**
398
+ * A signal identifier involved (e.g. a named/derived signal's debug name),
399
+ * when applicable. This is the signal's *identity*, never its current value —
400
+ * signal contents may be user data and are never placed in a diagnostic.
401
+ */
402
+ readonly signal?: string;
403
+ /**
404
+ * Where in a node's lifecycle the diagnostic arose: `render` (building the
405
+ * subtree), `effect` (a reactive effect), `setup` (a component's setup),
406
+ * `loader` (a resource loader), `event` (a DOM handler), or `hydrate`.
407
+ */
408
+ readonly phase?: 'render' | 'effect' | 'setup' | 'loader' | 'event' | 'hydrate';
409
+ /**
410
+ * A non-sensitive DOM association for where the error surfaced, e.g.
411
+ * `div#app` or `button.primary`. Callers must pass only structural
412
+ * identifiers (tag / id / class) — never `textContent`, attribute *values*,
413
+ * or anything that could carry user data.
414
+ */
415
+ readonly element?: string;
275
416
  }
276
417
  /**
277
418
  * The application-provided logging seam. Every method is optional; the
@@ -305,6 +446,65 @@ declare function frameworkError(message: string, context?: DiagnosticContext): S
305
446
  declare function reportDiagnostic(sink: DiagnosticSink | undefined, level: 'debug' | 'info' | 'warn' | 'error', message: string, context?: DiagnosticContext): void;
306
447
  /** A sink that forwards to a `console`-like object, one call per level. */
307
448
  declare function consoleDiagnosticSink(logger?: Partial<Record<'debug' | 'info' | 'warn' | 'error', (msg: string) => void>>): DiagnosticSink;
449
+ /**
450
+ * Options controlling how much of an error is disclosed in a report.
451
+ *
452
+ * Everything defaults to the SAFE (production) posture: no stack, no cause
453
+ * chain. A stack trace can embed absolute file paths and, in some runtimes,
454
+ * source fragments, so it is opt-in and belongs to development / trusted server
455
+ * logging — never to a report that might reach a browser or a third party.
456
+ */
457
+ interface ErrorReportOptions {
458
+ /** Include `error.stack` in the report. Default `false` (production-safe). */
459
+ readonly includeStack?: boolean;
460
+ /**
461
+ * Follow and describe `error.cause` (recursively, up to a small depth) when
462
+ * present. Default `false`. The cause is described with the SAME redaction
463
+ * rules — only its name/message/(optional)stack, never arbitrary properties.
464
+ */
465
+ readonly includeCause?: boolean;
466
+ }
467
+ /**
468
+ * A production-safe, serializable description of an error plus the framework
469
+ * context in which it surfaced. This is what §7 asks a diagnostics sink to
470
+ * receive: enough to locate the failure (component / route / resource / signal /
471
+ * phase / DOM association via {@link DiagnosticContext}) WITHOUT any sensitive
472
+ * data. It deliberately carries ONLY the error's class name and message (both
473
+ * author-controlled), never enumerated own-properties (which frequently hold
474
+ * request bodies, tokens, or user records), and the stack only when explicitly
475
+ * opted in.
476
+ */
477
+ interface ErrorReport {
478
+ /** The error's constructor name, e.g. `TypeError` (`Error` when unknown). */
479
+ readonly name: string;
480
+ /** The error's message. For a non-Error throw, its `String(...)` form. */
481
+ readonly message: string;
482
+ /** Whether the thrown value was a real `Error` instance. */
483
+ readonly isError: boolean;
484
+ /** The framework context, when supplied. */
485
+ readonly context?: DiagnosticContext;
486
+ /** The stack, only when `includeStack` was set and one exists. */
487
+ readonly stack?: string;
488
+ /** The described cause, only when `includeCause` was set and one exists. */
489
+ readonly cause?: ErrorReport;
490
+ }
491
+ /**
492
+ * Build a {@link ErrorReport} from any thrown value and (optionally) the
493
+ * framework {@link DiagnosticContext} it surfaced in. Production-safe by
494
+ * default: no stack, no cause, no enumerated properties. This is a pure
495
+ * function — it performs no logging and has no side effects, so it is safe to
496
+ * call from any layer (an `errorBoundary` `onError`, an `asyncBoundary` error
497
+ * branch, a resource loader `catch`).
498
+ */
499
+ declare function describeError(error: unknown, context?: DiagnosticContext, options?: ErrorReportOptions): ErrorReport;
500
+ /**
501
+ * Convenience bridge: build a production-safe {@link ErrorReport} and route it
502
+ * to a {@link DiagnosticSink} at the `error` level. The sink receives the
503
+ * report's message and the structured {@link DiagnosticContext}; the full
504
+ * report is returned to the caller for forwarding elsewhere (e.g. an app's own
505
+ * crash reporter). Safe with `undefined` sink (no-op) and never throws.
506
+ */
507
+ declare function reportError(sink: DiagnosticSink | undefined, error: unknown, context?: DiagnosticContext, options?: ErrorReportOptions): ErrorReport;
308
508
 
309
509
  /**
310
510
  * Compiler-phase validation of the ApplicationGraph.
@@ -574,6 +774,7 @@ declare class BrowserDOMAdapter implements DOMAdapter {
574
774
  getElementById(id: string): Element | null;
575
775
  focus(element: Element): void;
576
776
  body(): Element | null;
777
+ head(): Element | null;
577
778
  activeElement(): Element | null;
578
779
  contains(ancestor: Element, node: Node): boolean;
579
780
  matches(element: Element, selector: string): boolean;
@@ -779,61 +980,6 @@ interface Announcer {
779
980
  */
780
981
  declare function createAnnouncer(dom: DOMAdapter): Announcer;
781
982
 
782
- /**
783
- * NodeInstance — the renderer's live counterpart to a GraphNode.
784
- *
785
- * Tracks the actual DOM node(s), all signal subscriptions that drive
786
- * targeted DOM updates, and DOM event listener teardowns.
787
- */
788
-
789
- declare class NodeInstance {
790
- readonly graphNode: GraphNode;
791
- /** The primary DOM node for this instance (element or text node). */
792
- domNode: Node;
793
- readonly children: NodeInstance[];
794
- readonly cleanup: CleanupRegistry;
795
- constructor(graphNode: GraphNode, domNode: Node);
796
- addChild(child: NodeInstance): void;
797
- /** Subscribe to a signal; auto-cleanup on unmount. */
798
- trackSignal<T>(sig: ReadonlySignal<T>, handler: (v: T) => void): void;
799
- /** Register a raw cleanup fn (DOM event removal, etc.). */
800
- trackCleanup(fn: () => void): void;
801
- dispose(): void;
802
- }
803
-
804
- /**
805
- * RenderContext — shared state for a single mount operation.
806
- *
807
- * Passed through the render pipeline so every sub-function has access
808
- * to the DOM adapter, graph, and instance map without prop-drilling.
809
- */
810
-
811
- interface RenderContext {
812
- readonly dom: DOMAdapter;
813
- readonly graph: ApplicationGraph;
814
- /** Maps GraphNode.id → its live NodeInstance */
815
- readonly instances: Map<string, NodeInstance>;
816
- /** The root container element. */
817
- readonly container: Element;
818
- /**
819
- * Optional dev-only sink that observes hydration mismatch repairs. When
820
- * absent (the default) the hydration path does no extra work — this is how
821
- * DevTools/diagnostics stay off the production runtime path.
822
- */
823
- readonly hydrationDiagnostics?: HydrationDiagnosticSink;
824
- /**
825
- * Optional SSR-only static-subtree plan (v1.7). Maps a maximal
826
- * static-subtree root GraphNode.id → its precomputed, verbatim HTML string.
827
- * Present only on the server render path when a plan has been built; on the
828
- * browser mount path it is always `undefined`, so the client hot path is
829
- * unaffected (a single `=== undefined` check short-circuits). When a mounted
830
- * node's id is in this map, the renderer emits the precomputed HTML via
831
- * `dom.createRawHTML` instead of recursively constructing the subtree.
832
- */
833
- readonly staticHTML?: ReadonlyMap<string, string>;
834
- }
835
- declare function createRenderContext(dom: DOMAdapter, graph: ApplicationGraph, container: Element, hydrationDiagnostics?: HydrationDiagnosticSink, staticHTML?: ReadonlyMap<string, string>): RenderContext;
836
-
837
983
  /**
838
984
  * Attribute and property application helpers.
839
985
  *
@@ -879,6 +1025,7 @@ declare function textUpdate(dom: DOMAdapter, el: Element, textNode: Text): (prop
879
1025
  declare function headingUpdate(dom: DOMAdapter, el: Element): (propKey: string, value: unknown) => void;
880
1026
  declare function inputUpdate(dom: DOMAdapter, el: Element): (propKey: string, value: unknown) => void;
881
1027
  declare function buttonUpdate(dom: DOMAdapter, el: Element): (propKey: string, value: unknown) => void;
1028
+ declare function linkUpdate(dom: DOMAdapter, el: Element): (propKey: string, value: unknown) => void;
882
1029
  declare function applyNodeProps(ctx: RenderContext, graphNode: GraphNode, el: Element): void;
883
1030
  declare function wireSignalBindings(ctx: RenderContext, graphNode: GraphNode, instance: NodeInstance, onUpdate: (propKey: string, value: unknown) => void): void;
884
1031
  /**
@@ -2037,12 +2184,35 @@ interface PerformancePanel {
2037
2184
  readonly snapshot: ApplicationInspection['perf'];
2038
2185
  readonly diagnostics: readonly PerfDiagnostic[];
2039
2186
  }
2187
+ /**
2188
+ * Compilation diagnostics for the DevTools "Diagnostics" panel (§8). These are
2189
+ * the compiler's own findings (errors + warnings), surfaced verbatim — not a
2190
+ * runtime error stream. Runtime/production error reports flow through the
2191
+ * separate `reportError`/`DiagnosticSink` seam in `streetui` (§7).
2192
+ */
2193
+ interface DiagnosticsPanel {
2194
+ readonly errors: number;
2195
+ readonly warnings: number;
2196
+ readonly messages: readonly string[];
2197
+ }
2040
2198
  /** All panels captured at one `refresh()`. */
2041
2199
  interface DevToolsSnapshot {
2042
2200
  readonly application: ApplicationPanel;
2043
2201
  readonly graph: InspectedNode;
2044
2202
  readonly signals: SignalsPanel;
2045
2203
  readonly performance: PerformancePanel;
2204
+ /**
2205
+ * Component tree (§9): every `component()` instance in document order, with
2206
+ * its stable key/name/location. Read structurally from the graph — the same
2207
+ * data an app/component-tree UI panel renders.
2208
+ */
2209
+ readonly components: readonly InspectedComponent[];
2210
+ /** Overlays currently wired (§13), in graph/containment order (dialog/popover/…). */
2211
+ readonly overlays: readonly InspectedOverlay[];
2212
+ /** Transitions currently wired on nodes (§14). Structural — never interferes with lifecycle. */
2213
+ readonly transitions: readonly InspectedTransition[];
2214
+ /** Compilation diagnostics (§8). */
2215
+ readonly diagnostics: DiagnosticsPanel;
2046
2216
  readonly router?: RouterInspection;
2047
2217
  readonly resources?: Readonly<Record<string, ResourceInspection>>;
2048
2218
  readonly forms?: Readonly<Record<string, FormInspection>>;
@@ -2112,6 +2282,44 @@ interface DevToolsSession {
2112
2282
  */
2113
2283
  declare function createDevTools(compiled: CompiledApplication, sources?: DevToolsSources, options?: DevToolsOptions): DevToolsSession;
2114
2284
 
2285
+ /**
2286
+ * DevTools view (2.0 §8) — a DOM-free HTML renderer for a {@link DevToolsSnapshot}.
2287
+ *
2288
+ * This is the "initial UI" layer: it turns the headless snapshot (already
2289
+ * composed from the existing read-only inspectors) into a single self-contained
2290
+ * HTML string a host can inject into a panel, an iframe, or a static report. It
2291
+ * is deliberately a *pure string builder*:
2292
+ *
2293
+ * - No DOM API is touched and nothing is mounted, so it runs anywhere (Node,
2294
+ * a worker, a test) and adds nothing to the running app.
2295
+ * - It reads ONLY the snapshot — no second graph, no reactive subscriptions,
2296
+ * no retained resource/DOM references.
2297
+ * - Every dynamic value is HTML-escaped, so an app's data (signal values that
2298
+ * the caller chose to expose, route paths, form labels) can never break out
2299
+ * of the markup.
2300
+ *
2301
+ * HONEST SCOPE: this produces markup. Whether it *renders* correctly in a real
2302
+ * browser, is accessible to a screen reader, or performs at 60fps is NOT claimed
2303
+ * here and has NOT been verified — no browser/AT is available in this
2304
+ * environment (the §24 browser gate remains BLOCKED). The value proven by tests
2305
+ * is that the string faithfully and safely reflects the snapshot.
2306
+ *
2307
+ * EFFECTS (§8): StreetUI keeps no global registry of effects (that would require
2308
+ * instrumenting the reactive runtime, which DevTools deliberately does not do).
2309
+ * The closest safe signal is each live signal's `observerCount` — the number of
2310
+ * effects/derivations currently depending on it — which the Signals section
2311
+ * shows. The view labels this honestly rather than inventing an effect list.
2312
+ */
2313
+
2314
+ /** Escape a string for safe interpolation into HTML text/attribute content. */
2315
+ declare function escapeHtml(value: unknown): string;
2316
+ /**
2317
+ * Render a snapshot to a complete, self-contained HTML document string. The
2318
+ * markup is static; call `renderDevToolsHTML(session.refresh())` again to
2319
+ * reflect new state (DevTools pulls — it is never pushed to).
2320
+ */
2321
+ declare function renderDevToolsHTML(s: DevToolsSnapshot): string;
2322
+
2115
2323
  /**
2116
2324
  * StreetUI project configuration (Phase 9). The config is intentionally tiny:
2117
2325
  * every field has a sensible default so `streetui.config.ts` is optional. A
@@ -2166,4 +2374,4 @@ interface ResolvedConfig {
2166
2374
  /** Identity helper that gives `streetui.config.ts` full type-checking + inference. */
2167
2375
  declare function defineConfig(config: StreetUIConfig): StreetUIConfig;
2168
2376
 
2169
- export { type A11yIds, type Announcer, 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 InspectedComponent, type InspectedInteractions, type InspectedNode, type InspectedOverlay, type InspectedPage, type InspectedTransition, 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 ResolvedTransitionLike, 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, ServerRawHTML, 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, TransitionConfig, TransitionController, type TransitionHooks, type TransitionPhase, Unsubscribe, type Validator, a11yIds, applyNodeProps, applyProp, bindDomEvent, browserDOMAdapter, buttonUpdate, consoleDiagnosticSink, containFocus, createAnnouncer, createApplication, createBrowserHistory, createContext, createDevTools, createForm, createI18n, createMemoryHistory, createRenderContext, createRenderer, createRouter, createRuntime, createStore, createStreetEvent, defineConfig, diagnosePerformance, email, environment, escapeHtmlAttr, escapeHtmlText, flushSync, focusById, focusFirst, focusInitial, formatDiagnosticContext, frameworkError, getFocusable, getResolvedTransition, globalEventBus, headingUpdate, hydrateGraph, inputUpdate, inspectApplication, inspectComponents, inspectContext, inspectForm, inspectGraph, inspectI18n, inspectInteractions, inspectResource, inspectRouter, inspectSignal, interpolate, matchPattern, matchRoutes, maxLength, minLength, mountGraph, mountNode, mountRouter, nodeTypeStats, normalizePath, onEscape, patchNode, patchProp, pattern, printDiagnostics, printGraph, reconcileChildren, reconcileChildrenByPlan, reportDiagnostic, required, resolveTag, resource, restoreFocus, routerOutlet, rovingMenu, runElementTransition, runValidators, saveFocus, scheduleImmediate, scheduleUpdate, scheduler, serializeChildren, serializeServerNode, splitTarget, textUpdate, toIdToken, transformGraph, trapFocus, validateGraph, wireComponentBehavior, wireEvents, wireOverlayBehavior, wireReactiveList, wireSignalBindings };
2377
+ export { type A11yIds, type Announcer, Application, ApplicationGraph, ApplicationId, type ApplicationIdentity, type ApplicationInspection, type ApplicationOptions, type ApplicationPanel, type AuthSession, type AuthSessionConfig, type AuthStatus, BrowserDOMAdapter, CleanupRegistry, type Client, type ClientConfig, 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 DiagnosticsPanel, type DiagnosticsSummary, type DomBinding, DomEventRegistry, Environment, type EnvironmentCapabilities, type EnvironmentKind, type ErrorReport, type ErrorReportOptions, EventBus, type EventHandler, FOCUSABLE_SELECTOR, type FetchLike, type Field, type Form, type FormConfig, type FormInspection, type FormLike, type FormValidators, type FormValues, GraphNode, HttpError, type HttpMethod, HydrationDiagnosticSink, type I18n, type I18nConfig, type I18nInspection, type I18nLike, type InspectFormOptions, type InspectI18nOptions, type InspectResourceOptions, type InspectSignalOptions, type InspectedComponent, type InspectedInteractions, type InspectedNode, type InspectedOverlay, type InspectedPage, type InspectedTransition, type InterpolationParams, type IsActiveOptions, type Job, Lifecycle, type LoadUser, type MatchResult, type MessageMap, type MountFn, type MountRouterOptions, type MountedApplication, type MountedRouter, type Mutation, type MutationOptions, type MutationStatus, type Mutator, 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, RenderContext, RenderHandle, type RequestConfig, type ResolvedConfig, type ResolvedTransitionLike, Resource, type ResourceInspection, type ResourceLike, ResourceLoaderContext, ResourceOptions, 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, ServerRawHTML, 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, TransitionConfig, TransitionController, type TransitionHooks, type TransitionPhase, Unsubscribe, type Validator, a11yIds, applyNodeProps, applyProp, bindDomEvent, browserDOMAdapter, buttonUpdate, consoleDiagnosticSink, containFocus, createAnnouncer, createApplication, createAuthSession, createBrowserHistory, createClient, createContext, createDevTools, createForm, createI18n, createMemoryHistory, createRenderer, createRouter, createRuntime, createStore, createStreetEvent, defineConfig, describeError, diagnosePerformance, email, environment, escapeHtml, escapeHtmlAttr, escapeHtmlText, flushSync, focusById, focusFirst, focusInitial, formatDiagnosticContext, frameworkError, getFocusable, getResolvedTransition, globalEventBus, headingUpdate, hydrateGraph, inputUpdate, inspectApplication, inspectComponents, inspectContext, inspectForm, inspectGraph, inspectI18n, inspectInteractions, inspectResource, inspectRouter, inspectSignal, interpolate, linkUpdate, matchPattern, matchRoutes, maxLength, minLength, mountGraph, mountNode, mountRouter, mutation, nodeTypeStats, normalizePath, onEscape, patchNode, patchProp, pattern, printDiagnostics, printGraph, reconcileChildren, reconcileChildrenByPlan, renderDevToolsHTML, reportDiagnostic, reportError, required, resolveTag, restoreFocus, routerOutlet, rovingMenu, runElementTransition, runValidators, saveFocus, scheduleImmediate, scheduleUpdate, scheduler, serializeChildren, serializeServerNode, splitTarget, textUpdate, toIdToken, transformGraph, trapFocus, validateGraph, wireComponentBehavior, wireEvents, wireOverlayBehavior, wireReactiveList, wireSignalBindings };