chore(sdk): add generated effect example
This commit is contained in:
parent
321d257beb
commit
bdbf2d4102
18 changed files with 2275 additions and 23 deletions
|
|
@ -42,23 +42,7 @@ await createClient({
|
|||
],
|
||||
})
|
||||
|
||||
// Patch a @hey-api/openapi-ts codegen bug: SseFn incorrectly passes the
|
||||
// endpoint's TError into the second generic of ServerSentEventsResult, which
|
||||
// is the AsyncGenerator's TReturn slot. Iterator return values have nothing
|
||||
// to do with HTTP errors, and any consumer that calls `.return()` or returns
|
||||
// from a mock generator gets type-checked against the wrong shape. Drop the
|
||||
// arg so TReturn defaults to void.
|
||||
const sseTypesPath = "./src/v2/gen/client/types.gen.ts"
|
||||
const sseTypesFile = Bun.file(sseTypesPath)
|
||||
const sseTypesSource = await sseTypesFile.text()
|
||||
const sseTypesPatched = sseTypesSource.replace(
|
||||
"=> Promise<ServerSentEventsResult<TData, TError>>",
|
||||
"=> Promise<ServerSentEventsResult<TData>>",
|
||||
)
|
||||
if (sseTypesPatched === sseTypesSource) {
|
||||
throw new Error(`SseFn patch did not apply; @hey-api/openapi-ts output may have changed (${sseTypesPath})`)
|
||||
}
|
||||
await Bun.write(sseTypesPath, sseTypesPatched)
|
||||
await patchSseTypes("./src/v2/gen/client/types.gen.ts")
|
||||
|
||||
const openapi = await Bun.file("./openapi.json").json()
|
||||
const effect = generateFiles(await parseSpec("./openapi.json"), effectEmitter, {
|
||||
|
|
@ -72,12 +56,65 @@ for (const file of effect.files) {
|
|||
await Bun.write(path.join("./src/v2/gen", file.path), file.content)
|
||||
}
|
||||
|
||||
await createClient({
|
||||
input: "./example/effect-sdk/openapi.json",
|
||||
output: {
|
||||
path: "./example/effect-sdk/gen",
|
||||
tsConfigPath: path.join(dir, "tsconfig.json"),
|
||||
clean: true,
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: "@hey-api/typescript",
|
||||
exportFromIndex: false,
|
||||
},
|
||||
{
|
||||
name: "@hey-api/client-fetch",
|
||||
exportFromIndex: false,
|
||||
baseUrl: "https://api.example.com",
|
||||
},
|
||||
],
|
||||
})
|
||||
await patchSseTypes("./example/effect-sdk/gen/client/types.gen.ts")
|
||||
|
||||
const exampleOpenapi = await Bun.file("./example/effect-sdk/openapi.json").json()
|
||||
const exampleEffect = generateFiles(await parseSpec("./example/effect-sdk/openapi.json"), effectEmitter, {
|
||||
namespace: "Example",
|
||||
outputDir: "./example/effect-sdk/gen",
|
||||
emitterOptions: {
|
||||
serverSentEvents: serverSentEvents(exampleOpenapi),
|
||||
},
|
||||
})
|
||||
for (const file of exampleEffect.files) {
|
||||
await Bun.write(path.join("./example/effect-sdk/gen", file.path), file.content)
|
||||
}
|
||||
|
||||
await $`bun prettier --write src/gen`
|
||||
await $`bun prettier --write src/v2`
|
||||
await $`bun prettier --write example/effect-sdk/gen`
|
||||
await $`rm -rf dist`
|
||||
await $`bun tsc`
|
||||
await $`rm openapi.json`
|
||||
|
||||
async function patchSseTypes(sseTypesPath: string) {
|
||||
// Patch a @hey-api/openapi-ts codegen bug: SseFn incorrectly passes the
|
||||
// endpoint's TError into the second generic of ServerSentEventsResult, which
|
||||
// is the AsyncGenerator's TReturn slot. Iterator return values have nothing
|
||||
// to do with HTTP errors, and any consumer that calls `.return()` or returns
|
||||
// from a mock generator gets type-checked against the wrong shape. Drop the
|
||||
// arg so TReturn defaults to void.
|
||||
const sseTypesFile = Bun.file(sseTypesPath)
|
||||
const sseTypesSource = await sseTypesFile.text()
|
||||
const sseTypesPatched = sseTypesSource.replace(
|
||||
"=> Promise<ServerSentEventsResult<TData, TError>>",
|
||||
"=> Promise<ServerSentEventsResult<TData>>",
|
||||
)
|
||||
if (sseTypesPatched === sseTypesSource) {
|
||||
throw new Error(`SseFn patch did not apply; @hey-api/openapi-ts output may have changed (${sseTypesPath})`)
|
||||
}
|
||||
await Bun.write(sseTypesPath, sseTypesPatched)
|
||||
}
|
||||
|
||||
function serverSentEvents(spec: unknown) {
|
||||
if (!spec || typeof spec !== "object" || !("paths" in spec) || !spec.paths || typeof spec.paths !== "object")
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -57,13 +57,13 @@ function generateEffectClient(spec: ApiSpec, ctx: EmitterContext): GeneratedFile
|
|||
.map((name) => ` ${name},`),
|
||||
`} from "./types.gen.js"`,
|
||||
``,
|
||||
`export type OpencodeEffectOptions<TData extends TDataShape, TResponse> = Omit<`,
|
||||
`export type ${ctx.namespacePascal}EffectOptions<TData extends TDataShape, TResponse> = Omit<`,
|
||||
` Options<TData, true, TResponse, "data">,`,
|
||||
` "responseStyle" | "throwOnError"`,
|
||||
`>`,
|
||||
``,
|
||||
`export interface ${ctx.namespacePascal}EffectClient {`,
|
||||
...spec.services.flatMap((service) => serviceShape(service, sse)),
|
||||
...spec.services.flatMap((service) => serviceShape(service, sse, `${ctx.namespacePascal}EffectOptions`)),
|
||||
`}`,
|
||||
``,
|
||||
`export function create${ctx.namespacePascal}EffectClient(client: Client): ${ctx.namespacePascal}EffectClient {`,
|
||||
|
|
@ -82,10 +82,13 @@ function generateEffectClient(spec: ApiSpec, ctx: EmitterContext): GeneratedFile
|
|||
}
|
||||
}
|
||||
|
||||
function serviceShape(service: Service, sse: Set<string>) {
|
||||
function serviceShape(service: Service, sse: Set<string>, optionsType: string) {
|
||||
return [
|
||||
` ${propertyName(service.name)}: {`,
|
||||
...service.operations.flatMap((operation) => [doc(operation, " "), ` ${methodSignature(operation, sse)}`]),
|
||||
...service.operations.flatMap((operation) => [
|
||||
doc(operation, " "),
|
||||
` ${methodSignature(operation, sse, optionsType)}`,
|
||||
]),
|
||||
` }`,
|
||||
]
|
||||
}
|
||||
|
|
@ -100,9 +103,9 @@ function serviceFactory(service: Service, sse: Set<string>) {
|
|||
]
|
||||
}
|
||||
|
||||
function methodSignature(operation: Operation, sse: Set<string>) {
|
||||
function methodSignature(operation: Operation, sse: Set<string>, optionsType: string) {
|
||||
const optional = hasRequiredOptions(operation) ? "" : "?"
|
||||
return `${propertyName(operation.name)}(options${optional}: OpencodeEffectOptions<${operationType(
|
||||
return `${propertyName(operation.name)}(options${optional}: ${optionsType}<${operationType(
|
||||
operation,
|
||||
)}Data, ${operationType(operation)}Responses>): Effect.Effect<${operationResponse(operation, sse)}, ${operationError(
|
||||
operation,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue