effortless-aws 0.28.0 → 0.29.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
@@ -95,6 +95,8 @@ type EffortlessConfig = {
95
95
  */
96
96
  declare const defineConfig: (config: EffortlessConfig) => EffortlessConfig;
97
97
 
98
+ /** Generator spec for auto-creating secrets at deploy time. */
99
+ type GenerateSpec = `hex:${number}` | `base64:${number}` | "uuid";
98
100
  type AwsService = "dynamodb" | "s3" | "sqs" | "sns" | "ses" | "ssm" | "lambda" | "events" | "secretsmanager" | "cognito-idp" | "logs";
99
101
  type Permission = `${AwsService}:${string}` | (string & {});
100
102
  /**
@@ -143,7 +145,7 @@ type AnySecretRef = SecretRef<any>;
143
145
  type SecretRef<T = string> = {
144
146
  readonly __brand: "effortless-secret";
145
147
  readonly key?: string;
146
- readonly generate?: () => string;
148
+ readonly generate?: GenerateSpec;
147
149
  readonly transform?: (raw: string) => T;
148
150
  };
149
151
  /**
@@ -155,76 +157,46 @@ type SecretRef<T = string> = {
155
157
  type ResolveConfig<P> = {
156
158
  [K in keyof P]: P[K] extends SecretRef<infer T> ? T : string;
157
159
  };
158
- /** Options for `secret()` without a transform. */
159
- type SecretOptions = {
160
+ /** Options for `defineSecret()` without a transform. */
161
+ type DefineSecretOptions = {
160
162
  /** Custom SSM key (default: derived from config property name in kebab-case) */
161
163
  key?: string;
162
- /** Generator function called at deploy time if the SSM parameter doesn't exist yet */
163
- generate?: () => string;
164
+ /** Generator spec for auto-creating the secret at deploy time: `"hex:32"`, `"base64:32"`, `"uuid"` */
165
+ generate?: GenerateSpec;
164
166
  };
165
- /** Options for `secret()` with a transform. */
166
- type SecretOptionsWithTransform<T> = SecretOptions & {
167
+ /** Options for `defineSecret()` with a transform. */
168
+ type DefineSecretOptionsWithTransform<T> = DefineSecretOptions & {
167
169
  /** Transform the raw SSM string value into a typed value */
168
170
  transform: (raw: string) => T;
169
171
  };
170
- /**
171
- * Declare an SSM Parameter Store secret.
172
- *
173
- * The SSM key is derived from the config property name (camelCase kebab-case)
174
- * unless overridden with `key`. The full SSM path is `/${project}/${stage}/${key}`.
175
- *
176
- * @param options - Optional configuration (key override, generator, transform)
177
- * @returns A SecretRef used by the deployment and runtime systems
178
- *
179
- * @example Simple secret
180
- * ```typescript
181
- * config: {
182
- * dbUrl: secret(),
183
- * // → SSM path: /${project}/${stage}/db-url
184
- * }
185
- * ```
186
- *
187
- * @example Auto-generated secret
188
- * ```typescript
189
- * config: {
190
- * authSecret: secret({ generate: generateHex(64) }),
191
- * // → auto-creates SSM param if missing
192
- * }
193
- * ```
194
- *
195
- * @example With transform
196
- * ```typescript
197
- * config: {
198
- * appConfig: secret({ transform: TOML.parse }),
199
- * }
200
- * ```
201
- */
202
- declare function secret(): SecretRef<string>;
203
- declare function secret(options: SecretOptions): SecretRef<string>;
204
- declare function secret<T>(options: SecretOptionsWithTransform<T>): SecretRef<T>;
205
- /**
206
- * Returns a generator that produces a random hex string.
207
- * @param bytes - Number of random bytes (output will be 2x this length in hex chars)
208
- */
209
- declare const generateHex: (bytes: number) => () => string;
210
- /**
211
- * Returns a generator that produces a random base64url string.
212
- * @param bytes - Number of random bytes
213
- */
214
- declare const generateBase64: (bytes: number) => () => string;
215
- /**
216
- * Returns a generator that produces a random UUID v4.
217
- */
218
- declare const generateUuid: () => () => string;
172
+ /** The defineSecret helper function type, injected into config callbacks. */
173
+ type DefineSecretFn = {
174
+ (): SecretRef<string>;
175
+ (options: DefineSecretOptions): SecretRef<string>;
176
+ <T>(options: DefineSecretOptionsWithTransform<T>): SecretRef<T>;
177
+ };
178
+ /** Helpers injected into the `config` callback. */
179
+ type ConfigHelpers = {
180
+ defineSecret: DefineSecretFn;
181
+ };
182
+ /** Config factory: a function receiving helpers and returning a record of SecretRefs. */
183
+ type ConfigFactory<P> = (helpers: ConfigHelpers) => P;
184
+ /** The `defineSecret` implementation, passed to config callbacks. */
185
+ declare const defineSecret: DefineSecretFn;
186
+ /** @deprecated Use `defineSecret()` inside a config callback instead. */
187
+ declare const secret: DefineSecretFn;
219
188
  /** @deprecated Use `SecretRef` instead */
220
189
  type ParamRef<T = string> = SecretRef<T>;
221
190
  /** @deprecated Use `AnySecretRef` instead */
222
191
  type AnyParamRef = AnySecretRef;
223
- /**
224
- * @deprecated Use `secret()` instead. `param("key")` is equivalent to `secret({ key: "key" })`.
225
- */
226
- declare function param(key: string): SecretRef<string>;
227
- declare function param<T>(key: string, transform: (raw: string) => T): SecretRef<T>;
192
+ /** @deprecated Use `defineSecret()` instead. */
193
+ declare const param: <T = string>(key: string, transform?: (raw: string) => T) => SecretRef<T>;
194
+ /** @deprecated Use `defineSecret({ generate: "hex:N" })` instead. */
195
+ declare const generateHex: (bytes: number) => string;
196
+ /** @deprecated Use `defineSecret({ generate: "base64:N" })` instead. */
197
+ declare const generateBase64: (bytes: number) => string;
198
+ /** @deprecated Use `defineSecret({ generate: "uuid" })` instead. */
199
+ declare const generateUuid: () => string;
228
200
  /**
229
201
  * DynamoDB table key (always pk + sk strings in single-table design).
230
202
  */
@@ -387,7 +359,7 @@ type TableClient<T = Record<string, unknown>> = {
387
359
  };
388
360
 
389
361
  /** HTTP methods supported by Lambda Function URLs */
390
- type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "ANY";
362
+ type HttpMethod$1 = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "ANY";
391
363
  /** Short content-type aliases for common response formats */
392
364
  type ContentType = "json" | "html" | "text" | "css" | "js" | "xml" | "csv" | "svg";
393
365
  /**
@@ -493,21 +465,6 @@ type BucketClient = {
493
465
  bucketName: string;
494
466
  };
495
467
 
496
- /**
497
- * Common conditional args injected into handler callbacks.
498
- * Resolves ctx, deps, config, and files based on whether each generic is defined.
499
- * @internal
500
- */
501
- type HandlerArgs<C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = ([C] extends [undefined] ? {} : {
502
- ctx: C;
503
- }) & ([D] extends [undefined] ? {} : {
504
- deps: ResolveDeps<D>;
505
- }) & ([P] extends [undefined] ? {} : {
506
- config: ResolveConfig<P>;
507
- }) & ([S] extends [undefined] ? {} : {
508
- files: StaticFiles;
509
- });
510
-
511
468
  /**
512
469
  * Configuration options for defineBucket.
513
470
  */
@@ -536,20 +493,20 @@ type BucketEvent = {
536
493
  /** S3 bucket name */
537
494
  bucketName: string;
538
495
  };
496
+ /** Spread ctx into callback args (empty when no setup) */
497
+ type SpreadCtx$3<C> = [C] extends [undefined] ? {} : C & {};
539
498
  /**
540
499
  * Callback function type for S3 ObjectCreated events
541
500
  */
542
- type BucketObjectCreatedFn<C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = (args: {
501
+ type BucketObjectCreatedFn<C = undefined> = (args: {
543
502
  event: BucketEvent;
544
- bucket: BucketClient;
545
- } & HandlerArgs<C, D, P, S>) => Promise<void>;
503
+ } & SpreadCtx$3<C>) => Promise<void>;
546
504
  /**
547
505
  * Callback function type for S3 ObjectRemoved events
548
506
  */
549
- type BucketObjectRemovedFn<C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = (args: {
507
+ type BucketObjectRemovedFn<C = undefined> = (args: {
550
508
  event: BucketEvent;
551
- bucket: BucketClient;
552
- } & HandlerArgs<C, D, P, S>) => Promise<void>;
509
+ } & SpreadCtx$3<C>) => Promise<void>;
553
510
  /**
554
511
  * Setup factory type for bucket handlers.
555
512
  * Always receives `bucket: BucketClient` (self-client for the handler's own bucket).
@@ -572,16 +529,16 @@ type DefineBucketBase<C = undefined, D = undefined, P = undefined, S extends str
572
529
  */
573
530
  onError?: (args: {
574
531
  error: unknown;
575
- } & HandlerArgs<C, D, P, S>) => void;
532
+ } & SpreadCtx$3<C>) => void;
576
533
  /** Called after each invocation completes, right before Lambda freezes the process */
577
- onAfterInvoke?: (args: HandlerArgs<C, D, P, S>) => void | Promise<void>;
534
+ onAfterInvoke?: (args: SpreadCtx$3<C>) => void | Promise<void>;
578
535
  /**
579
536
  * Factory function to initialize shared state for callbacks.
580
537
  * Called once on cold start, result is cached and reused across invocations.
581
538
  * Always receives `bucket: BucketClient` (self-client). When deps/config
582
539
  * are declared, receives them as well.
583
540
  */
584
- setup?: SetupFactory$3<C, D, P, S>;
541
+ setup?: SetupFactory$3<C, NoInfer<D>, NoInfer<P>, NoInfer<S>>;
585
542
  /**
586
543
  * Dependencies on other handlers (tables, buckets, etc.).
587
544
  * Typed clients are injected into the handler via the `deps` argument.
@@ -590,9 +547,9 @@ type DefineBucketBase<C = undefined, D = undefined, P = undefined, S extends str
590
547
  deps?: () => D & {};
591
548
  /**
592
549
  * SSM Parameter Store parameters.
593
- * Declare with `param()` helper. Values are fetched and cached at cold start.
550
+ * Declare with `defineSecret()` helper. Values are fetched and cached at cold start.
594
551
  */
595
- config?: P;
552
+ config?: ConfigFactory<P>;
596
553
  /**
597
554
  * Static file glob patterns to bundle into the Lambda ZIP.
598
555
  * Files are accessible at runtime via the `files` callback argument.
@@ -601,15 +558,15 @@ type DefineBucketBase<C = undefined, D = undefined, P = undefined, S extends str
601
558
  };
602
559
  /** With event handlers (at least one callback) */
603
560
  type DefineBucketWithHandlers<C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineBucketBase<C, D, P, S> & {
604
- onObjectCreated?: BucketObjectCreatedFn<C, D, P, S>;
605
- onObjectRemoved?: BucketObjectRemovedFn<C, D, P, S>;
561
+ onObjectCreated?: BucketObjectCreatedFn<C>;
562
+ onObjectRemoved?: BucketObjectRemovedFn<C>;
606
563
  };
607
564
  /** Resource-only: no Lambda, just creates the bucket */
608
565
  type DefineBucketResourceOnly<C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineBucketBase<C, D, P, S> & {
609
566
  onObjectCreated?: never;
610
567
  onObjectRemoved?: never;
611
568
  };
612
- type DefineBucketOptions<C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined> = DefineBucketWithHandlers<C, D, P, S> | DefineBucketResourceOnly<C, D, P, S>;
569
+ type DefineBucketOptions<C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnySecretRef> | undefined = undefined, S extends string[] | undefined = undefined> = DefineBucketWithHandlers<C, D, P, S> | DefineBucketResourceOnly<C, D, P, S>;
613
570
  /**
614
571
  * Internal handler object created by defineBucket
615
572
  * @internal
@@ -661,7 +618,7 @@ type BucketHandler<C = any> = {
661
618
  * });
662
619
  * ```
663
620
  */
664
- declare const defineBucket: <C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined>(options: DefineBucketOptions<C, D, P, S>) => BucketHandler<C>;
621
+ declare const defineBucket: () => <C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnySecretRef> | undefined = undefined, S extends string[] | undefined = undefined>(options: DefineBucketOptions<C, D, P, S>) => BucketHandler<C>;
665
622
 
666
623
  /**
667
624
  * Configuration options for defining a mailer (SES email identity)
@@ -709,7 +666,7 @@ type MailerHandler = {
709
666
  * });
710
667
  * ```
711
668
  */
712
- declare const defineMailer: (options: MailerConfig) => MailerHandler;
669
+ declare const defineMailer: () => (options: MailerConfig) => MailerHandler;
713
670
 
714
671
  /**
715
672
  * Parsed SQS FIFO message passed to the handler callbacks.
@@ -773,20 +730,25 @@ type SetupFactory$2<C, D, P, S extends string[] | undefined = undefined> = (args
773
730
  }) & ([S] extends [undefined] ? {} : {
774
731
  files: StaticFiles;
775
732
  })) => C | Promise<C>;
733
+ /** Spread ctx into callback args (empty when no setup) */
734
+ type SpreadCtx$2<C> = [C] extends [undefined] ? {} : C & {};
776
735
  /**
777
736
  * Per-message handler function.
778
737
  * Called once per message in the batch. Failures are reported individually.
779
738
  */
780
- type FifoQueueMessageFn<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = (args: {
739
+ type FifoQueueMessageFn<T = unknown, C = undefined> = (args: {
781
740
  message: FifoQueueMessage<T>;
782
- } & HandlerArgs<C, D, P, S>) => Promise<void>;
741
+ } & SpreadCtx$2<C>) => Promise<void>;
783
742
  /**
784
743
  * Batch handler function.
785
744
  * Called once with all messages in the batch.
745
+ * Return `{ failures: string[] }` (messageIds) for partial batch failure reporting.
786
746
  */
787
- type FifoQueueBatchFn<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = (args: {
747
+ type FifoQueueBatchFn<T = unknown, C = undefined> = (args: {
788
748
  messages: FifoQueueMessage<T>[];
789
- } & HandlerArgs<C, D, P, S>) => Promise<void>;
749
+ } & SpreadCtx$2<C>) => Promise<void | {
750
+ failures: string[];
751
+ }>;
790
752
  /** Base options shared by all defineFifoQueue variants */
791
753
  type DefineFifoQueueBase<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = FifoQueueConfig & {
792
754
  /**
@@ -795,20 +757,20 @@ type DefineFifoQueueBase<T = unknown, C = undefined, D = undefined, P = undefine
795
757
  */
796
758
  schema?: (input: unknown) => T;
797
759
  /**
798
- * Error handler called when onMessage or onBatch throws.
760
+ * Error handler called when onMessage or onMessageBatch throws.
799
761
  * If not provided, defaults to `console.error`.
800
762
  */
801
763
  onError?: (args: {
802
764
  error: unknown;
803
- } & HandlerArgs<C, D, P, S>) => void;
765
+ } & SpreadCtx$2<C>) => void;
804
766
  /** Called after each invocation completes, right before Lambda freezes the process */
805
- onAfterInvoke?: (args: HandlerArgs<C, D, P, S>) => void | Promise<void>;
767
+ onAfterInvoke?: (args: SpreadCtx$2<C>) => void | Promise<void>;
806
768
  /**
807
769
  * Factory function to initialize shared state for the handler.
808
770
  * Called once on cold start, result is cached and reused across invocations.
809
771
  * When deps/params are declared, receives them as argument.
810
772
  */
811
- setup?: SetupFactory$2<C, D, P, S>;
773
+ setup?: SetupFactory$2<C, NoInfer<D>, NoInfer<P>, NoInfer<S>>;
812
774
  /**
813
775
  * Dependencies on other handlers (tables, queues, etc.).
814
776
  * Typed clients are injected into the handler via the `deps` argument.
@@ -817,9 +779,9 @@ type DefineFifoQueueBase<T = unknown, C = undefined, D = undefined, P = undefine
817
779
  deps?: () => D & {};
818
780
  /**
819
781
  * SSM Parameter Store parameters.
820
- * Declare with `param()` helper. Values are fetched and cached at cold start.
782
+ * Declare with `defineSecret()` helper. Values are fetched and cached at cold start.
821
783
  */
822
- config?: P;
784
+ config?: ConfigFactory<P>;
823
785
  /**
824
786
  * Static file glob patterns to bundle into the Lambda ZIP.
825
787
  * Files are accessible at runtime via the `files` callback argument.
@@ -828,15 +790,15 @@ type DefineFifoQueueBase<T = unknown, C = undefined, D = undefined, P = undefine
828
790
  };
829
791
  /** Per-message processing */
830
792
  type DefineFifoQueueWithOnMessage<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineFifoQueueBase<T, C, D, P, S> & {
831
- onMessage: FifoQueueMessageFn<T, C, D, P, S>;
832
- onBatch?: never;
793
+ onMessage: FifoQueueMessageFn<T, C>;
794
+ onMessageBatch?: never;
833
795
  };
834
796
  /** Batch processing: all messages at once */
835
797
  type DefineFifoQueueWithOnBatch<T = unknown, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineFifoQueueBase<T, C, D, P, S> & {
836
- onBatch: FifoQueueBatchFn<T, C, D, P, S>;
798
+ onMessageBatch: FifoQueueBatchFn<T, C>;
837
799
  onMessage?: never;
838
800
  };
839
- type DefineFifoQueueOptions<T = unknown, C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined> = DefineFifoQueueWithOnMessage<T, C, D, P, S> | DefineFifoQueueWithOnBatch<T, C, D, P, S>;
801
+ type DefineFifoQueueOptions<T = unknown, C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnySecretRef> | undefined = undefined, S extends string[] | undefined = undefined> = DefineFifoQueueWithOnMessage<T, C, D, P, S> | DefineFifoQueueWithOnBatch<T, C, D, P, S>;
840
802
  /**
841
803
  * Internal handler object created by defineFifoQueue
842
804
  * @internal
@@ -852,7 +814,7 @@ type FifoQueueHandler<T = unknown, C = any> = {
852
814
  readonly config?: Record<string, unknown>;
853
815
  readonly static?: string[];
854
816
  readonly onMessage?: (...args: any[]) => any;
855
- readonly onBatch?: (...args: any[]) => any;
817
+ readonly onMessageBatch?: (...args: any[]) => any;
856
818
  };
857
819
  /**
858
820
  * Define a FIFO SQS queue with a Lambda message handler
@@ -878,13 +840,13 @@ type FifoQueueHandler<T = unknown, C = any> = {
878
840
  * export const notifications = defineFifoQueue({
879
841
  * schema: (input) => NotificationSchema.parse(input),
880
842
  * batchSize: 5,
881
- * onBatch: async ({ messages }) => {
843
+ * onMessageBatch: async ({ messages }) => {
882
844
  * await sendAll(messages.map(m => m.body));
883
845
  * }
884
846
  * });
885
847
  * ```
886
848
  */
887
- declare const defineFifoQueue: <T = unknown, C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined>(options: DefineFifoQueueOptions<T, C, D, P, S>) => FifoQueueHandler<T, C>;
849
+ declare const defineFifoQueue: <T = unknown>() => <C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnySecretRef> | undefined = undefined, S extends string[] | undefined = undefined>(options: DefineFifoQueueOptions<T, C, D, P, S>) => FifoQueueHandler<T, C>;
888
850
 
889
851
  /**
890
852
  * Options for sending an email via EmailClient.send()
@@ -973,18 +935,12 @@ type TableConfig = {
973
935
  batchWindow?: Duration;
974
936
  /** Where to start reading the stream (default: "LATEST") */
975
937
  startingPosition?: "LATEST" | "TRIM_HORIZON";
938
+ /** Number of records to process concurrently within a batch (default: 1 — sequential) */
939
+ concurrency?: number;
976
940
  /**
977
941
  * Name of the field in `data` that serves as the entity type discriminant.
978
942
  * Effortless auto-copies `data[tagField]` to the top-level DynamoDB `tag` attribute on `put()`.
979
943
  * Defaults to `"tag"`.
980
- *
981
- * @example
982
- * ```typescript
983
- * export const orders = defineTable<{ type: "order"; amount: number }>({
984
- * tagField: "type",
985
- * onRecord: async ({ record }) => { ... }
986
- * });
987
- * ```
988
944
  */
989
945
  tagField?: string;
990
946
  };
@@ -1012,20 +968,9 @@ type TableRecord<T = Record<string, unknown>> = {
1012
968
  /** Approximate timestamp when the modification occurred */
1013
969
  timestamp?: number;
1014
970
  };
1015
- /**
1016
- * Information about a failed record during batch processing
1017
- *
1018
- * @typeParam T - Type of the domain data
1019
- */
1020
- type FailedRecord<T = Record<string, unknown>> = {
1021
- /** The record that failed to process */
1022
- record: TableRecord<T>;
1023
- /** The error that occurred */
1024
- error: unknown;
1025
- };
1026
971
  /**
1027
972
  * Setup factory type for table handlers.
1028
- * Always receives `table: TableClient<T>` (self-client for the handler's own table).
973
+ * Receives `table: TableClient<T>` (self-client for the handler's own table).
1029
974
  * Also receives `deps` and/or `config` when declared.
1030
975
  */
1031
976
  type SetupFactory$1<C, T, D, P, S extends string[] | undefined = undefined> = (args: {
@@ -1037,28 +982,26 @@ type SetupFactory$1<C, T, D, P, S extends string[] | undefined = undefined> = (a
1037
982
  }) & ([S] extends [undefined] ? {} : {
1038
983
  files: StaticFiles;
1039
984
  })) => C | Promise<C>;
985
+ /** Spread ctx into callback args (empty when no setup) */
986
+ type SpreadCtx$1<C> = [C] extends [undefined] ? {} : C & {};
1040
987
  /**
1041
- * Callback function type for processing a single DynamoDB stream record
988
+ * Callback function type for processing a single DynamoDB stream record.
989
+ * Receives the current record and the full batch for context.
1042
990
  */
1043
- type TableRecordFn<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> = (args: {
991
+ type TableRecordFn<T = Record<string, unknown>, C = undefined> = (args: {
1044
992
  record: TableRecord<T>;
1045
- table: TableClient<T>;
1046
- } & HandlerArgs<C, D, P, S>) => Promise<R>;
1047
- /**
1048
- * Callback function type for processing accumulated batch results
1049
- */
1050
- type TableBatchCompleteFn<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> = (args: {
1051
- results: R[];
1052
- failures: FailedRecord<T>[];
1053
- table: TableClient<T>;
1054
- } & HandlerArgs<C, D, P, S>) => Promise<void>;
1055
- /**
1056
- * Callback function type for processing all records in a batch at once
1057
- */
1058
- type TableBatchFn<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = (args: {
1059
- records: TableRecord<T>[];
1060
- table: TableClient<T>;
1061
- } & HandlerArgs<C, D, P, S>) => Promise<void>;
993
+ batch: readonly TableRecord<T>[];
994
+ } & SpreadCtx$1<C>) => Promise<void>;
995
+ /**
996
+ * Batch handler function for DynamoDB stream records.
997
+ * Called once with all records in the batch.
998
+ * Return `{ failures: string[] }` (sequence numbers) for partial batch failure reporting.
999
+ */
1000
+ type TableBatchFn<T = Record<string, unknown>, C = undefined> = (args: {
1001
+ records: readonly TableRecord<T>[];
1002
+ } & SpreadCtx$1<C>) => Promise<void | {
1003
+ failures: string[];
1004
+ }>;
1062
1005
  /** Base options shared by all defineTable variants */
1063
1006
  type DefineTableBase<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = Omit<TableConfig, "tagField"> & {
1064
1007
  /** Name of the field in `data` that serves as the entity type discriminant (default: `"tag"`). */
@@ -1070,58 +1013,60 @@ type DefineTableBase<T = Record<string, unknown>, C = undefined, D = undefined,
1070
1013
  */
1071
1014
  schema?: (input: unknown) => T;
1072
1015
  /**
1073
- * Error handler called when onRecord, onBatch, or onBatchComplete throws.
1074
- * Receives the error. If not provided, defaults to `console.error`.
1016
+ * Error handler called when onRecord/onRecordBatch throws.
1017
+ * If not provided, defaults to `console.error`.
1075
1018
  */
1076
1019
  onError?: (args: {
1077
1020
  error: unknown;
1078
- } & HandlerArgs<C, D, P, S>) => void;
1021
+ } & SpreadCtx$1<C>) => void;
1079
1022
  /** Called after each invocation completes, right before Lambda freezes the process */
1080
- onAfterInvoke?: (args: HandlerArgs<C, D, P, S>) => void | Promise<void>;
1023
+ onAfterInvoke?: (args: SpreadCtx$1<C>) => void | Promise<void>;
1081
1024
  /**
1082
1025
  * Factory function to initialize shared state for callbacks.
1083
1026
  * Called once on cold start, result is cached and reused across invocations.
1084
- * When deps/params are declared, receives them as argument.
1085
- * Supports both sync and async return values.
1027
+ * Receives `table` (self-client), plus `deps`/`config`/`files` when declared.
1086
1028
  */
1087
- setup?: SetupFactory$1<C, T, D, P, S>;
1029
+ setup?: SetupFactory$1<C, T, NoInfer<D>, NoInfer<P>, NoInfer<S>>;
1088
1030
  /**
1089
1031
  * Dependencies on other handlers (tables, queues, etc.).
1090
- * Typed clients are injected into the handler via the `deps` argument.
1032
+ * Typed clients are injected into setup via the `deps` argument.
1091
1033
  * Pass a function returning the deps object: `deps: () => ({ orders })`.
1092
1034
  */
1093
1035
  deps?: () => D & {};
1094
1036
  /**
1095
1037
  * SSM Parameter Store parameters.
1096
- * Declare with `param()` helper. Values are fetched and cached at cold start.
1097
- * Typed values are injected into the handler via the `config` argument.
1038
+ * Declare with `defineSecret()` helper. Values are fetched and cached at cold start.
1098
1039
  */
1099
- config?: P;
1040
+ config?: ConfigFactory<P>;
1100
1041
  /**
1101
1042
  * Static file glob patterns to bundle into the Lambda ZIP.
1102
- * Files are accessible at runtime via the `files` callback argument.
1043
+ * Files are accessible at runtime via the `files` argument in setup.
1103
1044
  */
1104
1045
  static?: S;
1105
1046
  };
1106
- /** Per-record processing: onRecord with optional onBatchComplete */
1107
- type DefineTableWithOnRecord<T = Record<string, unknown>, C = undefined, R = void, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {
1108
- onRecord: TableRecordFn<T, C, R, D, P, S>;
1109
- onBatchComplete?: TableBatchCompleteFn<T, C, R, D, P, S>;
1110
- onBatch?: never;
1111
- };
1112
- /** Batch processing: onBatch processes all records at once */
1113
- type DefineTableWithOnBatch<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {
1114
- onBatch: TableBatchFn<T, C, D, P, S>;
1047
+ /**
1048
+ * Options for defineTable.
1049
+ * `onRecord` and `onRecordBatch` are mutually exclusive. Both are optional (table-only mode).
1050
+ */
1051
+ type DefineTableOptions<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & ({
1052
+ /**
1053
+ * Per-record stream handler. Called once per record in the batch.
1054
+ * Runtime handles partial batch failure reporting automatically.
1055
+ * Records are processed with configurable `concurrency` (default: 1 — sequential).
1056
+ */
1057
+ onRecord?: TableRecordFn<T, C>;
1058
+ onRecordBatch?: never;
1059
+ } | {
1060
+ /**
1061
+ * Batch stream handler. Called once with all records in the batch.
1062
+ * Return `{ failures: string[] }` with sequence numbers for partial batch failure.
1063
+ */
1064
+ onRecordBatch?: TableBatchFn<T, C>;
1115
1065
  onRecord?: never;
1116
- onBatchComplete?: never;
1117
- };
1118
- /** Resource-only: no handler, just creates the table */
1119
- type DefineTableResourceOnly<T = Record<string, unknown>, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined> = DefineTableBase<T, C, D, P, S> & {
1066
+ } | {
1120
1067
  onRecord?: never;
1121
- onBatch?: never;
1122
- onBatchComplete?: never;
1123
- };
1124
- type DefineTableOptions<T = Record<string, unknown>, C = undefined, R = void, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined> = DefineTableWithOnRecord<T, C, R, D, P, S> | DefineTableWithOnBatch<T, C, D, P, S> | DefineTableResourceOnly<T, C, D, P, S>;
1068
+ onRecordBatch?: never;
1069
+ });
1125
1070
  /**
1126
1071
  * Internal handler object created by defineTable
1127
1072
  * @internal
@@ -1137,8 +1082,7 @@ type TableHandler<T = Record<string, unknown>, C = any> = {
1137
1082
  readonly config?: Record<string, unknown>;
1138
1083
  readonly static?: string[];
1139
1084
  readonly onRecord?: (...args: any[]) => any;
1140
- readonly onBatchComplete?: (...args: any[]) => any;
1141
- readonly onBatch?: (...args: any[]) => any;
1085
+ readonly onRecordBatch?: (...args: any[]) => any;
1142
1086
  };
1143
1087
  /**
1144
1088
  * Define a DynamoDB table with optional stream handler (single-table design).
@@ -1148,25 +1092,32 @@ type TableHandler<T = Record<string, unknown>, C = any> = {
1148
1092
  *
1149
1093
  * @example Table with stream handler
1150
1094
  * ```typescript
1151
- * type OrderData = { amount: number; status: string };
1152
- *
1153
- * export const orders = defineTable<OrderData>({
1154
- * streamView: "NEW_AND_OLD_IMAGES",
1095
+ * export const orders = defineTable<OrderData>()({
1155
1096
  * batchSize: 10,
1156
- * onRecord: async ({ record }) => {
1097
+ * concurrency: 5,
1098
+ * setup: ({ table }) => ({ table }),
1099
+ * onRecord: async ({ record, batch, table }) => {
1157
1100
  * if (record.eventName === "INSERT") {
1158
- * console.log("New order:", record.new?.data.amount);
1101
+ * console.log("New order:", record.new?.data);
1159
1102
  * }
1160
1103
  * }
1161
1104
  * });
1162
1105
  * ```
1163
1106
  *
1107
+ * @example Table with runtime validation
1108
+ * ```typescript
1109
+ * export const orders = defineTable<OrderData>()({
1110
+ * schema: (input) => OrderSchema.parse(input),
1111
+ * onRecord: async ({ record }) => { ... }
1112
+ * });
1113
+ * ```
1114
+ *
1164
1115
  * @example Table only (no Lambda)
1165
1116
  * ```typescript
1166
- * export const users = defineTable({});
1117
+ * export const users = defineTable()({});
1167
1118
  * ```
1168
1119
  */
1169
- declare const defineTable: <T = Record<string, unknown>, C = undefined, R = void, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined>(options: DefineTableOptions<T, C, R, D, P, S>) => TableHandler<T, C>;
1120
+ declare const defineTable: <T = Record<string, unknown>>() => <C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined>(options: DefineTableOptions<T, C, D, P, S>) => TableHandler<T, C>;
1170
1121
 
1171
1122
  /**
1172
1123
  * Configuration for deploying an SSR framework (Nuxt, Astro, etc.)
@@ -1223,94 +1174,7 @@ type AppHandler = {
1223
1174
  * });
1224
1175
  * ```
1225
1176
  */
1226
- declare const defineApp: (options: AppConfig) => AppHandler;
1227
-
1228
- type AuthConfig<_T = undefined> = {
1229
- /** Path to redirect unauthenticated users to (used by static sites). */
1230
- loginPath: string;
1231
- /** Paths that don't require authentication. Supports trailing `*` wildcard. */
1232
- public?: string[];
1233
- /** Default session lifetime (default: "7d"). Accepts seconds or duration string. */
1234
- expiresIn?: Duration;
1235
- };
1236
- /**
1237
- * Branded auth object returned by `defineAuth()`.
1238
- * Pass to `defineApi({ auth })` and `defineStaticSite({ auth })`.
1239
- */
1240
- type Auth<T = undefined> = AuthConfig<T> & {
1241
- readonly __brand: "effortless-auth";
1242
- /** @internal phantom type marker for session data */
1243
- readonly __session?: T;
1244
- };
1245
- /** API token authentication strategy. Verifies tokens from HTTP headers (e.g. Authorization: Bearer). */
1246
- type ApiTokenStrategy<T, D = undefined> = {
1247
- /** HTTP header to read the token from. Default: "authorization" (strips "Bearer " prefix). */
1248
- header?: string;
1249
- /** Verify the token value and return session data, or null if invalid. */
1250
- verify: [D] extends [undefined] ? (value: string) => T | null | Promise<T | null> : (value: string, ctx: {
1251
- deps: D;
1252
- }) => T | null | Promise<T | null>;
1253
- /** Cache verified token results for this duration. Avoids calling verify on every request. */
1254
- cacheTtl?: Duration;
1255
- };
1256
- /**
1257
- * Define authentication for API handlers and static sites.
1258
- *
1259
- * Session-based auth uses HMAC-signed cookies (auto-managed by the framework).
1260
- *
1261
- * - Lambda@Edge middleware verifies cookie signatures for static sites
1262
- * - API handler gets `auth.createSession()` / `auth.clearSession()` / `auth.session` helpers
1263
- * - HMAC secret is auto-generated and stored in SSM Parameter Store
1264
- *
1265
- * @typeParam T - Session data type. When provided, `createSession(data)` requires typed payload
1266
- * and `auth.session` is typed as `T` in handler args.
1267
- *
1268
- * @example
1269
- * ```typescript
1270
- * type Session = { userId: string; role: "admin" | "user" };
1271
- *
1272
- * const auth = defineAuth<Session>({
1273
- * loginPath: '/login',
1274
- * public: ['/login', '/api/login'],
1275
- * expiresIn: '7d',
1276
- * })
1277
- *
1278
- * export const api = defineApi({ auth, ... })
1279
- * export const webapp = defineStaticSite({ auth, ... })
1280
- * ```
1281
- */
1282
- declare const defineAuth: <T = undefined>(options: AuthConfig<T>) => Auth<T>;
1283
- /** Options for creating a session */
1284
- type SessionOptions = {
1285
- expiresIn?: Duration;
1286
- };
1287
- /** Session response with Set-Cookie header */
1288
- type SessionResponse = {
1289
- status: 200;
1290
- body: {
1291
- ok: true;
1292
- };
1293
- headers: Record<string, string>;
1294
- };
1295
- /**
1296
- * Auth helpers injected into API handler callback args when `auth` is configured.
1297
- * @typeParam T - Session data type (undefined = no custom data)
1298
- */
1299
- type AuthHelpers<T = undefined> = {
1300
- clearSession(): {
1301
- status: 200;
1302
- body: {
1303
- ok: true;
1304
- };
1305
- headers: Record<string, string>;
1306
- };
1307
- /** The current session data (from cookie or API token). Undefined if no valid session. */
1308
- session: T extends undefined ? undefined : T | undefined;
1309
- } & ([T] extends [undefined] ? {
1310
- createSession(options?: SessionOptions): SessionResponse;
1311
- } : {
1312
- createSession(data: T, options?: SessionOptions): SessionResponse;
1313
- });
1177
+ declare const defineApp: () => (options: AppConfig) => AppHandler;
1314
1178
 
1315
1179
  /** Any branded handler that deploys to API Gateway (HttpHandler, ApiHandler, etc.) */
1316
1180
  type AnyRoutableHandler = {
@@ -1371,8 +1235,6 @@ type StaticSiteConfig = {
1371
1235
  errorPage?: string;
1372
1236
  /** Lambda@Edge middleware that runs before serving pages. Use for auth checks, redirects, etc. */
1373
1237
  middleware?: MiddlewareHandler;
1374
- /** Cookie-based authentication. Auto-generates Lambda@Edge middleware that verifies signed cookies. */
1375
- auth?: Auth<any>;
1376
1238
  /** SEO: auto-generate sitemap.xml and robots.txt at deploy time, optionally submit URLs to Google Indexing API */
1377
1239
  seo?: StaticSiteSeo;
1378
1240
  };
@@ -1407,48 +1269,107 @@ type StaticSiteHandler = {
1407
1269
  * });
1408
1270
  * ```
1409
1271
  *
1410
- * @example Protected site with middleware (Lambda@Edge)
1411
- * ```typescript
1412
- * export const admin = defineStaticSite({
1413
- * dir: "admin/dist",
1414
- * middleware: async (request) => {
1415
- * if (!request.cookies.session) {
1416
- * return { redirect: "/login" };
1417
- * }
1418
- * },
1419
- * });
1420
- * ```
1421
1272
  */
1422
- declare const defineStaticSite: (options: StaticSiteConfig) => StaticSiteHandler;
1273
+ declare const defineStaticSite: () => (options: StaticSiteConfig) => StaticSiteHandler;
1423
1274
 
1424
- /** Extract session type T from Auth<T> */
1425
- type SessionOf<A> = A extends Auth<infer T> ? T : undefined;
1426
- /** GET route handler — no schema, no data */
1427
- type ApiGetHandlerFn<C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined, ST extends boolean | undefined = undefined, A extends Auth<any> | undefined = undefined> = (args: {
1428
- req: HttpRequest;
1429
- } & HandlerArgs<C, D, P, S> & ([ST] extends [true] ? {
1430
- stream: ResponseStream;
1431
- } : {}) & ([A] extends [undefined] ? {} : {
1432
- auth: AuthHelpers<SessionOf<A>>;
1433
- })) => Promise<HttpResponse | void> | HttpResponse | void;
1434
- /** POST handler — with typed data from schema */
1435
- type ApiPostHandlerFn<T = undefined, C = undefined, D = undefined, P = undefined, S extends string[] | undefined = undefined, ST extends boolean | undefined = undefined, A extends Auth<any> | undefined = undefined> = (args: {
1275
+ /** Options for creating a session */
1276
+ type SessionOptions = {
1277
+ expiresIn?: Duration;
1278
+ };
1279
+ /** Session response with Set-Cookie header */
1280
+ type SessionResponse = {
1281
+ status: 200;
1282
+ body: {
1283
+ ok: true;
1284
+ };
1285
+ headers: Record<string, string>;
1286
+ };
1287
+ /**
1288
+ * Auth helpers injected into API handler callback args when `auth` is configured.
1289
+ * @typeParam T - Session data type (from `AuthOptions<T>`)
1290
+ */
1291
+ type AuthHelpers<T = unknown> = {
1292
+ /** Create a signed session cookie with typed data. */
1293
+ createSession(data: T, options?: SessionOptions): SessionResponse;
1294
+ /** Clear the session cookie. */
1295
+ clearSession(): {
1296
+ status: 200;
1297
+ body: {
1298
+ ok: true;
1299
+ };
1300
+ headers: Record<string, string>;
1301
+ };
1302
+ /** The current session data (from cookie or API token). Undefined if no valid session. */
1303
+ session: T | undefined;
1304
+ };
1305
+
1306
+ /** Auth config options (user-facing) */
1307
+ type AuthOptions<A = unknown> = {
1308
+ /** HMAC secret for signing session cookies. Use `secret()` or `param()` in config. */
1309
+ secret: string;
1310
+ /** Default session lifetime (default: "7d"). */
1311
+ expiresIn?: Duration;
1312
+ /** Optional API token strategy for external clients. */
1313
+ apiToken?: {
1314
+ /** HTTP header to read the token from. Default: "authorization" (strips "Bearer " prefix). */
1315
+ header?: string;
1316
+ /** Verify the token value and return session data, or null if invalid. */
1317
+ verify: (value: string) => A | null | Promise<A | null>;
1318
+ /** Cache verified token results for this duration. */
1319
+ cacheTtl?: Duration;
1320
+ };
1321
+ };
1322
+ /** Branded auth config — created by `enableAuth<A>()` helper, carries session type A */
1323
+ type ApiAuthConfig<A = unknown> = AuthOptions<A> & {
1324
+ readonly __sessionType: A;
1325
+ };
1326
+ /** Type of the `enableAuth` helper injected into setup args */
1327
+ type EnableAuth = <A = unknown>(options: AuthOptions<A>) => ApiAuthConfig<A>;
1328
+ /** Extract session type A from ctx.auth if present */
1329
+ type ExtractAuth<C> = C extends {
1330
+ auth: ApiAuthConfig<infer A>;
1331
+ } ? A : undefined;
1332
+ /** Property names reserved by the framework — cannot be used in setup return */
1333
+ type ReservedKeys = 'req' | 'input' | 'stream';
1334
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
1335
+ /** Parsed route definition stored at runtime */
1336
+ type RouteEntry = {
1337
+ method: HttpMethod;
1338
+ path: string;
1339
+ onRequest: (...args: any[]) => any;
1340
+ public?: boolean;
1341
+ };
1342
+ /** Spread ctx into route args: Omit auth config, add AuthHelpers if present */
1343
+ type SpreadCtx<C> = ([C] extends [undefined] ? {} : Omit<C & {}, 'auth'>) & ([ExtractAuth<C>] extends [undefined] ? {} : {
1344
+ auth: AuthHelpers<ExtractAuth<C>>;
1345
+ });
1346
+ /** Callback args available inside each route — ctx is spread into args */
1347
+ type RouteArgs<C, ST> = SpreadCtx<C> & {
1436
1348
  req: HttpRequest;
1437
- } & ([T] extends [undefined] ? {} : {
1438
- data: T;
1439
- }) & HandlerArgs<C, D, P, S> & ([ST] extends [true] ? {
1349
+ input: unknown;
1350
+ } & ([ST] extends [true] ? {
1440
1351
  stream: ResponseStream;
1441
- } : {}) & ([A] extends [undefined] ? {} : {
1442
- auth: AuthHelpers<SessionOf<A>>;
1443
- })) => Promise<HttpResponse | void> | HttpResponse | void;
1444
- /** Setup factory — receives deps/config/files when declared */
1445
- type SetupFactory<C, D, P, S extends string[] | undefined = undefined> = (args: ([D] extends [undefined] ? {} : {
1352
+ } : {});
1353
+ /** Route definition with typed args */
1354
+ type RouteDefinition<C, ST> = {
1355
+ path: `${HttpMethod} /${string}`;
1356
+ onRequest: (args: RouteArgs<C, ST>) => Promise<HttpResponse | void> | HttpResponse | void;
1357
+ public?: boolean;
1358
+ };
1359
+ /** Validate that setup return type does not use reserved property names */
1360
+ type ValidateSetupReturn<C> = C & {
1361
+ [K in ReservedKeys]?: never;
1362
+ };
1363
+ /** Setup factory — receives deps/config/files/enableAuth when declared */
1364
+ type SetupFactory<C, D, P, S extends string[] | undefined = undefined> = (args: {
1365
+ enableAuth: EnableAuth;
1366
+ } & ([D] extends [undefined] ? {} : {
1446
1367
  deps: ResolveDeps<D>;
1447
1368
  }) & ([P] extends [undefined] ? {} : {
1448
1369
  config: ResolveConfig<P & {}>;
1449
1370
  }) & ([S] extends [undefined] ? {} : {
1450
1371
  files: StaticFiles;
1451
- })) => C | Promise<C>;
1372
+ })) => ValidateSetupReturn<C> | Promise<ValidateSetupReturn<C>>;
1452
1373
  /** Static config extracted by AST (no runtime callbacks) */
1453
1374
  type ApiConfig = {
1454
1375
  /** Lambda function settings (memory, timeout, permissions, etc.) */
@@ -1458,104 +1379,79 @@ type ApiConfig = {
1458
1379
  /** Enable response streaming. When true, the Lambda Function URL uses RESPONSE_STREAM invoke mode. */
1459
1380
  stream?: boolean;
1460
1381
  };
1461
- /**
1462
- * Options for defining a CQRS-style API endpoint.
1463
- *
1464
- * - `get` routes handle queries (path-based routing, no body)
1465
- * - `post` handles commands (single entry point, discriminated union via `schema`)
1466
- */
1467
- type DefineApiOptions<T = undefined, C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined, ST extends boolean | undefined = undefined, A extends Auth<any> | undefined = undefined> = {
1382
+ type DefineApiOptions<C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnySecretRef> | undefined = undefined, S extends string[] | undefined = undefined, ST extends boolean | undefined = undefined> = {
1468
1383
  /** Lambda function settings (memory, timeout, permissions, etc.) */
1469
1384
  lambda?: LambdaWithPermissions;
1470
1385
  /** Base path prefix for all routes (e.g., "/api") */
1471
1386
  basePath: `/${string}`;
1472
- /** Enable response streaming. When true, routes receive a `stream` arg for SSE. Routes can still return HttpResponse normally. */
1387
+ /** Enable response streaming. When true, routes receive a `stream` arg for SSE. */
1473
1388
  stream?: ST;
1474
- /** Session-based authentication. Injects `auth` helpers (createSession/clearSession/session) into handler args. */
1475
- auth?: A;
1476
- /** API token authentication for external clients (Bearer tokens, API keys). Has access to deps. */
1477
- apiToken?: ApiTokenStrategy<SessionOf<A>, [D] extends [undefined] ? undefined : ResolveDeps<D>>;
1478
- /**
1479
- * Factory function to initialize shared state.
1480
- * Called once on cold start, result is cached and reused across invocations.
1481
- */
1482
- setup?: SetupFactory<C, D, P, S>;
1389
+ /** Factory function to initialize shared state. Called once on cold start. */
1390
+ setup?: SetupFactory<C, NoInfer<D>, NoInfer<P>, NoInfer<S>>;
1483
1391
  /** Dependencies on other handlers (tables, queues, etc.): `deps: () => ({ users })` */
1484
1392
  deps?: () => D & {};
1485
- /** SSM Parameter Store parameters */
1486
- config?: P;
1393
+ /** SSM Parameter Store parameters. Receives `{ defineSecret }` helper. */
1394
+ config?: ConfigFactory<P>;
1487
1395
  /** Static file glob patterns to bundle into the Lambda ZIP */
1488
1396
  static?: S;
1489
- /** Error handler called when schema validation or handler throws */
1397
+ /** Error handler called when a route throws */
1490
1398
  onError?: (args: {
1491
1399
  error: unknown;
1492
1400
  req: HttpRequest;
1493
- } & HandlerArgs<C, D, P, S>) => HttpResponse;
1494
- /** Called after each invocation completes, right before Lambda freezes the process */
1495
- onAfterInvoke?: (args: HandlerArgs<C, D, P, S>) => void | Promise<void>;
1496
- /** GET routesquery handlers keyed by relative path (e.g., "/users/{id}") */
1497
- get?: Record<`/${string}`, ApiGetHandlerFn<C, D, P, S, ST, A>>;
1498
- /**
1499
- * Schema for POST body validation. Use with discriminated unions:
1500
- * ```typescript
1501
- * schema: Action.parse,
1502
- * post: async ({ data }) => { switch (data.action) { ... } }
1503
- * ```
1504
- */
1505
- schema?: (input: unknown) => T;
1506
- /** POST handler — single entry point for commands */
1507
- post?: ApiPostHandlerFn<T, C, D, P, S, ST, A>;
1401
+ } & SpreadCtx<C>) => HttpResponse;
1402
+ /** Called after each invocation completes */
1403
+ onAfterInvoke?: (args: SpreadCtx<C>) => void | Promise<void>;
1404
+ /** Route definitionsplain array of route objects */
1405
+ routes?: RouteDefinition<C, ST>[];
1508
1406
  };
1509
1407
  /** Internal handler object created by defineApi */
1510
- type ApiHandler<T = undefined, C = undefined> = {
1408
+ type ApiHandler<C = undefined> = {
1511
1409
  readonly __brand: "effortless-api";
1512
1410
  readonly __spec: ApiConfig;
1513
- readonly schema?: (input: unknown) => T;
1514
1411
  readonly onError?: (...args: any[]) => any;
1515
1412
  readonly onAfterInvoke?: (...args: any[]) => any;
1516
1413
  readonly setup?: (...args: any[]) => C | Promise<C>;
1517
1414
  readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);
1518
1415
  readonly config?: Record<string, unknown>;
1519
1416
  readonly static?: string[];
1520
- readonly auth?: Auth;
1521
- readonly apiToken?: ApiTokenStrategy<any, any>;
1522
- readonly get?: Record<`/${string}`, (...args: any[]) => any>;
1523
- readonly post?: (...args: any[]) => any;
1417
+ readonly routes?: RouteEntry[];
1524
1418
  };
1525
1419
  /**
1526
- * Define a CQRS-style API with typed GET routes and POST commands.
1420
+ * Define an API with typed routes.
1527
1421
  *
1528
- * GET routes handle queriespath-based routing, no request body.
1529
- * POST handles commands single entry point with discriminated union schema.
1530
- * Deploys as a single Lambda (fat Lambda) with one API Gateway catch-all route.
1422
+ * Setup return is spread into route args all properties are directly accessible.
1423
+ * Reserved names (`req`, `input`, `stream`) cannot be used in setup return.
1424
+ * Auth is configured via an `auth` property in setup return runtime replaces it with `AuthHelpers`.
1531
1425
  *
1532
1426
  * @example
1533
1427
  * ```typescript
1534
1428
  * export default defineApi({
1535
1429
  * basePath: "/api",
1536
- * deps: { users },
1537
- *
1538
- * get: {
1539
- * "/users": async ({ req, deps }) => ({
1540
- * status: 200,
1541
- * body: await deps.users.scan()
1542
- * }),
1543
- * "/users/{id}": async ({ req, deps }) => ({
1544
- * status: 200,
1545
- * body: await deps.users.get(req.params.id)
1546
- * }),
1547
- * },
1548
- *
1549
- * schema: Action.parse,
1550
- * post: async ({ data, deps }) => {
1551
- * switch (data.action) {
1552
- * case "create": return { status: 201, body: await deps.users.put(data) }
1553
- * case "delete": return { status: 200, body: await deps.users.delete(data.id) }
1554
- * }
1555
- * },
1430
+ * deps: () => ({ users }),
1431
+ * setup: ({ deps }) => ({
1432
+ * users: deps.users,
1433
+ * auth: {
1434
+ * schema: unsafeAs<Session>(),
1435
+ * apiToken: {
1436
+ * verify: async (value) => {
1437
+ * const user = await deps.users.query({ pk: value });
1438
+ * return user[0] ? { userId: user[0].sk } : null;
1439
+ * },
1440
+ * },
1441
+ * },
1442
+ * }),
1443
+ * routes: [
1444
+ * {
1445
+ * path: "GET /me",
1446
+ * onRequest: async ({ users, auth }) => ({
1447
+ * status: 200,
1448
+ * body: { user: await users.get(auth.session.userId) },
1449
+ * }),
1450
+ * },
1451
+ * ],
1556
1452
  * })
1557
1453
  * ```
1558
1454
  */
1559
- declare const defineApi: <T = undefined, C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnyParamRef> | undefined = undefined, S extends string[] | undefined = undefined, ST extends boolean | undefined = undefined, A extends Auth<any> | undefined = undefined>(options: DefineApiOptions<T, C, D, P, S, ST, A>) => ApiHandler<T, C>;
1455
+ declare const defineApi: () => <C = undefined, D extends Record<string, AnyDepHandler> | undefined = undefined, P extends Record<string, AnySecretRef> | undefined = undefined, S extends string[] | undefined = undefined, ST extends boolean | undefined = undefined>(options: DefineApiOptions<C, D, P, S, ST>) => ApiHandler<C>;
1560
1456
 
1561
- export { type AnyParamRef, type AnySecretRef, type ApiConfig, type ApiHandler, type ApiTokenStrategy, type AppConfig, type AppHandler, type Auth, type AuthConfig, type AuthHelpers, type BucketClient, type BucketConfig, type BucketEvent, type BucketHandler, type ContentType, type Duration, type EffortlessConfig, type EmailClient, type FailedRecord, type FifoQueueConfig, type FifoQueueHandler, type FifoQueueMessage, type 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, defineAuth, defineBucket, defineConfig, defineFifoQueue, defineMailer, defineStaticSite, defineTable, generateBase64, generateHex, generateUuid, param, result, secret, toSeconds, unsafeAs };
1457
+ export { type AnyParamRef, type AnySecretRef, type ApiAuthConfig, type ApiConfig, type ApiHandler, 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, result, secret, toSeconds, unsafeAs };