effortless-aws 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/handlers/define-http.ts","../src/handlers/define-table.ts","../src/handlers/define-app.ts","../src/handlers/define-static-site.ts","../src/handlers/param.ts","../src/runtime/platform-client.ts","../src/runtime/platform-types.ts"],"sourcesContent":["/**\n * Configuration for an Effortless project.\n *\n * @example\n * ```typescript\n * // effortless.config.ts\n * import { defineConfig } from \"effortless-aws\";\n *\n * export default defineConfig({\n * name: \"my-service\",\n * region: \"eu-central-1\",\n * handlers: \"src\",\n * });\n * ```\n */\nexport type EffortlessConfig = {\n /**\n * Project name used for resource naming and tagging.\n * This becomes part of Lambda function names, IAM roles, etc.\n */\n name: string;\n\n /**\n * Default AWS region for all handlers.\n * Can be overridden per-handler or via CLI `--region` flag.\n * @default \"eu-central-1\"\n */\n region?: string;\n\n /**\n * Deployment stage (e.g., \"dev\", \"staging\", \"prod\").\n * Used for resource isolation and tagging.\n * @default \"dev\"\n */\n stage?: string;\n\n /**\n * Glob patterns or directory paths to scan for handlers.\n * Used by `eff deploy` (without file argument) to auto-discover handlers.\n *\n * @example\n * ```typescript\n * // Single directory - scans for all .ts files\n * handlers: \"src\"\n *\n * // Glob patterns\n * handlers: [\"src/**\\/*.ts\", \"lib/**\\/*.handler.ts\"]\n * ```\n */\n handlers?: string | string[];\n\n /**\n * Default settings applied to all handlers unless overridden.\n */\n defaults?: {\n /**\n * Lambda memory in MB.\n * @default 256\n */\n memory?: number;\n\n /**\n * Lambda timeout as a human-readable string.\n * @example \"30 seconds\", \"5 minutes\"\n */\n timeout?: string;\n\n /**\n * Lambda runtime.\n * @default \"nodejs22.x\"\n */\n runtime?: string;\n };\n};\n\n/**\n * Helper function for type-safe configuration.\n * Returns the config object as-is, but provides TypeScript autocompletion.\n *\n * @example\n * ```typescript\n * import { defineConfig } from \"effortless-aws\";\n *\n * export default defineConfig({\n * name: \"my-service\",\n * region: \"eu-central-1\",\n * handlers: \"src\",\n * });\n * ```\n */\nexport const defineConfig = (config: EffortlessConfig): EffortlessConfig => config;\n","import type { Permission } from \"./permissions\";\nimport type { TableHandler } from \"./define-table\";\nimport type { TableClient } from \"../runtime/table-client\";\nimport type { AnyParamRef, ResolveParams } from \"./param\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyTableHandler = TableHandler<any, any, any, any, any, any>;\n\n/** Maps a deps declaration to resolved runtime client types */\nexport type ResolveDeps<D> = {\n [K in keyof D]: D[K] extends TableHandler<infer T, any, any, any, any> ? TableClient<T> : never;\n};\n\n/** HTTP methods supported by API Gateway */\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/** Short content-type aliases for common response formats */\nexport type ContentType = \"json\" | \"html\" | \"text\" | \"css\" | \"js\" | \"xml\" | \"csv\" | \"svg\";\n\n/**\n * Incoming HTTP request object passed to the handler\n */\nexport type HttpRequest = {\n /** HTTP method (GET, POST, etc.) */\n method: string;\n /** Request path (e.g., \"/users/123\") */\n path: string;\n /** Request headers */\n headers: Record<string, string | undefined>;\n /** Query string parameters */\n query: Record<string, string | undefined>;\n /** Path parameters extracted from route (e.g., {id} -> params.id) */\n params: Record<string, string | undefined>;\n /** Parsed request body (JSON parsed if Content-Type is application/json) */\n body: unknown;\n /** Raw unparsed request body */\n rawBody?: string;\n};\n\n/**\n * HTTP response returned from the handler\n */\nexport type HttpResponse = {\n /** HTTP status code (e.g., 200, 404, 500) */\n status: number;\n /** Response body — JSON-serialized by default, or sent as string when contentType is set */\n body?: unknown;\n /**\n * Short content-type alias. Resolves to full MIME type automatically:\n * - `\"json\"` → `application/json` (default if omitted)\n * - `\"html\"` → `text/html; charset=utf-8`\n * - `\"text\"` → `text/plain; charset=utf-8`\n * - `\"css\"` → `text/css; charset=utf-8`\n * - `\"js\"` → `application/javascript; charset=utf-8`\n * - `\"xml\"` → `application/xml; charset=utf-8`\n * - `\"csv\"` → `text/csv; charset=utf-8`\n * - `\"svg\"` → `image/svg+xml; charset=utf-8`\n */\n contentType?: ContentType;\n /** Response headers (use for custom content-types not covered by contentType) */\n headers?: Record<string, string>;\n};\n\n/**\n * Configuration options extracted from DefineHttpOptions (without onRequest callback)\n */\nexport type HttpConfig = {\n /** Handler name. Defaults to export name if not specified */\n name?: string;\n /** HTTP method for the route */\n method: HttpMethod;\n /** Route path (e.g., \"/api/users\", \"/api/users/{id}\") */\n path: string;\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout in seconds (default: 30) */\n timeout?: number;\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n /** Enable observability logging to platform table (default: true) */\n observe?: boolean;\n};\n\n/**\n * Handler function type for HTTP endpoints\n *\n * @typeParam T - Type of the validated request body (from schema function)\n * @typeParam C - Type of the context/dependencies (from context function)\n * @typeParam D - Type of the deps (from deps declaration)\n * @typeParam P - Type of the params (from params declaration)\n */\nexport type HttpHandlerFn<T = undefined, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { req: HttpRequest }\n & ([T] extends [undefined] ? {} : { data: T })\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<HttpResponse>;\n\n/**\n * Context factory type — conditional on whether params are declared.\n * Without params: `() => C | Promise<C>`\n * With params: `(args: { params: ResolveParams<P> }) => C | Promise<C>`\n */\ntype ContextFactory<C, P> = [P] extends [undefined]\n ? () => C | Promise<C>\n : (args: { params: ResolveParams<P & {}> }) => C | Promise<C>;\n\n/**\n * Options for defining an HTTP endpoint\n *\n * @typeParam T - Type of the validated request body (inferred from schema function)\n * @typeParam C - Type of the context/dependencies returned by context function\n * @typeParam D - Type of the deps (from deps declaration)\n * @typeParam P - Type of the params (from params declaration)\n */\nexport type DefineHttpOptions<\n T = undefined,\n C = undefined,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n> = HttpConfig & {\n /**\n * Decode/validate function for the request body.\n * Called with the parsed body; should return typed data or throw on validation failure.\n * When provided, the handler receives validated `data` and invalid requests get a 400 response.\n *\n * Works with any validation library:\n * - Effect: `S.decodeUnknownSync(MySchema)`\n * - Zod: `(body) => myZodSchema.parse(body)`\n */\n schema?: (input: unknown) => T;\n /**\n * Error handler called when schema validation or onRequest throws.\n * Receives the error and request, returns an HttpResponse.\n * If not provided, defaults to 400 for validation errors and 500 for handler errors.\n */\n onError?: (error: unknown, req: HttpRequest) => HttpResponse;\n /**\n * Factory function to create context/dependencies for the request handler.\n * Called once on cold start, result is cached and reused across invocations.\n * When params are declared, receives resolved params as argument.\n * Supports both sync and async return values.\n */\n context?: ContextFactory<C, P>;\n /**\n * Dependencies on other handlers (tables, queues, etc.).\n * Typed clients are injected into the handler via the `deps` argument.\n */\n deps?: D;\n /**\n * SSM Parameter Store parameters.\n * Declare with `param()` helper. Values are fetched and cached at cold start.\n * Typed values are injected into the handler via the `params` argument.\n */\n params?: P;\n /**\n * Static file glob patterns to bundle into the Lambda ZIP.\n * Files are accessible at runtime via the `readStatic` callback argument.\n */\n static?: S;\n /** HTTP request handler function */\n onRequest: HttpHandlerFn<T, C, D, P, S>;\n};\n\n/**\n * Internal handler object created by defineHttp\n * @internal\n */\nexport type HttpHandler<T = undefined, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = {\n readonly __brand: \"effortless-http\";\n readonly config: HttpConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (error: unknown, req: HttpRequest) => HttpResponse;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly context?: (...args: any[]) => C | Promise<C>;\n readonly deps?: D;\n readonly params?: P;\n readonly static?: string[];\n readonly onRequest: HttpHandlerFn<T, C, D, P, S>;\n};\n\n/**\n * Define an HTTP endpoint that creates an API Gateway route + Lambda function\n *\n * @typeParam T - Type of the validated request body (inferred from schema function)\n * @typeParam C - Type of the context/dependencies (inferred from context function)\n * @typeParam D - Type of the deps (from deps declaration)\n * @typeParam P - Type of the params (from params declaration)\n * @param options - Configuration, optional schema, optional context factory, and request handler\n * @returns Handler object used by the deployment system\n *\n * @example Basic GET endpoint\n * ```typescript\n * export const hello = defineHttp({\n * method: \"GET\",\n * path: \"/hello\",\n * onRequest: async ({ req }) => ({\n * status: 200,\n * body: { message: \"Hello World!\" }\n * })\n * });\n * ```\n *\n * @example With SSM parameters\n * ```typescript\n * import { param } from \"effortless-aws\";\n *\n * export const api = defineHttp({\n * method: \"GET\",\n * path: \"/orders\",\n * params: {\n * dbUrl: param(\"database-url\"),\n * },\n * context: async ({ params }) => ({\n * pool: createPool(params.dbUrl),\n * }),\n * onRequest: async ({ req, ctx, params }) => ({\n * status: 200,\n * body: { dbUrl: params.dbUrl }\n * })\n * });\n * ```\n */\nexport const defineHttp = <\n T = undefined,\n C = undefined,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n>(\n options: DefineHttpOptions<T, C, D, P, S>\n): HttpHandler<T, C, D, P, S> => {\n const { onRequest, onError, context, schema, deps, params, static: staticFiles, ...config } = options;\n return {\n __brand: \"effortless-http\",\n config,\n ...(schema ? { schema } : {}),\n ...(onError ? { onError } : {}),\n ...(context ? { context } : {}),\n ...(deps ? { deps } : {}),\n ...(params ? { params } : {}),\n ...(staticFiles ? { static: staticFiles } : {}),\n onRequest\n } as HttpHandler<T, C, D, P, S>;\n};\n","import type { Permission } from \"./permissions\";\nimport type { TableClient } from \"../runtime/table-client\";\nimport type { AnyParamRef, ResolveParams } from \"./param\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyTableHandler = TableHandler<any, any, any, any, any, any>;\n\n/** Maps a deps declaration to resolved runtime client types */\ntype ResolveDeps<D> = {\n [K in keyof D]: D[K] extends TableHandler<infer T, any, any, any, any> ? TableClient<T> : never;\n};\n\n/** DynamoDB attribute types for keys */\nexport type KeyType = \"string\" | \"number\" | \"binary\";\n\n/**\n * DynamoDB table key definition\n */\nexport type TableKey = {\n /** Attribute name */\n name: string;\n /** Attribute type */\n type: KeyType;\n};\n\n/** DynamoDB Streams view type - determines what data is captured in stream records */\nexport type StreamView = \"NEW_AND_OLD_IMAGES\" | \"NEW_IMAGE\" | \"OLD_IMAGE\" | \"KEYS_ONLY\";\n\n/**\n * Configuration options extracted from DefineTableOptions (without onRecord/context)\n */\nexport type TableConfig = {\n /** Table/handler name. Defaults to export name if not specified */\n name?: string;\n /** Partition key definition */\n pk: TableKey;\n /** Sort key definition (optional) */\n sk?: TableKey;\n /** DynamoDB billing mode (default: \"PAY_PER_REQUEST\") */\n billingMode?: \"PAY_PER_REQUEST\" | \"PROVISIONED\";\n /** TTL attribute name for automatic item expiration */\n ttlAttribute?: string;\n /** Stream view type - what data to include in stream records (default: \"NEW_AND_OLD_IMAGES\") */\n streamView?: StreamView;\n /** Number of records to process in each Lambda invocation (1-10000, default: 100) */\n batchSize?: number;\n /** Maximum time in seconds to gather records before invoking (0-300, default: 2) */\n batchWindow?: number;\n /** Where to start reading the stream (default: \"LATEST\") */\n startingPosition?: \"LATEST\" | \"TRIM_HORIZON\";\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout in seconds (default: 30) */\n timeout?: number;\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n /** Enable observability logging to platform table (default: true) */\n observe?: boolean;\n};\n\n/**\n * DynamoDB stream record passed to onRecord callback\n *\n * @typeParam T - Type of the table items (new/old values)\n */\nexport type TableRecord<T = Record<string, unknown>> = {\n /** Type of modification: INSERT, MODIFY, or REMOVE */\n eventName: \"INSERT\" | \"MODIFY\" | \"REMOVE\";\n /** New item value (present for INSERT and MODIFY) */\n new?: T;\n /** Old item value (present for MODIFY and REMOVE) */\n old?: T;\n /** Primary key of the affected item */\n keys: Record<string, unknown>;\n /** Sequence number for ordering */\n sequenceNumber?: string;\n /** Approximate timestamp when the modification occurred */\n timestamp?: number;\n};\n\n/**\n * Information about a failed record during batch processing\n *\n * @typeParam T - Type of the table items\n */\nexport type FailedRecord<T = Record<string, unknown>> = {\n /** The record that failed to process */\n record: TableRecord<T>;\n /** The error that occurred */\n error: unknown;\n};\n\n/**\n * Context factory type — conditional on whether params are declared.\n * Without params: `() => C | Promise<C>`\n * With params: `(args: { params: ResolveParams<P> }) => C | Promise<C>`\n */\ntype ContextFactory<C, P> = [P] extends [undefined]\n ? () => C | Promise<C>\n : (args: { params: ResolveParams<P & {}> }) => C | Promise<C>;\n\n/**\n * Callback function type for processing a single DynamoDB stream record\n */\nexport type TableRecordFn<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { record: TableRecord<T>; table: TableClient<T> }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<R>;\n\n/**\n * Callback function type for processing accumulated batch results\n */\nexport type TableBatchCompleteFn<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { results: R[]; failures: FailedRecord<T>[]; table: TableClient<T> }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<void>;\n\n/**\n * Callback function type for processing all records in a batch at once\n */\nexport type TableBatchFn<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { records: TableRecord<T>[]; table: TableClient<T> }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<void>;\n\n/** Base options shared by all defineTable variants */\ntype DefineTableBase<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = TableConfig & {\n /**\n * Decode/validate function for stream record items (new/old images).\n * Called with the unmarshalled DynamoDB item; should return typed data or throw on validation failure.\n * When provided, T is inferred from the return type — no need to specify generic parameters.\n */\n schema?: (input: unknown) => T;\n /**\n * Error handler called when onRecord, onBatch, or onBatchComplete throws.\n * Receives the error. If not provided, defaults to `console.error`.\n */\n onError?: (error: unknown) => void;\n /**\n * Factory function to create context/dependencies for callbacks.\n * Called once on cold start, result is cached and reused across invocations.\n * When params are declared, receives resolved params as argument.\n * Supports both sync and async return values.\n */\n context?: ContextFactory<C, P>;\n /**\n * Dependencies on other handlers (tables, queues, etc.).\n * Typed clients are injected into the handler via the `deps` argument.\n */\n deps?: D;\n /**\n * SSM Parameter Store parameters.\n * Declare with `param()` helper. Values are fetched and cached at cold start.\n * Typed values are injected into the handler via the `params` argument.\n */\n params?: P;\n /**\n * Static file glob patterns to bundle into the Lambda ZIP.\n * Files are accessible at runtime via the `readStatic` callback argument.\n */\n static?: S;\n};\n\n/** Per-record processing: onRecord with optional onBatchComplete */\ntype DefineTableWithOnRecord<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {\n onRecord: TableRecordFn<T, C, R, D, P, S>;\n onBatchComplete?: TableBatchCompleteFn<T, C, R, D, P, S>;\n onBatch?: never;\n};\n\n/** Batch processing: onBatch processes all records at once */\ntype DefineTableWithOnBatch<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {\n onBatch: TableBatchFn<T, C, D, P, S>;\n onRecord?: never;\n onBatchComplete?: never;\n};\n\n/** Resource-only: no handler, just creates the table */\ntype DefineTableResourceOnly<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {\n onRecord?: never;\n onBatch?: never;\n onBatchComplete?: never;\n};\n\nexport type DefineTableOptions<\n T = Record<string, unknown>,\n C = undefined,\n R = void,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n> =\n | DefineTableWithOnRecord<T, C, R, D, P, S>\n | DefineTableWithOnBatch<T, C, D, P, S>\n | DefineTableResourceOnly<T, C, D, P, S>;\n\n/**\n * Internal handler object created by defineTable\n * @internal\n */\nexport type TableHandler<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> = {\n readonly __brand: \"effortless-table\";\n readonly config: TableConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (error: unknown) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly context?: (...args: any[]) => C | Promise<C>;\n readonly deps?: D;\n readonly params?: P;\n readonly static?: string[];\n readonly onRecord?: TableRecordFn<T, C, R, D, P, S>;\n readonly onBatchComplete?: TableBatchCompleteFn<T, C, R, D, P, S>;\n readonly onBatch?: TableBatchFn<T, C, D, P, S>;\n};\n\n/**\n * Define a DynamoDB table with optional stream handler\n *\n * Creates:\n * - DynamoDB table with specified key schema\n * - (If onRecord or onBatch provided) DynamoDB Stream + Lambda + Event Source Mapping\n *\n * @example Table with stream handler (typed)\n * ```typescript\n * type Order = { id: string; amount: number; status: string };\n *\n * export const orders = defineTable<Order>({\n * pk: { name: \"id\", type: \"string\" },\n * streamView: \"NEW_AND_OLD_IMAGES\",\n * batchSize: 10,\n * onRecord: async ({ record }) => {\n * if (record.eventName === \"INSERT\") {\n * console.log(\"New order:\", record.new?.amount);\n * }\n * }\n * });\n * ```\n *\n * @example Table only (no Lambda)\n * ```typescript\n * export const users = defineTable({\n * pk: { name: \"id\", type: \"string\" },\n * sk: { name: \"email\", type: \"string\" }\n * });\n * ```\n */\nexport const defineTable = <\n T = Record<string, unknown>,\n C = undefined,\n R = void,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n>(\n options: DefineTableOptions<T, C, R, D, P, S>\n): TableHandler<T, C, R, D, P, S> => {\n const { onRecord, onBatchComplete, onBatch, onError, schema, context, deps, params, static: staticFiles, ...config } = options;\n return {\n __brand: \"effortless-table\",\n config,\n ...(schema ? { schema } : {}),\n ...(onError ? { onError } : {}),\n ...(context ? { context } : {}),\n ...(deps ? { deps } : {}),\n ...(params ? { params } : {}),\n ...(staticFiles ? { static: staticFiles } : {}),\n ...(onRecord ? { onRecord } : {}),\n ...(onBatchComplete ? { onBatchComplete } : {}),\n ...(onBatch ? { onBatch } : {})\n } as TableHandler<T, C, R, D, P, S>;\n};\n","/**\n * Configuration for a Lambda-served static site (API Gateway + Lambda)\n */\nexport type AppConfig = {\n /** Handler name. Defaults to export name if not specified */\n name?: string;\n /** Base URL path the site is served under (e.g., \"/app\") */\n path?: string;\n /** Directory containing the static site files, relative to project root */\n dir: string;\n /** Default file for directory requests (default: \"index.html\") */\n index?: string;\n /** SPA mode: serve index.html for all paths that don't match a file (default: false) */\n spa?: boolean;\n /** Shell command to run before deploy to generate site content (e.g., \"npx astro build\") */\n build?: string;\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout in seconds (default: 5) */\n timeout?: number;\n /** Enable observability logging to platform table (default: false) */\n observe?: boolean;\n};\n\n/**\n * Internal handler object created by defineApp\n * @internal\n */\nexport type AppHandler = {\n readonly __brand: \"effortless-app\";\n readonly config: AppConfig;\n};\n\n/**\n * Deploy a static site via Lambda + API Gateway.\n * Files are bundled into the Lambda ZIP and served with auto-detected content types.\n *\n * For CDN-backed sites (S3 + CloudFront), use {@link defineStaticSite} instead.\n *\n * @param options - Site configuration: path, directory, optional SPA mode\n * @returns Handler object used by the deployment system\n *\n * @example Basic static site\n * ```typescript\n * export const app = defineApp({\n * path: \"/app\",\n * dir: \"src/webapp\",\n * });\n * ```\n */\nexport const defineApp = (options: AppConfig): AppHandler => ({\n __brand: \"effortless-app\",\n config: options,\n});\n","/**\n * Configuration for a static site handler (S3 + CloudFront)\n */\nexport type StaticSiteConfig = {\n /** Handler name. Defaults to export name if not specified */\n name?: string;\n /** Directory containing the static site files, relative to project root */\n dir: string;\n /** Default file for directory requests (default: \"index.html\") */\n index?: string;\n /** SPA mode: serve index.html for all paths that don't match a file (default: false) */\n spa?: boolean;\n /** Shell command to run before deploy to generate site content (e.g., \"npx astro build\") */\n build?: string;\n};\n\n/**\n * Internal handler object created by defineStaticSite\n * @internal\n */\nexport type StaticSiteHandler = {\n readonly __brand: \"effortless-static-site\";\n readonly config: StaticSiteConfig;\n};\n\n/**\n * Deploy a static site via S3 + CloudFront CDN.\n *\n * @param options - Static site configuration: directory, optional SPA mode, build command\n * @returns Handler object used by the deployment system\n *\n * @example Documentation site\n * ```typescript\n * export const docs = defineStaticSite({\n * dir: \"dist\",\n * build: \"npx astro build\",\n * });\n * ```\n *\n * @example SPA with client-side routing\n * ```typescript\n * export const app = defineStaticSite({\n * dir: \"dist\",\n * spa: true,\n * build: \"npm run build\",\n * });\n * ```\n */\nexport const defineStaticSite = (options: StaticSiteConfig): StaticSiteHandler => ({\n __brand: \"effortless-static-site\",\n config: options,\n});\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyParamRef = ParamRef<any>;\n\n/**\n * Reference to an SSM Parameter Store parameter.\n *\n * @typeParam T - The resolved type after optional transform (default: string)\n */\nexport type ParamRef<T = string> = {\n readonly __brand: \"effortless-param\";\n readonly key: string;\n readonly transform?: (raw: string) => T;\n};\n\n/**\n * Maps a params declaration to resolved value types.\n *\n * @typeParam P - Record of param names to ParamRef instances\n */\nexport type ResolveParams<P> = {\n [K in keyof P]: P[K] extends ParamRef<infer T> ? T : never;\n};\n\n/**\n * Declare an SSM Parameter Store parameter.\n *\n * The key is combined with project and stage at deploy time to form the full\n * SSM path: `/${project}/${stage}/${key}`.\n *\n * @param key - Parameter key (e.g., \"database-url\")\n * @param transform - Optional function to transform the raw string value\n * @returns A ParamRef used by the deployment and runtime systems\n *\n * @example Simple string parameter\n * ```typescript\n * params: {\n * dbUrl: param(\"database-url\"),\n * }\n * ```\n *\n * @example With transform (e.g., TOML parsing)\n * ```typescript\n * import TOML from \"smol-toml\";\n *\n * params: {\n * config: param(\"app-config\", TOML.parse),\n * }\n * ```\n */\nexport function param(key: string): ParamRef<string>;\nexport function param<T>(key: string, transform: (raw: string) => T): ParamRef<T>;\nexport function param<T = string>(\n key: string,\n transform?: (raw: string) => T\n): ParamRef<T> {\n return {\n __brand: \"effortless-param\",\n key,\n ...(transform ? { transform } : {}),\n } as ParamRef<T>;\n}\n","import { DynamoDB } from \"@aws-sdk/client-dynamodb\";\nimport { marshall, unmarshall } from \"@aws-sdk/util-dynamodb\";\nimport type {\n PlatformEntity,\n ExecutionEntry,\n ErrorEntry,\n} from \"./platform-types\";\nimport {\n ENV_PLATFORM_TABLE,\n dateBucket,\n computeTtl,\n} from \"./platform-types\";\n\nexport type PlatformClient = {\n appendExecution(handlerName: string, handlerType: \"http\" | \"table\" | \"app\", entry: ExecutionEntry): Promise<void>;\n appendError(handlerName: string, handlerType: \"http\" | \"table\" | \"app\", entry: ErrorEntry): Promise<void>;\n get<T extends PlatformEntity>(pk: string, sk: string): Promise<T | undefined>;\n query<T extends PlatformEntity>(pk: string, skPrefix?: string): Promise<T[]>;\n put(entity: PlatformEntity): Promise<void>;\n tableName: string;\n};\n\nexport const createPlatformClient = (): PlatformClient | undefined => {\n const tableName = process.env[ENV_PLATFORM_TABLE];\n if (!tableName) return undefined;\n\n let client: DynamoDB | null = null;\n const getClient = () => (client ??= new DynamoDB({}));\n\n const appendToList = async (\n handlerName: string,\n handlerType: \"http\" | \"table\" | \"app\",\n listAttr: \"executions\" | \"errors\",\n entry: ExecutionEntry | ErrorEntry\n ): Promise<void> => {\n const sk = `EXEC#${dateBucket()}`;\n\n try {\n await getClient().updateItem({\n TableName: tableName,\n Key: marshall({ pk: `HANDLER#${handlerName}`, sk }),\n UpdateExpression:\n \"SET #list = list_append(if_not_exists(#list, :empty), :entry), \" +\n \"#type = :type, #hn = :hn, #ht = :ht, #ttl = :ttl\",\n ExpressionAttributeNames: {\n \"#list\": listAttr,\n \"#type\": \"type\",\n \"#hn\": \"handlerName\",\n \"#ht\": \"handlerType\",\n \"#ttl\": \"ttl\",\n },\n ExpressionAttributeValues: marshall(\n {\n \":entry\": [entry],\n \":empty\": [],\n \":type\": \"execution-log\",\n \":hn\": handlerName,\n \":ht\": handlerType,\n \":ttl\": computeTtl(),\n },\n { removeUndefinedValues: true }\n ),\n });\n } catch (err) {\n console.error(\"[effortless] Failed to write platform record:\", err);\n }\n };\n\n return {\n tableName,\n\n async appendExecution(handlerName, handlerType, entry) {\n await appendToList(handlerName, handlerType, \"executions\", entry);\n },\n\n async appendError(handlerName, handlerType, entry) {\n await appendToList(handlerName, handlerType, \"errors\", entry);\n },\n\n async get<T extends PlatformEntity>(pk: string, sk: string): Promise<T | undefined> {\n const result = await getClient().getItem({\n TableName: tableName,\n Key: marshall({ pk, sk }),\n });\n return result.Item ? (unmarshall(result.Item) as T) : undefined;\n },\n\n async query<T extends PlatformEntity>(pk: string, skPrefix?: string): Promise<T[]> {\n const names: Record<string, string> = { \"#pk\": \"pk\" };\n const values: Record<string, unknown> = { \":pk\": pk };\n let keyCondition = \"#pk = :pk\";\n\n if (skPrefix) {\n names[\"#sk\"] = \"sk\";\n values[\":sk\"] = skPrefix;\n keyCondition += \" AND begins_with(#sk, :sk)\";\n }\n\n const result = await getClient().query({\n TableName: tableName,\n KeyConditionExpression: keyCondition,\n ExpressionAttributeNames: names,\n ExpressionAttributeValues: marshall(values, { removeUndefinedValues: true }),\n });\n\n return (result.Items ?? []).map(item => unmarshall(item) as T);\n },\n\n async put(entity: PlatformEntity) {\n try {\n await getClient().putItem({\n TableName: tableName,\n Item: marshall(entity as Record<string, unknown>, { removeUndefinedValues: true }),\n });\n } catch (err) {\n console.error(\"[effortless] Failed to write platform record:\", err);\n }\n },\n };\n};\n","export const ENV_PLATFORM_TABLE = \"EFF_PLATFORM_TABLE\";\n\nexport const DEFAULT_TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days\n\n// ============ Base ============\n\nexport type BasePlatformEntity = {\n pk: string;\n sk: string;\n type: string;\n ttl?: number;\n};\n\n// ============ Execution Log ============\n\nexport type ExecutionEntry = {\n id: string;\n ts: string;\n ms: number;\n in: unknown;\n out?: unknown;\n};\n\nexport type ErrorEntry = {\n id: string;\n ts: string;\n ms: number;\n in: unknown;\n err: string;\n};\n\nexport type ExecutionLogEntity = BasePlatformEntity & {\n type: \"execution-log\";\n handlerName: string;\n handlerType: \"http\" | \"table\";\n executions: ExecutionEntry[];\n errors: ErrorEntry[];\n};\n\n// ============ Discriminated Union ============\n\nexport type PlatformEntity =\n | ExecutionLogEntity;\n // future: | IdempotencyEntity | HandlerMetaEntity\n\n// ============ Helpers ============\n\nexport const truncateForStorage = (value: unknown, maxLength = 4096): unknown => {\n if (value === undefined || value === null) return value;\n const str = typeof value === \"string\" ? value : JSON.stringify(value);\n if (str.length <= maxLength) return value;\n return str.slice(0, maxLength) + \"...[truncated]\";\n};\n\nexport const dateBucket = (date = new Date()): string =>\n date.toISOString().slice(0, 10);\n\nexport const computeTtl = (ttlSeconds = DEFAULT_TTL_SECONDS): number =>\n Math.floor(Date.now() / 1000) + ttlSeconds;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/config.ts","../src/handlers/define-http.ts","../src/handlers/define-table.ts","../src/handlers/define-app.ts","../src/handlers/define-static-site.ts","../src/handlers/define-fifo-queue.ts","../src/handlers/param.ts","../src/handlers/typed.ts","../src/runtime/platform-client.ts","../src/runtime/platform-types.ts"],"sourcesContent":["/**\n * Configuration for an Effortless project.\n *\n * @example\n * ```typescript\n * // effortless.config.ts\n * import { defineConfig } from \"effortless-aws\";\n *\n * export default defineConfig({\n * name: \"my-service\",\n * region: \"eu-central-1\",\n * handlers: \"src\",\n * });\n * ```\n */\nexport type EffortlessConfig = {\n /**\n * Project name used for resource naming and tagging.\n * This becomes part of Lambda function names, IAM roles, etc.\n */\n name: string;\n\n /**\n * Default AWS region for all handlers.\n * Can be overridden per-handler or via CLI `--region` flag.\n * @default \"eu-central-1\"\n */\n region?: string;\n\n /**\n * Deployment stage (e.g., \"dev\", \"staging\", \"prod\").\n * Used for resource isolation and tagging.\n * @default \"dev\"\n */\n stage?: string;\n\n /**\n * Glob patterns or directory paths to scan for handlers.\n * Used by `eff deploy` (without file argument) to auto-discover handlers.\n *\n * @example\n * ```typescript\n * // Single directory - scans for all .ts files\n * handlers: \"src\"\n *\n * // Glob patterns\n * handlers: [\"src/**\\/*.ts\", \"lib/**\\/*.handler.ts\"]\n * ```\n */\n handlers?: string | string[];\n\n /**\n * Default settings applied to all handlers unless overridden.\n */\n defaults?: {\n /**\n * Lambda memory in MB.\n * @default 256\n */\n memory?: number;\n\n /**\n * Lambda timeout as a human-readable string.\n * @example \"30 seconds\", \"5 minutes\"\n */\n timeout?: string;\n\n /**\n * Lambda runtime.\n * @default \"nodejs22.x\"\n */\n runtime?: string;\n };\n};\n\n/**\n * Helper function for type-safe configuration.\n * Returns the config object as-is, but provides TypeScript autocompletion.\n *\n * @example\n * ```typescript\n * import { defineConfig } from \"effortless-aws\";\n *\n * export default defineConfig({\n * name: \"my-service\",\n * region: \"eu-central-1\",\n * handlers: \"src\",\n * });\n * ```\n */\nexport const defineConfig = (config: EffortlessConfig): EffortlessConfig => config;\n","import type { Permission } from \"./permissions\";\nimport type { TableHandler } from \"./define-table\";\nimport type { TableClient } from \"../runtime/table-client\";\nimport type { AnyParamRef, ResolveParams } from \"./param\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyTableHandler = TableHandler<any, any, any, any, any, any>;\n\n/** Maps a deps declaration to resolved runtime client types */\nexport type ResolveDeps<D> = {\n [K in keyof D]: D[K] extends TableHandler<infer T, any, any, any, any> ? TableClient<T> : never;\n};\n\n/** HTTP methods supported by API Gateway */\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/** Short content-type aliases for common response formats */\nexport type ContentType = \"json\" | \"html\" | \"text\" | \"css\" | \"js\" | \"xml\" | \"csv\" | \"svg\";\n\n/**\n * Incoming HTTP request object passed to the handler\n */\nexport type HttpRequest = {\n /** HTTP method (GET, POST, etc.) */\n method: string;\n /** Request path (e.g., \"/users/123\") */\n path: string;\n /** Request headers */\n headers: Record<string, string | undefined>;\n /** Query string parameters */\n query: Record<string, string | undefined>;\n /** Path parameters extracted from route (e.g., {id} -> params.id) */\n params: Record<string, string | undefined>;\n /** Parsed request body (JSON parsed if Content-Type is application/json) */\n body: unknown;\n /** Raw unparsed request body */\n rawBody?: string;\n};\n\n/**\n * HTTP response returned from the handler\n */\nexport type HttpResponse = {\n /** HTTP status code (e.g., 200, 404, 500) */\n status: number;\n /** Response body — JSON-serialized by default, or sent as string when contentType is set */\n body?: unknown;\n /**\n * Short content-type alias. Resolves to full MIME type automatically:\n * - `\"json\"` → `application/json` (default if omitted)\n * - `\"html\"` → `text/html; charset=utf-8`\n * - `\"text\"` → `text/plain; charset=utf-8`\n * - `\"css\"` → `text/css; charset=utf-8`\n * - `\"js\"` → `application/javascript; charset=utf-8`\n * - `\"xml\"` → `application/xml; charset=utf-8`\n * - `\"csv\"` → `text/csv; charset=utf-8`\n * - `\"svg\"` → `image/svg+xml; charset=utf-8`\n */\n contentType?: ContentType;\n /** Response headers (use for custom content-types not covered by contentType) */\n headers?: Record<string, string>;\n};\n\n/**\n * Configuration options extracted from DefineHttpOptions (without onRequest callback)\n */\nexport type HttpConfig = {\n /** Handler name. Defaults to export name if not specified */\n name?: string;\n /** HTTP method for the route */\n method: HttpMethod;\n /** Route path (e.g., \"/api/users\", \"/api/users/{id}\") */\n path: string;\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout in seconds (default: 30) */\n timeout?: number;\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n /** Enable observability logging to platform table (default: true) */\n observe?: boolean;\n};\n\n/**\n * Handler function type for HTTP endpoints\n *\n * @typeParam T - Type of the validated request body (from schema function)\n * @typeParam C - Type of the context/dependencies (from context function)\n * @typeParam D - Type of the deps (from deps declaration)\n * @typeParam P - Type of the params (from params declaration)\n */\nexport type HttpHandlerFn<T = undefined, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { req: HttpRequest }\n & ([T] extends [undefined] ? {} : { data: T })\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<HttpResponse>;\n\n/**\n * Context factory type — conditional on whether params are declared.\n * Without params: `() => C | Promise<C>`\n * With params: `(args: { params: ResolveParams<P> }) => C | Promise<C>`\n */\ntype ContextFactory<C, P> = [P] extends [undefined]\n ? () => C | Promise<C>\n : (args: { params: ResolveParams<P & {}> }) => C | Promise<C>;\n\n/**\n * Options for defining an HTTP endpoint\n *\n * @typeParam T - Type of the validated request body (inferred from schema function)\n * @typeParam C - Type of the context/dependencies returned by context function\n * @typeParam D - Type of the deps (from deps declaration)\n * @typeParam P - Type of the params (from params declaration)\n */\nexport type DefineHttpOptions<\n T = undefined,\n C = undefined,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n> = HttpConfig & {\n /**\n * Decode/validate function for the request body.\n * Called with the parsed body; should return typed data or throw on validation failure.\n * When provided, the handler receives validated `data` and invalid requests get a 400 response.\n *\n * Works with any validation library:\n * - Effect: `S.decodeUnknownSync(MySchema)`\n * - Zod: `(body) => myZodSchema.parse(body)`\n */\n schema?: (input: unknown) => T;\n /**\n * Error handler called when schema validation or onRequest throws.\n * Receives the error and request, returns an HttpResponse.\n * If not provided, defaults to 400 for validation errors and 500 for handler errors.\n */\n onError?: (error: unknown, req: HttpRequest) => HttpResponse;\n /**\n * Factory function to create context/dependencies for the request handler.\n * Called once on cold start, result is cached and reused across invocations.\n * When params are declared, receives resolved params as argument.\n * Supports both sync and async return values.\n */\n context?: ContextFactory<C, P>;\n /**\n * Dependencies on other handlers (tables, queues, etc.).\n * Typed clients are injected into the handler via the `deps` argument.\n */\n deps?: D;\n /**\n * SSM Parameter Store parameters.\n * Declare with `param()` helper. Values are fetched and cached at cold start.\n * Typed values are injected into the handler via the `params` argument.\n */\n params?: P;\n /**\n * Static file glob patterns to bundle into the Lambda ZIP.\n * Files are accessible at runtime via the `readStatic` callback argument.\n */\n static?: S;\n /** HTTP request handler function */\n onRequest: HttpHandlerFn<T, C, D, P, S>;\n};\n\n/**\n * Internal handler object created by defineHttp\n * @internal\n */\nexport type HttpHandler<T = undefined, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = {\n readonly __brand: \"effortless-http\";\n readonly config: HttpConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (error: unknown, req: HttpRequest) => HttpResponse;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly context?: (...args: any[]) => C | Promise<C>;\n readonly deps?: D;\n readonly params?: P;\n readonly static?: string[];\n readonly onRequest: HttpHandlerFn<T, C, D, P, S>;\n};\n\n/**\n * Define an HTTP endpoint that creates an API Gateway route + Lambda function\n *\n * @typeParam T - Type of the validated request body (inferred from schema function)\n * @typeParam C - Type of the context/dependencies (inferred from context function)\n * @typeParam D - Type of the deps (from deps declaration)\n * @typeParam P - Type of the params (from params declaration)\n * @param options - Configuration, optional schema, optional context factory, and request handler\n * @returns Handler object used by the deployment system\n *\n * @example Basic GET endpoint\n * ```typescript\n * export const hello = defineHttp({\n * method: \"GET\",\n * path: \"/hello\",\n * onRequest: async ({ req }) => ({\n * status: 200,\n * body: { message: \"Hello World!\" }\n * })\n * });\n * ```\n *\n * @example With SSM parameters\n * ```typescript\n * import { param } from \"effortless-aws\";\n *\n * export const api = defineHttp({\n * method: \"GET\",\n * path: \"/orders\",\n * params: {\n * dbUrl: param(\"database-url\"),\n * },\n * context: async ({ params }) => ({\n * pool: createPool(params.dbUrl),\n * }),\n * onRequest: async ({ req, ctx, params }) => ({\n * status: 200,\n * body: { dbUrl: params.dbUrl }\n * })\n * });\n * ```\n */\nexport const defineHttp = <\n T = undefined,\n C = undefined,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n>(\n options: DefineHttpOptions<T, C, D, P, S>\n): HttpHandler<T, C, D, P, S> => {\n const { onRequest, onError, context, schema, deps, params, static: staticFiles, ...config } = options;\n return {\n __brand: \"effortless-http\",\n config,\n ...(schema ? { schema } : {}),\n ...(onError ? { onError } : {}),\n ...(context ? { context } : {}),\n ...(deps ? { deps } : {}),\n ...(params ? { params } : {}),\n ...(staticFiles ? { static: staticFiles } : {}),\n onRequest\n } as HttpHandler<T, C, D, P, S>;\n};\n","import type { Permission } from \"./permissions\";\nimport type { TableClient } from \"../runtime/table-client\";\nimport type { AnyParamRef, ResolveParams } from \"./param\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyTableHandler = TableHandler<any, any, any, any, any, any>;\n\n/** Maps a deps declaration to resolved runtime client types */\ntype ResolveDeps<D> = {\n [K in keyof D]: D[K] extends TableHandler<infer T, any, any, any, any> ? TableClient<T> : never;\n};\n\n/** DynamoDB attribute types for keys */\nexport type KeyType = \"string\" | \"number\" | \"binary\";\n\n/**\n * DynamoDB table key definition\n */\nexport type TableKey = {\n /** Attribute name */\n name: string;\n /** Attribute type */\n type: KeyType;\n};\n\n/** DynamoDB Streams view type - determines what data is captured in stream records */\nexport type StreamView = \"NEW_AND_OLD_IMAGES\" | \"NEW_IMAGE\" | \"OLD_IMAGE\" | \"KEYS_ONLY\";\n\n/**\n * Configuration options extracted from DefineTableOptions (without onRecord/context)\n */\nexport type TableConfig = {\n /** Table/handler name. Defaults to export name if not specified */\n name?: string;\n /** Partition key definition */\n pk: TableKey;\n /** Sort key definition (optional) */\n sk?: TableKey;\n /** DynamoDB billing mode (default: \"PAY_PER_REQUEST\") */\n billingMode?: \"PAY_PER_REQUEST\" | \"PROVISIONED\";\n /** TTL attribute name for automatic item expiration */\n ttlAttribute?: string;\n /** Stream view type - what data to include in stream records (default: \"NEW_AND_OLD_IMAGES\") */\n streamView?: StreamView;\n /** Number of records to process in each Lambda invocation (1-10000, default: 100) */\n batchSize?: number;\n /** Maximum time in seconds to gather records before invoking (0-300, default: 2) */\n batchWindow?: number;\n /** Where to start reading the stream (default: \"LATEST\") */\n startingPosition?: \"LATEST\" | \"TRIM_HORIZON\";\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout in seconds (default: 30) */\n timeout?: number;\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n /** Enable observability logging to platform table (default: true) */\n observe?: boolean;\n};\n\n/**\n * DynamoDB stream record passed to onRecord callback\n *\n * @typeParam T - Type of the table items (new/old values)\n */\nexport type TableRecord<T = Record<string, unknown>> = {\n /** Type of modification: INSERT, MODIFY, or REMOVE */\n eventName: \"INSERT\" | \"MODIFY\" | \"REMOVE\";\n /** New item value (present for INSERT and MODIFY) */\n new?: T;\n /** Old item value (present for MODIFY and REMOVE) */\n old?: T;\n /** Primary key of the affected item */\n keys: Record<string, unknown>;\n /** Sequence number for ordering */\n sequenceNumber?: string;\n /** Approximate timestamp when the modification occurred */\n timestamp?: number;\n};\n\n/**\n * Information about a failed record during batch processing\n *\n * @typeParam T - Type of the table items\n */\nexport type FailedRecord<T = Record<string, unknown>> = {\n /** The record that failed to process */\n record: TableRecord<T>;\n /** The error that occurred */\n error: unknown;\n};\n\n/**\n * Context factory type — conditional on whether params are declared.\n * Without params: `() => C | Promise<C>`\n * With params: `(args: { params: ResolveParams<P> }) => C | Promise<C>`\n */\ntype ContextFactory<C, P> = [P] extends [undefined]\n ? () => C | Promise<C>\n : (args: { params: ResolveParams<P & {}> }) => C | Promise<C>;\n\n/**\n * Callback function type for processing a single DynamoDB stream record\n */\nexport type TableRecordFn<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { record: TableRecord<T>; table: TableClient<T> }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<R>;\n\n/**\n * Callback function type for processing accumulated batch results\n */\nexport type TableBatchCompleteFn<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { results: R[]; failures: FailedRecord<T>[]; table: TableClient<T> }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<void>;\n\n/**\n * Callback function type for processing all records in a batch at once\n */\nexport type TableBatchFn<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { records: TableRecord<T>[]; table: TableClient<T> }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<void>;\n\n/** Base options shared by all defineTable variants */\ntype DefineTableBase<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = TableConfig & {\n /**\n * Decode/validate function for stream record items (new/old images).\n * Called with the unmarshalled DynamoDB item; should return typed data or throw on validation failure.\n * When provided, T is inferred from the return type — no need to specify generic parameters.\n */\n schema?: (input: unknown) => T;\n /**\n * Error handler called when onRecord, onBatch, or onBatchComplete throws.\n * Receives the error. If not provided, defaults to `console.error`.\n */\n onError?: (error: unknown) => void;\n /**\n * Factory function to create context/dependencies for callbacks.\n * Called once on cold start, result is cached and reused across invocations.\n * When params are declared, receives resolved params as argument.\n * Supports both sync and async return values.\n */\n context?: ContextFactory<C, P>;\n /**\n * Dependencies on other handlers (tables, queues, etc.).\n * Typed clients are injected into the handler via the `deps` argument.\n */\n deps?: D;\n /**\n * SSM Parameter Store parameters.\n * Declare with `param()` helper. Values are fetched and cached at cold start.\n * Typed values are injected into the handler via the `params` argument.\n */\n params?: P;\n /**\n * Static file glob patterns to bundle into the Lambda ZIP.\n * Files are accessible at runtime via the `readStatic` callback argument.\n */\n static?: S;\n};\n\n/** Per-record processing: onRecord with optional onBatchComplete */\ntype DefineTableWithOnRecord<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {\n onRecord: TableRecordFn<T, C, R, D, P, S>;\n onBatchComplete?: TableBatchCompleteFn<T, C, R, D, P, S>;\n onBatch?: never;\n};\n\n/** Batch processing: onBatch processes all records at once */\ntype DefineTableWithOnBatch<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {\n onBatch: TableBatchFn<T, C, D, P, S>;\n onRecord?: never;\n onBatchComplete?: never;\n};\n\n/** Resource-only: no handler, just creates the table */\ntype DefineTableResourceOnly<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {\n onRecord?: never;\n onBatch?: never;\n onBatchComplete?: never;\n};\n\nexport type DefineTableOptions<\n T = Record<string, unknown>,\n C = undefined,\n R = void,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n> =\n | DefineTableWithOnRecord<T, C, R, D, P, S>\n | DefineTableWithOnBatch<T, C, D, P, S>\n | DefineTableResourceOnly<T, C, D, P, S>;\n\n/**\n * Internal handler object created by defineTable\n * @internal\n */\nexport type TableHandler<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> = {\n readonly __brand: \"effortless-table\";\n readonly config: TableConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (error: unknown) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly context?: (...args: any[]) => C | Promise<C>;\n readonly deps?: D;\n readonly params?: P;\n readonly static?: string[];\n readonly onRecord?: TableRecordFn<T, C, R, D, P, S>;\n readonly onBatchComplete?: TableBatchCompleteFn<T, C, R, D, P, S>;\n readonly onBatch?: TableBatchFn<T, C, D, P, S>;\n};\n\n/**\n * Define a DynamoDB table with optional stream handler\n *\n * Creates:\n * - DynamoDB table with specified key schema\n * - (If onRecord or onBatch provided) DynamoDB Stream + Lambda + Event Source Mapping\n *\n * @example Table with stream handler (typed)\n * ```typescript\n * type Order = { id: string; amount: number; status: string };\n *\n * export const orders = defineTable<Order>({\n * pk: { name: \"id\", type: \"string\" },\n * streamView: \"NEW_AND_OLD_IMAGES\",\n * batchSize: 10,\n * onRecord: async ({ record }) => {\n * if (record.eventName === \"INSERT\") {\n * console.log(\"New order:\", record.new?.amount);\n * }\n * }\n * });\n * ```\n *\n * @example Table only (no Lambda)\n * ```typescript\n * export const users = defineTable({\n * pk: { name: \"id\", type: \"string\" },\n * sk: { name: \"email\", type: \"string\" }\n * });\n * ```\n */\nexport const defineTable = <\n T = Record<string, unknown>,\n C = undefined,\n R = void,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n>(\n options: DefineTableOptions<T, C, R, D, P, S>\n): TableHandler<T, C, R, D, P, S> => {\n const { onRecord, onBatchComplete, onBatch, onError, schema, context, deps, params, static: staticFiles, ...config } = options;\n return {\n __brand: \"effortless-table\",\n config,\n ...(schema ? { schema } : {}),\n ...(onError ? { onError } : {}),\n ...(context ? { context } : {}),\n ...(deps ? { deps } : {}),\n ...(params ? { params } : {}),\n ...(staticFiles ? { static: staticFiles } : {}),\n ...(onRecord ? { onRecord } : {}),\n ...(onBatchComplete ? { onBatchComplete } : {}),\n ...(onBatch ? { onBatch } : {})\n } as TableHandler<T, C, R, D, P, S>;\n};\n","/**\n * Configuration for a Lambda-served static site (API Gateway + Lambda)\n */\nexport type AppConfig = {\n /** Handler name. Defaults to export name if not specified */\n name?: string;\n /** Base URL path the site is served under (e.g., \"/app\") */\n path?: string;\n /** Directory containing the static site files, relative to project root */\n dir: string;\n /** Default file for directory requests (default: \"index.html\") */\n index?: string;\n /** SPA mode: serve index.html for all paths that don't match a file (default: false) */\n spa?: boolean;\n /** Shell command to run before deploy to generate site content (e.g., \"npx astro build\") */\n build?: string;\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout in seconds (default: 5) */\n timeout?: number;\n /** Enable observability logging to platform table (default: false) */\n observe?: boolean;\n};\n\n/**\n * Internal handler object created by defineApp\n * @internal\n */\nexport type AppHandler = {\n readonly __brand: \"effortless-app\";\n readonly config: AppConfig;\n};\n\n/**\n * Deploy a static site via Lambda + API Gateway.\n * Files are bundled into the Lambda ZIP and served with auto-detected content types.\n *\n * For CDN-backed sites (S3 + CloudFront), use {@link defineStaticSite} instead.\n *\n * @param options - Site configuration: path, directory, optional SPA mode\n * @returns Handler object used by the deployment system\n *\n * @example Basic static site\n * ```typescript\n * export const app = defineApp({\n * path: \"/app\",\n * dir: \"src/webapp\",\n * });\n * ```\n */\nexport const defineApp = (options: AppConfig): AppHandler => ({\n __brand: \"effortless-app\",\n config: options,\n});\n","/**\n * Configuration for a static site handler (S3 + CloudFront)\n */\nexport type StaticSiteConfig = {\n /** Handler name. Defaults to export name if not specified */\n name?: string;\n /** Directory containing the static site files, relative to project root */\n dir: string;\n /** Default file for directory requests (default: \"index.html\") */\n index?: string;\n /** SPA mode: serve index.html for all paths that don't match a file (default: false) */\n spa?: boolean;\n /** Shell command to run before deploy to generate site content (e.g., \"npx astro build\") */\n build?: string;\n};\n\n/**\n * Internal handler object created by defineStaticSite\n * @internal\n */\nexport type StaticSiteHandler = {\n readonly __brand: \"effortless-static-site\";\n readonly config: StaticSiteConfig;\n};\n\n/**\n * Deploy a static site via S3 + CloudFront CDN.\n *\n * @param options - Static site configuration: directory, optional SPA mode, build command\n * @returns Handler object used by the deployment system\n *\n * @example Documentation site\n * ```typescript\n * export const docs = defineStaticSite({\n * dir: \"dist\",\n * build: \"npx astro build\",\n * });\n * ```\n *\n * @example SPA with client-side routing\n * ```typescript\n * export const app = defineStaticSite({\n * dir: \"dist\",\n * spa: true,\n * build: \"npm run build\",\n * });\n * ```\n */\nexport const defineStaticSite = (options: StaticSiteConfig): StaticSiteHandler => ({\n __brand: \"effortless-static-site\",\n config: options,\n});\n","import type { Permission } from \"./permissions\";\nimport type { TableHandler } from \"./define-table\";\nimport type { TableClient } from \"../runtime/table-client\";\nimport type { AnyParamRef, ResolveParams } from \"./param\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyTableHandler = TableHandler<any, any, any, any, any, any>;\n\n/** Maps a deps declaration to resolved runtime client types */\ntype ResolveDeps<D> = {\n [K in keyof D]: D[K] extends TableHandler<infer T, any, any, any, any> ? TableClient<T> : never;\n};\n\n/**\n * Parsed SQS FIFO message passed to the handler callbacks.\n *\n * @typeParam T - Type of the decoded message body (from schema function)\n */\nexport type FifoQueueMessage<T = unknown> = {\n /** Unique message identifier */\n messageId: string;\n /** Receipt handle for acknowledgement */\n receiptHandle: string;\n /** Parsed message body (JSON-decoded, then optionally schema-validated) */\n body: T;\n /** Raw unparsed message body string */\n rawBody: string;\n /** Message group ID (FIFO ordering key) */\n messageGroupId: string;\n /** Message deduplication ID */\n messageDeduplicationId?: string;\n /** SQS message attributes */\n messageAttributes: Record<string, { dataType?: string; stringValue?: string }>;\n /** Approximate first receive timestamp */\n approximateFirstReceiveTimestamp?: string;\n /** Approximate receive count */\n approximateReceiveCount?: string;\n /** Sent timestamp */\n sentTimestamp?: string;\n};\n\n/**\n * Configuration options for a FIFO queue handler\n */\nexport type FifoQueueConfig = {\n /** Handler name. Defaults to export name if not specified */\n name?: string;\n /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */\n batchSize?: number;\n /** Maximum time in seconds to gather messages before invoking (0-300, default: 0) */\n batchWindow?: number;\n /** Visibility timeout in seconds (default: max of timeout or 30) */\n visibilityTimeout?: number;\n /** Message retention period in seconds (60-1209600, default: 345600 = 4 days) */\n retentionPeriod?: number;\n /** Enable content-based deduplication (default: true) */\n contentBasedDeduplication?: boolean;\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout in seconds (default: 30) */\n timeout?: number;\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n /** Enable observability logging to platform table (default: true) */\n observe?: boolean;\n};\n\n/**\n * Context factory type — conditional on whether params are declared.\n * Without params: `() => C | Promise<C>`\n * With params: `(args: { params: ResolveParams<P> }) => C | Promise<C>`\n */\ntype ContextFactory<C, P> = [P] extends [undefined]\n ? () => C | Promise<C>\n : (args: { params: ResolveParams<P & {}> }) => C | Promise<C>;\n\n/**\n * Per-message handler function.\n * Called once per message in the batch. Failures are reported individually.\n */\nexport type FifoQueueMessageFn<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { message: FifoQueueMessage<T> }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<void>;\n\n/**\n * Batch handler function.\n * Called once with all messages in the batch.\n */\nexport type FifoQueueBatchFn<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> =\n (args: { messages: FifoQueueMessage<T>[] }\n & ([C] extends [undefined] ? {} : { ctx: C })\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { params: ResolveParams<P> })\n & ([S] extends [undefined] ? {} : { readStatic: (path: string) => string })\n ) => Promise<void>;\n\n/** Base options shared by all defineFifoQueue variants */\ntype DefineFifoQueueBase<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = FifoQueueConfig & {\n /**\n * Decode/validate function for the message body.\n * Called with the JSON-parsed body; should return typed data or throw on validation failure.\n */\n schema?: (input: unknown) => T;\n /**\n * Error handler called when onMessage or onBatch throws.\n * If not provided, defaults to `console.error`.\n */\n onError?: (error: unknown) => void;\n /**\n * Factory function to create context/dependencies for the handler.\n * Called once on cold start, result is cached and reused across invocations.\n * When params are declared, receives resolved params as argument.\n */\n context?: ContextFactory<C, P>;\n /**\n * Dependencies on other handlers (tables, queues, etc.).\n * Typed clients are injected into the handler via the `deps` argument.\n */\n deps?: D;\n /**\n * SSM Parameter Store parameters.\n * Declare with `param()` helper. Values are fetched and cached at cold start.\n */\n params?: P;\n /**\n * Static file glob patterns to bundle into the Lambda ZIP.\n * Files are accessible at runtime via the `readStatic` callback argument.\n */\n static?: S;\n};\n\n/** Per-message processing */\ntype DefineFifoQueueWithOnMessage<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineFifoQueueBase<T, C, D, P, S> & {\n onMessage: FifoQueueMessageFn<T, C, D, P, S>;\n onBatch?: never;\n};\n\n/** Batch processing: all messages at once */\ntype DefineFifoQueueWithOnBatch<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineFifoQueueBase<T, C, D, P, S> & {\n onBatch: FifoQueueBatchFn<T, C, D, P, S>;\n onMessage?: never;\n};\n\nexport type DefineFifoQueueOptions<\n T = unknown,\n C = undefined,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n> =\n | DefineFifoQueueWithOnMessage<T, C, D, P, S>\n | DefineFifoQueueWithOnBatch<T, C, D, P, S>;\n\n/**\n * Internal handler object created by defineFifoQueue\n * @internal\n */\nexport type FifoQueueHandler<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = {\n readonly __brand: \"effortless-fifo-queue\";\n readonly config: FifoQueueConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (error: unknown) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly context?: (...args: any[]) => C | Promise<C>;\n readonly deps?: D;\n readonly params?: P;\n readonly static?: string[];\n readonly onMessage?: FifoQueueMessageFn<T, C, D, P, S>;\n readonly onBatch?: FifoQueueBatchFn<T, C, D, P, S>;\n};\n\n/**\n * Define a FIFO SQS queue with a Lambda message handler\n *\n * Creates:\n * - SQS FIFO queue (with `.fifo` suffix)\n * - Lambda function triggered by the queue\n * - Event source mapping with partial batch failure support\n *\n * @example Per-message processing\n * ```typescript\n * type OrderEvent = { orderId: string; action: string };\n *\n * export const orderQueue = defineFifoQueue<OrderEvent>({\n * onMessage: async ({ message }) => {\n * console.log(\"Processing order:\", message.body.orderId);\n * }\n * });\n * ```\n *\n * @example Batch processing with schema\n * ```typescript\n * export const notifications = defineFifoQueue({\n * schema: (input) => NotificationSchema.parse(input),\n * batchSize: 5,\n * onBatch: async ({ messages }) => {\n * await sendAll(messages.map(m => m.body));\n * }\n * });\n * ```\n */\nexport const defineFifoQueue = <\n T = unknown,\n C = undefined,\n D extends Record<string, AnyTableHandler> | undefined = undefined,\n P extends Record<string, AnyParamRef> | undefined = undefined,\n S extends string[] | undefined = undefined\n>(\n options: DefineFifoQueueOptions<T, C, D, P, S>\n): FifoQueueHandler<T, C, D, P, S> => {\n const { onMessage, onBatch, onError, schema, context, deps, params, static: staticFiles, ...config } = options;\n return {\n __brand: \"effortless-fifo-queue\",\n config,\n ...(schema ? { schema } : {}),\n ...(onError ? { onError } : {}),\n ...(context ? { context } : {}),\n ...(deps ? { deps } : {}),\n ...(params ? { params } : {}),\n ...(staticFiles ? { static: staticFiles } : {}),\n ...(onMessage ? { onMessage } : {}),\n ...(onBatch ? { onBatch } : {})\n } as FifoQueueHandler<T, C, D, P, S>;\n};\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyParamRef = ParamRef<any>;\n\n/**\n * Reference to an SSM Parameter Store parameter.\n *\n * @typeParam T - The resolved type after optional transform (default: string)\n */\nexport type ParamRef<T = string> = {\n readonly __brand: \"effortless-param\";\n readonly key: string;\n readonly transform?: (raw: string) => T;\n};\n\n/**\n * Maps a params declaration to resolved value types.\n *\n * @typeParam P - Record of param names to ParamRef instances\n */\nexport type ResolveParams<P> = {\n [K in keyof P]: P[K] extends ParamRef<infer T> ? T : never;\n};\n\n/**\n * Declare an SSM Parameter Store parameter.\n *\n * The key is combined with project and stage at deploy time to form the full\n * SSM path: `/${project}/${stage}/${key}`.\n *\n * @param key - Parameter key (e.g., \"database-url\")\n * @param transform - Optional function to transform the raw string value\n * @returns A ParamRef used by the deployment and runtime systems\n *\n * @example Simple string parameter\n * ```typescript\n * params: {\n * dbUrl: param(\"database-url\"),\n * }\n * ```\n *\n * @example With transform (e.g., TOML parsing)\n * ```typescript\n * import TOML from \"smol-toml\";\n *\n * params: {\n * config: param(\"app-config\", TOML.parse),\n * }\n * ```\n */\nexport function param(key: string): ParamRef<string>;\nexport function param<T>(key: string, transform: (raw: string) => T): ParamRef<T>;\nexport function param<T = string>(\n key: string,\n transform?: (raw: string) => T\n): ParamRef<T> {\n return {\n __brand: \"effortless-param\",\n key,\n ...(transform ? { transform } : {}),\n } as ParamRef<T>;\n}\n","/**\n * Type-only schema helper for handlers.\n *\n * Use this instead of explicit generic parameters like `defineTable<Order>(...)`.\n * It enables TypeScript to infer all generic types from the options object,\n * avoiding the partial-inference problem where specifying one generic\n * forces all others to their defaults.\n *\n * At runtime this is a no-op identity function — it simply returns the input unchanged.\n * The type narrowing happens entirely at the TypeScript level.\n *\n * @example Resource-only table\n * ```typescript\n * type User = { id: string; email: string };\n *\n * // Before (breaks inference for context, deps, params):\n * export const users = defineTable<User>({ pk: { name: \"id\", type: \"string\" } });\n *\n * // After (all generics inferred correctly):\n * export const users = defineTable({\n * pk: { name: \"id\", type: \"string\" },\n * schema: typed<User>(),\n * });\n * ```\n *\n * @example Table with stream handler\n * ```typescript\n * export const orders = defineTable({\n * pk: { name: \"id\", type: \"string\" },\n * schema: typed<Order>(),\n * context: async () => ({ db: createClient() }),\n * onRecord: async ({ record, ctx }) => {\n * // record.new is Order, ctx is { db: Client } — all inferred\n * },\n * });\n * ```\n */\nexport function typed<T>(): (input: unknown) => T {\n return (input: unknown) => input as T;\n}\n","import { DynamoDB } from \"@aws-sdk/client-dynamodb\";\nimport { marshall, unmarshall } from \"@aws-sdk/util-dynamodb\";\nimport type {\n PlatformEntity,\n ExecutionEntry,\n ErrorEntry,\n} from \"./platform-types\";\nimport {\n ENV_PLATFORM_TABLE,\n dateBucket,\n computeTtl,\n} from \"./platform-types\";\n\nexport type PlatformClient = {\n appendExecution(handlerName: string, handlerType: \"http\" | \"table\" | \"app\" | \"fifo-queue\", entry: ExecutionEntry): Promise<void>;\n appendError(handlerName: string, handlerType: \"http\" | \"table\" | \"app\" | \"fifo-queue\", entry: ErrorEntry): Promise<void>;\n get<T extends PlatformEntity>(pk: string, sk: string): Promise<T | undefined>;\n query<T extends PlatformEntity>(pk: string, skPrefix?: string): Promise<T[]>;\n put(entity: PlatformEntity): Promise<void>;\n tableName: string;\n};\n\nexport const createPlatformClient = (): PlatformClient | undefined => {\n const tableName = process.env[ENV_PLATFORM_TABLE];\n if (!tableName) return undefined;\n\n let client: DynamoDB | null = null;\n const getClient = () => (client ??= new DynamoDB({}));\n\n const appendToList = async (\n handlerName: string,\n handlerType: \"http\" | \"table\" | \"app\" | \"fifo-queue\",\n listAttr: \"executions\" | \"errors\",\n entry: ExecutionEntry | ErrorEntry\n ): Promise<void> => {\n const sk = `EXEC#${dateBucket()}`;\n\n try {\n await getClient().updateItem({\n TableName: tableName,\n Key: marshall({ pk: `HANDLER#${handlerName}`, sk }),\n UpdateExpression:\n \"SET #list = list_append(if_not_exists(#list, :empty), :entry), \" +\n \"#type = :type, #hn = :hn, #ht = :ht, #ttl = :ttl\",\n ExpressionAttributeNames: {\n \"#list\": listAttr,\n \"#type\": \"type\",\n \"#hn\": \"handlerName\",\n \"#ht\": \"handlerType\",\n \"#ttl\": \"ttl\",\n },\n ExpressionAttributeValues: marshall(\n {\n \":entry\": [entry],\n \":empty\": [],\n \":type\": \"execution-log\",\n \":hn\": handlerName,\n \":ht\": handlerType,\n \":ttl\": computeTtl(),\n },\n { removeUndefinedValues: true }\n ),\n });\n } catch (err) {\n console.error(\"[effortless] Failed to write platform record:\", err);\n }\n };\n\n return {\n tableName,\n\n async appendExecution(handlerName, handlerType, entry) {\n await appendToList(handlerName, handlerType, \"executions\", entry);\n },\n\n async appendError(handlerName, handlerType, entry) {\n await appendToList(handlerName, handlerType, \"errors\", entry);\n },\n\n async get<T extends PlatformEntity>(pk: string, sk: string): Promise<T | undefined> {\n const result = await getClient().getItem({\n TableName: tableName,\n Key: marshall({ pk, sk }),\n });\n return result.Item ? (unmarshall(result.Item) as T) : undefined;\n },\n\n async query<T extends PlatformEntity>(pk: string, skPrefix?: string): Promise<T[]> {\n const names: Record<string, string> = { \"#pk\": \"pk\" };\n const values: Record<string, unknown> = { \":pk\": pk };\n let keyCondition = \"#pk = :pk\";\n\n if (skPrefix) {\n names[\"#sk\"] = \"sk\";\n values[\":sk\"] = skPrefix;\n keyCondition += \" AND begins_with(#sk, :sk)\";\n }\n\n const result = await getClient().query({\n TableName: tableName,\n KeyConditionExpression: keyCondition,\n ExpressionAttributeNames: names,\n ExpressionAttributeValues: marshall(values, { removeUndefinedValues: true }),\n });\n\n return (result.Items ?? []).map(item => unmarshall(item) as T);\n },\n\n async put(entity: PlatformEntity) {\n try {\n await getClient().putItem({\n TableName: tableName,\n Item: marshall(entity as Record<string, unknown>, { removeUndefinedValues: true }),\n });\n } catch (err) {\n console.error(\"[effortless] Failed to write platform record:\", err);\n }\n },\n };\n};\n","export const ENV_PLATFORM_TABLE = \"EFF_PLATFORM_TABLE\";\n\nexport const DEFAULT_TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days\n\n// ============ Base ============\n\nexport type BasePlatformEntity = {\n pk: string;\n sk: string;\n type: string;\n ttl?: number;\n};\n\n// ============ Execution Log ============\n\nexport type ExecutionEntry = {\n id: string;\n ts: string;\n ms: number;\n in: unknown;\n out?: unknown;\n};\n\nexport type ErrorEntry = {\n id: string;\n ts: string;\n ms: number;\n in: unknown;\n err: string;\n};\n\nexport type ExecutionLogEntity = BasePlatformEntity & {\n type: \"execution-log\";\n handlerName: string;\n handlerType: \"http\" | \"table\" | \"app\" | \"fifo-queue\";\n executions: ExecutionEntry[];\n errors: ErrorEntry[];\n};\n\n// ============ Discriminated Union ============\n\nexport type PlatformEntity =\n | ExecutionLogEntity;\n // future: | IdempotencyEntity | HandlerMetaEntity\n\n// ============ Helpers ============\n\nexport const truncateForStorage = (value: unknown, maxLength = 4096): unknown => {\n if (value === undefined || value === null) return value;\n const str = typeof value === \"string\" ? value : JSON.stringify(value);\n if (str.length <= maxLength) return value;\n return str.slice(0, maxLength) + \"...[truncated]\";\n};\n\nexport const dateBucket = (date = new Date()): string =>\n date.toISOString().slice(0, 10);\n\nexport const computeTtl = (ttlSeconds = DEFAULT_TTL_SECONDS): number =>\n Math.floor(Date.now() / 1000) + ttlSeconds;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,84 @@
1
+ import {
2
+ createHandlerRuntime,
3
+ truncateForStorage
4
+ } from "../chunk-AHRNISIY.js";
5
+
6
+ // src/runtime/wrap-fifo-queue.ts
7
+ var parseMessages = (rawRecords, schema) => {
8
+ const messages = [];
9
+ const decode = schema ?? ((x) => x);
10
+ for (const record of rawRecords) {
11
+ let parsedBody;
12
+ try {
13
+ parsedBody = JSON.parse(record.body);
14
+ } catch {
15
+ parsedBody = record.body;
16
+ }
17
+ messages.push({
18
+ messageId: record.messageId,
19
+ receiptHandle: record.receiptHandle,
20
+ body: decode(parsedBody),
21
+ rawBody: record.body,
22
+ messageGroupId: record.attributes.MessageGroupId ?? "",
23
+ messageDeduplicationId: record.attributes.MessageDeduplicationId,
24
+ messageAttributes: record.messageAttributes ?? {},
25
+ approximateFirstReceiveTimestamp: record.attributes.ApproximateFirstReceiveTimestamp,
26
+ approximateReceiveCount: record.attributes.ApproximateReceiveCount,
27
+ sentTimestamp: record.attributes.SentTimestamp
28
+ });
29
+ }
30
+ return messages;
31
+ };
32
+ var wrapFifoQueue = (handler) => {
33
+ if (!handler.onMessage && !handler.onBatch) {
34
+ throw new Error("wrapFifoQueue requires a handler with onMessage or onBatch defined");
35
+ }
36
+ const rt = createHandlerRuntime(handler, "fifo-queue");
37
+ const handleError = handler.onError ?? ((e) => console.error(`[effortless:${rt.handlerName}]`, e));
38
+ return async (event) => {
39
+ const startTime = Date.now();
40
+ const rawRecords = event.Records ?? [];
41
+ const input = truncateForStorage({ messageCount: rawRecords.length });
42
+ let messages;
43
+ try {
44
+ messages = parseMessages(rawRecords, handler.schema);
45
+ } catch (error) {
46
+ handleError(error);
47
+ rt.logError(startTime, input, error);
48
+ return {
49
+ batchItemFailures: rawRecords.map((r) => ({ itemIdentifier: r.messageId }))
50
+ };
51
+ }
52
+ const shared = await rt.commonArgs();
53
+ const batchItemFailures = [];
54
+ if (handler.onBatch) {
55
+ try {
56
+ await handler.onBatch({ messages, ...shared });
57
+ } catch (error) {
58
+ handleError(error);
59
+ for (const message of messages) {
60
+ batchItemFailures.push({ itemIdentifier: message.messageId });
61
+ }
62
+ }
63
+ } else {
64
+ const onMessage = handler.onMessage;
65
+ for (const message of messages) {
66
+ try {
67
+ await onMessage({ message, ...shared });
68
+ } catch (error) {
69
+ handleError(error);
70
+ batchItemFailures.push({ itemIdentifier: message.messageId });
71
+ }
72
+ }
73
+ }
74
+ if (batchItemFailures.length > 0) {
75
+ rt.logError(startTime, input, `${batchItemFailures.length} message(s) failed`);
76
+ } else {
77
+ rt.logExecution(startTime, input, { processedCount: messages.length });
78
+ }
79
+ return { batchItemFailures };
80
+ };
81
+ };
82
+ export {
83
+ wrapFifoQueue
84
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effortless-aws",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Code-first AWS Lambda framework",
@@ -39,6 +39,7 @@
39
39
  "@aws-sdk/client-lambda": "^3.975.0",
40
40
  "@aws-sdk/client-resource-groups-tagging-api": "^3.975.0",
41
41
  "@aws-sdk/client-s3": "^3.989.0",
42
+ "@aws-sdk/client-sqs": "^3.989.0",
42
43
  "@aws-sdk/client-ssm": "^3.985.0",
43
44
  "@aws-sdk/util-dynamodb": "^3.975.0",
44
45
  "archiver": "^7.0.1",