88const { generateFooterWithMessages, getDetectionCautionAlert, generateXMLMarker } = require ( "./messages_footer.cjs" ) ;
99const { generateWorkflowCallIdMarker, matchesWorkflowId } = require ( "./generate_footer.cjs" ) ;
1010const { getRepositoryUrl } = require ( "./get_repository_url.cjs" ) ;
11- const { replaceTemporaryIdReferences, loadTemporaryIdMapFromResolved , resolveRepoIssueTarget } = require ( "./temporary_id.cjs" ) ;
11+ const { replaceTemporaryIdReferences, resolveSafeOutputIssueTarget } = require ( "./temporary_id.cjs" ) ;
1212const { getTrackerID } = require ( "./get_tracker_id.cjs" ) ;
1313const { getErrorMessage } = require ( "./error_helpers.cjs" ) ;
1414const { parseBoolTemplatable } = require ( "./templatable.cjs" ) ;
@@ -31,6 +31,21 @@ const { resolveInvocationContext } = require("./invocation_context_helpers.cjs")
3131/** @type {string } Safe output type handled by this module */
3232const HANDLER_TYPE = "add_comment" ;
3333
34+ /**
35+ * Deduplicate an array of strings using case-insensitive comparison, preserving original casing and order.
36+ * @param {string[] } aliases
37+ * @returns {string[] }
38+ */
39+ function deduplicateCaseInsensitive ( aliases ) {
40+ const seen = new Set ( ) ;
41+ return aliases . filter ( alias => {
42+ const key = alias . toLowerCase ( ) ;
43+ if ( seen . has ( key ) ) return false ;
44+ seen . add ( key ) ;
45+ return true ;
46+ } ) ;
47+ }
48+
3449/**
3550 * Resolve effective event name/payload for native and forwarded contexts.
3651 * Supports:
@@ -448,33 +463,11 @@ async function main(config = {}) {
448463 // Check if item_number or issue_number was explicitly provided in the message.
449464 // item_number takes precedence over issue_number when both are present.
450465 // pr-number is accepted as an alias for item_number for robustness.
451- const explicitItemNumber = message . item_number ?? message . issue_number ?? message [ "pr-number" ] ?? undefined ;
452-
453- if ( explicitItemNumber !== undefined ) {
454- // Resolve temporary IDs if present
455- const resolvedTarget = resolveRepoIssueTarget ( explicitItemNumber , temporaryIdMap , repoParts . owner , repoParts . repo ) ;
456-
457- // Check if this is an unresolved temporary ID
458- if ( resolvedTarget . wasTemporaryId && ! resolvedTarget . resolved ) {
459- core . info ( `Deferring add_comment: unresolved temporary ID (${ explicitItemNumber } )` ) ;
460- return {
461- success : false ,
462- deferred : true ,
463- error : resolvedTarget . errorMessage || `Unresolved temporary ID: ${ explicitItemNumber } ` ,
464- } ;
465- }
466+ const itemTargetResult = resolveSafeOutputIssueTarget ( { message, tempIdMap : temporaryIdMap , repoParts, handlerType : HANDLER_TYPE , aliases : [ "item_number" , "issue_number" , "pr-number" ] } ) ;
467+ if ( ! itemTargetResult . success ) return itemTargetResult ;
466468
467- // Check for other resolution errors (including null resolved)
468- if ( resolvedTarget . errorMessage || ! resolvedTarget . resolved ) {
469- core . warning ( `Invalid explicit target number specified: ${ explicitItemNumber } ` ) ;
470- return {
471- success : false ,
472- error : `Invalid explicit target number specified: ${ explicitItemNumber } ` ,
473- } ;
474- }
475-
476- // Use the resolved issue number (safe to access because we checked above)
477- itemNumber = resolvedTarget . resolved . number ;
469+ if ( itemTargetResult . number !== null ) {
470+ itemNumber = itemTargetResult . number ;
478471 core . info ( `Using explicitly provided target number (item_number/issue_number/pr-number): #${ itemNumber } ` ) ;
479472 } else {
480473 // Check if this is a discussion context
@@ -536,7 +529,7 @@ async function main(config = {}) {
536529 const parentAuthors = [ ] ;
537530 if ( ! mentionsDisabled ) {
538531 if ( ! isDiscussion ) {
539- if ( explicitItemNumber !== undefined ) {
532+ if ( itemTargetResult . number !== null ) {
540533 // Explicit item_number/issue_number: fetch the issue/PR to get its author
541534 try {
542535 const { data : issueData } = await githubClient . rest . issues . get ( {
@@ -566,24 +559,7 @@ async function main(config = {}) {
566559 }
567560 }
568561 }
569- const allowedMentionAliases = [ ] ;
570- const seenAllowedMentionAliases = new Set ( ) ;
571- for ( const alias of parentAuthors ) {
572- const key = alias . toLowerCase ( ) ;
573- if ( seenAllowedMentionAliases . has ( key ) ) {
574- continue ;
575- }
576- seenAllowedMentionAliases . add ( key ) ;
577- allowedMentionAliases . push ( alias ) ;
578- }
579- for ( const alias of configuredMentionAliases ) {
580- const key = alias . toLowerCase ( ) ;
581- if ( seenAllowedMentionAliases . has ( key ) ) {
582- continue ;
583- }
584- seenAllowedMentionAliases . add ( key ) ;
585- allowedMentionAliases . push ( alias ) ;
586- }
562+ const allowedMentionAliases = deduplicateCaseInsensitive ( [ ...parentAuthors , ...configuredMentionAliases ] ) ;
587563
588564 if ( allowedMentionAliases . length > 0 ) {
589565 core . info ( `[MENTIONS] Allowing aliases in comment: ${ allowedMentionAliases . join ( ", " ) } ` ) ;
@@ -722,7 +698,7 @@ async function main(config = {}) {
722698 // reply as a threaded comment to the triggering comment instead of posting top-level.
723699 // GitHub Discussions only supports two nesting levels, so if the triggering comment is
724700 // itself a reply, we resolve the top-level parent's node ID to use as replyToId.
725- const hasExplicitItemNumber = explicitItemNumber !== undefined ;
701+ const hasExplicitItemNumber = itemTargetResult . number !== null ;
726702 let replyToId ;
727703 if ( context . eventName === "discussion_comment" && ! hasExplicitItemNumber ) {
728704 // When triggered by a discussion_comment event, thread the reply under the triggering comment.
@@ -740,7 +716,7 @@ async function main(config = {}) {
740716 }
741717 comment = await commentOnDiscussion ( githubClient , repoParts . owner , repoParts . repo , itemNumber , processedBody , replyToId ) ;
742718 } else {
743- const shouldReplyToTriggeringPRReviewComment = effectiveContext . eventName === "pull_request_review_comment" && explicitItemNumber === undefined ;
719+ const shouldReplyToTriggeringPRReviewComment = effectiveContext . eventName === "pull_request_review_comment" && itemTargetResult . number === null ;
744720 const triggeringReviewCommentId = Number ( effectiveContext . payload ?. comment ?. id ) ;
745721
746722 if ( shouldReplyToTriggeringPRReviewComment && Number . isInteger ( triggeringReviewCommentId ) && triggeringReviewCommentId > 0 ) {
@@ -778,7 +754,7 @@ async function main(config = {}) {
778754
779755 // If 404 and item_number was explicitly provided and we tried as issue/PR,
780756 // retry as a discussion (the user may have provided a discussion number)
781- if ( is404 && ! isDiscussion && explicitItemNumber !== undefined ) {
757+ if ( is404 && ! isDiscussion && itemTargetResult . number !== null ) {
782758 core . info ( `Item #${ itemNumber } not found as issue/PR, retrying as discussion...` ) ;
783759
784760 try {
0 commit comments