/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as Record from "effect/Record" import type * as Stream from "effect/Stream" import type { Path } from "path-parser" import qs from "query-string" import type * as Effect from "../Effect.js" import type * as S from "../Schema.js" import { type Req } from "./apiClientFactory.js" export function makePathWithQuery( path: Path, pars: Record< string, | string | number | boolean | readonly string[] | readonly number[] | readonly boolean[] | null > ) { const forQs = Record.filter(pars, (_, k) => !path.params.includes(k)) const q = forQs // { ...forQs, _: JSON.stringify(forQs) } // TODO: drop completely individual keys from query?, sticking to json only return ( path.build(pars, { ignoreSearch: true, ignoreConstraints: true }) + (Object.keys(q).length ? "?" + qs.stringify(q) : "") ) } export function makePathWithBody( path: Path, pars: Record< string, | string | number | boolean | readonly string[] | readonly number[] | readonly boolean[] | null > ) { return path.build(pars, { ignoreSearch: true, ignoreConstraints: true }) } export type Requests = RequestsAny export type RequestsAny = Record export type ExtractModuleName = { [K in keyof M]: M[K] extends { moduleName: infer N extends string } ? N : never }[keyof M] extends infer R extends string ? R : string export type Client = RequestHandlers< never, never, M, ModuleName > export type ExtractResponse = T extends S.Codec ? S.Schema.Type : T extends unknown ? void : never export type ExtractEResponse = T extends S.Codec ? S.Codec.Encoded : T extends unknown ? void : never export interface ClientForOptions { readonly skipQueryKey?: readonly string[] /** * Middleware tag to attach to every rpc on the client. Schema-only — the * client never invokes the middleware (no Live impl required), but its * declared `error` schema joins the rpc failure union via * `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk. Required when * middleware can throw errors that aren't part of the resource's declared * error union (e.g. auth middleware throwing `NotLoggedInError`); without * it the client decode would fail with a `SchemaError` for stream rpcs. */ } // $Project/$Configuration.Index // -> "$Project", "$Configuration", "Index" export const makeQueryKey = ({ id, options }: { id: string; options?: ClientForOptions }) => id .split("/") .filter((segment: string) => !options || !options.skipQueryKey?.includes(segment)) .map((segment: string) => "$" + segment) .join(".") .split(".") export interface RequestHandlerWithInput { handler: (i: I) => Effect.Effect id: Id options?: ClientForOptions Request: Request } /** Type alias: a no-input handler is simply `RequestHandlerWithInput`. */ export type RequestHandler = RequestHandlerWithInput< void, A, E, R, Request, Id > export interface RequestStreamHandlerWithInput { handler: (i: I) => Stream.Stream id: Id options?: ClientForOptions Request: Request /** * Phantom type property (never set at runtime) that carries the `Final` type to * `StreamMutationWithExtensions`. The tilde prefix follows the Effect convention for * phantom/virtual properties and prevents accidental runtime access. * Stream failures bubble through the execute effect's typed error channel `E`; * the reactive `AsyncResult` ref also mirrors the failure for live progress UI. */ readonly "~final"?: Final } /** Type alias: a no-input stream handler is simply `RequestStreamHandlerWithInput`. */ export type RequestStreamHandler = RequestStreamHandlerWithInput // make sure this is exported or d.ts of apiClientFactory breaks?! export type RequestInputFromMake any }> = Parameters extends [] ? void : Parameters[0] // Has no input only when the request schema declares no payload fields (the auto-added // `_tag` field is ignored). Any payload fields (even all-optional) produce a function handler. type HasNoFields = I extends { readonly fields: infer F extends S.Struct.Fields } ? [Exclude] extends [never] ? true : false : false type RequestInput any }> = Parameters[0] /** * Caller-facing input type for a request. `void` when the request schema has no fields; * otherwise `make`'s first param type. */ export type HandlerInput any }> = HasNoFields extends true ? void : RequestInput /** Extracts the final-value type from a stream request. Defaults to the success type when no `final` schema is set. */ type FinalTypeOf = T extends { readonly final: infer F extends S.Top } ? S.Schema.Type : S.Schema.Type type RequestHandlerFor = T["stream"] extends true ? RequestStreamHandlerWithInput< HandlerInput, S.Schema.Type, S.Schema.Type | E, R | S.Codec.DecodingServices | S.Codec.DecodingServices, T, Id, FinalTypeOf > : RequestHandlerWithInput< HandlerInput, S.Schema.Type, S.Schema.Type | E, R | S.Codec.DecodingServices | S.Codec.DecodingServices, T, Id > export type RequestHandlers = { [K in keyof M as M[K] extends Req ? K : never]: Extract extends infer T extends Req ? RequestHandlerFor : never }