@stacksjs/ts-cloud 0.2.12 → 0.2.14
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/cli.js +2 -2
- package/dist/index.js +1 -1
- package/package.json +3 -3
package/dist/bin/cli.js
CHANGED
|
@@ -359,7 +359,7 @@ Run \`${this.name??"cli"} --help\` for usage.`;T1.stderr.write(`${W}${Z}${G}
|
|
|
359
359
|
`).length-1;this.output.write(K1.move(-999,$*-1))}render(){let $=v6(this._render(this)??"",y6.stdout.columns,{hard:!0,trim:!1});if($===this._prevFrame)return;if(this.state==="initial")this.output.write(K1.hide);else{let J=BQ(this._prevFrame,$);if(this.restoreCursor(),J&&J?.length===1){let Y=J[0];this.output.write(K1.move(0,Y)),this.output.write(Q9.lines(1));let Q=$.split(`
|
|
360
360
|
`);this.output.write(Q[Y]),this._prevFrame=$,this.output.write(K1.move(0,Q.length-Y-1));return}if(J&&J?.length>1){let Y=J[0];this.output.write(K1.move(0,Y)),this.output.write(Q9.down());let Z=$.split(`
|
|
361
361
|
`).slice(Y);this.output.write(Z.join(`
|
|
362
|
-
`)),this._prevFrame=$;return}this.output.write(Q9.down())}if(this.output.write($),this.state==="initial")this.state="active";this._prevFrame=$}}function qQ($,J){if($===void 0)return 0;if(J.length===0)return 0;let Q=J.findIndex((Z)=>Z.value===$);return Q!==-1?Q:0}function VQ($,J){return(J.label??String(J.value)).toLowerCase().includes($.toLowerCase())}function LQ($,J){if(!J)return;if($)return J;return J[0]}class wQ extends F9{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#$=0;#Y="";#Q;#J;get cursor(){return this.#$}get userInputWithCursor(){if(!this.userInput)return d2.inverse(d2.hidden("_"));if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let $=this.userInput.slice(0,this._cursor),[J,...Y]=this.userInput.slice(this._cursor);return`${$}${d2.inverse(J)}${Y.join("")}`}get options(){if(typeof this.#J==="function")return this.#J();return this.#J}constructor($){super($);this.#J=$.options;let J=this.options;this.filteredOptions=[...J],this.multiple=$.multiple===!0,this.#Q=$.filter??VQ;let Y;if($.initialValue&&Array.isArray($.initialValue))if(this.multiple)Y=$.initialValue;else Y=$.initialValue.slice(0,1);else if(!this.multiple&&this.options.length>0)Y=[this.options[0].value];if(Y)for(let Q of Y){let Z=J.findIndex((W)=>W.value===Q);if(Z!==-1)this.toggleSelected(Q),this.#$=Z}this.focusedValue=this.options[this.#$]?.value,this.on("key",(Q,Z)=>this.#Z(Q,Z)),this.on("userInput",(Q)=>this.#W(Q))}_isActionKey($,J){return $==="\t"||this.multiple&&this.isNavigating&&J.name==="space"&&$!==void 0&&$!==""}#Z($,J){let Y=J.name==="up",Q=J.name==="down",Z=J.name==="return";if(Y||Q){if(this.#$=Math.max(0,Math.min(this.#$+(Y?-1:1),this.filteredOptions.length-1)),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)this.selectedValues=[this.focusedValue];this.isNavigating=!0}else if(Z)this.value=LQ(this.multiple,this.selectedValues);else if(this.multiple)if(this.focusedValue!==void 0&&(J.name==="tab"||this.isNavigating&&J.name==="space"))this.toggleSelected(this.focusedValue);else this.isNavigating=!1;else{if(this.focusedValue)this.selectedValues=[this.focusedValue];this.isNavigating=!1}}deselectAll(){this.selectedValues=[]}toggleSelected($){if(this.filteredOptions.length===0)return;if(this.multiple)if(this.selectedValues.includes($))this.selectedValues=this.selectedValues.filter((J)=>J!==$);else this.selectedValues=[...this.selectedValues,$];else this.selectedValues=[$]}#W($){if($!==this.#Y){this.#Y=$;let J=this.options;if($)this.filteredOptions=J.filter((Y)=>this.#Q($,Y));else this.filteredOptions=[...J];if(this.#$=qQ(this.focusedValue,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)if(this.focusedValue!==void 0)this.toggleSelected(this.focusedValue);else this.deselectAll()}}}class MQ extends F9{options;cursor=0;#$;getGroupItems($){return this.options.filter((J)=>J.group===$)}isGroupSelected($){let J=this.getGroupItems($),Y=this.value;if(Y===void 0)return!1;return J.every((Q)=>Y.includes(Q.value))}toggleValue(){let $=this.options[this.cursor];if(this.value===void 0)this.value=[];if($.group===!0){let J=$.value,Y=this.getGroupItems(J);if(this.isGroupSelected(J))this.value=this.value.filter((Q)=>Y.findIndex((Z)=>Z.value===Q)===-1);else this.value=[...this.value,...Y.map((Q)=>Q.value)];this.value=Array.from(new Set(this.value))}else{let J=this.value.includes($.value);this.value=J?this.value.filter((Y)=>Y!==$.value):[...this.value,$.value]}}constructor($){super($,!1);let{options:J}=$;this.#$=$.selectableGroups!==!1,this.options=Object.entries(J).flatMap(([Y,Q])=>[{value:Y,group:!0,label:Y},...Q.map((Z)=>({...Z,group:Y}))]),this.value=[...$.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:Y})=>Y===$.cursorAt),this.#$?0:1),this.on("cursor",(Y)=>{switch(Y){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let Q=this.options[this.cursor]?.group===!0;if(!this.#$&&Q)this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let Q=this.options[this.cursor]?.group===!0;if(!this.#$&&Q)this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;break}case"space":this.toggleValue();break}})}}var TQ=IY();var K0=($,J)=>TQ?$:J,oU=K0("◆","*"),eU=K0("■","x"),$z=K0("▲","x"),Jz=K0("◇","o"),Yz=K0("┌","T"),DQ=K0("│","|"),Qz=K0("└","—"),Zz=K0("●",">"),Wz=K0("○"," "),Gz=K0("◻","[•]"),Uz=K0("◼","[+]"),zz=K0("◻","[ ]"),Fz=K0("▪","•"),Hz=K0("─","-"),Az=K0("╮","+"),Kz=K0("├","+"),Bz=K0("╯","+"),jz=K0("●","•"),Xz=K0("◆","*"),_z=K0("▲","!"),Oz=K0("■","x");var Ez={light:K0("─","-"),heavy:K0("━","="),block:K0("█","#")};function RQ(){return`${d2.gray(DQ)} `}var qz=RQ();var H9="0.2.
|
|
362
|
+
`)),this._prevFrame=$;return}this.output.write(Q9.down())}if(this.output.write($),this.state==="initial")this.state="active";this._prevFrame=$}}function qQ($,J){if($===void 0)return 0;if(J.length===0)return 0;let Q=J.findIndex((Z)=>Z.value===$);return Q!==-1?Q:0}function VQ($,J){return(J.label??String(J.value)).toLowerCase().includes($.toLowerCase())}function LQ($,J){if(!J)return;if($)return J;return J[0]}class wQ extends F9{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#$=0;#Y="";#Q;#J;get cursor(){return this.#$}get userInputWithCursor(){if(!this.userInput)return d2.inverse(d2.hidden("_"));if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let $=this.userInput.slice(0,this._cursor),[J,...Y]=this.userInput.slice(this._cursor);return`${$}${d2.inverse(J)}${Y.join("")}`}get options(){if(typeof this.#J==="function")return this.#J();return this.#J}constructor($){super($);this.#J=$.options;let J=this.options;this.filteredOptions=[...J],this.multiple=$.multiple===!0,this.#Q=$.filter??VQ;let Y;if($.initialValue&&Array.isArray($.initialValue))if(this.multiple)Y=$.initialValue;else Y=$.initialValue.slice(0,1);else if(!this.multiple&&this.options.length>0)Y=[this.options[0].value];if(Y)for(let Q of Y){let Z=J.findIndex((W)=>W.value===Q);if(Z!==-1)this.toggleSelected(Q),this.#$=Z}this.focusedValue=this.options[this.#$]?.value,this.on("key",(Q,Z)=>this.#Z(Q,Z)),this.on("userInput",(Q)=>this.#W(Q))}_isActionKey($,J){return $==="\t"||this.multiple&&this.isNavigating&&J.name==="space"&&$!==void 0&&$!==""}#Z($,J){let Y=J.name==="up",Q=J.name==="down",Z=J.name==="return";if(Y||Q){if(this.#$=Math.max(0,Math.min(this.#$+(Y?-1:1),this.filteredOptions.length-1)),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)this.selectedValues=[this.focusedValue];this.isNavigating=!0}else if(Z)this.value=LQ(this.multiple,this.selectedValues);else if(this.multiple)if(this.focusedValue!==void 0&&(J.name==="tab"||this.isNavigating&&J.name==="space"))this.toggleSelected(this.focusedValue);else this.isNavigating=!1;else{if(this.focusedValue)this.selectedValues=[this.focusedValue];this.isNavigating=!1}}deselectAll(){this.selectedValues=[]}toggleSelected($){if(this.filteredOptions.length===0)return;if(this.multiple)if(this.selectedValues.includes($))this.selectedValues=this.selectedValues.filter((J)=>J!==$);else this.selectedValues=[...this.selectedValues,$];else this.selectedValues=[$]}#W($){if($!==this.#Y){this.#Y=$;let J=this.options;if($)this.filteredOptions=J.filter((Y)=>this.#Q($,Y));else this.filteredOptions=[...J];if(this.#$=qQ(this.focusedValue,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)if(this.focusedValue!==void 0)this.toggleSelected(this.focusedValue);else this.deselectAll()}}}class MQ extends F9{options;cursor=0;#$;getGroupItems($){return this.options.filter((J)=>J.group===$)}isGroupSelected($){let J=this.getGroupItems($),Y=this.value;if(Y===void 0)return!1;return J.every((Q)=>Y.includes(Q.value))}toggleValue(){let $=this.options[this.cursor];if(this.value===void 0)this.value=[];if($.group===!0){let J=$.value,Y=this.getGroupItems(J);if(this.isGroupSelected(J))this.value=this.value.filter((Q)=>Y.findIndex((Z)=>Z.value===Q)===-1);else this.value=[...this.value,...Y.map((Q)=>Q.value)];this.value=Array.from(new Set(this.value))}else{let J=this.value.includes($.value);this.value=J?this.value.filter((Y)=>Y!==$.value):[...this.value,$.value]}}constructor($){super($,!1);let{options:J}=$;this.#$=$.selectableGroups!==!1,this.options=Object.entries(J).flatMap(([Y,Q])=>[{value:Y,group:!0,label:Y},...Q.map((Z)=>({...Z,group:Y}))]),this.value=[...$.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:Y})=>Y===$.cursorAt),this.#$?0:1),this.on("cursor",(Y)=>{switch(Y){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let Q=this.options[this.cursor]?.group===!0;if(!this.#$&&Q)this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let Q=this.options[this.cursor]?.group===!0;if(!this.#$&&Q)this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;break}case"space":this.toggleValue();break}})}}var TQ=IY();var K0=($,J)=>TQ?$:J,oU=K0("◆","*"),eU=K0("■","x"),$z=K0("▲","x"),Jz=K0("◇","o"),Yz=K0("┌","T"),DQ=K0("│","|"),Qz=K0("└","—"),Zz=K0("●",">"),Wz=K0("○"," "),Gz=K0("◻","[•]"),Uz=K0("◼","[+]"),zz=K0("◻","[ ]"),Fz=K0("▪","•"),Hz=K0("─","-"),Az=K0("╮","+"),Kz=K0("├","+"),Bz=K0("╯","+"),jz=K0("●","•"),Xz=K0("◆","*"),_z=K0("▲","!"),Oz=K0("■","x");var Ez={light:K0("─","-"),heavy:K0("━","="),block:K0("█","#")};function RQ(){return`${d2.gray(DQ)} `}var qz=RQ();var H9="0.2.14";import{existsSync as _9}from"node:fs";import{mkdir as yQ,writeFile as Z7}from"node:fs/promises";var e={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",white:"\x1B[37m",gray:"\x1B[90m"};function m1($,J){return`${e[J]}${$}${e.reset}`}function P($){console.log(`${e.green}✓${e.reset} ${$}`)}function w($){console.error(`${e.red}✗${e.reset} ${$}`)}function y($){console.warn(`${e.yellow}⚠${e.reset} ${$}`)}var W1=y;function F($){console.log(`${e.blue}ℹ${e.reset} ${$}`)}function a($){console.log(`${e.cyan}→${e.reset} ${$}`)}function V($){console.log(`
|
|
363
363
|
${e.bright}${e.cyan}${$}${e.reset}
|
|
364
364
|
`)}var j9=!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.BUILDKITE||process.env.CIRCLECI||process.env.GITLAB_CI);class L{frames=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];interval=null;currentFrame=0;message;constructor($){this.message=$}get text(){return this.message}set text($){if(this.message=$,j9&&this.interval)console.log(` ${$}`)}start(){if(j9){console.log(` ${this.message}`),this.interval=!0;return}this.interval=setInterval(()=>{process.stdout.write(`\r${e.cyan}${this.frames[this.currentFrame]}${e.reset} ${this.message}`),this.currentFrame=(this.currentFrame+1)%this.frames.length},80)}succeed($){this.stop(),P($||this.message)}fail($){this.stop(),w($||this.message)}warn($){this.stop(),W1($||this.message)}stop(){if(this.interval){if(!j9)process.stdout.write("\r");if(typeof this.interval==="object")clearInterval(this.interval);this.interval=null}}}async function F0($,J){let Q=(await import("node:readline")).createInterface({input:process.stdin,output:process.stdout});return new Promise((Z)=>{let W=J?`${e.cyan}?${e.reset} ${$} ${e.gray}(${J})${e.reset}: `:`${e.cyan}?${e.reset} ${$}: `;Q.question(W,(G)=>{Q.close(),Z(G||J||"")})})}async function k($,J=!1){let Y=await F0(`${$} (y/n)`,J?"y":"n");return Y.toLowerCase()==="y"||Y.toLowerCase()==="yes"}async function X9($,J){console.log(`${e.cyan}?${e.reset} ${$}`),J.forEach((Z,W)=>{console.log(` ${e.gray}${W+1}.${e.reset} ${Z}`)});let Y=await F0("Select","1"),Q=Number.parseInt(Y)-1;if(Q>=0&&Q<J.length)return J[Q];return J[0]}function g($,J){let Y=$.map((Z,W)=>{let G=Math.max(...J.map((U)=>(U[W]||"").length));return Math.max(Z.length,G)}),Q=$.map((Z,W)=>Z.padEnd(Y[W])).join(" ");console.log(m1(Q,"bright")),console.log(m1("─".repeat(Q.length),"gray")),J.forEach((Z)=>{let W=Z.map((G,U)=>(G||"").padEnd(Y[U])).join(" ");console.log(W)})}function o1($,J="cyan"){let Y=$.split(`
|
|
365
365
|
`),Q=Math.max(...Y.map((W)=>W.length)),Z="─".repeat(Q+2);console.log(m1(`┌${Z}┐`,J)),Y.forEach((W)=>{console.log(m1(`│ ${W.padEnd(Q)} │`,J))}),console.log(m1(`└${Z}┘`,J))}async function $7(){return!0}async function J7(){try{let{AWSClient:$}=await Promise.resolve().then(() => (G0(),d1));return await new $().request({service:"sts",region:"us-east-1",method:"POST",path:"/",body:new URLSearchParams({Action:"GetCallerIdentity",Version:"2011-06-15"}).toString()}),!0}catch{return!1}}async function Y7(){try{let{AWSClient:$}=await Promise.resolve().then(() => (G0(),d1)),Y=await new $().request({service:"sts",region:"us-east-1",method:"POST",path:"/",body:new URLSearchParams({Action:"GetCallerIdentity",Version:"2011-06-15"}).toString()});return Y.Account||Y.GetCallerIdentityResult?.Account||null}catch{return null}}async function Q7(){try{let{AWSClient:$}=await Promise.resolve().then(() => (G0(),d1)),Y=await new $().request({service:"ec2",region:"us-east-1",method:"POST",path:"/",body:new URLSearchParams({Action:"DescribeRegions",Version:"2016-11-15"}).toString()}),Q=[];if(Y.regionInfo){let Z=Array.isArray(Y.regionInfo)?Y.regionInfo:[Y.regionInfo];Q.push(...Z.map((W)=>W.regionName))}return Q.length>0?Q:e6()}catch{return e6()}}function e6(){return["us-east-1","us-east-2","us-west-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1","ap-northeast-1"]}function O9($){$.command("init","Initialize a new ts-cloud project").option("--mode <mode>","Deployment mode: server, serverless, or hybrid").option("--name <name>","Project name").option("--region <region>","AWS Region").action(async(J)=>{if(V("Initializing ts-cloud Project"),_9("cloud.config.ts")){if(!await k("cloud.config.ts already exists. Overwrite?",!1)){F("Initialization cancelled");return}}let Y=J?.name||await F0("Project name","my-app"),Q=J?.mode||await X9("Select deployment mode",["serverless","server","hybrid"]),Z=J?.region||await X9("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),W=new L("Creating configuration file...");W.start();let G=`import { defineConfig } from '@ts-cloud/core'
|
|
@@ -5376,7 +5376,7 @@ function unmarshallTest(item) {
|
|
|
5376
5376
|
return test;
|
|
5377
5377
|
}
|
|
5378
5378
|
`;static createAbTestsTable($){return{[`${$.slug}AbTestsTable`]:{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${$.slug}-ab-tests`,BillingMode:"PAY_PER_REQUEST",AttributeDefinitions:[{AttributeName:"id",AttributeType:"S"}],KeySchema:[{AttributeName:"id",KeyType:"HASH"}]}}}}static createAbTestManagerLambda($){return{[`${$.slug}AbTestManagerLambda`]:{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${$.slug}-ab-test-manager`,Runtime:"nodejs20.x",Handler:"index.handler",Role:$.roleArn,Timeout:30,MemorySize:256,Code:{ZipFile:I1.AbTestManagerCode},Environment:{Variables:{AB_TESTS_TABLE:$.abTestsTable}}}}}}static selectVariant($,J){let Q=I1.hashString(J+$.id)%100,Z=0;for(let W=0;W<$.variants.length;W++)if(Z+=$.trafficSplit[W],Q<Z)return $.variants[W];return $.variants[$.variants.length-1]}static determineWinner($){if($.variants.length===0)return null;let J=$.winningCriteria,Y=$.variants[0],Q=I1.getMetricValue(Y,J);for(let Z of $.variants.slice(1)){let W=I1.getMetricValue(Z,J);if(W>Q)Q=W,Y=Z}return Y}static calculateSignificance($,J,Y){let Q=$.stats.sent,Z=J.stats.sent;if(Q<30||Z<30)return 0;let W=I1.getMetricValue($,Y)/100,G=I1.getMetricValue(J,Y)/100,U=(W*Q+G*Z)/(Q+Z),z=Math.sqrt(U*(1-U)*(1/Q+1/Z));if(z===0)return 0;let H=Math.abs(W-G)/z;if(H>=2.576)return 99;if(H>=1.96)return 95;if(H>=1.645)return 90;if(H>=1.28)return 80;return Math.round(H*30)}static getMetricValue($,J){switch(J){case"delivery_rate":return $.stats.deliveryRate;case"click_rate":return $.stats.clickRate;case"reply_rate":return $.stats.replyRate;case"conversion_rate":return $.stats.conversionRate;default:return $.stats.deliveryRate}}static hashString($){let J=0;for(let Y=0;Y<$.length;Y++){let Q=$.charCodeAt(Y);J=(J<<5)-J+Q,J=J&J}return Math.abs(J)}}class gJ{queues=new Map;messageGroups=new Map;deduplicationConfigs=new Map;messages=new Map;queueCounter=0;groupCounter=0;deduplicationCounter=0;messageCounter=0;sequenceCounter=0;createFIFOQueue($){let J=`fifo-queue-${Date.now()}-${this.queueCounter++}`,Y=$.name.endsWith(".fifo")?$.name:`${$.name}.fifo`,{name:Q,...Z}=$,W={id:J,name:Y,queueUrl:`https://sqs.us-east-1.amazonaws.com/123456789012/${Y}`,...Z};return this.queues.set(J,W),this.createDeduplicationConfig({queueId:J,contentBasedDeduplication:$.contentBasedDeduplication,deduplicationInterval:300}),W}createHighThroughputFIFO($){return this.createFIFOQueue({name:$.name,contentBasedDeduplication:$.contentBasedDeduplication??!0,deduplicationScope:"messageGroup",fifoThroughputLimit:"perMessageGroupId",messageRetentionPeriod:345600,visibilityTimeout:30,receiveMessageWaitTime:0})}createStandardFIFO($){return this.createFIFOQueue({name:$.name,contentBasedDeduplication:$.contentBasedDeduplication??!1,deduplicationScope:"queue",fifoThroughputLimit:"perQueue",messageRetentionPeriod:345600,visibilityTimeout:30,receiveMessageWaitTime:0})}createDeduplicationConfig($){let Y={id:`dedup-${Date.now()}-${this.deduplicationCounter++}`,deduplicationHashes:new Map,...$};return this.deduplicationConfigs.set($.queueId,Y),Y}sendMessage($){let J=this.queues.get($.queueId);if(!J)throw Error(`Queue not found: ${$.queueId}`);let Y=this.deduplicationConfigs.get($.queueId);if(Y){let G=$.messageDeduplicationId||(J.contentBasedDeduplication?this.generateHash($.messageBody):void 0);if(G&&this.isDuplicate(Y,G))return null;if(G)Y.deduplicationHashes.set(G,new Date)}let Q=`msg-${Date.now()}-${this.messageCounter++}`,Z=this.generateSequenceNumber(),W={id:Q,messageId:Q,messageGroupId:$.messageGroupId,messageDeduplicationId:$.messageDeduplicationId,body:$.messageBody,attributes:$.messageAttributes||{},sentTimestamp:new Date,sequenceNumber:Z};return this.messages.set(Q,W),this.updateMessageGroup($.queueId,$.messageGroupId),W}isDuplicate($,J){let Y=$.deduplicationHashes.get(J);if(!Y)return!1;if((Date.now()-Y.getTime())/1000>$.deduplicationInterval)return $.deduplicationHashes.delete(J),!1;return!0}generateHash($){let J=0;for(let Y=0;Y<$.length;Y++){let Q=$.charCodeAt(Y);J=(J<<5)-J+Q,J=J&J}return Math.abs(J).toString(36)}generateSequenceNumber(){return`${Date.now()}${this.sequenceCounter++}`.padStart(20,"0")}updateMessageGroup($,J){let Y=`${$}-${J}`,Q=this.messageGroups.get(Y);if(!Q)Q={id:`group-${Date.now()}-${this.groupCounter++}`,messageGroupId:J,queueId:$,messagesInFlight:0},this.messageGroups.set(Y,Q);Q.messagesInFlight++,Q.lastMessageTimestamp=new Date}getMessageGroups($){return Array.from(this.messageGroups.values()).filter((J)=>J.queueId===$)}getQueue($){return this.queues.get($)}listQueues(){return Array.from(this.queues.values())}getMessages($,J){let Y=Array.from(this.messages.values());if(J)Y=Y.filter((Q)=>Q.messageGroupId===J);return Y.sort((Q,Z)=>Q.sequenceNumber.localeCompare(Z.sequenceNumber))}generateFIFOQueueCF($){return{Type:"AWS::SQS::Queue",Properties:{QueueName:$.name,FifoQueue:!0,ContentBasedDeduplication:$.contentBasedDeduplication,DeduplicationScope:$.deduplicationScope,FifoThroughputLimit:$.fifoThroughputLimit,MessageRetentionPeriod:$.messageRetentionPeriod,VisibilityTimeout:$.visibilityTimeout,ReceiveMessageWaitTimeSeconds:$.receiveMessageWaitTime,...$.deadLetterTargetArn&&{RedrivePolicy:{deadLetterTargetArn:$.deadLetterTargetArn,maxReceiveCount:$.maxReceiveCount||3}}}}}clear(){this.queues.clear(),this.messageGroups.clear(),this.deduplicationConfigs.clear(),this.messages.clear(),this.queueCounter=0,this.groupCounter=0,this.deduplicationCounter=0,this.messageCounter=0,this.sequenceCounter=0}}var sH=new gJ;class uJ{monitors=new Map;metrics=new Map;alerts=new Map;reprocessJobs=new Map;monitorCounter=0;metricsCounter=0;alertCounter=0;jobCounter=0;createDLQMonitor($){let J=`dlq-monitor-${Date.now()}-${this.monitorCounter++}`,Y={id:J,...$};return this.monitors.set(J,Y),Y}createAutomatedMonitor($){return this.createDLQMonitor({name:$.name,queueUrl:$.queueUrl,sourceQueues:$.sourceQueues,maxReceiveCount:3,alarmThreshold:10,autoReprocess:!0,reprocessStrategy:"scheduled",notificationTopicArn:$.notificationTopicArn})}collectMetrics($){let Y={id:`metrics-${Date.now()}-${this.metricsCounter++}`,queueUrl:$,timestamp:new Date,approximateNumberOfMessages:Math.floor(Math.random()*100),approximateAgeOfOldestMessage:Math.floor(Math.random()*86400),messagesReceived:Math.floor(Math.random()*50),messagesDeleted:Math.floor(Math.random()*30),messagesReprocessed:Math.floor(Math.random()*20)},Q=this.metrics.get($)||[];return Q.push(Y),this.metrics.set($,Q),this.checkForAlerts($,Y),Y}checkForAlerts($,J){let Y=Array.from(this.monitors.values()).find((Z)=>Z.queueUrl===$);if(!Y)return;if(J.approximateNumberOfMessages>=Y.alarmThreshold)this.createAlert({monitorId:Y.id,alertType:"threshold_exceeded",severity:"high",message:`DLQ ${Y.name} has ${J.approximateNumberOfMessages} messages (threshold: ${Y.alarmThreshold})`});let Q=3600;if(J.approximateAgeOfOldestMessage>Q)this.createAlert({monitorId:Y.id,alertType:"old_message",severity:"medium",message:`DLQ ${Y.name} has messages older than ${Q} seconds`})}createAlert($){let J=`alert-${Date.now()}-${this.alertCounter++}`,Y={id:J,timestamp:new Date,acknowledged:!1,...$};return this.alerts.set(J,Y),Y}acknowledgeAlert($){let J=this.alerts.get($);if(!J)throw Error(`Alert not found: ${$}`);return J.acknowledged=!0,J}createReprocessJob($){let J=`reprocess-${Date.now()}-${this.jobCounter++}`,Y={id:J,queueUrl:$.queueUrl,messageId:$.messageId,attempts:0,status:"pending"};return this.reprocessJobs.set(J,Y),Y}async executeReprocessJob($){let J=this.reprocessJobs.get($);if(!J)throw Error(`Reprocess job not found: ${$}`);J.status="processing",J.startedAt=new Date,J.attempts++,await new Promise((Q)=>setTimeout(Q,100));let Y=Math.random()>0.3;if(J.status=Y?"success":"failed",J.completedAt=new Date,!Y)J.error="Reprocessing failed - original error still present";return J}async batchReprocess($){let J=[];for(let Y=0;Y<$.maxMessages;Y++){let Q=this.createReprocessJob({queueUrl:$.queueUrl,messageId:`msg-${Y}`});await this.executeReprocessJob(Q.id),J.push(Q)}return J}getDLQStatistics($,J=24){let Y=new Date(Date.now()-J*60*60*1000),Q=(this.metrics.get($)||[]).filter((A)=>A.timestamp>=Y);if(Q.length===0)return{totalMessages:0,avgAge:0,messagesReceived:0,messagesDeleted:0,messagesReprocessed:0,successRate:0};let Z=Q[Q.length-1],W=Q.reduce((A,K)=>A+K.messagesReceived,0),G=Q.reduce((A,K)=>A+K.messagesDeleted,0),U=Q.reduce((A,K)=>A+K.messagesReprocessed,0),z=Q.reduce((A,K)=>A+K.approximateAgeOfOldestMessage,0)/Q.length,H=U>0?G/U*100:0;return{totalMessages:Z.approximateNumberOfMessages,avgAge:z,messagesReceived:W,messagesDeleted:G,messagesReprocessed:U,successRate:H}}getMonitor($){return this.monitors.get($)}listMonitors(){return Array.from(this.monitors.values())}getAlerts($,J){let Y=Array.from(this.alerts.values());if($)Y=Y.filter((Q)=>Q.monitorId===$);if(J!==void 0)Y=Y.filter((Q)=>Q.acknowledged===J);return Y.sort((Q,Z)=>Z.timestamp.getTime()-Q.timestamp.getTime())}getReprocessJobs($){let J=Array.from(this.reprocessJobs.values());if($)J=J.filter((Y)=>Y.queueUrl===$);return J}generateDLQAlarmCF($){return{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$.name}-messages-alarm`,AlarmDescription:`Alert when DLQ ${$.name} exceeds threshold`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:1,Threshold:$.alarmThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:$.queueUrl.split("/").pop()}],...$.notificationTopicArn&&{AlarmActions:[$.notificationTopicArn]}}}}clear(){this.monitors.clear(),this.metrics.clear(),this.alerts.clear(),this.reprocessJobs.clear(),this.monitorCounter=0,this.metricsCounter=0,this.alertCounter=0,this.jobCounter=0}}var oH=new uJ;class dJ{configs=new Map;jobs=new Map;metrics=new Map;configCounter=0;jobCounter=0;metricsCounter=0;createBatchConfig($){let J=`batch-config-${Date.now()}-${this.configCounter++}`,Y={id:J,...$};return this.configs.set(J,Y),Y}createHighThroughputConfig($){return this.createBatchConfig({queueUrl:$.queueUrl,batchSize:10,maxWaitTime:100,parallelProcessors:10,retryAttempts:3,visibilityTimeout:30})}createLowLatencyConfig($){return this.createBatchConfig({queueUrl:$.queueUrl,batchSize:1,maxWaitTime:0,parallelProcessors:5,retryAttempts:2,visibilityTimeout:10})}createBatchJob($){let J=`batch-job-${Date.now()}-${this.jobCounter++}`;if(!this.configs.get($.configId))throw Error(`Batch config not found: ${$.configId}`);let Q=[];for(let W=0;W<$.messageCount;W++)Q.push({id:`msg-${J}-${W}`,messageId:`${J}-${W}`,body:`Message ${W}`,receiptHandle:`receipt-${J}-${W}`,attributes:{},status:"pending"});let Z={id:J,configId:$.configId,messages:Q,status:"pending",processedCount:0,failedCount:0};return this.jobs.set(J,Z),Z}async processBatchJob($){let J=this.jobs.get($);if(!J)throw Error(`Batch job not found: ${$}`);let Y=this.configs.get(J.configId);if(!Y)throw Error(`Batch config not found: ${J.configId}`);J.status="processing",J.startedAt=new Date;let Q=this.chunkArray(J.messages,Y.batchSize);for(let Z of Q)await this.processBatch(Z,Y);return J.processedCount=J.messages.filter((Z)=>Z.status==="success").length,J.failedCount=J.messages.filter((Z)=>Z.status==="failed").length,J.status=J.failedCount===0?"completed":"failed",J.completedAt=new Date,this.collectProcessorMetrics(Y.id,J),J}async processBatch($,J){let Y=$.map((Q)=>this.processMessage(Q,J));await Promise.all(Y)}async processMessage($,J){$.status="processing";let Y=Date.now();await new Promise((W)=>setTimeout(W,Math.random()*100));let Q=Date.now()-Y;if($.processingTime=Q,Math.random()>0.1)$.status="success";else $.status="failed",$.error="Processing error"}chunkArray($,J){let Y=[];for(let Q=0;Q<$.length;Q+=J)Y.push($.slice(Q,Q+J));return Y}collectProcessorMetrics($,J){let Y=`metrics-${Date.now()}-${this.metricsCounter++}`,Z=J.messages.filter((K)=>K.status==="success").map((K)=>K.processingTime||0).filter((K)=>K>0),W=Z.length>0?Z.reduce((K,B)=>K+B,0)/Z.length:0,G=J.completedAt&&J.startedAt?(J.completedAt.getTime()-J.startedAt.getTime())/1000:1,U=J.processedCount/G,z=J.messages.length>0?J.failedCount/J.messages.length*100:0,H={id:Y,configId:$,timestamp:new Date,messagesProcessed:J.processedCount,averageProcessingTime:W,throughput:U,errorRate:z},A=this.metrics.get($)||[];A.push(H),this.metrics.set($,A)}getBatchStatistics($){let J=this.metrics.get($)||[];if(J.length===0)return{totalJobsProcessed:0,totalMessagesProcessed:0,averageThroughput:0,averageErrorRate:0,averageProcessingTime:0};let Y=J.reduce((G,U)=>G+U.messagesProcessed,0),Q=J.reduce((G,U)=>G+U.throughput,0)/J.length,Z=J.reduce((G,U)=>G+U.errorRate,0)/J.length,W=J.reduce((G,U)=>G+U.averageProcessingTime,0)/J.length;return{totalJobsProcessed:J.length,totalMessagesProcessed:Y,averageThroughput:Q,averageErrorRate:Z,averageProcessingTime:W}}optimizeBatchConfig($){let J=this.configs.get($);if(!J)throw Error(`Batch config not found: ${$}`);let Y=this.getBatchStatistics($);if(Y.averageErrorRate<5&&J.batchSize<10)J.batchSize=Math.min(10,J.batchSize+1);if(Y.averageErrorRate>20&&J.batchSize>1)J.batchSize=Math.max(1,J.batchSize-1);if(Y.averageThroughput<5&&J.parallelProcessors<20)J.parallelProcessors++;return J}getConfig($){return this.configs.get($)}listConfigs(){return Array.from(this.configs.values())}getJob($){return this.jobs.get($)}listJobs($){let J=Array.from(this.jobs.values());if($)J=J.filter((Y)=>Y.configId===$);return J}generateBatchProcessorCF($){return{Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:`arn:aws:sqs:us-east-1:123456789012:${$.queueUrl.split("/").pop()}`,FunctionName:"batch-processor-function",BatchSize:$.batchSize,MaximumBatchingWindowInSeconds:$.maxWaitTime/1000,FunctionResponseTypes:["ReportBatchItemFailures"]}}}clear(){this.configs.clear(),this.jobs.clear(),this.metrics.clear(),this.configCounter=0,this.jobCounter=0,this.metricsCounter=0}}var eH=new dJ;class mJ{queues=new Map;retentionPolicies=new Map;delayQueues=new Map;purgeOperations=new Map;metrics=new Map;queueCounter=0;retentionCounter=0;delayCounter=0;purgeCounter=0;metricsCounter=0;createQueue($){let J=`queue-${Date.now()}-${this.queueCounter++}`,Y={id:J,purgeInProgress:!1,...$};return this.queues.set(J,Y),Y}createStandardQueue($){return this.createQueue({queueUrl:`https://sqs.us-east-1.amazonaws.com/123456789012/${$.queueName}`,queueName:$.queueName,messageRetentionPeriod:($.messageRetentionDays||4)*24*60*60,delaySeconds:0,maximumMessageSize:262144,receiveMessageWaitTime:0})}createLongPollingQueue($){return this.createQueue({queueUrl:`https://sqs.us-east-1.amazonaws.com/123456789012/${$.queueName}`,queueName:$.queueName,messageRetentionPeriod:345600,delaySeconds:0,maximumMessageSize:262144,receiveMessageWaitTime:$.waitTimeSeconds||20})}createRetentionPolicy($){let J=`retention-${Date.now()}-${this.retentionCounter++}`,Y={id:J,...$};this.retentionPolicies.set(J,Y);let Q=this.queues.get($.queueId);if(Q)Q.messageRetentionPeriod=$.retentionPeriod;return Y}createShortRetentionPolicy($){return this.createRetentionPolicy({queueId:$.queueId,retentionPeriod:$.retentionHours*60*60,autoCleanup:!0,archiveExpiredMessages:!1})}createArchivalRetentionPolicy($){return this.createRetentionPolicy({queueId:$.queueId,retentionPeriod:$.retentionDays*24*60*60,autoCleanup:!0,cleanupSchedule:"cron(0 0 * * ? *)",archiveExpiredMessages:!0,archiveS3Bucket:$.s3Bucket})}createDelayQueue($){let J=`delay-${Date.now()}-${this.delayCounter++}`,Y={id:J,...$};this.delayQueues.set(J,Y);let Q=Array.from(this.queues.values()).find((Z)=>Z.queueUrl===$.queueUrl);if(Q)Q.delaySeconds=$.defaultDelay;return Y}createScheduledDelayQueue($){return this.createDelayQueue({queueUrl:$.queueUrl,defaultDelay:$.delayMinutes*60,perMessageDelay:!1,maxDelay:900})}async purgeQueue($){let J=this.queues.get($);if(!J)throw Error(`Queue not found: ${$}`);if(J.purgeInProgress)throw Error("Purge already in progress for this queue");let Y=`purge-${Date.now()}-${this.purgeCounter++}`,Q={id:Y,queueUrl:J.queueUrl,status:"in_progress",startedAt:new Date};return this.purgeOperations.set(Y,Q),J.purgeInProgress=!0,setTimeout(()=>{Q.status="completed",Q.completedAt=new Date,Q.messagesPurged=Math.floor(Math.random()*1000),J.purgeInProgress=!1},100),Q}collectQueueMetrics($){let Y={id:`metrics-${Date.now()}-${this.metricsCounter++}`,queueUrl:$,timestamp:new Date,approximateNumberOfMessages:Math.floor(Math.random()*1000),approximateNumberOfMessagesNotVisible:Math.floor(Math.random()*100),approximateNumberOfMessagesDelayed:Math.floor(Math.random()*50),oldestMessageAge:Math.floor(Math.random()*86400)},Q=this.metrics.get($)||[];return Q.push(Y),this.metrics.set($,Q),Y}getQueueHealth($){let J=this.metrics.get($)||[];if(J.length===0)return{status:"healthy",issues:[],recommendations:[]};let Y=J[J.length-1],Q=[],Z=[],W="healthy";if(Y.approximateNumberOfMessages>1e4)W="critical",Q.push("Large message backlog detected"),Z.push("Increase consumer capacity");else if(Y.approximateNumberOfMessages>1000)W="warning",Q.push("Growing message backlog"),Z.push("Monitor consumer performance");if(Y.oldestMessageAge&&Y.oldestMessageAge>3600){if(W!=="critical")W="warning";Q.push("Old messages in queue"),Z.push("Review message processing")}if(Y.approximateNumberOfMessagesDelayed>100){if(W!=="critical")W="warning";Q.push("High number of delayed messages")}return{status:W,issues:Q,recommendations:Z}}getQueue($){return this.queues.get($)}listQueues(){return Array.from(this.queues.values())}getRetentionPolicy($){return this.retentionPolicies.get($)}listRetentionPolicies(){return Array.from(this.retentionPolicies.values())}getPurgeOperations($){let J=Array.from(this.purgeOperations.values());if($)J=J.filter((Y)=>Y.queueUrl===$);return J}generateQueueCF($){return{Type:"AWS::SQS::Queue",Properties:{QueueName:$.queueName,MessageRetentionPeriod:$.messageRetentionPeriod,DelaySeconds:$.delaySeconds,MaximumMessageSize:$.maximumMessageSize,ReceiveMessageWaitTimeSeconds:$.receiveMessageWaitTime}}}generateCleanupRuleCF($){return{Type:"AWS::Events::Rule",Properties:{Name:`${$.id}-cleanup`,Description:"Automated queue cleanup",ScheduleExpression:$.cleanupSchedule||"cron(0 0 * * ? *)",State:"ENABLED",Targets:[{Arn:"arn:aws:lambda:us-east-1:123456789012:function:queue-cleanup",Id:$.id,Input:JSON.stringify({queueId:$.queueId,archiveBucket:$.archiveS3Bucket})}]}}}clear(){this.queues.clear(),this.retentionPolicies.clear(),this.delayQueues.clear(),this.purgeOperations.clear(),this.metrics.clear(),this.queueCounter=0,this.retentionCounter=0,this.delayCounter=0,this.purgeCounter=0,this.metricsCounter=0}}var $A=new mJ;class cJ{optimizations=new Map;imageConfigs=new Map;ssgConfigs=new Map;prerenderConfigs=new Map;counter=0;createAssetOptimization($){let J=`asset-opt-${Date.now()}-${this.counter++}`,Y={id:J,...$};return this.optimizations.set(J,Y),Y}createImageOptimization($){let J=`image-opt-${Date.now()}-${this.counter++}`,Y={id:J,...$};return this.imageConfigs.set(J,Y),Y}createSSGConfig($){let J=`ssg-${Date.now()}-${this.counter++}`,Y={id:J,...$};return this.ssgConfigs.set(J,Y),Y}createPrerenderConfig($){let J=`prerender-${Date.now()}-${this.counter++}`,Y={id:J,...$};return this.prerenderConfigs.set(J,Y),Y}listOptimizations(){return Array.from(this.optimizations.values())}listImageConfigs(){return Array.from(this.imageConfigs.values())}clear(){this.optimizations.clear(),this.imageConfigs.clear(),this.ssgConfigs.clear(),this.prerenderConfigs.clear()}}var JA=new cJ;class lJ{policies=new Map;versioningConfigs=new Map;replicationRules=new Map;tieringConfigs=new Map;objectLocks=new Map;transferAcceleration=new Map;accessPoints=new Map;glacierConfigs=new Map;inventories=new Map;batchOps=new Map;eventNotifications=new Map;counter=0;createLifecyclePolicy($,J){let Y=`lifecycle-${Date.now()}-${this.counter++}`,Q={id:Y,transitions:$,expiration:J};return this.policies.set(Y,Q),Q}enableVersioning($=!1){let J=`versioning-${Date.now()}-${this.counter++}`,Y={id:J,enabled:!0,mfaDelete:$};return this.versioningConfigs.set(J,Y),Y}createReplicationRule($,J,Y){let Q=`replication-${Date.now()}-${this.counter++}`,Z={id:Q,sourceRegion:$,destRegion:J,destBucket:Y};return this.replicationRules.set(Q,Z),Z}createIntelligentTiering($,J){let Y=`tiering-${Date.now()}-${this.counter++}`,Q={id:Y,archiveDays:$,deepArchiveDays:J};return this.tieringConfigs.set(Y,Q),Q}enableObjectLock($){let J=`object-lock-${Date.now()}-${this.counter++}`,Y={id:J,bucketName:$.bucketName,mode:$.mode||"COMPLIANCE",retentionDays:$.retentionDays,retentionYears:$.retentionYears,legalHoldEnabled:$.legalHoldEnabled||!1};return this.objectLocks.set(J,Y),Y}enableTransferAcceleration($){let J=`transfer-accel-${Date.now()}-${this.counter++}`,Y={id:J,bucketName:$,enabled:!0,endpoint:`${$}.s3-accelerate.amazonaws.com`};return this.transferAcceleration.set(J,Y),Y}createAccessPoint($){let J=`access-point-${Date.now()}-${this.counter++}`,Y={id:J,name:$.name,bucketName:$.bucketName,vpcId:$.vpcId,publicAccessBlock:$.publicAccessBlock!==!1,policy:$.policy};return this.accessPoints.set(J,Y),Y}createGlacierArchive($){let J=`glacier-${Date.now()}-${this.counter++}`,Y={id:J,bucketName:$.bucketName,archiveType:$.archiveType,transitionDays:$.transitionDays,restoreConfig:$.restoreTier?{tier:$.restoreTier,days:$.restoreDays||7}:void 0};return this.glacierConfigs.set(J,Y),Y}createInventory($){let J=`inventory-${Date.now()}-${this.counter++}`,Y={id:J,sourceBucket:$.sourceBucket,destinationBucket:$.destinationBucket,schedule:$.schedule||"Daily",format:$.format||"CSV",includedFields:$.includedFields||["Size","LastModifiedDate","StorageClass","ETag"],prefix:$.prefix};return this.inventories.set(J,Y),Y}createBatchOperation($){let J=`batch-op-${Date.now()}-${this.counter++}`,Y={id:J,operation:$.operation,manifestBucket:$.manifestBucket,manifestKey:$.manifestKey,priority:$.priority||10,status:"pending"};return this.batchOps.set(J,Y),Y}executeBatchOperation($){let J=this.batchOps.get($);if(!J)throw Error(`Batch operation not found: ${$}`);return J.status="in_progress",J.totalObjects=1000,J.processedObjects=0,J}getBatchOperationStatus($){return this.batchOps.get($)}createLambdaNotification($){let J=`event-${Date.now()}-${this.counter++}`,Y={id:J,bucketName:$.bucketName,events:$.events,destination:{type:"Lambda",arn:$.lambdaArn},filter:$.prefix||$.suffix?{prefix:$.prefix,suffix:$.suffix}:void 0};return this.eventNotifications.set(J,Y),Y}createSQSNotification($){let J=`event-${Date.now()}-${this.counter++}`,Y={id:J,bucketName:$.bucketName,events:$.events,destination:{type:"SQS",arn:$.queueArn},filter:$.prefix||$.suffix?{prefix:$.prefix,suffix:$.suffix}:void 0};return this.eventNotifications.set(J,Y),Y}createSNSNotification($){let J=`event-${Date.now()}-${this.counter++}`,Y={id:J,bucketName:$.bucketName,events:$.events,destination:{type:"SNS",arn:$.topicArn},filter:$.prefix||$.suffix?{prefix:$.prefix,suffix:$.suffix}:void 0};return this.eventNotifications.set(J,Y),Y}generateObjectLockCF($){let J={ObjectLockEnabled:"Enabled",ObjectLockConfiguration:{ObjectLockEnabled:"Enabled",Rule:{DefaultRetention:{Mode:$.mode}}}};if($.retentionDays)J.ObjectLockConfiguration.Rule.DefaultRetention.Days=$.retentionDays;if($.retentionYears)J.ObjectLockConfiguration.Rule.DefaultRetention.Years=$.retentionYears;return J}generateTransferAccelerationCF($){return{AccelerateConfiguration:{AccelerationStatus:$.enabled?"Enabled":"Suspended"}}}generateAccessPointCF($){return{Type:"AWS::S3::AccessPoint",Properties:{Name:$.name,Bucket:$.bucketName,...$.vpcId&&{VpcConfiguration:{VpcId:$.vpcId}},PublicAccessBlockConfiguration:$.publicAccessBlock?{BlockPublicAcls:!0,BlockPublicPolicy:!0,IgnorePublicAcls:!0,RestrictPublicBuckets:!0}:void 0,...$.policy&&{Policy:$.policy}}}}generateInventoryCF($){return{Type:"AWS::S3::Bucket",Properties:{InventoryConfigurations:[{Id:$.id,Destination:{BucketArn:`arn:aws:s3:::${$.destinationBucket}`,Format:$.format},Enabled:!0,IncludedObjectVersions:"Current",OptionalFields:$.includedFields,ScheduleFrequency:$.schedule,...$.prefix&&{Prefix:$.prefix}}]}}}generateEventNotificationCF($){let J=$.destination.type==="Lambda"?"LambdaConfigurations":$.destination.type==="SQS"?"QueueConfigurations":"TopicConfigurations",Y=$.destination.type==="Lambda"?"Function":$.destination.type==="SQS"?"Queue":"Topic",Q={Event:$.events[0],[Y]:$.destination.arn};if($.filter)Q.Filter={S3Key:{Rules:[...$.filter.prefix?[{Name:"prefix",Value:$.filter.prefix}]:[],...$.filter.suffix?[{Name:"suffix",Value:$.filter.suffix}]:[]]}};return{NotificationConfiguration:{[J]:[Q]}}}clear(){this.policies.clear(),this.versioningConfigs.clear(),this.replicationRules.clear(),this.tieringConfigs.clear(),this.objectLocks.clear(),this.transferAcceleration.clear(),this.accessPoints.clear(),this.glacierConfigs.clear(),this.inventories.clear(),this.batchOps.clear(),this.eventNotifications.clear()}}var YA=new lJ;class pJ{checks=new Map;synthetics=new Map;uptimeTrackers=new Map;counter=0;createHealthCheck($,J=30,Y=10){let Q=`health-${Date.now()}-${this.counter++}`,Z={id:Q,url:$,interval:J,timeout:Y,status:"healthy"};return this.checks.set(Q,Z),Z}createSyntheticMonitor($,J,Y,Q){let Z=`synthetic-${Date.now()}-${this.counter++}`,W={id:Z,name:$,script:J,frequency:Y,locations:Q};return this.synthetics.set(Z,W),W}trackUptime($,J,Y){let Q=`uptime-${Date.now()}-${this.counter++}`,Z=J/(J+Y)*100,W={id:Q,resource:$,uptime:J,downtime:Y,availability:Z};return this.uptimeTrackers.set(Q,W),W}clear(){this.checks.clear(),this.synthetics.clear(),this.uptimeTrackers.clear()}}var QA=new pJ;class rJ{wafRules=new Map;shieldProtections=new Map;securityGroups=new Map;nacls=new Map;counter=0;createWAFRule($,J,Y,Q){let Z=`waf-${Date.now()}-${this.counter++}`,W={id:Z,name:$,priority:J,action:Y,conditions:Q};return this.wafRules.set(Z,W),W}enableShield($,J="standard"){let Y=`shield-${Date.now()}-${this.counter++}`,Q={id:Y,resourceArn:$,protectionType:J};return this.shieldProtections.set(Y,Q),Q}createSecurityGroup($,J,Y){let Q=`sg-${Date.now()}-${this.counter++}`,Z={id:Q,name:$,vpcId:J,rules:Y};return this.securityGroups.set(Q,Z),Z}createNACL($,J){let Y=`nacl-${Date.now()}-${this.counter++}`,Q={id:Y,vpcId:$,rules:J};return this.nacls.set(Y,Q),Q}clear(){this.wafRules.clear(),this.shieldProtections.clear(),this.securityGroups.clear(),this.nacls.clear()}}var ZA=new rJ;class nJ{strategies=new Map;allocations=new Map;groups=new Map;counter=0;createTaggingStrategy($,J){let Y=`tagging-${Date.now()}-${this.counter++}`,Q={id:Y,tags:$,resources:J};return this.strategies.set(Y,Q),Q}createCostAllocation($,J){let Y=`cost-${Date.now()}-${this.counter++}`,Q={id:Y,tagKey:$,allocations:J};return this.allocations.set(Y,Q),Q}createResourceGroup($,J,Y){let Q=`group-${Date.now()}-${this.counter++}`,Z={id:Q,name:$,query:{resourceTypeFilters:J,tagFilters:Y}};return this.groups.set(Q,Z),Z}clear(){this.strategies.clear(),this.allocations.clear(),this.groups.clear()}}var WA=new nJ;class E2{builder;config;environment;mergedConfig;serverEipLogicalIds=new Map;constructor($){this.config=$.config,this.environment=$.environment,this.builder=new j8(`${this.config.project.name} - ${this.environment}`),this.mergedConfig=this.mergeEnvironmentConfig()}mergeEnvironmentConfig(){let J=this.config.environments[this.environment]?.infrastructure;if(!J)return this.config;return{...this.config,infrastructure:{...this.config.infrastructure,...J,compute:{...this.config.infrastructure?.compute,...J.compute},storage:{...this.config.infrastructure?.storage,...J.storage},functions:{...this.config.infrastructure?.functions,...J.functions},servers:{...this.config.infrastructure?.servers,...J.servers},databases:{...this.config.infrastructure?.databases,...J.databases},cdn:{...this.config.infrastructure?.cdn,...J.cdn},queues:{...this.config.infrastructure?.queues,...J.queues},redirects:{...this.config.infrastructure?.redirects,...J.redirects},realtime:{...this.config.infrastructure?.realtime,...J.realtime},cache:{...this.config.infrastructure?.cache,...J.cache},fileSystem:{...this.config.infrastructure?.fileSystem,...J.fileSystem},email:{...this.config.infrastructure?.email,...J.email},search:{...this.config.infrastructure?.search,...J.search},ai:{...this.config.infrastructure?.ai,...J.ai}}}}shouldDeploy($){if($.environments&&!$.environments.includes(this.environment))return!1;if($.requiresFeatures){let J=this.config.features||{};if(!$.requiresFeatures.every((Q)=>J[Q]===!0))return!1}if($.regions){let J=this.config.environments[this.environment]?.region||this.config.project.region;if(!$.regions.includes(J))return!1}if($.condition&&typeof $.condition==="function")return $.condition(this.config,this.environment);return!0}resolveApiOriginPort(){let $=this.mergedConfig.infrastructure?.api?.port??this.mergedConfig.ports?.api,J=Number($||3008);return Number.isFinite(J)&&J>0?J:3008}normalizeMountPath($){let J=$?.mountPath||$?.path;if(!J||J==="/")return;let Y=`/${J}`.replace(/\/+/g,"/").replace(/\/$/,"");return Y==="/"?void 0:Y}storageBucketLogicalId($,J,Y){return`${$}-${J}-s3-${Y}`.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((Q)=>Q.charAt(0).toUpperCase()+Q.slice(1)).join("")}pathMountRewriteFunctionCode($){return`function handler(event) { var request = event.request; var prefix = ${JSON.stringify($)}; var uri = request.uri; if (uri === prefix) { uri = '/'; } else if (uri.indexOf(prefix + '/') === 0) { uri = uri.substring(prefix.length); } if (uri === '' || uri === '/') { request.uri = '/index.html'; return request; } if (uri.endsWith('/')) { request.uri = uri + 'index.html'; return request; } var lastSegment = uri.substring(uri.lastIndexOf('/') + 1); if (lastSegment.indexOf('.') === -1) { request.uri = uri + '/index.html'; return request; } request.uri = uri; return request; }`}generate(){let $=this.mergedConfig.project.slug,J=this.environment,Y=!!(this.mergedConfig.infrastructure?.functions&&Object.keys(this.mergedConfig.infrastructure.functions).length>0||this.mergedConfig.infrastructure?.api),Q=!!(this.mergedConfig.infrastructure?.servers&&Object.keys(this.mergedConfig.infrastructure.servers).length>0),Z=!!this.mergedConfig.infrastructure?.compute;if(!!((this.mergedConfig.mode||"server")==="serverless"&&this.mergedConfig.infrastructure?.containers&&Object.keys(this.mergedConfig.infrastructure.containers).length>0))this.generateNetworkInfrastructure($,J),this.generateContainerInfrastructure($,J);if(Y)this.generateServerless($,J);if(Q)this.generateServer($,J);if(Z)this.generateComputeApp($,J);if(this.mergedConfig.infrastructure?.jumpBox)this.generateJumpBox($,J);if(this.generateSharedInfrastructure($,J),this.config.tags)this.applyGlobalTags(this.config.tags);return this}applyGlobalTags($){}generateNetworkInfrastructure($,J){if(this.builder.hasResource("VPC"))return;let Q=this.mergedConfig.infrastructure?.network?.cidr||"10.0.0.0/16",{vpc:Z,logicalId:W}=E0.createVpc({slug:$,environment:J,cidr:Q,enableDnsHostnames:!0,enableDnsSupport:!0});this.builder.addResource("VPC",Z);let G=`${$}${J}IGW`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(G,{Type:"AWS::EC2::InternetGateway",Properties:{Tags:[{Key:"Name",Value:`${$}-${J}-igw`},{Key:"Environment",Value:J}]}});let U=`${$}${J}IGWAttach`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(U,{Type:"AWS::EC2::VPCGatewayAttachment",Properties:{VpcId:{Ref:"VPC"},InternetGatewayId:{Ref:G}}});let z=`${$}${J}PublicRT`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(z,{Type:"AWS::EC2::RouteTable",Properties:{VpcId:{Ref:"VPC"},Tags:[{Key:"Name",Value:`${$}-${J}-public-rt`},{Key:"Environment",Value:J}]}}),this.builder.addResource(`${$}${J}PublicRoute`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::EC2::Route",Properties:{RouteTableId:{Ref:z},DestinationCidrBlock:"0.0.0.0/0",GatewayId:{Ref:G}},DependsOn:U});let H=this.mergedConfig.environments[J]?.region||this.mergedConfig.project.region||"us-east-1",A=["a","b"];for(let K=0;K<2;K++){let B=`PublicSubnet${K+1}`;this.builder.addResource(B,{Type:"AWS::EC2::Subnet",Properties:{VpcId:{Ref:"VPC"},CidrBlock:`10.0.${K}.0/24`,AvailabilityZone:`${H}${A[K]}`,MapPublicIpOnLaunch:!0,Tags:[{Key:"Name",Value:`${$}-${J}-public-${A[K]}`},{Key:"Environment",Value:J}]}}),this.builder.addResource(`${B}RTAssoc`,{Type:"AWS::EC2::SubnetRouteTableAssociation",Properties:{SubnetId:{Ref:B},RouteTableId:{Ref:z}}})}}generateContainerInfrastructure($,J){let Y=this.mergedConfig.infrastructure?.containers;if(!Y)return;let Q=this.mergedConfig.infrastructure?.loadBalancer,Z=this.mergedConfig.infrastructure?.ssl,W=this.mergedConfig.infrastructure?.dns,G;if(Z?.enabled&&Z.domains?.length)if(Z.certificateArn);else{let A=Z.domains[0],K=Z.domains.slice(1).map((O)=>{if(O.includes(".")&&O.endsWith(A))return O.replace(`.${A}`,"");return O}),{certificate:B,logicalId:j}=L0.createCertificate({domain:A,subdomains:K,slug:$,environment:J,validationMethod:"DNS",hostedZoneId:W?.hostedZoneId});G=j,this.builder.addResource(j,B)}let U="ECSCluster";this.builder.addResource(U,{Type:"AWS::ECS::Cluster",Properties:{ClusterName:`${$}-${J}`,ClusterSettings:[{Name:"containerInsights",Value:"enabled"}],Tags:[{Key:"Name",Value:`${$}-${J}`},{Key:"Environment",Value:J}]}});let z=`${$}${J}ALBSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(z,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ALB security group for ${$}-${J}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:80,ToPort:80,CidrIp:"0.0.0.0/0",Description:"HTTP"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${$}-${J}-alb-sg`},{Key:"Environment",Value:J}]}});let H=`${$}${J}ECSSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");for(let[A,K]of Object.entries(Y)){let B=K.port||3000;this.builder.addResource(H,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ECS tasks security group for ${$}-${J}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:B,ToPort:B,SourceSecurityGroupId:{Ref:z},Description:"Allow traffic from ALB"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${$}-${J}-ecs-sg`},{Key:"Environment",Value:J}]}});let j=`${$}${J}TaskExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(j,{Type:"AWS::IAM::Role",Properties:{RoleName:`${$}-${J}-ecs-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"],Policies:[{PolicyName:"ECRPullPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["ecr:GetAuthorizationToken","ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage","logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"}]}}]}});let O=`${$}${J}TaskRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(O,{Type:"AWS::IAM::Role",Properties:{RoleName:`${$}-${J}-ecs-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"TaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},{Effect:"Allow",Action:["s3:GetObject","s3:PutObject","s3:ListBucket"],Resource:"*"}]}}]}});let X=`${$}${J}${A}LogGroup`.replace(/[^a-zA-Z0-9]/g,""),_=`/ecs/${$}-${J}-${A}`;this.builder.addResource(X,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:_,RetentionInDays:30}});let q=`${$}${J}${A}TaskDef`.replace(/[^a-zA-Z0-9]/g,""),E=String(K.cpu||512),M=String(K.memory||1024);this.builder.addResource(q,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${$}-${J}-${A}`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:E,Memory:M,ExecutionRoleArn:{"Fn::GetAtt":[j,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[O,"Arn"]},ContainerDefinitions:[{Name:A,Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${$}:latest`},Essential:!0,PortMappings:[{ContainerPort:B,Protocol:"tcp"}],LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":_,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":A}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${B}${K.healthCheck||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${$}-${J}-${A}`},{Key:"Environment",Value:J}]},DependsOn:[j,O,X]});let D=`${$}${J}ALB`.replace(/[^a-zA-Z0-9]/g,"");if(Q?.enabled!==!1){this.builder.addResource(D,{Type:"AWS::ElasticLoadBalancingV2::LoadBalancer",Properties:{Name:`${$}-${J}-alb`,Scheme:"internet-facing",Type:"application",Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],SecurityGroups:[{Ref:z}],Tags:[{Key:"Name",Value:`${$}-${J}-alb`},{Key:"Environment",Value:J}]}});let R=`${$}${J}TargetGroup`.replace(/[^a-zA-Z0-9]/g,""),T=Q?.healthCheck?.path||K.healthCheck||"/health";this.builder.addResource(R,{Type:"AWS::ElasticLoadBalancingV2::TargetGroup",Properties:{Name:`${$}-${J}-tg`,Port:B,Protocol:"HTTP",VpcId:{Ref:"VPC"},TargetType:"ip",HealthCheckPath:T,HealthCheckIntervalSeconds:Q?.healthCheck?.interval||30,HealthyThresholdCount:Q?.healthCheck?.healthyThreshold||2,UnhealthyThresholdCount:Q?.healthCheck?.unhealthyThreshold||5,HealthCheckTimeoutSeconds:10,Tags:[{Key:"Name",Value:`${$}-${J}-tg`},{Key:"Environment",Value:J}]}});let S=`${$}${J}HTTPListener`.replace(/[^a-zA-Z0-9]/g,"");if(Z?.enabled&&Z?.redirectHttp)this.builder.addResource(S,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:D},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"redirect",RedirectConfig:{Protocol:"HTTPS",Port:"443",StatusCode:"HTTP_301"}}]}});else this.builder.addResource(S,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:D},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:R}}]}});if(Z?.enabled){let I=Z.certificateArn||(G?{Ref:G}:void 0);if(I){let f=`${$}${J}HTTPSListener`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(f,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:D},Port:443,Protocol:"HTTPS",Certificates:[{CertificateArn:I}],DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:R}}],SslPolicy:"ELBSecurityPolicy-TLS13-1-2-2021-06"}})}}let C=`${$}${J}${A}Service`.replace(/[^a-zA-Z0-9]/g,""),x=K.desiredCount||1;this.builder.addResource(C,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${$}-${J}-${A}`,Cluster:{Ref:U},TaskDefinition:{Ref:q},DesiredCount:x,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:H}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},LoadBalancers:[{ContainerName:A,ContainerPort:B,TargetGroupArn:{Ref:R}}],HealthCheckGracePeriodSeconds:120,Tags:[{Key:"Name",Value:`${$}-${J}-${A}`},{Key:"Environment",Value:J}]},DependsOn:[q,S,R]});let h=K.autoScaling;if(h){let I=`${$}${J}${A}ScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(I,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:h.max||10,MinCapacity:h.min||1,ResourceId:{"Fn::Sub":`service/\${${U}}/${$}-${J}-${A}`},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs",RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"}},DependsOn:C}),h.targetCpuUtilization)this.builder.addResource(`${$}${J}${A}CPUScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${$}-${J}-${A}-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:I},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:h.targetCpuUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}});if(h.targetMemoryUtilization)this.builder.addResource(`${$}${J}${A}MemoryScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${$}-${J}-${A}-memory-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:I},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageMemoryUtilization"},TargetValue:h.targetMemoryUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(W?.domain&&W?.hostedZoneId)this.builder.addResource(`${$}${J}ApiDnsRecord`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:W.hostedZoneId,Name:`api.${W.domain}`,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[D,"DNSName"]},HostedZoneId:{"Fn::GetAtt":[D,"CanonicalHostedZoneID"]}}}});this.builder.addOutput("ECSClusterArn",{Description:"ECS Cluster ARN",Value:{"Fn::GetAtt":[U,"Arn"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-cluster-arn"}}}),this.builder.addOutput("ECSServiceName",{Description:"ECS Service Name",Value:`${$}-${J}-${A}`,Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-service-name"}}}),this.builder.addOutput("LoadBalancerDNS",{Description:"Application Load Balancer DNS Name",Value:{"Fn::GetAtt":[D,"DNSName"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-alb-dns"}}})}else{let R=`${$}${J}${A}Service`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(R,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${$}-${J}-${A}`,Cluster:{Ref:U},TaskDefinition:{Ref:q},DesiredCount:K.desiredCount||1,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:H}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},Tags:[{Key:"Name",Value:`${$}-${J}-${A}`},{Key:"Environment",Value:J}]},DependsOn:[q]})}break}}generateServerless($,J){if(this.mergedConfig.infrastructure?.functions)for(let[Y,Q]of Object.entries(this.mergedConfig.infrastructure.functions)){if(!this.shouldDeploy(Q))continue;let{role:Z,logicalId:W}=s0.createRole({slug:$,environment:J,roleName:`${$}-${J}-${Y}-role`,servicePrincipal:"lambda.amazonaws.com",managedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]});this.builder.addResource(W,Z);let{lambdaFunction:G,logicalId:U}=l.createLambdaFunction({slug:$,environment:J,functionName:`${$}-${J}-${Y}`,handler:Q.handler||"index.handler",runtime:Q.runtime||"nodejs20.x",code:{zipFile:Q.code||"export const handler = async () => ({ statusCode: 200 });"},role:W,timeout:Q.timeout,memorySize:Q.memorySize});this.builder.addResource(U,G)}if(this.mergedConfig.infrastructure?.api){let{restApi:Y,logicalId:Q}=X8.createRestApi({slug:$,environment:J,apiName:`${$}-${J}-api`});this.builder.addResource(Q,Y)}}generateServer($,J){if(!this.mergedConfig.infrastructure?.servers)return;let Y=this.mergedConfig.infrastructure.servers,Q=this.mergedConfig.infrastructure?.compute,Z=this.mergedConfig.infrastructure?.ssl;if(Object.values(Y).some((G)=>!G.privateNetwork||G.privateNetwork==="create"))this.generateNetworkInfrastructure($,J);for(let[G,U]of Object.entries(Y)){let z=U.instanceType||U.size||Q?.size,A=(z?l.InstanceSize.specs[z]:void 0)?.instanceType||z||"t3.micro",K=U.type||"app",B=U.userData||U.startupScript;if(!B)B=l.UserData.generateAppServerScript({runtime:"bun",runtimeVersion:U.bunVersion||"latest",webServer:"none",domain:U.domain,enableSsl:!!Z?.enabled,sslEmail:Z?.letsEncrypt?.email,installRedis:K==="cache",installDatabaseClients:!!U.database});let j=U.privateNetwork&&U.privateNetwork!=="create"?U.privateNetwork:{Ref:"VPC"},O=U.subnet||{Ref:"PublicSubnet1"},_=!!this.mergedConfig.infrastructure?.email?.server?.enabled,q=[22,80,443,this.resolveApiOriginPort()];if(_)q.push(25,465,587,143,993);let E=l.createServerModeStack({slug:`${$}-${G}`,environment:J,vpcId:j,subnetId:O,instanceType:A,keyName:U.keyName||`${$}-${J}`,domain:U.domain,userData:B,volumeSize:U.diskSize||20,imageId:U.image,allowedPorts:q});for(let[M,D]of Object.entries(E.resources))this.builder.addResource(M,D);this.serverEipLogicalIds.set(G,E.outputs.eipLogicalId),this.serverEipLogicalIds.set(`${G}Instance`,E.outputs.instanceLogicalId),this.builder.addOutput(`${G}InstanceId`,{Value:{Ref:E.outputs.instanceLogicalId},Description:`Instance ID for ${G} server`}),this.builder.addOutput(`${G}PublicIp`,{Value:{Ref:E.outputs.eipLogicalId},Description:`Public IP for ${G} server`})}}generateComputeApp($,J){let Y=this.mergedConfig.infrastructure?.compute;if(!Y)return;let Q=this.mergedConfig.sites||{},Z=Object.entries(Q),G=this.mergedConfig.infrastructure?.dns?.domain,U=this.mergedConfig.infrastructure?.database,z=Y.size,A=(z?l.InstanceSize.specs[z]:void 0)?.instanceType||"t3.micro";if(!this.builder.hasResource("VPC"))this.generateNetworkInfrastructure($,J);let K=l.UserData.generateBunAppScript({runtime:Y.runtime||"bun",runtimeVersion:Y.runtimeVersion||"latest",systemPackages:Y.systemPackages,database:U}),B=Z.map(([,M])=>M.port).filter((M)=>typeof M==="number"&&![80,443].includes(M)),j=this.resolveApiOriginPort(),O=`${$}${J}DeployBucket`.replace(/[^a-zA-Z0-9]/g,""),X=`${$}-${J}-deploy`;this.builder.addResource(O,{Type:"AWS::S3::Bucket",Properties:{BucketName:X,PublicAccessBlockConfiguration:{BlockPublicAcls:!0,BlockPublicPolicy:!0,IgnorePublicAcls:!0,RestrictPublicBuckets:!0},LifecycleConfiguration:{Rules:[{Id:"expire-old-releases",Status:"Enabled",ExpirationInDays:7,Prefix:"releases/"}]},Tags:[{Key:"Project",Value:$},{Key:"Environment",Value:J},{Key:"ManagedBy",Value:"ts-cloud"}]}});let _=l.createServerModeStack({slug:`${$}-app`,environment:J,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},instanceType:A,keyName:`${$}-${J}`,domain:G,userData:K,volumeSize:Y.disk?.size||20,imageId:Y.image,allowedPorts:[...Y.allowSsh?[22]:[],80,443,j,...B]}),q=_.resources[_.outputs.instanceLogicalId];if(q?.Properties){let M=q.Properties.Tags||[];q.Properties.Tags=[...M,{Key:"Project",Value:$},{Key:"Environment",Value:J},{Key:"Role",Value:"app"},{Key:"ManagedBy",Value:"ts-cloud"}]}let E=_.resources[_.outputs.roleLogicalId];if(E?.Properties)E.Properties.Policies=E.Properties.Policies||[],E.Properties.Policies.push({PolicyName:"DeployBucketRead",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["s3:GetObject","s3:ListBucket"],Resource:[`arn:aws:s3:::${X}`,`arn:aws:s3:::${X}/*`]}]}});for(let[M,D]of Object.entries(_.resources))this.builder.addResource(M,D);this.builder.addOutput("deployBucketName",{Value:{Ref:O},Description:"S3 bucket where release tarballs are uploaded"}),this.builder.addOutput("appInstanceId",{Value:{Ref:_.outputs.instanceLogicalId},Description:"EC2 instance ID for the app server"}),this.builder.addOutput("appPublicIp",{Value:{Ref:_.outputs.eipLogicalId},Description:"Public IP for the app server"}),this.serverEipLogicalIds.set("app",_.outputs.eipLogicalId),this.serverEipLogicalIds.set("appInstance",_.outputs.instanceLogicalId)}generateJumpBox($,J){let Y=this.mergedConfig.infrastructure?.jumpBox;if(!Y)return;let Q=Y===!0?{}:Y;if(Q.enabled===!1)return;this.generateNetworkInfrastructure($,J);let Z=Q.size||"micro",G=l.InstanceSize.specs[Z]?.instanceType||Z||"t3.micro",U;if(Q.mountEfs)U={fileSystemId:typeof Q.mountEfs==="string"?Q.mountEfs:{Ref:"FileSystem"},mountPath:Q.mountPath||"/mnt/efs"};let z;if(Q.databaseTools)z=l.JumpBox.withDatabaseTools({slug:$,environment:J,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:Q.keyName||`${$}-${J}`,allowedCidrs:Q.allowedCidrs});else if(U)z=l.JumpBox.withEfsMount({slug:$,environment:J,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:Q.keyName||`${$}-${J}`,fileSystemId:U.fileSystemId,mountPath:U.mountPath,allowedCidrs:Q.allowedCidrs});else z=l.createJumpBox({slug:$,environment:J,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:Q.keyName||`${$}-${J}`,instanceType:G,allowedCidrs:Q.allowedCidrs,mountEfs:U});for(let[H,A]of Object.entries(z.resources))this.builder.addResource(H,A);this.builder.addOutput("JumpBoxInstanceId",{Value:{Ref:z.instanceLogicalId},Description:"The ID of the JumpBox EC2 instance (use with SSM Session Manager or SSH)"})}generateSharedInfrastructure($,J){let Y=this.mergedConfig.infrastructure?.ssl,Q=this.mergedConfig.infrastructure?.dns,Z=Q?.domain,W=Q?.hostedZoneId,G=[],U,z;if(Y?.certificateArn)z=Y.certificateArn;else if(Y?.enabled&&Z&&W){let X=Y.domains||[Z],_=X[0],q=X.slice(1).map((D)=>{if(D.includes(".")&&D.endsWith(_))return D.replace(`.${_}`,"");return D}),{certificate:E,logicalId:M}=L0.createCertificate({domain:_,subdomains:q,slug:$,environment:J,validationMethod:"DNS",hostedZoneId:W});U=M,this.builder.addResource(M,E)}if(this.mergedConfig.infrastructure?.storage){let X=Object.entries(this.mergedConfig.infrastructure.storage).some(([,E])=>E.website),_;if(X&&(U||z))_=`${$}${J}CloudFrontOAC`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(_,{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:`${$}-${J}-s3-oac`,Description:`OAC for ${$} ${J} S3 website buckets`,OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}});let q=Object.entries(this.mergedConfig.infrastructure.storage).map(([E,M])=>({name:E,config:M,mountPath:this.normalizeMountPath(M),logicalId:this.storageBucketLogicalId($,J,E)})).filter((E)=>E.name!=="public"&&E.config.website&&E.mountPath);for(let[E,M]of Object.entries(this.mergedConfig.infrastructure.storage)){let D=!!(M.website&&_),{bucket:R,logicalId:T}=U1.createBucket({slug:$,name:E,environment:J,bucketName:`${$}-${J}-${E}`,versioning:M.versioning,encryption:M.encryption,public:D?!1:M.public});if(D&&R.Properties)R.Properties.PublicAccessBlockConfiguration={BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!1,RestrictPublicBuckets:!1};if(this.builder.addResource(T,R),M.website){let C=typeof M.website==="object"?M.website:{},x=U1.enableWebsiteHosting(R,C.indexDocument||"index.html",C.errorDocument);this.builder.addResource(T,x)}let S=this.normalizeMountPath(M);if(D&&_&&Z&&!(S&&E!=="public")){let C=`${$}${J}${E}CDN`.replace(/[^a-zA-Z0-9]/g,""),x=[];if(M.aliases&&M.aliases.length>0)x.push(...M.aliases);else if(E==="public"){if(x.push(Z),Y?.domains?.includes(`www.${Z}`))x.push(`www.${Z}`)}else if(E==="docs")x.push(`docs.${Z}`);else if(E==="blog")x.push(`blog.${Z}`);let h=typeof M.website==="object"?M.website:{},I=M.spa===!0,f=h.errorDocument||(I?"index.html":"404.html"),d=[];if(I)d.push({ErrorCode:403,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300});else d.push({ErrorCode:403,ResponseCode:404,ResponsePagePath:`/${f}`,ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:404,ResponsePagePath:`/${f}`,ErrorCachingMinTTL:300});let r=z&&x.length>0?{AcmCertificateArn:z,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:U&&x.length>0?{AcmCertificateArn:{Ref:U},SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},c=`S3-${$}-${J}-${E}`,n=this.mergedConfig.project.region||"us-east-1",s;if(!I)s=`${$}${J}${E}UrlRewrite`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(s,{Type:"AWS::CloudFront::Function",Properties:{Name:`${$}-${J}-${E}-url-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`URL rewrite for ${$} ${J} ${E} - appends .html to extensionless paths`,Runtime:"cloudfront-js-2.0"},FunctionCode:`function handler(event) { var request = event.request; var uri = request.uri; if (uri.endsWith('/')) { request.uri += 'index.html'; }
|
|
5379
|
-
else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let J0=[{Id:c,DomainName:{"Fn::Sub":`\${${T}}.s3.${n}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:_}}],_0=[],l0=[];if(E==="public"){let f0=this.serverEipLogicalIds.get("app"),s1=this.serverEipLogicalIds.get("appInstance");if(f0){let D0=`EC2-${$}-${J}-api`,f1=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",g1=f1==="us-east-1"?".compute-1.amazonaws.com":`.${f1}.compute.amazonaws.com`,V2={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:f0}]}]},g1]]},e$=this.resolveApiOriginPort();if(J0.push({Id:D0,DomainName:V2,CustomOriginConfig:{HTTPPort:e$,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),l0.push(f0),s1)l0.push(s1);_0.push({PathPattern:"/api/*",TargetOriginId:D0,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:["GET","HEAD"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",OriginRequestPolicyId:"b689b0a8-53d0-40ab-baf2-68738e2966ac"})}for(let D0 of q){let f1=`S3-${$}-${J}-${D0.name}`,g1=`${$}${J}${D0.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(g1,{Type:"AWS::CloudFront::Function",Properties:{Name:`${$}-${J}-${D0.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${$} ${J} ${D0.name} at ${D0.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(D0.mountPath)}}),J0.push({Id:f1,DomainName:{"Fn::Sub":`\${${D0.logicalId}}.s3.${n}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:_}}),l0.push(D0.logicalId,g1);for(let V2 of[D0.mountPath,`${D0.mountPath}/*`])_0.push({PathPattern:V2,TargetOriginId:f1,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[g1,"FunctionARN"]}}]})}}let I0={Type:"AWS::CloudFront::Distribution",DependsOn:[T,_,...s?[s]:[],...l0],Properties:{DistributionConfig:{Enabled:!0,Comment:`${$} ${J} ${E} site`,DefaultRootObject:"index.html",Origins:J0,DefaultCacheBehavior:{TargetOriginId:c,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...s?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[s,"FunctionARN"]}}]}:{}},..._0.length>0?{CacheBehaviors:_0}:{},...x.length>0?{Aliases:x}:{},ViewerCertificate:r,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:d}}};if(U)I0.DependsOn.push(U);this.builder.addResource(C,I0);let t1=`${T}CloudFrontPolicy`;if(this.builder.addResource(t1,{Type:"AWS::S3::BucketPolicy",DependsOn:[T,C],Properties:{Bucket:{Ref:T},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${T}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${C}}`}}}}]}}}),E==="public")for(let f0 of q){let s1=`${f0.logicalId}CloudFrontPolicy`;this.builder.addResource(s1,{Type:"AWS::S3::BucketPolicy",DependsOn:[f0.logicalId,C],Properties:{Bucket:{Ref:f0.logicalId},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${f0.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${C}}`}}}}]}}})}if(x.length>0)G.push({name:E,bucketLogicalId:T,distLogicalId:C,oacLogicalId:_,aliases:x});this.builder.addOutput(`${E}CloudFrontDomain`,{Value:{"Fn::GetAtt":[C,"DomainName"]},Description:`CloudFront domain for ${E}`}),this.builder.addOutput(`${E}CloudFrontDistributionId`,{Value:{Ref:C},Description:`CloudFront distribution ID for ${E}`})}if(this.builder.addOutput(`${E}BucketName`,{Value:{Ref:T},Description:`S3 bucket name for ${E}`}),E==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:T},Description:"Frontend S3 bucket name"});if(E==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:T},Description:"Documentation S3 bucket name"});if(E==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:T},Description:"Blog S3 bucket name"})}}if(W&&G.length>0)for(let{name:X,distLogicalId:_,aliases:q}of G)for(let E of q){let M=E.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${M}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[_],Properties:{HostedZoneId:W,Name:E,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[_,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${M}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[_],Properties:{HostedZoneId:W,Name:E,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[_,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.databases))if(_.engine==="dynamodb"){let{table:q,logicalId:E}=n1.createTable({slug:$,environment:J,tableName:`${$}-${J}-${X}`,partitionKey:_.partitionKey||{name:"id",type:"S"},sortKey:_.sortKey});this.builder.addResource(E,q)}else if(_.engine==="postgres"){let{dbInstance:q,logicalId:E}=n1.createPostgres({slug:$,environment:J,dbInstanceIdentifier:`${$}-${J}-${X}`,masterUsername:_.username||"admin",masterUserPassword:_.password||"changeme123",allocatedStorage:_.storage||20,dbInstanceClass:_.instanceClass||"db.t3.micro"});this.builder.addResource(E,q)}else if(_.engine==="mysql"){let{dbInstance:q,logicalId:E}=n1.createMysql({slug:$,environment:J,dbInstanceIdentifier:`${$}-${J}-${X}`,masterUsername:_.username||"admin",masterUserPassword:_.password||"changeme123",allocatedStorage:_.storage||20,dbInstanceClass:_.instanceClass||"db.t3.micro"});this.builder.addResource(E,q)}}if(this.mergedConfig.infrastructure?.cdn)for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.cdn)){let{distribution:q,logicalId:E}=t0.createDistribution({slug:$,environment:J,origin:{domainName:_.origin,originId:`${$}-origin`}});this.builder.addResource(E,q)}if(this.mergedConfig.infrastructure?.queues)for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(_))continue;let{queue:q,logicalId:E}=m0.createQueue({slug:$,environment:J,name:`${$}-${J}-${X}`,fifo:_.fifo,visibilityTimeout:_.visibilityTimeout,messageRetentionPeriod:_.messageRetentionPeriod,delaySeconds:_.delaySeconds,maxMessageSize:_.maxMessageSize,receiveMessageWaitTime:_.receiveMessageWaitTime,contentBasedDeduplication:_.contentBasedDeduplication,encrypted:_.encrypted,kmsKeyId:_.kmsKeyId});this.builder.addResource(E,q);let M;if(_.deadLetterQueue){let{deadLetterQueue:D,updatedSourceQueue:R,deadLetterLogicalId:T}=m0.createDeadLetterQueue(E,{slug:$,environment:J,maxReceiveCount:_.maxReceiveCount});M=T,this.builder.addResource(T,D);let C=this.builder.getResources()[E];if(C?.Properties)C.Properties.RedrivePolicy=R.Properties?.RedrivePolicy}if(_.trigger){let D=_.trigger,R=`${$}${J}${D.functionName}`.replace(/[^a-zA-Z0-9]/g,""),T={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[E,"Arn"]},FunctionName:{Ref:R},BatchSize:D.batchSize||10,MaximumBatchingWindowInSeconds:D.batchWindow||0,Enabled:!0,...D.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...D.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:D.maxConcurrency}},...D.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(D.filterPattern)}]}}},DependsOn:[E,R]};this.builder.addResource(`${E}Trigger`,T)}if(_.alarms?.enabled){let D=_.alarms,R=D.notificationTopicArn;if(!R&&D.notificationEmails?.length){let C=`${E}AlarmTopic`;this.builder.addResource(C,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${$}-${J}-${X}-alarms`,DisplayName:`${X} Queue Alarms`}}),D.notificationEmails.forEach((x,h)=>{this.builder.addResource(`${C}Sub${h}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:C},Protocol:"email",Endpoint:x}})}),R={Ref:C}}let T=D.queueDepthThreshold||1000;this.builder.addResource(`${E}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-${X}-queue-depth`,AlarmDescription:`Queue ${X} depth exceeds ${T} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:T,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[E,"QueueName"]}}],...R&&{AlarmActions:[R],OKActions:[R]}}});let S=D.messageAgeThreshold||3600;if(this.builder.addResource(`${E}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-${X}-message-age`,AlarmDescription:`Queue ${X} oldest message exceeds ${S} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:S,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[E,"QueueName"]}}],...R&&{AlarmActions:[R],OKActions:[R]}}}),M&&D.dlqAlarm!==!1)this.builder.addResource(`${M}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-${X}-dlq-messages`,AlarmDescription:`Dead letter queue for ${X} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[M,"QueueName"]}}],...R&&{AlarmActions:[R]}}})}if(_.subscribe){let D=_.subscribe,R=D.topicArn;if(!R&&D.topicName)R={Ref:D.topicName};if(R){this.builder.addResource(`${E}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:E}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[E,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":R}}}]}}});let T={TopicArn:R,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[E,"Arn"]},RawMessageDelivery:D.rawMessageDelivery||!1};if(D.filterPolicy)T.FilterPolicy=D.filterPolicy,T.FilterPolicyScope=D.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${E}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:T,DependsOn:`${E}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources($,J);else this.generateRealtimeResources($,J);let H=this.mergedConfig.infrastructure?.redirects;if(H){let X=H.target||this.mergedConfig.infrastructure?.dns?.domain||"",_=H.protocol||"https";if(H.domains?.length&&X)for(let q of H.domains){let{bucket:E,bucketPolicy:M,logicalId:D,policyLogicalId:R}=h1.createDomainRedirectBucket({slug:$,environment:J,sourceDomain:q,targetDomain:X,protocol:_});this.builder.addResource(D,E),this.builder.addResource(R,M)}if(H.paths&&Object.keys(H.paths).length>0){let q=h1.fromMapping(H.paths,{statusCode:H.statusCode||301}),{function:E,logicalId:M}=h1.createPathRedirectFunction({slug:$,environment:J,rules:q});this.builder.addResource(M,E)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:q,logicalId:E}=c$.createAlarm({slug:$,environment:J,alarmName:`${$}-${J}-${X}`,metricName:_.metricName||"Errors",namespace:_.namespace||"AWS/Lambda",threshold:_.threshold||1,comparisonOperator:_.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(E,q)}let A=this.mergedConfig.infrastructure?.cache;if(A){let X=A.type||"redis";if(X==="redis"){this.generateNetworkInfrastructure($,J);let _=A.redis||{},{replicationGroup:q,subnetGroup:E,logicalId:M,subnetGroupId:D}=m$.createRedis({slug:$,environment:J,nodeType:_.nodeType||A.nodeType||"cache.t3.micro",engineVersion:_.engineVersion||"7.1",port:_.port||6379,numCacheClusters:_.numCacheNodes||2,automaticFailover:_.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:_.snapshotRetentionLimit||7,snapshotWindow:_.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(M,q),E&&D)this.builder.addResource(D,E);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[M,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[M,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(X==="memcached"){this.generateNetworkInfrastructure($,J);let _=A.elasticache||{},{cluster:q,subnetGroup:E,logicalId:M,subnetGroupId:D}=m$.createMemcached({slug:$,environment:J,nodeType:_.nodeType||A.nodeType||"cache.t3.micro",engineVersion:_.engineVersion||"1.6.22",numCacheNodes:_.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(M,q),E&&D)this.builder.addResource(D,E);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[M,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let K=this.mergedConfig.infrastructure?.email;if(K){let X=K.domain||this.mergedConfig.infrastructure?.dns?.domain;if(X){let{emailIdentity:_,logicalId:q}=z0.verifyDomain({domain:X,slug:$,environment:J,enableDkim:K.enableDkim!==!1,dkimKeyLength:K.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(q,_),K.configurationSet!==!1){let{configurationSet:D,logicalId:R}=z0.createConfigurationSet({slug:$,environment:J});this.builder.addResource(R,D)}let E=K.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(E){if(K.enableDkim!==!1)for(let C=1;C<=3;C++){let x=`DkimRecord${C}${X.replace(/\./g,"")}`;this.builder.addResource(x,{Type:"AWS::Route53::RecordSet",DependsOn:[q],Properties:{HostedZoneId:E,Name:{"Fn::GetAtt":[q,`DkimDNSTokenName${C}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[q,`DkimDNSTokenValue${C}`]}]}})}let{record:D,logicalId:R}=z0.createSpfRecord(X,E);this.builder.addResource(R,D);let{record:T,logicalId:S}=z0.createDmarcRecord(X,E,{policy:"none",reportingEmail:K.dmarcReportingEmail||`dmarc-reports@${X}`});this.builder.addResource(S,T)}this.builder.addOutput("EmailDomain",{Value:X,Description:"SES verified email domain"});let M=K.server;if(M?.enabled&&E){let D=this.mergedConfig.environments[J]?.region||this.mergedConfig.project.region||"us-east-1",R=`${$}-${J}-email`,{role:T,policy:S,roleLogicalId:C,policyLogicalId:x}=z0.createEmailLambdaRole({slug:$,environment:J,s3BucketArn:`arn:aws:s3:::${R}`,sesIdentityArn:`arn:aws:ses:${D}:*:identity/${X}`});this.builder.addResource(C,T),this.builder.addResource(x,S);let{function:h,permission:I,logicalId:f,permissionLogicalId:d}=z0.createInboundEmailLambda({slug:$,environment:J,roleArn:{"Fn::GetAtt":[C,"Arn"]},s3BucketName:R,organizedPrefix:"mailboxes/"});this.builder.addResource(f,h),this.builder.addResource(d,I);let r=z0.createInboundEmailSetup({slug:$,environment:J,domain:X,s3BucketName:R,s3KeyPrefix:"inbox/",region:D,hostedZoneId:E,lambdaFunctionArn:{"Fn::GetAtt":[f,"Arn"]}});for(let[n,s]of Object.entries(r.resources)){if(s.Type==="AWS::SES::ReceiptRule")s.DependsOn=[d,f];this.builder.addResource(n,s)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:f},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:R,Description:"S3 bucket for email storage"});let c=this.serverEipLogicalIds.get("app");if(c&&E){let s=`${M?.subdomain||"mail"}.${X}`,J0=s.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${J0}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:E,Name:s,Type:"A",TTL:"300",ResourceRecords:[{Ref:c}]}})}this.builder.addOutput("MailHost",{Value:`mail.${X}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let B=this.mergedConfig.infrastructure?.search;if(B){let X=B.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(X)this.generateNetworkInfrastructure($,J);let{domain:_,logicalId:q}=r1.createDomain({slug:$,environment:J,engineVersion:B.engineVersion||"OpenSearch_2.11",instanceType:B.instanceType||"t3.small.search",instanceCount:B.instanceCount||1,volumeSize:B.volumeSize||10,volumeType:B.volumeType||"gp3",dedicatedMaster:B.dedicatedMaster||!1,dedicatedMasterType:B.dedicatedMasterType,dedicatedMasterCount:B.dedicatedMasterCount||3,multiAz:B.multiAz||!1,encryption:B.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:B.advancedSecurity,autoTune:B.autoTune!==!1,vpc:X});this.builder.addResource(q,_),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[q,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[q,"Arn"]},Description:"OpenSearch domain ARN"})}let j=this.mergedConfig.infrastructure?.fileSystem;if(j&&Object.keys(j).length>0){this.generateNetworkInfrastructure($,J);for(let[X,_]of Object.entries(j)){let{fileSystem:q,logicalId:E}=V1.createFileSystem({slug:`${$}-${X}`,environment:J,encrypted:_.encrypted!==!1,performanceMode:_.performanceMode||"generalPurpose",throughputMode:_.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(E,q);let{securityGroup:M,logicalId:D}=V1.createEfsSecurityGroup({slug:`${$}-${X}`,environment:J,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(D,M);let{mountTargets:R,logicalIds:T}=V1.createMultiAzMountTargets(E,{slug:`${$}-${X}`,environment:J,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:D}});for(let S=0;S<R.length;S++)this.builder.addResource(T[S],R[S]);this.builder.addOutput(`${X}FileSystemId`,{Value:{Ref:E},Description:`EFS file system ID for ${X}`})}}let O=this.mergedConfig.infrastructure?.ai;if(O){let X=O.service||"ecs",_=O.models||["*"],q=O.allowStreaming!==!1,E;if(X==="ecs")E=z1.enableBedrockForEcs({slug:$,environment:J,models:_,allowStreaming:q});else if(X==="ec2")E=z1.enableBedrockForEc2({slug:$,environment:J,models:_,allowStreaming:q});else if(X==="lambda")E=z1.enableBedrockForLambda({slug:$,environment:J,models:_,allowStreaming:q});else E=z1.createBedrockRole(X,{slug:$,environment:J,models:_,allowStreaming:q});if(this.builder.addResource(E.logicalId,E.role),O.allowAsync){let{policy:M,logicalId:D}=z1.createBedrockPolicy({slug:$,environment:J,models:_,allowStreaming:q,allowAsync:!0});this.builder.addResource(D,M)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[E.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources($,J){let Y=this.mergedConfig.infrastructure?.realtime;if(!Y)return;let Q=Y.name||`${$}-${J}-realtime`,Z=Y.scaling||{},W=Y.storage||{type:"dynamodb"},G=Z.handlerMemory||256,U=Z.handlerTimeout||30,z=`${$}${J}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),H=`${$}${J}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(W.type==="dynamodb"){let T=W.dynamodb||{},S=T.billingMode||"PAY_PER_REQUEST";this.builder.addResource(z,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${$}-${J}-realtime-connections`,BillingMode:S,AttributeDefinitions:[{AttributeName:"connectionId",AttributeType:"S"},{AttributeName:"userId",AttributeType:"S"}],KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],GlobalSecondaryIndexes:[{IndexName:"userId-index",KeySchema:[{AttributeName:"userId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}},...T.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${$}-${J}-realtime-connections`},{Key:"Environment",Value:J}]}}),this.builder.addResource(H,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${$}-${J}-realtime-channels`,BillingMode:S,AttributeDefinitions:[{AttributeName:"channel",AttributeType:"S"},{AttributeName:"connectionId",AttributeType:"S"}],KeySchema:[{AttributeName:"channel",KeyType:"HASH"},{AttributeName:"connectionId",KeyType:"RANGE"}],GlobalSecondaryIndexes:[{IndexName:"connectionId-index",KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}},Tags:[{Key:"Name",Value:`${$}-${J}-realtime-channels`},{Key:"Environment",Value:J}]}})}let A=`${$}${J}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(A,{Type:"AWS::IAM::Role",Properties:{RoleName:`${$}-${J}-realtime-handler-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],Policies:[{PolicyName:"RealtimeHandlerPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["dynamodb:GetItem","dynamodb:PutItem","dynamodb:DeleteItem","dynamodb:Query","dynamodb:Scan","dynamodb:UpdateItem"],Resource:[{"Fn::GetAtt":[z,"Arn"]},{"Fn::Sub":`\${${z}.Arn}/index/*`},{"Fn::GetAtt":[H,"Arn"]},{"Fn::Sub":`\${${H}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let K=`${$}${J}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),B=`${$}${J}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),j=`${$}${J}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(K,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${$}-${J}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[A,"Arn"]},MemorySize:G,Timeout:U,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:z},CHANNELS_TABLE:{Ref:H},ENVIRONMENT:J}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[A,z]}),this.builder.addResource(B,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${$}-${J}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[A,"Arn"]},MemorySize:G,Timeout:U,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:z},CHANNELS_TABLE:{Ref:H},ENVIRONMENT:J}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[A,z,H]}),this.builder.addResource(j,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${$}-${J}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[A,"Arn"]},MemorySize:G,Timeout:U,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:z},CHANNELS_TABLE:{Ref:H},ENVIRONMENT:J}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[A,z,H]});let O=`${$}${J}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(O,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:Q,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:Q,Environment:J}}});let X=`${K}Permission`,_=`${B}Permission`,q=`${j}Permission`;this.builder.addResource(X,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:K},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${O}}/*/$connect`}}}),this.builder.addResource(_,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:B},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${O}}/*/$disconnect`}}}),this.builder.addResource(q,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:j},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${O}}/*/$default`}}});let E=`${O}ConnectInteg`,M=`${O}DisconnectInteg`,D=`${O}MessageInteg`;this.builder.addResource(E,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:O},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${K}.Arn}/invocations`}}}),this.builder.addResource(M,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:O},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${B}.Arn}/invocations`}}}),this.builder.addResource(D,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:O},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${j}.Arn}/invocations`}}}),this.builder.addResource(`${O}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:O},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${E}}`}}}),this.builder.addResource(`${O}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:O},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${M}}`}}}),this.builder.addResource(`${O}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:O},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${D}}`}}});let R=`${O}Stage`;if(this.builder.addResource(R,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:O},StageName:J,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:Z.messagesPerSecond||1000,ThrottlingRateLimit:Z.messagesPerSecond||1000}}}),Y.monitoring?.enabled){let T=Y.monitoring,S=T.notificationTopicArn;if(!S&&T.notificationEmails?.length){let C=`${O}AlarmTopic`;this.builder.addResource(C,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${$}-${J}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),T.notificationEmails.forEach((x,h)=>{this.builder.addResource(`${C}Sub${h}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:C},Protocol:"email",Endpoint:x}})}),S={Ref:C}}if(T.connectionThreshold)this.builder.addResource(`${O}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${T.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:T.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:O}}],...S&&{AlarmActions:[S]}}});if(T.errorThreshold)this.builder.addResource(`${O}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${T.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:T.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:O}}],...S&&{AlarmActions:[S]}}})}this.builder.addOutput(`${O}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${O}}.execute-api.\${AWS::Region}.amazonaws.com/${J}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${O}Id`,{Description:"WebSocket API ID",Value:{Ref:O},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
|
|
5379
|
+
else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let J0=[{Id:c,DomainName:{"Fn::Sub":`\${${T}}.s3.${n}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:_}}],_0=[],l0=[];if(E==="public"){let f0=this.serverEipLogicalIds.get("app"),s1=this.serverEipLogicalIds.get("appInstance");if(f0){let D0=`EC2-${$}-${J}-api`,f1=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",g1=f1==="us-east-1"?".compute-1.amazonaws.com":`.${f1}.compute.amazonaws.com`,V2={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:f0}]}]},g1]]},e$=this.resolveApiOriginPort();if(J0.push({Id:D0,DomainName:V2,CustomOriginConfig:{HTTPPort:e$,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),l0.push(f0),s1)l0.push(s1);_0.push({PathPattern:"/api/*",TargetOriginId:D0,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:["GET","HEAD"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",OriginRequestPolicyId:"b689b0a8-53d0-40ab-baf2-68738e2966ac"})}for(let D0 of q){let f1=`S3-${$}-${J}-${D0.name}`,g1=`${$}${J}${D0.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(g1,{Type:"AWS::CloudFront::Function",Properties:{Name:`${$}-${J}-${D0.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${$} ${J} ${D0.name} at ${D0.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(D0.mountPath)}}),J0.push({Id:f1,DomainName:{"Fn::Sub":`\${${D0.logicalId}}.s3.${n}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:_}}),l0.push(D0.logicalId,g1);for(let V2 of[D0.mountPath,`${D0.mountPath}/*`])_0.push({PathPattern:V2,TargetOriginId:f1,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[g1,"FunctionARN"]}}]})}}let I0={Type:"AWS::CloudFront::Distribution",DependsOn:[T,_,...s?[s]:[],...l0],Properties:{DistributionConfig:{Enabled:!0,Comment:`${$} ${J} ${E} site`,DefaultRootObject:"index.html",Origins:J0,DefaultCacheBehavior:{TargetOriginId:c,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...s?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[s,"FunctionARN"]}}]}:{}},..._0.length>0?{CacheBehaviors:_0}:{},...x.length>0?{Aliases:x}:{},ViewerCertificate:r,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:d}}};if(U)I0.DependsOn.push(U);this.builder.addResource(C,I0);let t1=`${T}CloudFrontPolicy`;if(this.builder.addResource(t1,{Type:"AWS::S3::BucketPolicy",DependsOn:[T,C],Properties:{Bucket:{Ref:T},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${T}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${C}}`}}}}]}}}),E==="public")for(let f0 of q){let s1=`${f0.logicalId}CloudFrontPolicy`;this.builder.addResource(s1,{Type:"AWS::S3::BucketPolicy",DependsOn:[f0.logicalId,C],Properties:{Bucket:{Ref:f0.logicalId},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${f0.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${C}}`}}}}]}}})}if(x.length>0)G.push({name:E,bucketLogicalId:T,distLogicalId:C,oacLogicalId:_,aliases:x});this.builder.addOutput(`${E}CloudFrontDomain`,{Value:{"Fn::GetAtt":[C,"DomainName"]},Description:`CloudFront domain for ${E}`}),this.builder.addOutput(`${E}CloudFrontDistributionId`,{Value:{Ref:C},Description:`CloudFront distribution ID for ${E}`})}if(this.builder.addOutput(`${E}BucketName`,{Value:{Ref:T},Description:`S3 bucket name for ${E}`}),E==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:T},Description:"Frontend S3 bucket name"});if(E==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:T},Description:"Documentation S3 bucket name"});if(E==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:T},Description:"Blog S3 bucket name"})}}if(W&&G.length>0)for(let{name:X,distLogicalId:_,aliases:q}of G)for(let E of q){let M=E.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${M}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[_],Properties:{HostedZoneId:W,Name:E,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[_,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${M}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[_],Properties:{HostedZoneId:W,Name:E,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[_,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.databases))if(_.engine==="dynamodb"){let{table:q,logicalId:E}=n1.createTable({slug:$,environment:J,tableName:`${$}-${J}-${X}`,partitionKey:_.partitionKey||{name:"id",type:"S"},sortKey:_.sortKey});this.builder.addResource(E,q)}else if(_.engine==="postgres"){let{dbInstance:q,logicalId:E}=n1.createPostgres({slug:$,environment:J,dbInstanceIdentifier:`${$}-${J}-${X}`,masterUsername:_.username||"admin",masterUserPassword:_.password||"changeme123",allocatedStorage:_.storage||20,dbInstanceClass:_.instanceClass||"db.t3.micro"});this.builder.addResource(E,q)}else if(_.engine==="mysql"){let{dbInstance:q,logicalId:E}=n1.createMysql({slug:$,environment:J,dbInstanceIdentifier:`${$}-${J}-${X}`,masterUsername:_.username||"admin",masterUserPassword:_.password||"changeme123",allocatedStorage:_.storage||20,dbInstanceClass:_.instanceClass||"db.t3.micro"});this.builder.addResource(E,q)}}if(this.mergedConfig.infrastructure?.cdn)for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.cdn)){let{distribution:q,logicalId:E}=t0.createDistribution({slug:$,environment:J,origin:{domainName:_.origin,originId:`${$}-origin`}});this.builder.addResource(E,q)}if(this.mergedConfig.infrastructure?.queues)for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(_))continue;let{queue:q,logicalId:E}=m0.createQueue({slug:$,environment:J,name:`${$}-${J}-${X}`,fifo:_.fifo,visibilityTimeout:_.visibilityTimeout,messageRetentionPeriod:_.messageRetentionPeriod,delaySeconds:_.delaySeconds,maxMessageSize:_.maxMessageSize,receiveMessageWaitTime:_.receiveMessageWaitTime,contentBasedDeduplication:_.contentBasedDeduplication,encrypted:_.encrypted,kmsKeyId:_.kmsKeyId});this.builder.addResource(E,q);let M;if(_.deadLetterQueue){let{deadLetterQueue:D,updatedSourceQueue:R,deadLetterLogicalId:T}=m0.createDeadLetterQueue(E,{slug:$,environment:J,maxReceiveCount:_.maxReceiveCount});M=T,this.builder.addResource(T,D);let C=this.builder.getResources()[E];if(C?.Properties)C.Properties.RedrivePolicy=R.Properties?.RedrivePolicy}if(_.trigger){let D=_.trigger,R=`${$}${J}${D.functionName}`.replace(/[^a-zA-Z0-9]/g,""),T={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[E,"Arn"]},FunctionName:{Ref:R},BatchSize:D.batchSize||10,MaximumBatchingWindowInSeconds:D.batchWindow||0,Enabled:!0,...D.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...D.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:D.maxConcurrency}},...D.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(D.filterPattern)}]}}},DependsOn:[E,R]};this.builder.addResource(`${E}Trigger`,T)}if(_.alarms?.enabled){let D=_.alarms,R=D.notificationTopicArn;if(!R&&D.notificationEmails?.length){let C=`${E}AlarmTopic`;this.builder.addResource(C,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${$}-${J}-${X}-alarms`,DisplayName:`${X} Queue Alarms`}}),D.notificationEmails.forEach((x,h)=>{this.builder.addResource(`${C}Sub${h}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:C},Protocol:"email",Endpoint:x}})}),R={Ref:C}}let T=D.queueDepthThreshold||1000;this.builder.addResource(`${E}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-${X}-queue-depth`,AlarmDescription:`Queue ${X} depth exceeds ${T} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:T,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[E,"QueueName"]}}],...R&&{AlarmActions:[R],OKActions:[R]}}});let S=D.messageAgeThreshold||3600;if(this.builder.addResource(`${E}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-${X}-message-age`,AlarmDescription:`Queue ${X} oldest message exceeds ${S} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:S,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[E,"QueueName"]}}],...R&&{AlarmActions:[R],OKActions:[R]}}}),M&&D.dlqAlarm!==!1)this.builder.addResource(`${M}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-${X}-dlq-messages`,AlarmDescription:`Dead letter queue for ${X} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[M,"QueueName"]}}],...R&&{AlarmActions:[R]}}})}if(_.subscribe){let D=_.subscribe,R=D.topicArn;if(!R&&D.topicName)R={Ref:D.topicName};if(R){this.builder.addResource(`${E}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:E}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[E,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":R}}}]}}});let T={TopicArn:R,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[E,"Arn"]},RawMessageDelivery:D.rawMessageDelivery||!1};if(D.filterPolicy)T.FilterPolicy=D.filterPolicy,T.FilterPolicyScope=D.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${E}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:T,DependsOn:`${E}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources($,J);else this.generateRealtimeResources($,J);let H=this.mergedConfig.infrastructure?.redirects;if(H){let X=H.target||this.mergedConfig.infrastructure?.dns?.domain||"",_=H.protocol||"https";if(H.domains?.length&&X)for(let q of H.domains){let{bucket:E,bucketPolicy:M,logicalId:D,policyLogicalId:R}=h1.createDomainRedirectBucket({slug:$,environment:J,sourceDomain:q,targetDomain:X,protocol:_});this.builder.addResource(D,E),this.builder.addResource(R,M)}if(H.paths&&Object.keys(H.paths).length>0){let q=h1.fromMapping(H.paths,{statusCode:H.statusCode||301}),{function:E,logicalId:M}=h1.createPathRedirectFunction({slug:$,environment:J,rules:q});this.builder.addResource(M,E)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[X,_]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:q,logicalId:E}=c$.createAlarm({slug:$,environment:J,alarmName:`${$}-${J}-${X}`,metricName:_.metricName||"Errors",namespace:_.namespace||"AWS/Lambda",threshold:_.threshold||1,comparisonOperator:_.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(E,q)}let A=this.mergedConfig.infrastructure?.cache;if(A){let X=A.type||"redis";if(X==="redis"){this.generateNetworkInfrastructure($,J);let _=A.redis||{},{replicationGroup:q,subnetGroup:E,logicalId:M,subnetGroupId:D}=m$.createRedis({slug:$,environment:J,nodeType:_.nodeType||A.nodeType||"cache.t3.micro",engineVersion:_.engineVersion||"7.1",port:_.port||6379,numCacheClusters:_.numCacheNodes||2,automaticFailover:_.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:_.snapshotRetentionLimit||7,snapshotWindow:_.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(M,q),E&&D)this.builder.addResource(D,E);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[M,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[M,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(X==="memcached"){this.generateNetworkInfrastructure($,J);let _=A.elasticache||{},{cluster:q,subnetGroup:E,logicalId:M,subnetGroupId:D}=m$.createMemcached({slug:$,environment:J,nodeType:_.nodeType||A.nodeType||"cache.t3.micro",engineVersion:_.engineVersion||"1.6.22",numCacheNodes:_.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(M,q),E&&D)this.builder.addResource(D,E);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[M,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let K=this.mergedConfig.infrastructure?.email;if(K){let X=K.domain||this.mergedConfig.infrastructure?.dns?.domain;if(X){let{emailIdentity:_,logicalId:q}=z0.verifyDomain({domain:X,slug:$,environment:J,enableDkim:K.enableDkim!==!1,dkimKeyLength:K.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(q,_),K.configurationSet!==!1){let{configurationSet:D,logicalId:R}=z0.createConfigurationSet({slug:$,environment:J});this.builder.addResource(R,D)}let E=K.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(E){if(K.enableDkim!==!1)for(let C=1;C<=3;C++){let x=`DkimRecord${C}${X.replace(/\./g,"")}`;this.builder.addResource(x,{Type:"AWS::Route53::RecordSet",DependsOn:[q],Properties:{HostedZoneId:E,Name:{"Fn::GetAtt":[q,`DkimDNSTokenName${C}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[q,`DkimDNSTokenValue${C}`]}]}})}let{record:D,logicalId:R}=z0.createSpfRecord(X,E);this.builder.addResource(R,D);let{record:T,logicalId:S}=z0.createDmarcRecord(X,E,{policy:"none",reportingEmail:K.dmarcReportingEmail||`dmarc-reports@${X}`});this.builder.addResource(S,T)}this.builder.addOutput("EmailDomain",{Value:X,Description:"SES verified email domain"});let M=K.server;if(M?.enabled&&E){let D=this.mergedConfig.environments[J]?.region||this.mergedConfig.project.region||"us-east-1",R=`${$}-${J}-email`,{role:T,policy:S,roleLogicalId:C,policyLogicalId:x}=z0.createEmailLambdaRole({slug:$,environment:J,s3BucketArn:`arn:aws:s3:::${R}`,sesIdentityArn:`arn:aws:ses:${D}:*:identity/${X}`});this.builder.addResource(C,T),this.builder.addResource(x,S);let{function:h,permission:I,logicalId:f,permissionLogicalId:d}=z0.createInboundEmailLambda({slug:$,environment:J,roleArn:{"Fn::GetAtt":[C,"Arn"]},s3BucketName:R,organizedPrefix:"mailboxes/"});this.builder.addResource(f,h),this.builder.addResource(d,I);let r=z0.createInboundEmailSetup({slug:$,environment:J,domain:X,s3BucketName:R,s3KeyPrefix:"inbox/",region:D,hostedZoneId:E,lambdaFunctionArn:{"Fn::GetAtt":[f,"Arn"]}});for(let[n,s]of Object.entries(r.resources)){if(s.Type==="AWS::SES::ReceiptRule")s.DependsOn=[d,f];this.builder.addResource(n,s)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:f},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:R,Description:"S3 bucket for email storage"});let c=this.serverEipLogicalIds.get("app");if(c&&E){let s=`${M?.subdomain||"mail"}.${X}`,J0=s.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${J0}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:E,Name:s,Type:"A",TTL:"300",ResourceRecords:[{Ref:c}]}})}this.builder.addOutput("MailHost",{Value:`mail.${X}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let B=this.mergedConfig.infrastructure?.search;if(B){let X=B.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(X)this.generateNetworkInfrastructure($,J);let{domain:_,logicalId:q}=r1.createDomain({slug:$,environment:J,engineVersion:B.engineVersion||"OpenSearch_2.11",instanceType:B.instanceType||"t3.small.search",instanceCount:B.instanceCount||1,volumeSize:B.volumeSize||10,volumeType:B.volumeType||"gp3",dedicatedMaster:B.dedicatedMaster||!1,dedicatedMasterType:B.dedicatedMasterType,dedicatedMasterCount:B.dedicatedMasterCount||3,multiAz:B.multiAz||!1,encryption:B.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:B.advancedSecurity,autoTune:B.autoTune!==!1,vpc:X});this.builder.addResource(q,_),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[q,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[q,"Arn"]},Description:"OpenSearch domain ARN"})}let j=this.mergedConfig.infrastructure?.fileSystem;if(j&&Object.keys(j).length>0){this.generateNetworkInfrastructure($,J);for(let[X,_]of Object.entries(j)){let{fileSystem:q,logicalId:E}=V1.createFileSystem({slug:`${$}-${X}`,environment:J,encrypted:_.encrypted!==!1,performanceMode:_.performanceMode||"generalPurpose",throughputMode:_.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(E,q);let{securityGroup:M,logicalId:D}=V1.createEfsSecurityGroup({slug:`${$}-${X}`,environment:J,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(D,M);let{mountTargets:R,logicalIds:T}=V1.createMultiAzMountTargets(E,{slug:`${$}-${X}`,environment:J,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:D}});for(let S=0;S<R.length;S++)this.builder.addResource(T[S],R[S]);this.builder.addOutput(`${X}FileSystemId`,{Value:{Ref:E},Description:`EFS file system ID for ${X}`})}}let O=this.mergedConfig.infrastructure?.ai;if(O){let X=O.service||"ecs",_=O.models||["*"],q=O.allowStreaming!==!1,E;if(X==="ecs")E=z1.enableBedrockForEcs({slug:$,environment:J,models:_,allowStreaming:q});else if(X==="ec2")E=z1.enableBedrockForEc2({slug:$,environment:J,models:_,allowStreaming:q});else if(X==="lambda")E=z1.enableBedrockForLambda({slug:$,environment:J,models:_,allowStreaming:q});else E=z1.createBedrockRole(X,{slug:$,environment:J,models:_,allowStreaming:q});if(this.builder.addResource(E.logicalId,E.role),O.allowAsync){let{policy:M,logicalId:D}=z1.createBedrockPolicy({slug:$,environment:J,models:_,allowStreaming:q,allowAsync:!0});this.builder.addResource(D,M)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[E.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources($,J){let Y=this.mergedConfig.infrastructure?.realtime;if(!Y)return;let Q=Y.name||`${$}-${J}-realtime`,Z=Y.scaling||{},W=Y.storage||{type:"dynamodb"},G=Z.handlerMemory||256,U=Z.handlerTimeout||30,z=`${$}${J}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),H=`${$}${J}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(W.type==="dynamodb"){let T=W.dynamodb||{},S=T.billingMode||"PAY_PER_REQUEST";this.builder.addResource(z,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${$}-${J}-realtime-connections`,BillingMode:S,AttributeDefinitions:[{AttributeName:"connectionId",AttributeType:"S"},{AttributeName:"userId",AttributeType:"S"}],KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],GlobalSecondaryIndexes:[{IndexName:"userId-index",KeySchema:[{AttributeName:"userId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}},...T.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${$}-${J}-realtime-connections`},{Key:"Environment",Value:J}]}}),this.builder.addResource(H,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${$}-${J}-realtime-channels`,BillingMode:S,AttributeDefinitions:[{AttributeName:"channel",AttributeType:"S"},{AttributeName:"connectionId",AttributeType:"S"}],KeySchema:[{AttributeName:"channel",KeyType:"HASH"},{AttributeName:"connectionId",KeyType:"RANGE"}],GlobalSecondaryIndexes:[{IndexName:"connectionId-index",KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:T.readCapacity||5,WriteCapacityUnits:T.writeCapacity||5}},Tags:[{Key:"Name",Value:`${$}-${J}-realtime-channels`},{Key:"Environment",Value:J}]}})}let A=`${$}${J}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(A,{Type:"AWS::IAM::Role",Properties:{RoleName:`${$}-${J}-realtime-handler-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],Policies:[{PolicyName:"RealtimeHandlerPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["dynamodb:GetItem","dynamodb:PutItem","dynamodb:DeleteItem","dynamodb:Query","dynamodb:Scan","dynamodb:UpdateItem"],Resource:[{"Fn::GetAtt":[z,"Arn"]},{"Fn::Sub":`\${${z}.Arn}/index/*`},{"Fn::GetAtt":[H,"Arn"]},{"Fn::Sub":`\${${H}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let K=`${$}${J}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),B=`${$}${J}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),j=`${$}${J}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(K,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${$}-${J}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[A,"Arn"]},MemorySize:G,Timeout:U,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:z},CHANNELS_TABLE:{Ref:H},ENVIRONMENT:J}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[A,z]}),this.builder.addResource(B,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${$}-${J}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[A,"Arn"]},MemorySize:G,Timeout:U,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:z},CHANNELS_TABLE:{Ref:H},ENVIRONMENT:J}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[A,z,H]}),this.builder.addResource(j,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${$}-${J}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[A,"Arn"]},MemorySize:G,Timeout:U,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:z},CHANNELS_TABLE:{Ref:H},ENVIRONMENT:J}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[A,z,H]});let O=`${$}${J}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(O,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:Q,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:Q,Environment:J}}});let X=`${K}Permission`,_=`${B}Permission`,q=`${j}Permission`;this.builder.addResource(X,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:K},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${O}}/*/$connect`}}}),this.builder.addResource(_,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:B},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${O}}/*/$disconnect`}}}),this.builder.addResource(q,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:j},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${O}}/*/$default`}}});let E=`${O}ConnectInteg`,M=`${O}DisconnectInteg`,D=`${O}MessageInteg`;this.builder.addResource(E,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:O},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${K}.Arn}/invocations`}}}),this.builder.addResource(M,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:O},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${B}.Arn}/invocations`}}}),this.builder.addResource(D,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:O},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${j}.Arn}/invocations`}}}),this.builder.addResource(`${O}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:O},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${E}}`}}}),this.builder.addResource(`${O}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:O},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${M}}`}}}),this.builder.addResource(`${O}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:O},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${D}}`}}});let R=`${O}Stage`;if(this.builder.addResource(R,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:O},StageName:J,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:Z.messagesPerSecond||1000,ThrottlingRateLimit:Z.messagesPerSecond||1000}}}),Y.monitoring?.enabled){let T=Y.monitoring,S=T.notificationTopicArn;if(!S&&T.notificationEmails?.length){let C=`${O}AlarmTopic`;this.builder.addResource(C,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${$}-${J}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),T.notificationEmails.forEach((x,h)=>{this.builder.addResource(`${C}Sub${h}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:C},Protocol:"email",Endpoint:x}})}),S={Ref:C}}if(T.connectionThreshold)this.builder.addResource(`${O}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${T.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:T.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:O}}],...S&&{AlarmActions:[S]}}});if(T.errorThreshold)this.builder.addResource(`${O}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${$}-${J}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${T.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:T.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:O}}],...S&&{AlarmActions:[S]}}})}this.builder.addOutput(`${O}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${O}}.execute-api.\${AWS::Region}.amazonaws.com/${J}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${O}Id`,{Description:"WebSocket API ID",Value:{Ref:O},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
|
|
5380
5380
|
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
|
|
5381
5381
|
const { DynamoDBDocumentClient, PutCommand } = require('@aws-sdk/lib-dynamodb');
|
|
5382
5382
|
|
package/dist/index.js
CHANGED
|
@@ -63161,7 +63161,7 @@ else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`
|
|
|
63161
63161
|
AllowedMethods: ["GET", "HEAD", "OPTIONS"],
|
|
63162
63162
|
CachedMethods: ["GET", "HEAD", "OPTIONS"],
|
|
63163
63163
|
Compress: true,
|
|
63164
|
-
CachePolicyId: "
|
|
63164
|
+
CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
|
|
63165
63165
|
FunctionAssociations: [{
|
|
63166
63166
|
EventType: "viewer-request",
|
|
63167
63167
|
FunctionARN: { "Fn::GetAtt": [mountedFunctionLogicalId, "FunctionARN"] }
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/ts-cloud",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.14",
|
|
5
5
|
"description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
"test": "bun test"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
|
-
"@ts-cloud/aws-types": "0.2.
|
|
89
|
-
"@ts-cloud/core": "0.2.
|
|
88
|
+
"@ts-cloud/aws-types": "0.2.14",
|
|
89
|
+
"@ts-cloud/core": "0.2.14",
|
|
90
90
|
"@stacksjs/ts-xml": "^0.1.0"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|