@walkeros/server-source-aws 4.4.0-next-1785285516008 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -2
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/dev.mjs +1 -1
- package/dist/dev.mjs.map +1 -1
- package/dist/{index-DNA9kN6b.d.mts → index-GaXbL334.d.mts} +7 -5
- package/dist/{index-DNA9kN6b.d.ts → index-GaXbL334.d.ts} +7 -5
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/sqs/index.d.mts +1 -1
- package/dist/sqs/index.d.ts +1 -1
- package/dist/sqs/index.js +1 -1
- package/dist/sqs/index.js.map +1 -1
- package/dist/sqs/index.mjs +1 -1
- package/dist/sqs/index.mjs.map +1 -1
- package/dist/walkerOS.json +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{SourceLambda:()=>types_exports,SourceSqs:()=>types_exports2,sourceLambda:()=>lambda_default,sourceSqs:()=>sqs_default}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core=require("@walkeros/core");function isAPIGatewayV2(event){return"version"in event&&"2.0"===event.version}function createResponse(statusCode,body,headers={},requestId){const responseHeaders={"Content-Type":"object"==typeof body?"application/json":"text/plain",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:statusCode,headers:responseHeaders,body:"object"==typeof body?JSON.stringify(body):String(body),isBase64Encoded:!1}}var types_exports={},sourceLambda=async context=>{const{config:config={},env:env}=context,userSettings=config.settings||{},settings={...userSettings,cors:userSettings.cors??!0,timeout:userSettings.timeout??3e4,enablePixelTracking:userSettings.enablePixelTracking??!0,healthPath:userSettings.healthPath??"/health"};return{type:"lambda",config:{...config,settings:settings},push:async(event,lambdaContext)=>{const requestId=lambdaContext.awsRequestId;let parsed;try{const corsHeaders=function(corsOptions){if(!corsOptions)return{};if(!0===corsOptions)return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Authorization","Access-Control-Max-Age":"3600"};const headers={};if(corsOptions.origin){const origin=Array.isArray(corsOptions.origin)?corsOptions.origin.join(", "):corsOptions.origin;headers["Access-Control-Allow-Origin"]=origin}return corsOptions.methods&&(headers["Access-Control-Allow-Methods"]=corsOptions.methods.join(", ")),corsOptions.headers&&(headers["Access-Control-Allow-Headers"]=corsOptions.headers.join(", ")),corsOptions.credentials&&(headers["Access-Control-Allow-Credentials"]="true"),void 0!==corsOptions.maxAge&&(headers["Access-Control-Max-Age"]=corsOptions.maxAge.toString()),headers}(settings.cors||!1);parsed=function(event){if(isAPIGatewayV2(event)){const headers={};return event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)}),{method:event.requestContext.http.method,body:event.body,queryString:event.rawQueryString||null,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}{const headers={};event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)});let queryString=null;if(event.queryStringParameters){const params=new URLSearchParams;Object.entries(event.queryStringParameters).forEach(([key,value])=>{value&¶ms.append(key,value)}),queryString=params.toString()||null}return{method:event.httpMethod,body:event.body,queryString:queryString,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}}(event);const path=function(event){return isAPIGatewayV2(event)?event.rawPath:event.path}(event);return settings.healthPath&&path===settings.healthPath?createResponse(200,{status:"ok",timestamp:Date.now(),source:"lambda",requestId:requestId},corsHeaders,requestId):"OPTIONS"===parsed.method?createResponse(204,"",corsHeaders,requestId):await context.withScope(event,void 0,async scopeEnv=>{const envPush=scopeEnv.push;if("GET"===parsed.method){if(!settings.enablePixelTracking)return createResponse(405,{success:!1,error:"GET not allowed",requestId:requestId},corsHeaders,requestId);if(parsed.queryString){const parsedData=(0,import_core.requestToData)(parsed.queryString);parsedData&&"object"==typeof parsedData&&await envPush(parsedData)}return function(headers={},requestId){const responseHeaders={"Content-Type":"image/gif","Cache-Control":"no-cache, no-store, must-revalidate",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:200,headers:responseHeaders,body:"R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",isBase64Encoded:!0}}(corsHeaders,requestId)}if("POST"===parsed.method){if(!parsed.body)return createResponse(400,{success:!1,error:"Request body is required",requestId:requestId},corsHeaders,requestId);const body=function(body,isBase64Encoded){if(!body||"string"!=typeof body)return body;try{const decoded=isBase64Encoded?Buffer.from(body,"base64").toString("utf8"):body;return JSON.parse(decoded)}catch{return body}}(parsed.body,parsed.isBase64Encoded);if(!body||"object"!=typeof body)return await envPush({}),createResponse(200,{success:!0,requestId:requestId},corsHeaders,requestId);if(function(body){return"object"==typeof body&&null!==body&&"event"in body&&"string"==typeof body.event}(body)){const result=await async function(eventReq,push,logger,requestId){try{const result=await push({name:eventReq.event,data:eventReq.data||{},context:eventReq.context,user:eventReq.user,globals:eventReq.globals,consent:eventReq.consent});return{id:result?.event?.id}}catch(error){return logger?.error("Event processing failed",{error:error,eventName:eventReq.event,requestId:requestId}),{error:error instanceof Error?error.message:"Unknown error"}}}(body,envPush,env.logger,requestId);return result.error?createResponse(400,{success:!1,error:result.error,requestId:requestId},corsHeaders,requestId):createResponse(200,{success:!0,id:result.id,requestId:requestId},corsHeaders,requestId)}return createResponse(400,{success:!1,error:"Invalid request format",requestId:requestId},corsHeaders,requestId)}return createResponse(405,{success:!1,error:"Method not allowed",requestId:requestId},corsHeaders,requestId)})}catch(error){return env.logger?.error("Lambda handler error",{error:error,requestId:requestId,method:parsed?.method}),createResponse(500,{success:!1,error:error instanceof Error?error.message:"Internal server error",requestId:requestId},{},requestId)}}}},lambda_default=sourceLambda,import_client_sqs3=require("@aws-sdk/client-sqs"),DecoderError=class extends Error{constructor(message){super(message),this.name="DecoderError"}};var import_client_sqs=require("@aws-sdk/client-sqs");var import_client_sqs2=require("@aws-sdk/client-sqs"),import_client_sns=require("@aws-sdk/client-sns"),import_core2=require("@walkeros/core"),DEFAULT_SETUP={region:"eu-central-1",fifoQueue:!1,visibilityTimeoutSeconds:30,messageRetentionSeconds:345600,maximumMessageSize:262144};function buildDlqAttributes(args){const attrs={};return attrs.MessageRetentionPeriod=String(1209600),args.kmsMasterKeyId&&(attrs.KmsMasterKeyId=args.kmsMasterKeyId),args.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),attrs}async function ensureQueue(args){const{sqs:sqs,logger:logger,queueName:queueName,attributes:attributes,tags:tags}=args;let queueUrl,queueCreated=!1;try{queueUrl=(await sqs.send(new import_client_sqs2.CreateQueueCommand({QueueName:queueName,Attributes:attributes,tags:tags}))).QueueUrl??"",queueCreated=!0,logger.info(`SQS queue "${queueName}" created.`)}catch(err){if(function(err){return!!function(err){return"object"==typeof err&&null!==err&&"string"==typeof err.name}(err)&&("QueueNameExists"===err.name||"QueueAlreadyExists"===err.name||"AWS.SimpleQueueService.QueueNameExists"===err.name)}(err)){const message=err instanceof Error?err.message:String(err);return logger.throw(`SQS queue "${queueName}" exists with different attributes (${message}). Setup will not mutate an existing queue's attributes. Delete or rename the queue, then re-run setup.`)}throw err}if(!queueUrl)return logger.throw(`SQS CreateQueueCommand returned no QueueUrl for "${queueName}".`);const attrsRes=await sqs.send(new import_client_sqs2.GetQueueAttributesCommand({QueueUrl:queueUrl,AttributeNames:["QueueArn"]})),queueArn=attrsRes.Attributes?.QueueArn??"";return queueArn?{queueCreated:queueCreated,queueUrl:queueUrl,queueArn:queueArn}:logger.throw(`SQS GetQueueAttributesCommand returned no QueueArn for "${queueName}".`)}function dlqName(queueName,fifo){if(fifo){return`${queueName.endsWith(".fifo")?queueName.slice(0,-5):queueName}-dlq.fifo`}return`${queueName}-dlq`}var setup=async context=>{const{id:id,config:config,env:env,logger:logger}=context,merged=(0,import_core2.resolveSetup)(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("SQS source setup skipped (config.setup is falsy).");const options={...DEFAULT_SETUP,...merged},settings=config.settings;if(!settings)return logger.throw("setup: settings missing");const queueName=settings.queueName;if(!queueName)return logger.throw("setup: settings.queueName is missing");const region=options.region??"eu-central-1",sqs=new(env.AWS?.SQSClient??import_client_sqs2.SQSClient)({region:region});let dlqArn,dlqCreated=!1;if(options.deadLetterQueue?.arn)dlqArn=options.deadLetterQueue.arn;else if(options.deadLetterQueue?.create){const dlqResult=await ensureQueue({sqs:sqs,logger:logger,queueName:dlqName(queueName,options.fifoQueue??!1),attributes:buildDlqAttributes({fifoQueue:options.fifoQueue??!1,kmsMasterKeyId:options.kmsMasterKeyId}),tags:(parentTags=options.tags,{...parentTags??{},walkerOS:"dlq"})});dlqArn=dlqResult.queueArn,dlqCreated=dlqResult.queueCreated}var parentTags;const finalQueueName=options.fifoQueue?queueName.endsWith(".fifo")?queueName:`${queueName}.fifo`:queueName;var args;const mainAttrs=function(args){const{options:options,dlqArn:dlqArn,queuePolicy:queuePolicy}=args,attrs={};if(attrs.VisibilityTimeout=String(options.visibilityTimeoutSeconds??30),attrs.MessageRetentionPeriod=String(options.messageRetentionSeconds??345600),attrs.MaximumMessageSize=String(options.maximumMessageSize??262144),options.kmsMasterKeyId&&(attrs.KmsMasterKeyId=options.kmsMasterKeyId),options.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),dlqArn){const maxReceiveCount=options.deadLetterQueue?.maxReceiveCount??5;attrs.RedrivePolicy=JSON.stringify({deadLetterTargetArn:dlqArn,maxReceiveCount:maxReceiveCount})}return queuePolicy&&(attrs.Policy=queuePolicy),attrs}({options:options,dlqArn:dlqArn,queuePolicy:options.subscribeToSnsTopic?(args={sourceId:id,topicArn:options.subscribeToSnsTopic.topicArn},JSON.stringify({Version:"2012-10-17",Statement:[{Sid:`walkerOSAllowSNSPublish-${args.sourceId}`,Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"SQS:SendMessage",Resource:"*",Condition:{ArnEquals:{"aws:SourceArn":args.topicArn}}}]})):void 0}),mainResult=await ensureQueue({sqs:sqs,logger:logger,queueName:finalQueueName,attributes:mainAttrs,tags:options.tags});let subscriptionArn;if(options.subscribeToSnsTopic){const sns=new(env.AWS?.SNSClient??import_client_sns.SNSClient)({region:region}),subAttrs={};options.subscribeToSnsTopic.rawMessageDelivery&&(subAttrs.RawMessageDelivery="true"),options.subscribeToSnsTopic.filterPolicy&&(subAttrs.FilterPolicy=JSON.stringify(options.subscribeToSnsTopic.filterPolicy));subscriptionArn=(await sns.send(new import_client_sns.SubscribeCommand({TopicArn:options.subscribeToSnsTopic.topicArn,Protocol:"sqs",Endpoint:mainResult.queueArn,Attributes:Object.keys(subAttrs).length>0?subAttrs:void 0,ReturnSubscriptionArn:!0}))).SubscriptionArn??void 0,logger.info("SQS source setup: SNS subscription created",{topicArn:options.subscribeToSnsTopic.topicArn,subscriptionArn:subscriptionArn})}return{queueCreated:mainResult.queueCreated,queueUrl:mainResult.queueUrl,queueArn:mainResult.queueArn,dlqCreated:dlqArn?dlqCreated:void 0,dlqArn:dlqArn,subscriptionArn:subscriptionArn}};async function handleMessage(args){const{message:message,settings:settings,pushFn:pushFn,logger:logger}=args;try{const decoded=function(messageId,body,decoder){if("raw"===decoder)return Buffer.from(body,"utf8");if("text"===decoder)return body;try{return JSON.parse(body)}catch(err){throw new DecoderError(`SQS message ${messageId} JSON decode failed: ${err instanceof Error?err.message:String(err)}`)}}(message.id,message.body,settings.decoder??"json");if(null==decoded)return logger.debug("SQS message acked-and-dropped (decoder returned null)",{id:message.id}),void await message.ack();await pushFn((value=decoded,"object"==typeof value&&null!==value?value:"string"==typeof value?{data:{payload:value}}:Buffer.isBuffer(value)?{data:{payload:value.toString("base64")}}:{data:{payload:String(value)}})),await message.ack()}catch(err){const error=err instanceof Error?err.message:String(err);err instanceof DecoderError?logger.error("SQS message decode failed",{id:message.id,error:error}):logger.error("SQS message handler failed",{id:message.id,error:error}),"ack"===settings.onPushError?await message.ack():await message.nack()}var value}function isQueueDoesNotExist(err){if("object"!=typeof err||null===err)return!1;if(!("name"in err))return!1;const obj=err;return"QueueDoesNotExist"===obj.name||"AWS.SimpleQueueService.NonExistentQueue"===obj.name}function isFetchedMessage(value){if("object"!=typeof value||null===value)return!1;const obj=value;return"string"==typeof obj.MessageId&&"string"==typeof obj.ReceiptHandle&&"string"==typeof obj.Body}var sourceSqs=async context=>{const{config:partialConfig={},env:env,logger:logger,id:id}=context,config=function(partial={},env,logger){const partialSettings=partial.settings??{},queueName="string"==typeof partialSettings.queueName?partialSettings.queueName:"";queueName||logger.throw("Config settings queueName missing");const region=partialSettings.region??"eu-central-1";let client=partialSettings.client;client||(client=new(env?.AWS?.SQSClient??import_client_sqs.SQSClient)({region:region,...partialSettings.config}));const settings={...partialSettings,client:client,queueName:queueName,region:region,queueUrl:partialSettings.queueUrl,config:partialSettings.config,decoder:partialSettings.decoder??"json",maxMessages:partialSettings.maxMessages??10,waitTimeSeconds:partialSettings.waitTimeSeconds??20,visibilityTimeout:partialSettings.visibilityTimeout,shutdownTimeoutMs:partialSettings.shutdownTimeoutMs??3e4,onPushError:partialSettings.onPushError??"nack"};return{...partial,settings:settings}}(partialConfig,env,logger),settings=config.settings;if(!settings.queueUrl)try{const res=await settings.client.send(new import_client_sqs3.GetQueueUrlCommand({QueueName:settings.queueName}));if(!res.QueueUrl)return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);settings.queueUrl=res.QueueUrl}catch(err){if(isQueueDoesNotExist(err))return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);throw err}if(!settings.queueArn)try{const res=await settings.client.send(new import_client_sqs3.GetQueueAttributesCommand({QueueUrl:settings.queueUrl,AttributeNames:["QueueArn"]}));settings.queueArn=res.Attributes?.QueueArn}catch(err){logger.debug("SQS GetQueueAttributesCommand failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}let stopped=!1;const inFlight=new Set,loopPromise=(async()=>{for(;!stopped;)try{const messages=(await settings.client.send(new import_client_sqs3.ReceiveMessageCommand({QueueUrl:settings.queueUrl,MaxNumberOfMessages:settings.maxMessages??10,WaitTimeSeconds:settings.waitTimeSeconds??20,VisibilityTimeout:settings.visibilityTimeout,MessageAttributeNames:["All"],AttributeNames:["All"]}))).Messages??[];for(const msg of messages){if(!isFetchedMessage(msg)){logger.warn("SQS received malformed message; skipping",{messageId:"string"==typeof msg.MessageId?msg.MessageId:void 0});continue}const fetched=msg,handler=handleMessage({message:{id:fetched.MessageId,body:fetched.Body,receiptHandle:fetched.ReceiptHandle,ack:async()=>{await settings.client.send(new import_client_sqs3.DeleteMessageCommand({QueueUrl:settings.queueUrl??"",ReceiptHandle:fetched.ReceiptHandle}))},nack:async()=>{}},settings:settings,pushFn:env.push,logger:logger});inFlight.add(handler),handler.finally(()=>{inFlight.delete(handler)})}}catch(err){if(stopped)break;const error=err instanceof Error?err.message:String(err);if(isQueueDoesNotExist(err)){logger.error(`SQS queue "${settings.queueName}" not found at runtime. Run "walkeros setup source.${id}" to create it. Original: ${error}`),stopped=!0;break}logger.error("SQS receive error (will retry)",{error:error}),await new Promise(r=>setTimeout(r,1e3))}})();return{type:"sqs",config:config,setup:setup,push:async content=>{if(!content)return;const result={acked:!1,nacked:!1};return await handleMessage({message:{id:content.MessageId,body:content.Body,receiptHandle:content.ReceiptHandle,ack:async()=>{result.acked=!0},nack:async()=>{result.nacked=!0}},settings:settings,pushFn:env.push,logger:logger}),result},destroy:async()=>{stopped=!0;const timeoutMs=settings.shutdownTimeoutMs??3e4;let timer;try{await Promise.race([(async()=>{await loopPromise,await Promise.all(Array.from(inFlight))})(),new Promise(resolve=>{timer=setTimeout(()=>{logger.warn(`SQS source close timed out after ${timeoutMs}ms; forcing close.`),resolve()},timeoutMs)})])}finally{timer&&clearTimeout(timer),function(client,logger){const candidate=client;if("function"==typeof candidate.destroy)try{candidate.destroy()}catch(err){logger.debug("SQS client destroy failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}}(settings.client,logger)}}}},sqs_default=sourceSqs,types_exports2={};//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{SourceLambda:()=>types_exports,SourceSqs:()=>types_exports2,sourceLambda:()=>lambda_default,sourceSqs:()=>sqs_default}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core=require("@walkeros/core");function isAPIGatewayV2(event){return"version"in event&&"2.0"===event.version}function createResponse(statusCode,body,headers={},requestId){const responseHeaders={"Content-Type":"object"==typeof body?"application/json":"text/plain",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:statusCode,headers:responseHeaders,body:"object"==typeof body?JSON.stringify(body):String(body),isBase64Encoded:!1}}var types_exports={},sourceLambda=async context=>{const{config:config={},env:env}=context,userSettings=config.settings||{},settings={...userSettings,cors:userSettings.cors??!0,timeout:userSettings.timeout??3e4,enablePixelTracking:userSettings.enablePixelTracking??!0,healthPath:userSettings.healthPath??"/health"};return{type:"lambda",config:{...config,settings:settings},push:async(event,lambdaContext)=>{const requestId=lambdaContext.awsRequestId;let parsed;try{const corsHeaders=function(corsOptions){if(!corsOptions)return{};if(!0===corsOptions)return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Authorization","Access-Control-Max-Age":"3600"};const headers={};if(corsOptions.origin){const origin=Array.isArray(corsOptions.origin)?corsOptions.origin.join(", "):corsOptions.origin;headers["Access-Control-Allow-Origin"]=origin}return corsOptions.methods&&(headers["Access-Control-Allow-Methods"]=corsOptions.methods.join(", ")),corsOptions.headers&&(headers["Access-Control-Allow-Headers"]=corsOptions.headers.join(", ")),corsOptions.credentials&&(headers["Access-Control-Allow-Credentials"]="true"),void 0!==corsOptions.maxAge&&(headers["Access-Control-Max-Age"]=corsOptions.maxAge.toString()),headers}(settings.cors||!1);parsed=function(event){if(isAPIGatewayV2(event)){const headers={};return event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)}),{method:event.requestContext.http.method,body:event.body,queryString:event.rawQueryString||null,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}{const headers={};event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)});let queryString=null;if(event.queryStringParameters){const params=new URLSearchParams;Object.entries(event.queryStringParameters).forEach(([key,value])=>{value&¶ms.append(key,value)}),queryString=params.toString()||null}return{method:event.httpMethod,body:event.body,queryString:queryString,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}}(event);const path=function(event){return isAPIGatewayV2(event)?event.rawPath:event.path}(event);return settings.healthPath&&path===settings.healthPath?createResponse(200,{status:"ok",timestamp:Date.now(),source:"lambda",requestId:requestId},corsHeaders,requestId):"OPTIONS"===parsed.method?createResponse(204,"",corsHeaders,requestId):await context.withScope(event,void 0,async scopeEnv=>{const envPush=scopeEnv.push;if("GET"===parsed.method){if(!settings.enablePixelTracking)return createResponse(405,{success:!1,error:"GET not allowed",requestId:requestId},corsHeaders,requestId);if(parsed.queryString){const parsedData=(0,import_core.requestToData)(parsed.queryString);parsedData&&"object"==typeof parsedData&&await envPush(parsedData)}return function(headers={},requestId){const responseHeaders={"Content-Type":"image/gif","Cache-Control":"no-cache, no-store, must-revalidate",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:200,headers:responseHeaders,body:"R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",isBase64Encoded:!0}}(corsHeaders,requestId)}if("POST"===parsed.method){if(!parsed.body)return createResponse(400,{success:!1,error:"Request body is required",requestId:requestId},corsHeaders,requestId);const body=function(body,isBase64Encoded){if(!body||"string"!=typeof body)return body;try{const decoded=isBase64Encoded?Buffer.from(body,"base64").toString("utf8"):body;return JSON.parse(decoded)}catch{return body}}(parsed.body,parsed.isBase64Encoded);if(!body||"object"!=typeof body)return await envPush({}),createResponse(200,{success:!0,requestId:requestId},corsHeaders,requestId);if(function(body){return"object"==typeof body&&null!==body&&"event"in body&&"string"==typeof body.event}(body)){const result=await async function(eventReq,push,logger,requestId){try{const result=await push({name:eventReq.event,data:eventReq.data||{},context:eventReq.context,user:eventReq.user,globals:eventReq.globals,consent:eventReq.consent});return{id:result?.event?.id}}catch(error){return logger?.error("Event processing failed",{error:error,eventName:eventReq.event,requestId:requestId}),{error:error instanceof Error?error.message:"Unknown error"}}}(body,envPush,env.logger,requestId);return result.error?createResponse(400,{success:!1,error:result.error,requestId:requestId},corsHeaders,requestId):createResponse(200,{success:!0,id:result.id,requestId:requestId},corsHeaders,requestId)}return createResponse(400,{success:!1,error:"Invalid request format",requestId:requestId},corsHeaders,requestId)}return createResponse(405,{success:!1,error:"Method not allowed",requestId:requestId},corsHeaders,requestId)})}catch(error){return env.logger?.error("Lambda handler error",{error:error,requestId:requestId,method:parsed?.method}),createResponse(500,{success:!1,error:error instanceof Error?error.message:"Internal server error",requestId:requestId},{},requestId)}}}},lambda_default=sourceLambda,import_client_sqs3=require("@aws-sdk/client-sqs"),DecoderError=class extends Error{constructor(message){super(message),this.name="DecoderError"}};var import_client_sqs=require("@aws-sdk/client-sqs");var import_client_sqs2=require("@aws-sdk/client-sqs"),import_client_sns=require("@aws-sdk/client-sns"),import_core2=require("@walkeros/core"),DEFAULT_SETUP={region:"eu-central-1",fifoQueue:!1,visibilityTimeoutSeconds:30,messageRetentionSeconds:345600,maximumMessageSize:262144};function buildDlqAttributes(args){const attrs={};return attrs.MessageRetentionPeriod=String(1209600),args.kmsMasterKeyId&&(attrs.KmsMasterKeyId=args.kmsMasterKeyId),args.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),attrs}async function ensureQueue(args){const{sqs:sqs,logger:logger,queueName:queueName,attributes:attributes,tags:tags}=args;let queueUrl,queueCreated=!1;try{queueUrl=(await sqs.send(new import_client_sqs2.CreateQueueCommand({QueueName:queueName,Attributes:attributes,tags:tags}))).QueueUrl??"",queueCreated=!0,logger.info(`SQS queue "${queueName}" created.`)}catch(err){if(function(err){return!!function(err){return"object"==typeof err&&null!==err&&"string"==typeof err.name}(err)&&("QueueNameExists"===err.name||"QueueAlreadyExists"===err.name||"AWS.SimpleQueueService.QueueNameExists"===err.name)}(err)){const message=err instanceof Error?err.message:String(err);return logger.throw(`SQS queue "${queueName}" exists with different attributes (${message}). Setup will not mutate an existing queue's attributes. Delete or rename the queue, then re-run setup.`)}throw err}if(!queueUrl)return logger.throw(`SQS CreateQueueCommand returned no QueueUrl for "${queueName}".`);const attrsRes=await sqs.send(new import_client_sqs2.GetQueueAttributesCommand({QueueUrl:queueUrl,AttributeNames:["QueueArn"]})),queueArn=attrsRes.Attributes?.QueueArn??"";return queueArn?{queueCreated:queueCreated,queueUrl:queueUrl,queueArn:queueArn}:logger.throw(`SQS GetQueueAttributesCommand returned no QueueArn for "${queueName}".`)}function dlqName(queueName,fifo){if(fifo){return`${queueName.endsWith(".fifo")?queueName.slice(0,-5):queueName}-dlq.fifo`}return`${queueName}-dlq`}var setup=async context=>{const{id:id,config:config,env:env,logger:logger}=context,merged=(0,import_core2.resolveSetup)(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("SQS source setup skipped (config.setup is falsy).");const options={...DEFAULT_SETUP,...merged},settings=config.settings;if(!settings)return logger.throw("setup: settings missing");const queueName=settings.queueName;if(!queueName)return logger.throw("setup: settings.queueName is missing");const region=options.region??"eu-central-1",sqs=new(env.AWS?.SQSClient??import_client_sqs2.SQSClient)({region:region});let dlqArn,dlqCreated=!1;if(options.deadLetterQueue?.arn)dlqArn=options.deadLetterQueue.arn;else if(options.deadLetterQueue?.create){const dlqResult=await ensureQueue({sqs:sqs,logger:logger,queueName:dlqName(queueName,options.fifoQueue??!1),attributes:buildDlqAttributes({fifoQueue:options.fifoQueue??!1,kmsMasterKeyId:options.kmsMasterKeyId}),tags:(parentTags=options.tags,{...parentTags??{},walkerOS:"dlq"})});dlqArn=dlqResult.queueArn,dlqCreated=dlqResult.queueCreated}var parentTags;const finalQueueName=options.fifoQueue?queueName.endsWith(".fifo")?queueName:`${queueName}.fifo`:queueName;var args;const mainAttrs=function(args){const{options:options,dlqArn:dlqArn,queuePolicy:queuePolicy}=args,attrs={};if(attrs.VisibilityTimeout=String(options.visibilityTimeoutSeconds??30),attrs.MessageRetentionPeriod=String(options.messageRetentionSeconds??345600),attrs.MaximumMessageSize=String(options.maximumMessageSize??262144),options.kmsMasterKeyId&&(attrs.KmsMasterKeyId=options.kmsMasterKeyId),options.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),dlqArn){const maxReceiveCount=options.deadLetterQueue?.maxReceiveCount??5;attrs.RedrivePolicy=JSON.stringify({deadLetterTargetArn:dlqArn,maxReceiveCount:maxReceiveCount})}return queuePolicy&&(attrs.Policy=queuePolicy),attrs}({options:options,dlqArn:dlqArn,queuePolicy:options.subscribeToSnsTopic?(args={sourceId:id,topicArn:options.subscribeToSnsTopic.topicArn},JSON.stringify({Version:"2012-10-17",Statement:[{Sid:`walkerOSAllowSNSPublish-${args.sourceId}`,Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"SQS:SendMessage",Resource:"*",Condition:{ArnEquals:{"aws:SourceArn":args.topicArn}}}]})):void 0}),mainResult=await ensureQueue({sqs:sqs,logger:logger,queueName:finalQueueName,attributes:mainAttrs,tags:options.tags});let subscriptionArn;if(options.subscribeToSnsTopic){const sns=new(env.AWS?.SNSClient??import_client_sns.SNSClient)({region:region}),subAttrs={};options.subscribeToSnsTopic.rawMessageDelivery&&(subAttrs.RawMessageDelivery="true"),options.subscribeToSnsTopic.filterPolicy&&(subAttrs.FilterPolicy=JSON.stringify(options.subscribeToSnsTopic.filterPolicy));subscriptionArn=(await sns.send(new import_client_sns.SubscribeCommand({TopicArn:options.subscribeToSnsTopic.topicArn,Protocol:"sqs",Endpoint:mainResult.queueArn,Attributes:Object.keys(subAttrs).length>0?subAttrs:void 0,ReturnSubscriptionArn:!0}))).SubscriptionArn??void 0,logger.info("SQS source setup: SNS subscription created",{topicArn:options.subscribeToSnsTopic.topicArn,subscriptionArn:subscriptionArn})}return{queueCreated:mainResult.queueCreated,queueUrl:mainResult.queueUrl,queueArn:mainResult.queueArn,dlqCreated:dlqArn?dlqCreated:void 0,dlqArn:dlqArn,subscriptionArn:subscriptionArn}};async function handleMessage(args){const{message:message,settings:settings,pushFn:pushFn,logger:logger}=args;try{const decoded=function(messageId,body,decoder){if("raw"===decoder)return Buffer.from(body,"utf8");if("text"===decoder)return body;try{return JSON.parse(body)}catch(err){throw new DecoderError(`SQS message ${messageId} JSON decode failed: ${err instanceof Error?err.message:String(err)}`)}}(message.id,message.body,settings.decoder??"json");if(null==decoded)return logger.debug("SQS message acked-and-dropped (decoder returned null)",{id:message.id}),void await message.ack();const result=await pushFn((value=decoded,"object"==typeof value&&null!==value?value:"string"==typeof value?{data:{payload:value}}:Buffer.isBuffer(value)?{data:{payload:value.toString("base64")}}:{data:{payload:String(value)}}));if(!1===result?.ok)throw new Error("push rejected the message (ok:false)");await message.ack()}catch(err){const error=err instanceof Error?err.message:String(err);err instanceof DecoderError?logger.error("SQS message decode failed",{id:message.id,error:error}):logger.error("SQS message handler failed",{id:message.id,error:error}),"ack"===settings.onPushError?await message.ack():await message.nack()}var value}function isQueueDoesNotExist(err){if("object"!=typeof err||null===err)return!1;if(!("name"in err))return!1;const obj=err;return"QueueDoesNotExist"===obj.name||"AWS.SimpleQueueService.NonExistentQueue"===obj.name}function isFetchedMessage(value){if("object"!=typeof value||null===value)return!1;const obj=value;return"string"==typeof obj.MessageId&&"string"==typeof obj.ReceiptHandle&&"string"==typeof obj.Body}var sourceSqs=async context=>{const{config:partialConfig={},env:env,logger:logger,id:id}=context,config=function(partial={},env,logger){const partialSettings=partial.settings??{},queueName="string"==typeof partialSettings.queueName?partialSettings.queueName:"";queueName||logger.throw("Config settings queueName missing");const region=partialSettings.region??"eu-central-1";let client=partialSettings.client;client||(client=new(env?.AWS?.SQSClient??import_client_sqs.SQSClient)({region:region,...partialSettings.config}));const settings={...partialSettings,client:client,queueName:queueName,region:region,queueUrl:partialSettings.queueUrl,config:partialSettings.config,decoder:partialSettings.decoder??"json",maxMessages:partialSettings.maxMessages??10,waitTimeSeconds:partialSettings.waitTimeSeconds??20,visibilityTimeout:partialSettings.visibilityTimeout,shutdownTimeoutMs:partialSettings.shutdownTimeoutMs??3e4,onPushError:partialSettings.onPushError??"nack"};return{...partial,settings:settings}}(partialConfig,env,logger),settings=config.settings;if(!settings.queueUrl)try{const res=await settings.client.send(new import_client_sqs3.GetQueueUrlCommand({QueueName:settings.queueName}));if(!res.QueueUrl)return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);settings.queueUrl=res.QueueUrl}catch(err){if(isQueueDoesNotExist(err))return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);throw err}if(!settings.queueArn)try{const res=await settings.client.send(new import_client_sqs3.GetQueueAttributesCommand({QueueUrl:settings.queueUrl,AttributeNames:["QueueArn"]}));settings.queueArn=res.Attributes?.QueueArn}catch(err){logger.debug("SQS GetQueueAttributesCommand failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}let stopped=!1;const inFlight=new Set;let loopPromise;const startConsuming=()=>{loopPromise||(loopPromise=(async()=>{for(;!stopped;)try{const messages=(await settings.client.send(new import_client_sqs3.ReceiveMessageCommand({QueueUrl:settings.queueUrl,MaxNumberOfMessages:settings.maxMessages??10,WaitTimeSeconds:settings.waitTimeSeconds??20,VisibilityTimeout:settings.visibilityTimeout,MessageAttributeNames:["All"],AttributeNames:["All"]}))).Messages??[];for(const msg of messages){if(!isFetchedMessage(msg)){logger.warn("SQS received malformed message; skipping",{messageId:"string"==typeof msg.MessageId?msg.MessageId:void 0});continue}const fetched=msg,handler=handleMessage({message:{id:fetched.MessageId,body:fetched.Body,receiptHandle:fetched.ReceiptHandle,ack:async()=>{await settings.client.send(new import_client_sqs3.DeleteMessageCommand({QueueUrl:settings.queueUrl??"",ReceiptHandle:fetched.ReceiptHandle}))},nack:async()=>{}},settings:settings,pushFn:env.push,logger:logger});inFlight.add(handler),handler.finally(()=>{inFlight.delete(handler)})}}catch(err){if(stopped)break;const error=err instanceof Error?err.message:String(err);if(isQueueDoesNotExist(err)){logger.error(`SQS queue "${settings.queueName}" not found at runtime. Run "walkeros setup source.${id}" to create it. Original: ${error}`),stopped=!0;break}logger.error("SQS receive error (will retry)",{error:error}),await new Promise(r=>setTimeout(r,1e3))}})())};return{type:"sqs",config:config,setup:setup,on:type=>{"run"===type&&startConsuming()},push:async content=>{if(!content)return;const result={acked:!1,nacked:!1};return await handleMessage({message:{id:content.MessageId,body:content.Body,receiptHandle:content.ReceiptHandle,ack:async()=>{result.acked=!0},nack:async()=>{result.nacked=!0}},settings:settings,pushFn:env.push,logger:logger}),result},destroy:async()=>{stopped=!0;const timeoutMs=settings.shutdownTimeoutMs??3e4;let timer;try{await Promise.race([(async()=>{await loopPromise,await Promise.all(Array.from(inFlight))})(),new Promise(resolve=>{timer=setTimeout(()=>{logger.warn(`SQS source close timed out after ${timeoutMs}ms; forcing close.`),resolve()},timeoutMs)})])}finally{timer&&clearTimeout(timer),function(client,logger){const candidate=client;if("function"==typeof candidate.destroy)try{candidate.destroy()}catch(err){logger.debug("SQS client destroy failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}}(settings.client,logger)}}}},sqs_default=sourceSqs,types_exports2={};//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lambda/index.ts","../src/lambda/utils.ts","../src/lambda/push.ts","../src/lambda/types.ts","../src/sqs/index.ts","../src/sqs/decoder.ts","../src/sqs/config.ts","../src/sqs/setup.ts","../src/sqs/types.ts"],"sourcesContent":["export * from './lambda';\nexport { default as sourceLambda } from './lambda';\nexport { default as sourceSqs } from './sqs';\nexport * as SourceSqs from './sqs/types';\n","import type { LambdaSource, EventRequest, Types } from './types';\nimport type { Source } from '@walkeros/core';\nimport { requestToData } from '@walkeros/core';\nimport {\n parseEvent,\n parseBody,\n isEventRequest,\n getCorsHeaders,\n createResponse,\n createPixelResponse,\n getPath,\n} from './utils';\nimport { processEvent } from './push';\n\nexport * as SourceLambda from './types';\n\nexport const sourceLambda: Source.Init<Types> = async (context) => {\n const { config = {}, env } = context;\n\n const userSettings = config.settings || {};\n const settings = {\n ...userSettings,\n cors: userSettings.cors ?? true,\n timeout: userSettings.timeout ?? 30000,\n enablePixelTracking: userSettings.enablePixelTracking ?? true,\n healthPath: userSettings.healthPath ?? '/health',\n };\n\n const fullConfig: Source.Config<Types> = {\n ...config,\n settings,\n };\n\n const push: Types['push'] = async (event, lambdaContext) => {\n const requestId = lambdaContext.awsRequestId;\n let parsed;\n\n try {\n const corsHeaders = getCorsHeaders(settings.cors || false);\n parsed = parseEvent(event);\n const path = getPath(event);\n\n // Health check\n if (settings.healthPath && path === settings.healthPath) {\n return createResponse(\n 200,\n {\n status: 'ok',\n timestamp: Date.now(),\n source: 'lambda',\n requestId,\n },\n corsHeaders,\n requestId,\n );\n }\n\n // Handle OPTIONS for CORS preflight\n if (parsed.method === 'OPTIONS') {\n return createResponse(204, '', corsHeaders, requestId);\n }\n\n // Per-invocation scope: each Lambda invocation gets its own ingest.\n // No respond fn — Lambda returns the response directly from this\n // handler, not via async respond.\n return await context.withScope(event, undefined, async (scopeEnv) => {\n const envPush = scopeEnv.push;\n\n // Handle GET for pixel tracking\n if (parsed!.method === 'GET') {\n if (!settings.enablePixelTracking) {\n return createResponse(\n 405,\n { success: false, error: 'GET not allowed', requestId },\n corsHeaders,\n requestId,\n );\n }\n if (parsed!.queryString) {\n const parsedData = requestToData(parsed!.queryString);\n if (parsedData && typeof parsedData === 'object') {\n await envPush(parsedData);\n }\n }\n return createPixelResponse(corsHeaders, requestId);\n }\n\n // Handle POST for event data\n if (parsed!.method === 'POST') {\n if (!parsed!.body) {\n return createResponse(\n 400,\n { success: false, error: 'Request body is required', requestId },\n corsHeaders,\n requestId,\n );\n }\n\n const body = parseBody(parsed!.body, parsed!.isBase64Encoded);\n\n // If body is not a valid object, push {} to let source.before transformers handle raw input via ingest\n if (!body || typeof body !== 'object') {\n await envPush({});\n return createResponse(\n 200,\n { success: true, requestId },\n corsHeaders,\n requestId,\n );\n }\n\n if (isEventRequest(body)) {\n const result = await processEvent(\n body as EventRequest,\n envPush,\n env.logger,\n requestId,\n );\n\n if (result.error) {\n return createResponse(\n 400,\n { success: false, error: result.error, requestId },\n corsHeaders,\n requestId,\n );\n }\n\n return createResponse(\n 200,\n { success: true, id: result.id, requestId },\n corsHeaders,\n requestId,\n );\n }\n\n return createResponse(\n 400,\n { success: false, error: 'Invalid request format', requestId },\n corsHeaders,\n requestId,\n );\n }\n\n return createResponse(\n 405,\n { success: false, error: 'Method not allowed', requestId },\n corsHeaders,\n requestId,\n );\n });\n } catch (error) {\n // Log handler errors with context - per using-logger skill\n env.logger?.error('Lambda handler error', {\n error,\n requestId,\n method: parsed?.method,\n });\n return createResponse(\n 500,\n {\n success: false,\n error:\n error instanceof Error ? error.message : 'Internal server error',\n requestId,\n },\n {},\n requestId,\n );\n }\n };\n\n return {\n type: 'lambda',\n config: fullConfig,\n push,\n };\n};\n\nexport default sourceLambda;\n","import type { APIGatewayProxyEventV2, APIGatewayProxyResult } from 'aws-lambda';\nimport type {\n LambdaEvent,\n ParsedRequest,\n CorsOptions,\n RequestBody,\n EventRequest,\n} from './types';\n\nexport function isAPIGatewayV2(\n event: LambdaEvent,\n): event is APIGatewayProxyEventV2 {\n return 'version' in event && event.version === '2.0';\n}\n\nexport function parseEvent(event: LambdaEvent): ParsedRequest {\n if (isAPIGatewayV2(event)) {\n const headers: Record<string, string> = {};\n if (event.headers) {\n Object.entries(event.headers).forEach(([key, value]) => {\n if (value) headers[key.toLowerCase()] = value;\n });\n }\n return {\n method: event.requestContext.http.method,\n body: event.body,\n queryString: event.rawQueryString || null,\n headers,\n isBase64Encoded: event.isBase64Encoded || false,\n };\n } else {\n const headers: Record<string, string> = {};\n if (event.headers) {\n Object.entries(event.headers).forEach(([key, value]) => {\n if (value) headers[key.toLowerCase()] = value;\n });\n }\n let queryString: string | null = null;\n if (event.queryStringParameters) {\n const params = new URLSearchParams();\n Object.entries(event.queryStringParameters).forEach(([key, value]) => {\n if (value) params.append(key, value);\n });\n queryString = params.toString() || null;\n }\n return {\n method: event.httpMethod,\n body: event.body,\n queryString,\n headers,\n isBase64Encoded: event.isBase64Encoded || false,\n };\n }\n}\n\nexport function getPath(event: LambdaEvent): string {\n if (isAPIGatewayV2(event)) {\n return event.rawPath;\n } else {\n return event.path;\n }\n}\n\nexport function parseBody(body: unknown, isBase64Encoded: boolean): unknown {\n if (!body || typeof body !== 'string') return body;\n try {\n const decoded = isBase64Encoded\n ? Buffer.from(body, 'base64').toString('utf8')\n : body;\n return JSON.parse(decoded);\n } catch {\n return body;\n }\n}\n\nexport function isEventRequest(body: unknown): body is EventRequest {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'event' in body &&\n typeof (body as EventRequest).event === 'string'\n );\n}\n\nexport function getCorsHeaders(\n corsOptions: boolean | CorsOptions,\n): Record<string, string> {\n if (!corsOptions) return {};\n if (corsOptions === true) {\n return {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n 'Access-Control-Max-Age': '3600',\n };\n }\n\n const headers: Record<string, string> = {};\n\n if (corsOptions.origin) {\n const origin = Array.isArray(corsOptions.origin)\n ? corsOptions.origin.join(', ')\n : corsOptions.origin;\n headers['Access-Control-Allow-Origin'] = origin;\n }\n if (corsOptions.methods) {\n headers['Access-Control-Allow-Methods'] = corsOptions.methods.join(', ');\n }\n if (corsOptions.headers) {\n headers['Access-Control-Allow-Headers'] = corsOptions.headers.join(', ');\n }\n if (corsOptions.credentials) {\n headers['Access-Control-Allow-Credentials'] = 'true';\n }\n if (corsOptions.maxAge !== undefined) {\n headers['Access-Control-Max-Age'] = corsOptions.maxAge.toString();\n }\n\n return headers;\n}\n\nexport function createResponse(\n statusCode: number,\n body: unknown,\n headers: Record<string, string> = {},\n requestId?: string,\n): APIGatewayProxyResult {\n const responseHeaders: Record<string, string> = {\n 'Content-Type':\n typeof body === 'object' ? 'application/json' : 'text/plain',\n ...headers,\n };\n\n if (requestId) {\n responseHeaders['X-Request-ID'] = requestId;\n }\n\n return {\n statusCode,\n headers: responseHeaders,\n body: typeof body === 'object' ? JSON.stringify(body) : String(body),\n isBase64Encoded: false,\n };\n}\n\nexport function createPixelResponse(\n headers: Record<string, string> = {},\n requestId?: string,\n): APIGatewayProxyResult {\n const responseHeaders: Record<string, string> = {\n 'Content-Type': 'image/gif',\n 'Cache-Control': 'no-cache, no-store, must-revalidate',\n ...headers,\n };\n\n if (requestId) {\n responseHeaders['X-Request-ID'] = requestId;\n }\n\n return {\n statusCode: 200,\n headers: responseHeaders,\n body: 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',\n isBase64Encoded: true,\n };\n}\n","import type { Collector, WalkerOS, Logger } from '@walkeros/core';\nimport type { EventRequest } from './types';\n\nexport async function processEvent(\n eventReq: EventRequest,\n push: Collector.PushFn,\n logger?: Logger.Instance,\n requestId?: string,\n): Promise<{ id?: string; error?: string }> {\n try {\n const result = await push({\n name: eventReq.event,\n data: (eventReq.data || {}) as WalkerOS.Properties,\n context: eventReq.context as WalkerOS.OrderedProperties | undefined,\n user: eventReq.user as WalkerOS.User | undefined,\n globals: eventReq.globals as WalkerOS.Properties | undefined,\n consent: eventReq.consent as WalkerOS.Consent | undefined,\n });\n\n return { id: result?.event?.id };\n } catch (error) {\n // Log with structured context - per using-logger skill\n logger?.error('Event processing failed', {\n error,\n eventName: eventReq.event,\n requestId,\n });\n return { error: error instanceof Error ? error.message : 'Unknown error' };\n }\n}\n","import type { WalkerOS, Source as CoreSource } from '@walkeros/core';\nimport type {\n APIGatewayProxyEvent,\n APIGatewayProxyEventV2,\n APIGatewayProxyResult,\n Context,\n} from 'aws-lambda';\nimport type { SettingsSchema, CorsOptionsSchema } from './schemas';\nimport type { z } from '@walkeros/core/dev';\n\ndeclare module '@walkeros/core' {\n interface SourceMap {\n lambda: { type: 'lambda'; platform: 'server' };\n }\n}\n\n// Lambda event types\nexport type LambdaEvent = APIGatewayProxyEvent | APIGatewayProxyEventV2;\nexport type LambdaResult = APIGatewayProxyResult;\nexport type LambdaContext = Context;\n\n// Types inferred from Zod schemas\nexport type Settings = z.infer<typeof SettingsSchema>;\nexport type CorsOptions = z.infer<typeof CorsOptionsSchema>;\n\n// InitSettings: user input (all optional)\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n // Custom source event mapping properties\n}\n\n// Lambda-specific push type\nexport type Push = (\n event: LambdaEvent,\n context: LambdaContext,\n) => Promise<LambdaResult>;\n\nexport interface Env extends CoreSource.Env {\n lambdaEvent?: LambdaEvent;\n lambdaContext?: LambdaContext;\n}\n\n// Type bundle (must be after Settings, Mapping, Push, Env are defined)\nexport type Types = CoreSource.Types<\n Settings,\n Mapping,\n Push,\n Env,\n InitSettings\n>;\n\nexport interface LambdaSource extends Omit<CoreSource.Instance<Types>, 'push'> {\n push: Push;\n}\n\n// Convenience Config type\nexport type Config = CoreSource.Config<Types>;\nexport type PartialConfig = CoreSource.PartialConfig<Types>;\n\n// Lambda source doesn't follow standard Source.Init pattern due to Lambda handler interface\n\nexport interface EventRequest {\n event: string;\n data?: WalkerOS.AnyObject;\n context?: WalkerOS.AnyObject;\n user?: WalkerOS.AnyObject;\n globals?: WalkerOS.AnyObject;\n consent?: WalkerOS.AnyObject;\n}\n\nexport interface EventResponse {\n success: boolean;\n id?: string;\n error?: string;\n}\n\nexport type RequestBody = EventRequest;\nexport type ResponseBody = EventResponse;\n\n// Parsed request data structure\nexport interface ParsedRequest {\n method: string;\n body: unknown;\n queryString: string | null;\n headers: Record<string, string>;\n isBase64Encoded: boolean;\n}\n","import {\n DeleteMessageCommand,\n GetQueueAttributesCommand,\n GetQueueUrlCommand,\n ReceiveMessageCommand,\n SQSClient,\n} from '@aws-sdk/client-sqs';\nimport type { Collector, Logger, Source, WalkerOS } from '@walkeros/core';\nimport type {\n Settings,\n SyntheticMessage,\n SyntheticPushResult,\n Types,\n} from './types';\nimport { decodeBody, DecoderError } from './decoder';\nimport { getConfig } from './config';\nimport { setup } from './setup';\n\nexport { setup } from './setup';\n\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 30000;\nconst RECEIVE_RETRY_BACKOFF_MS = 1000;\n\ninterface MessageDispatch {\n id: string;\n body: string;\n receiptHandle?: string;\n ack(): Promise<void>;\n nack(): Promise<void>;\n}\n\nfunction toDeepPartialEvent(value: unknown): WalkerOS.DeepPartialEvent {\n if (typeof value === 'object' && value !== null) {\n // Plain object payloads pass through. The collector treats them as\n // DeepPartialEvent shapes; downstream transformers see the same data.\n return value as WalkerOS.DeepPartialEvent;\n }\n if (typeof value === 'string') return { data: { payload: value } };\n if (Buffer.isBuffer(value))\n return { data: { payload: value.toString('base64') } };\n return { data: { payload: String(value) } };\n}\n\nasync function handleMessage(args: {\n message: MessageDispatch;\n settings: Settings;\n pushFn: Collector.PushFn;\n logger: Logger.Instance;\n}): Promise<void> {\n const { message, settings, pushFn, logger } = args;\n try {\n const decoded = decodeBody(\n message.id,\n message.body,\n settings.decoder ?? 'json',\n );\n if (decoded === null || decoded === undefined) {\n logger.debug('SQS message acked-and-dropped (decoder returned null)', {\n id: message.id,\n });\n await message.ack();\n return;\n }\n await pushFn(toDeepPartialEvent(decoded));\n await message.ack();\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n if (err instanceof DecoderError) {\n logger.error('SQS message decode failed', { id: message.id, error });\n } else {\n logger.error('SQS message handler failed', { id: message.id, error });\n }\n if (settings.onPushError === 'ack') await message.ack();\n else await message.nack();\n }\n}\n\nfunction isQueueDoesNotExist(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false;\n if (!('name' in err)) return false;\n const obj: { name?: unknown } = err;\n return (\n obj.name === 'QueueDoesNotExist' ||\n obj.name === 'AWS.SimpleQueueService.NonExistentQueue'\n );\n}\n\ninterface ClientWithDestroy {\n destroy?: () => void;\n}\n\nfunction tryDestroyClient(client: SQSClient, logger: Logger.Instance): void {\n // SQSClient implements destroy() in modern SDK versions; older versions\n // do not. Wrap defensively.\n const candidate: ClientWithDestroy = client;\n if (typeof candidate.destroy !== 'function') return;\n try {\n candidate.destroy();\n } catch (err) {\n logger.debug('SQS client destroy failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\ninterface FetchedMessage {\n MessageId: string;\n ReceiptHandle: string;\n Body: string;\n}\n\nfunction isFetchedMessage(value: unknown): value is FetchedMessage {\n if (typeof value !== 'object' || value === null) return false;\n const obj: {\n MessageId?: unknown;\n ReceiptHandle?: unknown;\n Body?: unknown;\n } = value;\n return (\n typeof obj.MessageId === 'string' &&\n typeof obj.ReceiptHandle === 'string' &&\n typeof obj.Body === 'string'\n );\n}\n\n/**\n * AWS SQS source.\n *\n * Long-running poll-and-forward subscriber. init() validates the queue exists,\n * captures the canonical queueUrl, and starts the long-poll loop as a\n * background task. push() accepts an optional synthetic message (used by\n * tests / triggers) and dispatches it through the same handler the loop uses;\n * called without arguments (production) it is a no-op since SQS is event-driven.\n *\n * destroy() stops the loop, drains in-flight handlers, and force-closes after\n * shutdownTimeoutMs (default 30000).\n */\nexport const sourceSqs: Source.Init<Types> = async (context) => {\n const { config: partialConfig = {}, env, logger, id } = context;\n const config = getConfig(partialConfig, env, logger);\n const settings: Settings = config.settings;\n\n // Resolve queueUrl from queueName at init time.\n if (!settings.queueUrl) {\n try {\n const res = await settings.client.send(\n new GetQueueUrlCommand({ QueueName: settings.queueName }),\n );\n if (!res.QueueUrl) {\n return logger.throw(\n `SQS queue not found: ${settings.queueName}. ` +\n `Run \"walkeros setup source.${id}\" to create it.`,\n );\n }\n settings.queueUrl = res.QueueUrl;\n } catch (err) {\n if (isQueueDoesNotExist(err)) {\n return logger.throw(\n `SQS queue not found: ${settings.queueName}. ` +\n `Run \"walkeros setup source.${id}\" to create it.`,\n );\n }\n throw err;\n }\n }\n\n // Capture queueArn for downstream use (e.g. SNS subscription verification).\n if (!settings.queueArn) {\n try {\n const res = await settings.client.send(\n new GetQueueAttributesCommand({\n QueueUrl: settings.queueUrl,\n AttributeNames: ['QueueArn'],\n }),\n );\n settings.queueArn = res.Attributes?.QueueArn;\n } catch (err) {\n // ARN is informational; continue without it.\n logger.debug('SQS GetQueueAttributesCommand failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // Long-poll loop control flag and in-flight tracker.\n let stopped = false;\n const inFlight = new Set<Promise<void>>();\n\n const loop = async (): Promise<void> => {\n while (!stopped) {\n try {\n const res = await settings.client.send(\n new ReceiveMessageCommand({\n QueueUrl: settings.queueUrl,\n MaxNumberOfMessages: settings.maxMessages ?? 10,\n WaitTimeSeconds: settings.waitTimeSeconds ?? 20,\n VisibilityTimeout: settings.visibilityTimeout,\n MessageAttributeNames: ['All'],\n AttributeNames: ['All'],\n }),\n );\n const messages = res.Messages ?? [];\n for (const msg of messages) {\n if (!isFetchedMessage(msg)) {\n logger.warn('SQS received malformed message; skipping', {\n messageId:\n typeof msg.MessageId === 'string' ? msg.MessageId : undefined,\n });\n continue;\n }\n const fetched: FetchedMessage = msg;\n const dispatch: MessageDispatch = {\n id: fetched.MessageId,\n body: fetched.Body,\n receiptHandle: fetched.ReceiptHandle,\n ack: async () => {\n await settings.client.send(\n new DeleteMessageCommand({\n QueueUrl: settings.queueUrl ?? '',\n ReceiptHandle: fetched.ReceiptHandle,\n }),\n );\n },\n nack: async () => {\n // SQS has no explicit nack: skipping DeleteMessage causes\n // redelivery after VisibilityTimeout expires.\n },\n };\n const handler = handleMessage({\n message: dispatch,\n settings,\n pushFn: env.push,\n logger,\n });\n inFlight.add(handler);\n handler.finally(() => {\n inFlight.delete(handler);\n });\n }\n } catch (err) {\n if (stopped) break;\n const error = err instanceof Error ? err.message : String(err);\n if (isQueueDoesNotExist(err)) {\n logger.error(\n `SQS queue \"${settings.queueName}\" not found at runtime. ` +\n `Run \"walkeros setup source.${id}\" to create it. Original: ${error}`,\n );\n // Stop the loop on a fatal error; operator must intervene.\n stopped = true;\n break;\n }\n logger.error('SQS receive error (will retry)', { error });\n // Backoff before retry to avoid tight-looping on persistent errors.\n await new Promise((r) => setTimeout(r, RECEIVE_RETRY_BACKOFF_MS));\n }\n }\n };\n\n // Launch the loop without awaiting so init returns immediately. Track the\n // promise so destroy() can await it before resolving (prevents leaked\n // timers in test runs).\n const loopPromise = loop();\n\n return {\n type: 'sqs',\n config,\n setup,\n push: async (\n content?: SyntheticMessage,\n ): Promise<SyntheticPushResult | void> => {\n if (!content) return;\n const result: SyntheticPushResult = { acked: false, nacked: false };\n await handleMessage({\n message: {\n id: content.MessageId,\n body: content.Body,\n receiptHandle: content.ReceiptHandle,\n ack: async () => {\n result.acked = true;\n },\n nack: async () => {\n result.nacked = true;\n },\n },\n settings,\n pushFn: env.push,\n logger,\n });\n return result;\n },\n destroy: async () => {\n stopped = true;\n const timeoutMs =\n settings.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;\n let timer: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n (async () => {\n // Wait for the loop to exit AND in-flight handlers to drain.\n await loopPromise;\n await Promise.all(Array.from(inFlight));\n })(),\n new Promise<void>((resolve) => {\n timer = setTimeout(() => {\n logger.warn(\n `SQS source close timed out after ${timeoutMs}ms; forcing close.`,\n );\n resolve();\n }, timeoutMs);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n tryDestroyClient(settings.client, logger);\n }\n },\n };\n};\n\nexport default sourceSqs;\n","import type { Decoder } from './types';\n\nexport class DecoderError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DecoderError';\n }\n}\n\n/**\n * Decode an SQS message body string.\n *\n * - 'json': JSON.parse(body). Throws DecoderError on parse failure.\n * - 'text': body returned as-is.\n * - 'raw': Buffer.from(body, 'utf8').\n */\nexport function decodeBody(\n messageId: string,\n body: string,\n decoder: Decoder,\n): unknown {\n if (decoder === 'raw') return Buffer.from(body, 'utf8');\n if (decoder === 'text') return body;\n // json\n try {\n return JSON.parse(body);\n } catch (err) {\n throw new DecoderError(\n `SQS message ${messageId} JSON decode failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n}\n","import { SQSClient } from '@aws-sdk/client-sqs';\nimport type { Logger } from '@walkeros/core';\nimport type {\n Config,\n Env,\n InitSettings,\n PartialConfig,\n Settings,\n} from './types';\n\nconst DEFAULT_REGION = 'eu-central-1';\nconst DEFAULT_DECODER = 'json' as const;\nconst DEFAULT_MAX_MESSAGES = 10;\nconst DEFAULT_WAIT_TIME = 20;\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 30000;\nconst DEFAULT_ON_PUSH_ERROR = 'nack' as const;\n\n/**\n * Runtime config with the narrow Settings shape (all defaults applied).\n *\n * The framework's `Source.Config` types `settings` as `InitSettings | undefined`;\n * this alias reflects the post-getConfig invariant where settings is fully\n * resolved and the SQSClient handle is built.\n */\nexport type RuntimeConfig = Omit<Config, 'settings'> & { settings: Settings };\n\n/**\n * Validate and normalize the partial config into a runtime Config.\n *\n * The client is built once: prefer pre-supplied `settings.client`, then env-\n * injected constructor (tests/DI), then the static SDK import. `Settings.client`\n * is always populated before init/destroy run.\n */\nexport function getConfig(\n partial: PartialConfig = {},\n env: Env | undefined,\n logger: Logger.Instance,\n): RuntimeConfig {\n const empty: Partial<InitSettings> = {};\n const partialSettings: Partial<InitSettings> = partial.settings ?? empty;\n const queueName =\n typeof partialSettings.queueName === 'string'\n ? partialSettings.queueName\n : '';\n if (!queueName) logger.throw('Config settings queueName missing');\n\n const region = partialSettings.region ?? DEFAULT_REGION;\n\n let client = partialSettings.client;\n if (!client) {\n const Constructor = env?.AWS?.SQSClient ?? SQSClient;\n client = new Constructor({ region, ...partialSettings.config });\n }\n\n const settings: Settings = {\n ...partialSettings,\n client,\n queueName,\n region,\n queueUrl: partialSettings.queueUrl,\n config: partialSettings.config,\n decoder: partialSettings.decoder ?? DEFAULT_DECODER,\n maxMessages: partialSettings.maxMessages ?? DEFAULT_MAX_MESSAGES,\n waitTimeSeconds: partialSettings.waitTimeSeconds ?? DEFAULT_WAIT_TIME,\n visibilityTimeout: partialSettings.visibilityTimeout,\n shutdownTimeoutMs:\n partialSettings.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS,\n onPushError: partialSettings.onPushError ?? DEFAULT_ON_PUSH_ERROR,\n };\n\n return { ...partial, settings };\n}\n","import {\n CreateQueueCommand,\n GetQueueAttributesCommand,\n SQSClient,\n} from '@aws-sdk/client-sqs';\nimport { SNSClient, SubscribeCommand } from '@aws-sdk/client-sns';\nimport type { Logger } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { Config, Env, Setup, SetupFn } from './types';\n\nconst DEFAULT_REGION = 'eu-central-1';\nconst DEFAULT_VISIBILITY_TIMEOUT_SECONDS = 30;\nconst DEFAULT_MESSAGE_RETENTION_SECONDS = 345600;\nconst DEFAULT_MAXIMUM_MESSAGE_SIZE = 262144;\nconst DEFAULT_DLQ_MAX_RECEIVE_COUNT = 5;\nconst DLQ_RETENTION_SECONDS = 1209600; // 14 days, AWS max\n\nexport const DEFAULT_SETUP: Setup = {\n region: DEFAULT_REGION,\n fifoQueue: false,\n visibilityTimeoutSeconds: DEFAULT_VISIBILITY_TIMEOUT_SECONDS,\n messageRetentionSeconds: DEFAULT_MESSAGE_RETENTION_SECONDS,\n maximumMessageSize: DEFAULT_MAXIMUM_MESSAGE_SIZE,\n};\n\nexport interface SetupResult {\n queueCreated: boolean;\n queueUrl: string;\n queueArn: string;\n dlqCreated?: boolean;\n dlqArn?: string;\n subscriptionArn?: string;\n}\n\ninterface QueueExistsError {\n name: string;\n}\n\nfunction hasName(err: unknown): err is QueueExistsError {\n if (typeof err !== 'object' || err === null) return false;\n const obj: { name?: unknown } = err;\n return typeof obj.name === 'string';\n}\n\nfunction isQueueNameExists(err: unknown): boolean {\n if (!hasName(err)) return false;\n return (\n err.name === 'QueueNameExists' ||\n err.name === 'QueueAlreadyExists' ||\n err.name === 'AWS.SimpleQueueService.QueueNameExists'\n );\n}\n\ninterface BuildQueueAttributesArgs {\n options: Setup;\n dlqArn: string | undefined;\n queuePolicy: string | undefined;\n}\n\nfunction buildQueueAttributes(\n args: BuildQueueAttributesArgs,\n): Record<string, string> {\n const { options, dlqArn, queuePolicy } = args;\n const attrs: Record<string, string> = {};\n attrs.VisibilityTimeout = String(\n options.visibilityTimeoutSeconds ?? DEFAULT_VISIBILITY_TIMEOUT_SECONDS,\n );\n attrs.MessageRetentionPeriod = String(\n options.messageRetentionSeconds ?? DEFAULT_MESSAGE_RETENTION_SECONDS,\n );\n attrs.MaximumMessageSize = String(\n options.maximumMessageSize ?? DEFAULT_MAXIMUM_MESSAGE_SIZE,\n );\n if (options.kmsMasterKeyId) {\n attrs.KmsMasterKeyId = options.kmsMasterKeyId;\n }\n if (options.fifoQueue) {\n attrs.FifoQueue = 'true';\n attrs.ContentBasedDeduplication = 'true';\n }\n if (dlqArn) {\n const maxReceiveCount =\n options.deadLetterQueue?.maxReceiveCount ?? DEFAULT_DLQ_MAX_RECEIVE_COUNT;\n attrs.RedrivePolicy = JSON.stringify({\n deadLetterTargetArn: dlqArn,\n maxReceiveCount,\n });\n }\n if (queuePolicy) {\n attrs.Policy = queuePolicy;\n }\n return attrs;\n}\n\nfunction buildDlqAttributes(args: {\n fifoQueue: boolean;\n kmsMasterKeyId?: string;\n}): Record<string, string> {\n const attrs: Record<string, string> = {};\n attrs.MessageRetentionPeriod = String(DLQ_RETENTION_SECONDS);\n if (args.kmsMasterKeyId) {\n attrs.KmsMasterKeyId = args.kmsMasterKeyId;\n }\n if (args.fifoQueue) {\n attrs.FifoQueue = 'true';\n attrs.ContentBasedDeduplication = 'true';\n }\n return attrs;\n}\n\nfunction buildDlqTags(\n parentTags: Record<string, string> | undefined,\n): Record<string, string> {\n return {\n ...(parentTags ?? {}),\n walkerOS: 'dlq',\n };\n}\n\ninterface BuildPolicyArgs {\n sourceId: string;\n topicArn: string;\n}\n\nfunction buildSnsQueuePolicy(args: BuildPolicyArgs): string {\n // Placeholder Resource value; AWS does not require it to match the actual\n // queue ARN at policy-attach time when the queue is being created in the\n // same call. The Sid keys the statement so re-runs upsert in place.\n return JSON.stringify({\n Version: '2012-10-17',\n Statement: [\n {\n Sid: `walkerOSAllowSNSPublish-${args.sourceId}`,\n Effect: 'Allow',\n Principal: { Service: 'sns.amazonaws.com' },\n Action: 'SQS:SendMessage',\n Resource: '*',\n Condition: {\n ArnEquals: {\n 'aws:SourceArn': args.topicArn,\n },\n },\n },\n ],\n });\n}\n\ninterface EnsureQueueArgs {\n sqs: SQSClient;\n logger: Logger.Instance;\n queueName: string;\n attributes: Record<string, string>;\n tags?: Record<string, string>;\n}\n\ninterface EnsureQueueResult {\n queueCreated: boolean;\n queueUrl: string;\n queueArn: string;\n}\n\n/**\n * Authoritative-apply: a single CreateQueueCommand with the full declared\n * attribute map and tags. AWS treats identical inputs as success; on\n * QueueNameExists (different attrs) we hard-fail with an actionable message.\n * Setup never calls SetQueueAttributesCommand.\n */\nasync function ensureQueue(args: EnsureQueueArgs): Promise<EnsureQueueResult> {\n const { sqs, logger, queueName, attributes, tags } = args;\n let queueUrl: string;\n let queueCreated = false;\n try {\n const res = await sqs.send(\n new CreateQueueCommand({\n QueueName: queueName,\n Attributes: attributes,\n tags,\n }),\n );\n queueUrl = res.QueueUrl ?? '';\n queueCreated = true;\n logger.info(`SQS queue \"${queueName}\" created.`);\n } catch (err) {\n if (isQueueNameExists(err)) {\n const message = err instanceof Error ? err.message : String(err);\n return logger.throw(\n `SQS queue \"${queueName}\" exists with different attributes (${message}). ` +\n `Setup will not mutate an existing queue's attributes. Delete or rename the queue, then re-run setup.`,\n );\n }\n throw err;\n }\n if (!queueUrl) {\n return logger.throw(\n `SQS CreateQueueCommand returned no QueueUrl for \"${queueName}\".`,\n );\n }\n // Fetch QueueArn for downstream Subscribe / RedrivePolicy use.\n const attrsRes = await sqs.send(\n new GetQueueAttributesCommand({\n QueueUrl: queueUrl,\n AttributeNames: ['QueueArn'],\n }),\n );\n const queueArn = attrsRes.Attributes?.QueueArn ?? '';\n if (!queueArn) {\n return logger.throw(\n `SQS GetQueueAttributesCommand returned no QueueArn for \"${queueName}\".`,\n );\n }\n return { queueCreated, queueUrl, queueArn };\n}\n\nfunction dlqName(queueName: string, fifo: boolean): string {\n if (fifo) {\n // FIFO queue names must end in .fifo. Strip the suffix if present, append -dlq, then re-add.\n const base = queueName.endsWith('.fifo')\n ? queueName.slice(0, -'.fifo'.length)\n : queueName;\n return `${base}-dlq.fifo`;\n }\n return `${queueName}-dlq`;\n}\n\nexport const setup: SetupFn = async (context) => {\n const { id, config, env, logger } = context;\n\n const merged = resolveSetup<Setup>(config.setup, DEFAULT_SETUP);\n if (!merged) {\n logger.debug('SQS source setup skipped (config.setup is falsy).');\n return undefined;\n }\n const options: Setup = { ...DEFAULT_SETUP, ...merged };\n\n const settings = config.settings;\n if (!settings) return logger.throw('setup: settings missing');\n\n const queueName = settings.queueName;\n if (!queueName) return logger.throw('setup: settings.queueName is missing');\n\n const region = options.region ?? DEFAULT_REGION;\n\n // Construct clients via env override (tests) or static SDK import (prod).\n const SqsCtor = env.AWS?.SQSClient ?? SQSClient;\n const sqs = new SqsCtor({ region });\n\n // 1. Optional DLQ first (so we have its ARN for RedrivePolicy on the main queue).\n let dlqArn: string | undefined;\n let dlqCreated = false;\n if (options.deadLetterQueue?.arn) {\n dlqArn = options.deadLetterQueue.arn;\n } else if (options.deadLetterQueue?.create) {\n const dlqResult = await ensureQueue({\n sqs,\n logger,\n queueName: dlqName(queueName, options.fifoQueue ?? false),\n attributes: buildDlqAttributes({\n fifoQueue: options.fifoQueue ?? false,\n kmsMasterKeyId: options.kmsMasterKeyId,\n }),\n tags: buildDlqTags(options.tags),\n });\n dlqArn = dlqResult.queueArn;\n dlqCreated = dlqResult.queueCreated;\n }\n\n // 2. Main queue.\n const finalQueueName = options.fifoQueue\n ? queueName.endsWith('.fifo')\n ? queueName\n : `${queueName}.fifo`\n : queueName;\n const queuePolicy = options.subscribeToSnsTopic\n ? buildSnsQueuePolicy({\n sourceId: id,\n topicArn: options.subscribeToSnsTopic.topicArn,\n })\n : undefined;\n\n const mainAttrs = buildQueueAttributes({\n options,\n dlqArn,\n queuePolicy,\n });\n const mainResult = await ensureQueue({\n sqs,\n logger,\n queueName: finalQueueName,\n attributes: mainAttrs,\n tags: options.tags,\n });\n\n // 3. Optional SNS subscription.\n let subscriptionArn: string | undefined;\n if (options.subscribeToSnsTopic) {\n const SnsCtor = env.AWS?.SNSClient ?? SNSClient;\n const sns = new SnsCtor({ region });\n const subAttrs: Record<string, string> = {};\n if (options.subscribeToSnsTopic.rawMessageDelivery) {\n subAttrs.RawMessageDelivery = 'true';\n }\n if (options.subscribeToSnsTopic.filterPolicy) {\n subAttrs.FilterPolicy = JSON.stringify(\n options.subscribeToSnsTopic.filterPolicy,\n );\n }\n const subscribeRes = await sns.send(\n new SubscribeCommand({\n TopicArn: options.subscribeToSnsTopic.topicArn,\n Protocol: 'sqs',\n Endpoint: mainResult.queueArn,\n Attributes: Object.keys(subAttrs).length > 0 ? subAttrs : undefined,\n ReturnSubscriptionArn: true,\n }),\n );\n subscriptionArn = subscribeRes.SubscriptionArn ?? undefined;\n logger.info('SQS source setup: SNS subscription created', {\n topicArn: options.subscribeToSnsTopic.topicArn,\n subscriptionArn,\n });\n }\n\n const result: SetupResult = {\n queueCreated: mainResult.queueCreated,\n queueUrl: mainResult.queueUrl,\n queueArn: mainResult.queueArn,\n dlqCreated: dlqArn ? dlqCreated : undefined,\n dlqArn,\n subscriptionArn,\n };\n return result;\n};\n","import type {\n Source as CoreSource,\n SetupFn as CoreSetupFn,\n} from '@walkeros/core';\nimport type {\n SQSClient,\n SQSClientConfig,\n Message,\n ReceiveMessageCommand,\n DeleteMessageCommand,\n CreateQueueCommand,\n GetQueueUrlCommand,\n GetQueueAttributesCommand,\n} from '@aws-sdk/client-sqs';\nimport type { SNSClient, SubscribeCommand } from '@aws-sdk/client-sns';\n\ndeclare module '@walkeros/core' {\n interface SourceMap {\n sqs: { type: 'sqs'; platform: 'server' };\n }\n}\n\nexport type Decoder = 'json' | 'text' | 'raw';\n\nexport interface Settings {\n // User-supplied OR populated by getConfig(); single field for both. Mirrors the Pub/Sub pull source.\n client: SQSClient;\n // Required. Used by setup to provision and by init to resolve the URL.\n queueName: string;\n // AWS region. Default: 'eu-central-1'.\n region?: string;\n // Optional pre-resolved URL. When unset, init resolves via GetQueueUrlCommand.\n queueUrl?: string;\n // SDK client config (credentials, etc.). Optional.\n config?: SQSClientConfig;\n // Decoder for message body. Default: 'json'.\n decoder?: Decoder;\n // Receive batch size. Cap 10, default 10.\n maxMessages?: number;\n // Long-poll duration in seconds. Cap 20, default 20.\n waitTimeSeconds?: number;\n // Per-receive visibility timeout override. Default: queue's configured value.\n visibilityTimeout?: number;\n // Graceful shutdown timeout in ms. Default: 30000.\n shutdownTimeoutMs?: number;\n // Error policy: 'ack' (DeleteMessage to drop) or 'nack' (no DeleteMessage, redeliver). Default: 'nack'.\n onPushError?: 'nack' | 'ack';\n // Runtime-only handle populated by init(); not user-facing.\n queueArn?: string;\n}\n\nexport interface InitSettings {\n queueName: string;\n client?: SQSClient;\n region?: string;\n queueUrl?: string;\n config?: SQSClientConfig;\n decoder?: Decoder;\n maxMessages?: number;\n waitTimeSeconds?: number;\n visibilityTimeout?: number;\n shutdownTimeoutMs?: number;\n onPushError?: 'nack' | 'ack';\n}\n\n// Mapping has no per-event fields in v1: SQS messages have no native ordering\n// key, no per-event visibility-extension API, no per-event delete strategy that\n// SQS itself consumes. Future per-event fields will be introduced when a real\n// feature needs them.\nexport interface Mapping {\n // Reserved for future use.\n}\n\n/**\n * Synthetic message input used by tests / triggers to dispatch through the\n * same handler the long-poll loop uses, without involving real SQS.\n *\n * In production, push() is invoked without arguments and is a no-op:\n * SQS is event-driven, the long-poll loop is the canonical delivery path.\n * Tests pass a synthetic input to dispatch directly.\n */\nexport interface SyntheticMessage {\n MessageId: string;\n Body: string;\n ReceiptHandle?: string;\n Attributes?: Record<string, string>;\n MessageAttributes?: Record<\n string,\n { DataType: string; StringValue?: string }\n >;\n}\n\nexport interface SyntheticPushResult {\n acked: boolean;\n nacked: boolean;\n}\n\nexport type Push = (\n content?: SyntheticMessage,\n) => Promise<SyntheticPushResult | void>;\n\nexport interface Env extends CoreSource.Env {\n AWS?: {\n SQSClient: typeof SQSClient;\n ReceiveMessageCommand: typeof ReceiveMessageCommand;\n DeleteMessageCommand: typeof DeleteMessageCommand;\n CreateQueueCommand: typeof CreateQueueCommand;\n GetQueueUrlCommand: typeof GetQueueUrlCommand;\n GetQueueAttributesCommand: typeof GetQueueAttributesCommand;\n // SNS used by setup only when subscribeToSnsTopic is set.\n SNSClient?: typeof SNSClient;\n SubscribeCommand?: typeof SubscribeCommand;\n };\n}\n\n/**\n * Provisioning options for `walkeros setup source.<id>`.\n *\n * Triggered only by the explicit CLI command. Idempotent. Never auto-run.\n *\n * `queueName` lives in Settings (one source of truth for setup AND runtime poll).\n */\nexport interface Setup {\n /** AWS region. Default: 'eu-central-1'. */\n region?: string;\n /** FIFO queue with content-based deduplication. Default: false. */\n fifoQueue?: boolean;\n /** Visibility timeout in seconds. Default: 30. */\n visibilityTimeoutSeconds?: number;\n /** Message retention period in seconds. Default: 345600 (4 days). */\n messageRetentionSeconds?: number;\n /** Max message size in bytes. Default: 262144 (256 KB). */\n maximumMessageSize?: number;\n /** KMS key for at-rest encryption. Optional. */\n kmsMasterKeyId?: string;\n /** Optional dead-letter queue. */\n deadLetterQueue?: {\n /** ARN of an existing DLQ. Mutually exclusive with `create: true`. */\n arn?: string;\n /** Create a sibling DLQ named <queueName>-dlq. Default: false. */\n create?: boolean;\n /** Max receive count before message goes to DLQ. Default: 5. */\n maxReceiveCount?: number;\n };\n /** Tags applied to the queue (and inherited by an auto-created DLQ). */\n tags?: Record<string, string>;\n /** Optional: subscribe this queue to an SNS topic. Creates SNS subscription + queue policy. */\n subscribeToSnsTopic?: {\n /** Topic ARN to subscribe to. Required. */\n topicArn: string;\n /** Deliver SNS messages without the SNS envelope. Default: false. */\n rawMessageDelivery?: boolean;\n /** SNS filter policy (subscription-time message filtering). */\n filterPolicy?: Record<string, unknown>;\n };\n}\n\n// Source.Types has 6 slots: <Settings, Mapping, Push, Env, InitSettings, Setup>.\n// Setup MUST be in slot 6; mis-positioning silently demotes Config['setup'] to unknown.\nexport type Types = CoreSource.Types<\n Settings,\n Mapping,\n Push,\n Env,\n InitSettings,\n Setup\n>;\n\nexport type Config = CoreSource.Config<Types>;\nexport type PartialConfig = CoreSource.PartialConfig<Types>;\nexport type SetupFn = CoreSetupFn<Config, Env>;\n\n// Re-export SDK types used in this package's public surface.\nexport type { SQSClient, SQSClientConfig, Message, SNSClient };\n"],"mappings":"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","names":["types_exports","import_client_sqs","import_client_sqs","import_core","DEFAULT_REGION","DEFAULT_SHUTDOWN_TIMEOUT_MS","types_exports"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lambda/index.ts","../src/lambda/utils.ts","../src/lambda/push.ts","../src/lambda/types.ts","../src/sqs/index.ts","../src/sqs/decoder.ts","../src/sqs/config.ts","../src/sqs/setup.ts","../src/sqs/types.ts"],"sourcesContent":["export * from './lambda';\nexport { default as sourceLambda } from './lambda';\nexport { default as sourceSqs } from './sqs';\nexport * as SourceSqs from './sqs/types';\n","import type { LambdaSource, EventRequest, Types } from './types';\nimport type { Source } from '@walkeros/core';\nimport { requestToData } from '@walkeros/core';\nimport {\n parseEvent,\n parseBody,\n isEventRequest,\n getCorsHeaders,\n createResponse,\n createPixelResponse,\n getPath,\n} from './utils';\nimport { processEvent } from './push';\n\nexport * as SourceLambda from './types';\n\nexport const sourceLambda: Source.Init<Types> = async (context) => {\n const { config = {}, env } = context;\n\n const userSettings = config.settings || {};\n const settings = {\n ...userSettings,\n cors: userSettings.cors ?? true,\n timeout: userSettings.timeout ?? 30000,\n enablePixelTracking: userSettings.enablePixelTracking ?? true,\n healthPath: userSettings.healthPath ?? '/health',\n };\n\n const fullConfig: Source.Config<Types> = {\n ...config,\n settings,\n };\n\n const push: Types['push'] = async (event, lambdaContext) => {\n const requestId = lambdaContext.awsRequestId;\n let parsed;\n\n try {\n const corsHeaders = getCorsHeaders(settings.cors || false);\n parsed = parseEvent(event);\n const path = getPath(event);\n\n // Health check\n if (settings.healthPath && path === settings.healthPath) {\n return createResponse(\n 200,\n {\n status: 'ok',\n timestamp: Date.now(),\n source: 'lambda',\n requestId,\n },\n corsHeaders,\n requestId,\n );\n }\n\n // Handle OPTIONS for CORS preflight\n if (parsed.method === 'OPTIONS') {\n return createResponse(204, '', corsHeaders, requestId);\n }\n\n // Per-invocation scope: each Lambda invocation gets its own ingest.\n // No respond fn — Lambda returns the response directly from this\n // handler, not via async respond.\n return await context.withScope(event, undefined, async (scopeEnv) => {\n const envPush = scopeEnv.push;\n\n // Handle GET for pixel tracking\n if (parsed!.method === 'GET') {\n if (!settings.enablePixelTracking) {\n return createResponse(\n 405,\n { success: false, error: 'GET not allowed', requestId },\n corsHeaders,\n requestId,\n );\n }\n if (parsed!.queryString) {\n const parsedData = requestToData(parsed!.queryString);\n if (parsedData && typeof parsedData === 'object') {\n await envPush(parsedData);\n }\n }\n return createPixelResponse(corsHeaders, requestId);\n }\n\n // Handle POST for event data\n if (parsed!.method === 'POST') {\n if (!parsed!.body) {\n return createResponse(\n 400,\n { success: false, error: 'Request body is required', requestId },\n corsHeaders,\n requestId,\n );\n }\n\n const body = parseBody(parsed!.body, parsed!.isBase64Encoded);\n\n // If body is not a valid object, push {} to let source.before transformers handle raw input via ingest\n if (!body || typeof body !== 'object') {\n await envPush({});\n return createResponse(\n 200,\n { success: true, requestId },\n corsHeaders,\n requestId,\n );\n }\n\n if (isEventRequest(body)) {\n const result = await processEvent(\n body as EventRequest,\n envPush,\n env.logger,\n requestId,\n );\n\n if (result.error) {\n return createResponse(\n 400,\n { success: false, error: result.error, requestId },\n corsHeaders,\n requestId,\n );\n }\n\n return createResponse(\n 200,\n { success: true, id: result.id, requestId },\n corsHeaders,\n requestId,\n );\n }\n\n return createResponse(\n 400,\n { success: false, error: 'Invalid request format', requestId },\n corsHeaders,\n requestId,\n );\n }\n\n return createResponse(\n 405,\n { success: false, error: 'Method not allowed', requestId },\n corsHeaders,\n requestId,\n );\n });\n } catch (error) {\n // Log handler errors with context - per using-logger skill\n env.logger?.error('Lambda handler error', {\n error,\n requestId,\n method: parsed?.method,\n });\n return createResponse(\n 500,\n {\n success: false,\n error:\n error instanceof Error ? error.message : 'Internal server error',\n requestId,\n },\n {},\n requestId,\n );\n }\n };\n\n return {\n type: 'lambda',\n config: fullConfig,\n push,\n };\n};\n\nexport default sourceLambda;\n","import type { APIGatewayProxyEventV2, APIGatewayProxyResult } from 'aws-lambda';\nimport type {\n LambdaEvent,\n ParsedRequest,\n CorsOptions,\n RequestBody,\n EventRequest,\n} from './types';\n\nexport function isAPIGatewayV2(\n event: LambdaEvent,\n): event is APIGatewayProxyEventV2 {\n return 'version' in event && event.version === '2.0';\n}\n\nexport function parseEvent(event: LambdaEvent): ParsedRequest {\n if (isAPIGatewayV2(event)) {\n const headers: Record<string, string> = {};\n if (event.headers) {\n Object.entries(event.headers).forEach(([key, value]) => {\n if (value) headers[key.toLowerCase()] = value;\n });\n }\n return {\n method: event.requestContext.http.method,\n body: event.body,\n queryString: event.rawQueryString || null,\n headers,\n isBase64Encoded: event.isBase64Encoded || false,\n };\n } else {\n const headers: Record<string, string> = {};\n if (event.headers) {\n Object.entries(event.headers).forEach(([key, value]) => {\n if (value) headers[key.toLowerCase()] = value;\n });\n }\n let queryString: string | null = null;\n if (event.queryStringParameters) {\n const params = new URLSearchParams();\n Object.entries(event.queryStringParameters).forEach(([key, value]) => {\n if (value) params.append(key, value);\n });\n queryString = params.toString() || null;\n }\n return {\n method: event.httpMethod,\n body: event.body,\n queryString,\n headers,\n isBase64Encoded: event.isBase64Encoded || false,\n };\n }\n}\n\nexport function getPath(event: LambdaEvent): string {\n if (isAPIGatewayV2(event)) {\n return event.rawPath;\n } else {\n return event.path;\n }\n}\n\nexport function parseBody(body: unknown, isBase64Encoded: boolean): unknown {\n if (!body || typeof body !== 'string') return body;\n try {\n const decoded = isBase64Encoded\n ? Buffer.from(body, 'base64').toString('utf8')\n : body;\n return JSON.parse(decoded);\n } catch {\n return body;\n }\n}\n\nexport function isEventRequest(body: unknown): body is EventRequest {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'event' in body &&\n typeof (body as EventRequest).event === 'string'\n );\n}\n\nexport function getCorsHeaders(\n corsOptions: boolean | CorsOptions,\n): Record<string, string> {\n if (!corsOptions) return {};\n if (corsOptions === true) {\n return {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n 'Access-Control-Max-Age': '3600',\n };\n }\n\n const headers: Record<string, string> = {};\n\n if (corsOptions.origin) {\n const origin = Array.isArray(corsOptions.origin)\n ? corsOptions.origin.join(', ')\n : corsOptions.origin;\n headers['Access-Control-Allow-Origin'] = origin;\n }\n if (corsOptions.methods) {\n headers['Access-Control-Allow-Methods'] = corsOptions.methods.join(', ');\n }\n if (corsOptions.headers) {\n headers['Access-Control-Allow-Headers'] = corsOptions.headers.join(', ');\n }\n if (corsOptions.credentials) {\n headers['Access-Control-Allow-Credentials'] = 'true';\n }\n if (corsOptions.maxAge !== undefined) {\n headers['Access-Control-Max-Age'] = corsOptions.maxAge.toString();\n }\n\n return headers;\n}\n\nexport function createResponse(\n statusCode: number,\n body: unknown,\n headers: Record<string, string> = {},\n requestId?: string,\n): APIGatewayProxyResult {\n const responseHeaders: Record<string, string> = {\n 'Content-Type':\n typeof body === 'object' ? 'application/json' : 'text/plain',\n ...headers,\n };\n\n if (requestId) {\n responseHeaders['X-Request-ID'] = requestId;\n }\n\n return {\n statusCode,\n headers: responseHeaders,\n body: typeof body === 'object' ? JSON.stringify(body) : String(body),\n isBase64Encoded: false,\n };\n}\n\nexport function createPixelResponse(\n headers: Record<string, string> = {},\n requestId?: string,\n): APIGatewayProxyResult {\n const responseHeaders: Record<string, string> = {\n 'Content-Type': 'image/gif',\n 'Cache-Control': 'no-cache, no-store, must-revalidate',\n ...headers,\n };\n\n if (requestId) {\n responseHeaders['X-Request-ID'] = requestId;\n }\n\n return {\n statusCode: 200,\n headers: responseHeaders,\n body: 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',\n isBase64Encoded: true,\n };\n}\n","import type { Collector, WalkerOS, Logger } from '@walkeros/core';\nimport type { EventRequest } from './types';\n\nexport async function processEvent(\n eventReq: EventRequest,\n push: Collector.PushFn,\n logger?: Logger.Instance,\n requestId?: string,\n): Promise<{ id?: string; error?: string }> {\n try {\n const result = await push({\n name: eventReq.event,\n data: (eventReq.data || {}) as WalkerOS.Properties,\n context: eventReq.context as WalkerOS.OrderedProperties | undefined,\n user: eventReq.user as WalkerOS.User | undefined,\n globals: eventReq.globals as WalkerOS.Properties | undefined,\n consent: eventReq.consent as WalkerOS.Consent | undefined,\n });\n\n return { id: result?.event?.id };\n } catch (error) {\n // Log with structured context - per using-logger skill\n logger?.error('Event processing failed', {\n error,\n eventName: eventReq.event,\n requestId,\n });\n return { error: error instanceof Error ? error.message : 'Unknown error' };\n }\n}\n","import type { WalkerOS, Source as CoreSource } from '@walkeros/core';\nimport type {\n APIGatewayProxyEvent,\n APIGatewayProxyEventV2,\n APIGatewayProxyResult,\n Context,\n} from 'aws-lambda';\nimport type { SettingsSchema, CorsOptionsSchema } from './schemas';\nimport type { z } from '@walkeros/core/dev';\n\ndeclare module '@walkeros/core' {\n interface SourceMap {\n lambda: { type: 'lambda'; platform: 'server' };\n }\n}\n\n// Lambda event types\nexport type LambdaEvent = APIGatewayProxyEvent | APIGatewayProxyEventV2;\nexport type LambdaResult = APIGatewayProxyResult;\nexport type LambdaContext = Context;\n\n// Types inferred from Zod schemas\nexport type Settings = z.infer<typeof SettingsSchema>;\nexport type CorsOptions = z.infer<typeof CorsOptionsSchema>;\n\n// InitSettings: user input (all optional)\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n // Custom source event mapping properties\n}\n\n// Lambda-specific push type\nexport type Push = (\n event: LambdaEvent,\n context: LambdaContext,\n) => Promise<LambdaResult>;\n\nexport interface Env extends CoreSource.Env {\n lambdaEvent?: LambdaEvent;\n lambdaContext?: LambdaContext;\n}\n\n// Type bundle (must be after Settings, Mapping, Push, Env are defined)\nexport type Types = CoreSource.Types<\n Settings,\n Mapping,\n Push,\n Env,\n InitSettings\n>;\n\nexport interface LambdaSource extends Omit<CoreSource.Instance<Types>, 'push'> {\n push: Push;\n}\n\n// Convenience Config type\nexport type Config = CoreSource.Config<Types>;\nexport type PartialConfig = CoreSource.PartialConfig<Types>;\n\n// Lambda source doesn't follow standard Source.Init pattern due to Lambda handler interface\n\nexport interface EventRequest {\n event: string;\n data?: WalkerOS.AnyObject;\n context?: WalkerOS.AnyObject;\n user?: WalkerOS.AnyObject;\n globals?: WalkerOS.AnyObject;\n consent?: WalkerOS.AnyObject;\n}\n\nexport interface EventResponse {\n success: boolean;\n id?: string;\n error?: string;\n}\n\nexport type RequestBody = EventRequest;\nexport type ResponseBody = EventResponse;\n\n// Parsed request data structure\nexport interface ParsedRequest {\n method: string;\n body: unknown;\n queryString: string | null;\n headers: Record<string, string>;\n isBase64Encoded: boolean;\n}\n","import {\n DeleteMessageCommand,\n GetQueueAttributesCommand,\n GetQueueUrlCommand,\n ReceiveMessageCommand,\n SQSClient,\n} from '@aws-sdk/client-sqs';\nimport type { Collector, Logger, Source, WalkerOS } from '@walkeros/core';\nimport type {\n Settings,\n SyntheticMessage,\n SyntheticPushResult,\n Types,\n} from './types';\nimport { decodeBody, DecoderError } from './decoder';\nimport { getConfig } from './config';\nimport { setup } from './setup';\n\nexport { setup } from './setup';\n\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 30000;\nconst RECEIVE_RETRY_BACKOFF_MS = 1000;\n\ninterface MessageDispatch {\n id: string;\n body: string;\n receiptHandle?: string;\n ack(): Promise<void>;\n nack(): Promise<void>;\n}\n\nfunction toDeepPartialEvent(value: unknown): WalkerOS.DeepPartialEvent {\n if (typeof value === 'object' && value !== null) {\n // Plain object payloads pass through. The collector treats them as\n // DeepPartialEvent shapes; downstream transformers see the same data.\n return value as WalkerOS.DeepPartialEvent;\n }\n if (typeof value === 'string') return { data: { payload: value } };\n if (Buffer.isBuffer(value))\n return { data: { payload: value.toString('base64') } };\n return { data: { payload: String(value) } };\n}\n\nasync function handleMessage(args: {\n message: MessageDispatch;\n settings: Settings;\n pushFn: Collector.PushFn;\n logger: Logger.Instance;\n}): Promise<void> {\n const { message, settings, pushFn, logger } = args;\n try {\n const decoded = decodeBody(\n message.id,\n message.body,\n settings.decoder ?? 'json',\n );\n if (decoded === null || decoded === undefined) {\n logger.debug('SQS message acked-and-dropped (decoder returned null)', {\n id: message.id,\n });\n await message.ack();\n return;\n }\n const result = await pushFn(toDeepPartialEvent(decoded));\n if (result?.ok === false) {\n // Never delete a message that was not delivered. Route through the\n // existing error disposition (nack by default, ack if configured).\n throw new Error('push rejected the message (ok:false)');\n }\n await message.ack();\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n if (err instanceof DecoderError) {\n logger.error('SQS message decode failed', { id: message.id, error });\n } else {\n logger.error('SQS message handler failed', { id: message.id, error });\n }\n if (settings.onPushError === 'ack') await message.ack();\n else await message.nack();\n }\n}\n\nfunction isQueueDoesNotExist(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false;\n if (!('name' in err)) return false;\n const obj: { name?: unknown } = err;\n return (\n obj.name === 'QueueDoesNotExist' ||\n obj.name === 'AWS.SimpleQueueService.NonExistentQueue'\n );\n}\n\ninterface ClientWithDestroy {\n destroy?: () => void;\n}\n\nfunction tryDestroyClient(client: SQSClient, logger: Logger.Instance): void {\n // SQSClient implements destroy() in modern SDK versions; older versions\n // do not. Wrap defensively.\n const candidate: ClientWithDestroy = client;\n if (typeof candidate.destroy !== 'function') return;\n try {\n candidate.destroy();\n } catch (err) {\n logger.debug('SQS client destroy failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\ninterface FetchedMessage {\n MessageId: string;\n ReceiptHandle: string;\n Body: string;\n}\n\nfunction isFetchedMessage(value: unknown): value is FetchedMessage {\n if (typeof value !== 'object' || value === null) return false;\n const obj: {\n MessageId?: unknown;\n ReceiptHandle?: unknown;\n Body?: unknown;\n } = value;\n return (\n typeof obj.MessageId === 'string' &&\n typeof obj.ReceiptHandle === 'string' &&\n typeof obj.Body === 'string'\n );\n}\n\n/**\n * AWS SQS source.\n *\n * Long-running poll-and-forward subscriber. The factory validates the queue\n * exists and captures the canonical queueUrl/queueArn; the long-poll loop\n * starts as a background task on the collector's run lifecycle (`on('run')`),\n * so no message is received into a dormant collector. push() accepts an\n * optional synthetic message (used by tests / triggers) and dispatches it\n * through the same handler the loop uses, bypassing the run gate; called\n * without arguments (production) it is a no-op since SQS is event-driven.\n *\n * destroy() stops the loop, drains in-flight handlers, and force-closes after\n * shutdownTimeoutMs (default 30000).\n */\nexport const sourceSqs: Source.Init<Types> = async (context) => {\n const { config: partialConfig = {}, env, logger, id } = context;\n const config = getConfig(partialConfig, env, logger);\n const settings: Settings = config.settings;\n\n // Resolve queueUrl from queueName at init time.\n if (!settings.queueUrl) {\n try {\n const res = await settings.client.send(\n new GetQueueUrlCommand({ QueueName: settings.queueName }),\n );\n if (!res.QueueUrl) {\n return logger.throw(\n `SQS queue not found: ${settings.queueName}. ` +\n `Run \"walkeros setup source.${id}\" to create it.`,\n );\n }\n settings.queueUrl = res.QueueUrl;\n } catch (err) {\n if (isQueueDoesNotExist(err)) {\n return logger.throw(\n `SQS queue not found: ${settings.queueName}. ` +\n `Run \"walkeros setup source.${id}\" to create it.`,\n );\n }\n throw err;\n }\n }\n\n // Capture queueArn for downstream use (e.g. SNS subscription verification).\n if (!settings.queueArn) {\n try {\n const res = await settings.client.send(\n new GetQueueAttributesCommand({\n QueueUrl: settings.queueUrl,\n AttributeNames: ['QueueArn'],\n }),\n );\n settings.queueArn = res.Attributes?.QueueArn;\n } catch (err) {\n // ARN is informational; continue without it.\n logger.debug('SQS GetQueueAttributesCommand failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // Long-poll loop control flag and in-flight tracker.\n let stopped = false;\n const inFlight = new Set<Promise<void>>();\n\n const loop = async (): Promise<void> => {\n while (!stopped) {\n try {\n const res = await settings.client.send(\n new ReceiveMessageCommand({\n QueueUrl: settings.queueUrl,\n MaxNumberOfMessages: settings.maxMessages ?? 10,\n WaitTimeSeconds: settings.waitTimeSeconds ?? 20,\n VisibilityTimeout: settings.visibilityTimeout,\n MessageAttributeNames: ['All'],\n AttributeNames: ['All'],\n }),\n );\n const messages = res.Messages ?? [];\n for (const msg of messages) {\n if (!isFetchedMessage(msg)) {\n logger.warn('SQS received malformed message; skipping', {\n messageId:\n typeof msg.MessageId === 'string' ? msg.MessageId : undefined,\n });\n continue;\n }\n const fetched: FetchedMessage = msg;\n const dispatch: MessageDispatch = {\n id: fetched.MessageId,\n body: fetched.Body,\n receiptHandle: fetched.ReceiptHandle,\n ack: async () => {\n await settings.client.send(\n new DeleteMessageCommand({\n QueueUrl: settings.queueUrl ?? '',\n ReceiptHandle: fetched.ReceiptHandle,\n }),\n );\n },\n nack: async () => {\n // SQS has no explicit nack: skipping DeleteMessage causes\n // redelivery after VisibilityTimeout expires.\n },\n };\n const handler = handleMessage({\n message: dispatch,\n settings,\n pushFn: env.push,\n logger,\n });\n inFlight.add(handler);\n handler.finally(() => {\n inFlight.delete(handler);\n });\n }\n } catch (err) {\n if (stopped) break;\n const error = err instanceof Error ? err.message : String(err);\n if (isQueueDoesNotExist(err)) {\n logger.error(\n `SQS queue \"${settings.queueName}\" not found at runtime. ` +\n `Run \"walkeros setup source.${id}\" to create it. Original: ${error}`,\n );\n // Stop the loop on a fatal error; operator must intervene.\n stopped = true;\n break;\n }\n logger.error('SQS receive error (will retry)', { error });\n // Backoff before retry to avoid tight-looping on persistent errors.\n await new Promise((r) => setTimeout(r, RECEIVE_RETRY_BACKOFF_MS));\n }\n }\n };\n\n // Run-gated start (see pubsub-pull): never poll a broker into a dormant\n // collector. The loop is launched without awaiting so `on('run')` returns\n // immediately; the promise is tracked so destroy() can await it (prevents\n // leaked timers in test runs), and destroy() awaits it only if it ever\n // started.\n let loopPromise: Promise<void> | undefined;\n const startConsuming = () => {\n if (loopPromise) return;\n loopPromise = loop();\n };\n\n return {\n type: 'sqs',\n config,\n setup,\n on: (type) => {\n if (type === 'run') startConsuming();\n },\n push: async (\n content?: SyntheticMessage,\n ): Promise<SyntheticPushResult | void> => {\n if (!content) return;\n const result: SyntheticPushResult = { acked: false, nacked: false };\n await handleMessage({\n message: {\n id: content.MessageId,\n body: content.Body,\n receiptHandle: content.ReceiptHandle,\n ack: async () => {\n result.acked = true;\n },\n nack: async () => {\n result.nacked = true;\n },\n },\n settings,\n pushFn: env.push,\n logger,\n });\n return result;\n },\n destroy: async () => {\n stopped = true;\n const timeoutMs =\n settings.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;\n let timer: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n (async () => {\n // Wait for the loop to exit AND in-flight handlers to drain.\n await loopPromise;\n await Promise.all(Array.from(inFlight));\n })(),\n new Promise<void>((resolve) => {\n timer = setTimeout(() => {\n logger.warn(\n `SQS source close timed out after ${timeoutMs}ms; forcing close.`,\n );\n resolve();\n }, timeoutMs);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n tryDestroyClient(settings.client, logger);\n }\n },\n };\n};\n\nexport default sourceSqs;\n","import type { Decoder } from './types';\n\nexport class DecoderError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DecoderError';\n }\n}\n\n/**\n * Decode an SQS message body string.\n *\n * - 'json': JSON.parse(body). Throws DecoderError on parse failure.\n * - 'text': body returned as-is.\n * - 'raw': Buffer.from(body, 'utf8').\n */\nexport function decodeBody(\n messageId: string,\n body: string,\n decoder: Decoder,\n): unknown {\n if (decoder === 'raw') return Buffer.from(body, 'utf8');\n if (decoder === 'text') return body;\n // json\n try {\n return JSON.parse(body);\n } catch (err) {\n throw new DecoderError(\n `SQS message ${messageId} JSON decode failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n}\n","import { SQSClient } from '@aws-sdk/client-sqs';\nimport type { Logger } from '@walkeros/core';\nimport type {\n Config,\n Env,\n InitSettings,\n PartialConfig,\n Settings,\n} from './types';\n\nconst DEFAULT_REGION = 'eu-central-1';\nconst DEFAULT_DECODER = 'json' as const;\nconst DEFAULT_MAX_MESSAGES = 10;\nconst DEFAULT_WAIT_TIME = 20;\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 30000;\nconst DEFAULT_ON_PUSH_ERROR = 'nack' as const;\n\n/**\n * Runtime config with the narrow Settings shape (all defaults applied).\n *\n * The framework's `Source.Config` types `settings` as `InitSettings | undefined`;\n * this alias reflects the post-getConfig invariant where settings is fully\n * resolved and the SQSClient handle is built.\n */\nexport type RuntimeConfig = Omit<Config, 'settings'> & { settings: Settings };\n\n/**\n * Validate and normalize the partial config into a runtime Config.\n *\n * The client is built once: prefer pre-supplied `settings.client`, then env-\n * injected constructor (tests/DI), then the static SDK import. `Settings.client`\n * is always populated before init/destroy run.\n */\nexport function getConfig(\n partial: PartialConfig = {},\n env: Env | undefined,\n logger: Logger.Instance,\n): RuntimeConfig {\n const empty: Partial<InitSettings> = {};\n const partialSettings: Partial<InitSettings> = partial.settings ?? empty;\n const queueName =\n typeof partialSettings.queueName === 'string'\n ? partialSettings.queueName\n : '';\n if (!queueName) logger.throw('Config settings queueName missing');\n\n const region = partialSettings.region ?? DEFAULT_REGION;\n\n let client = partialSettings.client;\n if (!client) {\n const Constructor = env?.AWS?.SQSClient ?? SQSClient;\n client = new Constructor({ region, ...partialSettings.config });\n }\n\n const settings: Settings = {\n ...partialSettings,\n client,\n queueName,\n region,\n queueUrl: partialSettings.queueUrl,\n config: partialSettings.config,\n decoder: partialSettings.decoder ?? DEFAULT_DECODER,\n maxMessages: partialSettings.maxMessages ?? DEFAULT_MAX_MESSAGES,\n waitTimeSeconds: partialSettings.waitTimeSeconds ?? DEFAULT_WAIT_TIME,\n visibilityTimeout: partialSettings.visibilityTimeout,\n shutdownTimeoutMs:\n partialSettings.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS,\n onPushError: partialSettings.onPushError ?? DEFAULT_ON_PUSH_ERROR,\n };\n\n return { ...partial, settings };\n}\n","import {\n CreateQueueCommand,\n GetQueueAttributesCommand,\n SQSClient,\n} from '@aws-sdk/client-sqs';\nimport { SNSClient, SubscribeCommand } from '@aws-sdk/client-sns';\nimport type { Logger } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { Config, Env, Setup, SetupFn } from './types';\n\nconst DEFAULT_REGION = 'eu-central-1';\nconst DEFAULT_VISIBILITY_TIMEOUT_SECONDS = 30;\nconst DEFAULT_MESSAGE_RETENTION_SECONDS = 345600;\nconst DEFAULT_MAXIMUM_MESSAGE_SIZE = 262144;\nconst DEFAULT_DLQ_MAX_RECEIVE_COUNT = 5;\nconst DLQ_RETENTION_SECONDS = 1209600; // 14 days, AWS max\n\nexport const DEFAULT_SETUP: Setup = {\n region: DEFAULT_REGION,\n fifoQueue: false,\n visibilityTimeoutSeconds: DEFAULT_VISIBILITY_TIMEOUT_SECONDS,\n messageRetentionSeconds: DEFAULT_MESSAGE_RETENTION_SECONDS,\n maximumMessageSize: DEFAULT_MAXIMUM_MESSAGE_SIZE,\n};\n\nexport interface SetupResult {\n queueCreated: boolean;\n queueUrl: string;\n queueArn: string;\n dlqCreated?: boolean;\n dlqArn?: string;\n subscriptionArn?: string;\n}\n\ninterface QueueExistsError {\n name: string;\n}\n\nfunction hasName(err: unknown): err is QueueExistsError {\n if (typeof err !== 'object' || err === null) return false;\n const obj: { name?: unknown } = err;\n return typeof obj.name === 'string';\n}\n\nfunction isQueueNameExists(err: unknown): boolean {\n if (!hasName(err)) return false;\n return (\n err.name === 'QueueNameExists' ||\n err.name === 'QueueAlreadyExists' ||\n err.name === 'AWS.SimpleQueueService.QueueNameExists'\n );\n}\n\ninterface BuildQueueAttributesArgs {\n options: Setup;\n dlqArn: string | undefined;\n queuePolicy: string | undefined;\n}\n\nfunction buildQueueAttributes(\n args: BuildQueueAttributesArgs,\n): Record<string, string> {\n const { options, dlqArn, queuePolicy } = args;\n const attrs: Record<string, string> = {};\n attrs.VisibilityTimeout = String(\n options.visibilityTimeoutSeconds ?? DEFAULT_VISIBILITY_TIMEOUT_SECONDS,\n );\n attrs.MessageRetentionPeriod = String(\n options.messageRetentionSeconds ?? DEFAULT_MESSAGE_RETENTION_SECONDS,\n );\n attrs.MaximumMessageSize = String(\n options.maximumMessageSize ?? DEFAULT_MAXIMUM_MESSAGE_SIZE,\n );\n if (options.kmsMasterKeyId) {\n attrs.KmsMasterKeyId = options.kmsMasterKeyId;\n }\n if (options.fifoQueue) {\n attrs.FifoQueue = 'true';\n attrs.ContentBasedDeduplication = 'true';\n }\n if (dlqArn) {\n const maxReceiveCount =\n options.deadLetterQueue?.maxReceiveCount ?? DEFAULT_DLQ_MAX_RECEIVE_COUNT;\n attrs.RedrivePolicy = JSON.stringify({\n deadLetterTargetArn: dlqArn,\n maxReceiveCount,\n });\n }\n if (queuePolicy) {\n attrs.Policy = queuePolicy;\n }\n return attrs;\n}\n\nfunction buildDlqAttributes(args: {\n fifoQueue: boolean;\n kmsMasterKeyId?: string;\n}): Record<string, string> {\n const attrs: Record<string, string> = {};\n attrs.MessageRetentionPeriod = String(DLQ_RETENTION_SECONDS);\n if (args.kmsMasterKeyId) {\n attrs.KmsMasterKeyId = args.kmsMasterKeyId;\n }\n if (args.fifoQueue) {\n attrs.FifoQueue = 'true';\n attrs.ContentBasedDeduplication = 'true';\n }\n return attrs;\n}\n\nfunction buildDlqTags(\n parentTags: Record<string, string> | undefined,\n): Record<string, string> {\n return {\n ...(parentTags ?? {}),\n walkerOS: 'dlq',\n };\n}\n\ninterface BuildPolicyArgs {\n sourceId: string;\n topicArn: string;\n}\n\nfunction buildSnsQueuePolicy(args: BuildPolicyArgs): string {\n // Placeholder Resource value; AWS does not require it to match the actual\n // queue ARN at policy-attach time when the queue is being created in the\n // same call. The Sid keys the statement so re-runs upsert in place.\n return JSON.stringify({\n Version: '2012-10-17',\n Statement: [\n {\n Sid: `walkerOSAllowSNSPublish-${args.sourceId}`,\n Effect: 'Allow',\n Principal: { Service: 'sns.amazonaws.com' },\n Action: 'SQS:SendMessage',\n Resource: '*',\n Condition: {\n ArnEquals: {\n 'aws:SourceArn': args.topicArn,\n },\n },\n },\n ],\n });\n}\n\ninterface EnsureQueueArgs {\n sqs: SQSClient;\n logger: Logger.Instance;\n queueName: string;\n attributes: Record<string, string>;\n tags?: Record<string, string>;\n}\n\ninterface EnsureQueueResult {\n queueCreated: boolean;\n queueUrl: string;\n queueArn: string;\n}\n\n/**\n * Authoritative-apply: a single CreateQueueCommand with the full declared\n * attribute map and tags. AWS treats identical inputs as success; on\n * QueueNameExists (different attrs) we hard-fail with an actionable message.\n * Setup never calls SetQueueAttributesCommand.\n */\nasync function ensureQueue(args: EnsureQueueArgs): Promise<EnsureQueueResult> {\n const { sqs, logger, queueName, attributes, tags } = args;\n let queueUrl: string;\n let queueCreated = false;\n try {\n const res = await sqs.send(\n new CreateQueueCommand({\n QueueName: queueName,\n Attributes: attributes,\n tags,\n }),\n );\n queueUrl = res.QueueUrl ?? '';\n queueCreated = true;\n logger.info(`SQS queue \"${queueName}\" created.`);\n } catch (err) {\n if (isQueueNameExists(err)) {\n const message = err instanceof Error ? err.message : String(err);\n return logger.throw(\n `SQS queue \"${queueName}\" exists with different attributes (${message}). ` +\n `Setup will not mutate an existing queue's attributes. Delete or rename the queue, then re-run setup.`,\n );\n }\n throw err;\n }\n if (!queueUrl) {\n return logger.throw(\n `SQS CreateQueueCommand returned no QueueUrl for \"${queueName}\".`,\n );\n }\n // Fetch QueueArn for downstream Subscribe / RedrivePolicy use.\n const attrsRes = await sqs.send(\n new GetQueueAttributesCommand({\n QueueUrl: queueUrl,\n AttributeNames: ['QueueArn'],\n }),\n );\n const queueArn = attrsRes.Attributes?.QueueArn ?? '';\n if (!queueArn) {\n return logger.throw(\n `SQS GetQueueAttributesCommand returned no QueueArn for \"${queueName}\".`,\n );\n }\n return { queueCreated, queueUrl, queueArn };\n}\n\nfunction dlqName(queueName: string, fifo: boolean): string {\n if (fifo) {\n // FIFO queue names must end in .fifo. Strip the suffix if present, append -dlq, then re-add.\n const base = queueName.endsWith('.fifo')\n ? queueName.slice(0, -'.fifo'.length)\n : queueName;\n return `${base}-dlq.fifo`;\n }\n return `${queueName}-dlq`;\n}\n\nexport const setup: SetupFn = async (context) => {\n const { id, config, env, logger } = context;\n\n const merged = resolveSetup<Setup>(config.setup, DEFAULT_SETUP);\n if (!merged) {\n logger.debug('SQS source setup skipped (config.setup is falsy).');\n return undefined;\n }\n const options: Setup = { ...DEFAULT_SETUP, ...merged };\n\n const settings = config.settings;\n if (!settings) return logger.throw('setup: settings missing');\n\n const queueName = settings.queueName;\n if (!queueName) return logger.throw('setup: settings.queueName is missing');\n\n const region = options.region ?? DEFAULT_REGION;\n\n // Construct clients via env override (tests) or static SDK import (prod).\n const SqsCtor = env.AWS?.SQSClient ?? SQSClient;\n const sqs = new SqsCtor({ region });\n\n // 1. Optional DLQ first (so we have its ARN for RedrivePolicy on the main queue).\n let dlqArn: string | undefined;\n let dlqCreated = false;\n if (options.deadLetterQueue?.arn) {\n dlqArn = options.deadLetterQueue.arn;\n } else if (options.deadLetterQueue?.create) {\n const dlqResult = await ensureQueue({\n sqs,\n logger,\n queueName: dlqName(queueName, options.fifoQueue ?? false),\n attributes: buildDlqAttributes({\n fifoQueue: options.fifoQueue ?? false,\n kmsMasterKeyId: options.kmsMasterKeyId,\n }),\n tags: buildDlqTags(options.tags),\n });\n dlqArn = dlqResult.queueArn;\n dlqCreated = dlqResult.queueCreated;\n }\n\n // 2. Main queue.\n const finalQueueName = options.fifoQueue\n ? queueName.endsWith('.fifo')\n ? queueName\n : `${queueName}.fifo`\n : queueName;\n const queuePolicy = options.subscribeToSnsTopic\n ? buildSnsQueuePolicy({\n sourceId: id,\n topicArn: options.subscribeToSnsTopic.topicArn,\n })\n : undefined;\n\n const mainAttrs = buildQueueAttributes({\n options,\n dlqArn,\n queuePolicy,\n });\n const mainResult = await ensureQueue({\n sqs,\n logger,\n queueName: finalQueueName,\n attributes: mainAttrs,\n tags: options.tags,\n });\n\n // 3. Optional SNS subscription.\n let subscriptionArn: string | undefined;\n if (options.subscribeToSnsTopic) {\n const SnsCtor = env.AWS?.SNSClient ?? SNSClient;\n const sns = new SnsCtor({ region });\n const subAttrs: Record<string, string> = {};\n if (options.subscribeToSnsTopic.rawMessageDelivery) {\n subAttrs.RawMessageDelivery = 'true';\n }\n if (options.subscribeToSnsTopic.filterPolicy) {\n subAttrs.FilterPolicy = JSON.stringify(\n options.subscribeToSnsTopic.filterPolicy,\n );\n }\n const subscribeRes = await sns.send(\n new SubscribeCommand({\n TopicArn: options.subscribeToSnsTopic.topicArn,\n Protocol: 'sqs',\n Endpoint: mainResult.queueArn,\n Attributes: Object.keys(subAttrs).length > 0 ? subAttrs : undefined,\n ReturnSubscriptionArn: true,\n }),\n );\n subscriptionArn = subscribeRes.SubscriptionArn ?? undefined;\n logger.info('SQS source setup: SNS subscription created', {\n topicArn: options.subscribeToSnsTopic.topicArn,\n subscriptionArn,\n });\n }\n\n const result: SetupResult = {\n queueCreated: mainResult.queueCreated,\n queueUrl: mainResult.queueUrl,\n queueArn: mainResult.queueArn,\n dlqCreated: dlqArn ? dlqCreated : undefined,\n dlqArn,\n subscriptionArn,\n };\n return result;\n};\n","import type {\n Source as CoreSource,\n SetupFn as CoreSetupFn,\n} from '@walkeros/core';\nimport type {\n SQSClient,\n SQSClientConfig,\n Message,\n ReceiveMessageCommand,\n DeleteMessageCommand,\n CreateQueueCommand,\n GetQueueUrlCommand,\n GetQueueAttributesCommand,\n} from '@aws-sdk/client-sqs';\nimport type { SNSClient, SubscribeCommand } from '@aws-sdk/client-sns';\n\ndeclare module '@walkeros/core' {\n interface SourceMap {\n sqs: { type: 'sqs'; platform: 'server' };\n }\n}\n\nexport type Decoder = 'json' | 'text' | 'raw';\n\nexport interface Settings {\n // User-supplied OR populated by getConfig(); single field for both. Mirrors the Pub/Sub pull source.\n client: SQSClient;\n // Required. Used by setup to provision and by init to resolve the URL.\n queueName: string;\n // AWS region. Default: 'eu-central-1'.\n region?: string;\n // Optional pre-resolved URL. When unset, init resolves via GetQueueUrlCommand.\n queueUrl?: string;\n // SDK client config (credentials, etc.). Optional.\n config?: SQSClientConfig;\n // Decoder for message body. Default: 'json'.\n decoder?: Decoder;\n // Receive batch size. Cap 10, default 10.\n maxMessages?: number;\n // Long-poll duration in seconds. Cap 20, default 20.\n waitTimeSeconds?: number;\n // Per-receive visibility timeout override. Default: queue's configured value.\n visibilityTimeout?: number;\n // Graceful shutdown timeout in ms. Default: 30000.\n shutdownTimeoutMs?: number;\n // Error policy: 'ack' (DeleteMessage to drop) or 'nack' (no DeleteMessage, redeliver). Default: 'nack'.\n onPushError?: 'nack' | 'ack';\n // Runtime-only handle populated by init(); not user-facing.\n queueArn?: string;\n}\n\nexport interface InitSettings {\n queueName: string;\n client?: SQSClient;\n region?: string;\n queueUrl?: string;\n config?: SQSClientConfig;\n decoder?: Decoder;\n maxMessages?: number;\n waitTimeSeconds?: number;\n visibilityTimeout?: number;\n shutdownTimeoutMs?: number;\n onPushError?: 'nack' | 'ack';\n}\n\n// Mapping has no per-event fields in v1: SQS messages have no native ordering\n// key, no per-event visibility-extension API, no per-event delete strategy that\n// SQS itself consumes. Future per-event fields will be introduced when a real\n// feature needs them.\nexport interface Mapping {\n // Reserved for future use.\n}\n\n/**\n * Synthetic message input used by tests / triggers to dispatch through the\n * same handler the long-poll loop uses, without involving real SQS.\n *\n * In production, push() is invoked without arguments and is a no-op:\n * SQS is event-driven, the long-poll loop is the canonical delivery path.\n * Tests pass a synthetic input to dispatch directly.\n */\nexport interface SyntheticMessage {\n MessageId: string;\n Body: string;\n ReceiptHandle?: string;\n Attributes?: Record<string, string>;\n MessageAttributes?: Record<\n string,\n { DataType: string; StringValue?: string }\n >;\n}\n\nexport interface SyntheticPushResult {\n acked: boolean;\n nacked: boolean;\n}\n\nexport type Push = (\n content?: SyntheticMessage,\n) => Promise<SyntheticPushResult | void>;\n\nexport interface Env extends CoreSource.Env {\n AWS?: {\n SQSClient: typeof SQSClient;\n ReceiveMessageCommand: typeof ReceiveMessageCommand;\n DeleteMessageCommand: typeof DeleteMessageCommand;\n CreateQueueCommand: typeof CreateQueueCommand;\n GetQueueUrlCommand: typeof GetQueueUrlCommand;\n GetQueueAttributesCommand: typeof GetQueueAttributesCommand;\n // SNS used by setup only when subscribeToSnsTopic is set.\n SNSClient?: typeof SNSClient;\n SubscribeCommand?: typeof SubscribeCommand;\n };\n}\n\n/**\n * Provisioning options for `walkeros setup source.<id>`.\n *\n * Triggered only by the explicit CLI command. Idempotent. Never auto-run.\n *\n * `queueName` lives in Settings (one source of truth for setup AND runtime poll).\n */\nexport interface Setup {\n /** AWS region. Default: 'eu-central-1'. */\n region?: string;\n /** FIFO queue with content-based deduplication. Default: false. */\n fifoQueue?: boolean;\n /** Visibility timeout in seconds. Default: 30. */\n visibilityTimeoutSeconds?: number;\n /** Message retention period in seconds. Default: 345600 (4 days). */\n messageRetentionSeconds?: number;\n /** Max message size in bytes. Default: 262144 (256 KB). */\n maximumMessageSize?: number;\n /** KMS key for at-rest encryption. Optional. */\n kmsMasterKeyId?: string;\n /** Optional dead-letter queue. */\n deadLetterQueue?: {\n /** ARN of an existing DLQ. Mutually exclusive with `create: true`. */\n arn?: string;\n /** Create a sibling DLQ named <queueName>-dlq. Default: false. */\n create?: boolean;\n /** Max receive count before message goes to DLQ. Default: 5. */\n maxReceiveCount?: number;\n };\n /** Tags applied to the queue (and inherited by an auto-created DLQ). */\n tags?: Record<string, string>;\n /** Optional: subscribe this queue to an SNS topic. Creates SNS subscription + queue policy. */\n subscribeToSnsTopic?: {\n /** Topic ARN to subscribe to. Required. */\n topicArn: string;\n /** Deliver SNS messages without the SNS envelope. Default: false. */\n rawMessageDelivery?: boolean;\n /** SNS filter policy (subscription-time message filtering). */\n filterPolicy?: Record<string, unknown>;\n };\n}\n\n// Source.Types has 6 slots: <Settings, Mapping, Push, Env, InitSettings, Setup>.\n// Setup MUST be in slot 6; mis-positioning silently demotes Config['setup'] to unknown.\nexport type Types = CoreSource.Types<\n Settings,\n Mapping,\n Push,\n Env,\n InitSettings,\n Setup\n>;\n\nexport type Config = CoreSource.Config<Types>;\nexport type PartialConfig = CoreSource.PartialConfig<Types>;\nexport type SetupFn = CoreSetupFn<Config, Env>;\n\n// Re-export SDK types used in this package's public surface.\nexport type { SQSClient, SQSClientConfig, Message, SNSClient };\n"],"mappings":"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","names":["types_exports","import_client_sqs","import_client_sqs","import_core","DEFAULT_REGION","DEFAULT_SHUTDOWN_TIMEOUT_MS","types_exports"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{requestToData}from"@walkeros/core";function isAPIGatewayV2(event){return"version"in event&&"2.0"===event.version}function createResponse(statusCode,body,headers={},requestId){const responseHeaders={"Content-Type":"object"==typeof body?"application/json":"text/plain",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:statusCode,headers:responseHeaders,body:"object"==typeof body?JSON.stringify(body):String(body),isBase64Encoded:!1}}var types_exports={},lambda_default=async context=>{const{config:config={},env:env}=context,userSettings=config.settings||{},settings={...userSettings,cors:userSettings.cors??!0,timeout:userSettings.timeout??3e4,enablePixelTracking:userSettings.enablePixelTracking??!0,healthPath:userSettings.healthPath??"/health"};return{type:"lambda",config:{...config,settings:settings},push:async(event,lambdaContext)=>{const requestId=lambdaContext.awsRequestId;let parsed;try{const corsHeaders=function(corsOptions){if(!corsOptions)return{};if(!0===corsOptions)return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Authorization","Access-Control-Max-Age":"3600"};const headers={};if(corsOptions.origin){const origin=Array.isArray(corsOptions.origin)?corsOptions.origin.join(", "):corsOptions.origin;headers["Access-Control-Allow-Origin"]=origin}return corsOptions.methods&&(headers["Access-Control-Allow-Methods"]=corsOptions.methods.join(", ")),corsOptions.headers&&(headers["Access-Control-Allow-Headers"]=corsOptions.headers.join(", ")),corsOptions.credentials&&(headers["Access-Control-Allow-Credentials"]="true"),void 0!==corsOptions.maxAge&&(headers["Access-Control-Max-Age"]=corsOptions.maxAge.toString()),headers}(settings.cors||!1);parsed=function(event){if(isAPIGatewayV2(event)){const headers={};return event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)}),{method:event.requestContext.http.method,body:event.body,queryString:event.rawQueryString||null,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}{const headers={};event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)});let queryString=null;if(event.queryStringParameters){const params=new URLSearchParams;Object.entries(event.queryStringParameters).forEach(([key,value])=>{value&¶ms.append(key,value)}),queryString=params.toString()||null}return{method:event.httpMethod,body:event.body,queryString:queryString,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}}(event);const path=function(event){return isAPIGatewayV2(event)?event.rawPath:event.path}(event);return settings.healthPath&&path===settings.healthPath?createResponse(200,{status:"ok",timestamp:Date.now(),source:"lambda",requestId:requestId},corsHeaders,requestId):"OPTIONS"===parsed.method?createResponse(204,"",corsHeaders,requestId):await context.withScope(event,void 0,async scopeEnv=>{const envPush=scopeEnv.push;if("GET"===parsed.method){if(!settings.enablePixelTracking)return createResponse(405,{success:!1,error:"GET not allowed",requestId:requestId},corsHeaders,requestId);if(parsed.queryString){const parsedData=requestToData(parsed.queryString);parsedData&&"object"==typeof parsedData&&await envPush(parsedData)}return function(headers={},requestId){const responseHeaders={"Content-Type":"image/gif","Cache-Control":"no-cache, no-store, must-revalidate",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:200,headers:responseHeaders,body:"R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",isBase64Encoded:!0}}(corsHeaders,requestId)}if("POST"===parsed.method){if(!parsed.body)return createResponse(400,{success:!1,error:"Request body is required",requestId:requestId},corsHeaders,requestId);const body=function(body,isBase64Encoded){if(!body||"string"!=typeof body)return body;try{const decoded=isBase64Encoded?Buffer.from(body,"base64").toString("utf8"):body;return JSON.parse(decoded)}catch{return body}}(parsed.body,parsed.isBase64Encoded);if(!body||"object"!=typeof body)return await envPush({}),createResponse(200,{success:!0,requestId:requestId},corsHeaders,requestId);if(function(body){return"object"==typeof body&&null!==body&&"event"in body&&"string"==typeof body.event}(body)){const result=await async function(eventReq,push,logger,requestId){try{const result=await push({name:eventReq.event,data:eventReq.data||{},context:eventReq.context,user:eventReq.user,globals:eventReq.globals,consent:eventReq.consent});return{id:result?.event?.id}}catch(error){return logger?.error("Event processing failed",{error:error,eventName:eventReq.event,requestId:requestId}),{error:error instanceof Error?error.message:"Unknown error"}}}(body,envPush,env.logger,requestId);return result.error?createResponse(400,{success:!1,error:result.error,requestId:requestId},corsHeaders,requestId):createResponse(200,{success:!0,id:result.id,requestId:requestId},corsHeaders,requestId)}return createResponse(400,{success:!1,error:"Invalid request format",requestId:requestId},corsHeaders,requestId)}return createResponse(405,{success:!1,error:"Method not allowed",requestId:requestId},corsHeaders,requestId)})}catch(error){return env.logger?.error("Lambda handler error",{error:error,requestId:requestId,method:parsed?.method}),createResponse(500,{success:!1,error:error instanceof Error?error.message:"Internal server error",requestId:requestId},{},requestId)}}}};import{DeleteMessageCommand,GetQueueAttributesCommand as GetQueueAttributesCommand2,GetQueueUrlCommand,ReceiveMessageCommand}from"@aws-sdk/client-sqs";var DecoderError=class extends Error{constructor(message){super(message),this.name="DecoderError"}};import{SQSClient}from"@aws-sdk/client-sqs";import{CreateQueueCommand,GetQueueAttributesCommand,SQSClient as SQSClient2}from"@aws-sdk/client-sqs";import{SNSClient,SubscribeCommand}from"@aws-sdk/client-sns";import{resolveSetup}from"@walkeros/core";var DEFAULT_SETUP={region:"eu-central-1",fifoQueue:!1,visibilityTimeoutSeconds:30,messageRetentionSeconds:345600,maximumMessageSize:262144};function buildDlqAttributes(args){const attrs={};return attrs.MessageRetentionPeriod=String(1209600),args.kmsMasterKeyId&&(attrs.KmsMasterKeyId=args.kmsMasterKeyId),args.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),attrs}async function ensureQueue(args){const{sqs:sqs,logger:logger,queueName:queueName,attributes:attributes,tags:tags}=args;let queueUrl,queueCreated=!1;try{queueUrl=(await sqs.send(new CreateQueueCommand({QueueName:queueName,Attributes:attributes,tags:tags}))).QueueUrl??"",queueCreated=!0,logger.info(`SQS queue "${queueName}" created.`)}catch(err){if(function(err){return!!function(err){return"object"==typeof err&&null!==err&&"string"==typeof err.name}(err)&&("QueueNameExists"===err.name||"QueueAlreadyExists"===err.name||"AWS.SimpleQueueService.QueueNameExists"===err.name)}(err)){const message=err instanceof Error?err.message:String(err);return logger.throw(`SQS queue "${queueName}" exists with different attributes (${message}). Setup will not mutate an existing queue's attributes. Delete or rename the queue, then re-run setup.`)}throw err}if(!queueUrl)return logger.throw(`SQS CreateQueueCommand returned no QueueUrl for "${queueName}".`);const attrsRes=await sqs.send(new GetQueueAttributesCommand({QueueUrl:queueUrl,AttributeNames:["QueueArn"]})),queueArn=attrsRes.Attributes?.QueueArn??"";return queueArn?{queueCreated:queueCreated,queueUrl:queueUrl,queueArn:queueArn}:logger.throw(`SQS GetQueueAttributesCommand returned no QueueArn for "${queueName}".`)}function dlqName(queueName,fifo){if(fifo){return`${queueName.endsWith(".fifo")?queueName.slice(0,-5):queueName}-dlq.fifo`}return`${queueName}-dlq`}var setup=async context=>{const{id:id,config:config,env:env,logger:logger}=context,merged=resolveSetup(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("SQS source setup skipped (config.setup is falsy).");const options={...DEFAULT_SETUP,...merged},settings=config.settings;if(!settings)return logger.throw("setup: settings missing");const queueName=settings.queueName;if(!queueName)return logger.throw("setup: settings.queueName is missing");const region=options.region??"eu-central-1",sqs=new(env.AWS?.SQSClient??SQSClient2)({region:region});let dlqArn,dlqCreated=!1;if(options.deadLetterQueue?.arn)dlqArn=options.deadLetterQueue.arn;else if(options.deadLetterQueue?.create){const dlqResult=await ensureQueue({sqs:sqs,logger:logger,queueName:dlqName(queueName,options.fifoQueue??!1),attributes:buildDlqAttributes({fifoQueue:options.fifoQueue??!1,kmsMasterKeyId:options.kmsMasterKeyId}),tags:(parentTags=options.tags,{...parentTags??{},walkerOS:"dlq"})});dlqArn=dlqResult.queueArn,dlqCreated=dlqResult.queueCreated}var parentTags;const finalQueueName=options.fifoQueue?queueName.endsWith(".fifo")?queueName:`${queueName}.fifo`:queueName;var args;const mainAttrs=function(args){const{options:options,dlqArn:dlqArn,queuePolicy:queuePolicy}=args,attrs={};if(attrs.VisibilityTimeout=String(options.visibilityTimeoutSeconds??30),attrs.MessageRetentionPeriod=String(options.messageRetentionSeconds??345600),attrs.MaximumMessageSize=String(options.maximumMessageSize??262144),options.kmsMasterKeyId&&(attrs.KmsMasterKeyId=options.kmsMasterKeyId),options.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),dlqArn){const maxReceiveCount=options.deadLetterQueue?.maxReceiveCount??5;attrs.RedrivePolicy=JSON.stringify({deadLetterTargetArn:dlqArn,maxReceiveCount:maxReceiveCount})}return queuePolicy&&(attrs.Policy=queuePolicy),attrs}({options:options,dlqArn:dlqArn,queuePolicy:options.subscribeToSnsTopic?(args={sourceId:id,topicArn:options.subscribeToSnsTopic.topicArn},JSON.stringify({Version:"2012-10-17",Statement:[{Sid:`walkerOSAllowSNSPublish-${args.sourceId}`,Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"SQS:SendMessage",Resource:"*",Condition:{ArnEquals:{"aws:SourceArn":args.topicArn}}}]})):void 0}),mainResult=await ensureQueue({sqs:sqs,logger:logger,queueName:finalQueueName,attributes:mainAttrs,tags:options.tags});let subscriptionArn;if(options.subscribeToSnsTopic){const sns=new(env.AWS?.SNSClient??SNSClient)({region:region}),subAttrs={};options.subscribeToSnsTopic.rawMessageDelivery&&(subAttrs.RawMessageDelivery="true"),options.subscribeToSnsTopic.filterPolicy&&(subAttrs.FilterPolicy=JSON.stringify(options.subscribeToSnsTopic.filterPolicy));subscriptionArn=(await sns.send(new SubscribeCommand({TopicArn:options.subscribeToSnsTopic.topicArn,Protocol:"sqs",Endpoint:mainResult.queueArn,Attributes:Object.keys(subAttrs).length>0?subAttrs:void 0,ReturnSubscriptionArn:!0}))).SubscriptionArn??void 0,logger.info("SQS source setup: SNS subscription created",{topicArn:options.subscribeToSnsTopic.topicArn,subscriptionArn:subscriptionArn})}return{queueCreated:mainResult.queueCreated,queueUrl:mainResult.queueUrl,queueArn:mainResult.queueArn,dlqCreated:dlqArn?dlqCreated:void 0,dlqArn:dlqArn,subscriptionArn:subscriptionArn}};async function handleMessage(args){const{message:message,settings:settings,pushFn:pushFn,logger:logger}=args;try{const decoded=function(messageId,body,decoder){if("raw"===decoder)return Buffer.from(body,"utf8");if("text"===decoder)return body;try{return JSON.parse(body)}catch(err){throw new DecoderError(`SQS message ${messageId} JSON decode failed: ${err instanceof Error?err.message:String(err)}`)}}(message.id,message.body,settings.decoder??"json");if(null==decoded)return logger.debug("SQS message acked-and-dropped (decoder returned null)",{id:message.id}),void await message.ack();await pushFn((value=decoded,"object"==typeof value&&null!==value?value:"string"==typeof value?{data:{payload:value}}:Buffer.isBuffer(value)?{data:{payload:value.toString("base64")}}:{data:{payload:String(value)}})),await message.ack()}catch(err){const error=err instanceof Error?err.message:String(err);err instanceof DecoderError?logger.error("SQS message decode failed",{id:message.id,error:error}):logger.error("SQS message handler failed",{id:message.id,error:error}),"ack"===settings.onPushError?await message.ack():await message.nack()}var value}function isQueueDoesNotExist(err){if("object"!=typeof err||null===err)return!1;if(!("name"in err))return!1;const obj=err;return"QueueDoesNotExist"===obj.name||"AWS.SimpleQueueService.NonExistentQueue"===obj.name}function isFetchedMessage(value){if("object"!=typeof value||null===value)return!1;const obj=value;return"string"==typeof obj.MessageId&&"string"==typeof obj.ReceiptHandle&&"string"==typeof obj.Body}var sqs_default=async context=>{const{config:partialConfig={},env:env,logger:logger,id:id}=context,config=function(partial={},env,logger){const partialSettings=partial.settings??{},queueName="string"==typeof partialSettings.queueName?partialSettings.queueName:"";queueName||logger.throw("Config settings queueName missing");const region=partialSettings.region??"eu-central-1";let client=partialSettings.client;client||(client=new(env?.AWS?.SQSClient??SQSClient)({region:region,...partialSettings.config}));const settings={...partialSettings,client:client,queueName:queueName,region:region,queueUrl:partialSettings.queueUrl,config:partialSettings.config,decoder:partialSettings.decoder??"json",maxMessages:partialSettings.maxMessages??10,waitTimeSeconds:partialSettings.waitTimeSeconds??20,visibilityTimeout:partialSettings.visibilityTimeout,shutdownTimeoutMs:partialSettings.shutdownTimeoutMs??3e4,onPushError:partialSettings.onPushError??"nack"};return{...partial,settings:settings}}(partialConfig,env,logger),settings=config.settings;if(!settings.queueUrl)try{const res=await settings.client.send(new GetQueueUrlCommand({QueueName:settings.queueName}));if(!res.QueueUrl)return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);settings.queueUrl=res.QueueUrl}catch(err){if(isQueueDoesNotExist(err))return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);throw err}if(!settings.queueArn)try{const res=await settings.client.send(new GetQueueAttributesCommand2({QueueUrl:settings.queueUrl,AttributeNames:["QueueArn"]}));settings.queueArn=res.Attributes?.QueueArn}catch(err){logger.debug("SQS GetQueueAttributesCommand failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}let stopped=!1;const inFlight=new Set,loopPromise=(async()=>{for(;!stopped;)try{const messages=(await settings.client.send(new ReceiveMessageCommand({QueueUrl:settings.queueUrl,MaxNumberOfMessages:settings.maxMessages??10,WaitTimeSeconds:settings.waitTimeSeconds??20,VisibilityTimeout:settings.visibilityTimeout,MessageAttributeNames:["All"],AttributeNames:["All"]}))).Messages??[];for(const msg of messages){if(!isFetchedMessage(msg)){logger.warn("SQS received malformed message; skipping",{messageId:"string"==typeof msg.MessageId?msg.MessageId:void 0});continue}const fetched=msg,handler=handleMessage({message:{id:fetched.MessageId,body:fetched.Body,receiptHandle:fetched.ReceiptHandle,ack:async()=>{await settings.client.send(new DeleteMessageCommand({QueueUrl:settings.queueUrl??"",ReceiptHandle:fetched.ReceiptHandle}))},nack:async()=>{}},settings:settings,pushFn:env.push,logger:logger});inFlight.add(handler),handler.finally(()=>{inFlight.delete(handler)})}}catch(err){if(stopped)break;const error=err instanceof Error?err.message:String(err);if(isQueueDoesNotExist(err)){logger.error(`SQS queue "${settings.queueName}" not found at runtime. Run "walkeros setup source.${id}" to create it. Original: ${error}`),stopped=!0;break}logger.error("SQS receive error (will retry)",{error:error}),await new Promise(r=>setTimeout(r,1e3))}})();return{type:"sqs",config:config,setup:setup,push:async content=>{if(!content)return;const result={acked:!1,nacked:!1};return await handleMessage({message:{id:content.MessageId,body:content.Body,receiptHandle:content.ReceiptHandle,ack:async()=>{result.acked=!0},nack:async()=>{result.nacked=!0}},settings:settings,pushFn:env.push,logger:logger}),result},destroy:async()=>{stopped=!0;const timeoutMs=settings.shutdownTimeoutMs??3e4;let timer;try{await Promise.race([(async()=>{await loopPromise,await Promise.all(Array.from(inFlight))})(),new Promise(resolve=>{timer=setTimeout(()=>{logger.warn(`SQS source close timed out after ${timeoutMs}ms; forcing close.`),resolve()},timeoutMs)})])}finally{timer&&clearTimeout(timer),function(client,logger){const candidate=client;if("function"==typeof candidate.destroy)try{candidate.destroy()}catch(err){logger.debug("SQS client destroy failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}}(settings.client,logger)}}}},types_exports2={};export{types_exports as SourceLambda,types_exports2 as SourceSqs,lambda_default as sourceLambda,sqs_default as sourceSqs};//# sourceMappingURL=index.mjs.map
|
|
1
|
+
import{requestToData}from"@walkeros/core";function isAPIGatewayV2(event){return"version"in event&&"2.0"===event.version}function createResponse(statusCode,body,headers={},requestId){const responseHeaders={"Content-Type":"object"==typeof body?"application/json":"text/plain",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:statusCode,headers:responseHeaders,body:"object"==typeof body?JSON.stringify(body):String(body),isBase64Encoded:!1}}var types_exports={},lambda_default=async context=>{const{config:config={},env:env}=context,userSettings=config.settings||{},settings={...userSettings,cors:userSettings.cors??!0,timeout:userSettings.timeout??3e4,enablePixelTracking:userSettings.enablePixelTracking??!0,healthPath:userSettings.healthPath??"/health"};return{type:"lambda",config:{...config,settings:settings},push:async(event,lambdaContext)=>{const requestId=lambdaContext.awsRequestId;let parsed;try{const corsHeaders=function(corsOptions){if(!corsOptions)return{};if(!0===corsOptions)return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Authorization","Access-Control-Max-Age":"3600"};const headers={};if(corsOptions.origin){const origin=Array.isArray(corsOptions.origin)?corsOptions.origin.join(", "):corsOptions.origin;headers["Access-Control-Allow-Origin"]=origin}return corsOptions.methods&&(headers["Access-Control-Allow-Methods"]=corsOptions.methods.join(", ")),corsOptions.headers&&(headers["Access-Control-Allow-Headers"]=corsOptions.headers.join(", ")),corsOptions.credentials&&(headers["Access-Control-Allow-Credentials"]="true"),void 0!==corsOptions.maxAge&&(headers["Access-Control-Max-Age"]=corsOptions.maxAge.toString()),headers}(settings.cors||!1);parsed=function(event){if(isAPIGatewayV2(event)){const headers={};return event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)}),{method:event.requestContext.http.method,body:event.body,queryString:event.rawQueryString||null,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}{const headers={};event.headers&&Object.entries(event.headers).forEach(([key,value])=>{value&&(headers[key.toLowerCase()]=value)});let queryString=null;if(event.queryStringParameters){const params=new URLSearchParams;Object.entries(event.queryStringParameters).forEach(([key,value])=>{value&¶ms.append(key,value)}),queryString=params.toString()||null}return{method:event.httpMethod,body:event.body,queryString:queryString,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}}(event);const path=function(event){return isAPIGatewayV2(event)?event.rawPath:event.path}(event);return settings.healthPath&&path===settings.healthPath?createResponse(200,{status:"ok",timestamp:Date.now(),source:"lambda",requestId:requestId},corsHeaders,requestId):"OPTIONS"===parsed.method?createResponse(204,"",corsHeaders,requestId):await context.withScope(event,void 0,async scopeEnv=>{const envPush=scopeEnv.push;if("GET"===parsed.method){if(!settings.enablePixelTracking)return createResponse(405,{success:!1,error:"GET not allowed",requestId:requestId},corsHeaders,requestId);if(parsed.queryString){const parsedData=requestToData(parsed.queryString);parsedData&&"object"==typeof parsedData&&await envPush(parsedData)}return function(headers={},requestId){const responseHeaders={"Content-Type":"image/gif","Cache-Control":"no-cache, no-store, must-revalidate",...headers};return requestId&&(responseHeaders["X-Request-ID"]=requestId),{statusCode:200,headers:responseHeaders,body:"R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",isBase64Encoded:!0}}(corsHeaders,requestId)}if("POST"===parsed.method){if(!parsed.body)return createResponse(400,{success:!1,error:"Request body is required",requestId:requestId},corsHeaders,requestId);const body=function(body,isBase64Encoded){if(!body||"string"!=typeof body)return body;try{const decoded=isBase64Encoded?Buffer.from(body,"base64").toString("utf8"):body;return JSON.parse(decoded)}catch{return body}}(parsed.body,parsed.isBase64Encoded);if(!body||"object"!=typeof body)return await envPush({}),createResponse(200,{success:!0,requestId:requestId},corsHeaders,requestId);if(function(body){return"object"==typeof body&&null!==body&&"event"in body&&"string"==typeof body.event}(body)){const result=await async function(eventReq,push,logger,requestId){try{const result=await push({name:eventReq.event,data:eventReq.data||{},context:eventReq.context,user:eventReq.user,globals:eventReq.globals,consent:eventReq.consent});return{id:result?.event?.id}}catch(error){return logger?.error("Event processing failed",{error:error,eventName:eventReq.event,requestId:requestId}),{error:error instanceof Error?error.message:"Unknown error"}}}(body,envPush,env.logger,requestId);return result.error?createResponse(400,{success:!1,error:result.error,requestId:requestId},corsHeaders,requestId):createResponse(200,{success:!0,id:result.id,requestId:requestId},corsHeaders,requestId)}return createResponse(400,{success:!1,error:"Invalid request format",requestId:requestId},corsHeaders,requestId)}return createResponse(405,{success:!1,error:"Method not allowed",requestId:requestId},corsHeaders,requestId)})}catch(error){return env.logger?.error("Lambda handler error",{error:error,requestId:requestId,method:parsed?.method}),createResponse(500,{success:!1,error:error instanceof Error?error.message:"Internal server error",requestId:requestId},{},requestId)}}}};import{DeleteMessageCommand,GetQueueAttributesCommand as GetQueueAttributesCommand2,GetQueueUrlCommand,ReceiveMessageCommand}from"@aws-sdk/client-sqs";var DecoderError=class extends Error{constructor(message){super(message),this.name="DecoderError"}};import{SQSClient}from"@aws-sdk/client-sqs";import{CreateQueueCommand,GetQueueAttributesCommand,SQSClient as SQSClient2}from"@aws-sdk/client-sqs";import{SNSClient,SubscribeCommand}from"@aws-sdk/client-sns";import{resolveSetup}from"@walkeros/core";var DEFAULT_SETUP={region:"eu-central-1",fifoQueue:!1,visibilityTimeoutSeconds:30,messageRetentionSeconds:345600,maximumMessageSize:262144};function buildDlqAttributes(args){const attrs={};return attrs.MessageRetentionPeriod=String(1209600),args.kmsMasterKeyId&&(attrs.KmsMasterKeyId=args.kmsMasterKeyId),args.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),attrs}async function ensureQueue(args){const{sqs:sqs,logger:logger,queueName:queueName,attributes:attributes,tags:tags}=args;let queueUrl,queueCreated=!1;try{queueUrl=(await sqs.send(new CreateQueueCommand({QueueName:queueName,Attributes:attributes,tags:tags}))).QueueUrl??"",queueCreated=!0,logger.info(`SQS queue "${queueName}" created.`)}catch(err){if(function(err){return!!function(err){return"object"==typeof err&&null!==err&&"string"==typeof err.name}(err)&&("QueueNameExists"===err.name||"QueueAlreadyExists"===err.name||"AWS.SimpleQueueService.QueueNameExists"===err.name)}(err)){const message=err instanceof Error?err.message:String(err);return logger.throw(`SQS queue "${queueName}" exists with different attributes (${message}). Setup will not mutate an existing queue's attributes. Delete or rename the queue, then re-run setup.`)}throw err}if(!queueUrl)return logger.throw(`SQS CreateQueueCommand returned no QueueUrl for "${queueName}".`);const attrsRes=await sqs.send(new GetQueueAttributesCommand({QueueUrl:queueUrl,AttributeNames:["QueueArn"]})),queueArn=attrsRes.Attributes?.QueueArn??"";return queueArn?{queueCreated:queueCreated,queueUrl:queueUrl,queueArn:queueArn}:logger.throw(`SQS GetQueueAttributesCommand returned no QueueArn for "${queueName}".`)}function dlqName(queueName,fifo){if(fifo){return`${queueName.endsWith(".fifo")?queueName.slice(0,-5):queueName}-dlq.fifo`}return`${queueName}-dlq`}var setup=async context=>{const{id:id,config:config,env:env,logger:logger}=context,merged=resolveSetup(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("SQS source setup skipped (config.setup is falsy).");const options={...DEFAULT_SETUP,...merged},settings=config.settings;if(!settings)return logger.throw("setup: settings missing");const queueName=settings.queueName;if(!queueName)return logger.throw("setup: settings.queueName is missing");const region=options.region??"eu-central-1",sqs=new(env.AWS?.SQSClient??SQSClient2)({region:region});let dlqArn,dlqCreated=!1;if(options.deadLetterQueue?.arn)dlqArn=options.deadLetterQueue.arn;else if(options.deadLetterQueue?.create){const dlqResult=await ensureQueue({sqs:sqs,logger:logger,queueName:dlqName(queueName,options.fifoQueue??!1),attributes:buildDlqAttributes({fifoQueue:options.fifoQueue??!1,kmsMasterKeyId:options.kmsMasterKeyId}),tags:(parentTags=options.tags,{...parentTags??{},walkerOS:"dlq"})});dlqArn=dlqResult.queueArn,dlqCreated=dlqResult.queueCreated}var parentTags;const finalQueueName=options.fifoQueue?queueName.endsWith(".fifo")?queueName:`${queueName}.fifo`:queueName;var args;const mainAttrs=function(args){const{options:options,dlqArn:dlqArn,queuePolicy:queuePolicy}=args,attrs={};if(attrs.VisibilityTimeout=String(options.visibilityTimeoutSeconds??30),attrs.MessageRetentionPeriod=String(options.messageRetentionSeconds??345600),attrs.MaximumMessageSize=String(options.maximumMessageSize??262144),options.kmsMasterKeyId&&(attrs.KmsMasterKeyId=options.kmsMasterKeyId),options.fifoQueue&&(attrs.FifoQueue="true",attrs.ContentBasedDeduplication="true"),dlqArn){const maxReceiveCount=options.deadLetterQueue?.maxReceiveCount??5;attrs.RedrivePolicy=JSON.stringify({deadLetterTargetArn:dlqArn,maxReceiveCount:maxReceiveCount})}return queuePolicy&&(attrs.Policy=queuePolicy),attrs}({options:options,dlqArn:dlqArn,queuePolicy:options.subscribeToSnsTopic?(args={sourceId:id,topicArn:options.subscribeToSnsTopic.topicArn},JSON.stringify({Version:"2012-10-17",Statement:[{Sid:`walkerOSAllowSNSPublish-${args.sourceId}`,Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"SQS:SendMessage",Resource:"*",Condition:{ArnEquals:{"aws:SourceArn":args.topicArn}}}]})):void 0}),mainResult=await ensureQueue({sqs:sqs,logger:logger,queueName:finalQueueName,attributes:mainAttrs,tags:options.tags});let subscriptionArn;if(options.subscribeToSnsTopic){const sns=new(env.AWS?.SNSClient??SNSClient)({region:region}),subAttrs={};options.subscribeToSnsTopic.rawMessageDelivery&&(subAttrs.RawMessageDelivery="true"),options.subscribeToSnsTopic.filterPolicy&&(subAttrs.FilterPolicy=JSON.stringify(options.subscribeToSnsTopic.filterPolicy));subscriptionArn=(await sns.send(new SubscribeCommand({TopicArn:options.subscribeToSnsTopic.topicArn,Protocol:"sqs",Endpoint:mainResult.queueArn,Attributes:Object.keys(subAttrs).length>0?subAttrs:void 0,ReturnSubscriptionArn:!0}))).SubscriptionArn??void 0,logger.info("SQS source setup: SNS subscription created",{topicArn:options.subscribeToSnsTopic.topicArn,subscriptionArn:subscriptionArn})}return{queueCreated:mainResult.queueCreated,queueUrl:mainResult.queueUrl,queueArn:mainResult.queueArn,dlqCreated:dlqArn?dlqCreated:void 0,dlqArn:dlqArn,subscriptionArn:subscriptionArn}};async function handleMessage(args){const{message:message,settings:settings,pushFn:pushFn,logger:logger}=args;try{const decoded=function(messageId,body,decoder){if("raw"===decoder)return Buffer.from(body,"utf8");if("text"===decoder)return body;try{return JSON.parse(body)}catch(err){throw new DecoderError(`SQS message ${messageId} JSON decode failed: ${err instanceof Error?err.message:String(err)}`)}}(message.id,message.body,settings.decoder??"json");if(null==decoded)return logger.debug("SQS message acked-and-dropped (decoder returned null)",{id:message.id}),void await message.ack();const result=await pushFn((value=decoded,"object"==typeof value&&null!==value?value:"string"==typeof value?{data:{payload:value}}:Buffer.isBuffer(value)?{data:{payload:value.toString("base64")}}:{data:{payload:String(value)}}));if(!1===result?.ok)throw new Error("push rejected the message (ok:false)");await message.ack()}catch(err){const error=err instanceof Error?err.message:String(err);err instanceof DecoderError?logger.error("SQS message decode failed",{id:message.id,error:error}):logger.error("SQS message handler failed",{id:message.id,error:error}),"ack"===settings.onPushError?await message.ack():await message.nack()}var value}function isQueueDoesNotExist(err){if("object"!=typeof err||null===err)return!1;if(!("name"in err))return!1;const obj=err;return"QueueDoesNotExist"===obj.name||"AWS.SimpleQueueService.NonExistentQueue"===obj.name}function isFetchedMessage(value){if("object"!=typeof value||null===value)return!1;const obj=value;return"string"==typeof obj.MessageId&&"string"==typeof obj.ReceiptHandle&&"string"==typeof obj.Body}var sqs_default=async context=>{const{config:partialConfig={},env:env,logger:logger,id:id}=context,config=function(partial={},env,logger){const partialSettings=partial.settings??{},queueName="string"==typeof partialSettings.queueName?partialSettings.queueName:"";queueName||logger.throw("Config settings queueName missing");const region=partialSettings.region??"eu-central-1";let client=partialSettings.client;client||(client=new(env?.AWS?.SQSClient??SQSClient)({region:region,...partialSettings.config}));const settings={...partialSettings,client:client,queueName:queueName,region:region,queueUrl:partialSettings.queueUrl,config:partialSettings.config,decoder:partialSettings.decoder??"json",maxMessages:partialSettings.maxMessages??10,waitTimeSeconds:partialSettings.waitTimeSeconds??20,visibilityTimeout:partialSettings.visibilityTimeout,shutdownTimeoutMs:partialSettings.shutdownTimeoutMs??3e4,onPushError:partialSettings.onPushError??"nack"};return{...partial,settings:settings}}(partialConfig,env,logger),settings=config.settings;if(!settings.queueUrl)try{const res=await settings.client.send(new GetQueueUrlCommand({QueueName:settings.queueName}));if(!res.QueueUrl)return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);settings.queueUrl=res.QueueUrl}catch(err){if(isQueueDoesNotExist(err))return logger.throw(`SQS queue not found: ${settings.queueName}. Run "walkeros setup source.${id}" to create it.`);throw err}if(!settings.queueArn)try{const res=await settings.client.send(new GetQueueAttributesCommand2({QueueUrl:settings.queueUrl,AttributeNames:["QueueArn"]}));settings.queueArn=res.Attributes?.QueueArn}catch(err){logger.debug("SQS GetQueueAttributesCommand failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}let stopped=!1;const inFlight=new Set;let loopPromise;const startConsuming=()=>{loopPromise||(loopPromise=(async()=>{for(;!stopped;)try{const messages=(await settings.client.send(new ReceiveMessageCommand({QueueUrl:settings.queueUrl,MaxNumberOfMessages:settings.maxMessages??10,WaitTimeSeconds:settings.waitTimeSeconds??20,VisibilityTimeout:settings.visibilityTimeout,MessageAttributeNames:["All"],AttributeNames:["All"]}))).Messages??[];for(const msg of messages){if(!isFetchedMessage(msg)){logger.warn("SQS received malformed message; skipping",{messageId:"string"==typeof msg.MessageId?msg.MessageId:void 0});continue}const fetched=msg,handler=handleMessage({message:{id:fetched.MessageId,body:fetched.Body,receiptHandle:fetched.ReceiptHandle,ack:async()=>{await settings.client.send(new DeleteMessageCommand({QueueUrl:settings.queueUrl??"",ReceiptHandle:fetched.ReceiptHandle}))},nack:async()=>{}},settings:settings,pushFn:env.push,logger:logger});inFlight.add(handler),handler.finally(()=>{inFlight.delete(handler)})}}catch(err){if(stopped)break;const error=err instanceof Error?err.message:String(err);if(isQueueDoesNotExist(err)){logger.error(`SQS queue "${settings.queueName}" not found at runtime. Run "walkeros setup source.${id}" to create it. Original: ${error}`),stopped=!0;break}logger.error("SQS receive error (will retry)",{error:error}),await new Promise(r=>setTimeout(r,1e3))}})())};return{type:"sqs",config:config,setup:setup,on:type=>{"run"===type&&startConsuming()},push:async content=>{if(!content)return;const result={acked:!1,nacked:!1};return await handleMessage({message:{id:content.MessageId,body:content.Body,receiptHandle:content.ReceiptHandle,ack:async()=>{result.acked=!0},nack:async()=>{result.nacked=!0}},settings:settings,pushFn:env.push,logger:logger}),result},destroy:async()=>{stopped=!0;const timeoutMs=settings.shutdownTimeoutMs??3e4;let timer;try{await Promise.race([(async()=>{await loopPromise,await Promise.all(Array.from(inFlight))})(),new Promise(resolve=>{timer=setTimeout(()=>{logger.warn(`SQS source close timed out after ${timeoutMs}ms; forcing close.`),resolve()},timeoutMs)})])}finally{timer&&clearTimeout(timer),function(client,logger){const candidate=client;if("function"==typeof candidate.destroy)try{candidate.destroy()}catch(err){logger.debug("SQS client destroy failed (non-fatal)",{error:err instanceof Error?err.message:String(err)})}}(settings.client,logger)}}}},types_exports2={};export{types_exports as SourceLambda,types_exports2 as SourceSqs,lambda_default as sourceLambda,sqs_default as sourceSqs};//# sourceMappingURL=index.mjs.map
|