@gfargo/doorman 3.0.2 → 3.0.4

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/bin/run.js CHANGED
@@ -1,71 +1,71 @@
1
- "use strict";var no=Object.create;var Xe=Object.defineProperty;var ro=Object.getOwnPropertyDescriptor;var so=Object.getOwnPropertyNames;var ao=Object.getPrototypeOf,lo=Object.prototype.hasOwnProperty;var We=(n,e)=>()=>(n&&(e=n(n=0)),e);var Z=(n,e)=>{for(var t in e)Xe(n,t,{get:e[t],enumerable:!0})},Xt=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of so(e))!lo.call(n,r)&&r!==t&&Xe(n,r,{get:()=>e[r],enumerable:!(i=ro(e,r))||i.enumerable});return n};var S=(n,e,t)=>(t=n!=null?no(ao(n)):{},Xt(e||!n||!n.__esModule?Xe(t,"default",{value:n,enumerable:!0}):t,n)),fe=n=>Xt(Xe({},"__esModule",{value:!0}),n);var Qt,o,k=We(()=>{"use strict";Qt=require("consola"),o=(0,Qt.createConsola)({formatOptions:{colors:!0,compact:!0,date:!1}})});var ei,C,ce=We(()=>{"use strict";ei=S(require("consola")),C=async(n,e)=>{let t=await ei.default.prompt(n,e);return t!=null&&t.toString()==="Symbol(clack:cancel)"&&process.exit(0),(!e?.type||e.type==="text")&&t===void 0?"":t}});var vi={};Z(vi,{promptForCredentials:()=>Et});async function Et(n){let e=n.token||process.env.VERCEL_TOKEN||await C("What is your Vercel API Auth Token? (https://vercel.com/guides/how-do-i-use-a-vercel-api-access-token#creating-an-access-token)",{type:"text"}),t=n.teamId||process.env.VERCEL_TEAM_ID||await C("What is your Vercel Team ID? (See https://vercel.com/docs/accounts/create-a-team#find-your-team-id)",{type:"text"}),i=n.projectId||process.env.VERCEL_PROJECT_ID||await C("What is your Vercel Project ID? (See https://vercel.com/docs/projects/project-configuration/general-settings#project-id)",{type:"text"});return{token:e,teamId:t,projectId:i}}var $t=We(()=>{"use strict";ce()});var me={};Z(me,{TranslationWarningSystem:()=>ot});var ot,pe=We(()=>{"use strict";ot=class n{static DOCS_BASE_URL="https://docs.doorman.griffen.codes";static WARNING_CONFIGS={managed_rules:{category:"feature_unsupported",severity:"critical",explanation:"Managed rules are provider-specific security rules that cannot be directly translated between platforms.",suggestion:"Replace with custom rules using equivalent conditions and actions.",alternativeApproach:"Create custom rules that match the same traffic patterns using standard conditions like IP, country, user agent, or request headers.",impact:"Security coverage may be reduced until equivalent custom rules are implemented.",docsUrl:`${n.DOCS_BASE_URL}/migration/managed-rules`},bot_management:{category:"feature_unsupported",severity:"critical",explanation:"Bot management features use provider-specific detection algorithms that cannot be translated.",suggestion:"Configure bot management separately in the target provider.",alternativeApproach:"Use user agent filtering, rate limiting, and challenge actions to implement basic bot protection.",impact:"Bot protection effectiveness may vary between providers.",docsUrl:`${n.DOCS_BASE_URL}/migration/bot-management`},complex_expressions:{category:"lossy_conversion",severity:"warning",explanation:"Complex expressions may not translate perfectly due to differences in syntax and supported operators between providers.",suggestion:"Review translated expressions and test thoroughly before deploying.",alternativeApproach:"Break complex expressions into simpler, more portable conditions.",impact:"Rule behavior may differ slightly from the original configuration.",docsUrl:`${n.DOCS_BASE_URL}/migration/expressions`},geo_blocking:{category:"lossy_conversion",severity:"warning",explanation:"Geographic blocking may use different country codes or geographic databases between providers.",suggestion:"Verify country codes and geographic targeting after translation.",alternativeApproach:"Use ISO country codes when possible for better compatibility.",impact:"Some geographic regions may be blocked or allowed differently than intended.",docsUrl:`${n.DOCS_BASE_URL}/migration/geo-blocking`},rate_limiting_precision:{category:"lossy_conversion",severity:"info",explanation:"Rate limiting configurations may be adjusted to match the target provider's supported time windows and request thresholds.",suggestion:"Review rate limiting settings and adjust if necessary.",alternativeApproach:"Use the closest supported time window and request threshold values.",impact:"Rate limiting may be slightly more or less restrictive than the original configuration.",docsUrl:`${n.DOCS_BASE_URL}/migration/rate-limiting`},regex_patterns:{category:"syntax_limitation",severity:"warning",explanation:"Regular expression patterns may have different syntax requirements or feature support between providers.",suggestion:"Test regex patterns in the target provider and adjust syntax if needed.",alternativeApproach:"Use simpler string matching operations (contains, starts_with, ends_with) when possible.",impact:"Pattern matching may behave differently or fail to match intended traffic.",docsUrl:`${n.DOCS_BASE_URL}/migration/regex-patterns`},header_manipulation:{category:"syntax_limitation",severity:"warning",explanation:"Header manipulation capabilities vary between providers in terms of supported headers and modification types.",suggestion:"Verify header modification support in the target provider.",alternativeApproach:"Use redirect rules or custom error pages to achieve similar functionality.",impact:"Header modifications may not work as expected or may be ignored.",docsUrl:`${n.DOCS_BASE_URL}/migration/headers`},large_ip_lists:{category:"performance_impact",severity:"info",explanation:"Large IP lists may impact rule evaluation performance differently between providers.",suggestion:"Consider using provider-specific IP list features for better performance.",alternativeApproach:"Break large IP lists into smaller, more targeted rules or use CIDR notation to reduce list size.",impact:"Rule evaluation may be slower with large IP lists, potentially affecting site performance.",docsUrl:`${n.DOCS_BASE_URL}/migration/ip-lists`},many_conditions:{category:"performance_impact",severity:"info",explanation:"Rules with many conditions may have different performance characteristics between providers.",suggestion:"Monitor rule performance and consider optimizing complex rules.",alternativeApproach:"Split complex rules into multiple simpler rules or use more efficient condition types.",impact:"Complex rules may impact request processing speed.",docsUrl:`${n.DOCS_BASE_URL}/migration/rule-optimization`},action_differences:{category:"security_consideration",severity:"warning",explanation:"Security actions (block, challenge, etc.) may behave differently between providers in terms of user experience and effectiveness.",suggestion:"Test security actions thoroughly and adjust based on provider capabilities.",alternativeApproach:"Use the most appropriate equivalent action available in the target provider.",impact:"Security effectiveness and user experience may differ from the original configuration.",docsUrl:`${n.DOCS_BASE_URL}/migration/security-actions`},challenge_types:{category:"security_consideration",severity:"info",explanation:"Challenge types (CAPTCHA, JavaScript, etc.) vary between providers and may affect user experience.",suggestion:"Choose challenge types that balance security and user experience for your use case.",alternativeApproach:"Use the provider's recommended challenge type for your security requirements.",impact:"User experience during security challenges may vary.",docsUrl:`${n.DOCS_BASE_URL}/migration/challenges`},version_compatibility:{category:"compatibility_issue",severity:"warning",explanation:"Some features may only be available in specific provider plan tiers or API versions.",suggestion:"Verify feature availability in your target provider plan.",alternativeApproach:"Use alternative features available in your plan tier.",impact:"Some rules may not work as expected if required features are unavailable.",docsUrl:`${n.DOCS_BASE_URL}/migration/compatibility`},plan_limitations:{category:"compatibility_issue",severity:"critical",explanation:"Your current plan may not support all features being translated.",suggestion:"Upgrade your plan or modify rules to use available features.",alternativeApproach:"Implement equivalent functionality using features available in your current plan.",impact:"Rules may be rejected or ignored if they use unavailable features.",docsUrl:`${n.DOCS_BASE_URL}/migration/plan-limits`}};static createWarning(e,t,i,r,s){let a=this.WARNING_CONFIGS[e];return a?{rule:t,field:i,category:a.category,message:r||`${e.replace(/_/g," ")} translation issue`,explanation:a.explanation,severity:a.severity,suggestion:s||a.suggestion,alternativeApproach:a.alternativeApproach,impact:a.impact,docsUrl:a.docsUrl}:{rule:t,field:i,category:"compatibility_issue",message:r||`Unknown translation issue: ${e}`,explanation:"This translation scenario is not fully documented.",severity:"warning",suggestion:s||"Review the translated configuration manually and test thoroughly.",docsUrl:`${this.DOCS_BASE_URL}/migration/troubleshooting`}}static createUnsupportedFeatureWarning(e,t,i,r,s){return{rule:r,field:s,category:"feature_unsupported",message:`${e} is not supported in ${i}`,explanation:`The ${e} feature from ${t} cannot be directly translated to ${i} due to platform differences.`,severity:"critical",suggestion:`Remove or replace ${e} with equivalent functionality supported by ${i}.`,alternativeApproach:`Check ${i} documentation for similar features or use custom rules to achieve equivalent functionality.`,impact:"This rule may not work as expected or may be ignored entirely.",docsUrl:`${this.DOCS_BASE_URL}/migration/unsupported-features`}}static createLossyConversionWarning(e,t,i,r){return{rule:i,field:r,category:"lossy_conversion",message:`${e} translation may be lossy`,explanation:`The ${e} configuration cannot be perfectly translated: ${t}`,severity:"warning",suggestion:"Review the translated configuration and test thoroughly to ensure it behaves as expected.",alternativeApproach:"Consider simplifying the configuration or using more portable alternatives.",impact:"The translated rule may behave differently than the original.",docsUrl:`${this.DOCS_BASE_URL}/migration/lossy-conversions`}}static createSyntaxLimitationWarning(e,t,i,r){return{rule:i,field:r,category:"syntax_limitation",message:`${e} has syntax limitations`,explanation:`The ${e} syntax has been modified due to provider limitations: ${t}`,severity:"warning",suggestion:"Verify the translated syntax works correctly in the target provider.",alternativeApproach:"Use simpler syntax or alternative approaches that are more widely supported.",impact:"The rule may not match traffic as precisely as intended.",docsUrl:`${this.DOCS_BASE_URL}/migration/syntax-limitations`}}static createPerformanceWarning(e,t,i,r){return{rule:i,field:r,category:"performance_impact",message:`${e} may impact performance`,explanation:`The ${e} configuration may have performance implications: ${t}`,severity:"info",suggestion:"Monitor performance after deployment and optimize if necessary.",alternativeApproach:"Consider using more efficient alternatives or breaking complex rules into simpler ones.",impact:"Request processing may be slower than expected.",docsUrl:`${this.DOCS_BASE_URL}/migration/performance`}}static formatWarning(e){let i={critical:"\u{1F6A8}",warning:"\u26A0\uFE0F",info:"\u2139\uFE0F"}[e.severity],r=e.rule?` (Rule: ${e.rule}${e.field?`, Field: ${e.field}`:""})`:"",s=`${i} ${e.severity.toUpperCase()}: ${e.message}${r}
1
+ "use strict";var no=Object.create;var Xe=Object.defineProperty;var ro=Object.getOwnPropertyDescriptor;var so=Object.getOwnPropertyNames;var ao=Object.getPrototypeOf,lo=Object.prototype.hasOwnProperty;var We=(n,e)=>()=>(n&&(e=n(n=0)),e);var Z=(n,e)=>{for(var t in e)Xe(n,t,{get:e[t],enumerable:!0})},Qt=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of so(e))!lo.call(n,r)&&r!==t&&Xe(n,r,{get:()=>e[r],enumerable:!(i=ro(e,r))||i.enumerable});return n};var S=(n,e,t)=>(t=n!=null?no(ao(n)):{},Qt(e||!n||!n.__esModule?Xe(t,"default",{value:n,enumerable:!0}):t,n)),fe=n=>Qt(Xe({},"__esModule",{value:!0}),n);var Yt,o,k=We(()=>{"use strict";Yt=require("consola"),o=(0,Yt.createConsola)({formatOptions:{colors:!0,compact:!0,date:!1}})});var ti,C,ce=We(()=>{"use strict";ti=S(require("consola")),C=async(n,e)=>{let t=await ti.default.prompt(n,e);return t!=null&&t.toString()==="Symbol(clack:cancel)"&&process.exit(0),(!e?.type||e.type==="text")&&t===void 0?"":t}});var vi={};Z(vi,{promptForCredentials:()=>$t});async function $t(n){let e=n.token||process.env.VERCEL_TOKEN||await C("What is your Vercel API Auth Token? (https://vercel.com/guides/how-do-i-use-a-vercel-api-access-token#creating-an-access-token)",{type:"text"}),t=n.teamId||process.env.VERCEL_TEAM_ID||await C("What is your Vercel Team ID? (See https://vercel.com/docs/accounts/create-a-team#find-your-team-id)",{type:"text"}),i=n.projectId||process.env.VERCEL_PROJECT_ID||await C("What is your Vercel Project ID? (See https://vercel.com/docs/projects/project-configuration/general-settings#project-id)",{type:"text"});return{token:e,teamId:t,projectId:i}}var At=We(()=>{"use strict";ce()});var me={};Z(me,{TranslationWarningSystem:()=>nt});var nt,pe=We(()=>{"use strict";nt=class n{static DOCS_BASE_URL="https://docs.doorman.griffen.codes";static WARNING_CONFIGS={managed_rules:{category:"feature_unsupported",severity:"critical",explanation:"Managed rules are provider-specific security rules that cannot be directly translated between platforms.",suggestion:"Replace with custom rules using equivalent conditions and actions.",alternativeApproach:"Create custom rules that match the same traffic patterns using standard conditions like IP, country, user agent, or request headers.",impact:"Security coverage may be reduced until equivalent custom rules are implemented.",docsUrl:`${n.DOCS_BASE_URL}/migration/managed-rules`},bot_management:{category:"feature_unsupported",severity:"critical",explanation:"Bot management features use provider-specific detection algorithms that cannot be translated.",suggestion:"Configure bot management separately in the target provider.",alternativeApproach:"Use user agent filtering, rate limiting, and challenge actions to implement basic bot protection.",impact:"Bot protection effectiveness may vary between providers.",docsUrl:`${n.DOCS_BASE_URL}/migration/bot-management`},complex_expressions:{category:"lossy_conversion",severity:"warning",explanation:"Complex expressions may not translate perfectly due to differences in syntax and supported operators between providers.",suggestion:"Review translated expressions and test thoroughly before deploying.",alternativeApproach:"Break complex expressions into simpler, more portable conditions.",impact:"Rule behavior may differ slightly from the original configuration.",docsUrl:`${n.DOCS_BASE_URL}/migration/expressions`},geo_blocking:{category:"lossy_conversion",severity:"warning",explanation:"Geographic blocking may use different country codes or geographic databases between providers.",suggestion:"Verify country codes and geographic targeting after translation.",alternativeApproach:"Use ISO country codes when possible for better compatibility.",impact:"Some geographic regions may be blocked or allowed differently than intended.",docsUrl:`${n.DOCS_BASE_URL}/migration/geo-blocking`},rate_limiting_precision:{category:"lossy_conversion",severity:"info",explanation:"Rate limiting configurations may be adjusted to match the target provider's supported time windows and request thresholds.",suggestion:"Review rate limiting settings and adjust if necessary.",alternativeApproach:"Use the closest supported time window and request threshold values.",impact:"Rate limiting may be slightly more or less restrictive than the original configuration.",docsUrl:`${n.DOCS_BASE_URL}/migration/rate-limiting`},regex_patterns:{category:"syntax_limitation",severity:"warning",explanation:"Regular expression patterns may have different syntax requirements or feature support between providers.",suggestion:"Test regex patterns in the target provider and adjust syntax if needed.",alternativeApproach:"Use simpler string matching operations (contains, starts_with, ends_with) when possible.",impact:"Pattern matching may behave differently or fail to match intended traffic.",docsUrl:`${n.DOCS_BASE_URL}/migration/regex-patterns`},header_manipulation:{category:"syntax_limitation",severity:"warning",explanation:"Header manipulation capabilities vary between providers in terms of supported headers and modification types.",suggestion:"Verify header modification support in the target provider.",alternativeApproach:"Use redirect rules or custom error pages to achieve similar functionality.",impact:"Header modifications may not work as expected or may be ignored.",docsUrl:`${n.DOCS_BASE_URL}/migration/headers`},large_ip_lists:{category:"performance_impact",severity:"info",explanation:"Large IP lists may impact rule evaluation performance differently between providers.",suggestion:"Consider using provider-specific IP list features for better performance.",alternativeApproach:"Break large IP lists into smaller, more targeted rules or use CIDR notation to reduce list size.",impact:"Rule evaluation may be slower with large IP lists, potentially affecting site performance.",docsUrl:`${n.DOCS_BASE_URL}/migration/ip-lists`},many_conditions:{category:"performance_impact",severity:"info",explanation:"Rules with many conditions may have different performance characteristics between providers.",suggestion:"Monitor rule performance and consider optimizing complex rules.",alternativeApproach:"Split complex rules into multiple simpler rules or use more efficient condition types.",impact:"Complex rules may impact request processing speed.",docsUrl:`${n.DOCS_BASE_URL}/migration/rule-optimization`},action_differences:{category:"security_consideration",severity:"warning",explanation:"Security actions (block, challenge, etc.) may behave differently between providers in terms of user experience and effectiveness.",suggestion:"Test security actions thoroughly and adjust based on provider capabilities.",alternativeApproach:"Use the most appropriate equivalent action available in the target provider.",impact:"Security effectiveness and user experience may differ from the original configuration.",docsUrl:`${n.DOCS_BASE_URL}/migration/security-actions`},challenge_types:{category:"security_consideration",severity:"info",explanation:"Challenge types (CAPTCHA, JavaScript, etc.) vary between providers and may affect user experience.",suggestion:"Choose challenge types that balance security and user experience for your use case.",alternativeApproach:"Use the provider's recommended challenge type for your security requirements.",impact:"User experience during security challenges may vary.",docsUrl:`${n.DOCS_BASE_URL}/migration/challenges`},version_compatibility:{category:"compatibility_issue",severity:"warning",explanation:"Some features may only be available in specific provider plan tiers or API versions.",suggestion:"Verify feature availability in your target provider plan.",alternativeApproach:"Use alternative features available in your plan tier.",impact:"Some rules may not work as expected if required features are unavailable.",docsUrl:`${n.DOCS_BASE_URL}/migration/compatibility`},plan_limitations:{category:"compatibility_issue",severity:"critical",explanation:"Your current plan may not support all features being translated.",suggestion:"Upgrade your plan or modify rules to use available features.",alternativeApproach:"Implement equivalent functionality using features available in your current plan.",impact:"Rules may be rejected or ignored if they use unavailable features.",docsUrl:`${n.DOCS_BASE_URL}/migration/plan-limits`}};static createWarning(e,t,i,r,s){let a=this.WARNING_CONFIGS[e];return a?{rule:t,field:i,category:a.category,message:r||`${e.replace(/_/g," ")} translation issue`,explanation:a.explanation,severity:a.severity,suggestion:s||a.suggestion,alternativeApproach:a.alternativeApproach,impact:a.impact,docsUrl:a.docsUrl}:{rule:t,field:i,category:"compatibility_issue",message:r||`Unknown translation issue: ${e}`,explanation:"This translation scenario is not fully documented.",severity:"warning",suggestion:s||"Review the translated configuration manually and test thoroughly.",docsUrl:`${this.DOCS_BASE_URL}/migration/troubleshooting`}}static createUnsupportedFeatureWarning(e,t,i,r,s){return{rule:r,field:s,category:"feature_unsupported",message:`${e} is not supported in ${i}`,explanation:`The ${e} feature from ${t} cannot be directly translated to ${i} due to platform differences.`,severity:"critical",suggestion:`Remove or replace ${e} with equivalent functionality supported by ${i}.`,alternativeApproach:`Check ${i} documentation for similar features or use custom rules to achieve equivalent functionality.`,impact:"This rule may not work as expected or may be ignored entirely.",docsUrl:`${this.DOCS_BASE_URL}/migration/unsupported-features`}}static createLossyConversionWarning(e,t,i,r){return{rule:i,field:r,category:"lossy_conversion",message:`${e} translation may be lossy`,explanation:`The ${e} configuration cannot be perfectly translated: ${t}`,severity:"warning",suggestion:"Review the translated configuration and test thoroughly to ensure it behaves as expected.",alternativeApproach:"Consider simplifying the configuration or using more portable alternatives.",impact:"The translated rule may behave differently than the original.",docsUrl:`${this.DOCS_BASE_URL}/migration/lossy-conversions`}}static createSyntaxLimitationWarning(e,t,i,r){return{rule:i,field:r,category:"syntax_limitation",message:`${e} has syntax limitations`,explanation:`The ${e} syntax has been modified due to provider limitations: ${t}`,severity:"warning",suggestion:"Verify the translated syntax works correctly in the target provider.",alternativeApproach:"Use simpler syntax or alternative approaches that are more widely supported.",impact:"The rule may not match traffic as precisely as intended.",docsUrl:`${this.DOCS_BASE_URL}/migration/syntax-limitations`}}static createPerformanceWarning(e,t,i,r){return{rule:i,field:r,category:"performance_impact",message:`${e} may impact performance`,explanation:`The ${e} configuration may have performance implications: ${t}`,severity:"info",suggestion:"Monitor performance after deployment and optimize if necessary.",alternativeApproach:"Consider using more efficient alternatives or breaking complex rules into simpler ones.",impact:"Request processing may be slower than expected.",docsUrl:`${this.DOCS_BASE_URL}/migration/performance`}}static formatWarning(e){let i={critical:"\u{1F6A8}",warning:"\u26A0\uFE0F",info:"\u2139\uFE0F"}[e.severity],r=e.rule?` (Rule: ${e.rule}${e.field?`, Field: ${e.field}`:""})`:"",s=`${i} ${e.severity.toUpperCase()}: ${e.message}${r}
2
2
  `;return s+=` ${e.explanation}
3
3
  `,e.suggestion&&(s+=` \u{1F4A1} Suggestion: ${e.suggestion}
4
4
  `),e.alternativeApproach&&(s+=` \u{1F504} Alternative: ${e.alternativeApproach}
5
5
  `),e.impact&&(s+=` \u{1F4CA} Impact: ${e.impact}
6
6
  `),e.docsUrl&&(s+=` \u{1F4D6} Documentation: ${e.docsUrl}
7
- `),s}static groupWarningsBySeverity(e){return{critical:e.filter(t=>t.severity==="critical"),warning:e.filter(t=>t.severity==="warning"),info:e.filter(t=>t.severity==="info")}}static getWarningSummary(e){let t={critical:0,warning:0,info:0},i={feature_unsupported:0,lossy_conversion:0,syntax_limitation:0,performance_impact:0,security_consideration:0,compatibility_issue:0};return e.forEach(r=>{t[r.severity]++,i[r.category]++}),{total:e.length,bySeverity:t,byCategory:i,hasBlockingIssues:t.critical>0}}}});var Ti={};Z(Ti,{OperationSafety:()=>Pt});var Pt,Pi=We(()=>{"use strict";k();ce();Pt=class{static async confirmDestructiveOperation(e){let{operation:t,target:i,changes:r,riskLevel:s,skipConfirmation:a=!1,dryRun:l=!1}=e;if(a||l)return l&&o.info(`\u{1F50D} Dry run mode: Would perform ${t} on ${i}`),!0;o.info(`
7
+ `),s}static groupWarningsBySeverity(e){return{critical:e.filter(t=>t.severity==="critical"),warning:e.filter(t=>t.severity==="warning"),info:e.filter(t=>t.severity==="info")}}static getWarningSummary(e){let t={critical:0,warning:0,info:0},i={feature_unsupported:0,lossy_conversion:0,syntax_limitation:0,performance_impact:0,security_consideration:0,compatibility_issue:0};return e.forEach(r=>{t[r.severity]++,i[r.category]++}),{total:e.length,bySeverity:t,byCategory:i,hasBlockingIssues:t.critical>0}}}});var Ti={};Z(Ti,{OperationSafety:()=>kt});var kt,Pi=We(()=>{"use strict";k();ce();kt=class{static async confirmDestructiveOperation(e){let{operation:t,target:i,changes:r,riskLevel:s,skipConfirmation:a=!1,dryRun:l=!1}=e;if(a||l)return l&&o.info(`\u{1F50D} Dry run mode: Would perform ${t} on ${i}`),!0;o.info(`
8
8
  \u{1F6A8} Destructive Operation Confirmation`),o.info(`Operation: ${t}`),o.info(`Target: ${i}`),o.info(`Risk Level: ${this.formatRiskLevel(s)}`),r&&this.displayChangeSummary(r),this.displayRiskWarnings(s,t);let c=this.getBackupRecommendation(t,s);c.recommended&&(o.warn(`
9
- \u{1F4BE} Backup Recommended: ${c.reason}`),c.instructions.forEach(g=>{o.info(` \u2022 ${g}`)}));let d=this.getRollbackGuidance(t);d.available&&(o.info(`
9
+ \u{1F4BE} Backup Recommended: ${c.reason}`),c.instructions.forEach(f=>{o.info(` \u2022 ${f}`)}));let d=this.getRollbackGuidance(t);d.available&&(o.info(`
10
10
  \u{1F504} Rollback Available: ${d.method}`),d.timeWindow&&o.info(` Time Window: ${d.timeWindow}`));let u=this.getConfirmationMessage(s,t);return await C(u,{type:"confirm"})?(o.info("\u2705 Operation confirmed by user"),!0):(o.info("Operation cancelled by user"),!1)}static async performDryRunValidation(e,t,i){o.info(`\u{1F50D} Performing dry-run validation for ${t}...`);let r=[],s=[],a={rulesToAdd:[],rulesToUpdate:[],rulesToDelete:[],ipsToAdd:[],ipsToUpdate:[],ipsToDelete:[],hasChanges:!1};try{this.validateConfigurationStructure(e,r),a=await i(e),this.validateChanges(a,s),this.checkForPotentialIssues(e,a,s);let l=r.length===0;return l?o.info("\u2705 Dry-run validation passed"):(o.warn(`\u26A0\uFE0F Dry-run validation found ${r.length} issues:`),r.forEach(c=>o.warn(` \u2022 ${c}`))),s.length>0&&(o.warn(`\u26A0\uFE0F Dry-run validation found ${s.length} warning(s):`),s.forEach(c=>o.warn(` \u2022 ${c}`))),{valid:l,changes:a,issues:r,warnings:s}}catch(l){let c=`Dry-run validation failed: ${l instanceof Error?l.message:String(l)}`;return r.push(c),o.error(c),{valid:!1,changes:a,issues:r,warnings:s}}}static getBackupRecommendation(e,t){return{"sync rules":{recommended:t!=="low",reason:"Rule synchronization can overwrite existing firewall configuration",instructions:['Run "doorman download" to backup current Cloudflare rules',"Save the downloaded configuration file with a timestamp","Consider using version control for configuration files"]},"delete ruleset":{recommended:!0,reason:"Ruleset deletion is irreversible and removes all associated rules",instructions:['Export current ruleset with "doorman export"',"Document the ruleset ID and configuration","Ensure you have the original configuration files"]},"update rules":{recommended:t==="high",reason:"Rule updates can affect traffic flow and security posture",instructions:["Download current configuration as backup","Test changes in a staging environment if possible","Have rollback plan ready"]},"clear all rules":{recommended:!0,reason:"Clearing all rules removes all firewall protection",instructions:['Export complete configuration with "doorman export"',"Save configuration to version control","Document business justification for clearing rules"]}}[e]||{recommended:t!=="low",reason:"Operation may modify existing configuration",instructions:["Download current configuration as backup","Document the changes being made"]}}static getRollbackGuidance(e){return{"sync rules":{available:!0,method:"Restore from backup configuration",instructions:['Use "doorman sync" with your backup configuration file',"Or manually restore rules in Cloudflare dashboard","Check that all rules are functioning as expected"],timeWindow:"No time limit - can rollback anytime"},"delete ruleset":{available:!1,method:"Recreation required",instructions:["Deleted rulesets cannot be restored","Must recreate ruleset and rules from backup",'Use "doorman sync" with backup configuration']},"update rules":{available:!0,method:"Restore previous rule version",instructions:["Sync with previous configuration version","Or use Cloudflare dashboard to revert changes","Verify rule functionality after rollback"],timeWindow:"Immediate - no time restrictions"},"clear all rules":{available:!0,method:"Restore from backup",instructions:["Sync with backup configuration file","Verify all rules are restored correctly","Test firewall functionality"],timeWindow:"No time limit"}}[e]||{available:!0,method:"Restore from backup",instructions:["Use backup configuration to restore previous state","Verify changes are reverted correctly"]}}static formatRiskLevel(e){let t=require("chalk");switch(e){case"low":return t.green("LOW");case"medium":return t.yellow("MEDIUM");case"high":return t.red("HIGH");default:return String(e).toUpperCase()}}static displayChangeSummary(e){o.info(`
11
11
  \u{1F4CA} Change Summary:`),e.rulesToAdd?.length&&o.info(` \u2022 Rules to add: ${e.rulesToAdd.length}`),e.rulesToUpdate?.length&&o.info(` \u2022 Rules to update: ${e.rulesToUpdate.length}`),e.rulesToDelete?.length&&o.info(` \u2022 Rules to delete: ${e.rulesToDelete.length}`),e.ipsToAdd?.length&&o.info(` \u2022 IPs to add: ${e.ipsToAdd.length}`),e.ipsToUpdate?.length&&o.info(` \u2022 IPs to update: ${e.ipsToUpdate.length}`),e.ipsToDelete?.length&&o.info(` \u2022 IPs to delete: ${e.ipsToDelete.length}`),e.hasChanges||o.info(" \u2022 No changes detected")}static displayRiskWarnings(e,t){let i={low:["This operation has minimal risk of disrupting service"],medium:["This operation may temporarily affect traffic filtering","Monitor your application after the change"],high:["\u26A0\uFE0F This operation can significantly impact security and traffic flow","\u26A0\uFE0F Ensure you have tested the configuration in a safe environment","\u26A0\uFE0F Have a rollback plan ready before proceeding"]},r={"clear all rules":["\u{1F6A8} This will remove ALL firewall protection","\u{1F6A8} Your application will be unprotected until new rules are added"],"delete ruleset":["\u{1F6A8} This action is IRREVERSIBLE","\u{1F6A8} All rules in the ruleset will be permanently deleted"]};o.info(`
12
- \u26A0\uFE0F Warnings:`),i[e]?.forEach(s=>{o.warn(` ${s}`)}),r[t]?.forEach(s=>{o.warn(` ${s}`)})}static getConfirmationMessage(e,t){let i=`Do you want to proceed with ${t}?`;switch(e){case"high":return`${i} (Type 'yes' to confirm high-risk operation)`;case"medium":return`${i} (Proceed with caution)`;default:return i}}static validateConfigurationStructure(e,t){if(!e){t.push("Configuration is null or undefined");return}e.version||t.push("Configuration missing version field"),e.provider||t.push("Configuration missing provider field"),Array.isArray(e.rules)||t.push("Configuration rules field is not an array"),e.ips&&!Array.isArray(e.ips)&&t.push("Configuration ips field is not an array")}static validateChanges(e,t){let i=(e.rulesToDelete?.length||0)+(e.ipsToDelete?.length||0),r=(e.rulesToAdd?.length||0)+(e.rulesToUpdate?.length||0)+(e.ipsToAdd?.length||0)+(e.ipsToUpdate?.length||0)+i;if(i>0&&r>0){let s=i/r;s>.5&&t.push(`High deletion ratio detected: ${Math.round(s*100)}% of changes are deletions`)}r>100&&t.push(`Large number of changes detected: ${r} total changes`)}static checkForPotentialIssues(e,t,i){t.rulesToDelete?.length===e.rules.length&&e.rules.length>0&&i.push("All existing rules will be deleted - this removes all firewall protection");let r=e.rules.filter(l=>l.action.type==="deny"&&l.conditions.some(c=>c.field==="path"&&(c.value==="/"||c.value==="*")));r.length>0&&i.push(`Found ${r.length} rules that may block all traffic`),e.ips&&e.ips.length>1e3&&i.push(`Large IP list detected: ${e.ips.length} IPs may impact performance`);let s=e.rules.map(l=>l.name),a=s.filter((l,c)=>s.indexOf(l)!==c);a.length>0&&i.push(`Duplicate rule names found: ${[...new Set(a)].join(", ")}`)}}});var io=require("dotenv"),oo=S(require("yargs"));var bt={};Z(bt,{builder:()=>ko,command:()=>To,desc:()=>Po,generateRuleId:()=>It,handler:()=>xo});var V=S(require("chalk")),wi=require("consola");k();var $=require("zod"),Qe=$.z.number().int().positive().optional(),co=$.z.string().ip().or($.z.string().regex(/^(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/)),Yt=$.z.string().optional(),uo=$.z.enum(["eq","pre","suf","inc","sub","re","ex","nex"]),po=$.z.enum(["host","path","method","header","query","cookie","target_path","ip_address","region","protocol","scheme","environment","user_agent","geo_continent","geo_country","geo_country_region","geo_city","geo_as_number","ja4_digest","ja3_digest","rate_limit_api_id"]),go=$.z.object({op:uo,neg:$.z.boolean().optional(),type:po,value:$.z.union([$.z.string(),$.z.array($.z.string()),$.z.array($.z.number())]).optional(),key:$.z.string().optional()}).refine(n=>{switch(n.type){case"ip_address":case"method":case"environment":case"protocol":return n.op==="eq"||n.op==="inc";default:return!0}},"Invalid operator for the given condition type").refine(n=>{let e=["header","cookie"];return!(e.includes(n.type)&&!n.key||e.includes(n.type)&&n.op!=="ex"&&n.op!=="nex"&&!n.value)},"Missing key from condition").refine(n=>!(["ex","nex"].includes(n.op)&&n.neg),"Negation is not supported for the given operator").refine(n=>!(n.op==="inc"&&!Array.isArray(n.value)),"When using `inc` operator, value must be an array"),lt=$.z.object({conditions:$.z.array(go).min(1,"Condition group must have at least one condition")}),fo=$.z.string().regex(/^\d+[smhd]$|^permanent$/),ct=$.z.object({requests:$.z.number().positive().int().refine(n=>n>0,"requests must be positive"),window:$.z.string().regex(/^\d+[smhd]$/).refine(n=>parseInt(n)>0,"window duration must be positive")}),mo=$.z.object({location:$.z.string(),permanent:$.z.boolean().optional()}),ho=$.z.enum(["log","deny","challenge","bypass","rate_limit","redirect"]),yo=$.z.object({action:ho,rateLimit:ct.nullable().optional(),redirect:mo.nullable().optional(),actionDuration:fo.nullable().optional()}),wo=$.z.object({mitigate:yo}),dt=$.z.object({id:Yt,name:$.z.string(),description:$.z.string().optional(),conditionGroup:$.z.array(lt),action:wo,active:$.z.boolean()});var ut=$.z.object({id:Yt,ip:co,hostname:$.z.string(),notes:$.z.string().optional(),action:$.z.literal("deny")}),Co=$.z.object({projectId:$.z.string().optional(),teamId:$.z.string().optional()}),we=Co.extend({rules:$.z.array(dt),ips:$.z.array(ut).optional(),version:$.z.number().optional(),updatedAt:$.z.string().optional()});ce();var Re=require("fs"),fi=require("path");k();var pi=S(require("ajv"));var ti="https://doorman.griffen.codes/schema.json",ii={$schema:"http://json-schema.org/draft-07/schema#",title:"Doorman Config",description:"Schema for doorman project configuration files",$ref:"#/definitions/FirewallConfig",definitions:{FirewallConfig:{type:"object",properties:{projectId:{type:"string"},teamId:{type:"string"},$schema:{type:"string"},version:{type:"number"},firewallEnabled:{type:"boolean"},rules:{type:"array",items:{$ref:"#/definitions/CustomRule"}},ips:{type:"array",items:{$ref:"#/definitions/IPBlockingRule"}},updatedAt:{type:"string"}},required:["rules"],additionalProperties:!1,description:"The main configuration type for Doorman"},CustomRule:{type:"object",properties:{id:{type:"string"},name:{type:"string"},description:{type:"string"},conditionGroup:{type:"array",items:{$ref:"#/definitions/ConditionGroup"}},action:{$ref:"#/definitions/RuleAction"},active:{type:"boolean"}},required:["name","conditionGroup","action","active"],additionalProperties:!1,description:"Rule Types"},ConditionGroup:{type:"object",properties:{conditions:{type:"array",items:{$ref:"#/definitions/RuleCondition"}}},required:["conditions"],additionalProperties:!1},RuleCondition:{type:"object",properties:{op:{$ref:"#/definitions/RuleOperator"},neg:{type:"boolean"},type:{$ref:"#/definitions/RuleType"},key:{type:"string"},value:{anyOf:[{type:"string"},{type:"number"},{type:"array",items:{type:"string"}},{type:"array",items:{type:"number"}}]}},required:["op","type"],additionalProperties:!1,description:"Rule Condition Types"},RuleOperator:{type:"string",enum:["eq","pre","suf","inc","sub","re","ex","nex"]},RuleType:{type:"string",enum:["host","path","method","header","query","cookie","target_path","ip_address","region","protocol","scheme","environment","user_agent","geo_continent","geo_country","geo_country_region","geo_city","geo_as_number","ja4_digest","ja3_digest","rate_limit_api_id"]},RuleAction:{type:"object",properties:{mitigate:{$ref:"#/definitions/MitigationAction"}},required:["mitigate"],additionalProperties:!1},MitigationAction:{type:"object",properties:{action:{$ref:"#/definitions/ActionType"},rateLimit:{anyOf:[{$ref:"#/definitions/RateLimit"},{type:"null"}]},redirect:{anyOf:[{$ref:"#/definitions/Redirect"},{type:"null"}]},actionDuration:{type:["string","null"]}},required:["action"],additionalProperties:!1},ActionType:{type:"string",enum:["log","deny","challenge","bypass","rate_limit","redirect"],description:"Core Types"},RateLimit:{type:"object",properties:{requests:{type:"number"},window:{type:"string"}},required:["requests","window"],additionalProperties:!1,description:"Action Types"},Redirect:{type:"object",properties:{location:{type:"string"},permanent:{type:"boolean"}},required:["location"],additionalProperties:!1},IPBlockingRule:{type:"object",properties:{id:{type:"string"},ip:{type:"string"},hostname:{type:"string"},notes:{type:"string"},action:{type:"string",const:"deny"}},required:["ip","hostname","action"],additionalProperties:!1}}};var L=require("zod");var _=require("zod"),pt=_.z.string().optional(),oi=_.z.string().ip().or(_.z.string().regex(/^(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/)),Ye=_.z.string().datetime().or(_.z.string().optional()),ni=_.z.enum(["log","deny","challenge","bypass","rate_limit","redirect","allow","block"]),ri=_.z.enum(["eq","ne","contains","not_contains","starts_with","ends_with","matches","in","not_in","gt","ge","lt","le","exists","not_exists"]),si=_.z.enum(["ip","country","region","city","asn","path","host","method","header","query","cookie","user_agent","referer","scheme","port"]),ur=_.z.string().regex(/^\d+[smhd]$|^permanent$/),ai=_.z.object({requests:_.z.number().positive().int(),window:_.z.string().regex(/^\d+[smhd]$/),characteristics:_.z.array(_.z.string()).optional()}),li=_.z.object({location:_.z.string().url(),statusCode:_.z.number().int().min(300).max(399).optional(),permanent:_.z.boolean().optional(),preserveQueryString:_.z.boolean().optional()}),gt=_.z.object({version:_.z.number().int().positive().optional(),updatedAt:Ye,createdAt:Ye,lastSyncedAt:Ye,migratedFrom:_.z.string().optional(),migratedAt:Ye}),Ro=_.z.enum(["vercel","cloudflare"]),ci=_.z.object({vercel:_.z.object({projectId:_.z.string().optional(),teamId:_.z.string().optional()}).optional(),cloudflare:_.z.object({zoneId:_.z.string().optional(),accountId:_.z.string().optional()}).optional()}),di=_.z.object({$schema:_.z.string().url().optional(),version:_.z.string().optional(),provider:Ro.optional(),metadata:gt.optional()});var Io=L.z.object({field:si.or(L.z.string()),operator:ri,value:L.z.union([L.z.string(),L.z.number(),L.z.array(L.z.string()),L.z.array(L.z.number())]),negated:L.z.boolean().optional(),key:L.z.string().optional()}),bo=L.z.object({type:ni,rateLimit:ai.optional(),redirect:li.optional(),response:L.z.object({statusCode:L.z.number().int().min(100).max(599).optional(),content:L.z.string().optional(),contentType:L.z.string().optional()}).optional(),duration:L.z.string().optional()}),vo=L.z.object({id:pt,name:L.z.string().min(1,"Rule name is required"),description:L.z.string().optional(),enabled:L.z.boolean(),conditions:L.z.array(Io).min(1,"At least one condition is required"),conditionLogic:L.z.enum(["AND","OR"]).optional().default("AND"),action:bo,priority:L.z.number().int().optional(),categories:L.z.array(L.z.string()).optional()}),Eo=L.z.object({id:pt,ip:oi,hostname:L.z.string().optional(),notes:L.z.string().optional(),action:L.z.enum(["deny","allow"])}),ft=di.extend({providers:ci.optional(),rules:L.z.array(vo),ips:L.z.array(Eo).optional(),metadata:gt.optional()}),ui=n=>{let e=ft.safeParse(n);if(!e.success)throw new Error(`Invalid unified configuration: ${e.error.message}`);if(e.data.provider&&!e.data.providers?.[e.data.provider])throw new Error(`Provider '${e.data.provider}' specified but no configuration found in providers section`);return e.data};function mt(n){return!!n&&typeof n=="object"&&"rules"in n&&Array.isArray(n.rules)}function Ee(n){if(!n||typeof n!="object")return!1;let e=n;return typeof e.provider=="string"||typeof e.providers=="object"&&e.providers!==null}var se=S(require("chalk")),Ce=class{static formatValidationError(e){let t=e.instancePath||"/",i=se.default.cyan(t);switch(e.keyword){case"type":return`${i}: Expected ${se.default.green(e.schema)}, got ${se.default.red(typeof e.data)}`;case"enum":{let r=e.params.allowedValues;return`${i}: Value must be one of: ${se.default.green(r.join(", "))}`}case"required":{let r=e.params;return`${i}: Missing required property ${se.default.yellow(r.missingProperty)}`}case"additionalProperties":{let r=e.params;return`${i}: Unknown property ${se.default.red(r.additionalProperty)}`}default:return`${i}: ${e.message}`}}static formatCustomError(e,t){return`${se.default.cyan(e)}: ${t}`}static wrapErrorBlock(e){let t=se.default.red.bold("Validation Errors:"),i=e.map(r=>` ${r}`).join(`
12
+ \u26A0\uFE0F Warnings:`),i[e]?.forEach(s=>{o.warn(` ${s}`)}),r[t]?.forEach(s=>{o.warn(` ${s}`)})}static getConfirmationMessage(e,t){let i=`Do you want to proceed with ${t}?`;switch(e){case"high":return`${i} (Type 'yes' to confirm high-risk operation)`;case"medium":return`${i} (Proceed with caution)`;default:return i}}static validateConfigurationStructure(e,t){if(!e){t.push("Configuration is null or undefined");return}e.version||t.push("Configuration missing version field"),e.provider||t.push("Configuration missing provider field"),Array.isArray(e.rules)||t.push("Configuration rules field is not an array"),e.ips&&!Array.isArray(e.ips)&&t.push("Configuration ips field is not an array")}static validateChanges(e,t){let i=(e.rulesToDelete?.length||0)+(e.ipsToDelete?.length||0),r=(e.rulesToAdd?.length||0)+(e.rulesToUpdate?.length||0)+(e.ipsToAdd?.length||0)+(e.ipsToUpdate?.length||0)+i;if(i>0&&r>0){let s=i/r;s>.5&&t.push(`High deletion ratio detected: ${Math.round(s*100)}% of changes are deletions`)}r>100&&t.push(`Large number of changes detected: ${r} total changes`)}static checkForPotentialIssues(e,t,i){t.rulesToDelete?.length===e.rules.length&&e.rules.length>0&&i.push("All existing rules will be deleted - this removes all firewall protection");let r=e.rules.filter(l=>l.action.type==="deny"&&l.conditions.some(c=>c.field==="path"&&(c.value==="/"||c.value==="*")));r.length>0&&i.push(`Found ${r.length} rules that may block all traffic`),e.ips&&e.ips.length>1e3&&i.push(`Large IP list detected: ${e.ips.length} IPs may impact performance`);let s=e.rules.map(l=>l.name),a=s.filter((l,c)=>s.indexOf(l)!==c);a.length>0&&i.push(`Duplicate rule names found: ${[...new Set(a)].join(", ")}`)}}});var io=require("dotenv"),oo=S(require("yargs"));var vt={};Z(vt,{builder:()=>ko,command:()=>To,desc:()=>Po,generateRuleId:()=>bt,handler:()=>xo});var B=S(require("chalk")),wi=require("consola");k();var $=require("zod"),Qe=$.z.number().int().positive().optional(),co=$.z.string().ip().or($.z.string().regex(/^(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/)),ei=$.z.string().optional(),uo=$.z.enum(["eq","pre","suf","inc","sub","re","ex","nex"]),po=$.z.enum(["host","path","method","header","query","cookie","target_path","ip_address","region","protocol","scheme","environment","user_agent","geo_continent","geo_country","geo_country_region","geo_city","geo_as_number","ja4_digest","ja3_digest","rate_limit_api_id"]),go=$.z.object({op:uo,neg:$.z.boolean().optional(),type:po,value:$.z.union([$.z.string(),$.z.array($.z.string()),$.z.array($.z.number())]).optional(),key:$.z.string().optional()}).refine(n=>{switch(n.type){case"ip_address":case"method":case"environment":case"protocol":return n.op==="eq"||n.op==="inc";default:return!0}},"Invalid operator for the given condition type").refine(n=>{let e=["header","cookie"];return!(e.includes(n.type)&&!n.key||e.includes(n.type)&&n.op!=="ex"&&n.op!=="nex"&&!n.value)},"Missing key from condition").refine(n=>!(["ex","nex"].includes(n.op)&&n.neg),"Negation is not supported for the given operator").refine(n=>!(n.op==="inc"&&!Array.isArray(n.value)),"When using `inc` operator, value must be an array"),ct=$.z.object({conditions:$.z.array(go).min(1,"Condition group must have at least one condition")}),fo=$.z.string().regex(/^\d+[smhd]$|^permanent$/),dt=$.z.object({requests:$.z.number().positive().int().refine(n=>n>0,"requests must be positive"),window:$.z.string().regex(/^\d+[smhd]$/).refine(n=>parseInt(n)>0,"window duration must be positive")}),mo=$.z.object({location:$.z.string(),permanent:$.z.boolean().optional()}),ho=$.z.enum(["log","deny","challenge","bypass","rate_limit","redirect"]),yo=$.z.object({action:ho,rateLimit:dt.nullable().optional(),redirect:mo.nullable().optional(),actionDuration:fo.nullable().optional()}),wo=$.z.object({mitigate:yo}),ut=$.z.object({id:ei,name:$.z.string(),description:$.z.string().optional(),conditionGroup:$.z.array(ct),action:wo,active:$.z.boolean()});var pt=$.z.object({id:ei,ip:co,hostname:$.z.string(),notes:$.z.string().optional(),action:$.z.literal("deny")}),Co=$.z.object({projectId:$.z.string().optional(),teamId:$.z.string().optional()}),we=Co.extend({rules:$.z.array(ut),ips:$.z.array(pt).optional(),version:$.z.number().optional(),updatedAt:$.z.string().optional()});ce();var Ie=require("fs"),fi=require("path");k();var pi=S(require("ajv"));var ii="https://doorman.griffen.codes/schema.json",oi={$schema:"http://json-schema.org/draft-07/schema#",title:"Doorman Config",description:"Schema for doorman project configuration files",$ref:"#/definitions/FirewallConfig",definitions:{FirewallConfig:{type:"object",properties:{projectId:{type:"string"},teamId:{type:"string"},$schema:{type:"string"},version:{type:"number"},firewallEnabled:{type:"boolean"},rules:{type:"array",items:{$ref:"#/definitions/CustomRule"}},ips:{type:"array",items:{$ref:"#/definitions/IPBlockingRule"}},updatedAt:{type:"string"}},required:["rules"],additionalProperties:!1,description:"The main configuration type for Doorman"},CustomRule:{type:"object",properties:{id:{type:"string"},name:{type:"string"},description:{type:"string"},conditionGroup:{type:"array",items:{$ref:"#/definitions/ConditionGroup"}},action:{$ref:"#/definitions/RuleAction"},active:{type:"boolean"}},required:["name","conditionGroup","action","active"],additionalProperties:!1,description:"Rule Types"},ConditionGroup:{type:"object",properties:{conditions:{type:"array",items:{$ref:"#/definitions/RuleCondition"}}},required:["conditions"],additionalProperties:!1},RuleCondition:{type:"object",properties:{op:{$ref:"#/definitions/RuleOperator"},neg:{type:"boolean"},type:{$ref:"#/definitions/RuleType"},key:{type:"string"},value:{anyOf:[{type:"string"},{type:"number"},{type:"array",items:{type:"string"}},{type:"array",items:{type:"number"}}]}},required:["op","type"],additionalProperties:!1,description:"Rule Condition Types"},RuleOperator:{type:"string",enum:["eq","pre","suf","inc","sub","re","ex","nex"]},RuleType:{type:"string",enum:["host","path","method","header","query","cookie","target_path","ip_address","region","protocol","scheme","environment","user_agent","geo_continent","geo_country","geo_country_region","geo_city","geo_as_number","ja4_digest","ja3_digest","rate_limit_api_id"]},RuleAction:{type:"object",properties:{mitigate:{$ref:"#/definitions/MitigationAction"}},required:["mitigate"],additionalProperties:!1},MitigationAction:{type:"object",properties:{action:{$ref:"#/definitions/ActionType"},rateLimit:{anyOf:[{$ref:"#/definitions/RateLimit"},{type:"null"}]},redirect:{anyOf:[{$ref:"#/definitions/Redirect"},{type:"null"}]},actionDuration:{type:["string","null"]}},required:["action"],additionalProperties:!1},ActionType:{type:"string",enum:["log","deny","challenge","bypass","rate_limit","redirect"],description:"Core Types"},RateLimit:{type:"object",properties:{requests:{type:"number"},window:{type:"string"}},required:["requests","window"],additionalProperties:!1,description:"Action Types"},Redirect:{type:"object",properties:{location:{type:"string"},permanent:{type:"boolean"}},required:["location"],additionalProperties:!1},IPBlockingRule:{type:"object",properties:{id:{type:"string"},ip:{type:"string"},hostname:{type:"string"},notes:{type:"string"},action:{type:"string",const:"deny"}},required:["ip","hostname","action"],additionalProperties:!1}}};var L=require("zod");var _=require("zod"),gt=_.z.string().optional(),et=_.z.string().ip().or(_.z.string().regex(/^(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/)),Ye=_.z.string().datetime().or(_.z.string().optional()),ni=_.z.enum(["log","deny","challenge","bypass","rate_limit","redirect","allow","block"]),ri=_.z.enum(["eq","ne","contains","not_contains","starts_with","ends_with","matches","in","not_in","gt","ge","lt","le","exists","not_exists"]),si=_.z.enum(["ip","country","region","city","asn","path","host","method","header","query","cookie","user_agent","referer","scheme","port"]),ur=_.z.string().regex(/^\d+[smhd]$|^permanent$/),ai=_.z.object({requests:_.z.number().positive().int(),window:_.z.string().regex(/^\d+[smhd]$/),characteristics:_.z.array(_.z.string()).optional()}),li=_.z.object({location:_.z.string().url(),statusCode:_.z.number().int().min(300).max(399).optional(),permanent:_.z.boolean().optional(),preserveQueryString:_.z.boolean().optional()}),ft=_.z.object({version:_.z.number().int().positive().optional(),updatedAt:Ye,createdAt:Ye,lastSyncedAt:Ye,migratedFrom:_.z.string().optional(),migratedAt:Ye}),Ro=_.z.enum(["vercel","cloudflare"]),ci=_.z.object({vercel:_.z.object({projectId:_.z.string().optional(),teamId:_.z.string().optional()}).optional(),cloudflare:_.z.object({zoneId:_.z.string().optional(),accountId:_.z.string().optional()}).optional()}),di=_.z.object({$schema:_.z.string().url().optional(),version:_.z.string().optional(),provider:Ro.optional(),metadata:ft.optional()});var Io=L.z.object({field:si.or(L.z.string()),operator:ri,value:L.z.union([L.z.string(),L.z.number(),L.z.array(L.z.string()),L.z.array(L.z.number())]),negated:L.z.boolean().optional(),key:L.z.string().optional()}),bo=L.z.object({type:ni,rateLimit:ai.optional(),redirect:li.optional(),response:L.z.object({statusCode:L.z.number().int().min(100).max(599).optional(),content:L.z.string().optional(),contentType:L.z.string().optional()}).optional(),duration:L.z.string().optional()}),vo=L.z.object({id:gt,name:L.z.string().min(1,"Rule name is required"),description:L.z.string().optional(),enabled:L.z.boolean(),conditions:L.z.array(Io).min(1,"At least one condition is required"),conditionLogic:L.z.enum(["AND","OR"]).optional().default("AND"),action:bo,priority:L.z.number().int().optional(),categories:L.z.array(L.z.string()).optional()}),Eo=L.z.object({id:gt,ip:et,hostname:L.z.string().optional(),notes:L.z.string().optional(),action:L.z.enum(["deny","allow"])}),mt=di.extend({providers:ci.optional(),rules:L.z.array(vo),ips:L.z.array(Eo).optional(),metadata:ft.optional()}),ui=n=>{let e=mt.safeParse(n);if(!e.success)throw new Error(`Invalid unified configuration: ${e.error.message}`);if(e.data.provider&&!e.data.providers?.[e.data.provider])throw new Error(`Provider '${e.data.provider}' specified but no configuration found in providers section`);return e.data};function ht(n){return!!n&&typeof n=="object"&&"rules"in n&&Array.isArray(n.rules)}function $e(n){if(!n||typeof n!="object")return!1;let e=n;return typeof e.provider=="string"||typeof e.providers=="object"&&e.providers!==null}var se=S(require("chalk")),Ce=class{static formatValidationError(e){let t=e.instancePath||"/",i=se.default.cyan(t);switch(e.keyword){case"type":return`${i}: Expected ${se.default.green(e.schema)}, got ${se.default.red(typeof e.data)}`;case"enum":{let r=e.params.allowedValues;return`${i}: Value must be one of: ${se.default.green(r.join(", "))}`}case"required":{let r=e.params;return`${i}: Missing required property ${se.default.yellow(r.missingProperty)}`}case"additionalProperties":{let r=e.params;return`${i}: Unknown property ${se.default.red(r.additionalProperty)}`}default:return`${i}: ${e.message}`}}static formatCustomError(e,t){return`${se.default.cyan(e)}: ${t}`}static wrapErrorBlock(e){let t=se.default.red.bold("Validation Errors:"),i=e.map(r=>` ${r}`).join(`
13
13
  `);return`${t}
14
14
  ${i}`}static formatDetailedRuleValidation(e,t){let i=se.default.cyan(`Rule: ${e}`),r=t.map(s=>{let a=s.passed?se.default.green("\u2713"):se.default.red("\u2717"),l=s.passed?se.default.green(s.name):se.default.red(s.name);return` ${a} ${l}`}).join(`
15
15
  `);return`${i}
16
- ${r}`}};var F=class n extends Error{constructor(t,i,r,s=[]){super(t);this.ajvErrors=i;this.zodError=r;this.customErrors=s;this.name="ValidationError"}ajvErrors;zodError;customErrors;static formatErrors(t,i,r=[]){let s=[];return t?.length&&s.push(...t.map(Ce.formatValidationError)),i&&s.push(...i.errors.map(a=>`${a.path.join(".")}: ${a.message}`)),r.length&&s.push(...r),Ce.wrapErrorBlock(s)}getFormattedMessage(){return n.formatErrors(this.ajvErrors,this.zodError,this.customErrors)}},$e=class n{static instance;ajv;schema;constructor(){this.ajv=new pi.default({allErrors:!0,verbose:!0}),this.schema=ii}static getInstance(){return n.instance||(n.instance=new n),n.instance}validateConfig(e){if(Ee(e))try{ui(e);return}catch(c){throw new F(`Invalid firewall configuration:
17
- `+(c instanceof Error?c.message:String(c)),null)}let t=this.ajv.compile(this.schema),i=t(e),r=t.errors,s=we.safeParse(e),a;s.success||(a=s.error);let l=[];if(i&&s.success){try{this.validateRuleNames(e),this.validateRuleConditionGroup(e),this.validateRuleAction(e)}catch(c){if(c instanceof F)l.push(c.message,...c.customErrors||[]);else throw c}for(let c of e.rules){if(c.action.mitigate?.rateLimit){let d=ct.safeParse(c.action.mitigate.rateLimit);d.success||l.push(...d.error.errors.map(u=>u.message))}if(c.conditionGroup)for(let d of c.conditionGroup){let u=lt.safeParse(d);u.success||l.push(...u.error.errors.map(p=>p.message))}}}if(!i||!s.success||l.length>0)throw new F(`Invalid firewall configuration:
18
- `+F.formatErrors(r,a,l),r,a,l)}validateRuleNames(e){let t=new Set;for(let i of e.rules){if(t.has(i.name))throw new F(`Duplicate rule name found: "${i.name}"`,null);t.add(i.name)}}validateRuleConditionGroup(e){for(let t of e.rules)if(t.conditionGroup)for(let i of t.conditionGroup){if(!i.conditions||i.conditions.length===0)throw new F(`Rule "${t.name}" has an empty condition group`,null);for(let r of i.conditions){if(!r.type||!r.op)throw new F(`Rule "${t.name}" has an invalid condition`,null);let s=r.type==="header"||r.type==="cookie",a=r.op==="ex"||r.op==="nex";if(s){if(!r.key||typeof r.key!="string")throw new F(`Rule "${t.name}" has an invalid condition`,null);if(!a&&(r.value===void 0||r.value===null))throw new F(`Rule "${t.name}" has an invalid condition`,null)}else if(r.value===void 0||r.value===null)throw new F(`Rule "${t.name}" has an invalid condition`,null);let l=Array.isArray(r.value)?r.value:r.value!==void 0?[r.value]:[];if(Array.isArray(r.value)&&l.length===0)throw new F(`Rule "${t.name}" has an invalid value in condition`,null);if(r.type==="ip_address"){for(let c of l)if(typeof c!="string"||!this.isValidIP(c))throw new F(`Rule "${t.name}" has an invalid IP address in condition`,null)}if(r.type==="geo_as_number")for(let c of l){let d=typeof c=="number"?c:parseInt(String(c),10);if(!this.isValidASN(String(d)))throw new F(`Rule "${t.name}" has an invalid ASN in condition`,null)}if(r.type==="path"&&(r.op==="eq"||r.op==="pre")){for(let c of l)if(typeof c!="string"||!this.isValidPath(c))throw new F(`Rule "${t.name}" has an invalid path in condition`,null)}}}else throw new F("Either conditionGroup or type+values must be provided",null)}validateRuleAction(e){for(let t of e.rules)if(typeof t.action=="object"&&"mitigate"in t.action){let i=t.action.mitigate;if(i?.rateLimit){if(i.rateLimit.requests<=0)throw new F("Invalid rate limit configuration: requests must be positive",null);if(!i.rateLimit.window.match(/^\d+[smhd]$/))throw new F("Invalid rate limit configuration: invalid window format",null);if(parseInt(i.rateLimit.window)<=0)throw new F("Invalid rate limit configuration: window duration must be positive",null)}if(i?.actionDuration&&!i.actionDuration.match(/^\d+[smhd]$|^permanent$/))throw new F("Invalid action duration format: "+i.actionDuration,null);if(i?.redirect&&!i.redirect.location)throw new F("Invalid redirect configuration: location is required",null)}}isValidIP(e){let t=e.split("/"),i=t[0]??"",r=t[1],s=d=>{let u=d.split(".");return u.length!==4?!1:u.every(p=>{if(!/^\d{1,3}$/.test(p))return!1;let g=parseInt(p,10);return g>=0&&g<=255})},a=d=>{let u=d.split(":");return u.length!==8?!1:u.every(p=>/^[0-9a-fA-F]{1,4}$/.test(p))},l=s(i),c=a(i);if(!l&&!c)return!1;if(r!==void 0){let d=parseInt(r,10);if(Number.isNaN(d)||l&&(d<0||d>32)||c&&(d<0||d>128))return!1}return!0}isValidASN(e){let t=parseInt(e,10);return!isNaN(t)&&t>=1&&t<=4294967295}isValidPath(e){return e.startsWith("/")&&!e.includes(" ")}};var gi=require("fs"),Me=require("path"),ht=[".doorman.json","vercel-firewall.config.json"],$o=ht[0],Ae=class{static async findConfig(e){let{findUp:t}=await import("find-up");for(let i of ht){let r=await t(i,{cwd:e||process.cwd()});if(r)return r}}static async findProjectRoot(e){let t=await this.findConfig(e);return t?(0,Me.dirname)(t):null}static configExists(e){return(0,gi.existsSync)((0,Me.resolve)(e))}static getDefaultConfigPath(){return(0,Me.resolve)(process.cwd(),$o)}static getSupportedFileNames(){return ht}};async function G(n,e="required"){let t=Ao(e),i=n||await Ae.findConfig();if(!i||!(0,Re.existsSync)(i)){if(t==="optional")return o.debug("No config file found, returning empty config"),{};let s=Ae.getDefaultConfigPath();throw new Error(`No config file found. Run \`doorman init\` to create one at ${s}, or use --config to specify a custom path.`)}let r;try{let s=(0,Re.readFileSync)(i,"utf8");r=JSON.parse(s)}catch(s){throw s instanceof SyntaxError?new Error(`Invalid JSON in config file (${i}): ${s.message}`):s}if(t!=="raw")try{$e.getInstance().validateConfig(r)}catch(s){if(t==="lenient")o.warn("Config validation failed:",s);else throw s}return r}async function z(n,e,t={validate:!0,throwOnError:!0}){let i=e||await Ae.findConfig()||Ae.getDefaultConfigPath();if(t.validate)try{$e.getInstance().validateConfig(n)}catch(s){if(o.error("Config validation failed:",s),t.throwOnError)throw s}let r=(0,fi.dirname)(i);(0,Re.existsSync)(r)||(0,Re.mkdirSync)(r,{recursive:!0}),(0,Re.writeFileSync)(i,JSON.stringify(n,null,2)),o.debug(`Config saved to ${i}`)}function Ao(n){if(typeof n=="string")return n;let{validate:e=!0,throwOnError:t=!0}=n;return e?t?"required":"lenient":"raw"}var yt=S(require("chalk")),mi=require("zod");k();function Q(n,e){n instanceof SyntaxError?o.log(Ce.wrapErrorBlock(["Invalid JSON format in config file:",` ${n.message}`])):n instanceof mi.ZodError?(o.error(yt.default.red("Schema validation failed:")),n.errors.forEach(t=>{let i=t.path.join(".");o.error(yt.default.red(` - ${i}: ${t.message}`))})):n instanceof Error&&n.name==="ValidationError"?o.error(n):n instanceof Error&&n.message.includes("Forbidden")?o.log(Ce.wrapErrorBlock([`Error ${e}:`," Access denied. Check that your token has the correct scope."," If using a team, ensure the token is scoped to that team."])):o.error(Ce.wrapErrorBlock([`Error ${e}:`,` ${n instanceof Error?n.message:String(n)}`])),process.exit(1)}var To="add [type]",Po="Add a new firewall rule to your configuration",ko={type:{type:"string",description:'Rule type: "rule" (default) or "ip"',default:"rule",choices:["rule","ip"]},interactive:{alias:"i",type:"boolean",description:"Guided prompts for rule creation",default:!1},name:{alias:"n",type:"string",description:"Rule name"},description:{type:"string",description:"Rule description"},field:{type:"string",description:"Condition field type (path, method, user_agent, etc.)"},op:{type:"string",description:"Operator (eq, pre, suf, sub, inc, re, ex, nex)"},value:{type:"string",description:"Match value (string or comma-separated for arrays)"},key:{type:"string",description:"Header/query/cookie key (required for header, cookie types)"},neg:{type:"boolean",description:"Negate the condition",default:!1},action:{alias:"a",type:"string",description:"Action type (deny, challenge, rate_limit, redirect, log, bypass)",default:"deny"},active:{type:"boolean",description:"Enable rule immediately",default:!0},requests:{type:"number",description:"Rate limit: max requests"},window:{type:"string",description:'Rate limit: time window (e.g., "60s", "5m")'},duration:{type:"string",description:'Action duration (e.g., "1h", "permanent")'},location:{type:"string",description:"Redirect URL"},permanent:{type:"boolean",description:"Redirect: use 301 instead of 302"},ip:{type:"string",description:"IP address or CIDR for IP blocking rules"},hostname:{type:"string",description:"Hostname for IP blocking rules",default:"*"},notes:{type:"string",description:"Notes for IP blocking rules"},config:{alias:"c",type:"string",description:"Config file path"},dryRun:{alias:"d",type:"boolean",description:"Show what would be added without writing",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}},wt=["host","path","method","header","query","cookie","target_path","ip_address","region","protocol","scheme","environment","user_agent","geo_continent","geo_country","geo_country_region","geo_city","geo_as_number","ja4_digest","ja3_digest","rate_limit_api_id"],Ct=["eq","pre","suf","inc","sub","re","ex","nex"],Rt=["log","deny","challenge","bypass","rate_limit","redirect"];function It(n){let e=n.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"");if(!e)throw new Error("Cannot generate rule ID: name must contain at least one alphanumeric character");return"rule_"+e}function Ci(n,e){return e==="inc"?n.split(",").map(t=>t.trim()):n}async function So(){let n=await C("Rule name:",{type:"text"});if(!n.trim())throw new Error("Rule name cannot be empty");let e=await C("Description (optional):",{type:"text"}),t=[],i=!0;for(;i;){let u=[],p=!0;for(;p;){let g=await C("Condition type:",{type:"select",options:wt,initial:"path"}),h=await C("Operator:",{type:"select",options:Ct,initial:"eq"}),b;(g==="header"||g==="cookie"||g==="query")&&(b=await C(`${g} key:`,{type:"text"}));let f;if(h!=="ex"&&h!=="nex"){let v=await C("Value:",{type:"text"});f=Ci(v,h)}let T=h!=="ex"&&h!=="nex"?await C("Negate this condition?",{type:"confirm",initial:!1}):!1,j={type:g,op:h,...f!==void 0&&{value:f},...b&&{key:b},...T&&{neg:T}};u.push(j),p=await C("Add another condition to this group? (AND logic)",{type:"confirm",initial:!1})}t.push({conditions:u}),i=await C("Add another condition group? (OR logic)",{type:"confirm",initial:!1})}let r=await C("Action:",{type:"select",options:Rt,initial:"deny"}),s;if(r==="rate_limit"){let u=parseInt(await C("Max requests:",{type:"text",initial:"100"}),10),p=await C('Time window (e.g., "60s", "1m", "1h"):',{type:"text",initial:"60s"});s={requests:u,window:p}}let a;if(r==="redirect"){let u=await C("Redirect URL:",{type:"text"}),p=await C("Permanent redirect (301)?",{type:"confirm",initial:!1});a={location:u,...p&&{permanent:p}}}let l;(r==="deny"||r==="challenge")&&await C("Set action duration? (default: permanent)",{type:"confirm",initial:!1})&&(l=await C('Duration (e.g., "1h", "1d", "permanent"):',{type:"text",initial:"permanent"}));let c=await C("Enable rule immediately?",{type:"confirm",initial:!0});return{id:It(n),name:n.trim(),...e.trim()&&{description:e.trim()},conditionGroup:t,action:{mitigate:{action:r,...s&&{rateLimit:s},...a&&{redirect:a},...l&&{actionDuration:l}}},active:c}}function _o(n){if(!n.name)throw new Error("--name is required in inline mode. Use --interactive for guided prompts.");if(!n.field)throw new Error("--field is required in inline mode (e.g., --field path)");if(!n.op)throw new Error("--op is required in inline mode (e.g., --op eq)");let e=n.op;if(!Ct.includes(e))throw new Error(`Invalid operator "${n.op}". Valid operators: ${Ct.join(", ")}`);let t=n.field;if(!wt.includes(t))throw new Error(`Invalid field type "${n.field}". Valid types: ${wt.join(", ")}`);let i=n.action||"deny";if(!Rt.includes(i))throw new Error(`Invalid action "${n.action}". Valid actions: ${Rt.join(", ")}`);if(e!=="ex"&&e!=="nex"&&!n.value)throw new Error("--value is required for the given operator");if((t==="header"||t==="cookie"||t==="query")&&!n.key)throw new Error(`--key is required for "${t}" condition type`);if(i==="rate_limit"&&(!n.requests||!n.window))throw new Error("--requests and --window are required when action is rate_limit");if(i==="redirect"&&!n.location)throw new Error("--location is required when action is redirect");let r=n.value?Ci(n.value,e):void 0,s={type:t,op:e,...r!==void 0&&{value:r},...n.key&&{key:n.key},...n.neg&&{neg:!0}};return{id:It(n.name),name:n.name.trim(),...n.description&&{description:n.description},conditionGroup:[{conditions:[s]}],action:{mitigate:{action:i,...i==="rate_limit"&&n.requests&&n.window&&{rateLimit:{requests:n.requests,window:n.window}},...i==="redirect"&&n.location&&{redirect:{location:n.location,...n.permanent&&{permanent:!0}}},...n.duration&&{actionDuration:n.duration}}},active:n.active!==!1}}async function Lo(){let n=await C("IP address or CIDR (e.g., 192.168.1.100/32):",{type:"text"});if(!n.trim())throw new Error("IP address cannot be empty");let e=await C("Hostname (use * for all):",{type:"text",initial:"*"}),t=await C("Notes (optional):",{type:"text"});return{ip:n.trim(),hostname:e.trim()||"*",...t.trim()&&{notes:t.trim()},action:"deny"}}function Do(n){if(!n.ip)throw new Error("--ip is required for IP blocking rules");return{ip:n.ip.trim(),hostname:n.hostname||"*",...n.notes&&{notes:n.notes},action:"deny"}}function Uo(n,e){if(n.rules.map(r=>r.name.toLowerCase()).includes(e.name.toLowerCase()))return`A rule named "${e.name}" already exists`;let i=n.rules.map(r=>r.id).filter(Boolean);return e.id&&i.includes(e.id)?`A rule with ID "${e.id}" already exists`:null}function hi(n){o.log(""),o.log(V.default.bold(` Rule: ${n.name}`)),n.description&&o.log(V.default.dim(` Description: ${n.description}`)),o.log(V.default.dim(` ID: ${n.id}`));let e=n.conditionGroup.map(r=>r.conditions.map(s=>{let a=s.neg?"NOT ":"",l=s.key?`[${s.key}] `:"",c=Array.isArray(s.value)?s.value.join(", "):s.value||"";return`${a}${s.type} ${l}${s.op} "${c}"`}).join(" AND "));o.log(V.default.dim(` Conditions: ${e.join(" OR ")}`));let t=n.action.mitigate.action,i=[];n.action.mitigate.rateLimit&&i.push(`${n.action.mitigate.rateLimit.requests} req/${n.action.mitigate.rateLimit.window}`),n.action.mitigate.redirect&&i.push(`\u2192 ${n.action.mitigate.redirect.location}`),n.action.mitigate.actionDuration&&i.push(`duration: ${n.action.mitigate.actionDuration}`),o.log(V.default.dim(` Action: ${t}${i.length?` (${i.join(", ")})`:""}`)),o.log(V.default.dim(` Active: ${n.active?"\u2705":"\u274C"}`))}function yi(n){o.log(""),o.log(V.default.bold(` IP Rule: ${n.ip}`)),o.log(V.default.dim(` Hostname: ${n.hostname}`)),n.notes&&o.log(V.default.dim(` Notes: ${n.notes}`)),o.log(V.default.dim(` Action: ${n.action}`))}var xo=async n=>{try{if(n.debug&&(o.level=wi.LogLevels.debug),o.debug("Add command arguments:",n),(n.type||"rule")==="ip"){let t=n.interactive?await Lo():Do(n),i=ut.safeParse(t);if(i.success||(o.error(V.default.red("Rule validation failed:")),i.error.errors.forEach(a=>{let l=a.path.join(".");o.error(V.default.red(` - ${l||"ip"}: ${a.message}`))}),process.exit(1)),n.dryRun){o.info(V.default.cyan(`
19
- Dry run - The following IP rule would be added:`)),yi(t),o.log(""),o.log(V.default.dim(JSON.stringify(t,null,2)));return}o.start("Loading configuration...");let r=await G(n.config,"raw"),s={...r,ips:[...r.ips||[],t]};await z(s,n.config),o.success(V.default.green(`\u2714 IP rule "${t.ip}" added to configuration`)),yi(t)}else{let t=n.interactive?await So():_o(n),i=dt.safeParse(t);if(i.success||(o.error(V.default.red("Rule validation failed:")),i.error.errors.forEach(c=>{let d=c.path.join(".")||"rule";o.error(V.default.red(` - ${d}: ${c.message}`))}),process.exit(1)),n.dryRun){o.info(V.default.cyan(`
20
- Dry run - The following rule would be added:`)),hi(t),o.log(""),o.log(V.default.dim(JSON.stringify(t,null,2)));return}o.start("Loading configuration...");let r=await G(n.config,"raw"),s=r.rules||[],a=Uo({...r,rules:s},t);if(a&&(o.warn(V.default.yellow(`\u26A0\uFE0F ${a}`)),!await C("Proceed anyway?",{type:"confirm",initial:!1}))){o.info("Cancelled.");return}let l={...r,rules:[...s,t]};await z(l,n.config),o.success(V.default.green(`\u2714 Rule "${t.name}" added to configuration`)),hi(t)}o.log(""),o.log(V.default.dim(`Run ${V.default.cyan("doorman sync")} to deploy this rule.`))}catch(e){Q(e,"adding rule")}};var kt={};Z(kt,{builder:()=>Zo,command:()=>Go,desc:()=>Ho,handler:()=>Ko});var te=S(require("chalk")),he=require("fs"),rt=require("path"),Li=require("consola");k();ce();var _i=require("consola");k();var Y=S(require("chalk")),Ii=require("consola");k();function ae(n,e){if(n===e)return!0;if(typeof n!="object"||n===null||typeof e!="object"||e===null||Array.isArray(n)!==Array.isArray(e))return!1;let t=Object.keys(n),i=Object.keys(e);if(t.length!==i.length)return!1;for(let r of t)if(!i.includes(r)||!ae(n[r],e[r]))return!1;return!0}function B(n){let{id:e,...t}=n;return t}k();async function oe(n,e={}){let{maxAttempts:t=3,delayMs:i=1e3,backoff:r=!0}=e,s;for(let a=1;a<=t;a++)try{return await n()}catch(l){if(s=l instanceof Error?l:new Error(String(l)),a===t)break;let c=r?i*a:i;o.debug(`Retry attempt ${a}/${t} failed. Retrying in ${c}ms...`),await new Promise(d=>setTimeout(d,c))}throw new Error(`Operation failed after ${t} attempts. Last error: ${s?.message}`)}function Ri(n){if(!n||n.trim().length===0)return"";let e=n.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2"),t=["IP","HTTP","XML","API"];return t.forEach(i=>{let r=new RegExp(`\\b${i}\\b`,"g");e=e.replace(r,i.toLowerCase())}),e=e.replace(/[A-Z]{2,}/g,i=>t.includes(i)?i.toLowerCase():i.split("").join("_")).toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"").replace(/_+/g,"_"),t.forEach(i=>{let r=i.toLowerCase();e=e.replace(new RegExp(`([a-z])${r}`,"g"),`$1_${r}`)}),e}var et=class{constructor(e){this.client=e}client;async getChanges(e){try{let t=we.safeParse(e);if(!t.success)throw new Error(`Invalid firewall configuration: ${t.error.message}`);o.debug("Fetching existing firewall configuration");let i=await this.client.fetchFirewallConfig();o.debug(`Fetched ${i.rules.length} custom rules and ${i.ips.length} IP blocking rules`);let r=e.rules,{toAdd:s,toUpdate:a,toDelete:l}=this.diffRules(r,i.rules);o.debug("Pre Filter IP Rules:",{ips:e.ips,activeConfig:i.ips});let{ipsToAdd:c,ipsToUpdate:d,ipsToDelete:u}=this.diffIPRules(e.ips||[],i.ips);return{version:i.version,toAdd:s,toUpdate:a,toDelete:l,ipsToAdd:c,ipsToUpdate:d,ipsToDelete:u}}catch(t){throw o.error("Error fetching existing firewall configuration:",t),t instanceof Error&&t.message.includes("Invalid firewall configuration")?t:new Error("Failed to fetch existing firewall configuration. Please check your network connection and try again.")}}async syncRules(e,t={}){let{dryRun:i=!1,retryAttempts:r=3,debug:s=!1}=t;s&&(o.level=Ii.LogLevels.debug);try{let{toAdd:a,toUpdate:l,toDelete:c,ipsToAdd:d,ipsToUpdate:u,ipsToDelete:p}=await this.getChanges(e);if(i)return o.info("Dry run mode. The following changes would be made:"),o.info(`Custom Rules - Add: ${a.length}, Update: ${l.length}, Delete: ${c.length}`),o.info(`IP Rules - Add: ${d.length}, Update: ${u.length}, Delete: ${p.length}`),{addedRules:[],updatedRules:[],deletedRules:[],addedIPRules:[],updatedIPRules:[],deletedIPRules:[],rulesToUpdateLocally:[]};let g=[],h=[],b=[],f=[],T=[],j=[],v=[];for(let m of c)o.debug(`Deleting custom rule: ${m.id}`),await oe(()=>this.client.deleteFirewallRule(m),{maxAttempts:r}),b.push(m),o.debug(`Custom rule deleted: ${m.id}`);for(let m of p)o.debug(`Deleting IP blocking rule: ${m.id}`),await oe(()=>this.client.deleteIPBlockingRule(m),{maxAttempts:r}),j.push(m),o.debug(`IP blocking rule deleted: ${m.id}`);for(let m of a){let P=`rule_${Ri(m.name)}`;o.debug(`Adding new custom rule: ${m.name}`);let w=await oe(()=>this.client.createFirewallRule(m),{maxAttempts:r});g.push(w),o.debug(`New custom rule added: ${w.id}`),m.id!==P&&(v.push({oldId:m.id??"",newId:P,name:m.name}),o.debug(`Rule ID needs update locally: ${m.id} -> ${P}`))}for(let m of d){o.debug(`Adding new IP blocking rule: ${m.ip}`);let P=await oe(()=>this.client.createIPBlockingRule(m),{maxAttempts:r});f.push(P),o.debug(`New IP blocking rule added: (hostname): ${P.hostname} (ip): ${P.ip}`)}for(let m of l){o.debug(`Updating custom rule: ${m.id}`);let P=await oe(()=>this.client.updateFirewallRule(m),{maxAttempts:r});h.push(P),o.debug(`Custom rule updated: ${P.id}`)}for(let m of u){o.debug(`Updating IP blocking rule: ${m.id}`);let P=await oe(()=>this.client.updateIPBlockingRule(m),{maxAttempts:r});T.push(P),o.debug(`IP blocking rule updated: ${P.id}`)}return o.debug(`${Y.default.underline("Custom Rules:")} ${Y.default.green("Added:")} ${Y.default.green(g.length)}, ${Y.default.cyan("Updated:")} ${Y.default.cyan(h.length)}, ${Y.default.red("Deleted:")} ${Y.default.red(b.length)}`),o.debug(`${Y.default.underline("IP Rules:")} ${Y.default.green("Added:")} ${Y.default.green(f.length)}, ${Y.default.cyan("Updated:")} ${Y.default.cyan(T.length)}, ${Y.default.red("Deleted:")} ${Y.default.red(j.length)}`),{addedRules:g,updatedRules:h,deletedRules:b,addedIPRules:f,updatedIPRules:T,deletedIPRules:j,rulesToUpdateLocally:v}}catch(a){throw o.error("Error during sync:",a),new Error("Failed to synchronize firewall rules. Please check the error logs and try again.")}}diffRules(e,t){let i=[],r=[],s=[...t];for(let a of e){let l=t.find(c=>c.id===a.id);if(!l)i.push(a);else{ae(B(a),B(l))||r.push({...a,id:l.id});let c=s.findIndex(d=>d.id===l.id);c!==-1&&s.splice(c,1)}}return{toAdd:i,toUpdate:r,toDelete:s}}diffIPRules(e,t){let i=[],r=[],s=[...t];for(let a of e){let l=t.find(c=>c.id===a.id);if(!l&&!a.id&&(l=t.find(c=>c.ip===a.ip&&c.hostname===a.hostname&&c.action===a.action)),!l)o.debug("Rule not found in existing rules:",{configRule:a}),i.push(a);else{ae(B(a),B(l))||r.push({...l,...a});let c=s.findIndex(d=>d.id===l.id);c!==-1&&s.splice(c,1)}}return{ipsToAdd:i,ipsToUpdate:r,ipsToDelete:s}}async validateAndUpdateConfig(e,t,i={}){if(i.dryRun)return o.info(Y.default.cyan("Dry run: Config metadata would be updated after successful sync")),e;await new Promise(w=>setTimeout(w,2e3));let r=await oe(()=>this.client.fetchFirewallConfig(),{maxAttempts:5,delayMs:1500,backoff:!0}),s=r.rules,a=r.ips,l=new Set(t.addedRules.map(w=>w.id)),c=new Set(t.updatedRules.map(w=>w.id)),d=new Set(t.deletedRules.map(w=>w.id)),u=new Set(t.addedIPRules.map(w=>w.id).filter(Boolean)),p=new Set(t.updatedIPRules.map(w=>w.id).filter(Boolean)),g=new Set(t.deletedIPRules.map(w=>w.id).filter(Boolean)),h=new Map(t.addedRules.map(w=>[w.name,w])),b=t.addedIPRules.map(w=>B(w)),f=s.filter(w=>{if(l.has(w.id)||c.has(w.id))return!1;let I=h.get(w.name);if(I&&ae(B(w),B(I)))return!1;if(d.has(w.id))return!0;let re=e.rules.find(ye=>ye.id===w.id);return re?!ae(B(w),B(re)):!e.rules.find(at=>ae(B(w),B(at)))}),T=a.filter(w=>u.has(w.id)||p.has(w.id)||b.some(ye=>ae(B(w),ye))?!1:!!(g.has(w.id)||!e.ips?.find(ye=>ae(B(w),B(ye))))),j=[];if(f.length>0){let w=f.map(I=>` - ${I.name} (${I.id})`).join(`
21
- `);j.push(`The following custom rules have unexpected state:
22
- `+w)}if(T.length>0){let w=T.map(I=>` - ${I.ip} (${I.id})`).join(`
23
- `);j.push(`The following IP blocking rules have unexpected state:
24
- `+w)}if(j.length>0)throw new Error(`Firewall configuration validation failed:
25
- `+j.join(`
16
+ ${r}`}};var V=class n extends Error{constructor(t,i,r,s=[]){super(t);this.ajvErrors=i;this.zodError=r;this.customErrors=s;this.name="ValidationError"}ajvErrors;zodError;customErrors;static formatErrors(t,i,r=[]){let s=[];return t?.length&&s.push(...t.map(Ce.formatValidationError)),i&&s.push(...i.errors.map(a=>`${a.path.join(".")}: ${a.message}`)),r.length&&s.push(...r),Ce.wrapErrorBlock(s)}getFormattedMessage(){return n.formatErrors(this.ajvErrors,this.zodError,this.customErrors)}},Re=class n{static instance;ajv;schema;constructor(){this.ajv=new pi.default({allErrors:!0,verbose:!0}),this.schema=oi}static getInstance(){return n.instance||(n.instance=new n),n.instance}validateConfig(e){if($e(e))try{ui(e);return}catch(c){throw new V(`Invalid firewall configuration:
17
+ `+(c instanceof Error?c.message:String(c)),null)}let t=this.ajv.compile(this.schema),i=t(e),r=t.errors,s=we.safeParse(e),a;s.success||(a=s.error);let l=[];if(i&&s.success){try{this.validateRuleNames(e),this.validateRuleConditionGroup(e),this.validateRuleAction(e)}catch(c){if(c instanceof V)l.push(c.message,...c.customErrors||[]);else throw c}for(let c of e.rules){if(c.action.mitigate?.rateLimit){let d=dt.safeParse(c.action.mitigate.rateLimit);d.success||l.push(...d.error.errors.map(u=>u.message))}if(c.conditionGroup)for(let d of c.conditionGroup){let u=ct.safeParse(d);u.success||l.push(...u.error.errors.map(p=>p.message))}}}if(!i||!s.success||l.length>0)throw new V(`Invalid firewall configuration:
18
+ `+V.formatErrors(r,a,l),r,a,l)}validateRuleNames(e){let t=new Set;for(let i of e.rules){if(t.has(i.name))throw new V(`Duplicate rule name found: "${i.name}"`,null);t.add(i.name)}}validateRuleConditionGroup(e){for(let t of e.rules)if(t.conditionGroup)for(let i of t.conditionGroup){if(!i.conditions||i.conditions.length===0)throw new V(`Rule "${t.name}" has an empty condition group`,null);for(let r of i.conditions){if(!r.type||!r.op)throw new V(`Rule "${t.name}" has an invalid condition`,null);let s=r.type==="header"||r.type==="cookie",a=r.op==="ex"||r.op==="nex";if(s){if(!r.key||typeof r.key!="string")throw new V(`Rule "${t.name}" has an invalid condition`,null);if(!a&&(r.value===void 0||r.value===null))throw new V(`Rule "${t.name}" has an invalid condition`,null)}else if(r.value===void 0||r.value===null)throw new V(`Rule "${t.name}" has an invalid condition`,null);let l=Array.isArray(r.value)?r.value:r.value!==void 0?[r.value]:[];if(Array.isArray(r.value)&&l.length===0)throw new V(`Rule "${t.name}" has an invalid value in condition`,null);if(r.type==="ip_address"){for(let c of l)if(typeof c!="string"||!this.isValidIP(c))throw new V(`Rule "${t.name}" has an invalid IP address in condition`,null)}if(r.type==="geo_as_number")for(let c of l){let d=typeof c=="number"?c:parseInt(String(c),10);if(!this.isValidASN(String(d)))throw new V(`Rule "${t.name}" has an invalid ASN in condition`,null)}if(r.type==="path"&&(r.op==="eq"||r.op==="pre")){for(let c of l)if(typeof c!="string"||!this.isValidPath(c))throw new V(`Rule "${t.name}" has an invalid path in condition`,null)}}}else throw new V("Either conditionGroup or type+values must be provided",null)}validateRuleAction(e){for(let t of e.rules)if(typeof t.action=="object"&&"mitigate"in t.action){let i=t.action.mitigate;if(i?.rateLimit){if(i.rateLimit.requests<=0)throw new V("Invalid rate limit configuration: requests must be positive",null);if(!i.rateLimit.window.match(/^\d+[smhd]$/))throw new V("Invalid rate limit configuration: invalid window format",null);if(parseInt(i.rateLimit.window)<=0)throw new V("Invalid rate limit configuration: window duration must be positive",null)}if(i?.actionDuration&&!i.actionDuration.match(/^\d+[smhd]$|^permanent$/))throw new V("Invalid action duration format: "+i.actionDuration,null);if(i?.redirect&&!i.redirect.location)throw new V("Invalid redirect configuration: location is required",null)}}isValidIP(e){let t=e.split("/"),i=t[0]??"",r=t[1],s=d=>{let u=d.split(".");return u.length!==4?!1:u.every(p=>{if(!/^\d{1,3}$/.test(p))return!1;let f=parseInt(p,10);return f>=0&&f<=255})},a=d=>{let u=d.split(":");return u.length!==8?!1:u.every(p=>/^[0-9a-fA-F]{1,4}$/.test(p))},l=s(i),c=a(i);if(!l&&!c)return!1;if(r!==void 0){let d=parseInt(r,10);if(Number.isNaN(d)||l&&(d<0||d>32)||c&&(d<0||d>128))return!1}return!0}isValidASN(e){let t=parseInt(e,10);return!isNaN(t)&&t>=1&&t<=4294967295}isValidPath(e){return e.startsWith("/")&&!e.includes(" ")}};var gi=require("fs"),Me=require("path"),yt=[".doorman.json","vercel-firewall.config.json"],$o=yt[0],Ae=class{static async findConfig(e){let{findUp:t}=await import("find-up");for(let i of yt){let r=await t(i,{cwd:e||process.cwd()});if(r)return r}}static async findProjectRoot(e){let t=await this.findConfig(e);return t?(0,Me.dirname)(t):null}static configExists(e){return(0,gi.existsSync)((0,Me.resolve)(e))}static getDefaultConfigPath(){return(0,Me.resolve)(process.cwd(),$o)}static getSupportedFileNames(){return yt}};async function G(n,e="required"){let t=Ao(e),i=n||await Ae.findConfig();if(!i||!(0,Ie.existsSync)(i)){if(t==="optional")return o.debug("No config file found, returning empty config"),{};let s=Ae.getDefaultConfigPath();throw new Error(`No config file found. Run \`doorman init\` to create one at ${s}, or use --config to specify a custom path.`)}let r;try{let s=(0,Ie.readFileSync)(i,"utf8");r=JSON.parse(s)}catch(s){throw s instanceof SyntaxError?new Error(`Invalid JSON in config file (${i}): ${s.message}`):s}if(t!=="raw")try{Re.getInstance().validateConfig(r)}catch(s){if(t==="lenient")o.warn("Config validation failed:",s);else throw s}return r}async function z(n,e,t={validate:!0,throwOnError:!0}){let i=e||await Ae.findConfig()||Ae.getDefaultConfigPath();if(t.validate)try{Re.getInstance().validateConfig(n)}catch(s){if(o.error("Config validation failed:",s),t.throwOnError)throw s}let r=(0,fi.dirname)(i);(0,Ie.existsSync)(r)||(0,Ie.mkdirSync)(r,{recursive:!0}),(0,Ie.writeFileSync)(i,JSON.stringify(n,null,2)),o.debug(`Config saved to ${i}`)}function Ao(n){if(typeof n=="string")return n;let{validate:e=!0,throwOnError:t=!0}=n;return e?t?"required":"lenient":"raw"}var wt=S(require("chalk")),mi=require("zod");k();function Q(n,e){n instanceof SyntaxError?o.log(Ce.wrapErrorBlock(["Invalid JSON format in config file:",` ${n.message}`])):n instanceof mi.ZodError?(o.error(wt.default.red("Schema validation failed:")),n.errors.forEach(t=>{let i=t.path.join(".");o.error(wt.default.red(` - ${i}: ${t.message}`))})):n instanceof Error&&n.name==="ValidationError"?o.error(n):n instanceof Error&&n.message.includes("Forbidden")?o.log(Ce.wrapErrorBlock([`Error ${e}:`," Access denied. Check that your token has the correct scope."," If using a team, ensure the token is scoped to that team."])):o.error(Ce.wrapErrorBlock([`Error ${e}:`,` ${n instanceof Error?n.message:String(n)}`])),process.exit(1)}var To="add [type]",Po="Add a new firewall rule to your configuration",ko={type:{type:"string",description:'Rule type: "rule" (default) or "ip"',default:"rule",choices:["rule","ip"]},interactive:{alias:"i",type:"boolean",description:"Guided prompts for rule creation",default:!1},name:{alias:"n",type:"string",description:"Rule name"},description:{type:"string",description:"Rule description"},field:{type:"string",description:"Condition field type (path, method, user_agent, etc.)"},op:{type:"string",description:"Operator (eq, pre, suf, sub, inc, re, ex, nex)"},value:{type:"string",description:"Match value (string or comma-separated for arrays)"},key:{type:"string",description:"Header/query/cookie key (required for header, cookie types)"},neg:{type:"boolean",description:"Negate the condition",default:!1},action:{alias:"a",type:"string",description:"Action type (deny, challenge, rate_limit, redirect, log, bypass)",default:"deny"},active:{type:"boolean",description:"Enable rule immediately",default:!0},requests:{type:"number",description:"Rate limit: max requests"},window:{type:"string",description:'Rate limit: time window (e.g., "60s", "5m")'},duration:{type:"string",description:'Action duration (e.g., "1h", "permanent")'},location:{type:"string",description:"Redirect URL"},permanent:{type:"boolean",description:"Redirect: use 301 instead of 302"},ip:{type:"string",description:"IP address or CIDR for IP blocking rules"},hostname:{type:"string",description:"Hostname for IP blocking rules",default:"*"},notes:{type:"string",description:"Notes for IP blocking rules"},config:{alias:"c",type:"string",description:"Config file path"},dryRun:{alias:"d",type:"boolean",description:"Show what would be added without writing",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}},Ct=["host","path","method","header","query","cookie","target_path","ip_address","region","protocol","scheme","environment","user_agent","geo_continent","geo_country","geo_country_region","geo_city","geo_as_number","ja4_digest","ja3_digest","rate_limit_api_id"],Rt=["eq","pre","suf","inc","sub","re","ex","nex"],It=["log","deny","challenge","bypass","rate_limit","redirect"];function bt(n){let e=n.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"");if(!e)throw new Error("Cannot generate rule ID: name must contain at least one alphanumeric character");return"rule_"+e}function Ci(n,e){return e==="inc"?n.split(",").map(t=>t.trim()):n}async function So(){let n=await C("Rule name:",{type:"text"});if(!n.trim())throw new Error("Rule name cannot be empty");let e=await C("Description (optional):",{type:"text"}),t=[],i=!0;for(;i;){let u=[],p=!0;for(;p;){let f=await C("Condition type:",{type:"select",options:Ct,initial:"path"}),h=await C("Operator:",{type:"select",options:Rt,initial:"eq"}),I;(f==="header"||f==="cookie"||f==="query")&&(I=await C(`${f} key:`,{type:"text"}));let g;if(h!=="ex"&&h!=="nex"){let v=await C("Value:",{type:"text"});g=Ci(v,h)}let A=h!=="ex"&&h!=="nex"?await C("Negate this condition?",{type:"confirm",initial:!1}):!1,N={type:f,op:h,...g!==void 0&&{value:g},...I&&{key:I},...A&&{neg:A}};u.push(N),p=await C("Add another condition to this group? (AND logic)",{type:"confirm",initial:!1})}t.push({conditions:u}),i=await C("Add another condition group? (OR logic)",{type:"confirm",initial:!1})}let r=await C("Action:",{type:"select",options:It,initial:"deny"}),s;if(r==="rate_limit"){let u=parseInt(await C("Max requests:",{type:"text",initial:"100"}),10),p=await C('Time window (e.g., "60s", "1m", "1h"):',{type:"text",initial:"60s"});s={requests:u,window:p}}let a;if(r==="redirect"){let u=await C("Redirect URL:",{type:"text"}),p=await C("Permanent redirect (301)?",{type:"confirm",initial:!1});a={location:u,...p&&{permanent:p}}}let l;(r==="deny"||r==="challenge")&&await C("Set action duration? (default: permanent)",{type:"confirm",initial:!1})&&(l=await C('Duration (e.g., "1h", "1d", "permanent"):',{type:"text",initial:"permanent"}));let c=await C("Enable rule immediately?",{type:"confirm",initial:!0});return{id:bt(n),name:n.trim(),...e.trim()&&{description:e.trim()},conditionGroup:t,action:{mitigate:{action:r,...s&&{rateLimit:s},...a&&{redirect:a},...l&&{actionDuration:l}}},active:c}}function _o(n){if(!n.name)throw new Error("--name is required in inline mode. Use --interactive for guided prompts.");if(!n.field)throw new Error("--field is required in inline mode (e.g., --field path)");if(!n.op)throw new Error("--op is required in inline mode (e.g., --op eq)");let e=n.op;if(!Rt.includes(e))throw new Error(`Invalid operator "${n.op}". Valid operators: ${Rt.join(", ")}`);let t=n.field;if(!Ct.includes(t))throw new Error(`Invalid field type "${n.field}". Valid types: ${Ct.join(", ")}`);let i=n.action||"deny";if(!It.includes(i))throw new Error(`Invalid action "${n.action}". Valid actions: ${It.join(", ")}`);if(e!=="ex"&&e!=="nex"&&!n.value)throw new Error("--value is required for the given operator");if((t==="header"||t==="cookie"||t==="query")&&!n.key)throw new Error(`--key is required for "${t}" condition type`);if(i==="rate_limit"&&(!n.requests||!n.window))throw new Error("--requests and --window are required when action is rate_limit");if(i==="redirect"&&!n.location)throw new Error("--location is required when action is redirect");let r=n.value?Ci(n.value,e):void 0,s={type:t,op:e,...r!==void 0&&{value:r},...n.key&&{key:n.key},...n.neg&&{neg:!0}};return{id:bt(n.name),name:n.name.trim(),...n.description&&{description:n.description},conditionGroup:[{conditions:[s]}],action:{mitigate:{action:i,...i==="rate_limit"&&n.requests&&n.window&&{rateLimit:{requests:n.requests,window:n.window}},...i==="redirect"&&n.location&&{redirect:{location:n.location,...n.permanent&&{permanent:!0}}},...n.duration&&{actionDuration:n.duration}}},active:n.active!==!1}}async function Lo(){let n=await C("IP address or CIDR (e.g., 192.168.1.100/32):",{type:"text"});if(!n.trim())throw new Error("IP address cannot be empty");let e=await C("Hostname (use * for all):",{type:"text",initial:"*"}),t=await C("Notes (optional):",{type:"text"});return{ip:n.trim(),hostname:e.trim()||"*",...t.trim()&&{notes:t.trim()},action:"deny"}}function Do(n){if(!n.ip)throw new Error("--ip is required for IP blocking rules");return{ip:n.ip.trim(),hostname:n.hostname||"*",...n.notes&&{notes:n.notes},action:"deny"}}function Uo(n,e){if(n.rules.map(r=>r.name.toLowerCase()).includes(e.name.toLowerCase()))return`A rule named "${e.name}" already exists`;let i=n.rules.map(r=>r.id).filter(Boolean);return e.id&&i.includes(e.id)?`A rule with ID "${e.id}" already exists`:null}function hi(n){o.log(""),o.log(B.default.bold(` Rule: ${n.name}`)),n.description&&o.log(B.default.dim(` Description: ${n.description}`)),o.log(B.default.dim(` ID: ${n.id}`));let e=n.conditionGroup.map(r=>r.conditions.map(s=>{let a=s.neg?"NOT ":"",l=s.key?`[${s.key}] `:"",c=Array.isArray(s.value)?s.value.join(", "):s.value||"";return`${a}${s.type} ${l}${s.op} "${c}"`}).join(" AND "));o.log(B.default.dim(` Conditions: ${e.join(" OR ")}`));let t=n.action.mitigate.action,i=[];n.action.mitigate.rateLimit&&i.push(`${n.action.mitigate.rateLimit.requests} req/${n.action.mitigate.rateLimit.window}`),n.action.mitigate.redirect&&i.push(`\u2192 ${n.action.mitigate.redirect.location}`),n.action.mitigate.actionDuration&&i.push(`duration: ${n.action.mitigate.actionDuration}`),o.log(B.default.dim(` Action: ${t}${i.length?` (${i.join(", ")})`:""}`)),o.log(B.default.dim(` Active: ${n.active?"\u2705":"\u274C"}`))}function yi(n){o.log(""),o.log(B.default.bold(` IP Rule: ${n.ip}`)),o.log(B.default.dim(` Hostname: ${n.hostname}`)),n.notes&&o.log(B.default.dim(` Notes: ${n.notes}`)),o.log(B.default.dim(` Action: ${n.action}`))}var xo=async n=>{try{if(n.debug&&(o.level=wi.LogLevels.debug),o.debug("Add command arguments:",n),(n.type||"rule")==="ip"){let t=n.interactive?await Lo():Do(n),i=pt.safeParse(t);if(i.success||(o.error(B.default.red("Rule validation failed:")),i.error.errors.forEach(a=>{let l=a.path.join(".");o.error(B.default.red(` - ${l||"ip"}: ${a.message}`))}),process.exit(1)),n.dryRun){o.info(B.default.cyan(`
19
+ Dry run - The following IP rule would be added:`)),yi(t),o.log(""),o.log(B.default.dim(JSON.stringify(t,null,2)));return}o.start("Loading configuration...");let r=await G(n.config,"raw"),s={...r,ips:[...r.ips||[],t]};await z(s,n.config),o.success(B.default.green(`\u2714 IP rule "${t.ip}" added to configuration`)),yi(t)}else{let t=n.interactive?await So():_o(n),i=ut.safeParse(t);if(i.success||(o.error(B.default.red("Rule validation failed:")),i.error.errors.forEach(c=>{let d=c.path.join(".")||"rule";o.error(B.default.red(` - ${d}: ${c.message}`))}),process.exit(1)),n.dryRun){o.info(B.default.cyan(`
20
+ Dry run - The following rule would be added:`)),hi(t),o.log(""),o.log(B.default.dim(JSON.stringify(t,null,2)));return}o.start("Loading configuration...");let r=await G(n.config,"raw"),s=r.rules||[],a=Uo({...r,rules:s},t);if(a&&(o.warn(B.default.yellow(`\u26A0\uFE0F ${a}`)),!await C("Proceed anyway?",{type:"confirm",initial:!1}))){o.info("Cancelled.");return}let l={...r,rules:[...s,t]};await z(l,n.config),o.success(B.default.green(`\u2714 Rule "${t.name}" added to configuration`)),hi(t)}o.log(""),o.log(B.default.dim(`Run ${B.default.cyan("doorman sync")} to deploy this rule.`))}catch(e){Q(e,"adding rule")}};var St={};Z(St,{builder:()=>Zo,command:()=>Go,desc:()=>Ho,handler:()=>Ko});var te=S(require("chalk")),he=require("fs"),st=require("path"),Li=require("consola");k();ce();var _i=require("consola");k();var Y=S(require("chalk")),Ii=require("consola");k();function ae(n,e){if(n===e)return!0;if(typeof n!="object"||n===null||typeof e!="object"||e===null||Array.isArray(n)!==Array.isArray(e))return!1;let t=Object.keys(n),i=Object.keys(e);if(t.length!==i.length)return!1;for(let r of t)if(!i.includes(r)||!ae(n[r],e[r]))return!1;return!0}function j(n){let{id:e,...t}=n;return t}k();async function oe(n,e={}){let{maxAttempts:t=3,delayMs:i=1e3,backoff:r=!0}=e,s;for(let a=1;a<=t;a++)try{return await n()}catch(l){if(s=l instanceof Error?l:new Error(String(l)),a===t)break;let c=r?i*a:i;o.debug(`Retry attempt ${a}/${t} failed. Retrying in ${c}ms...`),await new Promise(d=>setTimeout(d,c))}throw new Error(`Operation failed after ${t} attempts. Last error: ${s?.message}`)}function Ri(n){if(!n||n.trim().length===0)return"";let e=n.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2"),t=["IP","HTTP","XML","API"];return t.forEach(i=>{let r=new RegExp(`\\b${i}\\b`,"g");e=e.replace(r,i.toLowerCase())}),e=e.replace(/[A-Z]{2,}/g,i=>t.includes(i)?i.toLowerCase():i.split("").join("_")).toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"").replace(/_+/g,"_"),t.forEach(i=>{let r=i.toLowerCase();e=e.replace(new RegExp(`([a-z])${r}`,"g"),`$1_${r}`)}),e}var tt=class{constructor(e){this.client=e}client;async getChanges(e){try{let t=we.safeParse(e);if(!t.success)throw new Error(`Invalid firewall configuration: ${t.error.message}`);o.debug("Fetching existing firewall configuration");let i=await this.client.fetchFirewallConfig();o.debug(`Fetched ${i.rules.length} custom rules and ${i.ips.length} IP blocking rules`);let r=e.rules,{toAdd:s,toUpdate:a,toDelete:l}=this.diffRules(r,i.rules);o.debug("Pre Filter IP Rules:",{ips:e.ips,activeConfig:i.ips});let{ipsToAdd:c,ipsToUpdate:d,ipsToDelete:u}=this.diffIPRules(e.ips||[],i.ips);return{version:i.version,toAdd:s,toUpdate:a,toDelete:l,ipsToAdd:c,ipsToUpdate:d,ipsToDelete:u}}catch(t){throw o.error("Error fetching existing firewall configuration:",t),t instanceof Error&&t.message.includes("Invalid firewall configuration")?t:new Error("Failed to fetch existing firewall configuration. Please check your network connection and try again.")}}async syncRules(e,t={}){let{dryRun:i=!1,retryAttempts:r=3,debug:s=!1}=t;s&&(o.level=Ii.LogLevels.debug);try{let{toAdd:a,toUpdate:l,toDelete:c,ipsToAdd:d,ipsToUpdate:u,ipsToDelete:p}=await this.getChanges(e);if(i)return o.info("Dry run mode. The following changes would be made:"),o.info(`Custom Rules - Add: ${a.length}, Update: ${l.length}, Delete: ${c.length}`),o.info(`IP Rules - Add: ${d.length}, Update: ${u.length}, Delete: ${p.length}`),{addedRules:[],updatedRules:[],deletedRules:[],addedIPRules:[],updatedIPRules:[],deletedIPRules:[],rulesToUpdateLocally:[]};let f=[],h=[],I=[],g=[],A=[],N=[],v=[];for(let m of c)o.debug(`Deleting custom rule: ${m.id}`),await oe(()=>this.client.deleteFirewallRule(m),{maxAttempts:r}),I.push(m),o.debug(`Custom rule deleted: ${m.id}`);for(let m of p)o.debug(`Deleting IP blocking rule: ${m.id}`),await oe(()=>this.client.deleteIPBlockingRule(m),{maxAttempts:r}),N.push(m),o.debug(`IP blocking rule deleted: ${m.id}`);for(let m of a){let P=`rule_${Ri(m.name)}`;o.debug(`Adding new custom rule: ${m.name}`);let w=await oe(()=>this.client.createFirewallRule(m),{maxAttempts:r});f.push(w),o.debug(`New custom rule added: ${w.id}`),m.id!==P&&(v.push({oldId:m.id??"",newId:P,name:m.name}),o.debug(`Rule ID needs update locally: ${m.id} -> ${P}`))}for(let m of d){o.debug(`Adding new IP blocking rule: ${m.ip}`);let P=await oe(()=>this.client.createIPBlockingRule(m),{maxAttempts:r});g.push(P),o.debug(`New IP blocking rule added: (hostname): ${P.hostname} (ip): ${P.ip}`)}for(let m of l){o.debug(`Updating custom rule: ${m.id}`);let P=await oe(()=>this.client.updateFirewallRule(m),{maxAttempts:r});h.push(P),o.debug(`Custom rule updated: ${P.id}`)}for(let m of u){o.debug(`Updating IP blocking rule: ${m.id}`);let P=await oe(()=>this.client.updateIPBlockingRule(m),{maxAttempts:r});A.push(P),o.debug(`IP blocking rule updated: ${P.id}`)}return o.debug(`${Y.default.underline("Custom Rules:")} ${Y.default.green("Added:")} ${Y.default.green(f.length)}, ${Y.default.cyan("Updated:")} ${Y.default.cyan(h.length)}, ${Y.default.red("Deleted:")} ${Y.default.red(I.length)}`),o.debug(`${Y.default.underline("IP Rules:")} ${Y.default.green("Added:")} ${Y.default.green(g.length)}, ${Y.default.cyan("Updated:")} ${Y.default.cyan(A.length)}, ${Y.default.red("Deleted:")} ${Y.default.red(N.length)}`),{addedRules:f,updatedRules:h,deletedRules:I,addedIPRules:g,updatedIPRules:A,deletedIPRules:N,rulesToUpdateLocally:v}}catch(a){throw o.error("Error during sync:",a),new Error("Failed to synchronize firewall rules. Please check the error logs and try again.")}}diffRules(e,t){let i=[],r=[],s=[...t];for(let a of e){let l=t.find(c=>c.id===a.id);if(!l)i.push(a);else{ae(j(a),j(l))||r.push({...a,id:l.id});let c=s.findIndex(d=>d.id===l.id);c!==-1&&s.splice(c,1)}}return{toAdd:i,toUpdate:r,toDelete:s}}diffIPRules(e,t){let i=[],r=[],s=[...t];for(let a of e){let l=t.find(c=>c.id===a.id);if(!l&&!a.id&&(l=t.find(c=>c.ip===a.ip&&c.hostname===a.hostname&&c.action===a.action)),!l)o.debug("Rule not found in existing rules:",{configRule:a}),i.push(a);else{ae(j(a),j(l))||r.push({...l,...a});let c=s.findIndex(d=>d.id===l.id);c!==-1&&s.splice(c,1)}}return{ipsToAdd:i,ipsToUpdate:r,ipsToDelete:s}}async validateAndUpdateConfig(e,t,i={}){if(i.dryRun)return o.info(Y.default.cyan("Dry run: Config metadata would be updated after successful sync")),e;await new Promise(w=>setTimeout(w,2e3));let r=await oe(()=>this.client.fetchFirewallConfig(),{maxAttempts:5,delayMs:1500,backoff:!0}),s=r.rules,a=r.ips,l=new Set(t.addedRules.map(w=>w.id)),c=new Set(t.updatedRules.map(w=>w.id)),d=new Set(t.deletedRules.map(w=>w.id)),u=new Set(t.addedIPRules.map(w=>w.id).filter(Boolean)),p=new Set(t.updatedIPRules.map(w=>w.id).filter(Boolean)),f=new Set(t.deletedIPRules.map(w=>w.id).filter(Boolean)),h=new Map(t.addedRules.map(w=>[w.name,w])),I=t.addedIPRules.map(w=>j(w)),g=s.filter(w=>{if(l.has(w.id)||c.has(w.id))return!1;let b=h.get(w.name);if(b&&ae(j(w),j(b)))return!1;if(d.has(w.id))return!0;let re=e.rules.find(ye=>ye.id===w.id);return re?!ae(j(w),j(re)):!e.rules.find(lt=>ae(j(w),j(lt)))}),A=a.filter(w=>u.has(w.id)||p.has(w.id)||I.some(ye=>ae(j(w),ye))?!1:!!(f.has(w.id)||!e.ips?.find(ye=>ae(j(w),j(ye))))),N=[];if(g.length>0){let w=g.map(b=>` - ${b.name} (${b.id})`).join(`
21
+ `);N.push(`The following custom rules have unexpected state:
22
+ `+w)}if(A.length>0){let w=A.map(b=>` - ${b.ip} (${b.id})`).join(`
23
+ `);N.push(`The following IP blocking rules have unexpected state:
24
+ `+w)}if(N.length>0)throw new Error(`Firewall configuration validation failed:
25
+ `+N.join(`
26
26
 
27
- `));let v={...e,version:r.version,updatedAt:r.updatedAt},m=new Set;v.rules=v.rules.map(w=>{let I=s.find(re=>!m.has(re.id)&&ae(B(re),B(w))&&re.id!==w.id);return I?(m.add(I.id),{...w,id:I.id}):w});let P=new Set;return v.ips&&(v.ips=v.ips.map(w=>{let I=a.find(re=>!P.has(re.id)&&ae(B(re),B(w)));return I&&I.id!==w.id?(P.add(I.id),{...w,id:I.id}):w})),v}};var vt=S(require("chalk"));k();ce();var Le=n=>({$schema:ti,firewallEnabled:!0,rules:[],ips:[],...n});var bi=process.env.DOORMAN_VERCEL_API_BASE_URL||"https://api.vercel.com/v1/security/firewall/config",tt=class{constructor(e,t,i){this.projectId=e;this.teamId=t;this.token=i}projectId;teamId;token;getHeaders(){return{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"}}getUrl(e){let t=e!==void 0?`${bi}/${e}`:bi;return o.debug("API URL:",t),`${t}?projectId=${this.projectId}&teamId=${this.teamId}`}async handleResponse(e,t){if(!e.ok){let i=await e.text();throw new Error(`Error ${t?"creating":"updating"} firewall rule: ${e.statusText}
27
+ `));let v={...e,version:r.version,updatedAt:r.updatedAt},m=new Set;v.rules=v.rules.map(w=>{let b=s.find(re=>!m.has(re.id)&&ae(j(re),j(w))&&re.id!==w.id);return b?(m.add(b.id),{...w,id:b.id}):w});let P=new Set;return v.ips&&(v.ips=v.ips.map(w=>{let b=a.find(re=>!P.has(re.id)&&ae(j(re),j(w)));return b&&b.id!==w.id?(P.add(b.id),{...w,id:b.id}):w})),v}};var Et=S(require("chalk"));k();ce();var Le=n=>({$schema:ii,firewallEnabled:!0,rules:[],ips:[],...n});var bi=process.env.DOORMAN_VERCEL_API_BASE_URL||"https://api.vercel.com/v1/security/firewall/config",it=class{constructor(e,t,i){this.projectId=e;this.teamId=t;this.token=i}projectId;teamId;token;getHeaders(){return{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"}}getUrl(e){let t=e!==void 0?`${bi}/${e}`:bi;return o.debug("API URL:",t),`${t}?projectId=${this.projectId}&teamId=${this.teamId}`}async handleResponse(e,t){if(!e.ok){let i=await e.text();throw new Error(`Error ${t?"creating":"updating"} firewall rule: ${e.statusText}
28
28
  ${i}`)}return await e.json()}async fetchFirewallConfig(e){let t=await fetch(this.getUrl(e),{method:"GET",headers:this.getHeaders()});if(!t.ok){let r=await t.text();throw o.debug("Error fetching firewall rules:",t.statusText,r),new Error(`Error fetching firewall rules: ${t.statusText}
29
- ${r}`)}let i=await t.json();if(o.debug("Config Version:",e??"latest"),o.debug("Fetched Config:",e?i:i.active),e)return i;if(!i||"active"in i&&i.active===null)if(o.warn(vt.default.bold("No firewall configuration found.")),await C("Would you like to create one?",{type:"confirm"})){o.debug("Creating new empty firewall configuration...");let s=await this.putEmptyConfig();return o.debug("Empty firewall configuration created successfully"),o.debug(`New configuration version: ${vt.default.yellow(s.version)}`),s}else return i.active;return i.active}async putEmptyConfig(){let{$schema:e,...t}=Le();return o.debug("Empty Config:",t),this.putConfig(t)}async putConfig(e){let t=await fetch(this.getUrl(),{method:"PUT",headers:this.getHeaders(),body:JSON.stringify(e)});if(!t.ok){let i=await t.text();throw new Error(`Error putting config: ${t.statusText}
29
+ ${r}`)}let i=await t.json();if(o.debug("Config Version:",e??"latest"),o.debug("Fetched Config:",e?i:i.active),e)return i;if(!i||"active"in i&&i.active===null)if(o.warn(Et.default.bold("No firewall configuration found.")),await C("Would you like to create one?",{type:"confirm"})){o.debug("Creating new empty firewall configuration...");let s=await this.putEmptyConfig();return o.debug("Empty firewall configuration created successfully"),o.debug(`New configuration version: ${Et.default.yellow(s.version)}`),s}else return i.active;return i.active}async putEmptyConfig(){let{$schema:e,...t}=Le();return o.debug("Empty Config:",t),this.putConfig(t)}async putConfig(e){let t=await fetch(this.getUrl(),{method:"PUT",headers:this.getHeaders(),body:JSON.stringify(e)});if(!t.ok){let i=await t.text();throw new Error(`Error putting config: ${t.statusText}
30
30
  ${i}`)}return(await t.json()).active}async fetchActiveFirewallRules(){return(await this.fetchFirewallConfig())?.rules}async updateFirewallRule(e){let t=!e.id||e.id==="-",i={action:t?"rules.insert":"rules.update",id:t?null:e.id,value:{name:e.name,description:e.description,action:e.action,conditionGroup:e.conditionGroup,active:e.active}},r=await fetch(this.getUrl(),{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(i)});if(!r.ok){let s=await r.text();throw new Error(`Error ${t?"creating":"updating"} firewall rule: ${r.statusText}
31
31
  ${s}`)}return this.handleResponse(r,t)}async createFirewallRule(e){return this.updateFirewallRule({...e,id:"-"})}async deleteFirewallRule(e){let t=JSON.stringify({action:"rules.remove",id:e.id,value:null}),i=await fetch(this.getUrl(),{method:"PATCH",headers:this.getHeaders(),body:t});if(!i.ok){let r=await i.text();throw new Error(`Error deleting firewall rule: ${i.statusText}
32
32
  ${r}`)}}async updateIPBlockingRule(e){let t=!e.id||e.id==="-",i={action:t?"ip.insert":"ip.update",id:t?null:e.id,value:{action:e.action,hostname:e.hostname,ip:e.ip,...e.notes&&{notes:e.notes}}},r=await fetch(this.getUrl(),{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(i)});if(!r.ok){let s=await r.text();throw new Error(`Error ${t?"creating":"updating"} IP blocking rule: ${r.statusText}
33
33
  ${s}`)}return await r.json()}async createIPBlockingRule(e){return this.updateIPBlockingRule({...e,id:"-"})}async deleteIPBlockingRule(e){let t=JSON.stringify({action:"ip.remove",id:e.id,value:null}),i=await fetch(this.getUrl(),{method:"PATCH",headers:this.getHeaders(),body:t});if(!i.ok){let r=await i.text();throw new Error(`Error deleting IP blocking rule: ${i.statusText}
34
- ${r}`)}}};$t();var ki=S(require("chalk"));k();ce();k();var it=class{static detect(e){let t=[];if(e&&"provider"in e){let r=e.provider;if(typeof r=="string"&&this.isValidProvider(r))return t.push(`Explicit provider specified in config: ${e.provider}`),{provider:r,confidence:"high",reasons:t};o.warn(`Invalid provider specified in config: ${String(e.provider)}`)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let r=e.providers;if(typeof(r.cloudflare||{}).zoneId=="string")return t.push("Cloudflare zone ID found in config"),{provider:"cloudflare",confidence:"high",reasons:t};if(typeof(r.vercel||{}).projectId=="string")return t.push("Vercel project ID found in config"),{provider:"vercel",confidence:"high",reasons:t}}if(e&&"projectId"in e&&typeof e.projectId=="string")return t.push("Legacy Vercel project ID found in config"),{provider:"vercel",confidence:"high",reasons:t};let i=this.detectFromEnvironment();return i.provider?i:(o.debug("Unable to auto-detect provider"),{provider:null,confidence:"low",reasons:["No provider information found in config or environment"]})}static detectFromEnvironment(){let e=[],t=process.env.DOORMAN_PROVIDER;return t&&this.isValidProvider(t)?(e.push(`Provider set via DOORMAN_PROVIDER environment variable: ${t}`),{provider:t,confidence:"high",reasons:e}):process.env.CLOUDFLARE_ZONE_ID&&process.env.CLOUDFLARE_API_TOKEN?(e.push("Cloudflare credentials found in environment variables"),{provider:"cloudflare",confidence:"medium",reasons:e}):process.env.CLOUDFLARE_ZONE_ID?(e.push("Cloudflare zone ID found in environment"),{provider:"cloudflare",confidence:"medium",reasons:e}):process.env.VERCEL_PROJECT_ID&&process.env.VERCEL_TOKEN?(e.push("Vercel credentials found in environment variables"),{provider:"vercel",confidence:"medium",reasons:e}):process.env.VERCEL_PROJECT_ID?(e.push("Vercel project ID found in environment"),{provider:"vercel",confidence:"medium",reasons:e}):{provider:null,confidence:"low",reasons:["No provider credentials found in environment"]}}static isValidProvider(e){return e==="vercel"||e==="cloudflare"}static getProvider(e,t="vercel"){let i=this.detect(e);return i.provider?(o.debug(`Provider detected: ${i.provider} (${i.confidence} confidence)`),i.reasons.length>0&&o.debug(`Detection reasons: ${i.reasons.join(", ")}`),i.provider):(o.debug(`No provider detected, using fallback: ${t}`),t)}static detectAll(e){let t=new Set;if(e&&"provider"in e){let i=e.provider;typeof i=="string"&&this.isValidProvider(i)&&t.add(i)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let i=e.providers;typeof(i.cloudflare||{}).zoneId=="string"&&t.add("cloudflare"),typeof(i.vercel||{}).projectId=="string"&&t.add("vercel")}return e&&"projectId"in e&&typeof e.projectId=="string"&&t.add("vercel"),process.env.CLOUDFLARE_ZONE_ID&&t.add("cloudflare"),process.env.VERCEL_PROJECT_ID&&t.add("vercel"),Array.from(t)}};var At=S(require("chalk"));k();ce();k();var De=class{baseUrl;providerName;rateLimitInfo={};constructor(e,t){this.baseUrl=e,this.providerName=t}async makeRequest(e,t={}){let i=`${this.baseUrl}${e}`,{timeout:r=3e4,retries:s=3,retryDelay:a=1e3,...l}=t,c=null,d=null,u=null,p=()=>{u&&(clearTimeout(u),u=null),d&&(d.abort(),d=null)};try{for(let g=0;g<=s;g++)try{d=new AbortController,u=setTimeout(()=>{d&&d.abort()},r);let h=await fetch(i,{...l,headers:{"Content-Type":"application/json",...this.getAuthHeaders(),...l.headers},signal:d.signal});if(u&&(clearTimeout(u),u=null),this.updateRateLimitInfo(h),h.status===429){let f=this.calculateRateLimitWait(g);o.warn(`Rate limit exceeded for ${this.providerName}. Waiting ${f}ms before retry (attempt ${g+1}/${s+1})...`),await this.delay(f);continue}if(!h.ok)throw await this.handleErrorResponse(h);let b;try{let f=await h.text();f.trim()?b=JSON.parse(f):b={}}catch(f){throw o.error(`Failed to parse response from ${i}:`,f),new Error(`Invalid JSON response from ${this.providerName} API: ${f instanceof Error?f.message:String(f)}`)}return this.isLargeResponse(b)&&(o.debug(`Large response detected from ${i}, validating structure...`),this.validateLargeResponse(b)),b}catch(h){if(c=h,u&&(clearTimeout(u),u=null),this.isNonRetryableError(h)||g===s)throw h;let b=a*Math.pow(2,g),f=Math.random()*.1*b,T=Math.min(b+f,3e4);o.debug(`Request failed (attempt ${g+1}/${s+1}). Retrying in ${Math.round(T)}ms...`),o.debug(`Error details: ${h instanceof Error?h.message:String(h)}`),await this.delay(T)}throw c||new Error("Request failed after all retries")}finally{p()}}async get(e,t){return this.makeRequest(e,{...t,method:"GET"})}async post(e,t,i){return this.makeRequest(e,{...i,method:"POST",body:t?JSON.stringify(t):void 0})}async put(e,t,i){return this.makeRequest(e,{...i,method:"PUT",body:t?JSON.stringify(t):void 0})}async patch(e,t,i){return this.makeRequest(e,{...i,method:"PATCH",body:t?JSON.stringify(t):void 0})}async delete(e,t){return this.makeRequest(e,{...t,method:"DELETE"})}updateRateLimitInfo(e){let t=e.headers.get("X-RateLimit-Limit"),i=e.headers.get("X-RateLimit-Remaining"),r=e.headers.get("X-RateLimit-Reset");t&&(this.rateLimitInfo.limit=parseInt(t,10)),i&&(this.rateLimitInfo.remaining=parseInt(i,10)),r&&(this.rateLimitInfo.reset=parseInt(r,10)),this.rateLimitInfo.remaining&&this.rateLimitInfo.remaining<10&&o.warn(`Approaching rate limit for ${this.providerName}: ${this.rateLimitInfo.remaining} requests remaining`)}calculateRateLimitWait(e=0){if(this.rateLimitInfo.reset){let s=Math.floor(Date.now()/1e3),a=(this.rateLimitInfo.reset-s)*1e3;return Math.max(a,1e3)}let i=5e3*Math.pow(2,e);return Math.min(i,6e4)}async handleErrorResponse(e){let t=`${this.providerName} API error: ${e.status} ${e.statusText}`,i=t;try{let r=await e.json();if(typeof r=="object"&&r!==null){let s=r;if(typeof s.error=="string")i=`${t} - ${s.error}`;else if(typeof s.message=="string")i=`${t} - ${s.message}`;else if(Array.isArray(s.errors)){let a=s.errors.map(l=>typeof l=="object"&&l!==null&&"message"in l?String(l.message):JSON.stringify(l)).join(", ");i=`${t} - ${a}`}}}catch{}return o.error(i),new Error(i)}isNonRetryableError(e){if(typeof e=="object"&&e!==null&&e.name==="AbortError")return!0;let t=e.message;return typeof t=="string"&&t.includes("API error: 4")?!t.includes("429"):!1}delay(e){return new Promise(t=>setTimeout(t,e))}getRateLimitInfo(){return{...this.rateLimitInfo}}isLargeResponse(e){if(!e||typeof e!="object")return!1;let t=(i,r=0)=>{if(r>10)return!1;for(let[,s]of Object.entries(i))if(Array.isArray(s)&&s.length>1e3||typeof s=="object"&&s!==null&&t(s,r+1))return!0;return!1};return t(e)}validateLargeResponse(e){if(!e||typeof e!="object")return;let t=(r,s)=>{r.length>5e3&&o.warn(`Large array detected at ${s} with ${r.length} items. This may impact performance.`);let a=Math.min(10,r.length),l=r[0];if(l&&typeof l=="object"){let c=Object.keys(l);for(let d=1;d<a;d++){let u=r[d];if(!u||typeof u!="object"){o.warn(`Inconsistent array structure at ${s}[${d}]: expected object, got ${typeof u}`);continue}let p=Object.keys(u),g=c.filter(h=>!p.includes(h));g.length>0&&o.warn(`Inconsistent array structure at ${s}[${d}]: missing keys ${g.join(", ")}`)}}},i=(r,s="root",a=0)=>{if(!(a>10))for(let[l,c]of Object.entries(r)){let d=`${s}.${l}`;Array.isArray(c)?t(c,d):typeof c=="object"&&c!==null&&i(c,d,a+1)}};i(e)}};var Ei="https://api.vercel.com/v1/security/firewall/config",Ue=class extends De{constructor(t,i,r){super("","vercel");this.projectId=t;this.teamId=i;this.token=r}projectId;teamId;token;getAuthHeaders(){return{Authorization:`Bearer ${this.token}`}}getUrl(t){let i=t!==void 0?`${Ei}/${t}`:Ei;return o.debug("API URL:",i),`${i}?projectId=${this.projectId}&teamId=${this.teamId}`}async fetchFirewallConfig(t){let i=await this.get(this.getUrl(t));if(o.debug("Config Version:",t??"latest"),o.debug("Fetched Config:",t?i:i.active),t)return i;if(!i||"active"in i&&i.active===null)if(o.warn(At.default.bold("No firewall configuration found.")),await C("Would you like to create one?",{type:"confirm"})){o.debug("Creating new empty firewall configuration...");let s=await this.putEmptyConfig();return o.debug("Empty firewall configuration created successfully"),o.debug(`New configuration version: ${At.default.yellow(s.version)}`),s}else return i.active;return i.active}async putEmptyConfig(){let{$schema:t,...i}=Le();return o.debug("Empty Config:",i),this.putConfig(i)}async putConfig(t){return(await this.put(this.getUrl(),t)).active}async fetchActiveFirewallRules(){return(await this.fetchFirewallConfig())?.rules}async updateFirewallRule(t){let i=!t.id||t.id==="-",r={action:i?"rules.insert":"rules.update",id:i?null:t.id,value:{name:t.name,description:t.description,action:t.action,conditionGroup:t.conditionGroup,active:t.active}};return this.patch(this.getUrl(),r)}async createFirewallRule(t){return this.updateFirewallRule({...t,id:"-"})}async deleteFirewallRule(t){let i={action:"rules.remove",id:t.id,value:null};await this.patch(this.getUrl(),i)}async updateIPBlockingRule(t){let i=!t.id||t.id==="-",r={action:i?"ip.insert":"ip.update",id:i?null:t.id,value:{action:t.action,hostname:t.hostname,ip:t.ip,...t.notes&&{notes:t.notes}}};return this.patch(this.getUrl(),r)}async createIPBlockingRule(t){return this.updateIPBlockingRule({...t,id:"-"})}async deleteIPBlockingRule(t){let i={action:"ip.remove",id:t.id,value:null};await this.patch(this.getUrl(),i)}async verifyCredentials(){try{return await this.fetchFirewallConfig(),!0}catch(t){return o.debug("Credential verification failed:",t),!1}}};var ne=S(require("chalk"));k();k();var xe=class{validateConfig(e){let t=[],i=[];return e||t.push({path:"root",message:"Configuration is required",code:"CONFIG_REQUIRED"}),e&&!e.rules&&t.push({path:"rules",message:"Rules array is required",code:"RULES_REQUIRED"}),e&&e.rules&&!Array.isArray(e.rules)&&t.push({path:"rules",message:"Rules must be an array",code:"RULES_INVALID_TYPE"}),{valid:t.length===0,errors:t,warnings:i}}getHealthScore(e){let t=[],i=100;if(!e)return{score:0,grade:"poor",issues:[{severity:"error",category:"configuration",message:"No configuration found"}],recommendations:["Create a configuration file"]};if((!e.rules||e.rules.length===0)&&(i-=20,t.push({severity:"warning",category:"rules",message:"No rules defined",suggestion:"Add security rules to protect your application"})),e.rules&&e.rules.length>0){let a=e.rules.filter(l=>!l.description);a.length>0&&(i-=5,t.push({severity:"info",category:"maintainability",message:`${a.length} rule(s) missing descriptions`,suggestion:"Add descriptions to improve maintainability"}))}if(e.rules&&e.rules.length>0){let a=e.rules.filter(l=>l.active===!1||l.enabled===!1);a.length>0&&(i-=5,t.push({severity:"info",category:"maintenance",message:`${a.length} disabled rule(s) found`,suggestion:"Review and remove unused rules"}))}let r=this.calculateGrade(i),s=this.generateRecommendations(e,t);return{score:Math.max(0,i),grade:r,issues:t,recommendations:s}}diffItems(e,t,i,r="id"){let s=[],a=[],l=[],c=d=>d[r];for(let d of e){let u=t.find(p=>c(p)===c(d));u?i(d,u)||a.push(d):s.push(d)}for(let d of t)e.find(p=>c(p)===c(d))||l.push(d);return{toAdd:s,toUpdate:a,toDelete:l}}validateRuleCount(e,t){if(t&&e>t)throw new Error(`Rule count (${e}) exceeds provider limit (${t})`);t&&e>t*.9&&o.warn(`Approaching rule limit: ${e}/${t} rules`)}updateMetadata(e,t){return{...e,metadata:{...e.metadata,version:t,updatedAt:new Date().toISOString()}}}calculateGrade(e){return e>=80?"excellent":e>=60?"good":e>=40?"fair":"poor"}generateRecommendations(e,t){let i=[];return t.some(r=>r.category==="rules")&&i.push("Consider using predefined templates to get started"),t.some(r=>r.category==="maintainability")&&i.push("Add descriptions to all rules for better documentation"),t.some(r=>r.category==="maintenance")&&i.push("Remove or enable disabled rules to keep configuration clean"),i}logSyncStats(e){o.info(`Sync complete for ${this.name}:`),o.info(` Rules added: ${e.rulesAdded}`),o.info(` Rules updated: ${e.rulesUpdated}`),o.info(` Rules deleted: ${e.rulesDeleted}`),e.ipsAdded!==void 0&&(o.info(` IPs added: ${e.ipsAdded}`),o.info(` IPs updated: ${e.ipsUpdated}`),o.info(` IPs deleted: ${e.ipsDeleted}`)),e.version&&o.info(` New version: ${e.version}`)}};k();function Oe(n){return n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}var qe=class{static vercelToCloudflare={host:"http.host",path:"http.request.uri.path",method:"http.request.method",header:"http.request.headers",query:"http.request.uri.query",cookie:"http.cookie",target_path:"http.request.uri.path",ip_address:"ip.src",region:"ip.geoip.subdivision_1",protocol:"ssl",scheme:"ssl",environment:"",user_agent:"http.user_agent",geo_continent:"ip.geoip.continent",geo_country:"ip.geoip.country",geo_country_region:"ip.geoip.subdivision_1",geo_city:"ip.geoip.city",geo_as_number:"ip.geoip.asnum",ja4_digest:"",ja3_digest:"",rate_limit_api_id:""};static cloudflareToVercel={"http.host":"host","http.request.uri.path":"path","http.request.method":"method","http.request.headers":"header","http.request.uri.query":"query","http.cookie":"cookie","ip.src":"ip_address","ip.geoip.subdivision_1":"region",ssl:"protocol","http.user_agent":"user_agent","http.referer":"header","ip.geoip.continent":"geo_continent","ip.geoip.country":"geo_country","ip.geoip.city":"geo_city","ip.geoip.asnum":"geo_as_number"};static toCloudflare(e,t){let i=this.vercelToCloudflare[e];if(!i)throw o.warn(`No Cloudflare mapping for Vercel type: ${e}`),new Error(`Unsupported Vercel rule type for Cloudflare: ${e}`);return e==="header"&&t?`${i}["${Oe(t.toLowerCase())}"]`:e==="cookie"&&t?`${i}["${Oe(t)}"]`:i}static toVercel(e){let t=e.match(/http\.request\.headers\["([^"]+)"\]/);if(t)return{type:"header",key:t[1]};let i=e.match(/http\.cookie\["([^"]+)"\]/);if(i)return{type:"cookie",key:i[1]};let r=this.cloudflareToVercel[e];if(r)return{type:r};throw o.warn(`No Vercel mapping for Cloudflare field: ${e}`),new Error(`Unsupported Cloudflare field for Vercel: ${e}`)}static isCloudflareSupported(e){let t=this.vercelToCloudflare[e];return!!t&&t!==""}static isVercelSupported(e){return!!(this.cloudflareToVercel[e]||e.startsWith("http.request.headers[")||e.startsWith("http.cookie["))}static getSupportedCloudflareFields(){return Object.values(this.vercelToCloudflare).filter(e=>e!=="")}static getSupportedVercelTypes(){return Object.keys(this.vercelToCloudflare).filter(e=>this.isCloudflareSupported(e))}};var Ne=class{static fromVercelConditionGroups(e){if(!e||e.length===0)throw new Error("At least one condition group is required");let t=e.map(i=>{let r=i.conditions.map(s=>this.fromVercelCondition(s));if(r.length===0)throw new Error("Condition group must have at least one condition");return r.length>1?`(${r.join(" and ")})`:r[0]});return t.length>1?t.join(" or "):t[0]}static fromVercelCondition(e){let t=qe.toCloudflare(e.type,e.key),i=this.mapVercelOperator(e.op),r=this.formatValue(e.value,e.op),s=`${t} ${i} ${r}`;return e.neg&&(s=`not (${s})`),s}static fromUnifiedConditions(e,t="AND"){if(!e||e.length===0)throw new Error("At least one condition is required");let i=e.map(s=>this.fromUnifiedCondition(s)),r=t==="AND"?" and ":" or ";return i.length>1?`(${i.join(r)})`:i[0]}static fromUnifiedCondition(e){let t=e.key?`http.request.headers["${Oe(e.key)}"]`:this.mapUnifiedFieldToCloudflare(e.field),i=this.mapUnifiedOperator(e.operator),r=this.formatValue(e.value,i),s=`${t} ${i} ${r}`;return e.negated&&(s=`not (${s})`),s}static mapVercelOperator(e){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists",nex:"not exists"}[e]||e}static mapUnifiedOperator(e){return{eq:"eq",ne:"ne",contains:"contains",not_contains:"not contains",starts_with:"starts_with",ends_with:"ends_with",matches:"matches",in:"in",not_in:"not in",gt:"gt",ge:"ge",lt:"lt",le:"le",exists:"exists",not_exists:"not exists"}[e]||e}static mapUnifiedFieldToCloudflare(e){return{ip:"ip.src",country:"ip.geoip.country",region:"ip.geoip.subdivision_1",city:"ip.geoip.city",asn:"ip.geoip.asnum",path:"http.request.uri.path",host:"http.host",method:"http.request.method",header:"http.request.headers",query:"http.request.uri.query",cookie:"http.cookie",user_agent:"http.user_agent",referer:"http.referer",scheme:"ssl",port:"cf.edge.server_port"}[e]||e}static formatValue(e,t){return Array.isArray(e)?`{${e.map(r=>this.formatSingleValue(r)).join(" ")}}`:this.formatSingleValue(e)}static formatSingleValue(e){return typeof e=="string"?`"${Oe(e)}"`:typeof e=="number"?String(e):typeof e=="boolean"?e?"true":"false":String(e)}static validate(e){if(!e||e.trim().length===0)return!1;let t=0;for(let i of e)if(i==="("&&t++,i===")"&&t--,t<0)return!1;return t===0}static combineWithAnd(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" and ")})`}static combineWithOr(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" or ")})`}};k();var M=class{static vercelToCloudflare(e){let t=[];try{let i=Ne.fromVercelConditionGroups(e.conditionGroup),r=this.translateVercelActionToCloudflare(e.action.mitigate.action),s={id:e.id||crypto.randomUUID(),action:r,expression:i,description:e.description||e.name,enabled:e.active};if(e.action.mitigate.action==="rate_limit"&&e.action.mitigate.rateLimit){let a=e.action.mitigate.rateLimit;if(s.ratelimit={characteristics:a.characteristics||["ip.src"],period:this.parseWindowToSeconds(a.window),requests_per_period:a.requests},a.mitigationTimeout)s.ratelimit.mitigation_timeout=a.mitigationTimeout;else{s.ratelimit.mitigation_timeout=3600;let{TranslationWarningSystem:l}=(pe(),fe(me));t.push(l.createWarning("rate_limiting_precision",e.id,"rateLimit.mitigationTimeout","No mitigation timeout specified, using default 1 hour (3600 seconds)","Specify mitigationTimeout in your rate limit configuration for precise control"))}a.countingExpression&&(s.ratelimit.counting_expression=a.countingExpression)}return e.action.mitigate.action==="redirect"&&e.action.mitigate.redirect&&(s.action_parameters={from_value:{status_code:e.action.mitigate.redirect.permanent?301:302,target_url:{value:e.action.mitigate.redirect.location}}}),{result:s,warnings:t}}catch(i){throw o.error(`Failed to translate Vercel rule to Cloudflare: ${i}`),i}}static cloudflareToVercel(e){let t=[],{TranslationWarningSystem:i}=(pe(),fe(me));return t.push(i.createLossyConversionWarning("Cloudflare expression","Cloudflare expressions cannot be perfectly converted to Vercel structured conditions",e.id,"expression")),{result:{id:e.id,name:e.description||`Rule ${e.id}`,description:e.description,conditionGroup:[{conditions:[]}],action:{mitigate:{action:this.translateCloudflareActionToVercel(e.action)}},active:e.enabled??!0},warnings:t}}static vercelToUnified(e){let t=[],i=[];for(let a of e.conditionGroup)for(let l of a.conditions){let c=this.mapVercelOperatorToUnified(l.op);if(l.op==="re"&&typeof l.value=="string"){let{TranslationWarningSystem:d}=(pe(),fe(me));t.push(d.createWarning("regex_patterns",e.id,l.type,`Regular expression pattern may need adjustment for target provider: ${l.value}`,"Test the regex pattern in the target provider and adjust syntax if needed"))}i.push({field:this.mapVercelTypeToUnified(l.type),operator:c,value:l.value,negated:l.neg,key:l.key})}if(i.length>10){let{TranslationWarningSystem:a}=(pe(),fe(me));t.push(a.createWarning("many_conditions",e.id,void 0,`Rule has ${i.length} conditions which may impact performance`,"Consider splitting complex rules into multiple simpler rules for better performance"))}let r={type:e.action.mitigate.action,rateLimit:e.action.mitigate.rateLimit?{requests:e.action.mitigate.rateLimit.requests,window:e.action.mitigate.rateLimit.window,characteristics:e.action.mitigate.rateLimit.characteristics,mitigationTimeout:e.action.mitigate.rateLimit.mitigationTimeout,countingExpression:e.action.mitigate.rateLimit.countingExpression}:void 0,redirect:e.action.mitigate.redirect?{location:e.action.mitigate.redirect.location,permanent:e.action.mitigate.redirect.permanent}:void 0,duration:e.action.mitigate.actionDuration||void 0};return{result:{id:e.id,name:e.name,description:e.description,enabled:e.active,conditions:i,conditionLogic:"OR",action:r},warnings:t}}static cloudflareToUnified(e){let t=[],{TranslationWarningSystem:i}=(pe(),fe(me));t.push(i.createWarning("complex_expressions",e.id,"expression","Expression parsing not fully implemented. Using simplified translation.","Review the translated rule and add missing conditions manually if needed."));let r={type:this.mapCloudflareActionToUnified(e.action),rateLimit:e.ratelimit?{requests:e.ratelimit.requests_per_period,window:`${e.ratelimit.period}s`,characteristics:e.ratelimit.characteristics,mitigationTimeout:e.ratelimit.mitigation_timeout,countingExpression:e.ratelimit.counting_expression}:void 0};return{result:{id:e.id,name:e.description||`Rule ${e.id}`,description:e.description,enabled:e.enabled??!0,conditions:[],action:r},warnings:t}}static unifiedToCloudflare(e){let t=[],i=Ne.fromUnifiedConditions(e.conditions,e.conditionLogic),r={id:e.id||crypto.randomUUID(),action:this.mapUnifiedActionToCloudflare(e.action.type),expression:i,description:e.description||e.name,enabled:e.enabled};return e.action.rateLimit&&(r.ratelimit={characteristics:e.action.rateLimit.characteristics||["ip.src"],period:this.parseWindowToSeconds(e.action.rateLimit.window),requests_per_period:e.action.rateLimit.requests},e.action.rateLimit.mitigationTimeout?r.ratelimit.mitigation_timeout=e.action.rateLimit.mitigationTimeout:r.ratelimit.mitigation_timeout=3600,e.action.rateLimit.countingExpression&&(r.ratelimit.counting_expression=e.action.rateLimit.countingExpression)),{result:r,warnings:t}}static unifiedToVercel(e){let t=[],i=[],r=e.conditions.map(a=>({op:this.mapUnifiedOperatorToVercel(a.operator),neg:a.negated,type:this.mapUnifiedTypeToVercel(a.field),key:a.key,value:a.value}));return i.push({conditions:r}),{result:{id:e.id,name:e.name,description:e.description,conditionGroup:i,action:{mitigate:{action:e.action.type,rateLimit:e.action.rateLimit?{requests:e.action.rateLimit.requests,window:e.action.rateLimit.window,characteristics:e.action.rateLimit.characteristics,mitigationTimeout:e.action.rateLimit.mitigationTimeout,countingExpression:e.action.rateLimit.countingExpression}:null,redirect:e.action.redirect?{location:e.action.redirect.location,permanent:e.action.redirect.permanent}:null,actionDuration:e.action.duration||null}},active:e.enabled},warnings:t}}static vercelIPToUnified(e){return{id:e.id,ip:e.ip,hostname:e.hostname,notes:e.notes,action:e.action}}static unifiedIPToCloudflare(e){return{id:e.id||crypto.randomUUID(),action:e.action==="allow"?"allow":"block",expression:`ip.src eq ${e.ip}`,description:e.notes||`IP ${e.action}: ${e.ip}${e.hostname?` (${e.hostname})`:""}`,enabled:!0}}static translateVercelActionToCloudflare(e){return{log:"log",deny:"block",challenge:"managed_challenge",bypass:"skip",rate_limit:"block",redirect:"redirect"}[e]||"block"}static translateCloudflareActionToVercel(e){return{block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",log:"log",skip:"bypass",allow:"bypass",rewrite:"bypass",redirect:"redirect"}[e]||"deny"}static mapVercelOperatorToUnified(e){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists",nex:"not_exists"}[e]||"eq"}static mapUnifiedOperatorToVercel(e){return{eq:"eq",starts_with:"pre",ends_with:"suf",in:"inc",contains:"sub",matches:"re",exists:"ex",not_exists:"nex"}[e]||"eq"}static mapVercelTypeToUnified(e){return{host:"host",path:"path",method:"method",header:"header",query:"query",cookie:"cookie",ip_address:"ip",user_agent:"user_agent",geo_country:"country",geo_city:"city",geo_as_number:"asn",scheme:"scheme"}[e]||e}static mapUnifiedTypeToVercel(e){return{host:"host",path:"path",method:"method",header:"header",query:"query",cookie:"cookie",ip:"ip_address",user_agent:"user_agent",country:"geo_country",city:"geo_city",asn:"geo_as_number",scheme:"scheme"}[e]||"path"}static mapCloudflareActionToUnified(e){return{block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",log:"log",skip:"bypass",allow:"allow",rewrite:"bypass",redirect:"redirect"}[e]||"deny"}static mapUnifiedActionToCloudflare(e){return{log:"log",deny:"block",block:"block",challenge:"managed_challenge",bypass:"skip",rate_limit:"block",redirect:"redirect",allow:"allow"}[e]||"block"}static parseWindowToSeconds(e){let t=e.match(/^(\d+)([smhd])$/);if(!t||!t[1]||!t[2])throw new Error(`Invalid window format: ${e}`);let i=parseInt(t[1],10),r=t[2],a={s:1,m:60,h:3600,d:86400}[r];if(a===void 0)throw new Error(`Invalid time unit: ${r}`);return i*a}};pe();var Fe=class extends xe{constructor(t){super();this.client=t}client;name="vercel";async fetchConfig(t){try{o.debug("Fetching Vercel firewall configuration");let i=await this.client.fetchFirewallConfig(t),r=i.rules.map(a=>{let l=M.vercelToUnified(a);return l.warnings.length>0&&l.warnings.forEach(c=>{let{TranslationWarningSystem:d}=(pe(),fe(me)),u=d.formatWarning(c);o.warn(`Rule ${a.name}:
35
- ${u}`)}),l.result}),s=i.ips.map(a=>M.vercelIPToUnified(a));return{version:"2.0",provider:"vercel",rules:r,ips:s,metadata:{version:i.version,updatedAt:i.updatedAt}}}catch(i){throw o.error("Error fetching Vercel configuration:",i),new Error("Failed to fetch Vercel firewall configuration")}}async syncRules(t,i={}){let{dryRun:r=!1}=i;try{let s=await this.getChanges(t),{rulesToAdd:a,rulesToUpdate:l,rulesToDelete:c,version:d}=s,u=s.ipsToAdd||[],p=s.ipsToUpdate||[],g=s.ipsToDelete||[];if(r)return o.info("Dry run mode. The following changes would be made:"),o.info(`Custom Rules - Add: ${a.length}, Update: ${l.length}, Delete: ${c.length}`),o.info(`IP Rules - Add: ${u.length}, Update: ${p.length}, Delete: ${g.length}`),{success:!0,rulesAdded:0,rulesUpdated:0,rulesDeleted:0,ipsAdded:0,ipsUpdated:0,ipsDeleted:0,version:d};let h=a.map(E=>M.unifiedToVercel(E).result),b=l.map(E=>M.unifiedToVercel(E).result),f=c.map(E=>M.unifiedToVercel(E).result),T=u.map(E=>({id:E.id||"",ip:E.ip,hostname:E.hostname||"",action:E.action,notes:E.notes})),j=p.map(E=>({id:E.id||"",ip:E.ip,hostname:E.hostname||"",action:E.action,notes:E.notes})),v=g.map(E=>({id:E.id||"",ip:E.ip,hostname:E.hostname||"",action:E.action,notes:E.notes})),m=[],P=[],w=[],I=[],re=[],ye=[];for(let E of f)o.debug(`Deleting custom rule: ${E.id}`),await oe(()=>this.client.deleteFirewallRule(E),{maxAttempts:3}),w.push(E),o.debug(`Custom rule deleted: ${E.id}`);for(let E of v)o.debug(`Deleting IP blocking rule: ${E.id}`),await oe(()=>this.client.deleteIPBlockingRule(E),{maxAttempts:3}),ye.push(E),o.debug(`IP blocking rule deleted: ${E.id}`);for(let E of h){o.debug(`Adding new custom rule: ${E.name}`);let ue=await oe(()=>this.client.createFirewallRule(E),{maxAttempts:3});m.push(ue),o.debug(`New custom rule added: ${ue.id}`)}for(let E of T){o.debug(`Adding new IP blocking rule: ${E.ip}`);let ue=await oe(()=>this.client.createIPBlockingRule(E),{maxAttempts:3});I.push(ue),o.debug(`New IP blocking rule added: (hostname): ${ue.hostname} (ip): ${ue.ip}`)}for(let E of b){o.debug(`Updating custom rule: ${E.id}`);let ue=await oe(()=>this.client.updateFirewallRule(E),{maxAttempts:3});P.push(ue),o.debug(`Custom rule updated: ${ue.id}`)}for(let E of j){o.debug(`Updating IP blocking rule: ${E.id}`);let ue=await oe(()=>this.client.updateIPBlockingRule(E),{maxAttempts:3});re.push(ue),o.debug(`IP blocking rule updated: ${ue.id}`)}o.debug(`${ne.default.underline("Custom Rules:")} ${ne.default.green("Added:")} ${ne.default.green(m.length)}, ${ne.default.cyan("Updated:")} ${ne.default.cyan(P.length)}, ${ne.default.red("Deleted:")} ${ne.default.red(w.length)}`),o.debug(`${ne.default.underline("IP Rules:")} ${ne.default.green("Added:")} ${ne.default.green(I.length)}, ${ne.default.cyan("Updated:")} ${ne.default.cyan(re.length)}, ${ne.default.red("Deleted:")} ${ne.default.red(ye.length)}`);let at=await this.client.fetchFirewallConfig();return{success:!0,rulesAdded:m.length,rulesUpdated:P.length,rulesDeleted:w.length,ipsAdded:I.length,ipsUpdated:re.length,ipsDeleted:ye.length,version:at.version}}catch(s){throw o.error("Error during sync:",s),new Error("Failed to synchronize firewall rules")}}async getChanges(t){try{o.debug("Fetching existing firewall configuration");let i=await this.client.fetchFirewallConfig();o.debug(`Fetched ${i.rules.length} custom rules and ${i.ips.length} IP blocking rules`);let r=t.rules.map(v=>{let m=M.unifiedToVercel(v);return m.warnings.length>0&&m.warnings.forEach(P=>{let{TranslationWarningSystem:w}=(pe(),fe(me)),I=w.formatWarning(P);o.warn(`Rule ${v.name}:
36
- ${I}`)}),m.result}),{toAdd:s,toUpdate:a,toDelete:l}=this.diffRules(r,i.rules),c=(t.ips||[]).map(v=>({id:v.id||"",ip:v.ip,hostname:v.hostname||"",action:v.action,notes:v.notes})),{ipsToAdd:d,ipsToUpdate:u,ipsToDelete:p}=this.diffIPRules(c,i.ips),g=s.map(v=>M.vercelToUnified(v).result),h=a.map(v=>M.vercelToUnified(v).result),b=l.map(v=>M.vercelToUnified(v).result),f=d.map(v=>M.vercelIPToUnified(v)),T=u.map(v=>M.vercelIPToUnified(v)),j=p.map(v=>M.vercelIPToUnified(v));return{version:i.version,rulesToAdd:g,rulesToUpdate:h,rulesToDelete:b,ipsToAdd:f,ipsToUpdate:T,ipsToDelete:j,hasChanges:s.length>0||a.length>0||l.length>0||d.length>0||u.length>0||p.length>0}}catch(i){throw o.error("Error fetching existing firewall configuration:",i),new Error("Failed to fetch existing firewall configuration")}}getSupportedFeatures(){return{supportsCustomRules:!0,supportsIPBlocking:!0,supportsRateLimiting:!0,supportsGeoBlocking:!0,supportsManagedRules:!1,supportsRedirect:!0,supportsChallenge:!0}}async verifyCredentials(){return this.client.verifyCredentials()}validateConfig(t){let i=super.validateConfig(t),r=[...i.errors],s=[...i.warnings];t.provider&&t.provider!=="vercel"&&r.push({path:"provider",message:"Provider must be 'vercel' for VercelFirewallService",code:"INVALID_PROVIDER"});try{let a=we.safeParse(t);a.success||r.push({path:"root",message:`Schema validation failed: ${a.error.message}`,code:"SCHEMA_VALIDATION_FAILED"})}catch(a){o.debug("Schema validation error:",a)}return{valid:r.length===0,errors:r,warnings:s}}getHealthScore(t){let i=super.getHealthScore(t),r=[...i.issues],s=i.score;return t.rules.filter(l=>l.action?.type==="rate_limit").length===0&&(s-=10,r.push({severity:"info",category:"security",message:"No rate limiting rules configured",suggestion:"Consider adding rate limiting to protect against abuse"})),(!t.ips||t.ips.length===0)&&r.push({severity:"info",category:"security",message:"No IP blocking rules configured",suggestion:"Consider blocking known malicious IPs"}),{score:Math.max(0,s),grade:i.grade,issues:r,recommendations:i.recommendations}}diffRules(t,i){let r=[],s=[],a=[...i];for(let l of t){let c=i.find(d=>d.id===l.id);if(!c)r.push(l);else{ae(B(l),B(c))||s.push({...l,id:c.id});let d=a.findIndex(u=>u.id===c.id);d!==-1&&a.splice(d,1)}}return{toAdd:r,toUpdate:s,toDelete:a}}diffIPRules(t,i){let r=[],s=[],a=[...i];for(let l of t){let c=i.find(d=>d.id===l.id);if(!c)o.debug("Rule not found in existing rules:",{configRule:l}),r.push(l);else{ae(B(l),B(c))||s.push({...c,...l});let d=a.findIndex(u=>u.id===c.id);d!==-1&&a.splice(d,1)}}return{ipsToAdd:r,ipsToUpdate:s,ipsToDelete:a}}};k();var Ve=class{static fromEnv(){let e=process.env.VERCEL_TOKEN,t=process.env.VERCEL_PROJECT_ID,i=process.env.VERCEL_TEAM_ID;if(!e)throw new Error("VERCEL_TOKEN environment variable is required");if(!t)throw new Error("VERCEL_PROJECT_ID environment variable is required");if(!i)throw new Error("VERCEL_TEAM_ID environment variable is required");let r=new Ue(t,i,e);return new Fe(r)}static fromConfig(e){let t=e.token||process.env.VERCEL_TOKEN,i=e.projectId||process.env.VERCEL_PROJECT_ID,r=e.teamId||process.env.VERCEL_TEAM_ID;if(!t)throw new Error("Vercel API token is required (provide token or set VERCEL_TOKEN env var)");if(!i)throw new Error("Vercel project ID is required (provide projectId or set VERCEL_PROJECT_ID env var)");if(!r)throw new Error("Vercel team ID is required (provide teamId or set VERCEL_TEAM_ID env var)");o.debug("Creating Vercel provider with config:",{projectId:i,teamId:r,token:t.substring(0,10)+"..."});let s=new Ue(i,r,t);return new Fe(s)}static create(e,t,i){return this.fromConfig({projectId:e,teamId:t,token:i})}};k();var Te=S(require("chalk")),y=class n extends Error{code;suggestion;details;docsUrl;cause;constructor(e){super(e.message),this.name="DoormanError",this.code=e.code,this.suggestion=e.suggestion,this.details=e.details,this.docsUrl=e.docsUrl,this.cause=e.cause,Error.captureStackTrace&&Error.captureStackTrace(this,n)}format(){let e=[];return e.push(Te.default.red.bold(`[${this.code}] ${this.message}`)),e.push(""),this.suggestion&&(e.push(Te.default.yellow.bold("Suggestion:")),e.push(` ${this.suggestion}`),e.push("")),this.details&&Object.keys(this.details).length>0&&(e.push(Te.default.dim("Details:")),Object.entries(this.details).forEach(([t,i])=>{let r=typeof i=="object"?JSON.stringify(i,null,2).split(`
34
+ ${r}`)}}};At();var ki=S(require("chalk"));k();ce();k();var ot=class{static detect(e){let t=[];if(e&&"provider"in e){let r=e.provider;if(typeof r=="string"&&this.isValidProvider(r))return t.push(`Explicit provider specified in config: ${e.provider}`),{provider:r,confidence:"high",reasons:t};o.warn(`Invalid provider specified in config: ${String(e.provider)}`)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let r=e.providers;if(typeof(r.cloudflare||{}).zoneId=="string")return t.push("Cloudflare zone ID found in config"),{provider:"cloudflare",confidence:"high",reasons:t};if(typeof(r.vercel||{}).projectId=="string")return t.push("Vercel project ID found in config"),{provider:"vercel",confidence:"high",reasons:t}}if(e&&"projectId"in e&&typeof e.projectId=="string")return t.push("Legacy Vercel project ID found in config"),{provider:"vercel",confidence:"high",reasons:t};let i=this.detectFromEnvironment();return i.provider?i:(o.debug("Unable to auto-detect provider"),{provider:null,confidence:"low",reasons:["No provider information found in config or environment"]})}static detectFromEnvironment(){let e=[],t=process.env.DOORMAN_PROVIDER;return t&&this.isValidProvider(t)?(e.push(`Provider set via DOORMAN_PROVIDER environment variable: ${t}`),{provider:t,confidence:"high",reasons:e}):process.env.CLOUDFLARE_ZONE_ID&&process.env.CLOUDFLARE_API_TOKEN?(e.push("Cloudflare credentials found in environment variables"),{provider:"cloudflare",confidence:"medium",reasons:e}):process.env.CLOUDFLARE_ZONE_ID?(e.push("Cloudflare zone ID found in environment"),{provider:"cloudflare",confidence:"medium",reasons:e}):process.env.VERCEL_PROJECT_ID&&process.env.VERCEL_TOKEN?(e.push("Vercel credentials found in environment variables"),{provider:"vercel",confidence:"medium",reasons:e}):process.env.VERCEL_PROJECT_ID?(e.push("Vercel project ID found in environment"),{provider:"vercel",confidence:"medium",reasons:e}):{provider:null,confidence:"low",reasons:["No provider credentials found in environment"]}}static isValidProvider(e){return e==="vercel"||e==="cloudflare"}static getProvider(e,t="vercel"){let i=this.detect(e);return i.provider?(o.debug(`Provider detected: ${i.provider} (${i.confidence} confidence)`),i.reasons.length>0&&o.debug(`Detection reasons: ${i.reasons.join(", ")}`),i.provider):(o.debug(`No provider detected, using fallback: ${t}`),t)}static detectAll(e){let t=new Set;if(e&&"provider"in e){let i=e.provider;typeof i=="string"&&this.isValidProvider(i)&&t.add(i)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let i=e.providers;typeof(i.cloudflare||{}).zoneId=="string"&&t.add("cloudflare"),typeof(i.vercel||{}).projectId=="string"&&t.add("vercel")}return e&&"projectId"in e&&typeof e.projectId=="string"&&t.add("vercel"),process.env.CLOUDFLARE_ZONE_ID&&t.add("cloudflare"),process.env.VERCEL_PROJECT_ID&&t.add("vercel"),Array.from(t)}};var Tt=S(require("chalk"));k();ce();k();var De=class{baseUrl;providerName;rateLimitInfo={};constructor(e,t){this.baseUrl=e,this.providerName=t}async makeRequest(e,t={}){let i=`${this.baseUrl}${e}`,{timeout:r=3e4,retries:s=3,retryDelay:a=1e3,...l}=t,c=null,d=null,u=null,p=()=>{u&&(clearTimeout(u),u=null),d&&(d.abort(),d=null)};try{for(let f=0;f<=s;f++)try{d=new AbortController,u=setTimeout(()=>{d&&d.abort()},r);let h=await fetch(i,{...l,headers:{"Content-Type":"application/json",...this.getAuthHeaders(),...l.headers},signal:d.signal});if(u&&(clearTimeout(u),u=null),this.updateRateLimitInfo(h),h.status===429){let g=this.calculateRateLimitWait(f);o.warn(`Rate limit exceeded for ${this.providerName}. Waiting ${g}ms before retry (attempt ${f+1}/${s+1})...`),await this.delay(g);continue}if(!h.ok)throw await this.handleErrorResponse(h);let I;try{let g=await h.text();g.trim()?I=JSON.parse(g):I={}}catch(g){throw o.error(`Failed to parse response from ${i}:`,g),new Error(`Invalid JSON response from ${this.providerName} API: ${g instanceof Error?g.message:String(g)}`)}return this.isLargeResponse(I)&&(o.debug(`Large response detected from ${i}, validating structure...`),this.validateLargeResponse(I)),I}catch(h){if(c=h,u&&(clearTimeout(u),u=null),this.isNonRetryableError(h)||f===s)throw h;let I=a*Math.pow(2,f),g=Math.random()*.1*I,A=Math.min(I+g,3e4);o.debug(`Request failed (attempt ${f+1}/${s+1}). Retrying in ${Math.round(A)}ms...`),o.debug(`Error details: ${h instanceof Error?h.message:String(h)}`),await this.delay(A)}throw c||new Error("Request failed after all retries")}finally{p()}}async get(e,t){return this.makeRequest(e,{...t,method:"GET"})}async post(e,t,i){return this.makeRequest(e,{...i,method:"POST",body:t?JSON.stringify(t):void 0})}async put(e,t,i){return this.makeRequest(e,{...i,method:"PUT",body:t?JSON.stringify(t):void 0})}async patch(e,t,i){return this.makeRequest(e,{...i,method:"PATCH",body:t?JSON.stringify(t):void 0})}async delete(e,t){return this.makeRequest(e,{...t,method:"DELETE"})}updateRateLimitInfo(e){let t=e.headers.get("X-RateLimit-Limit"),i=e.headers.get("X-RateLimit-Remaining"),r=e.headers.get("X-RateLimit-Reset");t&&(this.rateLimitInfo.limit=parseInt(t,10)),i&&(this.rateLimitInfo.remaining=parseInt(i,10)),r&&(this.rateLimitInfo.reset=parseInt(r,10)),this.rateLimitInfo.remaining&&this.rateLimitInfo.remaining<10&&o.warn(`Approaching rate limit for ${this.providerName}: ${this.rateLimitInfo.remaining} requests remaining`)}calculateRateLimitWait(e=0){if(this.rateLimitInfo.reset){let s=Math.floor(Date.now()/1e3),a=(this.rateLimitInfo.reset-s)*1e3;return Math.max(a,1e3)}let i=5e3*Math.pow(2,e);return Math.min(i,6e4)}async handleErrorResponse(e){let t=`${this.providerName} API error: ${e.status} ${e.statusText}`,i=t;try{let r=await e.json();if(typeof r=="object"&&r!==null){let s=r;if(typeof s.error=="string")i=`${t} - ${s.error}`;else if(typeof s.message=="string")i=`${t} - ${s.message}`;else if(Array.isArray(s.errors)){let a=s.errors.map(l=>typeof l=="object"&&l!==null&&"message"in l?String(l.message):JSON.stringify(l)).join(", ");i=`${t} - ${a}`}}}catch{}return o.error(i),new Error(i)}isNonRetryableError(e){if(typeof e=="object"&&e!==null&&e.name==="AbortError")return!0;let t=e.message;return typeof t=="string"&&t.includes("API error: 4")?!t.includes("429"):!1}delay(e){return new Promise(t=>setTimeout(t,e))}getRateLimitInfo(){return{...this.rateLimitInfo}}isLargeResponse(e){if(!e||typeof e!="object")return!1;let t=(i,r=0)=>{if(r>10)return!1;for(let[,s]of Object.entries(i))if(Array.isArray(s)&&s.length>1e3||typeof s=="object"&&s!==null&&t(s,r+1))return!0;return!1};return t(e)}validateLargeResponse(e){if(!e||typeof e!="object")return;let t=(r,s)=>{r.length>5e3&&o.warn(`Large array detected at ${s} with ${r.length} items. This may impact performance.`);let a=Math.min(10,r.length),l=r[0];if(l&&typeof l=="object"){let c=Object.keys(l);for(let d=1;d<a;d++){let u=r[d];if(!u||typeof u!="object"){o.warn(`Inconsistent array structure at ${s}[${d}]: expected object, got ${typeof u}`);continue}let p=Object.keys(u),f=c.filter(h=>!p.includes(h));f.length>0&&o.warn(`Inconsistent array structure at ${s}[${d}]: missing keys ${f.join(", ")}`)}}},i=(r,s="root",a=0)=>{if(!(a>10))for(let[l,c]of Object.entries(r)){let d=`${s}.${l}`;Array.isArray(c)?t(c,d):typeof c=="object"&&c!==null&&i(c,d,a+1)}};i(e)}};var Ei="https://api.vercel.com/v1/security/firewall/config",Ue=class extends De{constructor(t,i,r){super("","vercel");this.projectId=t;this.teamId=i;this.token=r}projectId;teamId;token;getAuthHeaders(){return{Authorization:`Bearer ${this.token}`}}getUrl(t){let i=t!==void 0?`${Ei}/${t}`:Ei;return o.debug("API URL:",i),`${i}?projectId=${this.projectId}&teamId=${this.teamId}`}async fetchFirewallConfig(t){let i=await this.get(this.getUrl(t));if(o.debug("Config Version:",t??"latest"),o.debug("Fetched Config:",t?i:i.active),t)return i;if(!i||"active"in i&&i.active===null)if(o.warn(Tt.default.bold("No firewall configuration found.")),await C("Would you like to create one?",{type:"confirm"})){o.debug("Creating new empty firewall configuration...");let s=await this.putEmptyConfig();return o.debug("Empty firewall configuration created successfully"),o.debug(`New configuration version: ${Tt.default.yellow(s.version)}`),s}else return i.active;return i.active}async putEmptyConfig(){let{$schema:t,...i}=Le();return o.debug("Empty Config:",i),this.putConfig(i)}async putConfig(t){return(await this.put(this.getUrl(),t)).active}async fetchActiveFirewallRules(){return(await this.fetchFirewallConfig())?.rules}async updateFirewallRule(t){let i=!t.id||t.id==="-",r={action:i?"rules.insert":"rules.update",id:i?null:t.id,value:{name:t.name,description:t.description,action:t.action,conditionGroup:t.conditionGroup,active:t.active}};return this.patch(this.getUrl(),r)}async createFirewallRule(t){return this.updateFirewallRule({...t,id:"-"})}async deleteFirewallRule(t){let i={action:"rules.remove",id:t.id,value:null};await this.patch(this.getUrl(),i)}async updateIPBlockingRule(t){let i=!t.id||t.id==="-",r={action:i?"ip.insert":"ip.update",id:i?null:t.id,value:{action:t.action,hostname:t.hostname,ip:t.ip,...t.notes&&{notes:t.notes}}};return this.patch(this.getUrl(),r)}async createIPBlockingRule(t){return this.updateIPBlockingRule({...t,id:"-"})}async deleteIPBlockingRule(t){let i={action:"ip.remove",id:t.id,value:null};await this.patch(this.getUrl(),i)}async verifyCredentials(){try{return await this.fetchFirewallConfig(),!0}catch(t){return o.debug("Credential verification failed:",t),!1}}};var ne=S(require("chalk"));k();k();var xe=class{validateConfig(e){let t=[],i=[];return e||t.push({path:"root",message:"Configuration is required",code:"CONFIG_REQUIRED"}),e&&!e.rules&&t.push({path:"rules",message:"Rules array is required",code:"RULES_REQUIRED"}),e&&e.rules&&!Array.isArray(e.rules)&&t.push({path:"rules",message:"Rules must be an array",code:"RULES_INVALID_TYPE"}),{valid:t.length===0,errors:t,warnings:i}}getHealthScore(e){let t=[],i=100;if(!e)return{score:0,grade:"poor",issues:[{severity:"error",category:"configuration",message:"No configuration found"}],recommendations:["Create a configuration file"]};if((!e.rules||e.rules.length===0)&&(i-=20,t.push({severity:"warning",category:"rules",message:"No rules defined",suggestion:"Add security rules to protect your application"})),e.rules&&e.rules.length>0){let a=e.rules.filter(l=>!l.description);a.length>0&&(i-=5,t.push({severity:"info",category:"maintainability",message:`${a.length} rule(s) missing descriptions`,suggestion:"Add descriptions to improve maintainability"}))}if(e.rules&&e.rules.length>0){let a=e.rules.filter(l=>l.active===!1||l.enabled===!1);a.length>0&&(i-=5,t.push({severity:"info",category:"maintenance",message:`${a.length} disabled rule(s) found`,suggestion:"Review and remove unused rules"}))}let r=this.calculateGrade(i),s=this.generateRecommendations(e,t);return{score:Math.max(0,i),grade:r,issues:t,recommendations:s}}diffItems(e,t,i,r="id"){let s=[],a=[],l=[],c=d=>d[r];for(let d of e){let u=t.find(p=>c(p)===c(d));u?i(d,u)||a.push(d):s.push(d)}for(let d of t)e.find(p=>c(p)===c(d))||l.push(d);return{toAdd:s,toUpdate:a,toDelete:l}}validateRuleCount(e,t){if(t&&e>t)throw new Error(`Rule count (${e}) exceeds provider limit (${t})`);t&&e>t*.9&&o.warn(`Approaching rule limit: ${e}/${t} rules`)}updateMetadata(e,t){return{...e,metadata:{...e.metadata,version:t,updatedAt:new Date().toISOString()}}}calculateGrade(e){return e>=80?"excellent":e>=60?"good":e>=40?"fair":"poor"}generateRecommendations(e,t){let i=[];return t.some(r=>r.category==="rules")&&i.push("Consider using predefined templates to get started"),t.some(r=>r.category==="maintainability")&&i.push("Add descriptions to all rules for better documentation"),t.some(r=>r.category==="maintenance")&&i.push("Remove or enable disabled rules to keep configuration clean"),i}logSyncStats(e){o.info(`Sync complete for ${this.name}:`),o.info(` Rules added: ${e.rulesAdded}`),o.info(` Rules updated: ${e.rulesUpdated}`),o.info(` Rules deleted: ${e.rulesDeleted}`),e.ipsAdded!==void 0&&(o.info(` IPs added: ${e.ipsAdded}`),o.info(` IPs updated: ${e.ipsUpdated}`),o.info(` IPs deleted: ${e.ipsDeleted}`)),e.version&&o.info(` New version: ${e.version}`)}};k();function Oe(n){return n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}var qe=class{static vercelToCloudflare={host:"http.host",path:"http.request.uri.path",method:"http.request.method",header:"http.request.headers",query:"http.request.uri.query",cookie:"http.cookie",target_path:"http.request.uri.path",ip_address:"ip.src",region:"ip.geoip.subdivision_1",protocol:"ssl",scheme:"ssl",environment:"",user_agent:"http.user_agent",geo_continent:"ip.geoip.continent",geo_country:"ip.geoip.country",geo_country_region:"ip.geoip.subdivision_1",geo_city:"ip.geoip.city",geo_as_number:"ip.geoip.asnum",ja4_digest:"",ja3_digest:"",rate_limit_api_id:""};static cloudflareToVercel={"http.host":"host","http.request.uri.path":"path","http.request.method":"method","http.request.headers":"header","http.request.uri.query":"query","http.cookie":"cookie","ip.src":"ip_address","ip.geoip.subdivision_1":"region",ssl:"protocol","http.user_agent":"user_agent","http.referer":"header","ip.geoip.continent":"geo_continent","ip.geoip.country":"geo_country","ip.geoip.city":"geo_city","ip.geoip.asnum":"geo_as_number"};static toCloudflare(e,t){let i=this.vercelToCloudflare[e];if(!i)throw o.warn(`No Cloudflare mapping for Vercel type: ${e}`),new Error(`Unsupported Vercel rule type for Cloudflare: ${e}`);return e==="header"&&t?`${i}["${Oe(t.toLowerCase())}"]`:e==="cookie"&&t?`${i}["${Oe(t)}"]`:i}static toVercel(e){let t=e.match(/http\.request\.headers\["([^"]+)"\]/);if(t)return{type:"header",key:t[1]};let i=e.match(/http\.cookie\["([^"]+)"\]/);if(i)return{type:"cookie",key:i[1]};let r=this.cloudflareToVercel[e];if(r)return{type:r};throw o.warn(`No Vercel mapping for Cloudflare field: ${e}`),new Error(`Unsupported Cloudflare field for Vercel: ${e}`)}static isCloudflareSupported(e){let t=this.vercelToCloudflare[e];return!!t&&t!==""}static isVercelSupported(e){return!!(this.cloudflareToVercel[e]||e.startsWith("http.request.headers[")||e.startsWith("http.cookie["))}static getSupportedCloudflareFields(){return Object.values(this.vercelToCloudflare).filter(e=>e!=="")}static getSupportedVercelTypes(){return Object.keys(this.vercelToCloudflare).filter(e=>this.isCloudflareSupported(e))}};var Fe=class{static fromVercelConditionGroups(e){if(!e||e.length===0)throw new Error("At least one condition group is required");let t=e.map(i=>{let r=i.conditions.map(s=>this.fromVercelCondition(s));if(r.length===0)throw new Error("Condition group must have at least one condition");return r.length>1?`(${r.join(" and ")})`:r[0]});return t.length>1?t.join(" or "):t[0]}static fromVercelCondition(e){let t=qe.toCloudflare(e.type,e.key),i=this.mapVercelOperator(e.op),r=this.formatValue(e.value,e.op),s=`${t} ${i} ${r}`;return e.neg&&(s=`not (${s})`),s}static fromUnifiedConditions(e,t="AND"){if(!e||e.length===0)throw new Error("At least one condition is required");let i=e.map(s=>this.fromUnifiedCondition(s)),r=t==="AND"?" and ":" or ";return i.length>1?`(${i.join(r)})`:i[0]}static fromUnifiedCondition(e){let t=this.mapUnifiedFieldToCloudflare(e.field),i=e.key&&(e.field==="header"||e.field==="cookie")?`${t}["${Oe(e.key)}"]`:t,r=this.mapUnifiedOperator(e.operator),s=this.formatValue(e.value,r),a=`${i} ${r} ${s}`;return e.negated&&(a=`not (${a})`),a}static mapVercelOperator(e){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists",nex:"not exists"}[e]||e}static mapUnifiedOperator(e){return{eq:"eq",ne:"ne",contains:"contains",not_contains:"not contains",starts_with:"starts_with",ends_with:"ends_with",matches:"matches",in:"in",not_in:"not in",gt:"gt",ge:"ge",lt:"lt",le:"le",exists:"exists",not_exists:"not exists"}[e]||e}static mapUnifiedFieldToCloudflare(e){return{ip:"ip.src",country:"ip.geoip.country",region:"ip.geoip.subdivision_1",city:"ip.geoip.city",asn:"ip.geoip.asnum",path:"http.request.uri.path",host:"http.host",method:"http.request.method",header:"http.request.headers",query:"http.request.uri.query",cookie:"http.cookie",user_agent:"http.user_agent",referer:"http.referer",scheme:"ssl",port:"cf.edge.server_port"}[e]||e}static formatValue(e,t){return Array.isArray(e)?`{${e.map(r=>this.formatSingleValue(r)).join(" ")}}`:this.formatSingleValue(e)}static formatSingleValue(e){return typeof e=="string"?`"${Oe(e)}"`:typeof e=="number"?String(e):typeof e=="boolean"?e?"true":"false":String(e)}static validate(e){if(!e||e.trim().length===0)return!1;let t=0;for(let i of e)if(i==="("&&t++,i===")"&&t--,t<0)return!1;return t===0}static combineWithAnd(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" and ")})`}static combineWithOr(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" or ")})`}};k();var M=class{static vercelToCloudflare(e){let t=[];try{let i=Fe.fromVercelConditionGroups(e.conditionGroup),r=this.translateVercelActionToCloudflare(e.action.mitigate.action),s={id:e.id||crypto.randomUUID(),action:r,expression:i,description:e.description||e.name,enabled:e.active};if(e.action.mitigate.action==="rate_limit"&&e.action.mitigate.rateLimit){let a=e.action.mitigate.rateLimit;if(s.ratelimit={characteristics:a.characteristics||["ip.src"],period:this.parseWindowToSeconds(a.window),requests_per_period:a.requests},a.mitigationTimeout)s.ratelimit.mitigation_timeout=a.mitigationTimeout;else{s.ratelimit.mitigation_timeout=3600;let{TranslationWarningSystem:l}=(pe(),fe(me));t.push(l.createWarning("rate_limiting_precision",e.id,"rateLimit.mitigationTimeout","No mitigation timeout specified, using default 1 hour (3600 seconds)","Specify mitigationTimeout in your rate limit configuration for precise control"))}a.countingExpression&&(s.ratelimit.counting_expression=a.countingExpression)}return e.action.mitigate.action==="redirect"&&e.action.mitigate.redirect&&(s.action_parameters={from_value:{status_code:e.action.mitigate.redirect.permanent?301:302,target_url:{value:e.action.mitigate.redirect.location}}}),{result:s,warnings:t}}catch(i){throw o.error(`Failed to translate Vercel rule to Cloudflare: ${i}`),i}}static cloudflareToVercel(e){let t=[],{TranslationWarningSystem:i}=(pe(),fe(me));return t.push(i.createLossyConversionWarning("Cloudflare expression","Cloudflare expressions cannot be perfectly converted to Vercel structured conditions",e.id,"expression")),{result:{id:e.id,name:e.description||`Rule ${e.id}`,description:e.description,conditionGroup:[{conditions:[]}],action:{mitigate:{action:this.translateCloudflareActionToVercel(e.action)}},active:e.enabled??!0},warnings:t}}static vercelToUnified(e){let t=[],i=[];for(let a of e.conditionGroup)for(let l of a.conditions){let c=this.mapVercelOperatorToUnified(l.op);if(l.op==="re"&&typeof l.value=="string"){let{TranslationWarningSystem:d}=(pe(),fe(me));t.push(d.createWarning("regex_patterns",e.id,l.type,`Regular expression pattern may need adjustment for target provider: ${l.value}`,"Test the regex pattern in the target provider and adjust syntax if needed"))}i.push({field:this.mapVercelTypeToUnified(l.type),operator:c,value:l.value,negated:l.neg,key:l.key})}if(i.length>10){let{TranslationWarningSystem:a}=(pe(),fe(me));t.push(a.createWarning("many_conditions",e.id,void 0,`Rule has ${i.length} conditions which may impact performance`,"Consider splitting complex rules into multiple simpler rules for better performance"))}let r={type:e.action.mitigate.action,rateLimit:e.action.mitigate.rateLimit?{requests:e.action.mitigate.rateLimit.requests,window:e.action.mitigate.rateLimit.window,characteristics:e.action.mitigate.rateLimit.characteristics,mitigationTimeout:e.action.mitigate.rateLimit.mitigationTimeout,countingExpression:e.action.mitigate.rateLimit.countingExpression}:void 0,redirect:e.action.mitigate.redirect?{location:e.action.mitigate.redirect.location,permanent:e.action.mitigate.redirect.permanent}:void 0,duration:e.action.mitigate.actionDuration||void 0};return{result:{id:e.id,name:e.name,description:e.description,enabled:e.active,conditions:i,conditionLogic:"OR",action:r},warnings:t}}static cloudflareToUnified(e){let t=[],{TranslationWarningSystem:i}=(pe(),fe(me));t.push(i.createWarning("complex_expressions",e.id,"expression","Expression parsing not fully implemented. Using simplified translation.","Review the translated rule and add missing conditions manually if needed."));let r={type:this.mapCloudflareActionToUnified(e.action),rateLimit:e.ratelimit?{requests:e.ratelimit.requests_per_period,window:`${e.ratelimit.period}s`,characteristics:e.ratelimit.characteristics,mitigationTimeout:e.ratelimit.mitigation_timeout,countingExpression:e.ratelimit.counting_expression}:void 0};return{result:{id:e.id,name:e.description||`Rule ${e.id}`,description:e.description,enabled:e.enabled??!0,conditions:[],action:r},warnings:t}}static unifiedToCloudflare(e){let t=[],i=Fe.fromUnifiedConditions(e.conditions,e.conditionLogic),r={id:e.id||crypto.randomUUID(),action:this.mapUnifiedActionToCloudflare(e.action.type),expression:i,description:e.description||e.name,enabled:e.enabled};return e.action.rateLimit&&(r.ratelimit={characteristics:e.action.rateLimit.characteristics||["ip.src"],period:this.parseWindowToSeconds(e.action.rateLimit.window),requests_per_period:e.action.rateLimit.requests},e.action.rateLimit.mitigationTimeout?r.ratelimit.mitigation_timeout=e.action.rateLimit.mitigationTimeout:r.ratelimit.mitigation_timeout=3600,e.action.rateLimit.countingExpression&&(r.ratelimit.counting_expression=e.action.rateLimit.countingExpression)),{result:r,warnings:t}}static unifiedToVercel(e){let t=[],i=[],r=e.conditions.map(a=>({op:this.mapUnifiedOperatorToVercel(a.operator),neg:a.negated,type:this.mapUnifiedTypeToVercel(a.field),key:a.key,value:a.value}));return i.push({conditions:r}),{result:{id:e.id,name:e.name,description:e.description,conditionGroup:i,action:{mitigate:{action:e.action.type,rateLimit:e.action.rateLimit?{requests:e.action.rateLimit.requests,window:e.action.rateLimit.window,characteristics:e.action.rateLimit.characteristics,mitigationTimeout:e.action.rateLimit.mitigationTimeout,countingExpression:e.action.rateLimit.countingExpression}:null,redirect:e.action.redirect?{location:e.action.redirect.location,permanent:e.action.redirect.permanent}:null,actionDuration:e.action.duration||null}},active:e.enabled},warnings:t}}static vercelIPToUnified(e){return{id:e.id,ip:e.ip,hostname:e.hostname,notes:e.notes,action:e.action}}static unifiedIPToCloudflare(e){if(!et.safeParse(e.ip).success)throw new Error(`Invalid IP address or CIDR range: ${e.ip}`);return{id:e.id||crypto.randomUUID(),action:e.action==="allow"?"allow":"block",expression:`ip.src eq ${e.ip}`,description:e.notes||`IP ${e.action}: ${e.ip}${e.hostname?` (${e.hostname})`:""}`,enabled:!0}}static translateVercelActionToCloudflare(e){return{log:"log",deny:"block",challenge:"managed_challenge",bypass:"skip",rate_limit:"block",redirect:"redirect"}[e]||"block"}static translateCloudflareActionToVercel(e){return{block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",log:"log",skip:"bypass",allow:"bypass",rewrite:"bypass",redirect:"redirect"}[e]||"deny"}static mapVercelOperatorToUnified(e){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists",nex:"not_exists"}[e]||"eq"}static mapUnifiedOperatorToVercel(e){return{eq:"eq",starts_with:"pre",ends_with:"suf",in:"inc",contains:"sub",matches:"re",exists:"ex",not_exists:"nex"}[e]||"eq"}static mapVercelTypeToUnified(e){return{host:"host",path:"path",method:"method",header:"header",query:"query",cookie:"cookie",ip_address:"ip",user_agent:"user_agent",geo_country:"country",geo_city:"city",geo_as_number:"asn",scheme:"scheme"}[e]||e}static mapUnifiedTypeToVercel(e){return{host:"host",path:"path",method:"method",header:"header",query:"query",cookie:"cookie",ip:"ip_address",user_agent:"user_agent",country:"geo_country",city:"geo_city",asn:"geo_as_number",scheme:"scheme"}[e]||"path"}static mapCloudflareActionToUnified(e){return{block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",log:"log",skip:"bypass",allow:"allow",rewrite:"bypass",redirect:"redirect"}[e]||"deny"}static mapUnifiedActionToCloudflare(e){return{log:"log",deny:"block",block:"block",challenge:"managed_challenge",bypass:"skip",rate_limit:"block",redirect:"redirect",allow:"allow"}[e]||"block"}static parseWindowToSeconds(e){let t=e.match(/^(\d+)([smhd])$/);if(!t||!t[1]||!t[2])throw new Error(`Invalid window format: ${e}`);let i=parseInt(t[1],10),r=t[2],a={s:1,m:60,h:3600,d:86400}[r];if(a===void 0)throw new Error(`Invalid time unit: ${r}`);return i*a}};pe();var Ne=class extends xe{constructor(t){super();this.client=t}client;name="vercel";async fetchConfig(t){try{o.debug("Fetching Vercel firewall configuration");let i=await this.client.fetchFirewallConfig(t),r=i.rules.map(a=>{let l=M.vercelToUnified(a);return l.warnings.length>0&&l.warnings.forEach(c=>{let{TranslationWarningSystem:d}=(pe(),fe(me)),u=d.formatWarning(c);o.warn(`Rule ${a.name}:
35
+ ${u}`)}),l.result}),s=i.ips.map(a=>M.vercelIPToUnified(a));return{version:"2.0",provider:"vercel",rules:r,ips:s,metadata:{version:i.version,updatedAt:i.updatedAt}}}catch(i){throw o.error("Error fetching Vercel configuration:",i),new Error("Failed to fetch Vercel firewall configuration")}}async syncRules(t,i={}){let{dryRun:r=!1}=i;try{let s=await this.getChanges(t),{rulesToAdd:a,rulesToUpdate:l,rulesToDelete:c,version:d}=s,u=s.ipsToAdd||[],p=s.ipsToUpdate||[],f=s.ipsToDelete||[];if(r)return o.info("Dry run mode. The following changes would be made:"),o.info(`Custom Rules - Add: ${a.length}, Update: ${l.length}, Delete: ${c.length}`),o.info(`IP Rules - Add: ${u.length}, Update: ${p.length}, Delete: ${f.length}`),{success:!0,rulesAdded:0,rulesUpdated:0,rulesDeleted:0,ipsAdded:0,ipsUpdated:0,ipsDeleted:0,version:d};let h=a.map(E=>M.unifiedToVercel(E).result),I=l.map(E=>M.unifiedToVercel(E).result),g=c.map(E=>M.unifiedToVercel(E).result),A=u.map(E=>({id:E.id||"",ip:E.ip,hostname:E.hostname||"",action:E.action,notes:E.notes})),N=p.map(E=>({id:E.id||"",ip:E.ip,hostname:E.hostname||"",action:E.action,notes:E.notes})),v=f.map(E=>({id:E.id||"",ip:E.ip,hostname:E.hostname||"",action:E.action,notes:E.notes})),m=[],P=[],w=[],b=[],re=[],ye=[];for(let E of g)o.debug(`Deleting custom rule: ${E.id}`),await oe(()=>this.client.deleteFirewallRule(E),{maxAttempts:3}),w.push(E),o.debug(`Custom rule deleted: ${E.id}`);for(let E of v)o.debug(`Deleting IP blocking rule: ${E.id}`),await oe(()=>this.client.deleteIPBlockingRule(E),{maxAttempts:3}),ye.push(E),o.debug(`IP blocking rule deleted: ${E.id}`);for(let E of h){o.debug(`Adding new custom rule: ${E.name}`);let ue=await oe(()=>this.client.createFirewallRule(E),{maxAttempts:3});m.push(ue),o.debug(`New custom rule added: ${ue.id}`)}for(let E of A){o.debug(`Adding new IP blocking rule: ${E.ip}`);let ue=await oe(()=>this.client.createIPBlockingRule(E),{maxAttempts:3});b.push(ue),o.debug(`New IP blocking rule added: (hostname): ${ue.hostname} (ip): ${ue.ip}`)}for(let E of I){o.debug(`Updating custom rule: ${E.id}`);let ue=await oe(()=>this.client.updateFirewallRule(E),{maxAttempts:3});P.push(ue),o.debug(`Custom rule updated: ${ue.id}`)}for(let E of N){o.debug(`Updating IP blocking rule: ${E.id}`);let ue=await oe(()=>this.client.updateIPBlockingRule(E),{maxAttempts:3});re.push(ue),o.debug(`IP blocking rule updated: ${ue.id}`)}o.debug(`${ne.default.underline("Custom Rules:")} ${ne.default.green("Added:")} ${ne.default.green(m.length)}, ${ne.default.cyan("Updated:")} ${ne.default.cyan(P.length)}, ${ne.default.red("Deleted:")} ${ne.default.red(w.length)}`),o.debug(`${ne.default.underline("IP Rules:")} ${ne.default.green("Added:")} ${ne.default.green(b.length)}, ${ne.default.cyan("Updated:")} ${ne.default.cyan(re.length)}, ${ne.default.red("Deleted:")} ${ne.default.red(ye.length)}`);let lt=await this.client.fetchFirewallConfig();return{success:!0,rulesAdded:m.length,rulesUpdated:P.length,rulesDeleted:w.length,ipsAdded:b.length,ipsUpdated:re.length,ipsDeleted:ye.length,version:lt.version}}catch(s){throw o.error("Error during sync:",s),new Error("Failed to synchronize firewall rules")}}async getChanges(t){try{o.debug("Fetching existing firewall configuration");let i=await this.client.fetchFirewallConfig();o.debug(`Fetched ${i.rules.length} custom rules and ${i.ips.length} IP blocking rules`);let r=t.rules.map(v=>{let m=M.unifiedToVercel(v);return m.warnings.length>0&&m.warnings.forEach(P=>{let{TranslationWarningSystem:w}=(pe(),fe(me)),b=w.formatWarning(P);o.warn(`Rule ${v.name}:
36
+ ${b}`)}),m.result}),{toAdd:s,toUpdate:a,toDelete:l}=this.diffRules(r,i.rules),c=(t.ips||[]).map(v=>({id:v.id||"",ip:v.ip,hostname:v.hostname||"",action:v.action,notes:v.notes})),{ipsToAdd:d,ipsToUpdate:u,ipsToDelete:p}=this.diffIPRules(c,i.ips),f=s.map(v=>M.vercelToUnified(v).result),h=a.map(v=>M.vercelToUnified(v).result),I=l.map(v=>M.vercelToUnified(v).result),g=d.map(v=>M.vercelIPToUnified(v)),A=u.map(v=>M.vercelIPToUnified(v)),N=p.map(v=>M.vercelIPToUnified(v));return{version:i.version,rulesToAdd:f,rulesToUpdate:h,rulesToDelete:I,ipsToAdd:g,ipsToUpdate:A,ipsToDelete:N,hasChanges:s.length>0||a.length>0||l.length>0||d.length>0||u.length>0||p.length>0}}catch(i){throw o.error("Error fetching existing firewall configuration:",i),new Error("Failed to fetch existing firewall configuration")}}getSupportedFeatures(){return{supportsCustomRules:!0,supportsIPBlocking:!0,supportsRateLimiting:!0,supportsGeoBlocking:!0,supportsManagedRules:!1,supportsRedirect:!0,supportsChallenge:!0}}async verifyCredentials(){return this.client.verifyCredentials()}validateConfig(t){let i=super.validateConfig(t),r=[...i.errors],s=[...i.warnings];t.provider&&t.provider!=="vercel"&&r.push({path:"provider",message:"Provider must be 'vercel' for VercelFirewallService",code:"INVALID_PROVIDER"});try{let a=we.safeParse(t);a.success||r.push({path:"root",message:`Schema validation failed: ${a.error.message}`,code:"SCHEMA_VALIDATION_FAILED"})}catch(a){o.debug("Schema validation error:",a)}return{valid:r.length===0,errors:r,warnings:s}}getHealthScore(t){let i=super.getHealthScore(t),r=[...i.issues],s=i.score;return t.rules.filter(l=>l.action?.type==="rate_limit").length===0&&(s-=10,r.push({severity:"info",category:"security",message:"No rate limiting rules configured",suggestion:"Consider adding rate limiting to protect against abuse"})),(!t.ips||t.ips.length===0)&&r.push({severity:"info",category:"security",message:"No IP blocking rules configured",suggestion:"Consider blocking known malicious IPs"}),{score:Math.max(0,s),grade:i.grade,issues:r,recommendations:i.recommendations}}diffRules(t,i){let r=[],s=[],a=[...i];for(let l of t){let c=i.find(d=>d.id===l.id);if(!c)r.push(l);else{ae(j(l),j(c))||s.push({...l,id:c.id});let d=a.findIndex(u=>u.id===c.id);d!==-1&&a.splice(d,1)}}return{toAdd:r,toUpdate:s,toDelete:a}}diffIPRules(t,i){let r=[],s=[],a=[...i];for(let l of t){let c=i.find(d=>d.id===l.id);if(!c)o.debug("Rule not found in existing rules:",{configRule:l}),r.push(l);else{ae(j(l),j(c))||s.push({...c,...l});let d=a.findIndex(u=>u.id===c.id);d!==-1&&a.splice(d,1)}}return{ipsToAdd:r,ipsToUpdate:s,ipsToDelete:a}}};k();var Ve=class{static fromEnv(){let e=process.env.VERCEL_TOKEN,t=process.env.VERCEL_PROJECT_ID,i=process.env.VERCEL_TEAM_ID;if(!e)throw new Error("VERCEL_TOKEN environment variable is required");if(!t)throw new Error("VERCEL_PROJECT_ID environment variable is required");if(!i)throw new Error("VERCEL_TEAM_ID environment variable is required");let r=new Ue(t,i,e);return new Ne(r)}static fromConfig(e){let t=e.token||process.env.VERCEL_TOKEN,i=e.projectId||process.env.VERCEL_PROJECT_ID,r=e.teamId||process.env.VERCEL_TEAM_ID;if(!t)throw new Error("Vercel API token is required (provide token or set VERCEL_TOKEN env var)");if(!i)throw new Error("Vercel project ID is required (provide projectId or set VERCEL_PROJECT_ID env var)");if(!r)throw new Error("Vercel team ID is required (provide teamId or set VERCEL_TEAM_ID env var)");o.debug("Creating Vercel provider with config:",{projectId:i,teamId:r,token:t.substring(0,10)+"..."});let s=new Ue(i,r,t);return new Ne(s)}static create(e,t,i){return this.fromConfig({projectId:e,teamId:t,token:i})}};k();var Te=S(require("chalk")),y=class n extends Error{code;suggestion;details;docsUrl;cause;constructor(e){super(e.message),this.name="DoormanError",this.code=e.code,this.suggestion=e.suggestion,this.details=e.details,this.docsUrl=e.docsUrl,this.cause=e.cause,Error.captureStackTrace&&Error.captureStackTrace(this,n)}format(){let e=[];return e.push(Te.default.red.bold(`[${this.code}] ${this.message}`)),e.push(""),this.suggestion&&(e.push(Te.default.yellow.bold("Suggestion:")),e.push(` ${this.suggestion}`),e.push("")),this.details&&Object.keys(this.details).length>0&&(e.push(Te.default.dim("Details:")),Object.entries(this.details).forEach(([t,i])=>{let r=typeof i=="object"?JSON.stringify(i,null,2).split(`
37
37
  `).join(`
38
38
  `):i;e.push(` ${Te.default.cyan(t)}: ${r}`)}),e.push("")),this.cause&&(e.push(Te.default.dim("Caused by:")),e.push(` ${this.cause.message}`),e.push("")),this.docsUrl&&(e.push(Te.default.cyan.bold("Documentation:")),e.push(` ${this.docsUrl}`),e.push("")),e.join(`
39
39
  `)}toPlainText(){let e=[];return e.push(`[${this.code}] ${this.message}`),this.suggestion&&e.push(`Suggestion: ${this.suggestion}`),this.details&&Object.keys(this.details).length>0&&(e.push("Details:"),Object.entries(this.details).forEach(([t,i])=>{e.push(` ${t}: ${i}`)})),this.cause&&e.push(`Caused by: ${this.cause.message}`),this.docsUrl&&e.push(`Documentation: ${this.docsUrl}`),e.join(`
40
- `)}static isDoormanError(e){return e instanceof n}static from(e,t,i){return n.isDoormanError(e)?e:e instanceof Error?new n({code:t,message:i||e.message,cause:e}):new n({code:t,message:i||String(e)})}};var U="https://docs.doorman.griffen.codes/errors";var K={authFailed:(n,e)=>new y({code:"PROV_5000",message:`Authentication failed for ${n}`,suggestion:"Check that your API credentials are valid and have the correct permissions",details:{provider:n},cause:e,docsUrl:`${U}/PROV_5000`}),apiError:(n,e,t,i)=>new y({code:"PROV_5002",message:`${n} API error: ${t} ${i}`,suggestion:"Check the provider status page and your credentials",details:{provider:n,endpoint:e,statusCode:t,responseMessage:i},docsUrl:`${U}/PROV_5002`}),rateLimit:(n,e)=>new y({code:"PROV_5003",message:`Rate limit exceeded for ${n}`,suggestion:e?`Wait ${e} seconds and try again, or use --retry flag`:"Wait a few minutes and try again, or use --retry flag",details:{provider:n,retryAfter:e},docsUrl:`${U}/PROV_5003`}),timeout:(n,e)=>new y({code:"PROV_5006",message:`Request timeout for ${n} ${e}`,suggestion:"Check your internet connection and try again",details:{provider:n,operation:e},docsUrl:`${U}/PROV_5006`}),invalidCredentials:(n,e)=>new y({code:"PROV_5004",message:`Missing or invalid credentials for ${n}`,suggestion:`Please provide: ${e.join(", ")}`,details:{provider:n,missing:e},docsUrl:`${U}/PROV_5004`})},q={accountIdRequired:n=>new y({code:"CF_6004",message:`Account ID required for ${n}`,suggestion:"Provide CLOUDFLARE_ACCOUNT_ID in your environment or configuration to use Lists API",details:{operation:n},docsUrl:`${U}/CF_6004`}),zoneIdRequired:n=>new y({code:"CF_6005",message:`Zone ID required for ${n}`,suggestion:"Provide CLOUDFLARE_ZONE_ID in your environment or configuration",details:{operation:n},docsUrl:`${U}/CF_6005`}),invalidCredentials:(n,e)=>new y({code:"CF_6016",message:`Invalid Cloudflare ${n}`,suggestion:"Check your Cloudflare API credentials and ensure they have the correct permissions",details:{credentialType:n,...e},docsUrl:`${U}/CF_6016`}),insufficientPermissions:(n,e)=>new y({code:"CF_6017",message:`Insufficient permissions for ${n}`,suggestion:`Ensure your API token has the following permissions: ${e.join(", ")}`,details:{operation:n,requiredPermissions:e},docsUrl:`${U}/CF_6017`}),ruleLimitExceeded:(n,e)=>new y({code:"CF_6001",message:`Rule count (${n}) exceeds Cloudflare limit (${e})`,suggestion:"Consider consolidating rules, using Lists for IP blocking, or upgrading your Cloudflare plan",details:{count:n,limit:e},docsUrl:`${U}/CF_6001`}),invalidExpression:(n,e)=>new y({code:"CF_6002",message:`Invalid Cloudflare expression: ${e}`,suggestion:"Check Cloudflare Wirefilter expression syntax and field names",details:{expression:n,reason:e},docsUrl:`${U}/CF_6002`}),ruleNoConditions:n=>new y({code:"CF_6007",message:`Rule "${n}" has no conditions`,suggestion:"Add at least one condition to the rule or remove the empty rule",details:{ruleName:n},docsUrl:`${U}/CF_6007`}),invalidRateLimit:(n,e)=>new y({code:"CF_6008",message:`Invalid rate limit configuration for rule "${n}": ${e}`,suggestion:'Rate limit requests must be at least 1, and window format should be like "60s", "1h", "1d"',details:{ruleName:n,issue:e},docsUrl:`${U}/CF_6008`}),invalidWindowFormat:n=>new y({code:"CF_6009",message:`Invalid rate limit window format: ${n}`,suggestion:'Use format like "60s", "5m", "1h", or "1d"',details:{window:n},docsUrl:`${U}/CF_6009`}),shortMitigationTimeout:n=>new y({code:"CF_6010",message:`Mitigation timeout (${n}s) may be too short`,suggestion:"Consider using at least 60 seconds for effective rate limiting",details:{timeout:n},docsUrl:`${U}/CF_6010`}),redirectNoLocation:n=>new y({code:"CF_6011",message:`Redirect rule "${n}" is missing location URL`,suggestion:"Add a redirect.location property with a valid URL or path",details:{ruleName:n},docsUrl:`${U}/CF_6011`}),invalidRedirectUrl:n=>new y({code:"CF_6012",message:`Invalid redirect URL: ${n}`,suggestion:"Use a valid absolute URL (https://...) or relative path (/...)",details:{url:n},docsUrl:`${U}/CF_6012`}),invalidIP:n=>new y({code:"CF_6013",message:`Invalid IP address format: ${n}`,suggestion:"Use valid IPv4 or IPv6 address with optional CIDR notation (e.g., 192.168.1.1 or 192.168.1.0/24)",details:{ip:n},docsUrl:`${U}/CF_6013`}),largeIPList:n=>new y({code:"CF_6014",message:`Large IP list detected (${n} IPs)`,suggestion:"Consider providing CLOUDFLARE_ACCOUNT_ID to use Lists API for better performance with large IP lists",details:{count:n},docsUrl:`${U}/CF_6014`}),rulesetNotFound:n=>new y({code:"CF_6000",message:n?`Ruleset not found: ${n}`:"Ruleset not found",suggestion:"The ruleset may have been deleted. Try running the command again to create a new one.",details:{rulesetId:n},docsUrl:`${U}/CF_6000`}),listNotFound:n=>new y({code:"CF_6003",message:n?`List not found: ${n}`:"List not found",suggestion:"The IP list may have been deleted. Try running the command again to create a new one.",details:{listId:n},docsUrl:`${U}/CF_6003`}),emptyCharacteristics:n=>new y({code:"CF_6015",message:`Rate limit rule "${n}" has empty characteristics`,suggestion:'Add characteristics like ["ip.src"] or remove the characteristics array to use default',details:{ruleName:n},docsUrl:`${U}/CF_6015`}),translationWarning:(n,e)=>new y({code:"CF_6018",message:`Translation warning for feature "${n}": ${e}`,suggestion:"Review the translated configuration and adjust if necessary",details:{feature:n,limitation:e},docsUrl:`${U}/CF_6018`}),featureUnsupported:(n,e)=>new y({code:"CF_6019",message:`Feature "${n}" is not supported when migrating from ${e} to Cloudflare`,suggestion:"Remove this feature or implement it using Cloudflare-specific alternatives",details:{feature:n,provider:e},docsUrl:`${U}/CF_6019`}),planLimitExceeded:(n,e,t)=>new y({code:"CF_6020",message:`${n} limit exceeded: ${t}/${e}`,suggestion:"Consider upgrading your Cloudflare plan or reducing resource usage",details:{resource:n,limit:e,current:t},docsUrl:`${U}/CF_6020`}),zoneSuspended:(n,e)=>new y({code:"CF_6021",message:`Zone is suspended: ${n}${e?` (${e})`:""}`,suggestion:"Contact Cloudflare support to resolve zone suspension issues",details:{zoneId:n,reason:e},docsUrl:`${U}/CF_6021`}),maintenanceMode:n=>new y({code:"CF_6022",message:`Cloudflare ${n} is currently in maintenance mode`,suggestion:"Wait for maintenance to complete and try again. Check Cloudflare status page for updates.",details:{service:n,statusPage:"https://www.cloudflarestatus.com/"},docsUrl:`${U}/CF_6022`}),quotaExceeded:(n,e)=>new y({code:"CF_6023",message:`${n} quota exceeded`,suggestion:e?`Wait until ${e} for quota reset, or upgrade your plan for higher limits`:"Wait for quota reset or upgrade your plan for higher limits",details:{quotaType:n,resetTime:e},docsUrl:`${U}/CF_6023`}),listsAPIUnavailable:(n,e)=>new y({code:"CF_6004",message:`Lists API unavailable: ${n}`,suggestion:`Falling back to ${e}. To enable Lists API, provide CLOUDFLARE_ACCOUNT_ID and ensure your API token has Account:Read permissions.`,details:{reason:n,fallbackAction:e,severity:"warning"},docsUrl:`${U}/setup#account-id`}),performanceWarning:(n,e,t)=>new y({code:"CF_6014",message:`Performance warning: ${n} with ${e} items`,suggestion:`${t} Consider providing CLOUDFLARE_ACCOUNT_ID to use Lists API for better performance.`,details:{operation:n,count:e,impact:t,severity:"warning"},docsUrl:`${U}/performance#large-ip-lists`})};var J=class{static DOCS_BASE_URL="https://docs.doorman.griffen.codes/cloudflare";static ERROR_PATTERNS={authentication:{keywords:["authentication","token","unauthorized","invalid token"],suggestion:"Verify your CLOUDFLARE_API_TOKEN is correct and has not expired. Create a new token if needed.",docsSection:"setup#api-token"},permissions:{keywords:["forbidden","access denied","insufficient permissions"],suggestion:"Ensure your API token has the required permissions: Zone:Edit and Account:Read (for Lists API).",docsSection:"setup#permissions"},rateLimit:{keywords:["rate limit","too many requests","quota exceeded"],suggestion:"Wait before retrying or use --retry flag for automatic exponential backoff. Consider upgrading your plan.",docsSection:"troubleshooting#rate-limits"},network:{keywords:["timeout","connection","network","dns","enotfound","econnrefused"],suggestion:"Check your internet connection and ensure api.cloudflare.com is accessible through your firewall.",docsSection:"troubleshooting#connectivity"}};static detectErrorContext(e){let t=e.toLowerCase(),i={pattern:null,confidence:0};for(let[r,s]of Object.entries(this.ERROR_PATTERNS)){let l=s.keywords.filter(c=>t.includes(c)).length/s.keywords.length;l>i.confidence&&(i={pattern:r,confidence:l})}return i}static handleApiError(e){if(e.code&&e.code!==0){let t=this.mapCloudflareErrorCode(e);if(t.code!=="PROV_5002")return t}switch(e.status){case 400:return this.handleBadRequestError(e);case 401:return this.handleUnauthorizedError(e);case 403:return this.handleForbiddenError(e);case 404:return this.handleNotFoundError(e);case 429:return this.handleRateLimitError(e);case 500:case 502:case 503:case 504:return this.handleServerError(e);default:return this.handleGenericError(e)}}static handleApiResponse(e,t){if(e.success)throw new Error("Cannot handle successful response as error");let r={status:0,code:e.errors[0]?.code||0,message:e.errors.map(s=>s.message).join(", "),endpoint:t,details:{errors:e.errors,messages:e.messages}};return this.mapCloudflareErrorCode(r)}static handleCredentialError(e,t){switch(e){case"token":return new y({code:"PROV_5004",message:"Invalid or missing Cloudflare API token",suggestion:"Check your CLOUDFLARE_API_TOKEN environment variable or config file. Ensure the token has Zone:Edit and Account:Read permissions.",details:{credentialType:"api_token"},docsUrl:`${this.DOCS_BASE_URL}/setup#api-token`});case"zone":return new y({code:"CF_6005",message:t?`Invalid zone ID: ${t}`:"Missing zone ID",suggestion:"Provide a valid CLOUDFLARE_ZONE_ID. You can find this in your Cloudflare dashboard under the domain overview.",details:{zoneId:t,credentialType:"zone_id"},docsUrl:`${this.DOCS_BASE_URL}/setup#zone-id`});case"account":return new y({code:"CF_6004",message:t?`Invalid account ID: ${t}`:"Missing account ID for Lists API",suggestion:"Provide CLOUDFLARE_ACCOUNT_ID to use Lists for IP blocking. This is optional but recommended for large IP lists.",details:{accountId:t,credentialType:"account_id"},docsUrl:`${this.DOCS_BASE_URL}/setup#account-id`});default:return new y({code:"PROV_5004",message:"Invalid Cloudflare credentials",suggestion:"Check your Cloudflare API credentials",docsUrl:`${this.DOCS_BASE_URL}/setup`})}}static handleValidationError(e,t,i){let s={rules:{code:"CF_6006",suggestion:"Check your rule configuration syntax and ensure all required fields are present",docsUrl:`${this.DOCS_BASE_URL}/configuration#rules`},expression:{code:"CF_6002",suggestion:"Use valid Cloudflare Wirefilter expression syntax. Check field names and operators.",docsUrl:`${this.DOCS_BASE_URL}/expressions`},rateLimit:{code:"CF_6008",suggestion:'Rate limit requests must be at least 1, and window format should be like "60s", "1h", "1d"',docsUrl:`${this.DOCS_BASE_URL}/rate-limiting`},redirect:{code:"CF_6011",suggestion:"Redirect rules must include a valid location URL or path",docsUrl:`${this.DOCS_BASE_URL}/redirects`},ip:{code:"CF_6013",suggestion:"Use valid IPv4 or IPv6 address with optional CIDR notation (e.g., 192.168.1.1 or 192.168.1.0/24)",docsUrl:`${this.DOCS_BASE_URL}/ip-blocking`}}[e]||{code:"CF_6006",suggestion:"Check your configuration syntax",docsUrl:`${this.DOCS_BASE_URL}/configuration`};return new y({code:s.code,message:`Configuration validation failed for ${e}: ${t}`,suggestion:s.suggestion,details:{field:e,issue:t,value:i},docsUrl:s.docsUrl})}static createEnhancedSuggestion(e,t,i){let r=[e];if(i?.pattern&&i.confidence>.5){let l=this.ERROR_PATTERNS[i.pattern];r.push(l.suggestion)}let a={"syncing rules":"Try running with --dry-run first to validate your configuration.","fetching configuration":"Verify your zone ID is correct and accessible.","validating credentials":"Double-check your API token permissions in the Cloudflare dashboard.","creating ruleset":"Ensure you have Zone:Edit permissions for the specified zone.","updating rules":"Check if you have reached your plan's rule limit."}[t];return a&&r.push(a),r.join(" ")}static formatTranslationWarning(e,t,i){let{TranslationWarningSystem:r}=(pe(),fe(me)),s;try{s=r.createWarning(e,void 0,void 0,i)}catch{s=r.createLossyConversionWarning(e,`${i} (from ${t})`,void 0,void 0)}return r.formatWarning(s)}static handleNetworkError(e,t){let i=this.detectErrorContext(e.message);if(e.message.includes("timeout")){let s=this.createEnhancedSuggestion("Check your internet connection and try again. Use --retry flag for automatic retries.",t,i);return new y({code:"NET_8000",message:`Request timeout during ${t}`,suggestion:s,details:{operation:t,originalError:e.message,retryRecommended:!0,timeoutDuration:this.extractTimeoutDuration(e.message)},cause:e,docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#timeouts`})}if(e.message.includes("ENOTFOUND")||e.message.includes("ECONNREFUSED")){let s=this.createEnhancedSuggestion("Check your internet connection and firewall settings. Ensure api.cloudflare.com is accessible.",t,i);return new y({code:"NET_8001",message:`Network connection failed during ${t}`,suggestion:s,details:{operation:t,originalError:e.message,dnsResolutionFailed:e.message.includes("ENOTFOUND"),connectionRefused:e.message.includes("ECONNREFUSED")},cause:e,docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#connectivity`})}let r=this.createEnhancedSuggestion("Check your internet connection and try again",t,i);return new y({code:"NET_8004",message:`Network error during ${t}: ${e.message}`,suggestion:r,details:{operation:t,errorContext:i},cause:e,docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}static extractTimeoutDuration(e){let t=e.match(/timeout.*?(\d+)(?:ms|s)/i);if(t&&t[1]){let i=parseInt(t[1],10),r=e.toLowerCase().includes("ms")?1:1e3;return i*r}}static handleGracefulDegradation(e,t,i,r){let a={lists_api:{code:"CF_6004",suggestion:"Provide CLOUDFLARE_ACCOUNT_ID to enable Lists API for better performance with large IP lists."},managed_rules:{code:"CF_6019",suggestion:"Use custom rules with equivalent conditions instead of managed rules."},advanced_expressions:{code:"CF_6018",suggestion:"Simplify expressions to use supported Cloudflare Wirefilter syntax."}}[e]||{code:"CF_6019",suggestion:"Consider alternative approaches or manual configuration."};return new y({code:a.code,message:`Feature degradation: ${e} - ${t}`,suggestion:`${a.suggestion} Fallback: ${i}${r?` Impact: ${r}`:""}`,details:{feature:e,reason:t,fallbackAction:i,impact:r,degradationType:"graceful"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#feature-degradation`})}static createUserFriendlyMessage(e,t,i){let r=`Failed to ${e}`,s=i?` (${Object.entries(i).map(([c,d])=>`${c}: ${d}`).join(", ")})`:"",l={401:"Please check your API token in the Cloudflare dashboard.",403:"Verify your API token has the required permissions.",404:"The requested resource may have been deleted or moved.",429:"You are being rate limited. Please wait before retrying.",500:"Cloudflare is experiencing issues. Check their status page."}[t.status]||"Please check your configuration and try again.";return`${r}${s}. ${l}`}static handleBadRequestError(e){return e.message.includes("expression")?new y({code:"CF_6002",message:`Invalid Cloudflare expression: ${e.message}`,suggestion:"Check your rule conditions and ensure they use valid Cloudflare Wirefilter syntax",details:e.details,docsUrl:`${this.DOCS_BASE_URL}/expressions`}):e.message.includes("rule")&&e.message.includes("condition")?new y({code:"CF_6007",message:"Rule validation failed: rules must have at least one condition",suggestion:"Add conditions to your rule or remove empty rules from your configuration",details:e.details,docsUrl:`${this.DOCS_BASE_URL}/rules#conditions`}):new y({code:"CF_6006",message:`Invalid request: ${e.message}`,suggestion:"Check your rule configuration for syntax errors and missing required fields",details:e.details,docsUrl:`${this.DOCS_BASE_URL}/configuration`})}static handleUnauthorizedError(e){return new y({code:"PROV_5000",message:"Authentication failed: Invalid API token",suggestion:"Check your CLOUDFLARE_API_TOKEN. Ensure it's valid and not expired. Create a new token if needed.",details:{endpoint:e.endpoint,hint:"API tokens can be created at https://dash.cloudflare.com/profile/api-tokens"},docsUrl:`${this.DOCS_BASE_URL}/setup#api-token`})}static handleForbiddenError(e){return e.endpoint?.includes("/accounts/")?new y({code:"CF_6004",message:"Access denied: Invalid account ID or insufficient permissions",suggestion:"Ensure your API token has Account:Read permissions and the account ID is correct",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#permissions`}):e.endpoint?.includes("/zones/")?new y({code:"CF_6005",message:"Access denied: Invalid zone ID or insufficient permissions",suggestion:"Ensure your API token has Zone:Edit permissions and the zone ID is correct",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#permissions`}):new y({code:"PROV_5000",message:"Access denied: Insufficient permissions",suggestion:"Ensure your API token has the required permissions: Zone:Edit and Account:Read (for Lists)",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#permissions`})}static handleNotFoundError(e){return e.endpoint?.includes("/rulesets/")?new y({code:"CF_6000",message:"Ruleset not found",suggestion:"The ruleset may have been deleted. Try running the command again to create a new one.",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#ruleset-not-found`}):e.endpoint?.includes("/lists/")?new y({code:"CF_6003",message:"List not found",suggestion:"The IP list may have been deleted. Try running the command again to create a new one.",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#list-not-found`}):e.endpoint?.includes("/zones/")?new y({code:"CF_6005",message:"Zone not found: Invalid zone ID",suggestion:"Check your CLOUDFLARE_ZONE_ID. Find the correct zone ID in your Cloudflare dashboard.",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#zone-id`}):new y({code:"PROV_5001",message:`Resource not found: ${e.message}`,suggestion:"Check that the resource exists and your credentials have access to it",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}static handleRateLimitError(e){let t=e.details?.retryAfter||60;return new y({code:"PROV_5003",message:"Rate limit exceeded",suggestion:`Wait ${t} seconds and try again, or use --retry flag for automatic retries with exponential backoff`,details:{retryAfter:t,endpoint:e.endpoint,hint:"Consider upgrading your Cloudflare plan for higher rate limits"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#rate-limits`})}static handleServerError(e){return new y({code:"PROV_5002",message:`Cloudflare server error (${e.status}): ${e.message}`,suggestion:"This is a temporary Cloudflare issue. Wait a few minutes and try again, or check Cloudflare status page.",details:{status:e.status,endpoint:e.endpoint,statusPage:"https://www.cloudflarestatus.com/"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#server-errors`})}static handleGenericError(e){return new y({code:"PROV_5002",message:`Cloudflare API error (${e.status}): ${e.message}`,suggestion:"Check the Cloudflare API documentation and your request parameters",details:{status:e.status,code:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}static mapCloudflareErrorCode(e){switch(e.code){case 1e4:return this.handleUnauthorizedError(e);case 10001:return this.handleForbiddenError(e);case 10013:return this.handleRateLimitError(e);case 81044:return new y({code:"CF_6000",message:"Ruleset not found",suggestion:"The ruleset may have been deleted. Try running the command again to create a new one.",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#ruleset-not-found`});case 81045:return new y({code:"CF_6001",message:"Rule limit exceeded for your Cloudflare plan",suggestion:"Consider consolidating rules, using Lists for IP blocking, or upgrading your Cloudflare plan",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#rule-limits`});case 81046:return new y({code:"CF_6002",message:`Invalid Cloudflare expression: ${e.message}`,suggestion:"Check your rule conditions and ensure they use valid Cloudflare Wirefilter syntax",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/expressions`});case 1001:return new y({code:"CF_6021",message:"Zone is suspended and cannot be modified",suggestion:"Contact Cloudflare support to resolve zone suspension issues",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#zone-suspended`});case 1014:case 1015:return new y({code:"CF_6020",message:`Plan limit exceeded: ${e.message}`,suggestion:"Consider upgrading your Cloudflare plan for higher limits",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#plan-limits`});case 1020:return new y({code:"CF_6022",message:"Cloudflare service is currently in maintenance mode",suggestion:"Wait for maintenance to complete and try again. Check Cloudflare status page.",details:{cloudflareCode:e.code,endpoint:e.endpoint,statusPage:"https://www.cloudflarestatus.com/"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#maintenance`});default:let t=e.message.toLowerCase();return t.includes("authentication")||t.includes("invalid token")?(e.status=401,this.handleUnauthorizedError(e)):t.includes("forbidden")||t.includes("access denied")?(e.status=403,this.handleForbiddenError(e)):t.includes("not found")?(e.status=404,this.handleNotFoundError(e)):t.includes("rate limit")?(e.status=429,this.handleRateLimitError(e)):new y({code:"PROV_5002",message:`Cloudflare API error: ${e.message}`,suggestion:"Check the Cloudflare API documentation and your request parameters",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}}};k();var Bo={defaultTTL:300*1e3,maxEntries:100,enableLogging:!0},nt=class{cache=new Map;options;stats={hits:0,misses:0,evictions:0};constructor(e={}){this.options={...Bo,...e}}get(e){let t=this.cache.get(e);if(!t){this.stats.misses++,this.options.enableLogging&&o.debug(`Cache miss: ${e}`);return}if(Date.now()>t.expiresAt){this.cache.delete(e),this.stats.misses++,this.options.enableLogging&&o.debug(`Cache expired: ${e}`);return}return t.hits++,this.stats.hits++,this.options.enableLogging&&o.debug(`Cache hit: ${e} (${t.hits} total hits)`),t.value}set(e,t,i){this.cache.size>=this.options.maxEntries&&!this.cache.has(e)&&this.evictLeastRecentlyUsed();let r=i??this.options.defaultTTL;this.cache.set(e,{value:t,expiresAt:Date.now()+r,createdAt:Date.now(),hits:0}),this.options.enableLogging&&o.debug(`Cache set: ${e} (TTL: ${r}ms)`)}invalidate(e){let t=this.cache.delete(e);return t&&this.options.enableLogging&&o.debug(`Cache invalidated: ${e}`),t}invalidateByPrefix(e){let t=0;for(let i of this.cache.keys())i.startsWith(e)&&(this.cache.delete(i),t++);return t>0&&this.options.enableLogging&&o.debug(`Cache invalidated ${t} entries with prefix: ${e}`),t}clear(){let e=this.cache.size;this.cache.clear(),this.options.enableLogging&&o.debug(`Cache cleared (${e} entries removed)`)}getStats(){let e=this.stats.hits+this.stats.misses;return{hits:this.stats.hits,misses:this.stats.misses,size:this.cache.size,evictions:this.stats.evictions,hitRate:e>0?this.stats.hits/e:0}}resetStats(){this.stats={hits:0,misses:0,evictions:0}}has(e){let t=this.cache.get(e);return t?Date.now()>t.expiresAt?(this.cache.delete(e),!1):!0:!1}evictLeastRecentlyUsed(){let e,t;for(let[i,r]of this.cache.entries()){if(Date.now()>r.expiresAt){this.cache.delete(i),this.stats.evictions++;return}(!t||r.hits<t.hits||r.hits===t.hits&&r.createdAt<t.createdAt)&&(e=i,t=r)}e&&(this.cache.delete(e),this.stats.evictions++,this.options.enableLogging&&o.debug(`Cache evicted LRU entry: ${e}`))}},Pe={zoneInfo:n=>`zone:${n}:info`,rulesets:n=>`zone:${n}:rulesets`,ruleset:(n,e)=>`zone:${n}:ruleset:${e}`,lists:n=>`account:${n}:lists`,listItems:(n,e)=>`account:${n}:list:${e}:items`,credentialValidation:n=>`cred:${n}`,configValidation:n=>`config:${n}`},ke={ZONE_INFO:600*1e3,RULESETS:120*1e3,RULESET:60*1e3,LISTS:300*1e3,LIST_ITEMS:120*1e3,CREDENTIALS:900*1e3,CONFIG_VALIDATION:300*1e3};k();var jo={maxConnections:6,idleTimeout:3e4,keepAlive:!0},zo={ttl:5e3,maxEntries:100},Se=class{stats={diffOperations:0,diffTotalDuration:0,earlyExits:0,deduplicatedRequests:0,pooledConnections:0,chunkedOperations:0};poolOptions;dedupOptions;inFlightRequests=new Map;hashCache=new Map;activeConnections=0;connectionQueue=[];constructor(e={},t={}){this.poolOptions={...jo,...e},this.dedupOptions={...zo,...t}}computeRuleHash(e){let t=this.canonicalizeRule(e),i=this.hashCache.get(t);if(i)return i;let r=this.simpleHash(t);return this.hashCache.set(t,r),r}diffRules(e,t){let i=performance.now();if(this.stats.diffOperations++,e.length===0&&t.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};if(e===t)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[],unchanged:e.length,duration:performance.now()-i};if(e.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[...t],unchanged:0,duration:performance.now()-i};if(t.length===0)return this.stats.earlyExits++,{toAdd:[...e],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};let r=new Map;for(let p of t){let g=p.id||p.name;r.set(g,{rule:p,hash:this.computeRuleHash(p)})}let s=new Map,a=[],l=[],c=0;for(let p of e){let g=p.id||p.name;s.set(g,!0);let h=r.get(g);h?this.computeRuleHash(p)!==h.hash?l.push(p):c++:a.push(p)}let d=[];for(let[p,g]of r)s.has(p)||d.push(g.rule);let u=performance.now()-i;return this.stats.diffTotalDuration+=u,o.debug(`Optimized diff: +${a.length} ~${l.length} -${d.length} =${c} (${u.toFixed(1)}ms)`),{toAdd:a,toUpdate:l,toDelete:d,unchanged:c,duration:u}}diffIPRules(e,t){let i=performance.now();if(e.length===0&&t.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};if(e.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[...t],unchanged:0,duration:performance.now()-i};if(t.length===0)return this.stats.earlyExits++,{toAdd:[...e],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};let r=new Map;for(let p of t)r.set(p.ip,p);let s=new Set,a=[],l=[],c=0;for(let p of e){s.add(p.ip);let g=r.get(p.ip);g?p.action!==g.action?l.push(p):c++:a.push(p)}let d=[];for(let[p,g]of r)s.has(p)||d.push(g);let u=performance.now()-i;return{toAdd:a,toUpdate:l,toDelete:d,unchanged:c,duration:u}}async acquireConnection(){if(this.activeConnections<this.poolOptions.maxConnections){this.activeConnections++,this.stats.pooledConnections++;return}return new Promise(e=>{this.connectionQueue.push(()=>{this.activeConnections++,this.stats.pooledConnections++,e()})})}releaseConnection(){this.activeConnections--,this.connectionQueue.length>0&&this.connectionQueue.shift()()}async withConnection(e){await this.acquireConnection();try{return await e()}finally{this.releaseConnection()}}async executePooled(e){if(e.length===0)return[];let t=new Array(e.length),i=[];for(let r=0;r<e.length;r++){let s=r,a=e[s],l=this.withConnection(async()=>{t[s]=await a()});i.push(l)}return await Promise.all(i),t}getConnectionHeaders(){return this.poolOptions.keepAlive?{Connection:"keep-alive","Keep-Alive":`timeout=${Math.floor(this.poolOptions.idleTimeout/1e3)}`}:{}}async deduplicateRequest(e,t){this.cleanupExpiredRequests();let i=this.inFlightRequests.get(e);if(i&&Date.now()-i.createdAt<this.dedupOptions.ttl)return this.stats.deduplicatedRequests++,o.debug(`Request deduplicated: ${e}`),i.promise;let r=t().finally(()=>{this.inFlightRequests.delete(e)});return this.inFlightRequests.set(e,{promise:r,createdAt:Date.now()}),r}static requestKey(e,t,i){let r=[e.toUpperCase(),t];return i&&r.push(i),r.join(":")}cleanupExpiredRequests(){let e=Date.now();for(let[t,i]of this.inFlightRequests)e-i.createdAt>this.dedupOptions.ttl&&this.inFlightRequests.delete(t);if(this.inFlightRequests.size>this.dedupOptions.maxEntries){let t=Array.from(this.inFlightRequests.entries());t.sort((r,s)=>r[1].createdAt-s[1].createdAt);let i=t.slice(0,t.length-this.dedupOptions.maxEntries);for(let[r]of i)this.inFlightRequests.delete(r)}}async processInChunks(e,t,i){if(e.length===0)return[];this.stats.chunkedOperations++;let r=[],s=Math.ceil(e.length/t);e.length>t&&o.debug(`Processing ${e.length} items in ${s} chunks of ${t}`);for(let a=0;a<e.length;a+=t){let l=e.slice(a,a+t),c=Math.floor(a/t),d=await i(l,c);r.push(...d)}return r}estimateMemoryUsage(e){if(e.length===0)return 0;let t=Math.min(10,e.length),i=0;for(let s=0;s<t;s++)i+=JSON.stringify(e[s]).length*2;let r=i/t;return Math.ceil(r*e.length)}getOptimalChunkSize(e,t=500){let r=Math.max(10,Math.floor(1048576/t)),s=Math.min(r,100);return e<=s?e:s}getStats(){return{...this.stats}}resetStats(){this.stats={diffOperations:0,diffTotalDuration:0,earlyExits:0,deduplicatedRequests:0,pooledConnections:0,chunkedOperations:0}}clearCaches(){this.hashCache.clear(),this.inFlightRequests.clear()}getActiveConnections(){return this.activeConnections}getPendingConnections(){return this.connectionQueue.length}getInFlightCount(){return this.inFlightRequests.size}canonicalizeRule(e){let t={action:e.action,conditions:e.conditions.map(i=>({field:i.field,key:i.key,negated:i.negated,operator:i.operator,value:i.value})).sort((i,r)=>`${i.field}:${i.operator}`.localeCompare(`${r.field}:${r.operator}`)),enabled:e.enabled,name:e.name};return e.conditionLogic&&(t.conditionLogic=e.conditionLogic),e.description&&(t.description=e.description),e.priority!==void 0&&(t.priority=e.priority),JSON.stringify(t)}simpleHash(e){let t=5381;for(let i=0;i<e.length;i++)t=(t<<5)+t+e.charCodeAt(i)|0;return t.toString(36)}};var Be=class extends De{apiToken;zoneId;accountId;cache;optimizer;constructor(e,t,i){super("https://api.cloudflare.com/client/v4","cloudflare"),this.apiToken=e,this.zoneId=t,this.accountId=i,this.cache=new nt({enableLogging:!0}),this.optimizer=new Se}getCacheStats(){return this.cache.getStats()}clearCache(){this.cache.clear()}invalidateRulesetCache(){this.cache.invalidateByPrefix(`zone:${this.zoneId}`)}invalidateListCache(){this.accountId&&this.cache.invalidateByPrefix(`account:${this.accountId}`)}getAuthHeaders(){return{Authorization:`Bearer ${this.apiToken}`,...this.optimizer.getConnectionHeaders()}}getOptimizer(){return this.optimizer}async listRulesets(){o.debug(`Fetching rulesets for zone ${this.zoneId}`);let e=Pe.rulesets(this.zoneId),t=this.cache.get(e);if(t)return t;let i=Se.requestKey("GET",`/zones/${this.zoneId}/rulesets`);return this.optimizer.deduplicateRequest(i,async()=>{try{let r=await this.get(`/zones/${this.zoneId}/rulesets`);if(!r.success)throw J.handleApiResponse(r,`/zones/${this.zoneId}/rulesets`);if(!Array.isArray(r.result))return o.warn("Malformed API response: expected array of rulesets, got:",typeof r.result),[];let s=r.result.filter(a=>!a||typeof a!="object"?(o.warn("Skipping malformed ruleset object:",a),!1):!a.id||!a.name?(o.warn("Skipping ruleset with missing required fields:",{id:a.id,name:a.name}),!1):!0);return s.length!==r.result.length&&o.warn(`Filtered out ${r.result.length-s.length} malformed rulesets`),this.cache.set(e,s,ke.RULESETS),s}catch(r){throw r instanceof Error&&!r.code?J.handleNetworkError(r,"listing rulesets"):r}})}async getRulesets(){return this.listRulesets()}async getRuleset(e){o.debug(`Fetching ruleset ${e}`);let t=Pe.ruleset(this.zoneId,e),i=this.cache.get(t);if(i)return i;let r=Se.requestKey("GET",`/zones/${this.zoneId}/rulesets/${e}`);return this.optimizer.deduplicateRequest(r,async()=>{try{let s=await this.get(`/zones/${this.zoneId}/rulesets/${e}`);if(!s.success)throw J.handleApiResponse(s,`/zones/${this.zoneId}/rulesets/${e}`);if(!s.result||typeof s.result!="object")throw J.handleValidationError("ruleset","Invalid ruleset data received from API",s.result);let a=s.result;if(!a.id||!a.name)throw J.handleValidationError("ruleset","Ruleset missing required fields (id, name)",a);Array.isArray(a.rules)||(o.warn(`Ruleset ${e} has invalid rules array, initializing as empty`),a.rules=[]);let l=a.rules.filter(c=>!c||typeof c!="object"?(o.warn(`Skipping malformed rule in ruleset ${e}:`,c),!1):!c.id||!c.expression||!c.action?(o.warn(`Skipping rule with missing required fields in ruleset ${e}:`,{id:c.id,expression:c.expression,action:c.action}),!1):!0);return l.length!==a.rules.length&&(o.warn(`Filtered out ${a.rules.length-l.length} malformed rules from ruleset ${e}`),a.rules=l),this.cache.set(t,a,ke.RULESET),a}catch(s){throw s instanceof Error&&!s.code?J.handleNetworkError(s,"fetching ruleset"):s}})}async createRuleset(e){o.debug(`Creating ruleset: ${e.name}`);let t=await this.post(`/zones/${this.zoneId}/rulesets`,e);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets`,t.errors[0]?.code||0,i)}return o.info(`Created ruleset: ${t.result.name} (${t.result.id})`),this.invalidateRulesetCache(),t.result}async updateRuleset(e,t){o.debug(`Updating ruleset ${e}`);let i=await this.put(`/zones/${this.zoneId}/rulesets/${e}`,t);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}`,i.errors[0]?.code||0,r)}return o.info(`Updated ruleset to version ${i.result.version}`),this.invalidateRulesetCache(),i.result}async deleteRuleset(e){o.debug(`Deleting ruleset ${e}`);let t=await this.delete(`/zones/${this.zoneId}/rulesets/${e}`);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}`,t.errors[0]?.code||0,i)}o.info(`Deleted ruleset ${e}`),this.invalidateRulesetCache()}async createRule(e,t){o.debug(`Adding rule to ruleset ${e}`);let i=await this.post(`/zones/${this.zoneId}/rulesets/${e}/rules`,t);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}/rules`,i.errors[0]?.code||0,r)}return o.info(`Added rule to ruleset ${e}`),this.invalidateRulesetCache(),i.result}async updateRule(e,t,i){o.debug(`Updating rule ${t} in ruleset ${e}`);let r=await this.patch(`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`,i);if(!r.success){let s=r.errors.map(a=>a.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`,r.errors[0]?.code||0,s)}return o.info(`Updated rule ${t}`),this.invalidateRulesetCache(),r.result}async deleteRule(e,t){o.debug(`Deleting rule ${t} from ruleset ${e}`);let i=await this.delete(`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`,i.errors[0]?.code||0,r)}return o.info(`Deleted rule ${t}`),this.invalidateRulesetCache(),i.result}async getOrCreateFirewallRuleset(){o.debug("Looking for existing custom firewall ruleset");let t=(await this.listRulesets()).find(i=>i.kind==="custom"&&i.phase==="http_request_firewall_custom");return t?(o.debug(`Found existing ruleset: ${t.id}`),t):(o.info("No existing custom firewall ruleset found, creating new one"),this.createRuleset({name:"Doorman Custom Firewall Rules",kind:"custom",phase:"http_request_firewall_custom",description:"Custom firewall rules managed by Doorman",rules:[]}))}async verifyCredentials(){let e=this.apiToken.slice(-8),t=Pe.credentialValidation(e),i=this.cache.get(t);if(i!==void 0)return o.debug("Using cached credential validation result"),i;try{return o.debug("Verifying Cloudflare credentials"),await this.listRulesets(),o.info("Cloudflare credentials verified successfully"),this.cache.set(t,!0,ke.CREDENTIALS),!0}catch(r){if(o.error(`Cloudflare credential verification failed: ${r}`),r instanceof Error){let s=r.message.toLowerCase();if(s.includes("unauthorized")||s.includes("invalid token"))throw J.handleCredentialError("token");if(s.includes("forbidden")||s.includes("access denied"))throw J.handleCredentialError("zone",this.zoneId);if(s.includes("not found"))throw J.handleCredentialError("zone",this.zoneId)}return!1}}async getZoneInfo(){let e=Pe.zoneInfo(this.zoneId),t=this.cache.get(e);if(t)return t;let i=await this.get(`/zones/${this.zoneId}`);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}`,i.errors[0]?.code||0,r)}return this.cache.set(e,i.result,ke.ZONE_INFO),i.result}async listLists(){if(o.debug("Fetching Cloudflare Lists"),!this.accountId)return o.warn("Account ID not provided, Lists API requires account-level access"),[];let e=Pe.lists(this.accountId),t=this.cache.get(e);if(t)return t;let i=await this.get(`/accounts/${this.accountId}/rules/lists`);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists`,i.errors[0]?.code||0,r)}return this.cache.set(e,i.result,ke.LISTS),i.result}async getList(e){if(o.debug(`Fetching List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=await this.get(`/accounts/${this.accountId}/rules/lists/${e}`);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}`,t.errors[0]?.code||0,i)}return t.result}async createList(e){if(o.debug(`Creating List: ${e.name}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=await this.post(`/accounts/${this.accountId}/rules/lists`,e);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists`,t.errors[0]?.code||0,i)}return o.info(`Created List: ${t.result.name} (${t.result.id})`),this.invalidateListCache(),t.result}async updateList(e,t){if(o.debug(`Updating List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let i=await this.put(`/accounts/${this.accountId}/rules/lists/${e}`,t);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}`,i.errors[0]?.code||0,r)}return o.info(`Updated List ${e}`),this.invalidateListCache(),i.result}async deleteList(e){if(o.debug(`Deleting List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=await this.delete(`/accounts/${this.accountId}/rules/lists/${e}`);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}`,t.errors[0]?.code||0,i)}o.info(`Deleted List ${e}`),this.invalidateListCache()}async getListItems(e){if(o.debug(`Fetching items from List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=Pe.listItems(this.accountId,e),i=this.cache.get(t);if(i)return i;let r=Se.requestKey("GET",`/accounts/${this.accountId}/rules/lists/${e}/items`);return this.optimizer.deduplicateRequest(r,async()=>{let s=await this.get(`/accounts/${this.accountId}/rules/lists/${e}/items`);if(!s.success){let a=s.errors.map(l=>l.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}/items`,s.errors[0]?.code||0,a)}return this.cache.set(t,s.result,ke.LIST_ITEMS),s.result})}async addListItems(e,t){if(o.debug(`Adding ${t.items.length} items to List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let i=await this.post(`/accounts/${this.accountId}/rules/lists/${e}/items`,t.items);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}/items`,i.errors[0]?.code||0,r)}return o.info(`Added ${t.items.length} items to List ${e}`),this.invalidateListCache(),i.result}async removeListItems(e,t){if(o.debug(`Removing ${t.items.length} items from List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let i=await this.delete(`/accounts/${this.accountId}/rules/lists/${e}/items`,{body:JSON.stringify(t)});if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}/items`,i.errors[0]?.code||0,r)}o.info(`Removed ${t.items.length} items from List ${e}`),this.invalidateListCache()}async getOrCreateIPBlocklist(){if(o.debug("Looking for existing Doorman IP blocklist"),!this.accountId)throw o.warn("Account ID not provided, cannot use Lists for IP blocking"),J.handleCredentialError("account");try{let t=(await this.listLists()).find(i=>i.name==="Doorman IP Blocklist"&&i.kind==="ip");return t?(o.debug(`Found existing IP blocklist: ${t.id}`),t):(o.info("No existing IP blocklist found, creating new one"),this.createList({name:"Doorman IP Blocklist",description:"IP addresses blocked by Doorman",kind:"ip"}))}catch(e){throw e instanceof Error&&!e.code?J.handleNetworkError(e,"managing IP blocklist"):e}}};k();var Ge=class extends xe{name="cloudflare";client;optimizer;useListsForIPs;constructor(e,t,i){super(),this.client=new Be(e,t,i),this.optimizer=this.client.getOptimizer(),this.useListsForIPs=!!i,this.useListsForIPs?o.debug("Lists API enabled for IP blocking (account ID provided)"):o.debug("Lists API disabled - will use individual IP rules (no account ID)")}async fetchConfig(e){o.info("Fetching configuration from Cloudflare");try{let t=await this.client.getOrCreateFirewallRuleset(),i=[],r=[],s=[],a=[],l=50,c=t.rules.length;c>100&&o.info(`Processing ${c} rules in batches of ${l} to optimize memory usage`);for(let d=0;d<c;d+=l){let u=t.rules.slice(d,d+l);o.debug(`Processing rules batch ${Math.floor(d/l)+1}/${Math.ceil(c/l)} (${u.length} rules)`);for(let p of u)try{if(!p||typeof p!="object"){a.push(`Skipped malformed rule at index ${d}: not an object`);continue}if(!p.id||!p.expression||!p.action){a.push(`Skipped rule with missing required fields: ${JSON.stringify({id:p.id,expression:!!p.expression,action:p.action})}`);continue}if(this.isListBasedIPRule(p))continue;if(this.isIPBlockingRule(p)){let g=this.cloudflareRuleToIPRule(p);r.push(g)}else{let g=M.cloudflareToUnified(p);i.push(g.result),g.warnings.length>0&&g.warnings.forEach(h=>{let{TranslationWarningSystem:b}=(pe(),fe(me)),f=b.formatWarning(h);s.push(f)})}}catch(g){let h=`Failed to process rule ${p?.id||"unknown"}: ${g instanceof Error?g.message:String(g)}`;o.warn(h),a.push(h)}c>200&&d+l<c&&await new Promise(p=>setTimeout(p,10))}if(a.length>0&&(o.warn(`Encountered ${a.length} rule processing errors:`),a.slice(0,5).forEach(d=>o.warn(` - ${d}`)),a.length>5&&o.warn(` ... and ${a.length-5} more errors`)),this.useListsForIPs)try{let d=await this.client.getOrCreateIPBlocklist(),u=await this.client.getListItems(d.id);for(let p of u)p.ip&&r.push({id:p.id,ip:p.ip,notes:p.comment,action:"deny"});o.debug(`Fetched ${u.length} IPs from List`)}catch(d){this.handleListsAPIFallback(d,"fetching IPs from List")}else r.length>10&&o.warn(`\u26A0\uFE0F Large IP list detected (${r.length} IPs) without Lists API. Consider providing CLOUDFLARE_ACCOUNT_ID for better performance.`);return s.length>0&&(o.warn("Translation warnings detected:"),s.forEach(d=>o.warn(d)),s.length>3&&o.warn(`
41
- \u{1F4CA} Summary: ${s.length} translation warnings detected. Review each warning above for specific guidance.`)),{version:"2.0",provider:"cloudflare",providers:{cloudflare:{zoneId:this.client.zoneId,accountId:this.client.accountId}},rules:i,ips:r,metadata:{version:parseInt(t.version,10),updatedAt:t.last_updated}}}catch(t){throw t instanceof Error&&!("code"in t)?J.handleNetworkError(t,"fetching configuration"):t}}async syncRules(e,t){o.info(`Syncing ${e.rules.length} rules to Cloudflare${t?.dryRun?" (dry run)":""}`);let{OperationSafety:i}=(Pi(),fe(Ti)),r=await i.performDryRunValidation(e,"sync rules",async b=>await this.getChanges(b));if(!r.valid)throw new Error(`Dry-run validation failed: ${r.issues.join(", ")}`);if(t?.dryRun)return{success:!0,rulesAdded:r.changes.rulesToAdd.length,rulesUpdated:r.changes.rulesToUpdate.length,rulesDeleted:r.changes.rulesToDelete.length,ipsAdded:r.changes.ipsToAdd?.length||0,ipsUpdated:r.changes.ipsToUpdate?.length||0,ipsDeleted:r.changes.ipsToDelete?.length||0,warnings:r.warnings};let s=this.assessOperationRisk(r.changes,e);if(!await i.confirmDestructiveOperation({operation:"sync rules",target:`Cloudflare zone ${this.client.zoneId}`,changes:r.changes,riskLevel:s,skipConfirmation:t?.force||!1,dryRun:t?.dryRun||!1}))throw new Error("Operation cancelled by user");let l=await this.client.getOrCreateFirewallRuleset(),c=[];for(let b of e.rules){let f=M.unifiedToCloudflare(b);c.push(f.result),f.warnings.length>0&&f.warnings.forEach(T=>{let{TranslationWarningSystem:j}=(pe(),fe(me)),v=j.formatWarning(T);o.warn(v)})}let d=0,u=0,p=0;if(this.useListsForIPs&&e.ips&&e.ips.length>0)try{let b=await this.client.getOrCreateIPBlocklist(),f=await this.client.getListItems(b.id),T=new Set(f.map(I=>I.ip)),j=new Set(e.ips.map(I=>I.ip)),v=e.ips.filter(I=>!T.has(I.ip));v.length>0&&(await this.client.addListItems(b.id,{items:v.map(I=>({ip:I.ip,comment:I.notes||I.hostname||"Blocked by Doorman"}))}),d=v.length,o.info(`Added ${d} IPs to List`));let m=f.filter(I=>I.ip&&!j.has(I.ip));m.length>0&&(await this.client.removeListItems(b.id,{items:m.map(I=>({id:I.id}))}),p=m.length,o.info(`Removed ${p} IPs from List`));let P=`ip.src in $${b.name.replace(/\s+/g,"_").toLowerCase()}`;!l.rules.some(I=>I.expression.includes("in $"))&&e.ips.length>0&&c.push({id:"rule_doorman_ip_list",action:"block",expression:P,description:"Block IPs in Doorman IP Blocklist",enabled:!0})}catch(b){this.handleListsAPIFallback(b,"syncing IPs via List"),o.info(`\u{1F504} Falling back to individual IP rules for ${e.ips.length} IPs`);for(let f of e.ips){let T=M.unifiedIPToCloudflare(f);c.push(T)}d=e.ips.length,e.ips.length>50&&o.warn(`\u26A0\uFE0F Performance warning: Using ${e.ips.length} individual IP rules instead of Lists. This may impact rule processing speed and count against your rule limit.`)}else if(e.ips&&e.ips.length>0){o.info(`\u{1F4DD} Using individual IP rules for ${e.ips.length} IPs (Lists API not available)`),e.ips.length>20&&o.warn(`\u26A0\uFE0F Large IP list detected (${e.ips.length} IPs) without Lists API. Consider providing CLOUDFLARE_ACCOUNT_ID for better performance and rule efficiency.`);for(let b of e.ips){let f=M.unifiedIPToCloudflare(b);c.push(f)}d=e.ips.length}let g=await this.client.updateRuleset(l.id,{rules:c}),h={success:!0,rulesAdded:e.rules.length,rulesUpdated:0,rulesDeleted:0,ipsAdded:d,ipsUpdated:u,ipsDeleted:p,version:parseInt(g.version,10),warnings:r.warnings?.length?r.warnings:void 0};return this.logSyncStats(h),h}async getChanges(e){let t=await this.fetchConfig(),i=this.optimizer.diffRules(e.rules,t.rules),r={toAdd:[],toUpdate:[],toDelete:[]};return e.ips&&t.ips&&(r=this.optimizer.diffIPRules(e.ips,t.ips)),{rulesToAdd:i.toAdd,rulesToUpdate:i.toUpdate,rulesToDelete:i.toDelete,ipsToAdd:r.toAdd,ipsToUpdate:r.toUpdate,ipsToDelete:r.toDelete,hasChanges:i.toAdd.length>0||i.toUpdate.length>0||i.toDelete.length>0||r.toAdd.length>0||r.toUpdate.length>0||r.toDelete.length>0}}getSupportedFeatures(){return{supportsCustomRules:!0,supportsIPBlocking:!0,supportsRateLimiting:!0,supportsManagedRules:!0,supportsGeoBlocking:!0,supportsRedirect:!0,supportsChallenge:!0,maxRules:125}}validateConfig(e){let t=super.validateConfig(e),i=[...t.errors],r=[...t.warnings];if(e&&e.rules){let s=this.getSupportedFeatures();if(s.maxRules&&e.rules.length>s.maxRules){let a=q.ruleLimitExceeded(e.rules.length,s.maxRules);i.push({path:"rules",message:a.message,code:a.code})}if(e.rules.forEach((a,l)=>{try{if(!a.conditions||a.conditions.length===0){let c=q.ruleNoConditions(a.name||`Rule ${l+1}`);i.push({path:`rules[${l}]`,message:c.message,code:c.code})}if(a.action.type==="rate_limit"&&a.action.rateLimit){let c=a.action.rateLimit;if(c.requests<1){let d=q.invalidRateLimit(a.name||`Rule ${l+1}`,"requests must be at least 1");i.push({path:`rules[${l}].action.rateLimit.requests`,message:d.message,code:d.code})}if(!c.window.match(/^\d+[smhd]$/)){let d=q.invalidWindowFormat(c.window);i.push({path:`rules[${l}].action.rateLimit.window`,message:d.message,code:d.code})}if(c.characteristics&&c.characteristics.length===0){let d=q.emptyCharacteristics(a.name||`Rule ${l+1}`);r.push({path:`rules[${l}].action.rateLimit.characteristics`,message:d.message,code:d.code})}if(c.mitigationTimeout!==void 0&&c.mitigationTimeout<60){let d=q.shortMitigationTimeout(c.mitigationTimeout);r.push({path:`rules[${l}].action.rateLimit.mitigationTimeout`,message:d.message,code:d.code})}}if(a.action.type==="redirect"&&a.action.redirect){let c=a.action.redirect;if(c.location)try{new URL(c.location)}catch{if(!c.location.startsWith("/")){let d=q.invalidRedirectUrl(c.location);i.push({path:`rules[${l}].action.redirect.location`,message:d.message,code:d.code})}}else{let d=q.redirectNoLocation(a.name||`Rule ${l+1}`);i.push({path:`rules[${l}].action.redirect.location`,message:d.message,code:d.code})}}}catch(c){i.push({path:`rules[${l}]`,message:`Validation error: ${c instanceof Error?c.message:String(c)}`,code:"CLOUDFLARE_VALIDATION_ERROR"})}}),e.ips&&(e.ips.forEach((a,l)=>{try{if(!/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/(?:[0-9]|[1-2][0-9]|3[0-2]))?$/.test(a.ip)){let d=q.invalidIP(a.ip);i.push({path:`ips[${l}].ip`,message:d.message,code:d.code})}}catch(c){i.push({path:`ips[${l}]`,message:`IP validation error: ${c instanceof Error?c.message:String(c)}`,code:"CLOUDFLARE_IP_VALIDATION_ERROR"})}}),!this.useListsForIPs&&e.ips.length>50)){let a=q.largeIPList(e.ips.length);r.push({path:"ips",message:a.message,code:a.code})}}return{valid:i.length===0,errors:i,warnings:r}}getHealthScore(e){let t=super.getHealthScore(e),i=[...t.issues],r=this.getSupportedFeatures();return r.maxRules&&e.rules.length>r.maxRules*.8&&i.push({severity:"warning",category:"limits",message:`Approaching Cloudflare rule limit (${e.rules.length}/${r.maxRules})`,suggestion:"Consider consolidating rules or upgrading plan"}),{...t,issues:i}}async verifyCredentials(){return this.client.verifyCredentials()}getCacheStats(){return this.client.getCacheStats()}getOptimizerStats(){return this.optimizer.getStats()}clearCache(){this.client.clearCache(),this.optimizer.clearCaches()}isListBasedIPRule(e){return/ip\.src in \$/.test(e.expression.trim())}isIPBlockingRule(e){return/^ip\.src eq [\d.]+$/.test(e.expression.trim())}cloudflareRuleToIPRule(e){let i=e.expression.match(/ip\.src eq ([\d.]+)/)?.[1]||"",s=e.description?.match(/\(([^)]+)\)/)?.[1];return{id:e.id,ip:i,hostname:s,notes:e.description,action:e.action==="allow"?"allow":"deny"}}handleListsAPIFallback(e,t){if(e instanceof Error){let i=e.message.toLowerCase();i.includes("account")?(o.warn("\u{1F6AB} Lists API unavailable: Account ID not provided or invalid. Falling back to individual IP rules."),o.info(`\u{1F4A1} To enable Lists API for better performance with large IP lists:
40
+ `)}static isDoormanError(e){return e instanceof n}static from(e,t,i){return n.isDoormanError(e)?e:e instanceof Error?new n({code:t,message:i||e.message,cause:e}):new n({code:t,message:i||String(e)})}};var U="https://docs.doorman.griffen.codes/errors";var K={authFailed:(n,e)=>new y({code:"PROV_5000",message:`Authentication failed for ${n}`,suggestion:"Check that your API credentials are valid and have the correct permissions",details:{provider:n},cause:e,docsUrl:`${U}/PROV_5000`}),apiError:(n,e,t,i)=>new y({code:"PROV_5002",message:`${n} API error: ${t} ${i}`,suggestion:"Check the provider status page and your credentials",details:{provider:n,endpoint:e,statusCode:t,responseMessage:i},docsUrl:`${U}/PROV_5002`}),rateLimit:(n,e)=>new y({code:"PROV_5003",message:`Rate limit exceeded for ${n}`,suggestion:e?`Wait ${e} seconds and try again, or use --retry flag`:"Wait a few minutes and try again, or use --retry flag",details:{provider:n,retryAfter:e},docsUrl:`${U}/PROV_5003`}),timeout:(n,e)=>new y({code:"PROV_5006",message:`Request timeout for ${n} ${e}`,suggestion:"Check your internet connection and try again",details:{provider:n,operation:e},docsUrl:`${U}/PROV_5006`}),invalidCredentials:(n,e)=>new y({code:"PROV_5004",message:`Missing or invalid credentials for ${n}`,suggestion:`Please provide: ${e.join(", ")}`,details:{provider:n,missing:e},docsUrl:`${U}/PROV_5004`})},q={accountIdRequired:n=>new y({code:"CF_6004",message:`Account ID required for ${n}`,suggestion:"Provide CLOUDFLARE_ACCOUNT_ID in your environment or configuration to use Lists API",details:{operation:n},docsUrl:`${U}/CF_6004`}),zoneIdRequired:n=>new y({code:"CF_6005",message:`Zone ID required for ${n}`,suggestion:"Provide CLOUDFLARE_ZONE_ID in your environment or configuration",details:{operation:n},docsUrl:`${U}/CF_6005`}),invalidCredentials:(n,e)=>new y({code:"CF_6016",message:`Invalid Cloudflare ${n}`,suggestion:"Check your Cloudflare API credentials and ensure they have the correct permissions",details:{credentialType:n,...e},docsUrl:`${U}/CF_6016`}),insufficientPermissions:(n,e)=>new y({code:"CF_6017",message:`Insufficient permissions for ${n}`,suggestion:`Ensure your API token has the following permissions: ${e.join(", ")}`,details:{operation:n,requiredPermissions:e},docsUrl:`${U}/CF_6017`}),ruleLimitExceeded:(n,e)=>new y({code:"CF_6001",message:`Rule count (${n}) exceeds Cloudflare limit (${e})`,suggestion:"Consider consolidating rules, using Lists for IP blocking, or upgrading your Cloudflare plan",details:{count:n,limit:e},docsUrl:`${U}/CF_6001`}),invalidExpression:(n,e)=>new y({code:"CF_6002",message:`Invalid Cloudflare expression: ${e}`,suggestion:"Check Cloudflare Wirefilter expression syntax and field names",details:{expression:n,reason:e},docsUrl:`${U}/CF_6002`}),ruleNoConditions:n=>new y({code:"CF_6007",message:`Rule "${n}" has no conditions`,suggestion:"Add at least one condition to the rule or remove the empty rule",details:{ruleName:n},docsUrl:`${U}/CF_6007`}),invalidRateLimit:(n,e)=>new y({code:"CF_6008",message:`Invalid rate limit configuration for rule "${n}": ${e}`,suggestion:'Rate limit requests must be at least 1, and window format should be like "60s", "1h", "1d"',details:{ruleName:n,issue:e},docsUrl:`${U}/CF_6008`}),invalidWindowFormat:n=>new y({code:"CF_6009",message:`Invalid rate limit window format: ${n}`,suggestion:'Use format like "60s", "5m", "1h", or "1d"',details:{window:n},docsUrl:`${U}/CF_6009`}),shortMitigationTimeout:n=>new y({code:"CF_6010",message:`Mitigation timeout (${n}s) may be too short`,suggestion:"Consider using at least 60 seconds for effective rate limiting",details:{timeout:n},docsUrl:`${U}/CF_6010`}),redirectNoLocation:n=>new y({code:"CF_6011",message:`Redirect rule "${n}" is missing location URL`,suggestion:"Add a redirect.location property with a valid URL or path",details:{ruleName:n},docsUrl:`${U}/CF_6011`}),invalidRedirectUrl:n=>new y({code:"CF_6012",message:`Invalid redirect URL: ${n}`,suggestion:"Use a valid absolute URL (https://...) or relative path (/...)",details:{url:n},docsUrl:`${U}/CF_6012`}),invalidIP:n=>new y({code:"CF_6013",message:`Invalid IP address format: ${n}`,suggestion:"Use valid IPv4 or IPv6 address with optional CIDR notation (e.g., 192.168.1.1 or 192.168.1.0/24)",details:{ip:n},docsUrl:`${U}/CF_6013`}),largeIPList:n=>new y({code:"CF_6014",message:`Large IP list detected (${n} IPs)`,suggestion:"Consider providing CLOUDFLARE_ACCOUNT_ID to use Lists API for better performance with large IP lists",details:{count:n},docsUrl:`${U}/CF_6014`}),rulesetNotFound:n=>new y({code:"CF_6000",message:n?`Ruleset not found: ${n}`:"Ruleset not found",suggestion:"The ruleset may have been deleted. Try running the command again to create a new one.",details:{rulesetId:n},docsUrl:`${U}/CF_6000`}),listNotFound:n=>new y({code:"CF_6003",message:n?`List not found: ${n}`:"List not found",suggestion:"The IP list may have been deleted. Try running the command again to create a new one.",details:{listId:n},docsUrl:`${U}/CF_6003`}),emptyCharacteristics:n=>new y({code:"CF_6015",message:`Rate limit rule "${n}" has empty characteristics`,suggestion:'Add characteristics like ["ip.src"] or remove the characteristics array to use default',details:{ruleName:n},docsUrl:`${U}/CF_6015`}),translationWarning:(n,e)=>new y({code:"CF_6018",message:`Translation warning for feature "${n}": ${e}`,suggestion:"Review the translated configuration and adjust if necessary",details:{feature:n,limitation:e},docsUrl:`${U}/CF_6018`}),featureUnsupported:(n,e)=>new y({code:"CF_6019",message:`Feature "${n}" is not supported when migrating from ${e} to Cloudflare`,suggestion:"Remove this feature or implement it using Cloudflare-specific alternatives",details:{feature:n,provider:e},docsUrl:`${U}/CF_6019`}),planLimitExceeded:(n,e,t)=>new y({code:"CF_6020",message:`${n} limit exceeded: ${t}/${e}`,suggestion:"Consider upgrading your Cloudflare plan or reducing resource usage",details:{resource:n,limit:e,current:t},docsUrl:`${U}/CF_6020`}),zoneSuspended:(n,e)=>new y({code:"CF_6021",message:`Zone is suspended: ${n}${e?` (${e})`:""}`,suggestion:"Contact Cloudflare support to resolve zone suspension issues",details:{zoneId:n,reason:e},docsUrl:`${U}/CF_6021`}),maintenanceMode:n=>new y({code:"CF_6022",message:`Cloudflare ${n} is currently in maintenance mode`,suggestion:"Wait for maintenance to complete and try again. Check Cloudflare status page for updates.",details:{service:n,statusPage:"https://www.cloudflarestatus.com/"},docsUrl:`${U}/CF_6022`}),quotaExceeded:(n,e)=>new y({code:"CF_6023",message:`${n} quota exceeded`,suggestion:e?`Wait until ${e} for quota reset, or upgrade your plan for higher limits`:"Wait for quota reset or upgrade your plan for higher limits",details:{quotaType:n,resetTime:e},docsUrl:`${U}/CF_6023`}),listsAPIUnavailable:(n,e)=>new y({code:"CF_6004",message:`Lists API unavailable: ${n}`,suggestion:`Falling back to ${e}. To enable Lists API, provide CLOUDFLARE_ACCOUNT_ID and ensure your API token has Account:Read permissions.`,details:{reason:n,fallbackAction:e,severity:"warning"},docsUrl:`${U}/setup#account-id`}),performanceWarning:(n,e,t)=>new y({code:"CF_6014",message:`Performance warning: ${n} with ${e} items`,suggestion:`${t} Consider providing CLOUDFLARE_ACCOUNT_ID to use Lists API for better performance.`,details:{operation:n,count:e,impact:t,severity:"warning"},docsUrl:`${U}/performance#large-ip-lists`})};var J=class{static DOCS_BASE_URL="https://docs.doorman.griffen.codes/cloudflare";static ERROR_PATTERNS={authentication:{keywords:["authentication","token","unauthorized","invalid token"],suggestion:"Verify your CLOUDFLARE_API_TOKEN is correct and has not expired. Create a new token if needed.",docsSection:"setup#api-token"},permissions:{keywords:["forbidden","access denied","insufficient permissions"],suggestion:"Ensure your API token has the required permissions: Zone:Edit and Account:Read (for Lists API).",docsSection:"setup#permissions"},rateLimit:{keywords:["rate limit","too many requests","quota exceeded"],suggestion:"Wait before retrying or use --retry flag for automatic exponential backoff. Consider upgrading your plan.",docsSection:"troubleshooting#rate-limits"},network:{keywords:["timeout","connection","network","dns","enotfound","econnrefused"],suggestion:"Check your internet connection and ensure api.cloudflare.com is accessible through your firewall.",docsSection:"troubleshooting#connectivity"}};static detectErrorContext(e){let t=e.toLowerCase(),i={pattern:null,confidence:0};for(let[r,s]of Object.entries(this.ERROR_PATTERNS)){let l=s.keywords.filter(c=>t.includes(c)).length/s.keywords.length;l>i.confidence&&(i={pattern:r,confidence:l})}return i}static handleApiError(e){if(e.code&&e.code!==0){let t=this.mapCloudflareErrorCode(e);if(t.code!=="PROV_5002")return t}switch(e.status){case 400:return this.handleBadRequestError(e);case 401:return this.handleUnauthorizedError(e);case 403:return this.handleForbiddenError(e);case 404:return this.handleNotFoundError(e);case 429:return this.handleRateLimitError(e);case 500:case 502:case 503:case 504:return this.handleServerError(e);default:return this.handleGenericError(e)}}static handleApiResponse(e,t){if(e.success)throw new Error("Cannot handle successful response as error");let r={status:0,code:e.errors[0]?.code||0,message:e.errors.map(s=>s.message).join(", "),endpoint:t,details:{errors:e.errors,messages:e.messages}};return this.mapCloudflareErrorCode(r)}static handleCredentialError(e,t){switch(e){case"token":return new y({code:"PROV_5004",message:"Invalid or missing Cloudflare API token",suggestion:"Check your CLOUDFLARE_API_TOKEN environment variable or config file. Ensure the token has Zone:Edit and Account:Read permissions.",details:{credentialType:"api_token"},docsUrl:`${this.DOCS_BASE_URL}/setup#api-token`});case"zone":return new y({code:"CF_6005",message:t?`Invalid zone ID: ${t}`:"Missing zone ID",suggestion:"Provide a valid CLOUDFLARE_ZONE_ID. You can find this in your Cloudflare dashboard under the domain overview.",details:{zoneId:t,credentialType:"zone_id"},docsUrl:`${this.DOCS_BASE_URL}/setup#zone-id`});case"account":return new y({code:"CF_6004",message:t?`Invalid account ID: ${t}`:"Missing account ID for Lists API",suggestion:"Provide CLOUDFLARE_ACCOUNT_ID to use Lists for IP blocking. This is optional but recommended for large IP lists.",details:{accountId:t,credentialType:"account_id"},docsUrl:`${this.DOCS_BASE_URL}/setup#account-id`});default:return new y({code:"PROV_5004",message:"Invalid Cloudflare credentials",suggestion:"Check your Cloudflare API credentials",docsUrl:`${this.DOCS_BASE_URL}/setup`})}}static handleValidationError(e,t,i){let s={rules:{code:"CF_6006",suggestion:"Check your rule configuration syntax and ensure all required fields are present",docsUrl:`${this.DOCS_BASE_URL}/configuration#rules`},expression:{code:"CF_6002",suggestion:"Use valid Cloudflare Wirefilter expression syntax. Check field names and operators.",docsUrl:`${this.DOCS_BASE_URL}/expressions`},rateLimit:{code:"CF_6008",suggestion:'Rate limit requests must be at least 1, and window format should be like "60s", "1h", "1d"',docsUrl:`${this.DOCS_BASE_URL}/rate-limiting`},redirect:{code:"CF_6011",suggestion:"Redirect rules must include a valid location URL or path",docsUrl:`${this.DOCS_BASE_URL}/redirects`},ip:{code:"CF_6013",suggestion:"Use valid IPv4 or IPv6 address with optional CIDR notation (e.g., 192.168.1.1 or 192.168.1.0/24)",docsUrl:`${this.DOCS_BASE_URL}/ip-blocking`}}[e]||{code:"CF_6006",suggestion:"Check your configuration syntax",docsUrl:`${this.DOCS_BASE_URL}/configuration`};return new y({code:s.code,message:`Configuration validation failed for ${e}: ${t}`,suggestion:s.suggestion,details:{field:e,issue:t,value:i},docsUrl:s.docsUrl})}static createEnhancedSuggestion(e,t,i){let r=[e];if(i?.pattern&&i.confidence>.5){let l=this.ERROR_PATTERNS[i.pattern];r.push(l.suggestion)}let a={"syncing rules":"Try running with --dry-run first to validate your configuration.","fetching configuration":"Verify your zone ID is correct and accessible.","validating credentials":"Double-check your API token permissions in the Cloudflare dashboard.","creating ruleset":"Ensure you have Zone:Edit permissions for the specified zone.","updating rules":"Check if you have reached your plan's rule limit."}[t];return a&&r.push(a),r.join(" ")}static formatTranslationWarning(e,t,i){let{TranslationWarningSystem:r}=(pe(),fe(me)),s;try{s=r.createWarning(e,void 0,void 0,i)}catch{s=r.createLossyConversionWarning(e,`${i} (from ${t})`,void 0,void 0)}return r.formatWarning(s)}static handleNetworkError(e,t){let i=this.detectErrorContext(e.message);if(e.message.includes("timeout")){let s=this.createEnhancedSuggestion("Check your internet connection and try again. Use --retry flag for automatic retries.",t,i);return new y({code:"NET_8000",message:`Request timeout during ${t}`,suggestion:s,details:{operation:t,originalError:e.message,retryRecommended:!0,timeoutDuration:this.extractTimeoutDuration(e.message)},cause:e,docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#timeouts`})}if(e.message.includes("ENOTFOUND")||e.message.includes("ECONNREFUSED")){let s=this.createEnhancedSuggestion("Check your internet connection and firewall settings. Ensure api.cloudflare.com is accessible.",t,i);return new y({code:"NET_8001",message:`Network connection failed during ${t}`,suggestion:s,details:{operation:t,originalError:e.message,dnsResolutionFailed:e.message.includes("ENOTFOUND"),connectionRefused:e.message.includes("ECONNREFUSED")},cause:e,docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#connectivity`})}let r=this.createEnhancedSuggestion("Check your internet connection and try again",t,i);return new y({code:"NET_8004",message:`Network error during ${t}: ${e.message}`,suggestion:r,details:{operation:t,errorContext:i},cause:e,docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}static extractTimeoutDuration(e){let t=e.match(/timeout.*?(\d+)(?:ms|s)/i);if(t&&t[1]){let i=parseInt(t[1],10),r=e.toLowerCase().includes("ms")?1:1e3;return i*r}}static handleGracefulDegradation(e,t,i,r){let a={lists_api:{code:"CF_6004",suggestion:"Provide CLOUDFLARE_ACCOUNT_ID to enable Lists API for better performance with large IP lists."},managed_rules:{code:"CF_6019",suggestion:"Use custom rules with equivalent conditions instead of managed rules."},advanced_expressions:{code:"CF_6018",suggestion:"Simplify expressions to use supported Cloudflare Wirefilter syntax."}}[e]||{code:"CF_6019",suggestion:"Consider alternative approaches or manual configuration."};return new y({code:a.code,message:`Feature degradation: ${e} - ${t}`,suggestion:`${a.suggestion} Fallback: ${i}${r?` Impact: ${r}`:""}`,details:{feature:e,reason:t,fallbackAction:i,impact:r,degradationType:"graceful"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#feature-degradation`})}static createUserFriendlyMessage(e,t,i){let r=`Failed to ${e}`,s=i?` (${Object.entries(i).map(([c,d])=>`${c}: ${d}`).join(", ")})`:"",l={401:"Please check your API token in the Cloudflare dashboard.",403:"Verify your API token has the required permissions.",404:"The requested resource may have been deleted or moved.",429:"You are being rate limited. Please wait before retrying.",500:"Cloudflare is experiencing issues. Check their status page."}[t.status]||"Please check your configuration and try again.";return`${r}${s}. ${l}`}static handleBadRequestError(e){return e.message.includes("expression")?new y({code:"CF_6002",message:`Invalid Cloudflare expression: ${e.message}`,suggestion:"Check your rule conditions and ensure they use valid Cloudflare Wirefilter syntax",details:e.details,docsUrl:`${this.DOCS_BASE_URL}/expressions`}):e.message.includes("rule")&&e.message.includes("condition")?new y({code:"CF_6007",message:"Rule validation failed: rules must have at least one condition",suggestion:"Add conditions to your rule or remove empty rules from your configuration",details:e.details,docsUrl:`${this.DOCS_BASE_URL}/rules#conditions`}):new y({code:"CF_6006",message:`Invalid request: ${e.message}`,suggestion:"Check your rule configuration for syntax errors and missing required fields",details:e.details,docsUrl:`${this.DOCS_BASE_URL}/configuration`})}static handleUnauthorizedError(e){return new y({code:"PROV_5000",message:"Authentication failed: Invalid API token",suggestion:"Check your CLOUDFLARE_API_TOKEN. Ensure it's valid and not expired. Create a new token if needed.",details:{endpoint:e.endpoint,hint:"API tokens can be created at https://dash.cloudflare.com/profile/api-tokens"},docsUrl:`${this.DOCS_BASE_URL}/setup#api-token`})}static handleForbiddenError(e){return e.endpoint?.includes("/accounts/")?new y({code:"CF_6004",message:"Access denied: Invalid account ID or insufficient permissions",suggestion:"Ensure your API token has Account:Read permissions and the account ID is correct",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#permissions`}):e.endpoint?.includes("/zones/")?new y({code:"CF_6005",message:"Access denied: Invalid zone ID or insufficient permissions",suggestion:"Ensure your API token has Zone:Edit permissions and the zone ID is correct",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#permissions`}):new y({code:"PROV_5000",message:"Access denied: Insufficient permissions",suggestion:"Ensure your API token has the required permissions: Zone:Edit and Account:Read (for Lists)",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#permissions`})}static handleNotFoundError(e){return e.endpoint?.includes("/rulesets/")?new y({code:"CF_6000",message:"Ruleset not found",suggestion:"The ruleset may have been deleted. Try running the command again to create a new one.",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#ruleset-not-found`}):e.endpoint?.includes("/lists/")?new y({code:"CF_6003",message:"List not found",suggestion:"The IP list may have been deleted. Try running the command again to create a new one.",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#list-not-found`}):e.endpoint?.includes("/zones/")?new y({code:"CF_6005",message:"Zone not found: Invalid zone ID",suggestion:"Check your CLOUDFLARE_ZONE_ID. Find the correct zone ID in your Cloudflare dashboard.",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/setup#zone-id`}):new y({code:"PROV_5001",message:`Resource not found: ${e.message}`,suggestion:"Check that the resource exists and your credentials have access to it",details:{endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}static handleRateLimitError(e){let t=e.details?.retryAfter||60;return new y({code:"PROV_5003",message:"Rate limit exceeded",suggestion:`Wait ${t} seconds and try again, or use --retry flag for automatic retries with exponential backoff`,details:{retryAfter:t,endpoint:e.endpoint,hint:"Consider upgrading your Cloudflare plan for higher rate limits"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#rate-limits`})}static handleServerError(e){return new y({code:"PROV_5002",message:`Cloudflare server error (${e.status}): ${e.message}`,suggestion:"This is a temporary Cloudflare issue. Wait a few minutes and try again, or check Cloudflare status page.",details:{status:e.status,endpoint:e.endpoint,statusPage:"https://www.cloudflarestatus.com/"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#server-errors`})}static handleGenericError(e){return new y({code:"PROV_5002",message:`Cloudflare API error (${e.status}): ${e.message}`,suggestion:"Check the Cloudflare API documentation and your request parameters",details:{status:e.status,code:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}static mapCloudflareErrorCode(e){switch(e.code){case 1e4:return this.handleUnauthorizedError(e);case 10001:return this.handleForbiddenError(e);case 10013:return this.handleRateLimitError(e);case 81044:return new y({code:"CF_6000",message:"Ruleset not found",suggestion:"The ruleset may have been deleted. Try running the command again to create a new one.",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#ruleset-not-found`});case 81045:return new y({code:"CF_6001",message:"Rule limit exceeded for your Cloudflare plan",suggestion:"Consider consolidating rules, using Lists for IP blocking, or upgrading your Cloudflare plan",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#rule-limits`});case 81046:return new y({code:"CF_6002",message:`Invalid Cloudflare expression: ${e.message}`,suggestion:"Check your rule conditions and ensure they use valid Cloudflare Wirefilter syntax",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/expressions`});case 1001:return new y({code:"CF_6021",message:"Zone is suspended and cannot be modified",suggestion:"Contact Cloudflare support to resolve zone suspension issues",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#zone-suspended`});case 1014:case 1015:return new y({code:"CF_6020",message:`Plan limit exceeded: ${e.message}`,suggestion:"Consider upgrading your Cloudflare plan for higher limits",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#plan-limits`});case 1020:return new y({code:"CF_6022",message:"Cloudflare service is currently in maintenance mode",suggestion:"Wait for maintenance to complete and try again. Check Cloudflare status page.",details:{cloudflareCode:e.code,endpoint:e.endpoint,statusPage:"https://www.cloudflarestatus.com/"},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting#maintenance`});default:let t=e.message.toLowerCase();return t.includes("authentication")||t.includes("invalid token")?(e.status=401,this.handleUnauthorizedError(e)):t.includes("forbidden")||t.includes("access denied")?(e.status=403,this.handleForbiddenError(e)):t.includes("not found")?(e.status=404,this.handleNotFoundError(e)):t.includes("rate limit")?(e.status=429,this.handleRateLimitError(e)):new y({code:"PROV_5002",message:`Cloudflare API error: ${e.message}`,suggestion:"Check the Cloudflare API documentation and your request parameters",details:{cloudflareCode:e.code,endpoint:e.endpoint},docsUrl:`${this.DOCS_BASE_URL}/troubleshooting`})}}};k();var Bo={defaultTTL:300*1e3,maxEntries:100,enableLogging:!0},rt=class{cache=new Map;options;stats={hits:0,misses:0,evictions:0};constructor(e={}){this.options={...Bo,...e}}get(e){let t=this.cache.get(e);if(!t){this.stats.misses++,this.options.enableLogging&&o.debug(`Cache miss: ${e}`);return}if(Date.now()>t.expiresAt){this.cache.delete(e),this.stats.misses++,this.options.enableLogging&&o.debug(`Cache expired: ${e}`);return}return t.hits++,this.stats.hits++,this.options.enableLogging&&o.debug(`Cache hit: ${e} (${t.hits} total hits)`),t.value}set(e,t,i){this.cache.size>=this.options.maxEntries&&!this.cache.has(e)&&this.evictLeastRecentlyUsed();let r=i??this.options.defaultTTL;this.cache.set(e,{value:t,expiresAt:Date.now()+r,createdAt:Date.now(),hits:0}),this.options.enableLogging&&o.debug(`Cache set: ${e} (TTL: ${r}ms)`)}invalidate(e){let t=this.cache.delete(e);return t&&this.options.enableLogging&&o.debug(`Cache invalidated: ${e}`),t}invalidateByPrefix(e){let t=0;for(let i of this.cache.keys())i.startsWith(e)&&(this.cache.delete(i),t++);return t>0&&this.options.enableLogging&&o.debug(`Cache invalidated ${t} entries with prefix: ${e}`),t}clear(){let e=this.cache.size;this.cache.clear(),this.options.enableLogging&&o.debug(`Cache cleared (${e} entries removed)`)}getStats(){let e=this.stats.hits+this.stats.misses;return{hits:this.stats.hits,misses:this.stats.misses,size:this.cache.size,evictions:this.stats.evictions,hitRate:e>0?this.stats.hits/e:0}}resetStats(){this.stats={hits:0,misses:0,evictions:0}}has(e){let t=this.cache.get(e);return t?Date.now()>t.expiresAt?(this.cache.delete(e),!1):!0:!1}evictLeastRecentlyUsed(){let e,t;for(let[i,r]of this.cache.entries()){if(Date.now()>r.expiresAt){this.cache.delete(i),this.stats.evictions++;return}(!t||r.hits<t.hits||r.hits===t.hits&&r.createdAt<t.createdAt)&&(e=i,t=r)}e&&(this.cache.delete(e),this.stats.evictions++,this.options.enableLogging&&o.debug(`Cache evicted LRU entry: ${e}`))}},Pe={zoneInfo:n=>`zone:${n}:info`,rulesets:n=>`zone:${n}:rulesets`,ruleset:(n,e)=>`zone:${n}:ruleset:${e}`,lists:n=>`account:${n}:lists`,listItems:(n,e)=>`account:${n}:list:${e}:items`,credentialValidation:n=>`cred:${n}`,configValidation:n=>`config:${n}`},ke={ZONE_INFO:600*1e3,RULESETS:120*1e3,RULESET:60*1e3,LISTS:300*1e3,LIST_ITEMS:120*1e3,CREDENTIALS:900*1e3,CONFIG_VALIDATION:300*1e3};k();var jo={maxConnections:6,idleTimeout:3e4,keepAlive:!0},zo={ttl:5e3,maxEntries:100},Se=class{stats={diffOperations:0,diffTotalDuration:0,earlyExits:0,deduplicatedRequests:0,pooledConnections:0,chunkedOperations:0};poolOptions;dedupOptions;inFlightRequests=new Map;hashCache=new Map;activeConnections=0;connectionQueue=[];constructor(e={},t={}){this.poolOptions={...jo,...e},this.dedupOptions={...zo,...t}}computeRuleHash(e){let t=this.canonicalizeRule(e),i=this.hashCache.get(t);if(i)return i;let r=this.simpleHash(t);return this.hashCache.set(t,r),r}diffRules(e,t){let i=performance.now();if(this.stats.diffOperations++,e.length===0&&t.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};if(e===t)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[],unchanged:e.length,duration:performance.now()-i};if(e.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[...t],unchanged:0,duration:performance.now()-i};if(t.length===0)return this.stats.earlyExits++,{toAdd:[...e],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};let r=new Map;for(let p of t){let f=p.id||p.name;r.set(f,{rule:p,hash:this.computeRuleHash(p)})}let s=new Map,a=[],l=[],c=0;for(let p of e){let f=p.id||p.name;s.set(f,!0);let h=r.get(f);h?this.computeRuleHash(p)!==h.hash?l.push(p):c++:a.push(p)}let d=[];for(let[p,f]of r)s.has(p)||d.push(f.rule);let u=performance.now()-i;return this.stats.diffTotalDuration+=u,o.debug(`Optimized diff: +${a.length} ~${l.length} -${d.length} =${c} (${u.toFixed(1)}ms)`),{toAdd:a,toUpdate:l,toDelete:d,unchanged:c,duration:u}}diffIPRules(e,t){let i=performance.now();if(e.length===0&&t.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};if(e.length===0)return this.stats.earlyExits++,{toAdd:[],toUpdate:[],toDelete:[...t],unchanged:0,duration:performance.now()-i};if(t.length===0)return this.stats.earlyExits++,{toAdd:[...e],toUpdate:[],toDelete:[],unchanged:0,duration:performance.now()-i};let r=new Map;for(let p of t)r.set(p.ip,p);let s=new Set,a=[],l=[],c=0;for(let p of e){s.add(p.ip);let f=r.get(p.ip);f?p.action!==f.action?l.push(p):c++:a.push(p)}let d=[];for(let[p,f]of r)s.has(p)||d.push(f);let u=performance.now()-i;return{toAdd:a,toUpdate:l,toDelete:d,unchanged:c,duration:u}}async acquireConnection(){if(this.activeConnections<this.poolOptions.maxConnections){this.activeConnections++,this.stats.pooledConnections++;return}return new Promise(e=>{this.connectionQueue.push(()=>{this.activeConnections++,this.stats.pooledConnections++,e()})})}releaseConnection(){this.activeConnections--,this.connectionQueue.length>0&&this.connectionQueue.shift()()}async withConnection(e){await this.acquireConnection();try{return await e()}finally{this.releaseConnection()}}async executePooled(e){if(e.length===0)return[];let t=new Array(e.length),i=[];for(let r=0;r<e.length;r++){let s=r,a=e[s],l=this.withConnection(async()=>{t[s]=await a()});i.push(l)}return await Promise.all(i),t}getConnectionHeaders(){return this.poolOptions.keepAlive?{Connection:"keep-alive","Keep-Alive":`timeout=${Math.floor(this.poolOptions.idleTimeout/1e3)}`}:{}}async deduplicateRequest(e,t){this.cleanupExpiredRequests();let i=this.inFlightRequests.get(e);if(i&&Date.now()-i.createdAt<this.dedupOptions.ttl)return this.stats.deduplicatedRequests++,o.debug(`Request deduplicated: ${e}`),i.promise;let r=t().finally(()=>{this.inFlightRequests.delete(e)});return this.inFlightRequests.set(e,{promise:r,createdAt:Date.now()}),r}static requestKey(e,t,i){let r=[e.toUpperCase(),t];return i&&r.push(i),r.join(":")}cleanupExpiredRequests(){let e=Date.now();for(let[t,i]of this.inFlightRequests)e-i.createdAt>this.dedupOptions.ttl&&this.inFlightRequests.delete(t);if(this.inFlightRequests.size>this.dedupOptions.maxEntries){let t=Array.from(this.inFlightRequests.entries());t.sort((r,s)=>r[1].createdAt-s[1].createdAt);let i=t.slice(0,t.length-this.dedupOptions.maxEntries);for(let[r]of i)this.inFlightRequests.delete(r)}}async processInChunks(e,t,i){if(e.length===0)return[];this.stats.chunkedOperations++;let r=[],s=Math.ceil(e.length/t);e.length>t&&o.debug(`Processing ${e.length} items in ${s} chunks of ${t}`);for(let a=0;a<e.length;a+=t){let l=e.slice(a,a+t),c=Math.floor(a/t),d=await i(l,c);r.push(...d)}return r}estimateMemoryUsage(e){if(e.length===0)return 0;let t=Math.min(10,e.length),i=0;for(let s=0;s<t;s++)i+=JSON.stringify(e[s]).length*2;let r=i/t;return Math.ceil(r*e.length)}getOptimalChunkSize(e,t=500){let r=Math.max(10,Math.floor(1048576/t)),s=Math.min(r,100);return e<=s?e:s}getStats(){return{...this.stats}}resetStats(){this.stats={diffOperations:0,diffTotalDuration:0,earlyExits:0,deduplicatedRequests:0,pooledConnections:0,chunkedOperations:0}}clearCaches(){this.hashCache.clear(),this.inFlightRequests.clear()}getActiveConnections(){return this.activeConnections}getPendingConnections(){return this.connectionQueue.length}getInFlightCount(){return this.inFlightRequests.size}canonicalizeRule(e){let t={action:e.action,conditions:e.conditions.map(i=>({field:i.field,key:i.key,negated:i.negated,operator:i.operator,value:i.value})).sort((i,r)=>`${i.field}:${i.operator}`.localeCompare(`${r.field}:${r.operator}`)),enabled:e.enabled,name:e.name};return e.conditionLogic&&(t.conditionLogic=e.conditionLogic),e.description&&(t.description=e.description),e.priority!==void 0&&(t.priority=e.priority),JSON.stringify(t)}simpleHash(e){let t=5381;for(let i=0;i<e.length;i++)t=(t<<5)+t+e.charCodeAt(i)|0;return t.toString(36)}};var Be=class extends De{apiToken;zoneId;accountId;cache;optimizer;constructor(e,t,i){super("https://api.cloudflare.com/client/v4","cloudflare"),this.apiToken=e,this.zoneId=t,this.accountId=i,this.cache=new rt({enableLogging:!0}),this.optimizer=new Se}getCacheStats(){return this.cache.getStats()}clearCache(){this.cache.clear()}invalidateRulesetCache(){this.cache.invalidateByPrefix(`zone:${this.zoneId}`)}invalidateListCache(){this.accountId&&this.cache.invalidateByPrefix(`account:${this.accountId}`)}getAuthHeaders(){return{Authorization:`Bearer ${this.apiToken}`,...this.optimizer.getConnectionHeaders()}}getOptimizer(){return this.optimizer}async listRulesets(){o.debug(`Fetching rulesets for zone ${this.zoneId}`);let e=Pe.rulesets(this.zoneId),t=this.cache.get(e);if(t)return t;let i=Se.requestKey("GET",`/zones/${this.zoneId}/rulesets`);return this.optimizer.deduplicateRequest(i,async()=>{try{let r=await this.get(`/zones/${this.zoneId}/rulesets`);if(!r.success)throw J.handleApiResponse(r,`/zones/${this.zoneId}/rulesets`);if(!Array.isArray(r.result))return o.warn("Malformed API response: expected array of rulesets, got:",typeof r.result),[];let s=r.result.filter(a=>!a||typeof a!="object"?(o.warn("Skipping malformed ruleset object:",a),!1):!a.id||!a.name?(o.warn("Skipping ruleset with missing required fields:",{id:a.id,name:a.name}),!1):!0);return s.length!==r.result.length&&o.warn(`Filtered out ${r.result.length-s.length} malformed rulesets`),this.cache.set(e,s,ke.RULESETS),s}catch(r){throw r instanceof Error&&!r.code?J.handleNetworkError(r,"listing rulesets"):r}})}async getRulesets(){return this.listRulesets()}async getRuleset(e){o.debug(`Fetching ruleset ${e}`);let t=Pe.ruleset(this.zoneId,e),i=this.cache.get(t);if(i)return i;let r=Se.requestKey("GET",`/zones/${this.zoneId}/rulesets/${e}`);return this.optimizer.deduplicateRequest(r,async()=>{try{let s=await this.get(`/zones/${this.zoneId}/rulesets/${e}`);if(!s.success)throw J.handleApiResponse(s,`/zones/${this.zoneId}/rulesets/${e}`);if(!s.result||typeof s.result!="object")throw J.handleValidationError("ruleset","Invalid ruleset data received from API",s.result);let a=s.result;if(!a.id||!a.name)throw J.handleValidationError("ruleset","Ruleset missing required fields (id, name)",a);Array.isArray(a.rules)||(o.warn(`Ruleset ${e} has invalid rules array, initializing as empty`),a.rules=[]);let l=a.rules.filter(c=>!c||typeof c!="object"?(o.warn(`Skipping malformed rule in ruleset ${e}:`,c),!1):!c.id||!c.expression||!c.action?(o.warn(`Skipping rule with missing required fields in ruleset ${e}:`,{id:c.id,expression:c.expression,action:c.action}),!1):!0);return l.length!==a.rules.length&&(o.warn(`Filtered out ${a.rules.length-l.length} malformed rules from ruleset ${e}`),a.rules=l),this.cache.set(t,a,ke.RULESET),a}catch(s){throw s instanceof Error&&!s.code?J.handleNetworkError(s,"fetching ruleset"):s}})}async createRuleset(e){o.debug(`Creating ruleset: ${e.name}`);let t=await this.post(`/zones/${this.zoneId}/rulesets`,e);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets`,t.errors[0]?.code||0,i)}return o.info(`Created ruleset: ${t.result.name} (${t.result.id})`),this.invalidateRulesetCache(),t.result}async updateRuleset(e,t){o.debug(`Updating ruleset ${e}`);let i=await this.put(`/zones/${this.zoneId}/rulesets/${e}`,t);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}`,i.errors[0]?.code||0,r)}return o.info(`Updated ruleset to version ${i.result.version}`),this.invalidateRulesetCache(),i.result}async deleteRuleset(e){o.debug(`Deleting ruleset ${e}`);let t=await this.delete(`/zones/${this.zoneId}/rulesets/${e}`);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}`,t.errors[0]?.code||0,i)}o.info(`Deleted ruleset ${e}`),this.invalidateRulesetCache()}async createRule(e,t){o.debug(`Adding rule to ruleset ${e}`);let i=await this.post(`/zones/${this.zoneId}/rulesets/${e}/rules`,t);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}/rules`,i.errors[0]?.code||0,r)}return o.info(`Added rule to ruleset ${e}`),this.invalidateRulesetCache(),i.result}async updateRule(e,t,i){o.debug(`Updating rule ${t} in ruleset ${e}`);let r=await this.patch(`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`,i);if(!r.success){let s=r.errors.map(a=>a.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`,r.errors[0]?.code||0,s)}return o.info(`Updated rule ${t}`),this.invalidateRulesetCache(),r.result}async deleteRule(e,t){o.debug(`Deleting rule ${t} from ruleset ${e}`);let i=await this.delete(`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}/rulesets/${e}/rules/${t}`,i.errors[0]?.code||0,r)}return o.info(`Deleted rule ${t}`),this.invalidateRulesetCache(),i.result}async getOrCreateFirewallRuleset(){o.debug("Looking for existing custom firewall ruleset");let t=(await this.listRulesets()).find(i=>i.kind==="custom"&&i.phase==="http_request_firewall_custom");return t?(o.debug(`Found existing ruleset: ${t.id}`),t):(o.info("No existing custom firewall ruleset found, creating new one"),this.createRuleset({name:"Doorman Custom Firewall Rules",kind:"custom",phase:"http_request_firewall_custom",description:"Custom firewall rules managed by Doorman",rules:[]}))}async verifyCredentials(){let e=this.apiToken.slice(-8),t=Pe.credentialValidation(e),i=this.cache.get(t);if(i!==void 0)return o.debug("Using cached credential validation result"),i;try{return o.debug("Verifying Cloudflare credentials"),await this.listRulesets(),o.info("Cloudflare credentials verified successfully"),this.cache.set(t,!0,ke.CREDENTIALS),!0}catch(r){if(o.error(`Cloudflare credential verification failed: ${r}`),r instanceof Error){let s=r.message.toLowerCase();if(s.includes("unauthorized")||s.includes("invalid token"))throw J.handleCredentialError("token");if(s.includes("forbidden")||s.includes("access denied"))throw J.handleCredentialError("zone",this.zoneId);if(s.includes("not found"))throw J.handleCredentialError("zone",this.zoneId)}return!1}}async getZoneInfo(){let e=Pe.zoneInfo(this.zoneId),t=this.cache.get(e);if(t)return t;let i=await this.get(`/zones/${this.zoneId}`);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/zones/${this.zoneId}`,i.errors[0]?.code||0,r)}return this.cache.set(e,i.result,ke.ZONE_INFO),i.result}async listLists(){if(o.debug("Fetching Cloudflare Lists"),!this.accountId)return o.warn("Account ID not provided, Lists API requires account-level access"),[];let e=Pe.lists(this.accountId),t=this.cache.get(e);if(t)return t;let i=await this.get(`/accounts/${this.accountId}/rules/lists`);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists`,i.errors[0]?.code||0,r)}return this.cache.set(e,i.result,ke.LISTS),i.result}async getList(e){if(o.debug(`Fetching List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=await this.get(`/accounts/${this.accountId}/rules/lists/${e}`);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}`,t.errors[0]?.code||0,i)}return t.result}async createList(e){if(o.debug(`Creating List: ${e.name}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=await this.post(`/accounts/${this.accountId}/rules/lists`,e);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists`,t.errors[0]?.code||0,i)}return o.info(`Created List: ${t.result.name} (${t.result.id})`),this.invalidateListCache(),t.result}async updateList(e,t){if(o.debug(`Updating List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let i=await this.put(`/accounts/${this.accountId}/rules/lists/${e}`,t);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}`,i.errors[0]?.code||0,r)}return o.info(`Updated List ${e}`),this.invalidateListCache(),i.result}async deleteList(e){if(o.debug(`Deleting List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=await this.delete(`/accounts/${this.accountId}/rules/lists/${e}`);if(!t.success){let i=t.errors.map(r=>r.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}`,t.errors[0]?.code||0,i)}o.info(`Deleted List ${e}`),this.invalidateListCache()}async getListItems(e){if(o.debug(`Fetching items from List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let t=Pe.listItems(this.accountId,e),i=this.cache.get(t);if(i)return i;let r=Se.requestKey("GET",`/accounts/${this.accountId}/rules/lists/${e}/items`);return this.optimizer.deduplicateRequest(r,async()=>{let s=await this.get(`/accounts/${this.accountId}/rules/lists/${e}/items`);if(!s.success){let a=s.errors.map(l=>l.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}/items`,s.errors[0]?.code||0,a)}return this.cache.set(t,s.result,ke.LIST_ITEMS),s.result})}async addListItems(e,t){if(o.debug(`Adding ${t.items.length} items to List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let i=await this.post(`/accounts/${this.accountId}/rules/lists/${e}/items`,t.items);if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}/items`,i.errors[0]?.code||0,r)}return o.info(`Added ${t.items.length} items to List ${e}`),this.invalidateListCache(),i.result}async removeListItems(e,t){if(o.debug(`Removing ${t.items.length} items from List ${e}`),!this.accountId)throw q.accountIdRequired("Lists API");let i=await this.delete(`/accounts/${this.accountId}/rules/lists/${e}/items`,{body:JSON.stringify(t)});if(!i.success){let r=i.errors.map(s=>s.message).join(", ");throw K.apiError("cloudflare",`/accounts/${this.accountId}/rules/lists/${e}/items`,i.errors[0]?.code||0,r)}o.info(`Removed ${t.items.length} items from List ${e}`),this.invalidateListCache()}async getOrCreateIPBlocklist(){if(o.debug("Looking for existing Doorman IP blocklist"),!this.accountId)throw o.warn("Account ID not provided, cannot use Lists for IP blocking"),J.handleCredentialError("account");try{let t=(await this.listLists()).find(i=>i.name==="Doorman IP Blocklist"&&i.kind==="ip");return t?(o.debug(`Found existing IP blocklist: ${t.id}`),t):(o.info("No existing IP blocklist found, creating new one"),this.createList({name:"Doorman IP Blocklist",description:"IP addresses blocked by Doorman",kind:"ip"}))}catch(e){throw e instanceof Error&&!e.code?J.handleNetworkError(e,"managing IP blocklist"):e}}};k();var Ge=class extends xe{name="cloudflare";client;optimizer;useListsForIPs;constructor(e,t,i){super(),this.client=new Be(e,t,i),this.optimizer=this.client.getOptimizer(),this.useListsForIPs=!!i,this.useListsForIPs?o.debug("Lists API enabled for IP blocking (account ID provided)"):o.debug("Lists API disabled - will use individual IP rules (no account ID)")}async fetchConfig(e){o.info("Fetching configuration from Cloudflare");try{let t=await this.client.getOrCreateFirewallRuleset(),i=[],r=[],s=[],a=[],l=50,c=t.rules.length;c>100&&o.info(`Processing ${c} rules in batches of ${l} to optimize memory usage`);for(let d=0;d<c;d+=l){let u=t.rules.slice(d,d+l);o.debug(`Processing rules batch ${Math.floor(d/l)+1}/${Math.ceil(c/l)} (${u.length} rules)`);for(let p of u)try{if(!p||typeof p!="object"){a.push(`Skipped malformed rule at index ${d}: not an object`);continue}if(!p.id||!p.expression||!p.action){a.push(`Skipped rule with missing required fields: ${JSON.stringify({id:p.id,expression:!!p.expression,action:p.action})}`);continue}if(this.isListBasedIPRule(p))continue;if(this.isIPBlockingRule(p)){let f=this.cloudflareRuleToIPRule(p);r.push(f)}else{let f=M.cloudflareToUnified(p);i.push(f.result),f.warnings.length>0&&f.warnings.forEach(h=>{let{TranslationWarningSystem:I}=(pe(),fe(me)),g=I.formatWarning(h);s.push(g)})}}catch(f){let h=`Failed to process rule ${p?.id||"unknown"}: ${f instanceof Error?f.message:String(f)}`;o.warn(h),a.push(h)}c>200&&d+l<c&&await new Promise(p=>setTimeout(p,10))}if(a.length>0&&(o.warn(`Encountered ${a.length} rule processing errors:`),a.slice(0,5).forEach(d=>o.warn(` - ${d}`)),a.length>5&&o.warn(` ... and ${a.length-5} more errors`)),this.useListsForIPs)try{let d=await this.client.getOrCreateIPBlocklist(),u=await this.client.getListItems(d.id);for(let p of u)p.ip&&r.push({id:p.id,ip:p.ip,notes:p.comment,action:"deny"});o.debug(`Fetched ${u.length} IPs from List`)}catch(d){this.handleListsAPIFallback(d,"fetching IPs from List")}else r.length>10&&o.warn(`\u26A0\uFE0F Large IP list detected (${r.length} IPs) without Lists API. Consider providing CLOUDFLARE_ACCOUNT_ID for better performance.`);return s.length>0&&(o.warn("Translation warnings detected:"),s.forEach(d=>o.warn(d)),s.length>3&&o.warn(`
41
+ \u{1F4CA} Summary: ${s.length} translation warnings detected. Review each warning above for specific guidance.`)),{version:"2.0",provider:"cloudflare",providers:{cloudflare:{zoneId:this.client.zoneId,accountId:this.client.accountId}},rules:i,ips:r,metadata:{version:parseInt(t.version,10),updatedAt:t.last_updated}}}catch(t){throw t instanceof Error&&!("code"in t)?J.handleNetworkError(t,"fetching configuration"):t}}async syncRules(e,t){o.info(`Syncing ${e.rules.length} rules to Cloudflare${t?.dryRun?" (dry run)":""}`);let{OperationSafety:i}=(Pi(),fe(Ti)),r=await i.performDryRunValidation(e,"sync rules",async I=>await this.getChanges(I));if(!r.valid)throw new Error(`Dry-run validation failed: ${r.issues.join(", ")}`);if(t?.dryRun)return{success:!0,rulesAdded:r.changes.rulesToAdd.length,rulesUpdated:r.changes.rulesToUpdate.length,rulesDeleted:r.changes.rulesToDelete.length,ipsAdded:r.changes.ipsToAdd?.length||0,ipsUpdated:r.changes.ipsToUpdate?.length||0,ipsDeleted:r.changes.ipsToDelete?.length||0,warnings:r.warnings};let s=this.assessOperationRisk(r.changes,e);if(!await i.confirmDestructiveOperation({operation:"sync rules",target:`Cloudflare zone ${this.client.zoneId}`,changes:r.changes,riskLevel:s,skipConfirmation:t?.force||!1,dryRun:t?.dryRun||!1}))throw new Error("Operation cancelled by user");let l=await this.client.getOrCreateFirewallRuleset(),c=[];for(let I of e.rules){let g=M.unifiedToCloudflare(I);c.push(g.result),g.warnings.length>0&&g.warnings.forEach(A=>{let{TranslationWarningSystem:N}=(pe(),fe(me)),v=N.formatWarning(A);o.warn(v)})}let d=0,u=0,p=0;if(this.useListsForIPs&&e.ips&&e.ips.length>0)try{let I=await this.client.getOrCreateIPBlocklist(),g=await this.client.getListItems(I.id),A=new Set(g.map(b=>b.ip)),N=new Set(e.ips.map(b=>b.ip)),v=e.ips.filter(b=>!A.has(b.ip));v.length>0&&(await this.client.addListItems(I.id,{items:v.map(b=>({ip:b.ip,comment:b.notes||b.hostname||"Blocked by Doorman"}))}),d=v.length,o.info(`Added ${d} IPs to List`));let m=g.filter(b=>b.ip&&!N.has(b.ip));m.length>0&&(await this.client.removeListItems(I.id,{items:m.map(b=>({id:b.id}))}),p=m.length,o.info(`Removed ${p} IPs from List`));let P=`ip.src in $${I.name.replace(/\s+/g,"_").toLowerCase()}`;!l.rules.some(b=>b.expression.includes("in $"))&&e.ips.length>0&&c.push({id:"rule_doorman_ip_list",action:"block",expression:P,description:"Block IPs in Doorman IP Blocklist",enabled:!0})}catch(I){this.handleListsAPIFallback(I,"syncing IPs via List"),o.info(`\u{1F504} Falling back to individual IP rules for ${e.ips.length} IPs`);for(let g of e.ips){let A=M.unifiedIPToCloudflare(g);c.push(A)}d=e.ips.length,e.ips.length>50&&o.warn(`\u26A0\uFE0F Performance warning: Using ${e.ips.length} individual IP rules instead of Lists. This may impact rule processing speed and count against your rule limit.`)}else if(e.ips&&e.ips.length>0){o.info(`\u{1F4DD} Using individual IP rules for ${e.ips.length} IPs (Lists API not available)`),e.ips.length>20&&o.warn(`\u26A0\uFE0F Large IP list detected (${e.ips.length} IPs) without Lists API. Consider providing CLOUDFLARE_ACCOUNT_ID for better performance and rule efficiency.`);for(let I of e.ips){let g=M.unifiedIPToCloudflare(I);c.push(g)}d=e.ips.length}let f=await this.client.updateRuleset(l.id,{rules:c}),h={success:!0,rulesAdded:e.rules.length,rulesUpdated:0,rulesDeleted:0,ipsAdded:d,ipsUpdated:u,ipsDeleted:p,version:parseInt(f.version,10),warnings:r.warnings?.length?r.warnings:void 0};return this.logSyncStats(h),h}async getChanges(e){let t=await this.fetchConfig(),i=this.optimizer.diffRules(e.rules,t.rules),r={toAdd:[],toUpdate:[],toDelete:[]};return e.ips&&t.ips&&(r=this.optimizer.diffIPRules(e.ips,t.ips)),{rulesToAdd:i.toAdd,rulesToUpdate:i.toUpdate,rulesToDelete:i.toDelete,ipsToAdd:r.toAdd,ipsToUpdate:r.toUpdate,ipsToDelete:r.toDelete,hasChanges:i.toAdd.length>0||i.toUpdate.length>0||i.toDelete.length>0||r.toAdd.length>0||r.toUpdate.length>0||r.toDelete.length>0}}getSupportedFeatures(){return{supportsCustomRules:!0,supportsIPBlocking:!0,supportsRateLimiting:!0,supportsManagedRules:!0,supportsGeoBlocking:!0,supportsRedirect:!0,supportsChallenge:!0,maxRules:125}}validateConfig(e){let t=super.validateConfig(e),i=[...t.errors],r=[...t.warnings];if(e&&e.rules){let s=this.getSupportedFeatures();if(s.maxRules&&e.rules.length>s.maxRules){let a=q.ruleLimitExceeded(e.rules.length,s.maxRules);i.push({path:"rules",message:a.message,code:a.code})}if(e.rules.forEach((a,l)=>{try{if(!a.conditions||a.conditions.length===0){let c=q.ruleNoConditions(a.name||`Rule ${l+1}`);i.push({path:`rules[${l}]`,message:c.message,code:c.code})}if(a.action.type==="rate_limit"&&a.action.rateLimit){let c=a.action.rateLimit;if(c.requests<1){let d=q.invalidRateLimit(a.name||`Rule ${l+1}`,"requests must be at least 1");i.push({path:`rules[${l}].action.rateLimit.requests`,message:d.message,code:d.code})}if(!c.window.match(/^\d+[smhd]$/)){let d=q.invalidWindowFormat(c.window);i.push({path:`rules[${l}].action.rateLimit.window`,message:d.message,code:d.code})}if(c.characteristics&&c.characteristics.length===0){let d=q.emptyCharacteristics(a.name||`Rule ${l+1}`);r.push({path:`rules[${l}].action.rateLimit.characteristics`,message:d.message,code:d.code})}if(c.mitigationTimeout!==void 0&&c.mitigationTimeout<60){let d=q.shortMitigationTimeout(c.mitigationTimeout);r.push({path:`rules[${l}].action.rateLimit.mitigationTimeout`,message:d.message,code:d.code})}}if(a.action.type==="redirect"&&a.action.redirect){let c=a.action.redirect;if(c.location)try{new URL(c.location)}catch{if(!c.location.startsWith("/")){let d=q.invalidRedirectUrl(c.location);i.push({path:`rules[${l}].action.redirect.location`,message:d.message,code:d.code})}}else{let d=q.redirectNoLocation(a.name||`Rule ${l+1}`);i.push({path:`rules[${l}].action.redirect.location`,message:d.message,code:d.code})}}}catch(c){i.push({path:`rules[${l}]`,message:`Validation error: ${c instanceof Error?c.message:String(c)}`,code:"CLOUDFLARE_VALIDATION_ERROR"})}}),e.ips&&(e.ips.forEach((a,l)=>{try{if(!/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/(?:[0-9]|[1-2][0-9]|3[0-2]))?$/.test(a.ip)){let d=q.invalidIP(a.ip);i.push({path:`ips[${l}].ip`,message:d.message,code:d.code})}}catch(c){i.push({path:`ips[${l}]`,message:`IP validation error: ${c instanceof Error?c.message:String(c)}`,code:"CLOUDFLARE_IP_VALIDATION_ERROR"})}}),!this.useListsForIPs&&e.ips.length>50)){let a=q.largeIPList(e.ips.length);r.push({path:"ips",message:a.message,code:a.code})}}return{valid:i.length===0,errors:i,warnings:r}}getHealthScore(e){let t=super.getHealthScore(e),i=[...t.issues],r=this.getSupportedFeatures();return r.maxRules&&e.rules.length>r.maxRules*.8&&i.push({severity:"warning",category:"limits",message:`Approaching Cloudflare rule limit (${e.rules.length}/${r.maxRules})`,suggestion:"Consider consolidating rules or upgrading plan"}),{...t,issues:i}}async verifyCredentials(){return this.client.verifyCredentials()}getCacheStats(){return this.client.getCacheStats()}getOptimizerStats(){return this.optimizer.getStats()}clearCache(){this.client.clearCache(),this.optimizer.clearCaches()}isListBasedIPRule(e){return/ip\.src in \$/.test(e.expression.trim())}isIPBlockingRule(e){return/^ip\.src eq [\d.]+$/.test(e.expression.trim())}cloudflareRuleToIPRule(e){let i=e.expression.match(/ip\.src eq ([\d.]+)/)?.[1]||"",s=e.description?.match(/\(([^)]+)\)/)?.[1];return{id:e.id,ip:i,hostname:s,notes:e.description,action:e.action==="allow"?"allow":"deny"}}handleListsAPIFallback(e,t){if(e instanceof Error){let i=e.message.toLowerCase();i.includes("account")?(o.warn("\u{1F6AB} Lists API unavailable: Account ID not provided or invalid. Falling back to individual IP rules."),o.info(`\u{1F4A1} To enable Lists API for better performance with large IP lists:
42
42
  \u2022 Set CLOUDFLARE_ACCOUNT_ID in your environment
43
43
  \u2022 Ensure your API token has Account:Read permissions
44
44
  \u2022 Find your Account ID in the Cloudflare dashboard sidebar`)):i.includes("permission")||i.includes("forbidden")?(o.warn(`\u{1F6AB} Lists API permission denied: Insufficient permissions for ${t}. Falling back to individual IP rules.`),o.info(`\u{1F4A1} To fix Lists API permissions:
45
45
  \u2022 Ensure your API token has Account:Read permissions
46
46
  \u2022 Verify the account ID is correct
47
- \u2022 Check token permissions in Cloudflare dashboard`)):i.includes("not found")?(o.warn(`\u{1F6AB} Lists API resource not found during ${t}. Falling back to individual IP rules.`),o.info("\u{1F4A1} This may be temporary - the List will be recreated on next sync if Lists API is available.")):i.includes("rate limit")||i.includes("quota")?(o.warn(`\u{1F6AB} Lists API rate limited during ${t}. Falling back to individual IP rules.`),o.info("\u{1F4A1} Lists API rate limit reached. Individual rules will be used this time. Consider retrying later or upgrading your Cloudflare plan.")):(o.warn(`\u{1F6AB} Lists API error during ${t}: ${e.message}. Falling back to individual IP rules.`),o.info("\u{1F4A1} Lists API temporarily unavailable. Using individual IP rules as fallback. Check Cloudflare status if this persists."))}else o.warn(`\u{1F6AB} Unknown Lists API error during ${t}. Falling back to individual IP rules.`)}assessOperationRisk(e,t){let i=t.rules.length,r=t.ips?.length||0,s=(e.rulesToDelete?.length||0)+(e.ipsToDelete?.length||0),a=(e.rulesToAdd?.length||0)+(e.rulesToUpdate?.length||0)+(e.ipsToAdd?.length||0)+(e.ipsToUpdate?.length||0)+s;return s===i&&i>0||s>0&&s/Math.max(i+r,1)>.5||a>50||t.rules.some(c=>c.action.type==="deny"&&c.conditions.some(d=>d.field==="path"&&(d.value==="/"||d.value==="*")))?"high":s>0||a>10||e.rulesToUpdate&&e.rulesToUpdate.length>0?"medium":"low"}};var je=class{static create(e,t,i){let r=e||process.env.CLOUDFLARE_API_TOKEN,s=t||process.env.CLOUDFLARE_ZONE_ID,a=i||process.env.CLOUDFLARE_ACCOUNT_ID;if(!r)throw new Error("Cloudflare API token is required. Set CLOUDFLARE_API_TOKEN environment variable or pass as parameter.");if(!s)throw new Error("Cloudflare Zone ID is required. Set CLOUDFLARE_ZONE_ID environment variable or pass as parameter.");return new Ge(r,s,a)}static fromEnv(){return this.create()}static fromConfig(e){return this.create(e.apiToken,e.zoneId,e.accountId)}};k();async function Si(n){let e;if(n.provider)e=n.provider,o.debug(`Using explicitly specified provider: ${e}`);else{let t=it.detect(n.config);t.provider?(e=t.provider,o.debug(`Auto-detected provider: ${e} (${t.confidence} confidence)`),t.reasons.length>0&&o.debug(`Reasons: ${t.reasons.join(", ")}`)):n.interactive!==!1?e=await qo():(e="vercel",o.warn("No provider detected, defaulting to Vercel"))}try{if(e==="vercel")return await Wo(n);if(e==="cloudflare")return await Mo(n);throw new Error(`Unknown provider: ${e}`)}catch(t){throw o.error(`Failed to initialize ${e} provider:`,t),t}}async function Wo(n){let e=n.token||process.env.VERCEL_TOKEN,t=n.config&&!mt(n.config)?n.config:void 0,i=t?.projectId,r=t?.teamId,s=n.projectId||i||process.env.VERCEL_PROJECT_ID,a=n.teamId||r||process.env.VERCEL_TEAM_ID;if(!e||!s||!a){if(n.interactive===!1)throw new Error("Vercel credentials missing. Provide token, projectId, and teamId.");o.warn("Missing Vercel credentials"),o.info("Please provide your Vercel credentials:");let{promptForCredentials:l}=await Promise.resolve().then(()=>($t(),vi)),c=await l({token:e,projectId:s,teamId:a});return Ve.fromConfig({token:c.token,projectId:c.projectId,teamId:c.teamId})}return Ve.fromConfig({token:e,projectId:s,teamId:a})}async function Mo(n){let e=n.apiToken||process.env.CLOUDFLARE_API_TOKEN,t=n.zoneId||process.env.CLOUDFLARE_ZONE_ID,i=n.accountId||process.env.CLOUDFLARE_ACCOUNT_ID;if(!e||!t){if(n.interactive===!1)throw new Error("Cloudflare credentials missing. Provide apiToken and zoneId.");o.warn("Missing Cloudflare credentials"),o.info("Please provide your Cloudflare credentials:");let r=e||await C("Cloudflare API Token:",{type:"text"}),s=t||await C("Cloudflare Zone ID:",{type:"text"}),a=i||await C("Cloudflare Account ID (optional):",{type:"text"});return je.fromConfig({apiToken:r,zoneId:s,accountId:a||void 0})}return je.fromConfig({apiToken:e,zoneId:t,accountId:i})}async function qo(){o.info(ki.default.yellow("Unable to auto-detect firewall provider.")),o.info(`Please select a provider:
47
+ \u2022 Check token permissions in Cloudflare dashboard`)):i.includes("not found")?(o.warn(`\u{1F6AB} Lists API resource not found during ${t}. Falling back to individual IP rules.`),o.info("\u{1F4A1} This may be temporary - the List will be recreated on next sync if Lists API is available.")):i.includes("rate limit")||i.includes("quota")?(o.warn(`\u{1F6AB} Lists API rate limited during ${t}. Falling back to individual IP rules.`),o.info("\u{1F4A1} Lists API rate limit reached. Individual rules will be used this time. Consider retrying later or upgrading your Cloudflare plan.")):(o.warn(`\u{1F6AB} Lists API error during ${t}: ${e.message}. Falling back to individual IP rules.`),o.info("\u{1F4A1} Lists API temporarily unavailable. Using individual IP rules as fallback. Check Cloudflare status if this persists."))}else o.warn(`\u{1F6AB} Unknown Lists API error during ${t}. Falling back to individual IP rules.`)}assessOperationRisk(e,t){let i=t.rules.length,r=t.ips?.length||0,s=(e.rulesToDelete?.length||0)+(e.ipsToDelete?.length||0),a=(e.rulesToAdd?.length||0)+(e.rulesToUpdate?.length||0)+(e.ipsToAdd?.length||0)+(e.ipsToUpdate?.length||0)+s;return s===i&&i>0||s>0&&s/Math.max(i+r,1)>.5||a>50||t.rules.some(c=>c.action.type==="deny"&&c.conditions.some(d=>d.field==="path"&&(d.value==="/"||d.value==="*")))?"high":s>0||a>10||e.rulesToUpdate&&e.rulesToUpdate.length>0?"medium":"low"}};var je=class{static create(e,t,i){let r=e||process.env.CLOUDFLARE_API_TOKEN,s=t||process.env.CLOUDFLARE_ZONE_ID,a=i||process.env.CLOUDFLARE_ACCOUNT_ID;if(!r)throw new Error("Cloudflare API token is required. Set CLOUDFLARE_API_TOKEN environment variable or pass as parameter.");if(!s)throw new Error("Cloudflare Zone ID is required. Set CLOUDFLARE_ZONE_ID environment variable or pass as parameter.");return new Ge(r,s,a)}static fromEnv(){return this.create()}static fromConfig(e){return this.create(e.apiToken,e.zoneId,e.accountId)}};k();async function Si(n){let e;if(n.provider)e=n.provider,o.debug(`Using explicitly specified provider: ${e}`);else{let t=ot.detect(n.config);t.provider?(e=t.provider,o.debug(`Auto-detected provider: ${e} (${t.confidence} confidence)`),t.reasons.length>0&&o.debug(`Reasons: ${t.reasons.join(", ")}`)):n.interactive!==!1?e=await qo():(e="vercel",o.warn("No provider detected, defaulting to Vercel"))}try{if(e==="vercel")return await Wo(n);if(e==="cloudflare")return await Mo(n);throw new Error(`Unknown provider: ${e}`)}catch(t){throw o.error(`Failed to initialize ${e} provider:`,t),t}}async function Wo(n){let e=n.token||process.env.VERCEL_TOKEN,t=n.config&&!ht(n.config)?n.config:void 0,i=t?.projectId,r=t?.teamId,s=n.projectId||i||process.env.VERCEL_PROJECT_ID,a=n.teamId||r||process.env.VERCEL_TEAM_ID;if(!e||!s||!a){if(n.interactive===!1)throw new Error("Vercel credentials missing. Provide token, projectId, and teamId.");o.warn("Missing Vercel credentials"),o.info("Please provide your Vercel credentials:");let{promptForCredentials:l}=await Promise.resolve().then(()=>(At(),vi)),c=await l({token:e,projectId:s,teamId:a});return Ve.fromConfig({token:c.token,projectId:c.projectId,teamId:c.teamId})}return Ve.fromConfig({token:e,projectId:s,teamId:a})}async function Mo(n){let e=n.apiToken||process.env.CLOUDFLARE_API_TOKEN,t=n.zoneId||process.env.CLOUDFLARE_ZONE_ID,i=n.accountId||process.env.CLOUDFLARE_ACCOUNT_ID;if(!e||!t){if(n.interactive===!1)throw new Error("Cloudflare credentials missing. Provide apiToken and zoneId.");o.warn("Missing Cloudflare credentials"),o.info("Please provide your Cloudflare credentials:");let r=e||await C("Cloudflare API Token:",{type:"text"}),s=t||await C("Cloudflare Zone ID:",{type:"text"}),a=i||await C("Cloudflare Account ID (optional):",{type:"text"});return je.fromConfig({apiToken:r,zoneId:s,accountId:a||void 0})}return je.fromConfig({apiToken:e,zoneId:t,accountId:i})}async function qo(){o.info(ki.default.yellow("Unable to auto-detect firewall provider.")),o.info(`Please select a provider:
48
48
  `),o.info(" 1. Vercel Firewall"),o.info(` 2. Cloudflare WAF
49
- `);let n=await C("Select provider (1 or 2):",{type:"text"}),e=String(n).toLowerCase();return e==="1"||e==="vercel"?"vercel":e==="2"||e==="cloudflare"?"cloudflare":(o.warn(`Invalid choice: ${n}, defaulting to Vercel`),"vercel")}async function ee(n,e){try{n.debug&&(o.level=_i.LogLevels.debug);let t;if(n.optionalConfig)try{t=await G(n.config,"optional")}catch{t={}}else n.skipValidation?t=await G(n.config,"raw"):t=await G(n.config,"required");let i=await Si({provider:n.provider,config:t,interactive:!n.ci,token:n.token,projectId:n.projectId,teamId:n.teamId,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId}),r,s,a="",l="",c="";if(i.name==="vercel"){let d=await Et({token:n.token,projectId:n.projectId||t.projectId,teamId:n.teamId||t.teamId});a=d.token,l=d.projectId,c=d.teamId,r=new tt(l,c,a),s=new et(r)}else r={},s={};await e({config:t,provider:i,client:r,service:s,token:a,projectId:l,teamId:c})}catch(t){Q(t,n.errorContext)}}var Go="backup",Ho="Backup or restore firewall configurations",Zo={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},output:{alias:"o",type:"string",description:"Output directory for backups",default:"./backups"},restore:{alias:"r",type:"string",description:"Restore from backup file"},list:{alias:"l",type:"boolean",description:"List available backups",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Ko=async n=>{try{n.debug&&(o.level=Li.LogLevels.debug);let e=n.output||"./backups";if(n.list){if(!(0,he.existsSync)(e)){o.info(te.default.yellow("No backup directory found."));return}let t=(0,he.readdirSync)(e).filter(i=>i.endsWith(".json")).map(i=>{let r=(0,rt.join)(e,i),s=(0,he.statSync)(r);return{name:i,size:s.size,created:s.mtime}}).sort((i,r)=>r.created.getTime()-i.created.getTime());if(t.length===0){o.info(te.default.yellow("No backups found."));return}o.log(te.default.bold(`
49
+ `);let n=await C("Select provider (1 or 2):",{type:"text"}),e=String(n).toLowerCase();return e==="1"||e==="vercel"?"vercel":e==="2"||e==="cloudflare"?"cloudflare":(o.warn(`Invalid choice: ${n}, defaulting to Vercel`),"vercel")}async function ee(n,e){try{n.debug&&(o.level=_i.LogLevels.debug);let t;if(n.optionalConfig)try{t=await G(n.config,"optional")}catch{t={}}else n.skipValidation?t=await G(n.config,"raw"):t=await G(n.config,"required");let i=await Si({provider:n.provider,config:t,interactive:!n.ci,token:n.token,projectId:n.projectId,teamId:n.teamId,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId}),r,s,a="",l="",c="";if(i.name==="vercel"){let d=await $t({token:n.token,projectId:n.projectId||t.projectId,teamId:n.teamId||t.teamId});a=d.token,l=d.projectId,c=d.teamId,r=new it(l,c,a),s=new tt(r)}else r={},s={};await e({config:t,provider:i,client:r,service:s,token:a,projectId:l,teamId:c})}catch(t){Q(t,n.errorContext)}}var Go="backup",Ho="Backup or restore firewall configurations",Zo={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},output:{alias:"o",type:"string",description:"Output directory for backups",default:"./backups"},restore:{alias:"r",type:"string",description:"Restore from backup file"},list:{alias:"l",type:"boolean",description:"List available backups",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Ko=async n=>{try{n.debug&&(o.level=Li.LogLevels.debug);let e=n.output||"./backups";if(n.list){if(!(0,he.existsSync)(e)){o.info(te.default.yellow("No backup directory found."));return}let t=(0,he.readdirSync)(e).filter(i=>i.endsWith(".json")).map(i=>{let r=(0,st.join)(e,i),s=(0,he.statSync)(r);return{name:i,size:s.size,created:s.mtime}}).sort((i,r)=>r.created.getTime()-i.created.getTime());if(t.length===0){o.info(te.default.yellow("No backups found."));return}o.log(te.default.bold(`
50
50
  \u{1F4E6} Available Backups:
51
- `)),t.forEach(i=>{o.log(`${te.default.cyan(i.name)}`),o.log(` ${te.default.dim("Created:")} ${i.created.toLocaleString()}`),o.log(` ${te.default.dim("Size:")} ${(i.size/1024).toFixed(1)} KB`),o.log("")});return}if(n.restore){let t=n.restore.startsWith("/")?n.restore:(0,rt.join)(e,n.restore);(0,he.existsSync)(t)||(o.error(`Backup file not found: ${t}`),process.exit(1));let i=await G(t,"raw"),r=n.config||".doorman.json";if((0,he.existsSync)(r)&&!await C(`Config file ${r} already exists. Do you want to overwrite it?`,{type:"confirm"})){o.info(te.default.yellow("Restore cancelled."));return}await z(i,r),o.success(te.default.green(`\u2705 Restored configuration from ${t} to ${r}`));return}await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"creating backup"},async({client:t,provider:i,projectId:r,teamId:s})=>{o.start("Fetching current remote configuration...");let a=i.name==="vercel"?await t.fetchFirewallConfig():await i.fetchConfig();(0,he.existsSync)(e)||(0,he.mkdirSync)(e,{recursive:!0});let l=new Date,c=l.toISOString().split("T")[0]??"unknown-date",d=(l.toISOString().split("T")[1]??"").split(".")[0]?.replace(/:/g,"-")??"unknown-time",p=`firewall-backup-${`${c}_${d}`}.json`,g=(0,rt.join)(e,p),h={...a,backup:{createdAt:new Date().toISOString(),source:"remote",provider:i.name,...i.name==="vercel"?{projectId:r,teamId:s}:{},originalVersion:a.version}};await z(h,g,{validate:!1}),o.success(te.default.green(`\u2705 Backup created: ${g}`)),o.log(""),o.log(te.default.bold("Backup Details:")),o.log(`${te.default.dim("Version:")} ${a.version}`),o.log(`${te.default.dim("Rules:")} ${a.rules.length} custom, ${a.ips?.length??0} IP blocking`),o.log(`${te.default.dim("Created:")} ${new Date().toLocaleString()}`),o.log(""),o.log(te.default.dim("To restore this backup later, run:")),o.log(te.default.cyan(`doorman backup --restore ${p}`))})}catch(e){Q(e,"managing backup")}};var Nt={};Z(Nt,{builder:()=>Yo,command:()=>Xo,desc:()=>Qo,handler:()=>en});var O=S(require("chalk"));k();var H=S(require("chalk")),Ot=S(require("cli-table3"));k();var St=(n,e,t,i)=>{if(!e.length)throw new Error("minWidths array cannot be empty");if(i.some(c=>c>=e.length))throw new Error("flexColumns contains invalid column index");if(e.length!==t.length)throw new Error("minWidths and maxWidths arrays must have the same length");e.forEach((c,d)=>{let u=t[d];if(u!=null&&c>u)throw new Error(`Column ${d} has minimum width (${c}) greater than maximum width (${u})`)});let r=n||400,s=[...e],a=e.reduce((c,d)=>c+d,0),l=r-a-28;if(l>0&&i.length>0){let c=i.filter(d=>{let u=t[d],p=s[d];return u!==void 0&&p!==void 0&&(u===null||p<u)});for(;l>0&&c.length>0;){let d=Math.floor(l/c.length);if(d===0)break;let u=0;c=c.filter(p=>{let g=t[p],h=s[p];if(g===null)return s[p]!==void 0&&(s[p]+=d),u+=d,!0;{let b=(g??0)-(h??0);if(b<=0)return!1;let f=Math.min(d,b);return s[p]!==void 0&&(s[p]+=f),u+=f,f===d}}),l-=u}if(l>0){let d=i.findLast(u=>t[u]===null);d!==void 0&&d!==void 0&&s[d]!==void 0&&(s[d]+=l)}}return s};var de=S(require("chalk")),Di=n=>{if(n==="permanent")return"permanent";let e=n.slice(0,-1),t=n.slice(-1);return`${e}${{s:"sec",m:"min",h:"hr",d:"day"}[t]||t}`},Jo=(n,e)=>e==="60s"?`${n}/min`:e==="3600s"?`${n}/hr`:e==="86400s"?`${n}/day`:`${n}/${Di(e)}`;function Ui(n){if(!n.mitigate)return"unknown";let{mitigate:e}=n,t=[];switch(e.action){case"log":t.push(de.default.blue("log"));break;case"deny":t.push(de.default.red("deny"));break;case"challenge":t.push(de.default.yellow("challenge"));break;case"bypass":t.push(de.default.green("bypass"));break;case"rate_limit":t.push(de.default.yellow("rate-limit"));break;case"redirect":t.push(de.default.magenta("redirect"));break;default:t.push(e.action)}return e.rateLimit&&(t[0]=de.default.yellow("rate-limit"),t.push(de.default.yellow(Jo(e.rateLimit.requests,e.rateLimit.window)))),e.redirect&&(t[0]=de.default.magenta("redirect"),t.push(de.default.magenta(`\u2192 ${e.redirect.location}`)),e.redirect.permanent!==void 0&&t.push(de.default.gray(`(${e.redirect.permanent?"301":"302"})`))),e.actionDuration&&t.push(de.default.gray(`for ${Di(e.actionDuration)}`)),t.join(" ")}var He=S(require("chalk"));function _t(n){switch(n){case"unchanged":return He.default.bold.gray(" ");case"modified":return He.default.bold.yellow("\u25B4");case"new":return He.default.bold.green("+");case"deleted":return He.default.bold.red("-")}}var Ze=S(require("chalk"));function xi(n=[]){return n.map((e,t)=>{let i=e.conditions.map(r=>Ze.default.cyan(`${r.type}:${r.neg?Ze.default.red(`!${r.op}:`):`${r.op}:`}${Ze.default.white(r.value)}`)).join(`
51
+ `)),t.forEach(i=>{o.log(`${te.default.cyan(i.name)}`),o.log(` ${te.default.dim("Created:")} ${i.created.toLocaleString()}`),o.log(` ${te.default.dim("Size:")} ${(i.size/1024).toFixed(1)} KB`),o.log("")});return}if(n.restore){let t=n.restore.startsWith("/")?n.restore:(0,st.join)(e,n.restore);(0,he.existsSync)(t)||(o.error(`Backup file not found: ${t}`),process.exit(1));let i=await G(t,"raw"),r=n.config||".doorman.json";if((0,he.existsSync)(r)&&!await C(`Config file ${r} already exists. Do you want to overwrite it?`,{type:"confirm"})){o.info(te.default.yellow("Restore cancelled."));return}await z(i,r),o.success(te.default.green(`\u2705 Restored configuration from ${t} to ${r}`));return}await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"creating backup"},async({client:t,provider:i,projectId:r,teamId:s})=>{o.start("Fetching current remote configuration...");let a=i.name==="vercel"?await t.fetchFirewallConfig():await i.fetchConfig(),l=i.name==="vercel"?{version:a.version,firewallEnabled:a.firewallEnabled,rules:a.rules.map(({valid:A,validationErrors:N,...v})=>v),ips:a.ips,updatedAt:a.updatedAt}:a;Re.getInstance().validateConfig(l),(0,he.existsSync)(e)||(0,he.mkdirSync)(e,{recursive:!0});let d=new Date,u=d.toISOString().split("T")[0]??"unknown-date",p=(d.toISOString().split("T")[1]??"").split(".")[0]?.replace(/:/g,"-")??"unknown-time",h=`firewall-backup-${`${u}_${p}`}.json`,I=(0,st.join)(e,h),g={...l,backup:{createdAt:new Date().toISOString(),source:"remote",provider:i.name,...i.name==="vercel"?{projectId:r,teamId:s}:{},originalVersion:a.version}};await z(g,I,{validate:!1}),o.success(te.default.green(`\u2705 Backup created: ${I}`)),o.log(""),o.log(te.default.bold("Backup Details:")),o.log(`${te.default.dim("Version:")} ${a.version}`),o.log(`${te.default.dim("Rules:")} ${a.rules.length} custom, ${a.ips?.length??0} IP blocking`),o.log(`${te.default.dim("Created:")} ${new Date().toLocaleString()}`),o.log(""),o.log(te.default.dim("To restore this backup later, run:")),o.log(te.default.cyan(`doorman backup --restore ${h}`))})}catch(e){Q(e,"managing backup")}};var Nt={};Z(Nt,{builder:()=>Yo,command:()=>Xo,desc:()=>Qo,handler:()=>en});var O=S(require("chalk"));k();var H=S(require("chalk")),Ft=S(require("cli-table3"));k();var _t=(n,e,t,i)=>{if(!e.length)throw new Error("minWidths array cannot be empty");if(i.some(c=>c>=e.length))throw new Error("flexColumns contains invalid column index");if(e.length!==t.length)throw new Error("minWidths and maxWidths arrays must have the same length");e.forEach((c,d)=>{let u=t[d];if(u!=null&&c>u)throw new Error(`Column ${d} has minimum width (${c}) greater than maximum width (${u})`)});let r=n||400,s=[...e],a=e.reduce((c,d)=>c+d,0),l=r-a-28;if(l>0&&i.length>0){let c=i.filter(d=>{let u=t[d],p=s[d];return u!==void 0&&p!==void 0&&(u===null||p<u)});for(;l>0&&c.length>0;){let d=Math.floor(l/c.length);if(d===0)break;let u=0;c=c.filter(p=>{let f=t[p],h=s[p];if(f===null)return s[p]!==void 0&&(s[p]+=d),u+=d,!0;{let I=(f??0)-(h??0);if(I<=0)return!1;let g=Math.min(d,I);return s[p]!==void 0&&(s[p]+=g),u+=g,g===d}}),l-=u}if(l>0){let d=i.findLast(u=>t[u]===null);d!==void 0&&d!==void 0&&s[d]!==void 0&&(s[d]+=l)}}return s};var de=S(require("chalk")),Di=n=>{if(n==="permanent")return"permanent";let e=n.slice(0,-1),t=n.slice(-1);return`${e}${{s:"sec",m:"min",h:"hr",d:"day"}[t]||t}`},Jo=(n,e)=>e==="60s"?`${n}/min`:e==="3600s"?`${n}/hr`:e==="86400s"?`${n}/day`:`${n}/${Di(e)}`;function Ui(n){if(!n.mitigate)return"unknown";let{mitigate:e}=n,t=[];switch(e.action){case"log":t.push(de.default.blue("log"));break;case"deny":t.push(de.default.red("deny"));break;case"challenge":t.push(de.default.yellow("challenge"));break;case"bypass":t.push(de.default.green("bypass"));break;case"rate_limit":t.push(de.default.yellow("rate-limit"));break;case"redirect":t.push(de.default.magenta("redirect"));break;default:t.push(e.action)}return e.rateLimit&&(t[0]=de.default.yellow("rate-limit"),t.push(de.default.yellow(Jo(e.rateLimit.requests,e.rateLimit.window)))),e.redirect&&(t[0]=de.default.magenta("redirect"),t.push(de.default.magenta(`\u2192 ${e.redirect.location}`)),e.redirect.permanent!==void 0&&t.push(de.default.gray(`(${e.redirect.permanent?"301":"302"})`))),e.actionDuration&&t.push(de.default.gray(`for ${Di(e.actionDuration)}`)),t.join(" ")}var He=S(require("chalk"));function Lt(n){switch(n){case"unchanged":return He.default.bold.gray(" ");case"modified":return He.default.bold.yellow("\u25B4");case"new":return He.default.bold.green("+");case"deleted":return He.default.bold.red("-")}}var Ze=S(require("chalk"));function xi(n=[]){return n.map((e,t)=>{let i=e.conditions.map(r=>Ze.default.cyan(`${r.type}:${r.neg?Ze.default.red(`!${r.op}:`):`${r.op}:`}${Ze.default.white(r.value)}`)).join(`
52
52
  `);return t>0?`${Ze.default.yellow("OR")}
53
53
  ${i}`:i}).join(`
54
- `)}var Lt=S(require("chalk"));function Oi(n){return{id:n.id||"-",ip:n.ip,hostname:n.hostname,notes:n.notes||"-",status:n.changeStatus?Lt.default.yellow(n.changeStatus):Lt.default.red("deny")}}var Ke=S(require("chalk"));function Dt(n){switch(n){default:case"unchanged":return Ke.default.white;case"modified":return Ke.default.yellow;case"new":return Ke.default.green;case"deleted":return Ke.default.red}}var le={unchanged:"unchanged",modified:"modified",new:"new",deleted:"deleted"},Ut={minWidths:[10,28,20,5,12,35],maxWidths:[15,32,36,8,24,null],flexColumns:[0,1,2,4,5]},xt={minWidths:[10,28,24,5,12],maxWidths:[20,32,26,null,16],flexColumns:[0,1,3]},Ni=(n,e)=>n==="ip"?St(e,xt.minWidths,xt.maxWidths,xt.flexColumns):St(e,Ut.minWidths,Ut.maxWidths,Ut.flexColumns);function be(n,{showStatus:e}){let t=[H.default.bold.gray("ID"),H.default.bold.gray("Name"),H.default.bold.gray("Conditions"),H.default.bold.gray(" "),H.default.bold.gray("Action"),H.default.bold.gray("Description")],i=["left","left","left","center","left","left"],r=process.stdout.columns||400,s=Ni("rules",r);e&&(t.unshift(H.default.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new Ot.default({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c="changeStatus"in l&&e,d=c?Dt(l.changeStatus):H.default.white,u=[d(l.id?l.id:"-"),d(l.name),d(xi(l.conditionGroup)),l.active?H.default.bold.green("\u2713"):H.default.bold.red("\u2717"),d(Ui(l.action)),d(l.description||"")];c&&u.unshift(d(_t(l.changeStatus))),a.push(u)}),o.log(a.toString())}function ve(n,{showStatus:e}){let t=[H.default.bold.gray("ID"),H.default.bold.gray("IP"),H.default.bold.gray("Hostname"),H.default.bold.gray("Notes"),H.default.bold.gray("Action")],i=["left","left","left","left","center"],r=process.stdout.columns||400,s=Ni("ip",r);e&&(t.unshift(H.default.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new Ot.default({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c=l.changeStatus!==void 0&&e,d=c?Dt(l.changeStatus):H.default.white,u=Oi(l),p=[d(u.id),d(u.ip),d(u.hostname),d(u.notes),u.status];c&&p.unshift(d(_t(l.changeStatus))),a.push(p)}),o.log(a.toString())}var Xo="diff",Qo="Show detailed differences between local and remote firewall configuration",Yo={config:{alias:"c",type:"string",description:"Path to firewall config file"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID"},teamId:{alias:"t",type:"string",description:"Vercel Team ID"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",choices:["table","json"],description:"Output format",default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},en=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"calculating diff"},async({config:e,service:t,provider:i})=>{if(i.name!=="vercel"){await tn(i,e,n);return}o.start("Calculating differences...");let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await t.getChanges(e),p=r.length>0||s.length>0||a.length>0,g=l.length>0||c.length>0||d.length>0,h=e.version!==u;if(!p&&!g&&!h){o.success(O.default.green("No differences found. Local and remote configurations are in sync."));return}if(n.format==="json"){let f={version:{local:e.version,remote:u,changed:h},customRules:{toAdd:r.map(T=>({...T,status:"add"})),toUpdate:s.map(T=>({...T,status:"update"})),toDelete:a.map(T=>({...T,status:"delete"}))},ipRules:{toAdd:l.map(T=>({...T,status:"add"})),toUpdate:c.map(T=>({...T,status:"update"})),toDelete:d.map(T=>({...T,status:"delete"}))},summary:{hasChanges:p||g||h,customRuleChanges:r.length+s.length+a.length,ipRuleChanges:l.length+c.length+d.length}};o.log(JSON.stringify(f,null,2));return}o.log(O.default.bold(`
54
+ `)}var Dt=S(require("chalk"));function Oi(n){return{id:n.id||"-",ip:n.ip,hostname:n.hostname,notes:n.notes||"-",status:n.changeStatus?Dt.default.yellow(n.changeStatus):Dt.default.red("deny")}}var Ke=S(require("chalk"));function Ut(n){switch(n){default:case"unchanged":return Ke.default.white;case"modified":return Ke.default.yellow;case"new":return Ke.default.green;case"deleted":return Ke.default.red}}var le={unchanged:"unchanged",modified:"modified",new:"new",deleted:"deleted"},xt={minWidths:[10,28,20,5,12,35],maxWidths:[15,32,36,8,24,null],flexColumns:[0,1,2,4,5]},Ot={minWidths:[10,28,24,5,12],maxWidths:[20,32,26,null,16],flexColumns:[0,1,3]},Fi=(n,e)=>n==="ip"?_t(e,Ot.minWidths,Ot.maxWidths,Ot.flexColumns):_t(e,xt.minWidths,xt.maxWidths,xt.flexColumns);function ve(n,{showStatus:e}){let t=[H.default.bold.gray("ID"),H.default.bold.gray("Name"),H.default.bold.gray("Conditions"),H.default.bold.gray(" "),H.default.bold.gray("Action"),H.default.bold.gray("Description")],i=["left","left","left","center","left","left"],r=process.stdout.columns||400,s=Fi("rules",r);e&&(t.unshift(H.default.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new Ft.default({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c="changeStatus"in l&&e,d=c?Ut(l.changeStatus):H.default.white,u=[d(l.id?l.id:"-"),d(l.name),d(xi(l.conditionGroup)),l.active?H.default.bold.green("\u2713"):H.default.bold.red("\u2717"),d(Ui(l.action)),d(l.description||"")];c&&u.unshift(d(Lt(l.changeStatus))),a.push(u)}),o.log(a.toString())}function Ee(n,{showStatus:e}){let t=[H.default.bold.gray("ID"),H.default.bold.gray("IP"),H.default.bold.gray("Hostname"),H.default.bold.gray("Notes"),H.default.bold.gray("Action")],i=["left","left","left","left","center"],r=process.stdout.columns||400,s=Fi("ip",r);e&&(t.unshift(H.default.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new Ft.default({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c=l.changeStatus!==void 0&&e,d=c?Ut(l.changeStatus):H.default.white,u=Oi(l),p=[d(u.id),d(u.ip),d(u.hostname),d(u.notes),u.status];c&&p.unshift(d(Lt(l.changeStatus))),a.push(p)}),o.log(a.toString())}var Xo="diff",Qo="Show detailed differences between local and remote firewall configuration",Yo={config:{alias:"c",type:"string",description:"Path to firewall config file"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID"},teamId:{alias:"t",type:"string",description:"Vercel Team ID"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",choices:["table","json"],description:"Output format",default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},en=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"calculating diff"},async({config:e,service:t,provider:i})=>{if(i.name!=="vercel"){await tn(i,e,n);return}o.start("Calculating differences...");let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await t.getChanges(e),p=r.length>0||s.length>0||a.length>0,f=l.length>0||c.length>0||d.length>0,h=e.version!==u;if(!p&&!f&&!h){o.success(O.default.green("No differences found. Local and remote configurations are in sync."));return}if(n.format==="json"){let g={version:{local:e.version,remote:u,changed:h},customRules:{toAdd:r.map(A=>({...A,status:"add"})),toUpdate:s.map(A=>({...A,status:"update"})),toDelete:a.map(A=>({...A,status:"delete"}))},ipRules:{toAdd:l.map(A=>({...A,status:"add"})),toUpdate:c.map(A=>({...A,status:"update"})),toDelete:d.map(A=>({...A,status:"delete"}))},summary:{hasChanges:p||f||h,customRuleChanges:r.length+s.length+a.length,ipRuleChanges:l.length+c.length+d.length}};o.log(JSON.stringify(g,null,2));return}o.log(O.default.bold(`
55
55
  \u{1F50D} Configuration Differences
56
56
  `)),h&&(o.log(O.default.bold("Version Changes:")),o.log(` Local: ${O.default.red(e.version||"unknown")}`),o.log(` Remote: ${O.default.green(u)}`),o.log("")),p&&(o.log(O.default.bold(`Custom Rule Changes:
57
- `)),be([...r.map(f=>({...f,changeStatus:le.new,id:f.id})),...s.map(f=>({...f,changeStatus:le.modified,id:f.id})),...a.map(f=>({...f,changeStatus:le.deleted,id:f.id}))],{showStatus:!0}),o.log("")),g&&(o.log(O.default.bold(`IP Blocking Rule Changes:
58
- `)),ve([...l.map(f=>({...f,changeStatus:le.new,id:f.id||void 0})),...c.map(f=>({...f,changeStatus:le.modified,id:f.id||void 0})),...d.map(f=>({...f,changeStatus:le.deleted,id:f.id||void 0}))],{showStatus:!0}),o.log(""));let b=r.length+s.length+a.length+l.length+c.length+d.length;o.log(O.default.bold(`Summary: ${b} total changes detected`)),o.log(O.default.dim("Run `sync` to apply these changes to the remote configuration."))})};async function tn(n,e,t){o.start("Calculating differences...");let i=await n.getChanges(e);if(!i.hasChanges){o.success(O.default.green("No differences found. Local and remote configurations are in sync."));return}let r=i.ipsToAdd??[],s=i.ipsToUpdate??[],a=i.ipsToDelete??[];if(t.format==="json"){o.log(JSON.stringify({rules:{toAdd:i.rulesToAdd,toUpdate:i.rulesToUpdate,toDelete:i.rulesToDelete},ips:{toAdd:r,toUpdate:s,toDelete:a},summary:{hasChanges:i.hasChanges,ruleChanges:i.rulesToAdd.length+i.rulesToUpdate.length+i.rulesToDelete.length,ipChanges:r.length+s.length+a.length}},null,2));return}o.log(O.default.bold(`
57
+ `)),ve([...r.map(g=>({...g,changeStatus:le.new,id:g.id})),...s.map(g=>({...g,changeStatus:le.modified,id:g.id})),...a.map(g=>({...g,changeStatus:le.deleted,id:g.id}))],{showStatus:!0}),o.log("")),f&&(o.log(O.default.bold(`IP Blocking Rule Changes:
58
+ `)),Ee([...l.map(g=>({...g,changeStatus:le.new,id:g.id||void 0})),...c.map(g=>({...g,changeStatus:le.modified,id:g.id||void 0})),...d.map(g=>({...g,changeStatus:le.deleted,id:g.id||void 0}))],{showStatus:!0}),o.log(""));let I=r.length+s.length+a.length+l.length+c.length+d.length;o.log(O.default.bold(`Summary: ${I} total changes detected`)),o.log(O.default.dim("Run `sync` to apply these changes to the remote configuration."))})};async function tn(n,e,t){o.start("Calculating differences...");let i=await n.getChanges(e);if(!i.hasChanges){o.success(O.default.green("No differences found. Local and remote configurations are in sync."));return}let r=i.ipsToAdd??[],s=i.ipsToUpdate??[],a=i.ipsToDelete??[];if(t.format==="json"){o.log(JSON.stringify({rules:{toAdd:i.rulesToAdd,toUpdate:i.rulesToUpdate,toDelete:i.rulesToDelete},ips:{toAdd:r,toUpdate:s,toDelete:a},summary:{hasChanges:i.hasChanges,ruleChanges:i.rulesToAdd.length+i.rulesToUpdate.length+i.rulesToDelete.length,ipChanges:r.length+s.length+a.length}},null,2));return}o.log(O.default.bold(`
59
59
  \u{1F50D} ${n.name} Configuration Differences
60
- `)),o.log(O.default.bold("Rule Changes:")),o.log(` ${O.default.green("+")} ${i.rulesToAdd.length} to add`),i.rulesToAdd.forEach(c=>o.log(` ${O.default.green("+")} ${c.name}`)),o.log(` ${O.default.cyan("~")} ${i.rulesToUpdate.length} to update`),i.rulesToUpdate.forEach(c=>o.log(` ${O.default.cyan("~")} ${c.name}`)),o.log(` ${O.default.red("-")} ${i.rulesToDelete.length} to delete`),i.rulesToDelete.forEach(c=>o.log(` ${O.default.red("-")} ${c.name}`)),(r.length||s.length||a.length)&&(o.log(""),o.log(O.default.bold("IP Rule Changes:")),o.log(` ${O.default.green("+")} ${r.length} to add`),o.log(` ${O.default.cyan("~")} ${s.length} to update`),o.log(` ${O.default.red("-")} ${a.length} to delete`));let l=i.rulesToAdd.length+i.rulesToUpdate.length+i.rulesToDelete.length+r.length+s.length+a.length;o.log(""),o.log(O.default.bold(`Summary: ${l} total changes detected`)),o.log(O.default.dim("Run `sync` to apply these changes to the remote configuration."))}var Ft={};Z(Ft,{builder:()=>rn,command:()=>on,desc:()=>nn,handler:()=>sn});var ge=S(require("chalk")),Fi=require("zod");k();ce();var on="download [configVersion]",nn="Download remote Vercel Firewall rules and update local config, optionally for a specific configuration version",rn={configVersion:{type:"number",description:"Specific configuration version to download (defaults to latest)"},config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},dryRun:{alias:"d",type:"boolean",description:"Show rules that would be downloaded without making changes",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},sn=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,optionalConfig:!0,skipValidation:!0,errorContext:"downloading firewall rules"},async({config:e,client:t,provider:i,projectId:r,teamId:s})=>{if(n.configVersion!==void 0)try{Qe.parse(n.configVersion)}catch(p){throw p instanceof Fi.z.ZodError&&(o.error("Invalid configuration version number. Version must be a positive integer."),process.exit(1)),p}if(i.name!=="vercel"){await an(i,e,n);return}o.start(`Fetching remote firewall configuration${n.configVersion?` version ${n.configVersion}`:""} ...`);let a=await t.fetchFirewallConfig(n.configVersion),l=a.rules,c=a.ips;if(l.length>0?(o.log(ge.default.bold(`
60
+ `)),o.log(O.default.bold("Rule Changes:")),o.log(` ${O.default.green("+")} ${i.rulesToAdd.length} to add`),i.rulesToAdd.forEach(c=>o.log(` ${O.default.green("+")} ${c.name}`)),o.log(` ${O.default.cyan("~")} ${i.rulesToUpdate.length} to update`),i.rulesToUpdate.forEach(c=>o.log(` ${O.default.cyan("~")} ${c.name}`)),o.log(` ${O.default.red("-")} ${i.rulesToDelete.length} to delete`),i.rulesToDelete.forEach(c=>o.log(` ${O.default.red("-")} ${c.name}`)),(r.length||s.length||a.length)&&(o.log(""),o.log(O.default.bold("IP Rule Changes:")),o.log(` ${O.default.green("+")} ${r.length} to add`),o.log(` ${O.default.cyan("~")} ${s.length} to update`),o.log(` ${O.default.red("-")} ${a.length} to delete`));let l=i.rulesToAdd.length+i.rulesToUpdate.length+i.rulesToDelete.length+r.length+s.length+a.length;o.log(""),o.log(O.default.bold(`Summary: ${l} total changes detected`)),o.log(O.default.dim("Run `sync` to apply these changes to the remote configuration."))}var Vt={};Z(Vt,{builder:()=>rn,command:()=>on,desc:()=>nn,handler:()=>sn});var ge=S(require("chalk")),Ni=require("zod");k();ce();var on="download [configVersion]",nn="Download remote Vercel Firewall rules and update local config, optionally for a specific configuration version",rn={configVersion:{type:"number",description:"Specific configuration version to download (defaults to latest)"},config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},dryRun:{alias:"d",type:"boolean",description:"Show rules that would be downloaded without making changes",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},sn=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,optionalConfig:!0,skipValidation:!0,errorContext:"downloading firewall rules"},async({config:e,client:t,provider:i,projectId:r,teamId:s})=>{if(n.configVersion!==void 0)try{Qe.parse(n.configVersion)}catch(p){throw p instanceof Ni.z.ZodError&&(o.error("Invalid configuration version number. Version must be a positive integer."),process.exit(1)),p}if(i.name!=="vercel"){await an(i,e,n);return}o.start(`Fetching remote firewall configuration${n.configVersion?` version ${n.configVersion}`:""} ...`);let a=await t.fetchFirewallConfig(n.configVersion),l=a.rules,c=a.ips;if(l.length>0?(o.log(ge.default.bold(`
61
61
  Remote Custom Rules to Download:
62
- `)),be(l,{showStatus:!1})):o.info("No custom rules to download..."),c.length>0?(o.log(ge.default.bold(`
62
+ `)),ve(l,{showStatus:!1})):o.info("No custom rules to download..."),c.length>0?(o.log(ge.default.bold(`
63
63
  Remote IP Blocking Rules to Download:
64
- `)),ve(c,{showStatus:!1})):o.info("No IP blocking rules to download..."),n.dryRun){o.info(ge.default.cyan("Dry run completed. No changes made."));return}if(!await C(`Do you want to download${n.configVersion?` version ${n.configVersion}`:" the latest version"} of these rules? This will overwrite your local configuration.`,{type:"confirm"})){o.info(ge.default.yellow("Download cancelled."));return}let u={...e,projectId:r,teamId:s,version:a.version,updatedAt:a.updatedAt,rules:l.map(({valid:p,validationErrors:g,...h})=>h),ips:c};o.start(`Saving configuration with version: ${u.version}`),await z(u,n.config),o.success(ge.default.green("Successfully downloaded and updated configuration"))})};async function an(n,e,t){o.start(`Fetching remote firewall configuration${t.configVersion?` version ${t.configVersion}`:""} ...`);let i=await n.fetchConfig(t.configVersion),r=i.rules,s=i.ips??[];if(r.length>0?(o.log(ge.default.bold(`
64
+ `)),Ee(c,{showStatus:!1})):o.info("No IP blocking rules to download..."),n.dryRun){o.info(ge.default.cyan("Dry run completed. No changes made."));return}if(!await C(`Do you want to download${n.configVersion?` version ${n.configVersion}`:" the latest version"} of these rules? This will overwrite your local configuration.`,{type:"confirm"})){o.info(ge.default.yellow("Download cancelled."));return}let u={...e,projectId:r,teamId:s,version:a.version,updatedAt:a.updatedAt,rules:l.map(({valid:p,validationErrors:f,...h})=>h),ips:c};o.start(`Saving configuration with version: ${u.version}`),await z(u,n.config),o.success(ge.default.green("Successfully downloaded and updated configuration"))})};async function an(n,e,t){o.start(`Fetching remote firewall configuration${t.configVersion?` version ${t.configVersion}`:""} ...`);let i=await n.fetchConfig(t.configVersion),r=i.rules,s=i.ips??[];if(r.length>0?(o.log(ge.default.bold(`
65
65
  Remote Custom Rules to Download (${r.length}):
66
66
  `)),r.forEach(c=>o.log(` - ${c.name}${c.enabled?"":ge.default.dim(" (disabled)")}`))):o.info("No custom rules to download..."),s.length>0?(o.log(ge.default.bold(`
67
67
  Remote IP Blocking Rules to Download (${s.length}):
68
- `)),s.forEach(c=>o.log(` - ${c.ip} (${c.action})`))):o.info("No IP blocking rules to download..."),t.dryRun){o.info(ge.default.cyan("Dry run completed. No changes made."));return}if(!await C(`Do you want to download${t.configVersion?` version ${t.configVersion}`:" the latest version"} of these rules? This will overwrite your local configuration.`,{type:"confirm"})){o.info(ge.default.yellow("Download cancelled."));return}let l={...e,...i};o.start("Saving configuration..."),await z(l,t.config),o.success(ge.default.green("Successfully downloaded and updated configuration"))}var Vt={};Z(Vt,{builder:()=>dn,command:()=>ln,desc:()=>cn,handler:()=>gn});var _e=S(require("chalk")),Vi=require("fs");k();var ln="export",cn="Export firewall configuration in various formats",dn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",choices:["json","yaml","terraform","markdown"],description:"Export format",default:"json"},output:{alias:"o",type:"string",description:"Output file path"},source:{alias:"s",type:"string",choices:["local","remote"],description:"Export from local config or remote Vercel",default:"local"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},un=n=>{let{rules:e,ips:t=[],version:i,updatedAt:r}=n,s=`# Vercel Firewall Configuration Report
68
+ `)),s.forEach(c=>o.log(` - ${c.ip} (${c.action})`))):o.info("No IP blocking rules to download..."),t.dryRun){o.info(ge.default.cyan("Dry run completed. No changes made."));return}if(!await C(`Do you want to download${t.configVersion?` version ${t.configVersion}`:" the latest version"} of these rules? This will overwrite your local configuration.`,{type:"confirm"})){o.info(ge.default.yellow("Download cancelled."));return}let l={...e,...i};o.start("Saving configuration..."),await z(l,t.config),o.success(ge.default.green("Successfully downloaded and updated configuration"))}var Bt={};Z(Bt,{builder:()=>dn,command:()=>ln,desc:()=>cn,handler:()=>gn});var _e=S(require("chalk")),Vi=require("fs");k();var ln="export",cn="Export firewall configuration in various formats",dn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",choices:["json","yaml","terraform","markdown"],description:"Export format",default:"json"},output:{alias:"o",type:"string",description:"Output file path"},source:{alias:"s",type:"string",choices:["local","remote"],description:"Export from local config or remote Vercel",default:"local"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},un=n=>{let{rules:e,ips:t=[],version:i,updatedAt:r}=n,s=`# Vercel Firewall Configuration Report
69
69
 
70
70
  `;return s+=`**Version:** ${i}
71
71
  `,s+=`**Last Updated:** ${r?new Date(r).toLocaleString():"Unknown"}
@@ -115,7 +115,7 @@ Remote IP Blocking Rules to Download (${s.length}):
115
115
  `,i+=` action = "${r.action}"
116
116
  `,i+=`}
117
117
 
118
- `}),i},gn=async n=>{try{let e;if(n.source==="remote"?await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"exporting configuration"},async({client:s,provider:a})=>{o.start("Fetching remote configuration..."),e=a.name==="vercel"?await s.fetchFirewallConfig():await a.fetchConfig()}):e=await G(n.config),o.start(`Exporting configuration in ${n.format} format...`),n.format!=="json"&&Ee(e))throw new Error(`The '${n.format}' export format is currently only supported for Vercel configurations. Use --format json instead.`);let t,i;switch(n.format){case"json":t=JSON.stringify(e,null,2),i="json";break;case"yaml":t=`# Vercel Firewall Configuration
118
+ `}),i},gn=async n=>{try{let e;if(n.source==="remote"?await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"exporting configuration"},async({client:s,provider:a})=>{o.start("Fetching remote configuration..."),e=a.name==="vercel"?await s.fetchFirewallConfig():await a.fetchConfig()}):e=await G(n.config),o.start(`Exporting configuration in ${n.format} format...`),n.format!=="json"&&$e(e))throw new Error(`The '${n.format}' export format is currently only supported for Vercel configurations. Use --format json instead.`);let t,i;switch(n.format){case"json":t=JSON.stringify(e,null,2),i="json";break;case"yaml":t=`# Vercel Firewall Configuration
119
119
  `,t+=`version: ${e.version}
120
120
  `,t+=`updatedAt: "${e.updatedAt}"
121
121
  `,t+=`rules:
@@ -123,63 +123,63 @@ Remote IP Blocking Rules to Download (${s.length}):
123
123
  `,t+=` id: "${s.id}"
124
124
  `,t+=` active: ${s.active}
125
125
  `,t+=` action: "${s.action.mitigate.action}"
126
- `}),i="yaml";break;case"terraform":t=pn(e),i="tf";break;case"markdown":t=un(e),i="md";break;default:throw new Error(`Unsupported format: ${n.format}`)}let r=n.output||`firewall-export.${i}`;n.output==="-"||!n.output?o.log(t):((0,Vi.writeFileSync)(r,t,"utf8"),o.success(_e.default.green(`\u2705 Exported to ${r}`)),o.log(""),o.log(_e.default.bold("Export Summary:")),o.log(`${_e.default.dim("Format:")} ${n.format}`),o.log(`${_e.default.dim("Source:")} ${n.source}`),o.log(`${_e.default.dim("Rules:")} ${e.rules.length} custom, ${(e.ips||[]).length} IP blocking`),o.log(`${_e.default.dim("Size:")} ${(t.length/1024).toFixed(1)} KB`))}catch(e){Q(e,"exporting configuration")}};var Bt={};Z(Bt,{BASIC_TEMPLATE_RULES:()=>ji,SECURITY_FOCUSED_TEMPLATE_RULES:()=>zi,builder:()=>hn,command:()=>fn,desc:()=>mn,handler:()=>Rn});var R=S(require("chalk")),Bi=require("fs");k();ce();var ji=[{id:"rule_block_bad_bots",name:"Block Bad Bots",description:"Block known malicious bots and crawlers based on user agent patterns",conditionGroup:[{conditions:[{type:"user_agent",op:"sub",value:"bot"}]}],action:{mitigate:{action:"deny"}},active:!1},{id:"rule_block_scrapers",name:"Block Scrapers",description:"Block common scraping tools and automated requests",conditionGroup:[{conditions:[{type:"user_agent",op:"sub",value:"scraper"}]}],action:{mitigate:{action:"deny"}},active:!1}],zi=[{id:"rule_rate_limit_api",name:"Rate Limit API",description:"Rate limit API endpoints to prevent abuse and DoS attacks",conditionGroup:[{conditions:[{type:"path",op:"pre",value:"/api/"}]}],action:{mitigate:{action:"rate_limit",rateLimit:{requests:100,window:"1m"}}},active:!1},{id:"rule_block_suspicious_ips",name:"Block Suspicious IPs",description:"Block requests from a known suspicious IP range",conditionGroup:[{conditions:[{type:"ip_address",op:"eq",value:"10.0.0.0/24"}]}],action:{mitigate:{action:"deny"}},active:!1},{id:"rule_protect_admin",name:"Protect Admin Routes",description:"Add extra protection for admin and sensitive routes",conditionGroup:[{conditions:[{type:"path",op:"pre",value:"/admin"}]}],action:{mitigate:{action:"rate_limit",rateLimit:{requests:10,window:"1m"}}},active:!1}],fn="init [template]",mn="Initialize a new Doorman configuration",hn={template:{type:"string",description:"Template to use for initialization (empty, basic, or security-focused)",choices:["empty","basic","security-focused"],default:"empty"},config:{alias:"c",type:"string",description:"Path for the new config file",default:".doorman.json"},force:{alias:"f",type:"boolean",description:"Overwrite existing config file",default:!1},interactive:{alias:"i",type:"boolean",description:"Interactive setup with guided prompts",default:!0},projectId:{alias:"p",type:"string",description:"Vercel Project ID"},teamId:{alias:"t",type:"string",description:"Vercel Team ID"}},yn=()=>{o.log(""),o.log(R.default.bold.cyan("\u{1F6AA} Welcome to Doorman!")),o.log(R.default.dim(`Let's set up your firewall configuration.
126
+ `}),i="yaml";break;case"terraform":t=pn(e),i="tf";break;case"markdown":t=un(e),i="md";break;default:throw new Error(`Unsupported format: ${n.format}`)}let r=n.output||`firewall-export.${i}`;n.output==="-"||!n.output?o.log(t):((0,Vi.writeFileSync)(r,t,"utf8"),o.success(_e.default.green(`\u2705 Exported to ${r}`)),o.log(""),o.log(_e.default.bold("Export Summary:")),o.log(`${_e.default.dim("Format:")} ${n.format}`),o.log(`${_e.default.dim("Source:")} ${n.source}`),o.log(`${_e.default.dim("Rules:")} ${e.rules.length} custom, ${(e.ips||[]).length} IP blocking`),o.log(`${_e.default.dim("Size:")} ${(t.length/1024).toFixed(1)} KB`))}catch(e){Q(e,"exporting configuration")}};var jt={};Z(jt,{BASIC_TEMPLATE_RULES:()=>ji,SECURITY_FOCUSED_TEMPLATE_RULES:()=>zi,builder:()=>hn,command:()=>fn,desc:()=>mn,handler:()=>Rn});var R=S(require("chalk")),Bi=require("fs");k();ce();var ji=[{id:"rule_block_bad_bots",name:"Block Bad Bots",description:"Block known malicious bots and crawlers based on user agent patterns",conditionGroup:[{conditions:[{type:"user_agent",op:"sub",value:"bot"}]}],action:{mitigate:{action:"deny"}},active:!1},{id:"rule_block_scrapers",name:"Block Scrapers",description:"Block common scraping tools and automated requests",conditionGroup:[{conditions:[{type:"user_agent",op:"sub",value:"scraper"}]}],action:{mitigate:{action:"deny"}},active:!1}],zi=[{id:"rule_rate_limit_api",name:"Rate Limit API",description:"Rate limit API endpoints to prevent abuse and DoS attacks",conditionGroup:[{conditions:[{type:"path",op:"pre",value:"/api/"}]}],action:{mitigate:{action:"rate_limit",rateLimit:{requests:100,window:"1m"}}},active:!1},{id:"rule_block_suspicious_ips",name:"Block Suspicious IPs",description:"Block requests from a known suspicious IP range",conditionGroup:[{conditions:[{type:"ip_address",op:"eq",value:"10.0.0.0/24"}]}],action:{mitigate:{action:"deny"}},active:!1},{id:"rule_protect_admin",name:"Protect Admin Routes",description:"Add extra protection for admin and sensitive routes",conditionGroup:[{conditions:[{type:"path",op:"pre",value:"/admin"}]}],action:{mitigate:{action:"rate_limit",rateLimit:{requests:10,window:"1m"}}},active:!1}],fn="init [template]",mn="Initialize a new Doorman configuration",hn={template:{type:"string",description:"Template to use for initialization (empty, basic, or security-focused)",choices:["empty","basic","security-focused"],default:"empty"},config:{alias:"c",type:"string",description:"Path for the new config file",default:".doorman.json"},force:{alias:"f",type:"boolean",description:"Overwrite existing config file",default:!1},interactive:{alias:"i",type:"boolean",description:"Interactive setup with guided prompts",default:!0},projectId:{alias:"p",type:"string",description:"Vercel Project ID"},teamId:{alias:"t",type:"string",description:"Vercel Team ID"}},yn=()=>{o.log(""),o.log(R.default.bold.cyan("\u{1F6AA} Welcome to Doorman!")),o.log(R.default.dim(`Let's set up your firewall configuration.
127
127
  `))},wn=()=>{o.log(R.default.bold(`
128
128
  \u{1F4DA} Helpful Resources:`)),o.log(""),o.log(R.default.cyan("\u{1F517} Find your Project ID:")),o.log(R.default.dim(" https://vercel.com/dashboard \u2192 Select your project \u2192 Settings \u2192 General")),o.log(""),o.log(R.default.cyan("\u{1F517} Find your Team ID:")),o.log(R.default.dim(" https://vercel.com/dashboard \u2192 Team Settings \u2192 General")),o.log(R.default.dim(" (Leave empty if using personal account)")),o.log(""),o.log(R.default.cyan("\u{1F517} Create API Token:")),o.log(R.default.dim(" https://vercel.com/account/tokens \u2192 Create Token")),o.log(R.default.dim(" Scopes needed: Read & Write for your project")),o.log(""),o.log(R.default.cyan("\u{1F517} Documentation:")),o.log(R.default.dim(" https://doorman.griffen.codes/getting-started")),o.log("")},Cn=async n=>{let e=n.projectId,t=n.teamId;return n.interactive&&(o.log(R.default.bold("\u{1F4CB} Project Configuration")),o.log(R.default.dim(`We need your Vercel project details to configure the firewall.
129
129
  `)),e||(e=await C("Enter your Vercel Project ID:",{type:"text"})),t||await C("Are you using a Vercel team account?",{type:"confirm"})&&(t=await C("Enter your Vercel Team ID:",{type:"text"})),process.env.VERCEL_TOKEN?o.log(R.default.green("\u2705 VERCEL_TOKEN environment variable found")):(o.log(""),o.log(R.default.yellow("\u26A0\uFE0F VERCEL_TOKEN environment variable not found")),o.log(R.default.dim("You'll need to set this before running sync commands.")),await C("Would you like to see how to create an API token?",{type:"confirm"})&&(o.log(""),o.log(R.default.bold("\u{1F511} Creating a Vercel API Token:")),o.log("1. Go to https://vercel.com/account/tokens"),o.log('2. Click "Create Token"'),o.log('3. Give it a descriptive name (e.g., "Doorman Firewall")'),o.log("4. Set expiration as needed"),o.log("5. Copy the token and set it as an environment variable:"),o.log(""),o.log(R.default.cyan(' export VERCEL_TOKEN="your-token-here"')),o.log(R.default.dim(" # Add this to your ~/.bashrc, ~/.zshrc, or .env file")),o.log("")))),{projectId:e,teamId:t}},Rn=async n=>{try{let e=n.config||".doorman.json";if(n.interactive&&yn(),(0,Bi.existsSync)(e)&&!n.force&&!await C(`Config file ${e} already exists. Do you want to overwrite it?`,{type:"confirm"})){o.info(R.default.yellow("Initialization cancelled."));return}let{projectId:t,teamId:i}=await Cn(n);n.interactive&&(o.log(""),o.log(R.default.bold("\u{1F3A8} Template Selection")),o.log(R.default.dim(`Choose a starting template for your firewall configuration:
130
- `)),o.log(R.default.cyan("\u{1F4DD} empty")+R.default.dim(" - Minimal config, add your own rules")),o.log(R.default.cyan("\u{1F6E1}\uFE0F basic")+R.default.dim(" - Includes basic bot protection")),o.log(R.default.cyan("\u{1F512} security-focused")+R.default.dim(" - Comprehensive security rules")),o.log(""));let r=n.template;n.interactive&&!n.template&&(r=await C("Select a template:",{type:"select",options:["empty","basic","security-focused"],initial:"empty"})),o.start(`Creating ${r} configuration...`);let s=Le();switch(t&&(s.projectId=t),i&&(s.teamId=i),r){case"basic":s={...s,rules:ji};break;case"security-focused":s={...s,rules:zi};break;default:break}await z(s,e),o.success(R.default.green(`\u2705 Created ${e}`)),o.log(""),o.log(R.default.bold("\u{1F4CB} Configuration Summary:")),o.log(`${R.default.dim("File:")} ${e}`),o.log(`${R.default.dim("Template:")} ${r}`),o.log(`${R.default.dim("Project ID:")} ${t||R.default.yellow("Not set - add manually")}`),o.log(`${R.default.dim("Team ID:")} ${i||R.default.dim("None (personal account)")}`),o.log(`${R.default.dim("Rules:")} ${s.rules.length} (all disabled for safety)`),o.log(""),o.log(R.default.bold("\u{1F680} Next Steps:")),t||o.log(`1. ${R.default.yellow("Add your projectId")} to ${e}`),process.env.VERCEL_TOKEN||o.log(`${t?"1":"2"}. ${R.default.yellow("Set VERCEL_TOKEN")} environment variable`);let a=t?process.env.VERCEL_TOKEN?1:2:3;o.log(`${a}. ${R.default.dim("Review and enable rules in")} ${e}`),o.log(`${a+1}. ${R.default.dim("Run")} ${R.default.cyan("doorman validate")} ${R.default.dim("to check your config")}`),o.log(`${a+2}. ${R.default.dim("Run")} ${R.default.cyan("doorman status")} ${R.default.dim("to see sync status")}`),o.log(`${a+3}. ${R.default.dim("Run")} ${R.default.cyan("doorman sync")} ${R.default.dim("to deploy your rules")}`),r!=="empty"&&(o.log(""),o.log(R.default.yellow("\u26A0\uFE0F All template rules are disabled by default for safety.")),o.log(R.default.dim(" Review each rule and set active: true when ready."))),n.interactive&&(!t||!process.env.VERCEL_TOKEN)&&wn()}catch(e){Q(e,"initializing configuration")}};var jt={};Z(jt,{builder:()=>vn,command:()=>In,desc:()=>bn,handler:()=>En});var W=S(require("chalk")),Wi=require("zod");k();var In="list [configVersion]",bn="List Vercel Firewall rules, optionally for a specific configuration version",vn={configVersion:{type:"number",description:"Specific configuration version to fetch (defaults to latest)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file or VERCEL_PROJECT_ID environment variable)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file or VERCEL_TEAM_ID environment variable)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",description:"Output format (json or table)",choices:["json","table"],default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},En=async n=>{await ee({provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,optionalConfig:!0,skipValidation:!0,errorContext:"listing firewall rules"},async({client:e,provider:t,token:i,projectId:r,teamId:s})=>{if(n.configVersion!==void 0)try{Qe.parse(n.configVersion)}catch(p){throw p instanceof Wi.z.ZodError&&(o.error("Invalid configuration version number. Version must be a positive integer."),process.exit(1)),p}if(t.name!=="vercel"){await $n(t,n);return}o.start(`Fetching firewall configuration${n.configVersion?` version ${n.configVersion}`:""} ...`),o.verbose(`Token: ${i} projectId: ${r} teamId: ${s}`);let a=await e.fetchFirewallConfig(n.configVersion),l=a.rules,c=a.ips,d=new Date(a.updatedAt),u=new Intl.DateTimeFormat("en-US",{dateStyle:"medium",timeStyle:"medium"}).format(d);if(o.info(`Found ${W.default.cyan(l.length)} custom rules and ${W.default.cyan(c.length)} IP blocking rules
130
+ `)),o.log(R.default.cyan("\u{1F4DD} empty")+R.default.dim(" - Minimal config, add your own rules")),o.log(R.default.cyan("\u{1F6E1}\uFE0F basic")+R.default.dim(" - Includes basic bot protection")),o.log(R.default.cyan("\u{1F512} security-focused")+R.default.dim(" - Comprehensive security rules")),o.log(""));let r=n.template;n.interactive&&!n.template&&(r=await C("Select a template:",{type:"select",options:["empty","basic","security-focused"],initial:"empty"})),o.start(`Creating ${r} configuration...`);let s=Le();switch(t&&(s.projectId=t),i&&(s.teamId=i),r){case"basic":s={...s,rules:ji};break;case"security-focused":s={...s,rules:zi};break;default:break}await z(s,e),o.success(R.default.green(`\u2705 Created ${e}`)),o.log(""),o.log(R.default.bold("\u{1F4CB} Configuration Summary:")),o.log(`${R.default.dim("File:")} ${e}`),o.log(`${R.default.dim("Template:")} ${r}`),o.log(`${R.default.dim("Project ID:")} ${t||R.default.yellow("Not set - add manually")}`),o.log(`${R.default.dim("Team ID:")} ${i||R.default.dim("None (personal account)")}`),o.log(`${R.default.dim("Rules:")} ${s.rules.length} (all disabled for safety)`),o.log(""),o.log(R.default.bold("\u{1F680} Next Steps:")),t||o.log(`1. ${R.default.yellow("Add your projectId")} to ${e}`),process.env.VERCEL_TOKEN||o.log(`${t?"1":"2"}. ${R.default.yellow("Set VERCEL_TOKEN")} environment variable`);let a=t?process.env.VERCEL_TOKEN?1:2:3;o.log(`${a}. ${R.default.dim("Review and enable rules in")} ${e}`),o.log(`${a+1}. ${R.default.dim("Run")} ${R.default.cyan("doorman validate")} ${R.default.dim("to check your config")}`),o.log(`${a+2}. ${R.default.dim("Run")} ${R.default.cyan("doorman status")} ${R.default.dim("to see sync status")}`),o.log(`${a+3}. ${R.default.dim("Run")} ${R.default.cyan("doorman sync")} ${R.default.dim("to deploy your rules")}`),r!=="empty"&&(o.log(""),o.log(R.default.yellow("\u26A0\uFE0F All template rules are disabled by default for safety.")),o.log(R.default.dim(" Review each rule and set active: true when ready."))),n.interactive&&(!t||!process.env.VERCEL_TOKEN)&&wn()}catch(e){Q(e,"initializing configuration")}};var zt={};Z(zt,{builder:()=>vn,command:()=>In,desc:()=>bn,handler:()=>En});var W=S(require("chalk")),Wi=require("zod");k();var In="list [configVersion]",bn="List Vercel Firewall rules, optionally for a specific configuration version",vn={configVersion:{type:"number",description:"Specific configuration version to fetch (defaults to latest)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file or VERCEL_PROJECT_ID environment variable)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file or VERCEL_TEAM_ID environment variable)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",description:"Output format (json or table)",choices:["json","table"],default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},En=async n=>{await ee({provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,optionalConfig:!0,skipValidation:!0,errorContext:"listing firewall rules"},async({client:e,provider:t,token:i,projectId:r,teamId:s})=>{if(n.configVersion!==void 0)try{Qe.parse(n.configVersion)}catch(p){throw p instanceof Wi.z.ZodError&&(o.error("Invalid configuration version number. Version must be a positive integer."),process.exit(1)),p}if(t.name!=="vercel"){await $n(t,n);return}o.start(`Fetching firewall configuration${n.configVersion?` version ${n.configVersion}`:""} ...`),o.verbose(`Token: ${i} projectId: ${r} teamId: ${s}`);let a=await e.fetchFirewallConfig(n.configVersion),l=a.rules,c=a.ips,d=new Date(a.updatedAt),u=new Intl.DateTimeFormat("en-US",{dateStyle:"medium",timeStyle:"medium"}).format(d);if(o.info(`Found ${W.default.cyan(l.length)} custom rules and ${W.default.cyan(c.length)} IP blocking rules
131
131
  `+W.default.dim(`Version: ${W.default.yellow(a.version)} \u2022 Last Updated: ${W.default.yellow(u)}`)),n.format==="json")o.info(JSON.stringify({version:a.version,updatedAt:a.updatedAt,lastUpdated:u,rules:l,ips:c},null,2));else{if(l.length===0&&c.length===0){o.info(W.default.yellow("No rules found"));return}l.length>0?(o.log(W.default.bold.underline(`
132
132
  Custom Rules:`),`
133
- `),be(l,{showStatus:!1})):o.info(W.default.cyan("No custom rules found")),c.length>0?(o.log(W.default.bold.underline(`
133
+ `),ve(l,{showStatus:!1})):o.info(W.default.cyan("No custom rules found")),c.length>0?(o.log(W.default.bold.underline(`
134
134
  IP Blocking Rules:`),`
135
- `),ve(c,{showStatus:!1})):o.info(W.default.cyan("No IP blocking rules found"))}})};async function $n(n,e){o.start(`Fetching firewall configuration${e.configVersion?` version ${e.configVersion}`:""} ...`);let t=await n.fetchConfig(e.configVersion),i=t.rules,r=t.ips??[],s=t.metadata?.version??t.version,a=t.metadata?.updatedAt;if(o.info(`Found ${W.default.cyan(i.length)} custom rules and ${W.default.cyan(r.length)} IP blocking rules
135
+ `),Ee(c,{showStatus:!1})):o.info(W.default.cyan("No IP blocking rules found"))}})};async function $n(n,e){o.start(`Fetching firewall configuration${e.configVersion?` version ${e.configVersion}`:""} ...`);let t=await n.fetchConfig(e.configVersion),i=t.rules,r=t.ips??[],s=t.metadata?.version??t.version,a=t.metadata?.updatedAt;if(o.info(`Found ${W.default.cyan(i.length)} custom rules and ${W.default.cyan(r.length)} IP blocking rules
136
136
  `+W.default.dim(`Version: ${W.default.yellow(s??"unknown")}${a?` \u2022 Last Updated: ${W.default.yellow(a)}`:""}`)),e.format==="json"){o.info(JSON.stringify({version:s,updatedAt:a,rules:i,ips:r},null,2));return}if(i.length===0&&r.length===0){o.info(W.default.yellow("No rules found"));return}i.length>0?(o.log(W.default.bold.underline(`
137
137
  Custom Rules:`),`
138
138
  `),i.forEach(l=>o.log(` - ${l.name}${l.enabled?"":W.default.dim(" (disabled)")}`))):o.info(W.default.cyan("No custom rules found")),r.length>0?(o.log(W.default.bold.underline(`
139
139
  IP Blocking Rules:`),`
140
- `),r.forEach(l=>o.log(` - ${l.ip} (${l.action})`))):o.info(W.default.cyan("No IP blocking rules found"))}var Wt={};Z(Wt,{aliases:()=>Tn,builder:()=>kn,command:()=>An,desc:()=>Pn,findFuzzyMatches:()=>Zi,handler:()=>Sn,levenshtein:()=>Hi});var N=S(require("chalk")),Gi=require("consola");k();ce();var An="remove [type]",Tn=["rm","delete"],Pn="Remove a firewall rule or IP entry from your configuration",kn={type:{type:"string",description:'What to remove: "rule" (default) or "ip"',default:"rule",choices:["rule","ip"]},interactive:{alias:"i",type:"boolean",description:"Select rules to remove from a list",default:!1},name:{alias:"n",type:"string",description:"Remove rule by name (exact match)"},id:{type:"array",string:!0,description:"Remove rule by ID (supports multiple)"},ip:{type:"string",description:"Remove IP rule by IP address"},disabled:{type:"boolean",description:"Remove all disabled (active: false) rules",default:!1},all:{type:"boolean",description:"Remove all rules (requires confirmation)",default:!1},force:{alias:"f",type:"boolean",description:"Skip confirmation prompt",default:!1},config:{alias:"c",type:"string",description:"Config file path"},dryRun:{alias:"d",type:"boolean",description:"Show what would be removed without writing",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}};function Hi(n,e){let t=n.length,i=e.length;if(t===0)return i;if(i===0)return t;let r=Array.from({length:t+1},(a,l)=>l),s=new Array(t+1).fill(0);for(let a=1;a<=i;a++){s[0]=a;for(let l=1;l<=t;l++)e.charAt(a-1)===n.charAt(l-1)?s[l]=r[l-1]:s[l]=Math.min(r[l-1]+1,s[l-1]+1,r[l]+1);for(let l=0;l<=t;l++)r[l]=s[l]}return r[t]}function Zi(n,e,t=3){let i=n.toLowerCase(),r=e.filter(l=>l.name.toLowerCase().includes(i)||i.includes(l.name.toLowerCase()));if(r.length>0)return r.slice(0,t);let s=e.map(l=>({rule:l,distance:Hi(i,l.name.toLowerCase())})).sort((l,c)=>l.distance-c.distance),a=Math.max(n.length*.6,3);return s.filter(l=>l.distance<=a).slice(0,t).map(l=>l.rule)}function zt(n){for(let e of n){let t=e.id?` (${e.id})`:"",i=e.active?"":N.default.dim(" [disabled]"),r=e.action.mitigate.action;o.log(N.default.dim(` - ${e.name}${t} \u2014 ${r}${i}`))}}function Mi(n){for(let e of n){let t=e.notes?` \u2014 ${e.notes}`:"";o.log(N.default.dim(` - ${e.ip}${t}`))}}async function qi(n,e,t,i="rule"){if(e)return!0;if(t)return await C(N.default.red(`\u26A0\uFE0F Are you sure you want to remove ALL ${i}s? Type "YES" to confirm:`),{type:"text"})==="YES";let r=n===1?`Confirm removal of 1 ${i}?`:`Confirm removal of ${n} ${i}s?`;return await C(r,{type:"confirm",initial:!1})}var Sn=async n=>{try{n.debug&&(o.level=Gi.LogLevels.debug),o.debug("Remove command arguments:",n);let e=n.type||"rule";o.start("Loading configuration...");let t=await G(n.config);if(e==="ip"){let i=t.ips||[];if(i.length===0){o.info("No IP rules in configuration.");return}let r=[];if(n.interactive){let l=i.map(d=>{let u=d.notes?` \u2014 ${d.notes}`:"";return`${d.ip}${u}`}),c=await C("Select IP rules to remove:",{type:"multiselect",options:l});r=i.filter(d=>{let u=`${d.ip}${d.notes?` \u2014 ${d.notes}`:""}`;return c.includes(u)})}else n.ip?(r=i.filter(l=>l.ip===n.ip),r.length===0&&(o.error(N.default.red(`No IP rule found matching "${n.ip}"`)),i.length>0&&(o.log(N.default.dim(`
141
- Current IP rules:`)),Mi(i)),process.exit(1))):n.all?r=[...i]:(o.error("Specify --ip, --interactive, or --all to select IP rules to remove."),process.exit(1));if(r.length===0){o.info("No IP rules selected for removal.");return}if(o.log(N.default.cyan(`
142
- ${n.dryRun?"Would remove":"Removing"} ${r.length} IP rule(s):`)),Mi(r),n.dryRun){o.log(N.default.dim(`
143
- Dry run \u2014 no changes made.`));return}if(!await qi(r.length,n.force||!1,n.all||!1,"IP rule")){o.info("Cancelled.");return}let s=new Set(r.map(l=>l.ip)),a={...t,ips:i.filter(l=>!s.has(l.ip))};await z(a,n.config),o.success(N.default.green(`\u2714 Removed ${r.length} IP rule(s) from configuration`))}else{let i=t.rules||[];if(i.length===0){o.info("No rules in configuration.");return}let r=[];if(n.interactive){let l=i.map((d,u)=>{let p=d.id?` (${d.id})`:"",g=d.active?"":" [disabled]",h=d.action.mitigate.action;return`[${u}] ${d.name}${p} \u2014 ${h}${g}`}),c=await C("Select rules to remove:",{type:"multiselect",options:l});r=i.filter((d,u)=>c.some(p=>p.startsWith(`[${u}]`)))}else if(n.name){if(r=i.filter(l=>l.name===n.name),r.length>1&&(o.warn(N.default.yellow(`
144
- \u26A0\uFE0F Found ${r.length} rules named "${n.name}". All will be removed.`)),zt(r)),r.length===0){o.error(N.default.red(`No rule found with exact name "${n.name}"`));let l=Zi(n.name,i);l.length>0&&(o.log(N.default.yellow(`
145
- Did you mean one of these?`)),l.forEach((c,d)=>{let u=c.id?` (${c.id})`:"";o.log(N.default.dim(` ${d+1}. ${c.name}${u}`))}),o.log(N.default.dim(`
146
- Use --id for exact matching, or --interactive to select from a list.`))),process.exit(1)}}else if(n.id&&n.id.length>0){let l=new Set(n.id);r=i.filter(u=>u.id&&l.has(u.id));let c=new Set(r.map(u=>u.id)),d=n.id.filter(u=>!c.has(u));d.length>0&&o.warn(N.default.yellow(`No rules found for IDs: ${d.join(", ")}`)),r.length===0&&(o.error(N.default.red("No matching rules found for the given IDs.")),process.exit(1))}else if(n.disabled){if(r=i.filter(l=>!l.active),r.length===0){o.info("No disabled rules found.");return}}else n.all?r=[...i]:(o.error("Specify --name, --id, --disabled, --all, or --interactive to select rules to remove."),process.exit(1));if(r.length===0){o.info("No rules selected for removal.");return}if(r.length>=5&&!n.dryRun&&!n.force&&o.log(N.default.yellow(`
147
- \u{1F4A1} Tip: Run ${N.default.cyan("doorman backup")} before bulk removal operations.`)),o.log(N.default.cyan(`
148
- ${n.dryRun?"Would remove":"Removing"} ${r.length} rule(s):`)),zt(r),n.dryRun){o.log(N.default.dim(`
149
- Dry run \u2014 no changes made.`));return}if(!await qi(r.length,n.force||!1,n.all||!1)){o.info("Cancelled.");return}let s=new Set(r),a={...t,rules:i.filter(l=>!s.has(l))};a.rules.length===0&&o.warn(N.default.yellow("\u26A0\uFE0F Configuration will have no rules after removal.")),await z(a,n.config),o.success(N.default.green(`\u2714 Removed ${r.length} rule(s) from configuration`)),zt(r)}o.log(""),o.log(N.default.dim(`Run ${N.default.cyan("doorman sync")} to deploy these changes.`))}catch(e){Q(e,"removing rule")}};var Mt={};Z(Mt,{builder:()=>Dn,command:()=>_n,desc:()=>Ln,handler:()=>Un});var D=S(require("chalk"));k();var _n="setup",Ln="Show setup instructions and helpful links for Doorman",Dn={},Un=async n=>{o.log(""),o.log(D.default.bold.cyan("\u{1F6AA} Doorman Setup Guide")),o.log(D.default.dim(`Everything you need to get started with firewall management
140
+ `),r.forEach(l=>o.log(` - ${l.ip} (${l.action})`))):o.info(W.default.cyan("No IP blocking rules found"))}var Mt={};Z(Mt,{aliases:()=>Tn,builder:()=>kn,command:()=>An,desc:()=>Pn,findFuzzyMatches:()=>Zi,handler:()=>Sn,levenshtein:()=>Hi});var F=S(require("chalk")),Gi=require("consola");k();ce();var An="remove [type]",Tn=["rm","delete"],Pn="Remove a firewall rule or IP entry from your configuration",kn={type:{type:"string",description:'What to remove: "rule" (default) or "ip"',default:"rule",choices:["rule","ip"]},interactive:{alias:"i",type:"boolean",description:"Select rules to remove from a list",default:!1},name:{alias:"n",type:"string",description:"Remove rule by name (exact match)"},id:{type:"array",string:!0,description:"Remove rule by ID (supports multiple)"},ip:{type:"string",description:"Remove IP rule by IP address"},disabled:{type:"boolean",description:"Remove all disabled (active: false) rules",default:!1},all:{type:"boolean",description:"Remove all rules (requires confirmation)",default:!1},force:{alias:"f",type:"boolean",description:"Skip confirmation prompt",default:!1},config:{alias:"c",type:"string",description:"Config file path"},dryRun:{alias:"d",type:"boolean",description:"Show what would be removed without writing",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}};function Hi(n,e){let t=n.length,i=e.length;if(t===0)return i;if(i===0)return t;let r=Array.from({length:t+1},(a,l)=>l),s=new Array(t+1).fill(0);for(let a=1;a<=i;a++){s[0]=a;for(let l=1;l<=t;l++)e.charAt(a-1)===n.charAt(l-1)?s[l]=r[l-1]:s[l]=Math.min(r[l-1]+1,s[l-1]+1,r[l]+1);for(let l=0;l<=t;l++)r[l]=s[l]}return r[t]}function Zi(n,e,t=3){let i=n.toLowerCase(),r=e.filter(l=>l.name.toLowerCase().includes(i)||i.includes(l.name.toLowerCase()));if(r.length>0)return r.slice(0,t);let s=e.map(l=>({rule:l,distance:Hi(i,l.name.toLowerCase())})).sort((l,c)=>l.distance-c.distance),a=Math.max(n.length*.6,3);return s.filter(l=>l.distance<=a).slice(0,t).map(l=>l.rule)}function Wt(n){for(let e of n){let t=e.id?` (${e.id})`:"",i=e.active?"":F.default.dim(" [disabled]"),r=e.action.mitigate.action;o.log(F.default.dim(` - ${e.name}${t} \u2014 ${r}${i}`))}}function Mi(n){for(let e of n){let t=e.notes?` \u2014 ${e.notes}`:"";o.log(F.default.dim(` - ${e.ip}${t}`))}}async function qi(n,e,t,i="rule"){if(e)return!0;if(t)return await C(F.default.red(`\u26A0\uFE0F Are you sure you want to remove ALL ${i}s? Type "YES" to confirm:`),{type:"text"})==="YES";let r=n===1?`Confirm removal of 1 ${i}?`:`Confirm removal of ${n} ${i}s?`;return await C(r,{type:"confirm",initial:!1})}var Sn=async n=>{try{n.debug&&(o.level=Gi.LogLevels.debug),o.debug("Remove command arguments:",n);let e=n.type||"rule";o.start("Loading configuration...");let t=await G(n.config);if(e==="ip"){let i=t.ips||[];if(i.length===0){o.info("No IP rules in configuration.");return}let r=[];if(n.interactive){let l=i.map(d=>{let u=d.notes?` \u2014 ${d.notes}`:"";return`${d.ip}${u}`}),c=await C("Select IP rules to remove:",{type:"multiselect",options:l});r=i.filter(d=>{let u=`${d.ip}${d.notes?` \u2014 ${d.notes}`:""}`;return c.includes(u)})}else n.ip?(r=i.filter(l=>l.ip===n.ip),r.length===0&&(o.error(F.default.red(`No IP rule found matching "${n.ip}"`)),i.length>0&&(o.log(F.default.dim(`
141
+ Current IP rules:`)),Mi(i)),process.exit(1))):n.all?r=[...i]:(o.error("Specify --ip, --interactive, or --all to select IP rules to remove."),process.exit(1));if(r.length===0){o.info("No IP rules selected for removal.");return}if(o.log(F.default.cyan(`
142
+ ${n.dryRun?"Would remove":"Removing"} ${r.length} IP rule(s):`)),Mi(r),n.dryRun){o.log(F.default.dim(`
143
+ Dry run \u2014 no changes made.`));return}if(!await qi(r.length,n.force||!1,n.all||!1,"IP rule")){o.info("Cancelled.");return}let s=new Set(r.map(l=>l.ip)),a={...t,ips:i.filter(l=>!s.has(l.ip))};await z(a,n.config),o.success(F.default.green(`\u2714 Removed ${r.length} IP rule(s) from configuration`))}else{let i=t.rules||[];if(i.length===0){o.info("No rules in configuration.");return}let r=[];if(n.interactive){let l=i.map((d,u)=>{let p=d.id?` (${d.id})`:"",f=d.active?"":" [disabled]",h=d.action.mitigate.action;return`[${u}] ${d.name}${p} \u2014 ${h}${f}`}),c=await C("Select rules to remove:",{type:"multiselect",options:l});r=i.filter((d,u)=>c.some(p=>p.startsWith(`[${u}]`)))}else if(n.name){if(r=i.filter(l=>l.name===n.name),r.length>1&&(o.warn(F.default.yellow(`
144
+ \u26A0\uFE0F Found ${r.length} rules named "${n.name}". All will be removed.`)),Wt(r)),r.length===0){o.error(F.default.red(`No rule found with exact name "${n.name}"`));let l=Zi(n.name,i);l.length>0&&(o.log(F.default.yellow(`
145
+ Did you mean one of these?`)),l.forEach((c,d)=>{let u=c.id?` (${c.id})`:"";o.log(F.default.dim(` ${d+1}. ${c.name}${u}`))}),o.log(F.default.dim(`
146
+ Use --id for exact matching, or --interactive to select from a list.`))),process.exit(1)}}else if(n.id&&n.id.length>0){let l=new Set(n.id);r=i.filter(u=>u.id&&l.has(u.id));let c=new Set(r.map(u=>u.id)),d=n.id.filter(u=>!c.has(u));d.length>0&&o.warn(F.default.yellow(`No rules found for IDs: ${d.join(", ")}`)),r.length===0&&(o.error(F.default.red("No matching rules found for the given IDs.")),process.exit(1))}else if(n.disabled){if(r=i.filter(l=>!l.active),r.length===0){o.info("No disabled rules found.");return}}else n.all?r=[...i]:(o.error("Specify --name, --id, --disabled, --all, or --interactive to select rules to remove."),process.exit(1));if(r.length===0){o.info("No rules selected for removal.");return}if(r.length>=5&&!n.dryRun&&!n.force&&o.log(F.default.yellow(`
147
+ \u{1F4A1} Tip: Run ${F.default.cyan("doorman backup")} before bulk removal operations.`)),o.log(F.default.cyan(`
148
+ ${n.dryRun?"Would remove":"Removing"} ${r.length} rule(s):`)),Wt(r),n.dryRun){o.log(F.default.dim(`
149
+ Dry run \u2014 no changes made.`));return}if(!await qi(r.length,n.force||!1,n.all||!1)){o.info("Cancelled.");return}let s=new Set(r),a={...t,rules:i.filter(l=>!s.has(l))};a.rules.length===0&&o.warn(F.default.yellow("\u26A0\uFE0F Configuration will have no rules after removal.")),await z(a,n.config),o.success(F.default.green(`\u2714 Removed ${r.length} rule(s) from configuration`)),Wt(r)}o.log(""),o.log(F.default.dim(`Run ${F.default.cyan("doorman sync")} to deploy these changes.`))}catch(e){Q(e,"removing rule")}};var qt={};Z(qt,{builder:()=>Dn,command:()=>_n,desc:()=>Ln,handler:()=>Un});var D=S(require("chalk"));k();var _n="setup",Ln="Show setup instructions and helpful links for Doorman",Dn={},Un=async n=>{o.log(""),o.log(D.default.bold.cyan("\u{1F6AA} Doorman Setup Guide")),o.log(D.default.dim(`Everything you need to get started with firewall management
150
150
  `)),o.log(D.default.bold("\u{1F4CB} Prerequisites")),o.log("\u2022 Vercel account with a deployed project"),o.log("\u2022 Project with Pro plan or higher (firewall feature requirement)"),o.log(`\u2022 Admin access to the project/team
151
- `),o.log(D.default.bold("\u{1F50D} Finding Your Project Information")),o.log(""),o.log(D.default.cyan("1. Project ID:")),o.log(" \u2022 Go to https://vercel.com/dashboard"),o.log(" \u2022 Select your project"),o.log(" \u2022 Navigate to Settings \u2192 General"),o.log(" \u2022 Copy the Project ID from the top of the page"),o.log(""),o.log(D.default.cyan("2. Team ID (if using team account):")),o.log(" \u2022 Go to https://vercel.com/dashboard"),o.log(" \u2022 Click on your team name in the top-left"),o.log(" \u2022 Go to Team Settings \u2192 General"),o.log(" \u2022 Copy the Team ID"),o.log(" \u2022 Leave empty if using personal account"),o.log(""),o.log(D.default.bold("\u{1F511} Creating API Token")),o.log(""),o.log(D.default.cyan("Steps:")),o.log(" 1. Visit https://vercel.com/account/tokens"),o.log(' 2. Click "Create Token"'),o.log(' 3. Name: "Doorman Firewall Management"'),o.log(" 4. Expiration: Set as needed (90 days recommended)"),o.log(" 5. Scope: Select your project or team"),o.log(" 6. Copy the generated token"),o.log(""),o.log(D.default.cyan("Required Permissions:")),o.log(" \u2022 Read access to project settings"),o.log(" \u2022 Write access to firewall configuration"),o.log(""),o.log(D.default.bold("\u{1F30D} Environment Setup")),o.log(""),o.log(D.default.cyan("Set your API token as environment variable:")),o.log(""),o.log(D.default.green("# For current session:")),o.log('export VERCEL_TOKEN="your-token-here"'),o.log(""),o.log(D.default.green("# For permanent setup (add to ~/.bashrc or ~/.zshrc):")),o.log(`echo 'export VERCEL_TOKEN="your-token-here"' >> ~/.bashrc`),o.log(""),o.log(D.default.green("# Or create a .env file in your project:")),o.log('echo "VERCEL_TOKEN=your-token-here" > .env'),o.log(""),o.log(D.default.bold("\u{1F680} Quick Start Commands")),o.log(""),o.log(D.default.cyan("1. Initialize configuration:")),o.log(" doorman init --interactive"),o.log(""),o.log(D.default.cyan("2. Validate setup:")),o.log(" doorman validate"),o.log(""),o.log(D.default.cyan("3. Check current status:")),o.log(" doorman status"),o.log(""),o.log(D.default.cyan("4. View existing rules:")),o.log(" doorman list"),o.log(""),o.log(D.default.cyan("5. Sync your configuration:")),o.log(" doorman sync"),o.log(""),o.log(D.default.bold("\u{1F527} Common Issues")),o.log(""),o.log(D.default.yellow('\u274C "Project not found" error:')),o.log(" \u2022 Double-check your Project ID"),o.log(" \u2022 Ensure your token has access to the project"),o.log(" \u2022 Verify the project has Pro plan or higher"),o.log(""),o.log(D.default.yellow('\u274C "Unauthorized" error:')),o.log(" \u2022 Check your VERCEL_TOKEN is set correctly"),o.log(" \u2022 Ensure token hasn't expired"),o.log(" \u2022 Verify token has firewall permissions"),o.log(""),o.log(D.default.yellow('\u274C "Firewall not available" error:')),o.log(" \u2022 Upgrade to Pro plan or higher"),o.log(" \u2022 Contact Vercel support if issue persists"),o.log(""),o.log(D.default.bold("\u{1F4DA} Additional Resources")),o.log(""),o.log(D.default.cyan("Documentation:")),o.log(" \u2022 Doorman Docs: https://doorman.griffen.codes"),o.log(" \u2022 Vercel Firewall: https://vercel.com/docs/security/vercel-firewall"),o.log(" \u2022 API Reference: https://vercel.com/docs/rest-api/endpoints/firewall"),o.log(""),o.log(D.default.cyan("Support:")),o.log(" \u2022 GitHub Issues: https://github.com/gfargo/doorman/issues"),o.log(" \u2022 Vercel Support: https://vercel.com/help"),o.log(""),o.log(D.default.bold("\u{1F512} Security Best Practices")),o.log(""),o.log("\u2022 Store API tokens securely (use environment variables)"),o.log("\u2022 Set token expiration dates"),o.log("\u2022 Use principle of least privilege for token scopes"),o.log("\u2022 Regularly rotate API tokens"),o.log("\u2022 Test rules in staging before production"),o.log("\u2022 Keep backups of working configurations"),o.log(""),o.log(D.default.bold.green("\u2705 Ready to get started?")),o.log(D.default.dim("Run: ")+D.default.cyan("doorman init --interactive")),o.log("")};var qt={};Z(qt,{builder:()=>Nn,command:()=>xn,desc:()=>On,handler:()=>Fn});var A=S(require("chalk"));k();var Ie=S(require("chalk")),Je=class{static check(e){let t=[],i=[],r=100;return this.checkRuleNaming(e.rules,t),this.checkRuleComplexity(e.rules,t),this.checkSecurityBestPractices(e,t,i),this.checkPerformanceImpact(e,t,i),this.checkMaintainability(e,t,i),t.forEach(s=>{switch(s.severity){case"error":r-=20;break;case"warning":r-=10;break;case"info":r-=5;break}}),{score:Math.max(0,r),issues:t,recommendations:i}}static checkRuleNaming(e,t){e.forEach(i=>{i.id&&!i.id.match(/^rule_[a-z0-9_]+$/)&&t.push({severity:"warning",message:"Rule ID should follow snake_case format: rule_*",rule:i.name,suggestion:`Consider renaming to: rule_${i.name.toLowerCase().replace(/\s+/g,"_")}`}),i.name.length<5&&t.push({severity:"info",message:"Rule name is very short, consider making it more descriptive",rule:i.name}),(!i.description||i.description.length<10)&&t.push({severity:"info",message:"Rule lacks a detailed description",rule:i.name,suggestion:"Add a clear description explaining what this rule does and why"})})}static checkRuleComplexity(e,t){e.forEach(i=>{i.conditionGroup.reduce((s,a)=>s+a.conditions.length,0)>10&&t.push({severity:"warning",message:"Rule has many conditions, consider splitting into multiple rules",rule:i.name,suggestion:"Complex rules can be harder to maintain and debug"}),i.conditionGroup.length>5&&t.push({severity:"warning",message:"Rule has many condition groups, consider simplifying",rule:i.name})})}static checkSecurityBestPractices(e,t,i){let{rules:r,ips:s}=e;r.some(c=>c.action.mitigate.action==="rate_limit")||i.push("Consider adding rate limiting rules for API endpoints"),r.some(c=>c.conditionGroup.some(d=>d.conditions.some(u=>u.type==="user_agent"&&typeof u.value=="string"&&(u.value.toLowerCase().includes("bot")||u.value.toLowerCase().includes("crawler")))))||i.push("Consider adding bot protection rules"),r.forEach(c=>{c.conditionGroup.some(u=>u.conditions.some(p=>p.type==="path"&&p.value==="/"))&&c.action.mitigate.action==="deny"&&t.push({severity:"error",message:"Rule blocks root path - this could block all traffic",rule:c.name,suggestion:"Be more specific with path conditions"})}),s&&s.length>100&&t.push({severity:"warning",message:"Large number of IP blocking rules may impact performance",suggestion:"Consider using CIDR ranges or external IP lists"})}static checkPerformanceImpact(e,t,i){let{rules:r}=e;r.forEach(a=>{a.conditionGroup.some(c=>c.conditions.some(d=>d.op==="re"))&&t.push({severity:"info",message:"Rule uses regex matching, which may impact performance",rule:a.name,suggestion:"Consider using simpler string operations when possible"})}),r.filter(a=>a.active).length>50&&t.push({severity:"warning",message:"Large number of active rules may impact performance",suggestion:"Consider consolidating similar rules or disabling unused ones"})}static checkMaintainability(e,t,i){let{rules:r}=e;r.filter(c=>!c.active).length>r.length*.3&&i.push("Consider removing disabled rules to reduce config complexity");let a=r.map(c=>c.name);a.filter((c,d)=>a.indexOf(c)!==d).forEach(c=>{t.push({severity:"error",message:"Duplicate rule name found",rule:c,suggestion:"Rule names must be unique"})}),e.version||t.push({severity:"warning",message:"Configuration lacks version information",suggestion:"Version tracking helps with change management"})}static formatHealthReport(e){let t="",i=e.score>=80?Ie.default.green:e.score>=60?Ie.default.yellow:Ie.default.red;return t+=Ie.default.bold(`
151
+ `),o.log(D.default.bold("\u{1F50D} Finding Your Project Information")),o.log(""),o.log(D.default.cyan("1. Project ID:")),o.log(" \u2022 Go to https://vercel.com/dashboard"),o.log(" \u2022 Select your project"),o.log(" \u2022 Navigate to Settings \u2192 General"),o.log(" \u2022 Copy the Project ID from the top of the page"),o.log(""),o.log(D.default.cyan("2. Team ID (if using team account):")),o.log(" \u2022 Go to https://vercel.com/dashboard"),o.log(" \u2022 Click on your team name in the top-left"),o.log(" \u2022 Go to Team Settings \u2192 General"),o.log(" \u2022 Copy the Team ID"),o.log(" \u2022 Leave empty if using personal account"),o.log(""),o.log(D.default.bold("\u{1F511} Creating API Token")),o.log(""),o.log(D.default.cyan("Steps:")),o.log(" 1. Visit https://vercel.com/account/tokens"),o.log(' 2. Click "Create Token"'),o.log(' 3. Name: "Doorman Firewall Management"'),o.log(" 4. Expiration: Set as needed (90 days recommended)"),o.log(" 5. Scope: Select your project or team"),o.log(" 6. Copy the generated token"),o.log(""),o.log(D.default.cyan("Required Permissions:")),o.log(" \u2022 Read access to project settings"),o.log(" \u2022 Write access to firewall configuration"),o.log(""),o.log(D.default.bold("\u{1F30D} Environment Setup")),o.log(""),o.log(D.default.cyan("Set your API token as environment variable:")),o.log(""),o.log(D.default.green("# For current session:")),o.log('export VERCEL_TOKEN="your-token-here"'),o.log(""),o.log(D.default.green("# For permanent setup (add to ~/.bashrc or ~/.zshrc):")),o.log(`echo 'export VERCEL_TOKEN="your-token-here"' >> ~/.bashrc`),o.log(""),o.log(D.default.green("# Or create a .env file in your project:")),o.log('echo "VERCEL_TOKEN=your-token-here" > .env'),o.log(""),o.log(D.default.bold("\u{1F680} Quick Start Commands")),o.log(""),o.log(D.default.cyan("1. Initialize configuration:")),o.log(" doorman init --interactive"),o.log(""),o.log(D.default.cyan("2. Validate setup:")),o.log(" doorman validate"),o.log(""),o.log(D.default.cyan("3. Check current status:")),o.log(" doorman status"),o.log(""),o.log(D.default.cyan("4. View existing rules:")),o.log(" doorman list"),o.log(""),o.log(D.default.cyan("5. Sync your configuration:")),o.log(" doorman sync"),o.log(""),o.log(D.default.bold("\u{1F527} Common Issues")),o.log(""),o.log(D.default.yellow('\u274C "Project not found" error:')),o.log(" \u2022 Double-check your Project ID"),o.log(" \u2022 Ensure your token has access to the project"),o.log(" \u2022 Verify the project has Pro plan or higher"),o.log(""),o.log(D.default.yellow('\u274C "Unauthorized" error:')),o.log(" \u2022 Check your VERCEL_TOKEN is set correctly"),o.log(" \u2022 Ensure token hasn't expired"),o.log(" \u2022 Verify token has firewall permissions"),o.log(""),o.log(D.default.yellow('\u274C "Firewall not available" error:')),o.log(" \u2022 Upgrade to Pro plan or higher"),o.log(" \u2022 Contact Vercel support if issue persists"),o.log(""),o.log(D.default.bold("\u{1F4DA} Additional Resources")),o.log(""),o.log(D.default.cyan("Documentation:")),o.log(" \u2022 Doorman Docs: https://doorman.griffen.codes"),o.log(" \u2022 Vercel Firewall: https://vercel.com/docs/security/vercel-firewall"),o.log(" \u2022 API Reference: https://vercel.com/docs/rest-api/endpoints/firewall"),o.log(""),o.log(D.default.cyan("Support:")),o.log(" \u2022 GitHub Issues: https://github.com/gfargo/doorman/issues"),o.log(" \u2022 Vercel Support: https://vercel.com/help"),o.log(""),o.log(D.default.bold("\u{1F512} Security Best Practices")),o.log(""),o.log("\u2022 Store API tokens securely (use environment variables)"),o.log("\u2022 Set token expiration dates"),o.log("\u2022 Use principle of least privilege for token scopes"),o.log("\u2022 Regularly rotate API tokens"),o.log("\u2022 Test rules in staging before production"),o.log("\u2022 Keep backups of working configurations"),o.log(""),o.log(D.default.bold.green("\u2705 Ready to get started?")),o.log(D.default.dim("Run: ")+D.default.cyan("doorman init --interactive")),o.log("")};var Gt={};Z(Gt,{builder:()=>Fn,command:()=>xn,desc:()=>On,handler:()=>Nn});var T=S(require("chalk"));k();var be=S(require("chalk")),Je=class{static check(e){let t=[],i=[],r=100;return this.checkRuleNaming(e.rules,t),this.checkRuleComplexity(e.rules,t),this.checkSecurityBestPractices(e,t,i),this.checkPerformanceImpact(e,t,i),this.checkMaintainability(e,t,i),t.forEach(s=>{switch(s.severity){case"error":r-=20;break;case"warning":r-=10;break;case"info":r-=5;break}}),{score:Math.max(0,r),issues:t,recommendations:i}}static checkRuleNaming(e,t){e.forEach(i=>{i.id&&!i.id.match(/^rule_[a-z0-9_]+$/)&&t.push({severity:"warning",message:"Rule ID should follow snake_case format: rule_*",rule:i.name,suggestion:`Consider renaming to: rule_${i.name.toLowerCase().replace(/\s+/g,"_")}`}),i.name.length<5&&t.push({severity:"info",message:"Rule name is very short, consider making it more descriptive",rule:i.name}),(!i.description||i.description.length<10)&&t.push({severity:"info",message:"Rule lacks a detailed description",rule:i.name,suggestion:"Add a clear description explaining what this rule does and why"})})}static checkRuleComplexity(e,t){e.forEach(i=>{i.conditionGroup.reduce((s,a)=>s+a.conditions.length,0)>10&&t.push({severity:"warning",message:"Rule has many conditions, consider splitting into multiple rules",rule:i.name,suggestion:"Complex rules can be harder to maintain and debug"}),i.conditionGroup.length>5&&t.push({severity:"warning",message:"Rule has many condition groups, consider simplifying",rule:i.name})})}static checkSecurityBestPractices(e,t,i){let{rules:r,ips:s}=e;r.some(c=>c.action.mitigate.action==="rate_limit")||i.push("Consider adding rate limiting rules for API endpoints"),r.some(c=>c.conditionGroup.some(d=>d.conditions.some(u=>u.type==="user_agent"&&typeof u.value=="string"&&(u.value.toLowerCase().includes("bot")||u.value.toLowerCase().includes("crawler")))))||i.push("Consider adding bot protection rules"),r.forEach(c=>{c.conditionGroup.some(u=>u.conditions.some(p=>p.type==="path"&&p.value==="/"))&&c.action.mitigate.action==="deny"&&t.push({severity:"error",message:"Rule blocks root path - this could block all traffic",rule:c.name,suggestion:"Be more specific with path conditions"})}),s&&s.length>100&&t.push({severity:"warning",message:"Large number of IP blocking rules may impact performance",suggestion:"Consider using CIDR ranges or external IP lists"})}static checkPerformanceImpact(e,t,i){let{rules:r}=e;r.forEach(a=>{a.conditionGroup.some(c=>c.conditions.some(d=>d.op==="re"))&&t.push({severity:"info",message:"Rule uses regex matching, which may impact performance",rule:a.name,suggestion:"Consider using simpler string operations when possible"})}),r.filter(a=>a.active).length>50&&t.push({severity:"warning",message:"Large number of active rules may impact performance",suggestion:"Consider consolidating similar rules or disabling unused ones"})}static checkMaintainability(e,t,i){let{rules:r}=e;r.filter(c=>!c.active).length>r.length*.3&&i.push("Consider removing disabled rules to reduce config complexity");let a=r.map(c=>c.name);a.filter((c,d)=>a.indexOf(c)!==d).forEach(c=>{t.push({severity:"error",message:"Duplicate rule name found",rule:c,suggestion:"Rule names must be unique"})}),e.version||t.push({severity:"warning",message:"Configuration lacks version information",suggestion:"Version tracking helps with change management"})}static formatHealthReport(e){let t="",i=e.score>=80?be.default.green:e.score>=60?be.default.yellow:be.default.red;return t+=be.default.bold(`
152
152
  \u{1F3E5} Configuration Health Score: ${i(e.score)}/100
153
153
 
154
- `),e.issues.length>0&&(t+=Ie.default.bold(`Issues Found:
155
- `),e.issues.forEach(r=>{let s=r.severity==="error"?"\u274C":r.severity==="warning"?"\u26A0\uFE0F":"\u2139\uFE0F";t+=`${s} ${r.message}`,r.rule&&(t+=Ie.default.dim(` (${r.rule})`)),t+=`
156
- `,r.suggestion&&(t+=Ie.default.dim(` \u{1F4A1} ${r.suggestion}
154
+ `),e.issues.length>0&&(t+=be.default.bold(`Issues Found:
155
+ `),e.issues.forEach(r=>{let s=r.severity==="error"?"\u274C":r.severity==="warning"?"\u26A0\uFE0F":"\u2139\uFE0F";t+=`${s} ${r.message}`,r.rule&&(t+=be.default.dim(` (${r.rule})`)),t+=`
156
+ `,r.suggestion&&(t+=be.default.dim(` \u{1F4A1} ${r.suggestion}
157
157
  `))}),t+=`
158
- `),e.recommendations.length>0&&(t+=Ie.default.bold(`Recommendations:
158
+ `),e.recommendations.length>0&&(t+=be.default.bold(`Recommendations:
159
159
  `),e.recommendations.forEach(r=>{t+=`\u{1F4A1} ${r}
160
- `})),t}};var xn="status",On="Show sync status between local and remote firewall configuration",Nn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Fn=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"checking status"},async({config:e,service:t,provider:i})=>{if(i.name!=="vercel"){await Vn(i,e);return}o.start("Checking sync status...");let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await t.getChanges(e),p=r.length>0||s.length>0||a.length>0,g=l.length>0||c.length>0||d.length>0,h=e.version!==u;o.log(A.default.bold(`
160
+ `})),t}};var xn="status",On="Show sync status between local and remote firewall configuration",Fn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Nn=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"checking status"},async({config:e,service:t,provider:i})=>{if(i.name!=="vercel"){await Vn(i,e);return}o.start("Checking sync status...");let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await t.getChanges(e),p=r.length>0||s.length>0||a.length>0,f=l.length>0||c.length>0||d.length>0,h=e.version!==u;o.log(T.default.bold(`
161
161
  \u{1F4CA} Sync Status Summary
162
- `)),o.log(`${A.default.dim("Local Version:")} ${A.default.yellow(e.version||"unknown")}`),o.log(`${A.default.dim("Remote Version:")} ${A.default.yellow(u)}`),h?o.log(`${A.default.dim("Version Status:")} ${A.default.red("Out of sync")}`):o.log(`${A.default.dim("Version Status:")} ${A.default.green("In sync")}`),o.log(""),o.log(`${A.default.dim("Custom Rules:")}`),o.log(` ${A.default.green("+")} ${r.length} to add`),o.log(` ${A.default.cyan("~")} ${s.length} to update`),o.log(` ${A.default.red("-")} ${a.length} to delete`),o.log(`${A.default.dim("IP Blocking Rules:")}`),o.log(` ${A.default.green("+")} ${l.length} to add`),o.log(` ${A.default.cyan("~")} ${c.length} to update`),o.log(` ${A.default.red("-")} ${d.length} to delete`),o.log(""),!p&&!g&&!h?o.success(A.default.green("\u2705 Everything is in sync!")):(o.warn(A.default.yellow("\u26A0\uFE0F Changes detected. Run `sync` to apply changes.")),h&&o.info(A.default.dim("\u{1F4A1} Version mismatch detected - this will be updated during sync"))),e.updatedAt&&o.log(`
163
- ${A.default.dim("Last Updated:")} ${new Date(e.updatedAt).toLocaleString()}`),o.log(`
164
- `+A.default.bold("\u{1F3E5} Configuration Health Check"));let b=Je.check(e),f=Je.formatHealthReport(b);o.log(f)})};async function Vn(n,e){o.start("Checking sync status...");let t=await n.getChanges(e);o.log(A.default.bold(`
162
+ `)),o.log(`${T.default.dim("Local Version:")} ${T.default.yellow(e.version||"unknown")}`),o.log(`${T.default.dim("Remote Version:")} ${T.default.yellow(u)}`),h?o.log(`${T.default.dim("Version Status:")} ${T.default.red("Out of sync")}`):o.log(`${T.default.dim("Version Status:")} ${T.default.green("In sync")}`),o.log(""),o.log(`${T.default.dim("Custom Rules:")}`),o.log(` ${T.default.green("+")} ${r.length} to add`),o.log(` ${T.default.cyan("~")} ${s.length} to update`),o.log(` ${T.default.red("-")} ${a.length} to delete`),o.log(`${T.default.dim("IP Blocking Rules:")}`),o.log(` ${T.default.green("+")} ${l.length} to add`),o.log(` ${T.default.cyan("~")} ${c.length} to update`),o.log(` ${T.default.red("-")} ${d.length} to delete`),o.log(""),!p&&!f&&!h?o.success(T.default.green("\u2705 Everything is in sync!")):(o.warn(T.default.yellow("\u26A0\uFE0F Changes detected. Run `sync` to apply changes.")),h&&o.info(T.default.dim("\u{1F4A1} Version mismatch detected - this will be updated during sync"))),e.updatedAt&&o.log(`
163
+ ${T.default.dim("Last Updated:")} ${new Date(e.updatedAt).toLocaleString()}`),o.log(`
164
+ `+T.default.bold("\u{1F3E5} Configuration Health Check"));let I=Je.check(e),g=Je.formatHealthReport(I);o.log(g)})};async function Vn(n,e){o.start("Checking sync status...");let t=await n.getChanges(e);o.log(T.default.bold(`
165
165
  \u{1F4CA} ${n.name} Sync Status Summary
166
- `)),o.log(`${A.default.dim("Rules:")}`),o.log(` ${A.default.green("+")} ${t.rulesToAdd.length} to add`),o.log(` ${A.default.cyan("~")} ${t.rulesToUpdate.length} to update`),o.log(` ${A.default.red("-")} ${t.rulesToDelete.length} to delete`),o.log(`${A.default.dim("IPs:")}`),o.log(` ${A.default.green("+")} ${t.ipsToAdd?.length??0} to add`),o.log(` ${A.default.cyan("~")} ${t.ipsToUpdate?.length??0} to update`),o.log(` ${A.default.red("-")} ${t.ipsToDelete?.length??0} to delete`),o.log(""),t.hasChanges?o.warn(A.default.yellow("\u26A0\uFE0F Changes detected. Run `sync` to apply changes.")):o.success(A.default.green("\u2705 Everything is in sync!")),o.log(`
167
- `+A.default.bold("\u{1F3E5} Configuration Health Check"));let i=n.getHealthScore(e);o.log(`${A.default.dim("Score:")} ${i.score}/100 (${i.grade})`),i.issues.forEach(r=>{let s=r.severity==="error"?A.default.red("\u2717"):r.severity==="warning"?A.default.yellow("\u26A0"):A.default.dim("\u2139");o.log(` ${s} [${r.category}] ${r.message}`),r.suggestion&&o.log(` ${A.default.dim(r.suggestion)}`)}),i.recommendations.length>0&&(o.log(A.default.dim(`
168
- Recommendations:`)),i.recommendations.forEach(r=>o.log(` - ${r}`)))}var Gt={};Z(Gt,{builder:()=>zn,command:()=>Bn,desc:()=>jn,handler:()=>Wn});var x=S(require("chalk"));k();ce();var Bn="sync",jn="Sync Vercel Firewall rules with config file",zn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Wn=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"syncing firewall rules"},async e=>{if(e.provider.name!=="vercel"){await Mn(e.provider,e.config,n);return}let t=e.config,i=e.service;o.start(x.default.magenta("Calculating firewall configuration changes..."));let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await i.getChanges(t),p=r.length>0||s.length>0||a.length>0,g=l.length>0||c.length>0||d.length>0,h=t.version!==u;if(!p&&!g&&!h){o.success(x.default.green("No changes detected. Firewall rules are in sync."));return}if(p&&(o.log(x.default.bold(`
166
+ `)),o.log(`${T.default.dim("Rules:")}`),o.log(` ${T.default.green("+")} ${t.rulesToAdd.length} to add`),o.log(` ${T.default.cyan("~")} ${t.rulesToUpdate.length} to update`),o.log(` ${T.default.red("-")} ${t.rulesToDelete.length} to delete`),o.log(`${T.default.dim("IPs:")}`),o.log(` ${T.default.green("+")} ${t.ipsToAdd?.length??0} to add`),o.log(` ${T.default.cyan("~")} ${t.ipsToUpdate?.length??0} to update`),o.log(` ${T.default.red("-")} ${t.ipsToDelete?.length??0} to delete`),o.log(""),t.hasChanges?o.warn(T.default.yellow("\u26A0\uFE0F Changes detected. Run `sync` to apply changes.")):o.success(T.default.green("\u2705 Everything is in sync!")),o.log(`
167
+ `+T.default.bold("\u{1F3E5} Configuration Health Check"));let i=n.getHealthScore(e);o.log(`${T.default.dim("Score:")} ${i.score}/100 (${i.grade})`),i.issues.forEach(r=>{let s=r.severity==="error"?T.default.red("\u2717"):r.severity==="warning"?T.default.yellow("\u26A0"):T.default.dim("\u2139");o.log(` ${s} [${r.category}] ${r.message}`),r.suggestion&&o.log(` ${T.default.dim(r.suggestion)}`)}),i.recommendations.length>0&&(o.log(T.default.dim(`
168
+ Recommendations:`)),i.recommendations.forEach(r=>o.log(` - ${r}`)))}var Ht={};Z(Ht,{builder:()=>zn,command:()=>Bn,desc:()=>jn,handler:()=>Wn});var x=S(require("chalk"));k();ce();var Bn="sync",jn="Sync Vercel Firewall rules with config file",zn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Wn=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"syncing firewall rules"},async e=>{if(e.provider.name!=="vercel"){await Mn(e.provider,e.config,n);return}let t=e.config,i=e.service;o.start(x.default.magenta("Calculating firewall configuration changes..."));let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await i.getChanges(t),p=r.length>0||s.length>0||a.length>0,f=l.length>0||c.length>0||d.length>0,h=t.version!==u;if(!p&&!f&&!h){o.success(x.default.green("No changes detected. Firewall rules are in sync."));return}if(p&&(o.log(x.default.bold(`
169
169
  Proposed Custom Rule Changes:
170
- `)),be([...r.map(m=>({...m,changeStatus:le.new,id:m.id})),...s.map(m=>({...m,changeStatus:le.modified,id:m.id})),...a.map(m=>({...m,changeStatus:le.deleted,id:m.id}))],{showStatus:!0})),g&&(o.log(x.default.bold(`
170
+ `)),ve([...r.map(m=>({...m,changeStatus:le.new,id:m.id})),...s.map(m=>({...m,changeStatus:le.modified,id:m.id})),...a.map(m=>({...m,changeStatus:le.deleted,id:m.id}))],{showStatus:!0})),f&&(o.log(x.default.bold(`
171
171
  Proposed IP Blocking Rule Changes:
172
- `)),ve([...l.map(m=>({...m,changeStatus:le.new,id:m.id||void 0})),...c.map(m=>({...m,changeStatus:le.modified,id:m.id||void 0})),...d.map(m=>({...m,changeStatus:le.deleted,id:m.id||void 0}))],{showStatus:!0})),h&&(o.log(x.default.bold(`
172
+ `)),Ee([...l.map(m=>({...m,changeStatus:le.new,id:m.id||void 0})),...c.map(m=>({...m,changeStatus:le.modified,id:m.id||void 0})),...d.map(m=>({...m,changeStatus:le.deleted,id:m.id||void 0}))],{showStatus:!0})),h&&(o.log(x.default.bold(`
173
173
  Proposed Metadata Changes:
174
- `)),o.log(` - Version: ${x.default.red(t.version)} ${x.default.dim("->")} ${x.default.green(u)}`)),!await C("Do you want to apply these changes?",{type:"confirm"})){o.info(x.default.yellow("Sync cancelled."));return}o.start("Starting firewall rules sync...");let f=JSON.parse(JSON.stringify(t)),T=await i.syncRules(t,{debug:n.debug}),{rulesToUpdateLocally:j}=T;o.success(x.default.green("Firewall rules sync completed successfully"));try{t=await i.validateAndUpdateConfig(t,T,{dryRun:!1})}catch(m){throw o.error("Failed to validate sync result or update config metadata"),o.error(m instanceof Error?m.message:String(m)),await z(f,n.config),o.info(x.default.yellow("Restored original config due to validation failure")),new Error("Sync validation failed - original config restored")}let v=j.filter(m=>t.rules.some(P=>m.oldId===P.id||m.oldId===""&&m.name===P.name));v.length>0&&(o.log(""),o.info(x.default.yellow("Some rules have IDs that do not match their expected snake_case name:")),v.forEach(P=>{o.log(` - Rule "${P.name}": ${x.default.red(P.oldId||"empty")} ${x.default.dim("->")} ${x.default.green(P.newId)}`)}),await C("Do you want to update the local config with the new IDs?",{type:"confirm"})?(t={...t,rules:t.rules.map(P=>{let w=v.find(I=>I.oldId===P.id||I.oldId===""&&I.name===P.name);return w?{...P,id:w.newId}:P}),ips:t.ips||[]},await z(t,n.config),o.success(x.default.green("Updated local config with new rule IDs"))):o.warn(x.default.yellow("Local config not updated. Remember to update rule IDs manually if needed."))),(f.version!==t.version||f.updatedAt!==t.updatedAt)&&(await z(t,n.config),o.success(x.default.green(`Updated version ${x.default.dim(`(v${t.version})`)} and metadata in local config file`)))})};async function Mn(n,e,t){let i=e;o.start(x.default.magenta(`Calculating ${n.name} firewall configuration changes...`));let r=await n.getChanges(i);if(!r.hasChanges){o.success(x.default.green("No changes detected. Firewall rules are in sync."));return}o.log(x.default.bold(`
174
+ `)),o.log(` - Version: ${x.default.red(t.version)} ${x.default.dim("->")} ${x.default.green(u)}`)),!await C("Do you want to apply these changes?",{type:"confirm"})){o.info(x.default.yellow("Sync cancelled."));return}o.start("Starting firewall rules sync...");let g=JSON.parse(JSON.stringify(t)),A=await i.syncRules(t,{debug:n.debug}),{rulesToUpdateLocally:N}=A;o.success(x.default.green("Firewall rules sync completed successfully"));try{t=await i.validateAndUpdateConfig(t,A,{dryRun:!1})}catch(m){throw o.error("Failed to validate sync result or update config metadata"),o.error(m instanceof Error?m.message:String(m)),await z(g,n.config),o.info(x.default.yellow("Restored original config due to validation failure")),new Error("Sync validation failed - original config restored")}let v=N.filter(m=>t.rules.some(P=>m.oldId===P.id||m.oldId===""&&m.name===P.name));v.length>0&&(o.log(""),o.info(x.default.yellow("Some rules have IDs that do not match their expected snake_case name:")),v.forEach(P=>{o.log(` - Rule "${P.name}": ${x.default.red(P.oldId||"empty")} ${x.default.dim("->")} ${x.default.green(P.newId)}`)}),await C("Do you want to update the local config with the new IDs?",{type:"confirm"})?(t={...t,rules:t.rules.map(P=>{let w=v.find(b=>b.oldId===P.id||b.oldId===""&&b.name===P.name);return w?{...P,id:w.newId}:P}),ips:t.ips||[]},await z(t,n.config),o.success(x.default.green("Updated local config with new rule IDs"))):o.warn(x.default.yellow("Local config not updated. Remember to update rule IDs manually if needed."))),(g.version!==t.version||g.updatedAt!==t.updatedAt)&&(await z(t,n.config),o.success(x.default.green(`Updated version ${x.default.dim(`(v${t.version})`)} and metadata in local config file`)))})};async function Mn(n,e,t){let i=e;o.start(x.default.magenta(`Calculating ${n.name} firewall configuration changes...`));let r=await n.getChanges(i);if(!r.hasChanges){o.success(x.default.green("No changes detected. Firewall rules are in sync."));return}o.log(x.default.bold(`
175
175
  Proposed Changes:
176
- `)),o.log(` ${x.default.green("+")} ${r.rulesToAdd.length} rules to add`),o.log(` ${x.default.cyan("~")} ${r.rulesToUpdate.length} rules to update`),o.log(` ${x.default.red("-")} ${r.rulesToDelete.length} rules to delete`);let s=r.ipsToAdd??[],a=r.ipsToUpdate??[],l=r.ipsToDelete??[];(s.length||a.length||l.length)&&(o.log(` ${x.default.green("+")} ${s.length} IPs to add`),o.log(` ${x.default.cyan("~")} ${a.length} IPs to update`),o.log(` ${x.default.red("-")} ${l.length} IPs to delete`)),o.start("Starting firewall rules sync...");let c=await n.syncRules(i,{force:t.ci});if(!c.success)throw new Error(`Sync failed: ${c.errors?.join(", ")||"unknown error"}`);o.success(x.default.green("Firewall rules sync completed successfully")),o.log(` Rules: ${c.rulesAdded} added, ${c.rulesUpdated} updated, ${c.rulesDeleted} deleted`),(c.ipsAdded||c.ipsUpdated||c.ipsDeleted)&&o.log(` IPs: ${c.ipsAdded??0} added, ${c.ipsUpdated??0} updated, ${c.ipsDeleted??0} deleted`),c.warnings?.forEach(d=>o.warn(d))}var Zt={};Z(Zt,{builder:()=>Hn,command:()=>qn,desc:()=>Gn,handler:()=>Kn});var ze=S(require("chalk")),eo=require("consola");k();var Ki={metadata:{title:"Block AI Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-ai-bots-firewall-rule"},config:{rules:[{id:"rule_detect_ai_bots",name:"Detect AI Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"AI2Bot|Ai2Bot-Dolma|Amazonbot|Applebot|Applebot-Extended|Bytespider|CCBot|ChatGPT-User|Claude-Web|ClaudeBot|Diffbot|FacebookBot|FriendlyCrawler|GPTBot|Google-Extended|GoogleOther|GoogleOther-Image|GoogleOther-Video|ICC-Crawler|ImagesiftBot|Meta-ExternalAgent|Meta-ExternalFetcher|OAI-SearchBot|PerplexityBot|PetalBot|Scrapy|Timpibot|VelenPublicWebCrawler|Webzio-Extended|YouBot|anthropic-ai|cohere-ai|facebookexternalhit|img2dataset|omgili|omgilibot"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var Ji={metadata:{title:"Block Bad Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-bad-bots-firewall-rule"},config:{rules:[{id:"rule_detect_bad_bots",name:"Detect Bad Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"01h4x.com|360Spider|404checker|404enemy|80legs|ADmantX|AIBOT|ALittle Client|ASPSeek|Abonti|Aboundex|Aboundexbot|Acunetix|AdsTxtCrawlerTP|AfD-Verbotsverfahren|AhrefsBot|AiHitBot|Aipbot|Alexibot|AllSubmitter|Alligator|AlphaBot|Anarchie|Anarchy|Anarchy99|Ankit|Anthill|Apexoo|Aspiegel|Asterias|Atomseobot|Attach|AwarioBot|AwarioRssBot|AwarioSmartBot|BBBike|BDCbot|BDFetch|BLEXBot|BackDoorBot|BackStreet|BackWeb|Backlink-Ceck|BacklinkCrawler|BacklinksExtendedBot|Badass|Bandit|Barkrowler|BatchFTP|Battleztar Bazinga|BetaBot|Bigfoot|Bitacle|BlackWidow|Black Hole|Blackboard|Blow|BlowFish|Boardreader|Bolt|BotALot|Brandprotect|Brandwatch|Buck|Buddy|BuiltBotTough|BuiltWith|Bullseye|BunnySlippers|BuzzSumo|Bytespider|CATExplorador|CCBot|CODE87|CSHttp|Calculon|CazoodleBot|Cegbfeieh|CensysInspect|ChatGPT-User|CheTeam|CheeseBot|CherryPicker|ChinaClaw|Chlooe|Citoid|Claritybot|ClaudeBot|Cliqzbot|Cloud mapping|Cocolyzebot|Cogentbot|Collector|Copier|CopyRightCheck|Copyscape|Cosmos|Craftbot|Crawling at Home Project|CrazyWebCrawler|Crescent|CrunchBot|Curious|Custo|CyotekWebCopy|DBLBot|DIIbot|DSearch|DTS Agent|DataCha0s|DatabaseDriverMysqli|Demon|Deusu|Devil|Digincore|DigitalPebble|Dirbuster|Disco|Discobot|Discoverybot|Dispatch|DittoSpyder|DnBCrawler-Analytics|DnyzBot|DomCopBot|DomainAppender|DomainCrawler|DomainSigmaCrawler|DomainStatsBot|Domains Project|Dotbot|Download Wonder|Dragonfly|Drip|ECCP/1.0|EMail Siphon|EMail Wolf|EasyDL|Ebingbong|Ecxi|EirGrabber|EroCrawler|Evil|Exabot|Express WebPictures|ExtLinksBot|Extractor|ExtractorPro|Extreme Picture Finder|EyeNetIE|Ezooms|FDM|FHscan|FacebookBot|FemtosearchBot|Fimap|Firefox/7.0|FlashGet|Flunky|Foobot|Freeuploader|FrontPage|Fuzz|FyberSpider|Fyrebot|G-i-g-a-b-o-t|GPTBot|GT::WWW|GalaxyBot|Genieo|GermCrawler|GetRight|GetWeb|Getintent|Gigabot|Go!Zilla|Go-Ahead-Got-It|GoZilla|Gotit|GrabNet|Grabber|Grafula|GrapeFX|GrapeshotCrawler|GridBot|HEADMasterSEO|HMView|HTMLparser|HTTP::Lite|HTTrack|Haansoft|HaosouSpider|Harvest|Havij|Heritrix|Hloader|HonoluluBot|Humanlinks|HybridBot|IDBTE4M|IDBot|IRLbot|Iblog|Id-search|IlseBot|Image Fetch|Image Sucker|ImagesiftBot|IndeedBot|Indy Library|InfoNaviRobot|InfoTekies|Information Security Team InfraSec Scanner|InfraSec Scanner|Intelliseek|InterGET|InternetMeasurement|InternetSeer|Internet Ninja|Iria|Iskanie|IstellaBot|JOC Web Spider|JamesBOT|Jbrofuzz|JennyBot|JetCar|Jetty|JikeSpider|Joomla|Jorgee|JustView|Jyxobot|Kenjin Spider|Keybot Translation-Search-Machine|Keyword Density|Kinza|Kozmosbot|LNSpiderguy|LWP::Simple|Lanshanbot|Larbin|Leap|LeechFTP|LeechGet|LexiBot|Lftp|LibWeb|Libwhisker|LieBaoFast|Lightspeedsystems|Likse|LinkScan|LinkWalker|Linkbot|LinkextractorPro|LinkpadBot|LinksManager|LinqiaMetadataDownloaderBot|LinqiaRSSBot|LinqiaScrapeBot|Lipperhey|Lipperhey Spider|Litemage_walker|Lmspider|Ltx71|MFC_Tear_Sample|MIDown tool|MIIxpc|MJ12bot|MQQBrowser|MSFrontPage|MSIECrawler|MTRobot|Mag-Net|Magnet|Mail.RU_Bot|Majestic-SEO|Majestic12|Majestic SEO|MarkMonitor|MarkWatch|Mass Downloader|Masscan|Mata Hari|MauiBot|Mb2345Browser|MeanPath Bot|Meanpathbot|Mediatoolkitbot|MegaIndex.ru|Metauri|MicroMessenger|Microsoft Data Access|Microsoft URL Control|Minefield|Mister PiX|Moblie Safari|Mojeek|Mojolicious|MolokaiBot|Morfeus Fucking Scanner|Mozlila|Mr.4x3|Msrabot|Musobot|NICErsPRO|NPbot|Name Intelligence|Nameprotect|Navroad|NearSite|Needle|Nessus|NetAnts|NetLyzer|NetMechanic|NetSpider|NetZIP|Net Vampire|Netcraft|Nettrack|Netvibes|NextGenSearchBot|Nibbler|Niki-bot|Nikto|NimbleCrawler|Nimbostratus|Ninja|Nmap|Nuclei|Nutch|Octopus|Offline Explorer|Offline Navigator|OnCrawl|OpenLinkProfiler|OpenVAS|Openfind|Openvas|OrangeBot|OrangeSpider|OutclicksBot|OutfoxBot|PECL::HTTP|PHPCrawl|POE-Component-Client-HTTP|PageAnalyzer|PageGrabber|PageScorer|PageThing.com|Page Analyzer|Pandalytics|Panscient|Papa Foto|Pavuk|PeoplePal|Petalbot|Pi-Monster|Picscout|Picsearch|PictureFinder|Piepmatz|Pimonster|Pixray|PleaseCrawl|Pockey|ProPowerBot|ProWebWalker|Probethenet|Proximic|Psbot|Pu_iN|Pump|PxBroker|PyCurl|QueryN Metasearch|Quick-Crawler|RSSingBot|Rainbot|RankActive|RankActiveLinkBot|RankFlex|RankingBot|RankingBot2|Rankivabot|RankurBot|Re-re|ReGet|RealDownload|Reaper|RebelMouse|Recorder|RedesScrapy|RepoMonkey|Ripper|RocketCrawler|Rogerbot|SBIder|SEOkicks|SEOkicks-Robot|SEOlyt|SEOlyticsCrawler|SEOprofiler|SEOstats|SISTRIX|SMTBot|SalesIntelligent|ScanAlert|Scanbot|ScoutJet|Scrapy|Screaming|ScreenerBot|ScrepyBot|Searchestate|SearchmetricsBot|Seekport|SeekportBot|SemanticJuice|Semrush|SemrushBot|SentiBot|SenutoBot|SeoSiteCheckup|SeobilityBot|Seomoz|Shodan|Siphon|SiteCheckerBotCrawler|SiteExplorer|SiteLockSpider|SiteSnagger|SiteSucker|Site Sucker|Sitebeam|Siteimprove|Sitevigil|SlySearch|SmartDownload|Snake|Snapbot|Snoopy|SocialRankIOBot|Sociscraper|Sogou web spider|Sosospider|Sottopop|SpaceBison|Spammen|SpankBot|Spanner|Spbot|Spider_Bot|Spider_Bot/3.0|Spinn3r|SputnikBot|Sqlmap|Sqlworm|Sqworm|Steeler|Stripper|Sucker|Sucuri|SuperBot|SuperHTTP|Surfbot|SurveyBot|Suzuran|Swiftbot|Szukacz|T0PHackTeam|T8Abot|Teleport|TeleportPro|Telesoft|Telesphoreo|Telesphorep|TheNomad|The Intraformant|Thumbor|TightTwatBot|TinyTestBot|Titan|Toata|Toweyabot|Tracemyfile|Trendiction|Trendictionbot|True_Robot|Turingos|Turnitin|TurnitinBot|TwengaBot|Twice|Typhoeus|URLy.Warning|URLy Warning|UnisterBot|Upflow|V-BOT|VB Project|VCI|Vacuum|Vagabondo|VelenPublicWebCrawler|VeriCiteCrawler|VidibleScraper|Virusdie|VoidEYE|Voil|Voltron|WASALive-Bot|WBSearchBot|WEBDAV|WISENutbot|WPScan|WWW-Collector-E|WWW-Mechanize|WWW::Mechanize|WWWOFFLE|Wallpapers|Wallpapers/3.0|WallpapersHD|WeSEE|WebAuto|WebBandit|WebCollage|WebCopier|WebEnhancer|WebFetch|WebFuck|WebGo IS|WebImageCollector|WebLeacher|WebPix|WebReaper|WebSauger|WebStripper|WebSucker|WebWhacker|WebZIP|Web Auto|Web Collage|Web Enhancer|Web Fetch|Web Fuck|Web Pix|Web Sauger|Web Sucker|Webalta|WebmasterWorldForumBot|Webshag|WebsiteExtractor|WebsiteQuester|Website Quester|Webster|Whack|Whacker|Whatweb|Who.is Bot|Widow|WinHTTrack|WiseGuys Robot|Wonderbot|Woobot|Wotbox|Wprecon|Xaldon WebSpider|Xaldon_WebSpider|Xenu|YaK|YoudaoBot|Zade|Zauba|Zermelo|Zeus|Zitebot|ZmEu|ZoomBot|ZoominfoBot|ZumBot|ZyBorg|adscanner|anthropic-ai|archive.org_bot|arquivo-web-crawler|arquivo.pt|autoemailspider|awario.com|backlink-check|cah.io.community|check1.exe|clark-crawler|coccocbot|cognitiveseo|cohere-ai|com.plumanalytics|crawl.sogou.com|crawler.feedback|crawler4j|dataforseo.com|dataforseobot|demandbase-bot|domainsproject.org|eCatch|evc-batch|facebookscraper|gopher|heritrix|imagesift.com|instabid|internetVista monitor|ips-agent|isitwp.com|iubenda-radar|linkdexbot|linkfluence|lwp-request|lwp-trivial|magpie-crawler|meanpathbot|mediawords|muhstik-scan|netEstate NE Crawler|oBot|omgili|openai|openai.com|page scorer|pcBrowser|plumanalytics|polaris version|probe-image-size|ripz|s1z.ru|satoristudio.net|scalaj-http|scan.lol|seobility|seocompany.store|seoscanners|seostar|serpstatbot|sexsearcher|sitechecker.pro|siteripz|sogouspider|sp_auditbot|spyfu|sysscan|tAkeOut|trendiction.com|trendiction.de|ubermetrics-technologies.com|voyagerx.com|webgains-bot|webmeup-crawler|webpros.com|webprosbot|x09Mozilla|x22Mozilla|xpymep1.exe|zauba.io|zgrab"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var Xi={metadata:{title:"Block OFAC-Sanctioned Countries",reference:"https://vercel.com/templates/other/block-ofac-sanctioned-countries-firewall-rule"},config:{rules:[{id:"rule_block_traffic_from_ofac_sanctioned_countries",name:"Block traffic from OFAC-sanctioned countries",description:"Blocks traffic from OFAC-sanctioned countries and enforces a one-hour persistent block after the first violation.",conditionGroup:[{conditions:[{type:"geo_country",op:"inc",value:["SY","IR","RU","CU","KP"]}]}],action:{mitigate:{action:"deny",actionDuration:"1h"}},active:!0}]}};var Qi={metadata:{title:"Deny Common WordPress URLs Firewall Rule",reference:"https://vercel.com/templates/other/block-wordpress-urls-firewall-rule"},config:{rules:[{name:"Deny WordPress URLs",description:"",conditionGroup:[{conditions:[{type:"path",op:"re",value:"/(wp-admin|wp-login\\.php|xmlrpc\\.php|wp-content|wp-includes|wp-signup\\.php|wp-activate\\.php|register\\.php|wp-register\\.php)"}]}],action:{mitigate:{action:"deny"}},active:!0}]}};var Ht={"ai-bots":Ki,"bad-bots":Ji,"block-ofac-sanctioned-countries":Xi,wordpress:Qi};var Yi=n=>Ht[n]?.config;ce();var qn="template [name]",Gn="Add a firewall rule template to your configuration",Hn={name:{type:"string",description:"Name of the template to add"},dryRun:{alias:"d",type:"boolean",description:"Preview changes without applying them",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}},Zn=()=>Object.keys(Ht),Kn=async n=>{try{n.debug&&(o.level=eo.LogLevels.debug),o.debug("Template command arguments:",n);let e=n.name;if(!e){let a=Zn();e=await C("Select a template to add:",{type:"select",options:a,initial:a[0]})}let t=Yi(e);t||(o.error(`Template not found: ${e}`),process.exit(1)),o.debug("Template content:",t),o.start("Loading current configuration...");let i=await G(void 0,"raw");if(n.dryRun){o.info(ze.default.cyan(`
177
- Dry run - The following rules would be added:`)),o.log(JSON.stringify(t.rules,null,2));return}let r={...i,rules:[...i.rules,...t.rules]},s=we.safeParse(r);s.success||(o.error(ze.default.red("The resulting configuration would be invalid:")),s.error.errors.forEach(a=>{let l=a.path.join(".");o.error(ze.default.red(` - ${l}: ${a.message}`))}),o.info(ze.default.dim("Template was not applied. Fix the issues above and try again.")),process.exit(1)),o.start("Saving updated configuration..."),await z(r,void 0,{validate:!1}),o.success(ze.default.green(`Successfully added template '${e}' to configuration`))}catch(e){Q(e,"adding template")}};var Kt={};Z(Kt,{builder:()=>Qn,command:()=>Jn,desc:()=>Xn,handler:()=>Yn});var ie=S(require("chalk"));k();var Jn="validate",Xn="Validate firewall configuration file",Qn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},verbose:{alias:"v",type:"boolean",description:"Show detailed validation results",default:!1}},Yn=async n=>{try{let e=await G(n.config,"raw"),t=$e.getInstance(),i=Ee(e);n.verbose&&o.start(`Validating configuration file...
178
- `);let r=i?ft.safeParse(e):we.safeParse(e);n.verbose&&(o.log(ie.default.bold.underline("Zod Schema Validation:")),r.success?o.log(ie.default.green("\u2713 Schema validation passed")):(o.error(ie.default.red("\u2717 Schema validation failed:")),r.error.errors.forEach(l=>{let c=l.path.join(".");o.error(ie.default.red(` - ${c}: ${l.message}`))})));let s=!0,a=[];try{t.validateConfig(e),n.verbose&&(o.log(ie.default.bold.underline(`
179
- AJV Schema Validation:`)),o.log(ie.default.green("\u2713 JSON Schema validation passed")))}catch(l){if(s=!1,l instanceof F)a=l.ajvErrors||[],n.verbose&&(o.log(ie.default.bold.underline(`
176
+ `)),o.log(` ${x.default.green("+")} ${r.rulesToAdd.length} rules to add`),o.log(` ${x.default.cyan("~")} ${r.rulesToUpdate.length} rules to update`),o.log(` ${x.default.red("-")} ${r.rulesToDelete.length} rules to delete`);let s=r.ipsToAdd??[],a=r.ipsToUpdate??[],l=r.ipsToDelete??[];(s.length||a.length||l.length)&&(o.log(` ${x.default.green("+")} ${s.length} IPs to add`),o.log(` ${x.default.cyan("~")} ${a.length} IPs to update`),o.log(` ${x.default.red("-")} ${l.length} IPs to delete`)),o.start("Starting firewall rules sync...");let c=await n.syncRules(i,{force:t.ci});if(!c.success)throw new Error(`Sync failed: ${c.errors?.join(", ")||"unknown error"}`);o.success(x.default.green("Firewall rules sync completed successfully")),o.log(` Rules: ${c.rulesAdded} added, ${c.rulesUpdated} updated, ${c.rulesDeleted} deleted`),(c.ipsAdded||c.ipsUpdated||c.ipsDeleted)&&o.log(` IPs: ${c.ipsAdded??0} added, ${c.ipsUpdated??0} updated, ${c.ipsDeleted??0} deleted`),c.warnings?.forEach(d=>o.warn(d))}var Kt={};Z(Kt,{builder:()=>Hn,command:()=>qn,desc:()=>Gn,handler:()=>Kn});var ze=S(require("chalk")),eo=require("consola");k();var Ki={metadata:{title:"Block AI Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-ai-bots-firewall-rule"},config:{rules:[{id:"rule_detect_ai_bots",name:"Detect AI Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"AI2Bot|Ai2Bot-Dolma|Amazonbot|Applebot|Applebot-Extended|Bytespider|CCBot|ChatGPT-User|Claude-Web|ClaudeBot|Diffbot|FacebookBot|FriendlyCrawler|GPTBot|Google-Extended|GoogleOther|GoogleOther-Image|GoogleOther-Video|ICC-Crawler|ImagesiftBot|Meta-ExternalAgent|Meta-ExternalFetcher|OAI-SearchBot|PerplexityBot|PetalBot|Scrapy|Timpibot|VelenPublicWebCrawler|Webzio-Extended|YouBot|anthropic-ai|cohere-ai|facebookexternalhit|img2dataset|omgili|omgilibot"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var Ji={metadata:{title:"Block Bad Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-bad-bots-firewall-rule"},config:{rules:[{id:"rule_detect_bad_bots",name:"Detect Bad Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"01h4x.com|360Spider|404checker|404enemy|80legs|ADmantX|AIBOT|ALittle Client|ASPSeek|Abonti|Aboundex|Aboundexbot|Acunetix|AdsTxtCrawlerTP|AfD-Verbotsverfahren|AhrefsBot|AiHitBot|Aipbot|Alexibot|AllSubmitter|Alligator|AlphaBot|Anarchie|Anarchy|Anarchy99|Ankit|Anthill|Apexoo|Aspiegel|Asterias|Atomseobot|Attach|AwarioBot|AwarioRssBot|AwarioSmartBot|BBBike|BDCbot|BDFetch|BLEXBot|BackDoorBot|BackStreet|BackWeb|Backlink-Ceck|BacklinkCrawler|BacklinksExtendedBot|Badass|Bandit|Barkrowler|BatchFTP|Battleztar Bazinga|BetaBot|Bigfoot|Bitacle|BlackWidow|Black Hole|Blackboard|Blow|BlowFish|Boardreader|Bolt|BotALot|Brandprotect|Brandwatch|Buck|Buddy|BuiltBotTough|BuiltWith|Bullseye|BunnySlippers|BuzzSumo|Bytespider|CATExplorador|CCBot|CODE87|CSHttp|Calculon|CazoodleBot|Cegbfeieh|CensysInspect|ChatGPT-User|CheTeam|CheeseBot|CherryPicker|ChinaClaw|Chlooe|Citoid|Claritybot|ClaudeBot|Cliqzbot|Cloud mapping|Cocolyzebot|Cogentbot|Collector|Copier|CopyRightCheck|Copyscape|Cosmos|Craftbot|Crawling at Home Project|CrazyWebCrawler|Crescent|CrunchBot|Curious|Custo|CyotekWebCopy|DBLBot|DIIbot|DSearch|DTS Agent|DataCha0s|DatabaseDriverMysqli|Demon|Deusu|Devil|Digincore|DigitalPebble|Dirbuster|Disco|Discobot|Discoverybot|Dispatch|DittoSpyder|DnBCrawler-Analytics|DnyzBot|DomCopBot|DomainAppender|DomainCrawler|DomainSigmaCrawler|DomainStatsBot|Domains Project|Dotbot|Download Wonder|Dragonfly|Drip|ECCP/1.0|EMail Siphon|EMail Wolf|EasyDL|Ebingbong|Ecxi|EirGrabber|EroCrawler|Evil|Exabot|Express WebPictures|ExtLinksBot|Extractor|ExtractorPro|Extreme Picture Finder|EyeNetIE|Ezooms|FDM|FHscan|FacebookBot|FemtosearchBot|Fimap|Firefox/7.0|FlashGet|Flunky|Foobot|Freeuploader|FrontPage|Fuzz|FyberSpider|Fyrebot|G-i-g-a-b-o-t|GPTBot|GT::WWW|GalaxyBot|Genieo|GermCrawler|GetRight|GetWeb|Getintent|Gigabot|Go!Zilla|Go-Ahead-Got-It|GoZilla|Gotit|GrabNet|Grabber|Grafula|GrapeFX|GrapeshotCrawler|GridBot|HEADMasterSEO|HMView|HTMLparser|HTTP::Lite|HTTrack|Haansoft|HaosouSpider|Harvest|Havij|Heritrix|Hloader|HonoluluBot|Humanlinks|HybridBot|IDBTE4M|IDBot|IRLbot|Iblog|Id-search|IlseBot|Image Fetch|Image Sucker|ImagesiftBot|IndeedBot|Indy Library|InfoNaviRobot|InfoTekies|Information Security Team InfraSec Scanner|InfraSec Scanner|Intelliseek|InterGET|InternetMeasurement|InternetSeer|Internet Ninja|Iria|Iskanie|IstellaBot|JOC Web Spider|JamesBOT|Jbrofuzz|JennyBot|JetCar|Jetty|JikeSpider|Joomla|Jorgee|JustView|Jyxobot|Kenjin Spider|Keybot Translation-Search-Machine|Keyword Density|Kinza|Kozmosbot|LNSpiderguy|LWP::Simple|Lanshanbot|Larbin|Leap|LeechFTP|LeechGet|LexiBot|Lftp|LibWeb|Libwhisker|LieBaoFast|Lightspeedsystems|Likse|LinkScan|LinkWalker|Linkbot|LinkextractorPro|LinkpadBot|LinksManager|LinqiaMetadataDownloaderBot|LinqiaRSSBot|LinqiaScrapeBot|Lipperhey|Lipperhey Spider|Litemage_walker|Lmspider|Ltx71|MFC_Tear_Sample|MIDown tool|MIIxpc|MJ12bot|MQQBrowser|MSFrontPage|MSIECrawler|MTRobot|Mag-Net|Magnet|Mail.RU_Bot|Majestic-SEO|Majestic12|Majestic SEO|MarkMonitor|MarkWatch|Mass Downloader|Masscan|Mata Hari|MauiBot|Mb2345Browser|MeanPath Bot|Meanpathbot|Mediatoolkitbot|MegaIndex.ru|Metauri|MicroMessenger|Microsoft Data Access|Microsoft URL Control|Minefield|Mister PiX|Moblie Safari|Mojeek|Mojolicious|MolokaiBot|Morfeus Fucking Scanner|Mozlila|Mr.4x3|Msrabot|Musobot|NICErsPRO|NPbot|Name Intelligence|Nameprotect|Navroad|NearSite|Needle|Nessus|NetAnts|NetLyzer|NetMechanic|NetSpider|NetZIP|Net Vampire|Netcraft|Nettrack|Netvibes|NextGenSearchBot|Nibbler|Niki-bot|Nikto|NimbleCrawler|Nimbostratus|Ninja|Nmap|Nuclei|Nutch|Octopus|Offline Explorer|Offline Navigator|OnCrawl|OpenLinkProfiler|OpenVAS|Openfind|Openvas|OrangeBot|OrangeSpider|OutclicksBot|OutfoxBot|PECL::HTTP|PHPCrawl|POE-Component-Client-HTTP|PageAnalyzer|PageGrabber|PageScorer|PageThing.com|Page Analyzer|Pandalytics|Panscient|Papa Foto|Pavuk|PeoplePal|Petalbot|Pi-Monster|Picscout|Picsearch|PictureFinder|Piepmatz|Pimonster|Pixray|PleaseCrawl|Pockey|ProPowerBot|ProWebWalker|Probethenet|Proximic|Psbot|Pu_iN|Pump|PxBroker|PyCurl|QueryN Metasearch|Quick-Crawler|RSSingBot|Rainbot|RankActive|RankActiveLinkBot|RankFlex|RankingBot|RankingBot2|Rankivabot|RankurBot|Re-re|ReGet|RealDownload|Reaper|RebelMouse|Recorder|RedesScrapy|RepoMonkey|Ripper|RocketCrawler|Rogerbot|SBIder|SEOkicks|SEOkicks-Robot|SEOlyt|SEOlyticsCrawler|SEOprofiler|SEOstats|SISTRIX|SMTBot|SalesIntelligent|ScanAlert|Scanbot|ScoutJet|Scrapy|Screaming|ScreenerBot|ScrepyBot|Searchestate|SearchmetricsBot|Seekport|SeekportBot|SemanticJuice|Semrush|SemrushBot|SentiBot|SenutoBot|SeoSiteCheckup|SeobilityBot|Seomoz|Shodan|Siphon|SiteCheckerBotCrawler|SiteExplorer|SiteLockSpider|SiteSnagger|SiteSucker|Site Sucker|Sitebeam|Siteimprove|Sitevigil|SlySearch|SmartDownload|Snake|Snapbot|Snoopy|SocialRankIOBot|Sociscraper|Sogou web spider|Sosospider|Sottopop|SpaceBison|Spammen|SpankBot|Spanner|Spbot|Spider_Bot|Spider_Bot/3.0|Spinn3r|SputnikBot|Sqlmap|Sqlworm|Sqworm|Steeler|Stripper|Sucker|Sucuri|SuperBot|SuperHTTP|Surfbot|SurveyBot|Suzuran|Swiftbot|Szukacz|T0PHackTeam|T8Abot|Teleport|TeleportPro|Telesoft|Telesphoreo|Telesphorep|TheNomad|The Intraformant|Thumbor|TightTwatBot|TinyTestBot|Titan|Toata|Toweyabot|Tracemyfile|Trendiction|Trendictionbot|True_Robot|Turingos|Turnitin|TurnitinBot|TwengaBot|Twice|Typhoeus|URLy.Warning|URLy Warning|UnisterBot|Upflow|V-BOT|VB Project|VCI|Vacuum|Vagabondo|VelenPublicWebCrawler|VeriCiteCrawler|VidibleScraper|Virusdie|VoidEYE|Voil|Voltron|WASALive-Bot|WBSearchBot|WEBDAV|WISENutbot|WPScan|WWW-Collector-E|WWW-Mechanize|WWW::Mechanize|WWWOFFLE|Wallpapers|Wallpapers/3.0|WallpapersHD|WeSEE|WebAuto|WebBandit|WebCollage|WebCopier|WebEnhancer|WebFetch|WebFuck|WebGo IS|WebImageCollector|WebLeacher|WebPix|WebReaper|WebSauger|WebStripper|WebSucker|WebWhacker|WebZIP|Web Auto|Web Collage|Web Enhancer|Web Fetch|Web Fuck|Web Pix|Web Sauger|Web Sucker|Webalta|WebmasterWorldForumBot|Webshag|WebsiteExtractor|WebsiteQuester|Website Quester|Webster|Whack|Whacker|Whatweb|Who.is Bot|Widow|WinHTTrack|WiseGuys Robot|Wonderbot|Woobot|Wotbox|Wprecon|Xaldon WebSpider|Xaldon_WebSpider|Xenu|YaK|YoudaoBot|Zade|Zauba|Zermelo|Zeus|Zitebot|ZmEu|ZoomBot|ZoominfoBot|ZumBot|ZyBorg|adscanner|anthropic-ai|archive.org_bot|arquivo-web-crawler|arquivo.pt|autoemailspider|awario.com|backlink-check|cah.io.community|check1.exe|clark-crawler|coccocbot|cognitiveseo|cohere-ai|com.plumanalytics|crawl.sogou.com|crawler.feedback|crawler4j|dataforseo.com|dataforseobot|demandbase-bot|domainsproject.org|eCatch|evc-batch|facebookscraper|gopher|heritrix|imagesift.com|instabid|internetVista monitor|ips-agent|isitwp.com|iubenda-radar|linkdexbot|linkfluence|lwp-request|lwp-trivial|magpie-crawler|meanpathbot|mediawords|muhstik-scan|netEstate NE Crawler|oBot|omgili|openai|openai.com|page scorer|pcBrowser|plumanalytics|polaris version|probe-image-size|ripz|s1z.ru|satoristudio.net|scalaj-http|scan.lol|seobility|seocompany.store|seoscanners|seostar|serpstatbot|sexsearcher|sitechecker.pro|siteripz|sogouspider|sp_auditbot|spyfu|sysscan|tAkeOut|trendiction.com|trendiction.de|ubermetrics-technologies.com|voyagerx.com|webgains-bot|webmeup-crawler|webpros.com|webprosbot|x09Mozilla|x22Mozilla|xpymep1.exe|zauba.io|zgrab"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var Xi={metadata:{title:"Block OFAC-Sanctioned Countries",reference:"https://vercel.com/templates/other/block-ofac-sanctioned-countries-firewall-rule"},config:{rules:[{id:"rule_block_traffic_from_ofac_sanctioned_countries",name:"Block traffic from OFAC-sanctioned countries",description:"Blocks traffic from OFAC-sanctioned countries and enforces a one-hour persistent block after the first violation.",conditionGroup:[{conditions:[{type:"geo_country",op:"inc",value:["SY","IR","RU","CU","KP"]}]}],action:{mitigate:{action:"deny",actionDuration:"1h"}},active:!0}]}};var Qi={metadata:{title:"Deny Common WordPress URLs Firewall Rule",reference:"https://vercel.com/templates/other/block-wordpress-urls-firewall-rule"},config:{rules:[{name:"Deny WordPress URLs",description:"",conditionGroup:[{conditions:[{type:"path",op:"re",value:"/(wp-admin|wp-login\\.php|xmlrpc\\.php|wp-content|wp-includes|wp-signup\\.php|wp-activate\\.php|register\\.php|wp-register\\.php)"}]}],action:{mitigate:{action:"deny"}},active:!0}]}};var Zt={"ai-bots":Ki,"bad-bots":Ji,"block-ofac-sanctioned-countries":Xi,wordpress:Qi};var Yi=n=>Zt[n]?.config;ce();var qn="template [name]",Gn="Add a firewall rule template to your configuration",Hn={name:{type:"string",description:"Name of the template to add"},dryRun:{alias:"d",type:"boolean",description:"Preview changes without applying them",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}},Zn=()=>Object.keys(Zt),Kn=async n=>{try{n.debug&&(o.level=eo.LogLevels.debug),o.debug("Template command arguments:",n);let e=n.name;if(!e){let a=Zn();e=await C("Select a template to add:",{type:"select",options:a,initial:a[0]})}let t=Yi(e);t||(o.error(`Template not found: ${e}`),process.exit(1)),o.debug("Template content:",t),o.start("Loading current configuration...");let i=await G(void 0,"raw");if(n.dryRun){o.info(ze.default.cyan(`
177
+ Dry run - The following rules would be added:`)),o.log(JSON.stringify(t.rules,null,2));return}let r={...i,rules:[...i.rules,...t.rules]},s=we.safeParse(r);s.success||(o.error(ze.default.red("The resulting configuration would be invalid:")),s.error.errors.forEach(a=>{let l=a.path.join(".");o.error(ze.default.red(` - ${l}: ${a.message}`))}),o.info(ze.default.dim("Template was not applied. Fix the issues above and try again.")),process.exit(1)),o.start("Saving updated configuration..."),await z(r,void 0,{validate:!1}),o.success(ze.default.green(`Successfully added template '${e}' to configuration`))}catch(e){Q(e,"adding template")}};var Jt={};Z(Jt,{builder:()=>Qn,command:()=>Jn,desc:()=>Xn,handler:()=>Yn});var ie=S(require("chalk"));k();var Jn="validate",Xn="Validate firewall configuration file",Qn={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},verbose:{alias:"v",type:"boolean",description:"Show detailed validation results",default:!1}},Yn=async n=>{try{let e=await G(n.config,"raw"),t=Re.getInstance(),i=$e(e);n.verbose&&o.start(`Validating configuration file...
178
+ `);let r=i?mt.safeParse(e):we.safeParse(e);n.verbose&&(o.log(ie.default.bold.underline("Zod Schema Validation:")),r.success?o.log(ie.default.green("\u2713 Schema validation passed")):(o.error(ie.default.red("\u2717 Schema validation failed:")),r.error.errors.forEach(l=>{let c=l.path.join(".");o.error(ie.default.red(` - ${c}: ${l.message}`))})));let s=!0,a=[];try{t.validateConfig(e),n.verbose&&(o.log(ie.default.bold.underline(`
179
+ AJV Schema Validation:`)),o.log(ie.default.green("\u2713 JSON Schema validation passed")))}catch(l){if(s=!1,l instanceof V)a=l.ajvErrors||[],n.verbose&&(o.log(ie.default.bold.underline(`
180
180
  AJV Schema Validation:`)),o.error(ie.default.red("\u2717 JSON Schema validation failed:")),a.forEach(c=>{o.error(ie.default.red(` - ${c.instancePath}: ${c.message}`))}));else throw l}if(r.success&&s&&n.verbose){o.log(ie.default.bold.underline(`
181
181
  Custom Validations:`));let l=r.data,c=new Set,d=new Set;for(let u of l.rules)c.has(u.name)&&d.add(u.name),c.add(u.name);o.log(`${ie.default.cyan.dim(`
182
- Rule Names:`)}`),d.size>0?(o.error(ie.default.red("\u2717 Duplicate rule names found:")),d.forEach(u=>o.error(ie.default.red(` - "${u}"`)))):o.log(ie.default.green("\u2713 All rule names are unique")),o.log("")}if(r.success&&s)o.success(ie.default.green("Configuration is valid"));else throw new Error("Configuration validation failed")}catch(e){Q(e,"validating configuration")}};var Jt={};Z(Jt,{builder:()=>ir,command:()=>er,desc:()=>tr,handler:()=>or});var X=S(require("chalk")),st=require("fs");k();var er="watch",tr="Watch config file for changes and auto-sync",ir={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},interval:{alias:"i",type:"number",description:"Watch interval in milliseconds",default:1e3},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},or=async n=>{let e=n.config||".doorman.json";await ee({config:e,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"setting up watch mode"},async({service:t,provider:i})=>{let r=!1,s=0;o.success(X.default.green(`\u{1F440} Watching ${e} for changes...`)),o.log(X.default.dim("Press Ctrl+C to stop watching")),o.log("");let a=async(d,u)=>{if(!(r||d.mtime.getTime()===s)){r=!0,s=d.mtime.getTime();try{o.log(X.default.yellow(`\u{1F4DD} Config file changed at ${new Date().toLocaleTimeString()}`)),o.start("Validating and syncing changes...");let p=await G(e);if(i.name!=="vercel"){await nr(i,p),o.log(X.default.dim("Watching for more changes..."));return}let{toAdd:g,toUpdate:h,toDelete:b,ipsToAdd:f,ipsToUpdate:T,ipsToDelete:j,version:v}=await t.getChanges(p);if(!(g.length>0||h.length>0||b.length>0||f.length>0||T.length>0||j.length>0||p.version!==v)){o.info(X.default.blue("No changes detected, skipping sync"));return}let P=g.length+h.length+b.length+f.length+T.length+j.length;o.log(X.default.cyan(`Syncing ${P} changes...`));let w=await t.syncRules(p,{debug:n.debug});try{let I=await t.validateAndUpdateConfig(p,w,{dryRun:!1});await z(I,e),o.success(X.default.green(`\u2705 Sync completed at ${new Date().toLocaleTimeString()}`))}catch(I){o.warn(X.default.yellow(`\u26A0\uFE0F Sync applied but validation failed: ${I instanceof Error?I.message:String(I)}`)),o.info(X.default.dim("Run `download` to reconcile local config with remote state"))}o.log(X.default.dim("Watching for more changes..."))}catch(p){o.error(X.default.red(`\u274C Sync failed: ${p instanceof Error?p.message:String(p)}`)),o.log(X.default.dim("Continuing to watch for changes..."))}finally{r=!1}}};(0,st.watchFile)(e,{interval:n.interval},a);let l=()=>{o.log(`
183
- `),o.info(X.default.yellow("Stopping watch mode...")),(0,st.unwatchFile)(e),process.exit(0)};process.on("SIGINT",l),process.on("SIGTERM",l);let c=()=>{setTimeout(c,1e3)};c()})};async function nr(n,e){let t=await n.getChanges(e);if(!t.hasChanges){o.info(X.default.blue("No changes detected, skipping sync"));return}let i=t.rulesToAdd.length+t.rulesToUpdate.length+t.rulesToDelete.length+(t.ipsToAdd?.length??0)+(t.ipsToUpdate?.length??0)+(t.ipsToDelete?.length??0);o.log(X.default.cyan(`Syncing ${i} changes...`));let r=await n.syncRules(e,{force:!0});if(!r.success)throw new Error(`Sync failed: ${r.errors?.join(", ")||"unknown error"}`);o.success(X.default.green(`\u2705 Sync completed at ${new Date().toLocaleTimeString()}`))}var to=[Mt,Bt,bt,jt,qt,Nt,Gt,Kt,Ft,Zt,Wt,kt,Vt,Jt];(0,io.config)();async function rr(){let n=(await import("chalk")).default,e=(0,oo.default)(process.argv.slice(2));e.usage(`${n.bold("\u{1F6AA} Doorman")}
182
+ Rule Names:`)}`),d.size>0?(o.error(ie.default.red("\u2717 Duplicate rule names found:")),d.forEach(u=>o.error(ie.default.red(` - "${u}"`)))):o.log(ie.default.green("\u2713 All rule names are unique")),o.log("")}if(r.success&&s)o.success(ie.default.green("Configuration is valid"));else throw new Error("Configuration validation failed")}catch(e){Q(e,"validating configuration")}};var Xt={};Z(Xt,{builder:()=>ir,command:()=>er,desc:()=>tr,handler:()=>or});var X=S(require("chalk")),at=require("fs");k();var er="watch",tr="Watch config file for changes and auto-sync",ir={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},interval:{alias:"i",type:"number",description:"Watch interval in milliseconds",default:1e3},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},or=async n=>{let e=n.config||".doorman.json";await ee({config:e,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"setting up watch mode"},async({service:t,provider:i})=>{let r=!1,s=0;o.success(X.default.green(`\u{1F440} Watching ${e} for changes...`)),o.log(X.default.dim("Press Ctrl+C to stop watching")),o.log("");let a=async(d,u)=>{if(!(r||d.mtime.getTime()===s)){r=!0,s=d.mtime.getTime();try{o.log(X.default.yellow(`\u{1F4DD} Config file changed at ${new Date().toLocaleTimeString()}`)),o.start("Validating and syncing changes...");let p=await G(e);if(i.name!=="vercel"){await nr(i,p),o.log(X.default.dim("Watching for more changes..."));return}let{toAdd:f,toUpdate:h,toDelete:I,ipsToAdd:g,ipsToUpdate:A,ipsToDelete:N,version:v}=await t.getChanges(p);if(!(f.length>0||h.length>0||I.length>0||g.length>0||A.length>0||N.length>0||p.version!==v)){o.info(X.default.blue("No changes detected, skipping sync"));return}let P=f.length+h.length+I.length+g.length+A.length+N.length;o.log(X.default.cyan(`Syncing ${P} changes...`));let w=await t.syncRules(p,{debug:n.debug});try{let b=await t.validateAndUpdateConfig(p,w,{dryRun:!1});await z(b,e),o.success(X.default.green(`\u2705 Sync completed at ${new Date().toLocaleTimeString()}`))}catch(b){o.warn(X.default.yellow(`\u26A0\uFE0F Sync applied but validation failed: ${b instanceof Error?b.message:String(b)}`)),o.info(X.default.dim("Run `download` to reconcile local config with remote state"))}o.log(X.default.dim("Watching for more changes..."))}catch(p){o.error(X.default.red(`\u274C Sync failed: ${p instanceof Error?p.message:String(p)}`)),o.log(X.default.dim("Continuing to watch for changes..."))}finally{r=!1}}};(0,at.watchFile)(e,{interval:n.interval},a);let l=()=>{o.log(`
183
+ `),o.info(X.default.yellow("Stopping watch mode...")),(0,at.unwatchFile)(e),process.exit(0)};process.on("SIGINT",l),process.on("SIGTERM",l);let c=()=>{setTimeout(c,1e3)};c()})};async function nr(n,e){let t=await n.getChanges(e);if(!t.hasChanges){o.info(X.default.blue("No changes detected, skipping sync"));return}let i=t.rulesToAdd.length+t.rulesToUpdate.length+t.rulesToDelete.length+(t.ipsToAdd?.length??0)+(t.ipsToUpdate?.length??0)+(t.ipsToDelete?.length??0);o.log(X.default.cyan(`Syncing ${i} changes...`));let r=await n.syncRules(e,{force:!0});if(!r.success)throw new Error(`Sync failed: ${r.errors?.join(", ")||"unknown error"}`);o.success(X.default.green(`\u2705 Sync completed at ${new Date().toLocaleTimeString()}`)),r.warnings?.forEach(s=>o.warn(s))}var to=[qt,jt,vt,zt,Gt,Nt,Ht,Jt,Vt,Kt,Mt,St,Bt,Xt];(0,io.config)();async function rr(){let n=(await import("chalk")).default,e=(0,oo.default)(process.argv.slice(2));e.usage(`${n.bold("\u{1F6AA} Doorman")}
184
184
 
185
185
  ${n.dim("Manage firewall rules as code across multiple providers")}`);for(let t of to)e.command(t);e.demandCommand(1,"You need at least one command before moving on").help().epilogue(n.dim(`See ${n.bold("https://doorman.griffen.codes/getting-started")} for more info`)).argv}rr().catch(console.error);