@cosmicdrift/kumiko-headless 0.328.0 → 0.328.1
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/apex/css.d.ts +3 -0
- package/{src/apex/css.ts → dist/apex/css.js} +11 -24
- package/dist/apex/index.d.ts +229 -0
- package/dist/apex/index.js +360 -0
- package/dist/apex/lightbox.d.ts +4 -0
- package/{src/apex/lightbox.ts → dist/apex/lightbox.js} +0 -4
- package/dist/contracts/asset.d.ts +15 -0
- package/dist/contracts/asset.js +17 -0
- package/dist/contracts/index.d.ts +3 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/locale.d.ts +7 -0
- package/dist/contracts/locale.js +11 -0
- package/dist/contracts/primitives.d.ts +93 -0
- package/dist/contracts/primitives.js +21 -0
- package/dist/dispatcher/index.d.ts +2 -0
- package/dist/dispatcher/index.js +1 -0
- package/dist/dispatcher/types.d.ts +86 -0
- package/dist/dispatcher/types.js +34 -0
- package/dist/form/form-controller.d.ts +2 -0
- package/dist/form/form-controller.js +404 -0
- package/dist/form/index.d.ts +3 -0
- package/dist/form/index.js +2 -0
- package/dist/form/types.d.ts +71 -0
- package/dist/form/types.js +1 -0
- package/dist/form/zod-bridge.d.ts +5 -0
- package/{src/form/zod-bridge.ts → dist/form/zod-bridge.js} +52 -58
- package/dist/format/escape.d.ts +5 -0
- package/{src/format/escape.ts → dist/format/escape.js} +43 -47
- package/dist/format/html-template.d.ts +9 -0
- package/dist/format/html-template.js +39 -0
- package/dist/format/index.d.ts +10 -0
- package/dist/format/index.js +192 -0
- package/dist/format/money.d.ts +1 -0
- package/{src/format/money.ts → dist/format/money.js} +6 -5
- package/dist/index.d.ts +14 -0
- package/dist/index.js +9 -0
- package/dist/locale-routing/index.d.ts +39 -0
- package/dist/locale-routing/index.js +143 -0
- package/dist/nav/index.d.ts +2 -0
- package/dist/nav/index.js +1 -0
- package/dist/nav/resolve.d.ts +2 -0
- package/dist/nav/resolve.js +58 -0
- package/dist/nav/types.d.ts +26 -0
- package/dist/nav/types.js +1 -0
- package/dist/store/create-store.d.ts +2 -0
- package/{src/store/create-store.ts → dist/store/create-store.js} +21 -23
- package/dist/store/equality.d.ts +1 -0
- package/{src/store/equality.ts → dist/store/equality.js} +19 -17
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.js +2 -0
- package/dist/store/types.d.ts +7 -0
- package/dist/store/types.js +10 -0
- package/dist/view-model/edit.d.ts +10 -0
- package/dist/view-model/edit.js +392 -0
- package/dist/view-model/embedded-list.d.ts +27 -0
- package/dist/view-model/embedded-list.js +63 -0
- package/dist/view-model/index.d.ts +7 -0
- package/dist/view-model/index.js +3 -0
- package/dist/view-model/list.d.ts +16 -0
- package/dist/view-model/list.js +201 -0
- package/dist/view-model/types.d.ts +300 -0
- package/dist/view-model/types.js +1 -0
- package/package.json +31 -12
- package/src/apex/__tests__/lightbox.test.ts +0 -20
- package/src/apex/__tests__/page-head.test.ts +0 -63
- package/src/apex/__tests__/render.test.ts +0 -401
- package/src/apex/index.ts +0 -667
- package/src/contracts/__tests__/contracts.test.ts +0 -123
- package/src/contracts/__tests__/field-issue-compat.test.ts +0 -19
- package/src/contracts/asset.ts +0 -48
- package/src/contracts/index.ts +0 -23
- package/src/contracts/locale.ts +0 -33
- package/src/contracts/primitives.ts +0 -158
- package/src/dispatcher/__tests__/contract.test.ts +0 -163
- package/src/dispatcher/index.ts +0 -16
- package/src/dispatcher/types.ts +0 -217
- package/src/form/__tests__/conditional-fields.test.ts +0 -177
- package/src/form/__tests__/form-controller.test.ts +0 -231
- package/src/form/__tests__/submit.test.ts +0 -575
- package/src/form/__tests__/validation.test.ts +0 -274
- package/src/form/form-controller.ts +0 -458
- package/src/form/index.ts +0 -15
- package/src/form/types.ts +0 -297
- package/src/format/__tests__/escape.test.ts +0 -79
- package/src/format/__tests__/format.test.ts +0 -285
- package/src/format/__tests__/html-template.test.ts +0 -47
- package/src/format/html-template.ts +0 -49
- package/src/format/index.ts +0 -223
- package/src/index.ts +0 -118
- package/src/locale-routing/__tests__/locale-routing.test.ts +0 -276
- package/src/locale-routing/index.ts +0 -218
- package/src/nav/__tests__/resolve.test.ts +0 -279
- package/src/nav/index.ts +0 -8
- package/src/nav/resolve.ts +0 -60
- package/src/nav/types.ts +0 -70
- package/src/store/__tests__/create-store.test.ts +0 -139
- package/src/store/__tests__/equality.test.ts +0 -66
- package/src/store/index.ts +0 -3
- package/src/store/types.ts +0 -27
- package/src/view-model/__tests__/edit.test.ts +0 -1072
- package/src/view-model/__tests__/embedded-list.test.ts +0 -96
- package/src/view-model/__tests__/field-labels.test.ts +0 -62
- package/src/view-model/__tests__/list.test.ts +0 -439
- package/src/view-model/edit.ts +0 -645
- package/src/view-model/embedded-list.ts +0 -89
- package/src/view-model/index.ts +0 -43
- package/src/view-model/list.ts +0 -280
- package/src/view-model/types.ts +0 -400
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const APEX_NAV_MENU_CSS = "\n .nav-toggle { display: none; }\n .nav-toggle__trigger { display: none; align-items: center; justify-content: center;\n width: 2.25rem; height: 2.25rem; border-radius: 0.5rem; cursor: pointer;\n color: var(--fg-muted); list-style: none; }\n .nav-toggle__trigger::-webkit-details-marker { display: none; }\n .nav-toggle__trigger:hover { color: var(--fg); background: var(--bg-muted); }\n .nav-toggle__trigger svg { width: 1.35rem; height: 1.35rem; }\n .apex-dark .nav-toggle__trigger { color: var(--on-dark-muted); }\n .apex-dark .nav-toggle__trigger:hover { color: var(--on-dark); background: rgba(255,255,255,0.08); }\n\n .nav-menu { position: relative; display: inline-flex; }\n .nav-menu__trigger { display: inline-flex; align-items: center; gap: 0.3rem;\n font: inherit; font-size: 0.9375rem; color: var(--fg-muted);\n cursor: pointer; list-style: none; }\n .nav-menu__trigger::-webkit-details-marker { display: none; }\n .nav-menu__trigger:hover { color: var(--fg); }\n .nav-menu__chev { display: inline-flex; transition: transform 0.15s; opacity: 0.7; }\n .nav-menu__chev svg { width: 0.95em; height: 0.95em; }\n .nav-menu[open] .nav-menu__chev { transform: rotate(180deg); }\n .nav-menu__panel { position: absolute; top: calc(100% + 0.5rem); left: 0; z-index: 20;\n min-width: 21rem; padding: 0.5rem; background: var(--bg-card); color: var(--fg);\n border: 1px solid var(--border); border-radius: 0.75rem; box-shadow: var(--shadow);\n flex-direction: column; gap: 0.125rem; }\n .nav-menu[open] .nav-menu__panel { display: flex; }\n .nav-menu__item { display: flex; gap: 0.75rem; align-items: flex-start;\n padding: 0.6rem 0.7rem; border-radius: 0.5rem; color: var(--fg); }\n .nav-menu__item:hover { background: var(--bg-muted); color: var(--fg); }\n .nav-menu__icon { flex: none; display: inline-flex; align-items: center; justify-content: center;\n width: 2rem; height: 2rem; border-radius: 0.5rem;\n background: color-mix(in srgb, var(--primary) 12%, transparent); color: var(--primary); }\n .nav-menu__icon svg { width: 1.1rem; height: 1.1rem; }\n .nav-menu__text { display: flex; flex-direction: column; gap: 0.1rem; }\n .nav-menu__title { font-weight: 600; font-size: 0.9rem; line-height: 1.2; }\n .nav-menu__desc { font-size: 0.8rem; color: var(--fg-muted); line-height: 1.35; }\n .nav-menu__sep { height: 1px; background: var(--border); margin: 0.375rem 0.3rem; }\n .nav-menu__more { display: inline-flex; padding: 0.4rem 0.7rem; font-size: 0.85rem; font-weight: 600; color: var(--primary); }\n .nav-menu__more:hover { color: var(--primary-hover); }\n .apex-dark .nav-menu__trigger { color: var(--on-dark-muted); }\n .apex-dark .nav-menu__trigger:hover { color: var(--on-dark); }\n /* The panel is a light popover; its links must NOT inherit the dark-chrome\n nav-link color (.apex-dark .nav-links a, specificity 0,2,1) or a consumer's\n own .nav-links a. Pin both link types with matching specificity (0,2,1) so\n titles + the footer link stay readable in the dark theme. */\n .nav-menu__panel a.nav-menu__item { color: var(--fg); }\n .nav-menu__panel a.nav-menu__item:hover { color: var(--fg); }\n .nav-menu__panel a.nav-menu__more { color: var(--primary); }\n .nav-menu__panel a.nav-menu__more:hover { color: var(--primary-hover); }\n";
|
|
2
|
+
export declare const APEX_NAV_TOGGLE_RESPONSIVE_CSS = "\n @media (max-width: 640px) {\n .nav { position: relative; }\n .nav .nav-toggle { display: inline-flex; }\n .nav .nav-toggle__trigger { display: inline-flex; }\n .nav-toggle[open] > .nav-links { display: flex; flex-direction: column; align-items: stretch;\n gap: 0.25rem; position: absolute; top: calc(100% + 0.5rem); left: 0; right: 0; z-index: 30;\n background: var(--bg-card); border: 1px solid var(--border); border-radius: 0.75rem;\n box-shadow: var(--shadow); padding: 0.75rem; }\n .nav-toggle[open] > .nav-links > a { color: var(--fg); padding: 0.4rem 0.25rem; }\n .nav-toggle[open] > .nav-links > a:hover { color: var(--fg); }\n .nav-toggle .nav-menu { display: block; }\n .nav-toggle .nav-menu__panel { position: static; min-width: 0;\n margin-top: 0.375rem; box-shadow: none; }\n }\n";
|
|
3
|
+
export declare const APEX_STRUCTURAL_CSS: string;
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
// --accent-hover unset → eyebrow falls back to --primary
|
|
15
15
|
// --hero-tagline-max unset → hero tagline runs full column (var(--hero-tagline-max, none))
|
|
16
16
|
// icon tint → color-mix derives a faint --primary wash, no hardcoded rgba
|
|
17
|
-
|
|
18
17
|
const BASE_LAYOUT = `
|
|
19
18
|
* { box-sizing: border-box; }
|
|
20
19
|
html, body { margin: 0; padding: 0; }
|
|
@@ -48,7 +47,6 @@ const BASE_LAYOUT = `
|
|
|
48
47
|
.eyebrow { display: inline-block; font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.04em;
|
|
49
48
|
text-transform: uppercase; color: var(--accent-hover, var(--primary)); margin-bottom: 0.75rem; }
|
|
50
49
|
`;
|
|
51
|
-
|
|
52
50
|
const HERO = `
|
|
53
51
|
.hero { padding: 4.5rem 0 4rem; }
|
|
54
52
|
.hero-grid { display: grid; grid-template-columns: 1.05fr 1fr; gap: 3.5rem; align-items: center; }
|
|
@@ -74,7 +72,6 @@ const HERO = `
|
|
|
74
72
|
.apex-lightbox__close:hover { background: var(--bg-muted); }
|
|
75
73
|
.apex-dark .apex-lightbox__close { background: var(--on-dark); color: var(--primary); border-color: var(--on-dark-border); }
|
|
76
74
|
`;
|
|
77
|
-
|
|
78
75
|
const FEATURES = `
|
|
79
76
|
.features { background: var(--bg-muted); }
|
|
80
77
|
.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
|
|
@@ -85,7 +82,6 @@ const FEATURES = `
|
|
|
85
82
|
.feature h3 { font-size: 1.0625rem; margin: 0 0 0.4rem; }
|
|
86
83
|
.feature p { color: var(--fg-muted); margin: 0; font-size: 0.9375rem; }
|
|
87
84
|
`;
|
|
88
|
-
|
|
89
85
|
const PRICING = `
|
|
90
86
|
.price-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.25rem; align-items: stretch; }
|
|
91
87
|
.price-card { position: relative; background: var(--bg-card); border: 1px solid var(--border);
|
|
@@ -105,20 +101,17 @@ const PRICING = `
|
|
|
105
101
|
.price-list li.price-cap { font-weight: 600; color: var(--fg); }
|
|
106
102
|
.price-card .btn { text-align: center; margin-top: 0.5rem; }
|
|
107
103
|
`;
|
|
108
|
-
|
|
109
104
|
const TRUST = `
|
|
110
105
|
.trust-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; }
|
|
111
106
|
.trust-item h3 { font-size: 1.0625rem; margin: 0 0 0.4rem; }
|
|
112
107
|
.trust-item p { color: var(--fg-muted); margin: 0; font-size: 0.9375rem; }
|
|
113
108
|
`;
|
|
114
|
-
|
|
115
109
|
const FINAL_CTA = `
|
|
116
110
|
.final-cta { text-align: center; }
|
|
117
111
|
.final-cta-pony { width: 96px; height: auto; display: block; margin: 0 auto 1rem; }
|
|
118
112
|
.final-cta h2 { font-size: clamp(1.6rem, 3.5vw, 2.25rem); margin: 0 0 0.75rem; }
|
|
119
113
|
.final-cta p { color: var(--fg-muted); margin: 0 0 1.75rem; font-size: 1.0625rem; }
|
|
120
114
|
`;
|
|
121
|
-
|
|
122
115
|
// Light chrome (default). Header floats translucent over the page; footer is a
|
|
123
116
|
// muted band. Dark theme overrides both below.
|
|
124
117
|
const CHROME_LIGHT = `
|
|
@@ -150,7 +143,6 @@ const CHROME_LIGHT = `
|
|
|
150
143
|
.footer-bottom { margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border);
|
|
151
144
|
display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.75rem; }
|
|
152
145
|
`;
|
|
153
|
-
|
|
154
146
|
// Dark sandwich: header/hero/final-cta/footer ride on --primary. Buttons invert
|
|
155
147
|
// (a --primary button on --primary would vanish) → light button, --primary text.
|
|
156
148
|
const CHROME_DARK = `
|
|
@@ -185,7 +177,6 @@ const CHROME_DARK = `
|
|
|
185
177
|
.apex-dark .footer-col a:hover { color: var(--on-dark); }
|
|
186
178
|
.apex-dark .footer-bottom { border-top: 1px solid var(--on-dark-border); }
|
|
187
179
|
`;
|
|
188
|
-
|
|
189
180
|
const RESPONSIVE = `
|
|
190
181
|
@media (max-width: 900px) {
|
|
191
182
|
.hero-grid { grid-template-columns: 1fr; gap: 2.5rem; }
|
|
@@ -198,7 +189,6 @@ const RESPONSIVE = `
|
|
|
198
189
|
.feature-grid, .price-grid, .trust-grid, .footer-grid { grid-template-columns: 1fr; }
|
|
199
190
|
}
|
|
200
191
|
`;
|
|
201
|
-
|
|
202
192
|
// header renders navLinks TWICE (renderApexHeader): a plain always-visible
|
|
203
193
|
// .nav-links row, plus a copy nested in .nav-toggle for < 640px — a closed
|
|
204
194
|
// <details> can't be forced "always open" on desktop via CSS (Chrome keeps
|
|
@@ -256,7 +246,6 @@ export const APEX_NAV_MENU_CSS = `
|
|
|
256
246
|
.nav-menu__panel a.nav-menu__more { color: var(--primary); }
|
|
257
247
|
.nav-menu__panel a.nav-menu__more:hover { color: var(--primary-hover); }
|
|
258
248
|
`;
|
|
259
|
-
|
|
260
249
|
// Standalone mobile-open rules for .nav-toggle, split out of RESPONSIVE so a
|
|
261
250
|
// consumer calling renderApexHeader() outside renderApexPage (money-horse,
|
|
262
251
|
// show-pony, the docs sample) can include just this alongside
|
|
@@ -288,16 +277,14 @@ export const APEX_NAV_TOGGLE_RESPONSIVE_CSS = `
|
|
|
288
277
|
margin-top: 0.375rem; box-shadow: none; }
|
|
289
278
|
}
|
|
290
279
|
`;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
APEX_NAV_TOGGLE_RESPONSIVE_CSS +
|
|
303
|
-
RESPONSIVE;
|
|
280
|
+
export const APEX_STRUCTURAL_CSS = BASE_LAYOUT +
|
|
281
|
+
HERO +
|
|
282
|
+
FEATURES +
|
|
283
|
+
PRICING +
|
|
284
|
+
TRUST +
|
|
285
|
+
FINAL_CTA +
|
|
286
|
+
CHROME_LIGHT +
|
|
287
|
+
CHROME_DARK +
|
|
288
|
+
APEX_NAV_MENU_CSS +
|
|
289
|
+
APEX_NAV_TOGGLE_RESPONSIVE_CSS +
|
|
290
|
+
RESPONSIVE;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
export { APEX_NAV_MENU_CSS, APEX_NAV_TOGGLE_RESPONSIVE_CSS, APEX_STRUCTURAL_CSS } from "./css.js";
|
|
3
|
+
export { APEX_LIGHTBOX_HTML, APEX_LIGHTBOX_SCRIPT, APEX_LIGHTBOX_SCRIPT_CSP_HASH, } from "./lightbox.js";
|
|
4
|
+
export type ApexTheme = "light" | "dark";
|
|
5
|
+
/** Brand is raw CSS the app owns: the :root token block and optional @font-face.
|
|
6
|
+
* Token names referenced by the structural CSS: --bg --bg-card --bg-muted
|
|
7
|
+
* --border --fg --fg-muted --fg-subtle --primary --primary-hover --primary-fg
|
|
8
|
+
* --status-ok --shadow. Optional: --accent --accent-fg --font-mono --font-body,
|
|
9
|
+
* and (dark theme) --on-dark --on-dark-muted --on-dark-border. */
|
|
10
|
+
export type ApexBrand = {
|
|
11
|
+
readonly tokensCss: string;
|
|
12
|
+
readonly fontFaceCss?: string;
|
|
13
|
+
};
|
|
14
|
+
export type ApexCtaVariant = "primary" | "secondary" | "link";
|
|
15
|
+
export type ApexCta = {
|
|
16
|
+
readonly label: string;
|
|
17
|
+
/** App-authored only, no user input — escapeHtml encodes entities but does
|
|
18
|
+
* NOT sanitize the URL scheme (a `javascript:`/`data:` URI survives it
|
|
19
|
+
* unchanged). Trust boundary is the deploy-time apex config, not runtime. */
|
|
20
|
+
readonly href: string;
|
|
21
|
+
/** "link" renders a plain anchor (no .btn). Default "primary". */
|
|
22
|
+
readonly variant?: ApexCtaVariant;
|
|
23
|
+
};
|
|
24
|
+
export type ApexLink = {
|
|
25
|
+
readonly kind?: "link";
|
|
26
|
+
readonly label: string;
|
|
27
|
+
readonly href: string;
|
|
28
|
+
};
|
|
29
|
+
/** One entry inside a dropdown nav menu: icon + title + optional description. */
|
|
30
|
+
export type ApexNavMenuItem = {
|
|
31
|
+
/** Inner SVG markup (paths), wrapped by the standard 24px icon <svg>.
|
|
32
|
+
* ponytail: rendered verbatim, no sanitizer — trusted because it's
|
|
33
|
+
* app-authored at deploy time, same trust boundary as ApexCta.href. If an
|
|
34
|
+
* app ever sources this from tenant/user content, sanitize at that
|
|
35
|
+
* construction boundary before it reaches here. */
|
|
36
|
+
readonly icon?: string;
|
|
37
|
+
readonly title: string;
|
|
38
|
+
readonly desc?: string;
|
|
39
|
+
readonly href: string;
|
|
40
|
+
};
|
|
41
|
+
/** A header nav entry that opens a dropdown (icon/title/desc rows) instead of
|
|
42
|
+
* navigating. CSS-only: reveals on hover + keyboard focus. */
|
|
43
|
+
export type ApexNavMenu = {
|
|
44
|
+
readonly kind: "menu";
|
|
45
|
+
readonly label: string;
|
|
46
|
+
readonly items: readonly ApexNavMenuItem[];
|
|
47
|
+
/** Optional link under a divider at the foot of the panel (e.g. "See all →"). */
|
|
48
|
+
readonly footer?: ApexLink;
|
|
49
|
+
};
|
|
50
|
+
/** A header nav entry: a plain link, or a dropdown menu. */
|
|
51
|
+
export type ApexNavEntry = ApexLink | ApexNavMenu;
|
|
52
|
+
export type ApexImage = {
|
|
53
|
+
readonly src: string;
|
|
54
|
+
readonly alt: string;
|
|
55
|
+
readonly width?: number;
|
|
56
|
+
readonly height?: number;
|
|
57
|
+
};
|
|
58
|
+
export type ApexHeader = {
|
|
59
|
+
readonly brand: {
|
|
60
|
+
readonly href: string;
|
|
61
|
+
readonly label: string;
|
|
62
|
+
readonly logoSrc?: string;
|
|
63
|
+
};
|
|
64
|
+
/** Plain links and/or dropdown menus, in order. */
|
|
65
|
+
readonly navLinks?: readonly ApexNavEntry[];
|
|
66
|
+
readonly actions?: readonly ApexCta[];
|
|
67
|
+
/** aria-label for the mobile hamburger trigger (< 640px). Default "Menu". */
|
|
68
|
+
readonly menuLabel?: string;
|
|
69
|
+
};
|
|
70
|
+
export type ApexFooterColumn = {
|
|
71
|
+
readonly heading: string;
|
|
72
|
+
readonly links: readonly ApexLink[];
|
|
73
|
+
};
|
|
74
|
+
export type ApexFooter = {
|
|
75
|
+
readonly brand: {
|
|
76
|
+
readonly label: string;
|
|
77
|
+
readonly logoSrc?: string;
|
|
78
|
+
};
|
|
79
|
+
readonly tagline?: string;
|
|
80
|
+
readonly columns?: readonly ApexFooterColumn[];
|
|
81
|
+
/** Plain text — use unicode (© ·), not HTML entities; rendered escaped. */
|
|
82
|
+
readonly bottomLeft?: string;
|
|
83
|
+
readonly bottomRight?: string;
|
|
84
|
+
};
|
|
85
|
+
export type ApexHeroSection = {
|
|
86
|
+
readonly kind: "hero";
|
|
87
|
+
readonly logo?: ApexImage;
|
|
88
|
+
readonly title: string;
|
|
89
|
+
readonly tagline: string;
|
|
90
|
+
readonly ctas?: readonly ApexCta[];
|
|
91
|
+
/** App-authored trusted HTML (e.g. <strong>…<br/>); rendered verbatim. */
|
|
92
|
+
readonly metaHtml?: string;
|
|
93
|
+
readonly screenshot?: ApexImage;
|
|
94
|
+
};
|
|
95
|
+
export type ApexFeature = {
|
|
96
|
+
/** Inner SVG markup (paths), wrapped by the standard 24px icon <svg>.
|
|
97
|
+
* ponytail: rendered verbatim, no sanitizer — trusted because it's
|
|
98
|
+
* app-authored at deploy time, same trust boundary as ApexCta.href. If an
|
|
99
|
+
* app ever sources this from tenant/user content, sanitize at that
|
|
100
|
+
* construction boundary before it reaches here. */
|
|
101
|
+
readonly icon?: string;
|
|
102
|
+
readonly title: string;
|
|
103
|
+
readonly desc: string;
|
|
104
|
+
};
|
|
105
|
+
export type ApexFeatureGridSection = {
|
|
106
|
+
readonly kind: "feature-grid";
|
|
107
|
+
readonly id?: string;
|
|
108
|
+
readonly eyebrow?: string;
|
|
109
|
+
readonly heading: string;
|
|
110
|
+
readonly sub?: string;
|
|
111
|
+
/** Sit on the muted band (.features). Default true. */
|
|
112
|
+
readonly muted?: boolean;
|
|
113
|
+
readonly items: readonly ApexFeature[];
|
|
114
|
+
};
|
|
115
|
+
export type ApexPricingTier = {
|
|
116
|
+
readonly name: string;
|
|
117
|
+
readonly tagline?: string;
|
|
118
|
+
/** App-formatted ("4,99 €", "0 €", "auf Anfrage"). */
|
|
119
|
+
readonly amount: string;
|
|
120
|
+
/** Localized suffix after the amount ("/Monat", "/month"). */
|
|
121
|
+
readonly priceSuffix?: string;
|
|
122
|
+
readonly featured?: boolean;
|
|
123
|
+
readonly badge?: string;
|
|
124
|
+
readonly capLine?: string;
|
|
125
|
+
readonly benefits: readonly string[];
|
|
126
|
+
readonly cta: ApexCta;
|
|
127
|
+
};
|
|
128
|
+
export type ApexPricingGridSection = {
|
|
129
|
+
readonly kind: "pricing-grid";
|
|
130
|
+
readonly id?: string;
|
|
131
|
+
readonly eyebrow?: string;
|
|
132
|
+
readonly heading: string;
|
|
133
|
+
readonly sub?: string;
|
|
134
|
+
readonly tiers: readonly ApexPricingTier[];
|
|
135
|
+
};
|
|
136
|
+
export type ApexInfoItem = {
|
|
137
|
+
readonly title: string;
|
|
138
|
+
readonly desc: string;
|
|
139
|
+
};
|
|
140
|
+
export type ApexInfoGridSection = {
|
|
141
|
+
readonly kind: "info-grid";
|
|
142
|
+
readonly id?: string;
|
|
143
|
+
readonly eyebrow?: string;
|
|
144
|
+
readonly heading?: string;
|
|
145
|
+
readonly sub?: string;
|
|
146
|
+
readonly muted?: boolean;
|
|
147
|
+
readonly items: readonly ApexInfoItem[];
|
|
148
|
+
};
|
|
149
|
+
export type ApexFinalCtaSection = {
|
|
150
|
+
readonly kind: "final-cta";
|
|
151
|
+
readonly image?: ApexImage;
|
|
152
|
+
readonly heading: string;
|
|
153
|
+
readonly sub?: string;
|
|
154
|
+
readonly cta: ApexCta;
|
|
155
|
+
};
|
|
156
|
+
/** Escape hatch for an app-specific section: raw, app-authored HTML. */
|
|
157
|
+
export type ApexHtmlSection = {
|
|
158
|
+
readonly kind: "html";
|
|
159
|
+
readonly html: string;
|
|
160
|
+
};
|
|
161
|
+
export type ApexSection = ApexHeroSection | ApexFeatureGridSection | ApexPricingGridSection | ApexInfoGridSection | ApexFinalCtaSection | ApexHtmlSection;
|
|
162
|
+
export type ApexHead = {
|
|
163
|
+
readonly lang: string;
|
|
164
|
+
readonly title: string;
|
|
165
|
+
readonly description: string;
|
|
166
|
+
readonly canonicalUrl?: string;
|
|
167
|
+
readonly faviconHref?: string;
|
|
168
|
+
readonly ogImage?: string;
|
|
169
|
+
/** hreflang alternates for multilingual SEO (e.g. the other language's URL). */
|
|
170
|
+
readonly alternates?: readonly {
|
|
171
|
+
readonly hreflang: string;
|
|
172
|
+
readonly href: string;
|
|
173
|
+
}[];
|
|
174
|
+
/** Robots meta content (e.g. "index, follow", "noindex, nofollow"). Omit = no tag. */
|
|
175
|
+
readonly robots?: string;
|
|
176
|
+
/** og:site_name — brand name for social shares. */
|
|
177
|
+
readonly siteName?: string;
|
|
178
|
+
/** og:locale — e.g. "de_DE", "en_US". */
|
|
179
|
+
readonly locale?: string;
|
|
180
|
+
/** twitter:site — @handle for X/Twitter card. */
|
|
181
|
+
readonly twitterSite?: string;
|
|
182
|
+
/** URLs for <link rel="preconnect"> hints (Core Web Vitals). */
|
|
183
|
+
readonly preconnects?: readonly string[];
|
|
184
|
+
/** Arbitrary JSON-LD structured data (Schema.org). Rendered as-is into <script type="application/ld+json">. */
|
|
185
|
+
readonly schemaJson?: Record<string, unknown>;
|
|
186
|
+
};
|
|
187
|
+
export type ApexPage = {
|
|
188
|
+
readonly theme?: ApexTheme;
|
|
189
|
+
readonly brand: ApexBrand;
|
|
190
|
+
readonly head: ApexHead;
|
|
191
|
+
readonly header: ApexHeader;
|
|
192
|
+
readonly sections: readonly ApexSection[];
|
|
193
|
+
readonly footer: ApexFooter;
|
|
194
|
+
};
|
|
195
|
+
/** Render just the apex header chrome (brand + nav + actions). Exported so a
|
|
196
|
+
* consumer that composes its own page shell (not a full apex page) can reuse
|
|
197
|
+
* the identical header — markup stays single-source. */
|
|
198
|
+
export declare function renderApexHeader(h: ApexHeader): string;
|
|
199
|
+
export declare function renderApexHeadTags(head: ApexHead): string;
|
|
200
|
+
export declare function renderApexPage(page: ApexPage): string;
|
|
201
|
+
/** Per-request head metadata (Open-Graph/title/description) for a
|
|
202
|
+
* server-rendered HTML shell. Only `title` is required; everything else
|
|
203
|
+
* falls back to no tag rather than a placeholder. */
|
|
204
|
+
export type PageHeadMeta = {
|
|
205
|
+
readonly title: string;
|
|
206
|
+
readonly description?: string;
|
|
207
|
+
readonly ogImage?: string;
|
|
208
|
+
readonly canonicalUrl?: string;
|
|
209
|
+
readonly siteName?: string;
|
|
210
|
+
readonly locale?: string;
|
|
211
|
+
};
|
|
212
|
+
export type PageHeadSystemQuery = (type: string, payload: unknown, tenantId: TenantId) => Promise<unknown>;
|
|
213
|
+
export type PageHeadResolver = (input: {
|
|
214
|
+
readonly path: string;
|
|
215
|
+
readonly host: string;
|
|
216
|
+
readonly systemQuery: PageHeadSystemQuery;
|
|
217
|
+
}) => Promise<PageHeadMeta | null>;
|
|
218
|
+
export declare function injectPageHead(html: string, tagsHtml: string): string;
|
|
219
|
+
/** Resolves per-request head metadata and splices it into `html`, capped at
|
|
220
|
+
* the shared timeout. Never throws and never changes `html` on a resolver
|
|
221
|
+
* error, a `null` resolve, or a timeout — the caller's shell always ships
|
|
222
|
+
* unchanged. runProdApp and runDevApp both call this single function, so a
|
|
223
|
+
* future change to timeout or fallback semantics has one place to land
|
|
224
|
+
* (kumiko-framework#3026). */
|
|
225
|
+
export declare function resolveAndInjectPageHead(html: string, resolvePageHead: PageHeadResolver, input: {
|
|
226
|
+
readonly path: string;
|
|
227
|
+
readonly host: string;
|
|
228
|
+
readonly systemQuery: PageHeadSystemQuery;
|
|
229
|
+
}): Promise<string>;
|