brand-shell 0.11.0 → 0.13.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/brand-shell.schema.json +22 -3
- package/dist/index.d.mts +17 -61
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +61 -23
- package/dist/index.mjs.map +1 -1
- package/dist/svelte.d.mts +1 -1
- package/dist/svelte.mjs +1 -1
- package/dist/validation-Cic4hPME.d.mts +164 -0
- package/dist/validation-Cic4hPME.d.mts.map +1 -0
- package/dist/{validation-xdqzwr3p.mjs → validation-CtH2UkVv.mjs} +90 -39
- package/dist/validation-CtH2UkVv.mjs.map +1 -0
- package/dist/vue.d.mts +22 -2
- package/dist/vue.d.mts.map +1 -1
- package/dist/vue.mjs +10 -4
- package/dist/vue.mjs.map +1 -1
- package/dist/web.d.mts +17 -5
- package/dist/web.d.mts.map +1 -1
- package/dist/web.mjs +77 -33
- package/dist/web.mjs.map +1 -1
- package/package.json +6 -4
- package/src/svelte/BrandFooter.svelte +24 -0
- package/src/svelte/BrandHeader.svelte +24 -0
- package/src/svelte/index.svelte.ts +4 -0
- package/dist/types-PQziYg7Z.d.mts +0 -81
- package/dist/types-PQziYg7Z.d.mts.map +0 -1
- package/dist/validation-xdqzwr3p.mjs.map +0 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
//#region src/core/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A custom social link for platforms not built into brand-shell.
|
|
4
|
+
* Built-in platforms retain priority; custom links are appended in insertion order.
|
|
5
|
+
*/
|
|
6
|
+
interface CustomSocialLink {
|
|
7
|
+
/** Arbitrary identifier used as a key (e.g. "bluesky", "mastodon") */
|
|
8
|
+
platform: string;
|
|
9
|
+
/** Link URL (must be a safe href) */
|
|
10
|
+
href: string;
|
|
11
|
+
/** Accessible label (used as aria-label) */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Optional inline SVG string (24×24 viewBox). aria-hidden="true" is added automatically. */
|
|
14
|
+
iconSvg?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Schema for header/footer content. Caller passes these; package does not store them.
|
|
18
|
+
*/
|
|
19
|
+
interface BrandNavLink {
|
|
20
|
+
/** Visible label (e.g. Blog, Docs, About) */
|
|
21
|
+
label: string;
|
|
22
|
+
/** Destination URL or path */
|
|
23
|
+
href: string;
|
|
24
|
+
/** Optional custom aria-label for accessibility */
|
|
25
|
+
ariaLabel?: string;
|
|
26
|
+
/** Optional target attribute (defaults to _self) */
|
|
27
|
+
target?: "_blank" | "_self" | "_parent" | "_top";
|
|
28
|
+
/** Optional rel attribute (e.g. noopener) */
|
|
29
|
+
rel?: string;
|
|
30
|
+
}
|
|
31
|
+
interface BrandAction {
|
|
32
|
+
/** Visible label on the CTA button */
|
|
33
|
+
label: string;
|
|
34
|
+
/** URL the CTA points to */
|
|
35
|
+
href: string;
|
|
36
|
+
/** Optional aria-label override */
|
|
37
|
+
ariaLabel?: string;
|
|
38
|
+
/** Optional target attribute */
|
|
39
|
+
target?: "_blank" | "_self" | "_parent" | "_top";
|
|
40
|
+
/** Optional rel attribute */
|
|
41
|
+
rel?: string;
|
|
42
|
+
/** Style variant hint */
|
|
43
|
+
variant?: "primary" | "secondary" | "ghost";
|
|
44
|
+
}
|
|
45
|
+
interface BrandDetails {
|
|
46
|
+
/** Display name (e.g. in header and footer) */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Optional home URL (header name links here when set) */
|
|
49
|
+
homeHref?: string;
|
|
50
|
+
/** Primary nav links shown in the header/footer text nav */
|
|
51
|
+
navLinks?: BrandNavLink[];
|
|
52
|
+
/** Optional highlighted CTA button */
|
|
53
|
+
primaryAction?: BrandAction;
|
|
54
|
+
/** Optional secondary CTA button */
|
|
55
|
+
secondaryAction?: BrandAction;
|
|
56
|
+
/** LinkedIn profile URL */
|
|
57
|
+
linkedin?: string;
|
|
58
|
+
/** Email address (e.g. mailto: or plain) */
|
|
59
|
+
email?: string;
|
|
60
|
+
/** GitHub profile URL */
|
|
61
|
+
github?: string;
|
|
62
|
+
/** Twitter/X profile URL */
|
|
63
|
+
twitter?: string;
|
|
64
|
+
/** Discord community or profile URL */
|
|
65
|
+
discord?: string;
|
|
66
|
+
/** Personal or site website URL */
|
|
67
|
+
website?: string;
|
|
68
|
+
/** Optional tagline (e.g. in footer) */
|
|
69
|
+
tagline?: string;
|
|
70
|
+
/** Additional social links for custom platforms (Bluesky, Mastodon, YouTube, etc.) */
|
|
71
|
+
customSocialLinks?: CustomSocialLink[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Optional theme to adapt branding without custom CSS.
|
|
75
|
+
* Applied as CSS custom properties on the component root.
|
|
76
|
+
*/
|
|
77
|
+
interface BrandTheme {
|
|
78
|
+
/** Accent/link hover and active state color */
|
|
79
|
+
primaryColor?: string;
|
|
80
|
+
/** Header/footer background color */
|
|
81
|
+
backgroundColor?: string;
|
|
82
|
+
/** Main text color (name, nav labels) */
|
|
83
|
+
textColor?: string;
|
|
84
|
+
/** Font stack for header/footer */
|
|
85
|
+
fontFamily?: string;
|
|
86
|
+
/** Link default color (defaults from primaryColor if omitted) */
|
|
87
|
+
linkColor?: string;
|
|
88
|
+
/** Size for social icon buttons (e.g. 2rem, 32px) */
|
|
89
|
+
socialIconSize?: string;
|
|
90
|
+
/** Optional override for primary CTA text color */
|
|
91
|
+
buttonTextColor?: string;
|
|
92
|
+
/** Mobile CTA arrangement: side-by-side (`inline`) or one-per-row (`stacked`) */
|
|
93
|
+
ctaLayout?: "inline" | "stacked";
|
|
94
|
+
/** Border radius for buttons and containers (e.g. 0.5rem, 4px) → --brand-radius */
|
|
95
|
+
borderRadius?: string;
|
|
96
|
+
/** Header height override (e.g. 4rem, 64px) → --brand-header-height */
|
|
97
|
+
headerHeight?: string;
|
|
98
|
+
/** Footer padding override (e.g. 3rem 1.5rem) → --brand-footer-padding */
|
|
99
|
+
footerPadding?: string;
|
|
100
|
+
/** Secondary button background color → --brand-button-secondary */
|
|
101
|
+
secondaryButtonBg?: string;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/core/social.d.ts
|
|
105
|
+
type SocialPlatform = "website" | "linkedin" | "email" | "github" | "twitter" | "discord";
|
|
106
|
+
interface SocialLink {
|
|
107
|
+
platform: SocialPlatform | string;
|
|
108
|
+
href: string;
|
|
109
|
+
label: string;
|
|
110
|
+
iconSvg?: string;
|
|
111
|
+
}
|
|
112
|
+
declare function detailsToSocialLinks(details: BrandDetails): SocialLink[];
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/core/shell.d.ts
|
|
115
|
+
type LinkTarget = NonNullable<BrandNavLink["target"]>;
|
|
116
|
+
interface ShellNavLink extends BrandNavLink {
|
|
117
|
+
ariaLabel: string;
|
|
118
|
+
rel?: string;
|
|
119
|
+
target: LinkTarget;
|
|
120
|
+
}
|
|
121
|
+
interface ShellActionLink extends BrandAction {
|
|
122
|
+
ariaLabel: string;
|
|
123
|
+
rel?: string;
|
|
124
|
+
target: LinkTarget;
|
|
125
|
+
variant: NonNullable<BrandAction["variant"]>;
|
|
126
|
+
}
|
|
127
|
+
interface ShellViewModel {
|
|
128
|
+
navLinks: ShellNavLink[];
|
|
129
|
+
ctaLinks: ShellActionLink[];
|
|
130
|
+
socialLinks: SocialLink[];
|
|
131
|
+
}
|
|
132
|
+
type NormalizedActionLink = Omit<ShellActionLink, "variant"> & Pick<BrandAction, "variant">;
|
|
133
|
+
interface NormalizedBrandDetails extends Omit<BrandDetails, "navLinks" | "primaryAction" | "secondaryAction"> {
|
|
134
|
+
navLinks: ShellNavLink[];
|
|
135
|
+
primaryAction?: NormalizedActionLink;
|
|
136
|
+
secondaryAction?: NormalizedActionLink;
|
|
137
|
+
}
|
|
138
|
+
declare function normalizeNavLinks(navLinks?: BrandNavLink[]): ShellNavLink[];
|
|
139
|
+
declare function normalizeBrandDetails(details: BrandDetails): NormalizedBrandDetails;
|
|
140
|
+
declare function normalizeCtaLinks(primaryAction?: BrandAction, secondaryAction?: BrandAction): ShellActionLink[];
|
|
141
|
+
declare function buildShellViewModelFromNormalized(normalized: NormalizedBrandDetails): ShellViewModel;
|
|
142
|
+
declare function buildShellViewModel(details: BrandDetails): ShellViewModel;
|
|
143
|
+
declare function normalizeEmailHref(email?: string): string | undefined;
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/core/validation.d.ts
|
|
146
|
+
interface BrandValidationResult<T> {
|
|
147
|
+
valid: boolean;
|
|
148
|
+
errors: string[];
|
|
149
|
+
normalized: T | null;
|
|
150
|
+
}
|
|
151
|
+
declare class BrandShellValidationError extends Error {
|
|
152
|
+
readonly context: string;
|
|
153
|
+
readonly errors: string[];
|
|
154
|
+
constructor(context: string, errors: string[]);
|
|
155
|
+
}
|
|
156
|
+
declare function validateBrandDetails(details: unknown): BrandValidationResult<NormalizedBrandDetails>;
|
|
157
|
+
declare function validateBrandTheme(theme: unknown): BrandValidationResult<BrandTheme | null>;
|
|
158
|
+
declare function assertValidBrandDetails(details: unknown, context?: string): asserts details is BrandDetails;
|
|
159
|
+
declare function assertValidBrandTheme(theme: unknown, context?: string): asserts theme is BrandTheme | null | undefined;
|
|
160
|
+
declare function normalizeBrandTheme(theme?: BrandTheme | null): BrandTheme | null;
|
|
161
|
+
declare function formatValidationErrors(context: string, errors: string[]): string;
|
|
162
|
+
//#endregion
|
|
163
|
+
export { BrandAction as C, CustomSocialLink as D, BrandTheme as E, detailsToSocialLinks as S, BrandNavLink as T, normalizeCtaLinks as _, formatValidationErrors as a, SocialLink as b, validateBrandTheme as c, ShellActionLink as d, ShellNavLink as f, normalizeBrandDetails as g, buildShellViewModelFromNormalized as h, assertValidBrandTheme as i, LinkTarget as l, buildShellViewModel as m, BrandValidationResult as n, normalizeBrandTheme as o, ShellViewModel as p, assertValidBrandDetails as r, validateBrandDetails as s, BrandShellValidationError as t, NormalizedBrandDetails as u, normalizeEmailHref as v, BrandDetails as w, SocialPlatform as x, normalizeNavLinks as y };
|
|
164
|
+
//# sourceMappingURL=validation-Cic4hPME.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-Cic4hPME.d.mts","names":[],"sources":["../src/core/types.ts","../src/core/social.ts","../src/core/shell.ts","../src/core/validation.ts"],"mappings":"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"}
|
|
@@ -11,14 +11,11 @@ function detailsToSocialLinks(details) {
|
|
|
11
11
|
href: details.linkedin,
|
|
12
12
|
label: "LinkedIn"
|
|
13
13
|
});
|
|
14
|
-
if (details.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
label: "Email"
|
|
20
|
-
});
|
|
21
|
-
}
|
|
14
|
+
if (details.email) links.push({
|
|
15
|
+
platform: "email",
|
|
16
|
+
href: details.email,
|
|
17
|
+
label: "Email"
|
|
18
|
+
});
|
|
22
19
|
if (details.github) links.push({
|
|
23
20
|
platform: "github",
|
|
24
21
|
href: details.github,
|
|
@@ -34,6 +31,12 @@ function detailsToSocialLinks(details) {
|
|
|
34
31
|
href: details.discord,
|
|
35
32
|
label: "Discord"
|
|
36
33
|
});
|
|
34
|
+
if (details.customSocialLinks) for (const custom of details.customSocialLinks) links.push({
|
|
35
|
+
platform: custom.platform,
|
|
36
|
+
href: custom.href,
|
|
37
|
+
label: custom.label,
|
|
38
|
+
iconSvg: custom.iconSvg
|
|
39
|
+
});
|
|
37
40
|
return links;
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -91,7 +94,7 @@ function normalizeBrandDetails(details) {
|
|
|
91
94
|
return {
|
|
92
95
|
...details,
|
|
93
96
|
homeHref: normalizeSafeHref(details.homeHref),
|
|
94
|
-
|
|
97
|
+
email: normalizeEmailHref(details.email),
|
|
95
98
|
website: normalizeSafeHref(details.website),
|
|
96
99
|
linkedin: normalizeSafeHref(details.linkedin),
|
|
97
100
|
github: normalizeSafeHref(details.github),
|
|
@@ -99,7 +102,8 @@ function normalizeBrandDetails(details) {
|
|
|
99
102
|
discord: normalizeSafeHref(details.discord),
|
|
100
103
|
navLinks: normalizeNavLinks(details.navLinks),
|
|
101
104
|
primaryAction: normalizedPrimaryAction,
|
|
102
|
-
secondaryAction: normalizedSecondaryAction
|
|
105
|
+
secondaryAction: normalizedSecondaryAction,
|
|
106
|
+
customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks)
|
|
103
107
|
};
|
|
104
108
|
}
|
|
105
109
|
function normalizeCtaLinks(primaryAction, secondaryAction) {
|
|
@@ -113,17 +117,19 @@ function normalizeCtaLinks(primaryAction, secondaryAction) {
|
|
|
113
117
|
};
|
|
114
118
|
});
|
|
115
119
|
}
|
|
116
|
-
function
|
|
117
|
-
const normalizedDetails = normalizeBrandDetails(details);
|
|
120
|
+
function buildShellViewModelFromNormalized(normalized) {
|
|
118
121
|
return {
|
|
119
|
-
navLinks:
|
|
120
|
-
ctaLinks: normalizeCtaLinks(
|
|
121
|
-
socialLinks: detailsToSocialLinks(
|
|
122
|
+
navLinks: normalized.navLinks,
|
|
123
|
+
ctaLinks: normalizeCtaLinks(normalized.primaryAction, normalized.secondaryAction),
|
|
124
|
+
socialLinks: detailsToSocialLinks(normalized)
|
|
122
125
|
};
|
|
123
126
|
}
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
function buildShellViewModel(details) {
|
|
128
|
+
return buildShellViewModelFromNormalized(normalizeBrandDetails(details));
|
|
129
|
+
}
|
|
130
|
+
function normalizeEmailHref(email) {
|
|
131
|
+
if (typeof email !== "string") return void 0;
|
|
132
|
+
const trimmed = email.trim();
|
|
127
133
|
if (trimmed.length === 0) return void 0;
|
|
128
134
|
if (trimmed.toLowerCase().startsWith("mailto:")) {
|
|
129
135
|
const address = trimmed.slice(7).trim();
|
|
@@ -132,6 +138,19 @@ function normalizeGmailHref(gmail) {
|
|
|
132
138
|
}
|
|
133
139
|
return normalizeSafeHref(`mailto:${trimmed}`);
|
|
134
140
|
}
|
|
141
|
+
function normalizeCustomSocialLinks(links) {
|
|
142
|
+
if (!links || links.length === 0) return void 0;
|
|
143
|
+
const normalized = [];
|
|
144
|
+
for (const link of links) {
|
|
145
|
+
const href = normalizeSafeHref(link.href);
|
|
146
|
+
if (!href) continue;
|
|
147
|
+
normalized.push({
|
|
148
|
+
...link,
|
|
149
|
+
href
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
153
|
+
}
|
|
135
154
|
function normalizeAction(action) {
|
|
136
155
|
if (!action) return void 0;
|
|
137
156
|
const href = normalizeSafeHref(action.href);
|
|
@@ -182,10 +201,14 @@ function themeToCssVariables(theme) {
|
|
|
182
201
|
const buttonTextColor = getAccessibleTextColor(theme.primaryColor);
|
|
183
202
|
if (buttonTextColor) style[`--${THEME_VAR_PREFIX}-button-text`] = buttonTextColor;
|
|
184
203
|
}
|
|
204
|
+
if (theme.borderRadius != null) style[`--${THEME_VAR_PREFIX}-radius`] = theme.borderRadius;
|
|
205
|
+
if (theme.headerHeight != null) style[`--${THEME_VAR_PREFIX}-header-height`] = theme.headerHeight;
|
|
206
|
+
if (theme.footerPadding != null) style[`--${THEME_VAR_PREFIX}-footer-padding`] = theme.footerPadding;
|
|
207
|
+
if (theme.secondaryButtonBg != null) style[`--${THEME_VAR_PREFIX}-button-secondary`] = theme.secondaryButtonBg;
|
|
185
208
|
return style;
|
|
186
209
|
}
|
|
187
210
|
function getAccessibleTextColor(backgroundColor) {
|
|
188
|
-
const rgb =
|
|
211
|
+
const rgb = parseColorToRgb(backgroundColor);
|
|
189
212
|
if (!rgb) return void 0;
|
|
190
213
|
return contrastRatio(rgb, {
|
|
191
214
|
r: 15,
|
|
@@ -197,20 +220,31 @@ function getAccessibleTextColor(backgroundColor) {
|
|
|
197
220
|
b: 252
|
|
198
221
|
}) ? DARK_TEXT : LIGHT_TEXT;
|
|
199
222
|
}
|
|
200
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Parses a hex (#rgb / #rrggbb) or rgb()/rgba() color string to RGB components.
|
|
225
|
+
* hsl(), oklch(), and other formats are not supported and return undefined.
|
|
226
|
+
*/
|
|
227
|
+
function parseColorToRgb(color) {
|
|
201
228
|
const trimmed = color.trim();
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
229
|
+
const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed);
|
|
230
|
+
if (hexMatch) {
|
|
231
|
+
const value = hexMatch[1];
|
|
232
|
+
if (value.length === 3) return {
|
|
233
|
+
r: Number.parseInt(value[0] + value[0], 16),
|
|
234
|
+
g: Number.parseInt(value[1] + value[1], 16),
|
|
235
|
+
b: Number.parseInt(value[2] + value[2], 16)
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
r: Number.parseInt(value.slice(0, 2), 16),
|
|
239
|
+
g: Number.parseInt(value.slice(2, 4), 16),
|
|
240
|
+
b: Number.parseInt(value.slice(4, 6), 16)
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const rgbMatch = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(trimmed);
|
|
244
|
+
if (rgbMatch) return {
|
|
245
|
+
r: +rgbMatch[1],
|
|
246
|
+
g: +rgbMatch[2],
|
|
247
|
+
b: +rgbMatch[3]
|
|
214
248
|
};
|
|
215
249
|
}
|
|
216
250
|
function contrastRatio(first, second) {
|
|
@@ -254,7 +288,11 @@ const THEME_KEYS = new Set([
|
|
|
254
288
|
"linkColor",
|
|
255
289
|
"socialIconSize",
|
|
256
290
|
"buttonTextColor",
|
|
257
|
-
"ctaLayout"
|
|
291
|
+
"ctaLayout",
|
|
292
|
+
"borderRadius",
|
|
293
|
+
"headerHeight",
|
|
294
|
+
"footerPadding",
|
|
295
|
+
"secondaryButtonBg"
|
|
258
296
|
]);
|
|
259
297
|
var BrandShellValidationError = class extends Error {
|
|
260
298
|
constructor(context, errors) {
|
|
@@ -281,8 +319,8 @@ function validateBrandDetails(details) {
|
|
|
281
319
|
validateSafeHref(details.website, "details.website", errors);
|
|
282
320
|
validateOptionalString(details.linkedin, "details.linkedin", errors);
|
|
283
321
|
validateSafeHref(details.linkedin, "details.linkedin", errors);
|
|
284
|
-
validateOptionalString(details.
|
|
285
|
-
|
|
322
|
+
validateOptionalString(details.email, "details.email", errors);
|
|
323
|
+
validateEmail(details.email, "details.email", errors);
|
|
286
324
|
validateOptionalString(details.github, "details.github", errors);
|
|
287
325
|
validateSafeHref(details.github, "details.github", errors);
|
|
288
326
|
validateOptionalString(details.twitter, "details.twitter", errors);
|
|
@@ -294,6 +332,8 @@ function validateBrandDetails(details) {
|
|
|
294
332
|
else details.navLinks.forEach((link, index) => validateNavLink(link, `details.navLinks[${index}]`, errors));
|
|
295
333
|
if (details.primaryAction != null) validateAction(details.primaryAction, "details.primaryAction", errors);
|
|
296
334
|
if (details.secondaryAction != null) validateAction(details.secondaryAction, "details.secondaryAction", errors);
|
|
335
|
+
if (details.customSocialLinks != null) if (!Array.isArray(details.customSocialLinks)) errors.push("details.customSocialLinks must be an array.");
|
|
336
|
+
else details.customSocialLinks.forEach((link, index) => validateCustomSocialLink(link, `details.customSocialLinks[${index}]`, errors));
|
|
297
337
|
if (errors.length > 0) return {
|
|
298
338
|
valid: false,
|
|
299
339
|
errors,
|
|
@@ -396,6 +436,17 @@ function validateAction(action, path, errors) {
|
|
|
396
436
|
if (typeof action.variant !== "string" || !CTA_VARIANTS.has(action.variant)) errors.push(`${path}.variant must be one of: primary, secondary, ghost.`);
|
|
397
437
|
}
|
|
398
438
|
}
|
|
439
|
+
function validateCustomSocialLink(link, path, errors) {
|
|
440
|
+
if (!isRecord(link)) {
|
|
441
|
+
errors.push(`${path} must be an object.`);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (typeof link.platform !== "string" || link.platform.trim().length === 0) errors.push(`${path}.platform must be a non-empty string.`);
|
|
445
|
+
validateRequiredString(link.label, `${path}.label`, errors);
|
|
446
|
+
validateRequiredString(link.href, `${path}.href`, errors);
|
|
447
|
+
validateSafeHref(link.href, `${path}.href`, errors);
|
|
448
|
+
if (link.iconSvg != null) validateOptionalString(link.iconSvg, `${path}.iconSvg`, errors);
|
|
449
|
+
}
|
|
399
450
|
function validateTarget(target, path, errors) {
|
|
400
451
|
if (target == null) return;
|
|
401
452
|
if (typeof target !== "string" || !LINK_TARGETS.has(target)) errors.push(`${path} must be one of: _blank, _self, _parent, _top.`);
|
|
@@ -416,15 +467,15 @@ function validateSafeHref(value, path, errors) {
|
|
|
416
467
|
if (typeof value !== "string" || value.trim().length === 0) return;
|
|
417
468
|
if (!normalizeSafeHref(value)) errors.push(`${path} must use a safe URL/path (http, https, mailto, tel, or relative path).`);
|
|
418
469
|
}
|
|
419
|
-
function
|
|
470
|
+
function validateEmail(value, path, errors) {
|
|
420
471
|
if (value == null) return;
|
|
421
472
|
if (typeof value !== "string" || value.trim().length === 0) return;
|
|
422
|
-
if (!
|
|
473
|
+
if (!normalizeEmailHref(value)) errors.push(`${path} must be a valid email or mailto URL.`);
|
|
423
474
|
}
|
|
424
475
|
function isRecord(value) {
|
|
425
476
|
return typeof value === "object" && value !== null;
|
|
426
477
|
}
|
|
427
478
|
|
|
428
479
|
//#endregion
|
|
429
|
-
export { normalizeBrandTheme as a, themeToCssVariables as c,
|
|
430
|
-
//# sourceMappingURL=validation-
|
|
480
|
+
export { normalizeBrandTheme as a, themeToCssVariables as c, buildShellViewModelFromNormalized as d, normalizeBrandDetails as f, detailsToSocialLinks as g, normalizeNavLinks as h, formatValidationErrors as i, shouldValidateInDev as l, normalizeEmailHref as m, assertValidBrandDetails as n, validateBrandDetails as o, normalizeCtaLinks as p, assertValidBrandTheme as r, validateBrandTheme as s, BrandShellValidationError as t, buildShellViewModel as u };
|
|
481
|
+
//# sourceMappingURL=validation-CtH2UkVv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-CtH2UkVv.mjs","names":[],"sources":["../src/core/social.ts","../src/core/links.ts","../src/core/shell.ts","../src/core/dev.ts","../src/core/theme.ts","../src/core/validation.ts"],"sourcesContent":["import type { BrandDetails } from \"./types\";\n\nexport type SocialPlatform = \"website\" | \"linkedin\" | \"email\" | \"github\" | \"twitter\" | \"discord\";\n\nexport interface SocialLink {\n platform: SocialPlatform | string;\n href: string;\n label: string;\n iconSvg?: string;\n}\n\nexport function detailsToSocialLinks(details: BrandDetails): SocialLink[] {\n const links: SocialLink[] = [];\n if (details.website) links.push({ platform: \"website\", href: details.website, label: \"Website\" });\n if (details.linkedin) links.push({ platform: \"linkedin\", href: details.linkedin, label: \"LinkedIn\" });\n if (details.email) links.push({ platform: \"email\", href: details.email, label: \"Email\" });\n if (details.github) links.push({ platform: \"github\", href: details.github, label: \"GitHub\" });\n if (details.twitter) links.push({ platform: \"twitter\", href: details.twitter, label: \"Twitter\" });\n if (details.discord) links.push({ platform: \"discord\", href: details.discord, label: \"Discord\" });\n if (details.customSocialLinks) {\n for (const custom of details.customSocialLinks) {\n links.push({ platform: custom.platform, href: custom.href, label: custom.label, iconSvg: custom.iconSvg });\n }\n }\n return links;\n}\n","const ABSOLUTE_SCHEME_PATTERN = /^([a-zA-Z][a-zA-Z\\d+.-]*):/;\nconst UNSAFE_CHAR_PATTERN = /[\\u0000-\\u001f\\u007f]/;\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\"]);\nconst REQUIRED_BLANK_REL_TOKENS = [\"noopener\", \"noreferrer\"];\n\nexport function normalizeSafeHref(href: unknown): string | undefined {\n if (typeof href !== \"string\") return undefined;\n const trimmed = href.trim();\n if (trimmed.length === 0) return undefined;\n if (UNSAFE_CHAR_PATTERN.test(trimmed)) return undefined;\n if (trimmed.startsWith(\"//\")) return undefined;\n\n const schemeMatch = trimmed.match(ABSOLUTE_SCHEME_PATTERN);\n if (!schemeMatch) {\n return trimmed;\n }\n\n const protocol = `${schemeMatch[1]?.toLowerCase()}:`;\n if (!ALLOWED_PROTOCOLS.has(protocol)) {\n return undefined;\n }\n\n return trimmed;\n}\n\nexport function isSafeHref(href: unknown): boolean {\n return normalizeSafeHref(href) != null;\n}\n\nexport function normalizeRel(target: \"_blank\" | \"_self\" | \"_parent\" | \"_top\", rel?: string): string | undefined {\n const normalizedRel = typeof rel === \"string\" ? rel.trim() : \"\";\n if (target !== \"_blank\") {\n return normalizedRel.length > 0 ? normalizedRel : undefined;\n }\n\n const tokens = normalizedRel.length > 0 ? normalizedRel.split(/\\s+/).filter(Boolean) : [];\n const tokenSet = new Set(tokens.map((token) => token.toLowerCase()));\n\n for (const requiredToken of REQUIRED_BLANK_REL_TOKENS) {\n if (!tokenSet.has(requiredToken)) {\n tokens.push(requiredToken);\n }\n }\n\n return tokens.join(\" \");\n}\n","import { detailsToSocialLinks, type SocialLink } from \"./social\";\nimport type { BrandAction, BrandDetails, BrandNavLink, CustomSocialLink } from \"./types\";\nimport { normalizeRel, normalizeSafeHref } from \"./links\";\n\nexport type LinkTarget = NonNullable<BrandNavLink[\"target\"]>;\n\nexport interface ShellNavLink extends BrandNavLink {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n}\n\nexport interface ShellActionLink extends BrandAction {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n variant: NonNullable<BrandAction[\"variant\"]>;\n}\n\nexport interface ShellViewModel {\n navLinks: ShellNavLink[];\n ctaLinks: ShellActionLink[];\n socialLinks: SocialLink[];\n}\n\nexport type NormalizedActionLink = Omit<ShellActionLink, \"variant\"> & Pick<BrandAction, \"variant\">;\n\nexport interface NormalizedBrandDetails extends Omit<BrandDetails, \"navLinks\" | \"primaryAction\" | \"secondaryAction\"> {\n navLinks: ShellNavLink[];\n primaryAction?: NormalizedActionLink;\n secondaryAction?: NormalizedActionLink;\n}\n\nexport function normalizeNavLinks(navLinks: BrandNavLink[] = []): ShellNavLink[] {\n return navLinks.flatMap((link) => {\n const href = normalizeSafeHref(link.href);\n if (!href) return [];\n\n const { target, rel } = normalizeLinkTargetRel(link.target, link.rel);\n return {\n ...link,\n href,\n ariaLabel: link.ariaLabel ?? link.label,\n target,\n rel,\n };\n });\n}\n\nexport function normalizeBrandDetails(details: BrandDetails): NormalizedBrandDetails {\n const normalizedPrimaryAction = normalizeAction(details.primaryAction);\n const normalizedSecondaryAction = normalizeAction(details.secondaryAction);\n\n return {\n ...details,\n homeHref: normalizeSafeHref(details.homeHref),\n email: normalizeEmailHref(details.email),\n website: normalizeSafeHref(details.website),\n linkedin: normalizeSafeHref(details.linkedin),\n github: normalizeSafeHref(details.github),\n twitter: normalizeSafeHref(details.twitter),\n discord: normalizeSafeHref(details.discord),\n navLinks: normalizeNavLinks(details.navLinks),\n primaryAction: normalizedPrimaryAction,\n secondaryAction: normalizedSecondaryAction,\n customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks),\n };\n}\n\nexport function normalizeCtaLinks(\n primaryAction?: BrandAction,\n secondaryAction?: BrandAction,\n): ShellActionLink[] {\n const normalizedPrimaryAction = normalizeAction(primaryAction);\n const normalizedSecondaryAction = normalizeAction(secondaryAction);\n const actions = [normalizedSecondaryAction, normalizedPrimaryAction].filter(\n (action): action is NonNullable<typeof action> => Boolean(action),\n );\n\n return actions.map((action, index) => {\n const variant =\n action.variant ?? (action === normalizedPrimaryAction || index === actions.length - 1 ? \"primary\" : \"secondary\");\n\n return {\n ...action,\n variant,\n };\n });\n}\n\nexport function buildShellViewModelFromNormalized(normalized: NormalizedBrandDetails): ShellViewModel {\n return {\n navLinks: normalized.navLinks,\n ctaLinks: normalizeCtaLinks(normalized.primaryAction, normalized.secondaryAction),\n socialLinks: detailsToSocialLinks(normalized),\n };\n}\n\nexport function buildShellViewModel(details: BrandDetails): ShellViewModel {\n return buildShellViewModelFromNormalized(normalizeBrandDetails(details));\n}\n\nexport function normalizeEmailHref(email?: string): string | undefined {\n if (typeof email !== \"string\") return undefined;\n const trimmed = email.trim();\n if (trimmed.length === 0) return undefined;\n if (trimmed.toLowerCase().startsWith(\"mailto:\")) {\n const address = trimmed.slice(7).trim();\n if (address.length === 0) return undefined;\n return normalizeSafeHref(`mailto:${address}`);\n }\n return normalizeSafeHref(`mailto:${trimmed}`);\n}\n\nfunction normalizeCustomSocialLinks(links?: CustomSocialLink[]): CustomSocialLink[] | undefined {\n if (!links || links.length === 0) return undefined;\n const normalized: CustomSocialLink[] = [];\n for (const link of links) {\n const href = normalizeSafeHref(link.href);\n if (!href) continue;\n normalized.push({ ...link, href });\n }\n return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction normalizeAction(action?: BrandAction): NormalizedActionLink | undefined {\n if (!action) return undefined;\n\n const href = normalizeSafeHref(action.href);\n if (!href) return undefined;\n\n const { target, rel } = normalizeLinkTargetRel(action.target, action.rel);\n return {\n ...action,\n href,\n target,\n rel,\n ariaLabel: action.ariaLabel ?? action.label,\n };\n}\n\nexport function normalizeLinkTargetRel(target?: BrandNavLink[\"target\"], rel?: string) {\n const normalizedTarget: LinkTarget = target ?? \"_self\";\n const normalizedRel = normalizeRel(normalizedTarget, rel);\n return {\n target: normalizedTarget,\n rel: normalizedRel,\n };\n}\n","type ImportMetaEnv = {\n DEV?: boolean;\n MODE?: string;\n};\n\ntype ImportMetaLike = {\n env?: ImportMetaEnv;\n};\n\nexport function shouldValidateInDev(): boolean {\n const nodeEnv = typeof process !== \"undefined\" ? process.env?.NODE_ENV : undefined;\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n const { env } = import.meta as unknown as ImportMetaLike;\n if (typeof env?.DEV === \"boolean\") {\n return env.DEV;\n }\n\n return false;\n}\n","import type { BrandTheme } from \"./types\";\n\nconst THEME_VAR_PREFIX = \"brand\";\nconst DARK_TEXT = \"#0f172a\";\nconst LIGHT_TEXT = \"#f8fafc\";\n\nexport type ThemeVariables = Record<string, string>;\n\nexport function themeToCssVariables(theme?: BrandTheme | null): ThemeVariables {\n if (!theme) return {};\n const style: ThemeVariables = {};\n if (theme.primaryColor != null) style[`--${THEME_VAR_PREFIX}-primary`] = theme.primaryColor;\n if (theme.backgroundColor != null) style[`--${THEME_VAR_PREFIX}-bg`] = theme.backgroundColor;\n if (theme.textColor != null) style[`--${THEME_VAR_PREFIX}-text`] = theme.textColor;\n if (theme.fontFamily != null) style[`--${THEME_VAR_PREFIX}-font`] = theme.fontFamily;\n if (theme.linkColor != null) style[`--${THEME_VAR_PREFIX}-link`] = theme.linkColor;\n if (theme.socialIconSize != null) style[`--${THEME_VAR_PREFIX}-social-size`] = theme.socialIconSize;\n if (theme.buttonTextColor != null) {\n style[`--${THEME_VAR_PREFIX}-button-text`] = theme.buttonTextColor;\n } else if (theme.primaryColor != null) {\n const buttonTextColor = getAccessibleTextColor(theme.primaryColor);\n if (buttonTextColor) style[`--${THEME_VAR_PREFIX}-button-text`] = buttonTextColor;\n }\n if (theme.borderRadius != null) style[`--${THEME_VAR_PREFIX}-radius`] = theme.borderRadius;\n if (theme.headerHeight != null) style[`--${THEME_VAR_PREFIX}-header-height`] = theme.headerHeight;\n if (theme.footerPadding != null) style[`--${THEME_VAR_PREFIX}-footer-padding`] = theme.footerPadding;\n if (theme.secondaryButtonBg != null) style[`--${THEME_VAR_PREFIX}-button-secondary`] = theme.secondaryButtonBg;\n return style;\n}\n\nfunction getAccessibleTextColor(backgroundColor: string): string | undefined {\n const rgb = parseColorToRgb(backgroundColor);\n if (!rgb) return undefined;\n\n const contrastWithDark = contrastRatio(rgb, { r: 15, g: 23, b: 42 });\n const contrastWithLight = contrastRatio(rgb, { r: 248, g: 250, b: 252 });\n\n return contrastWithDark > contrastWithLight ? DARK_TEXT : LIGHT_TEXT;\n}\n\n/**\n * Parses a hex (#rgb / #rrggbb) or rgb()/rgba() color string to RGB components.\n * hsl(), oklch(), and other formats are not supported and return undefined.\n */\nfunction parseColorToRgb(color: string): { r: number; g: number; b: number } | undefined {\n const trimmed = color.trim();\n\n const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed);\n if (hexMatch) {\n const value = hexMatch[1];\n if (value.length === 3) {\n return {\n r: Number.parseInt(value[0] + value[0], 16),\n g: Number.parseInt(value[1] + value[1], 16),\n b: Number.parseInt(value[2] + value[2], 16),\n };\n }\n return {\n r: Number.parseInt(value.slice(0, 2), 16),\n g: Number.parseInt(value.slice(2, 4), 16),\n b: Number.parseInt(value.slice(4, 6), 16),\n };\n }\n\n const rgbMatch = /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/.exec(trimmed);\n if (rgbMatch) {\n return { r: +rgbMatch[1], g: +rgbMatch[2], b: +rgbMatch[3] };\n }\n\n return undefined;\n}\n\nfunction contrastRatio(\n first: { r: number; g: number; b: number },\n second: { r: number; g: number; b: number },\n): number {\n const firstLuminance = relativeLuminance(first);\n const secondLuminance = relativeLuminance(second);\n const lighter = Math.max(firstLuminance, secondLuminance);\n const darker = Math.min(firstLuminance, secondLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction relativeLuminance(color: { r: number; g: number; b: number }): number {\n const transform = (channel: number) => {\n const normalized = channel / 255;\n if (normalized <= 0.03928) return normalized / 12.92;\n return ((normalized + 0.055) / 1.055) ** 2.4;\n };\n\n const r = transform(color.r);\n const g = transform(color.g);\n const b = transform(color.b);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n","import { normalizeBrandDetails, normalizeEmailHref, type NormalizedBrandDetails } from \"./shell\";\nimport { normalizeSafeHref } from \"./links\";\nimport type { BrandAction, BrandDetails, BrandNavLink, BrandTheme, CustomSocialLink } from \"./types\";\n\nconst LINK_TARGETS = new Set<NonNullable<BrandNavLink[\"target\"]>>([\"_blank\", \"_self\", \"_parent\", \"_top\"]);\nconst CTA_VARIANTS = new Set<NonNullable<BrandAction[\"variant\"]>>([\"primary\", \"secondary\", \"ghost\"]);\nconst CTA_LAYOUTS = new Set<NonNullable<BrandTheme[\"ctaLayout\"]>>([\"inline\", \"stacked\"]);\nconst THEME_KEYS = new Set<keyof BrandTheme>([\n \"primaryColor\",\n \"backgroundColor\",\n \"textColor\",\n \"fontFamily\",\n \"linkColor\",\n \"socialIconSize\",\n \"buttonTextColor\",\n \"ctaLayout\",\n \"borderRadius\",\n \"headerHeight\",\n \"footerPadding\",\n \"secondaryButtonBg\",\n]);\n\ntype ValidationErrorPath = string;\n\nexport interface BrandValidationResult<T> {\n valid: boolean;\n errors: string[];\n normalized: T | null;\n}\n\nexport class BrandShellValidationError extends Error {\n readonly context: string;\n readonly errors: string[];\n\n constructor(context: string, errors: string[]) {\n super(formatValidationErrors(context, errors));\n this.name = \"BrandShellValidationError\";\n this.context = context;\n this.errors = errors;\n }\n}\n\nexport function validateBrandDetails(details: unknown): BrandValidationResult<NormalizedBrandDetails> {\n const errors: string[] = [];\n\n if (!isRecord(details)) {\n errors.push(\"details must be an object.\");\n return { valid: false, errors, normalized: null };\n }\n\n validateRequiredString(details.name, \"details.name\", errors);\n validateOptionalString(details.homeHref, \"details.homeHref\", errors);\n validateSafeHref(details.homeHref, \"details.homeHref\", errors);\n validateOptionalString(details.website, \"details.website\", errors);\n validateSafeHref(details.website, \"details.website\", errors);\n validateOptionalString(details.linkedin, \"details.linkedin\", errors);\n validateSafeHref(details.linkedin, \"details.linkedin\", errors);\n validateOptionalString(details.email, \"details.email\", errors);\n validateEmail(details.email, \"details.email\", errors);\n validateOptionalString(details.github, \"details.github\", errors);\n validateSafeHref(details.github, \"details.github\", errors);\n validateOptionalString(details.twitter, \"details.twitter\", errors);\n validateSafeHref(details.twitter, \"details.twitter\", errors);\n validateOptionalString(details.discord, \"details.discord\", errors);\n validateSafeHref(details.discord, \"details.discord\", errors);\n validateOptionalString(details.tagline, \"details.tagline\", errors);\n\n if (details.navLinks != null) {\n if (!Array.isArray(details.navLinks)) {\n errors.push(\"details.navLinks must be an array.\");\n } else {\n details.navLinks.forEach((link, index) => validateNavLink(link, `details.navLinks[${index}]`, errors));\n }\n }\n\n if (details.primaryAction != null) {\n validateAction(details.primaryAction, \"details.primaryAction\", errors);\n }\n if (details.secondaryAction != null) {\n validateAction(details.secondaryAction, \"details.secondaryAction\", errors);\n }\n\n if (details.customSocialLinks != null) {\n if (!Array.isArray(details.customSocialLinks)) {\n errors.push(\"details.customSocialLinks must be an array.\");\n } else {\n details.customSocialLinks.forEach((link, index) =>\n validateCustomSocialLink(link, `details.customSocialLinks[${index}]`, errors),\n );\n }\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandDetails(details as unknown as BrandDetails),\n };\n}\n\nexport function validateBrandTheme(theme: unknown): BrandValidationResult<BrandTheme | null> {\n if (theme == null) {\n return {\n valid: true,\n errors: [],\n normalized: null,\n };\n }\n\n const errors: string[] = [];\n\n if (!isRecord(theme)) {\n errors.push(\"theme must be an object when provided.\");\n return { valid: false, errors, normalized: null };\n }\n\n for (const key of Object.keys(theme)) {\n if (!THEME_KEYS.has(key as keyof BrandTheme)) {\n errors.push(`theme.${key} is not a supported theme key.`);\n continue;\n }\n if (key === \"ctaLayout\") {\n validateCtaLayout(theme[key], \"theme.ctaLayout\", errors);\n continue;\n }\n validateOptionalString(theme[key], `theme.${key}`, errors);\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandTheme(theme as BrandTheme),\n };\n}\n\nexport function assertValidBrandDetails(details: unknown, context = \"BrandDetails\"): asserts details is BrandDetails {\n const result = validateBrandDetails(details);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function assertValidBrandTheme(theme: unknown, context = \"BrandTheme\"): asserts theme is BrandTheme | null | undefined {\n const result = validateBrandTheme(theme);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function normalizeBrandTheme(theme?: BrandTheme | null): BrandTheme | null {\n if (!theme) return null;\n\n const normalized: BrandTheme = {};\n for (const key of THEME_KEYS) {\n const value = theme[key];\n if (key === \"ctaLayout\") {\n if (typeof value === \"string\" && CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n normalized[key] = value as BrandTheme[\"ctaLayout\"];\n }\n continue;\n }\n\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length > 0) {\n normalized[key] = trimmed;\n }\n }\n }\n\n return Object.keys(normalized).length > 0 ? normalized : null;\n}\n\nexport function formatValidationErrors(context: string, errors: string[]): string {\n return `${context} validation failed:\\n${errors.map((error) => `- ${error}`).join(\"\\n\")}`;\n}\n\nfunction validateNavLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n validateOptionalString(link.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(link.rel, `${path}.rel`, errors);\n validateTarget(link.target, `${path}.target`, errors);\n}\n\nfunction validateAction(action: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(action)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(action.label, `${path}.label`, errors);\n validateRequiredString(action.href, `${path}.href`, errors);\n validateSafeHref(action.href, `${path}.href`, errors);\n validateOptionalString(action.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(action.rel, `${path}.rel`, errors);\n validateTarget(action.target, `${path}.target`, errors);\n\n if (action.variant != null) {\n if (typeof action.variant !== \"string\" || !CTA_VARIANTS.has(action.variant as NonNullable<BrandAction[\"variant\"]>)) {\n errors.push(`${path}.variant must be one of: primary, secondary, ghost.`);\n }\n }\n}\n\nfunction validateCustomSocialLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n if (typeof link.platform !== \"string\" || link.platform.trim().length === 0) {\n errors.push(`${path}.platform must be a non-empty string.`);\n }\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n if (link.iconSvg != null) {\n validateOptionalString(link.iconSvg, `${path}.iconSvg`, errors);\n }\n}\n\nfunction validateTarget(target: unknown, path: ValidationErrorPath, errors: string[]) {\n if (target == null) return;\n if (typeof target !== \"string\" || !LINK_TARGETS.has(target as NonNullable<BrandNavLink[\"target\"]>)) {\n errors.push(`${path} must be one of: _blank, _self, _parent, _top.`);\n }\n}\n\nfunction validateCtaLayout(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || !CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n errors.push(`${path} must be one of: inline, stacked.`);\n }\n}\n\nfunction validateRequiredString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string.`);\n }\n}\n\nfunction validateOptionalString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string when provided.`);\n }\n}\n\nfunction validateSafeHref(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeSafeHref(value)) {\n errors.push(`${path} must use a safe URL/path (http, https, mailto, tel, or relative path).`);\n }\n}\n\nfunction validateEmail(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeEmailHref(value)) {\n errors.push(`${path} must be a valid email or mailto URL.`);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":"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"}
|
package/dist/vue.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { E as BrandTheme, c as validateBrandTheme, s as validateBrandDetails, w as BrandDetails } from "./validation-Cic4hPME.mjs";
|
|
2
|
+
import { LinkFactoryOptions } from "./web.mjs";
|
|
2
3
|
import * as vue from "vue";
|
|
3
4
|
import { PropType } from "vue";
|
|
4
5
|
|
|
@@ -7,6 +8,7 @@ interface BrandShellVueProps {
|
|
|
7
8
|
details: BrandDetails;
|
|
8
9
|
theme?: BrandTheme | null;
|
|
9
10
|
shellClass?: string | null;
|
|
11
|
+
linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement;
|
|
10
12
|
}
|
|
11
13
|
declare const BrandHeader: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
12
14
|
details: {
|
|
@@ -21,6 +23,10 @@ declare const BrandHeader: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
21
23
|
type: PropType<string | null>;
|
|
22
24
|
default: null;
|
|
23
25
|
};
|
|
26
|
+
linkFactory: {
|
|
27
|
+
type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
|
|
28
|
+
default: null;
|
|
29
|
+
};
|
|
24
30
|
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
25
31
|
[key: string]: any;
|
|
26
32
|
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
@@ -36,9 +42,14 @@ declare const BrandHeader: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
36
42
|
type: PropType<string | null>;
|
|
37
43
|
default: null;
|
|
38
44
|
};
|
|
45
|
+
linkFactory: {
|
|
46
|
+
type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
|
|
47
|
+
default: null;
|
|
48
|
+
};
|
|
39
49
|
}>> & Readonly<{}>, {
|
|
40
50
|
theme: BrandTheme | null;
|
|
41
51
|
shellClass: string | null;
|
|
52
|
+
linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;
|
|
42
53
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
43
54
|
declare const BrandFooter: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
44
55
|
details: {
|
|
@@ -53,6 +64,10 @@ declare const BrandFooter: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
53
64
|
type: PropType<string | null>;
|
|
54
65
|
default: null;
|
|
55
66
|
};
|
|
67
|
+
linkFactory: {
|
|
68
|
+
type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
|
|
69
|
+
default: null;
|
|
70
|
+
};
|
|
56
71
|
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
57
72
|
[key: string]: any;
|
|
58
73
|
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
@@ -68,14 +83,19 @@ declare const BrandFooter: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
68
83
|
type: PropType<string | null>;
|
|
69
84
|
default: null;
|
|
70
85
|
};
|
|
86
|
+
linkFactory: {
|
|
87
|
+
type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
|
|
88
|
+
default: null;
|
|
89
|
+
};
|
|
71
90
|
}>> & Readonly<{}>, {
|
|
72
91
|
theme: BrandTheme | null;
|
|
73
92
|
shellClass: string | null;
|
|
93
|
+
linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;
|
|
74
94
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
75
95
|
declare function registerBrandShellVueElements(): {
|
|
76
96
|
headerTagName: string;
|
|
77
97
|
footerTagName: string;
|
|
78
98
|
};
|
|
79
99
|
//#endregion
|
|
80
|
-
export { type BrandDetails, BrandFooter, BrandHeader, BrandShellVueProps, type BrandTheme, registerBrandShellVueElements };
|
|
100
|
+
export { type BrandDetails, BrandFooter, BrandHeader, BrandShellVueProps, type BrandTheme, type LinkFactoryOptions, registerBrandShellVueElements, validateBrandDetails, validateBrandTheme };
|
|
81
101
|
//# sourceMappingURL=vue.d.mts.map
|
package/dist/vue.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.d.mts","names":[],"sources":["../src/vue/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"vue.d.mts","names":[],"sources":["../src/vue/index.ts"],"mappings":";;;;;;UAUiB,kBAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,cAwEpC,WAAA,MAAW,eAAA,CA3CqB,GAAA,CA2CrB,gBAAA;;UAvDA,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;oBA2CxD,GAAA,CAAA,YAAA;;4GA3CqB,GAAA,CAAA,gBAAA;;UAZrB,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;;;;0BAAvB,kBAAA,KAAuB,iBAAA;AAAA,uBAAiB,GAAA,CAAA,uBAAA;AAAA,cA4CpF,WAAA,MAAW,eAAA,CA5CqB,GAAA,CA4CrB,gBAAA;;UAxDA,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;oBA4CxD,GAAA,CAAA,YAAA;;4GA5CqB,GAAA,CAAA,gBAAA;;UAZrB,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;;;;0BAAvB,kBAAA,KAAuB,iBAAA;AAAA,uBAAiB,GAAA,CAAA,uBAAA;AAAA,iBA8CjF,6BAAA,CAAA;EAA6B,aAAA;EAAA,aAAA;AAAA"}
|
package/dist/vue.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as shouldValidateInDev, n as assertValidBrandDetails, r as assertValidBrandTheme } from "./validation-
|
|
1
|
+
import { l as shouldValidateInDev, n as assertValidBrandDetails, o as validateBrandDetails, r as assertValidBrandTheme, s as validateBrandTheme } from "./validation-CtH2UkVv.mjs";
|
|
2
2
|
import { applyBrandShellProps, registerBrandShellElements } from "./web.mjs";
|
|
3
3
|
import { defineComponent, h, onMounted, ref, watch } from "vue";
|
|
4
4
|
|
|
@@ -21,6 +21,10 @@ function createBrandShellVueComponent(tagName, componentName) {
|
|
|
21
21
|
shellClass: {
|
|
22
22
|
type: String,
|
|
23
23
|
default: null
|
|
24
|
+
},
|
|
25
|
+
linkFactory: {
|
|
26
|
+
type: Function,
|
|
27
|
+
default: null
|
|
24
28
|
}
|
|
25
29
|
},
|
|
26
30
|
setup(props) {
|
|
@@ -34,7 +38,8 @@ function createBrandShellVueComponent(tagName, componentName) {
|
|
|
34
38
|
applyBrandShellProps(elementRef.value, {
|
|
35
39
|
details: props.details,
|
|
36
40
|
theme: props.theme ?? null,
|
|
37
|
-
shellClass: props.shellClass ?? null
|
|
41
|
+
shellClass: props.shellClass ?? null,
|
|
42
|
+
linkFactory: props.linkFactory ?? void 0
|
|
38
43
|
});
|
|
39
44
|
};
|
|
40
45
|
onMounted(() => {
|
|
@@ -43,7 +48,8 @@ function createBrandShellVueComponent(tagName, componentName) {
|
|
|
43
48
|
watch(() => [
|
|
44
49
|
props.details,
|
|
45
50
|
props.theme,
|
|
46
|
-
props.shellClass
|
|
51
|
+
props.shellClass,
|
|
52
|
+
props.linkFactory
|
|
47
53
|
], () => {
|
|
48
54
|
syncProps();
|
|
49
55
|
}, { deep: true });
|
|
@@ -58,5 +64,5 @@ function registerBrandShellVueElements() {
|
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
//#endregion
|
|
61
|
-
export { BrandFooter, BrandHeader, registerBrandShellVueElements };
|
|
67
|
+
export { BrandFooter, BrandHeader, registerBrandShellVueElements, validateBrandDetails, validateBrandTheme };
|
|
62
68
|
//# sourceMappingURL=vue.mjs.map
|
package/dist/vue.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.mjs","names":[],"sources":["../src/vue/index.ts"],"sourcesContent":["import { defineComponent, h, onMounted, ref, watch, type PropType } from \"vue\";\n\nimport type { BrandDetails, BrandTheme } from \"../core\";\nimport { assertValidBrandDetails, assertValidBrandTheme, shouldValidateInDev } from \"../core\";\nimport { applyBrandShellProps, registerBrandShellElements, type BrandShellElementLike } from \"../web\";\n\nexport type { BrandDetails, BrandTheme } from \"../core\";\n\nexport interface BrandShellVueProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n}\n\nfunction ensureBrandShellElementsRegistered() {\n if (typeof customElements !== \"undefined\") {\n registerBrandShellElements();\n }\n}\n\nfunction createBrandShellVueComponent(\n tagName: \"brand-header\" | \"brand-footer\",\n componentName: \"BrandHeader\" | \"BrandFooter\",\n) {\n return defineComponent({\n name: componentName,\n props: {\n details: {\n type: Object as PropType<BrandDetails>,\n required: true,\n },\n theme: {\n type: Object as PropType<BrandTheme | null>,\n default: null,\n },\n shellClass: {\n type: String as PropType<string | null>,\n default: null,\n },\n },\n setup(props) {\n ensureBrandShellElementsRegistered();\n\n const elementRef = ref<BrandShellElementLike | null>(null);\n\n const syncProps = () => {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, `brand-shell/vue ${componentName} details`);\n assertValidBrandTheme(props.theme, `brand-shell/vue ${componentName} theme`);\n }\n\n applyBrandShellProps(elementRef.value, {\n details: props.details,\n theme: props.theme ?? null,\n shellClass: props.shellClass ?? null,\n });\n };\n\n onMounted(() => {\n syncProps();\n });\n\n watch(\n () => [props.details, props.theme, props.shellClass],\n () => {\n syncProps();\n },\n { deep: true },\n );\n\n return () =>\n h(tagName, {\n ref: elementRef,\n });\n },\n });\n}\n\nexport const BrandHeader = createBrandShellVueComponent(\"brand-header\", \"BrandHeader\");\nexport const BrandFooter = createBrandShellVueComponent(\"brand-footer\", \"BrandFooter\");\n\nexport function registerBrandShellVueElements() {\n return registerBrandShellElements();\n}\n"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"vue.mjs","names":[],"sources":["../src/vue/index.ts"],"sourcesContent":["import { defineComponent, h, onMounted, ref, watch, type PropType } from \"vue\";\n\nimport type { BrandDetails, BrandTheme } from \"../core\";\nimport { assertValidBrandDetails, assertValidBrandTheme, shouldValidateInDev } from \"../core\";\nimport { applyBrandShellProps, registerBrandShellElements, type BrandShellElementLike, type LinkFactoryOptions } from \"../web\";\n\nexport type { BrandDetails, BrandTheme } from \"../core\";\nexport { validateBrandDetails, validateBrandTheme } from \"../core\";\nexport type { LinkFactoryOptions } from \"../web\";\n\nexport interface BrandShellVueProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement;\n}\n\nfunction ensureBrandShellElementsRegistered() {\n if (typeof customElements !== \"undefined\") {\n registerBrandShellElements();\n }\n}\n\nfunction createBrandShellVueComponent(\n tagName: \"brand-header\" | \"brand-footer\",\n componentName: \"BrandHeader\" | \"BrandFooter\",\n) {\n return defineComponent({\n name: componentName,\n props: {\n details: {\n type: Object as PropType<BrandDetails>,\n required: true,\n },\n theme: {\n type: Object as PropType<BrandTheme | null>,\n default: null,\n },\n shellClass: {\n type: String as PropType<string | null>,\n default: null,\n },\n linkFactory: {\n type: Function as unknown as PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>,\n default: null,\n },\n },\n setup(props) {\n ensureBrandShellElementsRegistered();\n\n const elementRef = ref<BrandShellElementLike | null>(null);\n\n const syncProps = () => {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, `brand-shell/vue ${componentName} details`);\n assertValidBrandTheme(props.theme, `brand-shell/vue ${componentName} theme`);\n }\n\n applyBrandShellProps(elementRef.value, {\n details: props.details,\n theme: props.theme ?? null,\n shellClass: props.shellClass ?? null,\n linkFactory: props.linkFactory ?? undefined,\n });\n };\n\n onMounted(() => {\n syncProps();\n });\n\n watch(\n () => [props.details, props.theme, props.shellClass, props.linkFactory],\n () => {\n syncProps();\n },\n { deep: true },\n );\n\n return () =>\n h(tagName, {\n ref: elementRef,\n });\n },\n });\n}\n\nexport const BrandHeader = createBrandShellVueComponent(\"brand-header\", \"BrandHeader\");\nexport const BrandFooter = createBrandShellVueComponent(\"brand-footer\", \"BrandFooter\");\n\nexport function registerBrandShellVueElements() {\n return registerBrandShellElements();\n}\n"],"mappings":";;;;;AAiBA,SAAS,qCAAqC;AAC5C,KAAI,OAAO,mBAAmB,YAC5B,6BAA4B;;AAIhC,SAAS,6BACP,SACA,eACA;AACA,QAAO,gBAAgB;EACrB,MAAM;EACN,OAAO;GACL,SAAS;IACP,MAAM;IACN,UAAU;IACX;GACD,OAAO;IACL,MAAM;IACN,SAAS;IACV;GACD,YAAY;IACV,MAAM;IACN,SAAS;IACV;GACD,aAAa;IACX,MAAM;IACN,SAAS;IACV;GACF;EACD,MAAM,OAAO;AACX,uCAAoC;GAEpC,MAAM,aAAa,IAAkC,KAAK;GAE1D,MAAM,kBAAkB;AACtB,QAAI,qBAAqB,EAAE;AACzB,6BAAwB,MAAM,SAAS,mBAAmB,cAAc,UAAU;AAClF,2BAAsB,MAAM,OAAO,mBAAmB,cAAc,QAAQ;;AAG9E,yBAAqB,WAAW,OAAO;KACrC,SAAS,MAAM;KACf,OAAO,MAAM,SAAS;KACtB,YAAY,MAAM,cAAc;KAChC,aAAa,MAAM,eAAe;KACnC,CAAC;;AAGJ,mBAAgB;AACd,eAAW;KACX;AAEF,eACQ;IAAC,MAAM;IAAS,MAAM;IAAO,MAAM;IAAY,MAAM;IAAY,QACjE;AACJ,eAAW;MAEb,EAAE,MAAM,MAAM,CACf;AAED,gBACE,EAAE,SAAS,EACT,KAAK,YACN,CAAC;;EAEP,CAAC;;AAGJ,MAAa,cAAc,6BAA6B,gBAAgB,cAAc;AACtF,MAAa,cAAc,6BAA6B,gBAAgB,cAAc;AAEtF,SAAgB,gCAAgC;AAC9C,QAAO,4BAA4B"}
|