effortless-aws 0.35.0 → 0.36.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/{chunk-6HFS224S.js → chunk-Q3ZDMZF3.js} +13 -4
- package/dist/index.d.ts +74 -80
- package/dist/index.js +36 -6
- package/dist/index.js.map +1 -1
- package/dist/runtime/wrap-api.js +1 -1
- package/dist/runtime/wrap-bucket.js +1 -1
- package/dist/runtime/wrap-cron.js +1 -1
- package/dist/runtime/wrap-fifo-queue.js +1 -1
- package/dist/runtime/wrap-mcp.js +1 -1
- package/dist/runtime/wrap-middleware.js +1 -1
- package/dist/runtime/wrap-table-stream.js +1 -1
- package/dist/runtime/wrap-worker.js +1 -1
- package/package.json +1 -1
|
@@ -765,9 +765,18 @@ var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupA
|
|
|
765
765
|
resolvedCfSigningConfig = { privateKey, keyPairId: cfKeyPairId, domain: cfDomain };
|
|
766
766
|
return resolvedCfSigningConfig;
|
|
767
767
|
};
|
|
768
|
-
const getAuthRuntime = async (
|
|
768
|
+
const getAuthRuntime = async () => {
|
|
769
769
|
if (resolvedAuthRuntime !== null) return resolvedAuthRuntime;
|
|
770
|
-
|
|
770
|
+
if (!handler.authFn) {
|
|
771
|
+
resolvedAuthRuntime = void 0;
|
|
772
|
+
return void 0;
|
|
773
|
+
}
|
|
774
|
+
const params = await getParams();
|
|
775
|
+
const deps = getDeps();
|
|
776
|
+
const authArgs = {};
|
|
777
|
+
if (params) authArgs.config = params;
|
|
778
|
+
if (deps) authArgs.deps = deps;
|
|
779
|
+
const authOpts = await handler.authFn(authArgs);
|
|
771
780
|
if (!authOpts?.secret) {
|
|
772
781
|
resolvedAuthRuntime = void 0;
|
|
773
782
|
return void 0;
|
|
@@ -795,7 +804,7 @@ var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupA
|
|
|
795
804
|
if (handler.setup) {
|
|
796
805
|
const params = await getParams();
|
|
797
806
|
const deps = getDeps();
|
|
798
|
-
const args = {
|
|
807
|
+
const args = {};
|
|
799
808
|
if (params) args.config = params;
|
|
800
809
|
if (deps) args.deps = deps;
|
|
801
810
|
if (handler.static) args.files = staticFiles;
|
|
@@ -813,7 +822,7 @@ var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupA
|
|
|
813
822
|
const params = await getParams();
|
|
814
823
|
if (params) args.config = params;
|
|
815
824
|
if (handler.static) args.files = staticFiles;
|
|
816
|
-
const authRuntime = await getAuthRuntime(
|
|
825
|
+
const authRuntime = await getAuthRuntime();
|
|
817
826
|
if (authRuntime) {
|
|
818
827
|
let finalAuthHeader = authHeader;
|
|
819
828
|
if (finalAuthHeader === void 0 && headers && resolvedAuthHeaderName) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1208,22 +1208,10 @@ type AppHandler = {
|
|
|
1208
1208
|
*/
|
|
1209
1209
|
declare const defineApp: () => (options: AppConfig) => AppHandler;
|
|
1210
1210
|
|
|
1211
|
-
/** Any branded handler that deploys to API Gateway
|
|
1211
|
+
/** Any branded handler that deploys to API Gateway or S3 */
|
|
1212
1212
|
type AnyRoutableHandler = {
|
|
1213
1213
|
readonly __brand: string;
|
|
1214
1214
|
};
|
|
1215
|
-
/** Route configuration for serving bucket content through CloudFront */
|
|
1216
|
-
type BucketRouteConfig = {
|
|
1217
|
-
bucket: {
|
|
1218
|
-
readonly __brand: "effortless-bucket";
|
|
1219
|
-
};
|
|
1220
|
-
/** Access control: "private" requires CloudFront signed cookies, "public" serves openly. Default: "public" */
|
|
1221
|
-
access?: "private" | "public";
|
|
1222
|
-
};
|
|
1223
|
-
/** A route value: either an API handler or a bucket route config */
|
|
1224
|
-
type RouteValue = AnyRoutableHandler | BucketRouteConfig;
|
|
1225
|
-
/** Type guard for bucket route entries */
|
|
1226
|
-
declare const isBucketRoute: (v: unknown) => v is BucketRouteConfig;
|
|
1227
1215
|
/** Simplified request object passed to middleware */
|
|
1228
1216
|
type MiddlewareRequest = {
|
|
1229
1217
|
uri: string;
|
|
@@ -1255,33 +1243,32 @@ type StaticSiteSeo = {
|
|
|
1255
1243
|
googleIndexing?: string;
|
|
1256
1244
|
};
|
|
1257
1245
|
/**
|
|
1258
|
-
* Configuration for a static site
|
|
1246
|
+
* Configuration for a static site (S3 + CloudFront)
|
|
1259
1247
|
*/
|
|
1260
1248
|
type StaticSiteConfig = {
|
|
1261
|
-
/** Handler name. Defaults to export name if not specified */
|
|
1262
|
-
name?: string;
|
|
1263
1249
|
/** Directory containing the static site files, relative to project root */
|
|
1264
1250
|
dir: string;
|
|
1265
1251
|
/** Default file for directory requests (default: "index.html") */
|
|
1266
1252
|
index?: string;
|
|
1267
|
-
/** SPA mode: serve index.html for all paths that don't match a file (default: false) */
|
|
1268
|
-
spa?: boolean;
|
|
1269
1253
|
/** Shell command to run before deploy to generate site content (e.g., "npx astro build") */
|
|
1270
1254
|
build?: string;
|
|
1255
|
+
/** Path to a custom error page relative to `dir`.
|
|
1256
|
+
* - If set to the same value as `index` (e.g. "index.html"), enables SPA mode:
|
|
1257
|
+
* all paths that don't match a file are served with `index.html` (HTTP 200), letting the client-side router handle them.
|
|
1258
|
+
* - If set to a different file (e.g. "404.html"), that file is served with HTTP 404 for missing paths.
|
|
1259
|
+
* - If omitted, a default 404 page is auto-generated. */
|
|
1260
|
+
errorPage?: string;
|
|
1271
1261
|
/** Custom domain name. Accepts a string (same domain for all stages) or a Record mapping stage names to domains (e.g., `{ prod: "example.com", dev: "dev.example.com" }`). Requires an ACM certificate in us-east-1. If the cert also covers www, a 301 redirect from www to non-www is set up automatically. */
|
|
1272
1262
|
domain?: string | Record<string, string>;
|
|
1273
|
-
/** CloudFront route overrides: path patterns forwarded to API Gateway or S3 bucket origins.
|
|
1274
|
-
* Keys are CloudFront path patterns (e.g., "/api/*"), values are HTTP handlers or bucket route configs.
|
|
1275
|
-
* Example: `routes: { "/api/*": api, "/files/*": { bucket: storage, access: "private" } }` */
|
|
1276
|
-
routes?: Record<string, RouteValue>;
|
|
1277
|
-
/** Custom 404 error page path relative to `dir` (e.g. "404.html").
|
|
1278
|
-
* For non-SPA sites only. If not set, a default page is generated automatically. */
|
|
1279
|
-
errorPage?: string;
|
|
1280
|
-
/** Lambda@Edge middleware that runs before serving pages. Use for auth checks, redirects, etc. */
|
|
1281
|
-
middleware?: MiddlewareHandler;
|
|
1282
1263
|
/** SEO: auto-generate sitemap.xml and robots.txt at deploy time, optionally submit URLs to Google Indexing API */
|
|
1283
1264
|
seo?: StaticSiteSeo;
|
|
1284
1265
|
};
|
|
1266
|
+
/** Route entry stored on the static site handler */
|
|
1267
|
+
type StaticSiteRouteEntry = {
|
|
1268
|
+
pattern: string;
|
|
1269
|
+
origin: AnyRoutableHandler;
|
|
1270
|
+
access?: "private" | "public";
|
|
1271
|
+
};
|
|
1285
1272
|
/**
|
|
1286
1273
|
* Internal handler object created by defineStaticSite
|
|
1287
1274
|
* @internal
|
|
@@ -1289,16 +1276,24 @@ type StaticSiteConfig = {
|
|
|
1289
1276
|
type StaticSiteHandler = {
|
|
1290
1277
|
readonly __brand: "effortless-static-site";
|
|
1291
1278
|
readonly __spec: StaticSiteConfig;
|
|
1279
|
+
readonly routes: StaticSiteRouteEntry[];
|
|
1280
|
+
readonly middleware?: MiddlewareHandler;
|
|
1292
1281
|
};
|
|
1293
1282
|
/**
|
|
1294
|
-
* Deploy a static site via S3 + CloudFront CDN.
|
|
1283
|
+
* Deploy a static site via S3 + CloudFront CDN, with optional API and bucket route overrides.
|
|
1295
1284
|
*
|
|
1296
1285
|
* @see {@link https://effortless-aws.website/use-cases/web-app | Web app guide}
|
|
1297
1286
|
*
|
|
1298
|
-
* @param options - Static site configuration: directory, optional SPA mode, build command
|
|
1299
|
-
* @returns
|
|
1287
|
+
* @param options - Static site configuration: directory, optional SPA mode, build command, domain
|
|
1288
|
+
* @returns Builder with `.route()`, `.middleware()`, and `.build()` methods
|
|
1300
1289
|
*/
|
|
1301
|
-
declare
|
|
1290
|
+
declare function defineStaticSite(options: StaticSiteConfig): {
|
|
1291
|
+
route(pattern: string, origin: AnyRoutableHandler, opts?: {
|
|
1292
|
+
access?: "private" | "public";
|
|
1293
|
+
}): /*elided*/ any;
|
|
1294
|
+
middleware(fn: MiddlewareHandler): /*elided*/ any;
|
|
1295
|
+
build(): StaticSiteHandler;
|
|
1296
|
+
};
|
|
1302
1297
|
|
|
1303
1298
|
/** Options for CloudFront signed cookie policy */
|
|
1304
1299
|
type CdnPolicyOptions = {
|
|
@@ -1357,16 +1352,6 @@ type AuthOptions<A = unknown> = {
|
|
|
1357
1352
|
cacheTtl?: Duration;
|
|
1358
1353
|
};
|
|
1359
1354
|
};
|
|
1360
|
-
/** Branded auth config — created by `enableAuth<A>()` helper, carries session type A */
|
|
1361
|
-
type ApiAuthConfig<A = unknown> = AuthOptions<A> & {
|
|
1362
|
-
readonly __sessionType: A;
|
|
1363
|
-
};
|
|
1364
|
-
/** Type of the `enableAuth` helper injected into setup args */
|
|
1365
|
-
type EnableAuth = <A = unknown>(options: AuthOptions<A>) => ApiAuthConfig<A>;
|
|
1366
|
-
/** Extract session type A from ctx.auth if present */
|
|
1367
|
-
type ExtractAuth<C> = C extends {
|
|
1368
|
-
auth: ApiAuthConfig<infer A>;
|
|
1369
|
-
} ? A : undefined;
|
|
1370
1355
|
/** Property names reserved by the framework — cannot be used in setup return */
|
|
1371
1356
|
type ReservedKeys = 'req' | 'input' | 'stream' | 'ok' | 'fail';
|
|
1372
1357
|
/** Success response helper: `ok({ data })` → `{ status: 200, body: { data } }` */
|
|
@@ -1398,14 +1383,14 @@ type RouteEntry = {
|
|
|
1398
1383
|
public?: boolean;
|
|
1399
1384
|
cache?: ResolvedCache;
|
|
1400
1385
|
};
|
|
1401
|
-
/** Spread ctx into route args
|
|
1402
|
-
type SpreadCtx$2<C> = ([C] extends [undefined] ? {} :
|
|
1403
|
-
auth: AuthHelpers<
|
|
1386
|
+
/** Spread ctx into route args, add AuthHelpers if A is set */
|
|
1387
|
+
type SpreadCtx$2<C, A = undefined> = ([C] extends [undefined] ? {} : C & {}) & ([A] extends [undefined] ? {} : {
|
|
1388
|
+
auth: AuthHelpers<A>;
|
|
1404
1389
|
});
|
|
1405
1390
|
/** Infer validated output type from a Standard Schema, or fall back to unknown */
|
|
1406
1391
|
type InferInput<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : unknown;
|
|
1407
1392
|
/** Callback args available inside each route — ctx is spread into args */
|
|
1408
|
-
type RouteArgs<C, ST, S = undefined> = SpreadCtx$2<C> & {
|
|
1393
|
+
type RouteArgs<C, ST, A = undefined, S = undefined> = SpreadCtx$2<C, A> & {
|
|
1409
1394
|
req: HttpRequest;
|
|
1410
1395
|
input: InferInput<S>;
|
|
1411
1396
|
ok: OkHelper;
|
|
@@ -1414,7 +1399,7 @@ type RouteArgs<C, ST, S = undefined> = SpreadCtx$2<C> & {
|
|
|
1414
1399
|
stream: ResponseStream;
|
|
1415
1400
|
} : {});
|
|
1416
1401
|
/** Route handler function */
|
|
1417
|
-
type RouteHandler<C, ST, S = undefined> = (args: RouteArgs<C, ST, S>) => Promise<HttpResponse | void> | HttpResponse | void;
|
|
1402
|
+
type RouteHandler<C, ST, A = undefined, S = undefined> = (args: RouteArgs<C, ST, A, S>) => Promise<HttpResponse | void> | HttpResponse | void;
|
|
1418
1403
|
/** Route definition — pass `input` for typed schema validation */
|
|
1419
1404
|
type RouteDef<S extends StandardSchemaV1 | undefined = undefined> = {
|
|
1420
1405
|
path: `/${string}`;
|
|
@@ -1422,9 +1407,8 @@ type RouteDef<S extends StandardSchemaV1 | undefined = undefined> = {
|
|
|
1422
1407
|
public?: boolean;
|
|
1423
1408
|
cache?: CacheOptions;
|
|
1424
1409
|
};
|
|
1425
|
-
/** Setup factory — receives deps/config/files
|
|
1410
|
+
/** Setup factory — receives deps/config/files based on what was declared */
|
|
1426
1411
|
type SetupArgs$2<D, P, HasFiles extends boolean> = {
|
|
1427
|
-
enableAuth: EnableAuth;
|
|
1428
1412
|
ok: OkHelper;
|
|
1429
1413
|
fail: FailHelper;
|
|
1430
1414
|
} & ([D] extends [undefined] ? {} : {
|
|
@@ -1434,6 +1418,12 @@ type SetupArgs$2<D, P, HasFiles extends boolean> = {
|
|
|
1434
1418
|
}) & (HasFiles extends true ? {
|
|
1435
1419
|
files: StaticFiles;
|
|
1436
1420
|
} : {});
|
|
1421
|
+
/** Auth factory args — receives config/deps based on what was declared */
|
|
1422
|
+
type AuthArgs$1<D, P> = ([D] extends [undefined] ? {} : {
|
|
1423
|
+
deps: ResolveDeps<D>;
|
|
1424
|
+
}) & ([P] extends [undefined] ? {} : {
|
|
1425
|
+
config: ResolveConfig<P & {}>;
|
|
1426
|
+
});
|
|
1437
1427
|
/** Validate that setup return type does not use reserved property names */
|
|
1438
1428
|
type ValidateSetupReturn<C> = C & {
|
|
1439
1429
|
[K in ReservedKeys]?: never;
|
|
@@ -1470,12 +1460,12 @@ type ApiOptions = {
|
|
|
1470
1460
|
* Finalized API handler with route-adding methods.
|
|
1471
1461
|
* Has `__brand` so CLI discovers it. Each `.get()/.post()` adds a route and returns self.
|
|
1472
1462
|
*/
|
|
1473
|
-
interface ApiRoutes<C = undefined, ST extends boolean = false> extends ApiHandler<C> {
|
|
1474
|
-
get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1475
|
-
post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1476
|
-
put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1477
|
-
patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1478
|
-
delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1463
|
+
interface ApiRoutes<C = undefined, ST extends boolean = false, A = undefined> extends ApiHandler<C> {
|
|
1464
|
+
get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1465
|
+
post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1466
|
+
put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1467
|
+
patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1468
|
+
delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1479
1469
|
}
|
|
1480
1470
|
/**
|
|
1481
1471
|
* Builder interface for defining API handlers.
|
|
@@ -1483,33 +1473,35 @@ interface ApiRoutes<C = undefined, ST extends boolean = false> extends ApiHandle
|
|
|
1483
1473
|
* Each method sets exactly one generic, so inference happens one step at a time.
|
|
1484
1474
|
* This prevents cascading type errors when one property has a mistake.
|
|
1485
1475
|
*/
|
|
1486
|
-
interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boolean = false, HasFiles extends boolean = false> {
|
|
1476
|
+
interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boolean = false, HasFiles extends boolean = false, A = undefined> {
|
|
1487
1477
|
/** Declare handler dependencies (tables, queues, buckets, mailers) */
|
|
1488
|
-
deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): ApiBuilder<D2, P, C, ST, HasFiles>;
|
|
1478
|
+
deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): ApiBuilder<D2, P, C, ST, HasFiles, A>;
|
|
1489
1479
|
/** Declare SSM secrets */
|
|
1490
|
-
config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): ApiBuilder<D, P2, C, ST, HasFiles>;
|
|
1480
|
+
config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): ApiBuilder<D, P2, C, ST, HasFiles, A>;
|
|
1491
1481
|
/** Include static files by glob pattern */
|
|
1492
|
-
include(glob: string): ApiBuilder<D, P, C, ST, true>;
|
|
1482
|
+
include(glob: string): ApiBuilder<D, P, C, ST, true, A>;
|
|
1483
|
+
/** Configure session-based authentication. Receives resolved config/deps. */
|
|
1484
|
+
auth<A2>(fn: (args: AuthArgs$1<D, P>) => AuthOptions<A2> | Promise<AuthOptions<A2>>): ApiBuilder<D, P, C, ST, HasFiles, A2>;
|
|
1493
1485
|
/** Configure Lambda settings only (no init function) */
|
|
1494
|
-
setup(lambda: LambdaOptions): ApiBuilder<D, P, C, ST, HasFiles>;
|
|
1486
|
+
setup(lambda: LambdaOptions): ApiBuilder<D, P, C, ST, HasFiles, A>;
|
|
1495
1487
|
/** Initialize shared state on cold start. Receives deps/config/files based on what was declared. */
|
|
1496
|
-
setup<C2>(fn: (args: SetupArgs$2<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>): ApiBuilder<D, P, C2, ST, HasFiles>;
|
|
1488
|
+
setup<C2>(fn: (args: SetupArgs$2<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>): ApiBuilder<D, P, C2, ST, HasFiles, A>;
|
|
1497
1489
|
/** Initialize shared state on cold start with Lambda config. */
|
|
1498
|
-
setup<C2>(fn: (args: SetupArgs$2<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>, lambda: LambdaOptions): ApiBuilder<D, P, C2, ST, HasFiles>;
|
|
1490
|
+
setup<C2>(fn: (args: SetupArgs$2<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>, lambda: LambdaOptions): ApiBuilder<D, P, C2, ST, HasFiles, A>;
|
|
1499
1491
|
/** Handle errors thrown by routes */
|
|
1500
1492
|
onError(fn: (args: {
|
|
1501
1493
|
error: unknown;
|
|
1502
1494
|
req: HttpRequest;
|
|
1503
1495
|
ok: OkHelper;
|
|
1504
1496
|
fail: FailHelper;
|
|
1505
|
-
} & SpreadCtx$2<C>) => HttpResponse | Promise<HttpResponse>): ApiBuilder<D, P, C, ST, HasFiles>;
|
|
1497
|
+
} & SpreadCtx$2<C, A>) => HttpResponse | Promise<HttpResponse>): ApiBuilder<D, P, C, ST, HasFiles, A>;
|
|
1506
1498
|
/** Cleanup callback — runs after each invocation, before Lambda freezes */
|
|
1507
|
-
onCleanup(fn: (args: SpreadCtx$2<C>) => void | Promise<void>): ApiBuilder<D, P, C, ST, HasFiles>;
|
|
1508
|
-
get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1509
|
-
post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1510
|
-
put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1511
|
-
patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1512
|
-
delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;
|
|
1499
|
+
onCleanup(fn: (args: SpreadCtx$2<C, A>) => void | Promise<void>): ApiBuilder<D, P, C, ST, HasFiles, A>;
|
|
1500
|
+
get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1501
|
+
post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1502
|
+
put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1503
|
+
patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1504
|
+
delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;
|
|
1513
1505
|
}
|
|
1514
1506
|
/**
|
|
1515
1507
|
* Define an API with typed routes using a builder pattern.
|
|
@@ -1518,14 +1510,10 @@ interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boo
|
|
|
1518
1510
|
*
|
|
1519
1511
|
* @example
|
|
1520
1512
|
* ```typescript
|
|
1521
|
-
* export const api = defineApi({ basePath: "/api"
|
|
1513
|
+
* export const api = defineApi({ basePath: "/api" })
|
|
1522
1514
|
* .deps(() => ({ users }))
|
|
1523
|
-
* .config(({ defineSecret }) => ({
|
|
1524
|
-
* .
|
|
1525
|
-
* users: deps.users,
|
|
1526
|
-
* auth: enableAuth<Session>({ secret: config.dbUrl }),
|
|
1527
|
-
* }))
|
|
1528
|
-
* .onError(({ error, fail }) => fail(String(error), 500))
|
|
1515
|
+
* .config(({ defineSecret }) => ({ authSecret: defineSecret() }))
|
|
1516
|
+
* .auth<Session>(({ config }) => ({ secret: config.authSecret, expiresIn: "1h" }))
|
|
1529
1517
|
* .get({ path: "/me" }, async ({ users, auth, ok }) => ok(auth.session))
|
|
1530
1518
|
* .post({ path: "/login", public: true }, async ({ auth, ok }) => ok(await auth.createSession()))
|
|
1531
1519
|
* ```
|
|
@@ -1811,16 +1799,20 @@ type McpToolDefInput<S extends StandardJSONSchemaV1 | McpInputSchema = McpInputS
|
|
|
1811
1799
|
};
|
|
1812
1800
|
/** Tool handler — receives input (typed or any) and ctx */
|
|
1813
1801
|
type McpToolHandler<C, S = McpInputSchema> = (input: InferToolInput<S>, ctx: SpreadCtx<C>) => unknown | Promise<unknown>;
|
|
1814
|
-
/** Setup factory — receives deps/config/files
|
|
1815
|
-
type SetupArgs<D, P, HasFiles extends boolean> = {
|
|
1816
|
-
enableAuth: EnableAuth;
|
|
1817
|
-
} & ([D] extends [undefined] ? {} : {
|
|
1802
|
+
/** Setup factory — receives deps/config/files based on what was declared */
|
|
1803
|
+
type SetupArgs<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {} : {
|
|
1818
1804
|
deps: ResolveDeps<D>;
|
|
1819
1805
|
}) & ([P] extends [undefined] ? {} : {
|
|
1820
1806
|
config: ResolveConfig<P & {}>;
|
|
1821
1807
|
}) & (HasFiles extends true ? {
|
|
1822
1808
|
files: StaticFiles;
|
|
1823
1809
|
} : {});
|
|
1810
|
+
/** Auth factory args — receives config/deps based on what was declared */
|
|
1811
|
+
type AuthArgs<D, P> = ([D] extends [undefined] ? {} : {
|
|
1812
|
+
deps: ResolveDeps<D>;
|
|
1813
|
+
}) & ([P] extends [undefined] ? {} : {
|
|
1814
|
+
config: ResolveConfig<P & {}>;
|
|
1815
|
+
});
|
|
1824
1816
|
/** Spread ctx into callback args (empty when no setup) */
|
|
1825
1817
|
type SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};
|
|
1826
1818
|
/**
|
|
@@ -1868,6 +1860,8 @@ interface McpBuilder<D = undefined, P = undefined, C = undefined, HasFiles exten
|
|
|
1868
1860
|
config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): McpBuilder<D, P2, C, HasFiles>;
|
|
1869
1861
|
/** Include static files in the bundle. Chainable — call multiple times. */
|
|
1870
1862
|
include(glob: string): McpBuilder<D, P, C, true>;
|
|
1863
|
+
/** Configure session-based authentication. Receives resolved config/deps. All requests require a valid session. */
|
|
1864
|
+
auth<A2>(fn: (args: AuthArgs<D, P>) => AuthOptions<A2> | Promise<AuthOptions<A2>>): McpBuilder<D, P, C, HasFiles>;
|
|
1871
1865
|
/** Configure Lambda settings only (memory, timeout, permissions, logLevel) */
|
|
1872
1866
|
setup(lambda: LambdaOptions): McpBuilder<D, P, C, HasFiles>;
|
|
1873
1867
|
/** Initialize shared state on cold start. Receives deps, config, files. */
|
|
@@ -1914,4 +1908,4 @@ type CronHandler = CronHandler$1<any>;
|
|
|
1914
1908
|
type WorkerHandler<T = any> = WorkerHandler$1<T, any>;
|
|
1915
1909
|
type McpHandler = McpHandler$1<any>;
|
|
1916
1910
|
|
|
1917
|
-
export { type
|
|
1911
|
+
export { type ApiConfig, type ApiHandler, type ApiRoutes, type AppConfig, type AppHandler, type AuthHelpers, type AuthOptions, type BucketClient, type BucketClientWithEntities, type BucketConfig, type BucketEntityConfig, type BucketEvent, type BucketHandler, type CacheOptions, type CdnPolicyOptions, type ConfigHelpers, type ContentType, type CronConfig, type CronHandler, type DefineSecretFn, type Duration, type EffortlessConfig, type EmailClient, type FifoQueueConfig, type FifoQueueHandler, type FifoQueueMessage, type HttpMethod$1 as HttpMethod, type HttpRequest, type HttpResponse, type LogLevel, type MailerConfig, type MailerHandler, type McpConfig, type McpEntries, type McpHandler, type McpInputSchema, type McpPromptArgument, type McpPromptContent, type McpPromptDef, type McpPromptMessage, type McpPromptResult, type McpResourceContent, type McpResourceDef, type McpResourceMap, type McpResourceTemplateDef, type McpToolContent, type McpToolDef, type McpToolResult, type MiddlewareDeny, type MiddlewareHandler, type MiddlewareRedirect, type MiddlewareRequest, type MiddlewareResult, type ParamRef, type Permission, type PutInput, type PutOptions, type QueryByTagParams, type QueryParams, type QueueClient, type ResponseStream, type SecretRef, type SendEmailOptions, type SendMessageInput, type SkCondition, type StaticFiles, type StaticSiteConfig, type StaticSiteHandler, type StaticSiteSeo, type StoreEntityClient, type StreamView, type TableClient, type TableConfig, type TableHandler, type TableItem, type TableKey, type TableRecord, type Timezone, type UpdateActions, type WorkerClient, type WorkerConfig, type WorkerHandler, type WorkerSendOptions, defineApi, defineApp, defineBucket, defineConfig, defineCron, defineFifoQueue, defineMailer, defineMcp, defineSecret, defineStaticSite, defineTable, defineWorker, generateBase64, generateHex, generateUuid, param, secret, toSeconds };
|
package/dist/index.js
CHANGED
|
@@ -116,11 +116,32 @@ var defineApp = () => (options) => ({
|
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
// src/handlers/define-static-site.ts
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
function defineStaticSite(options) {
|
|
120
|
+
const state = {
|
|
121
|
+
spec: { ...options },
|
|
122
|
+
routes: [],
|
|
123
|
+
middleware: void 0
|
|
124
|
+
};
|
|
125
|
+
const builder = {
|
|
126
|
+
route(pattern, origin, opts) {
|
|
127
|
+
state.routes.push({ pattern, origin, ...opts?.access ? { access: opts.access } : {} });
|
|
128
|
+
return builder;
|
|
129
|
+
},
|
|
130
|
+
middleware(fn) {
|
|
131
|
+
state.middleware = fn;
|
|
132
|
+
return builder;
|
|
133
|
+
},
|
|
134
|
+
build() {
|
|
135
|
+
return {
|
|
136
|
+
__brand: "effortless-static-site",
|
|
137
|
+
__spec: state.spec,
|
|
138
|
+
routes: state.routes,
|
|
139
|
+
...state.middleware ? { middleware: state.middleware } : {}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
return builder;
|
|
144
|
+
}
|
|
124
145
|
|
|
125
146
|
// src/handlers/define-fifo-queue.ts
|
|
126
147
|
function defineFifoQueue(options) {
|
|
@@ -325,6 +346,7 @@ function defineApi(options) {
|
|
|
325
346
|
...state.onError ? { onError: state.onError } : {},
|
|
326
347
|
...state.onCleanup ? { onCleanup: state.onCleanup } : {},
|
|
327
348
|
...state.setup ? { setup: state.setup } : {},
|
|
349
|
+
...state.authFn ? { authFn: state.authFn } : {},
|
|
328
350
|
...state.deps ? { deps: state.deps } : {},
|
|
329
351
|
...state.config ? { config: state.config } : {},
|
|
330
352
|
...state.static ? { static: state.static } : {}
|
|
@@ -351,6 +373,10 @@ function defineApi(options) {
|
|
|
351
373
|
state.static = [...state.static ?? [], glob];
|
|
352
374
|
return builder;
|
|
353
375
|
},
|
|
376
|
+
auth(fn) {
|
|
377
|
+
state.authFn = fn;
|
|
378
|
+
return builder;
|
|
379
|
+
},
|
|
354
380
|
setup(fnOrLambda, maybeLambda) {
|
|
355
381
|
if (typeof fnOrLambda === "function") {
|
|
356
382
|
state.setup = fnOrLambda;
|
|
@@ -542,6 +568,7 @@ function defineMcp(options) {
|
|
|
542
568
|
...state.onError ? { onError: state.onError } : {},
|
|
543
569
|
...state.onCleanup ? { onCleanup: state.onCleanup } : {},
|
|
544
570
|
...state.setup ? { setup: state.setup } : {},
|
|
571
|
+
...state.authFn ? { authFn: state.authFn } : {},
|
|
545
572
|
...state.deps ? { deps: state.deps } : {},
|
|
546
573
|
...state.config ? { config: state.config } : {},
|
|
547
574
|
...state.static ? { static: state.static } : {},
|
|
@@ -584,6 +611,10 @@ function defineMcp(options) {
|
|
|
584
611
|
state.static = [...state.static ?? [], glob];
|
|
585
612
|
return builder;
|
|
586
613
|
},
|
|
614
|
+
auth(fn) {
|
|
615
|
+
state.authFn = fn;
|
|
616
|
+
return builder;
|
|
617
|
+
},
|
|
587
618
|
setup(fnOrLambda, maybeLambda) {
|
|
588
619
|
if (typeof fnOrLambda === "function") {
|
|
589
620
|
state.setup = fnOrLambda;
|
|
@@ -637,7 +668,6 @@ export {
|
|
|
637
668
|
generateBase64,
|
|
638
669
|
generateHex,
|
|
639
670
|
generateUuid,
|
|
640
|
-
isBucketRoute,
|
|
641
671
|
param,
|
|
642
672
|
secret,
|
|
643
673
|
toSeconds
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts","../src/handlers/handler-options.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/define-bucket.ts","../src/handlers/define-mailer.ts","../src/handlers/define-api.ts","../src/handlers/define-cron.ts","../src/handlers/define-worker.ts","../src/handlers/define-mcp.ts"],"sourcesContent":["/**\n * Configuration for an Effortless project.\n *\n * @see {@link https://effortless-aws.website/configuration | Configuration guide}\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 Lambda settings applied to all handlers unless overridden.\n *\n * All Lambdas run on ARM64 (Graviton2) architecture — ~20% cheaper than x86_64\n * with better price-performance for most workloads.\n */\n lambda?: {\n /**\n * Lambda memory in MB. AWS allocates proportional CPU —\n * 1769 MB gives one full vCPU.\n * @default 256\n */\n memory?: number;\n\n /**\n * Lambda timeout as a human-readable string.\n * AWS maximum is 15 minutes.\n * @example \"30 seconds\", \"5 minutes\"\n */\n timeout?: string;\n\n /**\n * Node.js Lambda runtime version.\n * @default \"nodejs24.x\"\n */\n runtime?: string;\n };\n\n};\n\n/** Helper function for type-safe configuration with TypeScript autocompletion. */\nexport const defineConfig = (config: EffortlessConfig): EffortlessConfig => config;\n","// Public helpers — this file must have ZERO heavy imports (no effect, no AWS SDK, no deploy code).\n// It is the source of truth for param() and related types used by the public API.\n\n// ============ Generate spec ============\n\n/** Generator spec for auto-creating secrets at deploy time. */\nexport type GenerateSpec = `hex:${number}` | `base64:${number}` | \"uuid\";\n\n// ============ Permissions ============\n\ntype AwsService =\n | \"dynamodb\"\n | \"s3\"\n | \"sqs\"\n | \"sns\"\n | \"ses\"\n | \"ssm\"\n | \"lambda\"\n | \"events\"\n | \"secretsmanager\"\n | \"cognito-idp\"\n | \"logs\";\n\nexport type Permission = `${AwsService}:${string}` | (string & {});\n\n// ============ Duration ============\n\n/**\n * Human-readable duration. Accepts a plain number (seconds) or a string\n * with a unit suffix: `\"30s\"`, `\"5m\"`, `\"1h\"`, `\"2d\"`.\n *\n * @example\n * ```typescript\n * timeout: 30 // 30 seconds\n * timeout: \"30s\" // 30 seconds\n * timeout: \"5m\" // 300 seconds\n * timeout: \"1h\" // 3600 seconds\n * retentionPeriod: \"4d\" // 345600 seconds\n * ```\n */\nexport type Duration = number | `${number}s` | `${number}m` | `${number}h` | `${number}d`;\n\n/** Convert a Duration to seconds. */\nexport const toSeconds = (d: Duration): number => {\n if (typeof d === \"number\") return d;\n const match = d.match(/^(\\d+(?:\\.\\d+)?)(s|m|h|d)$/);\n if (!match) throw new Error(`Invalid duration: \"${d}\"`);\n const n = Number(match[1]);\n const unit = match[2];\n if (unit === \"d\") return n * 86400;\n if (unit === \"h\") return n * 3600;\n if (unit === \"m\") return n * 60;\n return n;\n};\n\n// ============ Lambda config ============\n\n/** Logging verbosity level for Lambda handlers */\nexport type LogLevel = \"error\" | \"info\" | \"debug\";\n\n/**\n * Common Lambda configuration shared by all handler types.\n */\nexport type LambdaConfig = {\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout (default: 30s). Accepts seconds or duration string: `\"30s\"`, `\"5m\"` */\n timeout?: Duration;\n /** Logging verbosity: \"error\" (errors only), \"info\" (+ execution summary), \"debug\" (+ input/output). Default: \"info\" */\n logLevel?: LogLevel;\n};\n\n/**\n * Lambda configuration with additional IAM permissions.\n * Used by handler types that support custom permissions (http, table, fifo-queue).\n */\nexport type LambdaWithPermissions = LambdaConfig & {\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n};\n\n// ============ Lambda options (for .setup()) ============\n\n/**\n * Lambda configuration passed as argument to `.setup()`.\n * Common across all handler types that create a Lambda function.\n */\nexport type LambdaOptions = {\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout (default: 30s). Accepts seconds or duration string: `\"30s\"`, `\"5m\"` */\n timeout?: Duration;\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n /** Logging verbosity: \"error\" (errors only), \"info\" (+ execution summary), \"debug\" (+ input/output). Default: \"info\" */\n logLevel?: LogLevel;\n};\n\n// ============ Secrets (SSM Parameters) ============\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnySecretRef = SecretRef<any>;\n\n/**\n * Reference to an SSM Parameter Store secret.\n *\n * @typeParam T - The resolved type after optional transform (default: string)\n */\nexport type SecretRef<T = string> = {\n readonly __brand: \"effortless-secret\";\n readonly key?: string;\n readonly generate?: GenerateSpec;\n readonly transform?: (raw: string) => T;\n};\n\n/**\n * Maps a config declaration to resolved value types.\n * `SecretRef<T>` resolves to `T`.\n *\n * @typeParam P - Record of config keys to SecretRef instances\n */\nexport type ResolveConfig<P> = {\n [K in keyof P]: P[K] extends SecretRef<infer T> ? T : string;\n};\n\n/** Options for `defineSecret()` without a transform. */\nexport type DefineSecretOptions = {\n /** Custom SSM key (default: derived from config property name in kebab-case) */\n key?: string;\n /** Generator spec for auto-creating the secret at deploy time: `\"hex:32\"`, `\"base64:32\"`, `\"uuid\"` */\n generate?: GenerateSpec;\n};\n\n/** Options for `defineSecret()` with a transform. */\nexport type DefineSecretOptionsWithTransform<T> = DefineSecretOptions & {\n /** Transform the raw SSM string value into a typed value */\n transform: (raw: string) => T;\n};\n\n/** The defineSecret helper function type, injected into config callbacks. */\nexport type DefineSecretFn = {\n (): SecretRef<string>;\n (options: DefineSecretOptions): SecretRef<string>;\n <T>(options: DefineSecretOptionsWithTransform<T>): SecretRef<T>;\n};\n\n/** Helpers injected into the `config` callback. */\nexport type ConfigHelpers = {\n defineSecret: DefineSecretFn;\n};\n\n/** Config factory: a function receiving helpers and returning a record of SecretRefs. */\nexport type ConfigFactory<P> = (helpers: ConfigHelpers) => P;\n\n/** The `defineSecret` implementation, passed to config callbacks. */\nexport const defineSecret: DefineSecretFn = <T = string>(\n options?: DefineSecretOptions | DefineSecretOptionsWithTransform<T>\n): SecretRef<T> => {\n return {\n __brand: \"effortless-secret\",\n ...(options?.key ? { key: options.key } : {}),\n ...(options?.generate ? { generate: options.generate } : {}),\n ...(\"transform\" in (options ?? {}) ? { transform: (options as DefineSecretOptionsWithTransform<T>).transform } : {}),\n } as SecretRef<T>;\n};\n\n/** Internal helpers object passed to config callbacks. */\nexport const configHelpers: ConfigHelpers = { defineSecret };\n\n/** Resolve a config factory to a plain record of SecretRefs. */\nexport const resolveConfigFactory = <P>(config: ConfigFactory<P>): P =>\n config(configHelpers);\n\n// ============ Backwards compatibility ============\n\n/** @deprecated Use `defineSecret()` inside a config callback instead. */\nexport const secret = defineSecret;\n/** @deprecated Use `SecretRef` instead */\nexport type ParamRef<T = string> = SecretRef<T>;\n/** @deprecated Use `AnySecretRef` instead */\nexport type AnyParamRef = AnySecretRef;\n/** @deprecated Use `defineSecret()` instead. */\nexport const param = <T = string>(key: string, transform?: (raw: string) => T): SecretRef<T> => {\n return {\n __brand: \"effortless-secret\",\n key,\n ...(transform ? { transform } : {}),\n } as SecretRef<T>;\n};\n/** @deprecated Use `defineSecret({ generate: \"hex:N\" })` instead. */\nexport const generateHex = (bytes: number) => `hex:${bytes}`;\n/** @deprecated Use `defineSecret({ generate: \"base64:N\" })` instead. */\nexport const generateBase64 = (bytes: number) => `base64:${bytes}`;\n/** @deprecated Use `defineSecret({ generate: \"uuid\" })` instead. */\nexport const generateUuid = () => \"uuid\";\n\n// ============ Single-table types ============\n\n/**\n * DynamoDB table key (always pk + sk strings in single-table design).\n */\nexport type TableKey = { pk: string; sk: string };\n\n/**\n * Full DynamoDB item in the single-table design.\n *\n * Every item has a fixed envelope: `pk`, `sk`, `tag`, `data`, and optional `ttl`.\n * `tag` is stored as a top-level DynamoDB attribute (GSI-ready).\n * If your domain type `T` includes a `tag` field, effortless auto-copies\n * `data.tag` to the top-level `tag` on `put()`, so you don't have to pass it twice.\n *\n * @typeParam T - The domain data type stored in the `data` attribute\n */\nexport type TableItem<T> = {\n pk: string;\n sk: string;\n tag: string;\n data: T;\n ttl?: number;\n};\n\n/**\n * Input type for `TableClient.put()`.\n *\n * Unlike `TableItem<T>`, this does NOT include `tag` — effortless auto-extracts\n * the top-level DynamoDB `tag` attribute from your data using the configured\n * tag field (defaults to `data.tag`, configurable via `defineTable({ tag: \"type\" })`).\n *\n * @typeParam T - The domain data type stored in the `data` attribute\n */\nexport type PutInput<T> = {\n pk: string;\n sk: string;\n data: T;\n ttl?: number;\n};\n\n","import type { AnySecretRef, ResolveConfig, Duration, ConfigFactory, LogLevel, Permission, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { TableClient } from \"../runtime/table-client\";\nimport type { TableItem } from \"./handler-options\";\nimport type { StaticFiles } from \"./shared\";\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 for defineTable (single-table design).\n *\n * Tables always use `pk (S)` + `sk (S)` keys, `tag (S)` discriminator,\n * `data (M)` for domain fields, and `ttl (N)` for optional expiration.\n */\nexport type TableConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: { memory?: number; timeout?: Duration; logLevel?: LogLevel; permissions?: Permission[] };\n /** DynamoDB billing mode (default: \"PAY_PER_REQUEST\") */\n billingMode?: \"PAY_PER_REQUEST\" | \"PROVISIONED\";\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 to gather records before invoking (default: `\"2s\"`). Accepts `\"5s\"`, `\"1m\"`, etc. */\n batchWindow?: Duration;\n /** Where to start reading the stream (default: \"LATEST\") */\n startingPosition?: \"LATEST\" | \"TRIM_HORIZON\";\n /** Number of records to process concurrently within a batch (default: 1 — sequential) */\n concurrency?: number;\n /**\n * Name of the field in `data` that serves as the entity type discriminant.\n * Effortless auto-copies `data[tagField]` to the top-level DynamoDB `tag` attribute on `put()`.\n * Defaults to `\"tag\"`.\n */\n tagField?: string;\n};\n\n/**\n * DynamoDB stream record passed to onRecord callback.\n *\n * `new` and `old` are full `TableItem<T>` objects with the single-table envelope.\n *\n * @typeParam T - Type of the domain data (inside `data`)\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?: TableItem<T>;\n /** Old item value (present for MODIFY and REMOVE) */\n old?: TableItem<T>;\n /** Primary key of the affected item */\n keys: { pk: string; sk: string };\n /** Sequence number for ordering */\n sequenceNumber?: string;\n /** Approximate timestamp when the modification occurred */\n timestamp?: number;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives table/deps/config/files based on what was declared */\ntype SetupArgs<T, D, P, HasFiles extends boolean> =\n & { table: TableClient<T> }\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n/**\n * Callback function type for processing a single DynamoDB stream record.\n * Receives the current record and the full batch for context.\n */\nexport type TableRecordFn<T = Record<string, unknown>, C = undefined> =\n (args: { record: TableRecord<T>; batch: readonly TableRecord<T>[] }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n/**\n * Batch handler function for DynamoDB stream records.\n * Called once with all records in the batch.\n * Return `{ failures: string[] }` (sequence numbers) for partial batch failure reporting.\n */\nexport type TableBatchFn<T = Record<string, unknown>, C = undefined> =\n (args: { records: readonly TableRecord<T>[] }\n & SpreadCtx<C>\n ) => Promise<void | { failures: string[] }>;\n\n// ============ Static config ============\n\n/** Static config extracted by AST (no runtime callbacks) */\n// TableConfig is already defined above and used as the extracted config type.\n\n// ============ Handler type (base interface for runtime wrappers and annotations) ============\n\n/**\n * Handler object created by defineTable.\n * Used by runtime wrappers and as type annotation for circular deps.\n * @internal\n */\nexport type TableHandler<T = Record<string, unknown>, C = any> = {\n readonly __brand: \"effortless-table\";\n readonly __spec: TableConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onRecord?: (...args: any[]) => any;\n readonly onRecordBatch?: (...args: any[]) => any;\n};\n\n// ============ Builder options ============\n\n/** Options passed to `defineTable()` — resource config only, no Lambda settings */\ntype TableOptions<T> = {\n /** DynamoDB billing mode (default: \"PAY_PER_REQUEST\") */\n billingMode?: \"PAY_PER_REQUEST\" | \"PROVISIONED\";\n /** Stream view type (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 to gather records before invoking (default: \"2s\") */\n batchWindow?: Duration;\n /** Where to start reading the stream (default: \"LATEST\") */\n startingPosition?: \"LATEST\" | \"TRIM_HORIZON\";\n /** Number of records to process concurrently within a batch (default: 1) */\n concurrency?: number;\n /** Name of the field in `data` that serves as the entity type discriminant (default: \"tag\") */\n tagField?: Extract<keyof T, string>;\n /** Decode/validate function for the `data` portion of stream records */\n schema?: (input: unknown) => T;\n};\n\n// ============ Builder ============\n\ninterface TableBuilder<\n T = Record<string, unknown>,\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): TableBuilder<T, D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): TableBuilder<T, D, P2, C, HasFiles>;\n\n /** Include static files in the Lambda bundle. Chainable — call multiple times. */\n include(glob: string): TableBuilder<T, D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, etc.) */\n setup(\n lambda: LambdaOptions\n ): TableBuilder<T, D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives table (self-client), deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<T, D, P, HasFiles>) => C2 | Promise<C2>\n ): TableBuilder<T, D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<T, D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): TableBuilder<T, D, P, C2, HasFiles>;\n\n /** Handle errors thrown by onRecord/onRecordBatch */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): TableBuilder<T, D, P, C, HasFiles>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): TableBuilder<T, D, P, C, HasFiles>;\n\n /** Per-record stream handler (terminal — returns finalized handler) */\n onRecord(\n fn: TableRecordFn<T, C>\n ): TableHandler<T, C>;\n\n /** Batch stream handler (terminal — returns finalized handler) */\n onRecordBatch(\n fn: TableBatchFn<T, C>\n ): TableHandler<T, C>;\n\n /** Finalize as resource-only table (no Lambda) */\n build(): TableHandler<T, C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a DynamoDB table with optional stream handler (single-table design).\n *\n * Creates a table with fixed key schema: `pk (S)` + `sk (S)`, plus `tag (S)`,\n * `data (M)`, and `ttl (N)` attributes. TTL is always enabled.\n *\n * @see {@link https://effortless-aws.website/use-cases/database | Database guide}\n *\n * @example\n * ```typescript\n * export const orders = defineTable<OrderData>({ batchSize: 10, concurrency: 5 })\n * .setup(({ table }) => ({ table }))\n * .onRecord(async ({ record, table }) => {\n * if (record.eventName === \"INSERT\") {\n * console.log(\"New order:\", record.new?.data);\n * }\n * })\n * ```\n */\nexport function defineTable<T = Record<string, unknown>>(): TableBuilder<T>;\nexport function defineTable<T = Record<string, unknown>>(\n options: TableOptions<T>,\n): TableBuilder<T>;\nexport function defineTable<T = Record<string, unknown>>(\n options?: TableOptions<T>,\n): TableBuilder<T> {\n const {\n schema,\n ...tableConfig\n } = options ?? {} as TableOptions<T>;\n\n const spec: TableConfig = { ...tableConfig };\n\n const state: {\n spec: TableConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n schema?: (input: unknown) => T;\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onRecord?: (...args: any[]) => any;\n onRecordBatch?: (...args: any[]) => any;\n } = {\n spec,\n ...(schema ? { schema } : {}),\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): TableHandler<T> => ({\n __brand: \"effortless-table\",\n __spec: state.spec,\n ...(state.schema ? { schema: state.schema } : {}),\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onRecord ? { onRecord: state.onRecord } : {}),\n ...(state.onRecordBatch ? { onRecordBatch: state.onRecordBatch } : {}),\n }) as TableHandler<T>;\n\n const builder: TableBuilder<T> = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onRecord(fn) {\n state.onRecord = fn as any;\n return finalize() as any;\n },\n onRecordBatch(fn) {\n state.onRecordBatch = fn as any;\n return finalize() as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n build() {\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","import type { LambdaWithPermissions } from \"./handler-options\";\n\n/**\n * Configuration for deploying an SSR framework (Nuxt, Astro, etc.)\n * via CloudFront + S3 (static assets) + Lambda Function URL (server-side rendering).\n */\nexport type AppConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** Directory containing the Lambda server handler (e.g., \".output/server\").\n * Must contain an `index.mjs` (or `index.js`) that exports a `handler` function. */\n server: string;\n /** Directory containing static assets for S3 (e.g., \".output/public\") */\n assets: string;\n /** Base URL path (default: \"/\") */\n path?: string;\n /** Shell command to build the framework output (e.g., \"nuxt build\") */\n build?: string;\n /** Custom domain name. String or stage-keyed record (e.g., { prod: \"app.example.com\" }). */\n domain?: string | Record<string, string>;\n /** CloudFront route overrides: path patterns forwarded to API Gateway instead of the SSR Lambda.\n * Keys are CloudFront path patterns (e.g., \"/api/*\"), values are handler references.\n * Example: `routes: { \"/api/*\": api }` */\n routes?: Record<string, { readonly __brand: string }>;\n};\n\n/**\n * Internal handler object created by defineApp\n * @internal\n */\nexport type AppHandler = {\n readonly __brand: \"effortless-app\";\n readonly __spec: AppConfig;\n};\n\n/**\n * Deploy an SSR framework application via CloudFront + Lambda Function URL.\n *\n * Static assets from the `assets` directory are served via S3 + CloudFront CDN.\n * Server-rendered pages are handled by a Lambda function using the framework's\n * built output from the `server` directory.\n *\n * For static-only sites (no SSR), use {@link defineStaticSite} instead.\n *\n * @see {@link https://effortless-aws.website/use-cases/web-app | Web app guide}\n *\n * @param options - App configuration: server directory, assets directory, optional build command\n * @returns Handler object used by the deployment system\n */\nexport const defineApp = () => (options: AppConfig): AppHandler => ({\n __brand: \"effortless-app\",\n __spec: options,\n});\n","/** Any branded handler that deploys to API Gateway (HttpHandler, ApiHandler, etc.) */\ntype AnyRoutableHandler = { readonly __brand: string };\n\n/** Route configuration for serving bucket content through CloudFront */\nexport type BucketRouteConfig = {\n bucket: { readonly __brand: \"effortless-bucket\" };\n /** Access control: \"private\" requires CloudFront signed cookies, \"public\" serves openly. Default: \"public\" */\n access?: \"private\" | \"public\";\n};\n\n/** A route value: either an API handler or a bucket route config */\ntype RouteValue = AnyRoutableHandler | BucketRouteConfig;\n\n/** Type guard for bucket route entries */\nexport const isBucketRoute = (v: unknown): v is BucketRouteConfig =>\n v != null && typeof v === \"object\" && \"bucket\" in v &&\n (v as any).bucket != null && typeof (v as any).bucket === \"object\" &&\n (v as any).bucket.__brand === \"effortless-bucket\";\n\n/** Simplified request object passed to middleware */\nexport type MiddlewareRequest = {\n uri: string;\n method: string;\n querystring: string;\n headers: Record<string, string>;\n cookies: Record<string, string>;\n};\n\n/** Redirect the user to another URL */\nexport type MiddlewareRedirect = {\n redirect: string;\n status?: 301 | 302 | 307 | 308;\n};\n\n/** Deny access with a 403 status */\nexport type MiddlewareDeny = {\n status: 403;\n body?: string;\n};\n\n/** Middleware return type: redirect, deny, or void (continue serving) */\nexport type MiddlewareResult = MiddlewareRedirect | MiddlewareDeny | void;\n\n/** Function that runs before serving static files via Lambda@Edge */\nexport type MiddlewareHandler = (\n request: MiddlewareRequest\n) => Promise<MiddlewareResult> | MiddlewareResult;\n\n/** SEO options for auto-generating sitemap.xml, robots.txt, and submitting to Google Indexing API */\nexport type StaticSiteSeo = {\n /** Sitemap filename (e.g. \"sitemap.xml\", \"sitemap-v2.xml\") */\n sitemap: string;\n /** Path to Google service account JSON key file for Indexing API batch submission.\n * Requires adding the service account email as an owner in Google Search Console. */\n googleIndexing?: string;\n};\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 /** Custom domain name. Accepts a string (same domain for all stages) or a Record mapping stage names to domains (e.g., `{ prod: \"example.com\", dev: \"dev.example.com\" }`). Requires an ACM certificate in us-east-1. If the cert also covers www, a 301 redirect from www to non-www is set up automatically. */\n domain?: string | Record<string, string>;\n /** CloudFront route overrides: path patterns forwarded to API Gateway or S3 bucket origins.\n * Keys are CloudFront path patterns (e.g., \"/api/*\"), values are HTTP handlers or bucket route configs.\n * Example: `routes: { \"/api/*\": api, \"/files/*\": { bucket: storage, access: \"private\" } }` */\n routes?: Record<string, RouteValue>;\n /** Custom 404 error page path relative to `dir` (e.g. \"404.html\").\n * For non-SPA sites only. If not set, a default page is generated automatically. */\n errorPage?: string;\n /** Lambda@Edge middleware that runs before serving pages. Use for auth checks, redirects, etc. */\n middleware?: MiddlewareHandler;\n /** SEO: auto-generate sitemap.xml and robots.txt at deploy time, optionally submit URLs to Google Indexing API */\n seo?: StaticSiteSeo;\n};\n\n/**\n * Internal handler object created by defineStaticSite\n * @internal\n */\nexport type StaticSiteHandler = {\n readonly __brand: \"effortless-static-site\";\n readonly __spec: StaticSiteConfig;\n};\n\n/**\n * Deploy a static site via S3 + CloudFront CDN.\n *\n * @see {@link https://effortless-aws.website/use-cases/web-app | Web app guide}\n *\n * @param options - Static site configuration: directory, optional SPA mode, build command\n * @returns Handler object used by the deployment system\n */\nexport const defineStaticSite = () => (options: StaticSiteConfig): StaticSiteHandler => ({\n __brand: \"effortless-static-site\",\n __spec: options,\n});\n","import type { AnySecretRef, ResolveConfig, Duration, ConfigFactory, LogLevel, Permission, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\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 /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: { memory?: number; timeout?: Duration; logLevel?: LogLevel; permissions?: Permission[] };\n /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */\n batchSize?: number;\n /** Maximum time to gather messages before invoking (default: 0). Accepts `\"5s\"`, `\"1m\"`, etc. */\n batchWindow?: Duration;\n /** Visibility timeout (default: max of timeout or 30s). Accepts `\"30s\"`, `\"5m\"`, etc. */\n visibilityTimeout?: Duration;\n /** Message retention period (default: `\"4d\"`). Accepts `\"1h\"`, `\"7d\"`, etc. */\n retentionPeriod?: Duration;\n /** Delivery delay for all messages in the queue (default: 0). Accepts `\"30s\"`, `\"5m\"`, etc. */\n delay?: Duration;\n /** Enable content-based deduplication (default: true) */\n contentBasedDeduplication?: boolean;\n /** Max number of receives before a message is sent to the dead-letter queue (default: 3) */\n maxReceiveCount?: number;\n};\n\n// ============ Setup args ============\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\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> =\n (args: { message: FifoQueueMessage<T> }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n/**\n * Batch handler function.\n * Called once with all messages in the batch.\n * Return `{ failures: string[] }` (messageIds) for partial batch failure reporting.\n */\nexport type FifoQueueBatchFn<T = unknown, C = undefined> =\n (args: { messages: FifoQueueMessage<T>[] }\n & SpreadCtx<C>\n ) => Promise<void | { failures: string[] }>;\n\n// ============ Internal handler object ============\n\n/**\n * Internal handler object created by defineFifoQueue\n * @internal\n */\nexport type FifoQueueHandler<T = unknown, C = any> = {\n readonly __brand: \"effortless-fifo-queue\";\n readonly __spec: FifoQueueConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onMessage?: (...args: any[]) => any;\n readonly onMessageBatch?: (...args: any[]) => any;\n};\n\n// ============ Builder options ============\n\n/** Options passed to `defineFifoQueue()` — static config */\ntype FifoQueueOptions<T> = {\n /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */\n batchSize?: number;\n /** Maximum time to gather messages before invoking (default: 0) */\n batchWindow?: Duration;\n /** Visibility timeout (default: max of timeout or 30s) */\n visibilityTimeout?: Duration;\n /** Message retention period (default: \"4d\") */\n retentionPeriod?: Duration;\n /** Delivery delay for all messages in the queue (default: 0) */\n delay?: Duration;\n /** Enable content-based deduplication (default: true) */\n contentBasedDeduplication?: boolean;\n /** Max number of receives before DLQ (default: 3) */\n maxReceiveCount?: number;\n /** Decode/validate function for the message body */\n schema?: (input: unknown) => T;\n};\n\n// ============ Builder ============\n\ninterface FifoQueueBuilder<\n T = unknown,\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): FifoQueueBuilder<T, D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): FifoQueueBuilder<T, D, P2, C, HasFiles>;\n\n /** Include static files in the Lambda bundle. Chainable — call multiple times. */\n include(glob: string): FifoQueueBuilder<T, D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, etc.) */\n setup(\n lambda: LambdaOptions\n ): FifoQueueBuilder<T, D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): FifoQueueBuilder<T, D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): FifoQueueBuilder<T, D, P, C2, HasFiles>;\n\n /** Handle errors thrown by message handlers */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): FifoQueueBuilder<T, D, P, C, HasFiles>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): FifoQueueBuilder<T, D, P, C, HasFiles>;\n\n /** Per-message handler (terminal — returns finalized handler) */\n onMessage(\n fn: FifoQueueMessageFn<T, C>\n ): FifoQueueHandler<T, C>;\n\n /** Batch handler (terminal — returns finalized handler) */\n onMessageBatch(\n fn: FifoQueueBatchFn<T, C>\n ): FifoQueueHandler<T, C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a FIFO SQS queue with a Lambda message handler.\n *\n * @see {@link https://effortless-aws.website/use-cases/queue | Queue guide}\n *\n * @example\n * ```typescript\n * export const notifications = defineFifoQueue({ batchSize: 5, schema: (i) => NotifSchema.parse(i) })\n * .onMessageBatch(async ({ messages }) => {\n * await sendAll(messages.map(m => m.body));\n * })\n * ```\n */\nexport function defineFifoQueue<T = unknown>(): FifoQueueBuilder<T>;\nexport function defineFifoQueue<T = unknown>(\n options: FifoQueueOptions<T>,\n): FifoQueueBuilder<T>;\nexport function defineFifoQueue<T = unknown>(\n options?: FifoQueueOptions<T>,\n): FifoQueueBuilder<T> {\n const {\n schema,\n ...queueConfig\n } = options ?? {} as FifoQueueOptions<T>;\n\n const spec: FifoQueueConfig = { ...queueConfig };\n\n const state: {\n spec: FifoQueueConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n schema?: (input: unknown) => T;\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onMessage?: (...args: any[]) => any;\n onMessageBatch?: (...args: any[]) => any;\n } = {\n spec,\n ...(schema ? { schema } : {}),\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): FifoQueueHandler<T> => ({\n __brand: \"effortless-fifo-queue\",\n __spec: state.spec,\n ...(state.schema ? { schema: state.schema } : {}),\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onMessage ? { onMessage: state.onMessage } : {}),\n ...(state.onMessageBatch ? { onMessageBatch: state.onMessageBatch } : {}),\n }) as FifoQueueHandler<T>;\n\n const builder: FifoQueueBuilder<T> = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob: string) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onMessage(fn) {\n state.onMessage = fn as any;\n return finalize() as any;\n },\n onMessageBatch(fn) {\n state.onMessageBatch = fn as any;\n return finalize() as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n };\n\n return builder;\n}\n","import type { AnySecretRef, ResolveConfig, Duration, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\nimport type { BucketClient, BucketClientWithEntities } from \"../runtime/bucket-client\";\n\n/**\n * Per-entity configuration for typed JSON key-value storage within a bucket.\n */\nexport type BucketEntityConfig = {\n /** Cache duration for CloudFront/browser caching (e.g., \"10s\", \"5m\", \"1h\"). No caching if omitted. */\n cache?: Duration;\n};\n\n/**\n * Configuration options for defineBucket.\n */\nexport type BucketConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: { memory?: number; timeout?: import(\"./handler-options\").Duration; logLevel?: import(\"./handler-options\").LogLevel; permissions?: import(\"./handler-options\").Permission[] };\n /** S3 key prefix filter for event notifications (e.g., \"uploads/\") */\n prefix?: string;\n /** S3 key suffix filter for event notifications (e.g., \".jpg\") */\n suffix?: string;\n /** Typed JSON entity definitions for key-value storage */\n entities?: Record<string, BucketEntityConfig>;\n /** Local directory to seed into bucket on deploy (only uploads files that don't already exist) */\n seed?: string;\n /** Local directory to sync into bucket on every deploy (uploads new/changed, deletes removed) */\n sync?: string;\n};\n\n/**\n * S3 event record passed to onObjectCreated/onObjectRemoved callbacks.\n */\nexport type BucketEvent = {\n /** S3 event name (e.g., \"ObjectCreated:Put\", \"ObjectRemoved:Delete\") */\n eventName: string;\n /** Object key (path within the bucket) */\n key: string;\n /** Object size in bytes (present for created events) */\n size?: number;\n /** Object ETag (present for created events) */\n eTag?: string;\n /** ISO 8601 timestamp of when the event occurred */\n eventTime?: string;\n /** S3 bucket name */\n bucketName: string;\n};\n\n// ============ Setup args ============\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n/** Setup factory — receives bucket/deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean, Entities extends Record<string, any> = {}> =\n & { bucket: {} extends Entities ? BucketClient : BucketClientWithEntities<Entities> }\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/**\n * Callback function type for S3 ObjectCreated events\n */\nexport type BucketObjectCreatedFn<C = undefined> =\n (args: { event: BucketEvent }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n/**\n * Callback function type for S3 ObjectRemoved events\n */\nexport type BucketObjectRemovedFn<C = undefined> =\n (args: { event: BucketEvent }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n// ============ Internal handler object ============\n\n/**\n * Internal handler object created by defineBucket\n * @internal\n */\nexport type BucketHandler<C = any, _Entities extends Record<string, any> = {}> = {\n readonly __brand: \"effortless-bucket\";\n readonly __spec: BucketConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onObjectCreated?: (...args: any[]) => any;\n readonly onObjectRemoved?: (...args: any[]) => any;\n};\n\n// ============ Builder options ============\n\n/** Options passed to `defineBucket()` — static config */\ntype BucketOptions = {\n /** S3 key prefix filter for event notifications (e.g., \"uploads/\") */\n prefix?: string;\n /** S3 key suffix filter for event notifications (e.g., \".jpg\") */\n suffix?: string;\n /** Local directory to seed into bucket on deploy (only uploads files that don't already exist) */\n seed?: string;\n /** Local directory to sync into bucket on every deploy (uploads new/changed, deletes removed) */\n sync?: string;\n};\n\n// ============ Builder ============\n\ninterface BucketBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n Entities extends Record<string, any> = {},\n> {\n /** Declare handler dependencies */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): BucketBuilder<D2, P, C, HasFiles, Entities>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): BucketBuilder<D, P2, C, HasFiles, Entities>;\n\n /** Include static files in the Lambda ZIP */\n include(glob: string): BucketBuilder<D, P, C, true, Entities>;\n\n /** Register a typed JSON entity stored as `{name}/{id}.json` in the bucket */\n entity<N extends string, T>(\n name: N,\n options?: BucketEntityConfig,\n ): BucketBuilder<D, P, C, HasFiles, Entities & { [K in N]: T }>;\n\n /** Initialize shared state on cold start with lambda options */\n setup(lambda: LambdaOptions): BucketBuilder<D, P, C, HasFiles, Entities>;\n /** Initialize shared state on cold start. Receives bucket (self-client), deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles, Entities>) => C2 | Promise<C2>\n ): BucketBuilder<D, P, C2, HasFiles, Entities>;\n /** Initialize shared state on cold start with lambda options. Receives bucket (self-client), deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles, Entities>) => C2 | Promise<C2>,\n lambda: LambdaOptions,\n ): BucketBuilder<D, P, C2, HasFiles, Entities>;\n\n /** Handle errors thrown by callbacks */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): BucketBuilder<D, P, C, HasFiles, Entities>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): BucketBuilder<D, P, C, HasFiles, Entities>;\n\n /** Handle S3 ObjectCreated events (terminal — returns finalized handler) */\n onObjectCreated(\n fn: BucketObjectCreatedFn<C>\n ): BucketHandler<C, Entities>;\n\n /** Handle S3 ObjectRemoved events (terminal — returns finalized handler) */\n onObjectRemoved(\n fn: BucketObjectRemovedFn<C>\n ): BucketHandler<C, Entities>;\n\n /** Finalize as resource-only bucket (no Lambda) */\n build(): BucketHandler<C, Entities>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define an S3 bucket with optional event handlers.\n *\n * @see {@link https://effortless-aws.website/use-cases/storage | Storage guide}\n *\n * @example\n * ```typescript\n * export const uploads = defineBucket({ prefix: \"images/\", suffix: \".jpg\" })\n * .onObjectCreated(async ({ event, bucket }) => {\n * console.log(\"New upload:\", event.key);\n * })\n * ```\n */\nexport function defineBucket(): BucketBuilder;\nexport function defineBucket(\n options: BucketOptions,\n): BucketBuilder;\nexport function defineBucket(\n options?: BucketOptions,\n): BucketBuilder {\n const bucketConfig = options ?? {} as BucketOptions;\n\n const spec: BucketConfig = {\n ...bucketConfig,\n };\n\n const state: {\n spec: BucketConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onObjectCreated?: (...args: any[]) => any;\n onObjectRemoved?: (...args: any[]) => any;\n } = {\n spec,\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): BucketHandler => ({\n __brand: \"effortless-bucket\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onObjectCreated ? { onObjectCreated: state.onObjectCreated } : {}),\n ...(state.onObjectRemoved ? { onObjectRemoved: state.onObjectRemoved } : {}),\n }) as BucketHandler;\n\n const builder: BucketBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob: string) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n entity(name: string, options?: BucketEntityConfig) {\n state.spec = {\n ...state.spec,\n entities: { ...state.spec.entities, [name]: options ?? {} },\n };\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onObjectCreated(fn) {\n state.onObjectCreated = fn as any;\n return finalize() as any;\n },\n onObjectRemoved(fn) {\n state.onObjectRemoved = fn as any;\n return finalize() as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n build() {\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","/**\n * Configuration options for defining a mailer (SES email identity)\n */\nexport type MailerConfig = {\n /** Domain to verify and send emails from (e.g., \"myapp.com\") */\n domain: string;\n};\n\n/**\n * Internal handler object created by defineMailer\n * @internal\n */\nexport type MailerHandler = {\n readonly __brand: \"effortless-mailer\";\n readonly __spec: MailerConfig;\n};\n\n/**\n * Define an email sender backed by Amazon SES.\n *\n * Creates an SES Email Identity for the specified domain and provides\n * a typed `EmailClient` to other handlers via `deps`.\n *\n * On first deploy, DKIM DNS records are printed to the console.\n * Add them to your DNS provider to verify the domain.\n *\n * @see {@link https://effortless-aws.website/use-cases/email | Email guide}\n *\n * @param options - Mailer configuration with the domain to send from\n * @returns Handler object used by the deployment system and as a `deps` value\n *\n * @example Basic mailer with HTTP handler\n * ```typescript\n * export const mailer = defineMailer({ domain: \"myapp.com\" });\n *\n * export const api = defineApi({\n * basePath: \"/signup\",\n * deps: { mailer },\n * post: async ({ req, deps }) => {\n * await deps.mailer.send({\n * from: \"hello@myapp.com\",\n * to: req.body.email,\n * subject: \"Welcome!\",\n * html: \"<h1>Hi!</h1>\",\n * });\n * return { status: 200, body: { ok: true } };\n * },\n * });\n * ```\n */\nexport const defineMailer = () => (options: MailerConfig): MailerHandler => ({\n __brand: \"effortless-mailer\",\n __spec: options,\n});\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { LambdaWithPermissions, AnySecretRef, ResolveConfig, Duration, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory, toSeconds } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles, ResponseStream } from \"./shared\";\nimport type { HttpRequest, HttpResponse } from \"./shared\";\nimport type { AuthHelpers } from \"./auth\";\n\n// ============ Auth types ============\n\n/** Auth config options (user-facing) */\nexport type AuthOptions<A = unknown> = {\n /** HMAC secret for signing session cookies. Use `secret()` or `param()` in config. */\n secret: string;\n /** Default session lifetime (default: \"7d\"). */\n expiresIn?: Duration;\n /** Optional API token strategy for external clients. */\n apiToken?: {\n /** HTTP header to read the token from. Default: \"authorization\" (strips \"Bearer \" prefix). */\n header?: string;\n /** Verify the token value and return session data, or null if invalid. */\n verify: (value: string) => A | null | Promise<A | null>;\n /** Cache verified token results for this duration. */\n cacheTtl?: Duration;\n };\n};\n\n/** Branded auth config — created by `enableAuth<A>()` helper, carries session type A */\nexport type ApiAuthConfig<A = unknown> = AuthOptions<A> & { readonly __sessionType: A };\n\n/** Type of the `enableAuth` helper injected into setup args */\nexport type EnableAuth = <A = unknown>(options: AuthOptions<A>) => ApiAuthConfig<A>;\n\n/** Runtime implementation of enableAuth — identity function with branded return type */\nexport const enableAuth: EnableAuth = <A = unknown>(options: AuthOptions<A>): ApiAuthConfig<A> =>\n options as ApiAuthConfig<A>;\n\n/** Extract session type A from ctx.auth if present */\ntype ExtractAuth<C> = C extends { auth: ApiAuthConfig<infer A> } ? A : undefined;\n\n/** Property names reserved by the framework — cannot be used in setup return */\ntype ReservedKeys = 'req' | 'input' | 'stream' | 'ok' | 'fail';\n\n// ============ Response helpers ============\n\n/** Success response helper: `ok({ data })` → `{ status: 200, body: { data } }` */\nexport type OkHelper = (body?: unknown, status?: number) => HttpResponse;\n/** Error response helper: `fail(\"message\")` → `{ status: 400, body: { error: \"message\" } }` */\nexport type FailHelper = (message: string, status?: number) => HttpResponse;\n\n// ============ Route types ============\n\ntype HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/** Cache options for a GET route. Duration shorthand (e.g. \"30s\", \"5m\") or object for fine-grained control. */\nexport type CacheOptions = Duration | {\n ttl: Duration;\n swr?: Duration;\n scope?: \"public\" | \"private\";\n};\n\n/** Resolved cache config with numeric seconds */\ntype ResolvedCache =\n | { private?: false; ttl: number; swr: number }\n | { private: true; ttl: number };\n\n/** Resolve CacheOptions into numeric seconds. Shorthand = public with swr = ttl * 2. */\nconst resolveCache = (cache: CacheOptions): ResolvedCache => {\n if (typeof cache === \"number\" || typeof cache === \"string\") {\n const ttl = toSeconds(cache);\n return { ttl, swr: ttl * 2 };\n }\n const ttl = toSeconds(cache.ttl);\n if (cache.scope === \"private\") {\n return { private: true, ttl };\n }\n return {\n ttl,\n swr: cache.swr != null ? toSeconds(cache.swr) : ttl * 2,\n };\n};\n\n/** Parsed route definition stored at runtime */\nexport type RouteEntry = {\n method: HttpMethod;\n path: string;\n onRequest: (...args: any[]) => any;\n schema?: unknown;\n public?: boolean;\n cache?: ResolvedCache;\n};\n\n/** Spread ctx into route args: Omit auth config, add AuthHelpers if present */\ntype SpreadCtx<C> =\n & ([C] extends [undefined] ? {} : Omit<C & {}, 'auth'>)\n & ([ExtractAuth<C>] extends [undefined] ? {} : { auth: AuthHelpers<ExtractAuth<C>> });\n\n/** Infer validated output type from a Standard Schema, or fall back to unknown */\ntype InferInput<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : unknown;\n\n/** Callback args available inside each route — ctx is spread into args */\ntype RouteArgs<C, ST, S = undefined> =\n & SpreadCtx<C>\n & { req: HttpRequest; input: InferInput<S>; ok: OkHelper; fail: FailHelper }\n & ([ST] extends [true] ? { stream: ResponseStream } : {});\n\n/** Route handler function */\ntype RouteHandler<C, ST, S = undefined> = (args: RouteArgs<C, ST, S>) => Promise<HttpResponse | void> | HttpResponse | void;\n\n/** Route definition — pass `input` for typed schema validation */\ntype RouteDef<S extends StandardSchemaV1 | undefined = undefined> = {\n path: `/${string}`;\n input?: S;\n public?: boolean;\n cache?: CacheOptions;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files/enableAuth based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & { enableAuth: EnableAuth; ok: OkHelper; fail: FailHelper }\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Validate that setup return type does not use reserved property names */\ntype ValidateSetupReturn<C> = C & { [K in ReservedKeys]?: never };\n\n// ============ Static config ============\n\n/** Static config extracted by AST (no runtime callbacks) */\nexport type ApiConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** Base path prefix for all routes (e.g., \"/api\") */\n basePath: `/${string}`;\n /** Enable response streaming. When true, the Lambda Function URL uses RESPONSE_STREAM invoke mode. */\n stream?: boolean;\n};\n\n// ============ Internal handler object ============\n\n/** Internal handler object created by defineApi */\nexport type ApiHandler<C = undefined> = {\n readonly __brand: \"effortless-api\";\n readonly __spec: ApiConfig;\n readonly onError?: (...args: any[]) => any | Promise<any>;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly routes?: RouteEntry[];\n};\n\n// ============ Builder options (plain values, no inference) ============\n\n/** Options passed to `defineApi()` */\ntype ApiOptions = {\n /** Base path prefix for all routes (e.g., \"/api\") */\n basePath: `/${string}`;\n /** Enable response streaming. When true, routes receive a `stream` arg for SSE. */\n stream?: boolean;\n};\n\n// ============ ApiRoutes — returned after first route method ============\n\n/**\n * Finalized API handler with route-adding methods.\n * Has `__brand` so CLI discovers it. Each `.get()/.post()` adds a route and returns self.\n */\nexport interface ApiRoutes<C = undefined, ST extends boolean = false> extends ApiHandler<C> {\n get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n}\n\n// ============ Builder ============\n\n/**\n * Builder interface for defining API handlers.\n *\n * Each method sets exactly one generic, so inference happens one step at a time.\n * This prevents cascading type errors when one property has a mistake.\n */\ninterface ApiBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n ST extends boolean = false,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): ApiBuilder<D2, P, C, ST, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): ApiBuilder<D, P2, C, ST, HasFiles>;\n\n /** Include static files by glob pattern */\n include(glob: string): ApiBuilder<D, P, C, ST, true>;\n\n /** Configure Lambda settings only (no init function) */\n setup(lambda: LambdaOptions): ApiBuilder<D, P, C, ST, HasFiles>;\n /** Initialize shared state on cold start. Receives deps/config/files based on what was declared. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>\n ): ApiBuilder<D, P, C2, ST, HasFiles>;\n /** Initialize shared state on cold start with Lambda config. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>,\n lambda: LambdaOptions,\n ): ApiBuilder<D, P, C2, ST, HasFiles>;\n\n /** Handle errors thrown by routes */\n onError(\n fn: (args: { error: unknown; req: HttpRequest; ok: OkHelper; fail: FailHelper } & SpreadCtx<C>) => HttpResponse | Promise<HttpResponse>\n ): ApiBuilder<D, P, C, ST, HasFiles>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): ApiBuilder<D, P, C, ST, HasFiles>;\n\n get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, S>): ApiRoutes<C, ST>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define an API with typed routes using a builder pattern.\n *\n * @see {@link https://effortless-aws.website/use-cases/http-api | HTTP API guide}\n *\n * @example\n * ```typescript\n * export const api = defineApi({ basePath: \"/api\", timeout: \"30s\" })\n * .deps(() => ({ users }))\n * .config(({ defineSecret }) => ({ dbUrl: defineSecret() }))\n * .setup(async ({ deps, config, enableAuth }) => ({\n * users: deps.users,\n * auth: enableAuth<Session>({ secret: config.dbUrl }),\n * }))\n * .onError(({ error, fail }) => fail(String(error), 500))\n * .get({ path: \"/me\" }, async ({ users, auth, ok }) => ok(auth.session))\n * .post({ path: \"/login\", public: true }, async ({ auth, ok }) => ok(await auth.createSession()))\n * ```\n */\nexport function defineApi<const O extends ApiOptions>(\n options: O,\n): ApiBuilder<undefined, undefined, undefined, O[\"stream\"] extends true ? true : false, false>;\nexport function defineApi(\n options: ApiOptions,\n): ApiBuilder {\n const { basePath, stream } = options;\n\n const state: {\n spec: ApiConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n routes: RouteEntry[];\n } = {\n spec: {\n basePath,\n ...(stream ? { stream } : {}),\n },\n routes: [],\n };\n\n const addRoute = (method: HttpMethod, def: { path: string; input?: unknown; public?: boolean; cache?: CacheOptions }, handler: Function) => {\n const routeCache = def.cache != null\n ? resolveCache(def.cache)\n : undefined;\n\n state.routes.push({\n method,\n path: def.path,\n onRequest: handler as any,\n ...(def.input ? { schema: def.input } : {}),\n ...(def.public ? { public: true } : {}),\n ...(routeCache ? { cache: routeCache } : {}),\n });\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): ApiRoutes => {\n const handler: any = {\n __brand: \"effortless-api\",\n __spec: state.spec,\n routes: state.routes,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n };\n\n // Add route methods to the finalized handler\n for (const m of [\"get\", \"post\", \"put\", \"patch\", \"delete\"] as const) {\n handler[m] = (def: any, fn: any) => {\n addRoute(m.toUpperCase() as HttpMethod, def, fn);\n handler.routes = state.routes;\n return handler;\n };\n }\n\n return handler as ApiRoutes;\n };\n\n const builder: ApiBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob: string) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n get(def: any, fn: any) { addRoute(\"GET\", def, fn); return finalize() as any; },\n post(def: any, fn: any) { addRoute(\"POST\", def, fn); return finalize() as any; },\n put(def: any, fn: any) { addRoute(\"PUT\", def, fn); return finalize() as any; },\n patch(def: any, fn: any) { addRoute(\"PATCH\", def, fn); return finalize() as any; },\n delete(def: any, fn: any) { addRoute(\"DELETE\", def, fn); return finalize() as any; },\n };\n\n return builder;\n}\n","import type { AnySecretRef, ResolveConfig, LambdaWithPermissions, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\nimport type { Timezone } from \"./timezone\";\n\n// ============ Schedule expression ============\n\n/** Singular/plural unit for rate expressions */\ntype RateUnit = \"minute\" | \"minutes\" | \"hour\" | \"hours\" | \"day\" | \"days\";\n\n/**\n * Rate expression: `rate(1 hour)`, `rate(5 minutes)`, `rate(2 days)`\n *\n * Strictly typed — autocomplete and compile-time validation for unit.\n */\ntype RateExpression = `rate(${number} ${RateUnit})`;\n\n/**\n * Cron expression: `cron(min hour dom month dow year)`\n *\n * Not deeply typed (too combinatorial for TS), but the `cron(...)` wrapper is enforced.\n *\n * @example\n * ```\n * \"cron(0 9 * * ? *)\" // daily at 9:00 UTC\n * \"cron(0 9 ? * MON-FRI *)\" // weekdays at 9:00\n * \"cron(0/15 * * * ? *)\" // every 15 minutes\n * ```\n */\ntype CronExpression = `cron(${string})`;\n\n/**\n * EventBridge Scheduler schedule expression.\n *\n * - **Rate**: `\"rate(5 minutes)\"`, `\"rate(1 hour)\"`, `\"rate(1 day)\"` — strictly typed units\n * - **Cron**: `\"cron(0 9 * * ? *)\"` — 6 fields: min hour dom month dow year\n */\nexport type ScheduleExpression = RateExpression | CronExpression;\n\n// ============ Static config ============\n\n/** Static config extracted at deploy time */\nexport type CronConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** EventBridge Scheduler schedule expression */\n schedule: ScheduleExpression;\n /** IANA timezone for the schedule (default: UTC) */\n timezone?: Timezone;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n// ============ Tick handler ============\n\n/** Callback function for cron tick */\nexport type CronTickFn<C = undefined> =\n (args: SpreadCtx<C>) => Promise<void> | void;\n\n// ============ Handler type ============\n\n/**\n * Handler object created by defineCron.\n * @internal\n */\nexport type CronHandler<C = any> = {\n readonly __brand: \"effortless-cron\";\n readonly __spec: CronConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onTick?: (...args: any[]) => any;\n};\n\n// ============ Options ============\n\n/** Options passed to `defineCron()` — resource config only */\ntype CronOptions = {\n /** EventBridge Scheduler schedule expression: `\"rate(5 minutes)\"` or `\"cron(0 9 * * ? *)\"` */\n schedule: ScheduleExpression;\n /** IANA timezone for the schedule (default: UTC). Full autocomplete for all timezones. */\n timezone?: Timezone;\n};\n\n// ============ Builder ============\n\ninterface CronBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): CronBuilder<D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): CronBuilder<D, P2, C, HasFiles>;\n\n /** Include static files in the Lambda bundle. Chainable — call multiple times. */\n include(glob: string): CronBuilder<D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */\n setup(lambda: LambdaOptions): CronBuilder<D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): CronBuilder<D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): CronBuilder<D, P, C2, HasFiles>;\n\n /** Handle errors thrown by onTick */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): CronBuilder<D, P, C, HasFiles>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): CronBuilder<D, P, C, HasFiles>;\n\n /** Tick handler — called on each scheduled invocation (terminal) */\n onTick(\n fn: CronTickFn<C>\n ): CronHandler<C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a cron job — scheduled Lambda invocation via EventBridge Scheduler.\n *\n * @example\n * ```typescript\n * export const sync = defineCron({ schedule: \"cron(0 9 * * ? *)\" })\n * .deps(() => ({ orders }))\n * .config(({ defineSecret }) => ({ apiKey: defineSecret() }))\n * .include(\"templates/*.html\")\n * .setup(async ({ deps, config, files }) => ({\n * db: deps.orders, key: config.apiKey, tpl: files,\n * }), { memory: 512 })\n * .onTick(async ({ db, key, tpl }) => {\n * const html = tpl.read(\"templates/report.html\")\n * })\n * ```\n */\nexport function defineCron(options: CronOptions): CronBuilder {\n const { schedule, timezone } = options;\n\n const spec: CronConfig = {\n schedule,\n ...(timezone ? { timezone } : {}),\n };\n\n const state: {\n spec: CronConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onTick?: (...args: any[]) => any;\n } = { spec };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): CronHandler => ({\n __brand: \"effortless-cron\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onTick ? { onTick: state.onTick } : {}),\n }) as CronHandler;\n\n const builder: CronBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n onTick(fn) {\n state.onTick = fn as any;\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","import type { AnySecretRef, ResolveConfig, LambdaWithPermissions, ConfigFactory, LambdaOptions, Duration } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\n\n// ============ Static config ============\n\n/** Fargate container size presets */\nexport type FargateSize =\n | \"0.25vCPU-512mb\"\n | \"0.5vCPU-1gb\"\n | \"1vCPU-2gb\"\n | \"2vCPU-4gb\"\n | \"4vCPU-8gb\";\n\n/** Static config extracted at deploy time */\nexport type WorkerConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** Fargate size (default: \"0.5vCPU-1gb\") */\n size?: FargateSize;\n /** How long the worker stays alive without messages before shutting down (default: \"5m\") */\n idleTimeout?: Duration;\n /** Max messages processed in parallel (default: 1, max: 10) */\n concurrency?: number;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n// ============ Handler callback ============\n\n/** Callback function for the worker's onMessage */\nexport type WorkerMessageFn<T, C = undefined> =\n (msg: T, ctx: SpreadCtx<C>) => Promise<void> | void;\n\n// ============ Handler type ============\n\n/**\n * Handler object created by defineWorker.\n * @internal\n */\nexport type WorkerHandler<_T = any, C = any> = {\n readonly __brand: \"effortless-worker\";\n readonly __spec: WorkerConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onMessage?: (...args: any[]) => any;\n};\n\n// ============ Options ============\n\n/** Options passed to `defineWorker()` — resource config only */\ntype WorkerOptions = {\n /** Fargate size (default: \"0.5vCPU-1gb\") */\n size?: FargateSize;\n /** How long the worker stays alive without messages before shutting down (default: \"5m\") */\n idleTimeout?: Duration;\n /** Max messages processed in parallel (default: 1, max: 10) */\n concurrency?: number;\n};\n\n// ============ Builder ============\n\ninterface WorkerBuilder<\n T,\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers, workers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): WorkerBuilder<T, D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): WorkerBuilder<T, D, P2, C, HasFiles>;\n\n /** Include static files in the bundle. Chainable — call multiple times. */\n include(glob: string): WorkerBuilder<T, D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */\n setup(lambda: LambdaOptions): WorkerBuilder<T, D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): WorkerBuilder<T, D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): WorkerBuilder<T, D, P, C2, HasFiles>;\n\n /** Handle errors thrown by onMessage. Return \"delete\" to discard, \"retry\" to reprocess (default). */\n onError(\n fn: (args: { error: unknown; msg: T; retryCount: number } & SpreadCtx<C>) => \"retry\" | \"delete\" | void | Promise<\"retry\" | \"delete\" | void>\n ): WorkerBuilder<T, D, P, C, HasFiles>;\n\n /** Cleanup callback — runs when the worker shuts down */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): WorkerBuilder<T, D, P, C, HasFiles>;\n\n /** Process a single message from the queue (terminal) */\n onMessage(\n fn: WorkerMessageFn<T, C>\n ): WorkerHandler<T, C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a worker — a long-running Fargate container with an SQS queue.\n *\n * The worker stays alive while processing messages and shuts down after\n * an idle timeout with no new messages. Other handlers can send messages\n * to the worker via `deps.worker.send(msg)`.\n *\n * @typeParam T - Type of messages the worker receives via its queue\n *\n * @example\n * ```typescript\n * type Job = { type: \"export\"; userId: string }\n *\n * export const worker = defineWorker<Job>({ memory: 2048, concurrency: 5 })\n * .deps(() => ({ orders }))\n * .setup(async ({ deps }) => ({ db: deps.orders }))\n * .onMessage(async (msg, { db }) => {\n * await processJob(msg, db)\n * })\n * ```\n */\nexport function defineWorker<T = unknown>(options?: WorkerOptions): WorkerBuilder<T> {\n const spec: WorkerConfig = {\n ...(options?.size ? { size: options.size } : {}),\n ...(options?.idleTimeout ? { idleTimeout: options.idleTimeout } : {}),\n ...(options?.concurrency ? { concurrency: options.concurrency } : {}),\n };\n\n const state: {\n spec: WorkerConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onMessage?: (...args: any[]) => any;\n } = { spec };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): WorkerHandler => ({\n __brand: \"effortless-worker\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onMessage ? { onMessage: state.onMessage } : {}),\n }) as WorkerHandler;\n\n const builder: WorkerBuilder<T> = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n onMessage(fn) {\n state.onMessage = fn as any;\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","import type { StandardSchemaV1, StandardJSONSchemaV1 } from \"@standard-schema/spec\";\nimport type { AnySecretRef, ResolveConfig, LambdaWithPermissions, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\n\n// ============ Static config ============\n\n/** Static config extracted at deploy time */\nexport type McpConfig = {\n /** MCP server name (used in server info) */\n name: string;\n /** MCP server version (default: \"1.0.0\") */\n version?: string;\n /** Human-readable description — sent to clients in initialize response as system prompt context */\n instructions?: string;\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n};\n\n// ============ MCP tool types ============\n\n/** JSON Schema for a tool's input parameters */\nexport type McpInputSchema = {\n type: \"object\";\n properties: Record<string, unknown>;\n required?: string[];\n};\n\n/** Content block returned by a tool handler */\nexport type McpToolContent =\n | { type: \"text\"; text: string }\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"resource\"; resource: { uri: string; text: string; mimeType?: string } };\n\n/** Result returned by a tool handler */\nexport type McpToolResult = {\n content: McpToolContent[];\n isError?: boolean;\n};\n\n// ============ MCP resource types ============\n\n/** Content returned when reading a resource */\nexport type McpResourceContent =\n | { uri: string; mimeType?: string; text: string }\n | { uri: string; mimeType?: string; blob: string };\n\n/** Legacy resource content result type used by runtime internals */\ntype McpResourceResult = McpResourceContent | McpResourceContent[] | Promise<McpResourceContent | McpResourceContent[]>;\n\n/** Infer resource params type from schema, or fall back to Record<string, string> */\ntype InferResourceParams<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : Record<string, string>;\n\n/** Resource definition — pass `params` for typed schema validation on URI template params */\nexport type McpResourceDefInput<S extends StandardSchemaV1 | undefined = undefined> = {\n /** Resource URI or URI template (e.g. \"resource://contacts/{id}\") */\n uri: string;\n /** Human-readable name */\n name: string;\n /** Schema for URI template params — provides type inference + validation */\n params?: S;\n /** Optional description */\n description?: string;\n /** Optional MIME type */\n mimeType?: string;\n};\n\n/** What resource handlers can return — plain data is auto-wrapped by the runtime */\ntype McpResourceReturn = unknown | Promise<unknown>;\n\n/** Resource handler — receives params (typed or Record<string, string>) and ctx */\ntype McpResourceHandler<C, S = undefined> = (params: InferResourceParams<S>, ctx: SpreadCtx<C>) => McpResourceReturn;\n\n// Legacy types used by wrap-mcp runtime\n/** @internal */\nexport type McpResourceDef<C = undefined> = {\n name: string;\n description?: string;\n mimeType?: string;\n handler: (ctx: SpreadCtx<C>) => McpResourceResult;\n};\n\n/** @internal */\nexport type McpResourceTemplateDef<C = undefined> = {\n name: string;\n description?: string;\n mimeType?: string;\n handler: (params: Record<string, string>, ctx: SpreadCtx<C>) => McpResourceResult;\n};\n\n/** @internal */\nexport type McpResourceMap<C = undefined> = {\n [uriOrTemplate: string]: McpResourceDef<C> | McpResourceTemplateDef<C>;\n};\n\n// ============ MCP prompt types ============\n\n/** An argument accepted by a prompt */\nexport type McpPromptArgument = {\n /** Argument name */\n name: string;\n /** Optional description */\n description?: string;\n /** Whether the argument is required (default: false) */\n required?: boolean;\n};\n\n/** Content block inside a prompt message */\nexport type McpPromptContent =\n | { type: \"text\"; text: string }\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"audio\"; data: string; mimeType: string }\n | { type: \"resource\"; resource: { uri: string; mimeType?: string; text?: string; blob?: string } };\n\n/** A single message returned by a prompt */\nexport type McpPromptMessage = {\n role: \"user\" | \"assistant\";\n content: McpPromptContent;\n};\n\n/** Result returned by a prompt handler */\nexport type McpPromptResult = {\n description?: string;\n messages: McpPromptMessage[];\n};\n\n/** @internal Legacy prompt definition used by runtime */\nexport type McpPromptDef<C = undefined> = {\n description?: string;\n arguments?: McpPromptArgument[];\n handler: (args: Record<string, string>, ctx: SpreadCtx<C>) => McpPromptResult | Promise<McpPromptResult>;\n};\n\n/** Infer prompt args type from schema, or fall back to Record<string, string> */\ntype InferPromptArgs<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : Record<string, string>;\n\n/** Prompt definition — pass a Standard Schema for `args` for typed validation, or McpPromptArgument[] for untyped */\nexport type McpPromptDefInput<S extends StandardSchemaV1 | McpPromptArgument[] | undefined = undefined> = {\n /** Prompt name */\n name: string;\n /** Human-readable description */\n description?: string;\n /** Args: Standard Schema for typed validation, or McpPromptArgument[] for untyped */\n args?: S;\n};\n\n/** Handler return: string auto-wraps as user message, or return full McpPromptResult */\ntype McpPromptReturn = string | McpPromptResult | Promise<string | McpPromptResult>;\n\n/** Prompt handler — receives args (typed or Record<string, string>) and ctx */\ntype McpPromptHandler<C, S = undefined> = (args: InferPromptArgs<S>, ctx: SpreadCtx<C>) => McpPromptReturn;\n\n/** Infer tool input type from schema, or fall back to any */\ntype InferToolInput<S> = S extends StandardJSONSchemaV1 ? StandardJSONSchemaV1.InferOutput<S> : any;\n\n/** Tool definition — pass a StandardJSONSchemaV1 for `input` for typed validation, or McpInputSchema for raw JSON Schema */\nexport type McpToolDefInput<S extends StandardJSONSchemaV1 | McpInputSchema = McpInputSchema> = {\n /** Tool name */\n name: string;\n /** Human-readable description of the tool */\n description: string;\n /** Schema for tool input: StandardJSONSchemaV1 (e.g. z.object({...})) or raw McpInputSchema */\n input: S;\n};\n\n/** Tool handler — receives input (typed or any) and ctx */\ntype McpToolHandler<C, S = McpInputSchema> = (input: InferToolInput<S>, ctx: SpreadCtx<C>) => unknown | Promise<unknown>;\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files/enableAuth based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & { enableAuth: import(\"./define-api\").EnableAuth }\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n// ============ Handler type ============\n\n/**\n * Handler object created by defineMcp.\n * @internal\n */\nexport type McpHandler<C = any> = {\n readonly __brand: \"effortless-mcp\";\n readonly __spec: McpConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly resources?: (...args: any[]) => any;\n readonly prompts?: (...args: any[]) => any;\n readonly tools?: (...args: any[]) => any;\n};\n\n// ============ McpEntries — returned after first singular method ============\n\n/**\n * Finalized MCP handler with chainable registration methods.\n * Has `__brand` so CLI discovers it. Each `.tool()/.resource()/.prompt()` adds an entry and returns self.\n */\nexport interface McpEntries<C = undefined> extends McpHandler<C> {\n /** Register a tool */\n tool<S extends StandardJSONSchemaV1 | McpInputSchema = McpInputSchema>(def: McpToolDefInput<S>, handler: McpToolHandler<C, S>): McpEntries<C>;\n /** Register a resource */\n resource<S extends StandardSchemaV1 | undefined = undefined>(def: McpResourceDefInput<S>, handler: McpResourceHandler<C, S>): McpEntries<C>;\n /** Register a prompt */\n prompt<S extends StandardSchemaV1 | McpPromptArgument[] | undefined = undefined>(def: McpPromptDefInput<S>, handler: McpPromptHandler<C, S>): McpEntries<C>;\n}\n\n// ============ Options ============\n\n/** Options passed to `defineMcp()` */\ntype McpOptions = {\n /** MCP server name (used in server info) */\n name: string;\n /** MCP server version (default: \"1.0.0\") */\n version?: string;\n /** Human-readable description — sent to clients in initialize response as system prompt context */\n instructions?: string;\n};\n\n// ============ Builder ============\n\ninterface McpBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers, workers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): McpBuilder<D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): McpBuilder<D, P2, C, HasFiles>;\n\n /** Include static files in the bundle. Chainable — call multiple times. */\n include(glob: string): McpBuilder<D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */\n setup(lambda: LambdaOptions): McpBuilder<D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): McpBuilder<D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): McpBuilder<D, P, C2, HasFiles>;\n\n /** Handle errors thrown by tool handlers */\n onError(\n fn: (args: { error: unknown; toolName: string } & SpreadCtx<C>) => void | Promise<void>\n ): McpBuilder<D, P, C, HasFiles>;\n\n /** Cleanup callback — runs on shutdown */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): McpBuilder<D, P, C, HasFiles>;\n\n /** Register a tool */\n tool<S extends StandardJSONSchemaV1 | McpInputSchema = McpInputSchema>(def: McpToolDefInput<S>, handler: McpToolHandler<C, S>): McpEntries<C>;\n /** Register a resource */\n resource<S extends StandardSchemaV1 | undefined = undefined>(def: McpResourceDefInput<S>, handler: McpResourceHandler<C, S>): McpEntries<C>;\n /** Register a prompt */\n prompt<S extends StandardSchemaV1 | McpPromptArgument[] | undefined = undefined>(def: McpPromptDefInput<S>, handler: McpPromptHandler<C, S>): McpEntries<C>;\n\n /** Finalize the handler without adding more entries */\n build(): McpHandler<C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define an MCP (Model Context Protocol) server endpoint.\n *\n * Creates a Lambda-backed MCP server that exposes tools, resources, and prompts\n * for AI models and MCP-compatible clients via Streamable HTTP (JSON-RPC over POST).\n *\n * @see https://modelcontextprotocol.io/specification/2025-03-26 — MCP specification\n * @see https://effortless-aws.com/use-cases/mcp-server/ — full documentation with examples\n */\nexport function defineMcp(options: McpOptions): McpBuilder {\n const spec: McpConfig = {\n name: options.name,\n ...(options.version ? { version: options.version } : {}),\n ...(options.instructions ? { instructions: options.instructions } : {}),\n };\n\n const state: {\n spec: McpConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n toolEntries: [string, any][];\n resourceEntries: [string, any][];\n promptEntries: [string, any][];\n } = { spec, toolEntries: [], resourceEntries: [], promptEntries: [] };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n // Build factory functions from accumulated entries\n const buildToolsFactory = () =>\n state.toolEntries.length > 0\n ? () => Object.fromEntries(state.toolEntries)\n : undefined;\n\n const buildResourcesFactory = () =>\n state.resourceEntries.length > 0\n ? () => Object.fromEntries(state.resourceEntries)\n : undefined;\n\n const buildPromptsFactory = () =>\n state.promptEntries.length > 0\n ? () => Object.fromEntries(state.promptEntries)\n : undefined;\n\n const finalize = (): McpHandler => {\n const tools = buildToolsFactory();\n const resources = buildResourcesFactory();\n const prompts = buildPromptsFactory();\n return {\n __brand: \"effortless-mcp\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(resources ? { resources } : {}),\n ...(prompts ? { prompts } : {}),\n ...(tools ? { tools } : {}),\n } as McpHandler;\n };\n\n const finalizeWithEntries = (): McpEntries => {\n const handler: any = finalize();\n\n handler.tool = (def: any, fn: any) => {\n state.toolEntries.push([def.name, { description: def.description, input: def.input, handler: fn }]);\n handler.tools = buildToolsFactory();\n return handler;\n };\n\n handler.resource = (def: any, fn: any) => {\n const entry = { name: def.name, ...(def.description ? { description: def.description } : {}), ...(def.mimeType ? { mimeType: def.mimeType } : {}), handler: fn, ...(def.params ? { params: def.params } : {}) };\n state.resourceEntries.push([def.uri, entry]);\n handler.resources = buildResourcesFactory();\n return handler;\n };\n\n handler.prompt = (def: any, fn: any) => {\n const entry = { ...(def.description ? { description: def.description } : {}), args: def.args, handler: fn };\n state.promptEntries.push([def.name, entry]);\n handler.prompts = buildPromptsFactory();\n return handler;\n };\n\n return handler as McpEntries;\n };\n\n const builder: McpBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n tool(def: any, fn: any) {\n state.toolEntries.push([def.name, { description: def.description, input: def.input, handler: fn }]);\n return finalizeWithEntries() as any;\n },\n resource(def: any, fn: any) {\n const entry = { name: def.name, ...(def.description ? { description: def.description } : {}), ...(def.mimeType ? { mimeType: def.mimeType } : {}), handler: fn, ...(def.params ? { params: def.params } : {}) };\n state.resourceEntries.push([def.uri, entry]);\n return finalizeWithEntries() as any;\n },\n prompt(def: any, fn: any) {\n const entry = { ...(def.description ? { description: def.description } : {}), args: def.args, handler: fn };\n state.promptEntries.push([def.name, entry]);\n return finalizeWithEntries() as any;\n },\n build() {\n return finalize() as any;\n },\n };\n\n return builder;\n}\n"],"mappings":";AAwEO,IAAM,eAAe,CAAC,WAA+C;;;AC7BrE,IAAM,YAAY,CAAC,MAAwB;AAChD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,QAAM,QAAQ,EAAE,MAAM,4BAA4B;AAClD,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sBAAsB,CAAC,GAAG;AACtD,QAAM,IAAI,OAAO,MAAM,CAAC,CAAC;AACzB,QAAM,OAAO,MAAM,CAAC;AACpB,MAAI,SAAS,IAAK,QAAO,IAAI;AAC7B,MAAI,SAAS,IAAK,QAAO,IAAI;AAC7B,MAAI,SAAS,IAAK,QAAO,IAAI;AAC7B,SAAO;AACT;AAsGO,IAAM,eAA+B,CAC1C,YACiB;AACjB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,GAAI,SAAS,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,IAC3C,GAAI,SAAS,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IAC1D,GAAI,gBAAgB,WAAW,CAAC,KAAK,EAAE,WAAY,QAAgD,UAAU,IAAI,CAAC;AAAA,EACpH;AACF;AAGO,IAAM,gBAA+B,EAAE,aAAa;AAGpD,IAAM,uBAAuB,CAAI,WACtC,OAAO,aAAa;AAKf,IAAM,SAAS;AAMf,IAAM,QAAQ,CAAa,KAAa,cAAiD;AAC9F,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;AAEO,IAAM,cAAc,CAAC,UAAkB,OAAO,KAAK;AAEnD,IAAM,iBAAiB,CAAC,UAAkB,UAAU,KAAK;AAEzD,IAAM,eAAe,MAAM;;;AC8C3B,SAAS,YACd,SACiB;AACjB,QAAM;AAAA,IACJ;AAAA,IACA,GAAG;AAAA,EACL,IAAI,WAAW,CAAC;AAEhB,QAAM,OAAoB,EAAE,GAAG,YAAY;AAE3C,QAAM,QAWF;AAAA,IACF;AAAA,IACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EAC7B;AAEA,QAAM,qBAAqB,CAAC,WAA0B;AACpD,QAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,YAAM,OAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,WAAW,OAAwB;AAAA,IACvC,SAAS;AAAA,IACT,QAAQ,MAAM;AAAA,IACd,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,IAClD,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IACxD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACzC,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;AAAA,IACrD,GAAI,MAAM,gBAAgB,EAAE,eAAe,MAAM,cAAc,IAAI,CAAC;AAAA,EACtE;AAEA,QAAM,UAA2B;AAAA,IAC/B,KAAK,IAAI;AACP,YAAM,OAAO;AACb,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS,qBAAqB,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,MAAM;AACZ,YAAM,SAAS,CAAC,GAAI,MAAM,UAAU,CAAC,GAAI,IAAI;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YAAiB,aAA6B;AAClD,UAAI,OAAO,eAAe,YAAY;AACpC,cAAM,QAAQ;AACd,YAAI,YAAa,oBAAmB,WAAW;AAAA,MACjD,OAAO;AACL,2BAAmB,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,IACA,SAAS,IAAI;AACX,YAAM,WAAW;AACjB,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,cAAc,IAAI;AAChB,YAAM,gBAAgB;AACtB,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,QAAQ,IAAI;AACV,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AACN,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;;;ACzRO,IAAM,YAAY,MAAM,CAAC,aAAoC;AAAA,EAClE,SAAS;AAAA,EACT,QAAQ;AACV;;;ACtCO,IAAM,gBAAgB,CAAC,MAC5B,KAAK,QAAQ,OAAO,MAAM,YAAY,YAAY,KACjD,EAAU,UAAU,QAAQ,OAAQ,EAAU,WAAW,YACzD,EAAU,OAAO,YAAY;AAsFzB,IAAM,mBAAmB,MAAM,CAAC,aAAkD;AAAA,EACvF,SAAS;AAAA,EACT,QAAQ;AACV;;;ACmGO,SAAS,gBACd,SACqB;AACrB,QAAM;AAAA,IACJ;AAAA,IACA,GAAG;AAAA,EACL,IAAI,WAAW,CAAC;AAEhB,QAAM,OAAwB,EAAE,GAAG,YAAY;AAE/C,QAAM,QAWF;AAAA,IACF;AAAA,IACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EAC7B;AAEA,QAAM,qBAAqB,CAAC,WAA0B;AACpD,QAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,YAAM,OAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,WAAW,OAA4B;AAAA,IAC3C,SAAS;AAAA,IACT,QAAQ,MAAM;AAAA,IACd,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,IAClD,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IACxD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACzC,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IACxD,GAAI,MAAM,iBAAiB,EAAE,gBAAgB,MAAM,eAAe,IAAI,CAAC;AAAA,EACzE;AAEA,QAAM,UAA+B;AAAA,IACnC,KAAK,IAAI;AACP,YAAM,OAAO;AACb,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS,qBAAqB,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,MAAc;AACpB,YAAM,SAAS,CAAC,GAAI,MAAM,UAAU,CAAC,GAAI,IAAI;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YAAiB,aAA6B;AAClD,UAAI,OAAO,eAAe,YAAY;AACpC,cAAM,QAAQ;AACd,YAAI,YAAa,oBAAmB,WAAW;AAAA,MACjD,OAAO;AACL,2BAAmB,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,eAAe,IAAI;AACjB,YAAM,iBAAiB;AACvB,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,QAAQ,IAAI;AACV,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;AClGO,SAAS,aACd,SACe;AACf,QAAM,eAAe,WAAW,CAAC;AAEjC,QAAM,OAAqB;AAAA,IACzB,GAAG;AAAA,EACL;AAEA,QAAM,QAUF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,qBAAqB,CAAC,WAA0B;AACpD,QAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,YAAM,OAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,WAAW,OAAsB;AAAA,IACrC,SAAS;AAAA,IACT,QAAQ,MAAM;AAAA,IACd,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,IAClD,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IACxD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACzC,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,kBAAkB,EAAE,iBAAiB,MAAM,gBAAgB,IAAI,CAAC;AAAA,IAC1E,GAAI,MAAM,kBAAkB,EAAE,iBAAiB,MAAM,gBAAgB,IAAI,CAAC;AAAA,EAC5E;AAEA,QAAM,UAAyB;AAAA,IAC7B,KAAK,IAAI;AACP,YAAM,OAAO;AACb,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS,qBAAqB,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,MAAc;AACpB,YAAM,SAAS,CAAC,GAAI,MAAM,UAAU,CAAC,GAAI,IAAI;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,OAAO,MAAcA,UAA8B;AACjD,YAAM,OAAO;AAAA,QACX,GAAG,MAAM;AAAA,QACT,UAAU,EAAE,GAAG,MAAM,KAAK,UAAU,CAAC,IAAI,GAAGA,YAAW,CAAC,EAAE;AAAA,MAC5D;AACA,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YAAiB,aAA6B;AAClD,UAAI,OAAO,eAAe,YAAY;AACpC,cAAM,QAAQ;AACd,YAAI,YAAa,oBAAmB,WAAW;AAAA,MACjD,OAAO;AACL,2BAAmB,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,IACA,gBAAgB,IAAI;AAClB,YAAM,kBAAkB;AACxB,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,gBAAgB,IAAI;AAClB,YAAM,kBAAkB;AACxB,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,QAAQ,IAAI;AACV,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AACN,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;;;AC7OO,IAAM,eAAe,MAAM,CAAC,aAA0C;AAAA,EAC3E,SAAS;AAAA,EACT,QAAQ;AACV;;;ACcA,IAAM,eAAe,CAAC,UAAuC;AAC3D,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;AAC1D,UAAMC,OAAM,UAAU,KAAK;AAC3B,WAAO,EAAE,KAAAA,MAAK,KAAKA,OAAM,EAAE;AAAA,EAC7B;AACA,QAAM,MAAM,UAAU,MAAM,GAAG;AAC/B,MAAI,MAAM,UAAU,WAAW;AAC7B,WAAO,EAAE,SAAS,MAAM,IAAI;AAAA,EAC9B;AACA,SAAO;AAAA,IACL;AAAA,IACA,KAAK,MAAM,OAAO,OAAO,UAAU,MAAM,GAAG,IAAI,MAAM;AAAA,EACxD;AACF;AAqLO,SAAS,UACd,SACY;AACZ,QAAM,EAAE,UAAU,OAAO,IAAI;AAE7B,QAAM,QASF;AAAA,IACF,MAAM;AAAA,MACJ;AAAA,MACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC7B;AAAA,IACA,QAAQ,CAAC;AAAA,EACX;AAEA,QAAM,WAAW,CAAC,QAAoB,KAAgF,YAAsB;AAC1I,UAAM,aAAa,IAAI,SAAS,OAC5B,aAAa,IAAI,KAAK,IACtB;AAEJ,UAAM,OAAO,KAAK;AAAA,MAChB;AAAA,MACA,MAAM,IAAI;AAAA,MACV,WAAW;AAAA,MACX,GAAI,IAAI,QAAQ,EAAE,QAAQ,IAAI,MAAM,IAAI,CAAC;AAAA,MACzC,GAAI,IAAI,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;AAAA,MACrC,GAAI,aAAa,EAAE,OAAO,WAAW,IAAI,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH;AAEA,QAAM,qBAAqB,CAAC,WAA0B;AACpD,QAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,YAAM,OAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,WAAW,MAAiB;AAChC,UAAM,UAAe;AAAA,MACnB,SAAS;AAAA,MACT,QAAQ,MAAM;AAAA,MACd,QAAQ,MAAM;AAAA,MACd,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,MAClD,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,MACxD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,MAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,MACzC,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,MAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IACjD;AAGA,eAAW,KAAK,CAAC,OAAO,QAAQ,OAAO,SAAS,QAAQ,GAAY;AAClE,cAAQ,CAAC,IAAI,CAAC,KAAU,OAAY;AAClC,iBAAS,EAAE,YAAY,GAAiB,KAAK,EAAE;AAC/C,gBAAQ,SAAS,MAAM;AACvB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,QAAM,UAAsB;AAAA,IAC1B,KAAK,IAAI;AACP,YAAM,OAAO;AACb,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS,qBAAqB,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,MAAc;AACpB,YAAM,SAAS,CAAC,GAAI,MAAM,UAAU,CAAC,GAAI,IAAI;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YAAiB,aAA6B;AAClD,UAAI,OAAO,eAAe,YAAY;AACpC,cAAM,QAAQ;AACd,YAAI,YAAa,oBAAmB,WAAW;AAAA,MACjD,OAAO;AACL,2BAAmB,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,IAAI;AACV,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO;AAAA,IACT;AAAA,IACA,IAAI,KAAU,IAAS;AAAE,eAAS,OAAO,KAAK,EAAE;AAAG,aAAO,SAAS;AAAA,IAAU;AAAA,IAC7E,KAAK,KAAU,IAAS;AAAE,eAAS,QAAQ,KAAK,EAAE;AAAG,aAAO,SAAS;AAAA,IAAU;AAAA,IAC/E,IAAI,KAAU,IAAS;AAAE,eAAS,OAAO,KAAK,EAAE;AAAG,aAAO,SAAS;AAAA,IAAU;AAAA,IAC7E,MAAM,KAAU,IAAS;AAAE,eAAS,SAAS,KAAK,EAAE;AAAG,aAAO,SAAS;AAAA,IAAU;AAAA,IACjF,OAAO,KAAU,IAAS;AAAE,eAAS,UAAU,KAAK,EAAE;AAAG,aAAO,SAAS;AAAA,IAAU;AAAA,EACrF;AAEA,SAAO;AACT;;;ACxMO,SAAS,WAAW,SAAmC;AAC5D,QAAM,EAAE,UAAU,SAAS,IAAI;AAE/B,QAAM,OAAmB;AAAA,IACvB;AAAA,IACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,EACjC;AAEA,QAAM,QASF,EAAE,KAAK;AAEX,QAAM,qBAAqB,CAAC,WAA0B;AACpD,QAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,YAAM,OAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,WAAW,OAAoB;AAAA,IACnC,SAAS;AAAA,IACT,QAAQ,MAAM;AAAA,IACd,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,IAClD,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IACxD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACzC,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,EACjD;AAEA,QAAM,UAAuB;AAAA,IAC3B,KAAK,IAAI;AACP,YAAM,OAAO;AACb,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS,qBAAqB,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,MAAM;AACZ,YAAM,SAAS,CAAC,GAAI,MAAM,UAAU,CAAC,GAAI,IAAI;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YAAiB,aAA6B;AAClD,UAAI,OAAO,eAAe,YAAY;AACpC,cAAM,QAAQ;AACd,YAAI,YAAa,oBAAmB,WAAW;AAAA,MACjD,OAAO;AACL,2BAAmB,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,IAAI;AACV,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS;AACf,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;;;AC5FO,SAAS,aAA0B,SAA2C;AACnF,QAAM,OAAqB;AAAA,IACzB,GAAI,SAAS,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC9C,GAAI,SAAS,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;AAAA,IACnE,GAAI,SAAS,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,QASF,EAAE,KAAK;AAEX,QAAM,qBAAqB,CAAC,WAA0B;AACpD,QAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,YAAM,OAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,WAAW,OAAsB;AAAA,IACrC,SAAS;AAAA,IACT,QAAQ,MAAM;AAAA,IACd,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,IAClD,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IACxD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACzC,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,EAC1D;AAEA,QAAM,UAA4B;AAAA,IAChC,KAAK,IAAI;AACP,YAAM,OAAO;AACb,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS,qBAAqB,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,MAAM;AACZ,YAAM,SAAS,CAAC,GAAI,MAAM,UAAU,CAAC,GAAI,IAAI;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YAAiB,aAA6B;AAClD,UAAI,OAAO,eAAe,YAAY;AACpC,cAAM,QAAQ;AACd,YAAI,YAAa,oBAAmB,WAAW;AAAA,MACjD,OAAO;AACL,2BAAmB,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,IAAI;AACV,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;;;ACyEO,SAAS,UAAU,SAAiC;AACzD,QAAM,OAAkB;AAAA,IACtB,MAAM,QAAQ;AAAA,IACd,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,IACtD,GAAI,QAAQ,eAAe,EAAE,cAAc,QAAQ,aAAa,IAAI,CAAC;AAAA,EACvE;AAEA,QAAM,QAWF,EAAE,MAAM,aAAa,CAAC,GAAG,iBAAiB,CAAC,GAAG,eAAe,CAAC,EAAE;AAEpE,QAAM,qBAAqB,CAAC,WAA0B;AACpD,QAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,YAAM,OAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAGA,QAAM,oBAAoB,MACxB,MAAM,YAAY,SAAS,IACvB,MAAM,OAAO,YAAY,MAAM,WAAW,IAC1C;AAEN,QAAM,wBAAwB,MAC5B,MAAM,gBAAgB,SAAS,IAC3B,MAAM,OAAO,YAAY,MAAM,eAAe,IAC9C;AAEN,QAAM,sBAAsB,MAC1B,MAAM,cAAc,SAAS,IACzB,MAAM,OAAO,YAAY,MAAM,aAAa,IAC5C;AAEN,QAAM,WAAW,MAAkB;AACjC,UAAM,QAAQ,kBAAkB;AAChC,UAAM,YAAY,sBAAsB;AACxC,UAAM,UAAU,oBAAoB;AACpC,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ,MAAM;AAAA,MACd,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,MAClD,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,MACxD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,MAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,MACzC,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,MAC/C,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,MAC/C,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC7B,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,sBAAsB,MAAkB;AAC5C,UAAM,UAAe,SAAS;AAE9B,YAAQ,OAAO,CAAC,KAAU,OAAY;AACpC,YAAM,YAAY,KAAK,CAAC,IAAI,MAAM,EAAE,aAAa,IAAI,aAAa,OAAO,IAAI,OAAO,SAAS,GAAG,CAAC,CAAC;AAClG,cAAQ,QAAQ,kBAAkB;AAClC,aAAO;AAAA,IACT;AAEA,YAAQ,WAAW,CAAC,KAAU,OAAY;AACxC,YAAM,QAAQ,EAAE,MAAM,IAAI,MAAM,GAAI,IAAI,cAAc,EAAE,aAAa,IAAI,YAAY,IAAI,CAAC,GAAI,GAAI,IAAI,WAAW,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC,GAAI,SAAS,IAAI,GAAI,IAAI,SAAS,EAAE,QAAQ,IAAI,OAAO,IAAI,CAAC,EAAG;AAC9M,YAAM,gBAAgB,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC;AAC3C,cAAQ,YAAY,sBAAsB;AAC1C,aAAO;AAAA,IACT;AAEA,YAAQ,SAAS,CAAC,KAAU,OAAY;AACtC,YAAM,QAAQ,EAAE,GAAI,IAAI,cAAc,EAAE,aAAa,IAAI,YAAY,IAAI,CAAC,GAAI,MAAM,IAAI,MAAM,SAAS,GAAG;AAC1G,YAAM,cAAc,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC;AAC1C,cAAQ,UAAU,oBAAoB;AACtC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAEA,QAAM,UAAsB;AAAA,IAC1B,KAAK,IAAI;AACP,YAAM,OAAO;AACb,aAAO;AAAA,IACT;AAAA,IACA,OAAO,IAAI;AACT,YAAM,SAAS,qBAAqB,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,MAAM;AACZ,YAAM,SAAS,CAAC,GAAI,MAAM,UAAU,CAAC,GAAI,IAAI;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YAAiB,aAA6B;AAClD,UAAI,OAAO,eAAe,YAAY;AACpC,cAAM,QAAQ;AACd,YAAI,YAAa,oBAAmB,WAAW;AAAA,MACjD,OAAO;AACL,2BAAmB,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,IAAI;AACV,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAI;AACZ,YAAM,YAAY;AAClB,aAAO;AAAA,IACT;AAAA,IACA,KAAK,KAAU,IAAS;AACtB,YAAM,YAAY,KAAK,CAAC,IAAI,MAAM,EAAE,aAAa,IAAI,aAAa,OAAO,IAAI,OAAO,SAAS,GAAG,CAAC,CAAC;AAClG,aAAO,oBAAoB;AAAA,IAC7B;AAAA,IACA,SAAS,KAAU,IAAS;AAC1B,YAAM,QAAQ,EAAE,MAAM,IAAI,MAAM,GAAI,IAAI,cAAc,EAAE,aAAa,IAAI,YAAY,IAAI,CAAC,GAAI,GAAI,IAAI,WAAW,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC,GAAI,SAAS,IAAI,GAAI,IAAI,SAAS,EAAE,QAAQ,IAAI,OAAO,IAAI,CAAC,EAAG;AAC9M,YAAM,gBAAgB,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC;AAC3C,aAAO,oBAAoB;AAAA,IAC7B;AAAA,IACA,OAAO,KAAU,IAAS;AACxB,YAAM,QAAQ,EAAE,GAAI,IAAI,cAAc,EAAE,aAAa,IAAI,YAAY,IAAI,CAAC,GAAI,MAAM,IAAI,MAAM,SAAS,GAAG;AAC1G,YAAM,cAAc,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC;AAC1C,aAAO,oBAAoB;AAAA,IAC7B;AAAA,IACA,QAAQ;AACN,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;","names":["options","ttl"]}
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/handlers/handler-options.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/define-bucket.ts","../src/handlers/define-mailer.ts","../src/handlers/define-api.ts","../src/handlers/define-cron.ts","../src/handlers/define-worker.ts","../src/handlers/define-mcp.ts"],"sourcesContent":["/**\n * Configuration for an Effortless project.\n *\n * @see {@link https://effortless-aws.website/configuration | Configuration guide}\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 Lambda settings applied to all handlers unless overridden.\n *\n * All Lambdas run on ARM64 (Graviton2) architecture — ~20% cheaper than x86_64\n * with better price-performance for most workloads.\n */\n lambda?: {\n /**\n * Lambda memory in MB. AWS allocates proportional CPU —\n * 1769 MB gives one full vCPU.\n * @default 256\n */\n memory?: number;\n\n /**\n * Lambda timeout as a human-readable string.\n * AWS maximum is 15 minutes.\n * @example \"30 seconds\", \"5 minutes\"\n */\n timeout?: string;\n\n /**\n * Node.js Lambda runtime version.\n * @default \"nodejs24.x\"\n */\n runtime?: string;\n };\n\n};\n\n/** Helper function for type-safe configuration with TypeScript autocompletion. */\nexport const defineConfig = (config: EffortlessConfig): EffortlessConfig => config;\n","// Public helpers — this file must have ZERO heavy imports (no effect, no AWS SDK, no deploy code).\n// It is the source of truth for param() and related types used by the public API.\n\n// ============ Generate spec ============\n\n/** Generator spec for auto-creating secrets at deploy time. */\nexport type GenerateSpec = `hex:${number}` | `base64:${number}` | \"uuid\";\n\n// ============ Permissions ============\n\ntype AwsService =\n | \"dynamodb\"\n | \"s3\"\n | \"sqs\"\n | \"sns\"\n | \"ses\"\n | \"ssm\"\n | \"lambda\"\n | \"events\"\n | \"secretsmanager\"\n | \"cognito-idp\"\n | \"logs\";\n\nexport type Permission = `${AwsService}:${string}` | (string & {});\n\n// ============ Duration ============\n\n/**\n * Human-readable duration. Accepts a plain number (seconds) or a string\n * with a unit suffix: `\"30s\"`, `\"5m\"`, `\"1h\"`, `\"2d\"`.\n *\n * @example\n * ```typescript\n * timeout: 30 // 30 seconds\n * timeout: \"30s\" // 30 seconds\n * timeout: \"5m\" // 300 seconds\n * timeout: \"1h\" // 3600 seconds\n * retentionPeriod: \"4d\" // 345600 seconds\n * ```\n */\nexport type Duration = number | `${number}s` | `${number}m` | `${number}h` | `${number}d`;\n\n/** Convert a Duration to seconds. */\nexport const toSeconds = (d: Duration): number => {\n if (typeof d === \"number\") return d;\n const match = d.match(/^(\\d+(?:\\.\\d+)?)(s|m|h|d)$/);\n if (!match) throw new Error(`Invalid duration: \"${d}\"`);\n const n = Number(match[1]);\n const unit = match[2];\n if (unit === \"d\") return n * 86400;\n if (unit === \"h\") return n * 3600;\n if (unit === \"m\") return n * 60;\n return n;\n};\n\n// ============ Lambda config ============\n\n/** Logging verbosity level for Lambda handlers */\nexport type LogLevel = \"error\" | \"info\" | \"debug\";\n\n/**\n * Common Lambda configuration shared by all handler types.\n */\nexport type LambdaConfig = {\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout (default: 30s). Accepts seconds or duration string: `\"30s\"`, `\"5m\"` */\n timeout?: Duration;\n /** Logging verbosity: \"error\" (errors only), \"info\" (+ execution summary), \"debug\" (+ input/output). Default: \"info\" */\n logLevel?: LogLevel;\n};\n\n/**\n * Lambda configuration with additional IAM permissions.\n * Used by handler types that support custom permissions (http, table, fifo-queue).\n */\nexport type LambdaWithPermissions = LambdaConfig & {\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n};\n\n// ============ Lambda options (for .setup()) ============\n\n/**\n * Lambda configuration passed as argument to `.setup()`.\n * Common across all handler types that create a Lambda function.\n */\nexport type LambdaOptions = {\n /** Lambda memory in MB (default: 256) */\n memory?: number;\n /** Lambda timeout (default: 30s). Accepts seconds or duration string: `\"30s\"`, `\"5m\"` */\n timeout?: Duration;\n /** Additional IAM permissions for the Lambda */\n permissions?: Permission[];\n /** Logging verbosity: \"error\" (errors only), \"info\" (+ execution summary), \"debug\" (+ input/output). Default: \"info\" */\n logLevel?: LogLevel;\n};\n\n// ============ Secrets (SSM Parameters) ============\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnySecretRef = SecretRef<any>;\n\n/**\n * Reference to an SSM Parameter Store secret.\n *\n * @typeParam T - The resolved type after optional transform (default: string)\n */\nexport type SecretRef<T = string> = {\n readonly __brand: \"effortless-secret\";\n readonly key?: string;\n readonly generate?: GenerateSpec;\n readonly transform?: (raw: string) => T;\n};\n\n/**\n * Maps a config declaration to resolved value types.\n * `SecretRef<T>` resolves to `T`.\n *\n * @typeParam P - Record of config keys to SecretRef instances\n */\nexport type ResolveConfig<P> = {\n [K in keyof P]: P[K] extends SecretRef<infer T> ? T : string;\n};\n\n/** Options for `defineSecret()` without a transform. */\nexport type DefineSecretOptions = {\n /** Custom SSM key (default: derived from config property name in kebab-case) */\n key?: string;\n /** Generator spec for auto-creating the secret at deploy time: `\"hex:32\"`, `\"base64:32\"`, `\"uuid\"` */\n generate?: GenerateSpec;\n};\n\n/** Options for `defineSecret()` with a transform. */\nexport type DefineSecretOptionsWithTransform<T> = DefineSecretOptions & {\n /** Transform the raw SSM string value into a typed value */\n transform: (raw: string) => T;\n};\n\n/** The defineSecret helper function type, injected into config callbacks. */\nexport type DefineSecretFn = {\n (): SecretRef<string>;\n (options: DefineSecretOptions): SecretRef<string>;\n <T>(options: DefineSecretOptionsWithTransform<T>): SecretRef<T>;\n};\n\n/** Helpers injected into the `config` callback. */\nexport type ConfigHelpers = {\n defineSecret: DefineSecretFn;\n};\n\n/** Config factory: a function receiving helpers and returning a record of SecretRefs. */\nexport type ConfigFactory<P> = (helpers: ConfigHelpers) => P;\n\n/** The `defineSecret` implementation, passed to config callbacks. */\nexport const defineSecret: DefineSecretFn = <T = string>(\n options?: DefineSecretOptions | DefineSecretOptionsWithTransform<T>\n): SecretRef<T> => {\n return {\n __brand: \"effortless-secret\",\n ...(options?.key ? { key: options.key } : {}),\n ...(options?.generate ? { generate: options.generate } : {}),\n ...(\"transform\" in (options ?? {}) ? { transform: (options as DefineSecretOptionsWithTransform<T>).transform } : {}),\n } as SecretRef<T>;\n};\n\n/** Internal helpers object passed to config callbacks. */\nexport const configHelpers: ConfigHelpers = { defineSecret };\n\n/** Resolve a config factory to a plain record of SecretRefs. */\nexport const resolveConfigFactory = <P>(config: ConfigFactory<P>): P =>\n config(configHelpers);\n\n// ============ Backwards compatibility ============\n\n/** @deprecated Use `defineSecret()` inside a config callback instead. */\nexport const secret = defineSecret;\n/** @deprecated Use `SecretRef` instead */\nexport type ParamRef<T = string> = SecretRef<T>;\n/** @deprecated Use `AnySecretRef` instead */\nexport type AnyParamRef = AnySecretRef;\n/** @deprecated Use `defineSecret()` instead. */\nexport const param = <T = string>(key: string, transform?: (raw: string) => T): SecretRef<T> => {\n return {\n __brand: \"effortless-secret\",\n key,\n ...(transform ? { transform } : {}),\n } as SecretRef<T>;\n};\n/** @deprecated Use `defineSecret({ generate: \"hex:N\" })` instead. */\nexport const generateHex = (bytes: number) => `hex:${bytes}`;\n/** @deprecated Use `defineSecret({ generate: \"base64:N\" })` instead. */\nexport const generateBase64 = (bytes: number) => `base64:${bytes}`;\n/** @deprecated Use `defineSecret({ generate: \"uuid\" })` instead. */\nexport const generateUuid = () => \"uuid\";\n\n// ============ Single-table types ============\n\n/**\n * DynamoDB table key (always pk + sk strings in single-table design).\n */\nexport type TableKey = { pk: string; sk: string };\n\n/**\n * Full DynamoDB item in the single-table design.\n *\n * Every item has a fixed envelope: `pk`, `sk`, `tag`, `data`, and optional `ttl`.\n * `tag` is stored as a top-level DynamoDB attribute (GSI-ready).\n * If your domain type `T` includes a `tag` field, effortless auto-copies\n * `data.tag` to the top-level `tag` on `put()`, so you don't have to pass it twice.\n *\n * @typeParam T - The domain data type stored in the `data` attribute\n */\nexport type TableItem<T> = {\n pk: string;\n sk: string;\n tag: string;\n data: T;\n ttl?: number;\n};\n\n/**\n * Input type for `TableClient.put()`.\n *\n * Unlike `TableItem<T>`, this does NOT include `tag` — effortless auto-extracts\n * the top-level DynamoDB `tag` attribute from your data using the configured\n * tag field (defaults to `data.tag`, configurable via `defineTable({ tag: \"type\" })`).\n *\n * @typeParam T - The domain data type stored in the `data` attribute\n */\nexport type PutInput<T> = {\n pk: string;\n sk: string;\n data: T;\n ttl?: number;\n};\n\n","import type { AnySecretRef, ResolveConfig, Duration, ConfigFactory, LogLevel, Permission, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { TableClient } from \"../runtime/table-client\";\nimport type { TableItem } from \"./handler-options\";\nimport type { StaticFiles } from \"./shared\";\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 for defineTable (single-table design).\n *\n * Tables always use `pk (S)` + `sk (S)` keys, `tag (S)` discriminator,\n * `data (M)` for domain fields, and `ttl (N)` for optional expiration.\n */\nexport type TableConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: { memory?: number; timeout?: Duration; logLevel?: LogLevel; permissions?: Permission[] };\n /** DynamoDB billing mode (default: \"PAY_PER_REQUEST\") */\n billingMode?: \"PAY_PER_REQUEST\" | \"PROVISIONED\";\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 to gather records before invoking (default: `\"2s\"`). Accepts `\"5s\"`, `\"1m\"`, etc. */\n batchWindow?: Duration;\n /** Where to start reading the stream (default: \"LATEST\") */\n startingPosition?: \"LATEST\" | \"TRIM_HORIZON\";\n /** Number of records to process concurrently within a batch (default: 1 — sequential) */\n concurrency?: number;\n /**\n * Name of the field in `data` that serves as the entity type discriminant.\n * Effortless auto-copies `data[tagField]` to the top-level DynamoDB `tag` attribute on `put()`.\n * Defaults to `\"tag\"`.\n */\n tagField?: string;\n};\n\n/**\n * DynamoDB stream record passed to onRecord callback.\n *\n * `new` and `old` are full `TableItem<T>` objects with the single-table envelope.\n *\n * @typeParam T - Type of the domain data (inside `data`)\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?: TableItem<T>;\n /** Old item value (present for MODIFY and REMOVE) */\n old?: TableItem<T>;\n /** Primary key of the affected item */\n keys: { pk: string; sk: string };\n /** Sequence number for ordering */\n sequenceNumber?: string;\n /** Approximate timestamp when the modification occurred */\n timestamp?: number;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives table/deps/config/files based on what was declared */\ntype SetupArgs<T, D, P, HasFiles extends boolean> =\n & { table: TableClient<T> }\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n/**\n * Callback function type for processing a single DynamoDB stream record.\n * Receives the current record and the full batch for context.\n */\nexport type TableRecordFn<T = Record<string, unknown>, C = undefined> =\n (args: { record: TableRecord<T>; batch: readonly TableRecord<T>[] }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n/**\n * Batch handler function for DynamoDB stream records.\n * Called once with all records in the batch.\n * Return `{ failures: string[] }` (sequence numbers) for partial batch failure reporting.\n */\nexport type TableBatchFn<T = Record<string, unknown>, C = undefined> =\n (args: { records: readonly TableRecord<T>[] }\n & SpreadCtx<C>\n ) => Promise<void | { failures: string[] }>;\n\n// ============ Static config ============\n\n/** Static config extracted by AST (no runtime callbacks) */\n// TableConfig is already defined above and used as the extracted config type.\n\n// ============ Handler type (base interface for runtime wrappers and annotations) ============\n\n/**\n * Handler object created by defineTable.\n * Used by runtime wrappers and as type annotation for circular deps.\n * @internal\n */\nexport type TableHandler<T = Record<string, unknown>, C = any> = {\n readonly __brand: \"effortless-table\";\n readonly __spec: TableConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onRecord?: (...args: any[]) => any;\n readonly onRecordBatch?: (...args: any[]) => any;\n};\n\n// ============ Builder options ============\n\n/** Options passed to `defineTable()` — resource config only, no Lambda settings */\ntype TableOptions<T> = {\n /** DynamoDB billing mode (default: \"PAY_PER_REQUEST\") */\n billingMode?: \"PAY_PER_REQUEST\" | \"PROVISIONED\";\n /** Stream view type (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 to gather records before invoking (default: \"2s\") */\n batchWindow?: Duration;\n /** Where to start reading the stream (default: \"LATEST\") */\n startingPosition?: \"LATEST\" | \"TRIM_HORIZON\";\n /** Number of records to process concurrently within a batch (default: 1) */\n concurrency?: number;\n /** Name of the field in `data` that serves as the entity type discriminant (default: \"tag\") */\n tagField?: Extract<keyof T, string>;\n /** Decode/validate function for the `data` portion of stream records */\n schema?: (input: unknown) => T;\n};\n\n// ============ Builder ============\n\ninterface TableBuilder<\n T = Record<string, unknown>,\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): TableBuilder<T, D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): TableBuilder<T, D, P2, C, HasFiles>;\n\n /** Include static files in the Lambda bundle. Chainable — call multiple times. */\n include(glob: string): TableBuilder<T, D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, etc.) */\n setup(\n lambda: LambdaOptions\n ): TableBuilder<T, D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives table (self-client), deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<T, D, P, HasFiles>) => C2 | Promise<C2>\n ): TableBuilder<T, D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<T, D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): TableBuilder<T, D, P, C2, HasFiles>;\n\n /** Handle errors thrown by onRecord/onRecordBatch */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): TableBuilder<T, D, P, C, HasFiles>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): TableBuilder<T, D, P, C, HasFiles>;\n\n /** Per-record stream handler (terminal — returns finalized handler) */\n onRecord(\n fn: TableRecordFn<T, C>\n ): TableHandler<T, C>;\n\n /** Batch stream handler (terminal — returns finalized handler) */\n onRecordBatch(\n fn: TableBatchFn<T, C>\n ): TableHandler<T, C>;\n\n /** Finalize as resource-only table (no Lambda) */\n build(): TableHandler<T, C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a DynamoDB table with optional stream handler (single-table design).\n *\n * Creates a table with fixed key schema: `pk (S)` + `sk (S)`, plus `tag (S)`,\n * `data (M)`, and `ttl (N)` attributes. TTL is always enabled.\n *\n * @see {@link https://effortless-aws.website/use-cases/database | Database guide}\n *\n * @example\n * ```typescript\n * export const orders = defineTable<OrderData>({ batchSize: 10, concurrency: 5 })\n * .setup(({ table }) => ({ table }))\n * .onRecord(async ({ record, table }) => {\n * if (record.eventName === \"INSERT\") {\n * console.log(\"New order:\", record.new?.data);\n * }\n * })\n * ```\n */\nexport function defineTable<T = Record<string, unknown>>(): TableBuilder<T>;\nexport function defineTable<T = Record<string, unknown>>(\n options: TableOptions<T>,\n): TableBuilder<T>;\nexport function defineTable<T = Record<string, unknown>>(\n options?: TableOptions<T>,\n): TableBuilder<T> {\n const {\n schema,\n ...tableConfig\n } = options ?? {} as TableOptions<T>;\n\n const spec: TableConfig = { ...tableConfig };\n\n const state: {\n spec: TableConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n schema?: (input: unknown) => T;\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onRecord?: (...args: any[]) => any;\n onRecordBatch?: (...args: any[]) => any;\n } = {\n spec,\n ...(schema ? { schema } : {}),\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): TableHandler<T> => ({\n __brand: \"effortless-table\",\n __spec: state.spec,\n ...(state.schema ? { schema: state.schema } : {}),\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onRecord ? { onRecord: state.onRecord } : {}),\n ...(state.onRecordBatch ? { onRecordBatch: state.onRecordBatch } : {}),\n }) as TableHandler<T>;\n\n const builder: TableBuilder<T> = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onRecord(fn) {\n state.onRecord = fn as any;\n return finalize() as any;\n },\n onRecordBatch(fn) {\n state.onRecordBatch = fn as any;\n return finalize() as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n build() {\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","import type { LambdaWithPermissions } from \"./handler-options\";\n\n/**\n * Configuration for deploying an SSR framework (Nuxt, Astro, etc.)\n * via CloudFront + S3 (static assets) + Lambda Function URL (server-side rendering).\n */\nexport type AppConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** Directory containing the Lambda server handler (e.g., \".output/server\").\n * Must contain an `index.mjs` (or `index.js`) that exports a `handler` function. */\n server: string;\n /** Directory containing static assets for S3 (e.g., \".output/public\") */\n assets: string;\n /** Base URL path (default: \"/\") */\n path?: string;\n /** Shell command to build the framework output (e.g., \"nuxt build\") */\n build?: string;\n /** Custom domain name. String or stage-keyed record (e.g., { prod: \"app.example.com\" }). */\n domain?: string | Record<string, string>;\n /** CloudFront route overrides: path patterns forwarded to API Gateway instead of the SSR Lambda.\n * Keys are CloudFront path patterns (e.g., \"/api/*\"), values are handler references.\n * Example: `routes: { \"/api/*\": api }` */\n routes?: Record<string, { readonly __brand: string }>;\n};\n\n/**\n * Internal handler object created by defineApp\n * @internal\n */\nexport type AppHandler = {\n readonly __brand: \"effortless-app\";\n readonly __spec: AppConfig;\n};\n\n/**\n * Deploy an SSR framework application via CloudFront + Lambda Function URL.\n *\n * Static assets from the `assets` directory are served via S3 + CloudFront CDN.\n * Server-rendered pages are handled by a Lambda function using the framework's\n * built output from the `server` directory.\n *\n * For static-only sites (no SSR), use {@link defineStaticSite} instead.\n *\n * @see {@link https://effortless-aws.website/use-cases/web-app | Web app guide}\n *\n * @param options - App configuration: server directory, assets directory, optional build command\n * @returns Handler object used by the deployment system\n */\nexport const defineApp = () => (options: AppConfig): AppHandler => ({\n __brand: \"effortless-app\",\n __spec: options,\n});\n","/** Any branded handler that deploys to API Gateway or S3 */\ntype AnyRoutableHandler = { readonly __brand: string };\n\n/** Simplified request object passed to middleware */\nexport type MiddlewareRequest = {\n uri: string;\n method: string;\n querystring: string;\n headers: Record<string, string>;\n cookies: Record<string, string>;\n};\n\n/** Redirect the user to another URL */\nexport type MiddlewareRedirect = {\n redirect: string;\n status?: 301 | 302 | 307 | 308;\n};\n\n/** Deny access with a 403 status */\nexport type MiddlewareDeny = {\n status: 403;\n body?: string;\n};\n\n/** Middleware return type: redirect, deny, or void (continue serving) */\nexport type MiddlewareResult = MiddlewareRedirect | MiddlewareDeny | void;\n\n/** Function that runs before serving static files via Lambda@Edge */\nexport type MiddlewareHandler = (\n request: MiddlewareRequest\n) => Promise<MiddlewareResult> | MiddlewareResult;\n\n/** SEO options for auto-generating sitemap.xml, robots.txt, and submitting to Google Indexing API */\nexport type StaticSiteSeo = {\n /** Sitemap filename (e.g. \"sitemap.xml\", \"sitemap-v2.xml\") */\n sitemap: string;\n /** Path to Google service account JSON key file for Indexing API batch submission.\n * Requires adding the service account email as an owner in Google Search Console. */\n googleIndexing?: string;\n};\n\n/**\n * Configuration for a static site (S3 + CloudFront)\n */\nexport type StaticSiteConfig = {\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 /** Shell command to run before deploy to generate site content (e.g., \"npx astro build\") */\n build?: string;\n /** Path to a custom error page relative to `dir`.\n * - If set to the same value as `index` (e.g. \"index.html\"), enables SPA mode:\n * all paths that don't match a file are served with `index.html` (HTTP 200), letting the client-side router handle them.\n * - If set to a different file (e.g. \"404.html\"), that file is served with HTTP 404 for missing paths.\n * - If omitted, a default 404 page is auto-generated. */\n errorPage?: string;\n /** Custom domain name. Accepts a string (same domain for all stages) or a Record mapping stage names to domains (e.g., `{ prod: \"example.com\", dev: \"dev.example.com\" }`). Requires an ACM certificate in us-east-1. If the cert also covers www, a 301 redirect from www to non-www is set up automatically. */\n domain?: string | Record<string, string>;\n /** SEO: auto-generate sitemap.xml and robots.txt at deploy time, optionally submit URLs to Google Indexing API */\n seo?: StaticSiteSeo;\n};\n\n/** Route entry stored on the static site handler */\ntype StaticSiteRouteEntry = {\n pattern: string;\n origin: AnyRoutableHandler;\n access?: \"private\" | \"public\";\n};\n\n/**\n * Internal handler object created by defineStaticSite\n * @internal\n */\nexport type StaticSiteHandler = {\n readonly __brand: \"effortless-static-site\";\n readonly __spec: StaticSiteConfig;\n readonly routes: StaticSiteRouteEntry[];\n readonly middleware?: MiddlewareHandler;\n};\n\n/**\n * Deploy a static site via S3 + CloudFront CDN, with optional API and bucket route overrides.\n *\n * @see {@link https://effortless-aws.website/use-cases/web-app | Web app guide}\n *\n * @param options - Static site configuration: directory, optional SPA mode, build command, domain\n * @returns Builder with `.route()`, `.middleware()`, and `.build()` methods\n */\nexport function defineStaticSite(options: StaticSiteConfig) {\n const state = {\n spec: { ...options },\n routes: [] as StaticSiteRouteEntry[],\n middleware: undefined as MiddlewareHandler | undefined,\n };\n\n const builder = {\n route(pattern: string, origin: AnyRoutableHandler, opts?: { access?: \"private\" | \"public\" }) {\n state.routes.push({ pattern, origin, ...(opts?.access ? { access: opts.access } : {}) });\n return builder;\n },\n middleware(fn: MiddlewareHandler) {\n state.middleware = fn;\n return builder;\n },\n build(): StaticSiteHandler {\n return {\n __brand: \"effortless-static-site\",\n __spec: state.spec,\n routes: state.routes,\n ...(state.middleware ? { middleware: state.middleware } : {}),\n };\n },\n };\n\n return builder;\n}\n","import type { AnySecretRef, ResolveConfig, Duration, ConfigFactory, LogLevel, Permission, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\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 /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: { memory?: number; timeout?: Duration; logLevel?: LogLevel; permissions?: Permission[] };\n /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */\n batchSize?: number;\n /** Maximum time to gather messages before invoking (default: 0). Accepts `\"5s\"`, `\"1m\"`, etc. */\n batchWindow?: Duration;\n /** Visibility timeout (default: max of timeout or 30s). Accepts `\"30s\"`, `\"5m\"`, etc. */\n visibilityTimeout?: Duration;\n /** Message retention period (default: `\"4d\"`). Accepts `\"1h\"`, `\"7d\"`, etc. */\n retentionPeriod?: Duration;\n /** Delivery delay for all messages in the queue (default: 0). Accepts `\"30s\"`, `\"5m\"`, etc. */\n delay?: Duration;\n /** Enable content-based deduplication (default: true) */\n contentBasedDeduplication?: boolean;\n /** Max number of receives before a message is sent to the dead-letter queue (default: 3) */\n maxReceiveCount?: number;\n};\n\n// ============ Setup args ============\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\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> =\n (args: { message: FifoQueueMessage<T> }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n/**\n * Batch handler function.\n * Called once with all messages in the batch.\n * Return `{ failures: string[] }` (messageIds) for partial batch failure reporting.\n */\nexport type FifoQueueBatchFn<T = unknown, C = undefined> =\n (args: { messages: FifoQueueMessage<T>[] }\n & SpreadCtx<C>\n ) => Promise<void | { failures: string[] }>;\n\n// ============ Internal handler object ============\n\n/**\n * Internal handler object created by defineFifoQueue\n * @internal\n */\nexport type FifoQueueHandler<T = unknown, C = any> = {\n readonly __brand: \"effortless-fifo-queue\";\n readonly __spec: FifoQueueConfig;\n readonly schema?: (input: unknown) => T;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onMessage?: (...args: any[]) => any;\n readonly onMessageBatch?: (...args: any[]) => any;\n};\n\n// ============ Builder options ============\n\n/** Options passed to `defineFifoQueue()` — static config */\ntype FifoQueueOptions<T> = {\n /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */\n batchSize?: number;\n /** Maximum time to gather messages before invoking (default: 0) */\n batchWindow?: Duration;\n /** Visibility timeout (default: max of timeout or 30s) */\n visibilityTimeout?: Duration;\n /** Message retention period (default: \"4d\") */\n retentionPeriod?: Duration;\n /** Delivery delay for all messages in the queue (default: 0) */\n delay?: Duration;\n /** Enable content-based deduplication (default: true) */\n contentBasedDeduplication?: boolean;\n /** Max number of receives before DLQ (default: 3) */\n maxReceiveCount?: number;\n /** Decode/validate function for the message body */\n schema?: (input: unknown) => T;\n};\n\n// ============ Builder ============\n\ninterface FifoQueueBuilder<\n T = unknown,\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): FifoQueueBuilder<T, D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): FifoQueueBuilder<T, D, P2, C, HasFiles>;\n\n /** Include static files in the Lambda bundle. Chainable — call multiple times. */\n include(glob: string): FifoQueueBuilder<T, D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, etc.) */\n setup(\n lambda: LambdaOptions\n ): FifoQueueBuilder<T, D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): FifoQueueBuilder<T, D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): FifoQueueBuilder<T, D, P, C2, HasFiles>;\n\n /** Handle errors thrown by message handlers */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): FifoQueueBuilder<T, D, P, C, HasFiles>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): FifoQueueBuilder<T, D, P, C, HasFiles>;\n\n /** Per-message handler (terminal — returns finalized handler) */\n onMessage(\n fn: FifoQueueMessageFn<T, C>\n ): FifoQueueHandler<T, C>;\n\n /** Batch handler (terminal — returns finalized handler) */\n onMessageBatch(\n fn: FifoQueueBatchFn<T, C>\n ): FifoQueueHandler<T, C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a FIFO SQS queue with a Lambda message handler.\n *\n * @see {@link https://effortless-aws.website/use-cases/queue | Queue guide}\n *\n * @example\n * ```typescript\n * export const notifications = defineFifoQueue({ batchSize: 5, schema: (i) => NotifSchema.parse(i) })\n * .onMessageBatch(async ({ messages }) => {\n * await sendAll(messages.map(m => m.body));\n * })\n * ```\n */\nexport function defineFifoQueue<T = unknown>(): FifoQueueBuilder<T>;\nexport function defineFifoQueue<T = unknown>(\n options: FifoQueueOptions<T>,\n): FifoQueueBuilder<T>;\nexport function defineFifoQueue<T = unknown>(\n options?: FifoQueueOptions<T>,\n): FifoQueueBuilder<T> {\n const {\n schema,\n ...queueConfig\n } = options ?? {} as FifoQueueOptions<T>;\n\n const spec: FifoQueueConfig = { ...queueConfig };\n\n const state: {\n spec: FifoQueueConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n schema?: (input: unknown) => T;\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onMessage?: (...args: any[]) => any;\n onMessageBatch?: (...args: any[]) => any;\n } = {\n spec,\n ...(schema ? { schema } : {}),\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): FifoQueueHandler<T> => ({\n __brand: \"effortless-fifo-queue\",\n __spec: state.spec,\n ...(state.schema ? { schema: state.schema } : {}),\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onMessage ? { onMessage: state.onMessage } : {}),\n ...(state.onMessageBatch ? { onMessageBatch: state.onMessageBatch } : {}),\n }) as FifoQueueHandler<T>;\n\n const builder: FifoQueueBuilder<T> = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob: string) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onMessage(fn) {\n state.onMessage = fn as any;\n return finalize() as any;\n },\n onMessageBatch(fn) {\n state.onMessageBatch = fn as any;\n return finalize() as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n };\n\n return builder;\n}\n","import type { AnySecretRef, ResolveConfig, Duration, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\nimport type { BucketClient, BucketClientWithEntities } from \"../runtime/bucket-client\";\n\n/**\n * Per-entity configuration for typed JSON key-value storage within a bucket.\n */\nexport type BucketEntityConfig = {\n /** Cache duration for CloudFront/browser caching (e.g., \"10s\", \"5m\", \"1h\"). No caching if omitted. */\n cache?: Duration;\n};\n\n/**\n * Configuration options for defineBucket.\n */\nexport type BucketConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: { memory?: number; timeout?: import(\"./handler-options\").Duration; logLevel?: import(\"./handler-options\").LogLevel; permissions?: import(\"./handler-options\").Permission[] };\n /** S3 key prefix filter for event notifications (e.g., \"uploads/\") */\n prefix?: string;\n /** S3 key suffix filter for event notifications (e.g., \".jpg\") */\n suffix?: string;\n /** Typed JSON entity definitions for key-value storage */\n entities?: Record<string, BucketEntityConfig>;\n /** Local directory to seed into bucket on deploy (only uploads files that don't already exist) */\n seed?: string;\n /** Local directory to sync into bucket on every deploy (uploads new/changed, deletes removed) */\n sync?: string;\n};\n\n/**\n * S3 event record passed to onObjectCreated/onObjectRemoved callbacks.\n */\nexport type BucketEvent = {\n /** S3 event name (e.g., \"ObjectCreated:Put\", \"ObjectRemoved:Delete\") */\n eventName: string;\n /** Object key (path within the bucket) */\n key: string;\n /** Object size in bytes (present for created events) */\n size?: number;\n /** Object ETag (present for created events) */\n eTag?: string;\n /** ISO 8601 timestamp of when the event occurred */\n eventTime?: string;\n /** S3 bucket name */\n bucketName: string;\n};\n\n// ============ Setup args ============\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n/** Setup factory — receives bucket/deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean, Entities extends Record<string, any> = {}> =\n & { bucket: {} extends Entities ? BucketClient : BucketClientWithEntities<Entities> }\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/**\n * Callback function type for S3 ObjectCreated events\n */\nexport type BucketObjectCreatedFn<C = undefined> =\n (args: { event: BucketEvent }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n/**\n * Callback function type for S3 ObjectRemoved events\n */\nexport type BucketObjectRemovedFn<C = undefined> =\n (args: { event: BucketEvent }\n & SpreadCtx<C>\n ) => Promise<void>;\n\n// ============ Internal handler object ============\n\n/**\n * Internal handler object created by defineBucket\n * @internal\n */\nexport type BucketHandler<C = any, _Entities extends Record<string, any> = {}> = {\n readonly __brand: \"effortless-bucket\";\n readonly __spec: BucketConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onObjectCreated?: (...args: any[]) => any;\n readonly onObjectRemoved?: (...args: any[]) => any;\n};\n\n// ============ Builder options ============\n\n/** Options passed to `defineBucket()` — static config */\ntype BucketOptions = {\n /** S3 key prefix filter for event notifications (e.g., \"uploads/\") */\n prefix?: string;\n /** S3 key suffix filter for event notifications (e.g., \".jpg\") */\n suffix?: string;\n /** Local directory to seed into bucket on deploy (only uploads files that don't already exist) */\n seed?: string;\n /** Local directory to sync into bucket on every deploy (uploads new/changed, deletes removed) */\n sync?: string;\n};\n\n// ============ Builder ============\n\ninterface BucketBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n Entities extends Record<string, any> = {},\n> {\n /** Declare handler dependencies */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): BucketBuilder<D2, P, C, HasFiles, Entities>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): BucketBuilder<D, P2, C, HasFiles, Entities>;\n\n /** Include static files in the Lambda ZIP */\n include(glob: string): BucketBuilder<D, P, C, true, Entities>;\n\n /** Register a typed JSON entity stored as `{name}/{id}.json` in the bucket */\n entity<N extends string, T>(\n name: N,\n options?: BucketEntityConfig,\n ): BucketBuilder<D, P, C, HasFiles, Entities & { [K in N]: T }>;\n\n /** Initialize shared state on cold start with lambda options */\n setup(lambda: LambdaOptions): BucketBuilder<D, P, C, HasFiles, Entities>;\n /** Initialize shared state on cold start. Receives bucket (self-client), deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles, Entities>) => C2 | Promise<C2>\n ): BucketBuilder<D, P, C2, HasFiles, Entities>;\n /** Initialize shared state on cold start with lambda options. Receives bucket (self-client), deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles, Entities>) => C2 | Promise<C2>,\n lambda: LambdaOptions,\n ): BucketBuilder<D, P, C2, HasFiles, Entities>;\n\n /** Handle errors thrown by callbacks */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): BucketBuilder<D, P, C, HasFiles, Entities>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): BucketBuilder<D, P, C, HasFiles, Entities>;\n\n /** Handle S3 ObjectCreated events (terminal — returns finalized handler) */\n onObjectCreated(\n fn: BucketObjectCreatedFn<C>\n ): BucketHandler<C, Entities>;\n\n /** Handle S3 ObjectRemoved events (terminal — returns finalized handler) */\n onObjectRemoved(\n fn: BucketObjectRemovedFn<C>\n ): BucketHandler<C, Entities>;\n\n /** Finalize as resource-only bucket (no Lambda) */\n build(): BucketHandler<C, Entities>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define an S3 bucket with optional event handlers.\n *\n * @see {@link https://effortless-aws.website/use-cases/storage | Storage guide}\n *\n * @example\n * ```typescript\n * export const uploads = defineBucket({ prefix: \"images/\", suffix: \".jpg\" })\n * .onObjectCreated(async ({ event, bucket }) => {\n * console.log(\"New upload:\", event.key);\n * })\n * ```\n */\nexport function defineBucket(): BucketBuilder;\nexport function defineBucket(\n options: BucketOptions,\n): BucketBuilder;\nexport function defineBucket(\n options?: BucketOptions,\n): BucketBuilder {\n const bucketConfig = options ?? {} as BucketOptions;\n\n const spec: BucketConfig = {\n ...bucketConfig,\n };\n\n const state: {\n spec: BucketConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onObjectCreated?: (...args: any[]) => any;\n onObjectRemoved?: (...args: any[]) => any;\n } = {\n spec,\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): BucketHandler => ({\n __brand: \"effortless-bucket\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onObjectCreated ? { onObjectCreated: state.onObjectCreated } : {}),\n ...(state.onObjectRemoved ? { onObjectRemoved: state.onObjectRemoved } : {}),\n }) as BucketHandler;\n\n const builder: BucketBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob: string) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n entity(name: string, options?: BucketEntityConfig) {\n state.spec = {\n ...state.spec,\n entities: { ...state.spec.entities, [name]: options ?? {} },\n };\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onObjectCreated(fn) {\n state.onObjectCreated = fn as any;\n return finalize() as any;\n },\n onObjectRemoved(fn) {\n state.onObjectRemoved = fn as any;\n return finalize() as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n build() {\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","/**\n * Configuration options for defining a mailer (SES email identity)\n */\nexport type MailerConfig = {\n /** Domain to verify and send emails from (e.g., \"myapp.com\") */\n domain: string;\n};\n\n/**\n * Internal handler object created by defineMailer\n * @internal\n */\nexport type MailerHandler = {\n readonly __brand: \"effortless-mailer\";\n readonly __spec: MailerConfig;\n};\n\n/**\n * Define an email sender backed by Amazon SES.\n *\n * Creates an SES Email Identity for the specified domain and provides\n * a typed `EmailClient` to other handlers via `deps`.\n *\n * On first deploy, DKIM DNS records are printed to the console.\n * Add them to your DNS provider to verify the domain.\n *\n * @see {@link https://effortless-aws.website/use-cases/email | Email guide}\n *\n * @param options - Mailer configuration with the domain to send from\n * @returns Handler object used by the deployment system and as a `deps` value\n *\n * @example Basic mailer with HTTP handler\n * ```typescript\n * export const mailer = defineMailer({ domain: \"myapp.com\" });\n *\n * export const api = defineApi({\n * basePath: \"/signup\",\n * deps: { mailer },\n * post: async ({ req, deps }) => {\n * await deps.mailer.send({\n * from: \"hello@myapp.com\",\n * to: req.body.email,\n * subject: \"Welcome!\",\n * html: \"<h1>Hi!</h1>\",\n * });\n * return { status: 200, body: { ok: true } };\n * },\n * });\n * ```\n */\nexport const defineMailer = () => (options: MailerConfig): MailerHandler => ({\n __brand: \"effortless-mailer\",\n __spec: options,\n});\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { LambdaWithPermissions, AnySecretRef, ResolveConfig, Duration, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory, toSeconds } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles, ResponseStream } from \"./shared\";\nimport type { HttpRequest, HttpResponse } from \"./shared\";\nimport type { AuthHelpers } from \"./auth\";\n\n// ============ Auth types ============\n\n/** Auth config options (user-facing) */\nexport type AuthOptions<A = unknown> = {\n /** HMAC secret for signing session cookies. Use `secret()` or `param()` in config. */\n secret: string;\n /** Default session lifetime (default: \"7d\"). */\n expiresIn?: Duration;\n /** Optional API token strategy for external clients. */\n apiToken?: {\n /** HTTP header to read the token from. Default: \"authorization\" (strips \"Bearer \" prefix). */\n header?: string;\n /** Verify the token value and return session data, or null if invalid. */\n verify: (value: string) => A | null | Promise<A | null>;\n /** Cache verified token results for this duration. */\n cacheTtl?: Duration;\n };\n};\n\n/** Property names reserved by the framework — cannot be used in setup return */\ntype ReservedKeys = 'req' | 'input' | 'stream' | 'ok' | 'fail';\n\n// ============ Response helpers ============\n\n/** Success response helper: `ok({ data })` → `{ status: 200, body: { data } }` */\nexport type OkHelper = (body?: unknown, status?: number) => HttpResponse;\n/** Error response helper: `fail(\"message\")` → `{ status: 400, body: { error: \"message\" } }` */\nexport type FailHelper = (message: string, status?: number) => HttpResponse;\n\n// ============ Route types ============\n\ntype HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/** Cache options for a GET route. Duration shorthand (e.g. \"30s\", \"5m\") or object for fine-grained control. */\nexport type CacheOptions = Duration | {\n ttl: Duration;\n swr?: Duration;\n scope?: \"public\" | \"private\";\n};\n\n/** Resolved cache config with numeric seconds */\ntype ResolvedCache =\n | { private?: false; ttl: number; swr: number }\n | { private: true; ttl: number };\n\n/** Resolve CacheOptions into numeric seconds. Shorthand = public with swr = ttl * 2. */\nconst resolveCache = (cache: CacheOptions): ResolvedCache => {\n if (typeof cache === \"number\" || typeof cache === \"string\") {\n const ttl = toSeconds(cache);\n return { ttl, swr: ttl * 2 };\n }\n const ttl = toSeconds(cache.ttl);\n if (cache.scope === \"private\") {\n return { private: true, ttl };\n }\n return {\n ttl,\n swr: cache.swr != null ? toSeconds(cache.swr) : ttl * 2,\n };\n};\n\n/** Parsed route definition stored at runtime */\nexport type RouteEntry = {\n method: HttpMethod;\n path: string;\n onRequest: (...args: any[]) => any;\n schema?: unknown;\n public?: boolean;\n cache?: ResolvedCache;\n};\n\n/** Spread ctx into route args, add AuthHelpers if A is set */\ntype SpreadCtx<C, A = undefined> =\n & ([C] extends [undefined] ? {} : C & {})\n & ([A] extends [undefined] ? {} : { auth: AuthHelpers<A> });\n\n/** Infer validated output type from a Standard Schema, or fall back to unknown */\ntype InferInput<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : unknown;\n\n/** Callback args available inside each route — ctx is spread into args */\ntype RouteArgs<C, ST, A = undefined, S = undefined> =\n & SpreadCtx<C, A>\n & { req: HttpRequest; input: InferInput<S>; ok: OkHelper; fail: FailHelper }\n & ([ST] extends [true] ? { stream: ResponseStream } : {});\n\n/** Route handler function */\ntype RouteHandler<C, ST, A = undefined, S = undefined> = (args: RouteArgs<C, ST, A, S>) => Promise<HttpResponse | void> | HttpResponse | void;\n\n/** Route definition — pass `input` for typed schema validation */\ntype RouteDef<S extends StandardSchemaV1 | undefined = undefined> = {\n path: `/${string}`;\n input?: S;\n public?: boolean;\n cache?: CacheOptions;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & { ok: OkHelper; fail: FailHelper }\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Auth factory args — receives config/deps based on what was declared */\ntype AuthArgs<D, P> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> });\n\n/** Validate that setup return type does not use reserved property names */\ntype ValidateSetupReturn<C> = C & { [K in ReservedKeys]?: never };\n\n// ============ Static config ============\n\n/** Static config extracted by AST (no runtime callbacks) */\nexport type ApiConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** Base path prefix for all routes (e.g., \"/api\") */\n basePath: `/${string}`;\n /** Enable response streaming. When true, the Lambda Function URL uses RESPONSE_STREAM invoke mode. */\n stream?: boolean;\n};\n\n// ============ Internal handler object ============\n\n/** Internal handler object created by defineApi */\nexport type ApiHandler<C = undefined> = {\n readonly __brand: \"effortless-api\";\n readonly __spec: ApiConfig;\n readonly onError?: (...args: any[]) => any | Promise<any>;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly routes?: RouteEntry[];\n};\n\n// ============ Builder options (plain values, no inference) ============\n\n/** Options passed to `defineApi()` */\ntype ApiOptions = {\n /** Base path prefix for all routes (e.g., \"/api\") */\n basePath: `/${string}`;\n /** Enable response streaming. When true, routes receive a `stream` arg for SSE. */\n stream?: boolean;\n};\n\n// ============ ApiRoutes — returned after first route method ============\n\n/**\n * Finalized API handler with route-adding methods.\n * Has `__brand` so CLI discovers it. Each `.get()/.post()` adds a route and returns self.\n */\nexport interface ApiRoutes<C = undefined, ST extends boolean = false, A = undefined> extends ApiHandler<C> {\n get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n}\n\n// ============ Builder ============\n\n/**\n * Builder interface for defining API handlers.\n *\n * Each method sets exactly one generic, so inference happens one step at a time.\n * This prevents cascading type errors when one property has a mistake.\n */\ninterface ApiBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n ST extends boolean = false,\n HasFiles extends boolean = false,\n A = undefined,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): ApiBuilder<D2, P, C, ST, HasFiles, A>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): ApiBuilder<D, P2, C, ST, HasFiles, A>;\n\n /** Include static files by glob pattern */\n include(glob: string): ApiBuilder<D, P, C, ST, true, A>;\n\n /** Configure session-based authentication. Receives resolved config/deps. */\n auth<A2>(\n fn: (args: AuthArgs<D, P>) => AuthOptions<A2> | Promise<AuthOptions<A2>>\n ): ApiBuilder<D, P, C, ST, HasFiles, A2>;\n\n /** Configure Lambda settings only (no init function) */\n setup(lambda: LambdaOptions): ApiBuilder<D, P, C, ST, HasFiles, A>;\n /** Initialize shared state on cold start. Receives deps/config/files based on what was declared. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>\n ): ApiBuilder<D, P, C2, ST, HasFiles, A>;\n /** Initialize shared state on cold start with Lambda config. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>,\n lambda: LambdaOptions,\n ): ApiBuilder<D, P, C2, ST, HasFiles, A>;\n\n /** Handle errors thrown by routes */\n onError(\n fn: (args: { error: unknown; req: HttpRequest; ok: OkHelper; fail: FailHelper } & SpreadCtx<C, A>) => HttpResponse | Promise<HttpResponse>\n ): ApiBuilder<D, P, C, ST, HasFiles, A>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C, A>) => void | Promise<void>\n ): ApiBuilder<D, P, C, ST, HasFiles, A>;\n\n get<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n post<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n put<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n patch<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n delete<S extends StandardSchemaV1 | undefined = undefined>(def: RouteDef<S>, handler: RouteHandler<C, ST, A, S>): ApiRoutes<C, ST, A>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define an API with typed routes using a builder pattern.\n *\n * @see {@link https://effortless-aws.website/use-cases/http-api | HTTP API guide}\n *\n * @example\n * ```typescript\n * export const api = defineApi({ basePath: \"/api\" })\n * .deps(() => ({ users }))\n * .config(({ defineSecret }) => ({ authSecret: defineSecret() }))\n * .auth<Session>(({ config }) => ({ secret: config.authSecret, expiresIn: \"1h\" }))\n * .get({ path: \"/me\" }, async ({ users, auth, ok }) => ok(auth.session))\n * .post({ path: \"/login\", public: true }, async ({ auth, ok }) => ok(await auth.createSession()))\n * ```\n */\nexport function defineApi<const O extends ApiOptions>(\n options: O,\n): ApiBuilder<undefined, undefined, undefined, O[\"stream\"] extends true ? true : false, false>;\nexport function defineApi(\n options: ApiOptions,\n): ApiBuilder {\n const { basePath, stream } = options;\n\n const state: {\n spec: ApiConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n authFn?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n routes: RouteEntry[];\n } = {\n spec: {\n basePath,\n ...(stream ? { stream } : {}),\n },\n routes: [],\n };\n\n const addRoute = (method: HttpMethod, def: { path: string; input?: unknown; public?: boolean; cache?: CacheOptions }, handler: Function) => {\n const routeCache = def.cache != null\n ? resolveCache(def.cache)\n : undefined;\n\n state.routes.push({\n method,\n path: def.path,\n onRequest: handler as any,\n ...(def.input ? { schema: def.input } : {}),\n ...(def.public ? { public: true } : {}),\n ...(routeCache ? { cache: routeCache } : {}),\n });\n };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): ApiRoutes => {\n const handler: any = {\n __brand: \"effortless-api\",\n __spec: state.spec,\n routes: state.routes,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.authFn ? { authFn: state.authFn } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n };\n\n // Add route methods to the finalized handler\n for (const m of [\"get\", \"post\", \"put\", \"patch\", \"delete\"] as const) {\n handler[m] = (def: any, fn: any) => {\n addRoute(m.toUpperCase() as HttpMethod, def, fn);\n handler.routes = state.routes;\n return handler;\n };\n }\n\n return handler as ApiRoutes;\n };\n\n const builder: ApiBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob: string) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n auth(fn: any) {\n state.authFn = fn;\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n get(def: any, fn: any) { addRoute(\"GET\", def, fn); return finalize() as any; },\n post(def: any, fn: any) { addRoute(\"POST\", def, fn); return finalize() as any; },\n put(def: any, fn: any) { addRoute(\"PUT\", def, fn); return finalize() as any; },\n patch(def: any, fn: any) { addRoute(\"PATCH\", def, fn); return finalize() as any; },\n delete(def: any, fn: any) { addRoute(\"DELETE\", def, fn); return finalize() as any; },\n };\n\n return builder;\n}\n","import type { AnySecretRef, ResolveConfig, LambdaWithPermissions, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\nimport type { Timezone } from \"./timezone\";\n\n// ============ Schedule expression ============\n\n/** Singular/plural unit for rate expressions */\ntype RateUnit = \"minute\" | \"minutes\" | \"hour\" | \"hours\" | \"day\" | \"days\";\n\n/**\n * Rate expression: `rate(1 hour)`, `rate(5 minutes)`, `rate(2 days)`\n *\n * Strictly typed — autocomplete and compile-time validation for unit.\n */\ntype RateExpression = `rate(${number} ${RateUnit})`;\n\n/**\n * Cron expression: `cron(min hour dom month dow year)`\n *\n * Not deeply typed (too combinatorial for TS), but the `cron(...)` wrapper is enforced.\n *\n * @example\n * ```\n * \"cron(0 9 * * ? *)\" // daily at 9:00 UTC\n * \"cron(0 9 ? * MON-FRI *)\" // weekdays at 9:00\n * \"cron(0/15 * * * ? *)\" // every 15 minutes\n * ```\n */\ntype CronExpression = `cron(${string})`;\n\n/**\n * EventBridge Scheduler schedule expression.\n *\n * - **Rate**: `\"rate(5 minutes)\"`, `\"rate(1 hour)\"`, `\"rate(1 day)\"` — strictly typed units\n * - **Cron**: `\"cron(0 9 * * ? *)\"` — 6 fields: min hour dom month dow year\n */\nexport type ScheduleExpression = RateExpression | CronExpression;\n\n// ============ Static config ============\n\n/** Static config extracted at deploy time */\nexport type CronConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** EventBridge Scheduler schedule expression */\n schedule: ScheduleExpression;\n /** IANA timezone for the schedule (default: UTC) */\n timezone?: Timezone;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n// ============ Tick handler ============\n\n/** Callback function for cron tick */\nexport type CronTickFn<C = undefined> =\n (args: SpreadCtx<C>) => Promise<void> | void;\n\n// ============ Handler type ============\n\n/**\n * Handler object created by defineCron.\n * @internal\n */\nexport type CronHandler<C = any> = {\n readonly __brand: \"effortless-cron\";\n readonly __spec: CronConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onTick?: (...args: any[]) => any;\n};\n\n// ============ Options ============\n\n/** Options passed to `defineCron()` — resource config only */\ntype CronOptions = {\n /** EventBridge Scheduler schedule expression: `\"rate(5 minutes)\"` or `\"cron(0 9 * * ? *)\"` */\n schedule: ScheduleExpression;\n /** IANA timezone for the schedule (default: UTC). Full autocomplete for all timezones. */\n timezone?: Timezone;\n};\n\n// ============ Builder ============\n\ninterface CronBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): CronBuilder<D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): CronBuilder<D, P2, C, HasFiles>;\n\n /** Include static files in the Lambda bundle. Chainable — call multiple times. */\n include(glob: string): CronBuilder<D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */\n setup(lambda: LambdaOptions): CronBuilder<D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): CronBuilder<D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): CronBuilder<D, P, C2, HasFiles>;\n\n /** Handle errors thrown by onTick */\n onError(\n fn: (args: { error: unknown } & SpreadCtx<C>) => void | Promise<void>\n ): CronBuilder<D, P, C, HasFiles>;\n\n /** Cleanup callback — runs after each invocation, before Lambda freezes */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): CronBuilder<D, P, C, HasFiles>;\n\n /** Tick handler — called on each scheduled invocation (terminal) */\n onTick(\n fn: CronTickFn<C>\n ): CronHandler<C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a cron job — scheduled Lambda invocation via EventBridge Scheduler.\n *\n * @example\n * ```typescript\n * export const sync = defineCron({ schedule: \"cron(0 9 * * ? *)\" })\n * .deps(() => ({ orders }))\n * .config(({ defineSecret }) => ({ apiKey: defineSecret() }))\n * .include(\"templates/*.html\")\n * .setup(async ({ deps, config, files }) => ({\n * db: deps.orders, key: config.apiKey, tpl: files,\n * }), { memory: 512 })\n * .onTick(async ({ db, key, tpl }) => {\n * const html = tpl.read(\"templates/report.html\")\n * })\n * ```\n */\nexport function defineCron(options: CronOptions): CronBuilder {\n const { schedule, timezone } = options;\n\n const spec: CronConfig = {\n schedule,\n ...(timezone ? { timezone } : {}),\n };\n\n const state: {\n spec: CronConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onTick?: (...args: any[]) => any;\n } = { spec };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): CronHandler => ({\n __brand: \"effortless-cron\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onTick ? { onTick: state.onTick } : {}),\n }) as CronHandler;\n\n const builder: CronBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n onTick(fn) {\n state.onTick = fn as any;\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","import type { AnySecretRef, ResolveConfig, LambdaWithPermissions, ConfigFactory, LambdaOptions, Duration } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\n\n// ============ Static config ============\n\n/** Fargate container size presets */\nexport type FargateSize =\n | \"0.25vCPU-512mb\"\n | \"0.5vCPU-1gb\"\n | \"1vCPU-2gb\"\n | \"2vCPU-4gb\"\n | \"4vCPU-8gb\";\n\n/** Static config extracted at deploy time */\nexport type WorkerConfig = {\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n /** Fargate size (default: \"0.5vCPU-1gb\") */\n size?: FargateSize;\n /** How long the worker stays alive without messages before shutting down (default: \"5m\") */\n idleTimeout?: Duration;\n /** Max messages processed in parallel (default: 1, max: 10) */\n concurrency?: number;\n};\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n// ============ Handler callback ============\n\n/** Callback function for the worker's onMessage */\nexport type WorkerMessageFn<T, C = undefined> =\n (msg: T, ctx: SpreadCtx<C>) => Promise<void> | void;\n\n// ============ Handler type ============\n\n/**\n * Handler object created by defineWorker.\n * @internal\n */\nexport type WorkerHandler<_T = any, C = any> = {\n readonly __brand: \"effortless-worker\";\n readonly __spec: WorkerConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly onMessage?: (...args: any[]) => any;\n};\n\n// ============ Options ============\n\n/** Options passed to `defineWorker()` — resource config only */\ntype WorkerOptions = {\n /** Fargate size (default: \"0.5vCPU-1gb\") */\n size?: FargateSize;\n /** How long the worker stays alive without messages before shutting down (default: \"5m\") */\n idleTimeout?: Duration;\n /** Max messages processed in parallel (default: 1, max: 10) */\n concurrency?: number;\n};\n\n// ============ Builder ============\n\ninterface WorkerBuilder<\n T,\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers, workers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): WorkerBuilder<T, D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): WorkerBuilder<T, D, P2, C, HasFiles>;\n\n /** Include static files in the bundle. Chainable — call multiple times. */\n include(glob: string): WorkerBuilder<T, D, P, C, true>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */\n setup(lambda: LambdaOptions): WorkerBuilder<T, D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): WorkerBuilder<T, D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): WorkerBuilder<T, D, P, C2, HasFiles>;\n\n /** Handle errors thrown by onMessage. Return \"delete\" to discard, \"retry\" to reprocess (default). */\n onError(\n fn: (args: { error: unknown; msg: T; retryCount: number } & SpreadCtx<C>) => \"retry\" | \"delete\" | void | Promise<\"retry\" | \"delete\" | void>\n ): WorkerBuilder<T, D, P, C, HasFiles>;\n\n /** Cleanup callback — runs when the worker shuts down */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): WorkerBuilder<T, D, P, C, HasFiles>;\n\n /** Process a single message from the queue (terminal) */\n onMessage(\n fn: WorkerMessageFn<T, C>\n ): WorkerHandler<T, C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define a worker — a long-running Fargate container with an SQS queue.\n *\n * The worker stays alive while processing messages and shuts down after\n * an idle timeout with no new messages. Other handlers can send messages\n * to the worker via `deps.worker.send(msg)`.\n *\n * @typeParam T - Type of messages the worker receives via its queue\n *\n * @example\n * ```typescript\n * type Job = { type: \"export\"; userId: string }\n *\n * export const worker = defineWorker<Job>({ memory: 2048, concurrency: 5 })\n * .deps(() => ({ orders }))\n * .setup(async ({ deps }) => ({ db: deps.orders }))\n * .onMessage(async (msg, { db }) => {\n * await processJob(msg, db)\n * })\n * ```\n */\nexport function defineWorker<T = unknown>(options?: WorkerOptions): WorkerBuilder<T> {\n const spec: WorkerConfig = {\n ...(options?.size ? { size: options.size } : {}),\n ...(options?.idleTimeout ? { idleTimeout: options.idleTimeout } : {}),\n ...(options?.concurrency ? { concurrency: options.concurrency } : {}),\n };\n\n const state: {\n spec: WorkerConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n onMessage?: (...args: any[]) => any;\n } = { spec };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n const finalize = (): WorkerHandler => ({\n __brand: \"effortless-worker\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(state.onMessage ? { onMessage: state.onMessage } : {}),\n }) as WorkerHandler;\n\n const builder: WorkerBuilder<T> = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n onMessage(fn) {\n state.onMessage = fn as any;\n return finalize() as any;\n },\n };\n\n return builder;\n}\n","import type { StandardSchemaV1, StandardJSONSchemaV1 } from \"@standard-schema/spec\";\nimport type { AnySecretRef, ResolveConfig, LambdaWithPermissions, ConfigFactory, LambdaOptions } from \"./handler-options\";\nimport { resolveConfigFactory } from \"./handler-options\";\nimport type { AnyDepHandler, ResolveDeps } from \"./handler-deps\";\nimport type { StaticFiles } from \"./shared\";\n\n// ============ Static config ============\n\n/** Static config extracted at deploy time */\nexport type McpConfig = {\n /** MCP server name (used in server info) */\n name: string;\n /** MCP server version (default: \"1.0.0\") */\n version?: string;\n /** Human-readable description — sent to clients in initialize response as system prompt context */\n instructions?: string;\n /** Lambda function settings (memory, timeout, permissions, etc.) */\n lambda?: LambdaWithPermissions;\n};\n\n// ============ MCP tool types ============\n\n/** JSON Schema for a tool's input parameters */\nexport type McpInputSchema = {\n type: \"object\";\n properties: Record<string, unknown>;\n required?: string[];\n};\n\n/** Content block returned by a tool handler */\nexport type McpToolContent =\n | { type: \"text\"; text: string }\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"resource\"; resource: { uri: string; text: string; mimeType?: string } };\n\n/** Result returned by a tool handler */\nexport type McpToolResult = {\n content: McpToolContent[];\n isError?: boolean;\n};\n\n// ============ MCP resource types ============\n\n/** Content returned when reading a resource */\nexport type McpResourceContent =\n | { uri: string; mimeType?: string; text: string }\n | { uri: string; mimeType?: string; blob: string };\n\n/** Legacy resource content result type used by runtime internals */\ntype McpResourceResult = McpResourceContent | McpResourceContent[] | Promise<McpResourceContent | McpResourceContent[]>;\n\n/** Infer resource params type from schema, or fall back to Record<string, string> */\ntype InferResourceParams<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : Record<string, string>;\n\n/** Resource definition — pass `params` for typed schema validation on URI template params */\nexport type McpResourceDefInput<S extends StandardSchemaV1 | undefined = undefined> = {\n /** Resource URI or URI template (e.g. \"resource://contacts/{id}\") */\n uri: string;\n /** Human-readable name */\n name: string;\n /** Schema for URI template params — provides type inference + validation */\n params?: S;\n /** Optional description */\n description?: string;\n /** Optional MIME type */\n mimeType?: string;\n};\n\n/** What resource handlers can return — plain data is auto-wrapped by the runtime */\ntype McpResourceReturn = unknown | Promise<unknown>;\n\n/** Resource handler — receives params (typed or Record<string, string>) and ctx */\ntype McpResourceHandler<C, S = undefined> = (params: InferResourceParams<S>, ctx: SpreadCtx<C>) => McpResourceReturn;\n\n// Legacy types used by wrap-mcp runtime\n/** @internal */\nexport type McpResourceDef<C = undefined> = {\n name: string;\n description?: string;\n mimeType?: string;\n handler: (ctx: SpreadCtx<C>) => McpResourceResult;\n};\n\n/** @internal */\nexport type McpResourceTemplateDef<C = undefined> = {\n name: string;\n description?: string;\n mimeType?: string;\n handler: (params: Record<string, string>, ctx: SpreadCtx<C>) => McpResourceResult;\n};\n\n/** @internal */\nexport type McpResourceMap<C = undefined> = {\n [uriOrTemplate: string]: McpResourceDef<C> | McpResourceTemplateDef<C>;\n};\n\n// ============ MCP prompt types ============\n\n/** An argument accepted by a prompt */\nexport type McpPromptArgument = {\n /** Argument name */\n name: string;\n /** Optional description */\n description?: string;\n /** Whether the argument is required (default: false) */\n required?: boolean;\n};\n\n/** Content block inside a prompt message */\nexport type McpPromptContent =\n | { type: \"text\"; text: string }\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"audio\"; data: string; mimeType: string }\n | { type: \"resource\"; resource: { uri: string; mimeType?: string; text?: string; blob?: string } };\n\n/** A single message returned by a prompt */\nexport type McpPromptMessage = {\n role: \"user\" | \"assistant\";\n content: McpPromptContent;\n};\n\n/** Result returned by a prompt handler */\nexport type McpPromptResult = {\n description?: string;\n messages: McpPromptMessage[];\n};\n\n/** @internal Legacy prompt definition used by runtime */\nexport type McpPromptDef<C = undefined> = {\n description?: string;\n arguments?: McpPromptArgument[];\n handler: (args: Record<string, string>, ctx: SpreadCtx<C>) => McpPromptResult | Promise<McpPromptResult>;\n};\n\n/** Infer prompt args type from schema, or fall back to Record<string, string> */\ntype InferPromptArgs<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : Record<string, string>;\n\n/** Prompt definition — pass a Standard Schema for `args` for typed validation, or McpPromptArgument[] for untyped */\nexport type McpPromptDefInput<S extends StandardSchemaV1 | McpPromptArgument[] | undefined = undefined> = {\n /** Prompt name */\n name: string;\n /** Human-readable description */\n description?: string;\n /** Args: Standard Schema for typed validation, or McpPromptArgument[] for untyped */\n args?: S;\n};\n\n/** Handler return: string auto-wraps as user message, or return full McpPromptResult */\ntype McpPromptReturn = string | McpPromptResult | Promise<string | McpPromptResult>;\n\n/** Prompt handler — receives args (typed or Record<string, string>) and ctx */\ntype McpPromptHandler<C, S = undefined> = (args: InferPromptArgs<S>, ctx: SpreadCtx<C>) => McpPromptReturn;\n\n/** Infer tool input type from schema, or fall back to any */\ntype InferToolInput<S> = S extends StandardJSONSchemaV1 ? StandardJSONSchemaV1.InferOutput<S> : any;\n\n/** Tool definition — pass a StandardJSONSchemaV1 for `input` for typed validation, or McpInputSchema for raw JSON Schema */\nexport type McpToolDefInput<S extends StandardJSONSchemaV1 | McpInputSchema = McpInputSchema> = {\n /** Tool name */\n name: string;\n /** Human-readable description of the tool */\n description: string;\n /** Schema for tool input: StandardJSONSchemaV1 (e.g. z.object({...})) or raw McpInputSchema */\n input: S;\n};\n\n/** Tool handler — receives input (typed or any) and ctx */\ntype McpToolHandler<C, S = McpInputSchema> = (input: InferToolInput<S>, ctx: SpreadCtx<C>) => unknown | Promise<unknown>;\n\n// ============ Setup args ============\n\n/** Setup factory — receives deps/config/files based on what was declared */\ntype SetupArgs<D, P, HasFiles extends boolean> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> })\n & (HasFiles extends true ? { files: StaticFiles } : {});\n\n/** Auth factory args — receives config/deps based on what was declared */\ntype AuthArgs<D, P> =\n & ([D] extends [undefined] ? {} : { deps: ResolveDeps<D> })\n & ([P] extends [undefined] ? {} : { config: ResolveConfig<P & {}> });\n\n/** Spread ctx into callback args (empty when no setup) */\ntype SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};\n\n// ============ Handler type ============\n\n/**\n * Handler object created by defineMcp.\n * @internal\n */\nexport type McpHandler<C = any> = {\n readonly __brand: \"effortless-mcp\";\n readonly __spec: McpConfig;\n readonly onError?: (...args: any[]) => any;\n readonly onCleanup?: (...args: any[]) => any;\n readonly setup?: (...args: any[]) => C | Promise<C>;\n readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);\n readonly config?: Record<string, unknown>;\n readonly static?: string[];\n readonly resources?: (...args: any[]) => any;\n readonly prompts?: (...args: any[]) => any;\n readonly tools?: (...args: any[]) => any;\n};\n\n// ============ McpEntries — returned after first singular method ============\n\n/**\n * Finalized MCP handler with chainable registration methods.\n * Has `__brand` so CLI discovers it. Each `.tool()/.resource()/.prompt()` adds an entry and returns self.\n */\nexport interface McpEntries<C = undefined> extends McpHandler<C> {\n /** Register a tool */\n tool<S extends StandardJSONSchemaV1 | McpInputSchema = McpInputSchema>(def: McpToolDefInput<S>, handler: McpToolHandler<C, S>): McpEntries<C>;\n /** Register a resource */\n resource<S extends StandardSchemaV1 | undefined = undefined>(def: McpResourceDefInput<S>, handler: McpResourceHandler<C, S>): McpEntries<C>;\n /** Register a prompt */\n prompt<S extends StandardSchemaV1 | McpPromptArgument[] | undefined = undefined>(def: McpPromptDefInput<S>, handler: McpPromptHandler<C, S>): McpEntries<C>;\n}\n\n// ============ Options ============\n\n/** Options passed to `defineMcp()` */\ntype McpOptions = {\n /** MCP server name (used in server info) */\n name: string;\n /** MCP server version (default: \"1.0.0\") */\n version?: string;\n /** Human-readable description — sent to clients in initialize response as system prompt context */\n instructions?: string;\n};\n\n// ============ Builder ============\n\ninterface McpBuilder<\n D = undefined,\n P = undefined,\n C = undefined,\n HasFiles extends boolean = false,\n> {\n /** Declare handler dependencies (tables, queues, buckets, mailers, workers) */\n deps<D2 extends Record<string, AnyDepHandler>>(\n fn: () => D2\n ): McpBuilder<D2, P, C, HasFiles>;\n\n /** Declare SSM secrets */\n config<P2 extends Record<string, AnySecretRef>>(\n fn: ConfigFactory<P2>\n ): McpBuilder<D, P2, C, HasFiles>;\n\n /** Include static files in the bundle. Chainable — call multiple times. */\n include(glob: string): McpBuilder<D, P, C, true>;\n\n /** Configure session-based authentication. Receives resolved config/deps. All requests require a valid session. */\n auth<A2>(\n fn: (args: AuthArgs<D, P>) => import(\"./define-api\").AuthOptions<A2> | Promise<import(\"./define-api\").AuthOptions<A2>>\n ): McpBuilder<D, P, C, HasFiles>;\n\n /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */\n setup(lambda: LambdaOptions): McpBuilder<D, P, C, HasFiles>;\n\n /** Initialize shared state on cold start. Receives deps, config, files. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>\n ): McpBuilder<D, P, C2, HasFiles>;\n\n /** Initialize shared state on cold start + configure Lambda settings. */\n setup<C2>(\n fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>,\n lambda: LambdaOptions\n ): McpBuilder<D, P, C2, HasFiles>;\n\n /** Handle errors thrown by tool handlers */\n onError(\n fn: (args: { error: unknown; toolName: string } & SpreadCtx<C>) => void | Promise<void>\n ): McpBuilder<D, P, C, HasFiles>;\n\n /** Cleanup callback — runs on shutdown */\n onCleanup(\n fn: (args: SpreadCtx<C>) => void | Promise<void>\n ): McpBuilder<D, P, C, HasFiles>;\n\n /** Register a tool */\n tool<S extends StandardJSONSchemaV1 | McpInputSchema = McpInputSchema>(def: McpToolDefInput<S>, handler: McpToolHandler<C, S>): McpEntries<C>;\n /** Register a resource */\n resource<S extends StandardSchemaV1 | undefined = undefined>(def: McpResourceDefInput<S>, handler: McpResourceHandler<C, S>): McpEntries<C>;\n /** Register a prompt */\n prompt<S extends StandardSchemaV1 | McpPromptArgument[] | undefined = undefined>(def: McpPromptDefInput<S>, handler: McpPromptHandler<C, S>): McpEntries<C>;\n\n /** Finalize the handler without adding more entries */\n build(): McpHandler<C>;\n}\n\n// ============ Implementation ============\n\n/**\n * Define an MCP (Model Context Protocol) server endpoint.\n *\n * Creates a Lambda-backed MCP server that exposes tools, resources, and prompts\n * for AI models and MCP-compatible clients via Streamable HTTP (JSON-RPC over POST).\n *\n * @see https://modelcontextprotocol.io/specification/2025-03-26 — MCP specification\n * @see https://effortless-aws.com/use-cases/mcp-server/ — full documentation with examples\n */\nexport function defineMcp(options: McpOptions): McpBuilder {\n const spec: McpConfig = {\n name: options.name,\n ...(options.version ? { version: options.version } : {}),\n ...(options.instructions ? { instructions: options.instructions } : {}),\n };\n\n const state: {\n spec: McpConfig;\n deps?: () => Record<string, unknown>;\n config?: Record<string, unknown>;\n static?: string[];\n setup?: (...args: any[]) => any;\n authFn?: (...args: any[]) => any;\n onError?: (...args: any[]) => any;\n onCleanup?: (...args: any[]) => any;\n toolEntries: [string, any][];\n resourceEntries: [string, any][];\n promptEntries: [string, any][];\n } = { spec, toolEntries: [], resourceEntries: [], promptEntries: [] };\n\n const applyLambdaOptions = (lambda: LambdaOptions) => {\n if (Object.keys(lambda).length > 0) {\n state.spec = { ...state.spec, lambda: { ...state.spec.lambda, ...lambda } };\n }\n };\n\n // Build factory functions from accumulated entries\n const buildToolsFactory = () =>\n state.toolEntries.length > 0\n ? () => Object.fromEntries(state.toolEntries)\n : undefined;\n\n const buildResourcesFactory = () =>\n state.resourceEntries.length > 0\n ? () => Object.fromEntries(state.resourceEntries)\n : undefined;\n\n const buildPromptsFactory = () =>\n state.promptEntries.length > 0\n ? () => Object.fromEntries(state.promptEntries)\n : undefined;\n\n const finalize = (): McpHandler => {\n const tools = buildToolsFactory();\n const resources = buildResourcesFactory();\n const prompts = buildPromptsFactory();\n return {\n __brand: \"effortless-mcp\",\n __spec: state.spec,\n ...(state.onError ? { onError: state.onError } : {}),\n ...(state.onCleanup ? { onCleanup: state.onCleanup } : {}),\n ...(state.setup ? { setup: state.setup } : {}),\n ...(state.authFn ? { authFn: state.authFn } : {}),\n ...(state.deps ? { deps: state.deps } : {}),\n ...(state.config ? { config: state.config } : {}),\n ...(state.static ? { static: state.static } : {}),\n ...(resources ? { resources } : {}),\n ...(prompts ? { prompts } : {}),\n ...(tools ? { tools } : {}),\n } as McpHandler;\n };\n\n const finalizeWithEntries = (): McpEntries => {\n const handler: any = finalize();\n\n handler.tool = (def: any, fn: any) => {\n state.toolEntries.push([def.name, { description: def.description, input: def.input, handler: fn }]);\n handler.tools = buildToolsFactory();\n return handler;\n };\n\n handler.resource = (def: any, fn: any) => {\n const entry = { name: def.name, ...(def.description ? { description: def.description } : {}), ...(def.mimeType ? { mimeType: def.mimeType } : {}), handler: fn, ...(def.params ? { params: def.params } : {}) };\n state.resourceEntries.push([def.uri, entry]);\n handler.resources = buildResourcesFactory();\n return handler;\n };\n\n handler.prompt = (def: any, fn: any) => {\n const entry = { ...(def.description ? { description: def.description } : {}), args: def.args, handler: fn };\n state.promptEntries.push([def.name, entry]);\n handler.prompts = buildPromptsFactory();\n return handler;\n };\n\n return handler as McpEntries;\n };\n\n const builder: McpBuilder = {\n deps(fn) {\n state.deps = fn as any;\n return builder as any;\n },\n config(fn) {\n state.config = resolveConfigFactory(fn) as any;\n return builder as any;\n },\n include(glob) {\n state.static = [...(state.static ?? []), glob];\n return builder as any;\n },\n auth(fn: any) {\n state.authFn = fn;\n return builder as any;\n },\n setup(fnOrLambda: any, maybeLambda?: LambdaOptions) {\n if (typeof fnOrLambda === \"function\") {\n state.setup = fnOrLambda;\n if (maybeLambda) applyLambdaOptions(maybeLambda);\n } else {\n applyLambdaOptions(fnOrLambda);\n }\n return builder as any;\n },\n onError(fn) {\n state.onError = fn as any;\n return builder as any;\n },\n onCleanup(fn) {\n state.onCleanup = fn as any;\n return builder as any;\n },\n tool(def: any, fn: any) {\n state.toolEntries.push([def.name, { description: def.description, input: def.input, handler: fn }]);\n return finalizeWithEntries() as any;\n },\n resource(def: any, fn: any) {\n const entry = { name: def.name, ...(def.description ? { description: def.description } : {}), ...(def.mimeType ? { mimeType: def.mimeType } : {}), handler: fn, ...(def.params ? { params: def.params } : {}) };\n state.resourceEntries.push([def.uri, entry]);\n return finalizeWithEntries() as any;\n },\n prompt(def: any, fn: any) {\n const entry = { ...(def.description ? { description: def.description } : {}), args: def.args, handler: fn };\n state.promptEntries.push([def.name, entry]);\n return finalizeWithEntries() as any;\n },\n build() {\n return finalize() as any;\n },\n };\n\n return builder;\n}\n"],"mappings":"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","names":["options","ttl"]}
|
package/dist/runtime/wrap-api.js
CHANGED
package/dist/runtime/wrap-mcp.js
CHANGED