effortless-aws 0.37.0 → 0.39.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.
@@ -112,7 +112,7 @@ var buildParams = async (params) => {
112
112
  var resolvePath = (filePath) => join(process.cwd(), filePath);
113
113
  var staticFiles = {
114
114
  read: (filePath) => readFileSync(resolvePath(filePath), "utf-8"),
115
- readBuffer: (filePath) => readFileSync(resolvePath(filePath)),
115
+ readBytes: (filePath) => readFileSync(resolvePath(filePath)),
116
116
  path: resolvePath
117
117
  };
118
118
  var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupArgs) => {
package/dist/index.d.ts CHANGED
@@ -101,7 +101,7 @@ type LambdaConfig = {
101
101
  };
102
102
  /**
103
103
  * Lambda configuration with additional IAM permissions.
104
- * Used by handler types that support custom permissions (http, table, fifo-queue).
104
+ * Used by handler types that support custom permissions (http, table, queue).
105
105
  */
106
106
  type LambdaWithPermissions = LambdaConfig & {
107
107
  /** Additional IAM permissions for the Lambda */
@@ -249,7 +249,12 @@ type HttpRequest = {
249
249
  type HttpResponse = {
250
250
  /** HTTP status code (e.g., 200, 404, 500) */
251
251
  status: number;
252
- /** Response body — JSON-serialized by default, or sent as string when contentType is set */
252
+ /**
253
+ * Response body. Type determines serialization:
254
+ * - `Uint8Array` (incl. `Buffer`) — sent as binary, base64-encoded
255
+ * - `Blob` — sent as binary, base64-encoded; `Blob.type` is used as Content-Type when not set explicitly
256
+ * - other values — JSON-serialized by default, or sent as string when `contentType` is set
257
+ */
253
258
  body?: unknown;
254
259
  /**
255
260
  * Short content-type alias. Resolves to full MIME type automatically:
@@ -272,10 +277,14 @@ type HttpResponse = {
272
277
  */
273
278
  cookies?: string[];
274
279
  /**
275
- * Set to `true` to return binary data.
276
- * When enabled, `body` must be a base64-encoded string and the response
277
- * will include `isBase64Encoded: true` so Lambda Function URLs / API Gateway
278
- * decode it back to binary for the client.
280
+ * Force the client to download the response as a file with the given filename.
281
+ * Sets `Content-Disposition: attachment; filename="<value>"`. Works with any body type
282
+ * (JSON, text, binary). An explicit `Content-Disposition` header overrides this.
283
+ */
284
+ downloadAs?: string;
285
+ /**
286
+ * Set to `true` to return binary data when `body` is already a base64-encoded string.
287
+ * Prefer returning `Uint8Array` / `Buffer` / `Blob` directly — the runtime detects them automatically.
279
288
  */
280
289
  binary?: boolean;
281
290
  };
@@ -294,8 +303,8 @@ type ResponseStream = {
294
303
  type StaticFiles = {
295
304
  /** Read file as UTF-8 string */
296
305
  read(path: string): string;
297
- /** Read file as Buffer (for binary content) */
298
- readBuffer(path: string): Buffer;
306
+ /** Read file as raw bytes (for binary content) */
307
+ readBytes(path: string): Uint8Array;
299
308
  /** Resolve absolute path to the bundled file */
300
309
  path(path: string): string;
301
310
  };
@@ -480,14 +489,6 @@ interface BucketBuilder<D = undefined, P = undefined, C = undefined, HasFiles ex
480
489
  * Define an S3 bucket with optional event handlers.
481
490
  *
482
491
  * @see {@link https://effortless-aws.website/use-cases/storage | Storage guide}
483
- *
484
- * @example
485
- * ```typescript
486
- * export const uploads = defineBucket({ prefix: "images/", suffix: ".jpg" })
487
- * .onObjectCreated(async ({ event, bucket }) => {
488
- * console.log("New upload:", event.key);
489
- * })
490
- * ```
491
492
  */
492
493
  declare function defineBucket(): BucketBuilder;
493
494
  declare function defineBucket(options: BucketOptions): BucketBuilder;
@@ -517,37 +518,15 @@ type MailerHandler = {
517
518
  * Add them to your DNS provider to verify the domain.
518
519
  *
519
520
  * @see {@link https://effortless-aws.website/use-cases/email | Email guide}
520
- *
521
- * @param options - Mailer configuration with the domain to send from
522
- * @returns Handler object used by the deployment system and as a `deps` value
523
- *
524
- * @example Basic mailer with HTTP handler
525
- * ```typescript
526
- * export const mailer = defineMailer({ domain: "myapp.com" });
527
- *
528
- * export const api = defineApi({
529
- * basePath: "/signup",
530
- * deps: { mailer },
531
- * post: async ({ req, deps }) => {
532
- * await deps.mailer.send({
533
- * from: "hello@myapp.com",
534
- * to: req.body.email,
535
- * subject: "Welcome!",
536
- * html: "<h1>Hi!</h1>",
537
- * });
538
- * return { status: 200, body: { ok: true } };
539
- * },
540
- * });
541
- * ```
542
521
  */
543
522
  declare const defineMailer: () => (options: MailerConfig) => MailerHandler;
544
523
 
545
524
  /**
546
- * Parsed SQS FIFO message passed to the handler callbacks.
525
+ * Parsed SQS message passed to the handler callbacks.
547
526
  *
548
527
  * @typeParam T - Type of the decoded message body (from schema function)
549
528
  */
550
- type FifoQueueMessage<T = unknown> = {
529
+ type QueueMessage<T = unknown> = {
551
530
  /** Unique message identifier */
552
531
  messageId: string;
553
532
  /** Receipt handle for acknowledgement */
@@ -556,9 +535,9 @@ type FifoQueueMessage<T = unknown> = {
556
535
  body: T;
557
536
  /** Raw unparsed message body string */
558
537
  rawBody: string;
559
- /** Message group ID (FIFO ordering key) */
538
+ /** Message group ID (FIFO ordering key, empty string for standard queues) */
560
539
  messageGroupId: string;
561
- /** Message deduplication ID */
540
+ /** Message deduplication ID (FIFO only) */
562
541
  messageDeduplicationId?: string;
563
542
  /** SQS message attributes */
564
543
  messageAttributes: Record<string, {
@@ -573,9 +552,18 @@ type FifoQueueMessage<T = unknown> = {
573
552
  sentTimestamp?: string;
574
553
  };
575
554
  /**
576
- * Configuration options for a FIFO queue handler
555
+ * Event source mapping (poller) configuration — how Lambda consumes the queue.
577
556
  */
578
- type FifoQueueConfig = {
557
+ type QueuePollerConfig = {
558
+ /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */
559
+ batchSize?: number;
560
+ /** Maximum time to gather messages before invoking (default: 0). Accepts `"5s"`, `"1m"`, etc. */
561
+ batchWindow?: Duration;
562
+ };
563
+ /**
564
+ * Configuration options for a queue handler.
565
+ */
566
+ type QueueConfig = {
579
567
  /** Lambda function settings (memory, timeout, permissions, etc.) */
580
568
  lambda?: {
581
569
  memory?: number;
@@ -583,20 +571,23 @@ type FifoQueueConfig = {
583
571
  logLevel?: LogLevel;
584
572
  permissions?: Permission[];
585
573
  };
586
- /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */
587
- batchSize?: number;
588
- /** Maximum time to gather messages before invoking (default: 0). Accepts `"5s"`, `"1m"`, etc. */
589
- batchWindow?: Duration;
574
+ /**
575
+ * Whether this is a FIFO queue (ordered, exactly-once).
576
+ * Currently only `true` is supported standard queue support is planned.
577
+ */
578
+ fifo?: boolean;
590
579
  /** Visibility timeout (default: max of timeout or 30s). Accepts `"30s"`, `"5m"`, etc. */
591
580
  visibilityTimeout?: Duration;
592
581
  /** Message retention period (default: `"4d"`). Accepts `"1h"`, `"7d"`, etc. */
593
582
  retentionPeriod?: Duration;
594
583
  /** Delivery delay for all messages in the queue (default: 0). Accepts `"30s"`, `"5m"`, etc. */
595
584
  delay?: Duration;
596
- /** Enable content-based deduplication (default: true) */
585
+ /** Enable content-based deduplication for FIFO queues (default: true) */
597
586
  contentBasedDeduplication?: boolean;
598
587
  /** Max number of receives before a message is sent to the dead-letter queue (default: 3) */
599
588
  maxReceiveCount?: number;
589
+ /** Event source mapping config — set via `.poller({...})` builder method. */
590
+ poller?: QueuePollerConfig;
600
591
  };
601
592
  /** Spread ctx into callback args (empty when no setup) */
602
593
  type SpreadCtx$5<C> = [C] extends [undefined] ? {} : C & {};
@@ -612,26 +603,26 @@ type SetupArgs$5<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {}
612
603
  * Per-message handler function.
613
604
  * Called once per message in the batch. Failures are reported individually.
614
605
  */
615
- type FifoQueueMessageFn<T = unknown, C = undefined> = (args: {
616
- message: FifoQueueMessage<T>;
606
+ type QueueMessageFn<T = unknown, C = undefined> = (args: {
607
+ message: QueueMessage<T>;
617
608
  } & SpreadCtx$5<C>) => Promise<void>;
618
609
  /**
619
610
  * Batch handler function.
620
611
  * Called once with all messages in the batch.
621
612
  * Return `{ failures: string[] }` (messageIds) for partial batch failure reporting.
622
613
  */
623
- type FifoQueueBatchFn<T = unknown, C = undefined> = (args: {
624
- messages: FifoQueueMessage<T>[];
614
+ type QueueBatchFn<T = unknown, C = undefined> = (args: {
615
+ messages: QueueMessage<T>[];
625
616
  } & SpreadCtx$5<C>) => Promise<void | {
626
617
  failures: string[];
627
618
  }>;
628
619
  /**
629
- * Internal handler object created by defineFifoQueue
620
+ * Internal handler object created by defineQueue.
630
621
  * @internal
631
622
  */
632
- type FifoQueueHandler$1<T = unknown, C = any> = {
633
- readonly __brand: "effortless-fifo-queue";
634
- readonly __spec: FifoQueueConfig;
623
+ type QueueHandler$1<T = unknown, C = any> = {
624
+ readonly __brand: "effortless-queue";
625
+ readonly __spec: QueueConfig;
635
626
  readonly schema?: (input: unknown) => T;
636
627
  readonly onError?: (...args: any[]) => any;
637
628
  readonly onCleanup?: (...args: any[]) => any;
@@ -642,64 +633,69 @@ type FifoQueueHandler$1<T = unknown, C = any> = {
642
633
  readonly onMessage?: (...args: any[]) => any;
643
634
  readonly onMessageBatch?: (...args: any[]) => any;
644
635
  };
645
- /** Options passed to `defineFifoQueue()` — static config */
646
- type FifoQueueOptions<T> = {
647
- /** Number of messages per Lambda invocation (1-10 for FIFO, default: 10) */
648
- batchSize?: number;
649
- /** Maximum time to gather messages before invoking (default: 0) */
650
- batchWindow?: Duration;
636
+ /** Options passed to `defineQueue()` — queue resource config */
637
+ type QueueOptions<T> = {
638
+ /**
639
+ * Whether this is a FIFO queue (ordered, exactly-once).
640
+ * Currently only `true` is supported standard queue support is planned.
641
+ */
642
+ fifo?: boolean;
651
643
  /** Visibility timeout (default: max of timeout or 30s) */
652
644
  visibilityTimeout?: Duration;
653
645
  /** Message retention period (default: "4d") */
654
646
  retentionPeriod?: Duration;
655
647
  /** Delivery delay for all messages in the queue (default: 0) */
656
648
  delay?: Duration;
657
- /** Enable content-based deduplication (default: true) */
649
+ /** Enable content-based deduplication for FIFO queues (default: true) */
658
650
  contentBasedDeduplication?: boolean;
659
651
  /** Max number of receives before DLQ (default: 3) */
660
652
  maxReceiveCount?: number;
661
653
  /** Decode/validate function for the message body */
662
654
  schema?: (input: unknown) => T;
663
655
  };
664
- interface FifoQueueBuilder<T = unknown, D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
656
+ interface QueueBuilder<T = unknown, D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
665
657
  /** Declare handler dependencies */
666
- deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): FifoQueueBuilder<T, D2, P, C, HasFiles>;
658
+ deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): QueueBuilder<T, D2, P, C, HasFiles>;
667
659
  /** Declare SSM secrets */
668
- config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): FifoQueueBuilder<T, D, P2, C, HasFiles>;
660
+ config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): QueueBuilder<T, D, P2, C, HasFiles>;
669
661
  /** Include static files in the Lambda bundle. Chainable — call multiple times. */
670
- include(glob: string): FifoQueueBuilder<T, D, P, C, true>;
662
+ include(glob: string): QueueBuilder<T, D, P, C, true>;
671
663
  /** Configure Lambda settings only (memory, timeout, permissions, etc.) */
672
- setup(lambda: LambdaOptions): FifoQueueBuilder<T, D, P, C, HasFiles>;
664
+ setup(lambda: LambdaOptions): QueueBuilder<T, D, P, C, HasFiles>;
673
665
  /** Initialize shared state on cold start. Receives deps, config, files. */
674
- setup<C2>(fn: (args: SetupArgs$5<D, P, HasFiles>) => C2 | Promise<C2>): FifoQueueBuilder<T, D, P, C2, HasFiles>;
666
+ setup<C2>(fn: (args: SetupArgs$5<D, P, HasFiles>) => C2 | Promise<C2>): QueueBuilder<T, D, P, C2, HasFiles>;
675
667
  /** Initialize shared state on cold start + configure Lambda settings. */
676
- setup<C2>(fn: (args: SetupArgs$5<D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): FifoQueueBuilder<T, D, P, C2, HasFiles>;
668
+ setup<C2>(fn: (args: SetupArgs$5<D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): QueueBuilder<T, D, P, C2, HasFiles>;
669
+ /**
670
+ * Configure the event source mapping (poller) that delivers messages to the Lambda.
671
+ * Call before the terminal `.onMessage` / `.onMessageBatch`.
672
+ */
673
+ poller(options: QueuePollerConfig): QueueBuilder<T, D, P, C, HasFiles>;
677
674
  /** Handle errors thrown by message handlers */
678
675
  onError(fn: (args: {
679
676
  error: unknown;
680
- } & SpreadCtx$5<C>) => void | Promise<void>): FifoQueueBuilder<T, D, P, C, HasFiles>;
677
+ } & SpreadCtx$5<C>) => void | Promise<void>): QueueBuilder<T, D, P, C, HasFiles>;
681
678
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
682
- onCleanup(fn: (args: SpreadCtx$5<C>) => void | Promise<void>): FifoQueueBuilder<T, D, P, C, HasFiles>;
679
+ onCleanup(fn: (args: SpreadCtx$5<C>) => void | Promise<void>): QueueBuilder<T, D, P, C, HasFiles>;
683
680
  /** Per-message handler (terminal — returns finalized handler) */
684
- onMessage(fn: FifoQueueMessageFn<T, C>): FifoQueueHandler$1<T, C>;
681
+ onMessage(fn: QueueMessageFn<T, C>): QueueHandler$1<T, C>;
685
682
  /** Batch handler (terminal — returns finalized handler) */
686
- onMessageBatch(fn: FifoQueueBatchFn<T, C>): FifoQueueHandler$1<T, C>;
683
+ onMessageBatch(fn: QueueBatchFn<T, C>): QueueHandler$1<T, C>;
684
+ /** Finalize as a resource-only queue (no Lambda). Use when the SQS queue is consumed by an external system. */
685
+ build(): QueueHandler$1<T, C>;
687
686
  }
688
687
  /**
689
- * Define a FIFO SQS queue with a Lambda message handler.
688
+ * Define an SQS queue with a Lambda message handler.
690
689
  *
691
- * @see {@link https://effortless-aws.website/use-cases/queue | Queue guide}
690
+ * Currently only FIFO queues are supported — pass `{ fifo: true }` explicitly
691
+ * (standard queue support is planned). Poller/batch settings live on the
692
+ * `.poller({...})` builder method; queue-level properties stay in the options
693
+ * object.
692
694
  *
693
- * @example
694
- * ```typescript
695
- * export const notifications = defineFifoQueue({ batchSize: 5, schema: (i) => NotifSchema.parse(i) })
696
- * .onMessageBatch(async ({ messages }) => {
697
- * await sendAll(messages.map(m => m.body));
698
- * })
699
- * ```
695
+ * @see {@link https://effortless-aws.website/use-cases/queue | Queue guide}
700
696
  */
701
- declare function defineFifoQueue<T = unknown>(): FifoQueueBuilder<T>;
702
- declare function defineFifoQueue<T = unknown>(options: FifoQueueOptions<T>): FifoQueueBuilder<T>;
697
+ declare function defineQueue<T = unknown>(): QueueBuilder<T>;
698
+ declare function defineQueue<T = unknown>(options: QueueOptions<T>): QueueBuilder<T>;
703
699
 
704
700
  /** Fargate container size presets */
705
701
  type FargateSize = "0.25vCPU-512mb" | "0.5vCPU-1gb" | "1vCPU-2gb" | "2vCPU-4gb" | "4vCPU-8gb";
@@ -783,17 +779,7 @@ interface WorkerBuilder<T, D = undefined, P = undefined, C = undefined, HasFiles
783
779
  *
784
780
  * @typeParam T - Type of messages the worker receives via its queue
785
781
  *
786
- * @example
787
- * ```typescript
788
- * type Job = { type: "export"; userId: string }
789
- *
790
- * export const worker = defineWorker<Job>({ memory: 2048, concurrency: 5 })
791
- * .deps(() => ({ orders }))
792
- * .setup(async ({ deps }) => ({ db: deps.orders }))
793
- * .onMessage(async (msg, { db }) => {
794
- * await processJob(msg, db)
795
- * })
796
- * ```
782
+ * @see {@link https://effortless-aws.website/definitions#defineworker | Worker reference}
797
783
  */
798
784
  declare function defineWorker<T = unknown>(options?: WorkerOptions): WorkerBuilder<T>;
799
785
 
@@ -978,14 +964,32 @@ type WorkerClient<T = unknown> = {
978
964
  };
979
965
 
980
966
  /** Dep value types supported by the deps declaration */
981
- type AnyDepHandler = TableHandler$1<any, any> | BucketHandler$1<any, any> | MailerHandler | FifoQueueHandler$1<any, any> | WorkerHandler$1<any, any>;
967
+ type AnyDepHandler = TableHandler$1<any, any> | BucketHandler$1<any, any> | MailerHandler | QueueHandler$1<any, any> | WorkerHandler$1<any, any>;
982
968
  /** Maps a deps declaration to resolved runtime client types */
983
969
  type ResolveDeps<D> = {
984
- [K in keyof D]: D[K] extends TableHandler$1<infer T> ? TableClient<T> : D[K] extends BucketHandler$1<any, infer E> ? ({} extends E ? BucketClient : BucketClientWithEntities<E>) : D[K] extends MailerHandler ? EmailClient : D[K] extends FifoQueueHandler$1<infer T> ? QueueClient<T> : D[K] extends WorkerHandler$1<infer T> ? WorkerClient<T> : never;
970
+ [K in keyof D]: D[K] extends TableHandler$1<infer T> ? TableClient<T> : D[K] extends BucketHandler$1<any, infer E> ? ({} extends E ? BucketClient : BucketClientWithEntities<E>) : D[K] extends MailerHandler ? EmailClient : D[K] extends QueueHandler$1<infer T> ? QueueClient<T> : D[K] extends WorkerHandler$1<infer T> ? WorkerClient<T> : never;
985
971
  };
986
972
 
987
973
  /** DynamoDB Streams view type - determines what data is captured in stream records */
988
974
  type StreamView = "NEW_AND_OLD_IMAGES" | "NEW_IMAGE" | "OLD_IMAGE" | "KEYS_ONLY";
975
+ /**
976
+ * Stream event source mapping configuration.
977
+ * @internal
978
+ */
979
+ type TableStreamConfig = {
980
+ /** Stream view type - what data to include in stream records (default: "NEW_AND_OLD_IMAGES") */
981
+ streamView?: StreamView;
982
+ /** Number of records to process in each Lambda invocation (1-10000, default: 100) */
983
+ batchSize?: number;
984
+ /** Maximum time to gather records before invoking (default: `"2s"`). Accepts `"5s"`, `"1m"`, etc. */
985
+ batchWindow?: Duration;
986
+ /** Where to start reading the stream (default: "LATEST") */
987
+ startingPosition?: "LATEST" | "TRIM_HORIZON";
988
+ /** Number of records to process concurrently within a batch (default: 1 — sequential) */
989
+ concurrency?: number;
990
+ /** Max retry attempts for failed records before sending to DLQ (default: 1) */
991
+ maxRetries?: number;
992
+ };
989
993
  /**
990
994
  * Configuration options for defineTable (single-table design).
991
995
  *
@@ -1002,22 +1006,14 @@ type TableConfig = {
1002
1006
  };
1003
1007
  /** DynamoDB billing mode (default: "PAY_PER_REQUEST") */
1004
1008
  billingMode?: "PAY_PER_REQUEST" | "PROVISIONED";
1005
- /** Stream view type - what data to include in stream records (default: "NEW_AND_OLD_IMAGES") */
1006
- streamView?: StreamView;
1007
- /** Number of records to process in each Lambda invocation (1-10000, default: 100) */
1008
- batchSize?: number;
1009
- /** Maximum time to gather records before invoking (default: `"2s"`). Accepts `"5s"`, `"1m"`, etc. */
1010
- batchWindow?: Duration;
1011
- /** Where to start reading the stream (default: "LATEST") */
1012
- startingPosition?: "LATEST" | "TRIM_HORIZON";
1013
- /** Number of records to process concurrently within a batch (default: 1 — sequential) */
1014
- concurrency?: number;
1015
1009
  /**
1016
1010
  * Name of the field in `data` that serves as the entity type discriminant.
1017
1011
  * Effortless auto-copies `data[tagField]` to the top-level DynamoDB `tag` attribute on `put()`.
1018
1012
  * Defaults to `"tag"`.
1019
1013
  */
1020
1014
  tagField?: string;
1015
+ /** Stream event source mapping config — set via `.stream({...})` builder method. */
1016
+ stream?: TableStreamConfig;
1021
1017
  };
1022
1018
  /**
1023
1019
  * DynamoDB stream record passed to onRecord callback.
@@ -1092,44 +1088,36 @@ type TableHandler$1<T = Record<string, unknown>, C = any> = {
1092
1088
  readonly onRecord?: (...args: any[]) => any;
1093
1089
  readonly onRecordBatch?: (...args: any[]) => any;
1094
1090
  };
1095
- /** Options passed to `defineTable()` — resource config only, no Lambda settings */
1091
+ /** Options passed to `defineTable()` — resource config only. Stream options go in `.stream({...})`. */
1096
1092
  type TableOptions<T> = {
1097
1093
  /** DynamoDB billing mode (default: "PAY_PER_REQUEST") */
1098
1094
  billingMode?: "PAY_PER_REQUEST" | "PROVISIONED";
1099
- /** Stream view type (default: "NEW_AND_OLD_IMAGES") */
1100
- streamView?: StreamView;
1101
- /** Number of records to process in each Lambda invocation (1-10000, default: 100) */
1102
- batchSize?: number;
1103
- /** Maximum time to gather records before invoking (default: "2s") */
1104
- batchWindow?: Duration;
1105
- /** Where to start reading the stream (default: "LATEST") */
1106
- startingPosition?: "LATEST" | "TRIM_HORIZON";
1107
- /** Number of records to process concurrently within a batch (default: 1) */
1108
- concurrency?: number;
1109
1095
  /** Name of the field in `data` that serves as the entity type discriminant (default: "tag") */
1110
1096
  tagField?: Extract<keyof T, string>;
1111
1097
  /** Decode/validate function for the `data` portion of stream records */
1112
1098
  schema?: (input: unknown) => T;
1113
1099
  };
1114
- interface TableBuilder<T = Record<string, unknown>, D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
1100
+ interface TableBuilder<T = Record<string, unknown>, D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false, HasStream extends boolean = false> {
1115
1101
  /** Declare handler dependencies (tables, queues, buckets, mailers) */
1116
- deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): TableBuilder<T, D2, P, C, HasFiles>;
1102
+ deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): TableBuilder<T, D2, P, C, HasFiles, HasStream>;
1117
1103
  /** Declare SSM secrets */
1118
- config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): TableBuilder<T, D, P2, C, HasFiles>;
1104
+ config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): TableBuilder<T, D, P2, C, HasFiles, HasStream>;
1119
1105
  /** Include static files in the Lambda bundle. Chainable — call multiple times. */
1120
- include(glob: string): TableBuilder<T, D, P, C, true>;
1106
+ include(glob: string): TableBuilder<T, D, P, C, true, HasStream>;
1107
+ /** Configure the DynamoDB stream event source mapping (batch size, retries, concurrency, etc.). */
1108
+ stream: HasStream extends true ? never : (opts: TableStreamConfig) => TableBuilder<T, D, P, C, HasFiles, true>;
1121
1109
  /** Configure Lambda settings only (memory, timeout, permissions, etc.) */
1122
- setup(lambda: LambdaOptions): TableBuilder<T, D, P, C, HasFiles>;
1110
+ setup(lambda: LambdaOptions): TableBuilder<T, D, P, C, HasFiles, HasStream>;
1123
1111
  /** Initialize shared state on cold start. Receives table (self-client), deps, config, files. */
1124
- setup<C2>(fn: (args: SetupArgs$3<T, D, P, HasFiles>) => C2 | Promise<C2>): TableBuilder<T, D, P, C2, HasFiles>;
1112
+ setup<C2>(fn: (args: SetupArgs$3<T, D, P, HasFiles>) => C2 | Promise<C2>): TableBuilder<T, D, P, C2, HasFiles, HasStream>;
1125
1113
  /** Initialize shared state on cold start + configure Lambda settings. */
1126
- setup<C2>(fn: (args: SetupArgs$3<T, D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): TableBuilder<T, D, P, C2, HasFiles>;
1114
+ setup<C2>(fn: (args: SetupArgs$3<T, D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): TableBuilder<T, D, P, C2, HasFiles, HasStream>;
1127
1115
  /** Handle errors thrown by onRecord/onRecordBatch */
1128
1116
  onError(fn: (args: {
1129
1117
  error: unknown;
1130
- } & SpreadCtx$3<C>) => void | Promise<void>): TableBuilder<T, D, P, C, HasFiles>;
1118
+ } & SpreadCtx$3<C>) => void | Promise<void>): TableBuilder<T, D, P, C, HasFiles, HasStream>;
1131
1119
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
1132
- onCleanup(fn: (args: SpreadCtx$3<C>) => void | Promise<void>): TableBuilder<T, D, P, C, HasFiles>;
1120
+ onCleanup(fn: (args: SpreadCtx$3<C>) => void | Promise<void>): TableBuilder<T, D, P, C, HasFiles, HasStream>;
1133
1121
  /** Per-record stream handler (terminal — returns finalized handler) */
1134
1122
  onRecord(fn: TableRecordFn<T, C>): TableHandler$1<T, C>;
1135
1123
  /** Batch stream handler (terminal — returns finalized handler) */
@@ -1144,17 +1132,6 @@ interface TableBuilder<T = Record<string, unknown>, D = undefined, P = undefined
1144
1132
  * `data (M)`, and `ttl (N)` attributes. TTL is always enabled.
1145
1133
  *
1146
1134
  * @see {@link https://effortless-aws.website/use-cases/database | Database guide}
1147
- *
1148
- * @example
1149
- * ```typescript
1150
- * export const orders = defineTable<OrderData>({ batchSize: 10, concurrency: 5 })
1151
- * .setup(({ table }) => ({ table }))
1152
- * .onRecord(async ({ record, table }) => {
1153
- * if (record.eventName === "INSERT") {
1154
- * console.log("New order:", record.new?.data);
1155
- * }
1156
- * })
1157
- * ```
1158
1135
  */
1159
1136
  declare function defineTable<T = Record<string, unknown>>(): TableBuilder<T>;
1160
1137
  declare function defineTable<T = Record<string, unknown>>(options: TableOptions<T>): TableBuilder<T>;
@@ -1509,16 +1486,6 @@ interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boo
1509
1486
  * Define an API with typed routes using a builder pattern.
1510
1487
  *
1511
1488
  * @see {@link https://effortless-aws.website/use-cases/http-api | HTTP API guide}
1512
- *
1513
- * @example
1514
- * ```typescript
1515
- * export const api = defineApi({ basePath: "/api" })
1516
- * .deps(() => ({ users }))
1517
- * .config(({ defineSecret }) => ({ authSecret: defineSecret() }))
1518
- * .auth<Session>(({ config }) => ({ secret: config.authSecret, expiresIn: "1h" }))
1519
- * .get({ path: "/me" }, async ({ users, auth, ok }) => ok(auth.session))
1520
- * .post({ path: "/login", public: true }, async ({ auth, ok }) => ok(await auth.createSession()))
1521
- * ```
1522
1489
  */
1523
1490
  declare function defineApi<const O extends ApiOptions>(options: O): ApiBuilder<undefined, undefined, undefined, O["stream"] extends true ? true : false, false>;
1524
1491
 
@@ -1624,19 +1591,7 @@ interface CronBuilder<D = undefined, P = undefined, C = undefined, HasFiles exte
1624
1591
  /**
1625
1592
  * Define a cron job — scheduled Lambda invocation via EventBridge Scheduler.
1626
1593
  *
1627
- * @example
1628
- * ```typescript
1629
- * export const sync = defineCron({ schedule: "cron(0 9 * * ? *)" })
1630
- * .deps(() => ({ orders }))
1631
- * .config(({ defineSecret }) => ({ apiKey: defineSecret() }))
1632
- * .include("templates/*.html")
1633
- * .setup(async ({ deps, config, files }) => ({
1634
- * db: deps.orders, key: config.apiKey, tpl: files,
1635
- * }), { memory: 512 })
1636
- * .onTick(async ({ db, key, tpl }) => {
1637
- * const html = tpl.read("templates/report.html")
1638
- * })
1639
- * ```
1594
+ * @see {@link https://effortless-aws.website/definitions#definecron | Cron reference}
1640
1595
  */
1641
1596
  declare function defineCron(options: CronOptions): CronBuilder;
1642
1597
 
@@ -1892,8 +1847,8 @@ interface McpBuilder<D = undefined, P = undefined, C = undefined, HasFiles exten
1892
1847
  * Creates a Lambda-backed MCP server that exposes tools, resources, and prompts
1893
1848
  * for AI models and MCP-compatible clients via Streamable HTTP (JSON-RPC over POST).
1894
1849
  *
1895
- * @see https://modelcontextprotocol.io/specification/2025-03-26 MCP specification
1896
- * @see https://effortless-aws.com/use-cases/mcp-server/ full documentation with examples
1850
+ * @see {@link https://effortless-aws.website/use-cases/mcp-server | MCP Server guide}
1851
+ * @see {@link https://modelcontextprotocol.io/specification/2025-03-26 | MCP specification}
1897
1852
  */
1898
1853
  declare function defineMcp(options: McpOptions): McpBuilder;
1899
1854
 
@@ -1904,10 +1859,10 @@ type McpResourceMap = McpResourceMap$1;
1904
1859
  type McpPromptDef = McpPromptDef$1;
1905
1860
 
1906
1861
  type TableHandler<T = Record<string, unknown>> = TableHandler$1<T, any>;
1907
- type FifoQueueHandler<T = unknown> = FifoQueueHandler$1<T, any>;
1862
+ type QueueHandler<T = unknown> = QueueHandler$1<T, any>;
1908
1863
  type BucketHandler<Entities extends Record<string, any> = {}> = BucketHandler$1<any, Entities>;
1909
1864
  type CronHandler = CronHandler$1<any>;
1910
1865
  type WorkerHandler<T = any> = WorkerHandler$1<T, any>;
1911
1866
  type McpHandler = McpHandler$1<any>;
1912
1867
 
1913
- 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 };
1868
+ 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 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 QueueConfig, type QueueHandler, type QueueMessage, type QueuePollerConfig, 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, defineMailer, defineMcp, defineQueue, defineSecret, defineStaticSite, defineTable, defineWorker, generateBase64, generateHex, generateUuid, param, secret, toSeconds };
package/dist/index.js CHANGED
@@ -77,6 +77,10 @@ function defineTable(options) {
77
77
  state.static = [...state.static ?? [], glob];
78
78
  return builder;
79
79
  },
80
+ stream(opts) {
81
+ state.spec = { ...state.spec, stream: { ...state.spec.stream, ...opts } };
82
+ return builder;
83
+ },
80
84
  setup(fnOrLambda, maybeLambda) {
81
85
  if (typeof fnOrLambda === "function") {
82
86
  state.setup = fnOrLambda;
@@ -143,8 +147,8 @@ function defineStaticSite(options) {
143
147
  return builder;
144
148
  }
145
149
 
146
- // src/handlers/define-fifo-queue.ts
147
- function defineFifoQueue(options) {
150
+ // src/handlers/define-queue.ts
151
+ function defineQueue(options) {
148
152
  const {
149
153
  schema,
150
154
  ...queueConfig
@@ -160,7 +164,7 @@ function defineFifoQueue(options) {
160
164
  }
161
165
  };
162
166
  const finalize = () => ({
163
- __brand: "effortless-fifo-queue",
167
+ __brand: "effortless-queue",
164
168
  __spec: state.spec,
165
169
  ...state.schema ? { schema: state.schema } : {},
166
170
  ...state.onError ? { onError: state.onError } : {},
@@ -194,6 +198,12 @@ function defineFifoQueue(options) {
194
198
  }
195
199
  return builder;
196
200
  },
201
+ poller(options2) {
202
+ if (Object.keys(options2).length > 0) {
203
+ state.spec = { ...state.spec, poller: { ...state.spec.poller, ...options2 } };
204
+ }
205
+ return builder;
206
+ },
197
207
  onMessage(fn) {
198
208
  state.onMessage = fn;
199
209
  return finalize();
@@ -209,6 +219,9 @@ function defineFifoQueue(options) {
209
219
  onCleanup(fn) {
210
220
  state.onCleanup = fn;
211
221
  return builder;
222
+ },
223
+ build() {
224
+ return finalize();
212
225
  }
213
226
  };
214
227
  return builder;
@@ -658,9 +671,9 @@ export {
658
671
  defineBucket,
659
672
  defineConfig,
660
673
  defineCron,
661
- defineFifoQueue,
662
674
  defineMailer,
663
675
  defineMcp,
676
+ defineQueue,
664
677
  defineSecret,
665
678
  defineStaticSite,
666
679
  defineTable,