@teamscala/service-boot 0.23.417 → 0.23.419
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/package.json +33 -1
- package/src/handlers/platform-fault-escalation/config.ts +54 -0
- package/src/handlers/platform-fault-escalation/delivery.ts +173 -0
- package/src/handlers/platform-fault-escalation/event-log-queries.ts +122 -0
- package/src/handlers/platform-fault-escalation/orpc-client.ts +97 -0
- package/src/handlers/platform-fault-escalation.ts +15 -387
- package/src/register-registry-page-route/page-data-shared.ts +124 -0
- package/src/register-registry-page-route/public-shell-page-data.ts +169 -0
- package/src/register-registry-page-route/region-slots-page-data.ts +175 -0
- package/src/register-registry-page-route/resolve-page-data.ts +16 -515
- package/src/register-registry-page-route/single-page-data.ts +171 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system ui-service-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Branch 3 of the resolved-graph → pageData conversion: the single-page
|
|
7
|
+
* fallback (form/quiz/proposal/gpt pages with no region slots and no
|
|
8
|
+
* header/footer sources).
|
|
9
|
+
*
|
|
10
|
+
* Split out of resolve-page-data.ts (547 lines) — byte-identical body; only
|
|
11
|
+
* the closure variables became explicit parameters.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { resolveThemeMode } from "../resolve-theme-mode.ts";
|
|
15
|
+
import { composePageTitle } from "../compose-page-title.ts";
|
|
16
|
+
import { resolveEntityTitle } from "./meta-title.ts";
|
|
17
|
+
import {
|
|
18
|
+
EMPTY_PUCK_DATA,
|
|
19
|
+
extractPuckData,
|
|
20
|
+
readBrandTokens,
|
|
21
|
+
type PuckData,
|
|
22
|
+
} from "./page-data-shared.ts";
|
|
23
|
+
|
|
24
|
+
export function singlePageData(
|
|
25
|
+
resolved: Record<string, unknown>,
|
|
26
|
+
meta: Record<string, unknown>,
|
|
27
|
+
providers: string[] | undefined,
|
|
28
|
+
themeOverride: "light" | "dark" | undefined,
|
|
29
|
+
orgPrefix: boolean | undefined,
|
|
30
|
+
): Record<string, unknown> {
|
|
31
|
+
const { brand, primaryColor, headingFont, bodyFont, logo, brandTheme } =
|
|
32
|
+
readBrandTokens(resolved);
|
|
33
|
+
|
|
34
|
+
const page = (resolved.page as Record<string, unknown>) ?? null;
|
|
35
|
+
const puckData = page
|
|
36
|
+
? extractPuckData(page) ?? EMPTY_PUCK_DATA
|
|
37
|
+
: ({ root: { props: {} }, content: [] } as PuckData);
|
|
38
|
+
|
|
39
|
+
const website = (resolved.website as Record<string, unknown>) ?? {};
|
|
40
|
+
const layoutSettings = (website.layout_settings ??
|
|
41
|
+
resolved.websiteDefaults) as { theme?: unknown } | null | undefined;
|
|
42
|
+
const websiteTheme =
|
|
43
|
+
layoutSettings && typeof layoutSettings.theme === "string"
|
|
44
|
+
? layoutSettings.theme
|
|
45
|
+
: undefined;
|
|
46
|
+
|
|
47
|
+
const pageRoot = (
|
|
48
|
+
puckData as { root?: { props?: Record<string, unknown> } } | null
|
|
49
|
+
)?.root?.props;
|
|
50
|
+
const rootTheme =
|
|
51
|
+
pageRoot && typeof pageRoot.theme === "string" ? pageRoot.theme : undefined;
|
|
52
|
+
const agent = (resolved.agent as Record<string, unknown>) ?? null;
|
|
53
|
+
// A GPT page's mode is the agent's dark_theme flag (the DB switch). It's the
|
|
54
|
+
// top candidate so the SSR :root matches the GPT shell's own scope (both
|
|
55
|
+
// derive from the same ai_agents.dark_theme) — flip the flag, the whole GPT
|
|
56
|
+
// + everything it frames flips. themeOverride (?theme=) still wins for an
|
|
57
|
+
// embedded render.
|
|
58
|
+
const agentTheme =
|
|
59
|
+
typeof agent?.dark_theme === "boolean"
|
|
60
|
+
? agent.dark_theme
|
|
61
|
+
? "dark"
|
|
62
|
+
: "light"
|
|
63
|
+
: undefined;
|
|
64
|
+
// themeOverride (?theme=) wins so a form/quiz framed in a GPT panel matches
|
|
65
|
+
// the host shell's light/dark scheme — this single-page branch serves
|
|
66
|
+
// form/quiz/proposal/gpt pages.
|
|
67
|
+
const { theme } = resolveThemeMode({
|
|
68
|
+
override: themeOverride,
|
|
69
|
+
candidates: [agentTheme, rootTheme, brandTheme, websiteTheme],
|
|
70
|
+
});
|
|
71
|
+
const orgId =
|
|
72
|
+
(page?.organisation_id as string) ??
|
|
73
|
+
(agent?.organisation_id as string) ??
|
|
74
|
+
null;
|
|
75
|
+
|
|
76
|
+
const overrideColor =
|
|
77
|
+
pageRoot &&
|
|
78
|
+
typeof pageRoot.primaryColor === "string" &&
|
|
79
|
+
pageRoot.primaryColor
|
|
80
|
+
? pageRoot.primaryColor
|
|
81
|
+
: null;
|
|
82
|
+
const overrideHeading =
|
|
83
|
+
pageRoot && typeof pageRoot.headingFont === "string" && pageRoot.headingFont
|
|
84
|
+
? pageRoot.headingFont
|
|
85
|
+
: null;
|
|
86
|
+
const overrideBody =
|
|
87
|
+
pageRoot && typeof pageRoot.bodyFont === "string" && pageRoot.bodyFont
|
|
88
|
+
? pageRoot.bodyFont
|
|
89
|
+
: null;
|
|
90
|
+
|
|
91
|
+
const screenMode = pageRoot?.screenMode as string | undefined;
|
|
92
|
+
|
|
93
|
+
const pageName = (page?.name as string) || null;
|
|
94
|
+
const entityTitle = resolveEntityTitle(resolved);
|
|
95
|
+
const entityDescription =
|
|
96
|
+
((resolved.form as Record<string, unknown>)?.description as string) ||
|
|
97
|
+
((resolved.quiz as Record<string, unknown>)?.description as string) ||
|
|
98
|
+
((resolved.proposal as Record<string, unknown>)?.description as string) ||
|
|
99
|
+
((resolved.agent as Record<string, unknown>)?.description as string);
|
|
100
|
+
const pageDescription =
|
|
101
|
+
(page?.meta_description as string) ||
|
|
102
|
+
entityDescription ||
|
|
103
|
+
(pageRoot?.description as string) ||
|
|
104
|
+
null;
|
|
105
|
+
const loaderDataPageMetaTitle = (
|
|
106
|
+
resolved.loaderData as { page?: { meta_title?: unknown } } | undefined
|
|
107
|
+
)?.page?.meta_title;
|
|
108
|
+
const pageMetaTitle =
|
|
109
|
+
(typeof loaderDataPageMetaTitle === "string" &&
|
|
110
|
+
loaderDataPageMetaTitle.trim()
|
|
111
|
+
? loaderDataPageMetaTitle.trim()
|
|
112
|
+
: typeof page?.meta_title === "string" && page.meta_title.trim()
|
|
113
|
+
? page.meta_title.trim()
|
|
114
|
+
: null) || null;
|
|
115
|
+
|
|
116
|
+
// Title is DERIVED via the single composer: meta_title is an explicit
|
|
117
|
+
// per-page OVERRIDE (priority 1), a curated resource template wins for
|
|
118
|
+
// resource surfaces (priority 2), otherwise <website>: <subject>. The
|
|
119
|
+
// /sign-in special-case is gone — pageMetaTitle now wins uniformly, so a
|
|
120
|
+
// sign-in page with a meta_title is honoured on every path, not just /sign-in.
|
|
121
|
+
const mergedMeta: Record<string, unknown> = {
|
|
122
|
+
...(pageDescription ? { description: pageDescription } : {}),
|
|
123
|
+
...meta,
|
|
124
|
+
};
|
|
125
|
+
mergedMeta.title = composePageTitle(
|
|
126
|
+
{
|
|
127
|
+
pageMetaTitle,
|
|
128
|
+
templateTitle: (meta.title as string) || undefined,
|
|
129
|
+
websiteName: (website.name as string) || undefined,
|
|
130
|
+
organisationName: (brand.companyName as string) || undefined,
|
|
131
|
+
pageName: pageName ?? ((pageRoot?.title as string) || undefined),
|
|
132
|
+
entityTitle: entityTitle || undefined,
|
|
133
|
+
},
|
|
134
|
+
{ orgPrefix },
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
puckData,
|
|
139
|
+
website: {
|
|
140
|
+
slug: website.slug as string,
|
|
141
|
+
name: website.name as string,
|
|
142
|
+
organisation_id: website.organisation_id as string | null,
|
|
143
|
+
// Pass header_settings through (DB-driven header blur etc.) — see the
|
|
144
|
+
// other two website assignments above. Dropping it kills the blur.
|
|
145
|
+
header_settings:
|
|
146
|
+
(website.header_settings as Record<string, unknown> | null) ?? null,
|
|
147
|
+
},
|
|
148
|
+
brand: {
|
|
149
|
+
primaryColor: overrideColor ?? primaryColor,
|
|
150
|
+
backgroundColor: (brand.backgroundColor as string | null) ?? null,
|
|
151
|
+
lightSurface: (brand.lightSurface as string | null) ?? null,
|
|
152
|
+
darkSurface: (brand.darkSurface as string | null) ?? null,
|
|
153
|
+
headingFont: overrideHeading ?? headingFont,
|
|
154
|
+
bodyFont: overrideBody ?? bodyFont,
|
|
155
|
+
logo,
|
|
156
|
+
companyName: (brand.companyName as string) ?? null,
|
|
157
|
+
companyFriendlyName: (brand.companyFriendlyName as string) ?? null,
|
|
158
|
+
contactEmail: (brand.contactEmail as string) ?? null,
|
|
159
|
+
favicon: (brand.favicon as string) ?? null,
|
|
160
|
+
},
|
|
161
|
+
theme,
|
|
162
|
+
orgId,
|
|
163
|
+
loaderData: resolved,
|
|
164
|
+
...(screenMode ? { fullScreen: screenMode === "full-screen" } : {}),
|
|
165
|
+
...(pageRoot?.standalone != null
|
|
166
|
+
? { standalone: pageRoot.standalone === false ? false : undefined }
|
|
167
|
+
: {}),
|
|
168
|
+
...(providers ? { providerKeys: providers } : {}),
|
|
169
|
+
meta: mergedMeta,
|
|
170
|
+
};
|
|
171
|
+
}
|