@walkeros/server-source-aws 4.4.0 → 4.5.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/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 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"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lambda/index.ts","../src/lambda/utils.ts","../src/lambda/push.ts","../src/lambda/scope.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, Types } from './types';\nimport type { Source } from '@walkeros/core';\nimport {\n batchResponse,\n isBatchBody,\n requestToData,\n toEventList,\n} from '@walkeros/core';\nimport {\n parseEvent,\n getCorsHeaders,\n createResponse,\n createPixelResponse,\n getPath,\n} from './utils';\nimport { processEvent } from './push';\nimport { buildScope } from './scope';\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 maxBatchSize: userSettings.maxBatchSize ?? 100,\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 const scope = buildScope(event);\n\n return await context.withScope(scope, 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 // Parsed once when the scope was built, so ingest.body and the event\n // the pipeline receives are the same value.\n const events = toEventList(scope.body);\n const batched = isBatchBody(scope.body);\n const maxBatchSize = settings.maxBatchSize ?? 100;\n\n if (batched && events.length > maxBatchSize) {\n return createResponse(\n 400,\n {\n success: false,\n error: `Batch too large. Maximum size: ${maxBatchSize} events`,\n requestId,\n },\n corsHeaders,\n requestId,\n );\n }\n\n const results = [];\n for (const event of events) {\n results.push(\n await processEvent(event, envPush, env.logger, requestId),\n );\n }\n\n if (batched) {\n const { status, body } = batchResponse(results);\n return createResponse(\n status,\n { ...body, requestId },\n corsHeaders,\n requestId,\n );\n }\n\n const result = results[0];\n if (result.error) {\n return createResponse(\n result.status ?? 500,\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 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} 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 // v1 carries repeated headers in a second bag. Without it every value but\n // the last is lost, so the multi-value bag wins where a name is in both.\n if (event.multiValueHeaders) {\n Object.entries(event.multiValueHeaders).forEach(([key, values]) => {\n if (values && values.length)\n headers[key.toLowerCase()] = values.join(', ');\n });\n }\n let queryString: string | null = null;\n const params = new URLSearchParams();\n if (event.queryStringParameters) {\n Object.entries(event.queryStringParameters).forEach(([key, value]) => {\n if (value) params.append(key, value);\n });\n }\n // Same for repeated query keys: the single-value bag keeps only the last.\n if (event.multiValueQueryStringParameters) {\n Object.entries(event.multiValueQueryStringParameters).forEach(\n ([key, values]) => {\n if (!values || !values.length) return;\n params.delete(key);\n values.forEach((value) => params.append(key, value));\n },\n );\n }\n queryString = params.toString() || null;\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 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, Logger } from '@walkeros/core';\nimport { isObject } 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; status?: number }> {\n try {\n // A batch element that is not an object carries no event fields. Treat it\n // as an empty event so the collector's gate classifies it as invalid\n // input, rather than throwing here and reporting a server fault.\n const { event, ...rest } = isObject(eventReq) ? eventReq : {};\n const result = await push({ ...rest, name: rest.name ?? event });\n\n if (result?.ok === false) {\n if (result.invalid === true) {\n return { error: result.error ?? 'Invalid event', status: 400 };\n }\n return {\n error: result.error ?? 'Event was not processed',\n status: 500,\n };\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.name ?? eventReq.event,\n requestId,\n });\n // A rejected push promise is a server fault: the collector resolves\n // pipeline failures, so a rejection is exceptional by construction.\n return {\n error: error instanceof Error ? error.message : 'Unknown error',\n status: 500,\n };\n }\n}\n","import { normalizeBody, normalizeQuery } from '@walkeros/core';\nimport type { Source } from '@walkeros/core';\nimport type { LambdaEvent } from './types';\nimport { getPath, isAPIGatewayV2, parseEvent } from './utils';\n\n/**\n * Builds the normalized scope from a Lambda event.\n *\n * API Gateway v1 and v2 describe the same request with different envelopes.\n * Both converge here, so a mapping written once resolves identically on\n * either. The v1 and v2 specific fields stay reachable through `raw`.\n *\n * @param event The Lambda event.\n * @returns The normalized scope.\n */\nexport function buildScope(event: LambdaEvent): Source.Scope {\n const parsed = parseEvent(event);\n const path = getPath(event);\n const queryString = parsed.queryString || '';\n\n // Lambda events carry no scheme. x-forwarded-proto when the proxy sets it,\n // otherwise https: API Gateway terminates TLS in every real deployment.\n const protocol = parsed.headers['x-forwarded-proto'] || 'https';\n const host = parsed.headers.host;\n const suffix = queryString ? `?${queryString}` : '';\n\n const scope: Source.Scope = {\n method: (parsed.method || '').toUpperCase(),\n url: host ? `${protocol}://${host}${path}${suffix}` : '',\n path,\n query: normalizeQuery(queryString),\n headers: parsed.headers,\n body: normalizeBody(parsed.body, parsed.isBase64Encoded),\n raw: event,\n };\n\n const ip = isAPIGatewayV2(event)\n ? event.requestContext?.http?.sourceIp\n : event.requestContext?.identity?.sourceIp;\n if (ip) scope.ip = ip;\n\n return scope;\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\n// `event` is a temporary alias for `name`, kept for the migration window and\n// removed at the next major. `name` takes precedence when both are present.\nexport type EventRequest = WalkerOS.DeepPartialEvent & {\n event?: string;\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_core","import_core","event","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&&params.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
1
+ import{batchResponse,isBatchBody,requestToData,toEventList}from"@walkeros/core";function isAPIGatewayV2(event){return"version"in event&&"2.0"===event.version}function parseEvent(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)}),event.multiValueHeaders&&Object.entries(event.multiValueHeaders).forEach(([key,values])=>{values&&values.length&&(headers[key.toLowerCase()]=values.join(", "))});let queryString=null;const params=new URLSearchParams;return event.queryStringParameters&&Object.entries(event.queryStringParameters).forEach(([key,value])=>{value&&params.append(key,value)}),event.multiValueQueryStringParameters&&Object.entries(event.multiValueQueryStringParameters).forEach(([key,values])=>{values&&values.length&&(params.delete(key),values.forEach(value=>params.append(key,value)))}),queryString=params.toString()||null,{method:event.httpMethod,body:event.body,queryString:queryString,headers:headers,isBase64Encoded:event.isBase64Encoded||!1}}}function getPath(event){return isAPIGatewayV2(event)?event.rawPath:event.path}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}}import{isObject}from"@walkeros/core";async function processEvent(eventReq,push,logger,requestId){try{const{event:event,...rest}=isObject(eventReq)?eventReq:{},result=await push({...rest,name:rest.name??event});return!1===result?.ok?!0===result.invalid?{error:result.error??"Invalid event",status:400}:{error:result.error??"Event was not processed",status:500}:{id:result?.event?.id}}catch(error){return logger?.error("Event processing failed",{error:error,eventName:eventReq.name??eventReq.event,requestId:requestId}),{error:error instanceof Error?error.message:"Unknown error",status:500}}}import{normalizeBody,normalizeQuery}from"@walkeros/core";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",maxBatchSize:userSettings.maxBatchSize??100};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=parseEvent(event);const path=getPath(event);if(settings.healthPath&&path===settings.healthPath)return createResponse(200,{status:"ok",timestamp:Date.now(),source:"lambda",requestId:requestId},corsHeaders,requestId);if("OPTIONS"===parsed.method)return createResponse(204,"",corsHeaders,requestId);const scope=function(event){const parsed=parseEvent(event),path=getPath(event),queryString=parsed.queryString||"",protocol=parsed.headers["x-forwarded-proto"]||"https",host=parsed.headers.host,suffix=queryString?`?${queryString}`:"",scope={method:(parsed.method||"").toUpperCase(),url:host?`${protocol}://${host}${path}${suffix}`:"",path:path,query:normalizeQuery(queryString),headers:parsed.headers,body:normalizeBody(parsed.body,parsed.isBase64Encoded),raw:event},ip=isAPIGatewayV2(event)?event.requestContext?.http?.sourceIp:event.requestContext?.identity?.sourceIp;return ip&&(scope.ip=ip),scope}(event);return await context.withScope(scope,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 events=toEventList(scope.body),batched=isBatchBody(scope.body),maxBatchSize=settings.maxBatchSize??100;if(batched&&events.length>maxBatchSize)return createResponse(400,{success:!1,error:`Batch too large. Maximum size: ${maxBatchSize} events`,requestId:requestId},corsHeaders,requestId);const results=[];for(const event2 of events)results.push(await processEvent(event2,envPush,env.logger,requestId));if(batched){const{status:status,body:body}=batchResponse(results);return createResponse(status,{...body,requestId:requestId},corsHeaders,requestId)}const result=results[0];return result.error?createResponse(result.status??500,{success:!1,error:result.error,requestId:requestId},corsHeaders,requestId):createResponse(200,{success:!0,id:result.id,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