diff --git a/packages/parser/src/envelopes/apigw.ts b/packages/parser/src/envelopes/apigw.ts index a9532f4bc1..86f716b234 100644 --- a/packages/parser/src/envelopes/apigw.ts +++ b/packages/parser/src/envelopes/apigw.ts @@ -2,17 +2,25 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { APIGatewayProxyEventSchema } from '../schemas/apigw.js'; import type { ParsedResult } from '../types/parser.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * API Gateway envelope to extract data within body key */ export const ApiGatewayEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { return Envelope.parse(APIGatewayProxyEventSchema.parse(data).body, schema); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult> { const parsedEnvelope = APIGatewayProxyEventSchema.safeParse(data); if (!parsedEnvelope.success) { return { diff --git a/packages/parser/src/envelopes/apigwv2.ts b/packages/parser/src/envelopes/apigwv2.ts index db69794649..45c80f0727 100644 --- a/packages/parser/src/envelopes/apigwv2.ts +++ b/packages/parser/src/envelopes/apigwv2.ts @@ -2,12 +2,17 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { APIGatewayProxyEventV2Schema } from '../schemas/apigwv2.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * API Gateway V2 envelope to extract data within body key */ export const ApiGatewayV2Envelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { return Envelope.parse( APIGatewayProxyEventV2Schema.parse(data).body, @@ -15,7 +20,10 @@ export const ApiGatewayV2Envelope = { ); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult> { const parsedEnvelope = APIGatewayProxyEventV2Schema.safeParse(data); if (!parsedEnvelope.success) { return { diff --git a/packages/parser/src/envelopes/cloudwatch.ts b/packages/parser/src/envelopes/cloudwatch.ts index cb3112f4ff..4bdde3c9a4 100644 --- a/packages/parser/src/envelopes/cloudwatch.ts +++ b/packages/parser/src/envelopes/cloudwatch.ts @@ -2,7 +2,7 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { CloudWatchLogsSchema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * CloudWatch Envelope to extract a List of log records. @@ -14,6 +14,11 @@ import { Envelope } from './envelope.js'; * Note: The record will be parsed the same way so if model is str */ export const CloudWatchEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, parse(data: unknown, schema: T): z.infer[] { const parsedEnvelope = CloudWatchLogsSchema.parse(data); @@ -22,7 +27,10 @@ export const CloudWatchEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult[]> { const parsedEnvelope = CloudWatchLogsSchema.safeParse(data); if (!parsedEnvelope.success) { diff --git a/packages/parser/src/envelopes/dynamodb.ts b/packages/parser/src/envelopes/dynamodb.ts index ab9cc18c8c..1a499d216f 100644 --- a/packages/parser/src/envelopes/dynamodb.ts +++ b/packages/parser/src/envelopes/dynamodb.ts @@ -3,7 +3,7 @@ import { ParseError } from '../errors.js'; import { DynamoDBStreamSchema } from '../schemas/index.js'; import type { DynamoDBStreamEnvelopeResponse } from '../types/envelope.js'; import type { ParsedResult, ParsedResultError } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * DynamoDB Stream Envelope to extract data within NewImage/OldImage @@ -12,6 +12,11 @@ import { Envelope } from './envelope.js'; * length of the list is the record's amount in the original event. */ export const DynamoDBStreamEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, parse( data: unknown, schema: T @@ -26,7 +31,10 @@ export const DynamoDBStreamEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult>[]> { const parsedEnvelope = DynamoDBStreamSchema.safeParse(data); if (!parsedEnvelope.success) { diff --git a/packages/parser/src/envelopes/envelope.ts b/packages/parser/src/envelopes/envelope.ts index 79b7de7651..df802b65af 100644 --- a/packages/parser/src/envelopes/envelope.ts +++ b/packages/parser/src/envelopes/envelope.ts @@ -2,7 +2,7 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import type { ParsedResult } from '../types/parser.js'; -export const Envelope = { +const Envelope = { /** * Abstract function to parse the content of the envelope using provided schema. * Both inputs are provided as unknown by the user. @@ -67,3 +67,11 @@ export const Envelope = { } }, }; + +/** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ +const envelopeDiscriminator = Symbol.for('returnType'); + +export { Envelope, envelopeDiscriminator }; diff --git a/packages/parser/src/envelopes/event-bridge.ts b/packages/parser/src/envelopes/event-bridge.ts index 7c44bb9a56..84f87f10db 100644 --- a/packages/parser/src/envelopes/event-bridge.ts +++ b/packages/parser/src/envelopes/event-bridge.ts @@ -2,12 +2,17 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { EventBridgeSchema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * Envelope for EventBridge schema that extracts and parses data from the `detail` key. */ export const EventBridgeEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { return Envelope.parse(EventBridgeSchema.parse(data).detail, schema); }, diff --git a/packages/parser/src/envelopes/kafka.ts b/packages/parser/src/envelopes/kafka.ts index 4442834aff..f0f0e36844 100644 --- a/packages/parser/src/envelopes/kafka.ts +++ b/packages/parser/src/envelopes/kafka.ts @@ -5,7 +5,7 @@ import { KafkaSelfManagedEventSchema, } from '../schemas/kafka.js'; import type { KafkaMskEvent, ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * Kafka event envelope to extract data within body key @@ -17,6 +17,11 @@ import { Envelope } from './envelope.js'; */ export const KafkaEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, parse(data: unknown, schema: T): z.infer[] { // manually fetch event source to decide between Msk or SelfManaged const eventSource = (data as KafkaMskEvent).eventSource; @@ -35,7 +40,10 @@ export const KafkaEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult[]> { // manually fetch event source to deside between Msk or SelfManaged const eventSource = (data as KafkaMskEvent).eventSource; diff --git a/packages/parser/src/envelopes/kinesis-firehose.ts b/packages/parser/src/envelopes/kinesis-firehose.ts index dd543f82b3..6f03bcd331 100644 --- a/packages/parser/src/envelopes/kinesis-firehose.ts +++ b/packages/parser/src/envelopes/kinesis-firehose.ts @@ -2,7 +2,7 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { KinesisFirehoseSchema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * Kinesis Firehose Envelope to extract array of Records @@ -17,6 +17,11 @@ import { Envelope } from './envelope.js'; * https://docs.aws.amazon.com/lambda/latest/dg/services-kinesisfirehose.html */ export const KinesisFirehoseEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, parse(data: unknown, schema: T): z.infer[] { const parsedEnvelope = KinesisFirehoseSchema.parse(data); @@ -25,7 +30,10 @@ export const KinesisFirehoseEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult[]> { const parsedEnvelope = KinesisFirehoseSchema.safeParse(data); if (!parsedEnvelope.success) { diff --git a/packages/parser/src/envelopes/kinesis.ts b/packages/parser/src/envelopes/kinesis.ts index 480addb756..248b753acf 100644 --- a/packages/parser/src/envelopes/kinesis.ts +++ b/packages/parser/src/envelopes/kinesis.ts @@ -2,7 +2,7 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { KinesisDataStreamSchema } from '../schemas/kinesis.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * Kinesis Data Stream Envelope to extract array of Records @@ -15,6 +15,11 @@ 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 = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, parse(data: unknown, schema: T): z.infer[] { const parsedEnvelope = KinesisDataStreamSchema.parse(data); @@ -23,7 +28,10 @@ export const KinesisEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult[]> { const parsedEnvelope = KinesisDataStreamSchema.safeParse(data); if (!parsedEnvelope.success) { return { diff --git a/packages/parser/src/envelopes/lambda.ts b/packages/parser/src/envelopes/lambda.ts index 69bf1447c5..ddee609064 100644 --- a/packages/parser/src/envelopes/lambda.ts +++ b/packages/parser/src/envelopes/lambda.ts @@ -2,12 +2,17 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { LambdaFunctionUrlSchema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * Lambda function URL envelope to extract data within body key */ export const LambdaFunctionUrlEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { const parsedEnvelope = LambdaFunctionUrlSchema.parse(data); diff --git a/packages/parser/src/envelopes/sns.ts b/packages/parser/src/envelopes/sns.ts index bf3f7aedf0..183cc84470 100644 --- a/packages/parser/src/envelopes/sns.ts +++ b/packages/parser/src/envelopes/sns.ts @@ -3,7 +3,7 @@ import { ParseError } from '../errors.js'; import { SnsSchema, SnsSqsNotificationSchema } from '../schemas/sns.js'; import { SqsSchema } from '../schemas/sqs.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * SNS Envelope to extract array of Records @@ -15,6 +15,11 @@ 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 = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, parse(data: unknown, schema: T): z.infer[] { const parsedEnvelope = SnsSchema.parse(data); @@ -23,7 +28,10 @@ export const SnsEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult[]> { const parsedEnvelope = SnsSchema.safeParse(data); if (!parsedEnvelope.success) { @@ -70,7 +78,12 @@ export const SnsEnvelope = { * */ export const SnsSqsEnvelope = { - parse(data: unknown, schema: T): z.infer { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, + parse(data: unknown, schema: T): z.infer[] { const parsedEnvelope = SqsSchema.parse(data); return parsedEnvelope.Records.map((record) => { @@ -82,7 +95,10 @@ export const SnsSqsEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult[]> { const parsedEnvelope = SqsSchema.safeParse(data); if (!parsedEnvelope.success) { return { diff --git a/packages/parser/src/envelopes/sqs.ts b/packages/parser/src/envelopes/sqs.ts index 95de353b46..c7bf36a9d1 100644 --- a/packages/parser/src/envelopes/sqs.ts +++ b/packages/parser/src/envelopes/sqs.ts @@ -2,7 +2,7 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { SqsSchema } from '../schemas/sqs.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * SQS Envelope to extract array of Records @@ -14,6 +14,11 @@ 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 = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'array' as const, parse(data: unknown, schema: T): z.infer[] { const parsedEnvelope = SqsSchema.parse(data); @@ -22,7 +27,10 @@ export const SqsEnvelope = { }); }, - safeParse(data: unknown, schema: T): ParsedResult { + safeParse( + data: unknown, + schema: T + ): ParsedResult[]> { const parsedEnvelope = SqsSchema.safeParse(data); if (!parsedEnvelope.success) { return { diff --git a/packages/parser/src/envelopes/vpc-lattice.ts b/packages/parser/src/envelopes/vpc-lattice.ts index 8c2124281b..c4665dca73 100644 --- a/packages/parser/src/envelopes/vpc-lattice.ts +++ b/packages/parser/src/envelopes/vpc-lattice.ts @@ -2,13 +2,18 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { VpcLatticeSchema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * Amazon VPC Lattice envelope to extract data within body key */ export const VpcLatticeEnvelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { const parsedEnvelope = VpcLatticeSchema.parse(data); diff --git a/packages/parser/src/envelopes/vpc-latticev2.ts b/packages/parser/src/envelopes/vpc-latticev2.ts index a184b1c6fa..caeedf650a 100644 --- a/packages/parser/src/envelopes/vpc-latticev2.ts +++ b/packages/parser/src/envelopes/vpc-latticev2.ts @@ -2,12 +2,17 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { VpcLatticeV2Schema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope } from './envelope.js'; +import { Envelope, envelopeDiscriminator } from './envelope.js'; /** * Amazon VPC Lattice envelope to extract data within body key */ export const VpcLatticeV2Envelope = { + /** + * This is a discriminator to differentiate whether an envelope returns an array or an object + * @hidden + */ + [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { const parsedEnvelope = VpcLatticeV2Schema.parse(data); diff --git a/packages/parser/src/middleware/parser.ts b/packages/parser/src/middleware/parser.ts index aa889125b5..3a31e0992f 100644 --- a/packages/parser/src/middleware/parser.ts +++ b/packages/parser/src/middleware/parser.ts @@ -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 diff --git a/packages/parser/src/types/envelope.ts b/packages/parser/src/types/envelope.ts index 8be125d144..6d8e16ede3 100644 --- a/packages/parser/src/types/envelope.ts +++ b/packages/parser/src/types/envelope.ts @@ -1,57 +1,35 @@ 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 { envelopeDiscriminator } from '../envelopes/envelope.js'; +import type { ParsedResult } from './parser.js'; type DynamoDBStreamEnvelopeResponse = { NewImage: z.infer; OldImage: z.infer; }; -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 { + [envelopeDiscriminator]: 'array'; + parse(data: unknown, schema: T): z.infer[]; + safeParse( + data: unknown, + schema: T + ): ParsedResult[]>; +} -/** - * 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 { + [envelopeDiscriminator]: 'object'; + parse(data: unknown, schema: T): z.infer; + safeParse( + data: unknown, + schema: T + ): ParsedResult>; +} + +type Envelope = ArrayEnvelope | ObjectEnvelope | undefined; export type { - Envelope, + ArrayEnvelope, DynamoDBStreamEnvelopeResponse, - EnvelopeArrayReturnType, + Envelope, + ObjectEnvelope, }; diff --git a/packages/parser/src/types/parser.ts b/packages/parser/src/types/parser.ts index c7fe891c6a..16178fa958 100644 --- a/packages/parser/src/types/parser.ts +++ b/packages/parser/src/types/parser.ts @@ -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 @@ -46,7 +46,7 @@ type ZodInferredResult< TEnvelope extends Envelope, > = undefined extends TEnvelope ? z.infer - : TEnvelope extends EnvelopeArrayReturnType + : TEnvelope extends ArrayEnvelope ? z.infer[] : z.infer; @@ -55,9 +55,9 @@ type ZodInferredSafeParseResult< TEnvelope extends Envelope, > = undefined extends TEnvelope ? ParsedResult> - : TEnvelope extends EnvelopeArrayReturnType - ? ParsedResult> - : ParsedResult[]>; + : TEnvelope extends ArrayEnvelope + ? ParsedResult[]> + : ParsedResult>; /** * The output of the parser function, can be either schema inferred type or a ParsedResult @@ -66,13 +66,9 @@ type ParserOutput< TSchema extends ZodSchema, TEnvelope extends Envelope, TSafeParse = false, -> = undefined extends TSafeParse - ? ZodInferredResult - : TSafeParse extends true - ? ZodInferredSafeParseResult - : TSafeParse extends false - ? ZodInferredResult - : never; +> = TSafeParse extends true + ? ZodInferredSafeParseResult + : ZodInferredResult; export type { ParserOptions, diff --git a/packages/parser/tests/unit/types.test.ts b/packages/parser/tests/unit/types.test.ts new file mode 100644 index 0000000000..344a3afd0d --- /dev/null +++ b/packages/parser/tests/unit/types.test.ts @@ -0,0 +1,77 @@ +import { z } from 'zod'; +import { + ApiGatewayEnvelope, + ApiGatewayV2Envelope, + CloudWatchEnvelope, + DynamoDBStreamEnvelope, + EventBridgeEnvelope, + KafkaEnvelope, + KinesisEnvelope, + KinesisFirehoseEnvelope, + LambdaFunctionUrlEnvelope, + SnsEnvelope, + SnsSqsEnvelope, + SqsEnvelope, + VpcLatticeEnvelope, + VpcLatticeV2Envelope, +} from '../../src/envelopes/index.js'; +import type { ParserOutput } from '../../src/types/parser.js'; + +describe('Types ', () => { + const userSchema = z.object({ + name: z.string(), + age: z.number(), + }); + + it.each([ + { envelope: ApiGatewayEnvelope, name: 'ApiGateway' }, + { envelope: ApiGatewayV2Envelope, name: 'ApiGatewayV2' }, + { envelope: EventBridgeEnvelope, name: 'EventBridge' }, + { envelope: LambdaFunctionUrlEnvelope, name: 'LambdaFunctionUrl' }, + { envelope: VpcLatticeEnvelope, name: 'VpcLattice' }, + { envelope: VpcLatticeV2Envelope, name: 'VpcLatticeV2' }, + ])('should infer object for $name envelope', (testCase) => { + type Result = ParserOutput; + // Define the expected type + + // This will fail TypeScript compilation if Result is is an array + const result = { name: 'John', age: 30 } satisfies Result; + + // Runtime checks to ensure it's an array with single element + expect(Array.isArray(result)).toBe(false); + expect(result).toEqual({ name: 'John', age: 30 }); + + // Type assertion to ensure it's specifically User[] + type AssertIsUserArray = T extends z.infer[] + ? true + : false; + type Test = AssertIsUserArray; + }); + + it.each([ + { envelope: CloudWatchEnvelope, name: 'CloudWatch' }, + { envelope: DynamoDBStreamEnvelope, name: 'DynamoDBStream' }, + { envelope: KafkaEnvelope, name: 'Kafka' }, + { envelope: KinesisFirehoseEnvelope, name: 'KinesisFirehose' }, + { envelope: KinesisEnvelope, name: 'Kinesis' }, + { envelope: SqsEnvelope, name: 'Sqs' }, + { envelope: SnsEnvelope, name: 'Sns' }, + { envelope: SnsSqsEnvelope, name: 'SnsSqs' }, + ])('should infer array type with $name envelope', (testCase) => { + // Define the expected type + type Result = ParserOutput; + + // This will fail TypeScript compilation if Result is is an array + const result = [{ name: 'John', age: 30 }] satisfies Result; + + // Runtime checks to ensure it's an array with single element + expect(Array.isArray(result)).toBe(true); + expect(result).toEqual([{ name: 'John', age: 30 }]); + + // Type assertion to ensure it's specifically User[] + type AssertIsUserArray = T extends z.infer[] + ? true + : false; + type Test = AssertIsUserArray; + }); +});