@uniformdev/richtext 20.72.2-alpha.3 → 20.72.2
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/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.esm.js +61 -6
- package/dist/index.js +64 -6
- package/dist/index.mjs +61 -6
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -28,6 +28,29 @@ type NodeStringRenderer = (props: NodeStringRendererProps) => string;
|
|
|
28
28
|
type ParameterRichTextValue = SerializedEditorState | undefined | null;
|
|
29
29
|
type RichTextBuiltInFormat = 'code' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'superscript' | 'subscript';
|
|
30
30
|
type RichTextBuiltInElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'unorderedList' | 'orderedList' | 'link' | 'quote' | 'code' | 'variable' | 'table' | 'asset';
|
|
31
|
+
/**
|
|
32
|
+
* One entry in the link-attributes config list.
|
|
33
|
+
* `options`, `label`, and `helpText` are optional presentation hints.
|
|
34
|
+
* The React SDK translates the literal `class` key to `className` on output.
|
|
35
|
+
*/
|
|
36
|
+
interface LinkAttrConfig {
|
|
37
|
+
key: string;
|
|
38
|
+
/** When present, the editor renders a select; the value must be one of these. */
|
|
39
|
+
options?: Array<{
|
|
40
|
+
name: string;
|
|
41
|
+
value: string;
|
|
42
|
+
}>;
|
|
43
|
+
label?: string;
|
|
44
|
+
helpText?: string;
|
|
45
|
+
}
|
|
46
|
+
interface LinkAttributesConfiguration {
|
|
47
|
+
/** When true, the editor sees an Anchor input; the fragment lives on value.path. */
|
|
48
|
+
enableAnchor?: boolean;
|
|
49
|
+
/** Flat, ordered list of attribute entries a project wants on its links. */
|
|
50
|
+
entries?: Array<LinkAttrConfig>;
|
|
51
|
+
}
|
|
52
|
+
/** Normalize a stored entry for rendering — fills in a default label. */
|
|
53
|
+
declare function resolveLinkAttrConfig(entry: LinkAttrConfig): Required<Pick<LinkAttrConfig, 'key' | 'label'>> & Pick<LinkAttrConfig, 'options' | 'helpText'>;
|
|
31
54
|
interface RichTextParamConfiguration {
|
|
32
55
|
required?: boolean;
|
|
33
56
|
formatting?: {
|
|
@@ -40,6 +63,8 @@ interface RichTextParamConfiguration {
|
|
|
40
63
|
asset?: {
|
|
41
64
|
allowedTypes?: AssetDefinitionType[];
|
|
42
65
|
};
|
|
66
|
+
/** Optional anchor (fragment) toggle and custom attribute config for the link element. */
|
|
67
|
+
link?: LinkAttributesConfiguration;
|
|
43
68
|
};
|
|
44
69
|
}
|
|
45
70
|
|
|
@@ -65,12 +90,28 @@ type ProjectMapLinkParamValue = {
|
|
|
65
90
|
nodeId: string;
|
|
66
91
|
path: string;
|
|
67
92
|
dynamicInputValues?: Record<string, string>;
|
|
93
|
+
attributes?: Record<string, string>;
|
|
68
94
|
};
|
|
69
95
|
type NonProjectMapLinkParamValue = {
|
|
70
96
|
type: Exclude<LinkParameterType, 'projectMapNode'>;
|
|
71
97
|
path: string;
|
|
98
|
+
attributes?: Record<string, string>;
|
|
72
99
|
};
|
|
73
100
|
type LinkParamValue = ProjectMapLinkParamValue | NonProjectMapLinkParamValue | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* True for attribute keys the Uniform web SDKs strip at render time: inline event
|
|
103
|
+
* handlers (`on*`) and dangerous attribute names (href, src, style, formaction).
|
|
104
|
+
* Authoring UIs should use this to warn before such keys are ever persisted.
|
|
105
|
+
*/
|
|
106
|
+
declare function isDangerousLinkAttributeKey(key: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Converts a link parameter value into a plain HTML attribute bag suitable for
|
|
109
|
+
* an `<a>` element. Applies a web-channel security denylist (event handlers,
|
|
110
|
+
* script-executing URL schemes on the href and any value, dangerous attribute names) and auto-adds
|
|
111
|
+
* `rel="noopener noreferrer"` for any case variant of `target="_blank"`.
|
|
112
|
+
*/
|
|
113
|
+
declare function linkParamValueToHtmlAttributes(link: LinkParamValue): Record<string, string>;
|
|
114
|
+
/** Derives the href string from a link parameter value. */
|
|
74
115
|
declare function linkParamValueToHref(link: LinkParamValue): string | undefined;
|
|
75
116
|
|
|
76
117
|
interface ListNode extends RichTextNode {
|
|
@@ -223,4 +264,4 @@ declare const emptyRichTextValue: {
|
|
|
223
264
|
};
|
|
224
265
|
declare const defaultParameterConfiguration: RichTextParamConfiguration;
|
|
225
266
|
|
|
226
|
-
export { type AssetNode, type HeadingNode, type LinkNode, type ListItemNode, type ListNode, type NodeStringRenderer, type NodeStringRendererProps, type ParagraphNode, type ParameterRichTextValue, type ResolveStringRenderer, type RichTextBuiltInElement, type RichTextBuiltInFormat, type RichTextNode, type RichTextNodeWithChildren, type RichTextParamConfiguration, type StringRenderContext, type TableCellNode, type TableNode, type TableRowNode, type TextNode, assetHtmlRenderer, defaultParameterConfiguration, emptyRichTextValue, getLabelForElement, getLabelForFormat, getRichTextTagFromTableCellHeaderState, getRichTextTagsFromTextFormat, hasChildren, headingHtmlRenderer, isArrayWithLength, isPureDirection, isPureTextAlign, isRichTextNode, isRichTextNodeType, isRichTextValue, isRichTextValueConsideredEmpty, linkHtmlRenderer, linkParamValueToHref, listHtmlRenderer, listitemHtmlRenderer, paragraphHtmlRenderer, purifyText, renderChildrenToHtml, renderChildrenToText, renderHtmlElement, renderToHtml, renderToText, resolveDefaultRenderer$1 as resolveDefaultHtmlRenderer, resolveDefaultRenderer as resolveDefaultTextRenderer, richTextBuiltInElements, richTextBuiltInFormats, rootHtmlRenderer, tableHtmlRenderer, tablecellHtmlRenderer, tablerowHtmlRenderer, textHtmlRenderer, walkRichTextTree };
|
|
267
|
+
export { type AssetNode, type HeadingNode, type LinkAttrConfig, type LinkAttributesConfiguration, type LinkNode, type ListItemNode, type ListNode, type NodeStringRenderer, type NodeStringRendererProps, type ParagraphNode, type ParameterRichTextValue, type ResolveStringRenderer, type RichTextBuiltInElement, type RichTextBuiltInFormat, type RichTextNode, type RichTextNodeWithChildren, type RichTextParamConfiguration, type StringRenderContext, type TableCellNode, type TableNode, type TableRowNode, type TextNode, assetHtmlRenderer, defaultParameterConfiguration, emptyRichTextValue, getLabelForElement, getLabelForFormat, getRichTextTagFromTableCellHeaderState, getRichTextTagsFromTextFormat, hasChildren, headingHtmlRenderer, isArrayWithLength, isDangerousLinkAttributeKey, isPureDirection, isPureTextAlign, isRichTextNode, isRichTextNodeType, isRichTextValue, isRichTextValueConsideredEmpty, linkHtmlRenderer, linkParamValueToHref, linkParamValueToHtmlAttributes, listHtmlRenderer, listitemHtmlRenderer, paragraphHtmlRenderer, purifyText, renderChildrenToHtml, renderChildrenToText, renderHtmlElement, renderToHtml, renderToText, resolveDefaultRenderer$1 as resolveDefaultHtmlRenderer, resolveDefaultRenderer as resolveDefaultTextRenderer, resolveLinkAttrConfig, richTextBuiltInElements, richTextBuiltInFormats, rootHtmlRenderer, tableHtmlRenderer, tablecellHtmlRenderer, tablerowHtmlRenderer, textHtmlRenderer, walkRichTextTree };
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,29 @@ type NodeStringRenderer = (props: NodeStringRendererProps) => string;
|
|
|
28
28
|
type ParameterRichTextValue = SerializedEditorState | undefined | null;
|
|
29
29
|
type RichTextBuiltInFormat = 'code' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'superscript' | 'subscript';
|
|
30
30
|
type RichTextBuiltInElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'unorderedList' | 'orderedList' | 'link' | 'quote' | 'code' | 'variable' | 'table' | 'asset';
|
|
31
|
+
/**
|
|
32
|
+
* One entry in the link-attributes config list.
|
|
33
|
+
* `options`, `label`, and `helpText` are optional presentation hints.
|
|
34
|
+
* The React SDK translates the literal `class` key to `className` on output.
|
|
35
|
+
*/
|
|
36
|
+
interface LinkAttrConfig {
|
|
37
|
+
key: string;
|
|
38
|
+
/** When present, the editor renders a select; the value must be one of these. */
|
|
39
|
+
options?: Array<{
|
|
40
|
+
name: string;
|
|
41
|
+
value: string;
|
|
42
|
+
}>;
|
|
43
|
+
label?: string;
|
|
44
|
+
helpText?: string;
|
|
45
|
+
}
|
|
46
|
+
interface LinkAttributesConfiguration {
|
|
47
|
+
/** When true, the editor sees an Anchor input; the fragment lives on value.path. */
|
|
48
|
+
enableAnchor?: boolean;
|
|
49
|
+
/** Flat, ordered list of attribute entries a project wants on its links. */
|
|
50
|
+
entries?: Array<LinkAttrConfig>;
|
|
51
|
+
}
|
|
52
|
+
/** Normalize a stored entry for rendering — fills in a default label. */
|
|
53
|
+
declare function resolveLinkAttrConfig(entry: LinkAttrConfig): Required<Pick<LinkAttrConfig, 'key' | 'label'>> & Pick<LinkAttrConfig, 'options' | 'helpText'>;
|
|
31
54
|
interface RichTextParamConfiguration {
|
|
32
55
|
required?: boolean;
|
|
33
56
|
formatting?: {
|
|
@@ -40,6 +63,8 @@ interface RichTextParamConfiguration {
|
|
|
40
63
|
asset?: {
|
|
41
64
|
allowedTypes?: AssetDefinitionType[];
|
|
42
65
|
};
|
|
66
|
+
/** Optional anchor (fragment) toggle and custom attribute config for the link element. */
|
|
67
|
+
link?: LinkAttributesConfiguration;
|
|
43
68
|
};
|
|
44
69
|
}
|
|
45
70
|
|
|
@@ -65,12 +90,28 @@ type ProjectMapLinkParamValue = {
|
|
|
65
90
|
nodeId: string;
|
|
66
91
|
path: string;
|
|
67
92
|
dynamicInputValues?: Record<string, string>;
|
|
93
|
+
attributes?: Record<string, string>;
|
|
68
94
|
};
|
|
69
95
|
type NonProjectMapLinkParamValue = {
|
|
70
96
|
type: Exclude<LinkParameterType, 'projectMapNode'>;
|
|
71
97
|
path: string;
|
|
98
|
+
attributes?: Record<string, string>;
|
|
72
99
|
};
|
|
73
100
|
type LinkParamValue = ProjectMapLinkParamValue | NonProjectMapLinkParamValue | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* True for attribute keys the Uniform web SDKs strip at render time: inline event
|
|
103
|
+
* handlers (`on*`) and dangerous attribute names (href, src, style, formaction).
|
|
104
|
+
* Authoring UIs should use this to warn before such keys are ever persisted.
|
|
105
|
+
*/
|
|
106
|
+
declare function isDangerousLinkAttributeKey(key: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Converts a link parameter value into a plain HTML attribute bag suitable for
|
|
109
|
+
* an `<a>` element. Applies a web-channel security denylist (event handlers,
|
|
110
|
+
* script-executing URL schemes on the href and any value, dangerous attribute names) and auto-adds
|
|
111
|
+
* `rel="noopener noreferrer"` for any case variant of `target="_blank"`.
|
|
112
|
+
*/
|
|
113
|
+
declare function linkParamValueToHtmlAttributes(link: LinkParamValue): Record<string, string>;
|
|
114
|
+
/** Derives the href string from a link parameter value. */
|
|
74
115
|
declare function linkParamValueToHref(link: LinkParamValue): string | undefined;
|
|
75
116
|
|
|
76
117
|
interface ListNode extends RichTextNode {
|
|
@@ -223,4 +264,4 @@ declare const emptyRichTextValue: {
|
|
|
223
264
|
};
|
|
224
265
|
declare const defaultParameterConfiguration: RichTextParamConfiguration;
|
|
225
266
|
|
|
226
|
-
export { type AssetNode, type HeadingNode, type LinkNode, type ListItemNode, type ListNode, type NodeStringRenderer, type NodeStringRendererProps, type ParagraphNode, type ParameterRichTextValue, type ResolveStringRenderer, type RichTextBuiltInElement, type RichTextBuiltInFormat, type RichTextNode, type RichTextNodeWithChildren, type RichTextParamConfiguration, type StringRenderContext, type TableCellNode, type TableNode, type TableRowNode, type TextNode, assetHtmlRenderer, defaultParameterConfiguration, emptyRichTextValue, getLabelForElement, getLabelForFormat, getRichTextTagFromTableCellHeaderState, getRichTextTagsFromTextFormat, hasChildren, headingHtmlRenderer, isArrayWithLength, isPureDirection, isPureTextAlign, isRichTextNode, isRichTextNodeType, isRichTextValue, isRichTextValueConsideredEmpty, linkHtmlRenderer, linkParamValueToHref, listHtmlRenderer, listitemHtmlRenderer, paragraphHtmlRenderer, purifyText, renderChildrenToHtml, renderChildrenToText, renderHtmlElement, renderToHtml, renderToText, resolveDefaultRenderer$1 as resolveDefaultHtmlRenderer, resolveDefaultRenderer as resolveDefaultTextRenderer, richTextBuiltInElements, richTextBuiltInFormats, rootHtmlRenderer, tableHtmlRenderer, tablecellHtmlRenderer, tablerowHtmlRenderer, textHtmlRenderer, walkRichTextTree };
|
|
267
|
+
export { type AssetNode, type HeadingNode, type LinkAttrConfig, type LinkAttributesConfiguration, type LinkNode, type ListItemNode, type ListNode, type NodeStringRenderer, type NodeStringRendererProps, type ParagraphNode, type ParameterRichTextValue, type ResolveStringRenderer, type RichTextBuiltInElement, type RichTextBuiltInFormat, type RichTextNode, type RichTextNodeWithChildren, type RichTextParamConfiguration, type StringRenderContext, type TableCellNode, type TableNode, type TableRowNode, type TextNode, assetHtmlRenderer, defaultParameterConfiguration, emptyRichTextValue, getLabelForElement, getLabelForFormat, getRichTextTagFromTableCellHeaderState, getRichTextTagsFromTextFormat, hasChildren, headingHtmlRenderer, isArrayWithLength, isDangerousLinkAttributeKey, isPureDirection, isPureTextAlign, isRichTextNode, isRichTextNodeType, isRichTextValue, isRichTextValueConsideredEmpty, linkHtmlRenderer, linkParamValueToHref, linkParamValueToHtmlAttributes, listHtmlRenderer, listitemHtmlRenderer, paragraphHtmlRenderer, purifyText, renderChildrenToHtml, renderChildrenToText, renderHtmlElement, renderToHtml, renderToText, resolveDefaultRenderer$1 as resolveDefaultHtmlRenderer, resolveDefaultRenderer as resolveDefaultTextRenderer, resolveLinkAttrConfig, richTextBuiltInElements, richTextBuiltInFormats, rootHtmlRenderer, tableHtmlRenderer, tablecellHtmlRenderer, tablerowHtmlRenderer, textHtmlRenderer, walkRichTextTree };
|
package/dist/index.esm.js
CHANGED
|
@@ -29,7 +29,7 @@ var voidElements = /* @__PURE__ */ new Set([
|
|
|
29
29
|
"wbr"
|
|
30
30
|
]);
|
|
31
31
|
function renderHtmlElement(tag, attributes, children) {
|
|
32
|
-
const attributesString = attributes && attributes.size > 0 ? " " + Array.from(attributes).reduce((result, [key, value]) => [...result, `${key}="${value}"`], []).join(" ") : "";
|
|
32
|
+
const attributesString = attributes && attributes.size > 0 ? " " + Array.from(attributes).reduce((result, [key, value]) => [...result, `${key}="${purifyText(value)}"`], []).join(" ") : "";
|
|
33
33
|
return voidElements.has(tag) ? `<${tag}${attributesString}>` : `<${tag}${attributesString}>${children != null ? children : ""}</${tag}>`;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -99,13 +99,54 @@ var headingTextRenderer = ({ context, renderChildren }) => {
|
|
|
99
99
|
// src/nodes/link.ts
|
|
100
100
|
var linkHtmlRenderer = ({ context, renderChildren }) => {
|
|
101
101
|
const node = context.currentNode;
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
if (href) {
|
|
105
|
-
attributes.set("href", href);
|
|
106
|
-
}
|
|
102
|
+
const htmlAttrs = linkParamValueToHtmlAttributes(node.link);
|
|
103
|
+
const attributes = new Map(Object.entries(htmlAttrs));
|
|
107
104
|
return renderHtmlElement("a", attributes, renderChildren(context.currentNode.children));
|
|
108
105
|
};
|
|
106
|
+
var DENYLIST_EXACT = /* @__PURE__ */ new Set(["href", "src", "style", "formaction"]);
|
|
107
|
+
var EVENT_HANDLER_PATTERN = /^on/i;
|
|
108
|
+
var SAFE_ATTRIBUTE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9:_.-]*$/;
|
|
109
|
+
var DANGEROUS_SCHEME_PATTERN = /^(?:javascript|data|vbscript):/i;
|
|
110
|
+
function hasDangerousScheme(value) {
|
|
111
|
+
const normalized = value.replace(/[\u0000-\u0020\u007f-\u00a0]+/g, "").toLowerCase();
|
|
112
|
+
return DANGEROUS_SCHEME_PATTERN.test(normalized);
|
|
113
|
+
}
|
|
114
|
+
function isDangerousLinkAttributeKey(key) {
|
|
115
|
+
return EVENT_HANDLER_PATTERN.test(key) || DENYLIST_EXACT.has(key.toLowerCase());
|
|
116
|
+
}
|
|
117
|
+
function linkParamValueToHtmlAttributes(link) {
|
|
118
|
+
var _a;
|
|
119
|
+
const result = {};
|
|
120
|
+
const href = linkParamValueToHref(link);
|
|
121
|
+
if (href && !hasDangerousScheme(href)) {
|
|
122
|
+
result.href = href;
|
|
123
|
+
}
|
|
124
|
+
if (!(link == null ? void 0 : link.attributes)) {
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
for (const [key, value] of Object.entries(link.attributes)) {
|
|
128
|
+
if (!key || typeof value !== "string") {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!SAFE_ATTRIBUTE_NAME_PATTERN.test(key)) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (EVENT_HANDLER_PATTERN.test(key)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (DENYLIST_EXACT.has(key.toLowerCase())) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (hasDangerousScheme(value)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
result[key] = value;
|
|
144
|
+
}
|
|
145
|
+
if (((_a = result["target"]) == null ? void 0 : _a.toLowerCase()) === "_blank" && !("rel" in result)) {
|
|
146
|
+
result.rel = "noopener noreferrer";
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
109
150
|
function linkParamValueToHref(link) {
|
|
110
151
|
switch (link == null ? void 0 : link.type) {
|
|
111
152
|
case "email":
|
|
@@ -387,6 +428,17 @@ function renderChildrenToText(children, context) {
|
|
|
387
428
|
return Array.isArray(children) ? children.map((node) => renderToText(node, context)).join("") : "";
|
|
388
429
|
}
|
|
389
430
|
|
|
431
|
+
// src/types.ts
|
|
432
|
+
function resolveLinkAttrConfig(entry) {
|
|
433
|
+
var _a;
|
|
434
|
+
return {
|
|
435
|
+
key: entry.key,
|
|
436
|
+
options: entry.options,
|
|
437
|
+
label: (_a = entry.label) != null ? _a : entry.key,
|
|
438
|
+
helpText: entry.helpText
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
390
442
|
// src/utils.ts
|
|
391
443
|
var walkRichTextTree = (node, callback, parent) => {
|
|
392
444
|
if (!isRichTextNode(node)) {
|
|
@@ -548,6 +600,7 @@ export {
|
|
|
548
600
|
hasChildren,
|
|
549
601
|
headingHtmlRenderer,
|
|
550
602
|
isArrayWithLength,
|
|
603
|
+
isDangerousLinkAttributeKey,
|
|
551
604
|
isPureDirection,
|
|
552
605
|
isPureTextAlign,
|
|
553
606
|
isRichTextNode,
|
|
@@ -556,6 +609,7 @@ export {
|
|
|
556
609
|
isRichTextValueConsideredEmpty,
|
|
557
610
|
linkHtmlRenderer,
|
|
558
611
|
linkParamValueToHref,
|
|
612
|
+
linkParamValueToHtmlAttributes,
|
|
559
613
|
listHtmlRenderer,
|
|
560
614
|
listitemHtmlRenderer,
|
|
561
615
|
paragraphHtmlRenderer,
|
|
@@ -567,6 +621,7 @@ export {
|
|
|
567
621
|
renderToText,
|
|
568
622
|
resolveDefaultRenderer as resolveDefaultHtmlRenderer,
|
|
569
623
|
resolveDefaultRenderer2 as resolveDefaultTextRenderer,
|
|
624
|
+
resolveLinkAttrConfig,
|
|
570
625
|
richTextBuiltInElements,
|
|
571
626
|
richTextBuiltInFormats,
|
|
572
627
|
rootHtmlRenderer,
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ __export(index_exports, {
|
|
|
30
30
|
hasChildren: () => hasChildren,
|
|
31
31
|
headingHtmlRenderer: () => headingHtmlRenderer,
|
|
32
32
|
isArrayWithLength: () => isArrayWithLength,
|
|
33
|
+
isDangerousLinkAttributeKey: () => isDangerousLinkAttributeKey,
|
|
33
34
|
isPureDirection: () => isPureDirection,
|
|
34
35
|
isPureTextAlign: () => isPureTextAlign,
|
|
35
36
|
isRichTextNode: () => isRichTextNode,
|
|
@@ -38,6 +39,7 @@ __export(index_exports, {
|
|
|
38
39
|
isRichTextValueConsideredEmpty: () => isRichTextValueConsideredEmpty,
|
|
39
40
|
linkHtmlRenderer: () => linkHtmlRenderer,
|
|
40
41
|
linkParamValueToHref: () => linkParamValueToHref,
|
|
42
|
+
linkParamValueToHtmlAttributes: () => linkParamValueToHtmlAttributes,
|
|
41
43
|
listHtmlRenderer: () => listHtmlRenderer,
|
|
42
44
|
listitemHtmlRenderer: () => listitemHtmlRenderer,
|
|
43
45
|
paragraphHtmlRenderer: () => paragraphHtmlRenderer,
|
|
@@ -49,6 +51,7 @@ __export(index_exports, {
|
|
|
49
51
|
renderToText: () => renderToText,
|
|
50
52
|
resolveDefaultHtmlRenderer: () => resolveDefaultRenderer,
|
|
51
53
|
resolveDefaultTextRenderer: () => resolveDefaultRenderer2,
|
|
54
|
+
resolveLinkAttrConfig: () => resolveLinkAttrConfig,
|
|
52
55
|
richTextBuiltInElements: () => richTextBuiltInElements,
|
|
53
56
|
richTextBuiltInFormats: () => richTextBuiltInFormats,
|
|
54
57
|
rootHtmlRenderer: () => rootHtmlRenderer,
|
|
@@ -91,7 +94,7 @@ var voidElements = /* @__PURE__ */ new Set([
|
|
|
91
94
|
"wbr"
|
|
92
95
|
]);
|
|
93
96
|
function renderHtmlElement(tag, attributes, children) {
|
|
94
|
-
const attributesString = attributes && attributes.size > 0 ? " " + Array.from(attributes).reduce((result, [key, value]) => [...result, `${key}="${value}"`], []).join(" ") : "";
|
|
97
|
+
const attributesString = attributes && attributes.size > 0 ? " " + Array.from(attributes).reduce((result, [key, value]) => [...result, `${key}="${purifyText(value)}"`], []).join(" ") : "";
|
|
95
98
|
return voidElements.has(tag) ? `<${tag}${attributesString}>` : `<${tag}${attributesString}>${children != null ? children : ""}</${tag}>`;
|
|
96
99
|
}
|
|
97
100
|
|
|
@@ -161,13 +164,54 @@ var headingTextRenderer = ({ context, renderChildren }) => {
|
|
|
161
164
|
// src/nodes/link.ts
|
|
162
165
|
var linkHtmlRenderer = ({ context, renderChildren }) => {
|
|
163
166
|
const node = context.currentNode;
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
if (href) {
|
|
167
|
-
attributes.set("href", href);
|
|
168
|
-
}
|
|
167
|
+
const htmlAttrs = linkParamValueToHtmlAttributes(node.link);
|
|
168
|
+
const attributes = new Map(Object.entries(htmlAttrs));
|
|
169
169
|
return renderHtmlElement("a", attributes, renderChildren(context.currentNode.children));
|
|
170
170
|
};
|
|
171
|
+
var DENYLIST_EXACT = /* @__PURE__ */ new Set(["href", "src", "style", "formaction"]);
|
|
172
|
+
var EVENT_HANDLER_PATTERN = /^on/i;
|
|
173
|
+
var SAFE_ATTRIBUTE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9:_.-]*$/;
|
|
174
|
+
var DANGEROUS_SCHEME_PATTERN = /^(?:javascript|data|vbscript):/i;
|
|
175
|
+
function hasDangerousScheme(value) {
|
|
176
|
+
const normalized = value.replace(/[\u0000-\u0020\u007f-\u00a0]+/g, "").toLowerCase();
|
|
177
|
+
return DANGEROUS_SCHEME_PATTERN.test(normalized);
|
|
178
|
+
}
|
|
179
|
+
function isDangerousLinkAttributeKey(key) {
|
|
180
|
+
return EVENT_HANDLER_PATTERN.test(key) || DENYLIST_EXACT.has(key.toLowerCase());
|
|
181
|
+
}
|
|
182
|
+
function linkParamValueToHtmlAttributes(link) {
|
|
183
|
+
var _a;
|
|
184
|
+
const result = {};
|
|
185
|
+
const href = linkParamValueToHref(link);
|
|
186
|
+
if (href && !hasDangerousScheme(href)) {
|
|
187
|
+
result.href = href;
|
|
188
|
+
}
|
|
189
|
+
if (!(link == null ? void 0 : link.attributes)) {
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
for (const [key, value] of Object.entries(link.attributes)) {
|
|
193
|
+
if (!key || typeof value !== "string") {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (!SAFE_ATTRIBUTE_NAME_PATTERN.test(key)) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (EVENT_HANDLER_PATTERN.test(key)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (DENYLIST_EXACT.has(key.toLowerCase())) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (hasDangerousScheme(value)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
result[key] = value;
|
|
209
|
+
}
|
|
210
|
+
if (((_a = result["target"]) == null ? void 0 : _a.toLowerCase()) === "_blank" && !("rel" in result)) {
|
|
211
|
+
result.rel = "noopener noreferrer";
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
171
215
|
function linkParamValueToHref(link) {
|
|
172
216
|
switch (link == null ? void 0 : link.type) {
|
|
173
217
|
case "email":
|
|
@@ -449,6 +493,17 @@ function renderChildrenToText(children, context) {
|
|
|
449
493
|
return Array.isArray(children) ? children.map((node) => renderToText(node, context)).join("") : "";
|
|
450
494
|
}
|
|
451
495
|
|
|
496
|
+
// src/types.ts
|
|
497
|
+
function resolveLinkAttrConfig(entry) {
|
|
498
|
+
var _a;
|
|
499
|
+
return {
|
|
500
|
+
key: entry.key,
|
|
501
|
+
options: entry.options,
|
|
502
|
+
label: (_a = entry.label) != null ? _a : entry.key,
|
|
503
|
+
helpText: entry.helpText
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
452
507
|
// src/utils.ts
|
|
453
508
|
var walkRichTextTree = (node, callback, parent) => {
|
|
454
509
|
if (!isRichTextNode(node)) {
|
|
@@ -611,6 +666,7 @@ var defaultParameterConfiguration = {
|
|
|
611
666
|
hasChildren,
|
|
612
667
|
headingHtmlRenderer,
|
|
613
668
|
isArrayWithLength,
|
|
669
|
+
isDangerousLinkAttributeKey,
|
|
614
670
|
isPureDirection,
|
|
615
671
|
isPureTextAlign,
|
|
616
672
|
isRichTextNode,
|
|
@@ -619,6 +675,7 @@ var defaultParameterConfiguration = {
|
|
|
619
675
|
isRichTextValueConsideredEmpty,
|
|
620
676
|
linkHtmlRenderer,
|
|
621
677
|
linkParamValueToHref,
|
|
678
|
+
linkParamValueToHtmlAttributes,
|
|
622
679
|
listHtmlRenderer,
|
|
623
680
|
listitemHtmlRenderer,
|
|
624
681
|
paragraphHtmlRenderer,
|
|
@@ -630,6 +687,7 @@ var defaultParameterConfiguration = {
|
|
|
630
687
|
renderToText,
|
|
631
688
|
resolveDefaultHtmlRenderer,
|
|
632
689
|
resolveDefaultTextRenderer,
|
|
690
|
+
resolveLinkAttrConfig,
|
|
633
691
|
richTextBuiltInElements,
|
|
634
692
|
richTextBuiltInFormats,
|
|
635
693
|
rootHtmlRenderer,
|
package/dist/index.mjs
CHANGED
|
@@ -29,7 +29,7 @@ var voidElements = /* @__PURE__ */ new Set([
|
|
|
29
29
|
"wbr"
|
|
30
30
|
]);
|
|
31
31
|
function renderHtmlElement(tag, attributes, children) {
|
|
32
|
-
const attributesString = attributes && attributes.size > 0 ? " " + Array.from(attributes).reduce((result, [key, value]) => [...result, `${key}="${value}"`], []).join(" ") : "";
|
|
32
|
+
const attributesString = attributes && attributes.size > 0 ? " " + Array.from(attributes).reduce((result, [key, value]) => [...result, `${key}="${purifyText(value)}"`], []).join(" ") : "";
|
|
33
33
|
return voidElements.has(tag) ? `<${tag}${attributesString}>` : `<${tag}${attributesString}>${children != null ? children : ""}</${tag}>`;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -99,13 +99,54 @@ var headingTextRenderer = ({ context, renderChildren }) => {
|
|
|
99
99
|
// src/nodes/link.ts
|
|
100
100
|
var linkHtmlRenderer = ({ context, renderChildren }) => {
|
|
101
101
|
const node = context.currentNode;
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
if (href) {
|
|
105
|
-
attributes.set("href", href);
|
|
106
|
-
}
|
|
102
|
+
const htmlAttrs = linkParamValueToHtmlAttributes(node.link);
|
|
103
|
+
const attributes = new Map(Object.entries(htmlAttrs));
|
|
107
104
|
return renderHtmlElement("a", attributes, renderChildren(context.currentNode.children));
|
|
108
105
|
};
|
|
106
|
+
var DENYLIST_EXACT = /* @__PURE__ */ new Set(["href", "src", "style", "formaction"]);
|
|
107
|
+
var EVENT_HANDLER_PATTERN = /^on/i;
|
|
108
|
+
var SAFE_ATTRIBUTE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9:_.-]*$/;
|
|
109
|
+
var DANGEROUS_SCHEME_PATTERN = /^(?:javascript|data|vbscript):/i;
|
|
110
|
+
function hasDangerousScheme(value) {
|
|
111
|
+
const normalized = value.replace(/[\u0000-\u0020\u007f-\u00a0]+/g, "").toLowerCase();
|
|
112
|
+
return DANGEROUS_SCHEME_PATTERN.test(normalized);
|
|
113
|
+
}
|
|
114
|
+
function isDangerousLinkAttributeKey(key) {
|
|
115
|
+
return EVENT_HANDLER_PATTERN.test(key) || DENYLIST_EXACT.has(key.toLowerCase());
|
|
116
|
+
}
|
|
117
|
+
function linkParamValueToHtmlAttributes(link) {
|
|
118
|
+
var _a;
|
|
119
|
+
const result = {};
|
|
120
|
+
const href = linkParamValueToHref(link);
|
|
121
|
+
if (href && !hasDangerousScheme(href)) {
|
|
122
|
+
result.href = href;
|
|
123
|
+
}
|
|
124
|
+
if (!(link == null ? void 0 : link.attributes)) {
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
for (const [key, value] of Object.entries(link.attributes)) {
|
|
128
|
+
if (!key || typeof value !== "string") {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!SAFE_ATTRIBUTE_NAME_PATTERN.test(key)) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (EVENT_HANDLER_PATTERN.test(key)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (DENYLIST_EXACT.has(key.toLowerCase())) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (hasDangerousScheme(value)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
result[key] = value;
|
|
144
|
+
}
|
|
145
|
+
if (((_a = result["target"]) == null ? void 0 : _a.toLowerCase()) === "_blank" && !("rel" in result)) {
|
|
146
|
+
result.rel = "noopener noreferrer";
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
109
150
|
function linkParamValueToHref(link) {
|
|
110
151
|
switch (link == null ? void 0 : link.type) {
|
|
111
152
|
case "email":
|
|
@@ -387,6 +428,17 @@ function renderChildrenToText(children, context) {
|
|
|
387
428
|
return Array.isArray(children) ? children.map((node) => renderToText(node, context)).join("") : "";
|
|
388
429
|
}
|
|
389
430
|
|
|
431
|
+
// src/types.ts
|
|
432
|
+
function resolveLinkAttrConfig(entry) {
|
|
433
|
+
var _a;
|
|
434
|
+
return {
|
|
435
|
+
key: entry.key,
|
|
436
|
+
options: entry.options,
|
|
437
|
+
label: (_a = entry.label) != null ? _a : entry.key,
|
|
438
|
+
helpText: entry.helpText
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
390
442
|
// src/utils.ts
|
|
391
443
|
var walkRichTextTree = (node, callback, parent) => {
|
|
392
444
|
if (!isRichTextNode(node)) {
|
|
@@ -548,6 +600,7 @@ export {
|
|
|
548
600
|
hasChildren,
|
|
549
601
|
headingHtmlRenderer,
|
|
550
602
|
isArrayWithLength,
|
|
603
|
+
isDangerousLinkAttributeKey,
|
|
551
604
|
isPureDirection,
|
|
552
605
|
isPureTextAlign,
|
|
553
606
|
isRichTextNode,
|
|
@@ -556,6 +609,7 @@ export {
|
|
|
556
609
|
isRichTextValueConsideredEmpty,
|
|
557
610
|
linkHtmlRenderer,
|
|
558
611
|
linkParamValueToHref,
|
|
612
|
+
linkParamValueToHtmlAttributes,
|
|
559
613
|
listHtmlRenderer,
|
|
560
614
|
listitemHtmlRenderer,
|
|
561
615
|
paragraphHtmlRenderer,
|
|
@@ -567,6 +621,7 @@ export {
|
|
|
567
621
|
renderToText,
|
|
568
622
|
resolveDefaultRenderer as resolveDefaultHtmlRenderer,
|
|
569
623
|
resolveDefaultRenderer2 as resolveDefaultTextRenderer,
|
|
624
|
+
resolveLinkAttrConfig,
|
|
570
625
|
richTextBuiltInElements,
|
|
571
626
|
richTextBuiltInFormats,
|
|
572
627
|
rootHtmlRenderer,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniformdev/richtext",
|
|
3
|
-
"version": "20.72.2
|
|
3
|
+
"version": "20.72.2",
|
|
4
4
|
"description": "Common functionality and types for Uniform Rich Text parameters",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@lexical/link": "0.45.0",
|
|
45
45
|
"@lexical/list": "0.45.0",
|
|
46
46
|
"@lexical/rich-text": "0.45.0",
|
|
47
|
-
"@uniformdev/assets": "20.72.2
|
|
47
|
+
"@uniformdev/assets": "20.72.2",
|
|
48
48
|
"lexical": "0.45.0"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"publishConfig": {
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "8b65535fb3ec81e23f9eb8e0ed0b0be478f6e0e8"
|
|
57
57
|
}
|