streetui 1.9.0 → 2.1.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "1.9.0";
2
+ var VERSION = "2.1.0";
3
3
 
4
4
  // ../state/src/signal.ts
5
5
  var _activeConsumer = null;
@@ -339,6 +339,198 @@ function resource(loader, options = {}) {
339
339
  };
340
340
  }
341
341
 
342
+ // ../state/src/mutation.ts
343
+ function mutation(mutator, options = {}) {
344
+ const status = signal("idle");
345
+ const data = signal(void 0);
346
+ const error = signal(void 0);
347
+ const pending = derived(() => status.get() === "loading");
348
+ let disposed = false;
349
+ let runId = 0;
350
+ const mutate = async (args) => {
351
+ if (disposed) {
352
+ return await mutator(args);
353
+ }
354
+ const myRun = ++runId;
355
+ batch(() => {
356
+ error.set(void 0);
357
+ status.set("loading");
358
+ });
359
+ try {
360
+ const result = await mutator(args);
361
+ if (!disposed && myRun === runId) {
362
+ batch(() => {
363
+ data.set(result);
364
+ error.set(void 0);
365
+ status.set("success");
366
+ });
367
+ }
368
+ if (!disposed && myRun === runId) {
369
+ await options.onSuccess?.(result, args);
370
+ await options.onSettled?.(args);
371
+ }
372
+ return result;
373
+ } catch (err) {
374
+ if (!disposed && myRun === runId) {
375
+ batch(() => {
376
+ error.set(err);
377
+ status.set("error");
378
+ });
379
+ await options.onError?.(err, args);
380
+ await options.onSettled?.(args);
381
+ }
382
+ throw err;
383
+ }
384
+ };
385
+ const reset = () => {
386
+ batch(() => {
387
+ status.set("idle");
388
+ data.set(void 0);
389
+ error.set(void 0);
390
+ });
391
+ };
392
+ const dispose = () => {
393
+ disposed = true;
394
+ };
395
+ if (options.onCleanup !== void 0) options.onCleanup(dispose);
396
+ return { status, data, error, pending, mutate, reset, dispose };
397
+ }
398
+
399
+ // ../state/src/client.ts
400
+ var HttpError = class extends Error {
401
+ status;
402
+ statusText;
403
+ url;
404
+ body;
405
+ constructor(status, statusText, url, body) {
406
+ super(`HTTP ${status} ${statusText} for ${url}`);
407
+ this.name = "HttpError";
408
+ this.status = status;
409
+ this.statusText = statusText;
410
+ this.url = url;
411
+ this.body = body;
412
+ }
413
+ };
414
+ function joinUrl(baseUrl, path) {
415
+ if (baseUrl === void 0 || baseUrl === "") return path;
416
+ const b = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
417
+ const p = path.startsWith("/") ? path : `/${path}`;
418
+ return `${b}${p}`;
419
+ }
420
+ function withQuery(url, query) {
421
+ if (query === void 0) return url;
422
+ const params = new URLSearchParams();
423
+ for (const [k, v] of Object.entries(query)) params.set(k, String(v));
424
+ const qs = params.toString();
425
+ if (qs === "") return url;
426
+ return url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
427
+ }
428
+ function createClient(config = {}) {
429
+ const doFetch = config.fetch ?? ((input, init) => {
430
+ if (typeof fetch === "undefined") {
431
+ throw new Error("createClient: no global fetch; pass { fetch } explicitly");
432
+ }
433
+ return fetch(input, init);
434
+ });
435
+ async function request(method, path, body, reqConfig = {}) {
436
+ const url = withQuery(joinUrl(config.baseUrl, path), reqConfig.query);
437
+ const headers = { ...config.headers, ...reqConfig.headers };
438
+ const init = { method, headers };
439
+ if (reqConfig.signal !== void 0) init.signal = reqConfig.signal;
440
+ if (body !== void 0) {
441
+ if (headers["Content-Type"] === void 0) headers["Content-Type"] = "application/json";
442
+ init.body = JSON.stringify(body);
443
+ }
444
+ const response = await doFetch(url, init);
445
+ const parsed = await parseBody(response);
446
+ if (!response.ok) {
447
+ throw new HttpError(response.status, response.statusText, url, parsed);
448
+ }
449
+ return parsed;
450
+ }
451
+ return {
452
+ request,
453
+ get: (path, c) => request("GET", path, void 0, c),
454
+ post: (path, b, c) => request("POST", path, b, c),
455
+ put: (path, b, c) => request("PUT", path, b, c),
456
+ patch: (path, b, c) => request("PATCH", path, b, c),
457
+ del: (path, c) => request("DELETE", path, void 0, c),
458
+ resource: (path, options = {}) => {
459
+ const { query, ...resourceOptions } = options;
460
+ return resource(
461
+ (ctx) => request("GET", path, void 0, { signal: ctx.signal, ...query !== void 0 ? { query } : {} }),
462
+ resourceOptions
463
+ );
464
+ },
465
+ mutation: (method, path, options) => mutation((args) => request(method, path, args), options ?? {})
466
+ };
467
+ }
468
+ async function parseBody(response) {
469
+ const text = await response.text();
470
+ if (text === "") return void 0;
471
+ const type = response.headers.get("content-type") ?? "";
472
+ if (type.includes("application/json")) {
473
+ try {
474
+ return JSON.parse(text);
475
+ } catch {
476
+ return text;
477
+ }
478
+ }
479
+ return text;
480
+ }
481
+
482
+ // ../state/src/auth.ts
483
+ function createAuthSession(config) {
484
+ const session = resource(
485
+ (ctx) => config.loadUser(ctx),
486
+ {
487
+ ...config.immediate !== void 0 ? { immediate: config.immediate } : {},
488
+ ...config.onCleanup !== void 0 ? { onCleanup: config.onCleanup } : {}
489
+ }
490
+ );
491
+ const status = derived(() => {
492
+ const s = session.status.get();
493
+ const d = session.data.get();
494
+ if (s === "error") return "error";
495
+ if (s === "idle" || s === "loading" && d === void 0) return "loading";
496
+ return d === null || d === void 0 ? "unauthenticated" : "authenticated";
497
+ });
498
+ const user = derived(() => {
499
+ const d = session.data.get();
500
+ return d === null ? void 0 : d;
501
+ });
502
+ const authenticated = derived(() => status.get() === "authenticated");
503
+ const unauthenticated = derived(() => status.get() === "unauthenticated");
504
+ const loading = derived(() => status.get() === "loading");
505
+ const logoutMutation = mutation(
506
+ async () => {
507
+ await config.logout?.();
508
+ },
509
+ { onSuccess: () => session.refetch() }
510
+ );
511
+ const dispose = () => {
512
+ session.dispose();
513
+ status.dispose();
514
+ user.dispose();
515
+ authenticated.dispose();
516
+ unauthenticated.dispose();
517
+ loading.dispose();
518
+ logoutMutation.dispose();
519
+ };
520
+ return {
521
+ status,
522
+ user,
523
+ error: session.error,
524
+ authenticated,
525
+ unauthenticated,
526
+ loading,
527
+ loggingOut: logoutMutation.pending,
528
+ refresh: () => session.refetch(),
529
+ logout: () => logoutMutation.mutate(void 0),
530
+ dispose
531
+ };
532
+ }
533
+
342
534
  // ../core/src/a11y-ids.ts
343
535
  var UNSAFE = /[^A-Za-z0-9_-]+/g;
344
536
  function toIdToken(base) {
@@ -605,9 +797,13 @@ function formatDiagnosticContext(context) {
605
797
  const parts = [];
606
798
  if (context.package !== void 0) parts.push(`package=${context.package}`);
607
799
  if (context.operation !== void 0) parts.push(`operation=${context.operation}`);
800
+ if (context.component !== void 0) parts.push(`component=${context.component}`);
801
+ if (context.phase !== void 0) parts.push(`phase=${context.phase}`);
608
802
  if (context.nodeId !== void 0) parts.push(`node=${context.nodeId}`);
803
+ if (context.element !== void 0) parts.push(`element=${context.element}`);
609
804
  if (context.route !== void 0) parts.push(`route=${context.route}`);
610
805
  if (context.resource !== void 0) parts.push(`resource=${context.resource}`);
806
+ if (context.signal !== void 0) parts.push(`signal=${context.signal}`);
611
807
  return parts.length > 0 ? ` [${parts.join(", ")}]` : "";
612
808
  }
613
809
  var StreetFrameworkError = class extends Error {
@@ -638,6 +834,42 @@ function consoleDiagnosticSink(logger = console) {
638
834
  error: (m, c) => logger.error?.(`${m}${formatDiagnosticContext(c)}`)
639
835
  };
640
836
  }
837
+ function describeErrorAt(error, context, options, depth) {
838
+ const isError = error instanceof Error;
839
+ const name = isError ? error.name : "Error";
840
+ const message = isError ? error.message : safeStringify(error);
841
+ const report = { name, message, isError };
842
+ if (context !== void 0) report.context = context;
843
+ if (options.includeStack === true && isError && typeof error.stack === "string") {
844
+ report.stack = error.stack;
845
+ }
846
+ if (options.includeCause === true && isError && depth < 4) {
847
+ const cause = error.cause;
848
+ if (cause !== void 0 && cause !== null) {
849
+ report.cause = describeErrorAt(cause, void 0, options, depth + 1);
850
+ }
851
+ }
852
+ return report;
853
+ }
854
+ function safeStringify(value) {
855
+ if (typeof value === "string") return value;
856
+ if (value === null) return "null";
857
+ if (value === void 0) return "undefined";
858
+ const t = typeof value;
859
+ if (t === "number" || t === "boolean" || t === "bigint" || t === "symbol") {
860
+ return String(value);
861
+ }
862
+ const ctor = t === "object" && value !== null ? value.constructor?.name ?? "Object" : t;
863
+ return `[non-Error ${ctor}]`;
864
+ }
865
+ function describeError(error, context, options = {}) {
866
+ return describeErrorAt(error, context, options, 0);
867
+ }
868
+ function reportError(sink, error, context, options = {}) {
869
+ const report = describeError(error, context, options);
870
+ reportDiagnostic(sink, "error", report.message, report.context);
871
+ return report;
872
+ }
641
873
 
642
874
  // ../graph/src/graph-node.ts
643
875
  var GraphNode = class _GraphNode {
@@ -808,6 +1040,7 @@ var ApplicationGraph = class {
808
1040
  this.handlers.delete(`__overlay__${node.id}`);
809
1041
  this.handlers.delete(`__component__${node.id}`);
810
1042
  this.handlers.delete(`__transition__${node.id}`);
1043
+ this.handlers.delete(`__head__${node.id}`);
811
1044
  }
812
1045
  // ── Handler registry ──────────────────────────────────────────────────────
813
1046
  registerHandler(key, fn) {
@@ -937,6 +1170,121 @@ function isTransitionConfig(value) {
937
1170
  return typeof o["name"] === "string" || typeof o["enter"] === "string" || typeof o["enterActive"] === "string" || typeof o["enterFrom"] === "string" || typeof o["leave"] === "string" || typeof o["leaveActive"] === "string" || typeof o["leaveFrom"] === "string";
938
1171
  }
939
1172
 
1173
+ // ../dsl/src/head.ts
1174
+ function metaDedupKey(m) {
1175
+ if (m.charset !== void 0) return "meta:charset";
1176
+ if (m.name !== void 0) return `meta:name=${m.name}`;
1177
+ if (m.property !== void 0) return `meta:property=${m.property}`;
1178
+ if (m.httpEquiv !== void 0) return `meta:http-equiv=${m.httpEquiv}`;
1179
+ return void 0;
1180
+ }
1181
+ function metaAttrs(m) {
1182
+ const attrs = {};
1183
+ if (m.charset !== void 0) attrs["charset"] = m.charset;
1184
+ if (m.name !== void 0) attrs["name"] = m.name;
1185
+ if (m.property !== void 0) attrs["property"] = m.property;
1186
+ if (m.httpEquiv !== void 0) attrs["http-equiv"] = m.httpEquiv;
1187
+ if (m.content !== void 0) attrs["content"] = m.content;
1188
+ return attrs;
1189
+ }
1190
+ function linkAttrs(l) {
1191
+ const attrs = { rel: l.rel, href: l.href };
1192
+ if (l.sizes !== void 0) attrs["sizes"] = l.sizes;
1193
+ if (l.type !== void 0) attrs["type"] = l.type;
1194
+ if (l.media !== void 0) attrs["media"] = l.media;
1195
+ if (l.as !== void 0) attrs["as"] = l.as;
1196
+ if (l.crossorigin !== void 0) attrs["crossorigin"] = l.crossorigin;
1197
+ if (l.hreflang !== void 0) attrs["hreflang"] = l.hreflang;
1198
+ return attrs;
1199
+ }
1200
+ function resolveHead(config) {
1201
+ const entries = [];
1202
+ if (config.charset !== void 0) {
1203
+ entries.push({ tag: "meta", dedupKey: "meta:charset", attrs: { charset: config.charset } });
1204
+ }
1205
+ if (config.base !== void 0) {
1206
+ entries.push({ tag: "base", dedupKey: "base", attrs: { href: config.base } });
1207
+ }
1208
+ if (config.title !== void 0) {
1209
+ entries.push({ tag: "title", dedupKey: "title", attrs: {}, text: config.title });
1210
+ }
1211
+ if (config.description !== void 0) {
1212
+ entries.push({
1213
+ tag: "meta",
1214
+ dedupKey: "meta:name=description",
1215
+ attrs: { name: "description", content: config.description }
1216
+ });
1217
+ }
1218
+ if (config.canonical !== void 0) {
1219
+ entries.push({
1220
+ tag: "link",
1221
+ dedupKey: "link:rel=canonical",
1222
+ attrs: { rel: "canonical", href: config.canonical }
1223
+ });
1224
+ }
1225
+ if (config.robots !== void 0) {
1226
+ entries.push({
1227
+ tag: "meta",
1228
+ dedupKey: "meta:name=robots",
1229
+ attrs: { name: "robots", content: config.robots }
1230
+ });
1231
+ }
1232
+ if (config.themeColor !== void 0) {
1233
+ entries.push({
1234
+ tag: "meta",
1235
+ dedupKey: "meta:name=theme-color",
1236
+ attrs: { name: "theme-color", content: config.themeColor }
1237
+ });
1238
+ }
1239
+ if (config.viewport !== void 0) {
1240
+ entries.push({
1241
+ tag: "meta",
1242
+ dedupKey: "meta:name=viewport",
1243
+ attrs: { name: "viewport", content: config.viewport }
1244
+ });
1245
+ }
1246
+ if (config.favicon !== void 0) {
1247
+ const l = typeof config.favicon === "string" ? { rel: "icon", href: config.favicon } : config.favicon;
1248
+ entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}`, attrs: linkAttrs(l) });
1249
+ }
1250
+ if (config.openGraph !== void 0) {
1251
+ for (const key of Object.keys(config.openGraph)) {
1252
+ const property = `og:${key}`;
1253
+ entries.push({
1254
+ tag: "meta",
1255
+ dedupKey: `meta:property=${property}`,
1256
+ attrs: { property, content: config.openGraph[key] }
1257
+ });
1258
+ }
1259
+ }
1260
+ if (config.twitter !== void 0) {
1261
+ for (const key of Object.keys(config.twitter)) {
1262
+ const name = `twitter:${key}`;
1263
+ entries.push({
1264
+ tag: "meta",
1265
+ dedupKey: `meta:name=${name}`,
1266
+ attrs: { name, content: config.twitter[key] }
1267
+ });
1268
+ }
1269
+ }
1270
+ if (config.meta !== void 0) {
1271
+ for (const m of config.meta) {
1272
+ const key = metaDedupKey(m);
1273
+ if (key === void 0) continue;
1274
+ entries.push({ tag: "meta", dedupKey: key, attrs: metaAttrs(m) });
1275
+ }
1276
+ }
1277
+ if (config.link !== void 0) {
1278
+ for (const l of config.link) {
1279
+ entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}:href=${l.href}`, attrs: linkAttrs(l) });
1280
+ }
1281
+ }
1282
+ return { entries };
1283
+ }
1284
+ function isHeadContribution(value) {
1285
+ return value !== null && typeof value === "object" && Array.isArray(value["entries"]);
1286
+ }
1287
+
940
1288
  // ../dsl/src/builders.ts
941
1289
  function isSignal(v) {
942
1290
  return v !== null && typeof v === "object" && typeof v["get"] === "function" && typeof v["subscribe"] === "function";
@@ -1318,8 +1666,14 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
1318
1666
  this.container(id, (c) => {
1319
1667
  c.when(
1320
1668
  hasError,
1321
- // Error state → fallback.
1322
- (fb) => options.fallback(fb, readError(), retry),
1669
+ // Error state → fallback. The fallback branch mounts exactly when the
1670
+ // boundary enters its error state, so this is also where the optional
1671
+ // `onError` reporting hook fires (observe-only; §6/§7).
1672
+ (fb) => {
1673
+ const currentError = readError();
1674
+ options.onError?.(currentError);
1675
+ options.fallback(fb, currentError, retry);
1676
+ },
1323
1677
  // Healthy state → body, guarded against synchronous build throws.
1324
1678
  (body) => {
1325
1679
  try {
@@ -1331,6 +1685,24 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
1331
1685
  );
1332
1686
  }, { id });
1333
1687
  }
1688
+ asyncBoundary(key, res, branches) {
1689
+ const isError = derived(() => res.status.get() === "error");
1690
+ const showSuccess = derived(
1691
+ () => res.status.get() !== "error" && res.data.get() !== void 0
1692
+ );
1693
+ const showLoading = derived(
1694
+ () => res.status.get() !== "error" && res.data.get() === void 0
1695
+ );
1696
+ const dataSignal = derived(() => res.data.get());
1697
+ const retry = () => {
1698
+ void res.refetch();
1699
+ };
1700
+ this.container(key, (c) => {
1701
+ c.when(isError, (fb) => branches.error?.(fb, res.error.peek(), retry));
1702
+ c.when(showSuccess, (sb) => branches.success(sb, dataSignal));
1703
+ c.when(showLoading, (lb) => branches.loading?.(lb));
1704
+ }, { key });
1705
+ }
1334
1706
  // ── Portals & overlays ──────────────────────────────────────────────────────
1335
1707
  portal(key, builder, options = {}) {
1336
1708
  const node = this._graph.createNode("portal", {
@@ -1340,6 +1712,31 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
1340
1712
  });
1341
1713
  builder(new ContainerBuilderImpl(node, this._graph));
1342
1714
  }
1715
+ /**
1716
+ * Declare document metadata (2.0 §1–§3). Creates a `'head'` node — a neutral,
1717
+ * empty inline anchor at this position (one node / one element, so positional
1718
+ * hydration is preserved) — and registers a `__head__<id>` descriptor holding
1719
+ * this call's normalized, dedup-keyed {@link resolveHead} contribution. The
1720
+ * renderer applies it to `document.head` on the browser (adopting server tags
1721
+ * on hydration, cleaning up on unmount / route change) and emits the active
1722
+ * graph's merged metadata as a string on the server (`renderHead`).
1723
+ *
1724
+ * Multiple `head()` nodes may be live at once (app default + route + component)
1725
+ * — the renderer merges them and, per dedup key, the last in document order
1726
+ * wins (see head.ts). No new render path: this reuses the same graph-node +
1727
+ * handler-registry convention as overlays/transitions/components.
1728
+ */
1729
+ head(metadata) {
1730
+ const node = this._graph.createNode("head", {
1731
+ parent: this._node,
1732
+ props: { "data-streetui-head-anchor": "" }
1733
+ });
1734
+ const contribution = resolveHead(metadata);
1735
+ this._graph.registerHandler(
1736
+ `__head__${node.id}`,
1737
+ () => contribution
1738
+ );
1739
+ }
1343
1740
  /**
1344
1741
  * Shared assembly for every overlay kind: a `portal` node whose single child
1345
1742
  * is a `when(open, panel)` conditional. The panel container carries the
@@ -1711,8 +2108,8 @@ var RuntimeNodeInstance = class {
1711
2108
  this.children.push(instance);
1712
2109
  }
1713
2110
  /** Subscribe to a signal and register the unsubscribe for cleanup. */
1714
- trackSignal(signal2, handler) {
1715
- const unsub = signal2.subscribe(handler);
2111
+ trackSignal(signal3, handler) {
2112
+ const unsub = signal3.subscribe(handler);
1716
2113
  this.cleanup.add(unsub);
1717
2114
  }
1718
2115
  /** Register an arbitrary cleanup function (e.g. DOM event removal). */
@@ -2083,6 +2480,9 @@ var BrowserDOMAdapter = class {
2083
2480
  body() {
2084
2481
  return document.body ?? null;
2085
2482
  }
2483
+ head() {
2484
+ return document.head ?? null;
2485
+ }
2086
2486
  activeElement() {
2087
2487
  return document.activeElement ?? null;
2088
2488
  }
@@ -2462,6 +2862,9 @@ var ServerDOMAdapter = class {
2462
2862
  body() {
2463
2863
  return null;
2464
2864
  }
2865
+ head() {
2866
+ return null;
2867
+ }
2465
2868
  activeElement() {
2466
2869
  return null;
2467
2870
  }
@@ -2844,7 +3247,13 @@ var TAG_MAP = {
2844
3247
  // its children are relocated to a document.body container on the browser
2845
3248
  // (see the portal branch in mount.ts). On the server (no body) it renders
2846
3249
  // inline, so the anchor tag is what SSR/hydration positionally match on.
2847
- portal: "div"
3250
+ portal: "div",
3251
+ // A `head()` node renders as a neutral, empty inline anchor <div> at its
3252
+ // declaration site (like a portal anchor). Its actual contribution — title/
3253
+ // meta/link/etc. — is applied to `document.head` by `wireHeadBehavior` on the
3254
+ // browser, and emitted separately by `renderHead()` on the server. Keeping a
3255
+ // one-node/one-element anchor preserves positional hydration.
3256
+ head: "div"
2848
3257
  };
2849
3258
  function resolveTag(type) {
2850
3259
  return TAG_MAP[type] ?? "div";
@@ -3300,6 +3709,184 @@ var TransitionController = class {
3300
3709
  }
3301
3710
  };
3302
3711
 
3712
+ // ../renderer/src/head.ts
3713
+ var HEAD_MARKER = "data-streetui-head";
3714
+ var HEAD_KEY = "data-streetui-head-key";
3715
+ function isSignalLike(v) {
3716
+ return v !== null && typeof v === "object" && typeof v["subscribe"] === "function" && typeof v["peek"] === "function";
3717
+ }
3718
+ function readValue(v) {
3719
+ if (isSignalLike(v)) return String(v.peek() ?? "");
3720
+ return String(v ?? "");
3721
+ }
3722
+ function resolveEntry(entry) {
3723
+ const attrs = {};
3724
+ for (const key of Object.keys(entry.attrs)) {
3725
+ attrs[key] = readValue(entry.attrs[key]);
3726
+ }
3727
+ const resolved = { tag: entry.tag, attrs };
3728
+ if (entry.tag === "title") {
3729
+ return { ...resolved, text: readValue(entry.text) };
3730
+ }
3731
+ return resolved;
3732
+ }
3733
+ var HeadManager = class {
3734
+ _dom;
3735
+ _head;
3736
+ _contributions = /* @__PURE__ */ new Map();
3737
+ _applied = /* @__PURE__ */ new Map();
3738
+ _order = 0;
3739
+ _adopted = false;
3740
+ constructor(dom, head) {
3741
+ this._dom = dom;
3742
+ this._head = head;
3743
+ }
3744
+ /** Register (or replace) a node's contribution and re-apply the merged result. */
3745
+ register(nodeId, entries) {
3746
+ this._contributions.set(nodeId, { order: this._order++, entries });
3747
+ this.apply();
3748
+ }
3749
+ /** Withdraw a node's contribution (unmount / route change) and re-apply. */
3750
+ unregister(nodeId) {
3751
+ if (this._contributions.delete(nodeId)) this.apply();
3752
+ }
3753
+ /** Recompute the merged head and patch `document.head` to match. */
3754
+ apply() {
3755
+ if (!this._adopted) {
3756
+ this._adoptServerTags();
3757
+ this._adopted = true;
3758
+ }
3759
+ const ordered = [...this._contributions.values()].sort((a, b) => a.order - b.order);
3760
+ const merged = /* @__PURE__ */ new Map();
3761
+ for (const contribution of ordered) {
3762
+ for (const entry of contribution.entries) {
3763
+ merged.set(entry.dedupKey, resolveEntry(entry));
3764
+ }
3765
+ }
3766
+ for (const [key, desired] of merged) {
3767
+ const existing = this._applied.get(key);
3768
+ if (existing !== void 0 && existing.tag === desired.tag) {
3769
+ this._reconcileAttrs(existing, desired);
3770
+ } else {
3771
+ if (existing !== void 0) {
3772
+ this._dom.removeChild(this._head, existing.el);
3773
+ this._applied.delete(key);
3774
+ }
3775
+ const el = this._createTag(key, desired);
3776
+ this._dom.appendChild(this._head, el);
3777
+ this._applied.set(key, { el, attrKeys: new Set(Object.keys(desired.attrs)), tag: desired.tag });
3778
+ }
3779
+ }
3780
+ for (const [key, record] of [...this._applied]) {
3781
+ if (!merged.has(key)) {
3782
+ this._dom.removeChild(this._head, record.el);
3783
+ this._applied.delete(key);
3784
+ }
3785
+ }
3786
+ }
3787
+ _createTag(key, desired) {
3788
+ const el = this._dom.createElement(desired.tag);
3789
+ this._dom.setAttribute(el, HEAD_MARKER, "");
3790
+ this._dom.setAttribute(el, HEAD_KEY, key);
3791
+ for (const attr of Object.keys(desired.attrs)) {
3792
+ this._dom.setAttribute(el, attr, desired.attrs[attr]);
3793
+ }
3794
+ if (desired.tag === "title") this._dom.setTextContent(el, desired.text ?? "");
3795
+ return el;
3796
+ }
3797
+ _reconcileAttrs(record, desired) {
3798
+ const nextKeys = new Set(Object.keys(desired.attrs));
3799
+ for (const attr of record.attrKeys) {
3800
+ if (!nextKeys.has(attr)) this._dom.removeAttribute(record.el, attr);
3801
+ }
3802
+ for (const attr of nextKeys) {
3803
+ this._dom.setAttribute(record.el, attr, desired.attrs[attr]);
3804
+ }
3805
+ if (desired.tag === "title") this._dom.setTextContent(record.el, desired.text ?? "");
3806
+ record.attrKeys = nextKeys;
3807
+ }
3808
+ /**
3809
+ * Seed `_applied` from server-emitted `[data-streetui-head-key]` tags already
3810
+ * in `document.head`. The subsequent diff reuses these elements when the
3811
+ * client desires the same key (no duplicate), rewrites them if the value
3812
+ * changed, or removes them if the client graph no longer wants them.
3813
+ */
3814
+ _adoptServerTags() {
3815
+ for (const child of this._dom.childNodes(this._head)) {
3816
+ if (!this._dom.isElement(child)) continue;
3817
+ const el = child;
3818
+ const key = this._dom.getAttribute(el, HEAD_KEY);
3819
+ if (key === null) continue;
3820
+ this._applied.set(key, {
3821
+ el,
3822
+ attrKeys: new Set(this._attrNames(el)),
3823
+ tag: this._dom.tagName(el)
3824
+ });
3825
+ }
3826
+ }
3827
+ /** The framework-managed attribute names currently on a server tag. */
3828
+ _attrNames(el) {
3829
+ const names = [];
3830
+ if (this._dom.getAttribute(el, HEAD_MARKER) !== null) names.push(HEAD_MARKER);
3831
+ if (this._dom.getAttribute(el, HEAD_KEY) !== null) names.push(HEAD_KEY);
3832
+ return names;
3833
+ }
3834
+ };
3835
+ function getHeadManager(ctx) {
3836
+ const head = ctx.dom.head();
3837
+ if (head === null) return null;
3838
+ const mutable = ctx;
3839
+ if (mutable.head === void 0) mutable.head = new HeadManager(ctx.dom, head);
3840
+ return mutable.head;
3841
+ }
3842
+ function wireHeadBehavior(ctx, graphNode, instance) {
3843
+ const manager = getHeadManager(ctx);
3844
+ if (manager === null) return;
3845
+ const descFn = ctx.graph.getHandler(`__head__${graphNode.id}`);
3846
+ if (descFn === void 0) return;
3847
+ const contribution = descFn();
3848
+ const nodeId = graphNode.id;
3849
+ manager.register(nodeId, contribution.entries);
3850
+ for (const entry of contribution.entries) {
3851
+ for (const attrKey of Object.keys(entry.attrs)) {
3852
+ const v = entry.attrs[attrKey];
3853
+ if (isSignalLike(v)) {
3854
+ instance.trackCleanup(v.subscribe(() => manager.apply()));
3855
+ }
3856
+ }
3857
+ if (isSignalLike(entry.text)) {
3858
+ instance.trackCleanup(entry.text.subscribe(() => manager.apply()));
3859
+ }
3860
+ }
3861
+ instance.trackCleanup(() => manager.unregister(nodeId));
3862
+ }
3863
+ function renderHead(compiled) {
3864
+ const graph = compiled.graph;
3865
+ const merged = /* @__PURE__ */ new Map();
3866
+ graph.walk((node) => {
3867
+ if (node.type !== "head") return;
3868
+ const descFn = graph.getHandler(`__head__${node.id}`);
3869
+ if (descFn === void 0) return;
3870
+ for (const entry of descFn().entries) {
3871
+ merged.set(entry.dedupKey, resolveEntry(entry));
3872
+ }
3873
+ });
3874
+ if (merged.size === 0) return "";
3875
+ const dom = new ServerDOMAdapter();
3876
+ let out = "";
3877
+ for (const [key, desired] of merged) {
3878
+ const el = dom.createElement(desired.tag);
3879
+ dom.setAttribute(el, HEAD_MARKER, "");
3880
+ dom.setAttribute(el, HEAD_KEY, key);
3881
+ for (const attr of Object.keys(desired.attrs)) {
3882
+ dom.setAttribute(el, attr, desired.attrs[attr]);
3883
+ }
3884
+ if (desired.tag === "title") dom.setTextContent(el, desired.text ?? "");
3885
+ out += dom.serializeOuter(el);
3886
+ }
3887
+ return out;
3888
+ }
3889
+
3303
3890
  // ../renderer/src/mount.ts
3304
3891
  var SKIP_PROP_KEYS = /* @__PURE__ */ new Set([
3305
3892
  "text",
@@ -3422,6 +4009,9 @@ function mountNode(ctx, graphNode, parentDom) {
3422
4009
  const instance2 = new NodeInstance(graphNode, el2);
3423
4010
  ctx.instances.set(graphNode.id, instance2);
3424
4011
  wireEvents(dom, graph, graphNode, el2, instance2);
4012
+ if (graphNode.stateRefs.length !== 0) {
4013
+ wireSignalBindings(ctx, graphNode, instance2, linkUpdate(dom, el2));
4014
+ }
3425
4015
  dom.appendChild(parentDom, el2);
3426
4016
  return instance2;
3427
4017
  }
@@ -3477,6 +4067,16 @@ function mountNode(ctx, graphNode, parentDom) {
3477
4067
  wireOverlayBehavior(ctx, graphNode, instance2, target);
3478
4068
  return instance2;
3479
4069
  }
4070
+ if (graphNode.type === "head") {
4071
+ const anchor = dom.createElement(resolveTag("head"));
4072
+ dom.setAttribute(anchor, "data-streetui-head-anchor", "");
4073
+ applyNodeProps(ctx, graphNode, anchor);
4074
+ const instance2 = new NodeInstance(graphNode, anchor);
4075
+ ctx.instances.set(graphNode.id, instance2);
4076
+ dom.appendChild(parentDom, anchor);
4077
+ wireHeadBehavior(ctx, graphNode, instance2);
4078
+ return instance2;
4079
+ }
3480
4080
  const tag = resolveTag(graphNode.type);
3481
4081
  const el = dom.createElement(tag);
3482
4082
  applyNodeProps(ctx, graphNode, el);
@@ -3541,6 +4141,17 @@ function buttonUpdate(dom, el) {
3541
4141
  }
3542
4142
  };
3543
4143
  }
4144
+ function linkUpdate(dom, el) {
4145
+ return (propKey, value) => {
4146
+ if (propKey === "label") {
4147
+ dom.setTextContent(el, String(value ?? ""));
4148
+ } else if (propKey === "href") {
4149
+ dom.setAttribute(el, "href", String(value ?? ""));
4150
+ } else {
4151
+ applyProp(dom, el, propKey, value);
4152
+ }
4153
+ };
4154
+ }
3544
4155
  function applyNodeProps(ctx, graphNode, el) {
3545
4156
  const props = graphNode.props;
3546
4157
  for (const key in props) {
@@ -3775,7 +4386,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
3775
4386
  case "link": {
3776
4387
  const instance = new NodeInstance(graphNode, domNode);
3777
4388
  ctx.instances.set(graphNode.id, instance);
3778
- if (graphNode.type === "link") wireEvents(dom, graph, graphNode, domNode, instance);
4389
+ if (graphNode.type === "link") {
4390
+ wireEvents(dom, graph, graphNode, domNode, instance);
4391
+ if (graphNode.stateRefs.length !== 0) {
4392
+ wireSignalBindings(ctx, graphNode, instance, linkUpdate(dom, domNode));
4393
+ }
4394
+ }
3779
4395
  return instance;
3780
4396
  }
3781
4397
  case "reactive-list":
@@ -3805,6 +4421,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
3805
4421
  wireOverlayBehavior(ctx, graphNode, instance, target);
3806
4422
  return instance;
3807
4423
  }
4424
+ case "head": {
4425
+ const instance = new NodeInstance(graphNode, domNode);
4426
+ ctx.instances.set(graphNode.id, instance);
4427
+ wireHeadBehavior(ctx, graphNode, instance);
4428
+ return instance;
4429
+ }
3808
4430
  default: {
3809
4431
  const instance = new NodeInstance(graphNode, domNode);
3810
4432
  ctx.instances.set(graphNode.id, instance);
@@ -4051,7 +4673,8 @@ function analyzeGraph(graph) {
4051
4673
  const isConditional = node.type === "conditional";
4052
4674
  const isPortal = node.type === "portal";
4053
4675
  const isComponent = node.type === "component";
4054
- const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent;
4676
+ const isHead = node.type === "head";
4677
+ const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent && !isHead;
4055
4678
  const isStaticSubtree = isStatic && allChildrenStatic;
4056
4679
  nodes.set(node.id, {
4057
4680
  isStatic,
@@ -5113,11 +5736,22 @@ function capture(compiled, sources, options) {
5113
5736
  snapshot: app.perf,
5114
5737
  diagnostics: diagnosePerformance(compiled, options.perfThresholds)
5115
5738
  };
5739
+ const components = inspectComponents(compiled.graph);
5740
+ const interactions = inspectInteractions(compiled.graph);
5741
+ const diagnostics = {
5742
+ errors: app.diagnostics.errors,
5743
+ warnings: app.diagnostics.warnings,
5744
+ messages: app.diagnostics.messages
5745
+ };
5116
5746
  const snapshot = {
5117
5747
  application,
5118
5748
  graph: app.graph,
5119
5749
  signals,
5120
5750
  performance,
5751
+ components,
5752
+ overlays: interactions.overlays,
5753
+ transitions: interactions.transitions,
5754
+ diagnostics,
5121
5755
  ...sources.router !== void 0 ? { router: inspectRouter(sources.router) } : {},
5122
5756
  ...sources.resources !== void 0 ? { resources: mapInspect(sources.resources, (r) => inspectResource(r)) } : {},
5123
5757
  ...sources.forms !== void 0 ? { forms: mapInspect(sources.forms, (f) => inspectForm(f)) } : {},
@@ -5158,6 +5792,25 @@ function formatSnapshot(s) {
5158
5792
  for (const [label, sig] of Object.entries(s.signals.live)) {
5159
5793
  lines.push(` ${label} [${sig.kind}] = ${format(sig.value)} \xB7 observers ${sig.observerCount ?? "?"}`);
5160
5794
  }
5795
+ if (s.components.length > 0) {
5796
+ lines.push(`Components: ${s.components.length}`);
5797
+ for (const c of s.components) {
5798
+ lines.push(` ${" ".repeat(c.depth)}${c.name}${c.key !== void 0 ? ` (#${c.key})` : ""} \xB7 children ${c.childCount}`);
5799
+ }
5800
+ }
5801
+ if (s.overlays.length > 0) {
5802
+ lines.push(`Overlays: ${s.overlays.length}`);
5803
+ for (const o of s.overlays) {
5804
+ const kind = o.modal ? "modal" : o.menu ? "menu" : o.takesFocus ? "focusable" : "non-modal";
5805
+ lines.push(` ${o.key ?? o.id} [${kind}] ${o.open ? "open" : "closed"}`);
5806
+ }
5807
+ }
5808
+ if (s.transitions.length > 0) {
5809
+ lines.push(`Transitions: ${s.transitions.length}`);
5810
+ for (const t of s.transitions) {
5811
+ lines.push(` ${t.key ?? t.id} on <${t.nodeType}> \xB7 ${t.duration}ms${t.appear ? " \xB7 appear" : ""}`);
5812
+ }
5813
+ }
5161
5814
  if (s.router !== void 0) {
5162
5815
  lines.push(`Router: ${s.router.path} (${s.router.pattern})${s.router.isFallback ? " [fallback]" : ""}`);
5163
5816
  }
@@ -5191,6 +5844,12 @@ function formatSnapshot(s) {
5191
5844
  for (const d of s.performance.diagnostics) {
5192
5845
  lines.push(` ${d.code}: ${d.message}`);
5193
5846
  }
5847
+ lines.push(
5848
+ `Diagnostics: ${s.diagnostics.errors} error(s), ${s.diagnostics.warnings} warning(s)`
5849
+ );
5850
+ for (const m of s.diagnostics.messages) {
5851
+ lines.push(` ${m}`);
5852
+ }
5194
5853
  return lines.join("\n");
5195
5854
  }
5196
5855
  function format(value) {
@@ -5206,6 +5865,182 @@ function format(value) {
5206
5865
  return String(value);
5207
5866
  }
5208
5867
 
5868
+ // ../devtools/src/view.ts
5869
+ function escapeHtml(value) {
5870
+ const s = typeof value === "string" ? value : stringifyValue(value);
5871
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5872
+ }
5873
+ function stringifyValue(value) {
5874
+ if (value === null || value === void 0) return String(value);
5875
+ if (typeof value === "object") {
5876
+ try {
5877
+ return JSON.stringify(value);
5878
+ } catch {
5879
+ return "[object]";
5880
+ }
5881
+ }
5882
+ return String(value);
5883
+ }
5884
+ function section(title, count, body) {
5885
+ const badge = count === void 0 ? "" : ` <span class="st-count">${count}</span>`;
5886
+ return `<section class="st-panel"><h2>${escapeHtml(title)}${badge}</h2>${body}</section>`;
5887
+ }
5888
+ function ul(items) {
5889
+ if (items.length === 0) return '<p class="st-empty">(none)</p>';
5890
+ return `<ul>${items.map((i) => `<li>${i}</li>`).join("")}</ul>`;
5891
+ }
5892
+ function renderDevToolsHTML(s) {
5893
+ const app = s.application;
5894
+ const parts = [];
5895
+ parts.push(
5896
+ section(
5897
+ "Application",
5898
+ void 0,
5899
+ `<p>${escapeHtml(app.identity.name)} <span class="st-dim">v${escapeHtml(app.identity.version)}</span></p><p class="st-dim">${app.nodeCount} nodes \xB7 depth ${app.maxDepth} \xB7 ${app.pages.length} page(s) \xB7 ${app.signalCount} signals \xB7 ${app.eventHandlers} handlers \xB7 ${app.stateBindings} bindings</p>`
5900
+ )
5901
+ );
5902
+ parts.push(
5903
+ section(
5904
+ "Components",
5905
+ s.components.length,
5906
+ ul(
5907
+ s.components.map(
5908
+ (c) => `<span class="st-depth" style="--d:${c.depth}"></span><code>${escapeHtml(c.name)}</code>` + (c.key !== void 0 ? ` <span class="st-key">#${escapeHtml(c.key)}</span>` : "") + ` <span class="st-dim">${c.childCount} child(ren)</span>`
5909
+ )
5910
+ )
5911
+ )
5912
+ );
5913
+ parts.push(section("Graph", void 0, `<pre class="st-tree">${escapeHtml(renderNodeTree(s.graph))}</pre>`));
5914
+ parts.push(
5915
+ section(
5916
+ "Signals",
5917
+ s.signals.boundSignalIds.length,
5918
+ `<p class="st-dim">${s.signals.boundSignalIds.length} bound in graph \xB7 effects shown as observer counts (no global effect registry)</p>` + ul(
5919
+ Object.entries(s.signals.live).map(
5920
+ ([label, sig]) => `<code>${escapeHtml(label)}</code> <span class="st-key">[${escapeHtml(sig.kind)}]</span> = <code>${escapeHtml(sig.value)}</code> <span class="st-dim">observers ${escapeHtml(sig.observerCount ?? "?")}</span>`
5921
+ )
5922
+ )
5923
+ )
5924
+ );
5925
+ if (s.router !== void 0) {
5926
+ parts.push(
5927
+ section(
5928
+ "Router",
5929
+ void 0,
5930
+ `<p><code>${escapeHtml(s.router.path)}</code> <span class="st-dim">(${escapeHtml(s.router.pattern)})${s.router.isFallback ? " \xB7 fallback" : ""}</span></p>`
5931
+ )
5932
+ );
5933
+ }
5934
+ if (s.resources !== void 0) {
5935
+ parts.push(
5936
+ section(
5937
+ "Resources",
5938
+ Object.keys(s.resources).length,
5939
+ ul(
5940
+ Object.entries(s.resources).map(
5941
+ ([label, r]) => `<code>${escapeHtml(label)}</code>: ${escapeHtml(r.status)}` + (r.loading ? ' <span class="st-dim">(loading)</span>' : "") + (r.hasError ? ` <span class="st-err">!${escapeHtml(r.errorName)}</span>` : "")
5942
+ )
5943
+ )
5944
+ )
5945
+ );
5946
+ }
5947
+ parts.push(
5948
+ section(
5949
+ "Overlays",
5950
+ s.overlays.length,
5951
+ ul(
5952
+ s.overlays.map((o) => {
5953
+ const kind = o.modal ? "modal" : o.menu ? "menu" : o.takesFocus ? "focusable" : "non-modal";
5954
+ return `<code>${escapeHtml(o.key ?? o.id)}</code> <span class="st-key">[${kind}]</span> <span class="st-dim">${o.open ? "open" : "closed"}${o.closeOnEscape ? " \xB7 esc" : ""}${o.restoreFocus ? " \xB7 restore" : ""}</span>`;
5955
+ })
5956
+ )
5957
+ )
5958
+ );
5959
+ parts.push(
5960
+ section(
5961
+ "Transitions",
5962
+ s.transitions.length,
5963
+ ul(
5964
+ s.transitions.map(
5965
+ (t) => `<code>${escapeHtml(t.key ?? t.id)}</code> on <code>&lt;${escapeHtml(t.nodeType)}&gt;</code> <span class="st-dim">${t.duration}ms${t.appear ? " \xB7 appear" : ""}</span>`
5966
+ )
5967
+ )
5968
+ )
5969
+ );
5970
+ if (s.forms !== void 0) {
5971
+ parts.push(
5972
+ section(
5973
+ "Forms",
5974
+ Object.keys(s.forms).length,
5975
+ ul(
5976
+ Object.entries(s.forms).map(
5977
+ ([label, f]) => `<code>${escapeHtml(label)}</code>: ${f.valid ? "valid" : "invalid"} \xB7 ${escapeHtml(f.status)} \xB7 ${f.fields.length} field(s)`
5978
+ )
5979
+ )
5980
+ )
5981
+ );
5982
+ }
5983
+ if (s.i18n !== void 0) {
5984
+ const missing = s.i18n.missingKeys;
5985
+ parts.push(
5986
+ section(
5987
+ "i18n",
5988
+ void 0,
5989
+ `<p><code>${escapeHtml(s.i18n.locale)}</code> of [${s.i18n.locales.map(escapeHtml).join(", ")}]${missing !== void 0 ? ` <span class="st-dim">\xB7 missing ${missing.length}</span>` : ""}</p>`
5990
+ )
5991
+ );
5992
+ }
5993
+ parts.push(
5994
+ section(
5995
+ "Performance",
5996
+ s.performance.diagnostics.length,
5997
+ `<p class="st-dim">Structural counts, not runtime timings \u2014 not a production profiler.</p>` + ul(s.performance.diagnostics.map((d) => `<code>${escapeHtml(d.code)}</code>: ${escapeHtml(d.message)}`))
5998
+ )
5999
+ );
6000
+ parts.push(
6001
+ section(
6002
+ "Diagnostics",
6003
+ s.diagnostics.errors + s.diagnostics.warnings,
6004
+ `<p class="st-dim">${s.diagnostics.errors} error(s) \xB7 ${s.diagnostics.warnings} warning(s)</p>` + ul(s.diagnostics.messages.map((m) => escapeHtml(m)))
6005
+ )
6006
+ );
6007
+ const title = `StreetUI DevTools \u2014 ${escapeHtml(app.identity.name)}`;
6008
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${title}</title><style>${DEVTOOLS_CSS}</style></head><body class="st-devtools"><header class="st-header"><h1>${title} <span class="st-dim">v${escapeHtml(app.identity.version)}</span></h1></header><main>${parts.join("")}</main></body></html>`;
6009
+ }
6010
+ function renderNodeTree(node) {
6011
+ const lines = [];
6012
+ const walk = (n) => {
6013
+ const indent = " ".repeat(n.depth);
6014
+ const key = n.key !== void 0 ? ` #${n.key}` : "";
6015
+ lines.push(`${indent}<${n.type}${key}> ${n.id}`);
6016
+ for (const c of n.children) walk(c);
6017
+ };
6018
+ walk(node);
6019
+ return lines.join("\n");
6020
+ }
6021
+ var DEVTOOLS_CSS = [
6022
+ ".st-devtools{font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;margin:0;color:#e6e6e6;background:#1e1e28}",
6023
+ ".st-header{padding:12px 16px;border-bottom:1px solid #333;background:#15151c}",
6024
+ ".st-header h1{font-size:14px;margin:0}",
6025
+ "main{padding:8px 16px}",
6026
+ ".st-panel{margin:12px 0;border:1px solid #2c2c38;border-radius:6px;overflow:hidden}",
6027
+ ".st-panel h2{font-size:12px;text-transform:uppercase;letter-spacing:.04em;margin:0;padding:6px 10px;background:#23232e}",
6028
+ ".st-panel ul{list-style:none;margin:0;padding:6px 10px}",
6029
+ ".st-panel li{padding:1px 0}",
6030
+ ".st-panel p{margin:6px 10px}",
6031
+ ".st-count{background:#3a3a4a;border-radius:10px;padding:0 7px;font-size:11px;float:right}",
6032
+ ".st-dim{color:#8a8a9a}.st-key{color:#7db4ff}.st-err{color:#ff8a8a}.st-empty{color:#6a6a7a}",
6033
+ ".st-tree{margin:6px 10px;white-space:pre;overflow:auto;color:#c8c8d4}",
6034
+ ".st-depth{display:inline-block}.st-depth{width:calc(var(--d,0)*12px)}",
6035
+ "code{color:#d7d7e0}"
6036
+ ].join("");
6037
+
6038
+ // ../devtools/src/devtools-report.ts
6039
+ function renderDevToolsReport(compiled, sources = {}, options = {}) {
6040
+ const session = createDevTools(compiled, sources, options);
6041
+ return renderDevToolsHTML(session.snapshot);
6042
+ }
6043
+
5209
6044
  // src/config.ts
5210
6045
  function defineConfig(config) {
5211
6046
  return config;
@@ -5228,6 +6063,8 @@ export {
5228
6063
  FOCUSABLE_SELECTOR,
5229
6064
  FormBuilderImpl,
5230
6065
  GraphNode,
6066
+ HeadManager,
6067
+ HttpError,
5231
6068
  Lifecycle,
5232
6069
  ListBuilderImpl,
5233
6070
  NodeInstance,
@@ -5268,7 +6105,9 @@ export {
5268
6105
  containFocus,
5269
6106
  createAnnouncer,
5270
6107
  createApplication,
6108
+ createAuthSession,
5271
6109
  createBrowserHistory,
6110
+ createClient,
5272
6111
  createContext,
5273
6112
  createDevTools,
5274
6113
  createForm,
@@ -5284,10 +6123,12 @@ export {
5284
6123
  createStreetEvent,
5285
6124
  defineConfig,
5286
6125
  derived,
6126
+ describeError,
5287
6127
  diagnosePerformance,
5288
6128
  effect,
5289
6129
  email,
5290
6130
  environment,
6131
+ escapeHtml,
5291
6132
  escapeHtmlAttr,
5292
6133
  escapeHtmlText,
5293
6134
  flushSync,
@@ -5319,7 +6160,9 @@ export {
5319
6160
  interpolate,
5320
6161
  isBatching,
5321
6162
  isComponentDefinition,
6163
+ isHeadContribution,
5322
6164
  isTransitionConfig,
6165
+ linkUpdate,
5323
6166
  matchPattern,
5324
6167
  matchRoutes,
5325
6168
  maxLength,
@@ -5327,6 +6170,7 @@ export {
5327
6170
  mountGraph,
5328
6171
  mountNode,
5329
6172
  mountRouter,
6173
+ mutation,
5330
6174
  nextId,
5331
6175
  nodeIdPrefix,
5332
6176
  nodeTypeStats,
@@ -5343,10 +6187,15 @@ export {
5343
6187
  readState,
5344
6188
  reconcileChildren,
5345
6189
  reconcileChildrenByPlan,
6190
+ renderDevToolsHTML,
6191
+ renderDevToolsReport,
6192
+ renderHead,
5346
6193
  renderToString,
5347
6194
  reportDiagnostic,
6195
+ reportError,
5348
6196
  required,
5349
6197
  resetIdCounter,
6198
+ resolveHead,
5350
6199
  resolveTag,
5351
6200
  resolveTransition,
5352
6201
  resource,
@@ -5374,6 +6223,7 @@ export {
5374
6223
  validateGraph,
5375
6224
  wireComponentBehavior,
5376
6225
  wireEvents,
6226
+ wireHeadBehavior,
5377
6227
  wireOverlayBehavior,
5378
6228
  wireReactiveList,
5379
6229
  wireSignalBindings