Skip to content

chore(parser): fix type inference for result types #3293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 7, 2024
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/apigw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import { Envelope } from './envelope.js';
* API Gateway envelope to extract data within body key
*/
export const ApiGatewayEnvelope = {
symbol: 'object' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T> {
return Envelope.parse(APIGatewayProxyEventSchema.parse(data).body, schema);
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>> {
const parsedEnvelope = APIGatewayProxyEventSchema.safeParse(data);
if (!parsedEnvelope.success) {
return {
Expand Down
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/apigwv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import { Envelope } from './envelope.js';
* API Gateway V2 envelope to extract data within body key
*/
export const ApiGatewayV2Envelope = {
symbol: 'object' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T> {
return Envelope.parse(
APIGatewayProxyEventV2Schema.parse(data).body,
schema
);
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>> {
const parsedEnvelope = APIGatewayProxyEventV2Schema.safeParse(data);
if (!parsedEnvelope.success) {
return {
Expand Down
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/cloudwatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Envelope } from './envelope.js';
* Note: The record will be parsed the same way so if model is str
*/
export const CloudWatchEnvelope = {
symbol: 'array' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
const parsedEnvelope = CloudWatchLogsSchema.parse(data);

Expand All @@ -22,7 +23,10 @@ export const CloudWatchEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]> {
const parsedEnvelope = CloudWatchLogsSchema.safeParse(data);

if (!parsedEnvelope.success) {
Expand Down
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/dynamodb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Envelope } from './envelope.js';
* length of the list is the record's amount in the original event.
*/
export const DynamoDBStreamEnvelope = {
symbol: 'array' as const,
parse<T extends ZodSchema>(
data: unknown,
schema: T
Expand All @@ -26,7 +27,10 @@ export const DynamoDBStreamEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, DynamoDBStreamEnvelopeResponse<z.infer<T>>[]> {
const parsedEnvelope = DynamoDBStreamSchema.safeParse(data);

if (!parsedEnvelope.success) {
Expand Down
1 change: 1 addition & 0 deletions packages/parser/src/envelopes/event-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Envelope } from './envelope.js';
* Envelope for EventBridge schema that extracts and parses data from the `detail` key.
*/
export const EventBridgeEnvelope = {
symbol: 'object' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T> {
return Envelope.parse(EventBridgeSchema.parse(data).detail, schema);
},
Expand Down
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/kafka.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Envelope } from './envelope.js';
*/

export const KafkaEnvelope = {
symbol: 'array' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
// manually fetch event source to decide between Msk or SelfManaged
const eventSource = (data as KafkaMskEvent).eventSource;
Expand All @@ -35,7 +36,10 @@ export const KafkaEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]> {
// manually fetch event source to deside between Msk or SelfManaged
const eventSource = (data as KafkaMskEvent).eventSource;

Expand Down
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/kinesis-firehose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Envelope } from './envelope.js';
* https://docs.aws.amazon.com/lambda/latest/dg/services-kinesisfirehose.html
*/
export const KinesisFirehoseEnvelope = {
symbol: 'array' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
const parsedEnvelope = KinesisFirehoseSchema.parse(data);

Expand All @@ -25,7 +26,10 @@ export const KinesisFirehoseEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]> {
const parsedEnvelope = KinesisFirehoseSchema.safeParse(data);

if (!parsedEnvelope.success) {
Expand Down
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/kinesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Envelope } from './envelope.js';
* all items in the list will be parsed as str and not as JSON (and vice versa)
*/
export const KinesisEnvelope = {
symbol: 'array' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
const parsedEnvelope = KinesisDataStreamSchema.parse(data);

Expand All @@ -23,7 +24,10 @@ export const KinesisEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]> {
const parsedEnvelope = KinesisDataStreamSchema.safeParse(data);
if (!parsedEnvelope.success) {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/parser/src/envelopes/lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Envelope } from './envelope.js';
* Lambda function URL envelope to extract data within body key
*/
export const LambdaFunctionUrlEnvelope = {
symbol: 'object' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T> {
const parsedEnvelope = LambdaFunctionUrlSchema.parse(data);

Expand Down
14 changes: 11 additions & 3 deletions packages/parser/src/envelopes/sns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Envelope } from './envelope.js';
* all items in the list will be parsed as str and npt as JSON (and vice versa)
*/
export const SnsEnvelope = {
symbol: 'array' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
const parsedEnvelope = SnsSchema.parse(data);

Expand All @@ -23,7 +24,10 @@ export const SnsEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]> {
const parsedEnvelope = SnsSchema.safeParse(data);

if (!parsedEnvelope.success) {
Expand Down Expand Up @@ -70,7 +74,8 @@ export const SnsEnvelope = {
*
*/
export const SnsSqsEnvelope = {
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T> {
symbol: 'array' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
const parsedEnvelope = SqsSchema.parse(data);

return parsedEnvelope.Records.map((record) => {
Expand All @@ -82,7 +87,10 @@ export const SnsSqsEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]> {
const parsedEnvelope = SqsSchema.safeParse(data);
if (!parsedEnvelope.success) {
return {
Expand Down
6 changes: 5 additions & 1 deletion packages/parser/src/envelopes/sqs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Envelope } from './envelope.js';
* all items in the list will be parsed as str and npt as JSON (and vice versa)
*/
export const SqsEnvelope = {
symbol: 'array' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
const parsedEnvelope = SqsSchema.parse(data);

Expand All @@ -22,7 +23,10 @@ export const SqsEnvelope = {
});
},

safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]> {
const parsedEnvelope = SqsSchema.safeParse(data);
if (!parsedEnvelope.success) {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/parser/src/envelopes/vpc-lattice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Envelope } from './envelope.js';
*/

export const VpcLatticeEnvelope = {
symbol: 'object' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T> {
const parsedEnvelope = VpcLatticeSchema.parse(data);

Expand Down
1 change: 1 addition & 0 deletions packages/parser/src/envelopes/vpc-latticev2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Envelope } from './envelope.js';
* Amazon VPC Lattice envelope to extract data within body key
*/
export const VpcLatticeV2Envelope = {
symbol: 'object' as const,
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T> {
const parsedEnvelope = VpcLatticeV2Schema.parse(data);

Expand Down
2 changes: 1 addition & 1 deletion packages/parser/src/middleware/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import type { ParserOptions, ParserOutput } from '../types/parser.js';
*/
const parser = <
TSchema extends ZodType,
TEnvelope extends Envelope = undefined,
TEnvelope extends Envelope,
TSafeParse extends boolean = false,
>(
options: ParserOptions<TSchema, TEnvelope, TSafeParse>
Expand Down
67 changes: 22 additions & 45 deletions packages/parser/src/types/envelope.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,34 @@
import type { ZodSchema, z } from 'zod';
import type {
ApiGatewayEnvelope,
ApiGatewayV2Envelope,
CloudWatchEnvelope,
DynamoDBStreamEnvelope,
EventBridgeEnvelope,
KafkaEnvelope,
KinesisEnvelope,
KinesisFirehoseEnvelope,
LambdaFunctionUrlEnvelope,
SnsEnvelope,
SnsSqsEnvelope,
SqsEnvelope,
VpcLatticeEnvelope,
VpcLatticeV2Envelope,
} from '../envelopes/index.js';
import type { ParsedResult } from './parser.js';

type DynamoDBStreamEnvelopeResponse<Schema extends ZodSchema> = {
NewImage: z.infer<Schema>;
OldImage: z.infer<Schema>;
};

type Envelope =
| typeof ApiGatewayEnvelope
| typeof ApiGatewayV2Envelope
| typeof CloudWatchEnvelope
| typeof DynamoDBStreamEnvelope
| typeof EventBridgeEnvelope
| typeof KafkaEnvelope
| typeof KinesisEnvelope
| typeof KinesisFirehoseEnvelope
| typeof LambdaFunctionUrlEnvelope
| typeof SnsEnvelope
| typeof SnsSqsEnvelope
| typeof SqsEnvelope
| typeof VpcLatticeEnvelope
| typeof VpcLatticeV2Envelope
| undefined;
interface ArrayEnvelope {
symbol: 'array';
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[];
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>[]>;
}

/**
* Envelopes that return an array, needed to narrow down the return type of the parser
*/
type EnvelopeArrayReturnType =
| typeof CloudWatchEnvelope
| typeof DynamoDBStreamEnvelope
| typeof KafkaEnvelope
| typeof KinesisEnvelope
| typeof KinesisFirehoseEnvelope
| typeof SnsEnvelope
| typeof SqsEnvelope;
interface ObjectEnvelope {
symbol: 'object';
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>;
safeParse<T extends ZodSchema>(
data: unknown,
schema: T
): ParsedResult<unknown, z.infer<T>>;
}

type Envelope = ArrayEnvelope | ObjectEnvelope | undefined;

export type {
Envelope,
ArrayEnvelope,
DynamoDBStreamEnvelopeResponse,
EnvelopeArrayReturnType,
Envelope,
ObjectEnvelope,
};
20 changes: 8 additions & 12 deletions packages/parser/src/types/parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ZodError, ZodSchema, z } from 'zod';
import type { Envelope, EnvelopeArrayReturnType } from './envelope.js';
import type { ArrayEnvelope, Envelope } from './envelope.js';

/**
* Options for the parser used in middy middleware and decorator
Expand Down Expand Up @@ -46,7 +46,7 @@ type ZodInferredResult<
TEnvelope extends Envelope,
> = undefined extends TEnvelope
? z.infer<TSchema>
: TEnvelope extends EnvelopeArrayReturnType
: TEnvelope extends ArrayEnvelope
? z.infer<TSchema>[]
: z.infer<TSchema>;

Expand All @@ -55,9 +55,9 @@ type ZodInferredSafeParseResult<
TEnvelope extends Envelope,
> = undefined extends TEnvelope
? ParsedResult<unknown, z.infer<TSchema>>
: TEnvelope extends EnvelopeArrayReturnType
? ParsedResult<unknown, z.infer<TSchema>>
: ParsedResult<unknown, z.infer<TSchema>[]>;
: TEnvelope extends ArrayEnvelope
? ParsedResult<unknown, z.infer<TSchema>[]>
: ParsedResult<unknown, z.infer<TSchema>>;

/**
* The output of the parser function, can be either schema inferred type or a ParsedResult
Expand All @@ -66,13 +66,9 @@ type ParserOutput<
TSchema extends ZodSchema,
TEnvelope extends Envelope,
TSafeParse = false,
> = undefined extends TSafeParse
? ZodInferredResult<TSchema, TEnvelope>
: TSafeParse extends true
? ZodInferredSafeParseResult<TSchema, TEnvelope>
: TSafeParse extends false
? ZodInferredResult<TSchema, TEnvelope>
: never;
> = TSafeParse extends true
? ZodInferredSafeParseResult<TSchema, TEnvelope>
: ZodInferredResult<TSchema, TEnvelope>;

export type {
ParserOptions,
Expand Down
Loading