diff --git a/supabase/functions/_shared/contracts/parse.ts b/supabase/functions/_shared/contracts/parse.ts index 52bb40e1d..857d2042c 100644 --- a/supabase/functions/_shared/contracts/parse.ts +++ b/supabase/functions/_shared/contracts/parse.ts @@ -51,32 +51,45 @@ export interface ParseOptions { prereadBody?: string; } -export type ParseResult> = - | { +/** + * Resultado de sucesso discriminado pela versão resolvida. + * + * Gera uma união `{ version: "1"; data: v1 } | { version: "2"; data: v2 } | ...` + * a partir de `C["versions"]`, de modo que `version` e `data` permaneçam + * correlacionados em tempo de compilação (cada versão expõe o shape do seu + * próprio schema, sem colapsar tudo no shape da versão default). + * + * Importante: indexamos os schemas diretamente (sem a interseção + * `ContractSchemas & { versions: S }` usada anteriormente). Aquela interseção + * forçava `versions[k]` a virar `ZodTypeAny & ZodObject<...>`, fazendo o checker + * recursar no retorno de `ZodObject.deepPartial()` e falhar com TS2345 + * (15 edge functions de contratos quebravam o `deno check`). + */ +type ParseSuccessByVersion = { + [K in keyof C["versions"] & string]: { ok: true; - version: V; - /** Dados parseados; o tipo casa com o schema da versão resolvida. */ - data: { [K in V]: z.infer }[V]; - /** Headers que a resposta de sucesso deve incluir (versão, deprecation). */ + version: K; + data: z.infer; responseHeaders: Record; - } + }; +}[keyof C["versions"] & string]; + +export type ParseResult = + | ParseSuccessByVersion | { ok: false; response: Response }; /** * Parseia, valida e versiona o body de uma requisição. */ -export async function parseContract< - V extends string, - S extends Record, ->( +export async function parseContract( req: Request, - schemas: ContractSchemas & { versions: S }, + schemas: C, opts: ParseOptions = {}, -): Promise> { +): Promise> { const corsHeaders = opts.corsHeaders ?? {}; // 1. Resolver versão - const supportedVersions = Object.keys(schemas.versions) as V[]; + const supportedVersions = Object.keys(schemas.versions); const versionConfig: VersionConfig = { supported: supportedVersions, default: schemas.defaultVersion, @@ -86,7 +99,7 @@ export async function parseContract< if (!vRes.ok) return { ok: false, response: vRes.response }; const { version, responseHeaders } = vRes.resolved; - const schema = schemas.versions[version as V]; + const schema = schemas.versions[version as keyof C["versions"]]; // 2. Ler body (uma única vez) let rawText: string; @@ -150,10 +163,14 @@ export async function parseContract< }; } + // O TS nao consegue inferir, dentro do corpo generico, que este objeto + // satisfaz a uniao mapeada `ParseSuccessByVersion` (a uniao colapsa para + // `never` sobre um `C` ainda nao resolvido). A validacao real foi feita por + // `resolveContractVersion` + `schema.safeParse`, entao o cast e seguro. return { ok: true, - version: version as V, + version, data: result.data, responseHeaders, - }; + } as ParseResult; }