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/bin.cjs +1 -1
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/create-bin.cjs +1 -1
- package/dist/create-bin.cjs.map +1 -1
- package/dist/create-bin.js +1 -1
- package/dist/create-bin.js.map +1 -1
- package/dist/{compile-DqsMQVJk.d.cts → hydration-diagnostics-CFYDXBlm.d.cts} +353 -3
- package/dist/{compile-DqsMQVJk.d.ts → hydration-diagnostics-CFYDXBlm.d.ts} +353 -3
- package/dist/index.cjs +865 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +355 -147
- package/dist/index.d.ts +355 -147
- package/dist/index.js +851 -8
- package/dist/index.js.map +1 -1
- package/dist/renderer-interface-BUhHV7ja.d.cts +26 -0
- package/dist/renderer-interface-B_-hWnKk.d.ts +26 -0
- package/dist/{server-BYnmb_hR.d.ts → server-C0PvFBzD.d.ts} +181 -2
- package/dist/{server-DnwqBTfE.d.cts → server-DnAOFpMP.d.cts} +181 -2
- package/dist/server.cjs +217 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +216 -3
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +368 -6
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +3 -3
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +368 -6
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/hydration-diagnostics-4pwt6v7b.d.cts +0 -89
- package/dist/hydration-diagnostics-B2FmY05M.d.ts +0 -89
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,8 @@ __export(src_exports, {
|
|
|
37
37
|
FOCUSABLE_SELECTOR: () => FOCUSABLE_SELECTOR,
|
|
38
38
|
FormBuilderImpl: () => FormBuilderImpl,
|
|
39
39
|
GraphNode: () => GraphNode,
|
|
40
|
+
HeadManager: () => HeadManager,
|
|
41
|
+
HttpError: () => HttpError,
|
|
40
42
|
Lifecycle: () => Lifecycle,
|
|
41
43
|
ListBuilderImpl: () => ListBuilderImpl,
|
|
42
44
|
NodeInstance: () => NodeInstance,
|
|
@@ -77,7 +79,9 @@ __export(src_exports, {
|
|
|
77
79
|
containFocus: () => containFocus,
|
|
78
80
|
createAnnouncer: () => createAnnouncer,
|
|
79
81
|
createApplication: () => createApplication,
|
|
82
|
+
createAuthSession: () => createAuthSession,
|
|
80
83
|
createBrowserHistory: () => createBrowserHistory,
|
|
84
|
+
createClient: () => createClient,
|
|
81
85
|
createContext: () => createContext,
|
|
82
86
|
createDevTools: () => createDevTools,
|
|
83
87
|
createForm: () => createForm,
|
|
@@ -93,10 +97,12 @@ __export(src_exports, {
|
|
|
93
97
|
createStreetEvent: () => createStreetEvent,
|
|
94
98
|
defineConfig: () => defineConfig,
|
|
95
99
|
derived: () => derived,
|
|
100
|
+
describeError: () => describeError,
|
|
96
101
|
diagnosePerformance: () => diagnosePerformance,
|
|
97
102
|
effect: () => effect,
|
|
98
103
|
email: () => email,
|
|
99
104
|
environment: () => environment,
|
|
105
|
+
escapeHtml: () => escapeHtml,
|
|
100
106
|
escapeHtmlAttr: () => escapeHtmlAttr,
|
|
101
107
|
escapeHtmlText: () => escapeHtmlText,
|
|
102
108
|
flushSync: () => flushSync,
|
|
@@ -128,7 +134,9 @@ __export(src_exports, {
|
|
|
128
134
|
interpolate: () => interpolate,
|
|
129
135
|
isBatching: () => isBatching,
|
|
130
136
|
isComponentDefinition: () => isComponentDefinition,
|
|
137
|
+
isHeadContribution: () => isHeadContribution,
|
|
131
138
|
isTransitionConfig: () => isTransitionConfig,
|
|
139
|
+
linkUpdate: () => linkUpdate,
|
|
132
140
|
matchPattern: () => matchPattern,
|
|
133
141
|
matchRoutes: () => matchRoutes,
|
|
134
142
|
maxLength: () => maxLength,
|
|
@@ -136,6 +144,7 @@ __export(src_exports, {
|
|
|
136
144
|
mountGraph: () => mountGraph,
|
|
137
145
|
mountNode: () => mountNode,
|
|
138
146
|
mountRouter: () => mountRouter,
|
|
147
|
+
mutation: () => mutation,
|
|
139
148
|
nextId: () => nextId,
|
|
140
149
|
nodeIdPrefix: () => nodeIdPrefix,
|
|
141
150
|
nodeTypeStats: () => nodeTypeStats,
|
|
@@ -152,10 +161,14 @@ __export(src_exports, {
|
|
|
152
161
|
readState: () => readState,
|
|
153
162
|
reconcileChildren: () => reconcileChildren,
|
|
154
163
|
reconcileChildrenByPlan: () => reconcileChildrenByPlan,
|
|
164
|
+
renderDevToolsHTML: () => renderDevToolsHTML,
|
|
165
|
+
renderHead: () => renderHead,
|
|
155
166
|
renderToString: () => renderToString,
|
|
156
167
|
reportDiagnostic: () => reportDiagnostic,
|
|
168
|
+
reportError: () => reportError,
|
|
157
169
|
required: () => required,
|
|
158
170
|
resetIdCounter: () => resetIdCounter,
|
|
171
|
+
resolveHead: () => resolveHead,
|
|
159
172
|
resolveTag: () => resolveTag,
|
|
160
173
|
resolveTransition: () => resolveTransition,
|
|
161
174
|
resource: () => resource,
|
|
@@ -183,6 +196,7 @@ __export(src_exports, {
|
|
|
183
196
|
validateGraph: () => validateGraph,
|
|
184
197
|
wireComponentBehavior: () => wireComponentBehavior,
|
|
185
198
|
wireEvents: () => wireEvents,
|
|
199
|
+
wireHeadBehavior: () => wireHeadBehavior,
|
|
186
200
|
wireOverlayBehavior: () => wireOverlayBehavior,
|
|
187
201
|
wireReactiveList: () => wireReactiveList,
|
|
188
202
|
wireSignalBindings: () => wireSignalBindings
|
|
@@ -190,7 +204,7 @@ __export(src_exports, {
|
|
|
190
204
|
module.exports = __toCommonJS(src_exports);
|
|
191
205
|
|
|
192
206
|
// src/version.ts
|
|
193
|
-
var VERSION = "
|
|
207
|
+
var VERSION = "2.0.0";
|
|
194
208
|
|
|
195
209
|
// ../state/src/signal.ts
|
|
196
210
|
var _activeConsumer = null;
|
|
@@ -530,6 +544,198 @@ function resource(loader, options = {}) {
|
|
|
530
544
|
};
|
|
531
545
|
}
|
|
532
546
|
|
|
547
|
+
// ../state/src/mutation.ts
|
|
548
|
+
function mutation(mutator, options = {}) {
|
|
549
|
+
const status = signal("idle");
|
|
550
|
+
const data = signal(void 0);
|
|
551
|
+
const error = signal(void 0);
|
|
552
|
+
const pending = derived(() => status.get() === "loading");
|
|
553
|
+
let disposed = false;
|
|
554
|
+
let runId = 0;
|
|
555
|
+
const mutate = async (args) => {
|
|
556
|
+
if (disposed) {
|
|
557
|
+
return await mutator(args);
|
|
558
|
+
}
|
|
559
|
+
const myRun = ++runId;
|
|
560
|
+
batch(() => {
|
|
561
|
+
error.set(void 0);
|
|
562
|
+
status.set("loading");
|
|
563
|
+
});
|
|
564
|
+
try {
|
|
565
|
+
const result = await mutator(args);
|
|
566
|
+
if (!disposed && myRun === runId) {
|
|
567
|
+
batch(() => {
|
|
568
|
+
data.set(result);
|
|
569
|
+
error.set(void 0);
|
|
570
|
+
status.set("success");
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
if (!disposed && myRun === runId) {
|
|
574
|
+
await options.onSuccess?.(result, args);
|
|
575
|
+
await options.onSettled?.(args);
|
|
576
|
+
}
|
|
577
|
+
return result;
|
|
578
|
+
} catch (err) {
|
|
579
|
+
if (!disposed && myRun === runId) {
|
|
580
|
+
batch(() => {
|
|
581
|
+
error.set(err);
|
|
582
|
+
status.set("error");
|
|
583
|
+
});
|
|
584
|
+
await options.onError?.(err, args);
|
|
585
|
+
await options.onSettled?.(args);
|
|
586
|
+
}
|
|
587
|
+
throw err;
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
const reset = () => {
|
|
591
|
+
batch(() => {
|
|
592
|
+
status.set("idle");
|
|
593
|
+
data.set(void 0);
|
|
594
|
+
error.set(void 0);
|
|
595
|
+
});
|
|
596
|
+
};
|
|
597
|
+
const dispose = () => {
|
|
598
|
+
disposed = true;
|
|
599
|
+
};
|
|
600
|
+
if (options.onCleanup !== void 0) options.onCleanup(dispose);
|
|
601
|
+
return { status, data, error, pending, mutate, reset, dispose };
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// ../state/src/client.ts
|
|
605
|
+
var HttpError = class extends Error {
|
|
606
|
+
status;
|
|
607
|
+
statusText;
|
|
608
|
+
url;
|
|
609
|
+
body;
|
|
610
|
+
constructor(status, statusText, url, body) {
|
|
611
|
+
super(`HTTP ${status} ${statusText} for ${url}`);
|
|
612
|
+
this.name = "HttpError";
|
|
613
|
+
this.status = status;
|
|
614
|
+
this.statusText = statusText;
|
|
615
|
+
this.url = url;
|
|
616
|
+
this.body = body;
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
function joinUrl(baseUrl, path) {
|
|
620
|
+
if (baseUrl === void 0 || baseUrl === "") return path;
|
|
621
|
+
const b = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
622
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
623
|
+
return `${b}${p}`;
|
|
624
|
+
}
|
|
625
|
+
function withQuery(url, query) {
|
|
626
|
+
if (query === void 0) return url;
|
|
627
|
+
const params = new URLSearchParams();
|
|
628
|
+
for (const [k, v] of Object.entries(query)) params.set(k, String(v));
|
|
629
|
+
const qs = params.toString();
|
|
630
|
+
if (qs === "") return url;
|
|
631
|
+
return url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
|
|
632
|
+
}
|
|
633
|
+
function createClient(config = {}) {
|
|
634
|
+
const doFetch = config.fetch ?? ((input, init) => {
|
|
635
|
+
if (typeof fetch === "undefined") {
|
|
636
|
+
throw new Error("createClient: no global fetch; pass { fetch } explicitly");
|
|
637
|
+
}
|
|
638
|
+
return fetch(input, init);
|
|
639
|
+
});
|
|
640
|
+
async function request(method, path, body, reqConfig = {}) {
|
|
641
|
+
const url = withQuery(joinUrl(config.baseUrl, path), reqConfig.query);
|
|
642
|
+
const headers = { ...config.headers, ...reqConfig.headers };
|
|
643
|
+
const init = { method, headers };
|
|
644
|
+
if (reqConfig.signal !== void 0) init.signal = reqConfig.signal;
|
|
645
|
+
if (body !== void 0) {
|
|
646
|
+
if (headers["Content-Type"] === void 0) headers["Content-Type"] = "application/json";
|
|
647
|
+
init.body = JSON.stringify(body);
|
|
648
|
+
}
|
|
649
|
+
const response = await doFetch(url, init);
|
|
650
|
+
const parsed = await parseBody(response);
|
|
651
|
+
if (!response.ok) {
|
|
652
|
+
throw new HttpError(response.status, response.statusText, url, parsed);
|
|
653
|
+
}
|
|
654
|
+
return parsed;
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
request,
|
|
658
|
+
get: (path, c) => request("GET", path, void 0, c),
|
|
659
|
+
post: (path, b, c) => request("POST", path, b, c),
|
|
660
|
+
put: (path, b, c) => request("PUT", path, b, c),
|
|
661
|
+
patch: (path, b, c) => request("PATCH", path, b, c),
|
|
662
|
+
del: (path, c) => request("DELETE", path, void 0, c),
|
|
663
|
+
resource: (path, options = {}) => {
|
|
664
|
+
const { query, ...resourceOptions } = options;
|
|
665
|
+
return resource(
|
|
666
|
+
(ctx) => request("GET", path, void 0, { signal: ctx.signal, ...query !== void 0 ? { query } : {} }),
|
|
667
|
+
resourceOptions
|
|
668
|
+
);
|
|
669
|
+
},
|
|
670
|
+
mutation: (method, path, options) => mutation((args) => request(method, path, args), options ?? {})
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
async function parseBody(response) {
|
|
674
|
+
const text = await response.text();
|
|
675
|
+
if (text === "") return void 0;
|
|
676
|
+
const type = response.headers.get("content-type") ?? "";
|
|
677
|
+
if (type.includes("application/json")) {
|
|
678
|
+
try {
|
|
679
|
+
return JSON.parse(text);
|
|
680
|
+
} catch {
|
|
681
|
+
return text;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return text;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ../state/src/auth.ts
|
|
688
|
+
function createAuthSession(config) {
|
|
689
|
+
const session = resource(
|
|
690
|
+
(ctx) => config.loadUser(ctx),
|
|
691
|
+
{
|
|
692
|
+
...config.immediate !== void 0 ? { immediate: config.immediate } : {},
|
|
693
|
+
...config.onCleanup !== void 0 ? { onCleanup: config.onCleanup } : {}
|
|
694
|
+
}
|
|
695
|
+
);
|
|
696
|
+
const status = derived(() => {
|
|
697
|
+
const s = session.status.get();
|
|
698
|
+
const d = session.data.get();
|
|
699
|
+
if (s === "error") return "error";
|
|
700
|
+
if (s === "idle" || s === "loading" && d === void 0) return "loading";
|
|
701
|
+
return d === null || d === void 0 ? "unauthenticated" : "authenticated";
|
|
702
|
+
});
|
|
703
|
+
const user = derived(() => {
|
|
704
|
+
const d = session.data.get();
|
|
705
|
+
return d === null ? void 0 : d;
|
|
706
|
+
});
|
|
707
|
+
const authenticated = derived(() => status.get() === "authenticated");
|
|
708
|
+
const unauthenticated = derived(() => status.get() === "unauthenticated");
|
|
709
|
+
const loading = derived(() => status.get() === "loading");
|
|
710
|
+
const logoutMutation = mutation(
|
|
711
|
+
async () => {
|
|
712
|
+
await config.logout?.();
|
|
713
|
+
},
|
|
714
|
+
{ onSuccess: () => session.refetch() }
|
|
715
|
+
);
|
|
716
|
+
const dispose = () => {
|
|
717
|
+
session.dispose();
|
|
718
|
+
status.dispose();
|
|
719
|
+
user.dispose();
|
|
720
|
+
authenticated.dispose();
|
|
721
|
+
unauthenticated.dispose();
|
|
722
|
+
loading.dispose();
|
|
723
|
+
logoutMutation.dispose();
|
|
724
|
+
};
|
|
725
|
+
return {
|
|
726
|
+
status,
|
|
727
|
+
user,
|
|
728
|
+
error: session.error,
|
|
729
|
+
authenticated,
|
|
730
|
+
unauthenticated,
|
|
731
|
+
loading,
|
|
732
|
+
loggingOut: logoutMutation.pending,
|
|
733
|
+
refresh: () => session.refetch(),
|
|
734
|
+
logout: () => logoutMutation.mutate(void 0),
|
|
735
|
+
dispose
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
|
|
533
739
|
// ../core/src/a11y-ids.ts
|
|
534
740
|
var UNSAFE = /[^A-Za-z0-9_-]+/g;
|
|
535
741
|
function toIdToken(base) {
|
|
@@ -796,9 +1002,13 @@ function formatDiagnosticContext(context) {
|
|
|
796
1002
|
const parts = [];
|
|
797
1003
|
if (context.package !== void 0) parts.push(`package=${context.package}`);
|
|
798
1004
|
if (context.operation !== void 0) parts.push(`operation=${context.operation}`);
|
|
1005
|
+
if (context.component !== void 0) parts.push(`component=${context.component}`);
|
|
1006
|
+
if (context.phase !== void 0) parts.push(`phase=${context.phase}`);
|
|
799
1007
|
if (context.nodeId !== void 0) parts.push(`node=${context.nodeId}`);
|
|
1008
|
+
if (context.element !== void 0) parts.push(`element=${context.element}`);
|
|
800
1009
|
if (context.route !== void 0) parts.push(`route=${context.route}`);
|
|
801
1010
|
if (context.resource !== void 0) parts.push(`resource=${context.resource}`);
|
|
1011
|
+
if (context.signal !== void 0) parts.push(`signal=${context.signal}`);
|
|
802
1012
|
return parts.length > 0 ? ` [${parts.join(", ")}]` : "";
|
|
803
1013
|
}
|
|
804
1014
|
var StreetFrameworkError = class extends Error {
|
|
@@ -829,6 +1039,42 @@ function consoleDiagnosticSink(logger = console) {
|
|
|
829
1039
|
error: (m, c) => logger.error?.(`${m}${formatDiagnosticContext(c)}`)
|
|
830
1040
|
};
|
|
831
1041
|
}
|
|
1042
|
+
function describeErrorAt(error, context, options, depth) {
|
|
1043
|
+
const isError = error instanceof Error;
|
|
1044
|
+
const name = isError ? error.name : "Error";
|
|
1045
|
+
const message = isError ? error.message : safeStringify(error);
|
|
1046
|
+
const report = { name, message, isError };
|
|
1047
|
+
if (context !== void 0) report.context = context;
|
|
1048
|
+
if (options.includeStack === true && isError && typeof error.stack === "string") {
|
|
1049
|
+
report.stack = error.stack;
|
|
1050
|
+
}
|
|
1051
|
+
if (options.includeCause === true && isError && depth < 4) {
|
|
1052
|
+
const cause = error.cause;
|
|
1053
|
+
if (cause !== void 0 && cause !== null) {
|
|
1054
|
+
report.cause = describeErrorAt(cause, void 0, options, depth + 1);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return report;
|
|
1058
|
+
}
|
|
1059
|
+
function safeStringify(value) {
|
|
1060
|
+
if (typeof value === "string") return value;
|
|
1061
|
+
if (value === null) return "null";
|
|
1062
|
+
if (value === void 0) return "undefined";
|
|
1063
|
+
const t = typeof value;
|
|
1064
|
+
if (t === "number" || t === "boolean" || t === "bigint" || t === "symbol") {
|
|
1065
|
+
return String(value);
|
|
1066
|
+
}
|
|
1067
|
+
const ctor = t === "object" && value !== null ? value.constructor?.name ?? "Object" : t;
|
|
1068
|
+
return `[non-Error ${ctor}]`;
|
|
1069
|
+
}
|
|
1070
|
+
function describeError(error, context, options = {}) {
|
|
1071
|
+
return describeErrorAt(error, context, options, 0);
|
|
1072
|
+
}
|
|
1073
|
+
function reportError(sink, error, context, options = {}) {
|
|
1074
|
+
const report = describeError(error, context, options);
|
|
1075
|
+
reportDiagnostic(sink, "error", report.message, report.context);
|
|
1076
|
+
return report;
|
|
1077
|
+
}
|
|
832
1078
|
|
|
833
1079
|
// ../graph/src/graph-node.ts
|
|
834
1080
|
var GraphNode = class _GraphNode {
|
|
@@ -999,6 +1245,7 @@ var ApplicationGraph = class {
|
|
|
999
1245
|
this.handlers.delete(`__overlay__${node.id}`);
|
|
1000
1246
|
this.handlers.delete(`__component__${node.id}`);
|
|
1001
1247
|
this.handlers.delete(`__transition__${node.id}`);
|
|
1248
|
+
this.handlers.delete(`__head__${node.id}`);
|
|
1002
1249
|
}
|
|
1003
1250
|
// ── Handler registry ──────────────────────────────────────────────────────
|
|
1004
1251
|
registerHandler(key, fn) {
|
|
@@ -1128,6 +1375,121 @@ function isTransitionConfig(value) {
|
|
|
1128
1375
|
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";
|
|
1129
1376
|
}
|
|
1130
1377
|
|
|
1378
|
+
// ../dsl/src/head.ts
|
|
1379
|
+
function metaDedupKey(m) {
|
|
1380
|
+
if (m.charset !== void 0) return "meta:charset";
|
|
1381
|
+
if (m.name !== void 0) return `meta:name=${m.name}`;
|
|
1382
|
+
if (m.property !== void 0) return `meta:property=${m.property}`;
|
|
1383
|
+
if (m.httpEquiv !== void 0) return `meta:http-equiv=${m.httpEquiv}`;
|
|
1384
|
+
return void 0;
|
|
1385
|
+
}
|
|
1386
|
+
function metaAttrs(m) {
|
|
1387
|
+
const attrs = {};
|
|
1388
|
+
if (m.charset !== void 0) attrs["charset"] = m.charset;
|
|
1389
|
+
if (m.name !== void 0) attrs["name"] = m.name;
|
|
1390
|
+
if (m.property !== void 0) attrs["property"] = m.property;
|
|
1391
|
+
if (m.httpEquiv !== void 0) attrs["http-equiv"] = m.httpEquiv;
|
|
1392
|
+
if (m.content !== void 0) attrs["content"] = m.content;
|
|
1393
|
+
return attrs;
|
|
1394
|
+
}
|
|
1395
|
+
function linkAttrs(l) {
|
|
1396
|
+
const attrs = { rel: l.rel, href: l.href };
|
|
1397
|
+
if (l.sizes !== void 0) attrs["sizes"] = l.sizes;
|
|
1398
|
+
if (l.type !== void 0) attrs["type"] = l.type;
|
|
1399
|
+
if (l.media !== void 0) attrs["media"] = l.media;
|
|
1400
|
+
if (l.as !== void 0) attrs["as"] = l.as;
|
|
1401
|
+
if (l.crossorigin !== void 0) attrs["crossorigin"] = l.crossorigin;
|
|
1402
|
+
if (l.hreflang !== void 0) attrs["hreflang"] = l.hreflang;
|
|
1403
|
+
return attrs;
|
|
1404
|
+
}
|
|
1405
|
+
function resolveHead(config) {
|
|
1406
|
+
const entries = [];
|
|
1407
|
+
if (config.charset !== void 0) {
|
|
1408
|
+
entries.push({ tag: "meta", dedupKey: "meta:charset", attrs: { charset: config.charset } });
|
|
1409
|
+
}
|
|
1410
|
+
if (config.base !== void 0) {
|
|
1411
|
+
entries.push({ tag: "base", dedupKey: "base", attrs: { href: config.base } });
|
|
1412
|
+
}
|
|
1413
|
+
if (config.title !== void 0) {
|
|
1414
|
+
entries.push({ tag: "title", dedupKey: "title", attrs: {}, text: config.title });
|
|
1415
|
+
}
|
|
1416
|
+
if (config.description !== void 0) {
|
|
1417
|
+
entries.push({
|
|
1418
|
+
tag: "meta",
|
|
1419
|
+
dedupKey: "meta:name=description",
|
|
1420
|
+
attrs: { name: "description", content: config.description }
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
if (config.canonical !== void 0) {
|
|
1424
|
+
entries.push({
|
|
1425
|
+
tag: "link",
|
|
1426
|
+
dedupKey: "link:rel=canonical",
|
|
1427
|
+
attrs: { rel: "canonical", href: config.canonical }
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
if (config.robots !== void 0) {
|
|
1431
|
+
entries.push({
|
|
1432
|
+
tag: "meta",
|
|
1433
|
+
dedupKey: "meta:name=robots",
|
|
1434
|
+
attrs: { name: "robots", content: config.robots }
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
if (config.themeColor !== void 0) {
|
|
1438
|
+
entries.push({
|
|
1439
|
+
tag: "meta",
|
|
1440
|
+
dedupKey: "meta:name=theme-color",
|
|
1441
|
+
attrs: { name: "theme-color", content: config.themeColor }
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
if (config.viewport !== void 0) {
|
|
1445
|
+
entries.push({
|
|
1446
|
+
tag: "meta",
|
|
1447
|
+
dedupKey: "meta:name=viewport",
|
|
1448
|
+
attrs: { name: "viewport", content: config.viewport }
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
if (config.favicon !== void 0) {
|
|
1452
|
+
const l = typeof config.favicon === "string" ? { rel: "icon", href: config.favicon } : config.favicon;
|
|
1453
|
+
entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}`, attrs: linkAttrs(l) });
|
|
1454
|
+
}
|
|
1455
|
+
if (config.openGraph !== void 0) {
|
|
1456
|
+
for (const key of Object.keys(config.openGraph)) {
|
|
1457
|
+
const property = `og:${key}`;
|
|
1458
|
+
entries.push({
|
|
1459
|
+
tag: "meta",
|
|
1460
|
+
dedupKey: `meta:property=${property}`,
|
|
1461
|
+
attrs: { property, content: config.openGraph[key] }
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
if (config.twitter !== void 0) {
|
|
1466
|
+
for (const key of Object.keys(config.twitter)) {
|
|
1467
|
+
const name = `twitter:${key}`;
|
|
1468
|
+
entries.push({
|
|
1469
|
+
tag: "meta",
|
|
1470
|
+
dedupKey: `meta:name=${name}`,
|
|
1471
|
+
attrs: { name, content: config.twitter[key] }
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
if (config.meta !== void 0) {
|
|
1476
|
+
for (const m of config.meta) {
|
|
1477
|
+
const key = metaDedupKey(m);
|
|
1478
|
+
if (key === void 0) continue;
|
|
1479
|
+
entries.push({ tag: "meta", dedupKey: key, attrs: metaAttrs(m) });
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
if (config.link !== void 0) {
|
|
1483
|
+
for (const l of config.link) {
|
|
1484
|
+
entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}:href=${l.href}`, attrs: linkAttrs(l) });
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return { entries };
|
|
1488
|
+
}
|
|
1489
|
+
function isHeadContribution(value) {
|
|
1490
|
+
return value !== null && typeof value === "object" && Array.isArray(value["entries"]);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1131
1493
|
// ../dsl/src/builders.ts
|
|
1132
1494
|
function isSignal(v) {
|
|
1133
1495
|
return v !== null && typeof v === "object" && typeof v["get"] === "function" && typeof v["subscribe"] === "function";
|
|
@@ -1509,8 +1871,14 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1509
1871
|
this.container(id, (c) => {
|
|
1510
1872
|
c.when(
|
|
1511
1873
|
hasError,
|
|
1512
|
-
// Error state → fallback.
|
|
1513
|
-
|
|
1874
|
+
// Error state → fallback. The fallback branch mounts exactly when the
|
|
1875
|
+
// boundary enters its error state, so this is also where the optional
|
|
1876
|
+
// `onError` reporting hook fires (observe-only; §6/§7).
|
|
1877
|
+
(fb) => {
|
|
1878
|
+
const currentError = readError();
|
|
1879
|
+
options.onError?.(currentError);
|
|
1880
|
+
options.fallback(fb, currentError, retry);
|
|
1881
|
+
},
|
|
1514
1882
|
// Healthy state → body, guarded against synchronous build throws.
|
|
1515
1883
|
(body) => {
|
|
1516
1884
|
try {
|
|
@@ -1522,6 +1890,24 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1522
1890
|
);
|
|
1523
1891
|
}, { id });
|
|
1524
1892
|
}
|
|
1893
|
+
asyncBoundary(key, res, branches) {
|
|
1894
|
+
const isError = derived(() => res.status.get() === "error");
|
|
1895
|
+
const showSuccess = derived(
|
|
1896
|
+
() => res.status.get() !== "error" && res.data.get() !== void 0
|
|
1897
|
+
);
|
|
1898
|
+
const showLoading = derived(
|
|
1899
|
+
() => res.status.get() !== "error" && res.data.get() === void 0
|
|
1900
|
+
);
|
|
1901
|
+
const dataSignal = derived(() => res.data.get());
|
|
1902
|
+
const retry = () => {
|
|
1903
|
+
void res.refetch();
|
|
1904
|
+
};
|
|
1905
|
+
this.container(key, (c) => {
|
|
1906
|
+
c.when(isError, (fb) => branches.error?.(fb, res.error.peek(), retry));
|
|
1907
|
+
c.when(showSuccess, (sb) => branches.success(sb, dataSignal));
|
|
1908
|
+
c.when(showLoading, (lb) => branches.loading?.(lb));
|
|
1909
|
+
}, { key });
|
|
1910
|
+
}
|
|
1525
1911
|
// ── Portals & overlays ──────────────────────────────────────────────────────
|
|
1526
1912
|
portal(key, builder, options = {}) {
|
|
1527
1913
|
const node = this._graph.createNode("portal", {
|
|
@@ -1531,6 +1917,31 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1531
1917
|
});
|
|
1532
1918
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1533
1919
|
}
|
|
1920
|
+
/**
|
|
1921
|
+
* Declare document metadata (2.0 §1–§3). Creates a `'head'` node — a neutral,
|
|
1922
|
+
* empty inline anchor at this position (one node / one element, so positional
|
|
1923
|
+
* hydration is preserved) — and registers a `__head__<id>` descriptor holding
|
|
1924
|
+
* this call's normalized, dedup-keyed {@link resolveHead} contribution. The
|
|
1925
|
+
* renderer applies it to `document.head` on the browser (adopting server tags
|
|
1926
|
+
* on hydration, cleaning up on unmount / route change) and emits the active
|
|
1927
|
+
* graph's merged metadata as a string on the server (`renderHead`).
|
|
1928
|
+
*
|
|
1929
|
+
* Multiple `head()` nodes may be live at once (app default + route + component)
|
|
1930
|
+
* — the renderer merges them and, per dedup key, the last in document order
|
|
1931
|
+
* wins (see head.ts). No new render path: this reuses the same graph-node +
|
|
1932
|
+
* handler-registry convention as overlays/transitions/components.
|
|
1933
|
+
*/
|
|
1934
|
+
head(metadata) {
|
|
1935
|
+
const node = this._graph.createNode("head", {
|
|
1936
|
+
parent: this._node,
|
|
1937
|
+
props: { "data-streetui-head-anchor": "" }
|
|
1938
|
+
});
|
|
1939
|
+
const contribution = resolveHead(metadata);
|
|
1940
|
+
this._graph.registerHandler(
|
|
1941
|
+
`__head__${node.id}`,
|
|
1942
|
+
() => contribution
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1534
1945
|
/**
|
|
1535
1946
|
* Shared assembly for every overlay kind: a `portal` node whose single child
|
|
1536
1947
|
* is a `when(open, panel)` conditional. The panel container carries the
|
|
@@ -1902,8 +2313,8 @@ var RuntimeNodeInstance = class {
|
|
|
1902
2313
|
this.children.push(instance);
|
|
1903
2314
|
}
|
|
1904
2315
|
/** Subscribe to a signal and register the unsubscribe for cleanup. */
|
|
1905
|
-
trackSignal(
|
|
1906
|
-
const unsub =
|
|
2316
|
+
trackSignal(signal3, handler) {
|
|
2317
|
+
const unsub = signal3.subscribe(handler);
|
|
1907
2318
|
this.cleanup.add(unsub);
|
|
1908
2319
|
}
|
|
1909
2320
|
/** Register an arbitrary cleanup function (e.g. DOM event removal). */
|
|
@@ -2274,6 +2685,9 @@ var BrowserDOMAdapter = class {
|
|
|
2274
2685
|
body() {
|
|
2275
2686
|
return document.body ?? null;
|
|
2276
2687
|
}
|
|
2688
|
+
head() {
|
|
2689
|
+
return document.head ?? null;
|
|
2690
|
+
}
|
|
2277
2691
|
activeElement() {
|
|
2278
2692
|
return document.activeElement ?? null;
|
|
2279
2693
|
}
|
|
@@ -2653,6 +3067,9 @@ var ServerDOMAdapter = class {
|
|
|
2653
3067
|
body() {
|
|
2654
3068
|
return null;
|
|
2655
3069
|
}
|
|
3070
|
+
head() {
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
2656
3073
|
activeElement() {
|
|
2657
3074
|
return null;
|
|
2658
3075
|
}
|
|
@@ -3035,7 +3452,13 @@ var TAG_MAP = {
|
|
|
3035
3452
|
// its children are relocated to a document.body container on the browser
|
|
3036
3453
|
// (see the portal branch in mount.ts). On the server (no body) it renders
|
|
3037
3454
|
// inline, so the anchor tag is what SSR/hydration positionally match on.
|
|
3038
|
-
portal: "div"
|
|
3455
|
+
portal: "div",
|
|
3456
|
+
// A `head()` node renders as a neutral, empty inline anchor <div> at its
|
|
3457
|
+
// declaration site (like a portal anchor). Its actual contribution — title/
|
|
3458
|
+
// meta/link/etc. — is applied to `document.head` by `wireHeadBehavior` on the
|
|
3459
|
+
// browser, and emitted separately by `renderHead()` on the server. Keeping a
|
|
3460
|
+
// one-node/one-element anchor preserves positional hydration.
|
|
3461
|
+
head: "div"
|
|
3039
3462
|
};
|
|
3040
3463
|
function resolveTag(type) {
|
|
3041
3464
|
return TAG_MAP[type] ?? "div";
|
|
@@ -3491,6 +3914,184 @@ var TransitionController = class {
|
|
|
3491
3914
|
}
|
|
3492
3915
|
};
|
|
3493
3916
|
|
|
3917
|
+
// ../renderer/src/head.ts
|
|
3918
|
+
var HEAD_MARKER = "data-streetui-head";
|
|
3919
|
+
var HEAD_KEY = "data-streetui-head-key";
|
|
3920
|
+
function isSignalLike(v) {
|
|
3921
|
+
return v !== null && typeof v === "object" && typeof v["subscribe"] === "function" && typeof v["peek"] === "function";
|
|
3922
|
+
}
|
|
3923
|
+
function readValue(v) {
|
|
3924
|
+
if (isSignalLike(v)) return String(v.peek() ?? "");
|
|
3925
|
+
return String(v ?? "");
|
|
3926
|
+
}
|
|
3927
|
+
function resolveEntry(entry) {
|
|
3928
|
+
const attrs = {};
|
|
3929
|
+
for (const key of Object.keys(entry.attrs)) {
|
|
3930
|
+
attrs[key] = readValue(entry.attrs[key]);
|
|
3931
|
+
}
|
|
3932
|
+
const resolved = { tag: entry.tag, attrs };
|
|
3933
|
+
if (entry.tag === "title") {
|
|
3934
|
+
return { ...resolved, text: readValue(entry.text) };
|
|
3935
|
+
}
|
|
3936
|
+
return resolved;
|
|
3937
|
+
}
|
|
3938
|
+
var HeadManager = class {
|
|
3939
|
+
_dom;
|
|
3940
|
+
_head;
|
|
3941
|
+
_contributions = /* @__PURE__ */ new Map();
|
|
3942
|
+
_applied = /* @__PURE__ */ new Map();
|
|
3943
|
+
_order = 0;
|
|
3944
|
+
_adopted = false;
|
|
3945
|
+
constructor(dom, head) {
|
|
3946
|
+
this._dom = dom;
|
|
3947
|
+
this._head = head;
|
|
3948
|
+
}
|
|
3949
|
+
/** Register (or replace) a node's contribution and re-apply the merged result. */
|
|
3950
|
+
register(nodeId, entries) {
|
|
3951
|
+
this._contributions.set(nodeId, { order: this._order++, entries });
|
|
3952
|
+
this.apply();
|
|
3953
|
+
}
|
|
3954
|
+
/** Withdraw a node's contribution (unmount / route change) and re-apply. */
|
|
3955
|
+
unregister(nodeId) {
|
|
3956
|
+
if (this._contributions.delete(nodeId)) this.apply();
|
|
3957
|
+
}
|
|
3958
|
+
/** Recompute the merged head and patch `document.head` to match. */
|
|
3959
|
+
apply() {
|
|
3960
|
+
if (!this._adopted) {
|
|
3961
|
+
this._adoptServerTags();
|
|
3962
|
+
this._adopted = true;
|
|
3963
|
+
}
|
|
3964
|
+
const ordered = [...this._contributions.values()].sort((a, b) => a.order - b.order);
|
|
3965
|
+
const merged = /* @__PURE__ */ new Map();
|
|
3966
|
+
for (const contribution of ordered) {
|
|
3967
|
+
for (const entry of contribution.entries) {
|
|
3968
|
+
merged.set(entry.dedupKey, resolveEntry(entry));
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
for (const [key, desired] of merged) {
|
|
3972
|
+
const existing = this._applied.get(key);
|
|
3973
|
+
if (existing !== void 0 && existing.tag === desired.tag) {
|
|
3974
|
+
this._reconcileAttrs(existing, desired);
|
|
3975
|
+
} else {
|
|
3976
|
+
if (existing !== void 0) {
|
|
3977
|
+
this._dom.removeChild(this._head, existing.el);
|
|
3978
|
+
this._applied.delete(key);
|
|
3979
|
+
}
|
|
3980
|
+
const el = this._createTag(key, desired);
|
|
3981
|
+
this._dom.appendChild(this._head, el);
|
|
3982
|
+
this._applied.set(key, { el, attrKeys: new Set(Object.keys(desired.attrs)), tag: desired.tag });
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
for (const [key, record] of [...this._applied]) {
|
|
3986
|
+
if (!merged.has(key)) {
|
|
3987
|
+
this._dom.removeChild(this._head, record.el);
|
|
3988
|
+
this._applied.delete(key);
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
_createTag(key, desired) {
|
|
3993
|
+
const el = this._dom.createElement(desired.tag);
|
|
3994
|
+
this._dom.setAttribute(el, HEAD_MARKER, "");
|
|
3995
|
+
this._dom.setAttribute(el, HEAD_KEY, key);
|
|
3996
|
+
for (const attr of Object.keys(desired.attrs)) {
|
|
3997
|
+
this._dom.setAttribute(el, attr, desired.attrs[attr]);
|
|
3998
|
+
}
|
|
3999
|
+
if (desired.tag === "title") this._dom.setTextContent(el, desired.text ?? "");
|
|
4000
|
+
return el;
|
|
4001
|
+
}
|
|
4002
|
+
_reconcileAttrs(record, desired) {
|
|
4003
|
+
const nextKeys = new Set(Object.keys(desired.attrs));
|
|
4004
|
+
for (const attr of record.attrKeys) {
|
|
4005
|
+
if (!nextKeys.has(attr)) this._dom.removeAttribute(record.el, attr);
|
|
4006
|
+
}
|
|
4007
|
+
for (const attr of nextKeys) {
|
|
4008
|
+
this._dom.setAttribute(record.el, attr, desired.attrs[attr]);
|
|
4009
|
+
}
|
|
4010
|
+
if (desired.tag === "title") this._dom.setTextContent(record.el, desired.text ?? "");
|
|
4011
|
+
record.attrKeys = nextKeys;
|
|
4012
|
+
}
|
|
4013
|
+
/**
|
|
4014
|
+
* Seed `_applied` from server-emitted `[data-streetui-head-key]` tags already
|
|
4015
|
+
* in `document.head`. The subsequent diff reuses these elements when the
|
|
4016
|
+
* client desires the same key (no duplicate), rewrites them if the value
|
|
4017
|
+
* changed, or removes them if the client graph no longer wants them.
|
|
4018
|
+
*/
|
|
4019
|
+
_adoptServerTags() {
|
|
4020
|
+
for (const child of this._dom.childNodes(this._head)) {
|
|
4021
|
+
if (!this._dom.isElement(child)) continue;
|
|
4022
|
+
const el = child;
|
|
4023
|
+
const key = this._dom.getAttribute(el, HEAD_KEY);
|
|
4024
|
+
if (key === null) continue;
|
|
4025
|
+
this._applied.set(key, {
|
|
4026
|
+
el,
|
|
4027
|
+
attrKeys: new Set(this._attrNames(el)),
|
|
4028
|
+
tag: this._dom.tagName(el)
|
|
4029
|
+
});
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
/** The framework-managed attribute names currently on a server tag. */
|
|
4033
|
+
_attrNames(el) {
|
|
4034
|
+
const names = [];
|
|
4035
|
+
if (this._dom.getAttribute(el, HEAD_MARKER) !== null) names.push(HEAD_MARKER);
|
|
4036
|
+
if (this._dom.getAttribute(el, HEAD_KEY) !== null) names.push(HEAD_KEY);
|
|
4037
|
+
return names;
|
|
4038
|
+
}
|
|
4039
|
+
};
|
|
4040
|
+
function getHeadManager(ctx) {
|
|
4041
|
+
const head = ctx.dom.head();
|
|
4042
|
+
if (head === null) return null;
|
|
4043
|
+
const mutable = ctx;
|
|
4044
|
+
if (mutable.head === void 0) mutable.head = new HeadManager(ctx.dom, head);
|
|
4045
|
+
return mutable.head;
|
|
4046
|
+
}
|
|
4047
|
+
function wireHeadBehavior(ctx, graphNode, instance) {
|
|
4048
|
+
const manager = getHeadManager(ctx);
|
|
4049
|
+
if (manager === null) return;
|
|
4050
|
+
const descFn = ctx.graph.getHandler(`__head__${graphNode.id}`);
|
|
4051
|
+
if (descFn === void 0) return;
|
|
4052
|
+
const contribution = descFn();
|
|
4053
|
+
const nodeId = graphNode.id;
|
|
4054
|
+
manager.register(nodeId, contribution.entries);
|
|
4055
|
+
for (const entry of contribution.entries) {
|
|
4056
|
+
for (const attrKey of Object.keys(entry.attrs)) {
|
|
4057
|
+
const v = entry.attrs[attrKey];
|
|
4058
|
+
if (isSignalLike(v)) {
|
|
4059
|
+
instance.trackCleanup(v.subscribe(() => manager.apply()));
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
if (isSignalLike(entry.text)) {
|
|
4063
|
+
instance.trackCleanup(entry.text.subscribe(() => manager.apply()));
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
instance.trackCleanup(() => manager.unregister(nodeId));
|
|
4067
|
+
}
|
|
4068
|
+
function renderHead(compiled) {
|
|
4069
|
+
const graph = compiled.graph;
|
|
4070
|
+
const merged = /* @__PURE__ */ new Map();
|
|
4071
|
+
graph.walk((node) => {
|
|
4072
|
+
if (node.type !== "head") return;
|
|
4073
|
+
const descFn = graph.getHandler(`__head__${node.id}`);
|
|
4074
|
+
if (descFn === void 0) return;
|
|
4075
|
+
for (const entry of descFn().entries) {
|
|
4076
|
+
merged.set(entry.dedupKey, resolveEntry(entry));
|
|
4077
|
+
}
|
|
4078
|
+
});
|
|
4079
|
+
if (merged.size === 0) return "";
|
|
4080
|
+
const dom = new ServerDOMAdapter();
|
|
4081
|
+
let out = "";
|
|
4082
|
+
for (const [key, desired] of merged) {
|
|
4083
|
+
const el = dom.createElement(desired.tag);
|
|
4084
|
+
dom.setAttribute(el, HEAD_MARKER, "");
|
|
4085
|
+
dom.setAttribute(el, HEAD_KEY, key);
|
|
4086
|
+
for (const attr of Object.keys(desired.attrs)) {
|
|
4087
|
+
dom.setAttribute(el, attr, desired.attrs[attr]);
|
|
4088
|
+
}
|
|
4089
|
+
if (desired.tag === "title") dom.setTextContent(el, desired.text ?? "");
|
|
4090
|
+
out += dom.serializeOuter(el);
|
|
4091
|
+
}
|
|
4092
|
+
return out;
|
|
4093
|
+
}
|
|
4094
|
+
|
|
3494
4095
|
// ../renderer/src/mount.ts
|
|
3495
4096
|
var SKIP_PROP_KEYS = /* @__PURE__ */ new Set([
|
|
3496
4097
|
"text",
|
|
@@ -3613,6 +4214,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3613
4214
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
3614
4215
|
ctx.instances.set(graphNode.id, instance2);
|
|
3615
4216
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
4217
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
4218
|
+
wireSignalBindings(ctx, graphNode, instance2, linkUpdate(dom, el2));
|
|
4219
|
+
}
|
|
3616
4220
|
dom.appendChild(parentDom, el2);
|
|
3617
4221
|
return instance2;
|
|
3618
4222
|
}
|
|
@@ -3668,6 +4272,16 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3668
4272
|
wireOverlayBehavior(ctx, graphNode, instance2, target);
|
|
3669
4273
|
return instance2;
|
|
3670
4274
|
}
|
|
4275
|
+
if (graphNode.type === "head") {
|
|
4276
|
+
const anchor = dom.createElement(resolveTag("head"));
|
|
4277
|
+
dom.setAttribute(anchor, "data-streetui-head-anchor", "");
|
|
4278
|
+
applyNodeProps(ctx, graphNode, anchor);
|
|
4279
|
+
const instance2 = new NodeInstance(graphNode, anchor);
|
|
4280
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
4281
|
+
dom.appendChild(parentDom, anchor);
|
|
4282
|
+
wireHeadBehavior(ctx, graphNode, instance2);
|
|
4283
|
+
return instance2;
|
|
4284
|
+
}
|
|
3671
4285
|
const tag = resolveTag(graphNode.type);
|
|
3672
4286
|
const el = dom.createElement(tag);
|
|
3673
4287
|
applyNodeProps(ctx, graphNode, el);
|
|
@@ -3732,6 +4346,17 @@ function buttonUpdate(dom, el) {
|
|
|
3732
4346
|
}
|
|
3733
4347
|
};
|
|
3734
4348
|
}
|
|
4349
|
+
function linkUpdate(dom, el) {
|
|
4350
|
+
return (propKey, value) => {
|
|
4351
|
+
if (propKey === "label") {
|
|
4352
|
+
dom.setTextContent(el, String(value ?? ""));
|
|
4353
|
+
} else if (propKey === "href") {
|
|
4354
|
+
dom.setAttribute(el, "href", String(value ?? ""));
|
|
4355
|
+
} else {
|
|
4356
|
+
applyProp(dom, el, propKey, value);
|
|
4357
|
+
}
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
3735
4360
|
function applyNodeProps(ctx, graphNode, el) {
|
|
3736
4361
|
const props = graphNode.props;
|
|
3737
4362
|
for (const key in props) {
|
|
@@ -3966,7 +4591,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
3966
4591
|
case "link": {
|
|
3967
4592
|
const instance = new NodeInstance(graphNode, domNode);
|
|
3968
4593
|
ctx.instances.set(graphNode.id, instance);
|
|
3969
|
-
if (graphNode.type === "link")
|
|
4594
|
+
if (graphNode.type === "link") {
|
|
4595
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
4596
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
4597
|
+
wireSignalBindings(ctx, graphNode, instance, linkUpdate(dom, domNode));
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
3970
4600
|
return instance;
|
|
3971
4601
|
}
|
|
3972
4602
|
case "reactive-list":
|
|
@@ -3996,6 +4626,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
3996
4626
|
wireOverlayBehavior(ctx, graphNode, instance, target);
|
|
3997
4627
|
return instance;
|
|
3998
4628
|
}
|
|
4629
|
+
case "head": {
|
|
4630
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
4631
|
+
ctx.instances.set(graphNode.id, instance);
|
|
4632
|
+
wireHeadBehavior(ctx, graphNode, instance);
|
|
4633
|
+
return instance;
|
|
4634
|
+
}
|
|
3999
4635
|
default: {
|
|
4000
4636
|
const instance = new NodeInstance(graphNode, domNode);
|
|
4001
4637
|
ctx.instances.set(graphNode.id, instance);
|
|
@@ -4242,7 +4878,8 @@ function analyzeGraph(graph) {
|
|
|
4242
4878
|
const isConditional = node.type === "conditional";
|
|
4243
4879
|
const isPortal = node.type === "portal";
|
|
4244
4880
|
const isComponent = node.type === "component";
|
|
4245
|
-
const
|
|
4881
|
+
const isHead = node.type === "head";
|
|
4882
|
+
const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent && !isHead;
|
|
4246
4883
|
const isStaticSubtree = isStatic && allChildrenStatic;
|
|
4247
4884
|
nodes.set(node.id, {
|
|
4248
4885
|
isStatic,
|
|
@@ -5304,11 +5941,22 @@ function capture(compiled, sources, options) {
|
|
|
5304
5941
|
snapshot: app.perf,
|
|
5305
5942
|
diagnostics: diagnosePerformance(compiled, options.perfThresholds)
|
|
5306
5943
|
};
|
|
5944
|
+
const components = inspectComponents(compiled.graph);
|
|
5945
|
+
const interactions = inspectInteractions(compiled.graph);
|
|
5946
|
+
const diagnostics = {
|
|
5947
|
+
errors: app.diagnostics.errors,
|
|
5948
|
+
warnings: app.diagnostics.warnings,
|
|
5949
|
+
messages: app.diagnostics.messages
|
|
5950
|
+
};
|
|
5307
5951
|
const snapshot = {
|
|
5308
5952
|
application,
|
|
5309
5953
|
graph: app.graph,
|
|
5310
5954
|
signals,
|
|
5311
5955
|
performance,
|
|
5956
|
+
components,
|
|
5957
|
+
overlays: interactions.overlays,
|
|
5958
|
+
transitions: interactions.transitions,
|
|
5959
|
+
diagnostics,
|
|
5312
5960
|
...sources.router !== void 0 ? { router: inspectRouter(sources.router) } : {},
|
|
5313
5961
|
...sources.resources !== void 0 ? { resources: mapInspect(sources.resources, (r) => inspectResource(r)) } : {},
|
|
5314
5962
|
...sources.forms !== void 0 ? { forms: mapInspect(sources.forms, (f) => inspectForm(f)) } : {},
|
|
@@ -5349,6 +5997,25 @@ function formatSnapshot(s) {
|
|
|
5349
5997
|
for (const [label, sig] of Object.entries(s.signals.live)) {
|
|
5350
5998
|
lines.push(` ${label} [${sig.kind}] = ${format(sig.value)} \xB7 observers ${sig.observerCount ?? "?"}`);
|
|
5351
5999
|
}
|
|
6000
|
+
if (s.components.length > 0) {
|
|
6001
|
+
lines.push(`Components: ${s.components.length}`);
|
|
6002
|
+
for (const c of s.components) {
|
|
6003
|
+
lines.push(` ${" ".repeat(c.depth)}${c.name}${c.key !== void 0 ? ` (#${c.key})` : ""} \xB7 children ${c.childCount}`);
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
if (s.overlays.length > 0) {
|
|
6007
|
+
lines.push(`Overlays: ${s.overlays.length}`);
|
|
6008
|
+
for (const o of s.overlays) {
|
|
6009
|
+
const kind = o.modal ? "modal" : o.menu ? "menu" : o.takesFocus ? "focusable" : "non-modal";
|
|
6010
|
+
lines.push(` ${o.key ?? o.id} [${kind}] ${o.open ? "open" : "closed"}`);
|
|
6011
|
+
}
|
|
6012
|
+
}
|
|
6013
|
+
if (s.transitions.length > 0) {
|
|
6014
|
+
lines.push(`Transitions: ${s.transitions.length}`);
|
|
6015
|
+
for (const t of s.transitions) {
|
|
6016
|
+
lines.push(` ${t.key ?? t.id} on <${t.nodeType}> \xB7 ${t.duration}ms${t.appear ? " \xB7 appear" : ""}`);
|
|
6017
|
+
}
|
|
6018
|
+
}
|
|
5352
6019
|
if (s.router !== void 0) {
|
|
5353
6020
|
lines.push(`Router: ${s.router.path} (${s.router.pattern})${s.router.isFallback ? " [fallback]" : ""}`);
|
|
5354
6021
|
}
|
|
@@ -5382,6 +6049,12 @@ function formatSnapshot(s) {
|
|
|
5382
6049
|
for (const d of s.performance.diagnostics) {
|
|
5383
6050
|
lines.push(` ${d.code}: ${d.message}`);
|
|
5384
6051
|
}
|
|
6052
|
+
lines.push(
|
|
6053
|
+
`Diagnostics: ${s.diagnostics.errors} error(s), ${s.diagnostics.warnings} warning(s)`
|
|
6054
|
+
);
|
|
6055
|
+
for (const m of s.diagnostics.messages) {
|
|
6056
|
+
lines.push(` ${m}`);
|
|
6057
|
+
}
|
|
5385
6058
|
return lines.join("\n");
|
|
5386
6059
|
}
|
|
5387
6060
|
function format(value) {
|
|
@@ -5397,6 +6070,176 @@ function format(value) {
|
|
|
5397
6070
|
return String(value);
|
|
5398
6071
|
}
|
|
5399
6072
|
|
|
6073
|
+
// ../devtools/src/view.ts
|
|
6074
|
+
function escapeHtml(value) {
|
|
6075
|
+
const s = typeof value === "string" ? value : stringifyValue(value);
|
|
6076
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6077
|
+
}
|
|
6078
|
+
function stringifyValue(value) {
|
|
6079
|
+
if (value === null || value === void 0) return String(value);
|
|
6080
|
+
if (typeof value === "object") {
|
|
6081
|
+
try {
|
|
6082
|
+
return JSON.stringify(value);
|
|
6083
|
+
} catch {
|
|
6084
|
+
return "[object]";
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
return String(value);
|
|
6088
|
+
}
|
|
6089
|
+
function section(title, count, body) {
|
|
6090
|
+
const badge = count === void 0 ? "" : ` <span class="st-count">${count}</span>`;
|
|
6091
|
+
return `<section class="st-panel"><h2>${escapeHtml(title)}${badge}</h2>${body}</section>`;
|
|
6092
|
+
}
|
|
6093
|
+
function ul(items) {
|
|
6094
|
+
if (items.length === 0) return '<p class="st-empty">(none)</p>';
|
|
6095
|
+
return `<ul>${items.map((i) => `<li>${i}</li>`).join("")}</ul>`;
|
|
6096
|
+
}
|
|
6097
|
+
function renderDevToolsHTML(s) {
|
|
6098
|
+
const app = s.application;
|
|
6099
|
+
const parts = [];
|
|
6100
|
+
parts.push(
|
|
6101
|
+
section(
|
|
6102
|
+
"Application",
|
|
6103
|
+
void 0,
|
|
6104
|
+
`<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>`
|
|
6105
|
+
)
|
|
6106
|
+
);
|
|
6107
|
+
parts.push(
|
|
6108
|
+
section(
|
|
6109
|
+
"Components",
|
|
6110
|
+
s.components.length,
|
|
6111
|
+
ul(
|
|
6112
|
+
s.components.map(
|
|
6113
|
+
(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>`
|
|
6114
|
+
)
|
|
6115
|
+
)
|
|
6116
|
+
)
|
|
6117
|
+
);
|
|
6118
|
+
parts.push(section("Graph", void 0, `<pre class="st-tree">${escapeHtml(renderNodeTree(s.graph))}</pre>`));
|
|
6119
|
+
parts.push(
|
|
6120
|
+
section(
|
|
6121
|
+
"Signals",
|
|
6122
|
+
s.signals.boundSignalIds.length,
|
|
6123
|
+
`<p class="st-dim">${s.signals.boundSignalIds.length} bound in graph \xB7 effects shown as observer counts (no global effect registry)</p>` + ul(
|
|
6124
|
+
Object.entries(s.signals.live).map(
|
|
6125
|
+
([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>`
|
|
6126
|
+
)
|
|
6127
|
+
)
|
|
6128
|
+
)
|
|
6129
|
+
);
|
|
6130
|
+
if (s.router !== void 0) {
|
|
6131
|
+
parts.push(
|
|
6132
|
+
section(
|
|
6133
|
+
"Router",
|
|
6134
|
+
void 0,
|
|
6135
|
+
`<p><code>${escapeHtml(s.router.path)}</code> <span class="st-dim">(${escapeHtml(s.router.pattern)})${s.router.isFallback ? " \xB7 fallback" : ""}</span></p>`
|
|
6136
|
+
)
|
|
6137
|
+
);
|
|
6138
|
+
}
|
|
6139
|
+
if (s.resources !== void 0) {
|
|
6140
|
+
parts.push(
|
|
6141
|
+
section(
|
|
6142
|
+
"Resources",
|
|
6143
|
+
Object.keys(s.resources).length,
|
|
6144
|
+
ul(
|
|
6145
|
+
Object.entries(s.resources).map(
|
|
6146
|
+
([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>` : "")
|
|
6147
|
+
)
|
|
6148
|
+
)
|
|
6149
|
+
)
|
|
6150
|
+
);
|
|
6151
|
+
}
|
|
6152
|
+
parts.push(
|
|
6153
|
+
section(
|
|
6154
|
+
"Overlays",
|
|
6155
|
+
s.overlays.length,
|
|
6156
|
+
ul(
|
|
6157
|
+
s.overlays.map((o) => {
|
|
6158
|
+
const kind = o.modal ? "modal" : o.menu ? "menu" : o.takesFocus ? "focusable" : "non-modal";
|
|
6159
|
+
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>`;
|
|
6160
|
+
})
|
|
6161
|
+
)
|
|
6162
|
+
)
|
|
6163
|
+
);
|
|
6164
|
+
parts.push(
|
|
6165
|
+
section(
|
|
6166
|
+
"Transitions",
|
|
6167
|
+
s.transitions.length,
|
|
6168
|
+
ul(
|
|
6169
|
+
s.transitions.map(
|
|
6170
|
+
(t) => `<code>${escapeHtml(t.key ?? t.id)}</code> on <code><${escapeHtml(t.nodeType)}></code> <span class="st-dim">${t.duration}ms${t.appear ? " \xB7 appear" : ""}</span>`
|
|
6171
|
+
)
|
|
6172
|
+
)
|
|
6173
|
+
)
|
|
6174
|
+
);
|
|
6175
|
+
if (s.forms !== void 0) {
|
|
6176
|
+
parts.push(
|
|
6177
|
+
section(
|
|
6178
|
+
"Forms",
|
|
6179
|
+
Object.keys(s.forms).length,
|
|
6180
|
+
ul(
|
|
6181
|
+
Object.entries(s.forms).map(
|
|
6182
|
+
([label, f]) => `<code>${escapeHtml(label)}</code>: ${f.valid ? "valid" : "invalid"} \xB7 ${escapeHtml(f.status)} \xB7 ${f.fields.length} field(s)`
|
|
6183
|
+
)
|
|
6184
|
+
)
|
|
6185
|
+
)
|
|
6186
|
+
);
|
|
6187
|
+
}
|
|
6188
|
+
if (s.i18n !== void 0) {
|
|
6189
|
+
const missing = s.i18n.missingKeys;
|
|
6190
|
+
parts.push(
|
|
6191
|
+
section(
|
|
6192
|
+
"i18n",
|
|
6193
|
+
void 0,
|
|
6194
|
+
`<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>`
|
|
6195
|
+
)
|
|
6196
|
+
);
|
|
6197
|
+
}
|
|
6198
|
+
parts.push(
|
|
6199
|
+
section(
|
|
6200
|
+
"Performance",
|
|
6201
|
+
s.performance.diagnostics.length,
|
|
6202
|
+
`<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)}`))
|
|
6203
|
+
)
|
|
6204
|
+
);
|
|
6205
|
+
parts.push(
|
|
6206
|
+
section(
|
|
6207
|
+
"Diagnostics",
|
|
6208
|
+
s.diagnostics.errors + s.diagnostics.warnings,
|
|
6209
|
+
`<p class="st-dim">${s.diagnostics.errors} error(s) \xB7 ${s.diagnostics.warnings} warning(s)</p>` + ul(s.diagnostics.messages.map((m) => escapeHtml(m)))
|
|
6210
|
+
)
|
|
6211
|
+
);
|
|
6212
|
+
const title = `StreetUI DevTools \u2014 ${escapeHtml(app.identity.name)}`;
|
|
6213
|
+
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>`;
|
|
6214
|
+
}
|
|
6215
|
+
function renderNodeTree(node) {
|
|
6216
|
+
const lines = [];
|
|
6217
|
+
const walk = (n) => {
|
|
6218
|
+
const indent = " ".repeat(n.depth);
|
|
6219
|
+
const key = n.key !== void 0 ? ` #${n.key}` : "";
|
|
6220
|
+
lines.push(`${indent}<${n.type}${key}> ${n.id}`);
|
|
6221
|
+
for (const c of n.children) walk(c);
|
|
6222
|
+
};
|
|
6223
|
+
walk(node);
|
|
6224
|
+
return lines.join("\n");
|
|
6225
|
+
}
|
|
6226
|
+
var DEVTOOLS_CSS = [
|
|
6227
|
+
".st-devtools{font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;margin:0;color:#e6e6e6;background:#1e1e28}",
|
|
6228
|
+
".st-header{padding:12px 16px;border-bottom:1px solid #333;background:#15151c}",
|
|
6229
|
+
".st-header h1{font-size:14px;margin:0}",
|
|
6230
|
+
"main{padding:8px 16px}",
|
|
6231
|
+
".st-panel{margin:12px 0;border:1px solid #2c2c38;border-radius:6px;overflow:hidden}",
|
|
6232
|
+
".st-panel h2{font-size:12px;text-transform:uppercase;letter-spacing:.04em;margin:0;padding:6px 10px;background:#23232e}",
|
|
6233
|
+
".st-panel ul{list-style:none;margin:0;padding:6px 10px}",
|
|
6234
|
+
".st-panel li{padding:1px 0}",
|
|
6235
|
+
".st-panel p{margin:6px 10px}",
|
|
6236
|
+
".st-count{background:#3a3a4a;border-radius:10px;padding:0 7px;font-size:11px;float:right}",
|
|
6237
|
+
".st-dim{color:#8a8a9a}.st-key{color:#7db4ff}.st-err{color:#ff8a8a}.st-empty{color:#6a6a7a}",
|
|
6238
|
+
".st-tree{margin:6px 10px;white-space:pre;overflow:auto;color:#c8c8d4}",
|
|
6239
|
+
".st-depth{display:inline-block}.st-depth{width:calc(var(--d,0)*12px)}",
|
|
6240
|
+
"code{color:#d7d7e0}"
|
|
6241
|
+
].join("");
|
|
6242
|
+
|
|
5400
6243
|
// src/config.ts
|
|
5401
6244
|
function defineConfig(config) {
|
|
5402
6245
|
return config;
|
|
@@ -5420,6 +6263,8 @@ function defineConfig(config) {
|
|
|
5420
6263
|
FOCUSABLE_SELECTOR,
|
|
5421
6264
|
FormBuilderImpl,
|
|
5422
6265
|
GraphNode,
|
|
6266
|
+
HeadManager,
|
|
6267
|
+
HttpError,
|
|
5423
6268
|
Lifecycle,
|
|
5424
6269
|
ListBuilderImpl,
|
|
5425
6270
|
NodeInstance,
|
|
@@ -5460,7 +6305,9 @@ function defineConfig(config) {
|
|
|
5460
6305
|
containFocus,
|
|
5461
6306
|
createAnnouncer,
|
|
5462
6307
|
createApplication,
|
|
6308
|
+
createAuthSession,
|
|
5463
6309
|
createBrowserHistory,
|
|
6310
|
+
createClient,
|
|
5464
6311
|
createContext,
|
|
5465
6312
|
createDevTools,
|
|
5466
6313
|
createForm,
|
|
@@ -5476,10 +6323,12 @@ function defineConfig(config) {
|
|
|
5476
6323
|
createStreetEvent,
|
|
5477
6324
|
defineConfig,
|
|
5478
6325
|
derived,
|
|
6326
|
+
describeError,
|
|
5479
6327
|
diagnosePerformance,
|
|
5480
6328
|
effect,
|
|
5481
6329
|
email,
|
|
5482
6330
|
environment,
|
|
6331
|
+
escapeHtml,
|
|
5483
6332
|
escapeHtmlAttr,
|
|
5484
6333
|
escapeHtmlText,
|
|
5485
6334
|
flushSync,
|
|
@@ -5511,7 +6360,9 @@ function defineConfig(config) {
|
|
|
5511
6360
|
interpolate,
|
|
5512
6361
|
isBatching,
|
|
5513
6362
|
isComponentDefinition,
|
|
6363
|
+
isHeadContribution,
|
|
5514
6364
|
isTransitionConfig,
|
|
6365
|
+
linkUpdate,
|
|
5515
6366
|
matchPattern,
|
|
5516
6367
|
matchRoutes,
|
|
5517
6368
|
maxLength,
|
|
@@ -5519,6 +6370,7 @@ function defineConfig(config) {
|
|
|
5519
6370
|
mountGraph,
|
|
5520
6371
|
mountNode,
|
|
5521
6372
|
mountRouter,
|
|
6373
|
+
mutation,
|
|
5522
6374
|
nextId,
|
|
5523
6375
|
nodeIdPrefix,
|
|
5524
6376
|
nodeTypeStats,
|
|
@@ -5535,10 +6387,14 @@ function defineConfig(config) {
|
|
|
5535
6387
|
readState,
|
|
5536
6388
|
reconcileChildren,
|
|
5537
6389
|
reconcileChildrenByPlan,
|
|
6390
|
+
renderDevToolsHTML,
|
|
6391
|
+
renderHead,
|
|
5538
6392
|
renderToString,
|
|
5539
6393
|
reportDiagnostic,
|
|
6394
|
+
reportError,
|
|
5540
6395
|
required,
|
|
5541
6396
|
resetIdCounter,
|
|
6397
|
+
resolveHead,
|
|
5542
6398
|
resolveTag,
|
|
5543
6399
|
resolveTransition,
|
|
5544
6400
|
resource,
|
|
@@ -5566,6 +6422,7 @@ function defineConfig(config) {
|
|
|
5566
6422
|
validateGraph,
|
|
5567
6423
|
wireComponentBehavior,
|
|
5568
6424
|
wireEvents,
|
|
6425
|
+
wireHeadBehavior,
|
|
5569
6426
|
wireOverlayBehavior,
|
|
5570
6427
|
wireReactiveList,
|
|
5571
6428
|
wireSignalBindings
|