@@ -13,8 +13,66 @@ const booleanOptions = [
1313 { label : 'True' , value : 'true' } ,
1414] ;
1515
16+ /**
17+ * Encodes a value to be safe for use in CSS selectors (data-key attributes).
18+ * Special characters like quotes, brackets, etc. can break querySelector,
19+ * so we encode them using base64.
20+ */
21+ export function encodeSafeSelectorValue ( value : string | number ) : string | number {
22+ // Numbers are safe to use as-is
23+ if ( typeof value === 'number' ) {
24+ return value ;
25+ }
26+ // Check if the value contains characters that would break CSS selectors
27+ // This includes quotes, brackets, backslashes, etc.
28+ const hasSpecialChars = / [ " ' \[ \] \\ ( ) { } ] / . test ( value ) ;
29+ if ( ! hasSpecialChars ) {
30+ return value ;
31+ }
32+ // Encode to base64 to make it safe for CSS selectors
33+ // We prefix with 'b64:' so we can decode it later if needed
34+ try {
35+ return 'b64:' + btoa ( value ) ;
36+ } catch ( e ) {
37+ // If btoa fails (e.g., with unicode), fallback to encodeURIComponent
38+ return 'enc:' + encodeURIComponent ( value ) ;
39+ }
40+ }
41+
42+ /**
43+ * Decodes a value that was encoded by encodeSafeSelectorValue
44+ */
45+ export function decodeSafeSelectorValue ( value : string | number ) : string | number {
46+ if ( typeof value === 'number' ) {
47+ return value ;
48+ }
49+
50+ if ( value . startsWith ( 'b64:' ) ) {
51+ try {
52+ return atob ( value . substring ( 4 ) ) ;
53+ } catch ( e ) {
54+ return value ;
55+ }
56+ }
57+ if ( value . startsWith ( 'enc:' ) ) {
58+ try {
59+ return decodeURIComponent ( value . substring ( 4 ) ) ;
60+ } catch ( e ) {
61+ return value ;
62+ }
63+ }
64+ return value ;
65+ }
66+
1667function enumOptions ( enumValues : JSONSchema7Type [ ] , required ?: boolean ) {
17- const options = map ( enumValues , v => ( { value : typeof v === 'number' ? v : String ( v ) } ) ) ;
68+ const options = map ( enumValues , v => {
69+ // Handle objects and arrays by stringifying them
70+ const stringValue =
71+ typeof v === 'object' && v !== null ? safeStringify ( v ) ?? String ( v ) : typeof v === 'number' ? v : String ( v ) ;
72+ // Encode the value to be safe for CSS selectors, but keep the original label
73+ const safeValue = encodeSafeSelectorValue ( stringValue ) ;
74+ return { value : safeValue , label : String ( stringValue ) } ;
75+ } ) ;
1876 return required ? options : [ { label : 'Not Set' , value : '' } , ...options ] ;
1977}
2078
@@ -32,7 +90,10 @@ export function exampleOptions(parameter: ParameterSpec) {
3290 return parameter . examples ?. length && parameter . examples . length > 1
3391 ? [
3492 selectExampleOption ,
35- ...parameter . examples . map ( example => ( { label : example . key , value : exampleValue ( example ) } ) ) ,
93+ ...parameter . examples . map ( example => ( {
94+ label : example . key ,
95+ value : encodeSafeSelectorValue ( exampleValue ( example ) ) ,
96+ } ) ) ,
3697 ]
3798 : null ;
3899}
@@ -48,18 +109,21 @@ export function parameterSupportsFileUpload(parameter?: Pick<ParameterSpec, 'sch
48109}
49110
50111function stringifyValue ( value : unknown ) {
51- return typeof value === 'object' ? JSON . stringify ( value ) : escapeQuotes ( String ( value ) ) ;
52- }
53-
54- function exampleValue ( example : Omit < INodeExample , 'id' > | Omit < INodeExternalExample , 'id' > ) {
55- const value = 'value' in example ? example . value : example . externalValue ;
56- return stringifyValue ( value ) ;
112+ if ( typeof value === 'object' && value !== null ) {
113+ return safeStringify ( value ) ?? String ( value ) ;
114+ }
115+ return String ( value ) ;
57116}
58117
59118function escapeQuotes ( value : string ) {
60119 return value . replace ( / " / g, '\\"' ) ;
61120}
62121
122+ function exampleValue ( example : Omit < INodeExample , 'id' > | Omit < INodeExternalExample , 'id' > ) {
123+ const value = 'value' in example ? example . value : example . externalValue ;
124+ return escapeQuotes ( stringifyValue ( value ) ) ;
125+ }
126+
63127export function getPlaceholderForParameter ( parameter : ParameterSpec ) {
64128 const { value : parameterValue , isDefault } = getValueForParameter ( parameter ) ;
65129
@@ -89,16 +153,18 @@ const getValueForParameter = (parameter: ParameterSpec) => {
89153 return { value : stringifyValue ( defaultValue ) , isDefault : true } ;
90154 }
91155
92- const examples = parameter . examples ?? [ ] ;
93- if ( examples . length > 0 ) {
94- return { value : exampleValue ( examples [ 0 ] ) } ;
95- }
96-
156+ // If the parameter has enums, prioritize using the first enum value
157+ // over examples, as examples might not match the enum values
97158 const enums = parameter . schema ?. enum ?? [ ] ;
98159 if ( enums . length > 0 ) {
99160 return { value : stringifyValue ( enums [ 0 ] ) } ;
100161 }
101162
163+ const examples = parameter . examples ?? [ ] ;
164+ if ( examples . length > 0 ) {
165+ return { value : exampleValue ( examples [ 0 ] ) } ;
166+ }
167+
102168 return { value : '' } ;
103169} ;
104170
0 commit comments