effortless-aws 0.30.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,17 +1,7 @@
1
1
  /**
2
2
  * Configuration for an Effortless project.
3
3
  *
4
- * @example
5
- * ```typescript
6
- * // effortless.config.ts
7
- * import { defineConfig } from "effortless-aws";
8
- *
9
- * export default defineConfig({
10
- * name: "my-service",
11
- * region: "eu-central-1",
12
- * handlers: "src",
13
- * });
14
- * ```
4
+ * @see {@link https://effortless-aws.website/configuration | Configuration guide}
15
5
  */
16
6
  type EffortlessConfig = {
17
7
  /**
@@ -78,21 +68,7 @@ type EffortlessConfig = {
78
68
  runtime?: string;
79
69
  };
80
70
  };
81
- /**
82
- * Helper function for type-safe configuration.
83
- * Returns the config object as-is, but provides TypeScript autocompletion.
84
- *
85
- * @example
86
- * ```typescript
87
- * import { defineConfig } from "effortless-aws";
88
- *
89
- * export default defineConfig({
90
- * name: "my-service",
91
- * region: "eu-central-1",
92
- * handlers: "src",
93
- * });
94
- * ```
95
- */
71
+ /** Helper function for type-safe configuration with TypeScript autocompletion. */
96
72
  declare const defineConfig: (config: EffortlessConfig) => EffortlessConfig;
97
73
 
98
74
  /** Generator spec for auto-creating secrets at deploy time. */
@@ -136,6 +112,20 @@ type LambdaWithPermissions = LambdaConfig & {
136
112
  /** Additional IAM permissions for the Lambda */
137
113
  permissions?: Permission[];
138
114
  };
115
+ /**
116
+ * Lambda configuration passed as argument to `.setup()`.
117
+ * Common across all handler types that create a Lambda function.
118
+ */
119
+ type LambdaOptions = {
120
+ /** Lambda memory in MB (default: 256) */
121
+ memory?: number;
122
+ /** Lambda timeout (default: 30s). Accepts seconds or duration string: `"30s"`, `"5m"` */
123
+ timeout?: Duration;
124
+ /** Additional IAM permissions for the Lambda */
125
+ permissions?: Permission[];
126
+ /** Logging verbosity: "error" (errors only), "info" (+ execution summary), "debug" (+ input/output). Default: "info" */
127
+ logLevel?: LogLevel;
128
+ };
139
129
  type AnySecretRef = SecretRef<any>;
140
130
  /**
141
131
  * Reference to an SSM Parameter Store secret.
@@ -236,22 +226,6 @@ type PutInput<T> = {
236
226
  data: T;
237
227
  ttl?: number;
238
228
  };
239
- /**
240
- * Create a schema function that casts input to T without runtime validation.
241
- * Use when you need T inference alongside other generics (deps, config).
242
- * For handlers without deps/config, prefer `defineTable<Order>({...})`.
243
- * For untrusted input, prefer a real parser (Zod, Effect Schema).
244
- *
245
- * @example
246
- * ```typescript
247
- * export const orders = defineTable({
248
- * schema: unsafeAs<Order>(),
249
- * deps: () => ({ notifications }),
250
- * onRecord: async ({ record, deps }) => { ... }
251
- * });
252
- * ```
253
- */
254
- declare function unsafeAs<T>(): (input: unknown) => T;
255
229
 
256
230
  /** HTTP methods supported by Lambda Function URLs */
257
231
  type HttpMethod$1 = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "ANY";
@@ -387,9 +361,9 @@ type BucketEvent = {
387
361
  bucketName: string;
388
362
  };
389
363
  /** Spread ctx into callback args (empty when no setup) */
390
- type SpreadCtx$3<C> = [C] extends [undefined] ? {} : C & {};
364
+ type SpreadCtx$5<C> = [C] extends [undefined] ? {} : C & {};
391
365
  /** Setup factory — receives bucket/deps/config/files based on what was declared */
392
- type SetupArgs$3<D, P, HasFiles extends boolean> = {
366
+ type SetupArgs$5<D, P, HasFiles extends boolean> = {
393
367
  bucket: BucketClient;
394
368
  } & ([D] extends [undefined] ? {} : {
395
369
  deps: ResolveDeps<D>;
@@ -403,13 +377,13 @@ type SetupArgs$3<D, P, HasFiles extends boolean> = {
403
377
  */
404
378
  type BucketObjectCreatedFn<C = undefined> = (args: {
405
379
  event: BucketEvent;
406
- } & SpreadCtx$3<C>) => Promise<void>;
380
+ } & SpreadCtx$5<C>) => Promise<void>;
407
381
  /**
408
382
  * Callback function type for S3 ObjectRemoved events
409
383
  */
410
384
  type BucketObjectRemovedFn<C = undefined> = (args: {
411
385
  event: BucketEvent;
412
- } & SpreadCtx$3<C>) => Promise<void>;
386
+ } & SpreadCtx$5<C>) => Promise<void>;
413
387
  /**
414
388
  * Internal handler object created by defineBucket
415
389
  * @internal
@@ -428,34 +402,30 @@ type BucketHandler<C = any> = {
428
402
  };
429
403
  /** Options passed to `defineBucket()` — static config */
430
404
  type BucketOptions = {
431
- /** Lambda memory in MB (default: 256) */
432
- memory?: number;
433
- /** Lambda timeout (default: 30s) */
434
- timeout?: Duration;
435
- /** Additional IAM permissions for the Lambda */
436
- permissions?: Permission[];
437
- /** Logging verbosity */
438
- logLevel?: LogLevel;
439
405
  /** S3 key prefix filter for event notifications (e.g., "uploads/") */
440
406
  prefix?: string;
441
407
  /** S3 key suffix filter for event notifications (e.g., ".jpg") */
442
408
  suffix?: string;
443
- /** Static file glob patterns to bundle into the Lambda ZIP */
444
- static?: string[];
445
409
  };
446
410
  interface BucketBuilder<D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
447
411
  /** Declare handler dependencies */
448
412
  deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): BucketBuilder<D2, P, C, HasFiles>;
449
413
  /** Declare SSM secrets */
450
414
  config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): BucketBuilder<D, P2, C, HasFiles>;
415
+ /** Include static files in the Lambda ZIP */
416
+ include(glob: string): BucketBuilder<D, P, C, true>;
417
+ /** Initialize shared state on cold start with lambda options */
418
+ setup(lambda: LambdaOptions): BucketBuilder<D, P, C, HasFiles>;
451
419
  /** Initialize shared state on cold start. Receives bucket (self-client), deps, config, files. */
452
- setup<C2>(fn: (args: SetupArgs$3<D, P, HasFiles>) => C2 | Promise<C2>): BucketBuilder<D, P, C2, HasFiles>;
420
+ setup<C2>(fn: (args: SetupArgs$5<D, P, HasFiles>) => C2 | Promise<C2>): BucketBuilder<D, P, C2, HasFiles>;
421
+ /** Initialize shared state on cold start with lambda options. Receives bucket (self-client), deps, config, files. */
422
+ setup<C2>(fn: (args: SetupArgs$5<D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): BucketBuilder<D, P, C2, HasFiles>;
453
423
  /** Handle errors thrown by callbacks */
454
424
  onError(fn: (args: {
455
425
  error: unknown;
456
- } & SpreadCtx$3<C>) => void): BucketBuilder<D, P, C, HasFiles>;
426
+ } & SpreadCtx$5<C>) => void | Promise<void>): BucketBuilder<D, P, C, HasFiles>;
457
427
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
458
- onCleanup(fn: (args: SpreadCtx$3<C>) => void | Promise<void>): BucketBuilder<D, P, C, HasFiles>;
428
+ onCleanup(fn: (args: SpreadCtx$5<C>) => void | Promise<void>): BucketBuilder<D, P, C, HasFiles>;
459
429
  /** Handle S3 ObjectCreated events (terminal — returns finalized handler) */
460
430
  onObjectCreated(fn: BucketObjectCreatedFn<C>): BucketHandler<C>;
461
431
  /** Handle S3 ObjectRemoved events (terminal — returns finalized handler) */
@@ -466,24 +436,17 @@ interface BucketBuilder<D = undefined, P = undefined, C = undefined, HasFiles ex
466
436
  /**
467
437
  * Define an S3 bucket with optional event handlers.
468
438
  *
469
- * @example Bucket with event handler
439
+ * @see {@link https://effortless-aws.website/use-cases/storage | Storage guide}
440
+ *
441
+ * @example
470
442
  * ```typescript
471
443
  * export const uploads = defineBucket({ prefix: "images/", suffix: ".jpg" })
472
444
  * .onObjectCreated(async ({ event, bucket }) => {
473
445
  * console.log("New upload:", event.key);
474
446
  * })
475
- *
476
- * ```
477
- *
478
- * @example Resource-only bucket (no Lambda)
479
- * ```typescript
480
- * export const assets = defineBucket().build()
481
447
  * ```
482
448
  */
483
449
  declare function defineBucket(): BucketBuilder;
484
- declare function defineBucket(options: BucketOptions & {
485
- static: string[];
486
- }): BucketBuilder<undefined, undefined, undefined, true>;
487
450
  declare function defineBucket(options: BucketOptions): BucketBuilder;
488
451
 
489
452
  /**
@@ -510,6 +473,8 @@ type MailerHandler = {
510
473
  * On first deploy, DKIM DNS records are printed to the console.
511
474
  * Add them to your DNS provider to verify the domain.
512
475
  *
476
+ * @see {@link https://effortless-aws.website/use-cases/email | Email guide}
477
+ *
513
478
  * @param options - Mailer configuration with the domain to send from
514
479
  * @returns Handler object used by the deployment system and as a `deps` value
515
480
  *
@@ -591,9 +556,9 @@ type FifoQueueConfig = {
591
556
  maxReceiveCount?: number;
592
557
  };
593
558
  /** Spread ctx into callback args (empty when no setup) */
594
- type SpreadCtx$2<C> = [C] extends [undefined] ? {} : C & {};
559
+ type SpreadCtx$4<C> = [C] extends [undefined] ? {} : C & {};
595
560
  /** Setup factory — receives deps/config/files based on what was declared */
596
- type SetupArgs$2<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {} : {
561
+ type SetupArgs$4<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {} : {
597
562
  deps: ResolveDeps<D>;
598
563
  }) & ([P] extends [undefined] ? {} : {
599
564
  config: ResolveConfig<P & {}>;
@@ -606,7 +571,7 @@ type SetupArgs$2<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {}
606
571
  */
607
572
  type FifoQueueMessageFn<T = unknown, C = undefined> = (args: {
608
573
  message: FifoQueueMessage<T>;
609
- } & SpreadCtx$2<C>) => Promise<void>;
574
+ } & SpreadCtx$4<C>) => Promise<void>;
610
575
  /**
611
576
  * Batch handler function.
612
577
  * Called once with all messages in the batch.
@@ -614,7 +579,7 @@ type FifoQueueMessageFn<T = unknown, C = undefined> = (args: {
614
579
  */
615
580
  type FifoQueueBatchFn<T = unknown, C = undefined> = (args: {
616
581
  messages: FifoQueueMessage<T>[];
617
- } & SpreadCtx$2<C>) => Promise<void | {
582
+ } & SpreadCtx$4<C>) => Promise<void | {
618
583
  failures: string[];
619
584
  }>;
620
585
  /**
@@ -636,14 +601,6 @@ type FifoQueueHandler<T = unknown, C = any> = {
636
601
  };
637
602
  /** Options passed to `defineFifoQueue()` — static config */
638
603
  type FifoQueueOptions<T> = {
639
- /** Lambda memory in MB (default: 256) */
640
- memory?: number;
641
- /** Lambda timeout (default: 30s) */
642
- timeout?: Duration;
643
- /** Additional IAM permissions for the Lambda */
644
- permissions?: Permission[];
645
- /** Logging verbosity */
646
- logLevel?: LogLevel;
647
604
  /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */
648
605
  batchSize?: number;
649
606
  /** Maximum time to gather messages before invoking (default: 0) */
@@ -660,22 +617,26 @@ type FifoQueueOptions<T> = {
660
617
  maxReceiveCount?: number;
661
618
  /** Decode/validate function for the message body */
662
619
  schema?: (input: unknown) => T;
663
- /** Static file glob patterns to bundle into the Lambda ZIP */
664
- static?: string[];
665
620
  };
666
621
  interface FifoQueueBuilder<T = unknown, D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
667
622
  /** Declare handler dependencies */
668
623
  deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): FifoQueueBuilder<T, D2, P, C, HasFiles>;
669
624
  /** Declare SSM secrets */
670
625
  config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): FifoQueueBuilder<T, D, P2, C, HasFiles>;
626
+ /** Include static files in the Lambda bundle. Chainable — call multiple times. */
627
+ include(glob: string): FifoQueueBuilder<T, D, P, C, true>;
628
+ /** Configure Lambda settings only (memory, timeout, permissions, etc.) */
629
+ setup(lambda: LambdaOptions): FifoQueueBuilder<T, D, P, C, HasFiles>;
671
630
  /** Initialize shared state on cold start. Receives deps, config, files. */
672
- setup<C2>(fn: (args: SetupArgs$2<D, P, HasFiles>) => C2 | Promise<C2>): FifoQueueBuilder<T, D, P, C2, HasFiles>;
631
+ setup<C2>(fn: (args: SetupArgs$4<D, P, HasFiles>) => C2 | Promise<C2>): FifoQueueBuilder<T, D, P, C2, HasFiles>;
632
+ /** Initialize shared state on cold start + configure Lambda settings. */
633
+ setup<C2>(fn: (args: SetupArgs$4<D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): FifoQueueBuilder<T, D, P, C2, HasFiles>;
673
634
  /** Handle errors thrown by message handlers */
674
635
  onError(fn: (args: {
675
636
  error: unknown;
676
- } & SpreadCtx$2<C>) => void): FifoQueueBuilder<T, D, P, C, HasFiles>;
637
+ } & SpreadCtx$4<C>) => void | Promise<void>): FifoQueueBuilder<T, D, P, C, HasFiles>;
677
638
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
678
- onCleanup(fn: (args: SpreadCtx$2<C>) => void | Promise<void>): FifoQueueBuilder<T, D, P, C, HasFiles>;
639
+ onCleanup(fn: (args: SpreadCtx$4<C>) => void | Promise<void>): FifoQueueBuilder<T, D, P, C, HasFiles>;
679
640
  /** Per-message handler (terminal — returns finalized handler) */
680
641
  onMessage(fn: FifoQueueMessageFn<T, C>): FifoQueueHandler<T, C>;
681
642
  /** Batch handler (terminal — returns finalized handler) */
@@ -684,30 +645,115 @@ interface FifoQueueBuilder<T = unknown, D = undefined, P = undefined, C = undefi
684
645
  /**
685
646
  * Define a FIFO SQS queue with a Lambda message handler.
686
647
  *
687
- * @example Per-message processing
688
- * ```typescript
689
- * export const orderQueue = defineFifoQueue<OrderEvent>()
690
- * .onMessage(async ({ message }) => {
691
- * console.log("Processing order:", message.body.orderId);
692
- * })
693
- *
694
- * ```
648
+ * @see {@link https://effortless-aws.website/use-cases/queue | Queue guide}
695
649
  *
696
- * @example Batch processing with schema
650
+ * @example
697
651
  * ```typescript
698
652
  * export const notifications = defineFifoQueue({ batchSize: 5, schema: (i) => NotifSchema.parse(i) })
699
653
  * .onMessageBatch(async ({ messages }) => {
700
654
  * await sendAll(messages.map(m => m.body));
701
655
  * })
702
- *
703
656
  * ```
704
657
  */
705
658
  declare function defineFifoQueue<T = unknown>(): FifoQueueBuilder<T>;
706
- declare function defineFifoQueue<T = unknown>(options: FifoQueueOptions<T> & {
707
- static: string[];
708
- }): FifoQueueBuilder<T, undefined, undefined, undefined, true>;
709
659
  declare function defineFifoQueue<T = unknown>(options: FifoQueueOptions<T>): FifoQueueBuilder<T>;
710
660
 
661
+ /** Fargate container size presets */
662
+ type FargateSize = "0.25vCPU-512mb" | "0.5vCPU-1gb" | "1vCPU-2gb" | "2vCPU-4gb" | "4vCPU-8gb";
663
+ /** Static config extracted at deploy time */
664
+ type WorkerConfig = {
665
+ /** Lambda function settings (memory, timeout, permissions, etc.) */
666
+ lambda?: LambdaWithPermissions;
667
+ /** Fargate size (default: "0.5vCPU-1gb") */
668
+ size?: FargateSize;
669
+ /** How long the worker stays alive without messages before shutting down (default: "5m") */
670
+ idleTimeout?: Duration;
671
+ /** Max messages processed in parallel (default: 1, max: 10) */
672
+ concurrency?: number;
673
+ };
674
+ /** Setup factory — receives deps/config/files based on what was declared */
675
+ type SetupArgs$3<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {} : {
676
+ deps: ResolveDeps<D>;
677
+ }) & ([P] extends [undefined] ? {} : {
678
+ config: ResolveConfig<P & {}>;
679
+ }) & (HasFiles extends true ? {
680
+ files: StaticFiles;
681
+ } : {});
682
+ /** Spread ctx into callback args (empty when no setup) */
683
+ type SpreadCtx$3<C> = [C] extends [undefined] ? {} : C & {};
684
+ /** Callback function for the worker's onMessage */
685
+ type WorkerMessageFn<T, C = undefined> = (msg: T, ctx: SpreadCtx$3<C>) => Promise<void> | void;
686
+ /**
687
+ * Handler object created by defineWorker.
688
+ * @internal
689
+ */
690
+ type WorkerHandler<T = any, C = any> = {
691
+ readonly __brand: "effortless-worker";
692
+ readonly __spec: WorkerConfig;
693
+ readonly onError?: (...args: any[]) => any;
694
+ readonly onCleanup?: (...args: any[]) => any;
695
+ readonly setup?: (...args: any[]) => C | Promise<C>;
696
+ readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);
697
+ readonly config?: Record<string, unknown>;
698
+ readonly static?: string[];
699
+ readonly onMessage?: (...args: any[]) => any;
700
+ };
701
+ /** Options passed to `defineWorker()` — resource config only */
702
+ type WorkerOptions = {
703
+ /** Fargate size (default: "0.5vCPU-1gb") */
704
+ size?: FargateSize;
705
+ /** How long the worker stays alive without messages before shutting down (default: "5m") */
706
+ idleTimeout?: Duration;
707
+ /** Max messages processed in parallel (default: 1, max: 10) */
708
+ concurrency?: number;
709
+ };
710
+ interface WorkerBuilder<T, D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
711
+ /** Declare handler dependencies (tables, queues, buckets, mailers, workers) */
712
+ deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): WorkerBuilder<T, D2, P, C, HasFiles>;
713
+ /** Declare SSM secrets */
714
+ config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): WorkerBuilder<T, D, P2, C, HasFiles>;
715
+ /** Include static files in the bundle. Chainable — call multiple times. */
716
+ include(glob: string): WorkerBuilder<T, D, P, C, true>;
717
+ /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */
718
+ setup(lambda: LambdaOptions): WorkerBuilder<T, D, P, C, HasFiles>;
719
+ /** Initialize shared state on cold start. Receives deps, config, files. */
720
+ setup<C2>(fn: (args: SetupArgs$3<D, P, HasFiles>) => C2 | Promise<C2>): WorkerBuilder<T, D, P, C2, HasFiles>;
721
+ /** Initialize shared state on cold start + configure Lambda settings. */
722
+ setup<C2>(fn: (args: SetupArgs$3<D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): WorkerBuilder<T, D, P, C2, HasFiles>;
723
+ /** Handle errors thrown by onMessage. Return "delete" to discard, "retry" to reprocess (default). */
724
+ onError(fn: (args: {
725
+ error: unknown;
726
+ msg: T;
727
+ retryCount: number;
728
+ } & SpreadCtx$3<C>) => "retry" | "delete" | void | Promise<"retry" | "delete" | void>): WorkerBuilder<T, D, P, C, HasFiles>;
729
+ /** Cleanup callback — runs when the worker shuts down */
730
+ onCleanup(fn: (args: SpreadCtx$3<C>) => void | Promise<void>): WorkerBuilder<T, D, P, C, HasFiles>;
731
+ /** Process a single message from the queue (terminal) */
732
+ onMessage(fn: WorkerMessageFn<T, C>): WorkerHandler<T, C>;
733
+ }
734
+ /**
735
+ * Define a worker — a long-running Fargate container with an SQS queue.
736
+ *
737
+ * The worker stays alive while processing messages and shuts down after
738
+ * an idle timeout with no new messages. Other handlers can send messages
739
+ * to the worker via `deps.worker.send(msg)`.
740
+ *
741
+ * @typeParam T - Type of messages the worker receives via its queue
742
+ *
743
+ * @example
744
+ * ```typescript
745
+ * type Job = { type: "export"; userId: string }
746
+ *
747
+ * export const worker = defineWorker<Job>({ memory: 2048, concurrency: 5 })
748
+ * .deps(() => ({ orders }))
749
+ * .setup(async ({ deps }) => ({ db: deps.orders }))
750
+ * .onMessage(async (msg, { db }) => {
751
+ * await processJob(msg, db)
752
+ * })
753
+ * ```
754
+ */
755
+ declare function defineWorker<T = unknown>(options?: WorkerOptions): WorkerBuilder<T>;
756
+
711
757
  /**
712
758
  * Sort key condition for TableClient.query()
713
759
  */
@@ -872,11 +918,27 @@ type QueueClient<T = unknown> = {
872
918
  queueName: string;
873
919
  };
874
920
 
921
+ /** Options for sending a message to a worker */
922
+ type WorkerSendOptions = {
923
+ /** Delay before the message becomes visible in the queue (max "15m") */
924
+ delay?: Duration;
925
+ /** Whether to start the worker container (default: true) */
926
+ start?: boolean;
927
+ };
928
+ type WorkerClient<T = unknown> = {
929
+ /** Send a message to the worker's queue. Wakes up the worker by default. */
930
+ send(msg: T, options?: WorkerSendOptions): Promise<void>;
931
+ /** Check if the worker is currently running */
932
+ status(): Promise<"running" | "idle">;
933
+ /** Stop the worker (scale ECS service to 0) */
934
+ stop(): Promise<void>;
935
+ };
936
+
875
937
  /** Dep value types supported by the deps declaration */
876
- type AnyDepHandler = TableHandler<any, any> | BucketHandler<any> | MailerHandler | FifoQueueHandler<any, any>;
938
+ type AnyDepHandler = TableHandler<any, any> | BucketHandler<any> | MailerHandler | FifoQueueHandler<any, any> | WorkerHandler<any, any>;
877
939
  /** Maps a deps declaration to resolved runtime client types */
878
940
  type ResolveDeps<D> = {
879
- [K in keyof D]: D[K] extends TableHandler<infer T> ? TableClient<T> : D[K] extends BucketHandler ? BucketClient : D[K] extends MailerHandler ? EmailClient : D[K] extends FifoQueueHandler<infer T> ? QueueClient<T> : never;
941
+ [K in keyof D]: D[K] extends TableHandler<infer T> ? TableClient<T> : D[K] extends BucketHandler ? BucketClient : D[K] extends MailerHandler ? EmailClient : D[K] extends FifoQueueHandler<infer T> ? QueueClient<T> : D[K] extends WorkerHandler<infer T> ? WorkerClient<T> : never;
880
942
  };
881
943
 
882
944
  /** DynamoDB Streams view type - determines what data is captured in stream records */
@@ -939,7 +1001,7 @@ type TableRecord<T = Record<string, unknown>> = {
939
1001
  timestamp?: number;
940
1002
  };
941
1003
  /** Setup factory — receives table/deps/config/files based on what was declared */
942
- type SetupArgs$1<T, D, P, HasFiles extends boolean> = {
1004
+ type SetupArgs$2<T, D, P, HasFiles extends boolean> = {
943
1005
  table: TableClient<T>;
944
1006
  } & ([D] extends [undefined] ? {} : {
945
1007
  deps: ResolveDeps<D>;
@@ -949,7 +1011,7 @@ type SetupArgs$1<T, D, P, HasFiles extends boolean> = {
949
1011
  files: StaticFiles;
950
1012
  } : {});
951
1013
  /** Spread ctx into callback args (empty when no setup) */
952
- type SpreadCtx$1<C> = [C] extends [undefined] ? {} : C & {};
1014
+ type SpreadCtx$2<C> = [C] extends [undefined] ? {} : C & {};
953
1015
  /**
954
1016
  * Callback function type for processing a single DynamoDB stream record.
955
1017
  * Receives the current record and the full batch for context.
@@ -957,7 +1019,7 @@ type SpreadCtx$1<C> = [C] extends [undefined] ? {} : C & {};
957
1019
  type TableRecordFn<T = Record<string, unknown>, C = undefined> = (args: {
958
1020
  record: TableRecord<T>;
959
1021
  batch: readonly TableRecord<T>[];
960
- } & SpreadCtx$1<C>) => Promise<void>;
1022
+ } & SpreadCtx$2<C>) => Promise<void>;
961
1023
  /**
962
1024
  * Batch handler function for DynamoDB stream records.
963
1025
  * Called once with all records in the batch.
@@ -965,7 +1027,7 @@ type TableRecordFn<T = Record<string, unknown>, C = undefined> = (args: {
965
1027
  */
966
1028
  type TableBatchFn<T = Record<string, unknown>, C = undefined> = (args: {
967
1029
  records: readonly TableRecord<T>[];
968
- } & SpreadCtx$1<C>) => Promise<void | {
1030
+ } & SpreadCtx$2<C>) => Promise<void | {
969
1031
  failures: string[];
970
1032
  }>;
971
1033
  /** Static config extracted by AST (no runtime callbacks) */
@@ -987,16 +1049,8 @@ type TableHandler<T = Record<string, unknown>, C = any> = {
987
1049
  readonly onRecord?: (...args: any[]) => any;
988
1050
  readonly onRecordBatch?: (...args: any[]) => any;
989
1051
  };
990
- /** Options passed to `defineTable()` — static config, no generics needed for inference */
1052
+ /** Options passed to `defineTable()` — resource config only, no Lambda settings */
991
1053
  type TableOptions<T> = {
992
- /** Lambda memory in MB (default: 256) */
993
- memory?: number;
994
- /** Lambda timeout (default: 30s). Accepts seconds or duration string: `"30s"`, `"5m"` */
995
- timeout?: Duration;
996
- /** Additional IAM permissions for the Lambda */
997
- permissions?: Permission[];
998
- /** Logging verbosity */
999
- logLevel?: LogLevel;
1000
1054
  /** DynamoDB billing mode (default: "PAY_PER_REQUEST") */
1001
1055
  billingMode?: "PAY_PER_REQUEST" | "PROVISIONED";
1002
1056
  /** Stream view type (default: "NEW_AND_OLD_IMAGES") */
@@ -1013,22 +1067,26 @@ type TableOptions<T> = {
1013
1067
  tagField?: Extract<keyof T, string>;
1014
1068
  /** Decode/validate function for the `data` portion of stream records */
1015
1069
  schema?: (input: unknown) => T;
1016
- /** Static file glob patterns to bundle into the Lambda ZIP */
1017
- static?: string[];
1018
1070
  };
1019
1071
  interface TableBuilder<T = Record<string, unknown>, D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
1020
1072
  /** Declare handler dependencies (tables, queues, buckets, mailers) */
1021
1073
  deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): TableBuilder<T, D2, P, C, HasFiles>;
1022
1074
  /** Declare SSM secrets */
1023
1075
  config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): TableBuilder<T, D, P2, C, HasFiles>;
1076
+ /** Include static files in the Lambda bundle. Chainable — call multiple times. */
1077
+ include(glob: string): TableBuilder<T, D, P, C, true>;
1078
+ /** Configure Lambda settings only (memory, timeout, permissions, etc.) */
1079
+ setup(lambda: LambdaOptions): TableBuilder<T, D, P, C, HasFiles>;
1024
1080
  /** Initialize shared state on cold start. Receives table (self-client), deps, config, files. */
1025
- setup<C2>(fn: (args: SetupArgs$1<T, D, P, HasFiles>) => C2 | Promise<C2>): TableBuilder<T, D, P, C2, HasFiles>;
1081
+ setup<C2>(fn: (args: SetupArgs$2<T, D, P, HasFiles>) => C2 | Promise<C2>): TableBuilder<T, D, P, C2, HasFiles>;
1082
+ /** Initialize shared state on cold start + configure Lambda settings. */
1083
+ setup<C2>(fn: (args: SetupArgs$2<T, D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): TableBuilder<T, D, P, C2, HasFiles>;
1026
1084
  /** Handle errors thrown by onRecord/onRecordBatch */
1027
1085
  onError(fn: (args: {
1028
1086
  error: unknown;
1029
- } & SpreadCtx$1<C>) => void): TableBuilder<T, D, P, C, HasFiles>;
1087
+ } & SpreadCtx$2<C>) => void | Promise<void>): TableBuilder<T, D, P, C, HasFiles>;
1030
1088
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
1031
- onCleanup(fn: (args: SpreadCtx$1<C>) => void | Promise<void>): TableBuilder<T, D, P, C, HasFiles>;
1089
+ onCleanup(fn: (args: SpreadCtx$2<C>) => void | Promise<void>): TableBuilder<T, D, P, C, HasFiles>;
1032
1090
  /** Per-record stream handler (terminal — returns finalized handler) */
1033
1091
  onRecord(fn: TableRecordFn<T, C>): TableHandler<T, C>;
1034
1092
  /** Batch stream handler (terminal — returns finalized handler) */
@@ -1042,7 +1100,9 @@ interface TableBuilder<T = Record<string, unknown>, D = undefined, P = undefined
1042
1100
  * Creates a table with fixed key schema: `pk (S)` + `sk (S)`, plus `tag (S)`,
1043
1101
  * `data (M)`, and `ttl (N)` attributes. TTL is always enabled.
1044
1102
  *
1045
- * @example Table with stream handler
1103
+ * @see {@link https://effortless-aws.website/use-cases/database | Database guide}
1104
+ *
1105
+ * @example
1046
1106
  * ```typescript
1047
1107
  * export const orders = defineTable<OrderData>({ batchSize: 10, concurrency: 5 })
1048
1108
  * .setup(({ table }) => ({ table }))
@@ -1052,21 +1112,8 @@ interface TableBuilder<T = Record<string, unknown>, D = undefined, P = undefined
1052
1112
  * }
1053
1113
  * })
1054
1114
  * ```
1055
- *
1056
- * @example Table only (no Lambda)
1057
- * ```typescript
1058
- * export const users = defineTable<User>().build()
1059
- * ```
1060
- *
1061
- * @example Table as dependency (resource-only, no Lambda)
1062
- * ```typescript
1063
- * export const sessions = defineTable<Session>().build()
1064
- * ```
1065
1115
  */
1066
1116
  declare function defineTable<T = Record<string, unknown>>(): TableBuilder<T>;
1067
- declare function defineTable<T = Record<string, unknown>>(options: TableOptions<T> & {
1068
- static: string[];
1069
- }): TableBuilder<T, undefined, undefined, undefined, true>;
1070
1117
  declare function defineTable<T = Record<string, unknown>>(options: TableOptions<T>): TableBuilder<T>;
1071
1118
 
1072
1119
  /**
@@ -1111,18 +1158,10 @@ type AppHandler = {
1111
1158
  *
1112
1159
  * For static-only sites (no SSR), use {@link defineStaticSite} instead.
1113
1160
  *
1161
+ * @see {@link https://effortless-aws.website/use-cases/web-app | Web app guide}
1162
+ *
1114
1163
  * @param options - App configuration: server directory, assets directory, optional build command
1115
1164
  * @returns Handler object used by the deployment system
1116
- *
1117
- * @example Nuxt SSR
1118
- * ```typescript
1119
- * export const app = defineApp({
1120
- * build: "nuxt build",
1121
- * server: ".output/server",
1122
- * assets: ".output/public",
1123
- * lambda: { memory: 1024 },
1124
- * });
1125
- * ```
1126
1165
  */
1127
1166
  declare const defineApp: () => (options: AppConfig) => AppHandler;
1128
1167
 
@@ -1199,26 +1238,10 @@ type StaticSiteHandler = {
1199
1238
  /**
1200
1239
  * Deploy a static site via S3 + CloudFront CDN.
1201
1240
  *
1241
+ * @see {@link https://effortless-aws.website/use-cases/web-app | Web app guide}
1242
+ *
1202
1243
  * @param options - Static site configuration: directory, optional SPA mode, build command
1203
1244
  * @returns Handler object used by the deployment system
1204
- *
1205
- * @example Documentation site
1206
- * ```typescript
1207
- * export const docs = defineStaticSite({
1208
- * dir: "dist",
1209
- * build: "npx astro build",
1210
- * });
1211
- * ```
1212
- *
1213
- * @example SPA with client-side routing
1214
- * ```typescript
1215
- * export const app = defineStaticSite({
1216
- * dir: "dist",
1217
- * spa: true,
1218
- * build: "npm run build",
1219
- * });
1220
- * ```
1221
- *
1222
1245
  */
1223
1246
  declare const defineStaticSite: () => (options: StaticSiteConfig) => StaticSiteHandler;
1224
1247
 
@@ -1294,11 +1317,11 @@ type RouteEntry = {
1294
1317
  public?: boolean;
1295
1318
  };
1296
1319
  /** Spread ctx into route args: Omit auth config, add AuthHelpers if present */
1297
- type SpreadCtx<C> = ([C] extends [undefined] ? {} : Omit<C & {}, 'auth'>) & ([ExtractAuth<C>] extends [undefined] ? {} : {
1320
+ type SpreadCtx$1<C> = ([C] extends [undefined] ? {} : Omit<C & {}, 'auth'>) & ([ExtractAuth<C>] extends [undefined] ? {} : {
1298
1321
  auth: AuthHelpers<ExtractAuth<C>>;
1299
1322
  });
1300
1323
  /** Callback args available inside each route — ctx is spread into args */
1301
- type RouteArgs<C, ST> = SpreadCtx<C> & {
1324
+ type RouteArgs<C, ST> = SpreadCtx$1<C> & {
1302
1325
  req: HttpRequest;
1303
1326
  input: unknown;
1304
1327
  ok: OkHelper;
@@ -1313,7 +1336,7 @@ type RouteOptions = {
1313
1336
  public?: boolean;
1314
1337
  };
1315
1338
  /** Setup factory — receives deps/config/files/enableAuth based on what was declared */
1316
- type SetupArgs<D, P, HasFiles extends boolean> = {
1339
+ type SetupArgs$1<D, P, HasFiles extends boolean> = {
1317
1340
  enableAuth: EnableAuth;
1318
1341
  ok: OkHelper;
1319
1342
  fail: FailHelper;
@@ -1341,7 +1364,7 @@ type ApiConfig = {
1341
1364
  type ApiHandler<C = undefined> = {
1342
1365
  readonly __brand: "effortless-api";
1343
1366
  readonly __spec: ApiConfig;
1344
- readonly onError?: (...args: any[]) => any;
1367
+ readonly onError?: (...args: any[]) => any | Promise<any>;
1345
1368
  readonly onCleanup?: (...args: any[]) => any;
1346
1369
  readonly setup?: (...args: any[]) => C | Promise<C>;
1347
1370
  readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);
@@ -1353,18 +1376,8 @@ type ApiHandler<C = undefined> = {
1353
1376
  type ApiOptions = {
1354
1377
  /** Base path prefix for all routes (e.g., "/api") */
1355
1378
  basePath: `/${string}`;
1356
- /** Lambda memory in MB (default: 256) */
1357
- memory?: number;
1358
- /** Lambda timeout (default: 30s). Accepts seconds or duration string: `"30s"`, `"5m"` */
1359
- timeout?: Duration;
1360
- /** Additional IAM permissions for the Lambda */
1361
- permissions?: Permission[];
1362
- /** Logging verbosity: "error" (errors only), "info" (+ execution summary), "debug" (+ input/output). Default: "info" */
1363
- logLevel?: LogLevel;
1364
1379
  /** Enable response streaming. When true, routes receive a `stream` arg for SSE. */
1365
1380
  stream?: boolean;
1366
- /** Static file glob patterns to bundle into the Lambda ZIP */
1367
- static?: string[];
1368
1381
  };
1369
1382
  /**
1370
1383
  * Finalized API handler with route-adding methods.
@@ -1388,17 +1401,23 @@ interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boo
1388
1401
  deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): ApiBuilder<D2, P, C, ST, HasFiles>;
1389
1402
  /** Declare SSM secrets */
1390
1403
  config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): ApiBuilder<D, P2, C, ST, HasFiles>;
1404
+ /** Include static files by glob pattern */
1405
+ include(glob: string): ApiBuilder<D, P, C, ST, true>;
1406
+ /** Configure Lambda settings only (no init function) */
1407
+ setup(lambda: LambdaOptions): ApiBuilder<D, P, C, ST, HasFiles>;
1391
1408
  /** Initialize shared state on cold start. Receives deps/config/files based on what was declared. */
1392
- setup<C2>(fn: (args: SetupArgs<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>): ApiBuilder<D, P, C2, ST, HasFiles>;
1409
+ setup<C2>(fn: (args: SetupArgs$1<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>): ApiBuilder<D, P, C2, ST, HasFiles>;
1410
+ /** Initialize shared state on cold start with Lambda config. */
1411
+ setup<C2>(fn: (args: SetupArgs$1<D, P, HasFiles>) => ValidateSetupReturn<C2> | Promise<ValidateSetupReturn<C2>>, lambda: LambdaOptions): ApiBuilder<D, P, C2, ST, HasFiles>;
1393
1412
  /** Handle errors thrown by routes */
1394
1413
  onError(fn: (args: {
1395
1414
  error: unknown;
1396
1415
  req: HttpRequest;
1397
1416
  ok: OkHelper;
1398
1417
  fail: FailHelper;
1399
- } & SpreadCtx<C>) => HttpResponse): ApiBuilder<D, P, C, ST, HasFiles>;
1418
+ } & SpreadCtx$1<C>) => HttpResponse | Promise<HttpResponse>): ApiBuilder<D, P, C, ST, HasFiles>;
1400
1419
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
1401
- onCleanup(fn: (args: SpreadCtx<C>) => void | Promise<void>): ApiBuilder<D, P, C, ST, HasFiles>;
1420
+ onCleanup(fn: (args: SpreadCtx$1<C>) => void | Promise<void>): ApiBuilder<D, P, C, ST, HasFiles>;
1402
1421
  /** Add a GET route (terminal — returns finalized handler with route methods) */
1403
1422
  get(path: `/${string}`, handler: RouteHandler<C, ST>, options?: RouteOptions): ApiRoutes<C, ST>;
1404
1423
  /** Add a POST route (terminal) */
@@ -1413,13 +1432,10 @@ interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boo
1413
1432
  /**
1414
1433
  * Define an API with typed routes using a builder pattern.
1415
1434
  *
1435
+ * @see {@link https://effortless-aws.website/use-cases/http-api | HTTP API guide}
1436
+ *
1416
1437
  * @example
1417
1438
  * ```typescript
1418
- * // Minimal
1419
- * export default defineApi({ basePath: "/hello" })
1420
- * .get("/", async ({ req, ok }) => ok({ message: "Hello!" }))
1421
- *
1422
- * // Full
1423
1439
  * export const api = defineApi({ basePath: "/api", timeout: "30s" })
1424
1440
  * .deps(() => ({ users }))
1425
1441
  * .config(({ defineSecret }) => ({ dbUrl: defineSecret() }))
@@ -1432,9 +1448,124 @@ interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boo
1432
1448
  * .post("/login", async ({ auth, ok }) => ok(await auth.createSession()), { public: true })
1433
1449
  * ```
1434
1450
  */
1435
- declare function defineApi(options: ApiOptions & {
1436
- static: string[];
1437
- }): ApiBuilder<undefined, undefined, undefined, false, true>;
1438
- declare function defineApi<const O extends ApiOptions>(options: O): ApiBuilder<undefined, undefined, undefined, O["stream"] extends true ? true : false, O["static"] extends string[] ? true : false>;
1451
+ declare function defineApi<const O extends ApiOptions>(options: O): ApiBuilder<undefined, undefined, undefined, O["stream"] extends true ? true : false, false>;
1452
+
1453
+ /**
1454
+ * All IANA time zones.
1455
+ * Generated from Intl.supportedValuesOf("timeZone") on Node.js 22.
1456
+ */
1457
+ type Timezone = "UTC" | "Africa/Abidjan" | "Africa/Accra" | "Africa/Addis_Ababa" | "Africa/Algiers" | "Africa/Asmera" | "Africa/Bamako" | "Africa/Bangui" | "Africa/Banjul" | "Africa/Bissau" | "Africa/Blantyre" | "Africa/Brazzaville" | "Africa/Bujumbura" | "Africa/Cairo" | "Africa/Casablanca" | "Africa/Ceuta" | "Africa/Conakry" | "Africa/Dakar" | "Africa/Dar_es_Salaam" | "Africa/Djibouti" | "Africa/Douala" | "Africa/El_Aaiun" | "Africa/Freetown" | "Africa/Gaborone" | "Africa/Harare" | "Africa/Johannesburg" | "Africa/Juba" | "Africa/Kampala" | "Africa/Khartoum" | "Africa/Kigali" | "Africa/Kinshasa" | "Africa/Lagos" | "Africa/Libreville" | "Africa/Lome" | "Africa/Luanda" | "Africa/Lubumbashi" | "Africa/Lusaka" | "Africa/Malabo" | "Africa/Maputo" | "Africa/Maseru" | "Africa/Mbabane" | "Africa/Mogadishu" | "Africa/Monrovia" | "Africa/Nairobi" | "Africa/Ndjamena" | "Africa/Niamey" | "Africa/Nouakchott" | "Africa/Ouagadougou" | "Africa/Porto-Novo" | "Africa/Sao_Tome" | "Africa/Tripoli" | "Africa/Tunis" | "Africa/Windhoek" | "America/Adak" | "America/Anchorage" | "America/Anguilla" | "America/Antigua" | "America/Araguaina" | "America/Argentina/La_Rioja" | "America/Argentina/Rio_Gallegos" | "America/Argentina/Salta" | "America/Argentina/San_Juan" | "America/Argentina/San_Luis" | "America/Argentina/Tucuman" | "America/Argentina/Ushuaia" | "America/Aruba" | "America/Asuncion" | "America/Bahia" | "America/Bahia_Banderas" | "America/Barbados" | "America/Belem" | "America/Belize" | "America/Blanc-Sablon" | "America/Boa_Vista" | "America/Bogota" | "America/Boise" | "America/Buenos_Aires" | "America/Cambridge_Bay" | "America/Campo_Grande" | "America/Cancun" | "America/Caracas" | "America/Catamarca" | "America/Cayenne" | "America/Cayman" | "America/Chicago" | "America/Chihuahua" | "America/Ciudad_Juarez" | "America/Coral_Harbour" | "America/Cordoba" | "America/Costa_Rica" | "America/Coyhaique" | "America/Creston" | "America/Cuiaba" | "America/Curacao" | "America/Danmarkshavn" | "America/Dawson" | "America/Dawson_Creek" | "America/Denver" | "America/Detroit" | "America/Dominica" | "America/Edmonton" | "America/Eirunepe" | "America/El_Salvador" | "America/Fort_Nelson" | "America/Fortaleza" | "America/Glace_Bay" | "America/Godthab" | "America/Goose_Bay" | "America/Grand_Turk" | "America/Grenada" | "America/Guadeloupe" | "America/Guatemala" | "America/Guayaquil" | "America/Guyana" | "America/Halifax" | "America/Havana" | "America/Hermosillo" | "America/Indiana/Knox" | "America/Indiana/Marengo" | "America/Indiana/Petersburg" | "America/Indiana/Tell_City" | "America/Indiana/Vevay" | "America/Indiana/Vincennes" | "America/Indiana/Winamac" | "America/Indianapolis" | "America/Inuvik" | "America/Iqaluit" | "America/Jamaica" | "America/Jujuy" | "America/Juneau" | "America/Kentucky/Monticello" | "America/Kralendijk" | "America/La_Paz" | "America/Lima" | "America/Los_Angeles" | "America/Louisville" | "America/Lower_Princes" | "America/Maceio" | "America/Managua" | "America/Manaus" | "America/Marigot" | "America/Martinique" | "America/Matamoros" | "America/Mazatlan" | "America/Mendoza" | "America/Menominee" | "America/Merida" | "America/Metlakatla" | "America/Mexico_City" | "America/Miquelon" | "America/Moncton" | "America/Monterrey" | "America/Montevideo" | "America/Montserrat" | "America/Nassau" | "America/New_York" | "America/Nome" | "America/Noronha" | "America/North_Dakota/Beulah" | "America/North_Dakota/Center" | "America/North_Dakota/New_Salem" | "America/Ojinaga" | "America/Panama" | "America/Paramaribo" | "America/Phoenix" | "America/Port-au-Prince" | "America/Port_of_Spain" | "America/Porto_Velho" | "America/Puerto_Rico" | "America/Punta_Arenas" | "America/Rankin_Inlet" | "America/Recife" | "America/Regina" | "America/Resolute" | "America/Rio_Branco" | "America/Santarem" | "America/Santiago" | "America/Santo_Domingo" | "America/Sao_Paulo" | "America/Scoresbysund" | "America/Sitka" | "America/St_Barthelemy" | "America/St_Johns" | "America/St_Kitts" | "America/St_Lucia" | "America/St_Thomas" | "America/St_Vincent" | "America/Swift_Current" | "America/Tegucigalpa" | "America/Thule" | "America/Tijuana" | "America/Toronto" | "America/Tortola" | "America/Vancouver" | "America/Whitehorse" | "America/Winnipeg" | "America/Yakutat" | "Antarctica/Casey" | "Antarctica/Davis" | "Antarctica/DumontDUrville" | "Antarctica/Macquarie" | "Antarctica/Mawson" | "Antarctica/McMurdo" | "Antarctica/Palmer" | "Antarctica/Rothera" | "Antarctica/Syowa" | "Antarctica/Troll" | "Antarctica/Vostok" | "Arctic/Longyearbyen" | "Asia/Aden" | "Asia/Almaty" | "Asia/Amman" | "Asia/Anadyr" | "Asia/Aqtau" | "Asia/Aqtobe" | "Asia/Ashgabat" | "Asia/Atyrau" | "Asia/Baghdad" | "Asia/Bahrain" | "Asia/Baku" | "Asia/Bangkok" | "Asia/Barnaul" | "Asia/Beirut" | "Asia/Bishkek" | "Asia/Brunei" | "Asia/Calcutta" | "Asia/Chita" | "Asia/Colombo" | "Asia/Damascus" | "Asia/Dhaka" | "Asia/Dili" | "Asia/Dubai" | "Asia/Dushanbe" | "Asia/Famagusta" | "Asia/Gaza" | "Asia/Hebron" | "Asia/Hong_Kong" | "Asia/Hovd" | "Asia/Irkutsk" | "Asia/Jakarta" | "Asia/Jayapura" | "Asia/Jerusalem" | "Asia/Kabul" | "Asia/Kamchatka" | "Asia/Karachi" | "Asia/Katmandu" | "Asia/Khandyga" | "Asia/Krasnoyarsk" | "Asia/Kuala_Lumpur" | "Asia/Kuching" | "Asia/Kuwait" | "Asia/Macau" | "Asia/Magadan" | "Asia/Makassar" | "Asia/Manila" | "Asia/Muscat" | "Asia/Nicosia" | "Asia/Novokuznetsk" | "Asia/Novosibirsk" | "Asia/Omsk" | "Asia/Oral" | "Asia/Phnom_Penh" | "Asia/Pontianak" | "Asia/Pyongyang" | "Asia/Qatar" | "Asia/Qostanay" | "Asia/Qyzylorda" | "Asia/Rangoon" | "Asia/Riyadh" | "Asia/Saigon" | "Asia/Sakhalin" | "Asia/Samarkand" | "Asia/Seoul" | "Asia/Shanghai" | "Asia/Singapore" | "Asia/Srednekolymsk" | "Asia/Taipei" | "Asia/Tashkent" | "Asia/Tbilisi" | "Asia/Tehran" | "Asia/Thimphu" | "Asia/Tokyo" | "Asia/Tomsk" | "Asia/Ulaanbaatar" | "Asia/Urumqi" | "Asia/Ust-Nera" | "Asia/Vientiane" | "Asia/Vladivostok" | "Asia/Yakutsk" | "Asia/Yekaterinburg" | "Asia/Yerevan" | "Atlantic/Azores" | "Atlantic/Bermuda" | "Atlantic/Canary" | "Atlantic/Cape_Verde" | "Atlantic/Faeroe" | "Atlantic/Madeira" | "Atlantic/Reykjavik" | "Atlantic/South_Georgia" | "Atlantic/St_Helena" | "Atlantic/Stanley" | "Australia/Adelaide" | "Australia/Brisbane" | "Australia/Broken_Hill" | "Australia/Darwin" | "Australia/Eucla" | "Australia/Hobart" | "Australia/Lindeman" | "Australia/Lord_Howe" | "Australia/Melbourne" | "Australia/Perth" | "Australia/Sydney" | "Europe/Amsterdam" | "Europe/Andorra" | "Europe/Astrakhan" | "Europe/Athens" | "Europe/Belgrade" | "Europe/Berlin" | "Europe/Bratislava" | "Europe/Brussels" | "Europe/Bucharest" | "Europe/Budapest" | "Europe/Busingen" | "Europe/Chisinau" | "Europe/Copenhagen" | "Europe/Dublin" | "Europe/Gibraltar" | "Europe/Guernsey" | "Europe/Helsinki" | "Europe/Isle_of_Man" | "Europe/Istanbul" | "Europe/Jersey" | "Europe/Kaliningrad" | "Europe/Kiev" | "Europe/Kirov" | "Europe/Lisbon" | "Europe/Ljubljana" | "Europe/London" | "Europe/Luxembourg" | "Europe/Madrid" | "Europe/Malta" | "Europe/Mariehamn" | "Europe/Minsk" | "Europe/Monaco" | "Europe/Moscow" | "Europe/Oslo" | "Europe/Paris" | "Europe/Podgorica" | "Europe/Prague" | "Europe/Riga" | "Europe/Rome" | "Europe/Samara" | "Europe/San_Marino" | "Europe/Sarajevo" | "Europe/Saratov" | "Europe/Simferopol" | "Europe/Skopje" | "Europe/Sofia" | "Europe/Stockholm" | "Europe/Tallinn" | "Europe/Tirane" | "Europe/Ulyanovsk" | "Europe/Vaduz" | "Europe/Vatican" | "Europe/Vienna" | "Europe/Vilnius" | "Europe/Volgograd" | "Europe/Warsaw" | "Europe/Zagreb" | "Europe/Zurich" | "Indian/Antananarivo" | "Indian/Chagos" | "Indian/Christmas" | "Indian/Cocos" | "Indian/Comoro" | "Indian/Kerguelen" | "Indian/Mahe" | "Indian/Maldives" | "Indian/Mauritius" | "Indian/Mayotte" | "Indian/Reunion" | "Pacific/Apia" | "Pacific/Auckland" | "Pacific/Bougainville" | "Pacific/Chatham" | "Pacific/Easter" | "Pacific/Efate" | "Pacific/Enderbury" | "Pacific/Fakaofo" | "Pacific/Fiji" | "Pacific/Funafuti" | "Pacific/Galapagos" | "Pacific/Gambier" | "Pacific/Guadalcanal" | "Pacific/Guam" | "Pacific/Honolulu" | "Pacific/Kiritimati" | "Pacific/Kosrae" | "Pacific/Kwajalein" | "Pacific/Majuro" | "Pacific/Marquesas" | "Pacific/Midway" | "Pacific/Nauru" | "Pacific/Niue" | "Pacific/Norfolk" | "Pacific/Noumea" | "Pacific/Pago_Pago" | "Pacific/Palau" | "Pacific/Pitcairn" | "Pacific/Ponape" | "Pacific/Port_Moresby" | "Pacific/Rarotonga" | "Pacific/Saipan" | "Pacific/Tahiti" | "Pacific/Tarawa" | "Pacific/Tongatapu" | "Pacific/Truk" | "Pacific/Wake" | "Pacific/Wallis";
1458
+
1459
+ /** Singular/plural unit for rate expressions */
1460
+ type RateUnit = "minute" | "minutes" | "hour" | "hours" | "day" | "days";
1461
+ /**
1462
+ * Rate expression: `rate(1 hour)`, `rate(5 minutes)`, `rate(2 days)`
1463
+ *
1464
+ * Strictly typed — autocomplete and compile-time validation for unit.
1465
+ */
1466
+ type RateExpression = `rate(${number} ${RateUnit})`;
1467
+ /**
1468
+ * Cron expression: `cron(min hour dom month dow year)`
1469
+ *
1470
+ * Not deeply typed (too combinatorial for TS), but the `cron(...)` wrapper is enforced.
1471
+ *
1472
+ * @example
1473
+ * ```
1474
+ * "cron(0 9 * * ? *)" // daily at 9:00 UTC
1475
+ * "cron(0 9 ? * MON-FRI *)" // weekdays at 9:00
1476
+ * "cron(0/15 * * * ? *)" // every 15 minutes
1477
+ * ```
1478
+ */
1479
+ type CronExpression = `cron(${string})`;
1480
+ /**
1481
+ * EventBridge Scheduler schedule expression.
1482
+ *
1483
+ * - **Rate**: `"rate(5 minutes)"`, `"rate(1 hour)"`, `"rate(1 day)"` — strictly typed units
1484
+ * - **Cron**: `"cron(0 9 * * ? *)"` — 6 fields: min hour dom month dow year
1485
+ */
1486
+ type ScheduleExpression = RateExpression | CronExpression;
1487
+ /** Static config extracted at deploy time */
1488
+ type CronConfig = {
1489
+ /** Lambda function settings (memory, timeout, permissions, etc.) */
1490
+ lambda?: LambdaWithPermissions;
1491
+ /** EventBridge Scheduler schedule expression */
1492
+ schedule: ScheduleExpression;
1493
+ /** IANA timezone for the schedule (default: UTC) */
1494
+ timezone?: Timezone;
1495
+ };
1496
+ /** Setup factory — receives deps/config/files based on what was declared */
1497
+ type SetupArgs<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {} : {
1498
+ deps: ResolveDeps<D>;
1499
+ }) & ([P] extends [undefined] ? {} : {
1500
+ config: ResolveConfig<P & {}>;
1501
+ }) & (HasFiles extends true ? {
1502
+ files: StaticFiles;
1503
+ } : {});
1504
+ /** Spread ctx into callback args (empty when no setup) */
1505
+ type SpreadCtx<C> = [C] extends [undefined] ? {} : C & {};
1506
+ /** Callback function for cron tick */
1507
+ type CronTickFn<C = undefined> = (args: SpreadCtx<C>) => Promise<void> | void;
1508
+ /**
1509
+ * Handler object created by defineCron.
1510
+ * @internal
1511
+ */
1512
+ type CronHandler<C = any> = {
1513
+ readonly __brand: "effortless-cron";
1514
+ readonly __spec: CronConfig;
1515
+ readonly onError?: (...args: any[]) => any;
1516
+ readonly onCleanup?: (...args: any[]) => any;
1517
+ readonly setup?: (...args: any[]) => C | Promise<C>;
1518
+ readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);
1519
+ readonly config?: Record<string, unknown>;
1520
+ readonly static?: string[];
1521
+ readonly onTick?: (...args: any[]) => any;
1522
+ };
1523
+ /** Options passed to `defineCron()` — resource config only */
1524
+ type CronOptions = {
1525
+ /** EventBridge Scheduler schedule expression: `"rate(5 minutes)"` or `"cron(0 9 * * ? *)"` */
1526
+ schedule: ScheduleExpression;
1527
+ /** IANA timezone for the schedule (default: UTC). Full autocomplete for all timezones. */
1528
+ timezone?: Timezone;
1529
+ };
1530
+ interface CronBuilder<D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
1531
+ /** Declare handler dependencies (tables, queues, buckets, mailers) */
1532
+ deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): CronBuilder<D2, P, C, HasFiles>;
1533
+ /** Declare SSM secrets */
1534
+ config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): CronBuilder<D, P2, C, HasFiles>;
1535
+ /** Include static files in the Lambda bundle. Chainable — call multiple times. */
1536
+ include(glob: string): CronBuilder<D, P, C, true>;
1537
+ /** Configure Lambda settings only (memory, timeout, permissions, logLevel) */
1538
+ setup(lambda: LambdaOptions): CronBuilder<D, P, C, HasFiles>;
1539
+ /** Initialize shared state on cold start. Receives deps, config, files. */
1540
+ setup<C2>(fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>): CronBuilder<D, P, C2, HasFiles>;
1541
+ /** Initialize shared state on cold start + configure Lambda settings. */
1542
+ setup<C2>(fn: (args: SetupArgs<D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): CronBuilder<D, P, C2, HasFiles>;
1543
+ /** Handle errors thrown by onTick */
1544
+ onError(fn: (args: {
1545
+ error: unknown;
1546
+ } & SpreadCtx<C>) => void | Promise<void>): CronBuilder<D, P, C, HasFiles>;
1547
+ /** Cleanup callback — runs after each invocation, before Lambda freezes */
1548
+ onCleanup(fn: (args: SpreadCtx<C>) => void | Promise<void>): CronBuilder<D, P, C, HasFiles>;
1549
+ /** Tick handler — called on each scheduled invocation (terminal) */
1550
+ onTick(fn: CronTickFn<C>): CronHandler<C>;
1551
+ }
1552
+ /**
1553
+ * Define a cron job — scheduled Lambda invocation via EventBridge Scheduler.
1554
+ *
1555
+ * @example
1556
+ * ```typescript
1557
+ * export const sync = defineCron({ schedule: "cron(0 9 * * ? *)" })
1558
+ * .deps(() => ({ orders }))
1559
+ * .config(({ defineSecret }) => ({ apiKey: defineSecret() }))
1560
+ * .include("templates/*.html")
1561
+ * .setup(async ({ deps, config, files }) => ({
1562
+ * db: deps.orders, key: config.apiKey, tpl: files,
1563
+ * }), { memory: 512 })
1564
+ * .onTick(async ({ db, key, tpl }) => {
1565
+ * const html = tpl.read("templates/report.html")
1566
+ * })
1567
+ * ```
1568
+ */
1569
+ declare function defineCron(options: CronOptions): CronBuilder;
1439
1570
 
1440
- export { type AnyParamRef, type AnySecretRef, type ApiAuthConfig, type ApiConfig, type ApiHandler, type ApiRoutes, type AppConfig, type AppHandler, type AuthHelpers, type BucketClient, type BucketConfig, type BucketEvent, type BucketHandler, type ConfigHelpers, type ContentType, type DefineSecretFn, type Duration, type EffortlessConfig, type EmailClient, type FifoQueueConfig, type FifoQueueHandler, type FifoQueueMessage, type GenerateSpec, type HttpMethod$1 as HttpMethod, type HttpRequest, type HttpResponse, type LambdaConfig, type LambdaWithPermissions, type LogLevel, type MailerConfig, type MailerHandler, 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 StreamView, type TableClient, type TableConfig, type TableHandler, type TableItem, type TableKey, type TableRecord, type UpdateActions, defineApi, defineApp, defineBucket, defineConfig, defineFifoQueue, defineMailer, defineSecret, defineStaticSite, defineTable, generateBase64, generateHex, generateUuid, param, secret, toSeconds, unsafeAs };
1571
+ export { type AnyParamRef, type AnySecretRef, type ApiAuthConfig, type ApiConfig, type ApiHandler, type ApiRoutes, type AppConfig, type AppHandler, type AuthHelpers, type BucketClient, type BucketConfig, type BucketEvent, type BucketHandler, type ConfigHelpers, type ContentType, type CronConfig, type CronHandler, type DefineSecretFn, type Duration, type EffortlessConfig, type EmailClient, type FifoQueueConfig, type FifoQueueHandler, type FifoQueueMessage, type GenerateSpec, type HttpMethod$1 as HttpMethod, type HttpRequest, type HttpResponse, type LambdaConfig, type LambdaWithPermissions, type LogLevel, type MailerConfig, type MailerHandler, 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 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, defineSecret, defineStaticSite, defineTable, defineWorker, generateBase64, generateHex, generateUuid, param, secret, toSeconds };