exlo 0.0.16 → 0.0.18

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{readFile as I}from"node:fs/promises";import p from"node:path";import{pathToFileURL as O}from"node:url";import{parseArgs as Y}from"node:util";var j="exlo",b="0.0.16";var g=!1,R=e=>{g=e},A=()=>g,l=(e,t)=>{g&&console.log(`\x1B[${e}m${t}\x1B[0m`)},C=e=>console.log(`\x1B[31m${e}\x1B[0m`),$=e=>l("32",e),q=e=>l("33",e),L=e=>l("36",e),S=e=>l("90",e),F=e=>l("94",e),_=e=>l("95",e),D=e=>g?console.log(e):void 0,n={GREEN:$,YELLOW:q,CYAN:L,BR_BLUE:F,RED:C,GREY:S,PINK:_,setDebug:R,getDebug:A,info:D};var y={config:{type:"string",short:"c",default:"exlo.config.ts",description:"Path to exlo 'defineConfig' file."},debug:{type:"boolean",default:!1,description:"Enable debug mode. When enabled exlo will print usefull informations."},create:{type:"boolean",default:!1,description:"Create exlo 'defineConfig' file based on passed options."},runtime:{type:"string",short:"r",description:"Lambda default runtime (ex: nodejs22.x, python3.7, ruby2.7 etc.)."},timeout:{type:"string",short:"t",default:"3",description:"Lambda default timeout."},port:{type:"string",short:"p",default:"0",description:"Lambda server port."},definitions:{type:"string",short:"d",description:"Path to .json, .mjs, .cjs file with Lambda function definitions."},functions:{type:"string",short:"f",multiple:!0,description:"Glob pattern to automatically find and define Lambda handlers."},exclude:{type:"string",short:"X",multiple:!0,default:[".(test|spec)."],description:"RegExp string to exclude found enteries from --functions."},"handler-name":{type:"string",default:"handler",description:"Handler function name. To be used with --functions."},env:{type:"string",short:"e",multiple:!0,default:[],description:"Environment variables to be injected into Lambdas. All existing AWS_* are automatically injected.",example:"-e API_KEY=supersecret -e API_URL=https://website.com"},"optimize-build":{type:"boolean",default:!1,description:"externalize dependencies and other stuff"},"shim-require":{type:"boolean",default:!1,description:"shim 'require()', '__dirname' and '__filename' when bundeling Lambdas with ESM format"},help:{type:"boolean",short:"h",skipFullPrint:!0},version:{type:"boolean",short:"v",skipFullPrint:!0},"esbuild-external":{type:"string",multiple:!0,skip:!0},"esbuild-resolveExtensions":{type:"string",multiple:!0,skip:!0},"esbuild-mainFields":{type:"string",multiple:!0,skip:!0},"esbuild-conditions":{type:"string",multiple:!0,skip:!0},"esbuild-entryPoints":{type:"string",multiple:!0,skip:!0},"esbuild-inject":{type:"string",multiple:!0,skip:!0},"esbuild-nodePaths":{type:"string",multiple:!0,skip:!0}};function M(){n.setDebug(!0),n.GREY("Usage examples:"),console.log("exlo -c exlo.config.ts"),console.log(`exlo -p 3000 --debug --functions "src/lambdas/**/*.ts"
2
+ import{readFile as I}from"node:fs/promises";import p from"node:path";import{pathToFileURL as O}from"node:url";import{parseArgs as Y}from"node:util";var j="exlo",b="0.0.18";var g=!1,R=e=>{g=e},A=()=>g,l=(e,t)=>{g&&console.log(`\x1B[${e}m${t}\x1B[0m`)},C=e=>console.log(`\x1B[31m${e}\x1B[0m`),$=e=>l("32",e),q=e=>l("33",e),L=e=>l("36",e),S=e=>l("90",e),F=e=>l("94",e),_=e=>l("95",e),D=e=>g?console.log(e):void 0,n={GREEN:$,YELLOW:q,CYAN:L,BR_BLUE:F,RED:C,GREY:S,PINK:_,setDebug:R,getDebug:A,info:D};var y={config:{type:"string",short:"c",default:"exlo.config.ts",description:"Path to exlo 'defineConfig' file."},debug:{type:"boolean",default:!1,description:"Enable debug mode. When enabled exlo will print usefull informations."},create:{type:"boolean",default:!1,description:"Create exlo 'defineConfig' file based on passed options."},runtime:{type:"string",short:"r",description:"Lambda default runtime (ex: nodejs22.x, python3.7, ruby2.7 etc.)."},timeout:{type:"string",short:"t",default:"3",description:"Lambda default timeout."},port:{type:"string",short:"p",default:"0",description:"Lambda server port."},definitions:{type:"string",short:"d",description:"Path to .json, .mjs, .cjs file with Lambda function definitions."},functions:{type:"string",short:"f",multiple:!0,description:"Glob pattern to automatically find and define Lambda handlers."},exclude:{type:"string",short:"X",multiple:!0,default:[".(test|spec)."],description:"RegExp string to exclude found enteries from --functions."},"handler-name":{type:"string",default:"handler",description:"Handler function name. To be used with --functions."},env:{type:"string",short:"e",multiple:!0,default:[],description:"Environment variables to be injected into Lambdas. All existing AWS_* are automatically injected.",example:"-e API_KEY=supersecret -e API_URL=https://website.com"},"optimize-build":{type:"boolean",default:!1,description:"externalize dependencies and other stuff"},"shim-require":{type:"boolean",default:!1,description:"shim 'require()', '__dirname' and '__filename' when bundeling Lambdas with ESM format"},help:{type:"boolean",short:"h",skipFullPrint:!0},version:{type:"boolean",short:"v",skipFullPrint:!0},"esbuild-external":{type:"string",multiple:!0,skip:!0},"esbuild-resolveExtensions":{type:"string",multiple:!0,skip:!0},"esbuild-mainFields":{type:"string",multiple:!0,skip:!0},"esbuild-conditions":{type:"string",multiple:!0,skip:!0},"esbuild-entryPoints":{type:"string",multiple:!0,skip:!0},"esbuild-inject":{type:"string",multiple:!0,skip:!0},"esbuild-nodePaths":{type:"string",multiple:!0,skip:!0}};function M(){n.setDebug(!0),n.GREY("Usage examples:"),console.log("exlo -c exlo.config.ts"),console.log(`exlo -p 3000 --debug --functions "src/lambdas/**/*.ts"
3
3
  `),n.BR_BLUE("Options:");for(let[e,t]of Object.entries(y)){let i=e;if(t.skip)continue;if(t.short&&(i+=`, -${t.short}`),t.skipFullPrint){n.CYAN(` --${i}
4
4
  `);continue}let s=` type: ${t.type}`;t.description&&(s+=`
5
5
  description: ${t.description}`),"default"in t&&(s+=`
@@ -8,7 +8,8 @@ import type { Config } from "./config";
8
8
  import type { ILambdaMock } from "./lib/runtime/rapidApi";
9
9
  import type { HttpMethod } from "./lib/server/handlers";
10
10
  import type { log } from "./lib/utils/colorize";
11
- import type { IAwsProvider, ILambdaFunction } from "./standalone_types";
11
+ import type { IAwsProvider, IFunctionUrlCors, ILambdaFunction } from "./standalone_types";
12
+ export type { IFunctionUrlCors };
12
13
  export { version as exloVersion } from "../package.json";
13
14
  export type ILambda = {
14
15
  /**
@@ -96,7 +97,7 @@ export interface Options {
96
97
  shimRequire?: boolean;
97
98
  /**
98
99
  * Speed up build process during dev mode
99
- * @default true
100
+ * @default false
100
101
  */
101
102
  optimizeBuild?: boolean;
102
103
  };
@@ -1 +1 @@
1
- var h="0.0.16";function k(e){let c=new Set;e.plugins&&(e.plugins=e.plugins.filter((i,a)=>typeof i!="object"||!i||!("name"in i)?!1:((!i.name||!i.name.length||typeof i.name!="string")&&(i.name=`plugin-${a}`,console.warn(`No Plugin name provided at index ${a}`)),c.has(i.name)?i.name=i.name+a:c.add(i.name),!0)));async function f({stop:i,lambdas:a,region:g,esbuild:v,getServices:y,setService:d,addLambda:p,log:l,setEndpoint:b,getEndpoint:x,getAwsProviderDefaults:C,setAwsProviderDefaults:S}){let r={build:{esbuild:e?.build?.esbuild??{},shimRequire:e?.build?.shimRequire,optimizeBuild:e?.build?.optimizeBuild},server:{port:e.server?.port,setEndpointEnvVar:e.server?.setEndpointEnvVar},onKill:[]};if(e.defaults&&typeof e.defaults=="object"&&!Array.isArray(e.defaults)&&S(e.defaults),e.functions&&typeof e.functions=="object"&&!Array.isArray(e.functions))for(let[m,s]of Object.entries(e.functions))p(m,s);e.services&&(e.services.sqs&&d("sqs",e.services.sqs),e.services.sns&&d("sns",e.services.sns),e.services.dynamodb&&d("dynamodb",e.services.dynamodb));let o={stop:i,lambdas:a,region:g,esbuild:v,options:e,config:r,getServices:y,setService:d,addLambda:p,log:l,setEndpoint:b,getEndpoint:x,getAwsProviderDefaults:C};if(e.plugins){r.server.onReady=async(s,n)=>{for(let t of e.plugins)if(t.server?.onReady)try{await t.server.onReady.call(o,s,n)}catch(u){l.RED(t.name),console.error(u)}},r.buildCallback=async(s,n)=>{for(let t of e.plugins)if(t.buildCallback)try{await t.buildCallback.call(o,s,n)}catch(u){l.RED(t.name),console.error(u),n||process.exit(1)}};let m=e.plugins.reduce((s,n)=>(n.server?.request?.length&&s.push(...n.server.request),s),[]);m?.length&&(r.server.request=m.map(s=>(s.callback=s.callback.bind(o),s)));for(let s of e.plugins)if(s.onKill&&r.onKill.push(s.onKill.bind(o)),s.onInit)try{await s.onInit.call(o)}catch(n){throw l.RED(s.name),n}}return r}return f.plugins=e.plugins??[],f}export{k as defineConfig,h as exloVersion};
1
+ var h="0.0.18";function k(e){let c=new Set;e.plugins&&(e.plugins=e.plugins.filter((i,a)=>typeof i!="object"||!i||!("name"in i)?!1:((!i.name||!i.name.length||typeof i.name!="string")&&(i.name=`plugin-${a}`,console.warn(`No Plugin name provided at index ${a}`)),c.has(i.name)?i.name=i.name+a:c.add(i.name),!0)));async function f({stop:i,lambdas:a,region:p,esbuild:v,getServices:y,setService:d,addLambda:g,log:l,setEndpoint:b,getEndpoint:x,getAwsProviderDefaults:C,setAwsProviderDefaults:S}){let r={build:{esbuild:e?.build?.esbuild??{},shimRequire:e?.build?.shimRequire,optimizeBuild:e?.build?.optimizeBuild},server:{port:e.server?.port,setEndpointEnvVar:e.server?.setEndpointEnvVar},onKill:[]};if(e.defaults&&typeof e.defaults=="object"&&!Array.isArray(e.defaults)&&S(e.defaults),e.functions&&typeof e.functions=="object"&&!Array.isArray(e.functions))for(let[m,s]of Object.entries(e.functions))g(m,s);e.services&&(e.services.sqs&&d("sqs",e.services.sqs),e.services.sns&&d("sns",e.services.sns),e.services.dynamodb&&d("dynamodb",e.services.dynamodb));let o={stop:i,lambdas:a,region:p,esbuild:v,options:e,config:r,getServices:y,setService:d,addLambda:g,log:l,setEndpoint:b,getEndpoint:x,getAwsProviderDefaults:C};if(e.plugins){r.server.onReady=async(s,n)=>{for(let t of e.plugins)if(t.server?.onReady)try{await t.server.onReady.call(o,s,n)}catch(u){l.RED(t.name),console.error(u)}},r.buildCallback=async(s,n)=>{for(let t of e.plugins)if(t.buildCallback)try{await t.buildCallback.call(o,s,n)}catch(u){l.RED(t.name),console.error(u),n||process.exit(1)}};let m=e.plugins.reduce((s,n)=>(n.server?.request?.length&&s.push(...n.server.request),s),[]);m?.length&&(r.server.request=m.map(s=>(s.callback=s.callback.bind(o),s)));for(let s of e.plugins)if(s.onKill&&r.onKill.push(s.onKill.bind(o)),s.onInit)try{await s.onInit.call(o)}catch(n){throw l.RED(s.name),n}}return r}return f.plugins=e.plugins??[],f}export{k as defineConfig,h as exloVersion};
@@ -1 +1 @@
1
- var N=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});import L from"node:path";import{Worker as _}from"node:worker_threads";import k from"node:path";import{createRequire as O}from"node:module";var p=N,g={};function A(n){if(g[n])return g[n];if(typeof p=="function"&&typeof p.resolve=="function"){let e=k.resolve(p.resolve(n),"../../");return g[n]=e,e}p=O(new URL(".",import.meta.url));let t=k.resolve(p.resolve(n),"../../");return g[n]=t,t}import{randomUUID as H}from"node:crypto";import $ from"node:events";import{EventSourceMapping as Q}from"exlo/internal/esm";var y=class{#e;#t;maximumRecordAgeInSeconds;onComplete;onRecordExpire;records=[];closed=!1;tumbling;#n={};#i;getStreamEvent;constructor({batchSize:t,maximumBatchingWindow:e,onComplete:i,onRecordExpire:s,maximumRecordAgeInSeconds:o,tumbling:r}){this.maximumRecordAgeInSeconds=o,this.#e=t,this.#t=e,this.onComplete=i,this.onRecordExpire=s,this.tumbling=r,this.tumbling?(this.getStreamEvent=a=>this.tumbling.getTimeWindowEvent(a??this.records),this.tumbling.onComplete||(this.tumbling.onComplete=()=>{this.onComplete(this,!0)})):this.getStreamEvent=a=>({Records:a??this.records})}#s(){this.records.length==this.#e&&(this.closed=!0,clearTimeout(this.#i),this.onComplete(this,!1))}setRecord(t,e){return this.closed?!1:(this.records.push(t),this.#s(),this.#r(t.eventID,e),this.records.length==1&&(this.#i=setTimeout(()=>{this.closed||(this.closed=!0,this.onComplete(this,!1))},this.#t).unref()),!0)}removeRecord(t){let e=this.records.findIndex(i=>i.eventID==t);e!=-1&&(clearTimeout(this.#n[t]),this.records.splice(e,1))}hasRecord(t){return this.records.find(e=>e.eventID==t)}#r(t,e){this.#n[t]=setTimeout(()=>{let i=this.records.findIndex(s=>s.eventID==t);i!=-1&&(this.records.splice(i,1),this.onRecordExpire&&this.onRecordExpire(e))},this.maximumRecordAgeInSeconds).unref()}};var W=n=>typeof n=="string"&&n.length,C=(n,t)=>{if(!(typeof t>"u"||t===null||typeof t=="object"&&(t.batchItemFailures===null||Array.isArray(t.batchItemFailures)&&!t.batchItemFailures.length))&&typeof t=="object"&&Array.isArray(t.batchItemFailures)){if(t.batchItemFailures.some(s=>!s.itemIdentifier||!W(s.itemIdentifier)||!n.find(o=>o.eventID==s.itemIdentifier)))throw new Error("ReportBatchItemFailures: complete failure.");let e=[],i=[];for(let s of n)t.batchItemFailures.find(r=>r.itemIdentifier==s.eventID)?i.push(s):e.push(s.eventID);return{success:e,failures:i}}};import{randomUUID as M}from"node:crypto";var S=class{constructor(t,e,i){this.bucketArn=t;this.esmId=e;this.getS3Client=i}#e;get client(){return this.#e||(this.#e=this.getS3Client()),this.#e}#t;get bucketName(){return this.#t||(this.#t=this.bucketArn.split(":")[5]),this.#t}createObjectKey(t){let[e,i]=new Date().toISOString().split("T"),[s,o,r]=e.split("-"),[a,c,d]=i.split(":");return`aws/lambda/${this.esmId}/${t}/${s}/${o}/${r}/${e}T${a}.${c}.${d}-${M()}`}async report(t){await this.client.putObject({Bucket:this.bucketName,Key:this.createObjectKey(t.DDBStreamBatchInfo.shardId),Body:JSON.stringify(t)})}};var w=class{constructor(t,e){this.topicArn=t;this.getSnsClient=e}#e;get client(){return this.#e||(this.#e=this.getSnsClient()),this.#e}async report(t){let e={...t,payload:void 0};await this.send(e)}async send(t){return this.client.publish({TopicArn:this.topicArn,Message:JSON.stringify(t)})}};var I=class{constructor(t,e){this.queueArn=t;this.getSqsClient=e}#e;#t;async getQueueUrl(){if(!this.#e){let e=this.queueArn.split(":")[5],i=await this.client.getQueueUrl({QueueName:e});this.#e=i.QueueUrl}return this.#e}get client(){return this.#t||(this.#t=this.getSqsClient()),this.#t}async report(t){let e={...t,payload:void 0};await this.send(e)}async send(t){return this.client.sendMessage({QueueUrl:await this.getQueueUrl(),MessageBody:JSON.stringify(t)})}};var q=n=>n&&!Array.isArray(n)&&Object.keys(n).length,x=class{shardId;eventSourceARN;window={start:"",end:""};state={};isFinalInvokeForWindow=!1;isWindowTerminatedEarly=!1;onComplete;constructor({shardId:t,eventSourceARN:e,tumblingWindowInSeconds:i}){this.shardId=t,this.eventSourceARN=e;let s=new Date;this.window.start=s.toISOString(),s.setSeconds(s.getSeconds()+i),this.window.end=s.toISOString(),setTimeout(()=>{this.isFinalInvokeForWindow=!0,this.onComplete?.()},i*1e3).unref()}setState(t){if(q(t))return this.isFinalInvokeForWindow||(this.state=t),this;throw new Error("Invalide state object")}getTimeWindowEvent(t){return{Records:t,window:this.window,state:this.state,shardId:this.shardId,eventSourceARN:this.eventSourceARN,isFinalInvokeForWindow:this.isFinalInvokeForWindow,isWindowTerminatedEarly:this.isWindowTerminatedEarly}}};var B=n=>new Promise(t=>setTimeout(t,n*1e3).unref()),U=86400*1e3,F=class extends Q{init(){}enable(){}disable(){}},v=class n extends ${constructor(e,i,s,o,r,a){super();this.getAwsServices=o;this.region=r;this.accountId=a;this.event=e,this.lambdaName=s,this.TableName=e.TableName,this.enabled=e.enabled,this.startingPosition=e.startingPosition,this.invoke=i,this.batchSize=e.batchSize,this.functionResponseType=e.functionResponseType,this.filterPatterns=e.filterPatterns,this.bisectBatchOnFunctionError=e.bisectBatchOnFunctionError,this.tumblingWindowInSeconds=e.tumblingWindowInSeconds,this.maximumRetryAttempts=e.maximumRetryAttempts,e.maximumRecordAgeInSeconds&&(this.maximumRecordAgeInSeconds=e.maximumRecordAgeInSeconds*1e3),!e.maximumBatchingWindow||Number.isNaN(e.maximumBatchingWindow)||e.maximumBatchingWindow==-1?this.maximumBatchingWindow=0:this.maximumBatchingWindow=e.maximumBatchingWindow*1e3;let c;if(Array.isArray(this.filterPatterns)){c=[];for(let d of this.filterPatterns)c.push({Pattern:JSON.stringify(d)});c.length||(c=void 0)}this.#n=new F({BatchSize:this.batchSize,Enabled:this.enabled,EventSourceArn:"",FunctionName:this.lambdaName,FilterCriteria:c?{Filters:c}:void 0}),typeof e.onFailureDestination=="string"&&(this.setFailureHandler(e.onFailureDestination),this.#n.config.DestinationConfig={OnFailure:{Destination:e.onFailureDestination}}),this.on("invoke",(d,m)=>{let u=this.#e.find(l=>!l.closed),R=u?.setRecord(d,m);if(!u||!R){let l=new y({batchSize:this.batchSize,maximumBatchingWindow:this.maximumBatchingWindow,maximumRecordAgeInSeconds:this.maximumRecordAgeInSeconds,tumbling:this.#a(m),onComplete:async(h,T)=>{n.invokationLog(this.TableName,this.lambdaName);let{error:f}=await this.callLambda(h);if(f&&await this.#i(h,m),T){let E=this.#e.findIndex(b=>b==h);if(E!=-1){let b=this.#t.findIndex(P=>P==this.#e[E].tumbling);b!=-1&&this.#t.splice(b,1),this.#e.splice(E,1)}}},onRecordExpire:this.failure?()=>{this.failure.report(this.createFailureMessage({totalRetry:1,condition:"RecordAgeExceeded",DDBStreamBatchInfo:m}))}:void 0});this.#e.push(l),l.setRecord(d,m)}})}lambdaName;TableName;enabled;startingPosition;invoke;batchSize;maximumBatchingWindow;maximumRetryAttempts;maximumRecordAgeInSeconds=U;bisectBatchOnFunctionError;tumblingWindowInSeconds;functionResponseType;filterPatterns;failure;event;#e=[];#t=[];#n;static invokationLog=process.env.SLS_DEGUG=="*"?(e,i)=>console.log(`\x1B[35mDynamoDB Stream:\x1B[0m \x1B[94m${e}\x1B[0m \x1B[35m|||/\x1B[0m \x1B[33m${i}\x1B[0m`):(e,i)=>{};setFailureHandler(e){let i=this,[s,o,r]=e.split(":");switch(r){case"sns":this.failure=new w(e,()=>{let a=i.getAwsServices().sns;if(!a)throw new Error("Dont know how to send SNS message without SNS client. Use SNS Plugin or provide SNS client in DynamoDB Plugin options. Make sure target SNS Topic exists.");return a});break;case"sqs":this.failure=new I(e,()=>{let a=i.getAwsServices().sqs;if(!a)throw new Error("Dont know how to send SQS message without SQS client. Use SQS Plugin or provide SQS client in DynamoDB Plugin options. Make sure target SQS Queue exists.");return a});break;case"s3":this.failure=new S(e,this.#n.UUID,()=>{let a=i.getAwsServices().s3;if(!a)throw new Error("Dont know how to store S3 Object without S3 client. Use S3 Plugin or provide S3 client in DynamoDB Plugin options. Make sure target S3 Bucket exists.");return a});break;default:throw new Error(`Invalid onFailureDestination ARN: ${e}. Service must be sns, sqs or s3`)}}setRecords=async(e,i)=>{let[s,o]=this.#n.filterRecords(e);for(let r of s)this.emit("invoke",r,i)};async callLambda(e,i){let s={},o=e.getStreamEvent(i);try{let r=await this.invoke(o,{kind:"ddb",event:this.event});if(this.tumblingWindowInSeconds&&e.tumbling.setState(r.state),this.functionResponseType=="ReportBatchItemFailures"){let a=C(e.records,r);if(a){let{success:c,failures:d}=a;for(let m of c)e.removeRecord(m);d.length&&(s.error=new Error("has failed items"))}else for(let c of e.records)e.removeRecord(c.eventID)}else for(let a of e.records)e.removeRecord(a.eventID)}catch(r){s.error=r}return s}async#i(e,i){if(this.maximumRetryAttempts)return this.#r(e,i);this.#s(e)}async#s(e){for(;e.records.length;)console.log(`\x1B[35mDynamoDB Stream retry until record expires!: ${this.TableName} |||/ ${this.lambdaName}\x1B[0m`),await this.callLambda(e),await B(.6)}async#r(e,i){let s=0;for(;s<=this.maximumRetryAttempts-1;){s++;let o=[e.records];if(this.bisectBatchOnFunctionError){let r=Math.ceil(e.records.length/2);o=this.#o(e.records,r)}console.log(`\x1B[35mDynamoDB Stream retry n\xB0${s}: ${this.TableName} |||/ ${this.lambdaName}\x1B[0m`);for(let r of o)await this.callLambda(e,r);if(!e.records.length)break;await B(.6)}if(this.failure&&e.records.length){let o={Records:e.records.slice()};for(let r of e.records)e.removeRecord(r.eventID);await this.failure.report(this.createFailureMessage({totalRetry:s,condition:"RetryAttemptsExhausted",DDBStreamBatchInfo:i,payload:o}))}}createFailureMessage({totalRetry:e,payload:i,DDBStreamBatchInfo:s,condition:o}){return{requestContext:{approximateInvokeCount:e,condition:o,functionArn:`arn:aws:lambda:${this.region}:${this.accountId}:function:${this.lambdaName}`,requestId:H()},timestamp:new Date().toISOString(),version:"1.0",responseContext:{executedVersion:"$LATEST",functionError:"Unhandled",statusCode:200},DDBStreamBatchInfo:s,payload:i?JSON.stringify(i):void 0}}#o(e,i){let s=[];for(let o=0;o<e.length;o+=i){let r=e.slice(o,o+i);s.push(r)}return s}#a=({shardId:e,streamArn:i})=>{if(this.tumblingWindowInSeconds){let s=this.#t.find(r=>!r.isFinalInvokeForWindow&&r.shardId==e);if(s)return s;let o=new x({shardId:e,eventSourceARN:i,tumblingWindowInSeconds:this.tumblingWindowInSeconds});return this.#t.push(o),o}}};var D=class{constructor(t,e,i,s){this.getAwsServices=e;this.region=i;this.accountId=s;this.#e=t,this.getSlsDeclarations(),this.getListenableTables()}subscribers=[];#e=[];listenableTables=[];getSlsDeclarations=()=>{for(let t of this.#e){let e=t.ddb;if(e.length)for(let i of e){let s=new v(i,t.invoke,t.name,this.getAwsServices,this.region,this.accountId);this.subscribers.push(s)}}};getListenableTables=()=>{for(let t of this.subscribers){let{TableName:e,enabled:i,startingPosition:s}=t,o=this.listenableTables.findIndex(r=>r.TableName==e);if(i){let r={TableName:e,startingPosition:s};s&&(r.startingPosition=s),o==-1?this.listenableTables.push(r):this.listenableTables[o]={...this.listenableTables[o],...r}}}};setRecords=({records:t,TableName:e,DDBStreamBatchInfo:i})=>{for(let s of this.subscribers)s.TableName==e&&s.setRecords(t,i)}};async function V(n){let{startProxy:t}=await import("exlo/dynamodbProxy"),[e,i]=await t(n);return e.unref(),i}var we=n=>{let t,e,i=[],s=async()=>{o.shared.isReady=!0;for(let r of i)await r()},o={name:"ddblocal-stream",shared:{isReady:!1,onReady:r=>{typeof r=="function"?i.push(r):console.warn("onReady callback must be a function")}},onInit:async function(){let{region:r,accountId:a}=this.getAwsProviderDefaults(),c={endpoint:"http://localhost:8000",region:r,credentials:{accessKeyId:"test",secretAccessKey:"test"}},d=n?.dynamoDbClientConfig??c,m;if(n?.proxy){let l={dynamoDbLocalClientConfig:d,region:r,accountId:a},h=!0;typeof n?.proxy=="object"&&(typeof n.proxy.setEndpointEnvVar=="boolean"&&(h=n.proxy.setEndpointEnvVar),typeof n.proxy.port=="number"&&(l.proxyPort=n.proxy.port),typeof n.proxy.maxShards=="number"&&(l.maxShards=n.proxy.maxShards),typeof n.proxy.inheritStreamsFromExistingTables=="boolean"&&(l.inheritStreamsFromExistingTables=n.proxy.inheritStreamsFromExistingTables));let f=`http://localhost:${await V(l)}`;this.setEndpoint("dynamodb_streams",f,h?"AWS_ENDPOINT_URL_DYNAMODB_STREAMS":void 0),o.shared.proxyEndpoint=f,m=f}else m=d.endpoint;let u=this;t=new D(this.lambdas,()=>{let l={...u.getServices()};return n?.services&&(n.services.s3&&(l.s3=n.services.s3),n.services.sns&&(l.sns=n.services.sns),n.services.sqs&&(l.sqs=n.services.sqs)),l},r,a);let R=L.join(A("exlo"),"dist/plugins/dynamodb/src/worker.mjs");if(e=new _(R,{name:"dynamodb-streams",workerData:{clientConfig:{...d,endpoint:m}}}),e.unref(),e.on("message",async l=>{l.channel=="ready"?await s():t.setRecords(l)}),typeof m=="string"){let l=typeof n?.setEndpointEnvVar=="boolean"?n.setEndpointEnvVar:!0;this.setEndpoint("dynamodb",m,l?"AWS_ENDPOINT_URL_DYNAMODB":void 0)}if(!u.getServices().dynamodb&&n?.setService){let{DynamoDB:l}=await import("@aws-sdk/client-dynamodb");u.setService("dynamodb",new l({...d,endpoint:m}))}},onKill(){e?.terminate()},server:{async onReady(r){return e?.postMessage({channel:"init",tables:t.listenableTables}),new Promise(a=>{i.unshift(a)})}}};return o};export{we as dynamoDbPlugin};
1
+ var A=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});import _ from"node:path";import{Worker as V}from"node:worker_threads";import C from"node:path";import{createRequire as W}from"node:module";var y=A,S={};function B(n){if(S[n])return S[n];if(typeof y=="function"&&typeof y.resolve=="function"){let e=C.resolve(y.resolve(n),"../../");return S[n]=e,e}y=W(new URL(".",import.meta.url));let t=C.resolve(y.resolve(n),"../../");return S[n]=t,t}import{randomUUID as $}from"node:crypto";import Q from"node:events";import{EventSourceMapping as U}from"exlo/internal/esm";var w=class{#e;#t;maximumRecordAgeInSeconds;onComplete;onRecordExpire;records=[];closed=!1;tumbling;#n={};#i;getStreamEvent;constructor({batchSize:t,maximumBatchingWindow:e,onComplete:i,onRecordExpire:s,maximumRecordAgeInSeconds:o,tumbling:r}){this.maximumRecordAgeInSeconds=o,this.#e=t,this.#t=e,this.onComplete=i,this.onRecordExpire=s,this.tumbling=r,this.tumbling?(this.getStreamEvent=a=>this.tumbling.getTimeWindowEvent(a??this.records),this.tumbling.onComplete||(this.tumbling.onComplete=()=>{this.onComplete(this,!0)})):this.getStreamEvent=a=>({Records:a??this.records})}#s(){this.records.length==this.#e&&(this.closed=!0,clearTimeout(this.#i),this.onComplete(this,!1))}setRecord(t,e){return this.closed?!1:(this.records.push(t),this.#s(),this.#r(t.eventID,e),this.records.length==1&&(this.#i=setTimeout(()=>{this.closed||(this.closed=!0,this.onComplete(this,!1))},this.#t).unref()),!0)}removeRecord(t){let e=this.records.findIndex(i=>i.eventID==t);e!=-1&&(clearTimeout(this.#n[t]),this.records.splice(e,1))}hasRecord(t){return this.records.find(e=>e.eventID==t)}#r(t,e){this.#n[t]=setTimeout(()=>{let i=this.records.findIndex(s=>s.eventID==t);i!=-1&&(this.records.splice(i,1),this.onRecordExpire&&this.onRecordExpire(e))},this.maximumRecordAgeInSeconds).unref()}};var M=n=>typeof n=="string"&&n.length,P=(n,t)=>{if(!(typeof t>"u"||t===null||typeof t=="object"&&(t.batchItemFailures===null||Array.isArray(t.batchItemFailures)&&!t.batchItemFailures.length))&&typeof t=="object"&&Array.isArray(t.batchItemFailures)){if(t.batchItemFailures.some(s=>!s.itemIdentifier||!M(s.itemIdentifier)||!n.find(o=>o.eventID==s.itemIdentifier)))throw new Error("ReportBatchItemFailures: complete failure.");let e=[],i=[];for(let s of n)t.batchItemFailures.find(r=>r.itemIdentifier==s.eventID)?i.push(s):e.push(s.eventID);return{success:e,failures:i}}};import{randomUUID as q}from"node:crypto";var I=class{constructor(t,e,i){this.bucketArn=t;this.esmId=e;this.getS3Client=i}#e;get client(){return this.#e||(this.#e=this.getS3Client()),this.#e}#t;get bucketName(){return this.#t||(this.#t=this.bucketArn.split(":")[5]),this.#t}createObjectKey(t){let[e,i]=new Date().toISOString().split("T"),[s,o,r]=e.split("-"),[a,m,l]=i.split(":");return`aws/lambda/${this.esmId}/${t}/${s}/${o}/${r}/${e}T${a}.${m}.${l}-${q()}`}async report(t){await this.client.putObject({Bucket:this.bucketName,Key:this.createObjectKey(t.DDBStreamBatchInfo.shardId),Body:JSON.stringify(t)})}};var x=class{constructor(t,e){this.topicArn=t;this.getSnsClient=e}#e;get client(){return this.#e||(this.#e=this.getSnsClient()),this.#e}async report(t){let e={...t,payload:void 0};await this.send(e)}async send(t){return this.client.publish({TopicArn:this.topicArn,Message:JSON.stringify(t)})}};var v=class{constructor(t,e){this.queueArn=t;this.getSqsClient=e}#e;#t;async getQueueUrl(){if(!this.#e){let e=this.queueArn.split(":")[5],i=await this.client.getQueueUrl({QueueName:e});this.#e=i.QueueUrl}return this.#e}get client(){return this.#t||(this.#t=this.getSqsClient()),this.#t}async report(t){let e={...t,payload:void 0};await this.send(e)}async send(t){return this.client.sendMessage({QueueUrl:await this.getQueueUrl(),MessageBody:JSON.stringify(t)})}};var H=n=>n&&!Array.isArray(n)&&Object.keys(n).length,D=class{shardId;eventSourceARN;window={start:"",end:""};state={};isFinalInvokeForWindow=!1;isWindowTerminatedEarly=!1;onComplete;constructor({shardId:t,eventSourceARN:e,tumblingWindowInSeconds:i}){this.shardId=t,this.eventSourceARN=e;let s=new Date;this.window.start=s.toISOString(),s.setSeconds(s.getSeconds()+i),this.window.end=s.toISOString(),setTimeout(()=>{this.isFinalInvokeForWindow=!0,this.onComplete?.()},i*1e3).unref()}setState(t){if(H(t))return this.isFinalInvokeForWindow||(this.state=t),this;throw new Error("Invalide state object")}getTimeWindowEvent(t){return{Records:t,window:this.window,state:this.state,shardId:this.shardId,eventSourceARN:this.eventSourceARN,isFinalInvokeForWindow:this.isFinalInvokeForWindow,isWindowTerminatedEarly:this.isWindowTerminatedEarly}}};var O=n=>new Promise(t=>setTimeout(t,n*1e3).unref()),L=86400*1e3,N=class extends U{init(){}enable(){}disable(){}},R=class n extends Q{constructor(e,i,s,o,r,a){super();this.getAwsServices=o;this.region=r;this.accountId=a;this.event=e,this.lambdaName=s,this.TableName=e.TableName,this.enabled=e.enabled,this.startingPosition=e.startingPosition,this.invoke=i,this.batchSize=e.batchSize,this.functionResponseType=e.functionResponseType,this.filterPatterns=e.filterPatterns,this.bisectBatchOnFunctionError=e.bisectBatchOnFunctionError,this.tumblingWindowInSeconds=e.tumblingWindowInSeconds,this.maximumRetryAttempts=e.maximumRetryAttempts,e.maximumRecordAgeInSeconds&&(this.maximumRecordAgeInSeconds=e.maximumRecordAgeInSeconds*1e3),!e.maximumBatchingWindow||Number.isNaN(e.maximumBatchingWindow)||e.maximumBatchingWindow==-1?this.maximumBatchingWindow=0:this.maximumBatchingWindow=e.maximumBatchingWindow*1e3;let m;if(Array.isArray(this.filterPatterns)){m=[];for(let l of this.filterPatterns)m.push({Pattern:JSON.stringify(l)});m.length||(m=void 0)}this.#n=new N({BatchSize:this.batchSize,Enabled:this.enabled,EventSourceArn:"",FunctionName:this.lambdaName,FilterCriteria:m?{Filters:m}:void 0}),typeof e.onFailureDestination=="string"&&(this.setFailureHandler(e.onFailureDestination),this.#n.config.DestinationConfig={OnFailure:{Destination:e.onFailureDestination}}),this.on("invoke",(l,u)=>{let g=this.#e.find(d=>!d.closed),f=g?.setRecord(l,u);if(!g||!f){let d=new w({batchSize:this.batchSize,maximumBatchingWindow:this.maximumBatchingWindow,maximumRecordAgeInSeconds:this.maximumRecordAgeInSeconds,tumbling:this.#a(u),onComplete:async(h,F)=>{n.invokationLog(this.TableName,this.lambdaName);let{error:c}=await this.callLambda(h);if(c&&await this.#i(h,u),F){let p=this.#e.findIndex(b=>b==h);if(p!=-1){let b=this.#t.findIndex(T=>T==this.#e[p].tumbling);b!=-1&&this.#t.splice(b,1),this.#e.splice(p,1)}}},onRecordExpire:this.failure?()=>{this.failure.report(this.createFailureMessage({totalRetry:1,condition:"RecordAgeExceeded",DDBStreamBatchInfo:u}))}:void 0});this.#e.push(d),d.setRecord(l,u)}})}lambdaName;TableName;enabled;startingPosition;invoke;batchSize;maximumBatchingWindow;maximumRetryAttempts;maximumRecordAgeInSeconds=L;bisectBatchOnFunctionError;tumblingWindowInSeconds;functionResponseType;filterPatterns;failure;event;#e=[];#t=[];#n;static invokationLog=process.env.SLS_DEGUG=="*"?(e,i)=>console.log(`\x1B[35mDynamoDB Stream:\x1B[0m \x1B[94m${e}\x1B[0m \x1B[35m|||/\x1B[0m \x1B[33m${i}\x1B[0m`):(e,i)=>{};setFailureHandler(e){let i=this,[s,o,r]=e.split(":");switch(r){case"sns":this.failure=new x(e,()=>{let a=i.getAwsServices().sns;if(!a)throw new Error("Dont know how to send SNS message without SNS client. Use SNS Plugin or provide SNS client in DynamoDB Plugin options. Make sure target SNS Topic exists.");return a});break;case"sqs":this.failure=new v(e,()=>{let a=i.getAwsServices().sqs;if(!a)throw new Error("Dont know how to send SQS message without SQS client. Use SQS Plugin or provide SQS client in DynamoDB Plugin options. Make sure target SQS Queue exists.");return a});break;case"s3":this.failure=new I(e,this.#n.UUID,()=>{let a=i.getAwsServices().s3;if(!a)throw new Error("Dont know how to store S3 Object without S3 client. Use S3 Plugin or provide S3 client in DynamoDB Plugin options. Make sure target S3 Bucket exists.");return a});break;default:throw new Error(`Invalid onFailureDestination ARN: ${e}. Service must be sns, sqs or s3`)}}setRecords=async(e,i)=>{let[s,o]=this.#n.filterRecords(e);for(let r of s)this.emit("invoke",r,i)};async callLambda(e,i){let s={},o=e.getStreamEvent(i);try{let r=await this.invoke(o,{kind:"ddb",event:this.event});if(this.tumblingWindowInSeconds&&e.tumbling.setState(r.state),this.functionResponseType=="ReportBatchItemFailures"){let a=P(e.records,r);if(a){let{success:m,failures:l}=a;for(let u of m)e.removeRecord(u);l.length&&(s.error=new Error("has failed items"))}else for(let m of e.records)e.removeRecord(m.eventID)}else for(let a of e.records)e.removeRecord(a.eventID)}catch(r){s.error=r}return s}async#i(e,i){if(this.maximumRetryAttempts)return this.#r(e,i);this.#s(e)}async#s(e){for(;e.records.length;)console.log(`\x1B[35mDynamoDB Stream retry until record expires!: ${this.TableName} |||/ ${this.lambdaName}\x1B[0m`),await this.callLambda(e),await O(.6)}async#r(e,i){let s=0;for(;s<=this.maximumRetryAttempts-1;){s++;let o=[e.records];if(this.bisectBatchOnFunctionError){let r=Math.ceil(e.records.length/2);o=this.#o(e.records,r)}console.log(`\x1B[35mDynamoDB Stream retry n\xB0${s}: ${this.TableName} |||/ ${this.lambdaName}\x1B[0m`);for(let r of o)await this.callLambda(e,r);if(!e.records.length)break;await O(.6)}if(this.failure&&e.records.length){let o={Records:e.records.slice()};for(let r of e.records)e.removeRecord(r.eventID);await this.failure.report(this.createFailureMessage({totalRetry:s,condition:"RetryAttemptsExhausted",DDBStreamBatchInfo:i,payload:o}))}}createFailureMessage({totalRetry:e,payload:i,DDBStreamBatchInfo:s,condition:o}){return{requestContext:{approximateInvokeCount:e,condition:o,functionArn:`arn:aws:lambda:${this.region}:${this.accountId}:function:${this.lambdaName}`,requestId:$()},timestamp:new Date().toISOString(),version:"1.0",responseContext:{executedVersion:"$LATEST",functionError:"Unhandled",statusCode:200},DDBStreamBatchInfo:s,payload:i?JSON.stringify(i):void 0}}#o(e,i){let s=[];for(let o=0;o<e.length;o+=i){let r=e.slice(o,o+i);s.push(r)}return s}#a=({shardId:e,streamArn:i})=>{if(this.tumblingWindowInSeconds){let s=this.#t.find(r=>!r.isFinalInvokeForWindow&&r.shardId==e);if(s)return s;let o=new D({shardId:e,eventSourceARN:i,tumblingWindowInSeconds:this.tumblingWindowInSeconds});return this.#t.push(o),o}}};var E=class{constructor(t,e,i,s){this.getAwsServices=e;this.region=i;this.accountId=s;this.#e=t,this.getSlsDeclarations(),this.getListenableTables()}subscribers=[];#e=[];listenableTables=[];getSlsDeclarations=()=>{for(let t of this.#e){let e=t.ddb;if(e.length)for(let i of e){let s=new R(i,t.invoke,t.name,this.getAwsServices,this.region,this.accountId);this.subscribers.push(s)}}};getListenableTables=()=>{for(let t of this.subscribers){let{TableName:e,enabled:i,startingPosition:s}=t,o=this.listenableTables.findIndex(r=>r.TableName==e);if(i){let r={TableName:e,startingPosition:s};s&&(r.startingPosition=s),o==-1?this.listenableTables.push(r):this.listenableTables[o]={...this.listenableTables[o],...r}}}};setRecords=({records:t,TableName:e,DDBStreamBatchInfo:i})=>{for(let s of this.subscribers)s.TableName==e&&s.setRecords(t,i)}};async function j(n){let{startProxy:t}=await import("exlo/dynamodbProxy"),[e,i]=await t(n);return e.unref(),[e,i]}var Ie=n=>{let t,e,i,s=[],o=async()=>{m.shared.isReady=!0;for(let l of s)await l()},r,a=new Promise(l=>{r=l}),m={name:"ddblocal-stream",shared:{isReady:!1,onReady:async l=>{typeof l=="function"?m.shared.isReady?await l():s.push(l):console.warn("onReady callback must be a function")}},onInit:async function(){let{region:l,accountId:u}=this.getAwsProviderDefaults(),g={endpoint:"http://localhost:8000",region:l,credentials:{accessKeyId:"test",secretAccessKey:"test"}},f=n?.dynamoDbClientConfig??g,d;if(n?.proxy){let c={dynamoDbLocalClientConfig:f,region:l,accountId:u},p=!0;typeof n?.proxy=="object"&&(typeof n.proxy.setEndpointEnvVar=="boolean"&&(p=n.proxy.setEndpointEnvVar),typeof n.proxy.port=="number"&&(c.proxyPort=n.proxy.port),typeof n.proxy.maxShards=="number"&&(c.maxShards=n.proxy.maxShards),typeof n.proxy.inheritStreamsFromExistingTables=="boolean"&&(c.inheritStreamsFromExistingTables=n.proxy.inheritStreamsFromExistingTables));let[b,T]=await j(c);i=b;let k=`http://localhost:${T}`;this.setEndpoint("dynamodb_streams",k,p?"AWS_ENDPOINT_URL_DYNAMODB_STREAMS":void 0),m.shared.proxyEndpoint=k,d=k}else d=f.endpoint;let h=this;t=new E(this.lambdas,()=>{let c={...h.getServices()};return n?.services&&(n.services.s3&&(c.s3=n.services.s3),n.services.sns&&(c.sns=n.services.sns),n.services.sqs&&(c.sqs=n.services.sqs)),c},l,u);let F=_.join(B("exlo"),"dist/plugins/dynamodb/src/worker.mjs");if(e=new V(F,{name:"dynamodb-streams",workerData:{clientConfig:{...f,endpoint:d}}}),e.unref(),e.on("message",async c=>{c.channel=="ready"?e.postMessage({channel:"start"}):c.channel=="started"?(await o(),r()):t.setRecords(c)}),typeof d=="string"){let c=typeof n?.setEndpointEnvVar=="boolean"?n.setEndpointEnvVar:!0;this.setEndpoint("dynamodb",d,c?"AWS_ENDPOINT_URL_DYNAMODB":void 0)}if(!h.getServices().dynamodb&&n?.setService){let{DynamoDB:c}=await import("@aws-sdk/client-dynamodb");h.setService("dynamodb",new c({...f,endpoint:d}))}e.postMessage({channel:"init",tables:t.listenableTables})},onKill(){e?.terminate(),i?.terminate()},server:{async onReady(){await a}}};return m};export{Ie as dynamoDbPlugin};
@@ -13,6 +13,7 @@ export declare class DynamoStream extends EventEmitter {
13
13
  }, clientConfig: DynamoDBClientConfig);
14
14
  private runningShards;
15
15
  private completedShards;
16
+ verifyStream(): Promise<void>;
16
17
  init(): Promise<void>;
17
18
  private start;
18
19
  getAvailableStream(): Promise<import("@aws-sdk/client-dynamodb-streams").StreamDescription | undefined>;
@@ -1 +1 @@
1
- import{parentPort as m,workerData as w}from"node:worker_threads";import u from"node:events";import{DynamoDBClient as g,waitUntilTableExists as y}from"@aws-sdk/client-dynamodb";import{DynamoDBStreams as p,ExpiredIteratorException as f,TrimmedDataAccessException as b}from"@aws-sdk/client-dynamodb-streams";var c=class extends u{cli;streamCli;TableName;#e=!0;ShardIteratorType;constructor(t,e){super(),this.cli=new g(e),this.streamCli=new p(e),this.TableName=t.TableName,this.ShardIteratorType=t.startingPosition}runningShards=new Set;completedShards=new Set;async init(){await this.#t(),await this.start();let t=0,e=async()=>{try{let a=await this.getAvailableStream();if(!a?.Shards||!a.StreamArn)return;let r=0;for(let s of a.Shards)this.runningShards.has(s.ShardId)||this.completedShards.has(s.ShardId)||setTimeout(async()=>{try{await this.watch(s,a.StreamArn),this.runningShards.add(s.ShardId),r++}catch(n){console.error(n)}},50);t+=r}catch(a){console.error(a)}setTimeout(e,1e3)};await e()}async#t(){try{await y({client:this.cli,maxWaitTime:25},{TableName:this.TableName}),console.log(`\u2705 Successfully connected to Table "${this.TableName}"`)}catch{console.log(`Could not connect to table ${this.TableName}`)}}async start(){}async getAvailableStream(){let{Streams:t}=await this.streamCli.listStreams({TableName:this.TableName});if(!t)return;let e=await Promise.all(t.filter(r=>r.StreamArn).map(r=>this.describeStream(r.StreamArn)));if(!e)return;let a=e.filter(r=>r?.StreamStatus=="ENABLED"&&r.Shards?.length);if(a?.length)return a[0]}async describeStream(t){let{StreamDescription:e}=await this.streamCli.describeStream({StreamArn:t});return e}async getShardIterator(t,e){return(await this.streamCli.getShardIterator({StreamArn:e,ShardId:t.ShardId,ShardIteratorType:this.ShardIteratorType})).ShardIterator}getNextWatcherTimeout(t){return t<=20?250:t>=60?6e3:t/10*1e3}totalConsumedRecords=0;async watch(t,e){let a=t.SequenceNumberRange?.StartingSequenceNumber,r=t.SequenceNumberRange?.EndingSequenceNumber,s=await this.getShardIterator(t,e),n=0,l=async()=>{try{let{NextShardIterator:i,Records:o}=await this.streamCli.getRecords({ShardIterator:s});if(o?.length){this.totalConsumedRecords+=o.length,n=0;let h=new Date().toISOString(),S={shardId:t.ShardId,startSequenceNumber:a,endSequenceNumber:r,approximateArrivalOfFirstRecord:h,approximateArrivalOfLastRecord:h,batchSize:o.length,streamArn:e};this.emit("records",o,S)}else n++;if(i)s=i;else{console.log("Total consumed in this Shard",this.TableName,this.totalConsumedRecords),this.completedShards.add(t.ShardId),s=void 0,await this.start();return}}catch(i){if(n=0,i instanceof f){await this.start();return}i instanceof b?s=await this.getShardIterator(t,e):console.log(i)}setTimeout(l,this.getNextWatcherTimeout(n))};await l()}};var{clientConfig:T}=w,I={endpoint:"http://127.0.0.1:8000",region:"us-east-1",credentials:{accessKeyId:"test",secretAccessKey:"test"}};m.on("message",async d=>{let{channel:t,tables:e}=d;if(t=="init"){for(let a of e)try{let r=new c(a,T??I);r.on("records",(s,n)=>{m.postMessage({records:s,DDBStreamBatchInfo:n,TableName:r.TableName})}),await r.init()}catch(r){console.log(r)}m.postMessage({channel:"ready"})}});
1
+ import{parentPort as m,workerData as w}from"node:worker_threads";import g from"node:events";import{DynamoDBClient as p,waitUntilTableExists as S}from"@aws-sdk/client-dynamodb";import{DynamoDBStreams as f,ExpiredIteratorException as b,TrimmedDataAccessException as T}from"@aws-sdk/client-dynamodb-streams";var l=class extends g{cli;streamCli;TableName;#t=!0;ShardIteratorType;constructor(t,e){super(),this.cli=new p(e),this.streamCli=new f(e),this.TableName=t.TableName,this.ShardIteratorType=t.startingPosition}runningShards=new Set;completedShards=new Set;async verifyStream(){try{await S({client:this.cli,maxWaitTime:25},{TableName:this.TableName}),console.log(`\u2705 Successfully connected to Table "${this.TableName}"`)}catch{throw new Error(`Could not connect to table ${this.TableName}`)}}async init(){await this.start();let t=0,e=async()=>{try{let a=await this.getAvailableStream();if(!a?.Shards||!a.StreamArn)return;let s=0;for(let r of a.Shards)this.runningShards.has(r.ShardId)||this.completedShards.has(r.ShardId)||setTimeout(async()=>{try{await this.watch(r,a.StreamArn),this.runningShards.add(r.ShardId),s++}catch(n){console.error(n)}},50);t+=s}catch(a){console.error(a)}setTimeout(e,1e3)};await e()}async#e(){try{await S({client:this.cli,maxWaitTime:25},{TableName:this.TableName}),console.log(`\u2705 Successfully connected to Table "${this.TableName}"`)}catch{console.log(`Could not connect to table ${this.TableName}`)}}async start(){}async getAvailableStream(){let{Streams:t}=await this.streamCli.listStreams({TableName:this.TableName});if(!t)return;let e=await Promise.all(t.filter(s=>s.StreamArn).map(s=>this.describeStream(s.StreamArn)));if(!e)return;let a=e.filter(s=>s?.StreamStatus=="ENABLED"&&s.Shards?.length);if(a?.length)return a[0]}async describeStream(t){let{StreamDescription:e}=await this.streamCli.describeStream({StreamArn:t});return e}async getShardIterator(t,e){return(await this.streamCli.getShardIterator({StreamArn:e,ShardId:t.ShardId,ShardIteratorType:this.ShardIteratorType})).ShardIterator}getNextWatcherTimeout(t){return t<=20?250:t>=60?6e3:t/10*1e3}totalConsumedRecords=0;async watch(t,e){let a=t.SequenceNumberRange?.StartingSequenceNumber,s=t.SequenceNumberRange?.EndingSequenceNumber,r=await this.getShardIterator(t,e),n=0,o=async()=>{try{let{NextShardIterator:i,Records:c}=await this.streamCli.getRecords({ShardIterator:r});if(c?.length){this.totalConsumedRecords+=c.length,n=0;let h=new Date().toISOString(),y={shardId:t.ShardId,startSequenceNumber:a,endSequenceNumber:s,approximateArrivalOfFirstRecord:h,approximateArrivalOfLastRecord:h,batchSize:c.length,streamArn:e};this.emit("records",c,y)}else n++;if(i)r=i;else{console.log("Total consumed in this Shard",this.TableName,this.totalConsumedRecords),this.completedShards.add(t.ShardId),r=void 0,await this.start();return}}catch(i){if(n=0,i instanceof b){await this.start();return}i instanceof T?r=await this.getShardIterator(t,e):console.log(i)}setTimeout(o,this.getNextWatcherTimeout(n))};await o()}};var{clientConfig:D}=w,I={endpoint:"http://127.0.0.1:8000",region:"us-east-1",credentials:{accessKeyId:"test",secretAccessKey:"test"}},u=[];m.on("message",async d=>{let{channel:t,tables:e}=d;if(t=="init"){let a=0;for(let s of e)try{let r=new l(s,D??I);r.on("records",(n,o)=>{m.postMessage({records:n,DDBStreamBatchInfo:o,TableName:r.TableName})}),await r.verifyStream(),a++,u.push(r)}catch(r){console.log(r)}a==e.length&&m.postMessage({channel:"ready"})}else if(t=="start"){for(let a of u)await a.init();m.postMessage({channel:"started"})}});
@@ -1 +1 @@
1
- import{randomUUID as oe}from"node:crypto";import{randomUUID as $}from"node:crypto";import{createServer as te}from"node:http";import{EventSourceMapping as Ae}from"exlo/internal/esm";import{EventSourceMapping as pe}from"exlo/internal/esm";var S=a=>Object.prototype.toString.call(a)=="[object Object]";import _ from"ajv";import A from"ajv-formats";var g=new _({discriminator:!1,useDefaults:!1,validateFormats:!0,jtd:!1,strictRequired:!0,allowUnionTypes:!0,unevaluated:!1,formats:{"date-time":A.get("date-time"),email:A.get("email"),hostname:A.get("hostname"),ipv4:A.get("ipv4"),ipv6:A.get("ipv6"),uri:A.get("uri")}});g.removeKeyword("if");g.removeKeyword("else");g.removeKeyword("then");g.removeKeyword("const");g.removeKeyword("contains");g.removeKeyword("contentEncoding");g.removeKeyword("contentMediaType");g.removeKeyword("contentSchema");g.removeKeyword("nullable");g.removeKeyword("prefixItems");g.removeKeyword("example");g.removeKeyword("examples");g.removeKeyword("deprecated");g.removeKeyword("readOnly");g.removeKeyword("writeOnly");g.removeKeyword("$comment");g.removeKeyword("$defs");g.removeKeyword("exclusiveMinimum");g.removeKeyword("exclusiveMaximum");g.removeKeyword("propertyNames");g.removeKeyword("$id");g.removeKeyword("id");g.addKeyword({keyword:["exclusiveMaximum","exclusiveMinimum"],type:["number"],schemaType:["number"],$data:!0,code(a){if(a.it.rootId!="http://json-schema.org/draft-07/schema#")throw new Error(`unsupported keyword "${a.keyword}"`)}});g.addKeyword({keyword:"propertyNames",type:["object"],schemaType:["object","boolean"],code(a){if(a.it.rootId!="http://json-schema.org/draft-07/schema#")throw new Error('unsupported keyword "propertyNames"')}});g.addKeyword({keyword:"$id",type:["object"],schemaType:["string"],code(a){if(a.it.rootId!="http://json-schema.org/draft-07/schema#")throw new Error('unsupported keyword "$id"')}});g.addKeyword({keyword:"id",schemaType:["string"]});var F=a=>{if(a&&typeof a=="object"&&!Array.isArray(a)){let e;return a.$schema?e={...a,$schema:"http://json-schema.org/draft-07/schema#"}:e=a,g.compile(e)}throw new Error("API Gateway validator Schema must be an object")};var w=class extends Error{res;constructor(e){super("RestApiError"),this.res=e}setHeaders(e){this.res.headers.Connection="keep-alive",this.res.headers["x-amzn-RequestId"]=e,this.res.body?this.res.headers["Content-Length"]=Buffer.byteLength(this.res.body,"utf-8").toString():this.res.headers["Content-Length"]="0"}completeRequest(e,t){e.shouldKeepAlive=!1,this.setHeaders(t),e.writeHead(this.res.statusCode,this.res.statusText,this.res.headers),e.end(this.res.body)}},P=class extends w{constructor(){super({statusCode:405,statusText:"Method Not Allowed",headers:{"Content-Type":"null"},body:"Unsupported HTTP method"})}},M=class extends w{constructor(){super({statusCode:415,statusText:"Unsupported Media Type",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"BadRequestException"},body:'{"message": "Unsupported Media Type"}'})}},I=class extends w{constructor(e){super({statusCode:400,statusText:"Bad Request",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"BadRequestException"},body:e})}},T=class extends w{constructor(){super({statusCode:403,statusText:"Forbidden",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"ForbiddenException"},body:'{"message": "Forbidden"}'})}},E=class extends w{constructor(){super({statusCode:403,statusText:"Forbidden",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"MissingAuthenticationTokenException"},body:'{"message": "Missing Authentication Token"}'})}},f=class extends w{constructor(e=502){super({statusCode:e,statusText:"BadGateway",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"InternalServerErrorException"},body:'{"message": "Internal server error"}'})}};function N(a,e,t){let n=e.split("/").filter(Boolean),c={};if(!n.length){let i;return t[a]?i=a:t.ANY&&(i="ANY"),i?{matchedMethod:i,pathParams:c,resource:t,resourcePath:"/"}:void 0}let d=q(a,n,t,c,"");if(d?.resource)return{matchedMethod:d.matchedMethod,pathParams:c,resource:d.resource,resourcePath:d?.resourcePath}}function q(a,e,t,n,c){if(!e.length)return;let d=`/${e[0]}`;if(d in t){let r=t[d],o=`${c}${d}`;if(e.length===1){let u=r[a]?a:r.ANY?"ANY":null;if(u)return{matchedMethod:u,resource:r,resourcePath:o}}return q(a,e.slice(1),r,n,o)}let i=Object.keys(t).find(r=>r.startsWith("/{")&&!r.endsWith("+}"));if(i){let r=i.slice(2,-1);n[r]=encodeURIComponent(e[0]);let o=t[i],u=`${c}${i}`;if(e.length===1){let h=o[a]?a:o.ANY?"ANY":void 0;if(h)return{matchedMethod:h,resource:o,resourcePath:u}}return q(a,e.slice(1),o,n,u)}let s=Object.keys(t).find(r=>r.startsWith("/{")&&r.endsWith("+}"));if(s){let r=s.match(/\{(.+?)\+\}/)?.[1];r&&(n[r]=e.map(encodeURIComponent).join("/"));let o=t[s],u=`${c}${s}`;if(o[a])return{matchedMethod:a,resource:o,resourcePath:u};if(o.ANY)return{matchedMethod:"ANY",resource:o,resourcePath:u}}}import{getRandomValues as k}from"node:crypto";import{networkInterfaces as B}from"node:os";var C="abcdefghijklmnopqrstuvwxyz0123456789",H=`ABCDEFGHIJKLMNOPQRSTUVWXYZ${C}`,J=40;function O(){let a="",e=new Uint8Array(45);k(e);for(let t=0;t<J;t++)a+=H[e[t]%H.length];return a}var X=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function j(){let a=new Date,e=String(a.getUTCDate()).padStart(2,"0"),t=X[a.getUTCMonth()],n=a.getUTCFullYear(),c=String(a.getUTCHours()).padStart(2,"0"),d=String(a.getUTCMinutes()).padStart(2,"0"),i=String(a.getUTCSeconds()).padStart(2,"0");return{requestTime:`${e}/${t}/${n}:${c}:${d}:${i} +0000`,requestTimeEpoch:a.getTime()}}function U(){let a="127.0.0.1";if(B){let e=Object.values(B()).reduce((t,n)=>(t.push(...n),t),[])?.filter(t=>!t.internal&&t.family==="IPv4").find(Boolean)?.address;e&&(a=e)}return a}function L(){let a=Math.floor(Date.now()/1e3).toString(16),e=k(new Uint8Array(12)).reduce((t,n)=>t+n.toString(16).padStart(2,"0"),"");return`1-${a}-${e}`}function Z(){return k(new Uint8Array(8)).reduce((a,e)=>a+e.toString(16).padStart(2,"0"),"")}function ee(a){let e=a.split("-");if(e.length===3){let t=(BigInt(`0x${e[2]}`)+BigInt(2)).toString(16).padStart(24,"0");return`${e[0]}-${e[1]}-${t}`}return a}function D(a){if(!a)return`Root=${L()}`;let e=a.match(/Root=([^\s;]+)/),t=e?e[1]:L(),n=ee(t);return`Self=1-${Z()};Root=${n}`}function Q(){let a="",e=new Uint8Array(6);crypto.getRandomValues(e);for(let t=0;t<6;t++)a+=C[e[t]%C.length];return a}function V(){let a=new Uint8Array(8);return crypto.getRandomValues(a),Buffer.from(String.fromCharCode(...a),"utf-8").toString("base64url")}var se=["connection","content-md5","content-length","expect","max-forwards","proxy-authenticate","server","te","host","trailer","transfer-encoding","upgrade","www-authenticate","x-forwarded-port","x-forwarded-proto"],re=["AWS4-HMAC-SHA256","Credential","SignedHeaders","Signature"],K=["DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT"],G=["authorization","connection","content-md5","max-forwards","user-agent","www-authenticate","date","server","content-length"],z=["expect","host","proxy-authenticate","te","transfer-encoding","trailer","upgrade","via"],ne=/^[a-z0-9-.+#!{}_'`]+\/([a-z0-9-.+#!{}_'`]+|\*{1})$/,v=class a{constructor({id:e,ip:t,getLambdas:n,port:c,stage:d,resources:i,requireStagePrefixInRequestPath:s,models:r,binaryMediaTypes:o,resolveLambdaNameFromArn:u},h){this.logger=h;if(e)this.id=e;else{let l=$();this.id=l.slice(0,l.indexOf("-"))}if(a.servers.find(l=>l.id==this.id))throw new Error(`[REST API Gateway] An API with the same id (${this.id}) already exists.`);this.ip=t,this.port=c,this.stage=d??"dev",this.getLambdas=n,this.resources=i,this.resolveLambdaNameFromArn=u,this.deploymentId=Q(),r&&typeof r=="object"?this.models=r:this.models={},typeof s=="boolean"&&(this.requireStagePrefixInRequestPath=s),Array.isArray(o)&&this.compileBinaryTypes(o),this.compileResources(this.resources)}static servers=[];server;id;ip;port;stage;requireStagePrefixInRequestPath=!0;deploymentId;apiKeys=[];resolveLambdaNameFromArn;binaryMediaTypes=[];getLambdas;resources;compiledResources={};sockets=[];models;compileBinaryTypes(e){for(let t of e){let n=t.toLowerCase();if(n=="*/*"){this.binaryMediaTypes.push(/^.*$/);continue}if(ne.test(n))if(n.endsWith("/*")){let c=new RegExp(`^${n.slice(0,-1)}[a-z0-9-_.$~+#'{}\`!*]+$`);this.binaryMediaTypes.push(c)}else{let c=new RegExp(`^${n}$`);this.binaryMediaTypes.push(c)}}}compileResources(e){for(let[t,n]of Object.entries(e))t.startsWith("/")&&this.compileResources(n);if(!e.resourceId){let t=$();e.resourceId=t.slice(0,t.indexOf("-"))}this.compiledResources[e.resourceId]={};for(let t of K){let n=e[t];if(!n)continue;let c=[],d=[],i={};if(n.request){if(n.request.queryStringParameters)for(let[s,r]of Object.entries(n.request.queryStringParameters))r.required&&c.push(s);if(n.request.headers)for(let[s,r]of Object.entries(n.request.headers))r.required&&d.push(s);if(n.request.body)for(let[s,r]of Object.entries(n.request.body)){let o=this.models[r];i[s]=F(o)}}this.compiledResources[e.resourceId][t]={requiredQueryParams:c,requiredHeaders:d,body:i}}}async start(){this.server=te((t,n)=>{this.requestHandler(t,n)}),await new Promise(t=>{this.server.listen(this.port,t)}),a.servers.push(this),this.port=this.server.address().port,this.server.on("connection",t=>{t.on("close",()=>{let n=this.sockets.findIndex(c=>c==t);n!=-1&&this.sockets.splice(n,1)}),this.sockets.push(t)});let e=this.requireStagePrefixInRequestPath?`/${this.stage}`:"";this.printPaths(this.resources,e)}printPaths(e,t){let n=Object.keys(e);for(let c of n){let d=e[c];if(!c.startsWith("/"))continue;let i=Object.keys(d).filter(r=>K.includes(r)),s=`${t}${c}`;i.length&&this.logger.CYAN(`${i.join(", ")} http://localhost:${this.port}${s}`),this.printPaths(d,s)}}dropConnections(){for(let e of this.sockets)e.writable&&e.destroy()}stop(){this.server?.close()}findResourceFromRequest(e){let{method:t,url:n}=e,c=n.indexOf("?");c==-1&&(c=void 0);let d=n.slice(0,c),i=d;for(;i!="/"&&i.endsWith("/");)i=i.slice(0,-1);let s;c&&(s=n.slice(c));let r=i;if(this.requireStagePrefixInRequestPath){if(!i.startsWith(`/${this.stage}`))return;r=i.slice(`/${this.stage}`.length)}let o=N(t,r,this.resources);if(o)return{resource:o.resource,pathname:i,requestPath:d,rawQueryStringParameters:s,matchedMethod:o.matchedMethod,requestMethod:t,pathParams:o.pathParams,resourcePath:o.resourcePath}}static async collectBody(e,t){let n=Buffer.alloc(0);return e.on("data",c=>{n=Buffer.concat([n,c])}),new Promise(c=>{e.on("end",async()=>{c(n?n.toString(t?"base64":"utf-8"):null)})})}static getQueryStrings(e){let t=null,n=null;if(!e)return{queryStringParameters:n,multiValueQueryStringParameters:t};let c=e.slice(1).split("&"),d={};for(let s of c){let[r,o]=s.split("="),u=r.split(";"),h=o?.split(";")??[""];if(u.length==1&&h.length==1){let R=decodeURIComponent(u[0]),b=decodeURIComponent(h[0]);R in d?d[R].push(b):d[R]=[b];continue}let l=u.slice(0,-1),p=u[u.length-1],m=decodeURIComponent(p),y=decodeURIComponent(h[0]);if(m in d?d[m].push(y):d[m]=[y],h.length>1){let R=h.slice(1);for(let b of R)l.push(b)}for(let R of l){let b=decodeURIComponent(R);b in d?d[b].push(""):d[b]=[""]}}let i=Object.keys(d);if(i.length){n={},t={};for(let s of i.sort()){let r=d[s];t[s]=r,n[s]=r[r.length-1]}}return{queryStringParameters:n,multiValueQueryStringParameters:t}}static shouldDropHeader(e){return!!se.includes(e)}static parseMultiValueHeaders(e){let t={},n={},c={};for(let i=0;i<e.length;i=i+2){let s=e[i],r=s.toLowerCase(),o=e[i+1]?.trim()??"";if(o=Buffer.from(o,"utf-8").toString("latin1"),r=="x-amzn-trace-id"){t["X-Amzn-Trace-Id"]=[o];continue}if(r=="user-agent"){t["User-Agent"]=[o];continue}if(r=="x-forwarded-for"){c[s]||(c[s]=[]),c[s].push(o);continue}if(a.shouldDropHeader(r))continue;let u=n[r];u&&u!=s&&delete t[u],s in t||(t[s]=[]),t[s].push(o),n[r]=s}let d=Object.keys(c);if(d.length){let i=d[d.length-1];t["X-Forwarded-For"]=c[i]}return t}getHeaders(e){let t=a.parseMultiValueHeaders(e),n=t["X-Amzn-Trace-Id"]?t["X-Amzn-Trace-Id"][0]:void 0;t.Host=[`localhost:${this.port}`],t["X-Amzn-Trace-Id"]=[D(n)],t["X-Forwarded-Proto"]=["http"],t["X-Forwarded-Port"]=[String(this.port)],t["X-Forwarded-For"]||(t["X-Forwarded-For"]=[]),t["X-Forwarded-For"].push(this.ip);let c={},d={},i=Object.keys(t).sort((l,p)=>l.toLowerCase().localeCompare(p.toLowerCase())),s,r,o,u="*/*",h;for(let l of i){let p=t[l],m=l.toLowerCase();if(m=="x-http-method-override"){s=p[p.length-1];continue}if(m=="x-api-key")r=p[p.length-1];else if(m=="authorization"){if(h=p[p.length-1],h.startsWith("AWS4-HMAC-SHA256 Credential=")&&re.every(y=>h.includes(y)))continue}else if(m=="content-type")o=p[p.length-1].toLowerCase();else if(m=="accept")u=p[p.length-1].toLowerCase().split(",")[0];else if(l=="X-Forwarded-For"){let y=p.join(", ");c[l]=[y],d[l]=y;continue}c[l]=p,d[l]=p[p.length-1]}return{headers:d,multiValueHeaders:c,overrideMethod:s,apiKey:r,contentType:o,accept:u,authorization:h}}createRequestContext({foundRequest:e,requestId:t,httpMethod:n,req:c,headers:d,apiKey:i}){let s,r;if(i){s={apiKey:i};let m=this.apiKeys.find(y=>y.value==i)?.id;m&&(r={apiKeyId:m})}let o={cognitoIdentityPoolId:null,accountId:null,cognitoIdentityId:null,...s,caller:null,sourceIp:c.socket.remoteAddress?.includes(":")?this.ip:c.socket.remoteAddress,principalOrgId:null,accessKey:null,cognitoAuthenticationType:null,cognitoAuthenticationProvider:null,userArn:null,...r,userAgent:d["User-Agent"]??null,user:null},{requestTime:u,requestTimeEpoch:h}=j(),l=e.requestPath;return this.requireStagePrefixInRequestPath||(l=`/${this.stage}${e.requestPath}`),{resourceId:e.resource.resourceId,resourcePath:e.resourcePath,httpMethod:n,extendedRequestId:V(),requestTime:u,path:l,accountId:"123456789012",protocol:"HTTP/1.1",stage:this.stage,domainPrefix:"localhost",requestTimeEpoch:h,requestId:t,identity:o,domainName:`http://localhost:${this.port}`,deploymentId:this.deploymentId,apiId:this.id}}async createPayloadFromRequest(e,t,n){let{rawHeaders:c,method:d}=e,{headers:i,multiValueHeaders:s,overrideMethod:r,apiKey:o,contentType:u,accept:h}=this.getHeaders(c),l=!1;u&&this.binaryMediaTypes.find(Y=>Y.test(u))&&(l=!0);let p=await a.collectBody(e,l)||null,{queryStringParameters:m,multiValueQueryStringParameters:y}=a.getQueryStrings(t.rawQueryStringParameters),R=null;Object.keys(t.pathParams).length&&(R=t.pathParams);let b=r??d,W=`/${this.stage}`,x=t.requestPath;return this.requireStagePrefixInRequestPath&&(x=x.slice(W.length),x||(x="/")),{payload:{resource:t.resourcePath,path:x,httpMethod:b,headers:i,multiValueHeaders:s,queryStringParameters:m,multiValueQueryStringParameters:y,pathParameters:R,stageVariables:null,requestContext:this.createRequestContext({foundRequest:t,requestId:n,apiKey:o,req:e,headers:i,httpMethod:b}),isBase64Encoded:l,body:p},contentType:u,accept:h}}validateRequest(e,t,n,c){let{payload:d,contentType:i}=e;if(!K.includes(d.httpMethod))throw new P;if(t.requiresApiKey&&!d.requestContext.identity.apiKeyId)throw new T;let s=i??"application/json";if(s.startsWith("application/json;")){let u=s.replaceAll(" ","").split(";").find(h=>h.startsWith("charset="));if(u&&u.replaceAll(",","")!="charset=utf-8")throw new M;s="application/json"}let r=this.compiledResources[n]?.[c];if(t.validate&&r){if(t.validate.query&&r.requiredQueryParams.length){if(!d.queryStringParameters)throw new I(`{"message": "Missing required request parameters: [${r.requiredQueryParams.join(", ")}]"}`);let l=r.requiredQueryParams.filter(p=>!d.queryStringParameters[p]);if(l.length)throw new I(`{"message": "Missing required request parameters: [${l.join(", ")}]"}`)}if(t.validate.headers&&r.requiredHeaders.length){let l=r.requiredHeaders.filter(p=>!d.headers[p]);if(l.length)throw new I(`{"message": "Missing required request parameters: [${l.join(", ")}]"}`)}if(!t.validate.body||d.isBase64Encoded||this.binaryMediaTypes.find(l=>l.test("anything")))return;let o=r.body[s];if(typeof o!="function")return;if(!d.body)throw new I('{"message":[Unknown error parsing request body]}');let u;try{u=JSON.parse(d.body)}catch{throw new I('{"message":[Unknown error parsing request body]}')}if(!o(u)){this.logger.getDebug()&&console.warn(o.errors);let l=o.errors.map(p=>`instancePath '${p.instancePath}' ${p.message}`).join(", ");throw new I(JSON.stringify({message:`[${l}]`}))}}}async invokeProxyLambda(e,t){let d=e;if(d.startsWith("arn:aws:lambda")){let o=d.split(":");(o.length==7||o.length==8)&&(d=o[6])}let i=this.getLambdas(),s=i.find(o=>o.name==d);if(!s&&typeof this.resolveLambdaNameFromArn=="function"){let o=this.resolveLambdaNameFromArn(e);s=i.find(u=>u.name==o)}if(!s)throw this.logger.RED(`REST APiG can not find Lambda ${e}`),new f;return await s.invoke(t)}async invokeIntegration(e,t,n,c){let d=e.matchedMethod,i=e.requestMethod,s=e.resource[d];try{let r=await this.createPayloadFromRequest(t,e,c),{payload:o,accept:u}=r;if(this.validateRequest(r,s,e.resource.resourceId,d),s.integration.lambda?.arn){let h=await this.invokeProxyLambda(s.integration.lambda.arn,o);this.responseFromIntegration(n,h,"lambda-proxy",c,u,i)}else if(typeof s.integration.mock=="function"){let h=await s.integration.mock(o);this.responseFromIntegration(n,h,"lambda-proxy",c,u,i)}else this.logger.RED("REST APiG must have integration"),n.statusCode=500,n.end("No resource integration found")}catch(r){if(r instanceof w){r.completeRequest(n,c);return}console.error(r),new f().completeRequest(n,c)}}async requestHandler(e,t){let n=this.findResourceFromRequest(e),c=$();n?await this.invokeIntegration(n,e,t,c):new E().completeRequest(t,c)}parseIntegrationResponseHeaders(e){let t={},n=typeof e.multiValueHeaders;if(S(e.multiValueHeaders))for(let[i,s]of Object.entries(e.multiValueHeaders)){let r=i,o=i.toLowerCase();if(o=="content-type")r="Content-Type";else if(G.includes(o)||o.startsWith("x-amzn-remapped-"))r=`x-amzn-Remapped-${r}`;else if(z.includes(o))continue;t[r]=[];for(let u of s)if(typeof u=="string")t[r].push(u);else if(typeof u=="number"||typeof u=="boolean")t[r].push(String(u));else if(u===null)t[r].includes("")||t[r].push("");else throw this.logger.RED(`Invalid integration response headers. MultiValueHeader '${r}' values must be string, number, boolean or null.`),new f}else if(n!="undefined"&&n!==null)throw this.logger.RED("Invalid integration response headers. multiValueHeaders must be an object of with string[] as value type."),new f;if(typeof e.headers=="undefined")return t;if(S(e.headers))for(let[i,s]of Object.entries(e.headers)){let r=i,o=i.toLowerCase();if(o=="content-type")r="Content-Type";else if(G.includes(o)||o.startsWith("x-amzn-remapped-"))r=`x-amzn-Remapped-${r}`;else if(z.includes(o))continue;if(t[r]||(t[r]=[]),typeof s=="string")t[r].includes(s)||t[r].push(s);else if(typeof s=="number"||typeof s=="boolean"){let u=String(s);t[r].includes(u)||t[r].push(u)}else if(s===null)t[r].includes("")||t[r].push("");else if(typeof s<"u")throw this.logger.RED(`Invalid integration response headers. Header '${r}' must be string, number, boolean or null.`),new f}else if(e.headers!==null)throw this.logger.RED("Invalid integration response headers. Headers must be an object or null."),new f;return t}parseIntegrationResponse(e,t){if(!S(e))throw this.logger.RED("Invalid integration response."),new f;let n=["statusCode","body","headers","isBase64Encoded","multiValueHeaders"],c=Object.keys(e).find(o=>!n.includes(o));if(c)throw this.logger.RED(`Invalid integration response. Field '${c}' is not supported.`),new f;let d=200,i;if(e.statusCode){if(Number.isNaN(e.statusCode))throw this.logger.RED("Invalid integration response statusCode"),new f;let o=Number(e.statusCode);if(!(o>99&&o<600))throw this.logger.RED("Invalid integration response statusCode"),new f;d=o}let s=typeof e.body;if(s=="string")i=e.body;else if(s=="number")Number.isNaN(e.body)||(i=String(e.body));else if(s=="boolean")i=String(e.body);else if(s!="undefined"&&e.body!==null)throw this.logger.RED("Invalid integration response body"),new f;if(i&&e.isBase64Encoded&&this.binaryMediaTypes.find(o=>o.test(t))){let o=Buffer.from(i,"base64");if(o.toString("base64")!=i)throw this.logger.RED("Invalid integration response body. Incorrect body base64 encoding"),new f;i=o}let r=this.parseIntegrationResponseHeaders(e);return{statusCode:d,body:i,headers:r}}responseFromIntegration(e,t,n,c,d,i){try{let s=this.parseIntegrationResponse(t,d);e.shouldKeepAlive=!1,s.headers.Connection=["keep-alive"],s.headers["x-amzn-RequestId"]=[c],s.headers["Content-Length"]=["0"],s.body&&s.statusCode!=204&&(s.headers["Content-Length"]=[Buffer.byteLength(s.body,typeof s.body=="string"?"utf-8":"base64").toString()]),s.headers["Content-Type"]?s.headers["Content-Type"]=[s.headers["Content-Type"][0]]:s.headers["Content-Type"]=["application/json"],i=="HEAD"&&s.headers["Content-Length"][0]!="0"&&(s.headers["x-amzn-Remapped-Content-Length"]||(s.headers["x-amzn-Remapped-Content-Length"]=[]),s.headers["x-amzn-Remapped-Content-Length"].push(s.headers["Content-Length"][0])),e.writeHead(s.statusCode,s.headers),i=="HEAD"?e.end():e.end(s.body)}catch(s){if(s instanceof w){s.completeRequest(e,c);return}console.error(s),new f().completeRequest(e,c)}}};var ie=U(),Ge=a=>{let e=a.gateways,t=a.apiKeys??[],n=a.usagePlans??[],c={name:"rest-api-plugin",onKill(){for(let d of v.servers)d.stop();v.servers=[]},shared:{gateways:e},server:{async onReady(){let d=[];for(let s of t){if(!s.id){let r=oe();s.id=r.slice(0,r.indexOf("-"))}s.value||(s.value=O(),console.log("API Key",s.name,":",s.value)),d.push(s)}let i={};for(let s of n)for(let r of s.apiKeys){let o=d.find(u=>u.id==r.keyId||u.name==r.keyId);if(!o)throw new Error(`[REST Api - Usage Plan] Can not find API Key with id: "${r.keyId}"`);i[r.apiId]||(i[r.apiId]=[]),i[r.apiId].push(o)}for(let s of e){let r=new v({ip:ie,...s,getLambdas:()=>this.lambdas},this.log),o=i[r.id];o&&(r.apiKeys=o),await r.start();let u=c.shared.gateways.find(h=>h.id==s.id);u&&(u.id=r.id,u.port=r.port,u.stage=r.stage),console.log(`REST API [${r.id}]`,`http://localhost:${r.port}`),r.server?.unref()}}},buildCallback(d,i){if(i)for(let s of v.servers)s.dropConnections()}};return c};export{Ge as restApiPlugin};
1
+ import{randomUUID as oe}from"node:crypto";import{randomUUID as $}from"node:crypto";import{createServer as te}from"node:http";import{EventSourceMapping as Ae}from"exlo/internal/esm";import{EventSourceMapping as pe}from"exlo/internal/esm";var S=a=>Object.prototype.toString.call(a)=="[object Object]";import _ from"ajv";import A from"ajv-formats";var g=new _({discriminator:!1,useDefaults:!1,validateFormats:!0,jtd:!1,strictRequired:!0,allowUnionTypes:!0,unevaluated:!1,formats:{"date-time":A.get("date-time"),email:A.get("email"),hostname:A.get("hostname"),ipv4:A.get("ipv4"),ipv6:A.get("ipv6"),uri:A.get("uri")}});g.removeKeyword("if");g.removeKeyword("else");g.removeKeyword("then");g.removeKeyword("const");g.removeKeyword("contains");g.removeKeyword("contentEncoding");g.removeKeyword("contentMediaType");g.removeKeyword("contentSchema");g.removeKeyword("nullable");g.removeKeyword("prefixItems");g.removeKeyword("example");g.removeKeyword("examples");g.removeKeyword("deprecated");g.removeKeyword("readOnly");g.removeKeyword("writeOnly");g.removeKeyword("$comment");g.removeKeyword("$defs");g.removeKeyword("exclusiveMinimum");g.removeKeyword("exclusiveMaximum");g.removeKeyword("propertyNames");g.removeKeyword("$id");g.removeKeyword("id");g.addKeyword({keyword:["exclusiveMaximum","exclusiveMinimum"],type:["number"],schemaType:["number"],$data:!0,code(a){if(a.it.rootId!="http://json-schema.org/draft-07/schema#")throw new Error(`unsupported keyword "${a.keyword}"`)}});g.addKeyword({keyword:"propertyNames",type:["object"],schemaType:["object","boolean"],code(a){if(a.it.rootId!="http://json-schema.org/draft-07/schema#")throw new Error('unsupported keyword "propertyNames"')}});g.addKeyword({keyword:"$id",type:["object"],schemaType:["string"],code(a){if(a.it.rootId!="http://json-schema.org/draft-07/schema#")throw new Error('unsupported keyword "$id"')}});g.addKeyword({keyword:"id",schemaType:["string"]});var F=a=>{if(a&&typeof a=="object"&&!Array.isArray(a)){let e;return a.$schema?e={...a,$schema:"http://json-schema.org/draft-07/schema#"}:e=a,g.compile(e)}throw new Error("API Gateway validator Schema must be an object")};var w=class extends Error{res;constructor(e){super("RestApiError"),this.res=e}setHeaders(e){this.res.headers.Connection="keep-alive",this.res.headers["x-amzn-RequestId"]=e,this.res.body?this.res.headers["Content-Length"]=Buffer.byteLength(this.res.body,"utf-8").toString():this.res.headers["Content-Length"]="0"}completeRequest(e,t){e.shouldKeepAlive=!1,this.setHeaders(t),e.writeHead(this.res.statusCode,this.res.statusText,this.res.headers),e.end(this.res.body)}},P=class extends w{constructor(){super({statusCode:405,statusText:"Method Not Allowed",headers:{"Content-Type":"null"},body:"Unsupported HTTP method"})}},M=class extends w{constructor(){super({statusCode:415,statusText:"Unsupported Media Type",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"BadRequestException"},body:'{"message": "Unsupported Media Type"}'})}},I=class extends w{constructor(e){super({statusCode:400,statusText:"Bad Request",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"BadRequestException"},body:e})}},T=class extends w{constructor(){super({statusCode:403,statusText:"Forbidden",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"ForbiddenException"},body:'{"message": "Forbidden"}'})}},E=class extends w{constructor(){super({statusCode:403,statusText:"Forbidden",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"MissingAuthenticationTokenException"},body:'{"message": "Missing Authentication Token"}'})}},f=class extends w{constructor(e=502){super({statusCode:e,statusText:"BadGateway",headers:{"Content-Type":"application/json","x-amzn-ErrorType":"InternalServerErrorException"},body:'{"message": "Internal server error"}'})}};function N(a,e,t){let n=e.split("/").filter(Boolean),c={};if(!n.length){let i;return t[a]?i=a:t.ANY&&(i="ANY"),i?{matchedMethod:i,pathParams:c,resource:t,resourcePath:"/"}:void 0}let d=q(a,n,t,c,"");if(d?.resource)return{matchedMethod:d.matchedMethod,pathParams:c,resource:d.resource,resourcePath:d?.resourcePath}}function q(a,e,t,n,c){if(!e.length)return;let d=`/${e[0]}`;if(d in t){let r=t[d],o=`${c}${d}`;if(e.length===1){let u=r[a]?a:r.ANY?"ANY":null;if(u)return{matchedMethod:u,resource:r,resourcePath:o}}return q(a,e.slice(1),r,n,o)}let i=Object.keys(t).find(r=>r.startsWith("/{")&&!r.endsWith("+}"));if(i){let r=i.slice(2,-1);n[r]=e[0];let o=t[i],u=`${c}${i}`;if(e.length===1){let h=o[a]?a:o.ANY?"ANY":void 0;if(h)return{matchedMethod:h,resource:o,resourcePath:u}}return q(a,e.slice(1),o,n,u)}let s=Object.keys(t).find(r=>r.startsWith("/{")&&r.endsWith("+}"));if(s){let r=s.match(/\{(.+?)\+\}/)?.[1];r&&(n[r]=e.join("/"));let o=t[s],u=`${c}${s}`;if(o[a])return{matchedMethod:a,resource:o,resourcePath:u};if(o.ANY)return{matchedMethod:"ANY",resource:o,resourcePath:u}}}import{getRandomValues as k}from"node:crypto";import{networkInterfaces as B}from"node:os";var C="abcdefghijklmnopqrstuvwxyz0123456789",H=`ABCDEFGHIJKLMNOPQRSTUVWXYZ${C}`,J=40;function O(){let a="",e=new Uint8Array(45);k(e);for(let t=0;t<J;t++)a+=H[e[t]%H.length];return a}var X=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function j(){let a=new Date,e=String(a.getUTCDate()).padStart(2,"0"),t=X[a.getUTCMonth()],n=a.getUTCFullYear(),c=String(a.getUTCHours()).padStart(2,"0"),d=String(a.getUTCMinutes()).padStart(2,"0"),i=String(a.getUTCSeconds()).padStart(2,"0");return{requestTime:`${e}/${t}/${n}:${c}:${d}:${i} +0000`,requestTimeEpoch:a.getTime()}}function D(){let a="127.0.0.1";if(B){let e=Object.values(B()).reduce((t,n)=>(t.push(...n),t),[])?.filter(t=>!t.internal&&t.family==="IPv4").find(Boolean)?.address;e&&(a=e)}return a}function L(){let a=Math.floor(Date.now()/1e3).toString(16),e=k(new Uint8Array(12)).reduce((t,n)=>t+n.toString(16).padStart(2,"0"),"");return`1-${a}-${e}`}function Z(){return k(new Uint8Array(8)).reduce((a,e)=>a+e.toString(16).padStart(2,"0"),"")}function ee(a){let e=a.split("-");if(e.length===3){let t=(BigInt(`0x${e[2]}`)+BigInt(2)).toString(16).padStart(24,"0");return`${e[0]}-${e[1]}-${t}`}return a}function U(a){if(!a)return`Root=${L()}`;let e=a.match(/Root=([^\s;]+)/),t=e?e[1]:L(),n=ee(t);return`Self=1-${Z()};Root=${n}`}function Q(){let a="",e=new Uint8Array(6);crypto.getRandomValues(e);for(let t=0;t<6;t++)a+=C[e[t]%C.length];return a}function V(){let a=new Uint8Array(8);return crypto.getRandomValues(a),Buffer.from(String.fromCharCode(...a),"utf-8").toString("base64url")}var se=["connection","content-md5","content-length","expect","max-forwards","proxy-authenticate","server","te","host","trailer","transfer-encoding","upgrade","www-authenticate","x-forwarded-port","x-forwarded-proto"],re=["AWS4-HMAC-SHA256","Credential","SignedHeaders","Signature"],K=["DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT"],G=["authorization","connection","content-md5","max-forwards","user-agent","www-authenticate","date","server","content-length"],z=["expect","host","proxy-authenticate","te","transfer-encoding","trailer","upgrade","via"],ne=/^[a-z0-9-.+#!{}_'`]+\/([a-z0-9-.+#!{}_'`]+|\*{1})$/,v=class a{constructor({id:e,ip:t,getLambdas:n,port:c,stage:d,resources:i,requireStagePrefixInRequestPath:s,models:r,binaryMediaTypes:o,resolveLambdaNameFromArn:u},h){this.logger=h;if(e)this.id=e;else{let l=$();this.id=l.slice(0,l.indexOf("-"))}if(a.servers.find(l=>l.id==this.id))throw new Error(`[REST API Gateway] An API with the same id (${this.id}) already exists.`);this.ip=t,this.port=c,this.stage=d??"dev",this.getLambdas=n,this.resources=i,this.resolveLambdaNameFromArn=u,this.deploymentId=Q(),r&&typeof r=="object"?this.models=r:this.models={},typeof s=="boolean"&&(this.requireStagePrefixInRequestPath=s),Array.isArray(o)&&this.compileBinaryTypes(o),this.compileResources(this.resources)}static servers=[];server;id;ip;port;stage;requireStagePrefixInRequestPath=!0;deploymentId;apiKeys=[];resolveLambdaNameFromArn;binaryMediaTypes=[];getLambdas;resources;compiledResources={};sockets=[];models;compileBinaryTypes(e){for(let t of e){let n=t.toLowerCase();if(n=="*/*"){this.binaryMediaTypes.push(/^.*$/);continue}if(ne.test(n))if(n.endsWith("/*")){let c=new RegExp(`^${n.slice(0,-1)}[a-z0-9-_.$~+#'{}\`!*]+$`);this.binaryMediaTypes.push(c)}else{let c=new RegExp(`^${n}$`);this.binaryMediaTypes.push(c)}}}compileResources(e){for(let[t,n]of Object.entries(e))t.startsWith("/")&&this.compileResources(n);if(!e.resourceId){let t=$();e.resourceId=t.slice(0,t.indexOf("-"))}this.compiledResources[e.resourceId]={};for(let t of K){let n=e[t];if(!n)continue;let c=[],d=[],i={};if(n.request){if(n.request.queryStringParameters)for(let[s,r]of Object.entries(n.request.queryStringParameters))r.required&&c.push(s);if(n.request.headers)for(let[s,r]of Object.entries(n.request.headers))r.required&&d.push(s);if(n.request.body)for(let[s,r]of Object.entries(n.request.body)){let o=this.models[r];i[s]=F(o)}}this.compiledResources[e.resourceId][t]={requiredQueryParams:c,requiredHeaders:d,body:i}}}async start(){this.server=te((t,n)=>{this.requestHandler(t,n)}),await new Promise(t=>{this.server.listen(this.port,t)}),a.servers.push(this),this.port=this.server.address().port,this.server.on("connection",t=>{t.on("close",()=>{let n=this.sockets.findIndex(c=>c==t);n!=-1&&this.sockets.splice(n,1)}),this.sockets.push(t)});let e=this.requireStagePrefixInRequestPath?`/${this.stage}`:"";this.printPaths(this.resources,e)}printPaths(e,t){let n=Object.keys(e);for(let c of n){let d=e[c];if(!c.startsWith("/"))continue;let i=Object.keys(d).filter(r=>K.includes(r)),s=`${t}${c}`;i.length&&this.logger.CYAN(`${i.join(", ")} http://localhost:${this.port}${s}`),this.printPaths(d,s)}}dropConnections(){for(let e of this.sockets)e.writable&&e.destroy()}stop(){this.server?.close()}findResourceFromRequest(e){let{method:t,url:n}=e,c=n.indexOf("?");c==-1&&(c=void 0);let d=n.slice(0,c),i=d;for(;i!="/"&&i.endsWith("/");)i=i.slice(0,-1);let s;c&&(s=n.slice(c));let r=i;if(this.requireStagePrefixInRequestPath){if(!i.startsWith(`/${this.stage}`))return;r=i.slice(`/${this.stage}`.length)}let o=N(t,r,this.resources);if(o)return{resource:o.resource,pathname:i,requestPath:d,rawQueryStringParameters:s,matchedMethod:o.matchedMethod,requestMethod:t,pathParams:o.pathParams,resourcePath:o.resourcePath}}static async collectBody(e,t){let n=Buffer.alloc(0);return e.on("data",c=>{n=Buffer.concat([n,c])}),new Promise(c=>{e.on("end",async()=>{c(n?n.toString(t?"base64":"utf-8"):null)})})}static getQueryStrings(e){let t=null,n=null;if(!e)return{queryStringParameters:n,multiValueQueryStringParameters:t};let c=e.slice(1).split("&"),d={};for(let s of c){let[r,o]=s.split("="),u=r.split(";"),h=o?.split(";")??[""];if(u.length==1&&h.length==1){let R=decodeURIComponent(u[0]),b=decodeURIComponent(h[0]);R in d?d[R].push(b):d[R]=[b];continue}let l=u.slice(0,-1),p=u[u.length-1],m=decodeURIComponent(p),y=decodeURIComponent(h[0]);if(m in d?d[m].push(y):d[m]=[y],h.length>1){let R=h.slice(1);for(let b of R)l.push(b)}for(let R of l){let b=decodeURIComponent(R);b in d?d[b].push(""):d[b]=[""]}}let i=Object.keys(d);if(i.length){n={},t={};for(let s of i.sort()){let r=d[s];t[s]=r,n[s]=r[r.length-1]}}return{queryStringParameters:n,multiValueQueryStringParameters:t}}static shouldDropHeader(e){return!!se.includes(e)}static parseMultiValueHeaders(e){let t={},n={},c={};for(let i=0;i<e.length;i=i+2){let s=e[i],r=s.toLowerCase(),o=e[i+1]?.trim()??"";if(o=Buffer.from(o,"utf-8").toString("latin1"),r=="x-amzn-trace-id"){t["X-Amzn-Trace-Id"]=[o];continue}if(r=="user-agent"){t["User-Agent"]=[o];continue}if(r=="x-forwarded-for"){c[s]||(c[s]=[]),c[s].push(o);continue}if(a.shouldDropHeader(r))continue;let u=n[r];u&&u!=s&&delete t[u],s in t||(t[s]=[]),t[s].push(o),n[r]=s}let d=Object.keys(c);if(d.length){let i=d[d.length-1];t["X-Forwarded-For"]=c[i]}return t}getHeaders(e){let t=a.parseMultiValueHeaders(e),n=t["X-Amzn-Trace-Id"]?t["X-Amzn-Trace-Id"][0]:void 0;t.Host=[`localhost:${this.port}`],t["X-Amzn-Trace-Id"]=[U(n)],t["X-Forwarded-Proto"]=["http"],t["X-Forwarded-Port"]=[String(this.port)],t["X-Forwarded-For"]||(t["X-Forwarded-For"]=[]),t["X-Forwarded-For"].push(this.ip);let c={},d={},i=Object.keys(t).sort((l,p)=>l.toLowerCase().localeCompare(p.toLowerCase())),s,r,o,u="*/*",h;for(let l of i){let p=t[l],m=l.toLowerCase();if(m=="x-http-method-override"){s=p[p.length-1];continue}if(m=="x-api-key")r=p[p.length-1];else if(m=="authorization"){if(h=p[p.length-1],h.startsWith("AWS4-HMAC-SHA256 Credential=")&&re.every(y=>h.includes(y)))continue}else if(m=="content-type")o=p[p.length-1].toLowerCase();else if(m=="accept")u=p[p.length-1].toLowerCase().split(",")[0];else if(l=="X-Forwarded-For"){let y=p.join(", ");c[l]=[y],d[l]=y;continue}c[l]=p,d[l]=p[p.length-1]}return{headers:d,multiValueHeaders:c,overrideMethod:s,apiKey:r,contentType:o,accept:u,authorization:h}}createRequestContext({foundRequest:e,requestId:t,httpMethod:n,req:c,headers:d,apiKey:i}){let s,r;if(i){s={apiKey:i};let m=this.apiKeys.find(y=>y.value==i)?.id;m&&(r={apiKeyId:m})}let o={cognitoIdentityPoolId:null,accountId:null,cognitoIdentityId:null,...s,caller:null,sourceIp:c.socket.remoteAddress?.includes(":")?this.ip:c.socket.remoteAddress,principalOrgId:null,accessKey:null,cognitoAuthenticationType:null,cognitoAuthenticationProvider:null,userArn:null,...r,userAgent:d["User-Agent"]??null,user:null},{requestTime:u,requestTimeEpoch:h}=j(),l=e.requestPath;return this.requireStagePrefixInRequestPath||(l=`/${this.stage}${e.requestPath}`),{resourceId:e.resource.resourceId,resourcePath:e.resourcePath,httpMethod:n,extendedRequestId:V(),requestTime:u,path:l,accountId:"123456789012",protocol:"HTTP/1.1",stage:this.stage,domainPrefix:"localhost",requestTimeEpoch:h,requestId:t,identity:o,domainName:`http://localhost:${this.port}`,deploymentId:this.deploymentId,apiId:this.id}}async createPayloadFromRequest(e,t,n){let{rawHeaders:c,method:d}=e,{headers:i,multiValueHeaders:s,overrideMethod:r,apiKey:o,contentType:u,accept:h}=this.getHeaders(c),l=!1;u&&this.binaryMediaTypes.find(Y=>Y.test(u))&&(l=!0);let p=await a.collectBody(e,l)||null,{queryStringParameters:m,multiValueQueryStringParameters:y}=a.getQueryStrings(t.rawQueryStringParameters),R=null;Object.keys(t.pathParams).length&&(R=t.pathParams);let b=r??d,W=`/${this.stage}`,x=t.requestPath;return this.requireStagePrefixInRequestPath&&(x=x.slice(W.length),x||(x="/")),{payload:{resource:t.resourcePath,path:x,httpMethod:b,headers:i,multiValueHeaders:s,queryStringParameters:m,multiValueQueryStringParameters:y,pathParameters:R,stageVariables:null,requestContext:this.createRequestContext({foundRequest:t,requestId:n,apiKey:o,req:e,headers:i,httpMethod:b}),isBase64Encoded:l,body:p},contentType:u,accept:h}}validateRequest(e,t,n,c){let{payload:d,contentType:i}=e;if(!K.includes(d.httpMethod))throw new P;if(t.requiresApiKey&&!d.requestContext.identity.apiKeyId)throw new T;let s=i??"application/json";if(s.startsWith("application/json;")){let u=s.replaceAll(" ","").split(";").find(h=>h.startsWith("charset="));if(u&&u.replaceAll(",","")!="charset=utf-8")throw new M;s="application/json"}let r=this.compiledResources[n]?.[c];if(t.validate&&r){if(t.validate.query&&r.requiredQueryParams.length){if(!d.queryStringParameters)throw new I(`{"message": "Missing required request parameters: [${r.requiredQueryParams.join(", ")}]"}`);let l=r.requiredQueryParams.filter(p=>!d.queryStringParameters[p]);if(l.length)throw new I(`{"message": "Missing required request parameters: [${l.join(", ")}]"}`)}if(t.validate.headers&&r.requiredHeaders.length){let l=r.requiredHeaders.filter(p=>!d.headers[p]);if(l.length)throw new I(`{"message": "Missing required request parameters: [${l.join(", ")}]"}`)}if(!t.validate.body||d.isBase64Encoded||this.binaryMediaTypes.find(l=>l.test("anything")))return;let o=r.body[s];if(typeof o!="function")return;if(!d.body)throw new I('{"message":[Unknown error parsing request body]}');let u;try{u=JSON.parse(d.body)}catch{throw new I('{"message":[Unknown error parsing request body]}')}if(!o(u)){this.logger.getDebug()&&console.warn(o.errors);let l=o.errors.map(p=>`instancePath '${p.instancePath}' ${p.message}`).join(", ");throw new I(JSON.stringify({message:`[${l}]`}))}}}async invokeProxyLambda(e,t){let d=e;if(d.startsWith("arn:aws:lambda")){let o=d.split(":");(o.length==7||o.length==8)&&(d=o[6])}let i=this.getLambdas(),s=i.find(o=>o.name==d);if(!s&&typeof this.resolveLambdaNameFromArn=="function"){let o=this.resolveLambdaNameFromArn(e);s=i.find(u=>u.name==o)}if(!s)throw this.logger.RED(`REST APiG can not find Lambda ${e}`),new f;return await s.invoke(t)}async invokeIntegration(e,t,n,c){let d=e.matchedMethod,i=e.requestMethod,s=e.resource[d];try{let r=await this.createPayloadFromRequest(t,e,c),{payload:o,accept:u}=r;if(this.validateRequest(r,s,e.resource.resourceId,d),s.integration.lambda?.arn){let h=await this.invokeProxyLambda(s.integration.lambda.arn,o);this.responseFromIntegration(n,h,"lambda-proxy",c,u,i)}else if(typeof s.integration.mock=="function"){let h=await s.integration.mock(o);this.responseFromIntegration(n,h,"lambda-proxy",c,u,i)}else this.logger.RED("REST APiG must have integration"),n.statusCode=500,n.end("No resource integration found")}catch(r){if(r instanceof w){r.completeRequest(n,c);return}console.error(r),new f().completeRequest(n,c)}}async requestHandler(e,t){let n=this.findResourceFromRequest(e),c=$();n?await this.invokeIntegration(n,e,t,c):new E().completeRequest(t,c)}parseIntegrationResponseHeaders(e){let t={},n=typeof e.multiValueHeaders;if(S(e.multiValueHeaders))for(let[i,s]of Object.entries(e.multiValueHeaders)){let r=i,o=i.toLowerCase();if(o=="content-type")r="Content-Type";else if(G.includes(o)||o.startsWith("x-amzn-remapped-"))r=`x-amzn-Remapped-${r}`;else if(z.includes(o))continue;t[r]=[];for(let u of s)if(typeof u=="string")t[r].push(u);else if(typeof u=="number"||typeof u=="boolean")t[r].push(String(u));else if(u===null)t[r].includes("")||t[r].push("");else throw this.logger.RED(`Invalid integration response headers. MultiValueHeader '${r}' values must be string, number, boolean or null.`),new f}else if(n!="undefined"&&n!==null)throw this.logger.RED("Invalid integration response headers. multiValueHeaders must be an object of with string[] as value type."),new f;if(typeof e.headers=="undefined")return t;if(S(e.headers))for(let[i,s]of Object.entries(e.headers)){let r=i,o=i.toLowerCase();if(o=="content-type")r="Content-Type";else if(G.includes(o)||o.startsWith("x-amzn-remapped-"))r=`x-amzn-Remapped-${r}`;else if(z.includes(o))continue;if(t[r]||(t[r]=[]),typeof s=="string")t[r].includes(s)||t[r].push(s);else if(typeof s=="number"||typeof s=="boolean"){let u=String(s);t[r].includes(u)||t[r].push(u)}else if(s===null)t[r].includes("")||t[r].push("");else if(typeof s<"u")throw this.logger.RED(`Invalid integration response headers. Header '${r}' must be string, number, boolean or null.`),new f}else if(e.headers!==null)throw this.logger.RED("Invalid integration response headers. Headers must be an object or null."),new f;return t}parseIntegrationResponse(e,t){if(!S(e))throw this.logger.RED("Invalid integration response."),new f;let n=["statusCode","body","headers","isBase64Encoded","multiValueHeaders"],c=Object.keys(e).find(o=>!n.includes(o));if(c)throw this.logger.RED(`Invalid integration response. Field '${c}' is not supported.`),new f;let d=200,i;if(e.statusCode){if(Number.isNaN(e.statusCode))throw this.logger.RED("Invalid integration response statusCode"),new f;let o=Number(e.statusCode);if(!(o>99&&o<600))throw this.logger.RED("Invalid integration response statusCode"),new f;d=o}let s=typeof e.body;if(s=="string")i=e.body;else if(s=="number")Number.isNaN(e.body)||(i=String(e.body));else if(s=="boolean")i=String(e.body);else if(s!="undefined"&&e.body!==null)throw this.logger.RED("Invalid integration response body"),new f;if(i&&e.isBase64Encoded&&this.binaryMediaTypes.find(o=>o.test(t))){let o=Buffer.from(i,"base64");if(o.toString("base64")!=i)throw this.logger.RED("Invalid integration response body. Incorrect body base64 encoding"),new f;i=o}let r=this.parseIntegrationResponseHeaders(e);return{statusCode:d,body:i,headers:r}}responseFromIntegration(e,t,n,c,d,i){try{let s=this.parseIntegrationResponse(t,d);e.shouldKeepAlive=!1,s.headers.Connection=["keep-alive"],s.headers["x-amzn-RequestId"]=[c],s.headers["Content-Length"]=["0"],s.body&&s.statusCode!=204&&(s.headers["Content-Length"]=[Buffer.byteLength(s.body,typeof s.body=="string"?"utf-8":"base64").toString()]),s.headers["Content-Type"]?s.headers["Content-Type"]=[s.headers["Content-Type"][0]]:s.headers["Content-Type"]=["application/json"],i=="HEAD"&&s.headers["Content-Length"][0]!="0"&&(s.headers["x-amzn-Remapped-Content-Length"]||(s.headers["x-amzn-Remapped-Content-Length"]=[]),s.headers["x-amzn-Remapped-Content-Length"].push(s.headers["Content-Length"][0])),e.writeHead(s.statusCode,s.headers),i=="HEAD"?e.end():e.end(s.body)}catch(s){if(s instanceof w){s.completeRequest(e,c);return}console.error(s),new f().completeRequest(e,c)}}};var ie=D(),Ge=a=>{let e=a.gateways,t=a.apiKeys??[],n=a.usagePlans??[],c={name:"rest-api-plugin",onKill(){for(let d of v.servers)d.stop();v.servers=[]},shared:{gateways:e},server:{async onReady(){let d=[];for(let s of t){if(!s.id){let r=oe();s.id=r.slice(0,r.indexOf("-"))}s.value||(s.value=O(),console.log("API Key",s.name,":",s.value)),d.push(s)}let i={};for(let s of n)for(let r of s.apiKeys){let o=d.find(u=>u.id==r.keyId||u.name==r.keyId);if(!o)throw new Error(`[REST Api - Usage Plan] Can not find API Key with id: "${r.keyId}"`);i[r.apiId]||(i[r.apiId]=[]),i[r.apiId].push(o)}for(let s of e){let r=new v({ip:ie,...s,getLambdas:()=>this.lambdas},this.log),o=i[r.id];o&&(r.apiKeys=o),await r.start();let u=c.shared.gateways.find(h=>h.id==s.id);u&&(u.id=r.id,u.port=r.port,u.stage=r.stage),console.log(`REST API [${r.id}]`,`http://localhost:${r.port}`),r.server?.unref()}}},buildCallback(d,i){if(i)for(let s of v.servers)s.dropConnections()}};return c};export{Ge as restApiPlugin};
@@ -1 +1 @@
1
- import{_ as v,createSecretsManagerServer as E,getSecretsFromFile as L}from"local-aws-secrets-manager";function O(t){let f=0,u=!0,m=!0,g=!0;t&&(typeof t.port=="number"&&(f=t.port),typeof t.setEndpointEnvVar=="boolean"&&(u=t.setEndpointEnvVar),typeof t.enable=="boolean"&&(m=t.enable),typeof t.rotateOnStart=="boolean"&&(g=t.rotateOnStart));let p=[],b=async()=>{s.shared.isReady=!0;for(let n of p)await n()},r,i,s={name:"secrets-manager-plugin",shared:{isReady:!1,onReady:n=>{typeof n=="function"?p.push(n):console.warn("onReady callback must be a function")}},async onInit(){if(!m)return;let{region:n,accountId:c}=this.getAwsProviderDefaults(),l=this,a=()=>l.lambdas,e=await E({port:f,region:n,accountId:c,secrets:t?.secrets,maxRotationCount:t?.maxRotationCount,rotationIntervalInSeconds:t?.rotationIntervalInSeconds,invokeLambdaForRotation:async(o,S)=>{let I=o.split(":function:")[1],y=a().find(R=>R.name==I);if(!y){console.warn(`Can not find Lambda "${o}" for Secret rotation. Make sure its declared in exlo config.`);return}try{await y.invoke(S)}catch(R){console.error(R)}}});s.shared.port=e.port,r=e.server,r.unref(),i=e[v];let d=`http://localhost:${e.port}`;this.setEndpoint("secrets_manager",d,u?"AWS_ENDPOINT_URL_SECRETS_MANAGER":void 0),this.log.PINK(`SecretsManager endpoint: ${d}`),b()},server:{async onReady(){if(!(g&&t?.secrets))return;let{region:n,accountId:c}=this.getAwsProviderDefaults();for(let[l,a]of Object.entries(t.secrets)){if(typeof a.rotation?.RotationLambdaARN!="string")continue;let e=a.rotation,o={RotationLambdaARN:e.RotationLambdaARN.startsWith("arn:")?e.RotationLambdaARN:`arn:aws:lambda:${n}:${c}:function:${e.RotationLambdaARN}`,SecretId:l};"ClientRequestToken"in e&&(o.ClientRequestToken=e.ClientRequestToken),"RotateImmediately"in e&&(o.RotateImmediately=e.RotateImmediately),"RotationRules"in e&&(o.RotationRules=e.RotationRules),i.constructor.validateRotateSecretBody(o),i.sm.rotateSecret(o)}}},onKill(){r?.close()}};return s}export{L as getSecretsFromFile,O as secretsManagerPlugin};
1
+ import{_ as v,createSecretsManagerServer as E,getSecretsFromFile as L}from"local-aws-secrets-manager";function O(t){let f=0,u=!0,m=!0,y=!0;t&&(typeof t.port=="number"&&(f=t.port),typeof t.setEndpointEnvVar=="boolean"&&(u=t.setEndpointEnvVar),typeof t.enable=="boolean"&&(m=t.enable),typeof t.rotateOnStart=="boolean"&&(y=t.rotateOnStart));let g=[],S=async()=>{a.shared.isReady=!0;for(let n of g)await n()},i,s,a={name:"secrets-manager-plugin",shared:{isReady:!1,onReady:async n=>{typeof n=="function"?a.shared.isReady?await n():g.push(n):console.warn("onReady callback must be a function")}},async onInit(){if(!m)return;let{region:n,accountId:c}=this.getAwsProviderDefaults(),l=this,r=()=>l.lambdas,e=await E({port:f,region:n,accountId:c,secrets:t?.secrets,maxRotationCount:t?.maxRotationCount,rotationIntervalInSeconds:t?.rotationIntervalInSeconds,invokeLambdaForRotation:async(o,b)=>{let I=o.split(":function:")[1],p=r().find(R=>R.name==I);if(!p){console.warn(`Can not find Lambda "${o}" for Secret rotation. Make sure its declared in exlo config.`);return}try{await p.invoke(b)}catch(R){console.error(R)}}});a.shared.port=e.port,i=e.server,i.unref(),s=e[v];let d=`http://localhost:${e.port}`;this.setEndpoint("secrets_manager",d,u?"AWS_ENDPOINT_URL_SECRETS_MANAGER":void 0),this.log.PINK(`SecretsManager endpoint: ${d}`),S()},server:{async onReady(){if(!(y&&t?.secrets))return;let{region:n,accountId:c}=this.getAwsProviderDefaults();for(let[l,r]of Object.entries(t.secrets)){if(typeof r.rotation?.RotationLambdaARN!="string")continue;let e=r.rotation,o={RotationLambdaARN:e.RotationLambdaARN.startsWith("arn:")?e.RotationLambdaARN:`arn:aws:lambda:${n}:${c}:function:${e.RotationLambdaARN}`,SecretId:l};"ClientRequestToken"in e&&(o.ClientRequestToken=e.ClientRequestToken),"RotateImmediately"in e&&(o.RotateImmediately=e.RotateImmediately),"RotationRules"in e&&(o.RotationRules=e.RotationRules),s.constructor.validateRotateSecretBody(o),s.sm.rotateSecret(o)}}},onKill(){i?.close()}};return a}export{L as getSecretsFromFile,O as secretsManagerPlugin};
@@ -1 +1 @@
1
- import{SQS as q}from"@aws-sdk/client-sqs";import{createSqsServer as b}from"local-aws-sqs";var E=e=>{let o,r,s=!1,u=!1,i=!1,f=0,d=!0,y=!0,c=[];e&&(typeof e.validateDlqDestination=="boolean"&&(s=e.validateDlqDestination),typeof e.emulateQueueCreationLifecycle=="boolean"&&(u=e.emulateQueueCreationLifecycle),typeof e.emulateLazyQueues=="boolean"&&(i=e.emulateLazyQueues),Array.isArray(e.queues)&&(c=e.queues),typeof e.port=="number"&&(f=e.port),typeof e.setEndpointEnvVar=="boolean"&&(d=e.setEndpointEnvVar),typeof e.enable=="boolean"&&(y=e.enable));let p=[],S=async()=>{a.shared.isReady=!0;for(let t of p)await t()},n,a={name:"sqs-plugin",shared:{isReady:!1,onReady:t=>{typeof t=="function"?p.push(t):console.warn("onReady callback must be a function")}},onInit:async function(){if(!y)return;let t=this.getAwsProviderDefaults();t.region&&(o=t.region),t.accountId&&(r=t.accountId),n=await b({port:f,region:o,accountId:r,validateDlqDestination:s,emulateQueueCreationLifecycle:u,emulateLazyQueues:i,queues:c}),n.unref();let m=n.address().port;a.shared.port=m;let l=`http://localhost:${m}`;this.getServices().sqs||this.setService("sqs",new q({region:o,endpoint:l,credentials:{accessKeyId:"test",secretAccessKey:"test"}})),this.setEndpoint("sqs",l,d?"AWS_ENDPOINT_URL_SQS":void 0),this.log.PINK(`SQS endpoint: ${l}`),S()},onKill(){n?.close()}};return a};export{E as sqsPlugin};
1
+ import{SQS as q}from"@aws-sdk/client-sqs";import{createSqsServer as b}from"local-aws-sqs";var E=e=>{let a,s,i=!1,r=!1,u=!1,f=0,d=!0,y=!0,c=[];e&&(typeof e.validateDlqDestination=="boolean"&&(i=e.validateDlqDestination),typeof e.emulateQueueCreationLifecycle=="boolean"&&(r=e.emulateQueueCreationLifecycle),typeof e.emulateLazyQueues=="boolean"&&(u=e.emulateLazyQueues),Array.isArray(e.queues)&&(c=e.queues),typeof e.port=="number"&&(f=e.port),typeof e.setEndpointEnvVar=="boolean"&&(d=e.setEndpointEnvVar),typeof e.enable=="boolean"&&(y=e.enable));let p=[],S=async()=>{o.shared.isReady=!0;for(let t of p)await t()},n,o={name:"sqs-plugin",shared:{isReady:!1,onReady:async t=>{typeof t=="function"?o.shared.isReady?await t():p.push(t):console.warn("onReady callback must be a function")}},onInit:async function(){if(!y)return;let t=this.getAwsProviderDefaults();t.region&&(a=t.region),t.accountId&&(s=t.accountId),n=await b({port:f,region:a,accountId:s,validateDlqDestination:i,emulateQueueCreationLifecycle:r,emulateLazyQueues:u,queues:c}),n.unref();let m=n.address().port;o.shared.port=m;let l=`http://localhost:${m}`;this.getServices().sqs||this.setService("sqs",new q({region:a,endpoint:l,credentials:{accessKeyId:"test",secretAccessKey:"test"}})),this.setEndpoint("sqs",l,d?"AWS_ENDPOINT_URL_SQS":void 0),this.log.PINK(`SQS endpoint: ${l}`),S()},onKill(){n?.close()}};return o};export{E as sqsPlugin};
@@ -1 +1 @@
1
- var F=Object.defineProperty;var O=(o,t)=>()=>(o&&(t=o(o=0)),t);var S=(o,t)=>{for(var r in t)F(o,r,{get:t[r],enumerable:!0})};var C={};S(C,{enhanceCoverageProvider:()=>G,generateLambdaCoverage:()=>T,getBaseCoverageProvider:()=>U,initMonocart:()=>D});import{access as j,readFile as A,rm as M}from"node:fs/promises";import{createRequire as B}from"node:module";import u from"node:path";import{fileURLToPath as N,pathToFileURL as P}from"node:url";async function D(){if(f)return;let{CoverageReport:o}=await import(k);f=o}async function T(o,t,r){if(!f)return;let i=u.join(r,"generated"),n=e=>!1;typeof o.coverageProvider?.isIncluded=="function"?n=e=>o.coverageProvider.isIncluded(e):typeof o.coverageProvider?.testExclude?.shouldInstrument=="function"?n=e=>o.coverageProvider.testExclude.shouldInstrument(e):console.warn(`Coverage by Exlo for Vitest ${o.version} isn't supported yet.`);let p=new f({name:"exlo coverage report",dataDir:r,outputDir:i,reports:["json"],cleanCache:!0,clean:!0,v8Ignore:!0,logging:"off",sourceFilter:e=>n(e.startsWith(m)?e:u.join(m,e)),entryFilter:e=>{let a=e.url.startsWith("file:")?N(e.url):e.url;return!n(a)}});try{await p.generate();let e=u.join(i,"coverage-final.json");try{await j(e)}catch{return}let a=JSON.parse(await A(e,"utf-8"));for(let l of Object.keys(a)){let s=u.join(process.cwd(),l);if(!n(s))continue;let c=a[l];c.path=s,t.addFileCoverage(c)}await M(r,{force:!0,recursive:!0})}catch(e){console.log(e)}}async function G(o,t){if(h)return;let r=q[o.config.coverage.provider],i=w.resolve(r.importPath),n=(await import(P(i).href))[r.exportName];n.prototype[y]=n.prototype.createCoverageMap,n.prototype.createCoverageMap=function(...p){let e=this[y](...p);return t.push(e),e},h=!0}async function U(){let o=w.resolve("vitest/coverage");return(await import(P(o).href)).BaseCoverageProvider}var f,k,w,m,q,y,h,x=O(()=>{"use strict";k="monocart-coverage-reports",w=B(process.cwd());m=process.cwd();q={v8:{importPath:"@vitest/coverage-v8/dist/provider.js",exportName:"V8CoverageProvider"},istanbul:{importPath:"@vitest/coverage-istanbul/dist/provider.js",exportName:"IstanbulCoverageProvider"}},y=Symbol(),h=!1});import{randomUUID as _}from"node:crypto";import b from"node:os";import L from"node:path";import{createAppRunner as W}from"exlo";var I="exlo-vitest-plugin",V=Symbol();function $(o){if(o?.length)return Promise.all(o.map(t=>new Promise(r=>{t.shared.onReady(r)})))}var R=!1,J=(o,t,r)=>{let i=r?.reRunOnRebuild??"failed-only";return{name:I,server:{onReady(n,p){o(this.options.plugins)}},async onInit(){if(R)return;t.onServerRestart(async()=>{await this.stop(),console.warn("Exlo isn't compatible with hot config changes. Please restart your tests."),process.exit()}),t.onClose(async()=>{await this.stop()});let n=async()=>{if(!t.config.coverage.enabled||t.config.coverage.provider=="custom")return;await t.packageInstaller.ensureInstalled("monocart-coverage-reports",t.config.root,"^2.12.9");let{generateLambdaCoverage:e,enhanceCoverageProvider:a,initMonocart:l,getBaseCoverageProvider:s}=await Promise.resolve().then(()=>(x(),C));await l();let c=await s(),g=r?.rawCoverageDir??L.join(b.tmpdir(),`l${_()}`);c.prototype[V]=c.prototype.readCoverageFiles;let d=[];c.prototype.readCoverageFiles=async function(...v){return await e(t,d[d.length-1],g),this[V](...v)},await a(t,d),process.env.NODE_V8_COVERAGE=b.tmpdir();for(let v of this.lambdas)v.setEnv("NODE_V8_COVERAGE",g);R=!0},p=this.options.plugins?.filter(e=>e&&typeof e=="object"&&e.shared?.onReady&&!e.shared.isReady);await $(p),await n()},async buildCallback(n,p){let e=n.errors.length;if(!p&&e){await t.close();return}if(e||!p)return;await t.cancelCurrentRun("keyboard-input");let a=[];if(i=="failed-only")for(let l of t.state.getFailedFilepaths()){let s=t.getModuleSpecifications(l);a.push(...s)}else i=="all"&&(a=await t.globTestSpecifications());a.length&&await t.rerunTestSpecifications(a)}}},E=!1;function oe(o,t){return{name:I,async configureVitest(r){if(E)return;E=!0;let i=typeof o=="string"?{config:o}:o,n,p=await new Promise(async l=>{let s=W(i),c=J(l,r.vitest,t);typeof i.config=="string"?s.exlo.onBeforeApplyConfigCallbacks.push(g=>{g.plugins.push(c)}):i.config?i.config.plugins?i.config.plugins.push(c):i.config.plugins=[c]:i.config={plugins:[c]},await s.exlo.start(),await s.waitServer,n=s.exlo.endpoints}),e={endpoints:n},a=p.find(l=>l.name=="rest-api-plugin");if(a?.shared?.gateways){let l=a.shared.gateways;e.restApiGateways={};for(let s of l){let{id:c,port:g,stage:d}=s;e.restApiGateways[c]={port:g,stage:d}}}r.vitest.provide("exlo",e)}}}export{oe as exloVitestPlugin};
1
+ var F=Object.defineProperty;var O=(o,t)=>()=>(o&&(t=o(o=0)),t);var S=(o,t)=>{for(var r in t)F(o,r,{get:t[r],enumerable:!0})};var C={};S(C,{enhanceCoverageProvider:()=>G,generateLambdaCoverage:()=>T,getBaseCoverageProvider:()=>U,initMonocart:()=>D});import{access as j,readFile as A,rm as M}from"node:fs/promises";import{createRequire as B}from"node:module";import u from"node:path";import{fileURLToPath as N,pathToFileURL as P}from"node:url";async function D(){if(f)return;let{CoverageReport:o}=await import(k);f=o}async function T(o,t,r){if(!f)return;let i=u.join(r,"generated"),n=e=>!1;typeof o.coverageProvider?.isIncluded=="function"?n=e=>o.coverageProvider.isIncluded(e):typeof o.coverageProvider?.testExclude?.shouldInstrument=="function"?n=e=>o.coverageProvider.testExclude.shouldInstrument(e):console.warn(`Coverage by Exlo for Vitest ${o.version} isn't supported yet.`);let p=new f({name:"exlo coverage report",dataDir:r,outputDir:i,reports:["json"],cleanCache:!0,clean:!0,v8Ignore:!0,logging:"off",sourceFilter:e=>n(e.startsWith(m)?e:u.join(m,e)),entryFilter:e=>{let a=e.url.startsWith("file:")?N(e.url):e.url;return!n(a)}});try{await p.generate();let e=u.join(i,"coverage-final.json");try{await j(e)}catch{return}let a=JSON.parse(await A(e,"utf-8"));for(let l of Object.keys(a)){let s=u.join(process.cwd(),l);if(!n(s))continue;let c=a[l];c.path=s,t.addFileCoverage(c)}await M(r,{force:!0,recursive:!0})}catch(e){console.log(e)}}async function G(o,t){if(h)return;let r=q[o.config.coverage.provider],i=w.resolve(r.importPath),n=(await import(P(i).href))[r.exportName];n.prototype[y]=n.prototype.createCoverageMap,n.prototype.createCoverageMap=function(...p){let e=this[y](...p);return t.push(e),e},h=!0}async function U(){let o=w.resolve("vitest/coverage");return(await import(P(o).href)).BaseCoverageProvider}var f,k,w,m,q,y,h,x=O(()=>{"use strict";k="monocart-coverage-reports",w=B(import.meta.dirname);m=process.cwd();q={v8:{importPath:"@vitest/coverage-v8/dist/provider.js",exportName:"V8CoverageProvider"},istanbul:{importPath:"@vitest/coverage-istanbul/dist/provider.js",exportName:"IstanbulCoverageProvider"}},y=Symbol(),h=!1});import{randomUUID as _}from"node:crypto";import b from"node:os";import L from"node:path";import{createAppRunner as W}from"exlo";var I="exlo-vitest-plugin",V=Symbol();function $(o){if(o?.length)return Promise.all(o.map(t=>new Promise(r=>{t.shared.onReady(r)})))}var R=!1,J=(o,t,r)=>{let i=r?.reRunOnRebuild??"failed-only";return{name:I,server:{onReady(n,p){o(this.options.plugins)}},async onInit(){if(R)return;t.onServerRestart(async()=>{await this.stop(),console.warn("Exlo isn't compatible with hot config changes. Please restart your tests."),process.exit()}),t.onClose(async()=>{await this.stop()});let n=async()=>{if(!t.config.coverage.enabled||t.config.coverage.provider=="custom")return;await t.packageInstaller.ensureInstalled("monocart-coverage-reports",t.config.root,"^2.12.9");let{generateLambdaCoverage:e,enhanceCoverageProvider:a,initMonocart:l,getBaseCoverageProvider:s}=await Promise.resolve().then(()=>(x(),C));await l();let c=await s(),g=r?.rawCoverageDir??L.join(b.tmpdir(),`l${_()}`);c.prototype[V]=c.prototype.readCoverageFiles;let d=[];c.prototype.readCoverageFiles=async function(...v){return await e(t,d[d.length-1],g),this[V](...v)},await a(t,d),process.env.NODE_V8_COVERAGE=b.tmpdir();for(let v of this.lambdas)v.setEnv("NODE_V8_COVERAGE",g);R=!0},p=this.options.plugins?.filter(e=>e&&typeof e=="object"&&e.shared?.onReady&&!e.shared.isReady);await $(p),await n()},async buildCallback(n,p){let e=n.errors.length;if(!p&&e){await t.close();return}if(e||!p)return;await t.cancelCurrentRun("keyboard-input");let a=[];if(i=="failed-only")for(let l of t.state.getFailedFilepaths()){let s=t.getModuleSpecifications(l);a.push(...s)}else i=="all"&&(a=await t.globTestSpecifications());a.length&&await t.rerunTestSpecifications(a)}}},E=!1;function oe(o,t){return{name:I,async configureVitest(r){if(E)return;E=!0;let i=typeof o=="string"?{config:o}:o,n,p=await new Promise(async l=>{let s=W(i),c=J(l,r.vitest,t);typeof i.config=="string"?s.exlo.onBeforeApplyConfigCallbacks.push(g=>{g.plugins.push(c)}):i.config?i.config.plugins?i.config.plugins.push(c):i.config.plugins=[c]:i.config={plugins:[c]},await s.exlo.start(),await s.waitServer,n=s.exlo.endpoints}),e={endpoints:n},a=p.find(l=>l.name=="rest-api-plugin");if(a?.shared?.gateways){let l=a.shared.gateways;e.restApiGateways={};for(let s of l){let{id:c,port:g,stage:d}=s;e.restApiGateways[c]={port:g,stage:d}}}r.vitest.provide("exlo",e)}}}export{oe as exloVitestPlugin};