@geekmidas/constructs 10.0.0-alpha.78 → 10.0.0-alpha.80
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/{Queue-umpX3683.cjs → Queue-A3wfD0it.cjs} +2 -2
- package/dist/{Queue-umpX3683.cjs.map → Queue-A3wfD0it.cjs.map} +1 -1
- package/dist/{Queue-CLRT_j5a.mjs → Queue-D6USGS4q.mjs} +2 -2
- package/dist/{Queue-CLRT_j5a.mjs.map → Queue-D6USGS4q.mjs.map} +1 -1
- package/dist/{QueueBuilder-B9ClmXjX.cjs → QueueBuilder-BjuF0UBl.cjs} +2 -2
- package/dist/{QueueBuilder-B9ClmXjX.cjs.map → QueueBuilder-BjuF0UBl.cjs.map} +1 -1
- package/dist/{QueueBuilder-B0mCqq74.mjs → QueueBuilder-Cue73M4t.mjs} +2 -2
- package/dist/{QueueBuilder-B0mCqq74.mjs.map → QueueBuilder-Cue73M4t.mjs.map} +1 -1
- package/dist/{Topic-DCxIstf9.mjs → Topic-D74b5cgs.mjs} +2 -2
- package/dist/{Topic-DCxIstf9.mjs.map → Topic-D74b5cgs.mjs.map} +1 -1
- package/dist/{Topic-BliAKi5r.cjs → Topic-GHxcU8AG.cjs} +2 -2
- package/dist/{Topic-BliAKi5r.cjs.map → Topic-GHxcU8AG.cjs.map} +1 -1
- package/dist/adaptors/aws.cjs +11 -2
- package/dist/adaptors/aws.cjs.map +1 -1
- package/dist/adaptors/aws.d.cts.map +1 -1
- package/dist/adaptors/aws.d.mts +1 -1
- package/dist/adaptors/aws.d.mts.map +1 -1
- package/dist/adaptors/aws.mjs +11 -2
- package/dist/adaptors/aws.mjs.map +1 -1
- package/dist/adaptors/testing.cjs +3 -3
- package/dist/adaptors/testing.mjs +3 -3
- package/dist/crons/index.cjs +25 -1
- package/dist/crons/index.cjs.map +1 -1
- package/dist/crons/index.d.cts +6 -1
- package/dist/crons/index.d.cts.map +1 -1
- package/dist/crons/index.d.mts +6 -1
- package/dist/crons/index.d.mts.map +1 -1
- package/dist/crons/index.mjs +25 -1
- package/dist/crons/index.mjs.map +1 -1
- package/dist/database/kysely.cjs +1 -1
- package/dist/database/kysely.mjs +1 -1
- package/dist/external-api-BEgXYeJD.d.mts.map +1 -1
- package/dist/external-api-BaIgJ6Ey.d.cts.map +1 -1
- package/dist/external-api.cjs +10 -1
- package/dist/external-api.cjs.map +1 -1
- package/dist/external-api.mjs +10 -1
- package/dist/external-api.mjs.map +1 -1
- package/dist/kysely-BEm5sDlU.d.cts.map +1 -1
- package/dist/kysely-CjuvdXEV.d.mts.map +1 -1
- package/dist/{kysely-BhJ3lLdE.mjs → kysely-DN88k0gD.mjs} +93 -23
- package/dist/kysely-DN88k0gD.mjs.map +1 -0
- package/dist/{kysely-B-xWOGmn.cjs → kysely-H7L1YDgE.cjs} +93 -23
- package/dist/kysely-H7L1YDgE.cjs.map +1 -0
- package/dist/queue/index.cjs +2 -2
- package/dist/queue/index.mjs +2 -2
- package/dist/subscribers/index.cjs +22 -15
- package/dist/subscribers/index.cjs.map +1 -1
- package/dist/subscribers/index.d.cts +1 -0
- package/dist/subscribers/index.d.cts.map +1 -1
- package/dist/subscribers/index.d.mts +1 -0
- package/dist/subscribers/index.d.mts.map +1 -1
- package/dist/subscribers/index.mjs +22 -15
- package/dist/subscribers/index.mjs.map +1 -1
- package/dist/topic/index.cjs +1 -1
- package/dist/topic/index.mjs +1 -1
- package/dist/tracing-3PpqRX07.cjs +98 -0
- package/dist/tracing-3PpqRX07.cjs.map +1 -0
- package/dist/tracing-DbcPuhEy.mjs +87 -0
- package/dist/tracing-DbcPuhEy.mjs.map +1 -0
- package/dist/worker.cjs +1 -1
- package/dist/worker.mjs +1 -1
- package/package.json +33 -31
- package/dist/kysely-B-xWOGmn.cjs.map +0 -1
- package/dist/kysely-BhJ3lLdE.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws.mjs","names":[],"sources":["../../src/functions/FunctionExecutionWrapper.ts","../../src/functions/AWSLambdaFunction.ts","../../src/crons/AWSScheduledFunction.ts","../../src/endpoints/AmazonApiGatewayEndpointAdaptor.ts","../../src/endpoints/parseQueryParams.ts","../../src/endpoints/AmazonApiGatewayV1EndpointAdaptor.ts","../../src/endpoints/AmazonApiGatewayV2EndpointAdaptor.ts","../../src/queue/AWSLambdaQueueAdaptor.ts","../../src/subscribers/AWSLambdaSubscriberAdaptor.ts"],"sourcesContent":["import type { AuditableAction, Auditor, AuditStorage } from '@geekmidas/audit';\nimport { DefaultAuditor } from '@geekmidas/audit';\nimport { withAuditableTransaction } from '@geekmidas/audit/kysely';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tComposableStandardSchema,\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishEvents } from '../publisher';\nimport type { Function, FunctionHandler } from './Function';\nimport { FunctionBuilder } from './FunctionBuilder';\n\nexport abstract class FunctionExecutionWrapper<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> {\n\tconstructor(\n\t\tprotected envParser: EnvironmentParser<{}>,\n\t\tprotected readonly fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tFunctionHandler<\n\t\t\t\tTInput,\n\t\t\t\tTServices,\n\t\t\t\tTLogger,\n\t\t\t\tTOutSchema,\n\t\t\t\tTDatabase,\n\t\t\t\tTAuditStorage,\n\t\t\t\tTAuditAction\n\t\t\t>\n\t\t>,\n\t) {}\n\n\tprotected _logger?: TLogger;\n\n\tget logger(): TLogger {\n\t\treturn this._logger || this.fn.logger;\n\t}\n\n\tget serviceDiscovery(): ServiceDiscovery<ServiceRecord<TServices>> {\n\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\tServiceRecord<TServices>\n\t\t>(this.envParser);\n\n\t\treturn serviceDiscovery;\n\t}\n\n\tgetServices(): Promise<ServiceRecord<TServices>> {\n\t\treturn this.serviceDiscovery.register(this.fn.services);\n\t}\n\n\tasync getDatabase(): Promise<TDatabase | undefined> {\n\t\tif (!this.fn.databaseService) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst services = await this.serviceDiscovery.register([\n\t\t\tthis.fn.databaseService,\n\t\t]);\n\n\t\treturn services[\n\t\t\tthis.fn.databaseService.serviceName as keyof typeof services\n\t\t] as TDatabase;\n\t}\n\n\t/**\n\t * Get the audit storage service if configured.\n\t * Returns undefined if no auditor storage is configured.\n\t */\n\tasync getAuditStorage(): Promise<TAuditStorage | undefined> {\n\t\tif (!this.fn.auditorStorageService) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst services = await this.serviceDiscovery.register([\n\t\t\tthis.fn.auditorStorageService,\n\t\t]);\n\n\t\treturn services[\n\t\t\tthis.fn.auditorStorageService.serviceName as keyof typeof services\n\t\t] as TAuditStorage;\n\t}\n\n\t/**\n\t * Create an auditor instance for the function.\n\t * Returns undefined if no auditor storage is configured.\n\t */\n\tasync createAuditor(): Promise<Auditor<TAuditAction> | undefined> {\n\t\tconst storage = await this.getAuditStorage();\n\t\tif (!storage) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn new DefaultAuditor<TAuditAction>({\n\t\t\tactor: { id: 'system', type: 'system' },\n\t\t\tstorage: storage as AuditStorage,\n\t\t\tmetadata: {\n\t\t\t\tfunction: this.fn.type,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Execute handler with audit transaction support.\n\t * If the audit storage has a database (via getDatabase()), wraps execution\n\t * in a transaction so audits are atomic with handler's database operations.\n\t *\n\t * @param handler - The handler function to execute (receives auditor and db)\n\t * @returns The handler result\n\t */\n\tasync executeWithAudit<T>(\n\t\thandler: (ctx: {\n\t\t\tauditor?: Auditor<TAuditAction>;\n\t\t\tdb?: TDatabase;\n\t\t}) => Promise<T>,\n\t): Promise<T> {\n\t\tconst auditor = await this.createAuditor();\n\t\tconst storage = await this.getAuditStorage();\n\n\t\t// No audit context - just run handler with regular db\n\t\tif (!auditor || !storage) {\n\t\t\tconst db = await this.getDatabase();\n\t\t\treturn handler({ db });\n\t\t}\n\n\t\t// Check if storage has a database and db service names match\n\t\tconst storageDb = (storage as AuditStorage).getDatabase?.();\n\t\tconst databaseServiceName = this.fn.databaseService?.serviceName;\n\t\tconst auditDbServiceName = this.fn.auditorStorageService?.serviceName;\n\n\t\t// If the audit storage has a database and we're using the same database service\n\t\t// (or the audit storage provides the database), use transactional execution\n\t\tif (storageDb && databaseServiceName && auditDbServiceName) {\n\t\t\treturn withAuditableTransaction(\n\t\t\t\tstorageDb as any,\n\t\t\t\tauditor as any,\n\t\t\t\tasync (trx) => {\n\t\t\t\t\t// Use transaction as db\n\t\t\t\t\tconst response = await handler({\n\t\t\t\t\t\tauditor,\n\t\t\t\t\t\tdb: trx as TDatabase,\n\t\t\t\t\t});\n\t\t\t\t\t// Audits are flushed by withAuditableTransaction before commit\n\t\t\t\t\treturn response;\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// No database on storage or service names don't match - run handler and flush audits after\n\t\tconst db = await this.getDatabase();\n\t\tconst response = await handler({ auditor, db });\n\n\t\t// Flush audits (no transaction)\n\t\tawait auditor.flush();\n\n\t\treturn response;\n\t}\n\n\tasync getFunctionInput<TEvent>(\n\t\tevent: TEvent,\n\t): Promise<InferComposableStandardSchema<TInput>> {\n\t\tconst parsedInput = await FunctionBuilder.parseComposableStandardSchema(\n\t\t\tevent,\n\t\t\tthis.fn.input,\n\t\t);\n\n\t\treturn parsedInput as InferComposableStandardSchema<TInput>;\n\t}\n\n\tasync publishEvents(response: InferStandardSchema<TOutSchema>) {\n\t\tawait publishEvents(\n\t\t\tthis.logger,\n\t\t\tthis.serviceDiscovery,\n\t\t\tthis.fn.events,\n\t\t\tresponse,\n\t\t);\n\t}\n\n\tasync parseComposableStandardSchema<T extends ComposableStandardSchema>(\n\t\tdata: unknown,\n\t\tschema: T,\n\t): Promise<InferComposableStandardSchema<T>> {\n\t\treturn FunctionBuilder.parseComposableStandardSchema(data, schema);\n\t}\n}\n","import type { AuditableAction, Auditor, AuditStorage } from '@geekmidas/audit';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport { wrapError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tComposableStandardSchema,\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext } from '@geekmidas/services';\nimport middy, { type MiddlewareObj } from '@middy/core';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Context, Handler } from 'aws-lambda';\nimport type { Function, FunctionHandler } from './Function';\nimport { FunctionBuilder } from './FunctionBuilder';\nimport { FunctionExecutionWrapper } from './FunctionExecutionWrapper';\n\nexport type AWSLambdaHandler<TEvent = any, TResult = any> = Handler<\n\tTEvent,\n\tTResult\n>;\n\ntype FunctionEvent<\n\tTEvent,\n\tTInput extends ComposableStandardSchema | undefined,\n\tTServices extends Service[],\n\tTLogger extends Logger,\n\tTDatabase = undefined,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> = TEvent & {\n\tparsedInput: InferComposableStandardSchema<TInput>;\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n\tdb: TDatabase | undefined;\n\tauditor: Auditor<TAuditAction> | undefined;\n};\n\ntype Middleware<\n\tTEvent,\n\tTInput extends ComposableStandardSchema | undefined,\n\tTServices extends Service[],\n\tTLogger extends Logger,\n\tTOutSchema extends StandardSchemaV1 | undefined,\n\tTDatabase = undefined,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> = MiddlewareObj<\n\tFunctionEvent<TEvent, TInput, TServices, TLogger, TDatabase, TAuditAction>,\n\tInferComposableStandardSchema<TOutSchema>,\n\tError,\n\tContext\n>;\n\nexport class AWSLambdaFunction<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> extends FunctionExecutionWrapper<\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tTAuditStorage,\n\tTAuditStorageServiceName,\n\tTDatabase,\n\tTDatabaseServiceName,\n\tTAuditAction\n> {\n\tconstructor(\n\t\tenvParser: EnvironmentParser<{}>,\n\t\toverride readonly fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tFunctionHandler<\n\t\t\t\tTInput,\n\t\t\t\tTServices,\n\t\t\t\tTLogger,\n\t\t\t\tTOutSchema,\n\t\t\t\tTDatabase,\n\t\t\t\tTAuditStorage,\n\t\t\t\tTAuditAction\n\t\t\t>\n\t\t>,\n\t) {\n\t\tsuper(envParser, fn);\n\t}\n\n\tprivate error<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tonError: (req) => {\n\t\t\t\tconst logger = req.event?.logger || this.fn.logger;\n\t\t\t\tlogger.error(req.error || {}, 'Error processing function');\n\n\t\t\t\t// Re-throw the wrapped error to let Lambda handle it\n\t\t\t\tthrow wrapError(req.error);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate baseInput<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: (_req) => {},\n\t\t};\n\t}\n\n\tprivate input<\n\t\tTEvent extends { input: InferComposableStandardSchema<TInput> },\n\t>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\ttry {\n\t\t\t\t\t// Parse input if schema is provided\n\t\t\t\t\tif (this.fn.input) {\n\t\t\t\t\t\tconst parsedInput =\n\t\t\t\t\t\t\tawait FunctionBuilder.parseComposableStandardSchema(\n\t\t\t\t\t\t\t\treq.event,\n\t\t\t\t\t\t\t\tthis.fn.input,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\treq.event.parsedInput =\n\t\t\t\t\t\t\tparsedInput as InferComposableStandardSchema<TInput>;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If no schema, pass the event as-is\n\t\t\t\t\t\treq.event.parsedInput = req.event as any;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t{ err: error, event: req.event },\n\t\t\t\t\t\t'Failed to parse input',\n\t\t\t\t\t);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate loggerMiddleware<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: (req) => {\n\t\t\t\tthis._logger = this.fn.logger.child({\n\t\t\t\t\tfn: {\n\t\t\t\t\t\tname: req.context.functionName,\n\t\t\t\t\t\tversion: req.context.functionVersion,\n\t\t\t\t\t\tmemory: req.context.memoryLimitInMB,\n\t\t\t\t\t},\n\t\t\t\t\treq: {\n\t\t\t\t\t\tid: req.context.awsRequestId,\n\t\t\t\t\t},\n\t\t\t\t}) as TLogger;\n\n\t\t\t\treq.event.logger = this._logger;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate services<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\treq.event.services = await this.getServices();\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate database<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\treq.event.db = await this.getDatabase();\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate auditor<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\treq.event.auditor = await this.createAuditor();\n\t\t\t},\n\t\t\tafter: async (req) => {\n\t\t\t\t// Flush any pending audits after successful execution\n\t\t\t\tif (req.event.auditor) {\n\t\t\t\t\tconst records = req.event.auditor.getRecords();\n\t\t\t\t\tif (records.length > 0) {\n\t\t\t\t\t\tthis.logger.debug(\n\t\t\t\t\t\t\t{ auditCount: records.length },\n\t\t\t\t\t\t\t'Flushing function audits',\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait req.event.auditor.flush();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate events<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tafter: async (req) => {\n\t\t\t\tconst response = (req.response ||\n\t\t\t\t\tundefined) as InferStandardSchema<TOutSchema>;\n\t\t\t\tawait this.publishEvents(response);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async _handler<TEvent>(\n\t\tevent: FunctionEvent<\n\t\t\tTEvent,\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTAuditAction\n\t\t>,\n\t) {\n\t\t// Execute the function with the parsed context\n\t\tconst result = await this.fn.fn({\n\t\t\tinput: event.parsedInput,\n\t\t\tservices: event.services,\n\t\t\tlogger: event.logger,\n\t\t\tdb: event.db,\n\t\t\tauditor: event.auditor,\n\t\t} as any);\n\n\t\t// Parse output if schema is provided\n\t\tconst output = await this.fn.parseOutput(result);\n\n\t\treturn output;\n\t}\n\n\tget handler(): AWSLambdaHandler {\n\t\tconst handler = this._handler.bind(this);\n\n\t\t// Apply middleware in order\n\t\tconst chain = middy(handler)\n\t\t\t.use(this.loggerMiddleware())\n\t\t\t.use(this.baseInput())\n\t\t\t.use(this.error())\n\t\t\t.use(this.services())\n\t\t\t.use(this.database())\n\t\t\t.use(this.auditor())\n\t\t\t.use(this.input())\n\t\t\t.use(this.events());\n\n\t\t// Wrap entire Middy chain in request context for service access\n\t\tconst wrappedHandler = async (event: unknown, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.fn.logger.child({ requestId }) as TLogger;\n\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation: context.functionName },\n\t\t\t\t() => chain(event as Parameters<typeof chain>[0], context),\n\t\t\t);\n\t\t};\n\n\t\treturn wrappedHandler as unknown as AWSLambdaHandler;\n\t}\n}\n","import type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { AWSLambdaFunction } from '../functions/AWSLambdaFunction';\nimport type { Cron } from './Cron';\n\n/**\n * AWS Lambda adaptor for {@link Cron} constructs.\n *\n * A `Cron` is a {@link import('../functions').Function} that is invoked on a\n * schedule (an EventBridge rule) rather than by an API request. Because of\n * that, the runtime execution pipeline is identical to a regular Lambda\n * function: input parsing, service/database/auditor wiring, event publishing,\n * error handling, and the `runWithRequestContext` wrapper that powers\n * request-scoped logging via `serviceContext.getLogger()`.\n *\n * This adaptor therefore reuses {@link AWSLambdaFunction} wholesale and only\n * narrows the constructor to accept a `Cron`. The schedule expression itself\n * (`cron.schedule`) is **deploy-time** infrastructure consumed by the CLI to\n * provision the EventBridge rule — it is not needed at runtime.\n *\n * @example\n * ```typescript\n * import { AWSScheduledFunction } from '@geekmidas/constructs/crons';\n * import { cancelPastBookings } from '../../../src/crons/cancelPastBookings.js';\n * import { envParser } from '../../../src/env';\n *\n * const adapter = new AWSScheduledFunction(envParser, cancelPastBookings);\n *\n * export const handler = adapter.handler;\n * ```\n */\nexport class AWSScheduledFunction<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends AWSLambdaFunction<\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tundefined,\n\tstring,\n\tTDatabase,\n\tTDatabaseServiceName\n> {\n\t// biome-ignore lint/complexity/noUselessConstructor: narrows the inherited `Function` parameter to `Cron` for a clearer public API\n\tconstructor(\n\t\tenvParser: EnvironmentParser<{}>,\n\t\tcron: Cron<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {\n\t\tsuper(envParser, cron);\n\t}\n}\n","import type { AuditableAction, AuditStorage } from '@geekmidas/audit';\nimport { withRlsContext } from '@geekmidas/db/rls';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport middy, { type MiddlewareObj } from '@middy/core';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type {\n\tAPIGatewayProxyEvent,\n\tAPIGatewayProxyEventV2,\n\tContext,\n} from 'aws-lambda';\nimport set from 'lodash.set';\nimport type { Telemetry } from '../telemetry';\nimport type { HttpMethod } from '../types';\nimport { Endpoint, type EndpointSchemas, ResponseBuilder } from './Endpoint';\nimport { envParserFor } from './surfaceEnv';\n\n/**\n * Telescope integration for request recording.\n * Uses Middy middleware pattern for compatibility with existing telescope package.\n */\nexport interface TelescopeIntegration {\n\tmiddleware: MiddlewareObj<any, any, Error, Context>;\n}\n\nimport {\n\tForbiddenError,\n\tUnprocessableEntityError,\n\twrapError,\n} from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type {\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport { publishConstructEvents } from '../publisher';\nimport type { MappedAudit } from './audit';\nimport type { CookieFn, HeaderFn } from './Endpoint';\nimport {\n\tcreateAuditContext,\n\texecuteWithAuditTransaction,\n} from './processAudits';\n\n// Helper function to publish events\n\n/**\n * Options for Amazon API Gateway endpoint adaptors\n */\nexport interface AmazonApiGatewayEndpointOptions {\n\t/**\n\t * Telescope integration for request recording and monitoring.\n\t *\n\t * @example\n\t * ```typescript\n\t * import { telescopeMiddleware } from '@geekmidas/telescope/lambda';\n\t *\n\t * const adaptor = new AmazonApiGatewayV2Endpoint(endpoint, {\n\t * telescope: { middleware: telescopeMiddleware(telescope) },\n\t * });\n\t * ```\n\t */\n\ttelescope?: TelescopeIntegration;\n\n\t/**\n\t * Telemetry integration for distributed tracing.\n\t * Works with any OpenTelemetry-compatible backend.\n\t *\n\t * @example\n\t * ```typescript\n\t * import { OTelTelemetry } from '@geekmidas/telescope/instrumentation';\n\t *\n\t * const adaptor = new AmazonApiGatewayV2Endpoint(endpoint, {\n\t * telemetry: new OTelTelemetry(),\n\t * });\n\t * ```\n\t */\n\ttelemetry?: Telemetry;\n}\n\nexport abstract class AmazonApiGatewayEndpoint<\n\tTHandler extends\n\t\t| AmazonApiGatewayV1EndpointHandler\n\t\t| AmazonApiGatewayV2EndpointHandler,\n\tTEvent extends HandlerEvent<THandler>,\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> {\n\tprotected options: AmazonApiGatewayEndpointOptions;\n\n\t/**\n\t * The parser this adaptor discovers services with.\n\t *\n\t * Taken from the endpoint's surface rather than handed in. An endpoint built\n\t * with `api.get()` carries its API, and the API carries the parser — so a\n\t * generated handler is `new AmazonApiGatewayV2Endpoint(endpoint)` with no\n\t * import written for it, which is the whole reason the surface is the\n\t * factory.\n\t */\n\tprotected envParser: EnvironmentParser<{}>;\n\n\tconstructor(\n\t\tprotected readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction\n\t\t>,\n\t\toptions: AmazonApiGatewayEndpointOptions = {},\n\t) {\n\t\tthis.options = options;\n\t\tthis.envParser = envParserFor(endpoint.surface);\n\t}\n\n\tprivate error(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tonError: (req) => {\n\t\t\t\t(req.event.logger || this.endpoint.logger).error(\n\t\t\t\t\treq.error || {},\n\t\t\t\t\t'Error processing request',\n\t\t\t\t);\n\t\t\t\tconst wrappedError = wrapError(req.error);\n\n\t\t\t\t// Set the response with the proper status code from the HttpError\n\t\t\t\treq.response = {\n\t\t\t\t\tstatusCode: wrappedError.statusCode,\n\t\t\t\t\tbody: wrappedError.body,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t}\n\tabstract getInput(e: TEvent): GetInputResponse;\n\n\t/**\n\t * Decodes the raw body from an API Gateway event.\n\t * Uses Content-Type to decide parsing strategy (like Express/Fastify/Hono):\n\t * - `application/json` (or missing) → JSON.parse the decoded string\n\t * - Anything else → return decoded string as-is, let the schema handle it\n\t *\n\t * Defaults to JSON when no Content-Type is provided for backwards compatibility.\n\t */\n\tstatic decodeBody(\n\t\tbody: string | undefined | null,\n\t\tisBase64Encoded: boolean | undefined,\n\t\tcontentType: string | undefined,\n\t): any {\n\t\tif (!body) return undefined;\n\n\t\tconst raw = isBase64Encoded\n\t\t\t? Buffer.from(body, 'base64').toString('utf-8')\n\t\t\t: body;\n\n\t\tconst isJson =\n\t\t\t!contentType || contentType.toLowerCase().includes('application/json');\n\t\tif (isJson) {\n\t\t\treturn JSON.parse(raw);\n\t\t}\n\n\t\treturn raw;\n\t}\n\n\tprotected getCookies(e: TEvent): CookieFn {\n\t\tconst headers = e.headers as Record<string, string>;\n\t\treturn Endpoint.createCookies(headers?.cookie);\n\t}\n\n\tprivate input(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst { body, query, params } = this.getInput(req.event);\n\t\t\t\t\tconst headers = req.event.headers as Record<string, string>;\n\t\t\t\t\tconst header = Endpoint.createHeaders(headers);\n\t\t\t\t\tconst cookie = this.getCookies(req.event);\n\n\t\t\t\t\tset(req.event, 'body', await this.endpoint.parseInput(body, 'body'));\n\n\t\t\t\t\tset(\n\t\t\t\t\t\treq.event,\n\t\t\t\t\t\t'query',\n\t\t\t\t\t\tawait this.endpoint.parseInput(query, 'query'),\n\t\t\t\t\t);\n\t\t\t\t\tset(\n\t\t\t\t\t\treq.event,\n\t\t\t\t\t\t'params',\n\t\t\t\t\t\tawait this.endpoint.parseInput(params, 'params'),\n\t\t\t\t\t);\n\t\t\t\t\tset(req.event, 'header', header);\n\t\t\t\t\tset(req.event, 'cookie', cookie);\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Convert validation errors to 422 Unprocessable Entity\n\t\t\t\t\tif (error && typeof error === 'object' && Array.isArray(error)) {\n\t\t\t\t\t\tthrow new UnprocessableEntityError('Validation failed', error);\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tabstract getLoggerContext(data: TEvent, context: Context): LoggerContext;\n\n\tprivate logger(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: (req) => {\n\t\t\t\treq.event.logger = this.endpoint.logger.child({\n\t\t\t\t\troute: this.endpoint.route,\n\t\t\t\t\thost: req.event.headers?.host,\n\t\t\t\t\tmethod: this.endpoint.method,\n\t\t\t\t\t...this.getLoggerContext(req.event, req.context),\n\t\t\t\t}) as TLogger;\n\t\t\t},\n\t\t};\n\t}\n\tprivate services(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst _logger = req.event.logger as TLogger;\n\t\t\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\t\t\tServiceRecord<TServices>\n\t\t\t\t>(this.envParser);\n\n\t\t\t\tconst services = await serviceDiscovery.register(\n\t\t\t\t\tthis.endpoint.services,\n\t\t\t\t);\n\n\t\t\t\treq.event.services = services;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate authorize(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst logger = req.event.logger as TLogger;\n\t\t\t\tconst services = req.event.services;\n\t\t\t\tconst header = req.event.header;\n\t\t\t\tconst cookie = req.event.cookie;\n\t\t\t\tconst session = req.event.session as TSession;\n\n\t\t\t\tconst isAuthorized = await this.endpoint.authorize({\n\t\t\t\t\theader,\n\t\t\t\t\tcookie,\n\t\t\t\t\tservices,\n\t\t\t\t\tlogger,\n\t\t\t\t\tsession,\n\t\t\t\t\t...((req.event as any).db !== undefined && {\n\t\t\t\t\t\tdb: (req.event as any).db,\n\t\t\t\t\t}),\n\t\t\t\t\tbody: (req.event as any).body,\n\t\t\t\t\tquery: (req.event as any).query,\n\t\t\t\t\tparams: (req.event as any).params,\n\t\t\t\t} as any);\n\n\t\t\t\tif (!isAuthorized) {\n\t\t\t\t\tlogger.warn('Forbidden access attempt');\n\t\t\t\t\tthrow new ForbiddenError(\n\t\t\t\t\t\t'Forbidden access to the endpoint',\n\t\t\t\t\t\t'You do not have permission to access this resource.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate database(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tif (!this.endpoint.databaseService) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst _logger = req.event.logger as TLogger;\n\t\t\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\t\t\tServiceRecord<TServices>\n\t\t\t\t>(this.envParser);\n\n\t\t\t\tconst db = await serviceDiscovery\n\t\t\t\t\t.register([this.endpoint.databaseService])\n\t\t\t\t\t.then(\n\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\ts[this.endpoint.databaseService?.serviceName as keyof typeof s],\n\t\t\t\t\t);\n\n\t\t\t\t(req.event as any).db = db;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate session(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst logger = req.event.logger as TLogger;\n\t\t\t\tconst services = req.event.services;\n\t\t\t\tconst db = (req.event as any).db;\n\t\t\t\treq.event.session = (await this.endpoint.getSession({\n\t\t\t\t\tlogger,\n\t\t\t\t\tservices,\n\t\t\t\t\theader: req.event.header,\n\t\t\t\t\tcookie: req.event.cookie,\n\t\t\t\t\t...(db !== undefined && { db }),\n\t\t\t\t} as any)) as TSession;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate events(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tafter: async (req) => {\n\t\t\t\tconst event = req.event;\n\t\t\t\tconst response = (event as any)\n\t\t\t\t\t.__response as InferStandardSchema<TOutSchema>;\n\t\t\t\tconst statusCode = req.response?.statusCode ?? this.endpoint.status;\n\n\t\t\t\t// Only publish events on successful responses (2xx status codes)\n\t\t\t\t// Note: Audits are processed inside the handler's transaction\n\t\t\t\tif (Endpoint.isSuccessStatus(statusCode)) {\n\t\t\t\t\tconst logger = event.logger as TLogger;\n\t\t\t\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\t\t\t\tServiceRecord<TServices>\n\t\t\t\t\t>(this.envParser);\n\n\t\t\t\t\t// Publish events\n\t\t\t\t\tawait publishConstructEvents(\n\t\t\t\t\t\tthis.endpoint,\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async _handler(\n\t\tevent: Event<TEvent, TInput, TServices, TLogger, TSession>,\n\t) {\n\t\tconst input = this.endpoint.refineInput(event);\n\t\tconst logger = event.logger as TLogger;\n\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\tServiceRecord<TServices>\n\t\t>(this.envParser);\n\n\t\t// Create audit context if audit storage is configured\n\t\tconst auditContext = await createAuditContext(\n\t\t\tthis.endpoint,\n\t\t\tserviceDiscovery,\n\t\t\tlogger,\n\t\t\t{\n\t\t\t\tsession: event.session,\n\t\t\t\theader: event.header,\n\t\t\t\tcookie: event.cookie,\n\t\t\t\tservices: event.services as Record<string, unknown>,\n\t\t\t},\n\t\t);\n\n\t\t// Warn if declarative audits are configured but no audit storage\n\t\tconst audits = this.endpoint.audits as MappedAudit<\n\t\t\tTAuditAction,\n\t\t\tTOutSchema\n\t\t>[];\n\t\tif (!auditContext && audits?.length) {\n\t\t\tlogger.warn('No auditor storage service available');\n\t\t}\n\n\t\t// Get pre-resolved database from middleware\n\t\tconst rawDb = (event as any).db;\n\n\t\t// Extract RLS context if configured and not bypassed\n\t\tconst rlsActive =\n\t\t\tthis.endpoint.rlsConfig &&\n\t\t\t!this.endpoint.rlsBypass &&\n\t\t\trawDb !== undefined;\n\t\tconst rlsContext = rlsActive\n\t\t\t? await this.endpoint.rlsConfig?.extractor({\n\t\t\t\t\tservices: event.services as ServiceRecord<TServices>,\n\t\t\t\t\tsession: event.session,\n\t\t\t\t\theader: event.header,\n\t\t\t\t\tcookie: event.cookie,\n\t\t\t\t\tlogger,\n\t\t\t\t})\n\t\t\t: undefined;\n\n\t\t// Execute handler with automatic audit transaction support\n\t\tconst result = await executeWithAuditTransaction(\n\t\t\tauditContext,\n\t\t\tasync (auditor) => {\n\t\t\t\t// Use audit transaction as db only if the storage uses the same database service\n\t\t\t\tconst sameDatabase =\n\t\t\t\t\tauditContext?.storage?.databaseServiceName &&\n\t\t\t\t\tauditContext.storage.databaseServiceName ===\n\t\t\t\t\t\tthis.endpoint.databaseService?.serviceName;\n\t\t\t\tconst baseDb = sameDatabase\n\t\t\t\t\t? (auditor?.getTransaction?.() ?? rawDb)\n\t\t\t\t\t: rawDb;\n\n\t\t\t\t// Helper to execute handler with given db\n\t\t\t\tconst executeHandler = async (db: any) => {\n\t\t\t\t\tconst responseBuilder = new ResponseBuilder();\n\t\t\t\t\tconst response = await this.endpoint.handler(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\theader: event.header,\n\t\t\t\t\t\t\tcookie: event.cookie,\n\t\t\t\t\t\t\tlogger: event.logger,\n\t\t\t\t\t\t\tservices: event.services,\n\t\t\t\t\t\t\tsession: event.session,\n\t\t\t\t\t\t\tauditor,\n\t\t\t\t\t\t\tdb,\n\t\t\t\t\t\t\t...input,\n\t\t\t\t\t\t} as any,\n\t\t\t\t\t\tresponseBuilder,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Check if response has metadata\n\t\t\t\t\tlet data = response;\n\t\t\t\t\tlet metadata = responseBuilder.getMetadata();\n\n\t\t\t\t\tif (Endpoint.hasMetadata(response)) {\n\t\t\t\t\t\tdata = response.data;\n\t\t\t\t\t\tmetadata = response.metadata;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst output = this.endpoint.outputSchema\n\t\t\t\t\t\t? await this.endpoint.parseOutput(data)\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\treturn { output, metadata, responseBuilder };\n\t\t\t\t};\n\n\t\t\t\t// If RLS is active, wrap handler with RLS context\n\t\t\t\tif (rlsActive && rlsContext && baseDb) {\n\t\t\t\t\treturn withRlsContext(\n\t\t\t\t\t\tbaseDb,\n\t\t\t\t\t\trlsContext,\n\t\t\t\t\t\tasync (trx: any) => executeHandler(trx),\n\t\t\t\t\t\t{ prefix: this.endpoint.rlsConfig?.prefix },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn executeHandler(baseDb);\n\t\t\t},\n\t\t\t// Process declarative audits after handler (inside transaction)\n\t\t\tasync (result, auditor) => {\n\t\t\t\tif (!audits?.length) return;\n\n\t\t\t\tfor (const audit of audits) {\n\t\t\t\t\tif (audit.when && !audit.when(result.output as any)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst payload = audit.payload(result.output as any);\n\t\t\t\t\tconst entityId = audit.entityId?.(result.output as any);\n\t\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\t\ttable: audit.table,\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Pass rawDb so storage can reuse existing transactions\n\t\t\t{ db: rawDb },\n\t\t);\n\n\t\tconst { output, metadata } = result;\n\t\tconst isJsonResponse = this.endpoint.responseType === 'application/json';\n\t\tconst body =\n\t\t\toutput === undefined\n\t\t\t\t? undefined\n\t\t\t\t: isJsonResponse\n\t\t\t\t\t? JSON.stringify(output)\n\t\t\t\t\t: typeof output === 'string'\n\t\t\t\t\t\t? output\n\t\t\t\t\t\t: String(output);\n\n\t\t// Store response for middleware access\n\t\t(event as any).__response = output;\n\n\t\t// Build response with metadata\n\t\tconst lambdaResponse: AmazonApiGatewayEndpointHandlerResponse = {\n\t\t\tstatusCode: metadata.status ?? this.endpoint.status,\n\t\t\tbody,\n\t\t};\n\n\t\t// Compose headers. For non-JSON responseType, emit a default\n\t\t// Content-Type header which metadata.headers (r.header()) can override.\n\t\tconst baseHeaders: Record<string, string> | undefined = isJsonResponse\n\t\t\t? undefined\n\t\t\t: { 'content-type': this.endpoint.responseType };\n\n\t\tif (\n\t\t\tbaseHeaders ||\n\t\t\t(metadata.headers && Object.keys(metadata.headers).length > 0)\n\t\t) {\n\t\t\tlambdaResponse.headers = {\n\t\t\t\t...(baseHeaders ?? {}),\n\t\t\t\t...(metadata.headers ?? {}),\n\t\t\t};\n\t\t}\n\n\t\t// Format cookies as Set-Cookie headers\n\t\tif (metadata.cookies && metadata.cookies.size > 0) {\n\t\t\tconst setCookieHeaders: string[] = [];\n\t\t\tfor (const [name, { value, options }] of metadata.cookies) {\n\t\t\t\tsetCookieHeaders.push(\n\t\t\t\t\tEndpoint.formatCookieHeader(name, value, options),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (setCookieHeaders.length > 0) {\n\t\t\t\tlambdaResponse.multiValueHeaders = {\n\t\t\t\t\t...lambdaResponse.multiValueHeaders,\n\t\t\t\t\t'Set-Cookie': setCookieHeaders,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn lambdaResponse;\n\t}\n\n\t/**\n\t * Convert Telemetry interface to Middy middleware\n\t */\n\tprivate telemetry(): Middleware<TEvent, TInput, TServices, TLogger> | null {\n\t\tif (!this.options.telemetry) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst telemetry = this.options.telemetry;\n\t\tlet ctx: any;\n\n\t\treturn {\n\t\t\tbefore: (request) => {\n\t\t\t\tctx = telemetry.onRequestStart({\n\t\t\t\t\tevent: request.event,\n\t\t\t\t\tcontext: request.context,\n\t\t\t\t});\n\t\t\t},\n\t\t\tafter: (request) => {\n\t\t\t\tif (ctx) {\n\t\t\t\t\ttelemetry.onRequestEnd(ctx, {\n\t\t\t\t\t\tstatusCode: request.response?.statusCode ?? 200,\n\t\t\t\t\t\tbody: request.response?.body,\n\t\t\t\t\t\theaders: request.response?.headers,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\tonError: (request) => {\n\t\t\t\tif (ctx && request.error) {\n\t\t\t\t\ttelemetry.onRequestError(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\trequest.error instanceof Error\n\t\t\t\t\t\t\t? request.error\n\t\t\t\t\t\t\t: new Error(String(request.error)),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tget handler() {\n\t\tconst handler = this._handler.bind(this);\n\t\tlet chain = middy(handler)\n\t\t\t.use(this.logger())\n\t\t\t.use(this.error())\n\t\t\t.use(this.services())\n\t\t\t.use(this.input())\n\t\t\t.use(this.database())\n\t\t\t.use(this.session())\n\t\t\t.use(this.authorize())\n\t\t\t.use(this.events());\n\n\t\t// Add telemetry middleware if configured (runs early for span creation)\n\t\tconst telemetryMiddleware = this.telemetry();\n\t\tif (telemetryMiddleware) {\n\t\t\tchain = chain.use(telemetryMiddleware);\n\t\t}\n\n\t\t// Add Telescope middleware if configured (runs first/last in chain)\n\t\tif (this.options.telescope?.middleware) {\n\t\t\tchain = chain.use(this.options.telescope.middleware);\n\t\t}\n\n\t\t// Wrap entire Middy chain in request context for service access\n\t\tconst wrappedHandler = async (event: TEvent, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.endpoint.logger.child({ requestId }) as TLogger;\n\n\t\t\t// Cast event to any since Middy middlewares enrich the event with\n\t\t\t// services, logger, session, etc. during the middleware chain execution\n\t\t\tconst operation = `${this.endpoint.method} ${this.endpoint.route}`;\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation },\n\t\t\t\t() => chain(event as unknown as Parameters<typeof chain>[0], context),\n\t\t\t);\n\t\t};\n\n\t\treturn wrappedHandler as unknown as THandler;\n\t}\n}\n\nexport type Event<\n\tTEvent extends APIGatewayProxyEvent | APIGatewayProxyEventV2,\n\tTInput extends EndpointSchemas = {},\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> = {\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n\theader: HeaderFn;\n\tcookie: CookieFn;\n\tsession: TSession;\n} & TEvent &\n\tInferComposableStandardSchema<TInput>;\n\ntype Middleware<\n\tTEvent extends APIGatewayProxyEvent | APIGatewayProxyEventV2,\n\tTInput extends EndpointSchemas = {},\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> = MiddlewareObj<Event<TEvent, TInput, TServices, TLogger, TSession>>;\n\nexport type AmazonApiGatewayEndpointHandlerResponse = {\n\tstatusCode: number;\n\tbody: string | undefined;\n\theaders?: Record<string, string>;\n\tmultiValueHeaders?: Record<string, string[]>;\n};\n\nexport type LoggerContext = {\n\tfn: {\n\t\tname: string;\n\t\tversion: string;\n\t};\n\treq: {\n\t\tid: string | undefined;\n\t\tawsRequestId: string;\n\t\tpath: string;\n\t\tip: string | undefined;\n\t\tuserAgent: string | undefined;\n\t};\n};\n\nexport type GetInputResponse = {\n\tbody: any;\n\tquery: any;\n\tparams: any;\n};\n\nexport type AmazonApiGatewayV1EndpointHandler = (\n\tevent: APIGatewayProxyEvent,\n\tcontext: Context,\n) => Promise<AmazonApiGatewayEndpointHandlerResponse>;\n\nexport type AmazonApiGatewayV2EndpointHandler = (\n\tevent: APIGatewayProxyEventV2,\n\tcontext: Context,\n) => Promise<AmazonApiGatewayEndpointHandlerResponse>;\n\nexport type HandlerEvent<T extends Function> = T extends (\n\tevent: infer E,\n\tcontext: Context,\n) => any\n\t? E\n\t: never;\n","import qs from 'qs';\n\n/**\n * Parse query parameters from a flat object into a nested structure\n * Uses qs bracket notation for standard interoperability\n *\n * @example\n * parseQueryParams({ 'tags[]': ['a', 'b'], 'filter[name]': 'john' })\n * // Returns: { tags: ['a', 'b'], filter: { name: 'john' } }\n */\nexport function parseQueryParams(\n\tqueryParams: Record<string, string | string[] | undefined> | null,\n): Record<string, any> {\n\tif (!queryParams) {\n\t\treturn {};\n\t}\n\n\t// Reconstruct a query string from the flat object so qs can parse bracket notation\n\tconst parts: string[] = [];\n\tfor (const [key, value] of Object.entries(queryParams)) {\n\t\tif (value === undefined) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) {\n\t\t\t\tparts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);\n\t\t\t}\n\t\t} else {\n\t\t\tparts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n\t\t}\n\t}\n\n\treturn qs.parse(parts.join('&')) as Record<string, any>;\n}\n","import type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { APIGatewayProxyEvent, Context } from 'aws-lambda';\nimport type { HttpMethod } from '../types';\nimport {\n\tAmazonApiGatewayEndpoint,\n\ttype AmazonApiGatewayEndpointOptions,\n\ttype AmazonApiGatewayV1EndpointHandler,\n\ttype GetInputResponse,\n\ttype LoggerContext,\n} from './AmazonApiGatewayEndpointAdaptor';\nimport type { Endpoint, EndpointSchemas } from './Endpoint';\nimport { parseQueryParams } from './parseQueryParams';\n\nexport class AmazonApiGatewayV1Endpoint<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> extends AmazonApiGatewayEndpoint<\n\tAmazonApiGatewayV1EndpointHandler,\n\tAPIGatewayProxyEvent,\n\tTRoute,\n\tTMethod,\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tTSession\n> {\n\toverride getInput(e: APIGatewayProxyEvent): GetInputResponse {\n\t\t// For arrays, AWS API Gateway V1 provides multiValueQueryStringParameters\n\t\tconst multiValueParams = e.multiValueQueryStringParameters || {};\n\t\tconst singleValueParams = e.queryStringParameters || {};\n\n\t\t// Merge single and multi-value parameters\n\t\tconst mergedParams: Record<string, string | string[]> = {};\n\n\t\t// Add single value parameters\n\t\tfor (const [key, value] of Object.entries(singleValueParams)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tmergedParams[key] = value;\n\t\t\t}\n\t\t}\n\n\t\t// Override with multi-value parameters where applicable\n\t\tfor (const [key, values] of Object.entries(multiValueParams)) {\n\t\t\tif (values && values.length > 1) {\n\t\t\t\tmergedParams[key] = values;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tbody: AmazonApiGatewayEndpoint.decodeBody(\n\t\t\t\te.body,\n\t\t\t\te.isBase64Encoded,\n\t\t\t\te.headers?.['Content-Type'] ?? e.headers?.['content-type'],\n\t\t\t),\n\t\t\tquery: parseQueryParams(mergedParams),\n\t\t\tparams: e.pathParameters || {},\n\t\t};\n\t}\n\toverride getLoggerContext(\n\t\tdata: APIGatewayProxyEvent,\n\t\tcontext: Context,\n\t): LoggerContext {\n\t\treturn {\n\t\t\tfn: {\n\t\t\t\tname: context.functionName,\n\t\t\t\tversion: context.functionVersion,\n\t\t\t},\n\t\t\treq: {\n\t\t\t\tid: data.requestContext.requestId,\n\t\t\t\tawsRequestId: context.awsRequestId,\n\t\t\t\tip: data.requestContext.identity.sourceIp,\n\t\t\t\tuserAgent: data.requestContext.identity.userAgent || undefined,\n\t\t\t\tpath: data.requestContext.path,\n\t\t\t},\n\t\t};\n\t}\n\tconstructor(\n\t\tprotected override readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession\n\t\t>,\n\t\toptions: AmazonApiGatewayEndpointOptions = {},\n\t) {\n\t\tsuper(endpoint, options);\n\t}\n}\n","import type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { APIGatewayProxyEventV2, Context } from 'aws-lambda';\nimport qs from 'qs';\nimport type { HttpMethod } from '../types';\nimport {\n\tAmazonApiGatewayEndpoint,\n\ttype AmazonApiGatewayEndpointOptions,\n\ttype AmazonApiGatewayV2EndpointHandler,\n\ttype GetInputResponse,\n\ttype LoggerContext,\n} from './AmazonApiGatewayEndpointAdaptor';\nimport type { CookieFn, Endpoint, EndpointSchemas } from './Endpoint';\nimport { createApiGatewayCookies } from './lazyAccessors';\n\nexport class AmazonApiGatewayV2Endpoint<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> extends AmazonApiGatewayEndpoint<\n\tAmazonApiGatewayV2EndpointHandler,\n\tAPIGatewayProxyEventV2,\n\tTRoute,\n\tTMethod,\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tTSession\n> {\n\toverride getCookies(e: APIGatewayProxyEventV2): CookieFn {\n\t\treturn createApiGatewayCookies(\n\t\t\te.cookies,\n\t\t\t(e.headers as Record<string, string>)?.cookie,\n\t\t);\n\t}\n\n\toverride getInput(e: APIGatewayProxyEventV2): GetInputResponse {\n\t\tconst raw = AmazonApiGatewayEndpoint.decodeBody(\n\t\t\te.body,\n\t\t\te.isBase64Encoded,\n\t\t\te.headers?.['content-type'],\n\t\t);\n\n\t\treturn {\n\t\t\tbody: raw,\n\t\t\tquery: qs.parse(e.rawQueryString) as Record<string, any>,\n\t\t\tparams: e.pathParameters || {},\n\t\t};\n\t}\n\toverride getLoggerContext(\n\t\tevent: APIGatewayProxyEventV2,\n\t\tcontext: Context,\n\t): LoggerContext {\n\t\treturn {\n\t\t\tfn: {\n\t\t\t\tname: context.functionName,\n\t\t\t\tversion: context.functionVersion,\n\t\t\t},\n\t\t\treq: {\n\t\t\t\tid: event.requestContext.requestId,\n\t\t\t\tawsRequestId: context.awsRequestId,\n\t\t\t\tip: event.requestContext.http.sourceIp,\n\t\t\t\tuserAgent: event.requestContext.http.userAgent || undefined,\n\t\t\t\tpath: event.requestContext.http.path,\n\t\t\t},\n\t\t};\n\t}\n\tconstructor(\n\t\tprotected override readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession\n\t\t>,\n\t\toptions: AmazonApiGatewayEndpointOptions = {},\n\t) {\n\t\tsuper(endpoint, options);\n\t}\n}\n\n// Re-export options type for convenience\nexport type { AmazonApiGatewayEndpointOptions };\n","import type { EnvironmentParser } from '@geekmidas/envkit';\nimport { wrapError } from '@geekmidas/errors';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext, ServiceDiscovery } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type {\n\tContext,\n\tHandler,\n\tSQSBatchResponse,\n\tSQSEvent,\n\tSQSRecord,\n} from 'aws-lambda';\nimport type { Queue, QueueContext } from './Queue';\n\nexport type AWSLambdaHandler<TEvent = any, TResult = any> = Handler<\n\tTEvent,\n\tTResult\n>;\n\n/**\n * Runs a {@link Queue} worker as an AWS Lambda backed by an SQS event-source\n * mapping. Unlike a subscriber, a queue drains *every* record (no type filter):\n * each record body (`{ type, payload }`, as written by the SQS publisher) is\n * unwrapped to its `payload`, validated against the queue's `messageSchema`, and\n * the whole batch is handed to the handler as `messages`.\n *\n * Uses SQS partial-batch responses: a record that fails to parse, validate, or\n * (when the handler throws) process is returned in `batchItemFailures` so SQS\n * retries only those records.\n */\nexport class AWSLambdaQueue<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _services?: ServiceRecord<TServices>;\n\n\tconstructor(\n\t\tprivate readonly envParser: EnvironmentParser<{}>,\n\t\treadonly queue: Queue<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {}\n\n\tget logger(): TLogger {\n\t\treturn this.queue.logger;\n\t}\n\n\tprivate async getServices(): Promise<ServiceRecord<TServices>> {\n\t\tif (this._services) {\n\t\t\treturn this._services;\n\t\t}\n\n\t\tconst serviceDiscovery = ServiceDiscovery.getInstance(this.envParser);\n\n\t\tthis._services =\n\t\t\tthis.queue.services.length > 0\n\t\t\t\t? ((await serviceDiscovery.register(\n\t\t\t\t\t\tthis.queue.services,\n\t\t\t\t\t)) as ServiceRecord<TServices>)\n\t\t\t\t: ({} as ServiceRecord<TServices>);\n\n\t\treturn this._services;\n\t}\n\n\t/** The queue's database, registered as functions register theirs. */\n\tprivate async getDatabase(): Promise<TDatabase | undefined> {\n\t\tconst service = this.queue.databaseService;\n\t\tif (!service) return undefined;\n\n\t\tconst registered = await ServiceDiscovery.getInstance(\n\t\t\tthis.envParser,\n\t\t).register([service]);\n\n\t\treturn registered[service.serviceName as keyof typeof registered] as\n\t\t\t| TDatabase\n\t\t\t| undefined;\n\t}\n\n\tprivate safeJsonParse(value: string): unknown {\n\t\ttry {\n\t\t\treturn JSON.parse(value);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Unwrap the queue payload from an SQS record. The SQS publisher writes\n\t * `{ type, payload }`; a payload-only body is also accepted as-is.\n\t */\n\tprivate extractPayload(record: SQSRecord): unknown {\n\t\tconst body = this.safeJsonParse(record.body);\n\t\tif (body && typeof body === 'object' && 'payload' in body) {\n\t\t\treturn (body as { payload: unknown }).payload;\n\t\t}\n\t\treturn body ?? record.body;\n\t}\n\n\tprivate async _handler(\n\t\tevent: SQSEvent,\n\t\tcontext: Context,\n\t): Promise<SQSBatchResponse> {\n\t\tconst logger = this.queue.logger.child({\n\t\t\tqueue: {\n\t\t\t\tname: this.queue.name,\n\t\t\t\tfn: context.functionName,\n\t\t\t},\n\t\t\treq: { id: context.awsRequestId },\n\t\t}) as TLogger;\n\n\t\tconst services = await this.getServices();\n\t\tconst db = await this.getDatabase();\n\t\tconst schema = this.queue.messageSchema;\n\n\t\tconst batchItemFailures: { itemIdentifier: string }[] = [];\n\t\tconst messages: InferStandardSchema<TMessage>[] = [];\n\n\t\tfor (const record of event.Records) {\n\t\t\ttry {\n\t\t\t\tconst payload = this.extractPayload(record);\n\t\t\t\tconst validation = await schema['~standard'].validate(payload);\n\t\t\t\tif (validation.issues) {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t{ issues: validation.issues, messageId: record.messageId },\n\t\t\t\t\t\t'Queue message failed validation',\n\t\t\t\t\t);\n\t\t\t\t\tbatchItemFailures.push({ itemIdentifier: record.messageId });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmessages.push(validation.value as InferStandardSchema<TMessage>);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t{ err: error, messageId: record.messageId },\n\t\t\t\t\t'Failed to parse SQS record',\n\t\t\t\t);\n\t\t\t\tbatchItemFailures.push({ itemIdentifier: record.messageId });\n\t\t\t}\n\t\t}\n\n\t\tif (messages.length === 0) {\n\t\t\treturn { batchItemFailures };\n\t\t}\n\n\t\ttry {\n\t\t\t// Cast because `db` is in the context only when TDatabase is set, a\n\t\t\t// condition a generic cannot resolve; `undefined` when it is not.\n\t\t\tawait this.queue.handler({\n\t\t\t\tmessages,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t\tdb,\n\t\t\t} as unknown as QueueContext<TMessage, TServices, TLogger, TDatabase>);\n\t\t} catch (error) {\n\t\t\tlogger.error(wrapError(error), 'Queue handler failed; retrying batch');\n\t\t\t// Handler processes the batch atomically — fail the whole batch so SQS\n\t\t\t// retries every (valid) record.\n\t\t\tfor (const record of event.Records) {\n\t\t\t\tif (\n\t\t\t\t\t!batchItemFailures.some((f) => f.itemIdentifier === record.messageId)\n\t\t\t\t) {\n\t\t\t\t\tbatchItemFailures.push({ itemIdentifier: record.messageId });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { batchItemFailures };\n\t}\n\n\tget handler(): AWSLambdaHandler<SQSEvent, SQSBatchResponse> {\n\t\tconst handler = this._handler.bind(this);\n\n\t\treturn (async (event: SQSEvent, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.queue.logger.child({ requestId }) as TLogger;\n\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation: `queue ${this.queue.name}` },\n\t\t\t\t() => handler(event, context),\n\t\t\t);\n\t\t}) as unknown as AWSLambdaHandler<SQSEvent, SQSBatchResponse>;\n\t}\n}\n","import type { EnvironmentParser } from '@geekmidas/envkit';\nimport { wrapError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext } from '@geekmidas/services';\nimport middy, { type MiddlewareObj } from '@middy/core';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Context, Handler, SNSEvent, SQSEvent } from 'aws-lambda';\nimport {\n\trunSubscriber,\n\tsubscribedEvents,\n\tsubscriberContext,\n} from './runSubscriber';\nimport type { Subscriber } from './Subscriber';\n\nexport type AWSLambdaHandler<TEvent = any, TResult = any> = Handler<\n\tTEvent,\n\tTResult\n>;\n\ntype SubscriberEvent<TServices extends Service[], TLogger extends Logger> = {\n\tevents: any[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n\t/** The subscriber's database, when it has one. */\n\tdb?: unknown;\n};\n\ntype Middleware<\n\tTServices extends Service[],\n\tTLogger extends Logger,\n\tTOutSchema extends StandardSchemaV1 | undefined,\n> = MiddlewareObj<\n\tSubscriberEvent<TServices, TLogger>,\n\tInferStandardSchema<TOutSchema>,\n\tError,\n\tContext\n>;\n\nexport class AWSLambdaSubscriber<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends any[] = [],\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _logger!: TLogger;\n\n\tconstructor(\n\t\tprivate envParser: EnvironmentParser<{}>,\n\t\treadonly subscriber: Subscriber<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {\n\t\tthis._logger = subscriber.logger;\n\t}\n\n\tget logger(): TLogger {\n\t\treturn this._logger;\n\t}\n\n\tprivate error(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tonError: (req) => {\n\t\t\t\tconst logger = req.event?.logger || this.subscriber.logger;\n\t\t\t\tlogger.error(req.error || {}, 'Error processing subscriber');\n\n\t\t\t\t// Re-throw the wrapped error to let Lambda handle it\n\t\t\t\tthrow wrapError(req.error);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate loggerMiddleware(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tbefore: (req) => {\n\t\t\t\tthis._logger = this.subscriber.logger.child({\n\t\t\t\t\tsubscriber: {\n\t\t\t\t\t\tname: req.context.functionName,\n\t\t\t\t\t\tversion: req.context.functionVersion,\n\t\t\t\t\t\tmemory: req.context.memoryLimitInMB,\n\t\t\t\t\t},\n\t\t\t\t\treq: {\n\t\t\t\t\t\tid: req.context.awsRequestId,\n\t\t\t\t\t},\n\t\t\t\t}) as TLogger;\n\n\t\t\t\treq.event.logger = this._logger;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate services(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst { services, db } = await subscriberContext(\n\t\t\t\t\tthis.subscriber,\n\t\t\t\t\tthis.envParser,\n\t\t\t\t);\n\t\t\t\treq.event.services = services as ServiceRecord<TServices>;\n\t\t\t\treq.event.db = db;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate parseEvents(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst { logger, ...rawEvent } = req.event;\n\n\t\t\t\tlogger.info({\n\t\t\t\t\trawEvent,\n\t\t\t\t});\n\n\t\t\t\treq.event.events = subscribedEvents(\n\t\t\t\t\tthis.subscriber,\n\t\t\t\t\trawEvent as unknown as SQSEvent | SNSEvent,\n\t\t\t\t\tthis.logger,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async _handler(event: SubscriberEvent<TServices, TLogger>) {\n\t\t// Only these four: the raw Lambda event stays out of the handler.\n\t\tconst { events, services, logger, db } = event;\n\t\treturn runSubscriber(this.subscriber, {\n\t\t\tevents,\n\t\t\tservices,\n\t\t\tlogger,\n\t\t\tdb,\n\t\t}) as Promise<InferStandardSchema<OutSchema>>;\n\t}\n\n\tget handler(): AWSLambdaHandler {\n\t\tconst handler = this._handler.bind(this);\n\n\t\t// Apply middleware in order\n\t\tconst chain = middy(handler)\n\t\t\t.use(this.loggerMiddleware())\n\t\t\t.use(this.parseEvents())\n\t\t\t.use(this.error())\n\t\t\t.use(this.services());\n\n\t\t// Wrap entire Middy chain in request context for service access\n\t\tconst wrappedHandler = async (event: unknown, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.subscriber.logger.child({\n\t\t\t\trequestId,\n\t\t\t}) as TLogger;\n\n\t\t\tconst operation = `subscriber ${this.subscriber.topicName ?? context.functionName}`;\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation },\n\t\t\t\t() => chain(event as Parameters<typeof chain>[0], context),\n\t\t\t);\n\t\t};\n\n\t\treturn wrappedHandler as unknown as AWSLambdaHandler;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aws.mjs","names":[],"sources":["../../src/functions/FunctionExecutionWrapper.ts","../../src/functions/AWSLambdaFunction.ts","../../src/crons/AWSScheduledFunction.ts","../../src/endpoints/AmazonApiGatewayEndpointAdaptor.ts","../../src/endpoints/parseQueryParams.ts","../../src/endpoints/AmazonApiGatewayV1EndpointAdaptor.ts","../../src/endpoints/AmazonApiGatewayV2EndpointAdaptor.ts","../../src/queue/AWSLambdaQueueAdaptor.ts","../../src/subscribers/AWSLambdaSubscriberAdaptor.ts"],"sourcesContent":["import type { AuditableAction, Auditor, AuditStorage } from '@geekmidas/audit';\nimport { DefaultAuditor } from '@geekmidas/audit';\nimport { withAuditableTransaction } from '@geekmidas/audit/kysely';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tComposableStandardSchema,\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishEvents } from '../publisher';\nimport type { Function, FunctionHandler } from './Function';\nimport { FunctionBuilder } from './FunctionBuilder';\n\nexport abstract class FunctionExecutionWrapper<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> {\n\tconstructor(\n\t\tprotected envParser: EnvironmentParser<{}>,\n\t\tprotected readonly fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tFunctionHandler<\n\t\t\t\tTInput,\n\t\t\t\tTServices,\n\t\t\t\tTLogger,\n\t\t\t\tTOutSchema,\n\t\t\t\tTDatabase,\n\t\t\t\tTAuditStorage,\n\t\t\t\tTAuditAction\n\t\t\t>\n\t\t>,\n\t) {}\n\n\tprotected _logger?: TLogger;\n\n\tget logger(): TLogger {\n\t\treturn this._logger || this.fn.logger;\n\t}\n\n\tget serviceDiscovery(): ServiceDiscovery<ServiceRecord<TServices>> {\n\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\tServiceRecord<TServices>\n\t\t>(this.envParser);\n\n\t\treturn serviceDiscovery;\n\t}\n\n\tgetServices(): Promise<ServiceRecord<TServices>> {\n\t\treturn this.serviceDiscovery.register(this.fn.services);\n\t}\n\n\tasync getDatabase(): Promise<TDatabase | undefined> {\n\t\tif (!this.fn.databaseService) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst services = await this.serviceDiscovery.register([\n\t\t\tthis.fn.databaseService,\n\t\t]);\n\n\t\treturn services[\n\t\t\tthis.fn.databaseService.serviceName as keyof typeof services\n\t\t] as TDatabase;\n\t}\n\n\t/**\n\t * Get the audit storage service if configured.\n\t * Returns undefined if no auditor storage is configured.\n\t */\n\tasync getAuditStorage(): Promise<TAuditStorage | undefined> {\n\t\tif (!this.fn.auditorStorageService) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst services = await this.serviceDiscovery.register([\n\t\t\tthis.fn.auditorStorageService,\n\t\t]);\n\n\t\treturn services[\n\t\t\tthis.fn.auditorStorageService.serviceName as keyof typeof services\n\t\t] as TAuditStorage;\n\t}\n\n\t/**\n\t * Create an auditor instance for the function.\n\t * Returns undefined if no auditor storage is configured.\n\t */\n\tasync createAuditor(): Promise<Auditor<TAuditAction> | undefined> {\n\t\tconst storage = await this.getAuditStorage();\n\t\tif (!storage) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn new DefaultAuditor<TAuditAction>({\n\t\t\tactor: { id: 'system', type: 'system' },\n\t\t\tstorage: storage as AuditStorage,\n\t\t\tmetadata: {\n\t\t\t\tfunction: this.fn.type,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Execute handler with audit transaction support.\n\t * If the audit storage has a database (via getDatabase()), wraps execution\n\t * in a transaction so audits are atomic with handler's database operations.\n\t *\n\t * @param handler - The handler function to execute (receives auditor and db)\n\t * @returns The handler result\n\t */\n\tasync executeWithAudit<T>(\n\t\thandler: (ctx: {\n\t\t\tauditor?: Auditor<TAuditAction>;\n\t\t\tdb?: TDatabase;\n\t\t}) => Promise<T>,\n\t): Promise<T> {\n\t\tconst auditor = await this.createAuditor();\n\t\tconst storage = await this.getAuditStorage();\n\n\t\t// No audit context - just run handler with regular db\n\t\tif (!auditor || !storage) {\n\t\t\tconst db = await this.getDatabase();\n\t\t\treturn handler({ db });\n\t\t}\n\n\t\t// Check if storage has a database and db service names match\n\t\tconst storageDb = (storage as AuditStorage).getDatabase?.();\n\t\tconst databaseServiceName = this.fn.databaseService?.serviceName;\n\t\tconst auditDbServiceName = this.fn.auditorStorageService?.serviceName;\n\n\t\t// If the audit storage has a database and we're using the same database service\n\t\t// (or the audit storage provides the database), use transactional execution\n\t\tif (storageDb && databaseServiceName && auditDbServiceName) {\n\t\t\treturn withAuditableTransaction(\n\t\t\t\tstorageDb as any,\n\t\t\t\tauditor as any,\n\t\t\t\tasync (trx) => {\n\t\t\t\t\t// Use transaction as db\n\t\t\t\t\tconst response = await handler({\n\t\t\t\t\t\tauditor,\n\t\t\t\t\t\tdb: trx as TDatabase,\n\t\t\t\t\t});\n\t\t\t\t\t// Audits are flushed by withAuditableTransaction before commit\n\t\t\t\t\treturn response;\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// No database on storage or service names don't match - run handler and flush audits after\n\t\tconst db = await this.getDatabase();\n\t\tconst response = await handler({ auditor, db });\n\n\t\t// Flush audits (no transaction)\n\t\tawait auditor.flush();\n\n\t\treturn response;\n\t}\n\n\tasync getFunctionInput<TEvent>(\n\t\tevent: TEvent,\n\t): Promise<InferComposableStandardSchema<TInput>> {\n\t\tconst parsedInput = await FunctionBuilder.parseComposableStandardSchema(\n\t\t\tevent,\n\t\t\tthis.fn.input,\n\t\t);\n\n\t\treturn parsedInput as InferComposableStandardSchema<TInput>;\n\t}\n\n\tasync publishEvents(response: InferStandardSchema<TOutSchema>) {\n\t\tawait publishEvents(\n\t\t\tthis.logger,\n\t\t\tthis.serviceDiscovery,\n\t\t\tthis.fn.events,\n\t\t\tresponse,\n\t\t);\n\t}\n\n\tasync parseComposableStandardSchema<T extends ComposableStandardSchema>(\n\t\tdata: unknown,\n\t\tschema: T,\n\t): Promise<InferComposableStandardSchema<T>> {\n\t\treturn FunctionBuilder.parseComposableStandardSchema(data, schema);\n\t}\n}\n","import type { AuditableAction, Auditor, AuditStorage } from '@geekmidas/audit';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport { wrapError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tComposableStandardSchema,\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext } from '@geekmidas/services';\nimport middy, { type MiddlewareObj } from '@middy/core';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Context, Handler } from 'aws-lambda';\nimport type { Function, FunctionHandler } from './Function';\nimport { FunctionBuilder } from './FunctionBuilder';\nimport { FunctionExecutionWrapper } from './FunctionExecutionWrapper';\n\nexport type AWSLambdaHandler<TEvent = any, TResult = any> = Handler<\n\tTEvent,\n\tTResult\n>;\n\ntype FunctionEvent<\n\tTEvent,\n\tTInput extends ComposableStandardSchema | undefined,\n\tTServices extends Service[],\n\tTLogger extends Logger,\n\tTDatabase = undefined,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> = TEvent & {\n\tparsedInput: InferComposableStandardSchema<TInput>;\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n\tdb: TDatabase | undefined;\n\tauditor: Auditor<TAuditAction> | undefined;\n};\n\ntype Middleware<\n\tTEvent,\n\tTInput extends ComposableStandardSchema | undefined,\n\tTServices extends Service[],\n\tTLogger extends Logger,\n\tTOutSchema extends StandardSchemaV1 | undefined,\n\tTDatabase = undefined,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> = MiddlewareObj<\n\tFunctionEvent<TEvent, TInput, TServices, TLogger, TDatabase, TAuditAction>,\n\tInferComposableStandardSchema<TOutSchema>,\n\tError,\n\tContext\n>;\n\nexport class AWSLambdaFunction<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> extends FunctionExecutionWrapper<\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tTAuditStorage,\n\tTAuditStorageServiceName,\n\tTDatabase,\n\tTDatabaseServiceName,\n\tTAuditAction\n> {\n\tconstructor(\n\t\tenvParser: EnvironmentParser<{}>,\n\t\toverride readonly fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tFunctionHandler<\n\t\t\t\tTInput,\n\t\t\t\tTServices,\n\t\t\t\tTLogger,\n\t\t\t\tTOutSchema,\n\t\t\t\tTDatabase,\n\t\t\t\tTAuditStorage,\n\t\t\t\tTAuditAction\n\t\t\t>\n\t\t>,\n\t) {\n\t\tsuper(envParser, fn);\n\t}\n\n\tprivate error<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tonError: (req) => {\n\t\t\t\tconst logger = req.event?.logger || this.fn.logger;\n\t\t\t\tlogger.error(req.error || {}, 'Error processing function');\n\n\t\t\t\t// Re-throw the wrapped error to let Lambda handle it\n\t\t\t\tthrow wrapError(req.error);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate baseInput<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: (_req) => {},\n\t\t};\n\t}\n\n\tprivate input<\n\t\tTEvent extends { input: InferComposableStandardSchema<TInput> },\n\t>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\ttry {\n\t\t\t\t\t// Parse input if schema is provided\n\t\t\t\t\tif (this.fn.input) {\n\t\t\t\t\t\tconst parsedInput =\n\t\t\t\t\t\t\tawait FunctionBuilder.parseComposableStandardSchema(\n\t\t\t\t\t\t\t\treq.event,\n\t\t\t\t\t\t\t\tthis.fn.input,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\treq.event.parsedInput =\n\t\t\t\t\t\t\tparsedInput as InferComposableStandardSchema<TInput>;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If no schema, pass the event as-is\n\t\t\t\t\t\treq.event.parsedInput = req.event as any;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t{ err: error, event: req.event },\n\t\t\t\t\t\t'Failed to parse input',\n\t\t\t\t\t);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate loggerMiddleware<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: (req) => {\n\t\t\t\tthis._logger = this.fn.logger.child({\n\t\t\t\t\tfn: {\n\t\t\t\t\t\tname: req.context.functionName,\n\t\t\t\t\t\tversion: req.context.functionVersion,\n\t\t\t\t\t\tmemory: req.context.memoryLimitInMB,\n\t\t\t\t\t},\n\t\t\t\t\treq: {\n\t\t\t\t\t\tid: req.context.awsRequestId,\n\t\t\t\t\t},\n\t\t\t\t}) as TLogger;\n\n\t\t\t\treq.event.logger = this._logger;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate services<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\treq.event.services = await this.getServices();\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate database<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\treq.event.db = await this.getDatabase();\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate auditor<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\treq.event.auditor = await this.createAuditor();\n\t\t\t},\n\t\t\tafter: async (req) => {\n\t\t\t\t// Flush any pending audits after successful execution\n\t\t\t\tif (req.event.auditor) {\n\t\t\t\t\tconst records = req.event.auditor.getRecords();\n\t\t\t\t\tif (records.length > 0) {\n\t\t\t\t\t\tthis.logger.debug(\n\t\t\t\t\t\t\t{ auditCount: records.length },\n\t\t\t\t\t\t\t'Flushing function audits',\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait req.event.auditor.flush();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate events<TEvent>(): Middleware<\n\t\tTEvent,\n\t\tTInput,\n\t\tTServices,\n\t\tTLogger,\n\t\tTOutSchema,\n\t\tTDatabase,\n\t\tTAuditAction\n\t> {\n\t\treturn {\n\t\t\tafter: async (req) => {\n\t\t\t\tconst response = (req.response ||\n\t\t\t\t\tundefined) as InferStandardSchema<TOutSchema>;\n\t\t\t\tawait this.publishEvents(response);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async _handler<TEvent>(\n\t\tevent: FunctionEvent<\n\t\t\tTEvent,\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTAuditAction\n\t\t>,\n\t) {\n\t\t// Execute the function with the parsed context\n\t\tconst result = await this.fn.fn({\n\t\t\tinput: event.parsedInput,\n\t\t\tservices: event.services,\n\t\t\tlogger: event.logger,\n\t\t\tdb: event.db,\n\t\t\tauditor: event.auditor,\n\t\t} as any);\n\n\t\t// Parse output if schema is provided\n\t\tconst output = await this.fn.parseOutput(result);\n\n\t\treturn output;\n\t}\n\n\tget handler(): AWSLambdaHandler {\n\t\tconst handler = this._handler.bind(this);\n\n\t\t// Apply middleware in order\n\t\tconst chain = middy(handler)\n\t\t\t.use(this.loggerMiddleware())\n\t\t\t.use(this.baseInput())\n\t\t\t.use(this.error())\n\t\t\t.use(this.services())\n\t\t\t.use(this.database())\n\t\t\t.use(this.auditor())\n\t\t\t.use(this.input())\n\t\t\t.use(this.events());\n\n\t\t// Wrap entire Middy chain in request context for service access\n\t\tconst wrappedHandler = async (event: unknown, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.fn.logger.child({ requestId }) as TLogger;\n\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation: context.functionName },\n\t\t\t\t() => chain(event as Parameters<typeof chain>[0], context),\n\t\t\t);\n\t\t};\n\n\t\treturn wrappedHandler as unknown as AWSLambdaHandler;\n\t}\n}\n","import type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { AWSLambdaFunction } from '../functions/AWSLambdaFunction';\nimport type { Cron } from './Cron';\n\n/**\n * AWS Lambda adaptor for {@link Cron} constructs.\n *\n * A `Cron` is a {@link import('../functions').Function} that is invoked on a\n * schedule (an EventBridge rule) rather than by an API request. Because of\n * that, the runtime execution pipeline is identical to a regular Lambda\n * function: input parsing, service/database/auditor wiring, event publishing,\n * error handling, and the `runWithRequestContext` wrapper that powers\n * request-scoped logging via `serviceContext.getLogger()`.\n *\n * This adaptor therefore reuses {@link AWSLambdaFunction} wholesale and only\n * narrows the constructor to accept a `Cron`. The schedule expression itself\n * (`cron.schedule`) is **deploy-time** infrastructure consumed by the CLI to\n * provision the EventBridge rule — it is not needed at runtime.\n *\n * @example\n * ```typescript\n * import { AWSScheduledFunction } from '@geekmidas/constructs/crons';\n * import { cancelPastBookings } from '../../../src/crons/cancelPastBookings.js';\n * import { envParser } from '../../../src/env';\n *\n * const adapter = new AWSScheduledFunction(envParser, cancelPastBookings);\n *\n * export const handler = adapter.handler;\n * ```\n */\nexport class AWSScheduledFunction<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends AWSLambdaFunction<\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tundefined,\n\tstring,\n\tTDatabase,\n\tTDatabaseServiceName\n> {\n\t// biome-ignore lint/complexity/noUselessConstructor: narrows the inherited `Function` parameter to `Cron` for a clearer public API\n\tconstructor(\n\t\tenvParser: EnvironmentParser<{}>,\n\t\tcron: Cron<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {\n\t\tsuper(envParser, cron);\n\t}\n}\n","import type { AuditableAction, AuditStorage } from '@geekmidas/audit';\nimport { withRlsContext } from '@geekmidas/db/rls';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport middy, { type MiddlewareObj } from '@middy/core';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type {\n\tAPIGatewayProxyEvent,\n\tAPIGatewayProxyEventV2,\n\tContext,\n} from 'aws-lambda';\nimport set from 'lodash.set';\nimport type { Telemetry } from '../telemetry';\nimport type { HttpMethod } from '../types';\nimport { Endpoint, type EndpointSchemas, ResponseBuilder } from './Endpoint';\nimport { envParserFor } from './surfaceEnv';\n\n/**\n * Telescope integration for request recording.\n * Uses Middy middleware pattern for compatibility with existing telescope package.\n */\nexport interface TelescopeIntegration {\n\tmiddleware: MiddlewareObj<any, any, Error, Context>;\n}\n\nimport {\n\tForbiddenError,\n\tUnprocessableEntityError,\n\twrapError,\n} from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type {\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport { publishConstructEvents } from '../publisher';\nimport type { MappedAudit } from './audit';\nimport type { CookieFn, HeaderFn } from './Endpoint';\nimport {\n\tcreateAuditContext,\n\texecuteWithAuditTransaction,\n} from './processAudits';\n\n// Helper function to publish events\n\n/**\n * Options for Amazon API Gateway endpoint adaptors\n */\nexport interface AmazonApiGatewayEndpointOptions {\n\t/**\n\t * Telescope integration for request recording and monitoring.\n\t *\n\t * @example\n\t * ```typescript\n\t * import { telescopeMiddleware } from '@geekmidas/telescope/lambda';\n\t *\n\t * const adaptor = new AmazonApiGatewayV2Endpoint(endpoint, {\n\t * telescope: { middleware: telescopeMiddleware(telescope) },\n\t * });\n\t * ```\n\t */\n\ttelescope?: TelescopeIntegration;\n\n\t/**\n\t * Telemetry integration for distributed tracing.\n\t * Works with any OpenTelemetry-compatible backend.\n\t *\n\t * @example\n\t * ```typescript\n\t * import { OTelTelemetry } from '@geekmidas/telescope/instrumentation';\n\t *\n\t * const adaptor = new AmazonApiGatewayV2Endpoint(endpoint, {\n\t * telemetry: new OTelTelemetry(),\n\t * });\n\t * ```\n\t */\n\ttelemetry?: Telemetry;\n}\n\nexport abstract class AmazonApiGatewayEndpoint<\n\tTHandler extends\n\t\t| AmazonApiGatewayV1EndpointHandler\n\t\t| AmazonApiGatewayV2EndpointHandler,\n\tTEvent extends HandlerEvent<THandler>,\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> {\n\tprotected options: AmazonApiGatewayEndpointOptions;\n\n\t/**\n\t * The parser this adaptor discovers services with.\n\t *\n\t * Taken from the endpoint's surface rather than handed in. An endpoint built\n\t * with `api.get()` carries its API, and the API carries the parser — so a\n\t * generated handler is `new AmazonApiGatewayV2Endpoint(endpoint)` with no\n\t * import written for it, which is the whole reason the surface is the\n\t * factory.\n\t */\n\tprotected envParser: EnvironmentParser<{}>;\n\n\tconstructor(\n\t\tprotected readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction\n\t\t>,\n\t\toptions: AmazonApiGatewayEndpointOptions = {},\n\t) {\n\t\tthis.options = options;\n\t\tthis.envParser = envParserFor(endpoint.surface);\n\t}\n\n\tprivate error(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tonError: (req) => {\n\t\t\t\t(req.event.logger || this.endpoint.logger).error(\n\t\t\t\t\treq.error || {},\n\t\t\t\t\t'Error processing request',\n\t\t\t\t);\n\t\t\t\tconst wrappedError = wrapError(req.error);\n\n\t\t\t\t// Set the response with the proper status code from the HttpError\n\t\t\t\treq.response = {\n\t\t\t\t\tstatusCode: wrappedError.statusCode,\n\t\t\t\t\tbody: wrappedError.body,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t}\n\tabstract getInput(e: TEvent): GetInputResponse;\n\n\t/**\n\t * Decodes the raw body from an API Gateway event.\n\t * Uses Content-Type to decide parsing strategy (like Express/Fastify/Hono):\n\t * - `application/json` (or missing) → JSON.parse the decoded string\n\t * - Anything else → return decoded string as-is, let the schema handle it\n\t *\n\t * Defaults to JSON when no Content-Type is provided for backwards compatibility.\n\t */\n\tstatic decodeBody(\n\t\tbody: string | undefined | null,\n\t\tisBase64Encoded: boolean | undefined,\n\t\tcontentType: string | undefined,\n\t): any {\n\t\tif (!body) return undefined;\n\n\t\tconst raw = isBase64Encoded\n\t\t\t? Buffer.from(body, 'base64').toString('utf-8')\n\t\t\t: body;\n\n\t\tconst isJson =\n\t\t\t!contentType || contentType.toLowerCase().includes('application/json');\n\t\tif (isJson) {\n\t\t\treturn JSON.parse(raw);\n\t\t}\n\n\t\treturn raw;\n\t}\n\n\tprotected getCookies(e: TEvent): CookieFn {\n\t\tconst headers = e.headers as Record<string, string>;\n\t\treturn Endpoint.createCookies(headers?.cookie);\n\t}\n\n\tprivate input(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst { body, query, params } = this.getInput(req.event);\n\t\t\t\t\tconst headers = req.event.headers as Record<string, string>;\n\t\t\t\t\tconst header = Endpoint.createHeaders(headers);\n\t\t\t\t\tconst cookie = this.getCookies(req.event);\n\n\t\t\t\t\tset(req.event, 'body', await this.endpoint.parseInput(body, 'body'));\n\n\t\t\t\t\tset(\n\t\t\t\t\t\treq.event,\n\t\t\t\t\t\t'query',\n\t\t\t\t\t\tawait this.endpoint.parseInput(query, 'query'),\n\t\t\t\t\t);\n\t\t\t\t\tset(\n\t\t\t\t\t\treq.event,\n\t\t\t\t\t\t'params',\n\t\t\t\t\t\tawait this.endpoint.parseInput(params, 'params'),\n\t\t\t\t\t);\n\t\t\t\t\tset(req.event, 'header', header);\n\t\t\t\t\tset(req.event, 'cookie', cookie);\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Convert validation errors to 422 Unprocessable Entity\n\t\t\t\t\tif (error && typeof error === 'object' && Array.isArray(error)) {\n\t\t\t\t\t\tthrow new UnprocessableEntityError('Validation failed', error);\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tabstract getLoggerContext(data: TEvent, context: Context): LoggerContext;\n\n\tprivate logger(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: (req) => {\n\t\t\t\treq.event.logger = this.endpoint.logger.child({\n\t\t\t\t\troute: this.endpoint.route,\n\t\t\t\t\thost: req.event.headers?.host,\n\t\t\t\t\tmethod: this.endpoint.method,\n\t\t\t\t\t...this.getLoggerContext(req.event, req.context),\n\t\t\t\t}) as TLogger;\n\t\t\t},\n\t\t};\n\t}\n\tprivate services(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst _logger = req.event.logger as TLogger;\n\t\t\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\t\t\tServiceRecord<TServices>\n\t\t\t\t>(this.envParser);\n\n\t\t\t\tconst services = await serviceDiscovery.register(\n\t\t\t\t\tthis.endpoint.services,\n\t\t\t\t);\n\n\t\t\t\treq.event.services = services;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate authorize(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst logger = req.event.logger as TLogger;\n\t\t\t\tconst services = req.event.services;\n\t\t\t\tconst header = req.event.header;\n\t\t\t\tconst cookie = req.event.cookie;\n\t\t\t\tconst session = req.event.session as TSession;\n\n\t\t\t\tconst isAuthorized = await this.endpoint.authorize({\n\t\t\t\t\theader,\n\t\t\t\t\tcookie,\n\t\t\t\t\tservices,\n\t\t\t\t\tlogger,\n\t\t\t\t\tsession,\n\t\t\t\t\t...((req.event as any).db !== undefined && {\n\t\t\t\t\t\tdb: (req.event as any).db,\n\t\t\t\t\t}),\n\t\t\t\t\tbody: (req.event as any).body,\n\t\t\t\t\tquery: (req.event as any).query,\n\t\t\t\t\tparams: (req.event as any).params,\n\t\t\t\t} as any);\n\n\t\t\t\tif (!isAuthorized) {\n\t\t\t\t\tlogger.warn('Forbidden access attempt');\n\t\t\t\t\tthrow new ForbiddenError(\n\t\t\t\t\t\t'Forbidden access to the endpoint',\n\t\t\t\t\t\t'You do not have permission to access this resource.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate database(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tif (!this.endpoint.databaseService) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst _logger = req.event.logger as TLogger;\n\t\t\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\t\t\tServiceRecord<TServices>\n\t\t\t\t>(this.envParser);\n\n\t\t\t\tconst db = await serviceDiscovery\n\t\t\t\t\t.register([this.endpoint.databaseService])\n\t\t\t\t\t.then(\n\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\ts[this.endpoint.databaseService?.serviceName as keyof typeof s],\n\t\t\t\t\t);\n\n\t\t\t\t(req.event as any).db = db;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate session(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst logger = req.event.logger as TLogger;\n\t\t\t\tconst services = req.event.services;\n\t\t\t\tconst db = (req.event as any).db;\n\t\t\t\treq.event.session = (await this.endpoint.getSession({\n\t\t\t\t\tlogger,\n\t\t\t\t\tservices,\n\t\t\t\t\theader: req.event.header,\n\t\t\t\t\tcookie: req.event.cookie,\n\t\t\t\t\t...(db !== undefined && { db }),\n\t\t\t\t} as any)) as TSession;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate events(): Middleware<TEvent, TInput, TServices, TLogger> {\n\t\treturn {\n\t\t\tafter: async (req) => {\n\t\t\t\tconst event = req.event;\n\t\t\t\tconst response = (event as any)\n\t\t\t\t\t.__response as InferStandardSchema<TOutSchema>;\n\t\t\t\tconst statusCode = req.response?.statusCode ?? this.endpoint.status;\n\n\t\t\t\t// Only publish events on successful responses (2xx status codes)\n\t\t\t\t// Note: Audits are processed inside the handler's transaction\n\t\t\t\tif (Endpoint.isSuccessStatus(statusCode)) {\n\t\t\t\t\tconst logger = event.logger as TLogger;\n\t\t\t\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\t\t\t\tServiceRecord<TServices>\n\t\t\t\t\t>(this.envParser);\n\n\t\t\t\t\t// Publish events\n\t\t\t\t\tawait publishConstructEvents(\n\t\t\t\t\t\tthis.endpoint,\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async _handler(\n\t\tevent: Event<TEvent, TInput, TServices, TLogger, TSession>,\n\t) {\n\t\tconst input = this.endpoint.refineInput(event);\n\t\tconst logger = event.logger as TLogger;\n\t\tconst serviceDiscovery = ServiceDiscovery.getInstance<\n\t\t\tServiceRecord<TServices>\n\t\t>(this.envParser);\n\n\t\t// Create audit context if audit storage is configured\n\t\tconst auditContext = await createAuditContext(\n\t\t\tthis.endpoint,\n\t\t\tserviceDiscovery,\n\t\t\tlogger,\n\t\t\t{\n\t\t\t\tsession: event.session,\n\t\t\t\theader: event.header,\n\t\t\t\tcookie: event.cookie,\n\t\t\t\tservices: event.services as Record<string, unknown>,\n\t\t\t},\n\t\t);\n\n\t\t// Warn if declarative audits are configured but no audit storage\n\t\tconst audits = this.endpoint.audits as MappedAudit<\n\t\t\tTAuditAction,\n\t\t\tTOutSchema\n\t\t>[];\n\t\tif (!auditContext && audits?.length) {\n\t\t\tlogger.warn('No auditor storage service available');\n\t\t}\n\n\t\t// Get pre-resolved database from middleware\n\t\tconst rawDb = (event as any).db;\n\n\t\t// Extract RLS context if configured and not bypassed\n\t\tconst rlsActive =\n\t\t\tthis.endpoint.rlsConfig &&\n\t\t\t!this.endpoint.rlsBypass &&\n\t\t\trawDb !== undefined;\n\t\tconst rlsContext = rlsActive\n\t\t\t? await this.endpoint.rlsConfig?.extractor({\n\t\t\t\t\tservices: event.services as ServiceRecord<TServices>,\n\t\t\t\t\tsession: event.session,\n\t\t\t\t\theader: event.header,\n\t\t\t\t\tcookie: event.cookie,\n\t\t\t\t\tlogger,\n\t\t\t\t})\n\t\t\t: undefined;\n\n\t\t// Execute handler with automatic audit transaction support\n\t\tconst result = await executeWithAuditTransaction(\n\t\t\tauditContext,\n\t\t\tasync (auditor) => {\n\t\t\t\t// Use audit transaction as db only if the storage uses the same database service\n\t\t\t\tconst sameDatabase =\n\t\t\t\t\tauditContext?.storage?.databaseServiceName &&\n\t\t\t\t\tauditContext.storage.databaseServiceName ===\n\t\t\t\t\t\tthis.endpoint.databaseService?.serviceName;\n\t\t\t\tconst baseDb = sameDatabase\n\t\t\t\t\t? (auditor?.getTransaction?.() ?? rawDb)\n\t\t\t\t\t: rawDb;\n\n\t\t\t\t// Helper to execute handler with given db\n\t\t\t\tconst executeHandler = async (db: any) => {\n\t\t\t\t\tconst responseBuilder = new ResponseBuilder();\n\t\t\t\t\tconst response = await this.endpoint.handler(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\theader: event.header,\n\t\t\t\t\t\t\tcookie: event.cookie,\n\t\t\t\t\t\t\tlogger: event.logger,\n\t\t\t\t\t\t\tservices: event.services,\n\t\t\t\t\t\t\tsession: event.session,\n\t\t\t\t\t\t\tauditor,\n\t\t\t\t\t\t\tdb,\n\t\t\t\t\t\t\t...input,\n\t\t\t\t\t\t} as any,\n\t\t\t\t\t\tresponseBuilder,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Check if response has metadata\n\t\t\t\t\tlet data = response;\n\t\t\t\t\tlet metadata = responseBuilder.getMetadata();\n\n\t\t\t\t\tif (Endpoint.hasMetadata(response)) {\n\t\t\t\t\t\tdata = response.data;\n\t\t\t\t\t\tmetadata = response.metadata;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst output = this.endpoint.outputSchema\n\t\t\t\t\t\t? await this.endpoint.parseOutput(data)\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\treturn { output, metadata, responseBuilder };\n\t\t\t\t};\n\n\t\t\t\t// If RLS is active, wrap handler with RLS context\n\t\t\t\tif (rlsActive && rlsContext && baseDb) {\n\t\t\t\t\treturn withRlsContext(\n\t\t\t\t\t\tbaseDb,\n\t\t\t\t\t\trlsContext,\n\t\t\t\t\t\tasync (trx: any) => executeHandler(trx),\n\t\t\t\t\t\t{ prefix: this.endpoint.rlsConfig?.prefix },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn executeHandler(baseDb);\n\t\t\t},\n\t\t\t// Process declarative audits after handler (inside transaction)\n\t\t\tasync (result, auditor) => {\n\t\t\t\tif (!audits?.length) return;\n\n\t\t\t\tfor (const audit of audits) {\n\t\t\t\t\tif (audit.when && !audit.when(result.output as any)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst payload = audit.payload(result.output as any);\n\t\t\t\t\tconst entityId = audit.entityId?.(result.output as any);\n\t\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\t\ttable: audit.table,\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Pass rawDb so storage can reuse existing transactions\n\t\t\t{ db: rawDb },\n\t\t);\n\n\t\tconst { output, metadata } = result;\n\t\tconst isJsonResponse = this.endpoint.responseType === 'application/json';\n\t\tconst body =\n\t\t\toutput === undefined\n\t\t\t\t? undefined\n\t\t\t\t: isJsonResponse\n\t\t\t\t\t? JSON.stringify(output)\n\t\t\t\t\t: typeof output === 'string'\n\t\t\t\t\t\t? output\n\t\t\t\t\t\t: String(output);\n\n\t\t// Store response for middleware access\n\t\t(event as any).__response = output;\n\n\t\t// Build response with metadata\n\t\tconst lambdaResponse: AmazonApiGatewayEndpointHandlerResponse = {\n\t\t\tstatusCode: metadata.status ?? this.endpoint.status,\n\t\t\tbody,\n\t\t};\n\n\t\t// Compose headers. For non-JSON responseType, emit a default\n\t\t// Content-Type header which metadata.headers (r.header()) can override.\n\t\tconst baseHeaders: Record<string, string> | undefined = isJsonResponse\n\t\t\t? undefined\n\t\t\t: { 'content-type': this.endpoint.responseType };\n\n\t\tif (\n\t\t\tbaseHeaders ||\n\t\t\t(metadata.headers && Object.keys(metadata.headers).length > 0)\n\t\t) {\n\t\t\tlambdaResponse.headers = {\n\t\t\t\t...(baseHeaders ?? {}),\n\t\t\t\t...(metadata.headers ?? {}),\n\t\t\t};\n\t\t}\n\n\t\t// Format cookies as Set-Cookie headers\n\t\tif (metadata.cookies && metadata.cookies.size > 0) {\n\t\t\tconst setCookieHeaders: string[] = [];\n\t\t\tfor (const [name, { value, options }] of metadata.cookies) {\n\t\t\t\tsetCookieHeaders.push(\n\t\t\t\t\tEndpoint.formatCookieHeader(name, value, options),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (setCookieHeaders.length > 0) {\n\t\t\t\tlambdaResponse.multiValueHeaders = {\n\t\t\t\t\t...lambdaResponse.multiValueHeaders,\n\t\t\t\t\t'Set-Cookie': setCookieHeaders,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn lambdaResponse;\n\t}\n\n\t/**\n\t * Convert Telemetry interface to Middy middleware\n\t */\n\tprivate telemetry(): Middleware<TEvent, TInput, TServices, TLogger> | null {\n\t\tif (!this.options.telemetry) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst telemetry = this.options.telemetry;\n\t\tlet ctx: any;\n\n\t\treturn {\n\t\t\tbefore: (request) => {\n\t\t\t\tctx = telemetry.onRequestStart({\n\t\t\t\t\tevent: request.event,\n\t\t\t\t\tcontext: request.context,\n\t\t\t\t});\n\t\t\t},\n\t\t\tafter: (request) => {\n\t\t\t\tif (ctx) {\n\t\t\t\t\ttelemetry.onRequestEnd(ctx, {\n\t\t\t\t\t\tstatusCode: request.response?.statusCode ?? 200,\n\t\t\t\t\t\tbody: request.response?.body,\n\t\t\t\t\t\theaders: request.response?.headers,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\tonError: (request) => {\n\t\t\t\tif (ctx && request.error) {\n\t\t\t\t\ttelemetry.onRequestError(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\trequest.error instanceof Error\n\t\t\t\t\t\t\t? request.error\n\t\t\t\t\t\t\t: new Error(String(request.error)),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tget handler() {\n\t\tconst handler = this._handler.bind(this);\n\t\tlet chain = middy(handler)\n\t\t\t.use(this.logger())\n\t\t\t.use(this.error())\n\t\t\t.use(this.services())\n\t\t\t.use(this.input())\n\t\t\t.use(this.database())\n\t\t\t.use(this.session())\n\t\t\t.use(this.authorize())\n\t\t\t.use(this.events());\n\n\t\t// Add telemetry middleware if configured (runs early for span creation)\n\t\tconst telemetryMiddleware = this.telemetry();\n\t\tif (telemetryMiddleware) {\n\t\t\tchain = chain.use(telemetryMiddleware);\n\t\t}\n\n\t\t// Add Telescope middleware if configured (runs first/last in chain)\n\t\tif (this.options.telescope?.middleware) {\n\t\t\tchain = chain.use(this.options.telescope.middleware);\n\t\t}\n\n\t\t// Wrap entire Middy chain in request context for service access\n\t\tconst wrappedHandler = async (event: TEvent, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.endpoint.logger.child({ requestId }) as TLogger;\n\n\t\t\t// Cast event to any since Middy middlewares enrich the event with\n\t\t\t// services, logger, session, etc. during the middleware chain execution\n\t\t\tconst operation = `${this.endpoint.method} ${this.endpoint.route}`;\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation },\n\t\t\t\t() => chain(event as unknown as Parameters<typeof chain>[0], context),\n\t\t\t);\n\t\t};\n\n\t\treturn wrappedHandler as unknown as THandler;\n\t}\n}\n\nexport type Event<\n\tTEvent extends APIGatewayProxyEvent | APIGatewayProxyEventV2,\n\tTInput extends EndpointSchemas = {},\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> = {\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n\theader: HeaderFn;\n\tcookie: CookieFn;\n\tsession: TSession;\n} & TEvent &\n\tInferComposableStandardSchema<TInput>;\n\ntype Middleware<\n\tTEvent extends APIGatewayProxyEvent | APIGatewayProxyEventV2,\n\tTInput extends EndpointSchemas = {},\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> = MiddlewareObj<Event<TEvent, TInput, TServices, TLogger, TSession>>;\n\nexport type AmazonApiGatewayEndpointHandlerResponse = {\n\tstatusCode: number;\n\tbody: string | undefined;\n\theaders?: Record<string, string>;\n\tmultiValueHeaders?: Record<string, string[]>;\n};\n\nexport type LoggerContext = {\n\tfn: {\n\t\tname: string;\n\t\tversion: string;\n\t};\n\treq: {\n\t\tid: string | undefined;\n\t\tawsRequestId: string;\n\t\tpath: string;\n\t\tip: string | undefined;\n\t\tuserAgent: string | undefined;\n\t};\n};\n\nexport type GetInputResponse = {\n\tbody: any;\n\tquery: any;\n\tparams: any;\n};\n\nexport type AmazonApiGatewayV1EndpointHandler = (\n\tevent: APIGatewayProxyEvent,\n\tcontext: Context,\n) => Promise<AmazonApiGatewayEndpointHandlerResponse>;\n\nexport type AmazonApiGatewayV2EndpointHandler = (\n\tevent: APIGatewayProxyEventV2,\n\tcontext: Context,\n) => Promise<AmazonApiGatewayEndpointHandlerResponse>;\n\nexport type HandlerEvent<T extends Function> = T extends (\n\tevent: infer E,\n\tcontext: Context,\n) => any\n\t? E\n\t: never;\n","import qs from 'qs';\n\n/**\n * Parse query parameters from a flat object into a nested structure\n * Uses qs bracket notation for standard interoperability\n *\n * @example\n * parseQueryParams({ 'tags[]': ['a', 'b'], 'filter[name]': 'john' })\n * // Returns: { tags: ['a', 'b'], filter: { name: 'john' } }\n */\nexport function parseQueryParams(\n\tqueryParams: Record<string, string | string[] | undefined> | null,\n): Record<string, any> {\n\tif (!queryParams) {\n\t\treturn {};\n\t}\n\n\t// Reconstruct a query string from the flat object so qs can parse bracket notation\n\tconst parts: string[] = [];\n\tfor (const [key, value] of Object.entries(queryParams)) {\n\t\tif (value === undefined) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) {\n\t\t\t\tparts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);\n\t\t\t}\n\t\t} else {\n\t\t\tparts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n\t\t}\n\t}\n\n\treturn qs.parse(parts.join('&')) as Record<string, any>;\n}\n","import type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { APIGatewayProxyEvent, Context } from 'aws-lambda';\nimport type { HttpMethod } from '../types';\nimport {\n\tAmazonApiGatewayEndpoint,\n\ttype AmazonApiGatewayEndpointOptions,\n\ttype AmazonApiGatewayV1EndpointHandler,\n\ttype GetInputResponse,\n\ttype LoggerContext,\n} from './AmazonApiGatewayEndpointAdaptor';\nimport type { Endpoint, EndpointSchemas } from './Endpoint';\nimport { parseQueryParams } from './parseQueryParams';\n\nexport class AmazonApiGatewayV1Endpoint<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> extends AmazonApiGatewayEndpoint<\n\tAmazonApiGatewayV1EndpointHandler,\n\tAPIGatewayProxyEvent,\n\tTRoute,\n\tTMethod,\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tTSession\n> {\n\toverride getInput(e: APIGatewayProxyEvent): GetInputResponse {\n\t\t// For arrays, AWS API Gateway V1 provides multiValueQueryStringParameters\n\t\tconst multiValueParams = e.multiValueQueryStringParameters || {};\n\t\tconst singleValueParams = e.queryStringParameters || {};\n\n\t\t// Merge single and multi-value parameters\n\t\tconst mergedParams: Record<string, string | string[]> = {};\n\n\t\t// Add single value parameters\n\t\tfor (const [key, value] of Object.entries(singleValueParams)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tmergedParams[key] = value;\n\t\t\t}\n\t\t}\n\n\t\t// Override with multi-value parameters where applicable\n\t\tfor (const [key, values] of Object.entries(multiValueParams)) {\n\t\t\tif (values && values.length > 1) {\n\t\t\t\tmergedParams[key] = values;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tbody: AmazonApiGatewayEndpoint.decodeBody(\n\t\t\t\te.body,\n\t\t\t\te.isBase64Encoded,\n\t\t\t\te.headers?.['Content-Type'] ?? e.headers?.['content-type'],\n\t\t\t),\n\t\t\tquery: parseQueryParams(mergedParams),\n\t\t\tparams: e.pathParameters || {},\n\t\t};\n\t}\n\toverride getLoggerContext(\n\t\tdata: APIGatewayProxyEvent,\n\t\tcontext: Context,\n\t): LoggerContext {\n\t\treturn {\n\t\t\tfn: {\n\t\t\t\tname: context.functionName,\n\t\t\t\tversion: context.functionVersion,\n\t\t\t},\n\t\t\treq: {\n\t\t\t\tid: data.requestContext.requestId,\n\t\t\t\tawsRequestId: context.awsRequestId,\n\t\t\t\tip: data.requestContext.identity.sourceIp,\n\t\t\t\tuserAgent: data.requestContext.identity.userAgent || undefined,\n\t\t\t\tpath: data.requestContext.path,\n\t\t\t},\n\t\t};\n\t}\n\tconstructor(\n\t\tprotected override readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession\n\t\t>,\n\t\toptions: AmazonApiGatewayEndpointOptions = {},\n\t) {\n\t\tsuper(endpoint, options);\n\t}\n}\n","import type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { APIGatewayProxyEventV2, Context } from 'aws-lambda';\nimport qs from 'qs';\nimport type { HttpMethod } from '../types';\nimport {\n\tAmazonApiGatewayEndpoint,\n\ttype AmazonApiGatewayEndpointOptions,\n\ttype AmazonApiGatewayV2EndpointHandler,\n\ttype GetInputResponse,\n\ttype LoggerContext,\n} from './AmazonApiGatewayEndpointAdaptor';\nimport type { CookieFn, Endpoint, EndpointSchemas } from './Endpoint';\nimport { createApiGatewayCookies } from './lazyAccessors';\n\nexport class AmazonApiGatewayV2Endpoint<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n> extends AmazonApiGatewayEndpoint<\n\tAmazonApiGatewayV2EndpointHandler,\n\tAPIGatewayProxyEventV2,\n\tTRoute,\n\tTMethod,\n\tTInput,\n\tTOutSchema,\n\tTServices,\n\tTLogger,\n\tTSession\n> {\n\toverride getCookies(e: APIGatewayProxyEventV2): CookieFn {\n\t\treturn createApiGatewayCookies(\n\t\t\te.cookies,\n\t\t\t(e.headers as Record<string, string>)?.cookie,\n\t\t);\n\t}\n\n\toverride getInput(e: APIGatewayProxyEventV2): GetInputResponse {\n\t\tconst raw = AmazonApiGatewayEndpoint.decodeBody(\n\t\t\te.body,\n\t\t\te.isBase64Encoded,\n\t\t\te.headers?.['content-type'],\n\t\t);\n\n\t\treturn {\n\t\t\tbody: raw,\n\t\t\tquery: qs.parse(e.rawQueryString) as Record<string, any>,\n\t\t\tparams: e.pathParameters || {},\n\t\t};\n\t}\n\toverride getLoggerContext(\n\t\tevent: APIGatewayProxyEventV2,\n\t\tcontext: Context,\n\t): LoggerContext {\n\t\treturn {\n\t\t\tfn: {\n\t\t\t\tname: context.functionName,\n\t\t\t\tversion: context.functionVersion,\n\t\t\t},\n\t\t\treq: {\n\t\t\t\tid: event.requestContext.requestId,\n\t\t\t\tawsRequestId: context.awsRequestId,\n\t\t\t\tip: event.requestContext.http.sourceIp,\n\t\t\t\tuserAgent: event.requestContext.http.userAgent || undefined,\n\t\t\t\tpath: event.requestContext.http.path,\n\t\t\t},\n\t\t};\n\t}\n\tconstructor(\n\t\tprotected override readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession\n\t\t>,\n\t\toptions: AmazonApiGatewayEndpointOptions = {},\n\t) {\n\t\tsuper(endpoint, options);\n\t}\n}\n\n// Re-export options type for convenience\nexport type { AmazonApiGatewayEndpointOptions };\n","import type { EnvironmentParser } from '@geekmidas/envkit';\nimport { wrapError } from '@geekmidas/errors';\nimport { consumeBatchTraced } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext, ServiceDiscovery } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type {\n\tContext,\n\tHandler,\n\tSQSBatchResponse,\n\tSQSEvent,\n\tSQSRecord,\n} from 'aws-lambda';\nimport { recordCarrier } from '../tracing';\nimport type { Queue, QueueContext } from './Queue';\n\nexport type AWSLambdaHandler<TEvent = any, TResult = any> = Handler<\n\tTEvent,\n\tTResult\n>;\n\n/**\n * Runs a {@link Queue} worker as an AWS Lambda backed by an SQS event-source\n * mapping. Unlike a subscriber, a queue drains *every* record (no type filter):\n * each record body (`{ type, payload }`, as written by the SQS publisher) is\n * unwrapped to its `payload`, validated against the queue's `messageSchema`, and\n * the whole batch is handed to the handler as `messages`.\n *\n * Uses SQS partial-batch responses: a record that fails to parse, validate, or\n * (when the handler throws) process is returned in `batchItemFailures` so SQS\n * retries only those records.\n */\nexport class AWSLambdaQueue<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _services?: ServiceRecord<TServices>;\n\n\tconstructor(\n\t\tprivate readonly envParser: EnvironmentParser<{}>,\n\t\treadonly queue: Queue<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {}\n\n\tget logger(): TLogger {\n\t\treturn this.queue.logger;\n\t}\n\n\tprivate async getServices(): Promise<ServiceRecord<TServices>> {\n\t\tif (this._services) {\n\t\t\treturn this._services;\n\t\t}\n\n\t\tconst serviceDiscovery = ServiceDiscovery.getInstance(this.envParser);\n\n\t\tthis._services =\n\t\t\tthis.queue.services.length > 0\n\t\t\t\t? ((await serviceDiscovery.register(\n\t\t\t\t\t\tthis.queue.services,\n\t\t\t\t\t)) as ServiceRecord<TServices>)\n\t\t\t\t: ({} as ServiceRecord<TServices>);\n\n\t\treturn this._services;\n\t}\n\n\t/** The queue's database, registered as functions register theirs. */\n\tprivate async getDatabase(): Promise<TDatabase | undefined> {\n\t\tconst service = this.queue.databaseService;\n\t\tif (!service) return undefined;\n\n\t\tconst registered = await ServiceDiscovery.getInstance(\n\t\t\tthis.envParser,\n\t\t).register([service]);\n\n\t\treturn registered[service.serviceName as keyof typeof registered] as\n\t\t\t| TDatabase\n\t\t\t| undefined;\n\t}\n\n\tprivate safeJsonParse(value: string): unknown {\n\t\ttry {\n\t\t\treturn JSON.parse(value);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Unwrap the queue payload from an SQS record. The SQS publisher writes\n\t * `{ type, payload }`; a payload-only body is also accepted as-is.\n\t */\n\tprivate extractPayload(record: SQSRecord): unknown {\n\t\tconst body = this.safeJsonParse(record.body);\n\t\tif (body && typeof body === 'object' && 'payload' in body) {\n\t\t\treturn (body as { payload: unknown }).payload;\n\t\t}\n\t\treturn body ?? record.body;\n\t}\n\n\tprivate async _handler(\n\t\tevent: SQSEvent,\n\t\tcontext: Context,\n\t): Promise<SQSBatchResponse> {\n\t\tconst logger = this.queue.logger.child({\n\t\t\tqueue: {\n\t\t\t\tname: this.queue.name,\n\t\t\t\tfn: context.functionName,\n\t\t\t},\n\t\t\treq: { id: context.awsRequestId },\n\t\t}) as TLogger;\n\n\t\tconst services = await this.getServices();\n\t\tconst db = await this.getDatabase();\n\t\tconst schema = this.queue.messageSchema;\n\n\t\tconst batchItemFailures: { itemIdentifier: string }[] = [];\n\t\tconst messages: InferStandardSchema<TMessage>[] = [];\n\n\t\tfor (const record of event.Records) {\n\t\t\ttry {\n\t\t\t\tconst payload = this.extractPayload(record);\n\t\t\t\tconst validation = await schema['~standard'].validate(payload);\n\t\t\t\tif (validation.issues) {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t{ issues: validation.issues, messageId: record.messageId },\n\t\t\t\t\t\t'Queue message failed validation',\n\t\t\t\t\t);\n\t\t\t\t\tbatchItemFailures.push({ itemIdentifier: record.messageId });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmessages.push(validation.value as InferStandardSchema<TMessage>);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t{ err: error, messageId: record.messageId },\n\t\t\t\t\t'Failed to parse SQS record',\n\t\t\t\t);\n\t\t\t\tbatchItemFailures.push({ itemIdentifier: record.messageId });\n\t\t\t}\n\t\t}\n\n\t\tif (messages.length === 0) {\n\t\t\treturn { batchItemFailures };\n\t\t}\n\n\t\ttry {\n\t\t\t// Cast because `db` is in the context only when TDatabase is set, a\n\t\t\t// condition a generic cannot resolve; `undefined` when it is not.\n\t\t\tawait this.queue.handler({\n\t\t\t\tmessages,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t\tdb,\n\t\t\t} as unknown as QueueContext<TMessage, TServices, TLogger, TDatabase>);\n\t\t} catch (error) {\n\t\t\tlogger.error(wrapError(error), 'Queue handler failed; retrying batch');\n\t\t\t// Handler processes the batch atomically — fail the whole batch so SQS\n\t\t\t// retries every (valid) record.\n\t\t\tfor (const record of event.Records) {\n\t\t\t\tif (\n\t\t\t\t\t!batchItemFailures.some((f) => f.itemIdentifier === record.messageId)\n\t\t\t\t) {\n\t\t\t\t\tbatchItemFailures.push({ itemIdentifier: record.messageId });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { batchItemFailures };\n\t}\n\n\tget handler(): AWSLambdaHandler<SQSEvent, SQSBatchResponse> {\n\t\tconst handler = this._handler.bind(this);\n\n\t\treturn (async (event: SQSEvent, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.queue.logger.child({ requestId }) as TLogger;\n\n\t\t\t// One CONSUMER span for the batch: a child of the producer when it is\n\t\t\t// one message, linked to each producer when it is several.\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation: `queue ${this.queue.name}` },\n\t\t\t\t() =>\n\t\t\t\t\tconsumeBatchTraced(\n\t\t\t\t\t\t{ system: 'aws_sqs', destination: this.queue.name },\n\t\t\t\t\t\tevent.Records.map(recordCarrier),\n\t\t\t\t\t\t() => handler(event, context),\n\t\t\t\t\t),\n\t\t\t);\n\t\t}) as unknown as AWSLambdaHandler<SQSEvent, SQSBatchResponse>;\n\t}\n}\n","import type { EnvironmentParser } from '@geekmidas/envkit';\nimport { wrapError } from '@geekmidas/errors';\nimport { consumeBatchTraced, type EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext } from '@geekmidas/services';\nimport middy, { type MiddlewareObj } from '@middy/core';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Context, Handler, SNSEvent, SQSEvent } from 'aws-lambda';\nimport { recordCarrier } from '../tracing';\nimport {\n\trunSubscriber,\n\tsubscribedEvents,\n\tsubscriberContext,\n} from './runSubscriber';\nimport type { Subscriber } from './Subscriber';\n\nexport type AWSLambdaHandler<TEvent = any, TResult = any> = Handler<\n\tTEvent,\n\tTResult\n>;\n\ntype SubscriberEvent<TServices extends Service[], TLogger extends Logger> = {\n\tevents: any[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n\t/** The subscriber's database, when it has one. */\n\tdb?: unknown;\n};\n\ntype Middleware<\n\tTServices extends Service[],\n\tTLogger extends Logger,\n\tTOutSchema extends StandardSchemaV1 | undefined,\n> = MiddlewareObj<\n\tSubscriberEvent<TServices, TLogger>,\n\tInferStandardSchema<TOutSchema>,\n\tError,\n\tContext\n>;\n\nexport class AWSLambdaSubscriber<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends any[] = [],\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _logger!: TLogger;\n\n\tconstructor(\n\t\tprivate envParser: EnvironmentParser<{}>,\n\t\treadonly subscriber: Subscriber<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {\n\t\tthis._logger = subscriber.logger;\n\t}\n\n\tget logger(): TLogger {\n\t\treturn this._logger;\n\t}\n\n\tprivate error(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tonError: (req) => {\n\t\t\t\tconst logger = req.event?.logger || this.subscriber.logger;\n\t\t\t\tlogger.error(req.error || {}, 'Error processing subscriber');\n\n\t\t\t\t// Re-throw the wrapped error to let Lambda handle it\n\t\t\t\tthrow wrapError(req.error);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate loggerMiddleware(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tbefore: (req) => {\n\t\t\t\tthis._logger = this.subscriber.logger.child({\n\t\t\t\t\tsubscriber: {\n\t\t\t\t\t\tname: req.context.functionName,\n\t\t\t\t\t\tversion: req.context.functionVersion,\n\t\t\t\t\t\tmemory: req.context.memoryLimitInMB,\n\t\t\t\t\t},\n\t\t\t\t\treq: {\n\t\t\t\t\t\tid: req.context.awsRequestId,\n\t\t\t\t\t},\n\t\t\t\t}) as TLogger;\n\n\t\t\t\treq.event.logger = this._logger;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate services(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst { services, db } = await subscriberContext(\n\t\t\t\t\tthis.subscriber,\n\t\t\t\t\tthis.envParser,\n\t\t\t\t);\n\t\t\t\treq.event.services = services as ServiceRecord<TServices>;\n\t\t\t\treq.event.db = db;\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate parseEvents(): Middleware<TServices, TLogger, OutSchema> {\n\t\treturn {\n\t\t\tbefore: async (req) => {\n\t\t\t\tconst { logger, ...rawEvent } = req.event;\n\n\t\t\t\tlogger.info({\n\t\t\t\t\trawEvent,\n\t\t\t\t});\n\n\t\t\t\treq.event.events = subscribedEvents(\n\t\t\t\t\tthis.subscriber,\n\t\t\t\t\trawEvent as unknown as SQSEvent | SNSEvent,\n\t\t\t\t\tthis.logger,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async _handler(event: SubscriberEvent<TServices, TLogger>) {\n\t\t// Only these four: the raw Lambda event stays out of the handler.\n\t\tconst { events, services, logger, db } = event;\n\t\treturn runSubscriber(this.subscriber, {\n\t\t\tevents,\n\t\t\tservices,\n\t\t\tlogger,\n\t\t\tdb,\n\t\t}) as Promise<InferStandardSchema<OutSchema>>;\n\t}\n\n\tget handler(): AWSLambdaHandler {\n\t\tconst handler = this._handler.bind(this);\n\n\t\t// Apply middleware in order\n\t\tconst chain = middy(handler)\n\t\t\t.use(this.loggerMiddleware())\n\t\t\t.use(this.parseEvents())\n\t\t\t.use(this.error())\n\t\t\t.use(this.services());\n\n\t\t// Wrap entire Middy chain in request context for service access\n\t\tconst wrappedHandler = async (event: unknown, context: Context) => {\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = context.awsRequestId;\n\t\t\tconst logger = this.subscriber.logger.child({\n\t\t\t\trequestId,\n\t\t\t}) as TLogger;\n\n\t\t\tconst operation = `subscriber ${this.subscriber.topicName ?? context.functionName}`;\n\t\t\t// One CONSUMER span for the batch: a child of the publisher's span\n\t\t\t// when it is one record, linked to each when it is several.\n\t\t\tconst records = ((event as { Records?: unknown[] })?.Records ??\n\t\t\t\t[]) as unknown[];\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{ logger, requestId, startTime, operation },\n\t\t\t\t() =>\n\t\t\t\t\tconsumeBatchTraced(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsystem: (records[0] as { Sns?: unknown })?.Sns\n\t\t\t\t\t\t\t\t? 'aws_sns'\n\t\t\t\t\t\t\t\t: 'aws_sqs',\n\t\t\t\t\t\t\tdestination: this.subscriber.topicName ?? context.functionName,\n\t\t\t\t\t\t},\n\t\t\t\t\t\trecords.map(recordCarrier),\n\t\t\t\t\t\t() => chain(event as Parameters<typeof chain>[0], context),\n\t\t\t\t\t),\n\t\t\t);\n\t\t};\n\n\t\treturn wrappedHandler as unknown as AWSLambdaHandler;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -4,11 +4,11 @@ const require_construct_interface = require("../construct-interface.cjs");
|
|
|
4
4
|
const require_auth = require("../auth.cjs");
|
|
5
5
|
const require_functions = require("../functions-CfzJG9hK.cjs");
|
|
6
6
|
const require_Endpoint = require("../Endpoint-Ba1PiyuJ.cjs");
|
|
7
|
-
const require_Queue = require("../Queue-
|
|
7
|
+
const require_Queue = require("../Queue-A3wfD0it.cjs");
|
|
8
8
|
const require_email = require("../email.cjs");
|
|
9
|
-
const require_kysely = require("../kysely-
|
|
9
|
+
const require_kysely = require("../kysely-H7L1YDgE.cjs");
|
|
10
10
|
const require_lazyAccessors = require("../lazyAccessors-hJKGv8Ay.cjs");
|
|
11
|
-
const require_Topic = require("../Topic-
|
|
11
|
+
const require_Topic = require("../Topic-GHxcU8AG.cjs");
|
|
12
12
|
const require_HonoEndpointAdaptor = require("../HonoEndpointAdaptor-0yLvYunI.cjs");
|
|
13
13
|
const require_processAudits = require("../processAudits-DasOZjPl.cjs");
|
|
14
14
|
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
@@ -2,11 +2,11 @@ import { isConsumable } from "../construct-interface.mjs";
|
|
|
2
2
|
import { BetterAuth } from "../auth.mjs";
|
|
3
3
|
import { a as publishEvents, i as publishConstructEvents, t as FunctionBuilder } from "../functions-BFizikJU.mjs";
|
|
4
4
|
import { n as ResponseBuilder, t as Endpoint } from "../Endpoint-ZJhv68qq.mjs";
|
|
5
|
-
import { t as Queue } from "../Queue-
|
|
5
|
+
import { t as Queue } from "../Queue-D6USGS4q.mjs";
|
|
6
6
|
import { Email } from "../email.mjs";
|
|
7
|
-
import { t as KyselyDatabase } from "../kysely-
|
|
7
|
+
import { t as KyselyDatabase } from "../kysely-DN88k0gD.mjs";
|
|
8
8
|
import { c as createObjectHeaders, r as createCookieHeaderAccessor } from "../lazyAccessors-CFanua0O.mjs";
|
|
9
|
-
import { t as Topic } from "../Topic-
|
|
9
|
+
import { t as Topic } from "../Topic-D74b5cgs.mjs";
|
|
10
10
|
import { t as HonoEndpoint } from "../HonoEndpointAdaptor-chgcjPmg.mjs";
|
|
11
11
|
import { n as executeWithAuditTransaction } from "../processAudits-BRRVVHUB.mjs";
|
|
12
12
|
import { provideKey } from "@geekmidas/manifest";
|
package/dist/crons/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_functions = require("../functions-CfzJG9hK.cjs");
|
|
3
3
|
const require_CronBuilder = require("../CronBuilder-C0Ek1-RA.cjs");
|
|
4
|
+
let _opentelemetry_api = require("@opentelemetry/api");
|
|
4
5
|
let _geekmidas_services = require("@geekmidas/services");
|
|
5
6
|
let cron_parser = require("cron-parser");
|
|
6
7
|
//#region src/crons/inProcess.ts
|
|
@@ -60,8 +61,31 @@ function nextAfter(expression, from) {
|
|
|
60
61
|
* would pass locally and fail where it matters. Services come through the
|
|
61
62
|
* discovery, which keeps one instance of each, so running every minute does not
|
|
62
63
|
* reconnect a database every minute.
|
|
64
|
+
*
|
|
65
|
+
* Each run is a trace of its own: a root span, `cron <name>`, whatever was
|
|
66
|
+
* active when the timer or the broker fired it.
|
|
63
67
|
*/
|
|
64
|
-
async function runCron(cron, serviceDiscovery) {
|
|
68
|
+
async function runCron(cron, serviceDiscovery, name) {
|
|
69
|
+
const span = _opentelemetry_api.trace.getTracer("@geekmidas/constructs").startSpan(`cron ${name ?? cron.schedule ?? "manual"}`, {
|
|
70
|
+
kind: _opentelemetry_api.SpanKind.INTERNAL,
|
|
71
|
+
attributes: {
|
|
72
|
+
...name && { "gkm.cron.name": name },
|
|
73
|
+
...cron.schedule && { "gkm.cron.schedule": cron.schedule }
|
|
74
|
+
}
|
|
75
|
+
}, _opentelemetry_api.ROOT_CONTEXT);
|
|
76
|
+
return _opentelemetry_api.context.with(_opentelemetry_api.trace.setSpan(_opentelemetry_api.ROOT_CONTEXT, span), async () => {
|
|
77
|
+
try {
|
|
78
|
+
return await run(cron, serviceDiscovery);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error instanceof Error) span.recordException(error);
|
|
81
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
82
|
+
throw error;
|
|
83
|
+
} finally {
|
|
84
|
+
span.end();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function run(cron, serviceDiscovery) {
|
|
65
89
|
const services = cron.services.length > 0 ? await serviceDiscovery.register(cron.services) : {};
|
|
66
90
|
const db = cron.databaseService ? (await serviceDiscovery.register([cron.databaseService]))[cron.databaseService.serviceName] : void 0;
|
|
67
91
|
let auditor;
|
package/dist/crons/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["CronExpressionParser","runWithRequestContext","publishEvents"],"sources":["../../src/crons/inProcess.ts","../../src/crons/runCron.ts","../../src/crons/schedule.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser';\n\n/**\n * The longest delay a timer takes. Node clamps anything longer to 1ms, so a\n * monthly cron would fire at once and then every millisecond.\n */\nconst MAX_DELAY = 2 ** 31 - 1;\n\n/** A schedule that is running, and the way to stop it. */\nexport interface InProcessSchedule {\n\tstop(): void;\n}\n\n/**\n * Run `fire` on a cron expression, in this process, by timer.\n *\n * For the one process that has no broker to keep a schedule: `gkm dev` on a\n * target whose crons are infrastructure once deployed — an EventBridge rule on\n * AWS. Deployed, a timer is the wrong tool: each replica would fire every job\n * once, and nothing would say so. That is why a server schedules through\n * pg-boss, and why this is never what a deployed process does.\n *\n * In UTC, as the deployed schedule is. The next firing is worked out from the\n * clock after each run rather than read off a sequence, so a run that outlasts\n * its interval skips what it overran instead of firing them back to back.\n */\nexport function scheduleInProcess(\n\texpression: string,\n\tfire: () => Promise<unknown> | unknown,\n): InProcessSchedule {\n\t// Parsed once up front, so a bad expression throws here — to the caller\n\t// scheduling it — rather than inside a timer.\n\tCronExpressionParser.parse(expression, { tz: 'UTC' });\n\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet stopped = false;\n\n\tconst arm = (at: number) => {\n\t\tif (stopped) return;\n\t\tconst delay = at - Date.now();\n\t\ttimer = setTimeout(\n\t\t\tasync () => {\n\t\t\t\tif (Date.now() < at) return arm(at);\n\t\t\t\tawait fire();\n\t\t\t\tarm(nextAfter(expression, new Date()));\n\t\t\t},\n\t\t\tMath.min(Math.max(delay, 0), MAX_DELAY),\n\t\t);\n\t\t// The server keeps the process alive; a schedule alone should not.\n\t\ttimer.unref?.();\n\t};\n\n\tarm(nextAfter(expression, new Date()));\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tclearTimeout(timer);\n\t\t},\n\t};\n}\n\nfunction nextAfter(expression: string, from: Date): number {\n\treturn CronExpressionParser.parse(expression, {\n\t\ttz: 'UTC',\n\t\tcurrentDate: from,\n\t})\n\t\t.next()\n\t\t.getTime();\n}\n","import type { AuditStorage } from '@geekmidas/audit';\nimport {\n\trunWithRequestContext,\n\ttype ServiceDiscovery,\n} from '@geekmidas/services';\nimport { publishEvents } from '../publisher';\nimport type { Cron } from './Cron';\n\n/**\n * Run a cron once, in this process: what an EventBridge rule's invocation does\n * on Lambda, for a server that schedules its own.\n *\n * The same steps as the Lambda adaptor — its services, its worker's database\n * as `db`, an auditor when it declared one, its output parsed, its events\n * published — because a cron that ran differently on a server than deployed\n * would pass locally and fail where it matters. Services come through the\n * discovery, which keeps one instance of each, so running every minute does not\n * reconnect a database every minute.\n */\nexport async function runCron(\n\tcron: Cron<any, any, any, any, any, any>,\n\tserviceDiscovery: ServiceDiscovery<any>,\n): Promise<unknown> {\n\tconst services =\n\t\tcron.services.length > 0\n\t\t\t? await serviceDiscovery.register(cron.services)\n\t\t\t: {};\n\n\tconst db = cron.databaseService\n\t\t? (await serviceDiscovery.register([cron.databaseService]))[\n\t\t\t\tcron.databaseService.serviceName\n\t\t\t]\n\t\t: undefined;\n\n\t// Only imported for a cron that audits: `@geekmidas/audit` is optional.\n\tlet auditor: { flush(): Promise<void> } | undefined;\n\tif (cron.auditorStorageService) {\n\t\tconst { DefaultAuditor } = await import('@geekmidas/audit');\n\t\tconst registered: Record<string, unknown> = await serviceDiscovery.register(\n\t\t\t[cron.auditorStorageService],\n\t\t);\n\t\tconst storage = registered[\n\t\t\tcron.auditorStorageService.serviceName\n\t\t] as AuditStorage;\n\t\tauditor = new DefaultAuditor({\n\t\t\tactor: { id: 'system', type: 'system' },\n\t\t\tstorage,\n\t\t\tmetadata: { function: cron.type },\n\t\t});\n\t}\n\n\tconst logger = cron.logger;\n\tconst requestId = `cron-${Date.now()}`;\n\n\treturn runWithRequestContext(\n\t\t{\n\t\t\tlogger,\n\t\t\trequestId,\n\t\t\tstartTime: Date.now(),\n\t\t\toperation: `cron ${cron.schedule ?? 'manual'}`,\n\t\t},\n\t\tasync () => {\n\t\t\tconst response = await cron.fn({\n\t\t\t\tinput: undefined,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t\tdb,\n\t\t\t\tauditor,\n\t\t\t} as any);\n\t\t\tconst output = await cron.parseOutput(response);\n\n\t\t\tawait auditor?.flush();\n\t\t\t// The cron's output schema is erased by the `any` it is typed with here.\n\t\t\tawait publishEvents(\n\t\t\t\tlogger,\n\t\t\t\tserviceDiscovery,\n\t\t\t\tcron.events,\n\t\t\t\toutput as never,\n\t\t\t);\n\n\t\t\treturn output;\n\t\t},\n\t);\n}\n","import type { ScheduleExpression } from './Cron';\n\n/**\n * A schedule a cron runner can take, as standard five-field cron.\n *\n * `ScheduleExpression` has two spellings. `cron(m h dom mon dow)` is already\n * five-field cron and unwraps; `rate(n unit)` is an interval, and an interval\n * is not a schedule — it says how often, where cron says when.\n *\n * Most intervals still have an exact cron equivalent, because they divide their\n * unit evenly: `rate(5 minutes)` is every fifth minute of every hour, and that\n * is `*/5 * * * *` precisely. `rate(7 hours)` has none. There is no cron field\n * that means \"every seven hours\" — `*/7` in the hour field fires at 0, 7, 14\n * and 21 and then starts over at midnight, so the gap between the last firing\n * of one day and the first of the next is three hours rather than seven.\n *\n * Those are refused rather than rounded. A job that runs at the wrong hour is\n * far harder to notice than one that refused to build, and a schedule quietly\n * adjusted by the framework is a bug nobody goes looking for.\n */\nexport class UnrepresentableSchedule extends Error {\n\tconstructor(\n\t\treadonly schedule: string,\n\t\treason: string,\n\t) {\n\t\tsuper(\n\t\t\t`\\`${schedule}\\` has no exact cron equivalent: ${reason}.\\n\\n` +\n\t\t\t\t'Cron says *when*, not *how often*, so an interval is only exact when ' +\n\t\t\t\t'it divides its unit evenly.\\n' +\n\t\t\t\t'Write the schedule you mean instead — `cron(0 */6 * * *)` for every ' +\n\t\t\t\t'sixth hour, say — or pick an interval that divides: minutes into 60, ' +\n\t\t\t\t'hours into 24.',\n\t\t);\n\t\tthis.name = 'UnrepresentableSchedule';\n\t}\n}\n\n/** Units `rate(…)` accepts, singular or plural. */\nconst UNITS = ['minute', 'hour', 'day'] as const;\ntype Unit = (typeof UNITS)[number];\n\n/**\n * Standard five-field cron for a schedule expression.\n *\n * @throws {UnrepresentableSchedule} when a `rate(…)` has no exact equivalent.\n */\nexport function toCronExpression(schedule: ScheduleExpression): string {\n\tconst cron = /^cron\\((.+)\\)$/.exec(schedule);\n\tif (cron?.[1]) return cron[1].trim();\n\n\tconst rate = /^rate\\(\\s*(\\d+)\\s+(\\w+?)s?\\s*\\)$/.exec(schedule);\n\tif (!rate) {\n\t\tthrow new UnrepresentableSchedule(\n\t\t\tschedule,\n\t\t\t'it is neither `cron(…)` nor `rate(n unit)`',\n\t\t);\n\t}\n\n\tconst every = Number(rate[1]);\n\tconst unit = rate[2] as Unit;\n\t/* c8 ignore next */ if (!unit)\n\t\tthrow new UnrepresentableSchedule(schedule, 'no unit');\n\n\tif (!UNITS.includes(unit)) {\n\t\tthrow new UnrepresentableSchedule(\n\t\t\tschedule,\n\t\t\t`\\`${unit}\\` is not one of ${UNITS.join(', ')}`,\n\t\t);\n\t}\n\n\tif (every < 1) {\n\t\tthrow new UnrepresentableSchedule(\n\t\t\tschedule,\n\t\t\t'an interval must be at least 1',\n\t\t);\n\t}\n\n\tswitch (unit) {\n\t\tcase 'minute':\n\t\t\tif (every === 1) return '* * * * *';\n\t\t\tif (60 % every !== 0) {\n\t\t\t\tthrow new UnrepresentableSchedule(\n\t\t\t\t\tschedule,\n\t\t\t\t\t`${every} does not divide 60, so the interval would reset at the top of each hour`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn `*/${every} * * * *`;\n\n\t\tcase 'hour':\n\t\t\tif (every === 1) return '0 * * * *';\n\t\t\tif (24 % every !== 0) {\n\t\t\t\tthrow new UnrepresentableSchedule(\n\t\t\t\t\tschedule,\n\t\t\t\t\t`${every} does not divide 24, so the interval would reset at midnight`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn `0 */${every} * * *`;\n\n\t\tcase 'day':\n\t\t\tif (every === 1) return '0 0 * * *';\n\t\t\t// `*/n` in day-of-month restarts on the 1st, so a 2-day interval fires\n\t\t\t// on the 1st and 3rd of a 31-day month and then again on the 1st — one\n\t\t\t// day later, not two. No month length makes that come out even.\n\t\t\tthrow new UnrepresentableSchedule(\n\t\t\t\tschedule,\n\t\t\t\t'day-of-month restarts every month, so no multi-day interval is even',\n\t\t\t);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["CronExpressionParser","trace","SpanKind","ROOT_CONTEXT","context","SpanStatusCode","runWithRequestContext","publishEvents"],"sources":["../../src/crons/inProcess.ts","../../src/crons/runCron.ts","../../src/crons/schedule.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser';\n\n/**\n * The longest delay a timer takes. Node clamps anything longer to 1ms, so a\n * monthly cron would fire at once and then every millisecond.\n */\nconst MAX_DELAY = 2 ** 31 - 1;\n\n/** A schedule that is running, and the way to stop it. */\nexport interface InProcessSchedule {\n\tstop(): void;\n}\n\n/**\n * Run `fire` on a cron expression, in this process, by timer.\n *\n * For the one process that has no broker to keep a schedule: `gkm dev` on a\n * target whose crons are infrastructure once deployed — an EventBridge rule on\n * AWS. Deployed, a timer is the wrong tool: each replica would fire every job\n * once, and nothing would say so. That is why a server schedules through\n * pg-boss, and why this is never what a deployed process does.\n *\n * In UTC, as the deployed schedule is. The next firing is worked out from the\n * clock after each run rather than read off a sequence, so a run that outlasts\n * its interval skips what it overran instead of firing them back to back.\n */\nexport function scheduleInProcess(\n\texpression: string,\n\tfire: () => Promise<unknown> | unknown,\n): InProcessSchedule {\n\t// Parsed once up front, so a bad expression throws here — to the caller\n\t// scheduling it — rather than inside a timer.\n\tCronExpressionParser.parse(expression, { tz: 'UTC' });\n\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet stopped = false;\n\n\tconst arm = (at: number) => {\n\t\tif (stopped) return;\n\t\tconst delay = at - Date.now();\n\t\ttimer = setTimeout(\n\t\t\tasync () => {\n\t\t\t\tif (Date.now() < at) return arm(at);\n\t\t\t\tawait fire();\n\t\t\t\tarm(nextAfter(expression, new Date()));\n\t\t\t},\n\t\t\tMath.min(Math.max(delay, 0), MAX_DELAY),\n\t\t);\n\t\t// The server keeps the process alive; a schedule alone should not.\n\t\ttimer.unref?.();\n\t};\n\n\tarm(nextAfter(expression, new Date()));\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tclearTimeout(timer);\n\t\t},\n\t};\n}\n\nfunction nextAfter(expression: string, from: Date): number {\n\treturn CronExpressionParser.parse(expression, {\n\t\ttz: 'UTC',\n\t\tcurrentDate: from,\n\t})\n\t\t.next()\n\t\t.getTime();\n}\n","import type { AuditStorage } from '@geekmidas/audit';\nimport {\n\trunWithRequestContext,\n\ttype ServiceDiscovery,\n} from '@geekmidas/services';\nimport {\n\tcontext,\n\tROOT_CONTEXT,\n\tSpanKind,\n\tSpanStatusCode,\n\ttrace,\n} from '@opentelemetry/api';\nimport { publishEvents } from '../publisher';\nimport type { Cron } from './Cron';\n\n/**\n * Run a cron once, in this process: what an EventBridge rule's invocation does\n * on Lambda, for a server that schedules its own.\n *\n * The same steps as the Lambda adaptor — its services, its worker's database\n * as `db`, an auditor when it declared one, its output parsed, its events\n * published — because a cron that ran differently on a server than deployed\n * would pass locally and fail where it matters. Services come through the\n * discovery, which keeps one instance of each, so running every minute does not\n * reconnect a database every minute.\n *\n * Each run is a trace of its own: a root span, `cron <name>`, whatever was\n * active when the timer or the broker fired it.\n */\nexport async function runCron(\n\tcron: Cron<any, any, any, any, any, any>,\n\tserviceDiscovery: ServiceDiscovery<any>,\n\t/** The cron's export name, for its span. */\n\tname?: string,\n): Promise<unknown> {\n\tconst span = trace.getTracer('@geekmidas/constructs').startSpan(\n\t\t`cron ${name ?? cron.schedule ?? 'manual'}`,\n\t\t{\n\t\t\tkind: SpanKind.INTERNAL,\n\t\t\tattributes: {\n\t\t\t\t...(name && { 'gkm.cron.name': name }),\n\t\t\t\t...(cron.schedule && { 'gkm.cron.schedule': cron.schedule }),\n\t\t\t},\n\t\t},\n\t\tROOT_CONTEXT,\n\t);\n\n\treturn context.with(trace.setSpan(ROOT_CONTEXT, span), async () => {\n\t\ttry {\n\t\t\treturn await run(cron, serviceDiscovery);\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) span.recordException(error);\n\t\t\tspan.setStatus({ code: SpanStatusCode.ERROR });\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tspan.end();\n\t\t}\n\t});\n}\n\nasync function run(\n\tcron: Cron<any, any, any, any, any, any>,\n\tserviceDiscovery: ServiceDiscovery<any>,\n): Promise<unknown> {\n\tconst services =\n\t\tcron.services.length > 0\n\t\t\t? await serviceDiscovery.register(cron.services)\n\t\t\t: {};\n\n\tconst db = cron.databaseService\n\t\t? (await serviceDiscovery.register([cron.databaseService]))[\n\t\t\t\tcron.databaseService.serviceName\n\t\t\t]\n\t\t: undefined;\n\n\t// Only imported for a cron that audits: `@geekmidas/audit` is optional.\n\tlet auditor: { flush(): Promise<void> } | undefined;\n\tif (cron.auditorStorageService) {\n\t\tconst { DefaultAuditor } = await import('@geekmidas/audit');\n\t\tconst registered: Record<string, unknown> = await serviceDiscovery.register(\n\t\t\t[cron.auditorStorageService],\n\t\t);\n\t\tconst storage = registered[\n\t\t\tcron.auditorStorageService.serviceName\n\t\t] as AuditStorage;\n\t\tauditor = new DefaultAuditor({\n\t\t\tactor: { id: 'system', type: 'system' },\n\t\t\tstorage,\n\t\t\tmetadata: { function: cron.type },\n\t\t});\n\t}\n\n\tconst logger = cron.logger;\n\tconst requestId = `cron-${Date.now()}`;\n\n\treturn runWithRequestContext(\n\t\t{\n\t\t\tlogger,\n\t\t\trequestId,\n\t\t\tstartTime: Date.now(),\n\t\t\toperation: `cron ${cron.schedule ?? 'manual'}`,\n\t\t},\n\t\tasync () => {\n\t\t\tconst response = await cron.fn({\n\t\t\t\tinput: undefined,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t\tdb,\n\t\t\t\tauditor,\n\t\t\t} as any);\n\t\t\tconst output = await cron.parseOutput(response);\n\n\t\t\tawait auditor?.flush();\n\t\t\t// The cron's output schema is erased by the `any` it is typed with here.\n\t\t\tawait publishEvents(\n\t\t\t\tlogger,\n\t\t\t\tserviceDiscovery,\n\t\t\t\tcron.events,\n\t\t\t\toutput as never,\n\t\t\t);\n\n\t\t\treturn output;\n\t\t},\n\t);\n}\n","import type { ScheduleExpression } from './Cron';\n\n/**\n * A schedule a cron runner can take, as standard five-field cron.\n *\n * `ScheduleExpression` has two spellings. `cron(m h dom mon dow)` is already\n * five-field cron and unwraps; `rate(n unit)` is an interval, and an interval\n * is not a schedule — it says how often, where cron says when.\n *\n * Most intervals still have an exact cron equivalent, because they divide their\n * unit evenly: `rate(5 minutes)` is every fifth minute of every hour, and that\n * is `*/5 * * * *` precisely. `rate(7 hours)` has none. There is no cron field\n * that means \"every seven hours\" — `*/7` in the hour field fires at 0, 7, 14\n * and 21 and then starts over at midnight, so the gap between the last firing\n * of one day and the first of the next is three hours rather than seven.\n *\n * Those are refused rather than rounded. A job that runs at the wrong hour is\n * far harder to notice than one that refused to build, and a schedule quietly\n * adjusted by the framework is a bug nobody goes looking for.\n */\nexport class UnrepresentableSchedule extends Error {\n\tconstructor(\n\t\treadonly schedule: string,\n\t\treason: string,\n\t) {\n\t\tsuper(\n\t\t\t`\\`${schedule}\\` has no exact cron equivalent: ${reason}.\\n\\n` +\n\t\t\t\t'Cron says *when*, not *how often*, so an interval is only exact when ' +\n\t\t\t\t'it divides its unit evenly.\\n' +\n\t\t\t\t'Write the schedule you mean instead — `cron(0 */6 * * *)` for every ' +\n\t\t\t\t'sixth hour, say — or pick an interval that divides: minutes into 60, ' +\n\t\t\t\t'hours into 24.',\n\t\t);\n\t\tthis.name = 'UnrepresentableSchedule';\n\t}\n}\n\n/** Units `rate(…)` accepts, singular or plural. */\nconst UNITS = ['minute', 'hour', 'day'] as const;\ntype Unit = (typeof UNITS)[number];\n\n/**\n * Standard five-field cron for a schedule expression.\n *\n * @throws {UnrepresentableSchedule} when a `rate(…)` has no exact equivalent.\n */\nexport function toCronExpression(schedule: ScheduleExpression): string {\n\tconst cron = /^cron\\((.+)\\)$/.exec(schedule);\n\tif (cron?.[1]) return cron[1].trim();\n\n\tconst rate = /^rate\\(\\s*(\\d+)\\s+(\\w+?)s?\\s*\\)$/.exec(schedule);\n\tif (!rate) {\n\t\tthrow new UnrepresentableSchedule(\n\t\t\tschedule,\n\t\t\t'it is neither `cron(…)` nor `rate(n unit)`',\n\t\t);\n\t}\n\n\tconst every = Number(rate[1]);\n\tconst unit = rate[2] as Unit;\n\t/* c8 ignore next */ if (!unit)\n\t\tthrow new UnrepresentableSchedule(schedule, 'no unit');\n\n\tif (!UNITS.includes(unit)) {\n\t\tthrow new UnrepresentableSchedule(\n\t\t\tschedule,\n\t\t\t`\\`${unit}\\` is not one of ${UNITS.join(', ')}`,\n\t\t);\n\t}\n\n\tif (every < 1) {\n\t\tthrow new UnrepresentableSchedule(\n\t\t\tschedule,\n\t\t\t'an interval must be at least 1',\n\t\t);\n\t}\n\n\tswitch (unit) {\n\t\tcase 'minute':\n\t\t\tif (every === 1) return '* * * * *';\n\t\t\tif (60 % every !== 0) {\n\t\t\t\tthrow new UnrepresentableSchedule(\n\t\t\t\t\tschedule,\n\t\t\t\t\t`${every} does not divide 60, so the interval would reset at the top of each hour`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn `*/${every} * * * *`;\n\n\t\tcase 'hour':\n\t\t\tif (every === 1) return '0 * * * *';\n\t\t\tif (24 % every !== 0) {\n\t\t\t\tthrow new UnrepresentableSchedule(\n\t\t\t\t\tschedule,\n\t\t\t\t\t`${every} does not divide 24, so the interval would reset at midnight`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn `0 */${every} * * *`;\n\n\t\tcase 'day':\n\t\t\tif (every === 1) return '0 0 * * *';\n\t\t\t// `*/n` in day-of-month restarts on the 1st, so a 2-day interval fires\n\t\t\t// on the 1st and 3rd of a 31-day month and then again on the 1st — one\n\t\t\t// day later, not two. No month length makes that come out even.\n\t\t\tthrow new UnrepresentableSchedule(\n\t\t\t\tschedule,\n\t\t\t\t'day-of-month restarts every month, so no multi-day interval is even',\n\t\t\t);\n\t}\n}\n"],"mappings":"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"}
|
package/dist/crons/index.d.cts
CHANGED
|
@@ -32,8 +32,13 @@ export declare function scheduleInProcess(expression: string, fire: () => Promis
|
|
|
32
32
|
* would pass locally and fail where it matters. Services come through the
|
|
33
33
|
* discovery, which keeps one instance of each, so running every minute does not
|
|
34
34
|
* reconnect a database every minute.
|
|
35
|
+
*
|
|
36
|
+
* Each run is a trace of its own: a root span, `cron <name>`, whatever was
|
|
37
|
+
* active when the timer or the broker fired it.
|
|
35
38
|
*/
|
|
36
|
-
export declare function runCron(cron: Cron<any, any, any, any, any, any>, serviceDiscovery: ServiceDiscovery<any
|
|
39
|
+
export declare function runCron(cron: Cron<any, any, any, any, any, any>, serviceDiscovery: ServiceDiscovery<any>,
|
|
40
|
+
/** The cron's export name, for its span. */
|
|
41
|
+
name?: string): Promise<unknown>;
|
|
37
42
|
//#endregion
|
|
38
43
|
//#region src/crons/schedule.d.ts
|
|
39
44
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/crons/inProcess.ts","../../src/crons/runCron.ts","../../src/crons/schedule.ts"],"mappings":";;;;;UASiB;EAChB;;;;;;;;;;;;;;;wBAgBe,kBACf,oBACA,YAAY,6BACV
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/crons/inProcess.ts","../../src/crons/runCron.ts","../../src/crons/schedule.ts"],"mappings":";;;;;UASiB;EAChB;;;;;;;;;;;;;;;wBAgBe,kBACf,oBACA,YAAY,6BACV;;;;;;;;;;;;;;;;;wBCAmB,QACrB,MAAM,oCACN,kBAAkB,uBAElB;;AAAA,gBACE;;;;;;;;;;;;;;;;;;;;;qBCdU,gCAAgC;WAElC;EADV,YACU,kBACT;;;;;;;wBAuBc,iBAAiB,UAAU"}
|
package/dist/crons/index.d.mts
CHANGED
|
@@ -32,8 +32,13 @@ export declare function scheduleInProcess(expression: string, fire: () => Promis
|
|
|
32
32
|
* would pass locally and fail where it matters. Services come through the
|
|
33
33
|
* discovery, which keeps one instance of each, so running every minute does not
|
|
34
34
|
* reconnect a database every minute.
|
|
35
|
+
*
|
|
36
|
+
* Each run is a trace of its own: a root span, `cron <name>`, whatever was
|
|
37
|
+
* active when the timer or the broker fired it.
|
|
35
38
|
*/
|
|
36
|
-
export declare function runCron(cron: Cron<any, any, any, any, any, any>, serviceDiscovery: ServiceDiscovery<any
|
|
39
|
+
export declare function runCron(cron: Cron<any, any, any, any, any, any>, serviceDiscovery: ServiceDiscovery<any>,
|
|
40
|
+
/** The cron's export name, for its span. */
|
|
41
|
+
name?: string): Promise<unknown>;
|
|
37
42
|
//#endregion
|
|
38
43
|
//#region src/crons/schedule.d.ts
|
|
39
44
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/crons/inProcess.ts","../../src/crons/runCron.ts","../../src/crons/schedule.ts"],"mappings":";;;;;UASiB;EAChB;;;;;;;;;;;;;;;wBAgBe,kBACf,oBACA,YAAY,6BACV
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/crons/inProcess.ts","../../src/crons/runCron.ts","../../src/crons/schedule.ts"],"mappings":";;;;;UASiB;EAChB;;;;;;;;;;;;;;;wBAgBe,kBACf,oBACA,YAAY,6BACV;;;;;;;;;;;;;;;;;wBCAmB,QACrB,MAAM,oCACN,kBAAkB,uBAElB;;AAAA,gBACE;;;;;;;;;;;;;;;;;;;;;qBCdU,gCAAgC;WAElC;EADV,YACU,kBACT;;;;;;;wBAuBc,iBAAiB,UAAU"}
|
package/dist/crons/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { a as publishEvents } from "../functions-BFizikJU.mjs";
|
|
2
2
|
import { n as Cron, t as CronBuilder } from "../CronBuilder-Dmix_Pen.mjs";
|
|
3
|
+
import { ROOT_CONTEXT, SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
3
4
|
import { runWithRequestContext } from "@geekmidas/services";
|
|
4
5
|
import { CronExpressionParser } from "cron-parser";
|
|
5
6
|
//#region src/crons/inProcess.ts
|
|
@@ -59,8 +60,31 @@ function nextAfter(expression, from) {
|
|
|
59
60
|
* would pass locally and fail where it matters. Services come through the
|
|
60
61
|
* discovery, which keeps one instance of each, so running every minute does not
|
|
61
62
|
* reconnect a database every minute.
|
|
63
|
+
*
|
|
64
|
+
* Each run is a trace of its own: a root span, `cron <name>`, whatever was
|
|
65
|
+
* active when the timer or the broker fired it.
|
|
62
66
|
*/
|
|
63
|
-
async function runCron(cron, serviceDiscovery) {
|
|
67
|
+
async function runCron(cron, serviceDiscovery, name) {
|
|
68
|
+
const span = trace.getTracer("@geekmidas/constructs").startSpan(`cron ${name ?? cron.schedule ?? "manual"}`, {
|
|
69
|
+
kind: SpanKind.INTERNAL,
|
|
70
|
+
attributes: {
|
|
71
|
+
...name && { "gkm.cron.name": name },
|
|
72
|
+
...cron.schedule && { "gkm.cron.schedule": cron.schedule }
|
|
73
|
+
}
|
|
74
|
+
}, ROOT_CONTEXT);
|
|
75
|
+
return context.with(trace.setSpan(ROOT_CONTEXT, span), async () => {
|
|
76
|
+
try {
|
|
77
|
+
return await run(cron, serviceDiscovery);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof Error) span.recordException(error);
|
|
80
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
81
|
+
throw error;
|
|
82
|
+
} finally {
|
|
83
|
+
span.end();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async function run(cron, serviceDiscovery) {
|
|
64
88
|
const services = cron.services.length > 0 ? await serviceDiscovery.register(cron.services) : {};
|
|
65
89
|
const db = cron.databaseService ? (await serviceDiscovery.register([cron.databaseService]))[cron.databaseService.serviceName] : void 0;
|
|
66
90
|
let auditor;
|