@gfargo/doorman 3.15.4 → 3.15.5

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,4 +1,4 @@
1
- "use strict";var Po=Object.create;var Tt=Object.defineProperty;var _o=Object.getOwnPropertyDescriptor;var ko=Object.getOwnPropertyNames;var So=Object.getPrototypeOf,Uo=Object.prototype.hasOwnProperty;var At=(i,e)=>()=>(i&&(e=i(i=0)),e);var oe=(i,e)=>{for(var t in e)Tt(i,t,{get:e[t],enumerable:!0})},Si=(i,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of ko(e))!Uo.call(i,o)&&o!==t&&Tt(i,o,{get:()=>e[o],enumerable:!(n=_o(e,o))||n.enumerable});return i};var O=(i,e,t)=>(t=i!=null?Po(So(i)):{},Si(e||!i||!i.__esModule?Tt(t,"default",{value:i,enumerable:!0}):t,i)),Re=i=>Si(Tt({},"__esModule",{value:!0}),i);var Ui,r,_=At(()=>{"use strict";Ui=require("consola"),r=(0,Ui.createConsola)({formatOptions:{colors:!0,compact:!0,date:!1}})});var xi,C,$e=At(()=>{"use strict";xi=O(require("consola")),C=async(i,e)=>{if(!process.stdin.isTTY)throw new Error(`Cannot prompt for "${i}" \u2014 no interactive terminal available. Re-run with the appropriate non-interactive flag for this command (e.g. --interactive=false, --ci, --force).`);let t=await xi.default.prompt(i,e);return t!=null&&t.toString()==="Symbol(clack:cancel)"&&process.exit(0),(!e?.type||e.type==="text")&&t===void 0?"":t}});var Se={};oe(Se,{TranslationWarningSystem:()=>U});var U,pe=At(()=>{"use strict";U=class i{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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.DOCS_BASE_URL}/migration/plan-limits`}};static createWarning(e,t,n,o,s){let a=this.WARNING_CONFIGS[e];return a?{rule:t,field:n,category:a.category,message:o||`${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:n,category:"compatibility_issue",message:o||`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,n,o,s){return{rule:o,field:s,category:"feature_unsupported",message:`${e} is not supported in ${n}`,explanation:`The ${e} feature from ${t} cannot be directly translated to ${n} due to platform differences.`,severity:"critical",suggestion:`Remove or replace ${e} with equivalent functionality supported by ${n}.`,alternativeApproach:`Check ${n} 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,n,o){return{rule:n,field:o,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,n,o){return{rule:n,field:o,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,n,o){return{rule:n,field:o,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 n={critical:"\u{1F6A8}",warning:"\u26A0\uFE0F",info:"\u2139\uFE0F"}[e.severity],o=e.rule?` (Rule: ${e.rule}${e.field?`, Field: ${e.field}`:""})`:"",s=`${n} ${e.severity.toUpperCase()}: ${e.message}${o}
1
+ "use strict";var Po=Object.create;var Tt=Object.defineProperty;var _o=Object.getOwnPropertyDescriptor;var ko=Object.getOwnPropertyNames;var So=Object.getPrototypeOf,Uo=Object.prototype.hasOwnProperty;var At=(i,e)=>()=>(i&&(e=i(i=0)),e);var oe=(i,e)=>{for(var t in e)Tt(i,t,{get:e[t],enumerable:!0})},Si=(i,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of ko(e))!Uo.call(i,o)&&o!==t&&Tt(i,o,{get:()=>e[o],enumerable:!(n=_o(e,o))||n.enumerable});return i};var O=(i,e,t)=>(t=i!=null?Po(So(i)):{},Si(e||!i||!i.__esModule?Tt(t,"default",{value:i,enumerable:!0}):t,i)),Re=i=>Si(Tt({},"__esModule",{value:!0}),i);var Ui,r,_=At(()=>{"use strict";Ui=require("consola"),r=(0,Ui.createConsola)({formatOptions:{colors:!0,compact:!0,date:!1}})});var xi,C,$e=At(()=>{"use strict";xi=O(require("consola")),C=async(i,e)=>{if(!process.stdin.isTTY)throw new Error(`Cannot prompt for "${i}" \u2014 no interactive terminal available. Re-run with the appropriate non-interactive flag for this command (e.g. --interactive=false, --ci, --force; "sync --yes" creates a missing first firewall config).`);let t=await xi.default.prompt(i,e);return t!=null&&t.toString()==="Symbol(clack:cancel)"&&process.exit(0),(!e?.type||e.type==="text")&&t===void 0?"":t}});var Se={};oe(Se,{TranslationWarningSystem:()=>U});var U,pe=At(()=>{"use strict";U=class i{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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.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:`${i.DOCS_BASE_URL}/migration/plan-limits`}};static createWarning(e,t,n,o,s){let a=this.WARNING_CONFIGS[e];return a?{rule:t,field:n,category:a.category,message:o||`${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:n,category:"compatibility_issue",message:o||`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,n,o,s){return{rule:o,field:s,category:"feature_unsupported",message:`${e} is not supported in ${n}`,explanation:`The ${e} feature from ${t} cannot be directly translated to ${n} due to platform differences.`,severity:"critical",suggestion:`Remove or replace ${e} with equivalent functionality supported by ${n}.`,alternativeApproach:`Check ${n} 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,n,o){return{rule:n,field:o,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,n,o){return{rule:n,field:o,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,n,o){return{rule:n,field:o,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 n={critical:"\u{1F6A8}",warning:"\u26A0\uFE0F",info:"\u2139\uFE0F"}[e.severity],o=e.rule?` (Rule: ${e.rule}${e.field?`, Field: ${e.field}`:""})`:"",s=`${n} ${e.severity.toUpperCase()}: ${e.message}${o}
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}
@@ -21,8 +21,8 @@ ${o}`}};var B=class i extends Error{constructor(t,n,o,s=[]){super(t);this.ajvErr
21
21
  Dry run - The following IP rule would be added:`)),Ji(t),r.log(""),r.log(N.default.dim(JSON.stringify(t,null,2)));return}r.start("Loading configuration...");let o=await Z(i.config,"raw"),s={...o,ips:[...o.ips||[],t]};await Y(s,i.config),r.success(N.default.green(`\u2714 IP rule "${t.ip}" added to configuration`)),Ji(t)}else{let t=i.interactive?await tr():ir(i),n=Wt.safeParse(t);if(n.success||(r.error(N.default.red("Rule validation failed:")),n.error.errors.forEach(c=>{let d=c.path.join(".")||"rule";r.error(N.default.red(` - ${d}: ${c.message}`))}),process.exit(1)),i.dryRun){r.info(N.default.cyan(`
22
22
  Dry run - The following rule would be added:`)),Yi(t),r.log(""),r.log(N.default.dim(JSON.stringify(t,null,2)));return}r.start("Loading configuration...");let o=await Z(i.config,"raw"),s=o.rules||[],a=rr({...o,rules:s},t);if(a){if(r.warn(N.default.yellow(`\u26A0\uFE0F ${a}`)),i.ci||!process.stdin.isTTY){r.info(N.default.dim("Skipping \u2014 non-interactive and a conflicting rule already exists. Rule not added."));return}if(!await C("Proceed anyway?",{type:"confirm",initial:!1})){r.info("Cancelled.");return}}let l={...o,rules:[...s,t]};await Y(l,i.config),r.success(N.default.green(`\u2714 Rule "${t.name}" added to configuration`)),Yi(t)}r.log(""),r.log(N.default.dim(`Run ${N.default.cyan("doorman sync")} to deploy this rule.`))}catch(e){re(e,"adding rule")}};var fi={};oe(fi,{builder:()=>ks,command:()=>Ps,desc:()=>_s,handler:()=>Ss});var ne=O(require("chalk")),Te=require("fs"),at=require("path"),ro=require("consola");_();$e();var ut=require("fs"),Xt=require("path");function en(i=process.cwd()){return(0,ut.existsSync)((0,Xt.join)(i,".git"))}function tn(i,e=process.cwd()){let t=(0,Xt.join)(e,".gitignore");if(!(0,ut.existsSync)(t))return!1;let n=i.replace(/^\.\//,"").replace(/^\//,"").replace(/\/$/,""),o;try{o=(0,ut.readFileSync)(t,"utf8").split(`
23
23
  `)}catch{return!1}return o.some(s=>{let a=s.trim();if(!a||a.startsWith("#"))return!1;let l=a.replace(/^\//,"").replace(/\/$/,"");return l===n||n.startsWith(`${l}/`)})}var se={type:"string",choices:H,description:"Firewall provider (auto-detected)"};var oo=require("consola");_();var io=O(require("chalk"));_();$e();var nn=require("readline");function ar(i,e){return e?.ctrl&&e.name==="c"?{type:"cancel"}:e?.name==="return"||e?.name==="enter"?{type:"submit"}:e?.name==="backspace"?{type:"backspace"}:i&&!e?.ctrl&&!e?.meta&&i.length===1&&i>=" "?{type:"append",char:i}:{type:"ignore"}}function lr(i,e){switch(e.type){case"append":return{value:i+e.char,done:!1,cancelled:!1,echo:"add"};case"backspace":return i.length===0?{value:i,done:!1,cancelled:!1,echo:"none"}:{value:i.slice(0,-1),done:!1,cancelled:!1,echo:"remove"};case"submit":return{value:i,done:!0,cancelled:!1,echo:"none"};case"cancel":return{value:i,done:!0,cancelled:!0,echo:"none"};case"ignore":return{value:i,done:!1,cancelled:!1,echo:"none"}}}async function on(i){if(process.stdout.write(i),!process.stdin.isTTY){let{createInterface:e}=await import("readline"),t=e({input:process.stdin,output:process.stdout,terminal:!1});return new Promise(n=>{t.question("",o=>{t.close(),n(o)})})}return new Promise(e=>{let t=process.stdin,n=t.isRaw;(0,nn.emitKeypressEvents)(t),t.setRawMode(!0),t.resume();let o="",s=()=>{t.removeListener("keypress",a),t.setRawMode(n??!1)},a=(l,c)=>{let d=ar(l,c),u=lr(o,d);o=u.value,u.echo==="add"?process.stdout.write("*"):u.echo==="remove"&&process.stdout.write("\b \b"),u.done&&(s(),process.stdout.write(`
24
- `),u.cancelled&&process.exit(0),e(o))};t.on("keypress",a)})}_();var rn={provider:"vercel",fields:[{key:"token",envVar:"VERCEL_TOKEN",label:"Vercel API Token",required:!0,secret:!0,promptMessage:"What is your Vercel API Auth Token? (https://vercel.com/guides/how-do-i-use-a-vercel-api-access-token#creating-an-access-token) "},{key:"projectId",envVar:"VERCEL_PROJECT_ID",label:"Vercel Project ID",required:!0,configKey:"projectId",cliAlias:"p",legacyConfigKey:"projectId",promptMessage:"What is your Vercel Project ID? (See https://vercel.com/docs/projects/project-configuration/general-settings#project-id)"},{key:"teamId",envVar:"VERCEL_TEAM_ID",label:"Vercel Team ID",required:!1,configKey:"teamId",cliAlias:"t",legacyConfigKey:"teamId",promptMessage:"What is your Vercel Team ID? Leave blank to use your Vercel default team (every account, including Hobby, is a team). Only needed if you belong to more than one team and want to target a non-default one. (See https://vercel.com/docs/accounts#find-your-team-id)"}]};var sn={provider:"cloudflare",fields:[{key:"apiToken",envVar:"CLOUDFLARE_API_TOKEN",label:"Cloudflare API Token",required:!0,secret:!0},{key:"zoneId",envVar:"CLOUDFLARE_ZONE_ID",label:"Cloudflare Zone ID",required:!0,configKey:"zoneId"},{key:"accountId",envVar:"CLOUDFLARE_ACCOUNT_ID",label:"Cloudflare Account ID",required:!1,configKey:"accountId"}]};var an={provider:"fastly",fields:[{key:"apiToken",envVar:"FASTLY_API_TOKEN",label:"Fastly API Token",required:!0,secret:!0},{key:"workspaceId",envVar:"FASTLY_WORKSPACE_ID",label:"Fastly Next-Gen WAF Workspace ID",required:!0,configKey:"workspaceId"}]};var ln={provider:"gcp",fields:[{key:"serviceAccountKeyPath",envVar:"GOOGLE_APPLICATION_CREDENTIALS",label:"GCP Service Account Key Path",required:!1,promptMessage:"Path to a GCP service account JSON key file (leave blank to use Application Default Credentials \u2014 gcloud auth application-default login, or the GCE/Cloud Run metadata server): "},{key:"projectId",envVar:"GOOGLE_CLOUD_PROJECT",label:"GCP Project ID",required:!0,configKey:"projectId"},{key:"policyName",envVar:"GCP_POLICY_NAME",label:"Cloud Armor Security Policy Name",required:!0,configKey:"policyName"}]};function cn(i,e={}){let{explicit:t={},config:n={},env:o=process.env}=e,s={};for(let a of i.fields)s[a.key]=t[a.key]||n[a.key]||o[a.envVar];return s}function dn(i,e){return i.fields.filter(t=>t.required&&!e[t.key])}var He={vercel:rn,cloudflare:sn,fastly:an,gcp:ln};var St=class{static detectionField(e){return He[e].fields.find(t=>t.required&&t.configKey)}static detect(e){let t=[];if(e&&"provider"in e){let o=e.provider;if(typeof o=="string"&&this.isValidProvider(o))return t.push(`Explicit provider specified in config: ${e.provider}`),{provider:o,confidence:"high",reasons:t};r.warn(`Invalid provider specified in config: ${String(e.provider)}`)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let o=e.providers;for(let s of H){let a=this.detectionField(s);if(!a?.configKey)continue;if(typeof(o[s]||{})[a.configKey]=="string")return t.push(`${a.label} found in config`),{provider:s,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 n=this.detectFromEnvironment();return n.provider?n:(r.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;if(t&&this.isValidProvider(t))return e.push(`Provider set via DOORMAN_PROVIDER environment variable: ${t}`),{provider:t,confidence:"high",reasons:e};for(let n of H){let o=this.detectionField(n);if(!(!o||!process.env[o.envVar]))return e.push(`${o.label} found in environment (${o.envVar})`),{provider:n,confidence:"medium",reasons:e}}return{provider:null,confidence:"low",reasons:["No provider credentials found in environment"]}}static isValidProvider(e){return H.includes(e)}static getProvider(e,t="vercel"){let n=this.detect(e);return n.provider?(r.debug(`Provider detected: ${n.provider} (${n.confidence} confidence)`),n.reasons.length>0&&r.debug(`Detection reasons: ${n.reasons.join(", ")}`),n.provider):(r.debug(`No provider detected, using fallback: ${t}`),t)}static detectAll(e){let t=new Set;if(e&&"provider"in e){let n=e.provider;typeof n=="string"&&this.isValidProvider(n)&&t.add(n)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let n=e.providers;for(let o of H){let s=this.detectionField(o);if(!s?.configKey)continue;typeof(n[o]||{})[s.configKey]=="string"&&t.add(o)}}e&&"projectId"in e&&typeof e.projectId=="string"&&t.add("vercel");for(let n of H){let o=this.detectionField(n);o&&process.env[o.envVar]&&t.add(n)}return Array.from(t)}};var ei=O(require("chalk"));_();$e();var Ut=i=>({$schema:Di,firewallEnabled:!0,rules:[],ips:[],...i});_();var _e=class{baseUrl;providerName;rateLimitInfo={};constructor(e,t){this.baseUrl=e,this.providerName=t}async makeRequest(e,t={}){let n=`${this.baseUrl}${e}`,{timeout:o=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()},o);let g=await fetch(n,{...l,headers:{"Content-Type":"application/json",...await this.getAuthHeaders(),...l.headers},signal:d.signal});if(u&&(clearTimeout(u),u=null),this.updateRateLimitInfo(g),g.status===429){let v=this.calculateRateLimitWait(f,g);if(f===s)throw new Error(`${this.providerName} API rate limit exceeded after ${s+1} attempt(s) (429 Too Many Requests). Retry after ~${Math.round(v/1e3)}s.`);r.warn(`Rate limit exceeded for ${this.providerName}. Waiting ${v}ms before retry (attempt ${f+1}/${s+1})...`),await this.delay(v);continue}if(!g.ok)throw await this.handleErrorResponse(g);let m;try{let v=await g.text();v.trim()?m=JSON.parse(v):m={}}catch(v){throw r.error(`Failed to parse response from ${n}:`,v),new Error(`Invalid JSON response from ${this.providerName} API: ${v instanceof Error?v.message:String(v)}`)}return this.isLargeResponse(m)&&(r.debug(`Large response detected from ${n}, validating structure...`),this.validateLargeResponse(m)),m}catch(g){if(c=g,u&&(clearTimeout(u),u=null),this.isNonRetryableError(g)||f===s)throw g;let m=a*Math.pow(2,f),v=Math.random()*.1*m,z=Math.min(m+v,3e4);r.debug(`Request failed (attempt ${f+1}/${s+1}). Retrying in ${Math.round(z)}ms...`),r.debug(`Error details: ${g instanceof Error?g.message:String(g)}`),await this.delay(z)}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,n){return this.makeRequest(e,{...n,method:"POST",body:t?JSON.stringify(t):void 0})}async put(e,t,n){return this.makeRequest(e,{...n,method:"PUT",body:t?JSON.stringify(t):void 0})}async patch(e,t,n){return this.makeRequest(e,{...n,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"),n=e.headers.get("X-RateLimit-Remaining"),o=e.headers.get("X-RateLimit-Reset");t&&(this.rateLimitInfo.limit=parseInt(t,10)),n&&(this.rateLimitInfo.remaining=parseInt(n,10)),o&&(this.rateLimitInfo.reset=parseInt(o,10)),this.rateLimitInfo.remaining&&this.rateLimitInfo.remaining<10&&r.warn(`Approaching rate limit for ${this.providerName}: ${this.rateLimitInfo.remaining} requests remaining`)}calculateRateLimitWait(e=0,t){let o=t?this.parseRetryAfter(t.headers.get("Retry-After")):null;if(o!==null)return Math.min(Math.max(o,1e3),6e4);if(this.rateLimitInfo.reset){let l=Math.floor(Date.now()/1e3),c=(this.rateLimitInfo.reset-l)*1e3;return Math.min(Math.max(c,1e3),6e4)}let a=5e3*Math.pow(2,e);return Math.min(a,6e4)}parseRetryAfter(e){if(!e)return null;let t=Number(e);if(Number.isFinite(t))return t*1e3;let n=Date.parse(e);return Number.isNaN(n)?null:n-Date.now()}async handleErrorResponse(e){let t=`${this.providerName} API error: ${e.status} ${e.statusText}`,n=t;try{let o=await e.json();if(typeof o=="object"&&o!==null){let s=o;if(typeof s.error=="string")n=`${t} - ${s.error}`;else if(typeof s.message=="string")n=`${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(", ");n=`${t} - ${a}`}}}catch{}return r.error(n),new Error(n)}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=(n,o=0)=>{if(o>10)return!1;for(let[,s]of Object.entries(n))if(Array.isArray(s)&&s.length>1e3||typeof s=="object"&&s!==null&&t(s,o+1))return!0;return!1};return t(e)}validateLargeResponse(e){if(!e||typeof e!="object")return;let t=(o,s)=>{o.length>5e3&&r.warn(`Large array detected at ${s} with ${o.length} items. This may impact performance.`);let a=Math.min(10,o.length),l=o[0];if(l&&typeof l=="object"){let c=Object.keys(l);for(let d=1;d<a;d++){let u=o[d];if(!u||typeof u!="object"){r.warn(`Inconsistent array structure at ${s}[${d}]: expected object, got ${typeof u}`);continue}let p=Object.keys(u),f=c.filter(g=>!p.includes(g));f.length>0&&r.warn(`Inconsistent array structure at ${s}[${d}]: missing keys ${f.join(", ")}`)}}},n=(o,s="root",a=0)=>{if(!(a>10))for(let[l,c]of Object.entries(o)){let d=`${s}.${l}`;Array.isArray(c)?t(c,d):typeof c=="object"&&c!==null&&n(c,d,a+1)}};n(e)}};var un=process.env.DOORMAN_VERCEL_API_BASE_URL||"https://api.vercel.com/v1/security/firewall/config",Ze=class extends _e{constructor(t,n,o){super("","vercel");this.projectId=t;this.teamId=n;this.token=o}projectId;teamId;token;async getAuthHeaders(){return{Authorization:`Bearer ${this.token}`}}getUrl(t){let n=t!==void 0?`${un}/${t}`:un;r.debug("API URL:",n);let o=this.teamId?`&teamId=${this.teamId}`:"";return`${n}?projectId=${this.projectId}${o}`}async fetchFirewallConfig(t,n){let{allowCreate:o=!0}=n??{},s=await this.get(this.getUrl(t));if(r.debug("Config Version:",t??"latest"),r.debug("Fetched Config:",t?s:s.active),t)return s;if(!s||"active"in s&&s.active===null){if(r.warn(ei.default.bold("No firewall configuration found.")),!o)return r.debug("Skipping create-configuration prompt for read-only fetch."),this.emptyConfigPlaceholder();if(await C("Would you like to create one?",{type:"confirm"})){r.debug("Creating new empty firewall configuration...");let l=await this.putEmptyConfig();return r.debug("Empty firewall configuration created successfully"),r.debug(`New configuration version: ${ei.default.yellow(l.version)}`),l}else return s.active}return s.active}emptyConfigPlaceholder(){return{version:0,id:"",firewallEnabled:!0,crs:null,rules:[],ips:[],projectKey:"",ownerId:"",updatedAt:new Date(0).toISOString()}}async putEmptyConfig(){let{$schema:t,...n}=Ut();return r.debug("Empty Config:",n),this.putConfig(n)}async putConfig(t){return(await this.put(this.getUrl(),t)).active}async fetchActiveFirewallRules(){return(await this.fetchFirewallConfig())?.rules}async updateFirewallRule(t){let n=!t.id||t.id==="-",o={action:n?"rules.insert":"rules.update",id:n?null:t.id,value:{name:t.name,description:t.description,action:t.action,conditionGroup:t.conditionGroup,active:t.active}};return this.patch(this.getUrl(),o,n?{retries:0}:void 0)}async createFirewallRule(t){return this.updateFirewallRule({...t,id:"-"})}async deleteFirewallRule(t){let n={action:"rules.remove",id:t.id,value:null};await this.patch(this.getUrl(),n)}async updateIPBlockingRule(t){let n=!t.id||t.id==="-",o={action:n?"ip.insert":"ip.update",id:n?null:t.id,value:{action:t.action,hostname:t.hostname,ip:t.ip,notes:t.notes??null}};return this.patch(this.getUrl(),o,n?{retries:0}:void 0)}async createIPBlockingRule(t){return this.updateIPBlockingRule({...t,id:"-"})}async deleteIPBlockingRule(t){let n={action:"ip.remove",id:t.id,value:null};await this.patch(this.getUrl(),n)}async verifyCredentials(){try{return await this.fetchFirewallConfig(),!0}catch(t){return r.debug("Credential verification failed:",t),!1}}};var fe=O(require("chalk"));_();_();var ke=class{validateConfig(e){let t=[],n=[];if(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"}),e&&Array.isArray(e.rules)){let o=new Map;e.rules.forEach((s,a)=>{let l=s.id||s.name;if(!l)return;let c=o.get(l);if(c===void 0){o.set(l,a);return}let d=e.rules[c];n.push({path:`rules[${a}]`,message:s.id?`Rule "${s.name}" has the same id ("${s.id}") as rules[${c}] ("${d?.name}") \u2014 they will collide during sync diffing and one may be silently skipped.`:`Rule "${s.name}" has the same name as rules[${c}] and neither has an explicit id \u2014 they will collide during sync diffing and one may be silently skipped. Add unique ids to disambiguate.`,code:"DUPLICATE_RULE_IDENTIFIER"})})}return{valid:t.length===0,errors:t,warnings:n}}getHealthScore(e){let t=[],n=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)&&(n-=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&&(n-=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&&(n-=5,t.push({severity:"info",category:"maintenance",message:`${a.length} disabled rule(s) found`,suggestion:"Review and remove unused rules"}))}let o=this.calculateGrade(n),s=this.generateRecommendations(e,t);return{score:Math.max(0,n),grade:o,issues:t,recommendations:s}}diffItems(e,t,n,o="id"){let s=[],a=[],l=[],c=d=>d[o];for(let d of e){let u=t.find(p=>c(p)===c(d));u?n(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}}assertManagedRulesSupported(e){if(!(!e.managedRules||e.managedRules.length===0)&&!this.getSupportedFeatures().supportsManagedRules)throw new Error(`${this.name} does not support managed rule groups, but this config declares ${e.managedRules.length}. Remove \`managedRules\` from the config, or switch to a provider that supports them (currently Cloudflare only).`)}validateRuleCount(e,t){if(t&&e>t)throw new Error(`Rule count (${e}) exceeds provider limit (${t})`);t&&e>t*.9&&r.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 n=[];return t.some(o=>o.category==="rules")&&n.push("Consider using predefined templates to get started"),t.some(o=>o.category==="maintainability")&&n.push("Add descriptions to all rules for better documentation"),t.some(o=>o.category==="maintenance")&&n.push("Remove or enable disabled rules to keep configuration clean"),n}logSyncStats(e){r.info(`Sync complete for ${this.name}:`),r.info(` Rules added: ${e.rulesAdded}`),r.info(` Rules updated: ${e.rulesUpdated}`),r.info(` Rules deleted: ${e.rulesDeleted}`),e.ipsAdded!==void 0&&(r.info(` IPs added: ${e.ipsAdded}`),r.info(` IPs updated: ${e.ipsUpdated}`),r.info(` IPs deleted: ${e.ipsDeleted}`)),e.managedRulesAdded!==void 0&&(r.info(` Managed rule groups added: ${e.managedRulesAdded}`),r.info(` Managed rule groups updated: ${e.managedRulesUpdated}`),r.info(` Managed rule groups deleted: ${e.managedRulesDeleted}`)),e.version&&r.info(` New version: ${e.version}`)}};pe();function pn(i){let e=[],t=[];i.conditionGroup.forEach((s,a)=>{for(let l of s.conditions){let c=dr(l.op);t.push({field:fr(l.type),operator:c,...l.value!==void 0?{value:l.value}:{},...l.neg?{negated:!0}:{},...l.key?{key:l.key}:{},group:a})}}),t.length>10&&e.push(U.createWarning("many_conditions",i.id,void 0,`Rule has ${t.length} conditions which may impact performance`,"Consider splitting complex rules into multiple simpler rules for better performance"));let n={type:i.action.mitigate.action,...i.action.mitigate.rateLimit?{rateLimit:{requests:i.action.mitigate.rateLimit.requests,window:i.action.mitigate.rateLimit.window,...i.action.mitigate.rateLimit.characteristics?{characteristics:i.action.mitigate.rateLimit.characteristics}:{},...i.action.mitigate.rateLimit.mitigationTimeout?{mitigationTimeout:i.action.mitigate.rateLimit.mitigationTimeout}:{},...i.action.mitigate.rateLimit.countingExpression?{countingExpression:i.action.mitigate.rateLimit.countingExpression}:{}}}:{},...i.action.mitigate.redirect?{redirect:{location:i.action.mitigate.redirect.location,...i.action.mitigate.redirect.permanent?{permanent:i.action.mitigate.redirect.permanent}:{}}}:{},...i.action.mitigate.actionDuration?{duration:i.action.mitigate.actionDuration}:{}};return{result:{id:i.id,name:i.name,...i.description?{description:i.description}:{},enabled:i.active,conditions:t,conditionLogic:i.conditionGroup.length>1?"OR":"AND",action:n},warnings:e}}function fn(i){let e=[],t=cr(i.conditions,i.id,e);if(i.action.response&&e.push(U.createUnsupportedFeatureWarning("custom response body","unified config","Vercel",i.id,"action.response")),t.length===0)throw new Error(`Rule "${i.name}" has no conditions Vercel can represent \u2014 every condition field is unsupported by Vercel (e.g. Cloudflare-only fields like referer/port). Cannot sync this rule to Vercel.`);return{result:{id:i.id,name:i.name,description:i.description,conditionGroup:t,action:{mitigate:{action:pr(i.action.type),rateLimit:i.action.rateLimit?{requests:i.action.rateLimit.requests,window:i.action.rateLimit.window,characteristics:i.action.rateLimit.characteristics,mitigationTimeout:i.action.rateLimit.mitigationTimeout,countingExpression:i.action.rateLimit.countingExpression}:null,redirect:i.action.redirect?{location:i.action.redirect.location,permanent:i.action.redirect.permanent}:null,actionDuration:i.action.duration||null}},active:i.enabled},warnings:e}}function gn(i){return{id:i.id,ip:i.ip,...i.hostname?{hostname:i.hostname}:{},...i.notes?{notes:i.notes}:{},action:i.action}}function cr(i,e,t){let n=new Map;for(let s of i){let a=s.group??0,l=n.get(a);l?l.push(s):n.set(a,[s])}let o=[];for(let s of n.values()){let a=[];for(let l of s){let c=mr(l.field);if(!c){t.push(U.createUnsupportedFeatureWarning(`condition field '${l.field}'`,"unified config","Vercel",e,l.field));continue}let d=ur(l.operator);if(!d){t.push(U.createUnsupportedFeatureWarning(`operator '${l.operator}'`,"unified config","Vercel",e,l.field));continue}a.push({op:d.op,neg:d.forceNeg!==!!l.negated,type:c,key:l.key,value:l.value})}a.length>0&&o.push({conditions:a})}return o}function dr(i){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists",nex:"not_exists"}[i]||"eq"}function ur(i){return{eq:{op:"eq",forceNeg:!1},ne:{op:"eq",forceNeg:!0},starts_with:{op:"pre",forceNeg:!1},ends_with:{op:"suf",forceNeg:!1},in:{op:"inc",forceNeg:!1},not_in:{op:"inc",forceNeg:!0},contains:{op:"sub",forceNeg:!1},not_contains:{op:"sub",forceNeg:!0},matches:{op:"re",forceNeg:!1},exists:{op:"ex",forceNeg:!1},not_exists:{op:"nex",forceNeg:!1}}[i]??null}function pr(i){return{log:"log",deny:"deny",challenge:"challenge",bypass:"bypass",rate_limit:"rate_limit",redirect:"redirect",allow:"bypass",block:"deny"}[i]}function fr(i){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",region:"vercel_region",geo_country_region:"region"}[i]||i}var gr={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",target_path:"target_path",vercel_region:"region",region:"geo_country_region",protocol:"protocol",environment:"environment",geo_continent:"geo_continent",ja4_digest:"ja4_digest",ja3_digest:"ja3_digest",rate_limit_api_id:"rate_limit_api_id"};function mr(i){return gr[i]??null}pe();_();function Ye(i){return i.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function mn(i){let e="";for(let t=0;t<i.length;t++)i[t]==="\\"&&(i[t+1]==="\\"||i[t+1]==='"')?(e+=i[t+1],t++):e+=i[t];return e}var pt=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","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 n=this.vercelToCloudflare[e];if(!n)throw r.warn(`No Cloudflare mapping for Vercel type: ${e}`),new Error(`Unsupported Vercel rule type for Cloudflare: ${e}`);return e==="header"&&t?`${n}["${Ye(t.toLowerCase())}"]`:e==="cookie"&&t?`${n}["${Ye(t)}"]`:n}static toVercel(e){let t=e.match(/http\.request\.headers\["([^"]+)"\]/);if(t)return{type:"header",key:t[1]};let n=e.match(/http\.cookie\["([^"]+)"\]/);if(n)return{type:"cookie",key:n[1]};if(e==="http.referer")return{type:"header",key:"referer"};let o=this.cloudflareToVercel[e];if(o)return{type:o};throw r.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==="http.referer"||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 hr=new Set(["ip.src"]),yr="http.request.uri.args",Rr="http.request.headers",wr="http.request.cookies",Je=class{static fromVercelConditionGroups(e){if(!e||e.length===0)throw new Error("At least one condition group is required");let t=e.map(n=>{let o=n.conditions.map(s=>this.fromVercelCondition(s));if(o.length===0)throw new Error("Condition group must have at least one condition");return o.length>1?`(${o.join(" and ")})`:o[0]});return t.length>1?t.join(" or "):t[0]}static fromVercelCondition(e){let t=pt.toCloudflare(e.type,e.key),n=this.buildExistsOrComparisonExpression(t,e.op,()=>`${this.mapVercelOperator(e.op)} ${this.formatValue(t,e.value)}`);return e.neg&&(n=`not (${n})`),n}static fromUnifiedConditions(e,t="AND"){if(!e||e.length===0)throw new Error("At least one condition is required");if(!e.some(a=>a.group!==void 0)){let a=e.map(c=>this.fromUnifiedCondition(c)),l=t==="AND"?" and ":" or ";return a.length>1?`(${a.join(l)})`:a[0]}let o=new Map;for(let a of e){let l=a.group??0,c=o.get(l);c?c.push(a):o.set(l,[a])}let s=Array.from(o.values()).map(a=>this.combineWithAnd(a.map(l=>this.fromUnifiedCondition(l))));return s.length>1?this.combineWithOr(s):s[0]}static fromUnifiedCondition(e){if(e.key&&e.field==="query")return this.buildKeyedMapExpression(yr,e.key,e);if(e.field==="header"){if(!e.key)throw new Error('A "header" condition requires a key naming the header');return this.buildKeyedMapExpression(Rr,e.key.toLowerCase(),e)}if(e.key&&e.field==="cookie")return this.buildKeyedMapExpression(wr,e.key,e);let t=this.mapUnifiedFieldToCloudflare(e.field);if(t===null)throw new Error(`Unsupported condition field '${e.field}' for Cloudflare \u2014 filter it out with a warning before calling fromUnifiedCondition (see unifiedToCloudflare).`);let n=this.buildUnifiedExpression(t,e.operator,e.value);return e.negated&&(n=`not (${n})`),n}static buildKeyedMapExpression(e,t,n){let o=Ye(t),s=`${e}["${o}"]`,a;if(n.operator==="exists")a=`has_key(${e}, "${o}")`;else if(n.operator==="not_exists")a=`not (has_key(${e}, "${o}"))`;else if(n.operator==="not_contains")a=`not (any(${s}[*] contains ${this.formatValue(e,n.value)}))`;else if(n.operator==="not_in")a=`not (any(${s}[*] in ${this.formatValue(e,n.value)}))`;else{let l=this.mapUnifiedOperator(n.operator);a=`any(${s}[*] ${l} ${this.formatValue(e,n.value)})`}return n.negated?`not (${a})`:a}static buildExistsOrComparisonExpression(e,t,n){return t==="ex"||t==="exists"?`${e} exists`:t==="nex"||t==="not_exists"?`not (${e} exists)`:`${e} ${n()}`}static buildUnifiedExpression(e,t,n){return t==="not_contains"?`not (${e} contains ${this.formatValue(e,n)})`:t==="not_in"?`not (${e} in ${this.formatValue(e,n)})`:this.buildExistsOrComparisonExpression(e,t,()=>`${this.mapUnifiedOperator(t)} ${this.formatValue(e,n)}`)}static mapVercelOperator(e){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists"}[e]||e}static mapUnifiedOperator(e){return{eq:"eq",ne:"ne",contains:"contains",starts_with:"starts_with",ends_with:"ends_with",matches:"matches",in:"in",gt:"gt",ge:"ge",lt:"lt",le:"le",exists:"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",threat_score:"cf.threat_score"}[e]??null}static isFieldSupported(e){return this.mapUnifiedFieldToCloudflare(e)!==null}static formatValue(e,t){return Array.isArray(t)?`{${t.map(o=>this.formatSingleValue(e,o)).join(" ")}}`:this.formatSingleValue(e,t)}static formatSingleValue(e,t){if(typeof t=="string"){if(hr.has(e)){if(!Pe.safeParse(t).success)throw new Error(`Invalid IP address or CIDR range: ${t}`);return t}return`"${Ye(t)}"`}return typeof t=="number"?String(t):typeof t=="boolean"?t?"true":"false":String(t)}static validate(e){if(!e||e.trim().length===0)return!1;let t=0;for(let n of e)if(n==="("&&t++,n===")"&&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 ")})`}};var j=class extends Error{},Qe=class{constructor(e){this.tokens=e}tokens;pos=0;peek(){return this.tokens[this.pos]}next(){let e=this.tokens[this.pos];if(!e)throw new j("Unexpected end of expression");return this.pos++,e}expect(e){let t=this.next();if(t.type!==e)throw new j(`Expected ${e} but got '${t.value}'`);return t}is(e,t){let n=this.peek();return!!n&&n.type===e&&n.value===t}atEnd(){return this.pos>=this.tokens.length}};function Lt(i,e,t,n){let o=[];i.forEach((l,c)=>{if(e(l)){let d=n(l,c);d&&o.push(d);return}if(t(l))for(let d of l.children){if(!e(d))return;let u=n(d,c);u&&o.push(u)}});let s=i.length;return new Set(o.map(l=>l.group)).size!==s?null:{conditions:o,conditionLogic:"OR"}}var Cr={"ip.src":"ip","ip.geoip.country":"country","ip.geoip.subdivision_1":"region","ip.geoip.city":"city","ip.geoip.asnum":"asn","http.request.uri.path":"path","http.host":"host","http.request.method":"method","http.request.headers":"header","http.request.uri.query":"query","http.request.uri.args":"query","http.cookie":"cookie","http.request.cookies":"cookie","http.user_agent":"user_agent","http.referer":"referer",ssl:"scheme","cf.edge.server_port":"port","cf.threat_score":"threat_score"},wn=new Set(["eq","ne","contains","starts_with","ends_with","matches","in","gt","ge","lt","le"]),br=new Set(["and","or","not"]);function vr(i){let e=[],t=0,n=i.length;for(;t<n;){let o=i[t];if(/\s/.test(o)){t++;continue}if(o===","){t++;continue}if(o==="("){e.push({type:"LPAREN",value:o}),t++;continue}if(o===")"){e.push({type:"RPAREN",value:o}),t++;continue}if(o==="{"){e.push({type:"LBRACE",value:o}),t++;continue}if(o==="}"){e.push({type:"RBRACE",value:o}),t++;continue}if(o==="["){e.push({type:"LBRACKET",value:o}),t++;continue}if(o==="]"){e.push({type:"RBRACKET",value:o}),t++;continue}if(o==='"'){let a=t+1,l="",c=!1;for(;a<n;){let d=i[a];if(d==="\\"&&a+1<n){l+=d+i[a+1],a+=2;continue}if(i[a]==='"'){c=!0,a++;break}l+=i[a],a++}if(!c)throw new j("Unterminated string literal");e.push({type:"STRING",value:mn(l)}),t=a;continue}let s=t;for(;s<n&&!/[\s(){}[\]",]/.test(i[s]);)s++;if(s===t)throw new j(`Unexpected character '${o}' at position ${t}`);e.push({type:"WORD",value:i.slice(t,s)}),t=s}return e}function ni(i){let e=[hn(i)];for(;i.is("WORD","or");)i.next(),e.push(hn(i));return e.length===1?e[0]:{type:"or",children:e}}function hn(i){let e=[yn(i)];for(;i.is("WORD","and");)i.next(),e.push(yn(i));return e.length===1?e[0]:{type:"and",children:e}}function yn(i){if(i.is("WORD","not")){i.next(),i.expect("LPAREN");let e=ni(i);return i.expect("RPAREN"),{type:"not",child:e}}return Ir(i)}function Ir(i){let e=i.peek();if(e&&e.type==="LPAREN"){i.next();let t=ni(i);return i.expect("RPAREN"),t}return $r(i)}function Er(i){let e=i.expect("WORD");if(br.has(e.value))throw new j(`Expected a field reference but got keyword '${e.value}'`);if(i.peek()?.type==="LBRACKET"){i.next();let t=i.expect("STRING");return i.expect("RBRACKET"),{field:e.value,key:t.value}}return{field:e.value}}function Rn(i){let e=i.next();if(e.type==="STRING")return e.value;if(e.type==="WORD")return/^-?\d+(\.\d+)?$/.test(e.value)?Number(e.value):e.value;throw new j(`Unexpected token '${e.value}' where a value was expected`)}function Cn(i){if(i.peek()?.type==="LBRACE"){i.next();let e=[];for(;i.peek()?.type!=="RBRACE";)e.push(Rn(i));return i.expect("RBRACE"),e}return Rn(i)}function Tr(i){i.next(),i.expect("LPAREN");let e=i.expect("WORD");i.expect("LBRACKET");let t=i.expect("STRING");i.expect("RBRACKET"),i.expect("LBRACKET");let n=i.expect("WORD");if(n.value!=="*")throw new j(`Expected '*' in any(...) array index but got '${n.value}'`);i.expect("RBRACKET");let o=i.next();if(o.type!=="WORD"||!wn.has(o.value))throw new j(`Unsupported or unrecognized operator '${o.value}' inside any(...)`);let s=Cn(i);return i.expect("RPAREN"),{type:"comparison",field:e.value,key:t.value,operator:o.value,value:s}}function Ar(i){i.next(),i.expect("LPAREN");let e=i.expect("WORD"),t=i.expect("STRING");return i.expect("RPAREN"),{type:"exists",field:e.value,key:t.value}}function $r(i){if(i.is("WORD","any"))return Tr(i);if(i.is("WORD","has_key"))return Ar(i);let{field:e,key:t}=Er(i);if(i.is("WORD","exists"))return i.next(),{type:"exists",field:e,key:t};let n=i.next();if(n.type!=="WORD"||!wn.has(n.value))throw new j(`Unsupported or unrecognized operator '${n.value}'`);let o=Cn(i);return{type:"comparison",field:e,key:t,operator:n.value,value:o}}function Pr(i){let e=new Qe(vr(i)),t=ni(e);if(!e.atEnd())throw new j("Unexpected trailing tokens");return t}function ti(i){return i.type==="comparison"||i.type==="exists"?!0:i.type==="not"?i.child.type==="comparison"||i.child.type==="exists":!1}function ii(i,e){let t=i.type==="not",n=i.type==="not"?i.child:i;if(n.type!=="comparison"&&n.type!=="exists")return null;let o=Cr[n.field];if(!o)return null;if(n.type==="exists")return{field:o,operator:t?"not_exists":"exists",value:"",key:n.key,group:e};let s=n.operator,a;return t&&(n.operator==="contains"?s="not_contains":n.operator==="in"?s="not_in":a=!0),{field:o,operator:s,value:n.value,key:n.key,negated:a,group:e}}function _r(i){if(ti(i)){let e=ii(i,0);return e?{conditions:[e],conditionLogic:"AND"}:null}if(i.type==="and"){let e=[];for(let t of i.children){if(!ti(t))return null;let n=ii(t,0);if(!n)return null;e.push(n)}return{conditions:e,conditionLogic:"AND"}}return i.type==="or"?Lt(i.children,ti,e=>e.type==="and",ii):null}function oi(i){if(!i||!i.trim())return null;try{let e=Pr(i);return _r(e)}catch{return null}}function In(i){let e=[],t=oi(i.expression),n=[],o;t?(n=t.conditions,o=t.conditionLogic):e.push(U.createWarning("complex_expressions",i.id,"expression","Expression could not be parsed back into structured conditions \u2014 it may be hand-authored or use syntax outside what doorman itself generates.","Review the translated rule and add missing conditions manually if needed."));let s=i.action_parameters&&"response"in i.action_parameters?i.action_parameters.response:void 0,a={type:kr(i.action),rateLimit:i.ratelimit?{requests:i.ratelimit.requests_per_period,window:`${i.ratelimit.period}s`,characteristics:i.ratelimit.characteristics,mitigationTimeout:i.ratelimit.mitigation_timeout,countingExpression:i.ratelimit.counting_expression}:void 0,response:s?{statusCode:s.status_code,content:s.content,contentType:s.content_type}:void 0};return{result:{id:i.id,name:i.description||`Rule ${i.id}`,description:i.description,enabled:i.enabled??!0,conditions:n,conditionLogic:o,action:a},warnings:e}}function En(i){let e=[],t=Ur(i.conditions,i.id,e);if(t.length===0)throw new Error(`Rule "${i.name}" has no conditions Cloudflare can represent \u2014 every condition field is unsupported by Cloudflare (e.g. Vercel-only fields like environment/ja3_digest, or vercel_region). Cannot sync this rule to Cloudflare.`);let n=Je.fromUnifiedConditions(t,i.conditionLogic),o={id:i.id||crypto.randomUUID(),action:Sr(i.action.type),expression:n,description:i.description||i.name,enabled:i.enabled};return i.action.rateLimit&&(o.ratelimit={characteristics:i.action.rateLimit.characteristics||["ip.src"],period:Lr(i.action.rateLimit.window),requests_per_period:i.action.rateLimit.requests},i.action.rateLimit.mitigationTimeout?o.ratelimit.mitigation_timeout=i.action.rateLimit.mitigationTimeout:(o.ratelimit.mitigation_timeout=3600,e.push(U.createWarning("rate_limiting_precision",i.id,"action.rateLimit.mitigationTimeout",`No mitigationTimeout set for rate limit rule "${i.name}" \u2014 defaulted to Cloudflare's 3600s (1 hour) block duration.`))),i.action.rateLimit.countingExpression&&(o.ratelimit.counting_expression=i.action.rateLimit.countingExpression)),i.action.type==="redirect"&&i.action.redirect&&(o.action_parameters={from_value:{status_code:i.action.redirect.statusCode??(i.action.redirect.permanent?301:302),target_url:{value:i.action.redirect.location},...i.action.redirect.preserveQueryString!==void 0?{preserve_query_string:i.action.redirect.preserveQueryString}:{}}}),i.action.response&&(o.action!=="block"?e.push(U.createUnsupportedFeatureWarning(`custom response on a '${o.action}' action`,"unified config","Cloudflare",i.id,"action.response")):i.action.response.content?o.action_parameters={response:{status_code:i.action.response.statusCode??403,content:i.action.response.content,content_type:i.action.response.contentType??"text/plain"}}:e.push(U.createWarning("lossy_conversion",i.id,"action.response","Custom response declared without `content`; Cloudflare requires a response body, so it was dropped.","Set action.response.content to the body you want returned."))),{result:o,warnings:e}}function Tn(i){if(!Pe.safeParse(i.ip).success)throw new Error(`Invalid IP address or CIDR range: ${i.ip}`);let t=i.ip.includes("/")?`ip.src in {${i.ip}}`:`ip.src eq ${i.ip}`;return{id:i.id||crypto.randomUUID(),action:i.action==="allow"?"allow":"block",expression:t,description:i.notes||`IP ${i.action}: ${i.ip}${i.hostname?` (${i.hostname})`:""}`,enabled:!0}}function An(i){let e=[],t={};if(i.action){let{action:s,warning:a}=bn(i.action,i.id,"action");a&&e.push(a),s&&(t.action=s)}i.overrides&&i.overrides.length>0&&(t.rules=i.overrides.map(s=>{let a={id:s.ruleId};if(s.enabled!==void 0&&(a.enabled=s.enabled),s.action){let{action:l,warning:c}=bn(s.action,i.id,`overrides[${s.ruleId}].action`);c&&e.push(c),l&&(a.action=l)}return a}));let n={id:i.ruleset,...Object.keys(t).length>0?{overrides:t}:{}};return{result:{id:i.id||crypto.randomUUID(),action:"execute",expression:"true",description:i.name||`Managed ruleset ${i.ruleset}`,enabled:i.enabled,action_parameters:n},warnings:e}}function $n(i){let e=[],t=i.action_parameters,n={id:i.id,ruleset:t?.id??"",enabled:i.enabled??!0,...i.description?{name:i.description}:{}};return t?.overrides?.action&&(n.action=vn(t.overrides.action)),t?.overrides?.rules&&t.overrides.rules.length>0&&(n.overrides=t.overrides.rules.map(o=>({ruleId:o.id,...o.action!==void 0?{action:vn(o.action)}:{},...o.enabled!==void 0?{enabled:o.enabled}:{}}))),{result:n,warnings:e}}function bn(i,e,t){let o={log:"log",deny:"block",block:"block",challenge:"managed_challenge",allow:"allow"}[i];return o?{action:o}:{warning:U.createUnsupportedFeatureWarning(`'${i}' as a managed-ruleset override action`,"unified config","Cloudflare",e,t)}}function vn(i){return{log:"log",block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",allow:"allow"}[i]||"deny"}function kr(i){return{block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",log:"log",skip:"bypass",allow:"allow",rewrite:"bypass",redirect:"redirect",execute:"log"}[i]||"deny"}function Sr(i){return{log:"log",deny:"block",block:"block",challenge:"managed_challenge",bypass:"skip",rate_limit:"block",redirect:"redirect",allow:"allow"}[i]||"block"}function Ur(i,e,t){return i.filter(n=>Je.isFieldSupported(n.field)?!0:(t.push(U.createUnsupportedFeatureWarning(`condition field '${n.field}'`,"unified config","Cloudflare",e,n.field)),!1))}function Lr(i){let e=i.match(/^(\d+)([smhd])$/);if(!e||!e[1]||!e[2])throw new Error(`Invalid window format: ${i}`);let t=parseInt(e[1],10),n=e[2],s={s:1,m:60,h:3600,d:86400}[n];if(s===void 0)throw new Error(`Invalid time unit: ${n}`);return t*s}pe();function we(i,e,t){return U.createWarning("complex_expressions",i,void 0,e,t)}var xr={ip:"ip",path:"path",domain:"host",method:"method",country:"country",scheme:"scheme",user_agent:"user_agent"},Dr={ip:"ip",path:"path",host:"domain",method:"method",country:"country",scheme:"scheme",user_agent:"user_agent"},Or={header:"request_header",query:"query_parameter",cookie:"request_cookie"},Fr={request_header:"header",query_parameter:"query",request_cookie:"cookie",response_header:"response_header",post_parameter:"post_parameter",signal:"signal"};function Nr(i){return xr[i]??i}function Vr(i){return Dr[i]??null}var Br={equals:{operator:"eq",negated:!1},does_not_equal:{operator:"eq",negated:!0},contains:{operator:"contains",negated:!1},does_not_contain:{operator:"contains",negated:!0},like:{operator:"matches",negated:!1},not_like:{operator:"matches",negated:!0},in_list:{operator:"in",negated:!1},not_in_list:{operator:"in",negated:!0},matches:{operator:"matches",negated:!1},does_not_match:{operator:"matches",negated:!0},greater_equal:{operator:"ge",negated:!1},lesser_equal:{operator:"le",negated:!1}};function Pn(i){return Br[i]??{operator:"eq",negated:!1}}function _n(i,e,t,n){switch(i){case"eq":return e?"does_not_equal":"equals";case"ne":return"does_not_equal";case"contains":return e?"does_not_contain":"contains";case"not_contains":return"does_not_contain";case"starts_with":case"ends_with":return t.push(U.createWarning("regex_patterns",n,void 0,`Unified operator "${i}" has no direct Fastly equivalent \u2014 mapped to Fastly's wildcard "like" operator. Verify the translated value still matches as a wildcard pattern.`)),e?"not_like":"like";case"matches":return t.push(U.createWarning("regex_patterns",n,void 0,"Regular expression pattern may need adjustment for Fastly Next-Gen WAF regex syntax.")),e?"does_not_match":"matches";case"in":return e?"not_in_list":"in_list";case"not_in":return"not_in_list";case"gt":return t.push(we(n,'Unified operator "gt" (strictly greater than) has no Fastly equivalent \u2014 mapped to "greater_equal", which also matches the boundary value itself.')),"greater_equal";case"ge":return"greater_equal";case"lt":return t.push(we(n,'Unified operator "lt" (strictly less than) has no Fastly equivalent \u2014 mapped to "lesser_equal", which also matches the boundary value itself.')),"lesser_equal";case"le":return"lesser_equal";default:return"equals"}}function Mr(i,e){let t=[],n=i.conditions.filter(s=>s.type==="group"),o=i.conditions.filter(s=>s.type!=="group");if(n.length>0&&o.length===0&&i.group_operator==="any")return n.forEach((s,a)=>{s.group_operator!=="all"&&e.push(we(i.id,`A condition group using "any" (OR) inside an outer "any" (OR) rule cannot be represented in doorman's flat AND-within/OR-across condition model \u2014 its members were kept in the same group (effectively AND'd) instead of OR'd. Review the translated rule against the original Fastly logic.`));for(let l of s.conditions)xt(t,l,a,e,i.id)}),{conditions:t,conditionLogic:n.length>1?"OR":"AND"};if(n.length===0)return o.forEach((s,a)=>{let l=i.group_operator==="any"?a:0;xt(t,s,l,e,i.id)}),{conditions:t,conditionLogic:i.group_operator==="any"&&o.length>1?"OR":"AND"};e.push(we(i.id,`Rule mixes top-level conditions with condition groups under group_operator "${i.group_operator}" \u2014 this doesn't fit doorman's flat AND-within/OR-across condition model. All conditions were combined with AND; review the translated rule against the original Fastly rule logic.`));for(let s of o)xt(t,s,0,e,i.id);for(let s of n)for(let a of s.conditions)xt(t,a,0,e,i.id);return{conditions:t,conditionLogic:"AND"}}function xt(i,e,t,n,o){if(e.type==="single"){let{operator:u,negated:p}=Pn(e.operator);i.push({field:Nr(e.field),operator:u,value:e.value,...p?{negated:!0}:{},group:t});return}let s=Fr[e.field],a=e.conditions.find(u=>u.field==="name"),l=e.conditions.find(u=>u.field==="value"||u.field==="value_string");if(!l){if(a){i.push({field:s,operator:e.operator==="does_not_exist"?"not_exists":"exists",key:a.value,group:t});return}n.push(we(o,`Multival condition on '${e.field}' has no value or name sub-condition doorman could extract \u2014 skipped.`));return}let{operator:c,negated:d}=Pn(l.operator);i.push({field:s,operator:c,value:l.value,...d?{negated:!0}:{},...a?{key:a.value}:{},group:t})}function qr(i,e){i.actions.length>1&&e.push(we(i.id,`Rule has ${i.actions.length} actions \u2014 doorman only models one action per rule; only the first ("${i.actions[0]?.type}") was translated.`));let t=i.actions[0];if(i.type==="rate_limit")return{type:"rate_limit",rateLimit:i.rate_limit?{requests:i.rate_limit.threshold,window:`${i.rate_limit.interval}s`,mitigationTimeout:i.rate_limit.duration}:void 0};switch(t?.type){case"block":return{type:"block"};case"allow":return{type:"allow"};case"browser_challenge":case"dynamic_challenge":case"verify_token":return{type:"challenge"};case"redirect":return{type:"redirect",redirect:{location:t.redirect_url??"",...t.response_code!==void 0?{statusCode:t.response_code}:{}}};case"add_signal":case"exclude_signal":case"deception":return e.push(U.createWarning("action_differences",i.id,"action.type",`Fastly action "${t.type}" tags or excludes a WAF signal rather than allowing/blocking the request \u2014 doorman has no matching action type. Mapped to "log" as the closest non-destructive fallback; review this rule.`)),{type:"log"};default:return e.push(we(i.id,`Unrecognised Fastly action type "${String(t?.type)}" \u2014 mapped to "log".`)),{type:"log"}}}function kn(i){let e=[],{conditions:t,conditionLogic:n}=Mr(i,e),o=qr(i,e);return t.length>10&&e.push(U.createWarning("many_conditions",i.id,void 0,`Rule has ${t.length} conditions which may impact performance`)),{result:{id:i.id,name:i.description||i.id,enabled:i.enabled,conditions:t,conditionLogic:n,action:o},warnings:e}}function Wr(i,e,t){let n=new Map;for(let a of i){let l=a.group??0,c=n.get(l);c?c.push(a):n.set(l,[a])}let o=a=>{let l=Or[a.field];if(l){if(a.key===void 0)return t.push(we(e,`Condition on '${a.field}' has no 'key' set \u2014 Fastly needs a specific ${a.field} name to match on (e.g. a header name). Skipped.`)),null;if(a.operator==="exists"||a.operator==="not_exists")return{type:"multival",field:l,operator:a.operator==="exists"?"exists":"does_not_exist",group_operator:"all",conditions:[{type:"single",field:"name",operator:"equals",value:a.key}]};let d=l==="request_header"||l==="response_header"?"value_string":"value";return{type:"multival",field:l,operator:"exists",group_operator:"all",conditions:[{type:"single",field:"name",operator:"equals",value:a.key},{type:"single",field:d,operator:_n(a.operator,a.negated,t,e),value:String(a.value)}]}}if(a.operator==="exists"||a.operator==="not_exists")return t.push(U.createUnsupportedFeatureWarning(`"${a.operator}" on field '${a.field}'`,"unified config","Fastly",e,a.field)),null;let c=Vr(a.field);return c?{type:"single",field:c,operator:_n(a.operator,a.negated,t,e),value:String(a.value)}:(t.push(U.createUnsupportedFeatureWarning(`condition field '${a.field}'`,"unified config","Fastly",e,a.field)),null)};if(n.size<=1){let a=i.map(o).filter(l=>l!==null);return{conditions:a.filter(l=>l.type==="single"),groupConditions:[],multivalConditions:a.filter(l=>l.type==="multival"),groupOperator:"all"}}let s=[];for(let a of n.values()){let l=a.map(o).filter(c=>c!==null);l.length>0&&s.push({type:"group",group_operator:"all",conditions:l})}return{conditions:[],groupConditions:s,multivalConditions:[],groupOperator:"any"}}function jr(i,e,t){switch(i.type){case"block":case"deny":return{type:"block"};case"allow":return{type:"allow"};case"challenge":return{type:"browser_challenge"};case"bypass":return e.push(we(t,'Unified action "bypass" has no direct Fastly equivalent \u2014 mapped to "allow".')),{type:"allow"};case"log":return e.push(we(t,'Unified action "log" has no dedicated Fastly action \u2014 mapped to "allow" (request_logging is enabled at the rule level regardless).')),{type:"allow"};case"redirect":return i.redirect?{type:"redirect",redirect_url:i.redirect.location,response_code:i.redirect.statusCode??(i.redirect.permanent?301:302)}:(e.push(we(t,'Unified action "redirect" has no redirect target \u2014 mapped to "allow".')),{type:"allow"});case"rate_limit":return{type:"block_signal"};default:return e.push(U.createUnsupportedFeatureWarning(`action type '${i.type}'`,"unified config","Fastly",t,"action.type")),{type:"block"}}}function zr(i,e,t){let n=/^(\d+)\s*(s|m|h)?$/i.exec(i.trim()),o={s:1,m:60,h:3600},s=n?Number(n[1])*o[n[2]?.toLowerCase()||"s"]:NaN,a=[60,600,3600];if(!Number.isFinite(s)||s<=0)return e.push(U.createWarning("rate_limiting_precision",t,void 0,`Rate limit window "${i}" could not be parsed \u2014 defaulted to Fastly's 60-second interval.`)),60;let l=a.reduce((c,d)=>Math.abs(d-s)<Math.abs(c-s)?d:c);return l!==s&&e.push(U.createWarning("rate_limiting_precision",t,void 0,`Rate limit window "${i}" (${s}s) isn't one of Fastly's supported intervals (60s/600s/3600s) \u2014 rounded to ${l}s.`)),l}function Gr(i,e,t){if(!i)throw new Error(`Rule "${e??"(unnamed)"}" has action type "rate_limit" but no rateLimit configuration \u2014 Fastly requires a threshold and interval.`);let n=zr(i.window,t,e);return t.push(U.createWarning("rate_limiting_precision",e,void 0,`Fastly rate-limit rules count requests against a named custom signal, which doorman does not create automatically \u2014 create a signal named "doorman-rate-limit-${e??"rule"}" in this workspace before syncing, or the rule will be rejected.`)),{client_identifiers:[{type:"ip"}],duration:i.mitigationTimeout??n,interval:n,signal:`doorman-rate-limit-${e??"rule"}`,threshold:i.requests}}function Sn(i){let e=[];i.description&&i.description!==i.name&&e.push(we(i.id,'Fastly rules have no separate "name" field \u2014 only "description" carries over, and it was set from this rule\'s `name`. The `description` text was dropped rather than merged in, to keep it round-trip-stable on the next sync.'));let{conditions:t,groupConditions:n,multivalConditions:o,groupOperator:s}=Wr(i.conditions,i.id,e);if(t.length===0&&n.length===0&&o.length===0)throw new Error(`Rule "${i.name}" has no conditions Fastly can represent \u2014 every condition field is unsupported by Fastly. Cannot sync this rule to Fastly.`);let a=i.action.type==="rate_limit";return{result:{type:a?"rate_limit":"request",description:i.name,enabled:i.enabled,group_operator:s,request_logging:"sampled",conditions:t,group_conditions:n,multival_conditions:o,actions:[jr(i.action,e,i.id)],rate_limit:a?Gr(i.action.rateLimit,i.id,e):null},warnings:e}}function Un(i,e){return i.map(t=>({ip:t,action:e}))}function Ln(i){let e=[],t=[],n=[];for(let o of i)(o.hostname||o.notes)&&e.push(we(o.id,`IP rule "${o.ip}" sets ${o.hostname?"`hostname`":""}${o.hostname&&o.notes?" and ":""}${o.notes?"`notes`":""} \u2014 Fastly IP lists have no equivalent per-entry metadata; only the address was kept.`)),(o.action==="allow"?n:t).push(o.ip);return{result:{denyEntries:t,allowEntries:n},warnings:e}}pe();function Xe(i){return i.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}function xn(i){let e="";for(let t=0;t<i.length;t++)i[t]==="\\"&&(i[t+1]==="\\"||i[t+1]==="'")?(e+=i[t+1],t++):e+=i[t];return e}var Kr=new Set(["region","city","port","scheme"]),Hr={host:"host",user_agent:"user-agent",referer:"referer"},ft=class{static fromUnifiedConditions(e,t="AND"){if(!e||e.length===0)throw new Error("At least one condition is required");if(!e.some(a=>a.group!==void 0)){let a=e.map(c=>this.fromUnifiedCondition(c)),l=t==="AND"?" && ":" || ";return a.length>1?`(${a.join(l)})`:a[0]}let o=new Map;for(let a of e){let l=a.group??0,c=o.get(l);c?c.push(a):o.set(l,[a])}let s=Array.from(o.values()).map(a=>this.combineWithAnd(a.map(l=>this.fromUnifiedCondition(l))));return s.length>1?this.combineWithOr(s):s[0]}static fromUnifiedCondition(e){if(Kr.has(e.field))throw new Error(`Cloud Armor has no CEL equivalent for the "${e.field}" condition field \u2014 this rule cannot be represented and would otherwise be silently dropped.`);if(e.field==="ip")return this.buildIpCondition(e);if(e.field==="cookie")return this.buildCookieCondition(e);let t=Hr[e.field];if(t)return this.buildHeaderCondition(t,e);if(e.field==="header"||e.field==="query"){if(e.field==="header"){if(!e.key)throw new Error('A "header" condition requires a key naming the header');return this.buildHeaderCondition(e.key,e)}return this.buildComparison("request.query",e,!1)}let n=Yr[e.field];if(n)return this.buildComparison(n.path,e,n.numeric);throw new Error(`Cloud Armor CEL builder has no mapping for condition field "${e.field}"`)}static buildIpCondition(e){let t=(Array.isArray(e.value)?e.value:[e.value]).map(String);if(t.length===0||t.some(a=>!a))throw new Error('An "ip" condition requires at least one value');let n=t.map(a=>{if(!Pe.safeParse(a).success)throw new Error(`Invalid IP address or CIDR range: ${a}`);return a.includes("/")?`inIpRange(origin.ip, '${Xe(a)}')`:`origin.ip == '${Xe(a)}'`}),o=this.isNegated(e,e.operator==="not_in"||e.operator==="ne");if(n.length>1){let a=`(${n.join(" || ")})`;return o?`!${a}`:a}let s=n[0];return o?s.startsWith("inIpRange(")?`!${s}`:`!(${s})`:s}static buildHeaderCondition(e,t){let n=`request.headers['${Xe(e)}']`,o=`has(${n})`;return t.operator==="exists"||t.operator==="not_exists"?this.isNegated(t,t.operator==="not_exists")?`!${o}`:o:`(${o} && ${this.buildComparison(n,t,!1)})`}static buildCookieCondition(e){let t="request.headers['cookie']",n=`has(${t})`;if(e.operator==="exists"||e.operator==="not_exists")return this.isNegated(e,e.operator==="not_exists")?`!${n}`:n;if(!e.key)return`(${n} && ${this.buildComparison(t,e,!1)})`;if(!["eq","ne","contains","not_contains"].includes(e.operator))throw new Error(`Cloud Armor can only check a specific cookie's value via substring search (has no parsed cookie map) \u2014 "${e.operator}" on a keyed cookie condition has no unambiguous CEL representation.`);let o=`${e.key}=${String(e.value)}`,s=`${t}.contains('${Xe(o)}')`,a=this.isNegated(e,e.operator==="ne"||e.operator==="not_contains");return`(${n} && ${a?`!${s}`:s})`}static buildComparison(e,t,n){let{value:o}=t;if(t.operator==="exists"||t.operator==="not_exists")throw new Error(`"${t.operator}" is not meaningful for Cloud Armor's "${e}" \u2014 it is always present`);let s=t.operator,a=!1;if(t.negated){let d=Zr[s];d?s=d:a=!0}if(s==="in"||s==="not_in"){let u=(Array.isArray(o)?o:[o]).map(f=>this.formatLiteral(f,n)).join(", "),p=`${e} in [${u}]`;return s==="not_in"?`!(${p})`:p}let l=this.formatLiteral(o,n),c;switch(s){case"eq":c=`${e} == ${l}`;break;case"ne":c=`${e} != ${l}`;break;case"contains":c=`${e}.contains(${l})`;break;case"not_contains":c=`!${e}.contains(${l})`;break;case"starts_with":c=`${e}.startsWith(${l})`;break;case"ends_with":c=`${e}.endsWith(${l})`;break;case"matches":c=`${e}.matches(${l})`;break;case"gt":c=`${e} > ${l}`;break;case"ge":c=`${e} >= ${l}`;break;case"lt":c=`${e} < ${l}`;break;case"le":c=`${e} <= ${l}`;break;default:throw new Error(`Cloud Armor CEL builder has no mapping for operator "${s}"`)}return a?`!(${c})`:c}static isNegated(e,t){return t!==!!e.negated}static formatLiteral(e,t){if(t){let n=Number(e);if(!Number.isFinite(n))throw new Error(`Expected a numeric value, got "${String(e)}"`);return String(n)}return`'${Xe(String(e))}'`}static combineWithAnd(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" && ")})`}static combineWithOr(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" || ")})`}},Zr={eq:"ne",ne:"eq",contains:"not_contains",not_contains:"contains",in:"not_in",not_in:"in"},Yr={country:{path:"origin.region_code",numeric:!1},asn:{path:"origin.asn",numeric:!0},path:{path:"request.path",numeric:!1},method:{path:"request.method",numeric:!1}};var Jr={"origin.region_code":"country","origin.asn":"asn","request.path":"path","request.method":"method","request.query":"query"},Qr={host:"host","user-agent":"user_agent",referer:"referer",cookie:"cookie"},Bn={contains:"contains",startsWith:"starts_with",endsWith:"ends_with",matches:"matches"};function Xr(i){let e=[],t=0,n=i.length,o=(s,a)=>i[t]===s&&i[t+1]===a;for(;t<n;){let s=i[t];if(/\s/.test(s)){t++;continue}if(s==="("){e.push({type:"LPAREN",value:s}),t++;continue}if(s===")"){e.push({type:"RPAREN",value:s}),t++;continue}if(s==="["){e.push({type:"LBRACKET",value:s}),t++;continue}if(s==="]"){e.push({type:"RBRACKET",value:s}),t++;continue}if(s===","){e.push({type:"COMMA",value:s}),t++;continue}if(o("&","&")){e.push({type:"AND",value:"&&"}),t+=2;continue}if(o("|","|")){e.push({type:"OR",value:"||"}),t+=2;continue}if(o("=","=")){e.push({type:"EQ",value:"=="}),t+=2;continue}if(o("!","=")){e.push({type:"NE",value:"!="}),t+=2;continue}if(o(">","=")){e.push({type:"GE",value:">="}),t+=2;continue}if(o("<","=")){e.push({type:"LE",value:"<="}),t+=2;continue}if(s==="!"){e.push({type:"NOT",value:"!"}),t++;continue}if(s===">"){e.push({type:"GT",value:">"}),t++;continue}if(s==="<"){e.push({type:"LT",value:"<"}),t++;continue}if(s==="."){e.push({type:"DOT",value:s}),t++;continue}if(s==="'"){let a=t+1,l="",c=!1;for(;a<n;){let d=i[a];if(d==="\\"&&a+1<n){l+=d+i[a+1],a+=2;continue}if(i[a]==="'"){c=!0,a++;break}l+=i[a],a++}if(!c)throw new j("Unterminated string literal");e.push({type:"STRING",value:xn(l)}),t=a;continue}if(/[0-9]/.test(s)||s==="-"&&/[0-9]/.test(i[t+1]??"")){let a=t+1;for(;a<n&&/[0-9.]/.test(i[a]);)a++;e.push({type:"NUMBER",value:i.slice(t,a)}),t=a;continue}if(/[A-Za-z_]/.test(s)){let a=t;for(;a<n&&/[A-Za-z0-9_]/.test(i[a]);)a++;e.push({type:"IDENT",value:i.slice(t,a)}),t=a;continue}throw new j(`Unexpected character '${s}' at position ${t}`)}return e}function es(i){let e=i.expect("IDENT").value;for(;i.peek()?.type==="DOT";){let t=i.pos;i.next();let n=i.expect("IDENT");if(i.peek()?.type==="LPAREN"){i.pos=t;break}e+=`.${n.value}`}return e}function ri(i){let e=es(i);if(i.peek()?.type==="LBRACKET"){i.next();let t=i.expect("STRING");return i.expect("RBRACKET"),{field:e,key:t.value}}return{field:e}}function Dn(i){let e=i.next();if(e.type==="STRING")return e.value;if(e.type==="NUMBER")return Number(e.value);throw new j(`Unexpected token '${e.value}' where a literal was expected`)}function ts(i){if(i.is("IDENT","has")){i.next(),i.expect("LPAREN");let a=ri(i);return i.expect("RPAREN"),{type:"has",...a}}if(i.is("IDENT","inIpRange")){i.next(),i.expect("LPAREN");let a=ri(i);if(a.field!=="origin.ip")throw new j(`inIpRange must reference origin.ip, got '${a.field}'`);i.expect("COMMA");let l=i.expect("STRING");return i.expect("RPAREN"),{type:"inIpRange",value:l.value}}let e=ri(i);if(i.peek()?.type==="DOT"){i.next();let a=i.expect("IDENT").value;if(!Bn[a])throw new j(`Unrecognized method '${a}'`);i.expect("LPAREN");let l=i.expect("STRING");return i.expect("RPAREN"),{type:"methodCall",...e,method:a,value:l.value}}if(i.is("IDENT","in")){i.next(),i.expect("LBRACKET");let a=[];for(;i.peek()?.type!=="RBRACKET";)a.push(Dn(i)),i.peek()?.type==="COMMA"&&i.next();return i.expect("RBRACKET"),{type:"membership",...e,values:a}}let t=i.next(),o={EQ:"eq",NE:"ne",GT:"gt",GE:"ge",LT:"lt",LE:"le"}[t.type];if(!o)throw new j(`Unexpected token '${t.value}' after field reference`);let s=Dn(i);return{type:"comparison",...e,operator:o,value:s}}function On(i){if(i.peek()?.type==="LPAREN"){i.next();let e=Mn(i);return i.expect("RPAREN"),e}return ts(i)}function Fn(i){return i.peek()?.type==="NOT"?(i.next(),{type:"not",child:On(i)}):On(i)}function Nn(i){let e=[Fn(i)];for(;i.peek()?.type==="AND";)i.next(),e.push(Fn(i));return e.length===1?e[0]:{type:"and",children:e}}function Mn(i){let e=[Nn(i)];for(;i.peek()?.type==="OR";)i.next(),e.push(Nn(i));return e.length===1?e[0]:{type:"or",children:e}}function is(i){let e=new Qe(Xr(i)),t=Mn(e);if(!e.atEnd())throw new j("Unexpected trailing tokens");return t}function ns(i){return i.type==="inIpRange"||i.type==="comparison"&&i.field==="origin.ip"&&i.operator==="eq"}function li(i){return i.type==="or"&&i.children.length>0&&i.children.every(ns)}function os(i){return i.type==="inIpRange"?i.value:String(i.value)}function qn(i){if(i.type!=="and"||i.children.length!==2)return!1;let e=i.children[0],t=i.children[1];if(e.type!=="has")return!1;let n=t.type==="not"?t.child:t;return!("field"in n)||n.field!==e.field||n.key!==e.key?!1:n.type==="comparison"||n.type==="methodCall"||n.type==="membership"}function rs(i){let e=i.children[0],t=i.children[1],n=t.type==="not",o=n?t.child:t;return{guard:e,comparisonNegated:n,comparison:o}}function si(i){if(i.type==="has"||i.type==="comparison"||i.type==="methodCall"||i.type==="membership"||i.type==="inIpRange"||li(i)||qn(i))return!0;if(i.type==="not"){let e=i.child;return e.type==="has"||e.type==="comparison"||e.type==="methodCall"||e.type==="membership"||e.type==="inIpRange"||li(e)}return!1}function Wn(i,e){if(i==="origin.ip")return{unifiedField:"ip"};let t=Jr[i];if(t)return{unifiedField:t};if(i==="request.headers"&&e!==void 0){let n=Qr[e];return n?{unifiedField:n}:{unifiedField:"header",unifiedKey:e}}return null}function ai(i,e){let t=i.type==="not",n=i.type==="not"?i.child:i;if(li(n)){let o=n.children.map(s=>os(s));return{field:"ip",operator:t?"not_in":"in",value:o,...e!==void 0?{group:e}:{}}}if(n.type==="inIpRange"||n.type==="comparison"&&n.field==="origin.ip"){let o=n.type==="inIpRange"?n.value:String(n.value);return{field:"ip",operator:t?"ne":"eq",value:o,...e!==void 0?{group:e}:{}}}if(n.type==="and"&&qn(n)){if(t)return null;let{guard:o,comparisonNegated:s,comparison:a}=rs(n);return a.type!=="comparison"&&a.type!=="methodCall"&&a.type!=="membership"?null:Vn(a,s,o.field,o.key,e)}if(n.type==="comparison"||n.type==="methodCall"||n.type==="membership")return Vn(n,t,n.field,n.key,e);if(n.type==="has"){let o=Wn(n.field,n.key);return o?{field:o.unifiedField,operator:t?"not_exists":"exists",value:"",...o.unifiedKey!==void 0?{key:o.unifiedKey}:{},...e!==void 0?{group:e}:{}}:null}return null}function Vn(i,e,t,n,o){let s=Wn(t,n);if(!s)return null;let a,l,c=!1;if(i.type==="comparison")e?i.operator==="eq"?a="ne":i.operator==="ne"?a="eq":(a=i.operator,c=!0):a=i.operator,l=i.value;else if(i.type==="methodCall"){let d=Bn[i.method];if(!d)return null;e?d==="contains"?a="not_contains":(a=d,c=!0):a=d,l=i.value}else a=e?"not_in":"in",l=i.values;return{field:s.unifiedField,operator:a,value:l,...c?{negated:!0}:{},...s.unifiedKey!==void 0?{key:s.unifiedKey}:{},...o!==void 0?{group:o}:{}}}function ss(i){if(si(i)){let e=ai(i,void 0);return e?{conditions:[e],conditionLogic:"AND"}:null}if(i.type==="and"){let e=[];for(let t of i.children){if(!si(t))return null;let n=ai(t,void 0);if(!n)return null;e.push(n)}return{conditions:e,conditionLogic:"AND"}}return i.type==="or"?Lt(i.children,si,e=>e.type==="and",ai):null}function Me(i){if(!i||!i.trim())return null;try{let e=is(i);return ss(e)}catch{return null}}function ci(i){if(!i.match.expr)throw new Error(`Cloud Armor rule at priority ${i.priority} has no CEL match expression (uses the basic versionedExpr/config shape) \u2014 caller must filter these out before translating.`);return i.match.expr.expression}function jn(i){if(i.priority===void 0)throw new Error(`Rule "${i.name}" has no priority assigned \u2014 CloudArmorFirewallService must assign one before calling unifiedToGcp`);let e=[],t=ft.fromUnifiedConditions(i.conditions,i.conditionLogic),{action:n,actionWarning:o}=ls(i.action.type,i.name,i.id);o&&e.push(o);let s={priority:i.priority,description:(i.description||i.name).slice(0,64),match:{expr:{expression:t}},action:n,preview:i.action.type==="log"?!0:void 0};if((n==="throttle"||n==="rate_based_ban")&&i.action.rateLimit){let a=cs(i.action.rateLimit.window);s.rateLimitOptions={rateLimitThreshold:{count:i.action.rateLimit.requests,intervalSec:a},conformAction:"allow",exceedAction:"deny(429)",enforceOnKey:"IP"}}else n==="throttle"&&!i.action.rateLimit&&e.push(U.createWarning("lossy_conversion",i.id,"action.rateLimit",`Rule "${i.name}" maps to Cloud Armor's "throttle" action but declares no rateLimit block \u2014 Cloud Armor requires rateLimitOptions for this action; the rule will likely be rejected by the API.`));return n==="redirect"&&(i.action.redirect?s.redirectOptions={type:"EXTERNAL_302",target:i.action.redirect.location}:e.push(U.createWarning("lossy_conversion",i.id,"action.redirect",`Rule "${i.name}" maps to Cloud Armor's "redirect" action but declares no redirect target \u2014 Cloud Armor requires redirectOptions for this action; the rule will likely be rejected by the API.`))),{result:s,warnings:e}}function zn(i,e){let t=[],n=e!==void 0?e:Me(ci(i)),o=[],s;n?(o=n.conditions,s=n.conditionLogic):t.push(U.createWarning("complex_expressions",String(i.priority),"match.expr.expression","CEL expression could not be parsed back into structured conditions \u2014 it may be hand-authored or use syntax outside what doorman itself generates.","Review the translated rule and add missing conditions manually if needed."));let a={type:as(i.action),...i.rateLimitOptions?{rateLimit:{requests:i.rateLimitOptions.rateLimitThreshold.count,window:`${i.rateLimitOptions.rateLimitThreshold.intervalSec}s`}}:{},...i.redirectOptions?{redirect:{location:i.redirectOptions.target}}:{}};return{result:{id:String(i.priority),name:i.description||`Rule ${i.priority}`,...i.description?{description:i.description}:{},enabled:!i.preview,conditions:o,...s==="OR"?{conditionLogic:s}:{},action:a,priority:i.priority},warnings:t}}function Gn(i,e){if(i.action!=="allow"&&i.action!=="deny(403)"||i.preview)return!1;let t=e!==void 0?e:Me(ci(i));if(!t||t.conditions.length!==1)return!1;let n=t.conditions[0];return n.field==="ip"&&(n.operator==="eq"||n.operator==="in")&&!n.key}function Kn(i,e){let o=(e!==void 0?e:Me(ci(i))).conditions[0].value;return{id:String(i.priority),ip:Array.isArray(o)?String(o[0]):String(o),...i.description?{notes:i.description}:{},action:i.action==="allow"?"allow":"deny"}}function Hn(i,e){if(!Pe.safeParse(i.ip).success)throw new Error(`Invalid IP address or CIDR range: ${i.ip}`);let t=ft.fromUnifiedCondition({field:"ip",operator:"eq",value:i.ip});return{priority:e,description:(i.notes||`IP ${i.action}: ${i.ip}${i.hostname?` (${i.hostname})`:""}`).slice(0,64),match:{expr:{expression:t}},action:i.action==="allow"?"allow":"deny(403)"}}function as(i){return{allow:"allow","deny(403)":"deny","deny(404)":"deny","deny(502)":"deny",rate_based_ban:"rate_limit",throttle:"rate_limit",redirect:"redirect"}[i]||"deny"}function ls(i,e,t){switch(i){case"log":return{action:"allow"};case"deny":case"block":return{action:"deny(403)"};case"challenge":return{action:"deny(403)",actionWarning:U.createUnsupportedFeatureWarning("challenge action","unified config","Cloud Armor",t,"action")};case"bypass":return{action:"allow"};case"rate_limit":return{action:"throttle"};case"redirect":return{action:"redirect"};case"allow":return{action:"allow"};default:return{action:"deny(403)",actionWarning:U.createWarning("lossy_conversion",t,"action",`Rule "${e}" has unrecognized action "${i}" \u2014 defaulted to Cloud Armor's "deny(403)".`)}}}function cs(i){let e=i.match(/^(\d+)([smhd])$/);if(!e||!e[1]||!e[2])throw new Error(`Invalid window format: ${i}`);let t=parseInt(e[1],10),o={s:1,m:60,h:3600,d:86400}[e[2]];if(o===void 0)throw new Error(`Invalid time unit: ${e[2]}`);return t*o}var T=class{static vercelToUnified=pn;static unifiedToVercel=fn;static vercelIPToUnified=gn;static cloudflareToUnified=In;static unifiedToCloudflare=En;static unifiedIPToCloudflare=Tn;static unifiedManagedRuleGroupToCloudflare=An;static cloudflareToUnifiedManagedRuleGroup=$n;static fastlyToUnified=kn;static unifiedToFastly=Sn;static fastlyListEntriesToUnified=Un;static unifiedIPsToFastlyEntries=Ln;static gcpToUnified=zn;static unifiedToGcp=jn;static gcpToUnifiedIP=Kn;static unifiedIPToGcp=Hn;static gcpLooksLikeIpRule=Gn};pe();function Oe(i,e){if(i===e)return!0;if(typeof i!="object"||i===null||typeof e!="object"||e===null||Array.isArray(i)!==Array.isArray(e))return!1;let t=Object.keys(i),n=Object.keys(e);if(t.length!==n.length)return!1;for(let o of t)if(!n.includes(o)||!Oe(i[o],e[o]))return!1;return!0}function Ce(i){let{id:e,...t}=i;return t}function gt(i){return[...i].sort((e,t)=>{let n=e.priority!==void 0,o=t.priority!==void 0;return!n&&!o?0:n?o?e.priority-t.priority:-1:1})}function Dt(i){return i.some(e=>e.priority!==void 0)}var et=class extends ke{constructor(t){super();this.client=t}client;name="vercel";async fetchConfig(t){try{r.debug("Fetching Vercel firewall configuration");let n=await this.client.fetchFirewallConfig(t),o=n.rules.map(a=>{let l=T.vercelToUnified(a);return l.warnings.length>0&&l.warnings.forEach(c=>{let{TranslationWarningSystem:d}=(pe(),Re(Se)),u=d.formatWarning(c);r.warn(`Rule ${a.name}:
25
- ${u}`)}),l.result}),s=n.ips.map(a=>T.vercelIPToUnified(a));return{version:"2.0",provider:"vercel",providers:{vercel:{projectId:this.client.projectId,...this.client.teamId?{teamId:this.client.teamId}:{}}},rules:o,ips:s,metadata:{version:n.version,updatedAt:n.updatedAt}}}catch(n){throw r.error("Error fetching Vercel configuration:",n),new Error("Failed to fetch Vercel firewall configuration")}}async syncRules(t,n={}){let{dryRun:o=!1}=n;try{let{OperationSafety:s}=(ht(),Re(mt)),a=await s.performDryRunValidation(t,"sync rules",async y=>await this.getChanges(y,{allowCreate:!o}));if(!a.valid)throw new Error(`Dry-run validation failed: ${a.issues.join(", ")}`);let l=a.changes,{rulesToAdd:c,rulesToUpdate:d,rulesToDelete:u,version:p}=l,f=l.ipsToAdd||[],g=l.ipsToUpdate||[],m=l.ipsToDelete||[];if(o)return r.info("Dry run mode. The following changes would be made:"),r.info(`Custom Rules - Add: ${c.length}, Update: ${d.length}, Delete: ${u.length}`),r.info(`IP Rules - Add: ${f.length}, Update: ${g.length}, Delete: ${m.length}`),{success:!0,rulesAdded:0,rulesUpdated:0,rulesDeleted:0,ipsAdded:0,ipsUpdated:0,ipsDeleted:0,version:p,warnings:a.warnings};let v=s.assessOperationRisk(l,t);if(!await s.confirmDestructiveOperation({operation:"sync rules",target:`Vercel project ${this.client.projectId}`,changes:l,riskLevel:v,skipConfirmation:n.force||!1,dryRun:!1,allowDeletions:n.allowDeletions||!1}))throw new Error("Operation cancelled by user");let x=c.map(y=>T.unifiedToVercel(y).result),S=d.map(y=>T.unifiedToVercel(y).result),q=u.map(y=>T.unifiedToVercel(y).result),W=f.map(y=>({id:y.id||"",ip:y.ip,hostname:y.hostname||"",action:y.action,notes:y.notes})),Q=g.map(y=>({id:y.id||"",ip:y.ip,hostname:y.hostname||"",action:y.action,notes:y.notes})),ue=m.map(y=>({id:y.id||"",ip:y.ip,hostname:y.hostname||"",action:y.action,notes:y.notes})),K=[],ge=[],k=[],Ae=[],Ee=[],A=[],h=[],b=(y,P)=>`${y}: ${P instanceof Error?P.message:String(P)}`,ee=[];Dt(t.rules)&&t.rules.length>c.length&&ee.push("Rule `priority` is best-effort on Vercel: new rules are created in priority order, but rules that already exist remotely cannot be repositioned via the Vercel API. Recreate them (remove and re-add) if exact evaluation order matters.");let me=[];for(let y of q)try{r.debug(`Deleting custom rule: ${y.id}`),await this.client.deleteFirewallRule(y),k.push(y),r.debug(`Custom rule deleted: ${y.id}`)}catch(P){r.error(`Failed to delete custom rule ${y.id}:`,P),h.push(b(`Failed to delete custom rule ${y.id}`,P))}for(let y of ue)try{r.debug(`Deleting IP blocking rule: ${y.id}`),await this.client.deleteIPBlockingRule(y),A.push(y),r.debug(`IP blocking rule deleted: ${y.id}`)}catch(P){r.error(`Failed to delete IP blocking rule ${y.id}:`,P),h.push(b(`Failed to delete IP blocking rule ${y.id}`,P))}for(let y of x)try{r.debug(`Adding new custom rule: ${y.name}`);let P=await this.client.createFirewallRule(y);K.push(P),P.id&&P.id!==y.id&&me.push({oldId:y.id||void 0,newId:P.id,name:y.name}),r.debug(`New custom rule added: ${P.id}`)}catch(P){r.error(`Failed to add custom rule "${y.name}":`,P),h.push(b(`Failed to add custom rule "${y.name}"`,P))}for(let y of W)try{r.debug(`Adding new IP blocking rule: ${y.ip}`);let P=await this.client.createIPBlockingRule(y);Ae.push(P),r.debug(`New IP blocking rule added: (hostname): ${P.hostname} (ip): ${P.ip}`)}catch(P){r.error(`Failed to add IP blocking rule ${y.ip}:`,P),h.push(b(`Failed to add IP blocking rule ${y.ip}`,P))}for(let y of S)try{r.debug(`Updating custom rule: ${y.id}`);let P=await this.client.updateFirewallRule(y);ge.push(P),r.debug(`Custom rule updated: ${P.id}`)}catch(P){r.error(`Failed to update custom rule ${y.id}:`,P),h.push(b(`Failed to update custom rule ${y.id}`,P))}for(let y of Q)try{r.debug(`Updating IP blocking rule: ${y.id}`);let P=await this.client.updateIPBlockingRule(y);Ee.push(P),r.debug(`IP blocking rule updated: ${P.id}`)}catch(P){r.error(`Failed to update IP blocking rule ${y.id}:`,P),h.push(b(`Failed to update IP blocking rule ${y.id}`,P))}r.debug(`${fe.default.underline("Custom Rules:")} ${fe.default.green("Added:")} ${fe.default.green(K.length)}, ${fe.default.cyan("Updated:")} ${fe.default.cyan(ge.length)}, ${fe.default.red("Deleted:")} ${fe.default.red(k.length)}`),r.debug(`${fe.default.underline("IP Rules:")} ${fe.default.green("Added:")} ${fe.default.green(Ae.length)}, ${fe.default.cyan("Updated:")} ${fe.default.cyan(Ee.length)}, ${fe.default.red("Deleted:")} ${fe.default.red(A.length)}`);let lt=p,ct,Ne=[...ee];try{let y=await this.client.fetchFirewallConfig();lt=y.version,ct=y.updatedAt;let P=new Set(y.rules.map(M=>M.id)),ki=new Set(y.ips.map(M=>M.id));for(let M of[...K,...ge])M.id&&!P.has(M.id)&&Ne.push(`Rule "${M.name}" (${M.id}) was synced but is missing from the post-sync remote config \u2014 a subsequent sync may re-create or misdiff it.`);for(let M of k)M.id&&P.has(M.id)&&Ne.push(`Rule "${M.name}" (${M.id}) was deleted but still appears in the post-sync remote config.`);for(let M of[...Ae,...Ee])M.id&&!ki.has(M.id)&&Ne.push(`IP rule "${M.ip}" (${M.id}) was synced but is missing from the post-sync remote config \u2014 a subsequent sync may re-create or misdiff it.`);for(let M of A)M.id&&ki.has(M.id)&&Ne.push(`IP rule "${M.ip}" (${M.id}) was deleted but still appears in the post-sync remote config.`)}catch(y){r.error("Failed to fetch updated firewall configuration version after sync:",y),h.push(b("Failed to fetch updated configuration version after sync",y))}return{success:h.length===0,rulesAdded:K.length,rulesUpdated:ge.length,rulesDeleted:k.length,ipsAdded:Ae.length,ipsUpdated:Ee.length,ipsDeleted:A.length,version:lt,updatedAt:ct,idRemappings:me.length>0?me:void 0,errors:h.length>0?h:void 0,warnings:Ne.length>0?Ne:void 0}}catch(s){throw r.error("Error during sync:",s),new Error(`Failed to synchronize firewall rules: ${s instanceof Error?s.message:String(s)}`,{cause:s})}}async getChanges(t,n){let o=Ue.safeParse(t);if(!o.success)throw new Error(`Invalid firewall configuration: ${o.error.message}`);this.assertManagedRulesSupported(t);try{r.debug("Fetching existing firewall configuration");let s=await this.client.fetchFirewallConfig(void 0,{allowCreate:n?.allowCreate??!0});r.debug(`Fetched ${s.rules.length} custom rules and ${s.ips.length} IP blocking rules`);let a=s.rules.map(m=>{let v=T.vercelToUnified(m);return v.warnings.length>0&&v.warnings.forEach(z=>{let{TranslationWarningSystem:x}=(pe(),Re(Se)),S=x.formatWarning(z);r.warn(`Rule ${m.name}:
24
+ `),u.cancelled&&process.exit(0),e(o))};t.on("keypress",a)})}_();var rn={provider:"vercel",fields:[{key:"token",envVar:"VERCEL_TOKEN",label:"Vercel API Token",required:!0,secret:!0,promptMessage:"What is your Vercel API Auth Token? (https://vercel.com/guides/how-do-i-use-a-vercel-api-access-token#creating-an-access-token) "},{key:"projectId",envVar:"VERCEL_PROJECT_ID",label:"Vercel Project ID",required:!0,configKey:"projectId",cliAlias:"p",legacyConfigKey:"projectId",promptMessage:"What is your Vercel Project ID? (See https://vercel.com/docs/projects/project-configuration/general-settings#project-id)"},{key:"teamId",envVar:"VERCEL_TEAM_ID",label:"Vercel Team ID",required:!1,configKey:"teamId",cliAlias:"t",legacyConfigKey:"teamId",promptMessage:"What is your Vercel Team ID? Leave blank to use your Vercel default team (every account, including Hobby, is a team). Only needed if you belong to more than one team and want to target a non-default one. (See https://vercel.com/docs/accounts#find-your-team-id)"}]};var sn={provider:"cloudflare",fields:[{key:"apiToken",envVar:"CLOUDFLARE_API_TOKEN",label:"Cloudflare API Token",required:!0,secret:!0},{key:"zoneId",envVar:"CLOUDFLARE_ZONE_ID",label:"Cloudflare Zone ID",required:!0,configKey:"zoneId"},{key:"accountId",envVar:"CLOUDFLARE_ACCOUNT_ID",label:"Cloudflare Account ID",required:!1,configKey:"accountId"}]};var an={provider:"fastly",fields:[{key:"apiToken",envVar:"FASTLY_API_TOKEN",label:"Fastly API Token",required:!0,secret:!0},{key:"workspaceId",envVar:"FASTLY_WORKSPACE_ID",label:"Fastly Next-Gen WAF Workspace ID",required:!0,configKey:"workspaceId"}]};var ln={provider:"gcp",fields:[{key:"serviceAccountKeyPath",envVar:"GOOGLE_APPLICATION_CREDENTIALS",label:"GCP Service Account Key Path",required:!1,promptMessage:"Path to a GCP service account JSON key file (leave blank to use Application Default Credentials \u2014 gcloud auth application-default login, or the GCE/Cloud Run metadata server): "},{key:"projectId",envVar:"GOOGLE_CLOUD_PROJECT",label:"GCP Project ID",required:!0,configKey:"projectId"},{key:"policyName",envVar:"GCP_POLICY_NAME",label:"Cloud Armor Security Policy Name",required:!0,configKey:"policyName"}]};function cn(i,e={}){let{explicit:t={},config:n={},env:o=process.env}=e,s={};for(let a of i.fields)s[a.key]=t[a.key]||n[a.key]||o[a.envVar];return s}function dn(i,e){return i.fields.filter(t=>t.required&&!e[t.key])}var He={vercel:rn,cloudflare:sn,fastly:an,gcp:ln};var St=class{static detectionField(e){return He[e].fields.find(t=>t.required&&t.configKey)}static detect(e){let t=[];if(e&&"provider"in e){let o=e.provider;if(typeof o=="string"&&this.isValidProvider(o))return t.push(`Explicit provider specified in config: ${e.provider}`),{provider:o,confidence:"high",reasons:t};r.warn(`Invalid provider specified in config: ${String(e.provider)}`)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let o=e.providers;for(let s of H){let a=this.detectionField(s);if(!a?.configKey)continue;if(typeof(o[s]||{})[a.configKey]=="string")return t.push(`${a.label} found in config`),{provider:s,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 n=this.detectFromEnvironment();return n.provider?n:(r.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;if(t&&this.isValidProvider(t))return e.push(`Provider set via DOORMAN_PROVIDER environment variable: ${t}`),{provider:t,confidence:"high",reasons:e};for(let n of H){let o=this.detectionField(n);if(!(!o||!process.env[o.envVar]))return e.push(`${o.label} found in environment (${o.envVar})`),{provider:n,confidence:"medium",reasons:e}}return{provider:null,confidence:"low",reasons:["No provider credentials found in environment"]}}static isValidProvider(e){return H.includes(e)}static getProvider(e,t="vercel"){let n=this.detect(e);return n.provider?(r.debug(`Provider detected: ${n.provider} (${n.confidence} confidence)`),n.reasons.length>0&&r.debug(`Detection reasons: ${n.reasons.join(", ")}`),n.provider):(r.debug(`No provider detected, using fallback: ${t}`),t)}static detectAll(e){let t=new Set;if(e&&"provider"in e){let n=e.provider;typeof n=="string"&&this.isValidProvider(n)&&t.add(n)}if(e&&"providers"in e&&typeof e.providers=="object"&&e.providers!==null){let n=e.providers;for(let o of H){let s=this.detectionField(o);if(!s?.configKey)continue;typeof(n[o]||{})[s.configKey]=="string"&&t.add(o)}}e&&"projectId"in e&&typeof e.projectId=="string"&&t.add("vercel");for(let n of H){let o=this.detectionField(n);o&&process.env[o.envVar]&&t.add(n)}return Array.from(t)}};var ei=O(require("chalk"));_();$e();var Ut=i=>({$schema:Di,firewallEnabled:!0,rules:[],ips:[],...i});_();var _e=class{baseUrl;providerName;rateLimitInfo={};constructor(e,t){this.baseUrl=e,this.providerName=t}async makeRequest(e,t={}){let n=`${this.baseUrl}${e}`,{timeout:o=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()},o);let g=await fetch(n,{...l,headers:{"Content-Type":"application/json",...await this.getAuthHeaders(),...l.headers},signal:d.signal});if(u&&(clearTimeout(u),u=null),this.updateRateLimitInfo(g),g.status===429){let v=this.calculateRateLimitWait(f,g);if(f===s)throw new Error(`${this.providerName} API rate limit exceeded after ${s+1} attempt(s) (429 Too Many Requests). Retry after ~${Math.round(v/1e3)}s.`);r.warn(`Rate limit exceeded for ${this.providerName}. Waiting ${v}ms before retry (attempt ${f+1}/${s+1})...`),await this.delay(v);continue}if(!g.ok)throw await this.handleErrorResponse(g);let m;try{let v=await g.text();v.trim()?m=JSON.parse(v):m={}}catch(v){throw r.error(`Failed to parse response from ${n}:`,v),new Error(`Invalid JSON response from ${this.providerName} API: ${v instanceof Error?v.message:String(v)}`)}return this.isLargeResponse(m)&&(r.debug(`Large response detected from ${n}, validating structure...`),this.validateLargeResponse(m)),m}catch(g){if(c=g,u&&(clearTimeout(u),u=null),this.isNonRetryableError(g)||f===s)throw g;let m=a*Math.pow(2,f),v=Math.random()*.1*m,z=Math.min(m+v,3e4);r.debug(`Request failed (attempt ${f+1}/${s+1}). Retrying in ${Math.round(z)}ms...`),r.debug(`Error details: ${g instanceof Error?g.message:String(g)}`),await this.delay(z)}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,n){return this.makeRequest(e,{...n,method:"POST",body:t?JSON.stringify(t):void 0})}async put(e,t,n){return this.makeRequest(e,{...n,method:"PUT",body:t?JSON.stringify(t):void 0})}async patch(e,t,n){return this.makeRequest(e,{...n,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"),n=e.headers.get("X-RateLimit-Remaining"),o=e.headers.get("X-RateLimit-Reset");t&&(this.rateLimitInfo.limit=parseInt(t,10)),n&&(this.rateLimitInfo.remaining=parseInt(n,10)),o&&(this.rateLimitInfo.reset=parseInt(o,10)),this.rateLimitInfo.remaining&&this.rateLimitInfo.remaining<10&&r.warn(`Approaching rate limit for ${this.providerName}: ${this.rateLimitInfo.remaining} requests remaining`)}calculateRateLimitWait(e=0,t){let o=t?this.parseRetryAfter(t.headers.get("Retry-After")):null;if(o!==null)return Math.min(Math.max(o,1e3),6e4);if(this.rateLimitInfo.reset){let l=Math.floor(Date.now()/1e3),c=(this.rateLimitInfo.reset-l)*1e3;return Math.min(Math.max(c,1e3),6e4)}let a=5e3*Math.pow(2,e);return Math.min(a,6e4)}parseRetryAfter(e){if(!e)return null;let t=Number(e);if(Number.isFinite(t))return t*1e3;let n=Date.parse(e);return Number.isNaN(n)?null:n-Date.now()}async handleErrorResponse(e){let t=`${this.providerName} API error: ${e.status} ${e.statusText}`,n=t;try{let o=await e.json();if(typeof o=="object"&&o!==null){let s=o;if(typeof s.error=="string")n=`${t} - ${s.error}`;else if(typeof s.message=="string")n=`${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(", ");n=`${t} - ${a}`}}}catch{}return r.error(n),new Error(n)}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=(n,o=0)=>{if(o>10)return!1;for(let[,s]of Object.entries(n))if(Array.isArray(s)&&s.length>1e3||typeof s=="object"&&s!==null&&t(s,o+1))return!0;return!1};return t(e)}validateLargeResponse(e){if(!e||typeof e!="object")return;let t=(o,s)=>{o.length>5e3&&r.warn(`Large array detected at ${s} with ${o.length} items. This may impact performance.`);let a=Math.min(10,o.length),l=o[0];if(l&&typeof l=="object"){let c=Object.keys(l);for(let d=1;d<a;d++){let u=o[d];if(!u||typeof u!="object"){r.warn(`Inconsistent array structure at ${s}[${d}]: expected object, got ${typeof u}`);continue}let p=Object.keys(u),f=c.filter(g=>!p.includes(g));f.length>0&&r.warn(`Inconsistent array structure at ${s}[${d}]: missing keys ${f.join(", ")}`)}}},n=(o,s="root",a=0)=>{if(!(a>10))for(let[l,c]of Object.entries(o)){let d=`${s}.${l}`;Array.isArray(c)?t(c,d):typeof c=="object"&&c!==null&&n(c,d,a+1)}};n(e)}};var un=process.env.DOORMAN_VERCEL_API_BASE_URL||"https://api.vercel.com/v1/security/firewall/config",Ze=class extends _e{constructor(t,n,o){super("","vercel");this.projectId=t;this.teamId=n;this.token=o}projectId;teamId;token;async getAuthHeaders(){return{Authorization:`Bearer ${this.token}`}}getUrl(t){let n=t!==void 0?`${un}/${t}`:un;r.debug("API URL:",n);let o=this.teamId?`&teamId=${this.teamId}`:"";return`${n}?projectId=${this.projectId}${o}`}async fetchFirewallConfig(t,n){let{allowCreate:o=!0,assumeYes:s=!1}=n??{},a=await this.get(this.getUrl(t));if(r.debug("Config Version:",t??"latest"),r.debug("Fetched Config:",t?a:a.active),t)return a;if(!a||"active"in a&&a.active===null){if(r.warn(ei.default.bold("No firewall configuration found.")),!o)return r.debug("Skipping create-configuration prompt for read-only fetch."),this.emptyConfigPlaceholder();if(s||await C("Would you like to create one?",{type:"confirm"})){r.debug("Creating new empty firewall configuration...");let c=await this.putEmptyConfig();return r.debug("Empty firewall configuration created successfully"),r.debug(`New configuration version: ${ei.default.yellow(c.version)}`),c}else return a.active}return a.active}emptyConfigPlaceholder(){return{version:0,id:"",firewallEnabled:!0,crs:null,rules:[],ips:[],projectKey:"",ownerId:"",updatedAt:new Date(0).toISOString()}}async putEmptyConfig(){let{$schema:t,...n}=Ut();return r.debug("Empty Config:",n),this.putConfig(n)}async putConfig(t){return(await this.put(this.getUrl(),t)).active}async fetchActiveFirewallRules(){return(await this.fetchFirewallConfig())?.rules}async updateFirewallRule(t){let n=!t.id||t.id==="-",o={action:n?"rules.insert":"rules.update",id:n?null:t.id,value:{name:t.name,description:t.description,action:t.action,conditionGroup:t.conditionGroup,active:t.active}};return this.patch(this.getUrl(),o,n?{retries:0}:void 0)}async createFirewallRule(t){return this.updateFirewallRule({...t,id:"-"})}async deleteFirewallRule(t){let n={action:"rules.remove",id:t.id,value:null};await this.patch(this.getUrl(),n)}async updateIPBlockingRule(t){let n=!t.id||t.id==="-",o={action:n?"ip.insert":"ip.update",id:n?null:t.id,value:{action:t.action,hostname:t.hostname,ip:t.ip,notes:t.notes??null}};return this.patch(this.getUrl(),o,n?{retries:0}:void 0)}async createIPBlockingRule(t){return this.updateIPBlockingRule({...t,id:"-"})}async deleteIPBlockingRule(t){let n={action:"ip.remove",id:t.id,value:null};await this.patch(this.getUrl(),n)}async verifyCredentials(){try{return await this.fetchFirewallConfig(),!0}catch(t){return r.debug("Credential verification failed:",t),!1}}};var fe=O(require("chalk"));_();_();var ke=class{validateConfig(e){let t=[],n=[];if(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"}),e&&Array.isArray(e.rules)){let o=new Map;e.rules.forEach((s,a)=>{let l=s.id||s.name;if(!l)return;let c=o.get(l);if(c===void 0){o.set(l,a);return}let d=e.rules[c];n.push({path:`rules[${a}]`,message:s.id?`Rule "${s.name}" has the same id ("${s.id}") as rules[${c}] ("${d?.name}") \u2014 they will collide during sync diffing and one may be silently skipped.`:`Rule "${s.name}" has the same name as rules[${c}] and neither has an explicit id \u2014 they will collide during sync diffing and one may be silently skipped. Add unique ids to disambiguate.`,code:"DUPLICATE_RULE_IDENTIFIER"})})}return{valid:t.length===0,errors:t,warnings:n}}getHealthScore(e){let t=[],n=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)&&(n-=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&&(n-=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&&(n-=5,t.push({severity:"info",category:"maintenance",message:`${a.length} disabled rule(s) found`,suggestion:"Review and remove unused rules"}))}let o=this.calculateGrade(n),s=this.generateRecommendations(e,t);return{score:Math.max(0,n),grade:o,issues:t,recommendations:s}}diffItems(e,t,n,o="id"){let s=[],a=[],l=[],c=d=>d[o];for(let d of e){let u=t.find(p=>c(p)===c(d));u?n(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}}assertManagedRulesSupported(e){if(!(!e.managedRules||e.managedRules.length===0)&&!this.getSupportedFeatures().supportsManagedRules)throw new Error(`${this.name} does not support managed rule groups, but this config declares ${e.managedRules.length}. Remove \`managedRules\` from the config, or switch to a provider that supports them (currently Cloudflare only).`)}validateRuleCount(e,t){if(t&&e>t)throw new Error(`Rule count (${e}) exceeds provider limit (${t})`);t&&e>t*.9&&r.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 n=[];return t.some(o=>o.category==="rules")&&n.push("Consider using predefined templates to get started"),t.some(o=>o.category==="maintainability")&&n.push("Add descriptions to all rules for better documentation"),t.some(o=>o.category==="maintenance")&&n.push("Remove or enable disabled rules to keep configuration clean"),n}logSyncStats(e){r.info(`Sync complete for ${this.name}:`),r.info(` Rules added: ${e.rulesAdded}`),r.info(` Rules updated: ${e.rulesUpdated}`),r.info(` Rules deleted: ${e.rulesDeleted}`),e.ipsAdded!==void 0&&(r.info(` IPs added: ${e.ipsAdded}`),r.info(` IPs updated: ${e.ipsUpdated}`),r.info(` IPs deleted: ${e.ipsDeleted}`)),e.managedRulesAdded!==void 0&&(r.info(` Managed rule groups added: ${e.managedRulesAdded}`),r.info(` Managed rule groups updated: ${e.managedRulesUpdated}`),r.info(` Managed rule groups deleted: ${e.managedRulesDeleted}`)),e.version&&r.info(` New version: ${e.version}`)}};pe();function pn(i){let e=[],t=[];i.conditionGroup.forEach((s,a)=>{for(let l of s.conditions){let c=dr(l.op);t.push({field:fr(l.type),operator:c,...l.value!==void 0?{value:l.value}:{},...l.neg?{negated:!0}:{},...l.key?{key:l.key}:{},group:a})}}),t.length>10&&e.push(U.createWarning("many_conditions",i.id,void 0,`Rule has ${t.length} conditions which may impact performance`,"Consider splitting complex rules into multiple simpler rules for better performance"));let n={type:i.action.mitigate.action,...i.action.mitigate.rateLimit?{rateLimit:{requests:i.action.mitigate.rateLimit.requests,window:i.action.mitigate.rateLimit.window,...i.action.mitigate.rateLimit.characteristics?{characteristics:i.action.mitigate.rateLimit.characteristics}:{},...i.action.mitigate.rateLimit.mitigationTimeout?{mitigationTimeout:i.action.mitigate.rateLimit.mitigationTimeout}:{},...i.action.mitigate.rateLimit.countingExpression?{countingExpression:i.action.mitigate.rateLimit.countingExpression}:{}}}:{},...i.action.mitigate.redirect?{redirect:{location:i.action.mitigate.redirect.location,...i.action.mitigate.redirect.permanent?{permanent:i.action.mitigate.redirect.permanent}:{}}}:{},...i.action.mitigate.actionDuration?{duration:i.action.mitigate.actionDuration}:{}};return{result:{id:i.id,name:i.name,...i.description?{description:i.description}:{},enabled:i.active,conditions:t,conditionLogic:i.conditionGroup.length>1?"OR":"AND",action:n},warnings:e}}function fn(i){let e=[],t=cr(i.conditions,i.id,e);if(i.action.response&&e.push(U.createUnsupportedFeatureWarning("custom response body","unified config","Vercel",i.id,"action.response")),t.length===0)throw new Error(`Rule "${i.name}" has no conditions Vercel can represent \u2014 every condition field is unsupported by Vercel (e.g. Cloudflare-only fields like referer/port). Cannot sync this rule to Vercel.`);return{result:{id:i.id,name:i.name,description:i.description,conditionGroup:t,action:{mitigate:{action:pr(i.action.type),rateLimit:i.action.rateLimit?{requests:i.action.rateLimit.requests,window:i.action.rateLimit.window,characteristics:i.action.rateLimit.characteristics,mitigationTimeout:i.action.rateLimit.mitigationTimeout,countingExpression:i.action.rateLimit.countingExpression}:null,redirect:i.action.redirect?{location:i.action.redirect.location,permanent:i.action.redirect.permanent}:null,actionDuration:i.action.duration||null}},active:i.enabled},warnings:e}}function gn(i){return{id:i.id,ip:i.ip,...i.hostname?{hostname:i.hostname}:{},...i.notes?{notes:i.notes}:{},action:i.action}}function cr(i,e,t){let n=new Map;for(let s of i){let a=s.group??0,l=n.get(a);l?l.push(s):n.set(a,[s])}let o=[];for(let s of n.values()){let a=[];for(let l of s){let c=mr(l.field);if(!c){t.push(U.createUnsupportedFeatureWarning(`condition field '${l.field}'`,"unified config","Vercel",e,l.field));continue}let d=ur(l.operator);if(!d){t.push(U.createUnsupportedFeatureWarning(`operator '${l.operator}'`,"unified config","Vercel",e,l.field));continue}a.push({op:d.op,neg:d.forceNeg!==!!l.negated,type:c,key:l.key,value:l.value})}a.length>0&&o.push({conditions:a})}return o}function dr(i){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists",nex:"not_exists"}[i]||"eq"}function ur(i){return{eq:{op:"eq",forceNeg:!1},ne:{op:"eq",forceNeg:!0},starts_with:{op:"pre",forceNeg:!1},ends_with:{op:"suf",forceNeg:!1},in:{op:"inc",forceNeg:!1},not_in:{op:"inc",forceNeg:!0},contains:{op:"sub",forceNeg:!1},not_contains:{op:"sub",forceNeg:!0},matches:{op:"re",forceNeg:!1},exists:{op:"ex",forceNeg:!1},not_exists:{op:"nex",forceNeg:!1}}[i]??null}function pr(i){return{log:"log",deny:"deny",challenge:"challenge",bypass:"bypass",rate_limit:"rate_limit",redirect:"redirect",allow:"bypass",block:"deny"}[i]}function fr(i){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",region:"vercel_region",geo_country_region:"region"}[i]||i}var gr={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",target_path:"target_path",vercel_region:"region",region:"geo_country_region",protocol:"protocol",environment:"environment",geo_continent:"geo_continent",ja4_digest:"ja4_digest",ja3_digest:"ja3_digest",rate_limit_api_id:"rate_limit_api_id"};function mr(i){return gr[i]??null}pe();_();function Ye(i){return i.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function mn(i){let e="";for(let t=0;t<i.length;t++)i[t]==="\\"&&(i[t+1]==="\\"||i[t+1]==='"')?(e+=i[t+1],t++):e+=i[t];return e}var pt=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","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 n=this.vercelToCloudflare[e];if(!n)throw r.warn(`No Cloudflare mapping for Vercel type: ${e}`),new Error(`Unsupported Vercel rule type for Cloudflare: ${e}`);return e==="header"&&t?`${n}["${Ye(t.toLowerCase())}"]`:e==="cookie"&&t?`${n}["${Ye(t)}"]`:n}static toVercel(e){let t=e.match(/http\.request\.headers\["([^"]+)"\]/);if(t)return{type:"header",key:t[1]};let n=e.match(/http\.cookie\["([^"]+)"\]/);if(n)return{type:"cookie",key:n[1]};if(e==="http.referer")return{type:"header",key:"referer"};let o=this.cloudflareToVercel[e];if(o)return{type:o};throw r.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==="http.referer"||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 hr=new Set(["ip.src"]),yr="http.request.uri.args",Rr="http.request.headers",wr="http.request.cookies",Je=class{static fromVercelConditionGroups(e){if(!e||e.length===0)throw new Error("At least one condition group is required");let t=e.map(n=>{let o=n.conditions.map(s=>this.fromVercelCondition(s));if(o.length===0)throw new Error("Condition group must have at least one condition");return o.length>1?`(${o.join(" and ")})`:o[0]});return t.length>1?t.join(" or "):t[0]}static fromVercelCondition(e){let t=pt.toCloudflare(e.type,e.key),n=this.buildExistsOrComparisonExpression(t,e.op,()=>`${this.mapVercelOperator(e.op)} ${this.formatValue(t,e.value)}`);return e.neg&&(n=`not (${n})`),n}static fromUnifiedConditions(e,t="AND"){if(!e||e.length===0)throw new Error("At least one condition is required");if(!e.some(a=>a.group!==void 0)){let a=e.map(c=>this.fromUnifiedCondition(c)),l=t==="AND"?" and ":" or ";return a.length>1?`(${a.join(l)})`:a[0]}let o=new Map;for(let a of e){let l=a.group??0,c=o.get(l);c?c.push(a):o.set(l,[a])}let s=Array.from(o.values()).map(a=>this.combineWithAnd(a.map(l=>this.fromUnifiedCondition(l))));return s.length>1?this.combineWithOr(s):s[0]}static fromUnifiedCondition(e){if(e.key&&e.field==="query")return this.buildKeyedMapExpression(yr,e.key,e);if(e.field==="header"){if(!e.key)throw new Error('A "header" condition requires a key naming the header');return this.buildKeyedMapExpression(Rr,e.key.toLowerCase(),e)}if(e.key&&e.field==="cookie")return this.buildKeyedMapExpression(wr,e.key,e);let t=this.mapUnifiedFieldToCloudflare(e.field);if(t===null)throw new Error(`Unsupported condition field '${e.field}' for Cloudflare \u2014 filter it out with a warning before calling fromUnifiedCondition (see unifiedToCloudflare).`);let n=this.buildUnifiedExpression(t,e.operator,e.value);return e.negated&&(n=`not (${n})`),n}static buildKeyedMapExpression(e,t,n){let o=Ye(t),s=`${e}["${o}"]`,a;if(n.operator==="exists")a=`has_key(${e}, "${o}")`;else if(n.operator==="not_exists")a=`not (has_key(${e}, "${o}"))`;else if(n.operator==="not_contains")a=`not (any(${s}[*] contains ${this.formatValue(e,n.value)}))`;else if(n.operator==="not_in")a=`not (any(${s}[*] in ${this.formatValue(e,n.value)}))`;else{let l=this.mapUnifiedOperator(n.operator);a=`any(${s}[*] ${l} ${this.formatValue(e,n.value)})`}return n.negated?`not (${a})`:a}static buildExistsOrComparisonExpression(e,t,n){return t==="ex"||t==="exists"?`${e} exists`:t==="nex"||t==="not_exists"?`not (${e} exists)`:`${e} ${n()}`}static buildUnifiedExpression(e,t,n){return t==="not_contains"?`not (${e} contains ${this.formatValue(e,n)})`:t==="not_in"?`not (${e} in ${this.formatValue(e,n)})`:this.buildExistsOrComparisonExpression(e,t,()=>`${this.mapUnifiedOperator(t)} ${this.formatValue(e,n)}`)}static mapVercelOperator(e){return{eq:"eq",pre:"starts_with",suf:"ends_with",inc:"in",sub:"contains",re:"matches",ex:"exists"}[e]||e}static mapUnifiedOperator(e){return{eq:"eq",ne:"ne",contains:"contains",starts_with:"starts_with",ends_with:"ends_with",matches:"matches",in:"in",gt:"gt",ge:"ge",lt:"lt",le:"le",exists:"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",threat_score:"cf.threat_score"}[e]??null}static isFieldSupported(e){return this.mapUnifiedFieldToCloudflare(e)!==null}static formatValue(e,t){return Array.isArray(t)?`{${t.map(o=>this.formatSingleValue(e,o)).join(" ")}}`:this.formatSingleValue(e,t)}static formatSingleValue(e,t){if(typeof t=="string"){if(hr.has(e)){if(!Pe.safeParse(t).success)throw new Error(`Invalid IP address or CIDR range: ${t}`);return t}return`"${Ye(t)}"`}return typeof t=="number"?String(t):typeof t=="boolean"?t?"true":"false":String(t)}static validate(e){if(!e||e.trim().length===0)return!1;let t=0;for(let n of e)if(n==="("&&t++,n===")"&&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 ")})`}};var j=class extends Error{},Qe=class{constructor(e){this.tokens=e}tokens;pos=0;peek(){return this.tokens[this.pos]}next(){let e=this.tokens[this.pos];if(!e)throw new j("Unexpected end of expression");return this.pos++,e}expect(e){let t=this.next();if(t.type!==e)throw new j(`Expected ${e} but got '${t.value}'`);return t}is(e,t){let n=this.peek();return!!n&&n.type===e&&n.value===t}atEnd(){return this.pos>=this.tokens.length}};function Lt(i,e,t,n){let o=[];i.forEach((l,c)=>{if(e(l)){let d=n(l,c);d&&o.push(d);return}if(t(l))for(let d of l.children){if(!e(d))return;let u=n(d,c);u&&o.push(u)}});let s=i.length;return new Set(o.map(l=>l.group)).size!==s?null:{conditions:o,conditionLogic:"OR"}}var Cr={"ip.src":"ip","ip.geoip.country":"country","ip.geoip.subdivision_1":"region","ip.geoip.city":"city","ip.geoip.asnum":"asn","http.request.uri.path":"path","http.host":"host","http.request.method":"method","http.request.headers":"header","http.request.uri.query":"query","http.request.uri.args":"query","http.cookie":"cookie","http.request.cookies":"cookie","http.user_agent":"user_agent","http.referer":"referer",ssl:"scheme","cf.edge.server_port":"port","cf.threat_score":"threat_score"},wn=new Set(["eq","ne","contains","starts_with","ends_with","matches","in","gt","ge","lt","le"]),br=new Set(["and","or","not"]);function vr(i){let e=[],t=0,n=i.length;for(;t<n;){let o=i[t];if(/\s/.test(o)){t++;continue}if(o===","){t++;continue}if(o==="("){e.push({type:"LPAREN",value:o}),t++;continue}if(o===")"){e.push({type:"RPAREN",value:o}),t++;continue}if(o==="{"){e.push({type:"LBRACE",value:o}),t++;continue}if(o==="}"){e.push({type:"RBRACE",value:o}),t++;continue}if(o==="["){e.push({type:"LBRACKET",value:o}),t++;continue}if(o==="]"){e.push({type:"RBRACKET",value:o}),t++;continue}if(o==='"'){let a=t+1,l="",c=!1;for(;a<n;){let d=i[a];if(d==="\\"&&a+1<n){l+=d+i[a+1],a+=2;continue}if(i[a]==='"'){c=!0,a++;break}l+=i[a],a++}if(!c)throw new j("Unterminated string literal");e.push({type:"STRING",value:mn(l)}),t=a;continue}let s=t;for(;s<n&&!/[\s(){}[\]",]/.test(i[s]);)s++;if(s===t)throw new j(`Unexpected character '${o}' at position ${t}`);e.push({type:"WORD",value:i.slice(t,s)}),t=s}return e}function ni(i){let e=[hn(i)];for(;i.is("WORD","or");)i.next(),e.push(hn(i));return e.length===1?e[0]:{type:"or",children:e}}function hn(i){let e=[yn(i)];for(;i.is("WORD","and");)i.next(),e.push(yn(i));return e.length===1?e[0]:{type:"and",children:e}}function yn(i){if(i.is("WORD","not")){i.next(),i.expect("LPAREN");let e=ni(i);return i.expect("RPAREN"),{type:"not",child:e}}return Ir(i)}function Ir(i){let e=i.peek();if(e&&e.type==="LPAREN"){i.next();let t=ni(i);return i.expect("RPAREN"),t}return $r(i)}function Er(i){let e=i.expect("WORD");if(br.has(e.value))throw new j(`Expected a field reference but got keyword '${e.value}'`);if(i.peek()?.type==="LBRACKET"){i.next();let t=i.expect("STRING");return i.expect("RBRACKET"),{field:e.value,key:t.value}}return{field:e.value}}function Rn(i){let e=i.next();if(e.type==="STRING")return e.value;if(e.type==="WORD")return/^-?\d+(\.\d+)?$/.test(e.value)?Number(e.value):e.value;throw new j(`Unexpected token '${e.value}' where a value was expected`)}function Cn(i){if(i.peek()?.type==="LBRACE"){i.next();let e=[];for(;i.peek()?.type!=="RBRACE";)e.push(Rn(i));return i.expect("RBRACE"),e}return Rn(i)}function Tr(i){i.next(),i.expect("LPAREN");let e=i.expect("WORD");i.expect("LBRACKET");let t=i.expect("STRING");i.expect("RBRACKET"),i.expect("LBRACKET");let n=i.expect("WORD");if(n.value!=="*")throw new j(`Expected '*' in any(...) array index but got '${n.value}'`);i.expect("RBRACKET");let o=i.next();if(o.type!=="WORD"||!wn.has(o.value))throw new j(`Unsupported or unrecognized operator '${o.value}' inside any(...)`);let s=Cn(i);return i.expect("RPAREN"),{type:"comparison",field:e.value,key:t.value,operator:o.value,value:s}}function Ar(i){i.next(),i.expect("LPAREN");let e=i.expect("WORD"),t=i.expect("STRING");return i.expect("RPAREN"),{type:"exists",field:e.value,key:t.value}}function $r(i){if(i.is("WORD","any"))return Tr(i);if(i.is("WORD","has_key"))return Ar(i);let{field:e,key:t}=Er(i);if(i.is("WORD","exists"))return i.next(),{type:"exists",field:e,key:t};let n=i.next();if(n.type!=="WORD"||!wn.has(n.value))throw new j(`Unsupported or unrecognized operator '${n.value}'`);let o=Cn(i);return{type:"comparison",field:e,key:t,operator:n.value,value:o}}function Pr(i){let e=new Qe(vr(i)),t=ni(e);if(!e.atEnd())throw new j("Unexpected trailing tokens");return t}function ti(i){return i.type==="comparison"||i.type==="exists"?!0:i.type==="not"?i.child.type==="comparison"||i.child.type==="exists":!1}function ii(i,e){let t=i.type==="not",n=i.type==="not"?i.child:i;if(n.type!=="comparison"&&n.type!=="exists")return null;let o=Cr[n.field];if(!o)return null;if(n.type==="exists")return{field:o,operator:t?"not_exists":"exists",value:"",key:n.key,group:e};let s=n.operator,a;return t&&(n.operator==="contains"?s="not_contains":n.operator==="in"?s="not_in":a=!0),{field:o,operator:s,value:n.value,key:n.key,negated:a,group:e}}function _r(i){if(ti(i)){let e=ii(i,0);return e?{conditions:[e],conditionLogic:"AND"}:null}if(i.type==="and"){let e=[];for(let t of i.children){if(!ti(t))return null;let n=ii(t,0);if(!n)return null;e.push(n)}return{conditions:e,conditionLogic:"AND"}}return i.type==="or"?Lt(i.children,ti,e=>e.type==="and",ii):null}function oi(i){if(!i||!i.trim())return null;try{let e=Pr(i);return _r(e)}catch{return null}}function In(i){let e=[],t=oi(i.expression),n=[],o;t?(n=t.conditions,o=t.conditionLogic):e.push(U.createWarning("complex_expressions",i.id,"expression","Expression could not be parsed back into structured conditions \u2014 it may be hand-authored or use syntax outside what doorman itself generates.","Review the translated rule and add missing conditions manually if needed."));let s=i.action_parameters&&"response"in i.action_parameters?i.action_parameters.response:void 0,a={type:kr(i.action),rateLimit:i.ratelimit?{requests:i.ratelimit.requests_per_period,window:`${i.ratelimit.period}s`,characteristics:i.ratelimit.characteristics,mitigationTimeout:i.ratelimit.mitigation_timeout,countingExpression:i.ratelimit.counting_expression}:void 0,response:s?{statusCode:s.status_code,content:s.content,contentType:s.content_type}:void 0};return{result:{id:i.id,name:i.description||`Rule ${i.id}`,description:i.description,enabled:i.enabled??!0,conditions:n,conditionLogic:o,action:a},warnings:e}}function En(i){let e=[],t=Ur(i.conditions,i.id,e);if(t.length===0)throw new Error(`Rule "${i.name}" has no conditions Cloudflare can represent \u2014 every condition field is unsupported by Cloudflare (e.g. Vercel-only fields like environment/ja3_digest, or vercel_region). Cannot sync this rule to Cloudflare.`);let n=Je.fromUnifiedConditions(t,i.conditionLogic),o={id:i.id||crypto.randomUUID(),action:Sr(i.action.type),expression:n,description:i.description||i.name,enabled:i.enabled};return i.action.rateLimit&&(o.ratelimit={characteristics:i.action.rateLimit.characteristics||["ip.src"],period:Lr(i.action.rateLimit.window),requests_per_period:i.action.rateLimit.requests},i.action.rateLimit.mitigationTimeout?o.ratelimit.mitigation_timeout=i.action.rateLimit.mitigationTimeout:(o.ratelimit.mitigation_timeout=3600,e.push(U.createWarning("rate_limiting_precision",i.id,"action.rateLimit.mitigationTimeout",`No mitigationTimeout set for rate limit rule "${i.name}" \u2014 defaulted to Cloudflare's 3600s (1 hour) block duration.`))),i.action.rateLimit.countingExpression&&(o.ratelimit.counting_expression=i.action.rateLimit.countingExpression)),i.action.type==="redirect"&&i.action.redirect&&(o.action_parameters={from_value:{status_code:i.action.redirect.statusCode??(i.action.redirect.permanent?301:302),target_url:{value:i.action.redirect.location},...i.action.redirect.preserveQueryString!==void 0?{preserve_query_string:i.action.redirect.preserveQueryString}:{}}}),i.action.response&&(o.action!=="block"?e.push(U.createUnsupportedFeatureWarning(`custom response on a '${o.action}' action`,"unified config","Cloudflare",i.id,"action.response")):i.action.response.content?o.action_parameters={response:{status_code:i.action.response.statusCode??403,content:i.action.response.content,content_type:i.action.response.contentType??"text/plain"}}:e.push(U.createWarning("lossy_conversion",i.id,"action.response","Custom response declared without `content`; Cloudflare requires a response body, so it was dropped.","Set action.response.content to the body you want returned."))),{result:o,warnings:e}}function Tn(i){if(!Pe.safeParse(i.ip).success)throw new Error(`Invalid IP address or CIDR range: ${i.ip}`);let t=i.ip.includes("/")?`ip.src in {${i.ip}}`:`ip.src eq ${i.ip}`;return{id:i.id||crypto.randomUUID(),action:i.action==="allow"?"allow":"block",expression:t,description:i.notes||`IP ${i.action}: ${i.ip}${i.hostname?` (${i.hostname})`:""}`,enabled:!0}}function An(i){let e=[],t={};if(i.action){let{action:s,warning:a}=bn(i.action,i.id,"action");a&&e.push(a),s&&(t.action=s)}i.overrides&&i.overrides.length>0&&(t.rules=i.overrides.map(s=>{let a={id:s.ruleId};if(s.enabled!==void 0&&(a.enabled=s.enabled),s.action){let{action:l,warning:c}=bn(s.action,i.id,`overrides[${s.ruleId}].action`);c&&e.push(c),l&&(a.action=l)}return a}));let n={id:i.ruleset,...Object.keys(t).length>0?{overrides:t}:{}};return{result:{id:i.id||crypto.randomUUID(),action:"execute",expression:"true",description:i.name||`Managed ruleset ${i.ruleset}`,enabled:i.enabled,action_parameters:n},warnings:e}}function $n(i){let e=[],t=i.action_parameters,n={id:i.id,ruleset:t?.id??"",enabled:i.enabled??!0,...i.description?{name:i.description}:{}};return t?.overrides?.action&&(n.action=vn(t.overrides.action)),t?.overrides?.rules&&t.overrides.rules.length>0&&(n.overrides=t.overrides.rules.map(o=>({ruleId:o.id,...o.action!==void 0?{action:vn(o.action)}:{},...o.enabled!==void 0?{enabled:o.enabled}:{}}))),{result:n,warnings:e}}function bn(i,e,t){let o={log:"log",deny:"block",block:"block",challenge:"managed_challenge",allow:"allow"}[i];return o?{action:o}:{warning:U.createUnsupportedFeatureWarning(`'${i}' as a managed-ruleset override action`,"unified config","Cloudflare",e,t)}}function vn(i){return{log:"log",block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",allow:"allow"}[i]||"deny"}function kr(i){return{block:"deny",challenge:"challenge",managed_challenge:"challenge",js_challenge:"challenge",log:"log",skip:"bypass",allow:"allow",rewrite:"bypass",redirect:"redirect",execute:"log"}[i]||"deny"}function Sr(i){return{log:"log",deny:"block",block:"block",challenge:"managed_challenge",bypass:"skip",rate_limit:"block",redirect:"redirect",allow:"allow"}[i]||"block"}function Ur(i,e,t){return i.filter(n=>Je.isFieldSupported(n.field)?!0:(t.push(U.createUnsupportedFeatureWarning(`condition field '${n.field}'`,"unified config","Cloudflare",e,n.field)),!1))}function Lr(i){let e=i.match(/^(\d+)([smhd])$/);if(!e||!e[1]||!e[2])throw new Error(`Invalid window format: ${i}`);let t=parseInt(e[1],10),n=e[2],s={s:1,m:60,h:3600,d:86400}[n];if(s===void 0)throw new Error(`Invalid time unit: ${n}`);return t*s}pe();function we(i,e,t){return U.createWarning("complex_expressions",i,void 0,e,t)}var xr={ip:"ip",path:"path",domain:"host",method:"method",country:"country",scheme:"scheme",user_agent:"user_agent"},Dr={ip:"ip",path:"path",host:"domain",method:"method",country:"country",scheme:"scheme",user_agent:"user_agent"},Or={header:"request_header",query:"query_parameter",cookie:"request_cookie"},Fr={request_header:"header",query_parameter:"query",request_cookie:"cookie",response_header:"response_header",post_parameter:"post_parameter",signal:"signal"};function Nr(i){return xr[i]??i}function Vr(i){return Dr[i]??null}var Br={equals:{operator:"eq",negated:!1},does_not_equal:{operator:"eq",negated:!0},contains:{operator:"contains",negated:!1},does_not_contain:{operator:"contains",negated:!0},like:{operator:"matches",negated:!1},not_like:{operator:"matches",negated:!0},in_list:{operator:"in",negated:!1},not_in_list:{operator:"in",negated:!0},matches:{operator:"matches",negated:!1},does_not_match:{operator:"matches",negated:!0},greater_equal:{operator:"ge",negated:!1},lesser_equal:{operator:"le",negated:!1}};function Pn(i){return Br[i]??{operator:"eq",negated:!1}}function _n(i,e,t,n){switch(i){case"eq":return e?"does_not_equal":"equals";case"ne":return"does_not_equal";case"contains":return e?"does_not_contain":"contains";case"not_contains":return"does_not_contain";case"starts_with":case"ends_with":return t.push(U.createWarning("regex_patterns",n,void 0,`Unified operator "${i}" has no direct Fastly equivalent \u2014 mapped to Fastly's wildcard "like" operator. Verify the translated value still matches as a wildcard pattern.`)),e?"not_like":"like";case"matches":return t.push(U.createWarning("regex_patterns",n,void 0,"Regular expression pattern may need adjustment for Fastly Next-Gen WAF regex syntax.")),e?"does_not_match":"matches";case"in":return e?"not_in_list":"in_list";case"not_in":return"not_in_list";case"gt":return t.push(we(n,'Unified operator "gt" (strictly greater than) has no Fastly equivalent \u2014 mapped to "greater_equal", which also matches the boundary value itself.')),"greater_equal";case"ge":return"greater_equal";case"lt":return t.push(we(n,'Unified operator "lt" (strictly less than) has no Fastly equivalent \u2014 mapped to "lesser_equal", which also matches the boundary value itself.')),"lesser_equal";case"le":return"lesser_equal";default:return"equals"}}function Mr(i,e){let t=[],n=i.conditions.filter(s=>s.type==="group"),o=i.conditions.filter(s=>s.type!=="group");if(n.length>0&&o.length===0&&i.group_operator==="any")return n.forEach((s,a)=>{s.group_operator!=="all"&&e.push(we(i.id,`A condition group using "any" (OR) inside an outer "any" (OR) rule cannot be represented in doorman's flat AND-within/OR-across condition model \u2014 its members were kept in the same group (effectively AND'd) instead of OR'd. Review the translated rule against the original Fastly logic.`));for(let l of s.conditions)xt(t,l,a,e,i.id)}),{conditions:t,conditionLogic:n.length>1?"OR":"AND"};if(n.length===0)return o.forEach((s,a)=>{let l=i.group_operator==="any"?a:0;xt(t,s,l,e,i.id)}),{conditions:t,conditionLogic:i.group_operator==="any"&&o.length>1?"OR":"AND"};e.push(we(i.id,`Rule mixes top-level conditions with condition groups under group_operator "${i.group_operator}" \u2014 this doesn't fit doorman's flat AND-within/OR-across condition model. All conditions were combined with AND; review the translated rule against the original Fastly rule logic.`));for(let s of o)xt(t,s,0,e,i.id);for(let s of n)for(let a of s.conditions)xt(t,a,0,e,i.id);return{conditions:t,conditionLogic:"AND"}}function xt(i,e,t,n,o){if(e.type==="single"){let{operator:u,negated:p}=Pn(e.operator);i.push({field:Nr(e.field),operator:u,value:e.value,...p?{negated:!0}:{},group:t});return}let s=Fr[e.field],a=e.conditions.find(u=>u.field==="name"),l=e.conditions.find(u=>u.field==="value"||u.field==="value_string");if(!l){if(a){i.push({field:s,operator:e.operator==="does_not_exist"?"not_exists":"exists",key:a.value,group:t});return}n.push(we(o,`Multival condition on '${e.field}' has no value or name sub-condition doorman could extract \u2014 skipped.`));return}let{operator:c,negated:d}=Pn(l.operator);i.push({field:s,operator:c,value:l.value,...d?{negated:!0}:{},...a?{key:a.value}:{},group:t})}function qr(i,e){i.actions.length>1&&e.push(we(i.id,`Rule has ${i.actions.length} actions \u2014 doorman only models one action per rule; only the first ("${i.actions[0]?.type}") was translated.`));let t=i.actions[0];if(i.type==="rate_limit")return{type:"rate_limit",rateLimit:i.rate_limit?{requests:i.rate_limit.threshold,window:`${i.rate_limit.interval}s`,mitigationTimeout:i.rate_limit.duration}:void 0};switch(t?.type){case"block":return{type:"block"};case"allow":return{type:"allow"};case"browser_challenge":case"dynamic_challenge":case"verify_token":return{type:"challenge"};case"redirect":return{type:"redirect",redirect:{location:t.redirect_url??"",...t.response_code!==void 0?{statusCode:t.response_code}:{}}};case"add_signal":case"exclude_signal":case"deception":return e.push(U.createWarning("action_differences",i.id,"action.type",`Fastly action "${t.type}" tags or excludes a WAF signal rather than allowing/blocking the request \u2014 doorman has no matching action type. Mapped to "log" as the closest non-destructive fallback; review this rule.`)),{type:"log"};default:return e.push(we(i.id,`Unrecognised Fastly action type "${String(t?.type)}" \u2014 mapped to "log".`)),{type:"log"}}}function kn(i){let e=[],{conditions:t,conditionLogic:n}=Mr(i,e),o=qr(i,e);return t.length>10&&e.push(U.createWarning("many_conditions",i.id,void 0,`Rule has ${t.length} conditions which may impact performance`)),{result:{id:i.id,name:i.description||i.id,enabled:i.enabled,conditions:t,conditionLogic:n,action:o},warnings:e}}function Wr(i,e,t){let n=new Map;for(let a of i){let l=a.group??0,c=n.get(l);c?c.push(a):n.set(l,[a])}let o=a=>{let l=Or[a.field];if(l){if(a.key===void 0)return t.push(we(e,`Condition on '${a.field}' has no 'key' set \u2014 Fastly needs a specific ${a.field} name to match on (e.g. a header name). Skipped.`)),null;if(a.operator==="exists"||a.operator==="not_exists")return{type:"multival",field:l,operator:a.operator==="exists"?"exists":"does_not_exist",group_operator:"all",conditions:[{type:"single",field:"name",operator:"equals",value:a.key}]};let d=l==="request_header"||l==="response_header"?"value_string":"value";return{type:"multival",field:l,operator:"exists",group_operator:"all",conditions:[{type:"single",field:"name",operator:"equals",value:a.key},{type:"single",field:d,operator:_n(a.operator,a.negated,t,e),value:String(a.value)}]}}if(a.operator==="exists"||a.operator==="not_exists")return t.push(U.createUnsupportedFeatureWarning(`"${a.operator}" on field '${a.field}'`,"unified config","Fastly",e,a.field)),null;let c=Vr(a.field);return c?{type:"single",field:c,operator:_n(a.operator,a.negated,t,e),value:String(a.value)}:(t.push(U.createUnsupportedFeatureWarning(`condition field '${a.field}'`,"unified config","Fastly",e,a.field)),null)};if(n.size<=1){let a=i.map(o).filter(l=>l!==null);return{conditions:a.filter(l=>l.type==="single"),groupConditions:[],multivalConditions:a.filter(l=>l.type==="multival"),groupOperator:"all"}}let s=[];for(let a of n.values()){let l=a.map(o).filter(c=>c!==null);l.length>0&&s.push({type:"group",group_operator:"all",conditions:l})}return{conditions:[],groupConditions:s,multivalConditions:[],groupOperator:"any"}}function jr(i,e,t){switch(i.type){case"block":case"deny":return{type:"block"};case"allow":return{type:"allow"};case"challenge":return{type:"browser_challenge"};case"bypass":return e.push(we(t,'Unified action "bypass" has no direct Fastly equivalent \u2014 mapped to "allow".')),{type:"allow"};case"log":return e.push(we(t,'Unified action "log" has no dedicated Fastly action \u2014 mapped to "allow" (request_logging is enabled at the rule level regardless).')),{type:"allow"};case"redirect":return i.redirect?{type:"redirect",redirect_url:i.redirect.location,response_code:i.redirect.statusCode??(i.redirect.permanent?301:302)}:(e.push(we(t,'Unified action "redirect" has no redirect target \u2014 mapped to "allow".')),{type:"allow"});case"rate_limit":return{type:"block_signal"};default:return e.push(U.createUnsupportedFeatureWarning(`action type '${i.type}'`,"unified config","Fastly",t,"action.type")),{type:"block"}}}function zr(i,e,t){let n=/^(\d+)\s*(s|m|h)?$/i.exec(i.trim()),o={s:1,m:60,h:3600},s=n?Number(n[1])*o[n[2]?.toLowerCase()||"s"]:NaN,a=[60,600,3600];if(!Number.isFinite(s)||s<=0)return e.push(U.createWarning("rate_limiting_precision",t,void 0,`Rate limit window "${i}" could not be parsed \u2014 defaulted to Fastly's 60-second interval.`)),60;let l=a.reduce((c,d)=>Math.abs(d-s)<Math.abs(c-s)?d:c);return l!==s&&e.push(U.createWarning("rate_limiting_precision",t,void 0,`Rate limit window "${i}" (${s}s) isn't one of Fastly's supported intervals (60s/600s/3600s) \u2014 rounded to ${l}s.`)),l}function Gr(i,e,t){if(!i)throw new Error(`Rule "${e??"(unnamed)"}" has action type "rate_limit" but no rateLimit configuration \u2014 Fastly requires a threshold and interval.`);let n=zr(i.window,t,e);return t.push(U.createWarning("rate_limiting_precision",e,void 0,`Fastly rate-limit rules count requests against a named custom signal, which doorman does not create automatically \u2014 create a signal named "doorman-rate-limit-${e??"rule"}" in this workspace before syncing, or the rule will be rejected.`)),{client_identifiers:[{type:"ip"}],duration:i.mitigationTimeout??n,interval:n,signal:`doorman-rate-limit-${e??"rule"}`,threshold:i.requests}}function Sn(i){let e=[];i.description&&i.description!==i.name&&e.push(we(i.id,'Fastly rules have no separate "name" field \u2014 only "description" carries over, and it was set from this rule\'s `name`. The `description` text was dropped rather than merged in, to keep it round-trip-stable on the next sync.'));let{conditions:t,groupConditions:n,multivalConditions:o,groupOperator:s}=Wr(i.conditions,i.id,e);if(t.length===0&&n.length===0&&o.length===0)throw new Error(`Rule "${i.name}" has no conditions Fastly can represent \u2014 every condition field is unsupported by Fastly. Cannot sync this rule to Fastly.`);let a=i.action.type==="rate_limit";return{result:{type:a?"rate_limit":"request",description:i.name,enabled:i.enabled,group_operator:s,request_logging:"sampled",conditions:t,group_conditions:n,multival_conditions:o,actions:[jr(i.action,e,i.id)],rate_limit:a?Gr(i.action.rateLimit,i.id,e):null},warnings:e}}function Un(i,e){return i.map(t=>({ip:t,action:e}))}function Ln(i){let e=[],t=[],n=[];for(let o of i)(o.hostname||o.notes)&&e.push(we(o.id,`IP rule "${o.ip}" sets ${o.hostname?"`hostname`":""}${o.hostname&&o.notes?" and ":""}${o.notes?"`notes`":""} \u2014 Fastly IP lists have no equivalent per-entry metadata; only the address was kept.`)),(o.action==="allow"?n:t).push(o.ip);return{result:{denyEntries:t,allowEntries:n},warnings:e}}pe();function Xe(i){return i.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}function xn(i){let e="";for(let t=0;t<i.length;t++)i[t]==="\\"&&(i[t+1]==="\\"||i[t+1]==="'")?(e+=i[t+1],t++):e+=i[t];return e}var Kr=new Set(["region","city","port","scheme"]),Hr={host:"host",user_agent:"user-agent",referer:"referer"},ft=class{static fromUnifiedConditions(e,t="AND"){if(!e||e.length===0)throw new Error("At least one condition is required");if(!e.some(a=>a.group!==void 0)){let a=e.map(c=>this.fromUnifiedCondition(c)),l=t==="AND"?" && ":" || ";return a.length>1?`(${a.join(l)})`:a[0]}let o=new Map;for(let a of e){let l=a.group??0,c=o.get(l);c?c.push(a):o.set(l,[a])}let s=Array.from(o.values()).map(a=>this.combineWithAnd(a.map(l=>this.fromUnifiedCondition(l))));return s.length>1?this.combineWithOr(s):s[0]}static fromUnifiedCondition(e){if(Kr.has(e.field))throw new Error(`Cloud Armor has no CEL equivalent for the "${e.field}" condition field \u2014 this rule cannot be represented and would otherwise be silently dropped.`);if(e.field==="ip")return this.buildIpCondition(e);if(e.field==="cookie")return this.buildCookieCondition(e);let t=Hr[e.field];if(t)return this.buildHeaderCondition(t,e);if(e.field==="header"||e.field==="query"){if(e.field==="header"){if(!e.key)throw new Error('A "header" condition requires a key naming the header');return this.buildHeaderCondition(e.key,e)}return this.buildComparison("request.query",e,!1)}let n=Yr[e.field];if(n)return this.buildComparison(n.path,e,n.numeric);throw new Error(`Cloud Armor CEL builder has no mapping for condition field "${e.field}"`)}static buildIpCondition(e){let t=(Array.isArray(e.value)?e.value:[e.value]).map(String);if(t.length===0||t.some(a=>!a))throw new Error('An "ip" condition requires at least one value');let n=t.map(a=>{if(!Pe.safeParse(a).success)throw new Error(`Invalid IP address or CIDR range: ${a}`);return a.includes("/")?`inIpRange(origin.ip, '${Xe(a)}')`:`origin.ip == '${Xe(a)}'`}),o=this.isNegated(e,e.operator==="not_in"||e.operator==="ne");if(n.length>1){let a=`(${n.join(" || ")})`;return o?`!${a}`:a}let s=n[0];return o?s.startsWith("inIpRange(")?`!${s}`:`!(${s})`:s}static buildHeaderCondition(e,t){let n=`request.headers['${Xe(e)}']`,o=`has(${n})`;return t.operator==="exists"||t.operator==="not_exists"?this.isNegated(t,t.operator==="not_exists")?`!${o}`:o:`(${o} && ${this.buildComparison(n,t,!1)})`}static buildCookieCondition(e){let t="request.headers['cookie']",n=`has(${t})`;if(e.operator==="exists"||e.operator==="not_exists")return this.isNegated(e,e.operator==="not_exists")?`!${n}`:n;if(!e.key)return`(${n} && ${this.buildComparison(t,e,!1)})`;if(!["eq","ne","contains","not_contains"].includes(e.operator))throw new Error(`Cloud Armor can only check a specific cookie's value via substring search (has no parsed cookie map) \u2014 "${e.operator}" on a keyed cookie condition has no unambiguous CEL representation.`);let o=`${e.key}=${String(e.value)}`,s=`${t}.contains('${Xe(o)}')`,a=this.isNegated(e,e.operator==="ne"||e.operator==="not_contains");return`(${n} && ${a?`!${s}`:s})`}static buildComparison(e,t,n){let{value:o}=t;if(t.operator==="exists"||t.operator==="not_exists")throw new Error(`"${t.operator}" is not meaningful for Cloud Armor's "${e}" \u2014 it is always present`);let s=t.operator,a=!1;if(t.negated){let d=Zr[s];d?s=d:a=!0}if(s==="in"||s==="not_in"){let u=(Array.isArray(o)?o:[o]).map(f=>this.formatLiteral(f,n)).join(", "),p=`${e} in [${u}]`;return s==="not_in"?`!(${p})`:p}let l=this.formatLiteral(o,n),c;switch(s){case"eq":c=`${e} == ${l}`;break;case"ne":c=`${e} != ${l}`;break;case"contains":c=`${e}.contains(${l})`;break;case"not_contains":c=`!${e}.contains(${l})`;break;case"starts_with":c=`${e}.startsWith(${l})`;break;case"ends_with":c=`${e}.endsWith(${l})`;break;case"matches":c=`${e}.matches(${l})`;break;case"gt":c=`${e} > ${l}`;break;case"ge":c=`${e} >= ${l}`;break;case"lt":c=`${e} < ${l}`;break;case"le":c=`${e} <= ${l}`;break;default:throw new Error(`Cloud Armor CEL builder has no mapping for operator "${s}"`)}return a?`!(${c})`:c}static isNegated(e,t){return t!==!!e.negated}static formatLiteral(e,t){if(t){let n=Number(e);if(!Number.isFinite(n))throw new Error(`Expected a numeric value, got "${String(e)}"`);return String(n)}return`'${Xe(String(e))}'`}static combineWithAnd(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" && ")})`}static combineWithOr(e){if(e.length===0)throw new Error("At least one expression is required");return e.length===1?e[0]:`(${e.join(" || ")})`}},Zr={eq:"ne",ne:"eq",contains:"not_contains",not_contains:"contains",in:"not_in",not_in:"in"},Yr={country:{path:"origin.region_code",numeric:!1},asn:{path:"origin.asn",numeric:!0},path:{path:"request.path",numeric:!1},method:{path:"request.method",numeric:!1}};var Jr={"origin.region_code":"country","origin.asn":"asn","request.path":"path","request.method":"method","request.query":"query"},Qr={host:"host","user-agent":"user_agent",referer:"referer",cookie:"cookie"},Bn={contains:"contains",startsWith:"starts_with",endsWith:"ends_with",matches:"matches"};function Xr(i){let e=[],t=0,n=i.length,o=(s,a)=>i[t]===s&&i[t+1]===a;for(;t<n;){let s=i[t];if(/\s/.test(s)){t++;continue}if(s==="("){e.push({type:"LPAREN",value:s}),t++;continue}if(s===")"){e.push({type:"RPAREN",value:s}),t++;continue}if(s==="["){e.push({type:"LBRACKET",value:s}),t++;continue}if(s==="]"){e.push({type:"RBRACKET",value:s}),t++;continue}if(s===","){e.push({type:"COMMA",value:s}),t++;continue}if(o("&","&")){e.push({type:"AND",value:"&&"}),t+=2;continue}if(o("|","|")){e.push({type:"OR",value:"||"}),t+=2;continue}if(o("=","=")){e.push({type:"EQ",value:"=="}),t+=2;continue}if(o("!","=")){e.push({type:"NE",value:"!="}),t+=2;continue}if(o(">","=")){e.push({type:"GE",value:">="}),t+=2;continue}if(o("<","=")){e.push({type:"LE",value:"<="}),t+=2;continue}if(s==="!"){e.push({type:"NOT",value:"!"}),t++;continue}if(s===">"){e.push({type:"GT",value:">"}),t++;continue}if(s==="<"){e.push({type:"LT",value:"<"}),t++;continue}if(s==="."){e.push({type:"DOT",value:s}),t++;continue}if(s==="'"){let a=t+1,l="",c=!1;for(;a<n;){let d=i[a];if(d==="\\"&&a+1<n){l+=d+i[a+1],a+=2;continue}if(i[a]==="'"){c=!0,a++;break}l+=i[a],a++}if(!c)throw new j("Unterminated string literal");e.push({type:"STRING",value:xn(l)}),t=a;continue}if(/[0-9]/.test(s)||s==="-"&&/[0-9]/.test(i[t+1]??"")){let a=t+1;for(;a<n&&/[0-9.]/.test(i[a]);)a++;e.push({type:"NUMBER",value:i.slice(t,a)}),t=a;continue}if(/[A-Za-z_]/.test(s)){let a=t;for(;a<n&&/[A-Za-z0-9_]/.test(i[a]);)a++;e.push({type:"IDENT",value:i.slice(t,a)}),t=a;continue}throw new j(`Unexpected character '${s}' at position ${t}`)}return e}function es(i){let e=i.expect("IDENT").value;for(;i.peek()?.type==="DOT";){let t=i.pos;i.next();let n=i.expect("IDENT");if(i.peek()?.type==="LPAREN"){i.pos=t;break}e+=`.${n.value}`}return e}function ri(i){let e=es(i);if(i.peek()?.type==="LBRACKET"){i.next();let t=i.expect("STRING");return i.expect("RBRACKET"),{field:e,key:t.value}}return{field:e}}function Dn(i){let e=i.next();if(e.type==="STRING")return e.value;if(e.type==="NUMBER")return Number(e.value);throw new j(`Unexpected token '${e.value}' where a literal was expected`)}function ts(i){if(i.is("IDENT","has")){i.next(),i.expect("LPAREN");let a=ri(i);return i.expect("RPAREN"),{type:"has",...a}}if(i.is("IDENT","inIpRange")){i.next(),i.expect("LPAREN");let a=ri(i);if(a.field!=="origin.ip")throw new j(`inIpRange must reference origin.ip, got '${a.field}'`);i.expect("COMMA");let l=i.expect("STRING");return i.expect("RPAREN"),{type:"inIpRange",value:l.value}}let e=ri(i);if(i.peek()?.type==="DOT"){i.next();let a=i.expect("IDENT").value;if(!Bn[a])throw new j(`Unrecognized method '${a}'`);i.expect("LPAREN");let l=i.expect("STRING");return i.expect("RPAREN"),{type:"methodCall",...e,method:a,value:l.value}}if(i.is("IDENT","in")){i.next(),i.expect("LBRACKET");let a=[];for(;i.peek()?.type!=="RBRACKET";)a.push(Dn(i)),i.peek()?.type==="COMMA"&&i.next();return i.expect("RBRACKET"),{type:"membership",...e,values:a}}let t=i.next(),o={EQ:"eq",NE:"ne",GT:"gt",GE:"ge",LT:"lt",LE:"le"}[t.type];if(!o)throw new j(`Unexpected token '${t.value}' after field reference`);let s=Dn(i);return{type:"comparison",...e,operator:o,value:s}}function On(i){if(i.peek()?.type==="LPAREN"){i.next();let e=Mn(i);return i.expect("RPAREN"),e}return ts(i)}function Fn(i){return i.peek()?.type==="NOT"?(i.next(),{type:"not",child:On(i)}):On(i)}function Nn(i){let e=[Fn(i)];for(;i.peek()?.type==="AND";)i.next(),e.push(Fn(i));return e.length===1?e[0]:{type:"and",children:e}}function Mn(i){let e=[Nn(i)];for(;i.peek()?.type==="OR";)i.next(),e.push(Nn(i));return e.length===1?e[0]:{type:"or",children:e}}function is(i){let e=new Qe(Xr(i)),t=Mn(e);if(!e.atEnd())throw new j("Unexpected trailing tokens");return t}function ns(i){return i.type==="inIpRange"||i.type==="comparison"&&i.field==="origin.ip"&&i.operator==="eq"}function li(i){return i.type==="or"&&i.children.length>0&&i.children.every(ns)}function os(i){return i.type==="inIpRange"?i.value:String(i.value)}function qn(i){if(i.type!=="and"||i.children.length!==2)return!1;let e=i.children[0],t=i.children[1];if(e.type!=="has")return!1;let n=t.type==="not"?t.child:t;return!("field"in n)||n.field!==e.field||n.key!==e.key?!1:n.type==="comparison"||n.type==="methodCall"||n.type==="membership"}function rs(i){let e=i.children[0],t=i.children[1],n=t.type==="not",o=n?t.child:t;return{guard:e,comparisonNegated:n,comparison:o}}function si(i){if(i.type==="has"||i.type==="comparison"||i.type==="methodCall"||i.type==="membership"||i.type==="inIpRange"||li(i)||qn(i))return!0;if(i.type==="not"){let e=i.child;return e.type==="has"||e.type==="comparison"||e.type==="methodCall"||e.type==="membership"||e.type==="inIpRange"||li(e)}return!1}function Wn(i,e){if(i==="origin.ip")return{unifiedField:"ip"};let t=Jr[i];if(t)return{unifiedField:t};if(i==="request.headers"&&e!==void 0){let n=Qr[e];return n?{unifiedField:n}:{unifiedField:"header",unifiedKey:e}}return null}function ai(i,e){let t=i.type==="not",n=i.type==="not"?i.child:i;if(li(n)){let o=n.children.map(s=>os(s));return{field:"ip",operator:t?"not_in":"in",value:o,...e!==void 0?{group:e}:{}}}if(n.type==="inIpRange"||n.type==="comparison"&&n.field==="origin.ip"){let o=n.type==="inIpRange"?n.value:String(n.value);return{field:"ip",operator:t?"ne":"eq",value:o,...e!==void 0?{group:e}:{}}}if(n.type==="and"&&qn(n)){if(t)return null;let{guard:o,comparisonNegated:s,comparison:a}=rs(n);return a.type!=="comparison"&&a.type!=="methodCall"&&a.type!=="membership"?null:Vn(a,s,o.field,o.key,e)}if(n.type==="comparison"||n.type==="methodCall"||n.type==="membership")return Vn(n,t,n.field,n.key,e);if(n.type==="has"){let o=Wn(n.field,n.key);return o?{field:o.unifiedField,operator:t?"not_exists":"exists",value:"",...o.unifiedKey!==void 0?{key:o.unifiedKey}:{},...e!==void 0?{group:e}:{}}:null}return null}function Vn(i,e,t,n,o){let s=Wn(t,n);if(!s)return null;let a,l,c=!1;if(i.type==="comparison")e?i.operator==="eq"?a="ne":i.operator==="ne"?a="eq":(a=i.operator,c=!0):a=i.operator,l=i.value;else if(i.type==="methodCall"){let d=Bn[i.method];if(!d)return null;e?d==="contains"?a="not_contains":(a=d,c=!0):a=d,l=i.value}else a=e?"not_in":"in",l=i.values;return{field:s.unifiedField,operator:a,value:l,...c?{negated:!0}:{},...s.unifiedKey!==void 0?{key:s.unifiedKey}:{},...o!==void 0?{group:o}:{}}}function ss(i){if(si(i)){let e=ai(i,void 0);return e?{conditions:[e],conditionLogic:"AND"}:null}if(i.type==="and"){let e=[];for(let t of i.children){if(!si(t))return null;let n=ai(t,void 0);if(!n)return null;e.push(n)}return{conditions:e,conditionLogic:"AND"}}return i.type==="or"?Lt(i.children,si,e=>e.type==="and",ai):null}function Me(i){if(!i||!i.trim())return null;try{let e=is(i);return ss(e)}catch{return null}}function ci(i){if(!i.match.expr)throw new Error(`Cloud Armor rule at priority ${i.priority} has no CEL match expression (uses the basic versionedExpr/config shape) \u2014 caller must filter these out before translating.`);return i.match.expr.expression}function jn(i){if(i.priority===void 0)throw new Error(`Rule "${i.name}" has no priority assigned \u2014 CloudArmorFirewallService must assign one before calling unifiedToGcp`);let e=[],t=ft.fromUnifiedConditions(i.conditions,i.conditionLogic),{action:n,actionWarning:o}=ls(i.action.type,i.name,i.id);o&&e.push(o);let s={priority:i.priority,description:(i.description||i.name).slice(0,64),match:{expr:{expression:t}},action:n,preview:i.action.type==="log"?!0:void 0};if((n==="throttle"||n==="rate_based_ban")&&i.action.rateLimit){let a=cs(i.action.rateLimit.window);s.rateLimitOptions={rateLimitThreshold:{count:i.action.rateLimit.requests,intervalSec:a},conformAction:"allow",exceedAction:"deny(429)",enforceOnKey:"IP"}}else n==="throttle"&&!i.action.rateLimit&&e.push(U.createWarning("lossy_conversion",i.id,"action.rateLimit",`Rule "${i.name}" maps to Cloud Armor's "throttle" action but declares no rateLimit block \u2014 Cloud Armor requires rateLimitOptions for this action; the rule will likely be rejected by the API.`));return n==="redirect"&&(i.action.redirect?s.redirectOptions={type:"EXTERNAL_302",target:i.action.redirect.location}:e.push(U.createWarning("lossy_conversion",i.id,"action.redirect",`Rule "${i.name}" maps to Cloud Armor's "redirect" action but declares no redirect target \u2014 Cloud Armor requires redirectOptions for this action; the rule will likely be rejected by the API.`))),{result:s,warnings:e}}function zn(i,e){let t=[],n=e!==void 0?e:Me(ci(i)),o=[],s;n?(o=n.conditions,s=n.conditionLogic):t.push(U.createWarning("complex_expressions",String(i.priority),"match.expr.expression","CEL expression could not be parsed back into structured conditions \u2014 it may be hand-authored or use syntax outside what doorman itself generates.","Review the translated rule and add missing conditions manually if needed."));let a={type:as(i.action),...i.rateLimitOptions?{rateLimit:{requests:i.rateLimitOptions.rateLimitThreshold.count,window:`${i.rateLimitOptions.rateLimitThreshold.intervalSec}s`}}:{},...i.redirectOptions?{redirect:{location:i.redirectOptions.target}}:{}};return{result:{id:String(i.priority),name:i.description||`Rule ${i.priority}`,...i.description?{description:i.description}:{},enabled:!i.preview,conditions:o,...s==="OR"?{conditionLogic:s}:{},action:a,priority:i.priority},warnings:t}}function Gn(i,e){if(i.action!=="allow"&&i.action!=="deny(403)"||i.preview)return!1;let t=e!==void 0?e:Me(ci(i));if(!t||t.conditions.length!==1)return!1;let n=t.conditions[0];return n.field==="ip"&&(n.operator==="eq"||n.operator==="in")&&!n.key}function Kn(i,e){let o=(e!==void 0?e:Me(ci(i))).conditions[0].value;return{id:String(i.priority),ip:Array.isArray(o)?String(o[0]):String(o),...i.description?{notes:i.description}:{},action:i.action==="allow"?"allow":"deny"}}function Hn(i,e){if(!Pe.safeParse(i.ip).success)throw new Error(`Invalid IP address or CIDR range: ${i.ip}`);let t=ft.fromUnifiedCondition({field:"ip",operator:"eq",value:i.ip});return{priority:e,description:(i.notes||`IP ${i.action}: ${i.ip}${i.hostname?` (${i.hostname})`:""}`).slice(0,64),match:{expr:{expression:t}},action:i.action==="allow"?"allow":"deny(403)"}}function as(i){return{allow:"allow","deny(403)":"deny","deny(404)":"deny","deny(502)":"deny",rate_based_ban:"rate_limit",throttle:"rate_limit",redirect:"redirect"}[i]||"deny"}function ls(i,e,t){switch(i){case"log":return{action:"allow"};case"deny":case"block":return{action:"deny(403)"};case"challenge":return{action:"deny(403)",actionWarning:U.createUnsupportedFeatureWarning("challenge action","unified config","Cloud Armor",t,"action")};case"bypass":return{action:"allow"};case"rate_limit":return{action:"throttle"};case"redirect":return{action:"redirect"};case"allow":return{action:"allow"};default:return{action:"deny(403)",actionWarning:U.createWarning("lossy_conversion",t,"action",`Rule "${e}" has unrecognized action "${i}" \u2014 defaulted to Cloud Armor's "deny(403)".`)}}}function cs(i){let e=i.match(/^(\d+)([smhd])$/);if(!e||!e[1]||!e[2])throw new Error(`Invalid window format: ${i}`);let t=parseInt(e[1],10),o={s:1,m:60,h:3600,d:86400}[e[2]];if(o===void 0)throw new Error(`Invalid time unit: ${e[2]}`);return t*o}var T=class{static vercelToUnified=pn;static unifiedToVercel=fn;static vercelIPToUnified=gn;static cloudflareToUnified=In;static unifiedToCloudflare=En;static unifiedIPToCloudflare=Tn;static unifiedManagedRuleGroupToCloudflare=An;static cloudflareToUnifiedManagedRuleGroup=$n;static fastlyToUnified=kn;static unifiedToFastly=Sn;static fastlyListEntriesToUnified=Un;static unifiedIPsToFastlyEntries=Ln;static gcpToUnified=zn;static unifiedToGcp=jn;static gcpToUnifiedIP=Kn;static unifiedIPToGcp=Hn;static gcpLooksLikeIpRule=Gn};pe();function Oe(i,e){if(i===e)return!0;if(typeof i!="object"||i===null||typeof e!="object"||e===null||Array.isArray(i)!==Array.isArray(e))return!1;let t=Object.keys(i),n=Object.keys(e);if(t.length!==n.length)return!1;for(let o of t)if(!n.includes(o)||!Oe(i[o],e[o]))return!1;return!0}function Ce(i){let{id:e,...t}=i;return t}function gt(i){return[...i].sort((e,t)=>{let n=e.priority!==void 0,o=t.priority!==void 0;return!n&&!o?0:n?o?e.priority-t.priority:-1:1})}function Dt(i){return i.some(e=>e.priority!==void 0)}var et=class extends ke{constructor(t){super();this.client=t}client;name="vercel";async fetchConfig(t){try{r.debug("Fetching Vercel firewall configuration");let n=await this.client.fetchFirewallConfig(t),o=n.rules.map(a=>{let l=T.vercelToUnified(a);return l.warnings.length>0&&l.warnings.forEach(c=>{let{TranslationWarningSystem:d}=(pe(),Re(Se)),u=d.formatWarning(c);r.warn(`Rule ${a.name}:
25
+ ${u}`)}),l.result}),s=n.ips.map(a=>T.vercelIPToUnified(a));return{version:"2.0",provider:"vercel",providers:{vercel:{projectId:this.client.projectId,...this.client.teamId?{teamId:this.client.teamId}:{}}},rules:o,ips:s,metadata:{version:n.version,updatedAt:n.updatedAt}}}catch(n){throw r.error("Error fetching Vercel configuration:",n),new Error("Failed to fetch Vercel firewall configuration")}}async syncRules(t,n={}){let{dryRun:o=!1}=n;try{let{OperationSafety:s}=(ht(),Re(mt)),a=await s.performDryRunValidation(t,"sync rules",async y=>await this.getChanges(y,{allowCreate:!o,assumeYes:n.assumeYes}));if(!a.valid)throw new Error(`Dry-run validation failed: ${a.issues.join(", ")}`);let l=a.changes,{rulesToAdd:c,rulesToUpdate:d,rulesToDelete:u,version:p}=l,f=l.ipsToAdd||[],g=l.ipsToUpdate||[],m=l.ipsToDelete||[];if(o)return r.info("Dry run mode. The following changes would be made:"),r.info(`Custom Rules - Add: ${c.length}, Update: ${d.length}, Delete: ${u.length}`),r.info(`IP Rules - Add: ${f.length}, Update: ${g.length}, Delete: ${m.length}`),{success:!0,rulesAdded:0,rulesUpdated:0,rulesDeleted:0,ipsAdded:0,ipsUpdated:0,ipsDeleted:0,version:p,warnings:a.warnings};let v=s.assessOperationRisk(l,t);if(!await s.confirmDestructiveOperation({operation:"sync rules",target:`Vercel project ${this.client.projectId}`,changes:l,riskLevel:v,skipConfirmation:n.force||!1,dryRun:!1,allowDeletions:n.allowDeletions||!1}))throw new Error("Operation cancelled by user");let x=c.map(y=>T.unifiedToVercel(y).result),S=d.map(y=>T.unifiedToVercel(y).result),q=u.map(y=>T.unifiedToVercel(y).result),W=f.map(y=>({id:y.id||"",ip:y.ip,hostname:y.hostname||"",action:y.action,notes:y.notes})),Q=g.map(y=>({id:y.id||"",ip:y.ip,hostname:y.hostname||"",action:y.action,notes:y.notes})),ue=m.map(y=>({id:y.id||"",ip:y.ip,hostname:y.hostname||"",action:y.action,notes:y.notes})),K=[],ge=[],k=[],Ae=[],Ee=[],A=[],h=[],b=(y,P)=>`${y}: ${P instanceof Error?P.message:String(P)}`,ee=[];Dt(t.rules)&&t.rules.length>c.length&&ee.push("Rule `priority` is best-effort on Vercel: new rules are created in priority order, but rules that already exist remotely cannot be repositioned via the Vercel API. Recreate them (remove and re-add) if exact evaluation order matters.");let me=[];for(let y of q)try{r.debug(`Deleting custom rule: ${y.id}`),await this.client.deleteFirewallRule(y),k.push(y),r.debug(`Custom rule deleted: ${y.id}`)}catch(P){r.error(`Failed to delete custom rule ${y.id}:`,P),h.push(b(`Failed to delete custom rule ${y.id}`,P))}for(let y of ue)try{r.debug(`Deleting IP blocking rule: ${y.id}`),await this.client.deleteIPBlockingRule(y),A.push(y),r.debug(`IP blocking rule deleted: ${y.id}`)}catch(P){r.error(`Failed to delete IP blocking rule ${y.id}:`,P),h.push(b(`Failed to delete IP blocking rule ${y.id}`,P))}for(let y of x)try{r.debug(`Adding new custom rule: ${y.name}`);let P=await this.client.createFirewallRule(y);K.push(P),P.id&&P.id!==y.id&&me.push({oldId:y.id||void 0,newId:P.id,name:y.name}),r.debug(`New custom rule added: ${P.id}`)}catch(P){r.error(`Failed to add custom rule "${y.name}":`,P),h.push(b(`Failed to add custom rule "${y.name}"`,P))}for(let y of W)try{r.debug(`Adding new IP blocking rule: ${y.ip}`);let P=await this.client.createIPBlockingRule(y);Ae.push(P),r.debug(`New IP blocking rule added: (hostname): ${P.hostname} (ip): ${P.ip}`)}catch(P){r.error(`Failed to add IP blocking rule ${y.ip}:`,P),h.push(b(`Failed to add IP blocking rule ${y.ip}`,P))}for(let y of S)try{r.debug(`Updating custom rule: ${y.id}`);let P=await this.client.updateFirewallRule(y);ge.push(P),r.debug(`Custom rule updated: ${P.id}`)}catch(P){r.error(`Failed to update custom rule ${y.id}:`,P),h.push(b(`Failed to update custom rule ${y.id}`,P))}for(let y of Q)try{r.debug(`Updating IP blocking rule: ${y.id}`);let P=await this.client.updateIPBlockingRule(y);Ee.push(P),r.debug(`IP blocking rule updated: ${P.id}`)}catch(P){r.error(`Failed to update IP blocking rule ${y.id}:`,P),h.push(b(`Failed to update IP blocking rule ${y.id}`,P))}r.debug(`${fe.default.underline("Custom Rules:")} ${fe.default.green("Added:")} ${fe.default.green(K.length)}, ${fe.default.cyan("Updated:")} ${fe.default.cyan(ge.length)}, ${fe.default.red("Deleted:")} ${fe.default.red(k.length)}`),r.debug(`${fe.default.underline("IP Rules:")} ${fe.default.green("Added:")} ${fe.default.green(Ae.length)}, ${fe.default.cyan("Updated:")} ${fe.default.cyan(Ee.length)}, ${fe.default.red("Deleted:")} ${fe.default.red(A.length)}`);let lt=p,ct,Ne=[...ee];try{let y=await this.client.fetchFirewallConfig();lt=y.version,ct=y.updatedAt;let P=new Set(y.rules.map(M=>M.id)),ki=new Set(y.ips.map(M=>M.id));for(let M of[...K,...ge])M.id&&!P.has(M.id)&&Ne.push(`Rule "${M.name}" (${M.id}) was synced but is missing from the post-sync remote config \u2014 a subsequent sync may re-create or misdiff it.`);for(let M of k)M.id&&P.has(M.id)&&Ne.push(`Rule "${M.name}" (${M.id}) was deleted but still appears in the post-sync remote config.`);for(let M of[...Ae,...Ee])M.id&&!ki.has(M.id)&&Ne.push(`IP rule "${M.ip}" (${M.id}) was synced but is missing from the post-sync remote config \u2014 a subsequent sync may re-create or misdiff it.`);for(let M of A)M.id&&ki.has(M.id)&&Ne.push(`IP rule "${M.ip}" (${M.id}) was deleted but still appears in the post-sync remote config.`)}catch(y){r.error("Failed to fetch updated firewall configuration version after sync:",y),h.push(b("Failed to fetch updated configuration version after sync",y))}return{success:h.length===0,rulesAdded:K.length,rulesUpdated:ge.length,rulesDeleted:k.length,ipsAdded:Ae.length,ipsUpdated:Ee.length,ipsDeleted:A.length,version:lt,updatedAt:ct,idRemappings:me.length>0?me:void 0,errors:h.length>0?h:void 0,warnings:Ne.length>0?Ne:void 0}}catch(s){throw r.error("Error during sync:",s),new Error(`Failed to synchronize firewall rules: ${s instanceof Error?s.message:String(s)}`,{cause:s})}}async getChanges(t,n){let o=Ue.safeParse(t);if(!o.success)throw new Error(`Invalid firewall configuration: ${o.error.message}`);this.assertManagedRulesSupported(t);try{r.debug("Fetching existing firewall configuration");let s=await this.client.fetchFirewallConfig(void 0,{allowCreate:n?.allowCreate??!0,assumeYes:n?.assumeYes});r.debug(`Fetched ${s.rules.length} custom rules and ${s.ips.length} IP blocking rules`);let a=s.rules.map(m=>{let v=T.vercelToUnified(m);return v.warnings.length>0&&v.warnings.forEach(z=>{let{TranslationWarningSystem:x}=(pe(),Re(Se)),S=x.formatWarning(z);r.warn(`Rule ${m.name}:
26
26
  ${S}`)}),v.result}),{toAdd:l,toUpdate:c,toDelete:d}=this.diffRules(gt(o.data.rules),a),u=s.ips.map(m=>T.vercelIPToUnified(m)),{ipsToAdd:p,ipsToUpdate:f,ipsToDelete:g}=this.diffIPRules(o.data.ips||[],u);return{version:s.version,rulesToAdd:l,rulesToUpdate:c,rulesToDelete:d,ipsToAdd:p,ipsToUpdate:f,ipsToDelete:g,hasChanges:l.length>0||c.length>0||d.length>0||p.length>0||f.length>0||g.length>0}}catch(s){throw r.error("Error fetching existing firewall configuration:",s),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 n=super.validateConfig(t),o=[...n.errors],s=[...n.warnings];t.provider&&t.provider!=="vercel"&&o.push({path:"provider",message:"Provider must be 'vercel' for VercelFirewallService",code:"INVALID_PROVIDER"});try{let a=Ve.safeParse(t);a.success||o.push({path:"root",message:`Schema validation failed: ${a.error.message}`,code:"SCHEMA_VALIDATION_FAILED"})}catch(a){r.debug("Schema validation error:",a)}return{valid:o.length===0,errors:o,warnings:s}}getHealthScore(t){let n=super.getHealthScore(t),o=[...n.issues],s=n.score;return t.rules.filter(l=>l.action?.type==="rate_limit").length===0&&(s-=10,o.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)&&o.push({severity:"info",category:"security",message:"No IP blocking rules configured",suggestion:"Consider blocking known malicious IPs"}),{score:Math.max(0,s),grade:n.grade,issues:o,recommendations:n.recommendations}}diffRules(t,n){let o=[],s=[],a=[...n];for(let l of t){let c=n.find(d=>d.id===l.id);if(!c)o.push(l);else{Oe(Ce(l),Ce(c))||s.push({...l,id:c.id});let d=a.findIndex(u=>u.id===c.id);d!==-1&&a.splice(d,1)}}return{toAdd:o,toUpdate:s,toDelete:a}}diffIPRules(t,n){let o=[],s=[],a=[...n];for(let l of t){let c=n.find(d=>d.id===l.id);if(!c)r.debug("Rule not found in existing rules:",{configRule:l}),o.push(l);else{Oe(Ce(l),Ce(c))||s.push({...c,...l});let d=a.findIndex(u=>u.id===c.id);d!==-1&&a.splice(d,1)}}return{ipsToAdd:o,ipsToUpdate:s,ipsToDelete:a}}};_();var yt=class{static fromEnv(){let e=process.env.VERCEL_TOKEN,t=process.env.VERCEL_PROJECT_ID,n=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");let o=new Ze(t,n,e);return new et(o)}static fromConfig(e){let t=e.token||process.env.VERCEL_TOKEN,n=e.projectId||process.env.VERCEL_PROJECT_ID,o=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(!n)throw new Error("Vercel project ID is required (provide projectId or set VERCEL_PROJECT_ID env var)");r.debug("Creating Vercel provider with config:",{projectId:n,teamId:o});let s=new Ze(n,o,t);return new et(s)}static create(e,t,n){return this.fromConfig({projectId:e,teamId:t,token:n})}};var Jn=require("crypto");_();var qe=O(require("chalk")),R=class i 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,i)}format(){let e=[];return e.push(qe.default.red.bold(`[${this.code}] ${this.message}`)),e.push(""),this.suggestion&&(e.push(qe.default.yellow.bold("Suggestion:")),e.push(` ${this.suggestion}`),e.push("")),this.details&&Object.keys(this.details).length>0&&(e.push(qe.default.dim("Details:")),Object.entries(this.details).forEach(([t,n])=>{let o=typeof n=="object"?JSON.stringify(n,null,2).split(`
27
27
  `).join(`
28
28
  `):n;e.push(` ${qe.default.cyan(t)}: ${o}`)}),e.push("")),this.cause&&(e.push(qe.default.dim("Caused by:")),e.push(` ${this.cause.message}`),e.push("")),this.docsUrl&&(e.push(qe.default.cyan.bold("Documentation:")),e.push(` ${this.docsUrl}`),e.push("")),e.join(`
@@ -128,9 +128,9 @@ Dry run \u2014 no changes made.`));return}if(!await mo(o.length,i.force||!1,i.al
128
128
  \u{1F4CA} ${i.name} Sync Status Summary
129
129
  `)),o.version!==void 0&&(r.log(`${F.default.dim("Local Version:")} ${F.default.yellow(a??"unknown")}`),r.log(`${F.default.dim("Remote Version:")} ${F.default.yellow(o.version)}`),r.log(`${F.default.dim("Version Status:")} ${l?F.default.red("Out of sync"):F.default.green("In sync")}`),r.log("")),r.log(`${F.default.dim("Rules:")}`),r.log(` ${F.default.green("+")} ${o.rulesToAdd.length} to add`),r.log(` ${F.default.cyan("~")} ${o.rulesToUpdate.length} to update`),r.log(` ${F.default.red("-")} ${o.rulesToDelete.length} to delete`),r.log(`${F.default.dim("IPs:")}`),r.log(` ${F.default.green("+")} ${o.ipsToAdd?.length??0} to add`),r.log(` ${F.default.cyan("~")} ${o.ipsToUpdate?.length??0} to update`),r.log(` ${F.default.red("-")} ${o.ipsToDelete?.length??0} to delete`),r.log(""),c?r.success(F.default.green("\u2705 Everything is in sync!")):r.warn(F.default.yellow("\u26A0\uFE0F Changes detected. Run `sync` to apply changes.")),r.log(`
130
130
  `+F.default.bold("\u{1F3E5} Configuration Health Check")),r.log(`${F.default.dim("Score:")} ${s.score}/100 (${s.grade})`),s.issues.forEach(d=>{let u=d.severity==="error"?F.default.red("\u2717"):d.severity==="warning"?F.default.yellow("\u26A0"):F.default.dim("\u2139");r.log(` ${u} [${d.category}] ${d.message}`),d.suggestion&&r.log(` ${F.default.dim(d.suggestion)}`)}),s.recommendations.length>0&&(r.log(F.default.dim(`
131
- Recommendations:`)),s.recommendations.forEach(d=>r.log(` - ${d}`)))}var Ti={};oe(Ti,{builder:()=>Ea,command:()=>va,desc:()=>Ia,handler:()=>Ta});var J=O(require("chalk"));_();var va="sync",Ia="Sync Vercel Firewall rules with config file",Ea={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:se,...ae,debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1},allowDeletions:{type:"boolean",description:"Required together with --ci to let a non-interactive sync delete existing rules/IPs. Without it, a --ci sync that would delete anything is refused rather than applied silently.",default:!1}},Ta=async i=>{await le({config:i.config,provider:i.provider,...X(i),debug:i.debug,ci:i.ci,errorContext:"syncing firewall rules"},async e=>{await Aa(e.provider,e.config,i)})};async function Aa(i,e,t){let n=be(e);r.start(J.default.magenta(`Calculating ${i.name} firewall configuration changes...`));let o=await i.getChanges(n),s=n.metadata?.version,a=o.version!==void 0&&s!==o.version;if(!o.hasChanges&&!a){r.success(J.default.green("No changes detected. Firewall rules are in sync."));return}r.log(J.default.bold(`
131
+ Recommendations:`)),s.recommendations.forEach(d=>r.log(` - ${d}`)))}var Ti={};oe(Ti,{builder:()=>Ea,command:()=>va,desc:()=>Ia,handler:()=>Ta});var J=O(require("chalk"));_();var va="sync",Ia="Sync Vercel Firewall rules with config file",Ea={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:se,...ae,debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1},allowDeletions:{type:"boolean",description:"Required together with --ci to let a non-interactive sync delete existing rules/IPs. Without it, a --ci sync that would delete anything is refused rather than applied silently.",default:!1},yes:{alias:"y",type:"boolean",description:"Create the first firewall config without asking when the project has none. Does not authorize deletions.",default:!1}},Ta=async i=>{await le({config:i.config,provider:i.provider,...X(i),debug:i.debug,ci:i.ci,errorContext:"syncing firewall rules"},async e=>{await Aa(e.provider,e.config,i)})};async function Aa(i,e,t){let n=be(e);r.start(J.default.magenta(`Calculating ${i.name} firewall configuration changes...`));let o=await i.getChanges(n,{assumeYes:t.yes}),s=n.metadata?.version,a=o.version!==void 0&&s!==o.version;if(!o.hasChanges&&!a){r.success(J.default.green("No changes detected. Firewall rules are in sync."));return}r.log(J.default.bold(`
132
132
  Proposed Changes:
133
- `)),Ii("Rules to add",o.rulesToAdd,J.default.green("+")),Ii("Rules to update",o.rulesToUpdate,J.default.cyan("~")),Ii("Rules to delete",o.rulesToDelete,J.default.red("-"));let l=o.ipsToAdd??[],c=o.ipsToUpdate??[],d=o.ipsToDelete??[];(l.length||c.length||d.length)&&(Ei("IPs to add",l,J.default.green("+")),Ei("IPs to update",c,J.default.cyan("~")),Ei("IPs to delete",d,J.default.red("-"))),a&&r.log(` Remote config version: ${J.default.red(s??"unknown")} ${J.default.dim("->")} ${J.default.green(o.version)}`),r.start("Starting firewall rules sync...");let u=await i.syncRules(n,{force:t.ci,allowDeletions:t.allowDeletions});if(!u.success)throw new Error(`Sync failed: ${u.errors?.join(", ")||"unknown error"}`);r.success(J.default.green("Firewall rules sync completed successfully")),r.log(` Rules: ${u.rulesAdded} added, ${u.rulesUpdated} updated, ${u.rulesDeleted} deleted`),(u.ipsAdded||u.ipsUpdated||u.ipsDeleted)&&r.log(` IPs: ${u.ipsAdded??0} added, ${u.ipsUpdated??0} updated, ${u.ipsDeleted??0} deleted`),u.warnings?.forEach(f=>r.warn(f)),u.idRemappings?.length&&(r.info(J.default.yellow("Rule IDs were reassigned by the provider and written back to the local config:")),u.idRemappings.forEach(f=>{r.log(` - ${f.name}: ${J.default.red(f.oldId||"none")} ${J.default.dim("->")} ${J.default.green(f.newId)}`)}));let p=Vt(e,u);p.changed&&(await Y(p.config,t.config),r.success(J.default.green("Updated local config with new version/metadata")))}function Ii(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(n=>r.log(` - ${n.name}`)))}function Ei(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(n=>r.log(` - ${n.ip}${n.hostname?` (${n.hostname})`:""}`)))}var $i={};oe($i,{builder:()=>_a,command:()=>$a,desc:()=>Pa,handler:()=>Ua});var Fe=O(require("chalk")),Eo=require("consola");_();var wo={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 Co={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 bo={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 vo={metadata:{title:"Deny Common WordPress URLs Firewall Rule",reference:"https://vercel.com/templates/other/block-wordpress-urls-firewall-rule"},config:{rules:[{id:"rule_deny_word_press_ur_ls",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 Ai={"ai-bots":wo,"bad-bots":Co,"block-ofac-sanctioned-countries":bo,wordpress:vo};var Io=i=>Ai[i]?.config;$e();var $a="template [name]",Pa="Add a firewall rule template to your configuration",_a={name:{type:"string",description:"Name of the template to add"},config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},dryRun:{alias:"d",type:"boolean",description:"Preview changes without applying them",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}};function ka(i,e){let t=new Set(i.map(o=>o.name.toLowerCase())),n=e.filter(o=>t.has(o.name.toLowerCase())).map(o=>o.name);return[...new Set(n)]}var Sa=()=>Object.keys(Ai),Ua=async i=>{try{i.debug&&(r.level=Eo.LogLevels.debug),r.debug("Template command arguments:",i);let e=i.name;if(!e){let c=Sa();e=await C("Select a template to add:",{type:"select",options:c,initial:c[0]})}let t=Io(e);t||(r.error(`Template not found: ${e}`),process.exit(1)),r.debug("Template content:",t),r.start("Loading current configuration...");let n=await Z(i.config,"raw"),o=n.rules||[],s=ka(o,t.rules);if(i.dryRun){r.info(Fe.default.cyan(`
133
+ `)),Ii("Rules to add",o.rulesToAdd,J.default.green("+")),Ii("Rules to update",o.rulesToUpdate,J.default.cyan("~")),Ii("Rules to delete",o.rulesToDelete,J.default.red("-"));let l=o.ipsToAdd??[],c=o.ipsToUpdate??[],d=o.ipsToDelete??[];(l.length||c.length||d.length)&&(Ei("IPs to add",l,J.default.green("+")),Ei("IPs to update",c,J.default.cyan("~")),Ei("IPs to delete",d,J.default.red("-"))),a&&r.log(` Remote config version: ${J.default.red(s??"unknown")} ${J.default.dim("->")} ${J.default.green(o.version)}`),r.start("Starting firewall rules sync...");let u=await i.syncRules(n,{force:t.ci,allowDeletions:t.allowDeletions,assumeYes:t.yes});if(!u.success)throw new Error(`Sync failed: ${u.errors?.join(", ")||"unknown error"}`);r.success(J.default.green("Firewall rules sync completed successfully")),r.log(` Rules: ${u.rulesAdded} added, ${u.rulesUpdated} updated, ${u.rulesDeleted} deleted`),(u.ipsAdded||u.ipsUpdated||u.ipsDeleted)&&r.log(` IPs: ${u.ipsAdded??0} added, ${u.ipsUpdated??0} updated, ${u.ipsDeleted??0} deleted`),u.warnings?.forEach(f=>r.warn(f)),u.idRemappings?.length&&(r.info(J.default.yellow("Rule IDs were reassigned by the provider and written back to the local config:")),u.idRemappings.forEach(f=>{r.log(` - ${f.name}: ${J.default.red(f.oldId||"none")} ${J.default.dim("->")} ${J.default.green(f.newId)}`)}));let p=Vt(e,u);p.changed&&(await Y(p.config,t.config),r.success(J.default.green("Updated local config with new version/metadata")))}function Ii(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(n=>r.log(` - ${n.name}`)))}function Ei(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(n=>r.log(` - ${n.ip}${n.hostname?` (${n.hostname})`:""}`)))}var $i={};oe($i,{builder:()=>_a,command:()=>$a,desc:()=>Pa,handler:()=>Ua});var Fe=O(require("chalk")),Eo=require("consola");_();var wo={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 Co={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 bo={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 vo={metadata:{title:"Deny Common WordPress URLs Firewall Rule",reference:"https://vercel.com/templates/other/block-wordpress-urls-firewall-rule"},config:{rules:[{id:"rule_deny_word_press_ur_ls",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 Ai={"ai-bots":wo,"bad-bots":Co,"block-ofac-sanctioned-countries":bo,wordpress:vo};var Io=i=>Ai[i]?.config;$e();var $a="template [name]",Pa="Add a firewall rule template to your configuration",_a={name:{type:"string",description:"Name of the template to add"},config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},dryRun:{alias:"d",type:"boolean",description:"Preview changes without applying them",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}};function ka(i,e){let t=new Set(i.map(o=>o.name.toLowerCase())),n=e.filter(o=>t.has(o.name.toLowerCase())).map(o=>o.name);return[...new Set(n)]}var Sa=()=>Object.keys(Ai),Ua=async i=>{try{i.debug&&(r.level=Eo.LogLevels.debug),r.debug("Template command arguments:",i);let e=i.name;if(!e){let c=Sa();e=await C("Select a template to add:",{type:"select",options:c,initial:c[0]})}let t=Io(e);t||(r.error(`Template not found: ${e}`),process.exit(1)),r.debug("Template content:",t),r.start("Loading current configuration...");let n=await Z(i.config,"raw"),o=n.rules||[],s=ka(o,t.rules);if(i.dryRun){r.info(Fe.default.cyan(`
134
134
  Dry run - The following rules would be added:`)),r.log(JSON.stringify(t.rules,null,2)),s.length>0&&r.warn(Fe.default.yellow(`\u26A0\uFE0F Rule name(s) already exist in the configuration: ${s.join(", ")}`));return}if(s.length>0){if(r.warn(Fe.default.yellow(`\u26A0\uFE0F Rule name(s) already exist in the configuration: ${s.join(", ")}`)),i.ci||!process.stdin.isTTY){r.info(Fe.default.dim("Skipping \u2014 non-interactive and rule name(s) already exist. Template not applied."));return}if(!await C("Proceed anyway?",{type:"confirm",initial:!1})){r.info("Cancelled.");return}}let a={...n,rules:[...o,...t.rules]},l=Ve.safeParse(a);l.success||(r.error(Fe.default.red("The resulting configuration would be invalid:")),l.error.errors.forEach(c=>{let d=c.path.join(".");r.error(Fe.default.red(` - ${d}: ${c.message}`))}),r.info(Fe.default.dim("Template was not applied. Fix the issues above and try again.")),process.exit(1)),r.start("Saving updated configuration..."),await Y(a,i.config,{validate:!1}),r.success(Fe.default.green(`Successfully added template '${e}' to configuration`))}catch(e){re(e,"adding template")}};var Pi={};oe(Pi,{builder:()=>Da,command:()=>La,desc:()=>xa,handler:()=>Oa});var de=O(require("chalk"));_();var La="validate",xa="Validate firewall configuration file",Da={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}},Oa=async i=>{try{let e=await Z(i.config,"raw"),t=De.getInstance(),n=Le(e);i.verbose&&r.start(`Validating configuration file...
135
135
  `);let o=n?Ue.safeParse(e):Ve.safeParse(e);i.verbose&&(r.log(de.default.bold.underline("Zod Schema Validation:")),o.success?r.log(de.default.green("\u2713 Schema validation passed")):(r.error(de.default.red("\u2717 Schema validation failed:")),o.error.errors.forEach(l=>{let c=l.path.join(".");r.error(de.default.red(` - ${c}: ${l.message}`))})));let s=!0,a=[];try{t.validateConfig(e),i.verbose&&(r.log(de.default.bold.underline(`
136
136
  AJV Schema Validation:`)),r.log(de.default.green("\u2713 JSON Schema validation passed")))}catch(l){if(s=!1,l instanceof B)a=l.ajvErrors||[],i.verbose&&(r.log(de.default.bold.underline(`