feat(sdk): expose live event stream (#34098)
This commit is contained in:
parent
2c02f8bace
commit
42e6b7db32
19 changed files with 395 additions and 56 deletions
|
|
@ -230,7 +230,12 @@ export function emitEffectImported(
|
|||
}
|
||||
}
|
||||
|
||||
export function emitPromise(contract: Contract): Output {
|
||||
export function emitPromise(
|
||||
contract: Contract,
|
||||
options?: {
|
||||
readonly outputTypes?: Readonly<Record<string, { readonly name: string; readonly import: string }>>
|
||||
},
|
||||
): Output {
|
||||
const groups = contract.groups
|
||||
for (const group of groups) {
|
||||
for (const endpoint of group.endpoints) assertPromiseEndpoint(endpoint)
|
||||
|
|
@ -238,7 +243,7 @@ export function emitPromise(contract: Contract): Output {
|
|||
return {
|
||||
operations: operations(groups),
|
||||
files: [
|
||||
{ path: "types.ts", content: renderPromiseTypes(groups) },
|
||||
{ path: "types.ts", content: renderPromiseTypes(groups, options?.outputTypes) },
|
||||
{
|
||||
path: "client-error.ts",
|
||||
content: `export type ClientErrorReason = "Transport" | "UnexpectedStatus" | "UnsupportedContentType" | "MalformedResponse"\n\nexport class ClientError extends Error {\n override readonly name = "ClientError"\n constructor(readonly reason: ClientErrorReason, options?: ErrorOptions) {\n super(reason, options)\n }\n}\n`,
|
||||
|
|
@ -408,7 +413,10 @@ function renderImportedProjection(groups: ReadonlyArray<Group>, endpoints: Reado
|
|||
return { imports: [...new Set(imports)], source }
|
||||
}
|
||||
|
||||
function renderPromiseTypes(groups: ReadonlyArray<Group>) {
|
||||
function renderPromiseTypes(
|
||||
groups: ReadonlyArray<Group>,
|
||||
outputTypes?: Readonly<Record<string, { readonly name: string; readonly import: string }>>,
|
||||
) {
|
||||
const types = new Map<SchemaAST.AST, string>()
|
||||
const typeOf = (schema: Schema.Top, decoded = false) => {
|
||||
const projected = decoded ? Schema.toType(schema) : Schema.toEncoded(schema)
|
||||
|
|
@ -453,13 +461,15 @@ function renderPromiseTypes(groups: ReadonlyArray<Group>) {
|
|||
})
|
||||
.join("; ")
|
||||
const successSchema = endpoint.successes[0]
|
||||
const success = typeOf(
|
||||
isStreamSchema(successSchema) && successSchema._tag === "StreamSse"
|
||||
? successSchema.sseMode === "data"
|
||||
? streamEncodedDataSchema(successSchema)
|
||||
: successSchema.events
|
||||
: successSchema,
|
||||
)
|
||||
const success =
|
||||
outputTypes?.[`${group.identifier}.${endpoint.operation.name}`]?.name ??
|
||||
typeOf(
|
||||
isStreamSchema(successSchema) && successSchema._tag === "StreamSse"
|
||||
? successSchema.sseMode === "data"
|
||||
? streamEncodedDataSchema(successSchema)
|
||||
: successSchema.events
|
||||
: successSchema,
|
||||
)
|
||||
return [
|
||||
...(endpoint.operation.inputMode === "none" ? [] : [`export type ${prefix}Input = { ${input} }`]),
|
||||
`export type ${prefix}Output = ${endpoint.unwrapData ? `(${success})["data"]` : success}`,
|
||||
|
|
@ -470,7 +480,8 @@ function renderPromiseTypes(groups: ReadonlyArray<Group>) {
|
|||
const json = operations.includes("JsonValue")
|
||||
? "export type JsonValue = null | boolean | number | string | ReadonlyArray<JsonValue> | { readonly [key: string]: JsonValue }"
|
||||
: ""
|
||||
return [json, ...errorTypes, operations].filter(Boolean).join("\n\n")
|
||||
const imports = [...new Set(Object.values(outputTypes ?? {}).map((override) => override.import))]
|
||||
return [...imports, json, ...errorTypes, operations].filter(Boolean).join("\n\n")
|
||||
}
|
||||
|
||||
function renderPromiseClient(groups: ReadonlyArray<Group>) {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,24 @@ describe("HttpApiCodegen.generate", () => {
|
|||
)
|
||||
})
|
||||
|
||||
test("allows Promise outputs to use an authoritative imported wire type", () => {
|
||||
const contract = compileContract(
|
||||
api(HttpApiEndpoint.get("events", "/event", { success: HttpApiSchema.StreamSse({ data: Schema.Unknown }) })),
|
||||
)
|
||||
const output = emitPromise(contract, {
|
||||
outputTypes: {
|
||||
"session.events": {
|
||||
name: "EventWire",
|
||||
import: 'import type { EventWire } from "./event-wire"',
|
||||
},
|
||||
},
|
||||
})
|
||||
const types = output.files.find((file) => file.path === "types.ts")?.content
|
||||
|
||||
expect(types).toContain('import type { EventWire } from "./event-wire"')
|
||||
expect(types).toContain("export type SessionEventsOutput = EventWire")
|
||||
})
|
||||
|
||||
test("emits an Effect client against an imported authoritative API", () => {
|
||||
const output = emitEffectImported(
|
||||
compileContract(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue