@bettercms-ai/types 1.40.0 → 1.41.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/README.md +18 -18
- package/dist/index.d.ts +247 -28
- package/dist/index.js +147 -9
- package/dist/index.js.map +1 -1
- package/package.json +34 -34
package/dist/index.js
CHANGED
|
@@ -427,7 +427,27 @@ var COMPONENT_PROP_TYPES = [
|
|
|
427
427
|
"color",
|
|
428
428
|
"file",
|
|
429
429
|
"icon",
|
|
430
|
-
"multiselect"
|
|
430
|
+
"multiselect",
|
|
431
|
+
// V2 schema language (F1.x). `object` is ONE nested value of named `config.fields`, stored as
|
|
432
|
+
// `{ [childKey]: value }` like the model field `object`; the six after it store what the
|
|
433
|
+
// same-named content-model FIELD stores (`location` is the V2 geopoint) and are judged by
|
|
434
|
+
// that field's validator.
|
|
435
|
+
"object",
|
|
436
|
+
"datetime",
|
|
437
|
+
"slug",
|
|
438
|
+
"email",
|
|
439
|
+
"phone",
|
|
440
|
+
"json",
|
|
441
|
+
"location",
|
|
442
|
+
// V2 leaves, each storing what the same-named content-model FIELD stores: a taxonomy string[],
|
|
443
|
+
// a rating number, a code value (`{ language?, code }` or a string).
|
|
444
|
+
"tags",
|
|
445
|
+
"rating",
|
|
446
|
+
"code",
|
|
447
|
+
// CMS items, as the same-named content-model FIELDS store them: an entry id (or a polymorphic
|
|
448
|
+
// `{ entryId, modelSlug }`), and a list of them. Delivery hydrates them like an entry's.
|
|
449
|
+
"reference",
|
|
450
|
+
"multi-reference"
|
|
431
451
|
];
|
|
432
452
|
var SLOT_MAX_ITEMS = 12;
|
|
433
453
|
var SLOT_ITEMS_CAP = 50;
|
|
@@ -2688,7 +2708,7 @@ function listItems(f) {
|
|
|
2688
2708
|
}
|
|
2689
2709
|
function zoneFields(f) {
|
|
2690
2710
|
const c = f.config ?? {};
|
|
2691
|
-
if (f.type === "group") return c.fields ?? f.fields ?? [];
|
|
2711
|
+
if (f.type === "group" || f.type === "object") return c.fields ?? f.fields ?? [];
|
|
2692
2712
|
if (f.type === "array" && c.zones?.nonRepeatable) return c.zones.nonRepeatable;
|
|
2693
2713
|
return void 0;
|
|
2694
2714
|
}
|
|
@@ -2927,12 +2947,101 @@ function buttonLinkPairs(candidates) {
|
|
|
2927
2947
|
|
|
2928
2948
|
// src/button-look.ts
|
|
2929
2949
|
import { ICON_MARKUP, iconSvg } from "@bettercms-ai/icons/markup";
|
|
2950
|
+
|
|
2951
|
+
// src/link-value.ts
|
|
2952
|
+
var LINK_RELS = ["nofollow", "sponsored", "ugc"];
|
|
2953
|
+
var LINK_VALUE_TYPES = ["url", "page", "entry", "email", "phone", "anchor", "section", "file"];
|
|
2954
|
+
var isPlainObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
2955
|
+
function isLinkObject(value) {
|
|
2956
|
+
return isPlainObject(value) && typeof value.href === "string";
|
|
2957
|
+
}
|
|
2958
|
+
function linkHref(value) {
|
|
2959
|
+
if (typeof value === "string") return value;
|
|
2960
|
+
return isLinkObject(value) ? value.href : null;
|
|
2961
|
+
}
|
|
2962
|
+
function linkRefOf(value) {
|
|
2963
|
+
if (!isLinkObject(value) || value.type !== "page" && value.type !== "entry") return null;
|
|
2964
|
+
const id = value.ref ?? (value.type === "page" ? value.pageId : value.entryId);
|
|
2965
|
+
return typeof id === "string" && id.length > 0 ? { kind: value.type, id } : null;
|
|
2966
|
+
}
|
|
2967
|
+
var isScriptHref = (href) => /^(javascript|data|vbscript):/i.test(href.replace(/[\u0000- \u007F]/g, ""));
|
|
2968
|
+
var newTabOf = (v) => v.newTab === true || v.newTab === void 0 && v.target === "_blank";
|
|
2969
|
+
function linkValueProblem(value) {
|
|
2970
|
+
if (!isPlainObject(value)) return "link_value_invalid: must be an href string or a link object { type, href, \u2026 }";
|
|
2971
|
+
const v = value;
|
|
2972
|
+
if (typeof v.href !== "string") return "link_value_invalid: needs an href string";
|
|
2973
|
+
if (isScriptHref(v.href)) return "link_value_invalid: script links (javascript:, data:, vbscript:) aren't allowed";
|
|
2974
|
+
if (v.type !== void 0 && !LINK_VALUE_TYPES.includes(v.type)) {
|
|
2975
|
+
return `link_value_invalid: unknown type "${String(v.type)}" (${LINK_VALUE_TYPES.join(", ")})`;
|
|
2976
|
+
}
|
|
2977
|
+
for (const key of ["title", "section", "ref"]) {
|
|
2978
|
+
if (v[key] !== void 0 && typeof v[key] !== "string") return `link_value_invalid: ${key} must be a string`;
|
|
2979
|
+
}
|
|
2980
|
+
if (v.ref !== void 0 && v.type !== "page" && v.type !== "entry") return "link_value_invalid: ref is the target of a page or entry link";
|
|
2981
|
+
if (v.newTab !== void 0 && typeof v.newTab !== "boolean") return "link_value_invalid: newTab must be true or false";
|
|
2982
|
+
if (v.target !== void 0 && v.target !== "_self" && v.target !== "_blank") return 'link_value_invalid: target must be "_self" or "_blank"';
|
|
2983
|
+
if (typeof v.newTab === "boolean" && v.target !== void 0 && v.newTab !== (v.target === "_blank")) {
|
|
2984
|
+
return "link_value_invalid: newTab and target disagree";
|
|
2985
|
+
}
|
|
2986
|
+
if (v.rel !== void 0 && !LINK_RELS.includes(v.rel)) {
|
|
2987
|
+
return `link_value_invalid: unknown rel "${String(v.rel)}" (nofollow, sponsored or ugc)`;
|
|
2988
|
+
}
|
|
2989
|
+
return null;
|
|
2990
|
+
}
|
|
2991
|
+
function normalizeLinkValue(value) {
|
|
2992
|
+
if (!isLinkObject(value)) return value;
|
|
2993
|
+
const out = { ...value };
|
|
2994
|
+
delete out.resolved;
|
|
2995
|
+
if (out.rel === "follow") delete out.rel;
|
|
2996
|
+
if (out.newTab === void 0 && out.target !== void 0) out.newTab = out.target === "_blank";
|
|
2997
|
+
const ref = linkRefOf(out);
|
|
2998
|
+
if (ref && out.ref === void 0) out.ref = ref.id;
|
|
2999
|
+
return out;
|
|
3000
|
+
}
|
|
3001
|
+
function linkAnchorOf(value) {
|
|
3002
|
+
if (typeof value === "string") return { href: value, newTab: false };
|
|
3003
|
+
if (!isLinkObject(value)) return null;
|
|
3004
|
+
return {
|
|
3005
|
+
href: value.href,
|
|
3006
|
+
newTab: newTabOf(value),
|
|
3007
|
+
...LINK_RELS.includes(value.rel) ? { rel: value.rel } : {}
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
// src/button-look.ts
|
|
2930
3012
|
var BUTTON_VARIANTS = ["primary", "secondary", "tertiary", "brand", "dark", "light", "outline", "link"];
|
|
2931
3013
|
var BUTTON_ICONS = ICON_MARKUP;
|
|
2932
3014
|
var BUTTON_ICON_NAMES = DOCK_ICON_NAMES;
|
|
2933
3015
|
function buttonVariantOf(value) {
|
|
2934
3016
|
return BUTTON_VARIANTS.includes(value) ? value : "primary";
|
|
2935
3017
|
}
|
|
3018
|
+
var SITE_CLASS = /^[^\s"'<>`=]{1,64}$/;
|
|
3019
|
+
var LOOK_VALUE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
3020
|
+
function buttonSiteLookOf(value) {
|
|
3021
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
3022
|
+
const v = value;
|
|
3023
|
+
const tokens = (list) => Array.isArray(list) ? list.filter((c) => typeof c === "string" && SITE_CLASS.test(c)) : [];
|
|
3024
|
+
const variant = v.attrs?.["data-variant"];
|
|
3025
|
+
const classes = tokens(v.classes);
|
|
3026
|
+
const also = tokens(v.also);
|
|
3027
|
+
const size = tokens(v.size);
|
|
3028
|
+
const sizeAlso = tokens(v.sizeAlso);
|
|
3029
|
+
const attrs = typeof variant === "string" && LOOK_VALUE.test(variant) ? { "data-variant": variant } : void 0;
|
|
3030
|
+
if (classes.length === 0 && !attrs) return null;
|
|
3031
|
+
return {
|
|
3032
|
+
classes,
|
|
3033
|
+
...attrs ? { attrs } : {},
|
|
3034
|
+
...also.length ? { also } : {},
|
|
3035
|
+
...size.length ? { size } : {},
|
|
3036
|
+
...sizeAlso.length ? { sizeAlso } : {}
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
function buttonLookValue(slug, siteLook) {
|
|
3040
|
+
return siteLook?.attrs?.["data-variant"] ?? slug;
|
|
3041
|
+
}
|
|
3042
|
+
function isButtonLookValue(value) {
|
|
3043
|
+
return typeof value === "string" && LOOK_VALUE.test(value);
|
|
3044
|
+
}
|
|
2936
3045
|
function buttonIconOf(value) {
|
|
2937
3046
|
return typeof value === "string" && DOCK_ICON_NAME_SET.has(value) ? value : null;
|
|
2938
3047
|
}
|
|
@@ -2940,14 +3049,15 @@ function buttonIconSvg(name, cls) {
|
|
|
2940
3049
|
return iconSvg(name, cls);
|
|
2941
3050
|
}
|
|
2942
3051
|
var BUTTON_SIZES = ["sm", "md", "lg"];
|
|
2943
|
-
var LINK_RELS = ["nofollow", "sponsored", "ugc"];
|
|
2944
3052
|
function anchorTargetRel(newTab, rel) {
|
|
2945
3053
|
const tokens = [...newTab ? ["noopener", "noreferrer"] : [], ...LINK_RELS.includes(rel) ? [rel] : []];
|
|
2946
3054
|
return `${newTab ? ' target="_blank"' : ""}${tokens.length ? ` rel="${tokens.join(" ")}"` : ""}`;
|
|
2947
3055
|
}
|
|
2948
|
-
var hrefOf =
|
|
3056
|
+
var hrefOf = linkHref;
|
|
2949
3057
|
function readButtonValue(value) {
|
|
2950
3058
|
const v = value && typeof value === "object" ? value : {};
|
|
3059
|
+
const link = isLinkObject(v.href) ? linkAnchorOf(v.href) : null;
|
|
3060
|
+
const rel = v.rel ?? link?.rel;
|
|
2951
3061
|
return {
|
|
2952
3062
|
label: typeof v.label === "string" ? v.label : "",
|
|
2953
3063
|
href: hrefOf(v.href) ?? "",
|
|
@@ -2955,16 +3065,22 @@ function readButtonValue(value) {
|
|
|
2955
3065
|
size: BUTTON_SIZES.includes(v.size) ? v.size : "md",
|
|
2956
3066
|
arrow: v.arrow === true,
|
|
2957
3067
|
icon: buttonIconOf(v.icon),
|
|
2958
|
-
newTab: v.newTab === true,
|
|
2959
|
-
...LINK_RELS.includes(
|
|
3068
|
+
newTab: v.newTab === void 0 ? link?.newTab === true : v.newTab === true,
|
|
3069
|
+
...LINK_RELS.includes(rel) ? { rel } : {}
|
|
2960
3070
|
};
|
|
2961
3071
|
}
|
|
2962
|
-
function buttonValueProblem(value) {
|
|
3072
|
+
function buttonValueProblem(value, looks) {
|
|
2963
3073
|
if (!value || typeof value !== "object" || Array.isArray(value)) return "must be a { label, href } button";
|
|
2964
3074
|
const v = value;
|
|
2965
3075
|
if (typeof v.label !== "string") return "needs a label";
|
|
2966
3076
|
if (hrefOf(v.href) === null) return "needs an href";
|
|
2967
|
-
if (
|
|
3077
|
+
if (isLinkObject(v.href)) {
|
|
3078
|
+
const problem = linkValueProblem(v.href);
|
|
3079
|
+
if (problem) return `href ${problem}`;
|
|
3080
|
+
}
|
|
3081
|
+
if (v.variant !== void 0 && !BUTTON_VARIANTS.includes(v.variant) && !(typeof v.variant === "string" && looks?.has(v.variant))) {
|
|
3082
|
+
return `has an unknown variant "${String(v.variant)}"${looks?.size ? ` (this project's looks: ${[...looks].join(", ")})` : ""}`;
|
|
3083
|
+
}
|
|
2968
3084
|
if (v.size !== void 0 && !BUTTON_SIZES.includes(v.size)) return `has an unknown size "${String(v.size)}"`;
|
|
2969
3085
|
if (v.arrow !== void 0 && typeof v.arrow !== "boolean") return "arrow must be true or false";
|
|
2970
3086
|
if (v.newTab !== void 0 && typeof v.newTab !== "boolean") return "newTab must be true or false";
|
|
@@ -2979,7 +3095,14 @@ function buttonBlockProps(value) {
|
|
|
2979
3095
|
const out = {};
|
|
2980
3096
|
if (typeof v.label === "string") out.text = v.label;
|
|
2981
3097
|
const href = hrefOf(v.href);
|
|
2982
|
-
|
|
3098
|
+
const script = href !== null && isScriptHref(href);
|
|
3099
|
+
if (href !== null) out.href = script ? "#" : href;
|
|
3100
|
+
if (isLinkObject(v.href) && !script) {
|
|
3101
|
+
const link = linkAnchorOf(v.href);
|
|
3102
|
+
out.newTab = link.newTab;
|
|
3103
|
+
out.rel = link.rel;
|
|
3104
|
+
}
|
|
3105
|
+
if (v.rel !== void 0) out.rel = v.rel;
|
|
2983
3106
|
for (const key of BUTTON_LOOK_KEYS) if (key in v) out[key] = v[key];
|
|
2984
3107
|
return out;
|
|
2985
3108
|
}
|
|
@@ -3032,6 +3155,9 @@ function buttonValueOfInstance(def, overrides) {
|
|
|
3032
3155
|
return readButtonValue({ ...props, label: props.text });
|
|
3033
3156
|
}
|
|
3034
3157
|
|
|
3158
|
+
// src/content-model.ts
|
|
3159
|
+
var OPTION_DESCRIPTION_MAX = 140;
|
|
3160
|
+
|
|
3035
3161
|
// src/locator.ts
|
|
3036
3162
|
var LANDMARK_SELECTOR = "header,nav,footer";
|
|
3037
3163
|
var LANDMARK_TAGS = /* @__PURE__ */ new Set(["HEADER", "NAV", "FOOTER"]);
|
|
@@ -3113,8 +3239,10 @@ export {
|
|
|
3113
3239
|
LAYOUT_SECTION_ICONS,
|
|
3114
3240
|
LAYOUT_SECTION_ICON_SET,
|
|
3115
3241
|
LINK_RELS,
|
|
3242
|
+
LINK_VALUE_TYPES,
|
|
3116
3243
|
MAIN_SELECTOR,
|
|
3117
3244
|
NAVIGATION_SECTION_ID,
|
|
3245
|
+
OPTION_DESCRIPTION_MAX,
|
|
3118
3246
|
PAGE_CONTENT_ANCHOR_ID,
|
|
3119
3247
|
RESPONSIVE_CSS,
|
|
3120
3248
|
SECTION_DOCTRINE,
|
|
@@ -3131,6 +3259,8 @@ export {
|
|
|
3131
3259
|
buttonIconOf,
|
|
3132
3260
|
buttonIconSvg,
|
|
3133
3261
|
buttonLinkPairs,
|
|
3262
|
+
buttonLookValue,
|
|
3263
|
+
buttonSiteLookOf,
|
|
3134
3264
|
buttonValueOfInstance,
|
|
3135
3265
|
buttonValueProblem,
|
|
3136
3266
|
buttonVariantOf,
|
|
@@ -3149,10 +3279,18 @@ export {
|
|
|
3149
3279
|
hideOnAttr,
|
|
3150
3280
|
inWindow,
|
|
3151
3281
|
isBlock,
|
|
3282
|
+
isButtonLookValue,
|
|
3152
3283
|
isChromeLandmark,
|
|
3284
|
+
isLinkObject,
|
|
3285
|
+
isScriptHref,
|
|
3153
3286
|
keyWords,
|
|
3154
3287
|
landmarkRegions,
|
|
3288
|
+
linkAnchorOf,
|
|
3289
|
+
linkHref,
|
|
3290
|
+
linkRefOf,
|
|
3291
|
+
linkValueProblem,
|
|
3155
3292
|
locatorFor,
|
|
3293
|
+
normalizeLinkValue,
|
|
3156
3294
|
normalizeWindow,
|
|
3157
3295
|
parsePropsAttribute,
|
|
3158
3296
|
pickFieldStyle,
|