From 587264d9a91158dba30cad21b9f474678b2f1050 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 12 Feb 2026 15:04:02 +0000 Subject: [PATCH 1/4] fix: make openapi-effect a drop-in replacement for openapi-fetch --- .gitignore | 4 + package.json | 18 ++--- packages/app/dist/index.js | 2 + packages/app/dist/index.js.map | 1 + packages/app/dist/main.js | 2 + packages/app/dist/main.js.map | 1 + packages/app/package.json | 21 ++++-- packages/app/src/index.ts | 54 +++++--------- .../shell/api-client/create-client-types.ts | 8 +- .../app/src/shell/api-client/create-client.ts | 74 ++++++++++++++----- packages/app/vite.config.ts | 28 +++++-- pnpm-lock.yaml | 15 ++++ 12 files changed, 147 insertions(+), 81 deletions(-) create mode 100644 packages/app/dist/index.js create mode 100644 packages/app/dist/index.js.map create mode 100644 packages/app/dist/main.js create mode 100644 packages/app/dist/main.js.map diff --git a/.gitignore b/.gitignore index 358127e..d884688 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* reports/ + +# openapi-effect is consumed via git subdir; commit dist so consumers don't need prepare scripts +!packages/app/dist/ +!packages/app/dist/** diff --git a/package.json b/package.json index 0e40b57..bb45c3f 100644 --- a/package.json +++ b/package.json @@ -8,18 +8,18 @@ "packages/*" ], "scripts": { - "build": "pnpm --filter @effect-template/app build", - "check": "pnpm --filter @effect-template/app check", + "build": "pnpm --filter openapi-effect build", + "check": "pnpm --filter openapi-effect check", "changeset": "changeset", "changeset-publish": "node -e \"if (!process.env.NPM_TOKEN) { console.log('Skipping publish: NPM_TOKEN is not set'); process.exit(0); }\" && changeset publish", "changeset-version": "changeset version", - "dev": "pnpm --filter @effect-template/app dev", - "lint": "pnpm --filter @effect-template/app lint", - "lint:tests": "pnpm --filter @effect-template/app lint:tests", - "lint:effect": "pnpm --filter @effect-template/app lint:effect", - "test": "pnpm --filter @effect-template/app test", - "typecheck": "pnpm --filter @effect-template/app typecheck", - "start": "pnpm --filter @effect-template/app start" + "dev": "pnpm --filter openapi-effect dev", + "lint": "pnpm --filter openapi-effect lint", + "lint:tests": "pnpm --filter openapi-effect lint:tests", + "lint:effect": "pnpm --filter openapi-effect lint:effect", + "test": "pnpm --filter openapi-effect test", + "typecheck": "pnpm --filter openapi-effect typecheck", + "start": "pnpm --filter openapi-effect start" }, "devDependencies": { "@changesets/changelog-github": "^0.5.2", diff --git a/packages/app/dist/index.js b/packages/app/dist/index.js new file mode 100644 index 0000000..475bb92 --- /dev/null +++ b/packages/app/dist/index.js @@ -0,0 +1,2 @@ +export*from"openapi-fetch";import{default as ve,default as Ee}from"openapi-fetch";import*as de from"@effect/platform/FetchHttpClient";import*as D from"@effect/platform/HttpBody";import*as R from"@effect/platform/HttpClient";import*as d from"@effect/platform/HttpClientRequest";import{Effect as c,Match as a}from"effect";const le=e=>{throw new Error(`Unexpected value: ${JSON.stringify(e)}`)},A=e=>e,v=e=>e,O=e=>e,T=e=>e,H=e=>e,b=e=>e,U=e=>e,w=e=>c.gen(function*(){const r=yield*R.HttpClient,t=q(e),n=yield*c.mapError(c.gen(function*(){const s=yield*r.execute(t),o=yield*s.text;return O({status:s.status,headers:J(s.headers),text:o})}),s=>({_tag:"TransportError",error:s instanceof Error?s:new Error(String(s))}));return yield*e.dispatcher(n)}),q=e=>{let n=({get:d.get,post:d.post,put:d.put,patch:d.patch,delete:d.del,head:d.head,options:d.options}[e.method]??d.get)(e.url);if(e.headers!==void 0){const s=k(e.headers);n=d.setHeaders(n,s)}if(e.body!==void 0){const s=typeof e.body=="string"?e.body:JSON.stringify(e.body);n=d.setBody(n,D.text(s))}return n},k=e=>{if(e instanceof Headers){const r={};for(const[t,n]of e.entries())r[t]=n;return r}if(Array.isArray(e)){const r={};for(const t of e){const[n,s]=t;r[n]=s}return r}return e},J=e=>{const r=new Headers;for(const[t,n]of Object.entries(e))r.set(t,n);return r},h=e=>T(r=>{const t=r.headers.get("content-type")??void 0;return e(r.status,t,r.text)}),E=(e,r,t)=>c.try({try:()=>A(JSON.parse(t)),catch:n=>({_tag:"ParseError",status:e,contentType:r,error:n instanceof Error?n:new Error(String(n)),body:t})}),g=(e,r)=>({_tag:"UnexpectedStatus",status:e,body:r}),S=(e,r,t,n)=>({_tag:"UnexpectedContentType",status:e,expected:r,actual:t,body:n}),pe=()=>{const e=(r,t,n)=>{let s=`${n.baseUrl}${String(t)}`;if(n.params!==void 0)for(const[i,u]of Object.entries(n.params))s=s.replace(`{${i}}`,encodeURIComponent(String(u)));if(n.query!==void 0){const i=new URLSearchParams;for(const[u,l]of Object.entries(n.query))i.append(u,String(l));s=`${s}?${i.toString()}`}const o=H({method:r,url:s,dispatcher:n.dispatcher,...n.headers!==void 0&&{headers:n.headers},...n.body!==void 0&&{body:n.body},...n.signal!==void 0&&{signal:n.signal}});return w(o)};return{GET:(r,t)=>e("get",r,t),POST:(r,t)=>e("post",r,t),PUT:(r,t)=>e("put",r,t),PATCH:(r,t)=>e("patch",r,t),DELETE:(r,t)=>e("delete",r,t)}},N=()=>T(e=>{const r=e.headers.get("content-type")??void 0,t=e.status>=200&&e.status<300;if(e.text===""||e.status===204){const n={status:e.status,contentType:"none",body:void 0};return t?c.succeed(n):c.fail({_tag:"HttpError",...n})}return r?.includes("application/json")?c.gen(function*(){const n=yield*E(e.status,"application/json",e.text),s={status:e.status,contentType:"application/json",body:n};return t?s:yield*c.fail({_tag:"HttpError",...s})}):c.fail(S(e.status,["application/json"],r,e.text))});let _;const B=e=>{_=e},x=()=>{if(_===void 0)throw new Error("Default dispatchers are not registered. Import generated dispatchers module.");return U(_)},L=(e,r,t,n)=>{let s=r;if(t)for(const[o,i]of Object.entries(t))s=s.replace("{"+o+"}",encodeURIComponent(String(i)));if(n){const o=new URLSearchParams;for(const[u,l]of Object.entries(n))if(Array.isArray(l))for(const C of l)o.append(u,String(C));else o.set(u,String(l));const i=o.toString();i.length>0&&(s=s.includes("?")?s+"&"+i:s+"?"+i)}return e===void 0||e===""?s:new URL(s,e).toString()},M=e=>typeof e=="string"||e instanceof Blob||e instanceof ArrayBuffer||e instanceof ReadableStream||e instanceof FormData||e instanceof URLSearchParams,$=e=>e===void 0||M(e)?e:JSON.stringify(e),F=e=>e!==void 0&&typeof e!="string"&&!(e instanceof Blob)&&!(e instanceof FormData),P=e=>{const r=new Headers;if(e===void 0)return r;if(e instanceof Headers)return new Headers(e);if(Array.isArray(e)){for(const t of e)Array.isArray(t)&&t.length===2&&r.set(String(t[0]),String(t[1]));return r}for(const[t,n]of Object.entries(e))if(n!=null){if(Array.isArray(n)){r.set(t,n.map(String).join(","));continue}r.set(t,String(n))}return r},I=(e,r)=>{const t=P(e),n=P(r);for(const[s,o]of n.entries())t.set(s,o);return t},j=(e,r)=>(t,n,s)=>{const o=L(r.baseUrl,t,s?.params,s?.query),i=I(r.headers,s?.headers),u=$(s?.body);F(s?.body)&&i.set("Content-Type","application/json");const l=H({method:e,url:o,dispatcher:n,headers:i,body:u,signal:s?.signal});return w(l)},p=(e,r,t)=>(n,s)=>j(e,r)(n,t[n][e],s),fe=(e,r)=>{const t=r??x();return b({GET:p("get",e,t),POST:p("post",e,t),PUT:p("put",e,t),DELETE:p("delete",e,t),PATCH:p("patch",e,t),HEAD:p("head",e,t),OPTIONS:p("options",e,t)})},V=(e,r)=>(t,n)=>j(e,r)(t,N(),n),z=e=>typeof e=="object"&&e!==null&&"_tag"in e&&Reflect.get(e,"_tag")==="HttpError",G=e=>e.pipe(c.catchIf(r=>z(r),r=>c.succeed(r))),f=(e,r)=>(t,n)=>G(V(e,r)(t,n)),ye=e=>b({GET:f("get",e),POST:f("post",e),PUT:f("put",e),DELETE:f("delete",e),PATCH:f("patch",e),HEAD:f("head",e),OPTIONS:f("options",e)}),W=(e,r,t,n)=>c.succeed(n),K=(e,r,t,n)=>c.succeed(n),Q=(e,r,t,n)=>c.succeed(n),X=(e,r,t,n)=>c.succeed(n),Y=(e,r,t,n)=>c.succeed(n),Z=(e,r,t,n)=>c.succeed(n),ee=(e,r,t,n)=>c.succeed(n),te=(e,r,t,n)=>c.succeed(n),re=(e,r,t,n)=>c.succeed(n),ne=(e,r,t,n)=>c.succeed(n),m=(e,r,t,n)=>r?.includes("application/json")?c.gen(function*(){const s=yield*E(e,"application/json",t),o=yield*n(e,"application/json",t,s);return v({status:e,contentType:"application/json",body:o})}):c.fail(S(e,["application/json"],r,t)),y=(e,r,t,n)=>r?.includes("application/json")?c.gen(function*(){const s=yield*E(e,"application/json",t),o=yield*n(e,"application/json",t,s);return yield*c.fail(v({_tag:"HttpError",status:e,contentType:"application/json",body:o}))}):c.fail(S(e,["application/json"],r,t)),se=h((e,r,t)=>a.value(e).pipe(a.when(200,()=>m(200,r,t,W)),a.when(500,()=>y(500,r,t,K)),a.orElse(()=>c.fail(g(e,t))))),ce=h((e,r,t)=>a.value(e).pipe(a.when(201,()=>m(201,r,t,Q)),a.when(400,()=>y(400,r,t,X)),a.when(500,()=>y(500,r,t,Y)),a.orElse(()=>c.fail(g(e,t))))),ae=h((e,r,t)=>a.value(e).pipe(a.when(200,()=>m(200,r,t,Z)),a.when(404,()=>y(404,r,t,ee)),a.when(500,()=>y(500,r,t,te)),a.orElse(()=>c.fail(g(e,t))))),oe=h((e,r,t)=>a.value(e).pipe(a.when(204,()=>c.succeed(v({status:204,contentType:"none",body:void 0}))),a.when(404,()=>y(404,r,t,re)),a.when(500,()=>y(500,r,t,ne)),a.orElse(()=>c.fail(g(e,t))))),ie={"/pets":{get:se,post:ce},"/pets/{petId}":{get:ae,delete:oe}};B(ie);export{de as FetchHttpClient,le as assertNever,ve as createClient,ye as createClientEffect,fe as createClientStrict,h as createDispatcher,pe as createStrictClient,N as createUniversalDispatcher,Q as decodecreatePet_201,X as decodecreatePet_400,Y as decodecreatePet_500,re as decodedeletePet_404,ne as decodedeletePet_500,Z as decodegetPet_200,ee as decodegetPet_404,te as decodegetPet_500,W as decodelistPets_200,K as decodelistPets_500,Ee as default,ce as dispatchercreatePet,oe as dispatcherdeletePet,ae as dispatchergetPet,se as dispatcherlistPets,ie as dispatchersByPath,w as executeRequest,E as parseJSON,B as registerDefaultDispatchers,S as unexpectedContentType,g as unexpectedStatus}; +//# sourceMappingURL=index.js.map diff --git a/packages/app/dist/index.js.map b/packages/app/dist/index.js.map new file mode 100644 index 0000000..5db398f --- /dev/null +++ b/packages/app/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sources":["../src/core/api-client/strict-types.ts","../src/core/axioms.ts","../src/shell/api-client/strict-client.ts","../src/shell/api-client/create-client.ts","../src/generated/decoders.ts","../src/generated/dispatch.ts","../src/generated/dispatchers-by-path.ts"],"sourcesContent":["// CHANGE: Define core type-level operations for extracting OpenAPI types\n// WHY: Enable compile-time type safety without runtime overhead through pure type transformations\n// QUOTE(ТЗ): \"Success / HttpError являются коррелированными суммами (status → точный тип body) строго из OpenAPI типов\"\n// REF: issue-2, section 3.1, 4.1-4.3\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Op ∈ Operations: ResponseVariant = Success ⊎ Failure\n// PURITY: CORE\n// INVARIANT: All types computed at compile time, no runtime operations\n// COMPLEXITY: O(1) compile-time / O(0) runtime\n\nimport type { HttpMethod, PathsWithMethod } from \"openapi-typescript-helpers\"\n\n/**\n * Extract all paths that support a given HTTP method\n *\n * @pure true - compile-time only\n * @invariant Result ⊆ paths\n */\nexport type PathsForMethod<\n Paths extends object,\n Method extends HttpMethod\n> = PathsWithMethod\n\n/**\n * Extract operation definition for a path and method\n *\n * @pure true - compile-time only\n * @invariant ∀ path ∈ Paths, method ∈ Methods: Operation = Paths[path][method]\n */\nexport type OperationFor<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = Method extends keyof Paths[Path] ? Paths[Path][Method] : never\n\n/**\n * Extract all response definitions from an operation\n *\n * @pure true - compile-time only\n */\nexport type ResponsesFor = Op extends { responses: infer R } ? R : never\n\n// ============================================================================\n// Request-side typing (path/method → params/query/body)\n// ============================================================================\n\n/**\n * Extract path parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns path params type or undefined if none\n */\nexport type PathParamsFor = Op extends { parameters: { path: infer P } }\n ? P extends Record ? Record\n : never\n : undefined\n\n/**\n * Extract query parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns query params type or undefined if none\n */\nexport type QueryParamsFor = Op extends { parameters: { query?: infer Q } } ? Q\n : undefined\n\n/**\n * Extract request body type from operation\n *\n * @pure true - compile-time only\n * @invariant Returns body type or undefined if no requestBody\n */\nexport type RequestBodyFor = Op extends { requestBody: { content: infer C } }\n ? C extends { \"application/json\": infer J } ? J\n : C extends { [key: string]: infer V } ? V\n : never\n : undefined\n\n/**\n * Check if path params are required\n *\n * @pure true - compile-time only\n */\n\nexport type HasRequiredPathParams = Op extends { parameters: { path: infer P } }\n ? P extends Record ? keyof P extends never ? false : true\n : false\n : false\n\n/**\n * Check if request body is required\n *\n * @pure true - compile-time only\n */\nexport type HasRequiredBody = Op extends { requestBody: infer RB } ? RB extends { content: object } ? true\n : false\n : false\n\n/**\n * Build request options type from operation with all constraints\n * - params: required if path has required parameters\n * - query: optional, typed from operation\n * - body: required if operation has requestBody (accepts typed object OR string)\n *\n * For request body:\n * - Users can pass either the typed object (preferred, for type safety)\n * - Or a pre-stringified JSON string with headers (for backwards compatibility)\n *\n * @pure true - compile-time only\n * @invariant Options type is fully derived from operation definition\n */\nexport type RequestOptionsFor =\n & (HasRequiredPathParams extends true ? { readonly params: PathParamsFor }\n : { readonly params?: PathParamsFor })\n & (HasRequiredBody extends true ? { readonly body: RequestBodyFor | BodyInit }\n : { readonly body?: RequestBodyFor | BodyInit })\n & { readonly query?: QueryParamsFor }\n & { readonly headers?: HeadersInit }\n & { readonly signal?: AbortSignal }\n\n/**\n * Extract status codes from responses\n *\n * @pure true - compile-time only\n * @invariant Result = { s | s ∈ keys(Responses) }\n */\nexport type StatusCodes = keyof Responses & (number | string)\n\n/**\n * Extract content types for a specific status code\n *\n * @pure true - compile-time only\n */\nexport type ContentTypesFor<\n Responses,\n Status extends StatusCodes\n> = Status extends keyof Responses ? Responses[Status] extends { content: infer C } ? keyof C & string\n : \"none\"\n : never\n\n/**\n * Extract body type for a specific status and content-type\n *\n * @pure true - compile-time only\n * @invariant Strict correlation: Body type depends on both status and content-type\n */\nexport type BodyFor<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = Status extends keyof Responses\n ? Responses[Status] extends { content: infer C } ? ContentType extends keyof C ? C[ContentType]\n : never\n : ContentType extends \"none\" ? undefined\n : never\n : never\n\n/**\n * Build a correlated success response variant (status + contentType + body)\n * Used for 2xx responses that go to the success channel.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type ResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build a correlated HTTP error response variant (status + contentType + body + _tag)\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * The `_tag: \"HttpError\"` discriminator allows distinguishing HTTP errors from BoundaryErrors.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type HttpErrorResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly _tag: \"HttpError\"\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build all response variants for given responses\n *\n * @pure true - compile-time only\n */\ntype AllResponseVariants = StatusCodes extends infer Status\n ? Status extends StatusCodes\n ? ContentTypesFor extends infer CT\n ? CT extends ContentTypesFor ? ResponseVariant\n : never\n : never\n : never\n : never\n\n/**\n * Generic 2xx status detection without hardcoding\n * Uses template literal type to check if status string starts with \"2\"\n *\n * Works with any 2xx status including non-standard ones like 250.\n *\n * @pure true - compile-time only\n * @invariant Is2xx = true ⟺ 200 ≤ S < 300\n */\nexport type Is2xx = `${S}` extends `2${string}` ? true : false\n\n/**\n * Filter response variants to success statuses (2xx)\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ SuccessVariants: Is2xx = true\n */\nexport type SuccessVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? ResponseVariant\n : never\n : never\n : never\n\n/**\n * Filter response variants to error statuses (non-2xx from schema)\n * Returns HttpErrorResponseVariant with `_tag: \"HttpError\"` for discrimination.\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpErrorVariants: Is2xx = false ∧ v.status ∈ Schema ∧ v._tag = \"HttpError\"\n */\nexport type HttpErrorVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? never\n : HttpErrorResponseVariant\n : never\n : never\n\n/**\n * Boundary errors - always present regardless of schema\n *\n * @pure true - compile-time only\n * @invariant These errors represent protocol/parsing failures, not business logic\n */\nexport type TransportError = {\n readonly _tag: \"TransportError\"\n readonly error: Error\n}\n\nexport type UnexpectedStatus = {\n readonly _tag: \"UnexpectedStatus\"\n readonly status: number\n readonly body: string\n}\n\nexport type UnexpectedContentType = {\n readonly _tag: \"UnexpectedContentType\"\n readonly status: number\n readonly expected: ReadonlyArray\n readonly actual: string | undefined\n readonly body: string\n}\n\nexport type ParseError = {\n readonly _tag: \"ParseError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type DecodeError = {\n readonly _tag: \"DecodeError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type BoundaryError =\n | TransportError\n | UnexpectedStatus\n | UnexpectedContentType\n | ParseError\n | DecodeError\n\n/**\n * Success type for an operation (2xx statuses only)\n *\n * Goes to the **success channel** of Effect.\n * Developers receive this directly without needing to handle errors.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ ApiSuccess: v.status ∈ [200..299]\n */\nexport type ApiSuccess = SuccessVariants\n\n/**\n * HTTP error responses from schema (non-2xx statuses like 400, 404, 500)\n *\n * Goes to the **error channel** of Effect, forcing explicit handling.\n * These are business-level errors defined in the OpenAPI schema.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpError: v.status ∉ [200..299] ∧ v.status ∈ Schema\n */\nexport type HttpError = HttpErrorVariants\n\n/**\n * Complete failure type for API operations\n *\n * Includes both schema-defined HTTP errors (4xx, 5xx) and boundary errors.\n * All failures go to the **error channel** of Effect, forcing explicit handling.\n *\n * @pure true - compile-time only\n * @invariant ApiFailure = HttpError ⊎ BoundaryError\n *\n * BREAKING CHANGE: Previously, HTTP errors (404, 500) were in success channel.\n * Now they are in error channel, requiring explicit handling with Effect.catchTag\n * or Effect.match pattern.\n */\nexport type ApiFailure = HttpError | BoundaryError\n\n/**\n * @deprecated Use ApiSuccess for success channel\n * and ApiFailure for error channel instead.\n *\n * ApiResponse mixed success and error statuses in one type.\n * New API separates them into proper Effect channels.\n */\nexport type ApiResponse = SuccessVariants | HttpErrorVariants\n\n/**\n * Helper to ensure exhaustive pattern matching\n *\n * @pure true\n * @throws Compile-time error if called with non-never type\n */\nexport const assertNever = (x: never): never => {\n throw new Error(`Unexpected value: ${JSON.stringify(x)}`)\n}\n","// CHANGE: Create axioms module for type-safe cast operations\n// WHY: Centralize all type assertions in a single auditable location per CLAUDE.md\n// QUOTE(ТЗ): \"as: запрещён в обычном коде; допускается ТОЛЬКО в одном аксиоматическом модуле\"\n// REF: issue-2, section 3.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ cast ∈ Axioms: cast(x) → typed(x) ∨ runtime_validated(x)\n// PURITY: CORE\n// EFFECT: none - pure type-level operations\n// INVARIANT: All casts auditable in single file\n// COMPLEXITY: O(1)\n\n/**\n * JSON value type - result of JSON.parse()\n * This is the fundamental type for all parsed JSON values\n */\n/**\n * Cast function for dispatcher factory\n * AXIOM: Dispatcher factory receives valid classify function\n *\n * This enables generated dispatchers to work with heterogeneous Effect unions.\n * The cast is safe because:\n * 1. The classify function is generated from OpenAPI schema\n * 2. All status/content-type combinations are exhaustively covered\n * 3. The returned Effect conforms to Dispatcher signature\n *\n * @pure true\n */\nimport type { Effect } from \"effect\"\nimport type { ApiFailure, ApiSuccess, TransportError } from \"./api-client/strict-types.js\"\n\nexport type Json =\n | null\n | boolean\n | number\n | string\n | ReadonlyArray\n | { readonly [k: string]: Json }\n\n/**\n * Cast parsed JSON value to typed Json\n * AXIOM: JSON.parse returns a valid Json value\n *\n * @precondition value is result of JSON.parse on valid JSON string\n * @postcondition result conforms to Json type\n * @pure true\n */\nexport const asJson = (value: unknown): Json => value as Json\n\n/**\n * Cast a value to a specific type with const assertion\n * Used for creating literal typed objects in generated code\n *\n * @pure true\n */\nexport const asConst = (value: T): T => value\n\n/**\n * Create a typed RawResponse from raw values\n * AXIOM: HTTP response structure is known at runtime\n *\n * @pure true\n */\nexport type RawResponse = {\n readonly status: number\n readonly headers: Headers\n readonly text: string\n}\n\nexport const asRawResponse = (value: {\n status: number\n headers: Headers\n text: string\n}): RawResponse => value as RawResponse\n\n/**\n * Dispatcher classifies response and applies decoder\n *\n * NEW DESIGN (Effect-native):\n * - Success channel: `ApiSuccess` (2xx responses only)\n * - Error channel: `ApiFailure` (non-2xx schema errors + boundary errors)\n *\n * This forces developers to explicitly handle HTTP errors (404, 500, etc.)\n * using Effect.catchTag, Effect.match, or similar patterns.\n *\n * @pure false - applies decoders\n * @effect Effect\n * @invariant Must handle all statuses and content-types from schema\n */\nexport type Dispatcher = (\n response: RawResponse\n) => Effect.Effect<\n ApiSuccess,\n Exclude, TransportError>\n>\n\nexport const asDispatcher = (\n fn: (response: RawResponse) => Effect.Effect\n): Dispatcher => fn as Dispatcher\n\n/**\n * Cast for StrictRequestInit config object\n * AXIOM: Config object has correct structure when all properties assigned\n *\n * @pure true\n */\nexport const asStrictRequestInit = (config: object): T => config as T\n\n/**\n * Classifier function type for dispatcher creation\n * AXIOM: Classify function returns Effect with heterogeneous union types\n *\n * This type uses `unknown` to allow the classify function to return\n * heterogeneous Effect unions from switch statements. The actual types\n * are enforced by the generated dispatcher code.\n *\n * @pure true\n */\nexport type ClassifyFn = (\n status: number,\n contentType: string | undefined,\n text: string\n) => Effect.Effect\n\n/**\n * Cast internal client implementation to typed StrictApiClient\n * AXIOM: Client implementation correctly implements all method constraints\n *\n * This cast is safe because:\n * 1. StrictApiClient type enforces path/method constraints at call sites\n * 2. The runtime implementation correctly builds requests for any path/method\n * 3. Type checking happens at the call site, not in the implementation\n *\n * @pure true\n */\nexport const asStrictApiClient = (client: object): T => client as T\n\n/**\n * Cast default dispatchers registry to specific schema type\n * AXIOM: Default dispatcher registry was registered for the current Paths type\n *\n * @pure true\n */\nexport const asDispatchersFor = (value: unknown): T => value as T\n","// CHANGE: Implement Effect-based HTTP client with Effect-native error handling\n// WHY: Force explicit handling of HTTP errors (4xx, 5xx) via Effect error channel\n// QUOTE(ТЗ): \"каждый запрос возвращает Effect; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 2, 4, 5.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ req ∈ Requests: execute(req) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient.HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per request / O(n) for body size\n\nimport * as HttpBody from \"@effect/platform/HttpBody\"\nimport * as HttpClient from \"@effect/platform/HttpClient\"\nimport * as HttpClientRequest from \"@effect/platform/HttpClientRequest\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport type {\n ApiFailure,\n ApiSuccess,\n DecodeError,\n OperationFor,\n ParseError,\n ResponsesFor,\n TransportError,\n UnexpectedContentType,\n UnexpectedStatus\n} from \"../../core/api-client/strict-types.js\"\nimport {\n asDispatcher,\n asJson,\n asRawResponse,\n asStrictRequestInit,\n type ClassifyFn,\n type Dispatcher,\n type Json,\n type RawResponse\n} from \"../../core/axioms.js\"\n\n// Re-export Dispatcher type for consumers\n\n/**\n * Decoder for response body\n *\n * @pure false - may perform validation\n * @effect Effect\n */\nexport type Decoder = (\n status: number,\n contentType: string,\n body: string\n) => Effect.Effect\n\n/**\n * Configuration for a strict API client request\n */\nexport type StrictRequestInit = {\n readonly method: HttpMethod\n readonly url: string\n readonly dispatcher: Dispatcher\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Execute HTTP request with Effect-native error handling\n *\n * @param config - Request configuration with dispatcher\n * @returns Effect with success (2xx) and failures (non-2xx + boundary errors)\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses only\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * This forces developers to explicitly handle HTTP errors using:\n * - `Effect.catchTag` for specific error types\n * - `Effect.match` for exhaustive handling\n * - `Effect.catchAll` for generic error handling\n *\n * @pure false - performs HTTP request\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n * @invariant 2xx → success channel, non-2xx → error channel\n * @precondition config.dispatcher handles all schema statuses\n * @postcondition ∀ response: success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n * @complexity O(1) + O(|body|) for text extraction\n */\nexport const executeRequest = (\n config: StrictRequestInit\n): Effect.Effect, ApiFailure, HttpClient.HttpClient> =>\n Effect.gen(function*() {\n // STEP 1: Get HTTP client from context\n const client = yield* HttpClient.HttpClient\n\n // STEP 2: Build request based on method\n const request = buildRequest(config)\n\n // STEP 3: Execute request with error mapping\n const rawResponse = yield* Effect.mapError(\n Effect.gen(function*() {\n const response = yield* client.execute(request)\n const text = yield* response.text\n return asRawResponse({\n status: response.status,\n headers: toNativeHeaders(response.headers),\n text\n })\n }),\n (error): TransportError => ({\n _tag: \"TransportError\",\n error: error instanceof Error ? error : new Error(String(error))\n })\n )\n\n // STEP 4: Delegate classification to dispatcher (handles status/content-type/decode)\n return yield* config.dispatcher(rawResponse)\n })\n\n/**\n * Build HTTP request from config\n *\n * @pure true\n */\nconst buildRequest = (config: StrictRequestInit): HttpClientRequest.HttpClientRequest => {\n const methodMap: Record HttpClientRequest.HttpClientRequest> = {\n get: HttpClientRequest.get,\n post: HttpClientRequest.post,\n put: HttpClientRequest.put,\n patch: HttpClientRequest.patch,\n delete: HttpClientRequest.del,\n head: HttpClientRequest.head,\n options: HttpClientRequest.options\n }\n\n const createRequest = methodMap[config.method] ?? HttpClientRequest.get\n let request = createRequest(config.url)\n\n // Add headers if provided\n if (config.headers !== undefined) {\n const headers = toRecordHeaders(config.headers)\n request = HttpClientRequest.setHeaders(request, headers)\n }\n\n // Add body if provided\n if (config.body !== undefined) {\n const bodyText = typeof config.body === \"string\" ? config.body : JSON.stringify(config.body)\n request = HttpClientRequest.setBody(request, HttpBody.text(bodyText))\n }\n\n return request\n}\n\n/**\n * Convert Headers to Record\n *\n * @pure true\n */\nconst toRecordHeaders = (headers: HeadersInit): Record => {\n if (headers instanceof Headers) {\n const result: Record = {}\n for (const [key, value] of headers.entries()) {\n result[key] = value\n }\n return result\n }\n if (Array.isArray(headers)) {\n const result: Record = {}\n for (const headerPair of headers) {\n const [headerKey, headerValue] = headerPair\n result[headerKey] = headerValue\n }\n return result\n }\n return headers\n}\n\n/**\n * Convert @effect/platform Headers to native Headers\n *\n * @pure true\n */\nconst toNativeHeaders = (platformHeaders: { readonly [key: string]: string }): Headers => {\n const headers = new Headers()\n for (const [key, value] of Object.entries(platformHeaders)) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Helper to create dispatcher from switch-based classifier\n *\n * This function uses a permissive type signature to allow generated code\n * to work with any response variant without requiring exact type matching.\n * The classify function can return any Effect with union types for success/error.\n *\n * NOTE: Uses axioms module for type casts to allow heterogeneous Effect\n * unions from switch statements. The returned Dispatcher is properly typed.\n *\n * @pure true - returns pure function\n * @complexity O(1)\n */\n\nexport const createDispatcher = (\n classify: ClassifyFn\n): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n return classify(response.status, contentType, response.text)\n })\n}\n\n/**\n * Helper to parse JSON with error handling\n *\n * @pure false - performs parsing\n * @effect Effect\n */\nexport const parseJSON = (\n status: number,\n contentType: string,\n text: string\n): Effect.Effect =>\n Effect.try({\n try: () => asJson(JSON.parse(text)),\n catch: (error): ParseError => ({\n _tag: \"ParseError\",\n status,\n contentType,\n error: error instanceof Error ? error : new Error(String(error)),\n body: text\n })\n })\n\n/**\n * Helper to create UnexpectedStatus error\n *\n * @pure true\n */\nexport const unexpectedStatus = (status: number, body: string): UnexpectedStatus => ({\n _tag: \"UnexpectedStatus\",\n status,\n body\n})\n\n/**\n * Helper to create UnexpectedContentType error\n *\n * @pure true\n */\nexport const unexpectedContentType = (\n status: number,\n expected: ReadonlyArray,\n actual: string | undefined,\n body: string\n): UnexpectedContentType => ({\n _tag: \"UnexpectedContentType\",\n status,\n expected,\n actual,\n body\n})\n\n/**\n * Generic client interface for any OpenAPI schema with Effect-native error handling\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * @pure false - performs HTTP requests\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n */\nexport type StrictClient = {\n readonly GET: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly POST: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PUT: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PATCH: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly DELETE: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n}\n\n/**\n * Request options for a specific operation\n */\nexport type RequestOptions<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = {\n readonly dispatcher: Dispatcher>>\n readonly baseUrl: string\n readonly params?: Record\n readonly query?: Record\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Create a strict client for an OpenAPI schema\n *\n * @pure true - returns pure client object\n * @complexity O(1)\n */\nexport const createStrictClient = (): StrictClient<\n Paths\n> => {\n const makeRequest = (\n method: Method,\n path: Path,\n options: RequestOptions\n ) => {\n let url = `${options.baseUrl}${String(path)}`\n\n // Replace path parameters\n if (options.params !== undefined) {\n for (const [key, value] of Object.entries(options.params)) {\n url = url.replace(`{${key}}`, encodeURIComponent(String(value)))\n }\n }\n\n // Add query parameters\n if (options.query !== undefined) {\n const params = new URLSearchParams()\n for (const [key, value] of Object.entries(options.query)) {\n params.append(key, String(value))\n }\n url = `${url}?${params.toString()}`\n }\n\n // Build config object, only including optional properties if they are defined\n // This satisfies exactOptionalPropertyTypes constraint\n const config = asStrictRequestInit>>>({\n method,\n url,\n dispatcher: options.dispatcher,\n ...(options.headers !== undefined && { headers: options.headers }),\n ...(options.body !== undefined && { body: options.body }),\n ...(options.signal !== undefined && { signal: options.signal })\n })\n\n return executeRequest(config)\n }\n\n return {\n GET: (path, options) => makeRequest(\"get\", path, options),\n POST: (path, options) => makeRequest(\"post\", path, options),\n PUT: (path, options) => makeRequest(\"put\", path, options),\n PATCH: (path, options) => makeRequest(\"patch\", path, options),\n DELETE: (path, options) => makeRequest(\"delete\", path, options)\n } satisfies StrictClient\n}\n\n// CHANGE: Add universal dispatcher that handles any OpenAPI responses generically\n// WHY: Enable createClient(options) without code generation or manual dispatcher wiring\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ status, ct: universalDispatcher(status, ct, text) → success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n// PURITY: SHELL\n// EFFECT: Effect, Exclude, TransportError>, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel, no-content → body: undefined\n// COMPLEXITY: O(1) per dispatch + O(|text|) for JSON parsing\n\n/**\n * Create a universal dispatcher that handles any OpenAPI response generically\n *\n * The universal dispatcher classifies responses by status code range:\n * - 2xx → success channel (ApiSuccess)\n * - non-2xx → error channel (HttpError)\n *\n * For JSON content types, it parses the body. For no-content responses (empty body),\n * it returns undefined body with contentType \"none\".\n *\n * This enables using createClient(options) without generating\n * per-operation dispatchers, fulfilling the zero-boilerplate DSL requirement.\n *\n * @pure true - returns pure dispatcher function\n * @complexity O(1) creation + O(|body|) per dispatch\n */\nexport const createUniversalDispatcher = (): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n const is2xx = response.status >= 200 && response.status < 300\n\n // No-content response (empty body or 204)\n if (response.text === \"\" || response.status === 204) {\n const variant = {\n status: response.status,\n contentType: \"none\" as const,\n body: undefined\n } as const\n\n return is2xx\n ? Effect.succeed(variant)\n : Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n }\n\n // JSON content type\n if (contentType?.includes(\"application/json\")) {\n return Effect.gen(function*() {\n const parsed = yield* parseJSON(response.status, \"application/json\", response.text)\n const variant = {\n status: response.status,\n contentType: \"application/json\" as const,\n body: parsed\n } as const\n\n if (is2xx) {\n return variant\n }\n return yield* Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n })\n }\n\n // Unknown content type\n return Effect.fail(unexpectedContentType(\n response.status,\n [\"application/json\"],\n contentType,\n response.text\n ))\n })\n}\n\nexport { type Dispatcher, type RawResponse } from \"../../core/axioms.js\"\n","// CHANGE: Type-safe createClient API with full request-side enforcement\n// WHY: Ensure path/method → operation → request types are all linked\n// QUOTE(ТЗ): \"path + method определяют operation, и из неё выводятся request/response types\"\n// REF: PR#3 blocking review sections 3.2, 3.3\n// SOURCE: n/a\n// PURITY: SHELL\n// EFFECT: Creates Effect-based API client\n// INVARIANT: All operations are type-safe from path → operation → request → response\n// COMPLEXITY: O(1) client creation\n\nimport type * as HttpClient from \"@effect/platform/HttpClient\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport { asDispatchersFor, asStrictApiClient, asStrictRequestInit, type Dispatcher } from \"../../core/axioms.js\"\nimport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n DispatchersForMethod,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nimport type { StrictRequestInit } from \"./strict-client.js\"\nimport { createUniversalDispatcher, executeRequest } from \"./strict-client.js\"\n\nexport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n StrictApiClient,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nexport { createUniversalDispatcher } from \"./strict-client.js\"\n\n/**\n * Primitive value type for path/query parameters\n *\n * @pure true - type alias only\n */\ntype ParamValue = string | number | boolean\n\n/**\n * Query parameter value - can be primitive or array of primitives\n *\n * @pure true - type alias only\n */\ntype QueryValue = ParamValue | ReadonlyArray\n\n// CHANGE: Add default dispatcher registry for auto-dispatching createClient\n// WHY: Allow createClient(options) without explicitly passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: defaultDispatchers = dispatchersByPath ⇒ createClient uses dispatcher(path, method)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: defaultDispatchers is set before createClient use\n// COMPLEXITY: O(1)\nlet defaultDispatchers: DispatchersFor | undefined\n\n/**\n * Register default dispatcher map used by createClient(options)\n *\n * @pure false - mutates module-level registry\n * @invariant defaultDispatchers set exactly once per app boot\n */\nexport const registerDefaultDispatchers = (\n dispatchers: DispatchersFor\n): void => {\n defaultDispatchers = dispatchers\n}\n\n/**\n * Resolve default dispatcher map or fail fast\n *\n * @pure false - reads module-level registry\n * @invariant defaultDispatchers must be set for auto-dispatching client\n */\nconst resolveDefaultDispatchers = (): DispatchersFor => {\n if (defaultDispatchers === undefined) {\n throw new Error(\"Default dispatchers are not registered. Import generated dispatchers module.\")\n }\n return asDispatchersFor>(defaultDispatchers)\n}\n\n/**\n * Build URL with path parameters and query string\n *\n * @param baseUrl - Base URL for the API\n * @param path - Path template with placeholders\n * @param params - Path parameters to substitute\n * @param query - Query parameters to append\n * @returns Fully constructed URL\n *\n * @pure true\n * @complexity O(n + m) where n = |params|, m = |query|\n */\nconst buildUrl = (\n baseUrl: string | undefined,\n path: string,\n params?: Record,\n query?: Record\n): string => {\n // Replace path parameters\n let url = path\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url = url.replace(\"{\" + key + \"}\", encodeURIComponent(String(value)))\n }\n }\n\n // Add query parameters\n if (query) {\n const searchParams = new URLSearchParams()\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n searchParams.append(key, String(item))\n }\n } else {\n searchParams.set(key, String(value))\n }\n }\n const qs = searchParams.toString()\n if (qs.length > 0) {\n url = url.includes(\"?\") ? url + \"&\" + qs : url + \"?\" + qs\n }\n }\n\n // If baseUrl isn't provided, keep a relative URL (browser-friendly)\n if (baseUrl === undefined || baseUrl === \"\") {\n return url\n }\n\n // Construct full URL\n return new URL(url, baseUrl).toString()\n}\n\n\n/**\n * Check if body is already a BodyInit type (not a plain object needing serialization)\n *\n * @pure true\n */\nconst isBodyInit = (body: BodyInit | object): body is BodyInit =>\n typeof body === \"string\"\n || body instanceof Blob\n || body instanceof ArrayBuffer\n || body instanceof ReadableStream\n || body instanceof FormData\n || body instanceof URLSearchParams\n\n/**\n * Serialize body to BodyInit - passes through BodyInit types, JSON-stringifies objects\n *\n * @pure true\n * @returns BodyInit or undefined, with consistent return path\n */\nconst serializeBody = (body: BodyInit | object | undefined): BodyInit | undefined => {\n // Early return for undefined\n if (body === undefined) {\n return body\n }\n // Pass through existing BodyInit types\n if (isBodyInit(body)) {\n return body\n }\n // Plain object - serialize to JSON string (which is a valid BodyInit)\n const serialized: BodyInit = JSON.stringify(body)\n return serialized\n}\n\n/**\n * Check if body requires JSON Content-Type header\n *\n * @pure true\n */\nconst needsJsonContentType = (body: BodyInit | object | undefined): boolean =>\n body !== undefined\n && typeof body !== \"string\"\n && !(body instanceof Blob)\n && !(body instanceof FormData)\n\n/**\n * Merge headers from client options and request options\n *\n * @pure true\n * @complexity O(n) where n = number of headers\n */\nconst toHeaders = (headersInit: ClientOptions[\"headers\"] | undefined): Headers => {\n const headers = new Headers()\n if (headersInit === undefined) {\n return headers\n }\n\n if (headersInit instanceof Headers) {\n return new Headers(headersInit)\n }\n\n if (Array.isArray(headersInit)) {\n for (const entry of headersInit) {\n if (Array.isArray(entry) && entry.length === 2) {\n headers.set(String(entry[0]), String(entry[1]))\n }\n }\n return headers\n }\n\n for (const [key, value] of Object.entries(headersInit)) {\n if (value === null || value === undefined) {\n continue\n }\n if (Array.isArray(value)) {\n headers.set(key, value.map(String).join(\",\"))\n continue\n }\n headers.set(key, String(value))\n }\n\n return headers\n}\n\nconst mergeHeaders = (\n clientHeaders: ClientOptions[\"headers\"] | undefined,\n requestHeaders: ClientOptions[\"headers\"] | undefined\n): Headers => {\n const headers = toHeaders(clientHeaders)\n const optHeaders = toHeaders(requestHeaders)\n for (const [key, value] of optHeaders.entries()) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Request options type for method handlers\n *\n * @pure true - type alias only\n */\ntype MethodHandlerOptions = {\n params?: Record | undefined\n query?: Record | undefined\n body?: BodyInit | object | undefined\n headers?: ClientOptions[\"headers\"] | undefined\n signal?: AbortSignal | undefined\n}\n\n/**\n * Create HTTP method handler with full type constraints\n *\n * @param method - HTTP method\n * @param clientOptions - Client configuration\n * @returns Method handler function\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandler = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n dispatcher: Dispatcher,\n options?: MethodHandlerOptions\n) => {\n const url = buildUrl(clientOptions.baseUrl, path, options?.params, options?.query)\n const headers = mergeHeaders(clientOptions.headers, options?.headers)\n const body = serializeBody(options?.body)\n\n if (needsJsonContentType(options?.body)) {\n headers.set(\"Content-Type\", \"application/json\")\n }\n\n const config: StrictRequestInit = asStrictRequestInit({\n method,\n url,\n dispatcher,\n headers,\n body,\n signal: options?.signal\n })\n\n return executeRequest(config)\n}\n\n/**\n * Create method handler that infers dispatcher from map\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandlerWithDispatchers = (\n method: Method,\n clientOptions: ClientOptions,\n dispatchers: DispatchersForMethod\n) =>\n & string>(\n path: Path,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n dispatchers[path][method],\n options\n )\n\n// CHANGE: Create method handler that infers dispatcher from map\n// WHY: Allow per-call API without passing dispatcher parameter\n// QUOTE(ТЗ): \"Зачем передавать что либо в GET\"\n// REF: user-msg-1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path ∈ PathsForMethod: dispatchers[path][method] = Dispatcher>\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: Dispatcher lookup is total for all operations in Paths\n// COMPLEXITY: O(1) runtime + O(1) dispatcher lookup\n/**\n * Create type-safe Effect-based API client\n *\n * The client enforces:\n * 1. Method availability: GET only on paths with `get`, POST only on paths with `post`\n * 2. Dispatcher correlation: must match operation's responses\n * 3. Request options: params/query/body typed from operation\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import createClient from \"openapi-effect\"\n * import type { Paths } from \"./generated/schema\"\n * import \"./generated/dispatchers-by-path\" // registers default dispatchers\n *\n * const client = createClient({\n * baseUrl: \"https://api.example.com\",\n * credentials: \"include\"\n * })\n *\n * // Type-safe call - dispatcher inferred from path+method\n * const result = yield* client.GET(\"/pets/{petId}\", {\n * params: { petId: \"123\" } // Required because getPet has path params\n * })\n *\n * // Compile error: \"/pets/{petId}\" has no \"put\" method\n * // client.PUT(\"/pets/{petId}\", ...) // Type error!\n * ```\n */\nexport const createClient = (\n options: ClientOptions,\n dispatchers?: DispatchersFor\n): StrictApiClientWithDispatchers => {\n const resolvedDispatchers = dispatchers ?? resolveDefaultDispatchers()\n\n return asStrictApiClient>({\n GET: createMethodHandlerWithDispatchers(\"get\", options, resolvedDispatchers),\n POST: createMethodHandlerWithDispatchers(\"post\", options, resolvedDispatchers),\n PUT: createMethodHandlerWithDispatchers(\"put\", options, resolvedDispatchers),\n DELETE: createMethodHandlerWithDispatchers(\"delete\", options, resolvedDispatchers),\n PATCH: createMethodHandlerWithDispatchers(\"patch\", options, resolvedDispatchers),\n HEAD: createMethodHandlerWithDispatchers(\"head\", options, resolvedDispatchers),\n OPTIONS: createMethodHandlerWithDispatchers(\"options\", options, resolvedDispatchers)\n })\n}\n\n// CHANGE: Add createMethodHandlerWithUniversalDispatcher for zero-boilerplate client\n// WHY: Enable createClientEffect(options) without code generation or dispatcher registry\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path, method: universalDispatcher handles response classification generically\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel\n// COMPLEXITY: O(1) handler creation + O(1) universal dispatcher creation per call\nconst createMethodHandlerWithUniversalDispatcher = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n createUniversalDispatcher(),\n options\n )\n\ntype HttpErrorTag = { readonly _tag: \"HttpError\" }\n\nconst isHttpErrorValue = (error: unknown): error is HttpErrorTag =>\n typeof error === \"object\"\n && error !== null\n && \"_tag\" in error\n && Reflect.get(error, \"_tag\") === \"HttpError\"\n\nconst exposeHttpErrorsAsValues = (\n request: Effect.Effect\n): Effect.Effect<\n A | Extract,\n Exclude>,\n HttpClient.HttpClient\n> =>\n request.pipe(\n Effect.catchIf(\n (error): error is Extract => isHttpErrorValue(error),\n (error) => Effect.succeed(error)\n )\n )\n\nconst createMethodHandlerWithUniversalDispatcherValue = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n exposeHttpErrorsAsValues(\n createMethodHandlerWithUniversalDispatcher(method, clientOptions)(path, options)\n )\n\n// CHANGE: Add createClientEffect — zero-boilerplate Effect-based API client\n// WHY: Enable the user's desired DSL without any generated code or dispatcher setup\n// QUOTE(ТЗ): \"const apiClientEffect = createClientEffect(clientOptions); apiClientEffect.POST('/api/auth/login', { body: credentials })\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Paths, options: createClientEffect(options) → ClientEffect\n// PURITY: SHELL\n// EFFECT: Client methods return Effect\n// INVARIANT: ∀ path, method: path ∈ PathsForMethod (compile-time) ∧ response classified by status range (runtime)\n// COMPLEXITY: O(1) client creation\n/**\n * Create type-safe Effect-based API client with zero boilerplate\n *\n * Uses a universal dispatcher and exposes HTTP statuses as values:\n * - 2xx → success value (ApiSuccess)\n * - non-2xx schema statuses → success value (HttpError with _tag)\n * - boundary/protocol failures stay in error channel\n * - JSON parsed automatically for application/json content types\n *\n * **No code generation needed.** No dispatcher registry needed.\n * Just pass your OpenAPI Paths type and client options.\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration (baseUrl, credentials, headers, etc.)\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import { createClientEffect, type ClientOptions } from \"openapi-effect\"\n * import type { paths } from \"./openapi.d.ts\"\n *\n * const clientOptions: ClientOptions = {\n * baseUrl: \"https://petstore.example.com\",\n * credentials: \"include\"\n * }\n * const apiClientEffect = createClientEffect(clientOptions)\n *\n * // Type-safe call — path, method, and body all enforced at compile time\n * const result = yield* apiClientEffect.POST(\"/api/auth/login\", {\n * body: { email: \"user@example.com\", password: \"secret\" }\n * })\n * ```\n */\nexport const createClientEffect = (\n options: ClientOptions\n): ClientEffect => {\n return asStrictApiClient>({\n GET: createMethodHandlerWithUniversalDispatcherValue(\"get\", options),\n POST: createMethodHandlerWithUniversalDispatcherValue(\"post\", options),\n PUT: createMethodHandlerWithUniversalDispatcherValue(\"put\", options),\n DELETE: createMethodHandlerWithUniversalDispatcherValue(\"delete\", options),\n PATCH: createMethodHandlerWithUniversalDispatcherValue(\"patch\", options),\n HEAD: createMethodHandlerWithUniversalDispatcherValue(\"head\", options),\n OPTIONS: createMethodHandlerWithUniversalDispatcherValue(\"options\", options)\n })\n}\n","// CHANGE: Auto-generated decoder stubs for all operations\n// WHY: Provide type-safe runtime validation entry points\n// QUOTE(ТЗ): \"при изменении схемы сборка обязана падать, пока декодеры не обновлены\"\n// REF: issue-2, section 5.2\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op, status: decoder(op, status) → Effect\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: All decoders return typed DecodeError on failure\n// COMPLEXITY: O(n) where n = size of parsed object\n\nimport { Effect } from \"effect\"\nimport type { DecodeError } from \"../core/api-client/strict-types.js\"\n\n/**\n * JSON value type - result of JSON.parse()\n */\ntype Json = null | boolean | number | string | ReadonlyArray | { readonly [k: string]: Json }\n\n/**\n * Decoder for listPets status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for listPets status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 201 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_201 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 400 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_400 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n","// CHANGE: Auto-generated dispatchers for all operations with Effect-native error handling\n// WHY: Maintain compile-time correlation between status codes and body types\n// QUOTE(ТЗ): \"реализует switch(status) по всем статусам схемы; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 5.2, 4.1-4.3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op ∈ Operations: dispatcher(op) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, HttpError | BoundaryError, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per dispatch (Match lookup)\n\nimport { Effect, Match } from \"effect\"\nimport type { Operations } from \"../../tests/fixtures/petstore.openapi.js\"\nimport type { DecodeError, ResponsesFor } from \"../core/api-client/strict-types.js\"\nimport { asConst, type Json } from \"../core/axioms.js\"\nimport {\n createDispatcher,\n parseJSON,\n unexpectedContentType,\n unexpectedStatus\n} from \"../shell/api-client/strict-client.js\"\nimport * as Decoders from \"./decoders.js\"\n\n// Response types for each operation - used for type inference\ntype ListPetsResponses = ResponsesFor\ntype CreatePetResponses = ResponsesFor\ntype GetPetResponses = ResponsesFor\ntype DeletePetResponses = ResponsesFor\n\n/**\n * Helper: process JSON content type for a given status - returns SUCCESS variant\n * Used for 2xx responses that go to the success channel\n */\nconst processJsonContentSuccess = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n return asConst({\n status,\n contentType: \"application/json\" as const,\n body: decoded\n })\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Helper: process JSON content type for a given status - returns HTTP ERROR variant\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * Adds `_tag: \"HttpError\"` discriminator to distinguish from BoundaryError.\n */\nconst processJsonContentError = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n // Non-2xx: Return as FAILURE with _tag discriminator (goes to error channel)\n return yield* Effect.fail(asConst({\n _tag: \"HttpError\" as const,\n status,\n contentType: \"application/json\" as const,\n body: decoded\n }))\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Dispatcher for listPets\n * Handles statuses: 200 (success), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherlistPets = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodelistPets_200)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodelistPets_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for createPet\n * Handles statuses: 201 (success), 400 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 201: success channel → ApiSuccess\n * - 400, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchercreatePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(201, () => processJsonContentSuccess(201, contentType, text, Decoders.decodecreatePet_201)),\n Match.when(400, () => processJsonContentError(400, contentType, text, Decoders.decodecreatePet_400)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodecreatePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for getPet\n * Handles statuses: 200 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchergetPet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodegetPet_200)),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodegetPet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodegetPet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for deletePet\n * Handles statuses: 204 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 204: success channel → ApiSuccess (no content)\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherdeletePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(204, () =>\n Effect.succeed(\n asConst({\n status: 204,\n contentType: \"none\" as const,\n body: undefined\n })\n )),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodedeletePet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodedeletePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n","// CHANGE: Auto-generated dispatcher map by path+method\n// WHY: Provide a single dispatcher registry without manual wiring in examples\n// QUOTE(ТЗ): \"Этого в плане вообще не должно быть\"\n// REF: user-msg-3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ path, method: dispatchersByPath[path][method] = dispatcher(op)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: dispatcher map is total for all operations in Paths\n// COMPLEXITY: O(1)\n\nimport type { Paths } from \"../../tests/fixtures/petstore.openapi.js\"\nimport { type DispatchersFor, registerDefaultDispatchers } from \"../shell/api-client/create-client.js\"\nimport { dispatchercreatePet, dispatcherdeletePet, dispatchergetPet, dispatcherlistPets } from \"./dispatch.js\"\n\n/**\n * Dispatcher map keyed by OpenAPI path and HTTP method\n */\nexport const dispatchersByPath: DispatchersFor = {\n \"/pets\": {\n get: dispatcherlistPets,\n post: dispatchercreatePet\n },\n \"/pets/{petId}\": {\n get: dispatchergetPet,\n delete: dispatcherdeletePet\n }\n}\n\n// CHANGE: Register default dispatchers at module load\n// WHY: Enable createClient(options) without passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: createClient(options) uses dispatchersByPath\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: registerDefaultDispatchers is called exactly once per module load\n// COMPLEXITY: O(1)\nregisterDefaultDispatchers(dispatchersByPath)\n"],"names":["assertNever","x","asJson","value","asConst","asRawResponse","asDispatcher","fn","asStrictRequestInit","config","asStrictApiClient","client","asDispatchersFor","executeRequest","Effect","HttpClient","request","buildRequest","rawResponse","response","text","toNativeHeaders","error","HttpClientRequest","headers","toRecordHeaders","bodyText","HttpBody","result","key","headerPair","headerKey","headerValue","platformHeaders","createDispatcher","classify","contentType","parseJSON","status","unexpectedStatus","body","unexpectedContentType","expected","actual","createStrictClient","makeRequest","method","path","options","url","params","createUniversalDispatcher","is2xx","variant","parsed","defaultDispatchers","registerDefaultDispatchers","dispatchers","resolveDefaultDispatchers","buildUrl","baseUrl","query","searchParams","item","qs","isBodyInit","serializeBody","needsJsonContentType","toHeaders","headersInit","entry","mergeHeaders","clientHeaders","requestHeaders","optHeaders","createMethodHandler","clientOptions","dispatcher","createMethodHandlerWithDispatchers","createClient","resolvedDispatchers","createMethodHandlerWithUniversalDispatcher","isHttpErrorValue","exposeHttpErrorsAsValues","createMethodHandlerWithUniversalDispatcherValue","createClientEffect","decodelistPets_200","_status","_contentType","_body","decodelistPets_500","decodecreatePet_201","decodecreatePet_400","decodecreatePet_500","decodegetPet_200","decodegetPet_404","decodegetPet_500","decodedeletePet_404","decodedeletePet_500","processJsonContentSuccess","decoder","decoded","processJsonContentError","dispatcherlistPets","Match","Decoders.decodelistPets_200","Decoders.decodelistPets_500","dispatchercreatePet","Decoders.decodecreatePet_201","Decoders.decodecreatePet_400","Decoders.decodecreatePet_500","dispatchergetPet","Decoders.decodegetPet_200","Decoders.decodegetPet_404","Decoders.decodegetPet_500","dispatcherdeletePet","Decoders.decodedeletePet_404","Decoders.decodedeletePet_500","dispatchersByPath"],"mappings":"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"} \ No newline at end of file diff --git a/packages/app/dist/main.js b/packages/app/dist/main.js new file mode 100644 index 0000000..e9f2c3d --- /dev/null +++ b/packages/app/dist/main.js @@ -0,0 +1,2 @@ +import{NodeContext as m,NodeRuntime as r}from"@effect/platform-node";import{Match as t,pipe as a,Effect as n,Console as c}from"effect";import*as o from"@effect/schema/Schema";const f=e=>t.value(e).pipe(t.when({kind:"effect"},()=>"Hello from Effect!"),t.when({kind:"named"},({name:i})=>`Hello, ${i}!`),t.exhaustive),p=o.Struct({name:o.optionalWith(o.NonEmptyString,{default:()=>"Effect"})}),d=e=>e.name.toLowerCase()==="effect"?{kind:"effect"}:{kind:"named",name:e.name},l=a(n.sync(()=>process.argv.slice(2)),n.map(e=>e.length>0&&e[0]!==void 0?{name:e[0]}:{}),n.flatMap(o.decodeUnknown(p)),n.map(e=>d(e))),s=a(l,n.map(e=>f(e)),n.tap(c.log)),h=a(s,n.provide(m.layer));r.runMain(h); +//# sourceMappingURL=main.js.map diff --git a/packages/app/dist/main.js.map b/packages/app/dist/main.js.map new file mode 100644 index 0000000..2a954bb --- /dev/null +++ b/packages/app/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sources":["../src/core/greeting.ts","../src/shell/cli.ts","../src/app/program.ts","../src/app/main.ts"],"sourcesContent":["import { Match } from \"effect\"\n\nexport type GreetingVariant =\n | { readonly kind: \"effect\" }\n | { readonly kind: \"named\"; readonly name: string }\n\n/**\n * Formats a greeting message without side effects.\n *\n * @param variant - Non-empty, classified name information.\n * @returns Greeting text composed deterministically.\n *\n * @pure true\n * @invariant variant.kind === \"named\" ⇒ variant.name.length > 0\n * @complexity O(1) time / O(1) space\n */\nexport const formatGreeting = (variant: GreetingVariant): string =>\n Match.value(variant).pipe(\n Match.when({ kind: \"effect\" }, () => \"Hello from Effect!\"),\n Match.when({ kind: \"named\" }, ({ name }) => `Hello, ${name}!`),\n Match.exhaustive\n )\n","import * as S from \"@effect/schema/Schema\"\nimport { Effect, pipe } from \"effect\"\n\nimport type { GreetingVariant } from \"../core/greeting.js\"\n\nconst cliSchema = S.Struct({\n name: S.optionalWith(S.NonEmptyString, { default: () => \"Effect\" })\n})\n\ntype CliInput = S.Schema.Type\n\nconst toVariant = (input: CliInput): GreetingVariant =>\n input.name.toLowerCase() === \"effect\"\n ? { kind: \"effect\" }\n : { kind: \"named\", name: input.name }\n\nexport const readGreetingVariant = pipe(\n Effect.sync(() => process.argv.slice(2)),\n Effect.map((args) => args.length > 0 && args[0] !== undefined ? { name: args[0] } : {}),\n Effect.flatMap(S.decodeUnknown(cliSchema)),\n Effect.map((input) => toVariant(input))\n)\n","import { Console, Effect, pipe } from \"effect\"\n\nimport { formatGreeting } from \"../core/greeting.js\"\nimport { readGreetingVariant } from \"../shell/cli.js\"\n\n/**\n * Compose the CLI program as a single effect.\n *\n * @returns Effect that returns the greeting string and logs it once on success.\n *\n * @pure false - uses Console output\n * @effect Console\n * @invariant forall args in Argv: decode(args) = v -> logs exactly one greeting\n * @precondition true\n * @postcondition exists greeting: returned(greeting) and logged(greeting)\n * @complexity O(1)\n * @throws Never - all errors are typed in the Effect error channel\n */\n// CHANGE: extract the composed program into a reusable Effect\n// WHY: keep the entrypoint as a thin platform runtime shell and make testing deterministic\n// QUOTE(TZ): \"\\u0414\\u0430 \\u0434\\u0430\\u0432\\u0430\\u0439 \\u0442\\u0430\\u043a \\u044d\\u0442\\u043e \\u0431\\u043e\\u043b\\u0435\\u0435 \\u043f\\u0440\\u0430\\u0432\\u0438\\u043b\\u044c\\u043d\\u0430\\u044f \\u0440\\u0435\\u0430\\u043b\\u0438\\u0437\\u0430\\u0446\\u0438\\u044f\"\n// REF: user-2025-12-19-platform-node\n// SOURCE: https://effect.website/docs/platform/runtime/ \"runMain helps you execute a main effect with built-in error handling, logging, and signal management.\"\n// FORMAT THEOREM: forall args in Argv: decode(args) = v -> log(formatGreeting(v))\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: exactly one log entry per successful parse\n// COMPLEXITY: O(1)/O(1)\nexport const program = pipe(\n readGreetingVariant,\n Effect.map((variant) => formatGreeting(variant)),\n Effect.tap(Console.log)\n)\n","import { NodeContext, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, pipe } from \"effect\"\n\nimport { program } from \"./program.js\"\n\n// CHANGE: run the program through the Node platform runtime with its layer\n// WHY: ensure effects execute under the platform runtime with proper teardown/logging behavior\n// QUOTE(TZ): \"\\u0414\\u0430 \\u0434\\u0430\\u0432\\u0430\\u0439 \\u0442\\u0430\\u043a \\u044d\\u0442\\u043e \\u0431\\u043e\\u043b\\u0435\\u0435 \\u043f\\u0440\\u0430\\u0432\\u0438\\u043b\\u044c\\u043d\\u0430\\u044f \\u0440\\u0435\\u0430\\u043b\\u0438\\u0437\\u0430\\u0446\\u0438\\u044f\"\n// REF: user-2025-12-19-platform-node\n// SOURCE: https://effect.website/docs/platform/runtime/ \"runMain helps you execute a main effect with built-in error handling, logging, and signal management.\"\n// FORMAT THEOREM: forall args in Argv: decode(args) = v -> runMain(program)\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: program executed with NodeContext.layer\n// COMPLEXITY: O(1)/O(1)\nconst main = pipe(program, Effect.provide(NodeContext.layer))\n\nNodeRuntime.runMain(main)\n"],"names":["formatGreeting","variant","Match","name","cliSchema","S","toVariant","input","readGreetingVariant","pipe","Effect","args","program","Console","main","NodeContext","NodeRuntime"],"mappings":"+KAgBO,MAAMA,EAAkBC,GAC7BC,EAAM,MAAMD,CAAO,EAAE,KACnBC,EAAM,KAAK,CAAE,KAAM,QAAA,EAAY,IAAM,oBAAoB,EACzDA,EAAM,KAAK,CAAE,KAAM,OAAA,EAAW,CAAC,CAAE,KAAAC,CAAA,IAAW,UAAUA,CAAI,GAAG,EAC7DD,EAAM,UACR,EChBIE,EAAYC,EAAE,OAAO,CACzB,KAAMA,EAAE,aAAaA,EAAE,eAAgB,CAAE,QAAS,IAAM,QAAA,CAAU,CACpE,CAAC,EAIKC,EAAaC,GACjBA,EAAM,KAAK,gBAAkB,SACzB,CAAE,KAAM,UACR,CAAE,KAAM,QAAS,KAAMA,EAAM,IAAA,EAEtBC,EAAsBC,EACjCC,EAAO,KAAK,IAAM,QAAQ,KAAK,MAAM,CAAC,CAAC,EACvCA,EAAO,IAAKC,GAASA,EAAK,OAAS,GAAKA,EAAK,CAAC,IAAM,OAAY,CAAE,KAAMA,EAAK,CAAC,CAAA,EAAM,EAAE,EACtFD,EAAO,QAAQL,EAAE,cAAcD,CAAS,CAAC,EACzCM,EAAO,IAAKH,GAAUD,EAAUC,CAAK,CAAC,CACxC,ECOaK,EAAUH,EACrBD,EACAE,EAAO,IAAKT,GAAYD,EAAeC,CAAO,CAAC,EAC/CS,EAAO,IAAIG,EAAQ,GAAG,CACxB,ECjBMC,EAAOL,EAAKG,EAASF,EAAO,QAAQK,EAAY,KAAK,CAAC,EAE5DC,EAAY,QAAQF,CAAI"} \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index 8c542ce..f452ba7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,14 +1,19 @@ { - "name": "@effect-template/app", + "name": "openapi-effect", "version": "1.0.16", - "description": "Minimal Vite-powered TypeScript console starter using Effect", - "main": "dist/main.js", - "directories": { - "doc": "doc" + "description": "Drop-in replacement for openapi-fetch with an opt-in Effect API", + "type": "module", + "main": "dist/index.js", + "exports": { + ".": { + "types": "./src/index.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } }, "scripts": { - "build": "vite build --ssr src/app/main.ts", - "dev": "vite build --watch --ssr src/app/main.ts", + "build": "vite build", + "dev": "vite build --watch", "lint": "npx @ton-ai-core/vibecode-linter src/", "lint:tests": "npx @ton-ai-core/vibecode-linter tests/", "lint:effect": "npx eslint --config eslint.effect-ts-check.config.mjs .", @@ -32,7 +37,6 @@ ], "author": "", "license": "ISC", - "type": "module", "bugs": { "url": "https://github.com/ProverCoderAI/effect-template/issues" }, @@ -52,6 +56,7 @@ "@effect/typeclass": "^0.38.0", "@effect/workflow": "^0.16.0", "effect": "^3.19.16", + "openapi-fetch": "^0.15.2", "openapi-typescript-helpers": "^0.1.0", "ts-morph": "^27.0.2" }, diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index b07cca5..cec08a4 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -1,57 +1,39 @@ -// CHANGE: Main entry point for openapi-effect package with Effect-native error handling -// WHY: Enable default import of createClient function with proper error channel design -// QUOTE(ТЗ): "import createClient from \"openapi-effect\"" -// REF: PR#3 comment from skulidropek about Effect representation +// CHANGE: Make openapi-effect a drop-in replacement for openapi-fetch (Promise API), with an opt-in Effect API. +// WHY: Consumer projects must be able to swap openapi-fetch -> openapi-effect with near-zero code changes. +// QUOTE(ТЗ): "openapi-effect должен почти 1 в 1 заменяться с openapi-fetch" / "Просто добавлять effect поведение" // SOURCE: n/a // PURITY: SHELL (re-exports) // COMPLEXITY: O(1) -// High-level API (recommended for most users) -export { createClient as default } from "./shell/api-client/create-client.js" +// Promise-based client (openapi-fetch compatible) +export { default } from "openapi-fetch" +export { default as createClient } from "openapi-fetch" +export * from "openapi-fetch" + +// Effect-based client (opt-in) +export * as FetchHttpClient from "@effect/platform/FetchHttpClient" + +// Strict Effect client (advanced) +export type * from "./core/api-client/index.js" +export { assertNever } from "./core/api-client/index.js" + export type { - ClientEffect, - ClientOptions, DispatchersFor, StrictApiClient, StrictApiClientWithDispatchers } from "./shell/api-client/create-client.js" -export { createClientEffect, registerDefaultDispatchers } from "./shell/api-client/create-client.js" - -// Core types (for advanced type manipulation) -// Effect Channel Design: -// - ApiSuccess: 2xx responses → success channel -// - ApiFailure: HttpError (4xx, 5xx) + BoundaryError → error channel -export type { - ApiFailure, - ApiSuccess, - BodyFor, - BoundaryError, - ContentTypesFor, - DecodeError, - HttpError, - HttpErrorResponseVariant, - HttpErrorVariants, - OperationFor, - ParseError, - PathsForMethod, - ResponsesFor, - ResponseVariant, - StatusCodes, - SuccessVariants, - TransportError, - UnexpectedContentType, - UnexpectedStatus -} from "./core/api-client/index.js" -// Shell utilities (for custom implementations) export type { Decoder, Dispatcher, RawResponse, StrictClient, StrictRequestInit } from "./shell/api-client/index.js" export { + createClient as createClientStrict, + createClientEffect, createDispatcher, createStrictClient, createUniversalDispatcher, executeRequest, parseJSON, + registerDefaultDispatchers, unexpectedContentType, unexpectedStatus } from "./shell/api-client/index.js" diff --git a/packages/app/src/shell/api-client/create-client-types.ts b/packages/app/src/shell/api-client/create-client-types.ts index 762d90d..34970e7 100644 --- a/packages/app/src/shell/api-client/create-client-types.ts +++ b/packages/app/src/shell/api-client/create-client-types.ts @@ -10,6 +10,7 @@ import type * as HttpClient from "@effect/platform/HttpClient" import type { Effect } from "effect" +import type { ClientOptions as OpenapiFetchClientOptions } from "openapi-fetch" import type { HttpMethod } from "openapi-typescript-helpers" import type { @@ -28,12 +29,7 @@ import type { Dispatcher } from "../../core/axioms.js" * * @pure - immutable configuration */ -export type ClientOptions = { - readonly baseUrl: string - readonly credentials?: RequestCredentials - readonly headers?: HeadersInit - readonly fetch?: typeof globalThis.fetch -} +export type ClientOptions = OpenapiFetchClientOptions // CHANGE: Add dispatcher map type for auto-dispatching clients // WHY: Enable creating clients that infer dispatcher from path+method without per-call parameter diff --git a/packages/app/src/shell/api-client/create-client.ts b/packages/app/src/shell/api-client/create-client.ts index 8344dc5..540400a 100644 --- a/packages/app/src/shell/api-client/create-client.ts +++ b/packages/app/src/shell/api-client/create-client.ts @@ -96,7 +96,7 @@ const resolveDefaultDispatchers = (): DispatchersFor, query?: Record @@ -105,29 +105,38 @@ const buildUrl = ( let url = path if (params) { for (const [key, value] of Object.entries(params)) { - url = url.replace(`{${key}}`, encodeURIComponent(String(value))) + url = url.replace("{" + key + "}", encodeURIComponent(String(value))) } } - // Construct full URL - const fullUrl = new URL(url, baseUrl) - // Add query parameters if (query) { + const searchParams = new URLSearchParams() for (const [key, value] of Object.entries(query)) { if (Array.isArray(value)) { for (const item of value) { - fullUrl.searchParams.append(key, String(item)) + searchParams.append(key, String(item)) } } else { - fullUrl.searchParams.set(key, String(value)) + searchParams.set(key, String(value)) } } + const qs = searchParams.toString() + if (qs.length > 0) { + url = url.includes("?") ? url + "&" + qs : url + "?" + qs + } } - return fullUrl.toString() + // If baseUrl isn't provided, keep a relative URL (browser-friendly) + if (baseUrl === undefined || baseUrl === "") { + return url + } + + // Construct full URL + return new URL(url, baseUrl).toString() } + /** * Check if body is already a BodyInit type (not a plain object needing serialization) * @@ -178,16 +187,47 @@ const needsJsonContentType = (body: BodyInit | object | undefined): boolean => * @pure true * @complexity O(n) where n = number of headers */ +const toHeaders = (headersInit: ClientOptions["headers"] | undefined): Headers => { + const headers = new Headers() + if (headersInit === undefined) { + return headers + } + + if (headersInit instanceof Headers) { + return new Headers(headersInit) + } + + if (Array.isArray(headersInit)) { + for (const entry of headersInit) { + if (Array.isArray(entry) && entry.length === 2) { + headers.set(String(entry[0]), String(entry[1])) + } + } + return headers + } + + for (const [key, value] of Object.entries(headersInit)) { + if (value === null || value === undefined) { + continue + } + if (Array.isArray(value)) { + headers.set(key, value.map(String).join(",")) + continue + } + headers.set(key, String(value)) + } + + return headers +} + const mergeHeaders = ( - clientHeaders: HeadersInit | undefined, - requestHeaders: HeadersInit | undefined + clientHeaders: ClientOptions["headers"] | undefined, + requestHeaders: ClientOptions["headers"] | undefined ): Headers => { - const headers = new Headers(clientHeaders) - if (requestHeaders) { - const optHeaders = new Headers(requestHeaders) - for (const [key, value] of optHeaders.entries()) { - headers.set(key, value) - } + const headers = toHeaders(clientHeaders) + const optHeaders = toHeaders(requestHeaders) + for (const [key, value] of optHeaders.entries()) { + headers.set(key, value) } return headers } @@ -201,7 +241,7 @@ type MethodHandlerOptions = { params?: Record | undefined query?: Record | undefined body?: BodyInit | object | undefined - headers?: HeadersInit | undefined + headers?: ClientOptions["headers"] | undefined signal?: AbortSignal | undefined } diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts index cfd0bb5..249b519 100644 --- a/packages/app/vite.config.ts +++ b/packages/app/vite.config.ts @@ -1,3 +1,4 @@ +import fs from "node:fs" import path from "node:path" import { fileURLToPath } from "node:url" import { defineConfig } from "vite" @@ -6,6 +7,21 @@ import tsconfigPaths from "vite-tsconfig-paths" const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) +type Pkg = { + dependencies?: Record | undefined + peerDependencies?: Record | undefined +} + +// CHANGE: Build both the library entry (src/index.ts) and the CLI entry (src/app/main.ts). +// WHY: Consumers need a JS entrypoint in dist for `import "openapi-effect"`, while we keep the template CLI working. +// SOURCE: n/a +const pkgPath = path.resolve(__dirname, "package.json") +const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as Pkg +const dependencies = [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.peerDependencies ?? {})] + +const isExternal = (id: string): boolean => + dependencies.some((dep) => id === dep || id.startsWith(`${dep}/`)) + export default defineConfig({ plugins: [tsconfigPaths()], publicDir: false, @@ -18,15 +34,17 @@ export default defineConfig({ target: "node20", outDir: "dist", sourcemap: true, - ssr: "src/app/main.ts", rollupOptions: { + preserveEntrySignatures: "exports-only", + input: { + index: path.resolve(__dirname, "src/index.ts"), + main: path.resolve(__dirname, "src/app/main.ts") + }, + external: isExternal, output: { format: "es", - entryFileNames: "main.js" + entryFileNames: "[name].js" } } - }, - ssr: { - target: "node" } }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 238f79d..c1ecbb7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,6 +61,9 @@ importers: effect: specifier: ^3.19.16 version: 3.19.16 + openapi-fetch: + specifier: ^0.15.2 + version: 0.15.2 openapi-typescript-helpers: specifier: ^0.1.0 version: 0.1.0 @@ -2729,6 +2732,12 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + openapi-fetch@0.15.2: + resolution: {integrity: sha512-rdYTzUmSsJevmNqg7fwUVGuKc2Gfb9h6ph74EVPkPfIGJaZTfqdIbJahtbJ3qg1LKinln30hqZniLnKpH0RJBg==} + + openapi-typescript-helpers@0.0.15: + resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} + openapi-typescript-helpers@0.1.0: resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==} @@ -6284,6 +6293,12 @@ snapshots: dependencies: mimic-fn: 2.1.0 + openapi-fetch@0.15.2: + dependencies: + openapi-typescript-helpers: 0.0.15 + + openapi-typescript-helpers@0.0.15: {} + openapi-typescript-helpers@0.1.0: {} optionator@0.9.4: From cf2d573ee64f5837a36a07f04d1ca9aa81be582d Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 12 Feb 2026 15:47:31 +0000 Subject: [PATCH 2/4] fix(lint): reduce complexity in create-client helpers --- packages/app/dist/index.js | 2 +- packages/app/dist/index.js.map | 2 +- .../app/src/shell/api-client/create-client.ts | 135 +++++++++++------- 3 files changed, 83 insertions(+), 56 deletions(-) diff --git a/packages/app/dist/index.js b/packages/app/dist/index.js index 475bb92..3ab49d1 100644 --- a/packages/app/dist/index.js +++ b/packages/app/dist/index.js @@ -1,2 +1,2 @@ -export*from"openapi-fetch";import{default as ve,default as Ee}from"openapi-fetch";import*as de from"@effect/platform/FetchHttpClient";import*as D from"@effect/platform/HttpBody";import*as R from"@effect/platform/HttpClient";import*as d from"@effect/platform/HttpClientRequest";import{Effect as c,Match as a}from"effect";const le=e=>{throw new Error(`Unexpected value: ${JSON.stringify(e)}`)},A=e=>e,v=e=>e,O=e=>e,T=e=>e,H=e=>e,b=e=>e,U=e=>e,w=e=>c.gen(function*(){const r=yield*R.HttpClient,t=q(e),n=yield*c.mapError(c.gen(function*(){const s=yield*r.execute(t),o=yield*s.text;return O({status:s.status,headers:J(s.headers),text:o})}),s=>({_tag:"TransportError",error:s instanceof Error?s:new Error(String(s))}));return yield*e.dispatcher(n)}),q=e=>{let n=({get:d.get,post:d.post,put:d.put,patch:d.patch,delete:d.del,head:d.head,options:d.options}[e.method]??d.get)(e.url);if(e.headers!==void 0){const s=k(e.headers);n=d.setHeaders(n,s)}if(e.body!==void 0){const s=typeof e.body=="string"?e.body:JSON.stringify(e.body);n=d.setBody(n,D.text(s))}return n},k=e=>{if(e instanceof Headers){const r={};for(const[t,n]of e.entries())r[t]=n;return r}if(Array.isArray(e)){const r={};for(const t of e){const[n,s]=t;r[n]=s}return r}return e},J=e=>{const r=new Headers;for(const[t,n]of Object.entries(e))r.set(t,n);return r},h=e=>T(r=>{const t=r.headers.get("content-type")??void 0;return e(r.status,t,r.text)}),E=(e,r,t)=>c.try({try:()=>A(JSON.parse(t)),catch:n=>({_tag:"ParseError",status:e,contentType:r,error:n instanceof Error?n:new Error(String(n)),body:t})}),g=(e,r)=>({_tag:"UnexpectedStatus",status:e,body:r}),S=(e,r,t,n)=>({_tag:"UnexpectedContentType",status:e,expected:r,actual:t,body:n}),pe=()=>{const e=(r,t,n)=>{let s=`${n.baseUrl}${String(t)}`;if(n.params!==void 0)for(const[i,u]of Object.entries(n.params))s=s.replace(`{${i}}`,encodeURIComponent(String(u)));if(n.query!==void 0){const i=new URLSearchParams;for(const[u,l]of Object.entries(n.query))i.append(u,String(l));s=`${s}?${i.toString()}`}const o=H({method:r,url:s,dispatcher:n.dispatcher,...n.headers!==void 0&&{headers:n.headers},...n.body!==void 0&&{body:n.body},...n.signal!==void 0&&{signal:n.signal}});return w(o)};return{GET:(r,t)=>e("get",r,t),POST:(r,t)=>e("post",r,t),PUT:(r,t)=>e("put",r,t),PATCH:(r,t)=>e("patch",r,t),DELETE:(r,t)=>e("delete",r,t)}},N=()=>T(e=>{const r=e.headers.get("content-type")??void 0,t=e.status>=200&&e.status<300;if(e.text===""||e.status===204){const n={status:e.status,contentType:"none",body:void 0};return t?c.succeed(n):c.fail({_tag:"HttpError",...n})}return r?.includes("application/json")?c.gen(function*(){const n=yield*E(e.status,"application/json",e.text),s={status:e.status,contentType:"application/json",body:n};return t?s:yield*c.fail({_tag:"HttpError",...s})}):c.fail(S(e.status,["application/json"],r,e.text))});let _;const B=e=>{_=e},x=()=>{if(_===void 0)throw new Error("Default dispatchers are not registered. Import generated dispatchers module.");return U(_)},L=(e,r,t,n)=>{let s=r;if(t)for(const[o,i]of Object.entries(t))s=s.replace("{"+o+"}",encodeURIComponent(String(i)));if(n){const o=new URLSearchParams;for(const[u,l]of Object.entries(n))if(Array.isArray(l))for(const C of l)o.append(u,String(C));else o.set(u,String(l));const i=o.toString();i.length>0&&(s=s.includes("?")?s+"&"+i:s+"?"+i)}return e===void 0||e===""?s:new URL(s,e).toString()},M=e=>typeof e=="string"||e instanceof Blob||e instanceof ArrayBuffer||e instanceof ReadableStream||e instanceof FormData||e instanceof URLSearchParams,$=e=>e===void 0||M(e)?e:JSON.stringify(e),F=e=>e!==void 0&&typeof e!="string"&&!(e instanceof Blob)&&!(e instanceof FormData),P=e=>{const r=new Headers;if(e===void 0)return r;if(e instanceof Headers)return new Headers(e);if(Array.isArray(e)){for(const t of e)Array.isArray(t)&&t.length===2&&r.set(String(t[0]),String(t[1]));return r}for(const[t,n]of Object.entries(e))if(n!=null){if(Array.isArray(n)){r.set(t,n.map(String).join(","));continue}r.set(t,String(n))}return r},I=(e,r)=>{const t=P(e),n=P(r);for(const[s,o]of n.entries())t.set(s,o);return t},j=(e,r)=>(t,n,s)=>{const o=L(r.baseUrl,t,s?.params,s?.query),i=I(r.headers,s?.headers),u=$(s?.body);F(s?.body)&&i.set("Content-Type","application/json");const l=H({method:e,url:o,dispatcher:n,headers:i,body:u,signal:s?.signal});return w(l)},p=(e,r,t)=>(n,s)=>j(e,r)(n,t[n][e],s),fe=(e,r)=>{const t=r??x();return b({GET:p("get",e,t),POST:p("post",e,t),PUT:p("put",e,t),DELETE:p("delete",e,t),PATCH:p("patch",e,t),HEAD:p("head",e,t),OPTIONS:p("options",e,t)})},V=(e,r)=>(t,n)=>j(e,r)(t,N(),n),z=e=>typeof e=="object"&&e!==null&&"_tag"in e&&Reflect.get(e,"_tag")==="HttpError",G=e=>e.pipe(c.catchIf(r=>z(r),r=>c.succeed(r))),f=(e,r)=>(t,n)=>G(V(e,r)(t,n)),ye=e=>b({GET:f("get",e),POST:f("post",e),PUT:f("put",e),DELETE:f("delete",e),PATCH:f("patch",e),HEAD:f("head",e),OPTIONS:f("options",e)}),W=(e,r,t,n)=>c.succeed(n),K=(e,r,t,n)=>c.succeed(n),Q=(e,r,t,n)=>c.succeed(n),X=(e,r,t,n)=>c.succeed(n),Y=(e,r,t,n)=>c.succeed(n),Z=(e,r,t,n)=>c.succeed(n),ee=(e,r,t,n)=>c.succeed(n),te=(e,r,t,n)=>c.succeed(n),re=(e,r,t,n)=>c.succeed(n),ne=(e,r,t,n)=>c.succeed(n),m=(e,r,t,n)=>r?.includes("application/json")?c.gen(function*(){const s=yield*E(e,"application/json",t),o=yield*n(e,"application/json",t,s);return v({status:e,contentType:"application/json",body:o})}):c.fail(S(e,["application/json"],r,t)),y=(e,r,t,n)=>r?.includes("application/json")?c.gen(function*(){const s=yield*E(e,"application/json",t),o=yield*n(e,"application/json",t,s);return yield*c.fail(v({_tag:"HttpError",status:e,contentType:"application/json",body:o}))}):c.fail(S(e,["application/json"],r,t)),se=h((e,r,t)=>a.value(e).pipe(a.when(200,()=>m(200,r,t,W)),a.when(500,()=>y(500,r,t,K)),a.orElse(()=>c.fail(g(e,t))))),ce=h((e,r,t)=>a.value(e).pipe(a.when(201,()=>m(201,r,t,Q)),a.when(400,()=>y(400,r,t,X)),a.when(500,()=>y(500,r,t,Y)),a.orElse(()=>c.fail(g(e,t))))),ae=h((e,r,t)=>a.value(e).pipe(a.when(200,()=>m(200,r,t,Z)),a.when(404,()=>y(404,r,t,ee)),a.when(500,()=>y(500,r,t,te)),a.orElse(()=>c.fail(g(e,t))))),oe=h((e,r,t)=>a.value(e).pipe(a.when(204,()=>c.succeed(v({status:204,contentType:"none",body:void 0}))),a.when(404,()=>y(404,r,t,re)),a.when(500,()=>y(500,r,t,ne)),a.orElse(()=>c.fail(g(e,t))))),ie={"/pets":{get:se,post:ce},"/pets/{petId}":{get:ae,delete:oe}};B(ie);export{de as FetchHttpClient,le as assertNever,ve as createClient,ye as createClientEffect,fe as createClientStrict,h as createDispatcher,pe as createStrictClient,N as createUniversalDispatcher,Q as decodecreatePet_201,X as decodecreatePet_400,Y as decodecreatePet_500,re as decodedeletePet_404,ne as decodedeletePet_500,Z as decodegetPet_200,ee as decodegetPet_404,te as decodegetPet_500,W as decodelistPets_200,K as decodelistPets_500,Ee as default,ce as dispatchercreatePet,oe as dispatcherdeletePet,ae as dispatchergetPet,se as dispatcherlistPets,ie as dispatchersByPath,w as executeRequest,E as parseJSON,B as registerDefaultDispatchers,S as unexpectedContentType,g as unexpectedStatus}; +export*from"openapi-fetch";import{default as Se,default as He}from"openapi-fetch";import*as fe from"@effect/platform/FetchHttpClient";import*as C from"@effect/platform/HttpBody";import*as D from"@effect/platform/HttpClient";import*as i from"@effect/platform/HttpClientRequest";import{Effect as c,Match as a}from"effect";const he=e=>{throw new Error(`Unexpected value: ${JSON.stringify(e)}`)},R=e=>e,v=e=>e,O=e=>e,H=e=>e,T=e=>e,b=e=>e,U=e=>e,w=e=>c.gen(function*(){const t=yield*D.HttpClient,r=A(e),n=yield*c.mapError(c.gen(function*(){const s=yield*t.execute(r),o=yield*s.text;return O({status:s.status,headers:k(s.headers),text:o})}),s=>({_tag:"TransportError",error:s instanceof Error?s:new Error(String(s))}));return yield*e.dispatcher(n)}),A=e=>{let n=({get:i.get,post:i.post,put:i.put,patch:i.patch,delete:i.del,head:i.head,options:i.options}[e.method]??i.get)(e.url);if(e.headers!==void 0){const s=q(e.headers);n=i.setHeaders(n,s)}if(e.body!==void 0){const s=typeof e.body=="string"?e.body:JSON.stringify(e.body);n=i.setBody(n,C.text(s))}return n},q=e=>{if(e instanceof Headers){const t={};for(const[r,n]of e.entries())t[r]=n;return t}if(Array.isArray(e)){const t={};for(const r of e){const[n,s]=r;t[n]=s}return t}return e},k=e=>{const t=new Headers;for(const[r,n]of Object.entries(e))t.set(r,n);return t},y=e=>H(t=>{const r=t.headers.get("content-type")??void 0;return e(t.status,r,t.text)}),m=(e,t,r)=>c.try({try:()=>R(JSON.parse(r)),catch:n=>({_tag:"ParseError",status:e,contentType:t,error:n instanceof Error?n:new Error(String(n)),body:r})}),h=(e,t)=>({_tag:"UnexpectedStatus",status:e,body:t}),E=(e,t,r,n)=>({_tag:"UnexpectedContentType",status:e,expected:t,actual:r,body:n}),ge=()=>{const e=(t,r,n)=>{let s=`${n.baseUrl}${String(r)}`;if(n.params!==void 0)for(const[d,f]of Object.entries(n.params))s=s.replace(`{${d}}`,encodeURIComponent(String(f)));if(n.query!==void 0){const d=new URLSearchParams;for(const[f,g]of Object.entries(n.query))d.append(f,String(g));s=`${s}?${d.toString()}`}const o=T({method:t,url:s,dispatcher:n.dispatcher,...n.headers!==void 0&&{headers:n.headers},...n.body!==void 0&&{body:n.body},...n.signal!==void 0&&{signal:n.signal}});return w(o)};return{GET:(t,r)=>e("get",t,r),POST:(t,r)=>e("post",t,r),PUT:(t,r)=>e("put",t,r),PATCH:(t,r)=>e("patch",t,r),DELETE:(t,r)=>e("delete",t,r)}},B=()=>H(e=>{const t=e.headers.get("content-type")??void 0,r=e.status>=200&&e.status<300;if(e.text===""||e.status===204){const n={status:e.status,contentType:"none",body:void 0};return r?c.succeed(n):c.fail({_tag:"HttpError",...n})}return t?.includes("application/json")?c.gen(function*(){const n=yield*m(e.status,"application/json",e.text),s={status:e.status,contentType:"application/json",body:n};return r?s:yield*c.fail({_tag:"HttpError",...s})}):c.fail(E(e.status,["application/json"],t,e.text))});let _;const J=e=>{_=e},N=()=>{if(_===void 0)throw new Error("Default dispatchers are not registered. Import generated dispatchers module.");return U(_)},x=(e,t)=>{if(t===void 0)return e;let r=e;for(const[n,s]of Object.entries(t))r=r.replace("{"+n+"}",encodeURIComponent(String(s)));return r},L=e=>{if(e===void 0)return"";const t=new URLSearchParams;for(const[r,n]of Object.entries(e)){if(Array.isArray(n)){for(const s of n)t.append(r,String(s));continue}t.set(r,String(n))}return t.toString()},M=(e,t)=>t.length===0?e:e.includes("?")?e+"&"+t:e+"?"+t,$=(e,t)=>e===void 0||e===""?t:new URL(t,e).toString(),F=(e,t,r,n)=>{const s=x(t,r),o=L(n),d=M(s,o);return $(e,d)},W=e=>typeof e=="string"||e instanceof Blob||e instanceof ArrayBuffer||e instanceof ReadableStream||e instanceof FormData||e instanceof URLSearchParams,I=e=>e===void 0||W(e)?e:JSON.stringify(e),V=e=>e!==void 0&&typeof e!="string"&&!(e instanceof Blob)&&!(e instanceof FormData),z=e=>{const t=new Headers;for(const[r,n]of Object.entries(e))if(n!=null){if(Array.isArray(n)){t.set(r,n.map(String).join(","));continue}t.set(r,String(n))}return t},S=e=>e===void 0?new Headers:e instanceof Headers?new Headers(e):Array.isArray(e)?new Headers(e):z(e),G=(e,t)=>{const r=S(e),n=S(t);for(const[s,o]of n.entries())r.set(s,o);return r},j=(e,t)=>(r,n,s)=>{const o=F(t.baseUrl,r,s?.params,s?.query),d=G(t.headers,s?.headers),f=I(s?.body);V(s?.body)&&d.set("Content-Type","application/json");const g=T({method:e,url:o,dispatcher:n,headers:d,body:f,signal:s?.signal});return w(g)},u=(e,t,r)=>(n,s)=>j(e,t)(n,r[n][e],s),_e=(e,t)=>{const r=t??N();return b({GET:u("get",e,r),POST:u("post",e,r),PUT:u("put",e,r),DELETE:u("delete",e,r),PATCH:u("patch",e,r),HEAD:u("head",e,r),OPTIONS:u("options",e,r)})},Q=(e,t)=>(r,n)=>j(e,t)(r,B(),n),K=e=>typeof e=="object"&&e!==null&&"_tag"in e&&Reflect.get(e,"_tag")==="HttpError",X=e=>e.pipe(c.catchIf(t=>K(t),t=>c.succeed(t))),l=(e,t)=>(r,n)=>X(Q(e,t)(r,n)),ve=e=>b({GET:l("get",e),POST:l("post",e),PUT:l("put",e),DELETE:l("delete",e),PATCH:l("patch",e),HEAD:l("head",e),OPTIONS:l("options",e)}),Y=(e,t,r,n)=>c.succeed(n),Z=(e,t,r,n)=>c.succeed(n),ee=(e,t,r,n)=>c.succeed(n),te=(e,t,r,n)=>c.succeed(n),re=(e,t,r,n)=>c.succeed(n),ne=(e,t,r,n)=>c.succeed(n),se=(e,t,r,n)=>c.succeed(n),ce=(e,t,r,n)=>c.succeed(n),ae=(e,t,r,n)=>c.succeed(n),oe=(e,t,r,n)=>c.succeed(n),P=(e,t,r,n)=>t?.includes("application/json")?c.gen(function*(){const s=yield*m(e,"application/json",r),o=yield*n(e,"application/json",r,s);return v({status:e,contentType:"application/json",body:o})}):c.fail(E(e,["application/json"],t,r)),p=(e,t,r,n)=>t?.includes("application/json")?c.gen(function*(){const s=yield*m(e,"application/json",r),o=yield*n(e,"application/json",r,s);return yield*c.fail(v({_tag:"HttpError",status:e,contentType:"application/json",body:o}))}):c.fail(E(e,["application/json"],t,r)),ie=y((e,t,r)=>a.value(e).pipe(a.when(200,()=>P(200,t,r,Y)),a.when(500,()=>p(500,t,r,Z)),a.orElse(()=>c.fail(h(e,r))))),de=y((e,t,r)=>a.value(e).pipe(a.when(201,()=>P(201,t,r,ee)),a.when(400,()=>p(400,t,r,te)),a.when(500,()=>p(500,t,r,re)),a.orElse(()=>c.fail(h(e,r))))),ue=y((e,t,r)=>a.value(e).pipe(a.when(200,()=>P(200,t,r,ne)),a.when(404,()=>p(404,t,r,se)),a.when(500,()=>p(500,t,r,ce)),a.orElse(()=>c.fail(h(e,r))))),le=y((e,t,r)=>a.value(e).pipe(a.when(204,()=>c.succeed(v({status:204,contentType:"none",body:void 0}))),a.when(404,()=>p(404,t,r,ae)),a.when(500,()=>p(500,t,r,oe)),a.orElse(()=>c.fail(h(e,r))))),pe={"/pets":{get:ie,post:de},"/pets/{petId}":{get:ue,delete:le}};J(pe);export{fe as FetchHttpClient,he as assertNever,Se as createClient,ve as createClientEffect,_e as createClientStrict,y as createDispatcher,ge as createStrictClient,B as createUniversalDispatcher,ee as decodecreatePet_201,te as decodecreatePet_400,re as decodecreatePet_500,ae as decodedeletePet_404,oe as decodedeletePet_500,ne as decodegetPet_200,se as decodegetPet_404,ce as decodegetPet_500,Y as decodelistPets_200,Z as decodelistPets_500,He as default,de as dispatchercreatePet,le as dispatcherdeletePet,ue as dispatchergetPet,ie as dispatcherlistPets,pe as dispatchersByPath,w as executeRequest,m as parseJSON,J as registerDefaultDispatchers,E as unexpectedContentType,h as unexpectedStatus}; //# sourceMappingURL=index.js.map diff --git a/packages/app/dist/index.js.map b/packages/app/dist/index.js.map index 5db398f..9197be9 100644 --- a/packages/app/dist/index.js.map +++ b/packages/app/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sources":["../src/core/api-client/strict-types.ts","../src/core/axioms.ts","../src/shell/api-client/strict-client.ts","../src/shell/api-client/create-client.ts","../src/generated/decoders.ts","../src/generated/dispatch.ts","../src/generated/dispatchers-by-path.ts"],"sourcesContent":["// CHANGE: Define core type-level operations for extracting OpenAPI types\n// WHY: Enable compile-time type safety without runtime overhead through pure type transformations\n// QUOTE(ТЗ): \"Success / HttpError являются коррелированными суммами (status → точный тип body) строго из OpenAPI типов\"\n// REF: issue-2, section 3.1, 4.1-4.3\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Op ∈ Operations: ResponseVariant = Success ⊎ Failure\n// PURITY: CORE\n// INVARIANT: All types computed at compile time, no runtime operations\n// COMPLEXITY: O(1) compile-time / O(0) runtime\n\nimport type { HttpMethod, PathsWithMethod } from \"openapi-typescript-helpers\"\n\n/**\n * Extract all paths that support a given HTTP method\n *\n * @pure true - compile-time only\n * @invariant Result ⊆ paths\n */\nexport type PathsForMethod<\n Paths extends object,\n Method extends HttpMethod\n> = PathsWithMethod\n\n/**\n * Extract operation definition for a path and method\n *\n * @pure true - compile-time only\n * @invariant ∀ path ∈ Paths, method ∈ Methods: Operation = Paths[path][method]\n */\nexport type OperationFor<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = Method extends keyof Paths[Path] ? Paths[Path][Method] : never\n\n/**\n * Extract all response definitions from an operation\n *\n * @pure true - compile-time only\n */\nexport type ResponsesFor = Op extends { responses: infer R } ? R : never\n\n// ============================================================================\n// Request-side typing (path/method → params/query/body)\n// ============================================================================\n\n/**\n * Extract path parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns path params type or undefined if none\n */\nexport type PathParamsFor = Op extends { parameters: { path: infer P } }\n ? P extends Record ? Record\n : never\n : undefined\n\n/**\n * Extract query parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns query params type or undefined if none\n */\nexport type QueryParamsFor = Op extends { parameters: { query?: infer Q } } ? Q\n : undefined\n\n/**\n * Extract request body type from operation\n *\n * @pure true - compile-time only\n * @invariant Returns body type or undefined if no requestBody\n */\nexport type RequestBodyFor = Op extends { requestBody: { content: infer C } }\n ? C extends { \"application/json\": infer J } ? J\n : C extends { [key: string]: infer V } ? V\n : never\n : undefined\n\n/**\n * Check if path params are required\n *\n * @pure true - compile-time only\n */\n\nexport type HasRequiredPathParams = Op extends { parameters: { path: infer P } }\n ? P extends Record ? keyof P extends never ? false : true\n : false\n : false\n\n/**\n * Check if request body is required\n *\n * @pure true - compile-time only\n */\nexport type HasRequiredBody = Op extends { requestBody: infer RB } ? RB extends { content: object } ? true\n : false\n : false\n\n/**\n * Build request options type from operation with all constraints\n * - params: required if path has required parameters\n * - query: optional, typed from operation\n * - body: required if operation has requestBody (accepts typed object OR string)\n *\n * For request body:\n * - Users can pass either the typed object (preferred, for type safety)\n * - Or a pre-stringified JSON string with headers (for backwards compatibility)\n *\n * @pure true - compile-time only\n * @invariant Options type is fully derived from operation definition\n */\nexport type RequestOptionsFor =\n & (HasRequiredPathParams extends true ? { readonly params: PathParamsFor }\n : { readonly params?: PathParamsFor })\n & (HasRequiredBody extends true ? { readonly body: RequestBodyFor | BodyInit }\n : { readonly body?: RequestBodyFor | BodyInit })\n & { readonly query?: QueryParamsFor }\n & { readonly headers?: HeadersInit }\n & { readonly signal?: AbortSignal }\n\n/**\n * Extract status codes from responses\n *\n * @pure true - compile-time only\n * @invariant Result = { s | s ∈ keys(Responses) }\n */\nexport type StatusCodes = keyof Responses & (number | string)\n\n/**\n * Extract content types for a specific status code\n *\n * @pure true - compile-time only\n */\nexport type ContentTypesFor<\n Responses,\n Status extends StatusCodes\n> = Status extends keyof Responses ? Responses[Status] extends { content: infer C } ? keyof C & string\n : \"none\"\n : never\n\n/**\n * Extract body type for a specific status and content-type\n *\n * @pure true - compile-time only\n * @invariant Strict correlation: Body type depends on both status and content-type\n */\nexport type BodyFor<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = Status extends keyof Responses\n ? Responses[Status] extends { content: infer C } ? ContentType extends keyof C ? C[ContentType]\n : never\n : ContentType extends \"none\" ? undefined\n : never\n : never\n\n/**\n * Build a correlated success response variant (status + contentType + body)\n * Used for 2xx responses that go to the success channel.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type ResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build a correlated HTTP error response variant (status + contentType + body + _tag)\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * The `_tag: \"HttpError\"` discriminator allows distinguishing HTTP errors from BoundaryErrors.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type HttpErrorResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly _tag: \"HttpError\"\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build all response variants for given responses\n *\n * @pure true - compile-time only\n */\ntype AllResponseVariants = StatusCodes extends infer Status\n ? Status extends StatusCodes\n ? ContentTypesFor extends infer CT\n ? CT extends ContentTypesFor ? ResponseVariant\n : never\n : never\n : never\n : never\n\n/**\n * Generic 2xx status detection without hardcoding\n * Uses template literal type to check if status string starts with \"2\"\n *\n * Works with any 2xx status including non-standard ones like 250.\n *\n * @pure true - compile-time only\n * @invariant Is2xx = true ⟺ 200 ≤ S < 300\n */\nexport type Is2xx = `${S}` extends `2${string}` ? true : false\n\n/**\n * Filter response variants to success statuses (2xx)\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ SuccessVariants: Is2xx = true\n */\nexport type SuccessVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? ResponseVariant\n : never\n : never\n : never\n\n/**\n * Filter response variants to error statuses (non-2xx from schema)\n * Returns HttpErrorResponseVariant with `_tag: \"HttpError\"` for discrimination.\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpErrorVariants: Is2xx = false ∧ v.status ∈ Schema ∧ v._tag = \"HttpError\"\n */\nexport type HttpErrorVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? never\n : HttpErrorResponseVariant\n : never\n : never\n\n/**\n * Boundary errors - always present regardless of schema\n *\n * @pure true - compile-time only\n * @invariant These errors represent protocol/parsing failures, not business logic\n */\nexport type TransportError = {\n readonly _tag: \"TransportError\"\n readonly error: Error\n}\n\nexport type UnexpectedStatus = {\n readonly _tag: \"UnexpectedStatus\"\n readonly status: number\n readonly body: string\n}\n\nexport type UnexpectedContentType = {\n readonly _tag: \"UnexpectedContentType\"\n readonly status: number\n readonly expected: ReadonlyArray\n readonly actual: string | undefined\n readonly body: string\n}\n\nexport type ParseError = {\n readonly _tag: \"ParseError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type DecodeError = {\n readonly _tag: \"DecodeError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type BoundaryError =\n | TransportError\n | UnexpectedStatus\n | UnexpectedContentType\n | ParseError\n | DecodeError\n\n/**\n * Success type for an operation (2xx statuses only)\n *\n * Goes to the **success channel** of Effect.\n * Developers receive this directly without needing to handle errors.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ ApiSuccess: v.status ∈ [200..299]\n */\nexport type ApiSuccess = SuccessVariants\n\n/**\n * HTTP error responses from schema (non-2xx statuses like 400, 404, 500)\n *\n * Goes to the **error channel** of Effect, forcing explicit handling.\n * These are business-level errors defined in the OpenAPI schema.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpError: v.status ∉ [200..299] ∧ v.status ∈ Schema\n */\nexport type HttpError = HttpErrorVariants\n\n/**\n * Complete failure type for API operations\n *\n * Includes both schema-defined HTTP errors (4xx, 5xx) and boundary errors.\n * All failures go to the **error channel** of Effect, forcing explicit handling.\n *\n * @pure true - compile-time only\n * @invariant ApiFailure = HttpError ⊎ BoundaryError\n *\n * BREAKING CHANGE: Previously, HTTP errors (404, 500) were in success channel.\n * Now they are in error channel, requiring explicit handling with Effect.catchTag\n * or Effect.match pattern.\n */\nexport type ApiFailure = HttpError | BoundaryError\n\n/**\n * @deprecated Use ApiSuccess for success channel\n * and ApiFailure for error channel instead.\n *\n * ApiResponse mixed success and error statuses in one type.\n * New API separates them into proper Effect channels.\n */\nexport type ApiResponse = SuccessVariants | HttpErrorVariants\n\n/**\n * Helper to ensure exhaustive pattern matching\n *\n * @pure true\n * @throws Compile-time error if called with non-never type\n */\nexport const assertNever = (x: never): never => {\n throw new Error(`Unexpected value: ${JSON.stringify(x)}`)\n}\n","// CHANGE: Create axioms module for type-safe cast operations\n// WHY: Centralize all type assertions in a single auditable location per CLAUDE.md\n// QUOTE(ТЗ): \"as: запрещён в обычном коде; допускается ТОЛЬКО в одном аксиоматическом модуле\"\n// REF: issue-2, section 3.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ cast ∈ Axioms: cast(x) → typed(x) ∨ runtime_validated(x)\n// PURITY: CORE\n// EFFECT: none - pure type-level operations\n// INVARIANT: All casts auditable in single file\n// COMPLEXITY: O(1)\n\n/**\n * JSON value type - result of JSON.parse()\n * This is the fundamental type for all parsed JSON values\n */\n/**\n * Cast function for dispatcher factory\n * AXIOM: Dispatcher factory receives valid classify function\n *\n * This enables generated dispatchers to work with heterogeneous Effect unions.\n * The cast is safe because:\n * 1. The classify function is generated from OpenAPI schema\n * 2. All status/content-type combinations are exhaustively covered\n * 3. The returned Effect conforms to Dispatcher signature\n *\n * @pure true\n */\nimport type { Effect } from \"effect\"\nimport type { ApiFailure, ApiSuccess, TransportError } from \"./api-client/strict-types.js\"\n\nexport type Json =\n | null\n | boolean\n | number\n | string\n | ReadonlyArray\n | { readonly [k: string]: Json }\n\n/**\n * Cast parsed JSON value to typed Json\n * AXIOM: JSON.parse returns a valid Json value\n *\n * @precondition value is result of JSON.parse on valid JSON string\n * @postcondition result conforms to Json type\n * @pure true\n */\nexport const asJson = (value: unknown): Json => value as Json\n\n/**\n * Cast a value to a specific type with const assertion\n * Used for creating literal typed objects in generated code\n *\n * @pure true\n */\nexport const asConst = (value: T): T => value\n\n/**\n * Create a typed RawResponse from raw values\n * AXIOM: HTTP response structure is known at runtime\n *\n * @pure true\n */\nexport type RawResponse = {\n readonly status: number\n readonly headers: Headers\n readonly text: string\n}\n\nexport const asRawResponse = (value: {\n status: number\n headers: Headers\n text: string\n}): RawResponse => value as RawResponse\n\n/**\n * Dispatcher classifies response and applies decoder\n *\n * NEW DESIGN (Effect-native):\n * - Success channel: `ApiSuccess` (2xx responses only)\n * - Error channel: `ApiFailure` (non-2xx schema errors + boundary errors)\n *\n * This forces developers to explicitly handle HTTP errors (404, 500, etc.)\n * using Effect.catchTag, Effect.match, or similar patterns.\n *\n * @pure false - applies decoders\n * @effect Effect\n * @invariant Must handle all statuses and content-types from schema\n */\nexport type Dispatcher = (\n response: RawResponse\n) => Effect.Effect<\n ApiSuccess,\n Exclude, TransportError>\n>\n\nexport const asDispatcher = (\n fn: (response: RawResponse) => Effect.Effect\n): Dispatcher => fn as Dispatcher\n\n/**\n * Cast for StrictRequestInit config object\n * AXIOM: Config object has correct structure when all properties assigned\n *\n * @pure true\n */\nexport const asStrictRequestInit = (config: object): T => config as T\n\n/**\n * Classifier function type for dispatcher creation\n * AXIOM: Classify function returns Effect with heterogeneous union types\n *\n * This type uses `unknown` to allow the classify function to return\n * heterogeneous Effect unions from switch statements. The actual types\n * are enforced by the generated dispatcher code.\n *\n * @pure true\n */\nexport type ClassifyFn = (\n status: number,\n contentType: string | undefined,\n text: string\n) => Effect.Effect\n\n/**\n * Cast internal client implementation to typed StrictApiClient\n * AXIOM: Client implementation correctly implements all method constraints\n *\n * This cast is safe because:\n * 1. StrictApiClient type enforces path/method constraints at call sites\n * 2. The runtime implementation correctly builds requests for any path/method\n * 3. Type checking happens at the call site, not in the implementation\n *\n * @pure true\n */\nexport const asStrictApiClient = (client: object): T => client as T\n\n/**\n * Cast default dispatchers registry to specific schema type\n * AXIOM: Default dispatcher registry was registered for the current Paths type\n *\n * @pure true\n */\nexport const asDispatchersFor = (value: unknown): T => value as T\n","// CHANGE: Implement Effect-based HTTP client with Effect-native error handling\n// WHY: Force explicit handling of HTTP errors (4xx, 5xx) via Effect error channel\n// QUOTE(ТЗ): \"каждый запрос возвращает Effect; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 2, 4, 5.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ req ∈ Requests: execute(req) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient.HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per request / O(n) for body size\n\nimport * as HttpBody from \"@effect/platform/HttpBody\"\nimport * as HttpClient from \"@effect/platform/HttpClient\"\nimport * as HttpClientRequest from \"@effect/platform/HttpClientRequest\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport type {\n ApiFailure,\n ApiSuccess,\n DecodeError,\n OperationFor,\n ParseError,\n ResponsesFor,\n TransportError,\n UnexpectedContentType,\n UnexpectedStatus\n} from \"../../core/api-client/strict-types.js\"\nimport {\n asDispatcher,\n asJson,\n asRawResponse,\n asStrictRequestInit,\n type ClassifyFn,\n type Dispatcher,\n type Json,\n type RawResponse\n} from \"../../core/axioms.js\"\n\n// Re-export Dispatcher type for consumers\n\n/**\n * Decoder for response body\n *\n * @pure false - may perform validation\n * @effect Effect\n */\nexport type Decoder = (\n status: number,\n contentType: string,\n body: string\n) => Effect.Effect\n\n/**\n * Configuration for a strict API client request\n */\nexport type StrictRequestInit = {\n readonly method: HttpMethod\n readonly url: string\n readonly dispatcher: Dispatcher\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Execute HTTP request with Effect-native error handling\n *\n * @param config - Request configuration with dispatcher\n * @returns Effect with success (2xx) and failures (non-2xx + boundary errors)\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses only\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * This forces developers to explicitly handle HTTP errors using:\n * - `Effect.catchTag` for specific error types\n * - `Effect.match` for exhaustive handling\n * - `Effect.catchAll` for generic error handling\n *\n * @pure false - performs HTTP request\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n * @invariant 2xx → success channel, non-2xx → error channel\n * @precondition config.dispatcher handles all schema statuses\n * @postcondition ∀ response: success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n * @complexity O(1) + O(|body|) for text extraction\n */\nexport const executeRequest = (\n config: StrictRequestInit\n): Effect.Effect, ApiFailure, HttpClient.HttpClient> =>\n Effect.gen(function*() {\n // STEP 1: Get HTTP client from context\n const client = yield* HttpClient.HttpClient\n\n // STEP 2: Build request based on method\n const request = buildRequest(config)\n\n // STEP 3: Execute request with error mapping\n const rawResponse = yield* Effect.mapError(\n Effect.gen(function*() {\n const response = yield* client.execute(request)\n const text = yield* response.text\n return asRawResponse({\n status: response.status,\n headers: toNativeHeaders(response.headers),\n text\n })\n }),\n (error): TransportError => ({\n _tag: \"TransportError\",\n error: error instanceof Error ? error : new Error(String(error))\n })\n )\n\n // STEP 4: Delegate classification to dispatcher (handles status/content-type/decode)\n return yield* config.dispatcher(rawResponse)\n })\n\n/**\n * Build HTTP request from config\n *\n * @pure true\n */\nconst buildRequest = (config: StrictRequestInit): HttpClientRequest.HttpClientRequest => {\n const methodMap: Record HttpClientRequest.HttpClientRequest> = {\n get: HttpClientRequest.get,\n post: HttpClientRequest.post,\n put: HttpClientRequest.put,\n patch: HttpClientRequest.patch,\n delete: HttpClientRequest.del,\n head: HttpClientRequest.head,\n options: HttpClientRequest.options\n }\n\n const createRequest = methodMap[config.method] ?? HttpClientRequest.get\n let request = createRequest(config.url)\n\n // Add headers if provided\n if (config.headers !== undefined) {\n const headers = toRecordHeaders(config.headers)\n request = HttpClientRequest.setHeaders(request, headers)\n }\n\n // Add body if provided\n if (config.body !== undefined) {\n const bodyText = typeof config.body === \"string\" ? config.body : JSON.stringify(config.body)\n request = HttpClientRequest.setBody(request, HttpBody.text(bodyText))\n }\n\n return request\n}\n\n/**\n * Convert Headers to Record\n *\n * @pure true\n */\nconst toRecordHeaders = (headers: HeadersInit): Record => {\n if (headers instanceof Headers) {\n const result: Record = {}\n for (const [key, value] of headers.entries()) {\n result[key] = value\n }\n return result\n }\n if (Array.isArray(headers)) {\n const result: Record = {}\n for (const headerPair of headers) {\n const [headerKey, headerValue] = headerPair\n result[headerKey] = headerValue\n }\n return result\n }\n return headers\n}\n\n/**\n * Convert @effect/platform Headers to native Headers\n *\n * @pure true\n */\nconst toNativeHeaders = (platformHeaders: { readonly [key: string]: string }): Headers => {\n const headers = new Headers()\n for (const [key, value] of Object.entries(platformHeaders)) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Helper to create dispatcher from switch-based classifier\n *\n * This function uses a permissive type signature to allow generated code\n * to work with any response variant without requiring exact type matching.\n * The classify function can return any Effect with union types for success/error.\n *\n * NOTE: Uses axioms module for type casts to allow heterogeneous Effect\n * unions from switch statements. The returned Dispatcher is properly typed.\n *\n * @pure true - returns pure function\n * @complexity O(1)\n */\n\nexport const createDispatcher = (\n classify: ClassifyFn\n): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n return classify(response.status, contentType, response.text)\n })\n}\n\n/**\n * Helper to parse JSON with error handling\n *\n * @pure false - performs parsing\n * @effect Effect\n */\nexport const parseJSON = (\n status: number,\n contentType: string,\n text: string\n): Effect.Effect =>\n Effect.try({\n try: () => asJson(JSON.parse(text)),\n catch: (error): ParseError => ({\n _tag: \"ParseError\",\n status,\n contentType,\n error: error instanceof Error ? error : new Error(String(error)),\n body: text\n })\n })\n\n/**\n * Helper to create UnexpectedStatus error\n *\n * @pure true\n */\nexport const unexpectedStatus = (status: number, body: string): UnexpectedStatus => ({\n _tag: \"UnexpectedStatus\",\n status,\n body\n})\n\n/**\n * Helper to create UnexpectedContentType error\n *\n * @pure true\n */\nexport const unexpectedContentType = (\n status: number,\n expected: ReadonlyArray,\n actual: string | undefined,\n body: string\n): UnexpectedContentType => ({\n _tag: \"UnexpectedContentType\",\n status,\n expected,\n actual,\n body\n})\n\n/**\n * Generic client interface for any OpenAPI schema with Effect-native error handling\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * @pure false - performs HTTP requests\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n */\nexport type StrictClient = {\n readonly GET: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly POST: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PUT: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PATCH: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly DELETE: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n}\n\n/**\n * Request options for a specific operation\n */\nexport type RequestOptions<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = {\n readonly dispatcher: Dispatcher>>\n readonly baseUrl: string\n readonly params?: Record\n readonly query?: Record\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Create a strict client for an OpenAPI schema\n *\n * @pure true - returns pure client object\n * @complexity O(1)\n */\nexport const createStrictClient = (): StrictClient<\n Paths\n> => {\n const makeRequest = (\n method: Method,\n path: Path,\n options: RequestOptions\n ) => {\n let url = `${options.baseUrl}${String(path)}`\n\n // Replace path parameters\n if (options.params !== undefined) {\n for (const [key, value] of Object.entries(options.params)) {\n url = url.replace(`{${key}}`, encodeURIComponent(String(value)))\n }\n }\n\n // Add query parameters\n if (options.query !== undefined) {\n const params = new URLSearchParams()\n for (const [key, value] of Object.entries(options.query)) {\n params.append(key, String(value))\n }\n url = `${url}?${params.toString()}`\n }\n\n // Build config object, only including optional properties if they are defined\n // This satisfies exactOptionalPropertyTypes constraint\n const config = asStrictRequestInit>>>({\n method,\n url,\n dispatcher: options.dispatcher,\n ...(options.headers !== undefined && { headers: options.headers }),\n ...(options.body !== undefined && { body: options.body }),\n ...(options.signal !== undefined && { signal: options.signal })\n })\n\n return executeRequest(config)\n }\n\n return {\n GET: (path, options) => makeRequest(\"get\", path, options),\n POST: (path, options) => makeRequest(\"post\", path, options),\n PUT: (path, options) => makeRequest(\"put\", path, options),\n PATCH: (path, options) => makeRequest(\"patch\", path, options),\n DELETE: (path, options) => makeRequest(\"delete\", path, options)\n } satisfies StrictClient\n}\n\n// CHANGE: Add universal dispatcher that handles any OpenAPI responses generically\n// WHY: Enable createClient(options) without code generation or manual dispatcher wiring\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ status, ct: universalDispatcher(status, ct, text) → success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n// PURITY: SHELL\n// EFFECT: Effect, Exclude, TransportError>, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel, no-content → body: undefined\n// COMPLEXITY: O(1) per dispatch + O(|text|) for JSON parsing\n\n/**\n * Create a universal dispatcher that handles any OpenAPI response generically\n *\n * The universal dispatcher classifies responses by status code range:\n * - 2xx → success channel (ApiSuccess)\n * - non-2xx → error channel (HttpError)\n *\n * For JSON content types, it parses the body. For no-content responses (empty body),\n * it returns undefined body with contentType \"none\".\n *\n * This enables using createClient(options) without generating\n * per-operation dispatchers, fulfilling the zero-boilerplate DSL requirement.\n *\n * @pure true - returns pure dispatcher function\n * @complexity O(1) creation + O(|body|) per dispatch\n */\nexport const createUniversalDispatcher = (): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n const is2xx = response.status >= 200 && response.status < 300\n\n // No-content response (empty body or 204)\n if (response.text === \"\" || response.status === 204) {\n const variant = {\n status: response.status,\n contentType: \"none\" as const,\n body: undefined\n } as const\n\n return is2xx\n ? Effect.succeed(variant)\n : Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n }\n\n // JSON content type\n if (contentType?.includes(\"application/json\")) {\n return Effect.gen(function*() {\n const parsed = yield* parseJSON(response.status, \"application/json\", response.text)\n const variant = {\n status: response.status,\n contentType: \"application/json\" as const,\n body: parsed\n } as const\n\n if (is2xx) {\n return variant\n }\n return yield* Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n })\n }\n\n // Unknown content type\n return Effect.fail(unexpectedContentType(\n response.status,\n [\"application/json\"],\n contentType,\n response.text\n ))\n })\n}\n\nexport { type Dispatcher, type RawResponse } from \"../../core/axioms.js\"\n","// CHANGE: Type-safe createClient API with full request-side enforcement\n// WHY: Ensure path/method → operation → request types are all linked\n// QUOTE(ТЗ): \"path + method определяют operation, и из неё выводятся request/response types\"\n// REF: PR#3 blocking review sections 3.2, 3.3\n// SOURCE: n/a\n// PURITY: SHELL\n// EFFECT: Creates Effect-based API client\n// INVARIANT: All operations are type-safe from path → operation → request → response\n// COMPLEXITY: O(1) client creation\n\nimport type * as HttpClient from \"@effect/platform/HttpClient\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport { asDispatchersFor, asStrictApiClient, asStrictRequestInit, type Dispatcher } from \"../../core/axioms.js\"\nimport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n DispatchersForMethod,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nimport type { StrictRequestInit } from \"./strict-client.js\"\nimport { createUniversalDispatcher, executeRequest } from \"./strict-client.js\"\n\nexport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n StrictApiClient,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nexport { createUniversalDispatcher } from \"./strict-client.js\"\n\n/**\n * Primitive value type for path/query parameters\n *\n * @pure true - type alias only\n */\ntype ParamValue = string | number | boolean\n\n/**\n * Query parameter value - can be primitive or array of primitives\n *\n * @pure true - type alias only\n */\ntype QueryValue = ParamValue | ReadonlyArray\n\n// CHANGE: Add default dispatcher registry for auto-dispatching createClient\n// WHY: Allow createClient(options) without explicitly passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: defaultDispatchers = dispatchersByPath ⇒ createClient uses dispatcher(path, method)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: defaultDispatchers is set before createClient use\n// COMPLEXITY: O(1)\nlet defaultDispatchers: DispatchersFor | undefined\n\n/**\n * Register default dispatcher map used by createClient(options)\n *\n * @pure false - mutates module-level registry\n * @invariant defaultDispatchers set exactly once per app boot\n */\nexport const registerDefaultDispatchers = (\n dispatchers: DispatchersFor\n): void => {\n defaultDispatchers = dispatchers\n}\n\n/**\n * Resolve default dispatcher map or fail fast\n *\n * @pure false - reads module-level registry\n * @invariant defaultDispatchers must be set for auto-dispatching client\n */\nconst resolveDefaultDispatchers = (): DispatchersFor => {\n if (defaultDispatchers === undefined) {\n throw new Error(\"Default dispatchers are not registered. Import generated dispatchers module.\")\n }\n return asDispatchersFor>(defaultDispatchers)\n}\n\n/**\n * Build URL with path parameters and query string\n *\n * @param baseUrl - Base URL for the API\n * @param path - Path template with placeholders\n * @param params - Path parameters to substitute\n * @param query - Query parameters to append\n * @returns Fully constructed URL\n *\n * @pure true\n * @complexity O(n + m) where n = |params|, m = |query|\n */\nconst buildUrl = (\n baseUrl: string | undefined,\n path: string,\n params?: Record,\n query?: Record\n): string => {\n // Replace path parameters\n let url = path\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url = url.replace(\"{\" + key + \"}\", encodeURIComponent(String(value)))\n }\n }\n\n // Add query parameters\n if (query) {\n const searchParams = new URLSearchParams()\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n searchParams.append(key, String(item))\n }\n } else {\n searchParams.set(key, String(value))\n }\n }\n const qs = searchParams.toString()\n if (qs.length > 0) {\n url = url.includes(\"?\") ? url + \"&\" + qs : url + \"?\" + qs\n }\n }\n\n // If baseUrl isn't provided, keep a relative URL (browser-friendly)\n if (baseUrl === undefined || baseUrl === \"\") {\n return url\n }\n\n // Construct full URL\n return new URL(url, baseUrl).toString()\n}\n\n\n/**\n * Check if body is already a BodyInit type (not a plain object needing serialization)\n *\n * @pure true\n */\nconst isBodyInit = (body: BodyInit | object): body is BodyInit =>\n typeof body === \"string\"\n || body instanceof Blob\n || body instanceof ArrayBuffer\n || body instanceof ReadableStream\n || body instanceof FormData\n || body instanceof URLSearchParams\n\n/**\n * Serialize body to BodyInit - passes through BodyInit types, JSON-stringifies objects\n *\n * @pure true\n * @returns BodyInit or undefined, with consistent return path\n */\nconst serializeBody = (body: BodyInit | object | undefined): BodyInit | undefined => {\n // Early return for undefined\n if (body === undefined) {\n return body\n }\n // Pass through existing BodyInit types\n if (isBodyInit(body)) {\n return body\n }\n // Plain object - serialize to JSON string (which is a valid BodyInit)\n const serialized: BodyInit = JSON.stringify(body)\n return serialized\n}\n\n/**\n * Check if body requires JSON Content-Type header\n *\n * @pure true\n */\nconst needsJsonContentType = (body: BodyInit | object | undefined): boolean =>\n body !== undefined\n && typeof body !== \"string\"\n && !(body instanceof Blob)\n && !(body instanceof FormData)\n\n/**\n * Merge headers from client options and request options\n *\n * @pure true\n * @complexity O(n) where n = number of headers\n */\nconst toHeaders = (headersInit: ClientOptions[\"headers\"] | undefined): Headers => {\n const headers = new Headers()\n if (headersInit === undefined) {\n return headers\n }\n\n if (headersInit instanceof Headers) {\n return new Headers(headersInit)\n }\n\n if (Array.isArray(headersInit)) {\n for (const entry of headersInit) {\n if (Array.isArray(entry) && entry.length === 2) {\n headers.set(String(entry[0]), String(entry[1]))\n }\n }\n return headers\n }\n\n for (const [key, value] of Object.entries(headersInit)) {\n if (value === null || value === undefined) {\n continue\n }\n if (Array.isArray(value)) {\n headers.set(key, value.map(String).join(\",\"))\n continue\n }\n headers.set(key, String(value))\n }\n\n return headers\n}\n\nconst mergeHeaders = (\n clientHeaders: ClientOptions[\"headers\"] | undefined,\n requestHeaders: ClientOptions[\"headers\"] | undefined\n): Headers => {\n const headers = toHeaders(clientHeaders)\n const optHeaders = toHeaders(requestHeaders)\n for (const [key, value] of optHeaders.entries()) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Request options type for method handlers\n *\n * @pure true - type alias only\n */\ntype MethodHandlerOptions = {\n params?: Record | undefined\n query?: Record | undefined\n body?: BodyInit | object | undefined\n headers?: ClientOptions[\"headers\"] | undefined\n signal?: AbortSignal | undefined\n}\n\n/**\n * Create HTTP method handler with full type constraints\n *\n * @param method - HTTP method\n * @param clientOptions - Client configuration\n * @returns Method handler function\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandler = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n dispatcher: Dispatcher,\n options?: MethodHandlerOptions\n) => {\n const url = buildUrl(clientOptions.baseUrl, path, options?.params, options?.query)\n const headers = mergeHeaders(clientOptions.headers, options?.headers)\n const body = serializeBody(options?.body)\n\n if (needsJsonContentType(options?.body)) {\n headers.set(\"Content-Type\", \"application/json\")\n }\n\n const config: StrictRequestInit = asStrictRequestInit({\n method,\n url,\n dispatcher,\n headers,\n body,\n signal: options?.signal\n })\n\n return executeRequest(config)\n}\n\n/**\n * Create method handler that infers dispatcher from map\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandlerWithDispatchers = (\n method: Method,\n clientOptions: ClientOptions,\n dispatchers: DispatchersForMethod\n) =>\n & string>(\n path: Path,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n dispatchers[path][method],\n options\n )\n\n// CHANGE: Create method handler that infers dispatcher from map\n// WHY: Allow per-call API without passing dispatcher parameter\n// QUOTE(ТЗ): \"Зачем передавать что либо в GET\"\n// REF: user-msg-1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path ∈ PathsForMethod: dispatchers[path][method] = Dispatcher>\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: Dispatcher lookup is total for all operations in Paths\n// COMPLEXITY: O(1) runtime + O(1) dispatcher lookup\n/**\n * Create type-safe Effect-based API client\n *\n * The client enforces:\n * 1. Method availability: GET only on paths with `get`, POST only on paths with `post`\n * 2. Dispatcher correlation: must match operation's responses\n * 3. Request options: params/query/body typed from operation\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import createClient from \"openapi-effect\"\n * import type { Paths } from \"./generated/schema\"\n * import \"./generated/dispatchers-by-path\" // registers default dispatchers\n *\n * const client = createClient({\n * baseUrl: \"https://api.example.com\",\n * credentials: \"include\"\n * })\n *\n * // Type-safe call - dispatcher inferred from path+method\n * const result = yield* client.GET(\"/pets/{petId}\", {\n * params: { petId: \"123\" } // Required because getPet has path params\n * })\n *\n * // Compile error: \"/pets/{petId}\" has no \"put\" method\n * // client.PUT(\"/pets/{petId}\", ...) // Type error!\n * ```\n */\nexport const createClient = (\n options: ClientOptions,\n dispatchers?: DispatchersFor\n): StrictApiClientWithDispatchers => {\n const resolvedDispatchers = dispatchers ?? resolveDefaultDispatchers()\n\n return asStrictApiClient>({\n GET: createMethodHandlerWithDispatchers(\"get\", options, resolvedDispatchers),\n POST: createMethodHandlerWithDispatchers(\"post\", options, resolvedDispatchers),\n PUT: createMethodHandlerWithDispatchers(\"put\", options, resolvedDispatchers),\n DELETE: createMethodHandlerWithDispatchers(\"delete\", options, resolvedDispatchers),\n PATCH: createMethodHandlerWithDispatchers(\"patch\", options, resolvedDispatchers),\n HEAD: createMethodHandlerWithDispatchers(\"head\", options, resolvedDispatchers),\n OPTIONS: createMethodHandlerWithDispatchers(\"options\", options, resolvedDispatchers)\n })\n}\n\n// CHANGE: Add createMethodHandlerWithUniversalDispatcher for zero-boilerplate client\n// WHY: Enable createClientEffect(options) without code generation or dispatcher registry\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path, method: universalDispatcher handles response classification generically\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel\n// COMPLEXITY: O(1) handler creation + O(1) universal dispatcher creation per call\nconst createMethodHandlerWithUniversalDispatcher = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n createUniversalDispatcher(),\n options\n )\n\ntype HttpErrorTag = { readonly _tag: \"HttpError\" }\n\nconst isHttpErrorValue = (error: unknown): error is HttpErrorTag =>\n typeof error === \"object\"\n && error !== null\n && \"_tag\" in error\n && Reflect.get(error, \"_tag\") === \"HttpError\"\n\nconst exposeHttpErrorsAsValues = (\n request: Effect.Effect\n): Effect.Effect<\n A | Extract,\n Exclude>,\n HttpClient.HttpClient\n> =>\n request.pipe(\n Effect.catchIf(\n (error): error is Extract => isHttpErrorValue(error),\n (error) => Effect.succeed(error)\n )\n )\n\nconst createMethodHandlerWithUniversalDispatcherValue = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n exposeHttpErrorsAsValues(\n createMethodHandlerWithUniversalDispatcher(method, clientOptions)(path, options)\n )\n\n// CHANGE: Add createClientEffect — zero-boilerplate Effect-based API client\n// WHY: Enable the user's desired DSL without any generated code or dispatcher setup\n// QUOTE(ТЗ): \"const apiClientEffect = createClientEffect(clientOptions); apiClientEffect.POST('/api/auth/login', { body: credentials })\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Paths, options: createClientEffect(options) → ClientEffect\n// PURITY: SHELL\n// EFFECT: Client methods return Effect\n// INVARIANT: ∀ path, method: path ∈ PathsForMethod (compile-time) ∧ response classified by status range (runtime)\n// COMPLEXITY: O(1) client creation\n/**\n * Create type-safe Effect-based API client with zero boilerplate\n *\n * Uses a universal dispatcher and exposes HTTP statuses as values:\n * - 2xx → success value (ApiSuccess)\n * - non-2xx schema statuses → success value (HttpError with _tag)\n * - boundary/protocol failures stay in error channel\n * - JSON parsed automatically for application/json content types\n *\n * **No code generation needed.** No dispatcher registry needed.\n * Just pass your OpenAPI Paths type and client options.\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration (baseUrl, credentials, headers, etc.)\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import { createClientEffect, type ClientOptions } from \"openapi-effect\"\n * import type { paths } from \"./openapi.d.ts\"\n *\n * const clientOptions: ClientOptions = {\n * baseUrl: \"https://petstore.example.com\",\n * credentials: \"include\"\n * }\n * const apiClientEffect = createClientEffect(clientOptions)\n *\n * // Type-safe call — path, method, and body all enforced at compile time\n * const result = yield* apiClientEffect.POST(\"/api/auth/login\", {\n * body: { email: \"user@example.com\", password: \"secret\" }\n * })\n * ```\n */\nexport const createClientEffect = (\n options: ClientOptions\n): ClientEffect => {\n return asStrictApiClient>({\n GET: createMethodHandlerWithUniversalDispatcherValue(\"get\", options),\n POST: createMethodHandlerWithUniversalDispatcherValue(\"post\", options),\n PUT: createMethodHandlerWithUniversalDispatcherValue(\"put\", options),\n DELETE: createMethodHandlerWithUniversalDispatcherValue(\"delete\", options),\n PATCH: createMethodHandlerWithUniversalDispatcherValue(\"patch\", options),\n HEAD: createMethodHandlerWithUniversalDispatcherValue(\"head\", options),\n OPTIONS: createMethodHandlerWithUniversalDispatcherValue(\"options\", options)\n })\n}\n","// CHANGE: Auto-generated decoder stubs for all operations\n// WHY: Provide type-safe runtime validation entry points\n// QUOTE(ТЗ): \"при изменении схемы сборка обязана падать, пока декодеры не обновлены\"\n// REF: issue-2, section 5.2\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op, status: decoder(op, status) → Effect\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: All decoders return typed DecodeError on failure\n// COMPLEXITY: O(n) where n = size of parsed object\n\nimport { Effect } from \"effect\"\nimport type { DecodeError } from \"../core/api-client/strict-types.js\"\n\n/**\n * JSON value type - result of JSON.parse()\n */\ntype Json = null | boolean | number | string | ReadonlyArray | { readonly [k: string]: Json }\n\n/**\n * Decoder for listPets status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for listPets status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 201 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_201 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 400 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_400 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n","// CHANGE: Auto-generated dispatchers for all operations with Effect-native error handling\n// WHY: Maintain compile-time correlation between status codes and body types\n// QUOTE(ТЗ): \"реализует switch(status) по всем статусам схемы; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 5.2, 4.1-4.3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op ∈ Operations: dispatcher(op) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, HttpError | BoundaryError, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per dispatch (Match lookup)\n\nimport { Effect, Match } from \"effect\"\nimport type { Operations } from \"../../tests/fixtures/petstore.openapi.js\"\nimport type { DecodeError, ResponsesFor } from \"../core/api-client/strict-types.js\"\nimport { asConst, type Json } from \"../core/axioms.js\"\nimport {\n createDispatcher,\n parseJSON,\n unexpectedContentType,\n unexpectedStatus\n} from \"../shell/api-client/strict-client.js\"\nimport * as Decoders from \"./decoders.js\"\n\n// Response types for each operation - used for type inference\ntype ListPetsResponses = ResponsesFor\ntype CreatePetResponses = ResponsesFor\ntype GetPetResponses = ResponsesFor\ntype DeletePetResponses = ResponsesFor\n\n/**\n * Helper: process JSON content type for a given status - returns SUCCESS variant\n * Used for 2xx responses that go to the success channel\n */\nconst processJsonContentSuccess = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n return asConst({\n status,\n contentType: \"application/json\" as const,\n body: decoded\n })\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Helper: process JSON content type for a given status - returns HTTP ERROR variant\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * Adds `_tag: \"HttpError\"` discriminator to distinguish from BoundaryError.\n */\nconst processJsonContentError = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n // Non-2xx: Return as FAILURE with _tag discriminator (goes to error channel)\n return yield* Effect.fail(asConst({\n _tag: \"HttpError\" as const,\n status,\n contentType: \"application/json\" as const,\n body: decoded\n }))\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Dispatcher for listPets\n * Handles statuses: 200 (success), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherlistPets = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodelistPets_200)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodelistPets_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for createPet\n * Handles statuses: 201 (success), 400 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 201: success channel → ApiSuccess\n * - 400, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchercreatePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(201, () => processJsonContentSuccess(201, contentType, text, Decoders.decodecreatePet_201)),\n Match.when(400, () => processJsonContentError(400, contentType, text, Decoders.decodecreatePet_400)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodecreatePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for getPet\n * Handles statuses: 200 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchergetPet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodegetPet_200)),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodegetPet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodegetPet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for deletePet\n * Handles statuses: 204 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 204: success channel → ApiSuccess (no content)\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherdeletePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(204, () =>\n Effect.succeed(\n asConst({\n status: 204,\n contentType: \"none\" as const,\n body: undefined\n })\n )),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodedeletePet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodedeletePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n","// CHANGE: Auto-generated dispatcher map by path+method\n// WHY: Provide a single dispatcher registry without manual wiring in examples\n// QUOTE(ТЗ): \"Этого в плане вообще не должно быть\"\n// REF: user-msg-3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ path, method: dispatchersByPath[path][method] = dispatcher(op)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: dispatcher map is total for all operations in Paths\n// COMPLEXITY: O(1)\n\nimport type { Paths } from \"../../tests/fixtures/petstore.openapi.js\"\nimport { type DispatchersFor, registerDefaultDispatchers } from \"../shell/api-client/create-client.js\"\nimport { dispatchercreatePet, dispatcherdeletePet, dispatchergetPet, dispatcherlistPets } from \"./dispatch.js\"\n\n/**\n * Dispatcher map keyed by OpenAPI path and HTTP method\n */\nexport const dispatchersByPath: DispatchersFor = {\n \"/pets\": {\n get: dispatcherlistPets,\n post: dispatchercreatePet\n },\n \"/pets/{petId}\": {\n get: dispatchergetPet,\n delete: dispatcherdeletePet\n }\n}\n\n// CHANGE: Register default dispatchers at module load\n// WHY: Enable createClient(options) without passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: createClient(options) uses dispatchersByPath\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: registerDefaultDispatchers is called exactly once per module load\n// COMPLEXITY: O(1)\nregisterDefaultDispatchers(dispatchersByPath)\n"],"names":["assertNever","x","asJson","value","asConst","asRawResponse","asDispatcher","fn","asStrictRequestInit","config","asStrictApiClient","client","asDispatchersFor","executeRequest","Effect","HttpClient","request","buildRequest","rawResponse","response","text","toNativeHeaders","error","HttpClientRequest","headers","toRecordHeaders","bodyText","HttpBody","result","key","headerPair","headerKey","headerValue","platformHeaders","createDispatcher","classify","contentType","parseJSON","status","unexpectedStatus","body","unexpectedContentType","expected","actual","createStrictClient","makeRequest","method","path","options","url","params","createUniversalDispatcher","is2xx","variant","parsed","defaultDispatchers","registerDefaultDispatchers","dispatchers","resolveDefaultDispatchers","buildUrl","baseUrl","query","searchParams","item","qs","isBodyInit","serializeBody","needsJsonContentType","toHeaders","headersInit","entry","mergeHeaders","clientHeaders","requestHeaders","optHeaders","createMethodHandler","clientOptions","dispatcher","createMethodHandlerWithDispatchers","createClient","resolvedDispatchers","createMethodHandlerWithUniversalDispatcher","isHttpErrorValue","exposeHttpErrorsAsValues","createMethodHandlerWithUniversalDispatcherValue","createClientEffect","decodelistPets_200","_status","_contentType","_body","decodelistPets_500","decodecreatePet_201","decodecreatePet_400","decodecreatePet_500","decodegetPet_200","decodegetPet_404","decodegetPet_500","decodedeletePet_404","decodedeletePet_500","processJsonContentSuccess","decoder","decoded","processJsonContentError","dispatcherlistPets","Match","Decoders.decodelistPets_200","Decoders.decodelistPets_500","dispatchercreatePet","Decoders.decodecreatePet_201","Decoders.decodecreatePet_400","Decoders.decodecreatePet_500","dispatchergetPet","Decoders.decodegetPet_200","Decoders.decodegetPet_404","Decoders.decodegetPet_500","dispatcherdeletePet","Decoders.decodedeletePet_404","Decoders.decodedeletePet_500","dispatchersByPath"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.js","sources":["../src/core/api-client/strict-types.ts","../src/core/axioms.ts","../src/shell/api-client/strict-client.ts","../src/shell/api-client/create-client.ts","../src/generated/decoders.ts","../src/generated/dispatch.ts","../src/generated/dispatchers-by-path.ts"],"sourcesContent":["// CHANGE: Define core type-level operations for extracting OpenAPI types\n// WHY: Enable compile-time type safety without runtime overhead through pure type transformations\n// QUOTE(ТЗ): \"Success / HttpError являются коррелированными суммами (status → точный тип body) строго из OpenAPI типов\"\n// REF: issue-2, section 3.1, 4.1-4.3\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Op ∈ Operations: ResponseVariant = Success ⊎ Failure\n// PURITY: CORE\n// INVARIANT: All types computed at compile time, no runtime operations\n// COMPLEXITY: O(1) compile-time / O(0) runtime\n\nimport type { HttpMethod, PathsWithMethod } from \"openapi-typescript-helpers\"\n\n/**\n * Extract all paths that support a given HTTP method\n *\n * @pure true - compile-time only\n * @invariant Result ⊆ paths\n */\nexport type PathsForMethod<\n Paths extends object,\n Method extends HttpMethod\n> = PathsWithMethod\n\n/**\n * Extract operation definition for a path and method\n *\n * @pure true - compile-time only\n * @invariant ∀ path ∈ Paths, method ∈ Methods: Operation = Paths[path][method]\n */\nexport type OperationFor<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = Method extends keyof Paths[Path] ? Paths[Path][Method] : never\n\n/**\n * Extract all response definitions from an operation\n *\n * @pure true - compile-time only\n */\nexport type ResponsesFor = Op extends { responses: infer R } ? R : never\n\n// ============================================================================\n// Request-side typing (path/method → params/query/body)\n// ============================================================================\n\n/**\n * Extract path parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns path params type or undefined if none\n */\nexport type PathParamsFor = Op extends { parameters: { path: infer P } }\n ? P extends Record ? Record\n : never\n : undefined\n\n/**\n * Extract query parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns query params type or undefined if none\n */\nexport type QueryParamsFor = Op extends { parameters: { query?: infer Q } } ? Q\n : undefined\n\n/**\n * Extract request body type from operation\n *\n * @pure true - compile-time only\n * @invariant Returns body type or undefined if no requestBody\n */\nexport type RequestBodyFor = Op extends { requestBody: { content: infer C } }\n ? C extends { \"application/json\": infer J } ? J\n : C extends { [key: string]: infer V } ? V\n : never\n : undefined\n\n/**\n * Check if path params are required\n *\n * @pure true - compile-time only\n */\n\nexport type HasRequiredPathParams = Op extends { parameters: { path: infer P } }\n ? P extends Record ? keyof P extends never ? false : true\n : false\n : false\n\n/**\n * Check if request body is required\n *\n * @pure true - compile-time only\n */\nexport type HasRequiredBody = Op extends { requestBody: infer RB } ? RB extends { content: object } ? true\n : false\n : false\n\n/**\n * Build request options type from operation with all constraints\n * - params: required if path has required parameters\n * - query: optional, typed from operation\n * - body: required if operation has requestBody (accepts typed object OR string)\n *\n * For request body:\n * - Users can pass either the typed object (preferred, for type safety)\n * - Or a pre-stringified JSON string with headers (for backwards compatibility)\n *\n * @pure true - compile-time only\n * @invariant Options type is fully derived from operation definition\n */\nexport type RequestOptionsFor =\n & (HasRequiredPathParams extends true ? { readonly params: PathParamsFor }\n : { readonly params?: PathParamsFor })\n & (HasRequiredBody extends true ? { readonly body: RequestBodyFor | BodyInit }\n : { readonly body?: RequestBodyFor | BodyInit })\n & { readonly query?: QueryParamsFor }\n & { readonly headers?: HeadersInit }\n & { readonly signal?: AbortSignal }\n\n/**\n * Extract status codes from responses\n *\n * @pure true - compile-time only\n * @invariant Result = { s | s ∈ keys(Responses) }\n */\nexport type StatusCodes = keyof Responses & (number | string)\n\n/**\n * Extract content types for a specific status code\n *\n * @pure true - compile-time only\n */\nexport type ContentTypesFor<\n Responses,\n Status extends StatusCodes\n> = Status extends keyof Responses ? Responses[Status] extends { content: infer C } ? keyof C & string\n : \"none\"\n : never\n\n/**\n * Extract body type for a specific status and content-type\n *\n * @pure true - compile-time only\n * @invariant Strict correlation: Body type depends on both status and content-type\n */\nexport type BodyFor<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = Status extends keyof Responses\n ? Responses[Status] extends { content: infer C } ? ContentType extends keyof C ? C[ContentType]\n : never\n : ContentType extends \"none\" ? undefined\n : never\n : never\n\n/**\n * Build a correlated success response variant (status + contentType + body)\n * Used for 2xx responses that go to the success channel.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type ResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build a correlated HTTP error response variant (status + contentType + body + _tag)\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * The `_tag: \"HttpError\"` discriminator allows distinguishing HTTP errors from BoundaryErrors.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type HttpErrorResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly _tag: \"HttpError\"\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build all response variants for given responses\n *\n * @pure true - compile-time only\n */\ntype AllResponseVariants = StatusCodes extends infer Status\n ? Status extends StatusCodes\n ? ContentTypesFor extends infer CT\n ? CT extends ContentTypesFor ? ResponseVariant\n : never\n : never\n : never\n : never\n\n/**\n * Generic 2xx status detection without hardcoding\n * Uses template literal type to check if status string starts with \"2\"\n *\n * Works with any 2xx status including non-standard ones like 250.\n *\n * @pure true - compile-time only\n * @invariant Is2xx = true ⟺ 200 ≤ S < 300\n */\nexport type Is2xx = `${S}` extends `2${string}` ? true : false\n\n/**\n * Filter response variants to success statuses (2xx)\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ SuccessVariants: Is2xx = true\n */\nexport type SuccessVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? ResponseVariant\n : never\n : never\n : never\n\n/**\n * Filter response variants to error statuses (non-2xx from schema)\n * Returns HttpErrorResponseVariant with `_tag: \"HttpError\"` for discrimination.\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpErrorVariants: Is2xx = false ∧ v.status ∈ Schema ∧ v._tag = \"HttpError\"\n */\nexport type HttpErrorVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? never\n : HttpErrorResponseVariant\n : never\n : never\n\n/**\n * Boundary errors - always present regardless of schema\n *\n * @pure true - compile-time only\n * @invariant These errors represent protocol/parsing failures, not business logic\n */\nexport type TransportError = {\n readonly _tag: \"TransportError\"\n readonly error: Error\n}\n\nexport type UnexpectedStatus = {\n readonly _tag: \"UnexpectedStatus\"\n readonly status: number\n readonly body: string\n}\n\nexport type UnexpectedContentType = {\n readonly _tag: \"UnexpectedContentType\"\n readonly status: number\n readonly expected: ReadonlyArray\n readonly actual: string | undefined\n readonly body: string\n}\n\nexport type ParseError = {\n readonly _tag: \"ParseError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type DecodeError = {\n readonly _tag: \"DecodeError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type BoundaryError =\n | TransportError\n | UnexpectedStatus\n | UnexpectedContentType\n | ParseError\n | DecodeError\n\n/**\n * Success type for an operation (2xx statuses only)\n *\n * Goes to the **success channel** of Effect.\n * Developers receive this directly without needing to handle errors.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ ApiSuccess: v.status ∈ [200..299]\n */\nexport type ApiSuccess = SuccessVariants\n\n/**\n * HTTP error responses from schema (non-2xx statuses like 400, 404, 500)\n *\n * Goes to the **error channel** of Effect, forcing explicit handling.\n * These are business-level errors defined in the OpenAPI schema.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpError: v.status ∉ [200..299] ∧ v.status ∈ Schema\n */\nexport type HttpError = HttpErrorVariants\n\n/**\n * Complete failure type for API operations\n *\n * Includes both schema-defined HTTP errors (4xx, 5xx) and boundary errors.\n * All failures go to the **error channel** of Effect, forcing explicit handling.\n *\n * @pure true - compile-time only\n * @invariant ApiFailure = HttpError ⊎ BoundaryError\n *\n * BREAKING CHANGE: Previously, HTTP errors (404, 500) were in success channel.\n * Now they are in error channel, requiring explicit handling with Effect.catchTag\n * or Effect.match pattern.\n */\nexport type ApiFailure = HttpError | BoundaryError\n\n/**\n * @deprecated Use ApiSuccess for success channel\n * and ApiFailure for error channel instead.\n *\n * ApiResponse mixed success and error statuses in one type.\n * New API separates them into proper Effect channels.\n */\nexport type ApiResponse = SuccessVariants | HttpErrorVariants\n\n/**\n * Helper to ensure exhaustive pattern matching\n *\n * @pure true\n * @throws Compile-time error if called with non-never type\n */\nexport const assertNever = (x: never): never => {\n throw new Error(`Unexpected value: ${JSON.stringify(x)}`)\n}\n","// CHANGE: Create axioms module for type-safe cast operations\n// WHY: Centralize all type assertions in a single auditable location per CLAUDE.md\n// QUOTE(ТЗ): \"as: запрещён в обычном коде; допускается ТОЛЬКО в одном аксиоматическом модуле\"\n// REF: issue-2, section 3.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ cast ∈ Axioms: cast(x) → typed(x) ∨ runtime_validated(x)\n// PURITY: CORE\n// EFFECT: none - pure type-level operations\n// INVARIANT: All casts auditable in single file\n// COMPLEXITY: O(1)\n\n/**\n * JSON value type - result of JSON.parse()\n * This is the fundamental type for all parsed JSON values\n */\n/**\n * Cast function for dispatcher factory\n * AXIOM: Dispatcher factory receives valid classify function\n *\n * This enables generated dispatchers to work with heterogeneous Effect unions.\n * The cast is safe because:\n * 1. The classify function is generated from OpenAPI schema\n * 2. All status/content-type combinations are exhaustively covered\n * 3. The returned Effect conforms to Dispatcher signature\n *\n * @pure true\n */\nimport type { Effect } from \"effect\"\nimport type { ApiFailure, ApiSuccess, TransportError } from \"./api-client/strict-types.js\"\n\nexport type Json =\n | null\n | boolean\n | number\n | string\n | ReadonlyArray\n | { readonly [k: string]: Json }\n\n/**\n * Cast parsed JSON value to typed Json\n * AXIOM: JSON.parse returns a valid Json value\n *\n * @precondition value is result of JSON.parse on valid JSON string\n * @postcondition result conforms to Json type\n * @pure true\n */\nexport const asJson = (value: unknown): Json => value as Json\n\n/**\n * Cast a value to a specific type with const assertion\n * Used for creating literal typed objects in generated code\n *\n * @pure true\n */\nexport const asConst = (value: T): T => value\n\n/**\n * Create a typed RawResponse from raw values\n * AXIOM: HTTP response structure is known at runtime\n *\n * @pure true\n */\nexport type RawResponse = {\n readonly status: number\n readonly headers: Headers\n readonly text: string\n}\n\nexport const asRawResponse = (value: {\n status: number\n headers: Headers\n text: string\n}): RawResponse => value as RawResponse\n\n/**\n * Dispatcher classifies response and applies decoder\n *\n * NEW DESIGN (Effect-native):\n * - Success channel: `ApiSuccess` (2xx responses only)\n * - Error channel: `ApiFailure` (non-2xx schema errors + boundary errors)\n *\n * This forces developers to explicitly handle HTTP errors (404, 500, etc.)\n * using Effect.catchTag, Effect.match, or similar patterns.\n *\n * @pure false - applies decoders\n * @effect Effect\n * @invariant Must handle all statuses and content-types from schema\n */\nexport type Dispatcher = (\n response: RawResponse\n) => Effect.Effect<\n ApiSuccess,\n Exclude, TransportError>\n>\n\nexport const asDispatcher = (\n fn: (response: RawResponse) => Effect.Effect\n): Dispatcher => fn as Dispatcher\n\n/**\n * Cast for StrictRequestInit config object\n * AXIOM: Config object has correct structure when all properties assigned\n *\n * @pure true\n */\nexport const asStrictRequestInit = (config: object): T => config as T\n\n/**\n * Classifier function type for dispatcher creation\n * AXIOM: Classify function returns Effect with heterogeneous union types\n *\n * This type uses `unknown` to allow the classify function to return\n * heterogeneous Effect unions from switch statements. The actual types\n * are enforced by the generated dispatcher code.\n *\n * @pure true\n */\nexport type ClassifyFn = (\n status: number,\n contentType: string | undefined,\n text: string\n) => Effect.Effect\n\n/**\n * Cast internal client implementation to typed StrictApiClient\n * AXIOM: Client implementation correctly implements all method constraints\n *\n * This cast is safe because:\n * 1. StrictApiClient type enforces path/method constraints at call sites\n * 2. The runtime implementation correctly builds requests for any path/method\n * 3. Type checking happens at the call site, not in the implementation\n *\n * @pure true\n */\nexport const asStrictApiClient = (client: object): T => client as T\n\n/**\n * Cast default dispatchers registry to specific schema type\n * AXIOM: Default dispatcher registry was registered for the current Paths type\n *\n * @pure true\n */\nexport const asDispatchersFor = (value: unknown): T => value as T\n","// CHANGE: Implement Effect-based HTTP client with Effect-native error handling\n// WHY: Force explicit handling of HTTP errors (4xx, 5xx) via Effect error channel\n// QUOTE(ТЗ): \"каждый запрос возвращает Effect; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 2, 4, 5.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ req ∈ Requests: execute(req) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient.HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per request / O(n) for body size\n\nimport * as HttpBody from \"@effect/platform/HttpBody\"\nimport * as HttpClient from \"@effect/platform/HttpClient\"\nimport * as HttpClientRequest from \"@effect/platform/HttpClientRequest\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport type {\n ApiFailure,\n ApiSuccess,\n DecodeError,\n OperationFor,\n ParseError,\n ResponsesFor,\n TransportError,\n UnexpectedContentType,\n UnexpectedStatus\n} from \"../../core/api-client/strict-types.js\"\nimport {\n asDispatcher,\n asJson,\n asRawResponse,\n asStrictRequestInit,\n type ClassifyFn,\n type Dispatcher,\n type Json,\n type RawResponse\n} from \"../../core/axioms.js\"\n\n// Re-export Dispatcher type for consumers\n\n/**\n * Decoder for response body\n *\n * @pure false - may perform validation\n * @effect Effect\n */\nexport type Decoder = (\n status: number,\n contentType: string,\n body: string\n) => Effect.Effect\n\n/**\n * Configuration for a strict API client request\n */\nexport type StrictRequestInit = {\n readonly method: HttpMethod\n readonly url: string\n readonly dispatcher: Dispatcher\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Execute HTTP request with Effect-native error handling\n *\n * @param config - Request configuration with dispatcher\n * @returns Effect with success (2xx) and failures (non-2xx + boundary errors)\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses only\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * This forces developers to explicitly handle HTTP errors using:\n * - `Effect.catchTag` for specific error types\n * - `Effect.match` for exhaustive handling\n * - `Effect.catchAll` for generic error handling\n *\n * @pure false - performs HTTP request\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n * @invariant 2xx → success channel, non-2xx → error channel\n * @precondition config.dispatcher handles all schema statuses\n * @postcondition ∀ response: success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n * @complexity O(1) + O(|body|) for text extraction\n */\nexport const executeRequest = (\n config: StrictRequestInit\n): Effect.Effect, ApiFailure, HttpClient.HttpClient> =>\n Effect.gen(function*() {\n // STEP 1: Get HTTP client from context\n const client = yield* HttpClient.HttpClient\n\n // STEP 2: Build request based on method\n const request = buildRequest(config)\n\n // STEP 3: Execute request with error mapping\n const rawResponse = yield* Effect.mapError(\n Effect.gen(function*() {\n const response = yield* client.execute(request)\n const text = yield* response.text\n return asRawResponse({\n status: response.status,\n headers: toNativeHeaders(response.headers),\n text\n })\n }),\n (error): TransportError => ({\n _tag: \"TransportError\",\n error: error instanceof Error ? error : new Error(String(error))\n })\n )\n\n // STEP 4: Delegate classification to dispatcher (handles status/content-type/decode)\n return yield* config.dispatcher(rawResponse)\n })\n\n/**\n * Build HTTP request from config\n *\n * @pure true\n */\nconst buildRequest = (config: StrictRequestInit): HttpClientRequest.HttpClientRequest => {\n const methodMap: Record HttpClientRequest.HttpClientRequest> = {\n get: HttpClientRequest.get,\n post: HttpClientRequest.post,\n put: HttpClientRequest.put,\n patch: HttpClientRequest.patch,\n delete: HttpClientRequest.del,\n head: HttpClientRequest.head,\n options: HttpClientRequest.options\n }\n\n const createRequest = methodMap[config.method] ?? HttpClientRequest.get\n let request = createRequest(config.url)\n\n // Add headers if provided\n if (config.headers !== undefined) {\n const headers = toRecordHeaders(config.headers)\n request = HttpClientRequest.setHeaders(request, headers)\n }\n\n // Add body if provided\n if (config.body !== undefined) {\n const bodyText = typeof config.body === \"string\" ? config.body : JSON.stringify(config.body)\n request = HttpClientRequest.setBody(request, HttpBody.text(bodyText))\n }\n\n return request\n}\n\n/**\n * Convert Headers to Record\n *\n * @pure true\n */\nconst toRecordHeaders = (headers: HeadersInit): Record => {\n if (headers instanceof Headers) {\n const result: Record = {}\n for (const [key, value] of headers.entries()) {\n result[key] = value\n }\n return result\n }\n if (Array.isArray(headers)) {\n const result: Record = {}\n for (const headerPair of headers) {\n const [headerKey, headerValue] = headerPair\n result[headerKey] = headerValue\n }\n return result\n }\n return headers\n}\n\n/**\n * Convert @effect/platform Headers to native Headers\n *\n * @pure true\n */\nconst toNativeHeaders = (platformHeaders: { readonly [key: string]: string }): Headers => {\n const headers = new Headers()\n for (const [key, value] of Object.entries(platformHeaders)) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Helper to create dispatcher from switch-based classifier\n *\n * This function uses a permissive type signature to allow generated code\n * to work with any response variant without requiring exact type matching.\n * The classify function can return any Effect with union types for success/error.\n *\n * NOTE: Uses axioms module for type casts to allow heterogeneous Effect\n * unions from switch statements. The returned Dispatcher is properly typed.\n *\n * @pure true - returns pure function\n * @complexity O(1)\n */\n\nexport const createDispatcher = (\n classify: ClassifyFn\n): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n return classify(response.status, contentType, response.text)\n })\n}\n\n/**\n * Helper to parse JSON with error handling\n *\n * @pure false - performs parsing\n * @effect Effect\n */\nexport const parseJSON = (\n status: number,\n contentType: string,\n text: string\n): Effect.Effect =>\n Effect.try({\n try: () => asJson(JSON.parse(text)),\n catch: (error): ParseError => ({\n _tag: \"ParseError\",\n status,\n contentType,\n error: error instanceof Error ? error : new Error(String(error)),\n body: text\n })\n })\n\n/**\n * Helper to create UnexpectedStatus error\n *\n * @pure true\n */\nexport const unexpectedStatus = (status: number, body: string): UnexpectedStatus => ({\n _tag: \"UnexpectedStatus\",\n status,\n body\n})\n\n/**\n * Helper to create UnexpectedContentType error\n *\n * @pure true\n */\nexport const unexpectedContentType = (\n status: number,\n expected: ReadonlyArray,\n actual: string | undefined,\n body: string\n): UnexpectedContentType => ({\n _tag: \"UnexpectedContentType\",\n status,\n expected,\n actual,\n body\n})\n\n/**\n * Generic client interface for any OpenAPI schema with Effect-native error handling\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * @pure false - performs HTTP requests\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n */\nexport type StrictClient = {\n readonly GET: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly POST: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PUT: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PATCH: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly DELETE: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n}\n\n/**\n * Request options for a specific operation\n */\nexport type RequestOptions<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = {\n readonly dispatcher: Dispatcher>>\n readonly baseUrl: string\n readonly params?: Record\n readonly query?: Record\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Create a strict client for an OpenAPI schema\n *\n * @pure true - returns pure client object\n * @complexity O(1)\n */\nexport const createStrictClient = (): StrictClient<\n Paths\n> => {\n const makeRequest = (\n method: Method,\n path: Path,\n options: RequestOptions\n ) => {\n let url = `${options.baseUrl}${String(path)}`\n\n // Replace path parameters\n if (options.params !== undefined) {\n for (const [key, value] of Object.entries(options.params)) {\n url = url.replace(`{${key}}`, encodeURIComponent(String(value)))\n }\n }\n\n // Add query parameters\n if (options.query !== undefined) {\n const params = new URLSearchParams()\n for (const [key, value] of Object.entries(options.query)) {\n params.append(key, String(value))\n }\n url = `${url}?${params.toString()}`\n }\n\n // Build config object, only including optional properties if they are defined\n // This satisfies exactOptionalPropertyTypes constraint\n const config = asStrictRequestInit>>>({\n method,\n url,\n dispatcher: options.dispatcher,\n ...(options.headers !== undefined && { headers: options.headers }),\n ...(options.body !== undefined && { body: options.body }),\n ...(options.signal !== undefined && { signal: options.signal })\n })\n\n return executeRequest(config)\n }\n\n return {\n GET: (path, options) => makeRequest(\"get\", path, options),\n POST: (path, options) => makeRequest(\"post\", path, options),\n PUT: (path, options) => makeRequest(\"put\", path, options),\n PATCH: (path, options) => makeRequest(\"patch\", path, options),\n DELETE: (path, options) => makeRequest(\"delete\", path, options)\n } satisfies StrictClient\n}\n\n// CHANGE: Add universal dispatcher that handles any OpenAPI responses generically\n// WHY: Enable createClient(options) without code generation or manual dispatcher wiring\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ status, ct: universalDispatcher(status, ct, text) → success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n// PURITY: SHELL\n// EFFECT: Effect, Exclude, TransportError>, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel, no-content → body: undefined\n// COMPLEXITY: O(1) per dispatch + O(|text|) for JSON parsing\n\n/**\n * Create a universal dispatcher that handles any OpenAPI response generically\n *\n * The universal dispatcher classifies responses by status code range:\n * - 2xx → success channel (ApiSuccess)\n * - non-2xx → error channel (HttpError)\n *\n * For JSON content types, it parses the body. For no-content responses (empty body),\n * it returns undefined body with contentType \"none\".\n *\n * This enables using createClient(options) without generating\n * per-operation dispatchers, fulfilling the zero-boilerplate DSL requirement.\n *\n * @pure true - returns pure dispatcher function\n * @complexity O(1) creation + O(|body|) per dispatch\n */\nexport const createUniversalDispatcher = (): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n const is2xx = response.status >= 200 && response.status < 300\n\n // No-content response (empty body or 204)\n if (response.text === \"\" || response.status === 204) {\n const variant = {\n status: response.status,\n contentType: \"none\" as const,\n body: undefined\n } as const\n\n return is2xx\n ? Effect.succeed(variant)\n : Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n }\n\n // JSON content type\n if (contentType?.includes(\"application/json\")) {\n return Effect.gen(function*() {\n const parsed = yield* parseJSON(response.status, \"application/json\", response.text)\n const variant = {\n status: response.status,\n contentType: \"application/json\" as const,\n body: parsed\n } as const\n\n if (is2xx) {\n return variant\n }\n return yield* Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n })\n }\n\n // Unknown content type\n return Effect.fail(unexpectedContentType(\n response.status,\n [\"application/json\"],\n contentType,\n response.text\n ))\n })\n}\n\nexport { type Dispatcher, type RawResponse } from \"../../core/axioms.js\"\n","// CHANGE: Type-safe createClient API with full request-side enforcement\n// WHY: Ensure path/method → operation → request types are all linked\n// QUOTE(ТЗ): \"path + method определяют operation, и из неё выводятся request/response types\"\n// REF: PR#3 blocking review sections 3.2, 3.3\n// SOURCE: n/a\n// PURITY: SHELL\n// EFFECT: Creates Effect-based API client\n// INVARIANT: All operations are type-safe from path → operation → request → response\n// COMPLEXITY: O(1) client creation\n\nimport type * as HttpClient from \"@effect/platform/HttpClient\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport { asDispatchersFor, asStrictApiClient, asStrictRequestInit, type Dispatcher } from \"../../core/axioms.js\"\nimport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n DispatchersForMethod,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nimport type { StrictRequestInit } from \"./strict-client.js\"\nimport { createUniversalDispatcher, executeRequest } from \"./strict-client.js\"\n\nexport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n StrictApiClient,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nexport { createUniversalDispatcher } from \"./strict-client.js\"\n\n/**\n * Primitive value type for path/query parameters\n *\n * @pure true - type alias only\n */\ntype ParamValue = string | number | boolean\n\n/**\n * Query parameter value - can be primitive or array of primitives\n *\n * @pure true - type alias only\n */\ntype QueryValue = ParamValue | ReadonlyArray\n\n// CHANGE: Add default dispatcher registry for auto-dispatching createClient\n// WHY: Allow createClient(options) without explicitly passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: defaultDispatchers = dispatchersByPath ⇒ createClient uses dispatcher(path, method)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: defaultDispatchers is set before createClient use\n// COMPLEXITY: O(1)\nlet defaultDispatchers: DispatchersFor | undefined\n\n/**\n * Register default dispatcher map used by createClient(options)\n *\n * @pure false - mutates module-level registry\n * @invariant defaultDispatchers set exactly once per app boot\n */\nexport const registerDefaultDispatchers = (\n dispatchers: DispatchersFor\n): void => {\n defaultDispatchers = dispatchers\n}\n\n/**\n * Resolve default dispatcher map or fail fast\n *\n * @pure false - reads module-level registry\n * @invariant defaultDispatchers must be set for auto-dispatching client\n */\nconst resolveDefaultDispatchers = (): DispatchersFor => {\n if (defaultDispatchers === undefined) {\n throw new Error(\"Default dispatchers are not registered. Import generated dispatchers module.\")\n }\n return asDispatchersFor>(defaultDispatchers)\n}\n\nconst applyPathParams = (path: string, params?: Record): string => {\n if (params === undefined) {\n return path\n }\n\n let url = path\n for (const [key, value] of Object.entries(params)) {\n url = url.replace(\"{\" + key + \"}\", encodeURIComponent(String(value)))\n }\n return url\n}\n\nconst buildQueryString = (query?: Record): string => {\n if (query === undefined) {\n return \"\"\n }\n\n const searchParams = new URLSearchParams()\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n searchParams.append(key, String(item))\n }\n continue\n }\n searchParams.set(key, String(value))\n }\n return searchParams.toString()\n}\n\nconst appendQueryString = (url: string, queryString: string): string => {\n if (queryString.length === 0) {\n return url\n }\n return url.includes(\"?\") ? url + \"&\" + queryString : url + \"?\" + queryString\n}\n\nconst withBaseUrl = (baseUrl: string | undefined, url: string): string => {\n // If baseUrl is not provided, keep a relative URL (browser-friendly)\n if (baseUrl === undefined || baseUrl === \"\") {\n return url\n }\n\n // Construct full URL\n return new URL(url, baseUrl).toString()\n}\n\n/**\n * Build URL with path parameters and query string\n *\n * @param baseUrl - Base URL for the API\n * @param path - Path template with placeholders\n * @param params - Path parameters to substitute\n * @param query - Query parameters to append\n * @returns Fully constructed URL\n *\n * @pure true\n * @complexity O(n + m) where n = |params|, m = |query|\n */\nconst buildUrl = (\n baseUrl: string | undefined,\n path: string,\n params?: Record,\n query?: Record\n): string => {\n const urlWithParams = applyPathParams(path, params)\n const queryString = buildQueryString(query)\n const urlWithQuery = appendQueryString(urlWithParams, queryString)\n return withBaseUrl(baseUrl, urlWithQuery)\n}\n\n/**\n * Check if body is already a BodyInit type (not a plain object needing serialization)\n *\n * @pure true\n */\nconst isBodyInit = (body: BodyInit | object): body is BodyInit =>\n typeof body === \"string\"\n || body instanceof Blob\n || body instanceof ArrayBuffer\n || body instanceof ReadableStream\n || body instanceof FormData\n || body instanceof URLSearchParams\n\n/**\n * Serialize body to BodyInit - passes through BodyInit types, JSON-stringifies objects\n *\n * @pure true\n * @returns BodyInit or undefined, with consistent return path\n */\nconst serializeBody = (body: BodyInit | object | undefined): BodyInit | undefined => {\n // Early return for undefined\n if (body === undefined) {\n return body\n }\n // Pass through existing BodyInit types\n if (isBodyInit(body)) {\n return body\n }\n // Plain object - serialize to JSON string (which is a valid BodyInit)\n const serialized: BodyInit = JSON.stringify(body)\n return serialized\n}\n\n/**\n * Check if body requires JSON Content-Type header\n *\n * @pure true\n */\nconst needsJsonContentType = (body: BodyInit | object | undefined): boolean =>\n body !== undefined\n && typeof body !== \"string\"\n && !(body instanceof Blob)\n && !(body instanceof FormData)\n\nconst toHeadersFromRecord = (\n headersInit: Record<\n string,\n | string\n | number\n | boolean\n | ReadonlyArray\n | null\n | undefined\n >\n): Headers => {\n const headers = new Headers()\n\n for (const [key, value] of Object.entries(headersInit)) {\n if (value === null || value === undefined) {\n continue\n }\n if (Array.isArray(value)) {\n headers.set(key, value.map(String).join(\",\"))\n continue\n }\n headers.set(key, String(value))\n }\n\n return headers\n}\n\n/**\n * Merge headers from client options and request options\n *\n * @pure true\n * @complexity O(n) where n = number of headers\n */\nconst toHeaders = (headersInit: ClientOptions[\"headers\"] | undefined): Headers => {\n if (headersInit === undefined) {\n return new Headers()\n }\n\n if (headersInit instanceof Headers) {\n return new Headers(headersInit)\n }\n\n if (Array.isArray(headersInit)) {\n return new Headers(headersInit)\n }\n\n return toHeadersFromRecord(headersInit)\n}\n\nconst mergeHeaders = (\n clientHeaders: ClientOptions[\"headers\"] | undefined,\n requestHeaders: ClientOptions[\"headers\"] | undefined\n): Headers => {\n const headers = toHeaders(clientHeaders)\n const optHeaders = toHeaders(requestHeaders)\n for (const [key, value] of optHeaders.entries()) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Request options type for method handlers\n *\n * @pure true - type alias only\n */\ntype MethodHandlerOptions = {\n params?: Record | undefined\n query?: Record | undefined\n body?: BodyInit | object | undefined\n headers?: ClientOptions[\"headers\"] | undefined\n signal?: AbortSignal | undefined\n}\n\n/**\n * Create HTTP method handler with full type constraints\n *\n * @param method - HTTP method\n * @param clientOptions - Client configuration\n * @returns Method handler function\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandler = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n dispatcher: Dispatcher,\n options?: MethodHandlerOptions\n) => {\n const url = buildUrl(clientOptions.baseUrl, path, options?.params, options?.query)\n const headers = mergeHeaders(clientOptions.headers, options?.headers)\n const body = serializeBody(options?.body)\n\n if (needsJsonContentType(options?.body)) {\n headers.set(\"Content-Type\", \"application/json\")\n }\n\n const config: StrictRequestInit = asStrictRequestInit({\n method,\n url,\n dispatcher,\n headers,\n body,\n signal: options?.signal\n })\n\n return executeRequest(config)\n}\n\n/**\n * Create method handler that infers dispatcher from map\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandlerWithDispatchers = (\n method: Method,\n clientOptions: ClientOptions,\n dispatchers: DispatchersForMethod\n) =>\n & string>(\n path: Path,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n dispatchers[path][method],\n options\n )\n\n// CHANGE: Create method handler that infers dispatcher from map\n// WHY: Allow per-call API without passing dispatcher parameter\n// QUOTE(ТЗ): \"Зачем передавать что либо в GET\"\n// REF: user-msg-1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path ∈ PathsForMethod: dispatchers[path][method] = Dispatcher>\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: Dispatcher lookup is total for all operations in Paths\n// COMPLEXITY: O(1) runtime + O(1) dispatcher lookup\n/**\n * Create type-safe Effect-based API client\n *\n * The client enforces:\n * 1. Method availability: GET only on paths with `get`, POST only on paths with `post`\n * 2. Dispatcher correlation: must match operation's responses\n * 3. Request options: params/query/body typed from operation\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import createClient from \"openapi-effect\"\n * import type { Paths } from \"./generated/schema\"\n * import \"./generated/dispatchers-by-path\" // registers default dispatchers\n *\n * const client = createClient({\n * baseUrl: \"https://api.example.com\",\n * credentials: \"include\"\n * })\n *\n * // Type-safe call - dispatcher inferred from path+method\n * const result = yield* client.GET(\"/pets/{petId}\", {\n * params: { petId: \"123\" } // Required because getPet has path params\n * })\n *\n * // Compile error: \"/pets/{petId}\" has no \"put\" method\n * // client.PUT(\"/pets/{petId}\", ...) // Type error!\n * ```\n */\nexport const createClient = (\n options: ClientOptions,\n dispatchers?: DispatchersFor\n): StrictApiClientWithDispatchers => {\n const resolvedDispatchers = dispatchers ?? resolveDefaultDispatchers()\n\n return asStrictApiClient>({\n GET: createMethodHandlerWithDispatchers(\"get\", options, resolvedDispatchers),\n POST: createMethodHandlerWithDispatchers(\"post\", options, resolvedDispatchers),\n PUT: createMethodHandlerWithDispatchers(\"put\", options, resolvedDispatchers),\n DELETE: createMethodHandlerWithDispatchers(\"delete\", options, resolvedDispatchers),\n PATCH: createMethodHandlerWithDispatchers(\"patch\", options, resolvedDispatchers),\n HEAD: createMethodHandlerWithDispatchers(\"head\", options, resolvedDispatchers),\n OPTIONS: createMethodHandlerWithDispatchers(\"options\", options, resolvedDispatchers)\n })\n}\n\n// CHANGE: Add createMethodHandlerWithUniversalDispatcher for zero-boilerplate client\n// WHY: Enable createClientEffect(options) without code generation or dispatcher registry\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path, method: universalDispatcher handles response classification generically\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel\n// COMPLEXITY: O(1) handler creation + O(1) universal dispatcher creation per call\nconst createMethodHandlerWithUniversalDispatcher = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n createUniversalDispatcher(),\n options\n )\n\ntype HttpErrorTag = { readonly _tag: \"HttpError\" }\n\nconst isHttpErrorValue = (error: unknown): error is HttpErrorTag =>\n typeof error === \"object\"\n && error !== null\n && \"_tag\" in error\n && Reflect.get(error, \"_tag\") === \"HttpError\"\n\nconst exposeHttpErrorsAsValues = (\n request: Effect.Effect\n): Effect.Effect<\n A | Extract,\n Exclude>,\n HttpClient.HttpClient\n> =>\n request.pipe(\n Effect.catchIf(\n (error): error is Extract => isHttpErrorValue(error),\n (error) => Effect.succeed(error)\n )\n )\n\nconst createMethodHandlerWithUniversalDispatcherValue = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n exposeHttpErrorsAsValues(\n createMethodHandlerWithUniversalDispatcher(method, clientOptions)(path, options)\n )\n\n// CHANGE: Add createClientEffect — zero-boilerplate Effect-based API client\n// WHY: Enable the user's desired DSL without any generated code or dispatcher setup\n// QUOTE(ТЗ): \"const apiClientEffect = createClientEffect(clientOptions); apiClientEffect.POST('/api/auth/login', { body: credentials })\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Paths, options: createClientEffect(options) → ClientEffect\n// PURITY: SHELL\n// EFFECT: Client methods return Effect\n// INVARIANT: ∀ path, method: path ∈ PathsForMethod (compile-time) ∧ response classified by status range (runtime)\n// COMPLEXITY: O(1) client creation\n/**\n * Create type-safe Effect-based API client with zero boilerplate\n *\n * Uses a universal dispatcher and exposes HTTP statuses as values:\n * - 2xx → success value (ApiSuccess)\n * - non-2xx schema statuses → success value (HttpError with _tag)\n * - boundary/protocol failures stay in error channel\n * - JSON parsed automatically for application/json content types\n *\n * **No code generation needed.** No dispatcher registry needed.\n * Just pass your OpenAPI Paths type and client options.\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration (baseUrl, credentials, headers, etc.)\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import { createClientEffect, type ClientOptions } from \"openapi-effect\"\n * import type { paths } from \"./openapi.d.ts\"\n *\n * const clientOptions: ClientOptions = {\n * baseUrl: \"https://petstore.example.com\",\n * credentials: \"include\"\n * }\n * const apiClientEffect = createClientEffect(clientOptions)\n *\n * // Type-safe call — path, method, and body all enforced at compile time\n * const result = yield* apiClientEffect.POST(\"/api/auth/login\", {\n * body: { email: \"user@example.com\", password: \"secret\" }\n * })\n * ```\n */\nexport const createClientEffect = (\n options: ClientOptions\n): ClientEffect => {\n return asStrictApiClient>({\n GET: createMethodHandlerWithUniversalDispatcherValue(\"get\", options),\n POST: createMethodHandlerWithUniversalDispatcherValue(\"post\", options),\n PUT: createMethodHandlerWithUniversalDispatcherValue(\"put\", options),\n DELETE: createMethodHandlerWithUniversalDispatcherValue(\"delete\", options),\n PATCH: createMethodHandlerWithUniversalDispatcherValue(\"patch\", options),\n HEAD: createMethodHandlerWithUniversalDispatcherValue(\"head\", options),\n OPTIONS: createMethodHandlerWithUniversalDispatcherValue(\"options\", options)\n })\n}\n","// CHANGE: Auto-generated decoder stubs for all operations\n// WHY: Provide type-safe runtime validation entry points\n// QUOTE(ТЗ): \"при изменении схемы сборка обязана падать, пока декодеры не обновлены\"\n// REF: issue-2, section 5.2\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op, status: decoder(op, status) → Effect\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: All decoders return typed DecodeError on failure\n// COMPLEXITY: O(n) where n = size of parsed object\n\nimport { Effect } from \"effect\"\nimport type { DecodeError } from \"../core/api-client/strict-types.js\"\n\n/**\n * JSON value type - result of JSON.parse()\n */\ntype Json = null | boolean | number | string | ReadonlyArray | { readonly [k: string]: Json }\n\n/**\n * Decoder for listPets status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for listPets status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 201 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_201 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 400 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_400 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n","// CHANGE: Auto-generated dispatchers for all operations with Effect-native error handling\n// WHY: Maintain compile-time correlation between status codes and body types\n// QUOTE(ТЗ): \"реализует switch(status) по всем статусам схемы; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 5.2, 4.1-4.3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op ∈ Operations: dispatcher(op) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, HttpError | BoundaryError, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per dispatch (Match lookup)\n\nimport { Effect, Match } from \"effect\"\nimport type { Operations } from \"../../tests/fixtures/petstore.openapi.js\"\nimport type { DecodeError, ResponsesFor } from \"../core/api-client/strict-types.js\"\nimport { asConst, type Json } from \"../core/axioms.js\"\nimport {\n createDispatcher,\n parseJSON,\n unexpectedContentType,\n unexpectedStatus\n} from \"../shell/api-client/strict-client.js\"\nimport * as Decoders from \"./decoders.js\"\n\n// Response types for each operation - used for type inference\ntype ListPetsResponses = ResponsesFor\ntype CreatePetResponses = ResponsesFor\ntype GetPetResponses = ResponsesFor\ntype DeletePetResponses = ResponsesFor\n\n/**\n * Helper: process JSON content type for a given status - returns SUCCESS variant\n * Used for 2xx responses that go to the success channel\n */\nconst processJsonContentSuccess = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n return asConst({\n status,\n contentType: \"application/json\" as const,\n body: decoded\n })\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Helper: process JSON content type for a given status - returns HTTP ERROR variant\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * Adds `_tag: \"HttpError\"` discriminator to distinguish from BoundaryError.\n */\nconst processJsonContentError = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n // Non-2xx: Return as FAILURE with _tag discriminator (goes to error channel)\n return yield* Effect.fail(asConst({\n _tag: \"HttpError\" as const,\n status,\n contentType: \"application/json\" as const,\n body: decoded\n }))\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Dispatcher for listPets\n * Handles statuses: 200 (success), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherlistPets = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodelistPets_200)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodelistPets_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for createPet\n * Handles statuses: 201 (success), 400 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 201: success channel → ApiSuccess\n * - 400, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchercreatePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(201, () => processJsonContentSuccess(201, contentType, text, Decoders.decodecreatePet_201)),\n Match.when(400, () => processJsonContentError(400, contentType, text, Decoders.decodecreatePet_400)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodecreatePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for getPet\n * Handles statuses: 200 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchergetPet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodegetPet_200)),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodegetPet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodegetPet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for deletePet\n * Handles statuses: 204 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 204: success channel → ApiSuccess (no content)\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherdeletePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(204, () =>\n Effect.succeed(\n asConst({\n status: 204,\n contentType: \"none\" as const,\n body: undefined\n })\n )),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodedeletePet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodedeletePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n","// CHANGE: Auto-generated dispatcher map by path+method\n// WHY: Provide a single dispatcher registry without manual wiring in examples\n// QUOTE(ТЗ): \"Этого в плане вообще не должно быть\"\n// REF: user-msg-3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ path, method: dispatchersByPath[path][method] = dispatcher(op)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: dispatcher map is total for all operations in Paths\n// COMPLEXITY: O(1)\n\nimport type { Paths } from \"../../tests/fixtures/petstore.openapi.js\"\nimport { type DispatchersFor, registerDefaultDispatchers } from \"../shell/api-client/create-client.js\"\nimport { dispatchercreatePet, dispatcherdeletePet, dispatchergetPet, dispatcherlistPets } from \"./dispatch.js\"\n\n/**\n * Dispatcher map keyed by OpenAPI path and HTTP method\n */\nexport const dispatchersByPath: DispatchersFor = {\n \"/pets\": {\n get: dispatcherlistPets,\n post: dispatchercreatePet\n },\n \"/pets/{petId}\": {\n get: dispatchergetPet,\n delete: dispatcherdeletePet\n }\n}\n\n// CHANGE: Register default dispatchers at module load\n// WHY: Enable createClient(options) without passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: createClient(options) uses dispatchersByPath\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: registerDefaultDispatchers is called exactly once per module load\n// COMPLEXITY: O(1)\nregisterDefaultDispatchers(dispatchersByPath)\n"],"names":["assertNever","x","asJson","value","asConst","asRawResponse","asDispatcher","fn","asStrictRequestInit","config","asStrictApiClient","client","asDispatchersFor","executeRequest","Effect","HttpClient","request","buildRequest","rawResponse","response","text","toNativeHeaders","error","HttpClientRequest","headers","toRecordHeaders","bodyText","HttpBody","result","key","headerPair","headerKey","headerValue","platformHeaders","createDispatcher","classify","contentType","parseJSON","status","unexpectedStatus","body","unexpectedContentType","expected","actual","createStrictClient","makeRequest","method","path","options","url","params","createUniversalDispatcher","is2xx","variant","parsed","defaultDispatchers","registerDefaultDispatchers","dispatchers","resolveDefaultDispatchers","applyPathParams","buildQueryString","query","searchParams","item","appendQueryString","queryString","withBaseUrl","baseUrl","buildUrl","urlWithParams","urlWithQuery","isBodyInit","serializeBody","needsJsonContentType","toHeadersFromRecord","headersInit","toHeaders","mergeHeaders","clientHeaders","requestHeaders","optHeaders","createMethodHandler","clientOptions","dispatcher","createMethodHandlerWithDispatchers","createClient","resolvedDispatchers","createMethodHandlerWithUniversalDispatcher","isHttpErrorValue","exposeHttpErrorsAsValues","createMethodHandlerWithUniversalDispatcherValue","createClientEffect","decodelistPets_200","_status","_contentType","_body","decodelistPets_500","decodecreatePet_201","decodecreatePet_400","decodecreatePet_500","decodegetPet_200","decodegetPet_404","decodegetPet_500","decodedeletePet_404","decodedeletePet_500","processJsonContentSuccess","decoder","decoded","processJsonContentError","dispatcherlistPets","Match","Decoders.decodelistPets_200","Decoders.decodelistPets_500","dispatchercreatePet","Decoders.decodecreatePet_201","Decoders.decodecreatePet_400","Decoders.decodecreatePet_500","dispatchergetPet","Decoders.decodegetPet_200","Decoders.decodegetPet_404","Decoders.decodegetPet_500","dispatcherdeletePet","Decoders.decodedeletePet_404","Decoders.decodedeletePet_500","dispatchersByPath"],"mappings":"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"} \ No newline at end of file diff --git a/packages/app/src/shell/api-client/create-client.ts b/packages/app/src/shell/api-client/create-client.ts index 540400a..53571ac 100644 --- a/packages/app/src/shell/api-client/create-client.ts +++ b/packages/app/src/shell/api-client/create-client.ts @@ -83,6 +83,53 @@ const resolveDefaultDispatchers = (): DispatchersFor>(defaultDispatchers) } +const applyPathParams = (path: string, params?: Record): string => { + if (params === undefined) { + return path + } + + let url = path + for (const [key, value] of Object.entries(params)) { + url = url.replace("{" + key + "}", encodeURIComponent(String(value))) + } + return url +} + +const buildQueryString = (query?: Record): string => { + if (query === undefined) { + return "" + } + + const searchParams = new URLSearchParams() + for (const [key, value] of Object.entries(query)) { + if (Array.isArray(value)) { + for (const item of value) { + searchParams.append(key, String(item)) + } + continue + } + searchParams.set(key, String(value)) + } + return searchParams.toString() +} + +const appendQueryString = (url: string, queryString: string): string => { + if (queryString.length === 0) { + return url + } + return url.includes("?") ? url + "&" + queryString : url + "?" + queryString +} + +const withBaseUrl = (baseUrl: string | undefined, url: string): string => { + // If baseUrl is not provided, keep a relative URL (browser-friendly) + if (baseUrl === undefined || baseUrl === "") { + return url + } + + // Construct full URL + return new URL(url, baseUrl).toString() +} + /** * Build URL with path parameters and query string * @@ -101,42 +148,12 @@ const buildUrl = ( params?: Record, query?: Record ): string => { - // Replace path parameters - let url = path - if (params) { - for (const [key, value] of Object.entries(params)) { - url = url.replace("{" + key + "}", encodeURIComponent(String(value))) - } - } - - // Add query parameters - if (query) { - const searchParams = new URLSearchParams() - for (const [key, value] of Object.entries(query)) { - if (Array.isArray(value)) { - for (const item of value) { - searchParams.append(key, String(item)) - } - } else { - searchParams.set(key, String(value)) - } - } - const qs = searchParams.toString() - if (qs.length > 0) { - url = url.includes("?") ? url + "&" + qs : url + "?" + qs - } - } - - // If baseUrl isn't provided, keep a relative URL (browser-friendly) - if (baseUrl === undefined || baseUrl === "") { - return url - } - - // Construct full URL - return new URL(url, baseUrl).toString() + const urlWithParams = applyPathParams(path, params) + const queryString = buildQueryString(query) + const urlWithQuery = appendQueryString(urlWithParams, queryString) + return withBaseUrl(baseUrl, urlWithQuery) } - /** * Check if body is already a BodyInit type (not a plain object needing serialization) * @@ -181,6 +198,33 @@ const needsJsonContentType = (body: BodyInit | object | undefined): boolean => && !(body instanceof Blob) && !(body instanceof FormData) +const toHeadersFromRecord = ( + headersInit: Record< + string, + | string + | number + | boolean + | ReadonlyArray + | null + | undefined + > +): Headers => { + const headers = new Headers() + + for (const [key, value] of Object.entries(headersInit)) { + if (value === null || value === undefined) { + continue + } + if (Array.isArray(value)) { + headers.set(key, value.map(String).join(",")) + continue + } + headers.set(key, String(value)) + } + + return headers +} + /** * Merge headers from client options and request options * @@ -188,9 +232,8 @@ const needsJsonContentType = (body: BodyInit | object | undefined): boolean => * @complexity O(n) where n = number of headers */ const toHeaders = (headersInit: ClientOptions["headers"] | undefined): Headers => { - const headers = new Headers() if (headersInit === undefined) { - return headers + return new Headers() } if (headersInit instanceof Headers) { @@ -198,26 +241,10 @@ const toHeaders = (headersInit: ClientOptions["headers"] | undefined): Headers = } if (Array.isArray(headersInit)) { - for (const entry of headersInit) { - if (Array.isArray(entry) && entry.length === 2) { - headers.set(String(entry[0]), String(entry[1])) - } - } - return headers - } - - for (const [key, value] of Object.entries(headersInit)) { - if (value === null || value === undefined) { - continue - } - if (Array.isArray(value)) { - headers.set(key, value.map(String).join(",")) - continue - } - headers.set(key, String(value)) + return new Headers(headersInit) } - return headers + return toHeadersFromRecord(headersInit) } const mergeHeaders = ( From ddbc70982653ec78d12a73fe35fd5930651bc341 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 12 Feb 2026 15:53:40 +0000 Subject: [PATCH 3/4] chore(pkg): scope package name --- package.json | 18 +++++++++--------- packages/app/package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index bb45c3f..e721810 100644 --- a/package.json +++ b/package.json @@ -8,18 +8,18 @@ "packages/*" ], "scripts": { - "build": "pnpm --filter openapi-effect build", - "check": "pnpm --filter openapi-effect check", + "build": "pnpm --filter @prover-coder-ai/openapi-effect build", + "check": "pnpm --filter @prover-coder-ai/openapi-effect check", "changeset": "changeset", "changeset-publish": "node -e \"if (!process.env.NPM_TOKEN) { console.log('Skipping publish: NPM_TOKEN is not set'); process.exit(0); }\" && changeset publish", "changeset-version": "changeset version", - "dev": "pnpm --filter openapi-effect dev", - "lint": "pnpm --filter openapi-effect lint", - "lint:tests": "pnpm --filter openapi-effect lint:tests", - "lint:effect": "pnpm --filter openapi-effect lint:effect", - "test": "pnpm --filter openapi-effect test", - "typecheck": "pnpm --filter openapi-effect typecheck", - "start": "pnpm --filter openapi-effect start" + "dev": "pnpm --filter @prover-coder-ai/openapi-effect dev", + "lint": "pnpm --filter @prover-coder-ai/openapi-effect lint", + "lint:tests": "pnpm --filter @prover-coder-ai/openapi-effect lint:tests", + "lint:effect": "pnpm --filter @prover-coder-ai/openapi-effect lint:effect", + "test": "pnpm --filter @prover-coder-ai/openapi-effect test", + "typecheck": "pnpm --filter @prover-coder-ai/openapi-effect typecheck", + "start": "pnpm --filter @prover-coder-ai/openapi-effect start" }, "devDependencies": { "@changesets/changelog-github": "^0.5.2", diff --git a/packages/app/package.json b/packages/app/package.json index f452ba7..84ed361 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,5 +1,5 @@ { - "name": "openapi-effect", + "name": "@prover-coder-ai/openapi-effect", "version": "1.0.16", "description": "Drop-in replacement for openapi-fetch with an opt-in Effect API", "type": "module", From defe09a6b3217eeb0c6527d71589e548cf91ab16 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 12 Feb 2026 16:00:31 +0000 Subject: [PATCH 4/4] chore(dist): stop tracking build output --- .gitignore | 3 --- packages/app/dist/index.js | 2 -- packages/app/dist/index.js.map | 1 - packages/app/dist/main.js | 2 -- packages/app/dist/main.js.map | 1 - packages/app/package.json | 4 ++++ 6 files changed, 4 insertions(+), 9 deletions(-) delete mode 100644 packages/app/dist/index.js delete mode 100644 packages/app/dist/index.js.map delete mode 100644 packages/app/dist/main.js delete mode 100644 packages/app/dist/main.js.map diff --git a/.gitignore b/.gitignore index d884688..f6af235 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,3 @@ yarn-error.log* pnpm-debug.log* reports/ -# openapi-effect is consumed via git subdir; commit dist so consumers don't need prepare scripts -!packages/app/dist/ -!packages/app/dist/** diff --git a/packages/app/dist/index.js b/packages/app/dist/index.js deleted file mode 100644 index 3ab49d1..0000000 --- a/packages/app/dist/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export*from"openapi-fetch";import{default as Se,default as He}from"openapi-fetch";import*as fe from"@effect/platform/FetchHttpClient";import*as C from"@effect/platform/HttpBody";import*as D from"@effect/platform/HttpClient";import*as i from"@effect/platform/HttpClientRequest";import{Effect as c,Match as a}from"effect";const he=e=>{throw new Error(`Unexpected value: ${JSON.stringify(e)}`)},R=e=>e,v=e=>e,O=e=>e,H=e=>e,T=e=>e,b=e=>e,U=e=>e,w=e=>c.gen(function*(){const t=yield*D.HttpClient,r=A(e),n=yield*c.mapError(c.gen(function*(){const s=yield*t.execute(r),o=yield*s.text;return O({status:s.status,headers:k(s.headers),text:o})}),s=>({_tag:"TransportError",error:s instanceof Error?s:new Error(String(s))}));return yield*e.dispatcher(n)}),A=e=>{let n=({get:i.get,post:i.post,put:i.put,patch:i.patch,delete:i.del,head:i.head,options:i.options}[e.method]??i.get)(e.url);if(e.headers!==void 0){const s=q(e.headers);n=i.setHeaders(n,s)}if(e.body!==void 0){const s=typeof e.body=="string"?e.body:JSON.stringify(e.body);n=i.setBody(n,C.text(s))}return n},q=e=>{if(e instanceof Headers){const t={};for(const[r,n]of e.entries())t[r]=n;return t}if(Array.isArray(e)){const t={};for(const r of e){const[n,s]=r;t[n]=s}return t}return e},k=e=>{const t=new Headers;for(const[r,n]of Object.entries(e))t.set(r,n);return t},y=e=>H(t=>{const r=t.headers.get("content-type")??void 0;return e(t.status,r,t.text)}),m=(e,t,r)=>c.try({try:()=>R(JSON.parse(r)),catch:n=>({_tag:"ParseError",status:e,contentType:t,error:n instanceof Error?n:new Error(String(n)),body:r})}),h=(e,t)=>({_tag:"UnexpectedStatus",status:e,body:t}),E=(e,t,r,n)=>({_tag:"UnexpectedContentType",status:e,expected:t,actual:r,body:n}),ge=()=>{const e=(t,r,n)=>{let s=`${n.baseUrl}${String(r)}`;if(n.params!==void 0)for(const[d,f]of Object.entries(n.params))s=s.replace(`{${d}}`,encodeURIComponent(String(f)));if(n.query!==void 0){const d=new URLSearchParams;for(const[f,g]of Object.entries(n.query))d.append(f,String(g));s=`${s}?${d.toString()}`}const o=T({method:t,url:s,dispatcher:n.dispatcher,...n.headers!==void 0&&{headers:n.headers},...n.body!==void 0&&{body:n.body},...n.signal!==void 0&&{signal:n.signal}});return w(o)};return{GET:(t,r)=>e("get",t,r),POST:(t,r)=>e("post",t,r),PUT:(t,r)=>e("put",t,r),PATCH:(t,r)=>e("patch",t,r),DELETE:(t,r)=>e("delete",t,r)}},B=()=>H(e=>{const t=e.headers.get("content-type")??void 0,r=e.status>=200&&e.status<300;if(e.text===""||e.status===204){const n={status:e.status,contentType:"none",body:void 0};return r?c.succeed(n):c.fail({_tag:"HttpError",...n})}return t?.includes("application/json")?c.gen(function*(){const n=yield*m(e.status,"application/json",e.text),s={status:e.status,contentType:"application/json",body:n};return r?s:yield*c.fail({_tag:"HttpError",...s})}):c.fail(E(e.status,["application/json"],t,e.text))});let _;const J=e=>{_=e},N=()=>{if(_===void 0)throw new Error("Default dispatchers are not registered. Import generated dispatchers module.");return U(_)},x=(e,t)=>{if(t===void 0)return e;let r=e;for(const[n,s]of Object.entries(t))r=r.replace("{"+n+"}",encodeURIComponent(String(s)));return r},L=e=>{if(e===void 0)return"";const t=new URLSearchParams;for(const[r,n]of Object.entries(e)){if(Array.isArray(n)){for(const s of n)t.append(r,String(s));continue}t.set(r,String(n))}return t.toString()},M=(e,t)=>t.length===0?e:e.includes("?")?e+"&"+t:e+"?"+t,$=(e,t)=>e===void 0||e===""?t:new URL(t,e).toString(),F=(e,t,r,n)=>{const s=x(t,r),o=L(n),d=M(s,o);return $(e,d)},W=e=>typeof e=="string"||e instanceof Blob||e instanceof ArrayBuffer||e instanceof ReadableStream||e instanceof FormData||e instanceof URLSearchParams,I=e=>e===void 0||W(e)?e:JSON.stringify(e),V=e=>e!==void 0&&typeof e!="string"&&!(e instanceof Blob)&&!(e instanceof FormData),z=e=>{const t=new Headers;for(const[r,n]of Object.entries(e))if(n!=null){if(Array.isArray(n)){t.set(r,n.map(String).join(","));continue}t.set(r,String(n))}return t},S=e=>e===void 0?new Headers:e instanceof Headers?new Headers(e):Array.isArray(e)?new Headers(e):z(e),G=(e,t)=>{const r=S(e),n=S(t);for(const[s,o]of n.entries())r.set(s,o);return r},j=(e,t)=>(r,n,s)=>{const o=F(t.baseUrl,r,s?.params,s?.query),d=G(t.headers,s?.headers),f=I(s?.body);V(s?.body)&&d.set("Content-Type","application/json");const g=T({method:e,url:o,dispatcher:n,headers:d,body:f,signal:s?.signal});return w(g)},u=(e,t,r)=>(n,s)=>j(e,t)(n,r[n][e],s),_e=(e,t)=>{const r=t??N();return b({GET:u("get",e,r),POST:u("post",e,r),PUT:u("put",e,r),DELETE:u("delete",e,r),PATCH:u("patch",e,r),HEAD:u("head",e,r),OPTIONS:u("options",e,r)})},Q=(e,t)=>(r,n)=>j(e,t)(r,B(),n),K=e=>typeof e=="object"&&e!==null&&"_tag"in e&&Reflect.get(e,"_tag")==="HttpError",X=e=>e.pipe(c.catchIf(t=>K(t),t=>c.succeed(t))),l=(e,t)=>(r,n)=>X(Q(e,t)(r,n)),ve=e=>b({GET:l("get",e),POST:l("post",e),PUT:l("put",e),DELETE:l("delete",e),PATCH:l("patch",e),HEAD:l("head",e),OPTIONS:l("options",e)}),Y=(e,t,r,n)=>c.succeed(n),Z=(e,t,r,n)=>c.succeed(n),ee=(e,t,r,n)=>c.succeed(n),te=(e,t,r,n)=>c.succeed(n),re=(e,t,r,n)=>c.succeed(n),ne=(e,t,r,n)=>c.succeed(n),se=(e,t,r,n)=>c.succeed(n),ce=(e,t,r,n)=>c.succeed(n),ae=(e,t,r,n)=>c.succeed(n),oe=(e,t,r,n)=>c.succeed(n),P=(e,t,r,n)=>t?.includes("application/json")?c.gen(function*(){const s=yield*m(e,"application/json",r),o=yield*n(e,"application/json",r,s);return v({status:e,contentType:"application/json",body:o})}):c.fail(E(e,["application/json"],t,r)),p=(e,t,r,n)=>t?.includes("application/json")?c.gen(function*(){const s=yield*m(e,"application/json",r),o=yield*n(e,"application/json",r,s);return yield*c.fail(v({_tag:"HttpError",status:e,contentType:"application/json",body:o}))}):c.fail(E(e,["application/json"],t,r)),ie=y((e,t,r)=>a.value(e).pipe(a.when(200,()=>P(200,t,r,Y)),a.when(500,()=>p(500,t,r,Z)),a.orElse(()=>c.fail(h(e,r))))),de=y((e,t,r)=>a.value(e).pipe(a.when(201,()=>P(201,t,r,ee)),a.when(400,()=>p(400,t,r,te)),a.when(500,()=>p(500,t,r,re)),a.orElse(()=>c.fail(h(e,r))))),ue=y((e,t,r)=>a.value(e).pipe(a.when(200,()=>P(200,t,r,ne)),a.when(404,()=>p(404,t,r,se)),a.when(500,()=>p(500,t,r,ce)),a.orElse(()=>c.fail(h(e,r))))),le=y((e,t,r)=>a.value(e).pipe(a.when(204,()=>c.succeed(v({status:204,contentType:"none",body:void 0}))),a.when(404,()=>p(404,t,r,ae)),a.when(500,()=>p(500,t,r,oe)),a.orElse(()=>c.fail(h(e,r))))),pe={"/pets":{get:ie,post:de},"/pets/{petId}":{get:ue,delete:le}};J(pe);export{fe as FetchHttpClient,he as assertNever,Se as createClient,ve as createClientEffect,_e as createClientStrict,y as createDispatcher,ge as createStrictClient,B as createUniversalDispatcher,ee as decodecreatePet_201,te as decodecreatePet_400,re as decodecreatePet_500,ae as decodedeletePet_404,oe as decodedeletePet_500,ne as decodegetPet_200,se as decodegetPet_404,ce as decodegetPet_500,Y as decodelistPets_200,Z as decodelistPets_500,He as default,de as dispatchercreatePet,le as dispatcherdeletePet,ue as dispatchergetPet,ie as dispatcherlistPets,pe as dispatchersByPath,w as executeRequest,m as parseJSON,J as registerDefaultDispatchers,E as unexpectedContentType,h as unexpectedStatus}; -//# sourceMappingURL=index.js.map diff --git a/packages/app/dist/index.js.map b/packages/app/dist/index.js.map deleted file mode 100644 index 9197be9..0000000 --- a/packages/app/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sources":["../src/core/api-client/strict-types.ts","../src/core/axioms.ts","../src/shell/api-client/strict-client.ts","../src/shell/api-client/create-client.ts","../src/generated/decoders.ts","../src/generated/dispatch.ts","../src/generated/dispatchers-by-path.ts"],"sourcesContent":["// CHANGE: Define core type-level operations for extracting OpenAPI types\n// WHY: Enable compile-time type safety without runtime overhead through pure type transformations\n// QUOTE(ТЗ): \"Success / HttpError являются коррелированными суммами (status → точный тип body) строго из OpenAPI типов\"\n// REF: issue-2, section 3.1, 4.1-4.3\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Op ∈ Operations: ResponseVariant = Success ⊎ Failure\n// PURITY: CORE\n// INVARIANT: All types computed at compile time, no runtime operations\n// COMPLEXITY: O(1) compile-time / O(0) runtime\n\nimport type { HttpMethod, PathsWithMethod } from \"openapi-typescript-helpers\"\n\n/**\n * Extract all paths that support a given HTTP method\n *\n * @pure true - compile-time only\n * @invariant Result ⊆ paths\n */\nexport type PathsForMethod<\n Paths extends object,\n Method extends HttpMethod\n> = PathsWithMethod\n\n/**\n * Extract operation definition for a path and method\n *\n * @pure true - compile-time only\n * @invariant ∀ path ∈ Paths, method ∈ Methods: Operation = Paths[path][method]\n */\nexport type OperationFor<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = Method extends keyof Paths[Path] ? Paths[Path][Method] : never\n\n/**\n * Extract all response definitions from an operation\n *\n * @pure true - compile-time only\n */\nexport type ResponsesFor = Op extends { responses: infer R } ? R : never\n\n// ============================================================================\n// Request-side typing (path/method → params/query/body)\n// ============================================================================\n\n/**\n * Extract path parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns path params type or undefined if none\n */\nexport type PathParamsFor = Op extends { parameters: { path: infer P } }\n ? P extends Record ? Record\n : never\n : undefined\n\n/**\n * Extract query parameters from operation\n *\n * @pure true - compile-time only\n * @invariant Returns query params type or undefined if none\n */\nexport type QueryParamsFor = Op extends { parameters: { query?: infer Q } } ? Q\n : undefined\n\n/**\n * Extract request body type from operation\n *\n * @pure true - compile-time only\n * @invariant Returns body type or undefined if no requestBody\n */\nexport type RequestBodyFor = Op extends { requestBody: { content: infer C } }\n ? C extends { \"application/json\": infer J } ? J\n : C extends { [key: string]: infer V } ? V\n : never\n : undefined\n\n/**\n * Check if path params are required\n *\n * @pure true - compile-time only\n */\n\nexport type HasRequiredPathParams = Op extends { parameters: { path: infer P } }\n ? P extends Record ? keyof P extends never ? false : true\n : false\n : false\n\n/**\n * Check if request body is required\n *\n * @pure true - compile-time only\n */\nexport type HasRequiredBody = Op extends { requestBody: infer RB } ? RB extends { content: object } ? true\n : false\n : false\n\n/**\n * Build request options type from operation with all constraints\n * - params: required if path has required parameters\n * - query: optional, typed from operation\n * - body: required if operation has requestBody (accepts typed object OR string)\n *\n * For request body:\n * - Users can pass either the typed object (preferred, for type safety)\n * - Or a pre-stringified JSON string with headers (for backwards compatibility)\n *\n * @pure true - compile-time only\n * @invariant Options type is fully derived from operation definition\n */\nexport type RequestOptionsFor =\n & (HasRequiredPathParams extends true ? { readonly params: PathParamsFor }\n : { readonly params?: PathParamsFor })\n & (HasRequiredBody extends true ? { readonly body: RequestBodyFor | BodyInit }\n : { readonly body?: RequestBodyFor | BodyInit })\n & { readonly query?: QueryParamsFor }\n & { readonly headers?: HeadersInit }\n & { readonly signal?: AbortSignal }\n\n/**\n * Extract status codes from responses\n *\n * @pure true - compile-time only\n * @invariant Result = { s | s ∈ keys(Responses) }\n */\nexport type StatusCodes = keyof Responses & (number | string)\n\n/**\n * Extract content types for a specific status code\n *\n * @pure true - compile-time only\n */\nexport type ContentTypesFor<\n Responses,\n Status extends StatusCodes\n> = Status extends keyof Responses ? Responses[Status] extends { content: infer C } ? keyof C & string\n : \"none\"\n : never\n\n/**\n * Extract body type for a specific status and content-type\n *\n * @pure true - compile-time only\n * @invariant Strict correlation: Body type depends on both status and content-type\n */\nexport type BodyFor<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = Status extends keyof Responses\n ? Responses[Status] extends { content: infer C } ? ContentType extends keyof C ? C[ContentType]\n : never\n : ContentType extends \"none\" ? undefined\n : never\n : never\n\n/**\n * Build a correlated success response variant (status + contentType + body)\n * Used for 2xx responses that go to the success channel.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type ResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build a correlated HTTP error response variant (status + contentType + body + _tag)\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * The `_tag: \"HttpError\"` discriminator allows distinguishing HTTP errors from BoundaryErrors.\n *\n * @pure true - compile-time only\n * @invariant ∀ variant: variant.body = BodyFor\n */\nexport type HttpErrorResponseVariant<\n Responses,\n Status extends StatusCodes,\n ContentType extends ContentTypesFor\n> = {\n readonly _tag: \"HttpError\"\n readonly status: Status\n readonly contentType: ContentType\n readonly body: BodyFor\n}\n\n/**\n * Build all response variants for given responses\n *\n * @pure true - compile-time only\n */\ntype AllResponseVariants = StatusCodes extends infer Status\n ? Status extends StatusCodes\n ? ContentTypesFor extends infer CT\n ? CT extends ContentTypesFor ? ResponseVariant\n : never\n : never\n : never\n : never\n\n/**\n * Generic 2xx status detection without hardcoding\n * Uses template literal type to check if status string starts with \"2\"\n *\n * Works with any 2xx status including non-standard ones like 250.\n *\n * @pure true - compile-time only\n * @invariant Is2xx = true ⟺ 200 ≤ S < 300\n */\nexport type Is2xx = `${S}` extends `2${string}` ? true : false\n\n/**\n * Filter response variants to success statuses (2xx)\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ SuccessVariants: Is2xx = true\n */\nexport type SuccessVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? ResponseVariant\n : never\n : never\n : never\n\n/**\n * Filter response variants to error statuses (non-2xx from schema)\n * Returns HttpErrorResponseVariant with `_tag: \"HttpError\"` for discrimination.\n * Uses generic Is2xx instead of hardcoded status list.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpErrorVariants: Is2xx = false ∧ v.status ∈ Schema ∧ v._tag = \"HttpError\"\n */\nexport type HttpErrorVariants = AllResponseVariants extends infer V\n ? V extends ResponseVariant ? Is2xx extends true ? never\n : HttpErrorResponseVariant\n : never\n : never\n\n/**\n * Boundary errors - always present regardless of schema\n *\n * @pure true - compile-time only\n * @invariant These errors represent protocol/parsing failures, not business logic\n */\nexport type TransportError = {\n readonly _tag: \"TransportError\"\n readonly error: Error\n}\n\nexport type UnexpectedStatus = {\n readonly _tag: \"UnexpectedStatus\"\n readonly status: number\n readonly body: string\n}\n\nexport type UnexpectedContentType = {\n readonly _tag: \"UnexpectedContentType\"\n readonly status: number\n readonly expected: ReadonlyArray\n readonly actual: string | undefined\n readonly body: string\n}\n\nexport type ParseError = {\n readonly _tag: \"ParseError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type DecodeError = {\n readonly _tag: \"DecodeError\"\n readonly status: number\n readonly contentType: string\n readonly error: Error\n readonly body: string\n}\n\nexport type BoundaryError =\n | TransportError\n | UnexpectedStatus\n | UnexpectedContentType\n | ParseError\n | DecodeError\n\n/**\n * Success type for an operation (2xx statuses only)\n *\n * Goes to the **success channel** of Effect.\n * Developers receive this directly without needing to handle errors.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ ApiSuccess: v.status ∈ [200..299]\n */\nexport type ApiSuccess = SuccessVariants\n\n/**\n * HTTP error responses from schema (non-2xx statuses like 400, 404, 500)\n *\n * Goes to the **error channel** of Effect, forcing explicit handling.\n * These are business-level errors defined in the OpenAPI schema.\n *\n * @pure true - compile-time only\n * @invariant ∀ v ∈ HttpError: v.status ∉ [200..299] ∧ v.status ∈ Schema\n */\nexport type HttpError = HttpErrorVariants\n\n/**\n * Complete failure type for API operations\n *\n * Includes both schema-defined HTTP errors (4xx, 5xx) and boundary errors.\n * All failures go to the **error channel** of Effect, forcing explicit handling.\n *\n * @pure true - compile-time only\n * @invariant ApiFailure = HttpError ⊎ BoundaryError\n *\n * BREAKING CHANGE: Previously, HTTP errors (404, 500) were in success channel.\n * Now they are in error channel, requiring explicit handling with Effect.catchTag\n * or Effect.match pattern.\n */\nexport type ApiFailure = HttpError | BoundaryError\n\n/**\n * @deprecated Use ApiSuccess for success channel\n * and ApiFailure for error channel instead.\n *\n * ApiResponse mixed success and error statuses in one type.\n * New API separates them into proper Effect channels.\n */\nexport type ApiResponse = SuccessVariants | HttpErrorVariants\n\n/**\n * Helper to ensure exhaustive pattern matching\n *\n * @pure true\n * @throws Compile-time error if called with non-never type\n */\nexport const assertNever = (x: never): never => {\n throw new Error(`Unexpected value: ${JSON.stringify(x)}`)\n}\n","// CHANGE: Create axioms module for type-safe cast operations\n// WHY: Centralize all type assertions in a single auditable location per CLAUDE.md\n// QUOTE(ТЗ): \"as: запрещён в обычном коде; допускается ТОЛЬКО в одном аксиоматическом модуле\"\n// REF: issue-2, section 3.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ cast ∈ Axioms: cast(x) → typed(x) ∨ runtime_validated(x)\n// PURITY: CORE\n// EFFECT: none - pure type-level operations\n// INVARIANT: All casts auditable in single file\n// COMPLEXITY: O(1)\n\n/**\n * JSON value type - result of JSON.parse()\n * This is the fundamental type for all parsed JSON values\n */\n/**\n * Cast function for dispatcher factory\n * AXIOM: Dispatcher factory receives valid classify function\n *\n * This enables generated dispatchers to work with heterogeneous Effect unions.\n * The cast is safe because:\n * 1. The classify function is generated from OpenAPI schema\n * 2. All status/content-type combinations are exhaustively covered\n * 3. The returned Effect conforms to Dispatcher signature\n *\n * @pure true\n */\nimport type { Effect } from \"effect\"\nimport type { ApiFailure, ApiSuccess, TransportError } from \"./api-client/strict-types.js\"\n\nexport type Json =\n | null\n | boolean\n | number\n | string\n | ReadonlyArray\n | { readonly [k: string]: Json }\n\n/**\n * Cast parsed JSON value to typed Json\n * AXIOM: JSON.parse returns a valid Json value\n *\n * @precondition value is result of JSON.parse on valid JSON string\n * @postcondition result conforms to Json type\n * @pure true\n */\nexport const asJson = (value: unknown): Json => value as Json\n\n/**\n * Cast a value to a specific type with const assertion\n * Used for creating literal typed objects in generated code\n *\n * @pure true\n */\nexport const asConst = (value: T): T => value\n\n/**\n * Create a typed RawResponse from raw values\n * AXIOM: HTTP response structure is known at runtime\n *\n * @pure true\n */\nexport type RawResponse = {\n readonly status: number\n readonly headers: Headers\n readonly text: string\n}\n\nexport const asRawResponse = (value: {\n status: number\n headers: Headers\n text: string\n}): RawResponse => value as RawResponse\n\n/**\n * Dispatcher classifies response and applies decoder\n *\n * NEW DESIGN (Effect-native):\n * - Success channel: `ApiSuccess` (2xx responses only)\n * - Error channel: `ApiFailure` (non-2xx schema errors + boundary errors)\n *\n * This forces developers to explicitly handle HTTP errors (404, 500, etc.)\n * using Effect.catchTag, Effect.match, or similar patterns.\n *\n * @pure false - applies decoders\n * @effect Effect\n * @invariant Must handle all statuses and content-types from schema\n */\nexport type Dispatcher = (\n response: RawResponse\n) => Effect.Effect<\n ApiSuccess,\n Exclude, TransportError>\n>\n\nexport const asDispatcher = (\n fn: (response: RawResponse) => Effect.Effect\n): Dispatcher => fn as Dispatcher\n\n/**\n * Cast for StrictRequestInit config object\n * AXIOM: Config object has correct structure when all properties assigned\n *\n * @pure true\n */\nexport const asStrictRequestInit = (config: object): T => config as T\n\n/**\n * Classifier function type for dispatcher creation\n * AXIOM: Classify function returns Effect with heterogeneous union types\n *\n * This type uses `unknown` to allow the classify function to return\n * heterogeneous Effect unions from switch statements. The actual types\n * are enforced by the generated dispatcher code.\n *\n * @pure true\n */\nexport type ClassifyFn = (\n status: number,\n contentType: string | undefined,\n text: string\n) => Effect.Effect\n\n/**\n * Cast internal client implementation to typed StrictApiClient\n * AXIOM: Client implementation correctly implements all method constraints\n *\n * This cast is safe because:\n * 1. StrictApiClient type enforces path/method constraints at call sites\n * 2. The runtime implementation correctly builds requests for any path/method\n * 3. Type checking happens at the call site, not in the implementation\n *\n * @pure true\n */\nexport const asStrictApiClient = (client: object): T => client as T\n\n/**\n * Cast default dispatchers registry to specific schema type\n * AXIOM: Default dispatcher registry was registered for the current Paths type\n *\n * @pure true\n */\nexport const asDispatchersFor = (value: unknown): T => value as T\n","// CHANGE: Implement Effect-based HTTP client with Effect-native error handling\n// WHY: Force explicit handling of HTTP errors (4xx, 5xx) via Effect error channel\n// QUOTE(ТЗ): \"каждый запрос возвращает Effect; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 2, 4, 5.1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ req ∈ Requests: execute(req) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient.HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per request / O(n) for body size\n\nimport * as HttpBody from \"@effect/platform/HttpBody\"\nimport * as HttpClient from \"@effect/platform/HttpClient\"\nimport * as HttpClientRequest from \"@effect/platform/HttpClientRequest\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport type {\n ApiFailure,\n ApiSuccess,\n DecodeError,\n OperationFor,\n ParseError,\n ResponsesFor,\n TransportError,\n UnexpectedContentType,\n UnexpectedStatus\n} from \"../../core/api-client/strict-types.js\"\nimport {\n asDispatcher,\n asJson,\n asRawResponse,\n asStrictRequestInit,\n type ClassifyFn,\n type Dispatcher,\n type Json,\n type RawResponse\n} from \"../../core/axioms.js\"\n\n// Re-export Dispatcher type for consumers\n\n/**\n * Decoder for response body\n *\n * @pure false - may perform validation\n * @effect Effect\n */\nexport type Decoder = (\n status: number,\n contentType: string,\n body: string\n) => Effect.Effect\n\n/**\n * Configuration for a strict API client request\n */\nexport type StrictRequestInit = {\n readonly method: HttpMethod\n readonly url: string\n readonly dispatcher: Dispatcher\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Execute HTTP request with Effect-native error handling\n *\n * @param config - Request configuration with dispatcher\n * @returns Effect with success (2xx) and failures (non-2xx + boundary errors)\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses only\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * This forces developers to explicitly handle HTTP errors using:\n * - `Effect.catchTag` for specific error types\n * - `Effect.match` for exhaustive handling\n * - `Effect.catchAll` for generic error handling\n *\n * @pure false - performs HTTP request\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n * @invariant 2xx → success channel, non-2xx → error channel\n * @precondition config.dispatcher handles all schema statuses\n * @postcondition ∀ response: success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n * @complexity O(1) + O(|body|) for text extraction\n */\nexport const executeRequest = (\n config: StrictRequestInit\n): Effect.Effect, ApiFailure, HttpClient.HttpClient> =>\n Effect.gen(function*() {\n // STEP 1: Get HTTP client from context\n const client = yield* HttpClient.HttpClient\n\n // STEP 2: Build request based on method\n const request = buildRequest(config)\n\n // STEP 3: Execute request with error mapping\n const rawResponse = yield* Effect.mapError(\n Effect.gen(function*() {\n const response = yield* client.execute(request)\n const text = yield* response.text\n return asRawResponse({\n status: response.status,\n headers: toNativeHeaders(response.headers),\n text\n })\n }),\n (error): TransportError => ({\n _tag: \"TransportError\",\n error: error instanceof Error ? error : new Error(String(error))\n })\n )\n\n // STEP 4: Delegate classification to dispatcher (handles status/content-type/decode)\n return yield* config.dispatcher(rawResponse)\n })\n\n/**\n * Build HTTP request from config\n *\n * @pure true\n */\nconst buildRequest = (config: StrictRequestInit): HttpClientRequest.HttpClientRequest => {\n const methodMap: Record HttpClientRequest.HttpClientRequest> = {\n get: HttpClientRequest.get,\n post: HttpClientRequest.post,\n put: HttpClientRequest.put,\n patch: HttpClientRequest.patch,\n delete: HttpClientRequest.del,\n head: HttpClientRequest.head,\n options: HttpClientRequest.options\n }\n\n const createRequest = methodMap[config.method] ?? HttpClientRequest.get\n let request = createRequest(config.url)\n\n // Add headers if provided\n if (config.headers !== undefined) {\n const headers = toRecordHeaders(config.headers)\n request = HttpClientRequest.setHeaders(request, headers)\n }\n\n // Add body if provided\n if (config.body !== undefined) {\n const bodyText = typeof config.body === \"string\" ? config.body : JSON.stringify(config.body)\n request = HttpClientRequest.setBody(request, HttpBody.text(bodyText))\n }\n\n return request\n}\n\n/**\n * Convert Headers to Record\n *\n * @pure true\n */\nconst toRecordHeaders = (headers: HeadersInit): Record => {\n if (headers instanceof Headers) {\n const result: Record = {}\n for (const [key, value] of headers.entries()) {\n result[key] = value\n }\n return result\n }\n if (Array.isArray(headers)) {\n const result: Record = {}\n for (const headerPair of headers) {\n const [headerKey, headerValue] = headerPair\n result[headerKey] = headerValue\n }\n return result\n }\n return headers\n}\n\n/**\n * Convert @effect/platform Headers to native Headers\n *\n * @pure true\n */\nconst toNativeHeaders = (platformHeaders: { readonly [key: string]: string }): Headers => {\n const headers = new Headers()\n for (const [key, value] of Object.entries(platformHeaders)) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Helper to create dispatcher from switch-based classifier\n *\n * This function uses a permissive type signature to allow generated code\n * to work with any response variant without requiring exact type matching.\n * The classify function can return any Effect with union types for success/error.\n *\n * NOTE: Uses axioms module for type casts to allow heterogeneous Effect\n * unions from switch statements. The returned Dispatcher is properly typed.\n *\n * @pure true - returns pure function\n * @complexity O(1)\n */\n\nexport const createDispatcher = (\n classify: ClassifyFn\n): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n return classify(response.status, contentType, response.text)\n })\n}\n\n/**\n * Helper to parse JSON with error handling\n *\n * @pure false - performs parsing\n * @effect Effect\n */\nexport const parseJSON = (\n status: number,\n contentType: string,\n text: string\n): Effect.Effect =>\n Effect.try({\n try: () => asJson(JSON.parse(text)),\n catch: (error): ParseError => ({\n _tag: \"ParseError\",\n status,\n contentType,\n error: error instanceof Error ? error : new Error(String(error)),\n body: text\n })\n })\n\n/**\n * Helper to create UnexpectedStatus error\n *\n * @pure true\n */\nexport const unexpectedStatus = (status: number, body: string): UnexpectedStatus => ({\n _tag: \"UnexpectedStatus\",\n status,\n body\n})\n\n/**\n * Helper to create UnexpectedContentType error\n *\n * @pure true\n */\nexport const unexpectedContentType = (\n status: number,\n expected: ReadonlyArray,\n actual: string | undefined,\n body: string\n): UnexpectedContentType => ({\n _tag: \"UnexpectedContentType\",\n status,\n expected,\n actual,\n body\n})\n\n/**\n * Generic client interface for any OpenAPI schema with Effect-native error handling\n *\n * **Effect Channel Design:**\n * - Success channel: `ApiSuccess` - 2xx responses\n * - Error channel: `ApiFailure` - HTTP errors (4xx, 5xx) + boundary errors\n *\n * @pure false - performs HTTP requests\n * @effect Effect, ApiFailure, HttpClient.HttpClient>\n */\nexport type StrictClient = {\n readonly GET: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly POST: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PUT: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly PATCH: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n\n readonly DELETE: (\n path: Path,\n options: RequestOptions\n ) => Effect.Effect<\n ApiSuccess>>,\n ApiFailure>>,\n HttpClient.HttpClient\n >\n}\n\n/**\n * Request options for a specific operation\n */\nexport type RequestOptions<\n Paths extends object,\n Path extends keyof Paths,\n Method extends HttpMethod\n> = {\n readonly dispatcher: Dispatcher>>\n readonly baseUrl: string\n readonly params?: Record\n readonly query?: Record\n readonly headers?: HeadersInit\n readonly body?: BodyInit\n readonly signal?: AbortSignal\n}\n\n/**\n * Create a strict client for an OpenAPI schema\n *\n * @pure true - returns pure client object\n * @complexity O(1)\n */\nexport const createStrictClient = (): StrictClient<\n Paths\n> => {\n const makeRequest = (\n method: Method,\n path: Path,\n options: RequestOptions\n ) => {\n let url = `${options.baseUrl}${String(path)}`\n\n // Replace path parameters\n if (options.params !== undefined) {\n for (const [key, value] of Object.entries(options.params)) {\n url = url.replace(`{${key}}`, encodeURIComponent(String(value)))\n }\n }\n\n // Add query parameters\n if (options.query !== undefined) {\n const params = new URLSearchParams()\n for (const [key, value] of Object.entries(options.query)) {\n params.append(key, String(value))\n }\n url = `${url}?${params.toString()}`\n }\n\n // Build config object, only including optional properties if they are defined\n // This satisfies exactOptionalPropertyTypes constraint\n const config = asStrictRequestInit>>>({\n method,\n url,\n dispatcher: options.dispatcher,\n ...(options.headers !== undefined && { headers: options.headers }),\n ...(options.body !== undefined && { body: options.body }),\n ...(options.signal !== undefined && { signal: options.signal })\n })\n\n return executeRequest(config)\n }\n\n return {\n GET: (path, options) => makeRequest(\"get\", path, options),\n POST: (path, options) => makeRequest(\"post\", path, options),\n PUT: (path, options) => makeRequest(\"put\", path, options),\n PATCH: (path, options) => makeRequest(\"patch\", path, options),\n DELETE: (path, options) => makeRequest(\"delete\", path, options)\n } satisfies StrictClient\n}\n\n// CHANGE: Add universal dispatcher that handles any OpenAPI responses generically\n// WHY: Enable createClient(options) without code generation or manual dispatcher wiring\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ status, ct: universalDispatcher(status, ct, text) → success(2xx) ∨ httpError(non-2xx) ∨ boundaryError\n// PURITY: SHELL\n// EFFECT: Effect, Exclude, TransportError>, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel, no-content → body: undefined\n// COMPLEXITY: O(1) per dispatch + O(|text|) for JSON parsing\n\n/**\n * Create a universal dispatcher that handles any OpenAPI response generically\n *\n * The universal dispatcher classifies responses by status code range:\n * - 2xx → success channel (ApiSuccess)\n * - non-2xx → error channel (HttpError)\n *\n * For JSON content types, it parses the body. For no-content responses (empty body),\n * it returns undefined body with contentType \"none\".\n *\n * This enables using createClient(options) without generating\n * per-operation dispatchers, fulfilling the zero-boilerplate DSL requirement.\n *\n * @pure true - returns pure dispatcher function\n * @complexity O(1) creation + O(|body|) per dispatch\n */\nexport const createUniversalDispatcher = (): Dispatcher => {\n return asDispatcher((response: RawResponse) => {\n const contentType = response.headers.get(\"content-type\") ?? undefined\n const is2xx = response.status >= 200 && response.status < 300\n\n // No-content response (empty body or 204)\n if (response.text === \"\" || response.status === 204) {\n const variant = {\n status: response.status,\n contentType: \"none\" as const,\n body: undefined\n } as const\n\n return is2xx\n ? Effect.succeed(variant)\n : Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n }\n\n // JSON content type\n if (contentType?.includes(\"application/json\")) {\n return Effect.gen(function*() {\n const parsed = yield* parseJSON(response.status, \"application/json\", response.text)\n const variant = {\n status: response.status,\n contentType: \"application/json\" as const,\n body: parsed\n } as const\n\n if (is2xx) {\n return variant\n }\n return yield* Effect.fail({\n _tag: \"HttpError\" as const,\n ...variant\n })\n })\n }\n\n // Unknown content type\n return Effect.fail(unexpectedContentType(\n response.status,\n [\"application/json\"],\n contentType,\n response.text\n ))\n })\n}\n\nexport { type Dispatcher, type RawResponse } from \"../../core/axioms.js\"\n","// CHANGE: Type-safe createClient API with full request-side enforcement\n// WHY: Ensure path/method → operation → request types are all linked\n// QUOTE(ТЗ): \"path + method определяют operation, и из неё выводятся request/response types\"\n// REF: PR#3 blocking review sections 3.2, 3.3\n// SOURCE: n/a\n// PURITY: SHELL\n// EFFECT: Creates Effect-based API client\n// INVARIANT: All operations are type-safe from path → operation → request → response\n// COMPLEXITY: O(1) client creation\n\nimport type * as HttpClient from \"@effect/platform/HttpClient\"\nimport { Effect } from \"effect\"\nimport type { HttpMethod } from \"openapi-typescript-helpers\"\n\nimport { asDispatchersFor, asStrictApiClient, asStrictRequestInit, type Dispatcher } from \"../../core/axioms.js\"\nimport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n DispatchersForMethod,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nimport type { StrictRequestInit } from \"./strict-client.js\"\nimport { createUniversalDispatcher, executeRequest } from \"./strict-client.js\"\n\nexport type {\n ClientEffect,\n ClientOptions,\n DispatchersFor,\n StrictApiClient,\n StrictApiClientWithDispatchers\n} from \"./create-client-types.js\"\nexport { createUniversalDispatcher } from \"./strict-client.js\"\n\n/**\n * Primitive value type for path/query parameters\n *\n * @pure true - type alias only\n */\ntype ParamValue = string | number | boolean\n\n/**\n * Query parameter value - can be primitive or array of primitives\n *\n * @pure true - type alias only\n */\ntype QueryValue = ParamValue | ReadonlyArray\n\n// CHANGE: Add default dispatcher registry for auto-dispatching createClient\n// WHY: Allow createClient(options) without explicitly passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: defaultDispatchers = dispatchersByPath ⇒ createClient uses dispatcher(path, method)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: defaultDispatchers is set before createClient use\n// COMPLEXITY: O(1)\nlet defaultDispatchers: DispatchersFor | undefined\n\n/**\n * Register default dispatcher map used by createClient(options)\n *\n * @pure false - mutates module-level registry\n * @invariant defaultDispatchers set exactly once per app boot\n */\nexport const registerDefaultDispatchers = (\n dispatchers: DispatchersFor\n): void => {\n defaultDispatchers = dispatchers\n}\n\n/**\n * Resolve default dispatcher map or fail fast\n *\n * @pure false - reads module-level registry\n * @invariant defaultDispatchers must be set for auto-dispatching client\n */\nconst resolveDefaultDispatchers = (): DispatchersFor => {\n if (defaultDispatchers === undefined) {\n throw new Error(\"Default dispatchers are not registered. Import generated dispatchers module.\")\n }\n return asDispatchersFor>(defaultDispatchers)\n}\n\nconst applyPathParams = (path: string, params?: Record): string => {\n if (params === undefined) {\n return path\n }\n\n let url = path\n for (const [key, value] of Object.entries(params)) {\n url = url.replace(\"{\" + key + \"}\", encodeURIComponent(String(value)))\n }\n return url\n}\n\nconst buildQueryString = (query?: Record): string => {\n if (query === undefined) {\n return \"\"\n }\n\n const searchParams = new URLSearchParams()\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n searchParams.append(key, String(item))\n }\n continue\n }\n searchParams.set(key, String(value))\n }\n return searchParams.toString()\n}\n\nconst appendQueryString = (url: string, queryString: string): string => {\n if (queryString.length === 0) {\n return url\n }\n return url.includes(\"?\") ? url + \"&\" + queryString : url + \"?\" + queryString\n}\n\nconst withBaseUrl = (baseUrl: string | undefined, url: string): string => {\n // If baseUrl is not provided, keep a relative URL (browser-friendly)\n if (baseUrl === undefined || baseUrl === \"\") {\n return url\n }\n\n // Construct full URL\n return new URL(url, baseUrl).toString()\n}\n\n/**\n * Build URL with path parameters and query string\n *\n * @param baseUrl - Base URL for the API\n * @param path - Path template with placeholders\n * @param params - Path parameters to substitute\n * @param query - Query parameters to append\n * @returns Fully constructed URL\n *\n * @pure true\n * @complexity O(n + m) where n = |params|, m = |query|\n */\nconst buildUrl = (\n baseUrl: string | undefined,\n path: string,\n params?: Record,\n query?: Record\n): string => {\n const urlWithParams = applyPathParams(path, params)\n const queryString = buildQueryString(query)\n const urlWithQuery = appendQueryString(urlWithParams, queryString)\n return withBaseUrl(baseUrl, urlWithQuery)\n}\n\n/**\n * Check if body is already a BodyInit type (not a plain object needing serialization)\n *\n * @pure true\n */\nconst isBodyInit = (body: BodyInit | object): body is BodyInit =>\n typeof body === \"string\"\n || body instanceof Blob\n || body instanceof ArrayBuffer\n || body instanceof ReadableStream\n || body instanceof FormData\n || body instanceof URLSearchParams\n\n/**\n * Serialize body to BodyInit - passes through BodyInit types, JSON-stringifies objects\n *\n * @pure true\n * @returns BodyInit or undefined, with consistent return path\n */\nconst serializeBody = (body: BodyInit | object | undefined): BodyInit | undefined => {\n // Early return for undefined\n if (body === undefined) {\n return body\n }\n // Pass through existing BodyInit types\n if (isBodyInit(body)) {\n return body\n }\n // Plain object - serialize to JSON string (which is a valid BodyInit)\n const serialized: BodyInit = JSON.stringify(body)\n return serialized\n}\n\n/**\n * Check if body requires JSON Content-Type header\n *\n * @pure true\n */\nconst needsJsonContentType = (body: BodyInit | object | undefined): boolean =>\n body !== undefined\n && typeof body !== \"string\"\n && !(body instanceof Blob)\n && !(body instanceof FormData)\n\nconst toHeadersFromRecord = (\n headersInit: Record<\n string,\n | string\n | number\n | boolean\n | ReadonlyArray\n | null\n | undefined\n >\n): Headers => {\n const headers = new Headers()\n\n for (const [key, value] of Object.entries(headersInit)) {\n if (value === null || value === undefined) {\n continue\n }\n if (Array.isArray(value)) {\n headers.set(key, value.map(String).join(\",\"))\n continue\n }\n headers.set(key, String(value))\n }\n\n return headers\n}\n\n/**\n * Merge headers from client options and request options\n *\n * @pure true\n * @complexity O(n) where n = number of headers\n */\nconst toHeaders = (headersInit: ClientOptions[\"headers\"] | undefined): Headers => {\n if (headersInit === undefined) {\n return new Headers()\n }\n\n if (headersInit instanceof Headers) {\n return new Headers(headersInit)\n }\n\n if (Array.isArray(headersInit)) {\n return new Headers(headersInit)\n }\n\n return toHeadersFromRecord(headersInit)\n}\n\nconst mergeHeaders = (\n clientHeaders: ClientOptions[\"headers\"] | undefined,\n requestHeaders: ClientOptions[\"headers\"] | undefined\n): Headers => {\n const headers = toHeaders(clientHeaders)\n const optHeaders = toHeaders(requestHeaders)\n for (const [key, value] of optHeaders.entries()) {\n headers.set(key, value)\n }\n return headers\n}\n\n/**\n * Request options type for method handlers\n *\n * @pure true - type alias only\n */\ntype MethodHandlerOptions = {\n params?: Record | undefined\n query?: Record | undefined\n body?: BodyInit | object | undefined\n headers?: ClientOptions[\"headers\"] | undefined\n signal?: AbortSignal | undefined\n}\n\n/**\n * Create HTTP method handler with full type constraints\n *\n * @param method - HTTP method\n * @param clientOptions - Client configuration\n * @returns Method handler function\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandler = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n dispatcher: Dispatcher,\n options?: MethodHandlerOptions\n) => {\n const url = buildUrl(clientOptions.baseUrl, path, options?.params, options?.query)\n const headers = mergeHeaders(clientOptions.headers, options?.headers)\n const body = serializeBody(options?.body)\n\n if (needsJsonContentType(options?.body)) {\n headers.set(\"Content-Type\", \"application/json\")\n }\n\n const config: StrictRequestInit = asStrictRequestInit({\n method,\n url,\n dispatcher,\n headers,\n body,\n signal: options?.signal\n })\n\n return executeRequest(config)\n}\n\n/**\n * Create method handler that infers dispatcher from map\n *\n * @pure false - creates function that performs HTTP requests\n * @complexity O(1) handler creation\n */\nconst createMethodHandlerWithDispatchers = (\n method: Method,\n clientOptions: ClientOptions,\n dispatchers: DispatchersForMethod\n) =>\n & string>(\n path: Path,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n dispatchers[path][method],\n options\n )\n\n// CHANGE: Create method handler that infers dispatcher from map\n// WHY: Allow per-call API without passing dispatcher parameter\n// QUOTE(ТЗ): \"Зачем передавать что либо в GET\"\n// REF: user-msg-1\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path ∈ PathsForMethod: dispatchers[path][method] = Dispatcher>\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: Dispatcher lookup is total for all operations in Paths\n// COMPLEXITY: O(1) runtime + O(1) dispatcher lookup\n/**\n * Create type-safe Effect-based API client\n *\n * The client enforces:\n * 1. Method availability: GET only on paths with `get`, POST only on paths with `post`\n * 2. Dispatcher correlation: must match operation's responses\n * 3. Request options: params/query/body typed from operation\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import createClient from \"openapi-effect\"\n * import type { Paths } from \"./generated/schema\"\n * import \"./generated/dispatchers-by-path\" // registers default dispatchers\n *\n * const client = createClient({\n * baseUrl: \"https://api.example.com\",\n * credentials: \"include\"\n * })\n *\n * // Type-safe call - dispatcher inferred from path+method\n * const result = yield* client.GET(\"/pets/{petId}\", {\n * params: { petId: \"123\" } // Required because getPet has path params\n * })\n *\n * // Compile error: \"/pets/{petId}\" has no \"put\" method\n * // client.PUT(\"/pets/{petId}\", ...) // Type error!\n * ```\n */\nexport const createClient = (\n options: ClientOptions,\n dispatchers?: DispatchersFor\n): StrictApiClientWithDispatchers => {\n const resolvedDispatchers = dispatchers ?? resolveDefaultDispatchers()\n\n return asStrictApiClient>({\n GET: createMethodHandlerWithDispatchers(\"get\", options, resolvedDispatchers),\n POST: createMethodHandlerWithDispatchers(\"post\", options, resolvedDispatchers),\n PUT: createMethodHandlerWithDispatchers(\"put\", options, resolvedDispatchers),\n DELETE: createMethodHandlerWithDispatchers(\"delete\", options, resolvedDispatchers),\n PATCH: createMethodHandlerWithDispatchers(\"patch\", options, resolvedDispatchers),\n HEAD: createMethodHandlerWithDispatchers(\"head\", options, resolvedDispatchers),\n OPTIONS: createMethodHandlerWithDispatchers(\"options\", options, resolvedDispatchers)\n })\n}\n\n// CHANGE: Add createMethodHandlerWithUniversalDispatcher for zero-boilerplate client\n// WHY: Enable createClientEffect(options) without code generation or dispatcher registry\n// QUOTE(ТЗ): \"Я не хочу создавать какие-то дополнительные модули\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ path, method: universalDispatcher handles response classification generically\n// PURITY: SHELL\n// EFFECT: Effect, ApiFailure, HttpClient>\n// INVARIANT: 2xx → success channel, non-2xx → error channel\n// COMPLEXITY: O(1) handler creation + O(1) universal dispatcher creation per call\nconst createMethodHandlerWithUniversalDispatcher = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n createMethodHandler(method, clientOptions)(\n path,\n createUniversalDispatcher(),\n options\n )\n\ntype HttpErrorTag = { readonly _tag: \"HttpError\" }\n\nconst isHttpErrorValue = (error: unknown): error is HttpErrorTag =>\n typeof error === \"object\"\n && error !== null\n && \"_tag\" in error\n && Reflect.get(error, \"_tag\") === \"HttpError\"\n\nconst exposeHttpErrorsAsValues = (\n request: Effect.Effect\n): Effect.Effect<\n A | Extract,\n Exclude>,\n HttpClient.HttpClient\n> =>\n request.pipe(\n Effect.catchIf(\n (error): error is Extract => isHttpErrorValue(error),\n (error) => Effect.succeed(error)\n )\n )\n\nconst createMethodHandlerWithUniversalDispatcherValue = (\n method: HttpMethod,\n clientOptions: ClientOptions\n) =>\n(\n path: string,\n options?: MethodHandlerOptions\n) =>\n exposeHttpErrorsAsValues(\n createMethodHandlerWithUniversalDispatcher(method, clientOptions)(path, options)\n )\n\n// CHANGE: Add createClientEffect — zero-boilerplate Effect-based API client\n// WHY: Enable the user's desired DSL without any generated code or dispatcher setup\n// QUOTE(ТЗ): \"const apiClientEffect = createClientEffect(clientOptions); apiClientEffect.POST('/api/auth/login', { body: credentials })\"\n// REF: issue-5\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ Paths, options: createClientEffect(options) → ClientEffect\n// PURITY: SHELL\n// EFFECT: Client methods return Effect\n// INVARIANT: ∀ path, method: path ∈ PathsForMethod (compile-time) ∧ response classified by status range (runtime)\n// COMPLEXITY: O(1) client creation\n/**\n * Create type-safe Effect-based API client with zero boilerplate\n *\n * Uses a universal dispatcher and exposes HTTP statuses as values:\n * - 2xx → success value (ApiSuccess)\n * - non-2xx schema statuses → success value (HttpError with _tag)\n * - boundary/protocol failures stay in error channel\n * - JSON parsed automatically for application/json content types\n *\n * **No code generation needed.** No dispatcher registry needed.\n * Just pass your OpenAPI Paths type and client options.\n *\n * @typeParam Paths - OpenAPI paths type from openapi-typescript\n * @param options - Client configuration (baseUrl, credentials, headers, etc.)\n * @returns API client with typed methods for all operations\n *\n * @pure false - creates client that performs HTTP requests\n * @effect Client methods return Effect\n * @invariant ∀ path, method: path ∈ PathsForMethod\n * @complexity O(1) client creation\n *\n * @example\n * ```typescript\n * import { createClientEffect, type ClientOptions } from \"openapi-effect\"\n * import type { paths } from \"./openapi.d.ts\"\n *\n * const clientOptions: ClientOptions = {\n * baseUrl: \"https://petstore.example.com\",\n * credentials: \"include\"\n * }\n * const apiClientEffect = createClientEffect(clientOptions)\n *\n * // Type-safe call — path, method, and body all enforced at compile time\n * const result = yield* apiClientEffect.POST(\"/api/auth/login\", {\n * body: { email: \"user@example.com\", password: \"secret\" }\n * })\n * ```\n */\nexport const createClientEffect = (\n options: ClientOptions\n): ClientEffect => {\n return asStrictApiClient>({\n GET: createMethodHandlerWithUniversalDispatcherValue(\"get\", options),\n POST: createMethodHandlerWithUniversalDispatcherValue(\"post\", options),\n PUT: createMethodHandlerWithUniversalDispatcherValue(\"put\", options),\n DELETE: createMethodHandlerWithUniversalDispatcherValue(\"delete\", options),\n PATCH: createMethodHandlerWithUniversalDispatcherValue(\"patch\", options),\n HEAD: createMethodHandlerWithUniversalDispatcherValue(\"head\", options),\n OPTIONS: createMethodHandlerWithUniversalDispatcherValue(\"options\", options)\n })\n}\n","// CHANGE: Auto-generated decoder stubs for all operations\n// WHY: Provide type-safe runtime validation entry points\n// QUOTE(ТЗ): \"при изменении схемы сборка обязана падать, пока декодеры не обновлены\"\n// REF: issue-2, section 5.2\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op, status: decoder(op, status) → Effect\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: All decoders return typed DecodeError on failure\n// COMPLEXITY: O(n) where n = size of parsed object\n\nimport { Effect } from \"effect\"\nimport type { DecodeError } from \"../core/api-client/strict-types.js\"\n\n/**\n * JSON value type - result of JSON.parse()\n */\ntype Json = null | boolean | number | string | ReadonlyArray | { readonly [k: string]: Json }\n\n/**\n * Decoder for listPets status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for listPets status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodelistPets_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 201 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_201 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 400 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_400 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for createPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodecreatePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 200 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_200 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for getPet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodegetPet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 404 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_404 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n\n/**\n * Decoder for deletePet status 500 (application/json)\n * STUB: Replace with real schema decoder when needed\n *\n * @pure false - performs validation\n * @effect Effect\n */\nexport const decodedeletePet_500 = (\n _status: number,\n _contentType: string,\n _body: string,\n parsed: Json\n): Effect.Effect => {\n // STUB: Always succeeds with parsed value\n // Replace with: Schema.decodeUnknown(YourSchema)(parsed)\n return Effect.succeed(parsed)\n\n // Example of real decoder:\n // return Effect.mapError(\n // Schema.decodeUnknown(YourSchema)(parsed),\n // (error): DecodeError => ({\n // _tag: \"DecodeError\",\n // status,\n // contentType,\n // error,\n // body\n // })\n // )\n}\n","// CHANGE: Auto-generated dispatchers for all operations with Effect-native error handling\n// WHY: Maintain compile-time correlation between status codes and body types\n// QUOTE(ТЗ): \"реализует switch(status) по всем статусам схемы; Failure включает все инварианты протокола и схемы\"\n// REF: issue-2, section 5.2, 4.1-4.3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ op ∈ Operations: dispatcher(op) → Effect\n// PURITY: SHELL\n// EFFECT: Effect, HttpError | BoundaryError, never>\n// INVARIANT: 2xx → success channel, non-2xx → error channel (forced handling)\n// COMPLEXITY: O(1) per dispatch (Match lookup)\n\nimport { Effect, Match } from \"effect\"\nimport type { Operations } from \"../../tests/fixtures/petstore.openapi.js\"\nimport type { DecodeError, ResponsesFor } from \"../core/api-client/strict-types.js\"\nimport { asConst, type Json } from \"../core/axioms.js\"\nimport {\n createDispatcher,\n parseJSON,\n unexpectedContentType,\n unexpectedStatus\n} from \"../shell/api-client/strict-client.js\"\nimport * as Decoders from \"./decoders.js\"\n\n// Response types for each operation - used for type inference\ntype ListPetsResponses = ResponsesFor\ntype CreatePetResponses = ResponsesFor\ntype GetPetResponses = ResponsesFor\ntype DeletePetResponses = ResponsesFor\n\n/**\n * Helper: process JSON content type for a given status - returns SUCCESS variant\n * Used for 2xx responses that go to the success channel\n */\nconst processJsonContentSuccess = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n return asConst({\n status,\n contentType: \"application/json\" as const,\n body: decoded\n })\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Helper: process JSON content type for a given status - returns HTTP ERROR variant\n * Used for non-2xx responses (4xx, 5xx) that go to the error channel.\n *\n * Adds `_tag: \"HttpError\"` discriminator to distinguish from BoundaryError.\n */\nconst processJsonContentError = (\n status: S,\n contentType: string | undefined,\n text: string,\n decoder: (\n s: number,\n ct: string,\n body: string,\n parsed: Json\n ) => Effect.Effect\n) =>\n contentType?.includes(\"application/json\")\n ? Effect.gen(function*() {\n const parsed = yield* parseJSON(status, \"application/json\", text)\n const decoded = yield* decoder(status, \"application/json\", text, parsed)\n // Non-2xx: Return as FAILURE with _tag discriminator (goes to error channel)\n return yield* Effect.fail(asConst({\n _tag: \"HttpError\" as const,\n status,\n contentType: \"application/json\" as const,\n body: decoded\n }))\n })\n : Effect.fail(unexpectedContentType(status, [\"application/json\"], contentType, text))\n\n/**\n * Dispatcher for listPets\n * Handles statuses: 200 (success), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherlistPets = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodelistPets_200)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodelistPets_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for createPet\n * Handles statuses: 201 (success), 400 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 201: success channel → ApiSuccess\n * - 400, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchercreatePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(201, () => processJsonContentSuccess(201, contentType, text, Decoders.decodecreatePet_201)),\n Match.when(400, () => processJsonContentError(400, contentType, text, Decoders.decodecreatePet_400)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodecreatePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for getPet\n * Handles statuses: 200 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 200: success channel → ApiSuccess\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatchergetPet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(200, () => processJsonContentSuccess(200, contentType, text, Decoders.decodegetPet_200)),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodegetPet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodegetPet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n\n/**\n * Dispatcher for deletePet\n * Handles statuses: 204 (success), 404 (error), 500 (error)\n *\n * Effect channel mapping:\n * - 204: success channel → ApiSuccess (no content)\n * - 404, 500: error channel → HttpError (forces explicit handling)\n *\n * @pure false - applies decoders\n * @invariant Exhaustive coverage of all schema statuses\n */\nexport const dispatcherdeletePet = createDispatcher((status, contentType, text) =>\n Match.value(status).pipe(\n Match.when(204, () =>\n Effect.succeed(\n asConst({\n status: 204,\n contentType: \"none\" as const,\n body: undefined\n })\n )),\n Match.when(404, () => processJsonContentError(404, contentType, text, Decoders.decodedeletePet_404)),\n Match.when(500, () => processJsonContentError(500, contentType, text, Decoders.decodedeletePet_500)),\n Match.orElse(() => Effect.fail(unexpectedStatus(status, text)))\n )\n)\n","// CHANGE: Auto-generated dispatcher map by path+method\n// WHY: Provide a single dispatcher registry without manual wiring in examples\n// QUOTE(ТЗ): \"Этого в плане вообще не должно быть\"\n// REF: user-msg-3\n// SOURCE: Generated from tests/fixtures/petstore.openapi.json\n// FORMAT THEOREM: ∀ path, method: dispatchersByPath[path][method] = dispatcher(op)\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: dispatcher map is total for all operations in Paths\n// COMPLEXITY: O(1)\n\nimport type { Paths } from \"../../tests/fixtures/petstore.openapi.js\"\nimport { type DispatchersFor, registerDefaultDispatchers } from \"../shell/api-client/create-client.js\"\nimport { dispatchercreatePet, dispatcherdeletePet, dispatchergetPet, dispatcherlistPets } from \"./dispatch.js\"\n\n/**\n * Dispatcher map keyed by OpenAPI path and HTTP method\n */\nexport const dispatchersByPath: DispatchersFor = {\n \"/pets\": {\n get: dispatcherlistPets,\n post: dispatchercreatePet\n },\n \"/pets/{petId}\": {\n get: dispatchergetPet,\n delete: dispatcherdeletePet\n }\n}\n\n// CHANGE: Register default dispatchers at module load\n// WHY: Enable createClient(options) without passing dispatcher map\n// QUOTE(ТЗ): \"const apiClient = createClient(clientOptions)\"\n// REF: user-msg-4\n// SOURCE: n/a\n// FORMAT THEOREM: ∀ call: createClient(options) uses dispatchersByPath\n// PURITY: SHELL\n// EFFECT: none\n// INVARIANT: registerDefaultDispatchers is called exactly once per module load\n// COMPLEXITY: O(1)\nregisterDefaultDispatchers(dispatchersByPath)\n"],"names":["assertNever","x","asJson","value","asConst","asRawResponse","asDispatcher","fn","asStrictRequestInit","config","asStrictApiClient","client","asDispatchersFor","executeRequest","Effect","HttpClient","request","buildRequest","rawResponse","response","text","toNativeHeaders","error","HttpClientRequest","headers","toRecordHeaders","bodyText","HttpBody","result","key","headerPair","headerKey","headerValue","platformHeaders","createDispatcher","classify","contentType","parseJSON","status","unexpectedStatus","body","unexpectedContentType","expected","actual","createStrictClient","makeRequest","method","path","options","url","params","createUniversalDispatcher","is2xx","variant","parsed","defaultDispatchers","registerDefaultDispatchers","dispatchers","resolveDefaultDispatchers","applyPathParams","buildQueryString","query","searchParams","item","appendQueryString","queryString","withBaseUrl","baseUrl","buildUrl","urlWithParams","urlWithQuery","isBodyInit","serializeBody","needsJsonContentType","toHeadersFromRecord","headersInit","toHeaders","mergeHeaders","clientHeaders","requestHeaders","optHeaders","createMethodHandler","clientOptions","dispatcher","createMethodHandlerWithDispatchers","createClient","resolvedDispatchers","createMethodHandlerWithUniversalDispatcher","isHttpErrorValue","exposeHttpErrorsAsValues","createMethodHandlerWithUniversalDispatcherValue","createClientEffect","decodelistPets_200","_status","_contentType","_body","decodelistPets_500","decodecreatePet_201","decodecreatePet_400","decodecreatePet_500","decodegetPet_200","decodegetPet_404","decodegetPet_500","decodedeletePet_404","decodedeletePet_500","processJsonContentSuccess","decoder","decoded","processJsonContentError","dispatcherlistPets","Match","Decoders.decodelistPets_200","Decoders.decodelistPets_500","dispatchercreatePet","Decoders.decodecreatePet_201","Decoders.decodecreatePet_400","Decoders.decodecreatePet_500","dispatchergetPet","Decoders.decodegetPet_200","Decoders.decodegetPet_404","Decoders.decodegetPet_500","dispatcherdeletePet","Decoders.decodedeletePet_404","Decoders.decodedeletePet_500","dispatchersByPath"],"mappings":"gUA0VO,MAAMA,GAAeC,GAAoB,CAC9C,MAAM,IAAI,MAAM,qBAAqB,KAAK,UAAUA,CAAC,CAAC,EAAE,CAC1D,EC9SaC,EAAUC,GAAyBA,EAQnCC,EAAcD,GAAgBA,EAc9BE,EAAiBF,GAIXA,EAuBNG,EACXC,GAC0BA,EAQfC,EAA0BC,GAAsBA,EA6BhDC,EAAwBC,GAAsBA,EAQ9CC,EAAuBT,GAAsBA,ECvD7CU,EACXJ,GAEAK,EAAO,IAAI,WAAY,CAErB,MAAMH,EAAS,MAAOI,EAAW,WAG3BC,EAAUC,EAAaR,CAAM,EAG7BS,EAAc,MAAOJ,EAAO,SAChCA,EAAO,IAAI,WAAY,CACrB,MAAMK,EAAW,MAAOR,EAAO,QAAQK,CAAO,EACxCI,EAAO,MAAOD,EAAS,KAC7B,OAAOd,EAAc,CACnB,OAAQc,EAAS,OACjB,QAASE,EAAgBF,EAAS,OAAO,EACzC,KAAAC,CAAA,CACD,CACH,CAAC,EACAE,IAA2B,CAC1B,KAAM,iBACN,MAAOA,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,CAAA,EACjE,EAIF,OAAO,MAAOb,EAAO,WAAWS,CAAW,CAC7C,CAAC,EAOGD,EAA2BR,GAA8E,CAY7G,IAAIO,GAXoF,CACtF,IAAKO,EAAkB,IACvB,KAAMA,EAAkB,KACxB,IAAKA,EAAkB,IACvB,MAAOA,EAAkB,MACzB,OAAQA,EAAkB,IAC1B,KAAMA,EAAkB,KACxB,QAASA,EAAkB,OAAA,EAGGd,EAAO,MAAM,GAAKc,EAAkB,KACxCd,EAAO,GAAG,EAGtC,GAAIA,EAAO,UAAY,OAAW,CAChC,MAAMe,EAAUC,EAAgBhB,EAAO,OAAO,EAC9CO,EAAUO,EAAkB,WAAWP,EAASQ,CAAO,CACzD,CAGA,GAAIf,EAAO,OAAS,OAAW,CAC7B,MAAMiB,EAAW,OAAOjB,EAAO,MAAS,SAAWA,EAAO,KAAO,KAAK,UAAUA,EAAO,IAAI,EAC3FO,EAAUO,EAAkB,QAAQP,EAASW,EAAS,KAAKD,CAAQ,CAAC,CACtE,CAEA,OAAOV,CACT,EAOMS,EAAmBD,GAAiD,CACxE,GAAIA,aAAmB,QAAS,CAC9B,MAAMI,EAAiC,CAAA,EACvC,SAAW,CAACC,EAAK1B,CAAK,IAAKqB,EAAQ,UACjCI,EAAOC,CAAG,EAAI1B,EAEhB,OAAOyB,CACT,CACA,GAAI,MAAM,QAAQJ,CAAO,EAAG,CAC1B,MAAMI,EAAiC,CAAA,EACvC,UAAWE,KAAcN,EAAS,CAChC,KAAM,CAACO,EAAWC,CAAW,EAAIF,EACjCF,EAAOG,CAAS,EAAIC,CACtB,CACA,OAAOJ,CACT,CACA,OAAOJ,CACT,EAOMH,EAAmBY,GAAiE,CACxF,MAAMT,EAAU,IAAI,QACpB,SAAW,CAACK,EAAK1B,CAAK,IAAK,OAAO,QAAQ8B,CAAe,EACvDT,EAAQ,IAAIK,EAAK1B,CAAK,EAExB,OAAOqB,CACT,EAgBaU,EACXC,GAEO7B,EAAyBa,GAA0B,CACxD,MAAMiB,EAAcjB,EAAS,QAAQ,IAAI,cAAc,GAAK,OAC5D,OAAOgB,EAAShB,EAAS,OAAQiB,EAAajB,EAAS,IAAI,CAC7D,CAAC,EASUkB,EAAY,CACvBC,EACAF,EACAhB,IAEAN,EAAO,IAAI,CACT,IAAK,IAAMZ,EAAO,KAAK,MAAMkB,CAAI,CAAC,EAClC,MAAQE,IAAuB,CAC7B,KAAM,aACN,OAAAgB,EACA,YAAAF,EACA,MAAOd,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,EAC/D,KAAMF,CAAA,EAEV,CAAC,EAOUmB,EAAmB,CAACD,EAAgBE,KAAoC,CACnF,KAAM,mBACN,OAAAF,EACA,KAAAE,CACF,GAOaC,EAAwB,CACnCH,EACAI,EACAC,EACAH,KAC2B,CAC3B,KAAM,wBACN,OAAAF,EACA,SAAAI,EACA,OAAAC,EACA,KAAAH,CACF,GAkFaI,GAAqB,IAE7B,CACH,MAAMC,EAAc,CAClBC,EACAC,EACAC,IACG,CACH,IAAIC,EAAM,GAAGD,EAAQ,OAAO,GAAG,OAAOD,CAAI,CAAC,GAG3C,GAAIC,EAAQ,SAAW,OACrB,SAAW,CAACnB,EAAK1B,CAAK,IAAK,OAAO,QAAQ6C,EAAQ,MAAM,EACtDC,EAAMA,EAAI,QAAQ,IAAIpB,CAAG,IAAK,mBAAmB,OAAO1B,CAAK,CAAC,CAAC,EAKnE,GAAI6C,EAAQ,QAAU,OAAW,CAC/B,MAAME,EAAS,IAAI,gBACnB,SAAW,CAACrB,EAAK1B,CAAK,IAAK,OAAO,QAAQ6C,EAAQ,KAAK,EACrDE,EAAO,OAAOrB,EAAK,OAAO1B,CAAK,CAAC,EAElC8C,EAAM,GAAGA,CAAG,IAAIC,EAAO,UAAU,EACnC,CAIA,MAAMzC,EAASD,EAAwF,CACrG,OAAAsC,EACA,IAAAG,EACA,WAAYD,EAAQ,WACpB,GAAIA,EAAQ,UAAY,QAAa,CAAE,QAASA,EAAQ,OAAA,EACxD,GAAIA,EAAQ,OAAS,QAAa,CAAE,KAAMA,EAAQ,IAAA,EAClD,GAAIA,EAAQ,SAAW,QAAa,CAAE,OAAQA,EAAQ,MAAA,CAAO,CAC9D,EAED,OAAOnC,EAAeJ,CAAM,CAC9B,EAEA,MAAO,CACL,IAAK,CAACsC,EAAMC,IAAYH,EAAY,MAAOE,EAAMC,CAAO,EACxD,KAAM,CAACD,EAAMC,IAAYH,EAAY,OAAQE,EAAMC,CAAO,EAC1D,IAAK,CAACD,EAAMC,IAAYH,EAAY,MAAOE,EAAMC,CAAO,EACxD,MAAO,CAACD,EAAMC,IAAYH,EAAY,QAASE,EAAMC,CAAO,EAC5D,OAAQ,CAACD,EAAMC,IAAYH,EAAY,SAAUE,EAAMC,CAAO,CAAA,CAElE,EA6BaG,EAA4B,IAChC7C,EAAyBa,GAA0B,CACxD,MAAMiB,EAAcjB,EAAS,QAAQ,IAAI,cAAc,GAAK,OACtDiC,EAAQjC,EAAS,QAAU,KAAOA,EAAS,OAAS,IAG1D,GAAIA,EAAS,OAAS,IAAMA,EAAS,SAAW,IAAK,CACnD,MAAMkC,EAAU,CACd,OAAQlC,EAAS,OACjB,YAAa,OACb,KAAM,MAAA,EAGR,OAAOiC,EACHtC,EAAO,QAAQuC,CAAO,EACtBvC,EAAO,KAAK,CACZ,KAAM,YACN,GAAGuC,CAAA,CACJ,CACL,CAGA,OAAIjB,GAAa,SAAS,kBAAkB,EACnCtB,EAAO,IAAI,WAAY,CAC5B,MAAMwC,EAAS,MAAOjB,EAAUlB,EAAS,OAAQ,mBAAoBA,EAAS,IAAI,EAC5EkC,EAAU,CACd,OAAQlC,EAAS,OACjB,YAAa,mBACb,KAAMmC,CAAA,EAGR,OAAIF,EACKC,EAEF,MAAOvC,EAAO,KAAK,CACxB,KAAM,YACN,GAAGuC,CAAA,CACJ,CACH,CAAC,EAIIvC,EAAO,KAAK2B,EACjBtB,EAAS,OACT,CAAC,kBAAkB,EACnBiB,EACAjB,EAAS,IAAA,CACV,CACH,CAAC,ECzZH,IAAIoC,EAQG,MAAMC,EACXC,GACS,CACTF,EAAqBE,CACvB,EAQMC,EAA4B,IAAmD,CACnF,GAAIH,IAAuB,OACzB,MAAM,IAAI,MAAM,8EAA8E,EAEhG,OAAO3C,EAAwC2C,CAAkB,CACnE,EAEMI,EAAkB,CAACZ,EAAcG,IAAgD,CACrF,GAAIA,IAAW,OACb,OAAOH,EAGT,IAAIE,EAAMF,EACV,SAAW,CAAClB,EAAK1B,CAAK,IAAK,OAAO,QAAQ+C,CAAM,EAC9CD,EAAMA,EAAI,QAAQ,IAAMpB,EAAM,IAAK,mBAAmB,OAAO1B,CAAK,CAAC,CAAC,EAEtE,OAAO8C,CACT,EAEMW,EAAoBC,GAA+C,CACvE,GAAIA,IAAU,OACZ,MAAO,GAGT,MAAMC,EAAe,IAAI,gBACzB,SAAW,CAACjC,EAAK1B,CAAK,IAAK,OAAO,QAAQ0D,CAAK,EAAG,CAChD,GAAI,MAAM,QAAQ1D,CAAK,EAAG,CACxB,UAAW4D,KAAQ5D,EACjB2D,EAAa,OAAOjC,EAAK,OAAOkC,CAAI,CAAC,EAEvC,QACF,CACAD,EAAa,IAAIjC,EAAK,OAAO1B,CAAK,CAAC,CACrC,CACA,OAAO2D,EAAa,SAAA,CACtB,EAEME,EAAoB,CAACf,EAAagB,IAClCA,EAAY,SAAW,EAClBhB,EAEFA,EAAI,SAAS,GAAG,EAAIA,EAAM,IAAMgB,EAAchB,EAAM,IAAMgB,EAG7DC,EAAc,CAACC,EAA6BlB,IAE5CkB,IAAY,QAAaA,IAAY,GAChClB,EAIF,IAAI,IAAIA,EAAKkB,CAAO,EAAE,SAAA,EAezBC,EAAW,CACfD,EACApB,EACAG,EACAW,IACW,CACX,MAAMQ,EAAgBV,EAAgBZ,EAAMG,CAAM,EAC5Ce,EAAcL,EAAiBC,CAAK,EACpCS,EAAeN,EAAkBK,EAAeJ,CAAW,EACjE,OAAOC,EAAYC,EAASG,CAAY,CAC1C,EAOMC,EAAc/B,GAClB,OAAOA,GAAS,UACbA,aAAgB,MAChBA,aAAgB,aAChBA,aAAgB,gBAChBA,aAAgB,UAChBA,aAAgB,gBAQfgC,EAAiBhC,GAEjBA,IAAS,QAIT+B,EAAW/B,CAAI,EACVA,EAGoB,KAAK,UAAUA,CAAI,EAS5CiC,EAAwBjC,GAC5BA,IAAS,QACN,OAAOA,GAAS,UAChB,EAAEA,aAAgB,OAClB,EAAEA,aAAgB,UAEjBkC,EACJC,GASY,CACZ,MAAMnD,EAAU,IAAI,QAEpB,SAAW,CAACK,EAAK1B,CAAK,IAAK,OAAO,QAAQwE,CAAW,EACnD,GAAIxE,GAAU,KAGd,IAAI,MAAM,QAAQA,CAAK,EAAG,CACxBqB,EAAQ,IAAIK,EAAK1B,EAAM,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC,EAC5C,QACF,CACAqB,EAAQ,IAAIK,EAAK,OAAO1B,CAAK,CAAC,EAGhC,OAAOqB,CACT,EAQMoD,EAAaD,GACbA,IAAgB,OACX,IAAI,QAGTA,aAAuB,QAClB,IAAI,QAAQA,CAAW,EAG5B,MAAM,QAAQA,CAAW,EACpB,IAAI,QAAQA,CAAW,EAGzBD,EAAoBC,CAAW,EAGlCE,EAAe,CACnBC,EACAC,IACY,CACZ,MAAMvD,EAAUoD,EAAUE,CAAa,EACjCE,EAAaJ,EAAUG,CAAc,EAC3C,SAAW,CAAClD,EAAK1B,CAAK,IAAK6E,EAAW,UACpCxD,EAAQ,IAAIK,EAAK1B,CAAK,EAExB,OAAOqB,CACT,EAyBMyD,EAAsB,CAC1BnC,EACAoC,IAEF,CACEnC,EACAoC,EACAnC,IACG,CACH,MAAMC,EAAMmB,EAASc,EAAc,QAASnC,EAAMC,GAAS,OAAQA,GAAS,KAAK,EAC3ExB,EAAUqD,EAAaK,EAAc,QAASlC,GAAS,OAAO,EAC9DR,EAAOgC,EAAcxB,GAAS,IAAI,EAEpCyB,EAAqBzB,GAAS,IAAI,GACpCxB,EAAQ,IAAI,eAAgB,kBAAkB,EAGhD,MAAMf,EAAuCD,EAAoB,CAC/D,OAAAsC,EACA,IAAAG,EACA,WAAAkC,EACA,QAAA3D,EACA,KAAAgB,EACA,OAAQQ,GAAS,MAAA,CAClB,EAED,OAAOnC,EAAeJ,CAAM,CAC9B,EAQM2E,EAAqC,CACzCtC,EACAoC,EACAzB,IAEF,CACEV,EACAC,IAEAiC,EAAoBnC,EAAQoC,CAAa,EACvCnC,EACAU,EAAYV,CAAI,EAAED,CAAM,EACxBE,CACF,EAiDWqC,GAAe,CAC1BrC,EACAS,IAC0C,CAC1C,MAAM6B,EAAsB7B,GAAeC,EAAA,EAE3C,OAAOhD,EAAyD,CAC9D,IAAK0E,EAAmC,MAAOpC,EAASsC,CAAmB,EAC3E,KAAMF,EAAmC,OAAQpC,EAASsC,CAAmB,EAC7E,IAAKF,EAAmC,MAAOpC,EAASsC,CAAmB,EAC3E,OAAQF,EAAmC,SAAUpC,EAASsC,CAAmB,EACjF,MAAOF,EAAmC,QAASpC,EAASsC,CAAmB,EAC/E,KAAMF,EAAmC,OAAQpC,EAASsC,CAAmB,EAC7E,QAASF,EAAmC,UAAWpC,EAASsC,CAAmB,CAAA,CACpF,CACH,EAYMC,EAA6C,CACjDzC,EACAoC,IAEF,CACEnC,EACAC,IAEAiC,EAAoBnC,EAAQoC,CAAa,EACvCnC,EACAI,EAAA,EACAH,CACF,EAIIwC,EAAoBlE,GACxB,OAAOA,GAAU,UACdA,IAAU,MACV,SAAUA,GACV,QAAQ,IAAIA,EAAO,MAAM,IAAM,YAE9BmE,EACJzE,GAMAA,EAAQ,KACNF,EAAO,QACJQ,GAA6CkE,EAAiBlE,CAAK,EACnEA,GAAUR,EAAO,QAAQQ,CAAK,CAAA,CAEnC,EAEIoE,EAAkD,CACtD5C,EACAoC,IAEF,CACEnC,EACAC,IAEAyC,EACEF,EAA2CzC,EAAQoC,CAAa,EAAEnC,EAAMC,CAAO,CACjF,EAkDW2C,GACX3C,GAEOtC,EAAuC,CAC5C,IAAKgF,EAAgD,MAAO1C,CAAO,EACnE,KAAM0C,EAAgD,OAAQ1C,CAAO,EACrE,IAAK0C,EAAgD,MAAO1C,CAAO,EACnE,OAAQ0C,EAAgD,SAAU1C,CAAO,EACzE,MAAO0C,EAAgD,QAAS1C,CAAO,EACvE,KAAM0C,EAAgD,OAAQ1C,CAAO,EACrE,QAAS0C,EAAgD,UAAW1C,CAAO,CAAA,CAC5E,ECzeU4C,EAAqB,CAChCC,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjB0C,EAAqB,CAChCH,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjB2C,GAAsB,CACjCJ,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjB4C,GAAsB,CACjCL,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjB6C,GAAsB,CACjCN,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjB8C,GAAmB,CAC9BP,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjB+C,GAAmB,CAC9BR,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjBgD,GAAmB,CAC9BT,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjBiD,GAAsB,CACjCV,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EAsBjBkD,GAAsB,CACjCX,EACAC,EACAC,EACAzC,IAIOxC,EAAO,QAAQwC,CAAM,EC/QxBmD,EAA4B,CAChCnE,EACAF,EACAhB,EACAsF,IAOAtE,GAAa,SAAS,kBAAkB,EACpCtB,EAAO,IAAI,WAAY,CACvB,MAAMwC,EAAS,MAAOjB,EAAUC,EAAQ,mBAAoBlB,CAAI,EAC1DuF,EAAU,MAAOD,EAAQpE,EAAQ,mBAAoBlB,EAAMkC,CAAM,EACvE,OAAOlD,EAAQ,CACb,OAAAkC,EACA,YAAa,mBACb,KAAMqE,CAAA,CACP,CACH,CAAC,EACC7F,EAAO,KAAK2B,EAAsBH,EAAQ,CAAC,kBAAkB,EAAGF,EAAahB,CAAI,CAAC,EAQlFwF,EAA0B,CAC9BtE,EACAF,EACAhB,EACAsF,IAOAtE,GAAa,SAAS,kBAAkB,EACpCtB,EAAO,IAAI,WAAY,CACvB,MAAMwC,EAAS,MAAOjB,EAAUC,EAAQ,mBAAoBlB,CAAI,EAC1DuF,EAAU,MAAOD,EAAQpE,EAAQ,mBAAoBlB,EAAMkC,CAAM,EAEvE,OAAO,MAAOxC,EAAO,KAAKV,EAAQ,CAChC,KAAM,YACN,OAAAkC,EACA,YAAa,mBACb,KAAMqE,CAAA,CACP,CAAC,CACJ,CAAC,EACC7F,EAAO,KAAK2B,EAAsBH,EAAQ,CAAC,kBAAkB,EAAGF,EAAahB,CAAI,CAAC,EAa3EyF,GAAqB3E,EAAoC,CAACI,EAAQF,EAAahB,IAC1F0F,EAAM,MAAMxE,CAAM,EAAE,KAClBwE,EAAM,KAAK,IAAK,IAAML,EAA0B,IAAKrE,EAAahB,EAAM2F,CAA2B,CAAC,EACpGD,EAAM,KAAK,IAAK,IAAMF,EAAwB,IAAKxE,EAAahB,EAAM4F,CAA2B,CAAC,EAClGF,EAAM,OAAO,IAAMhG,EAAO,KAAKyB,EAAiBD,EAAQlB,CAAI,CAAC,CAAC,CAAA,CAElE,EAaa6F,GAAsB/E,EAAqC,CAACI,EAAQF,EAAahB,IAC5F0F,EAAM,MAAMxE,CAAM,EAAE,KAClBwE,EAAM,KAAK,IAAK,IAAML,EAA0B,IAAKrE,EAAahB,EAAM8F,EAA4B,CAAC,EACrGJ,EAAM,KAAK,IAAK,IAAMF,EAAwB,IAAKxE,EAAahB,EAAM+F,EAA4B,CAAC,EACnGL,EAAM,KAAK,IAAK,IAAMF,EAAwB,IAAKxE,EAAahB,EAAMgG,EAA4B,CAAC,EACnGN,EAAM,OAAO,IAAMhG,EAAO,KAAKyB,EAAiBD,EAAQlB,CAAI,CAAC,CAAC,CAAA,CAElE,EAaaiG,GAAmBnF,EAAkC,CAACI,EAAQF,EAAahB,IACtF0F,EAAM,MAAMxE,CAAM,EAAE,KAClBwE,EAAM,KAAK,IAAK,IAAML,EAA0B,IAAKrE,EAAahB,EAAMkG,EAAyB,CAAC,EAClGR,EAAM,KAAK,IAAK,IAAMF,EAAwB,IAAKxE,EAAahB,EAAMmG,EAAyB,CAAC,EAChGT,EAAM,KAAK,IAAK,IAAMF,EAAwB,IAAKxE,EAAahB,EAAMoG,EAAyB,CAAC,EAChGV,EAAM,OAAO,IAAMhG,EAAO,KAAKyB,EAAiBD,EAAQlB,CAAI,CAAC,CAAC,CAAA,CAElE,EAaaqG,GAAsBvF,EAAqC,CAACI,EAAQF,EAAahB,IAC5F0F,EAAM,MAAMxE,CAAM,EAAE,KAClBwE,EAAM,KAAK,IAAK,IACdhG,EAAO,QACLV,EAAQ,CACN,OAAQ,IACR,YAAa,OACb,KAAM,MAAA,CACP,CAAA,CACF,EACH0G,EAAM,KAAK,IAAK,IAAMF,EAAwB,IAAKxE,EAAahB,EAAMsG,EAA4B,CAAC,EACnGZ,EAAM,KAAK,IAAK,IAAMF,EAAwB,IAAKxE,EAAahB,EAAMuG,EAA4B,CAAC,EACnGb,EAAM,OAAO,IAAMhG,EAAO,KAAKyB,EAAiBD,EAAQlB,CAAI,CAAC,CAAC,CAAA,CAElE,ECzJawG,GAA2C,CACtD,QAAS,CACP,IAAKf,GACL,KAAMI,EAAA,EAER,gBAAiB,CACf,IAAKI,GACL,OAAQI,EAAA,CAEZ,EAYAjE,EAA2BoE,EAAiB"} \ No newline at end of file diff --git a/packages/app/dist/main.js b/packages/app/dist/main.js deleted file mode 100644 index e9f2c3d..0000000 --- a/packages/app/dist/main.js +++ /dev/null @@ -1,2 +0,0 @@ -import{NodeContext as m,NodeRuntime as r}from"@effect/platform-node";import{Match as t,pipe as a,Effect as n,Console as c}from"effect";import*as o from"@effect/schema/Schema";const f=e=>t.value(e).pipe(t.when({kind:"effect"},()=>"Hello from Effect!"),t.when({kind:"named"},({name:i})=>`Hello, ${i}!`),t.exhaustive),p=o.Struct({name:o.optionalWith(o.NonEmptyString,{default:()=>"Effect"})}),d=e=>e.name.toLowerCase()==="effect"?{kind:"effect"}:{kind:"named",name:e.name},l=a(n.sync(()=>process.argv.slice(2)),n.map(e=>e.length>0&&e[0]!==void 0?{name:e[0]}:{}),n.flatMap(o.decodeUnknown(p)),n.map(e=>d(e))),s=a(l,n.map(e=>f(e)),n.tap(c.log)),h=a(s,n.provide(m.layer));r.runMain(h); -//# sourceMappingURL=main.js.map diff --git a/packages/app/dist/main.js.map b/packages/app/dist/main.js.map deleted file mode 100644 index 2a954bb..0000000 --- a/packages/app/dist/main.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"main.js","sources":["../src/core/greeting.ts","../src/shell/cli.ts","../src/app/program.ts","../src/app/main.ts"],"sourcesContent":["import { Match } from \"effect\"\n\nexport type GreetingVariant =\n | { readonly kind: \"effect\" }\n | { readonly kind: \"named\"; readonly name: string }\n\n/**\n * Formats a greeting message without side effects.\n *\n * @param variant - Non-empty, classified name information.\n * @returns Greeting text composed deterministically.\n *\n * @pure true\n * @invariant variant.kind === \"named\" ⇒ variant.name.length > 0\n * @complexity O(1) time / O(1) space\n */\nexport const formatGreeting = (variant: GreetingVariant): string =>\n Match.value(variant).pipe(\n Match.when({ kind: \"effect\" }, () => \"Hello from Effect!\"),\n Match.when({ kind: \"named\" }, ({ name }) => `Hello, ${name}!`),\n Match.exhaustive\n )\n","import * as S from \"@effect/schema/Schema\"\nimport { Effect, pipe } from \"effect\"\n\nimport type { GreetingVariant } from \"../core/greeting.js\"\n\nconst cliSchema = S.Struct({\n name: S.optionalWith(S.NonEmptyString, { default: () => \"Effect\" })\n})\n\ntype CliInput = S.Schema.Type\n\nconst toVariant = (input: CliInput): GreetingVariant =>\n input.name.toLowerCase() === \"effect\"\n ? { kind: \"effect\" }\n : { kind: \"named\", name: input.name }\n\nexport const readGreetingVariant = pipe(\n Effect.sync(() => process.argv.slice(2)),\n Effect.map((args) => args.length > 0 && args[0] !== undefined ? { name: args[0] } : {}),\n Effect.flatMap(S.decodeUnknown(cliSchema)),\n Effect.map((input) => toVariant(input))\n)\n","import { Console, Effect, pipe } from \"effect\"\n\nimport { formatGreeting } from \"../core/greeting.js\"\nimport { readGreetingVariant } from \"../shell/cli.js\"\n\n/**\n * Compose the CLI program as a single effect.\n *\n * @returns Effect that returns the greeting string and logs it once on success.\n *\n * @pure false - uses Console output\n * @effect Console\n * @invariant forall args in Argv: decode(args) = v -> logs exactly one greeting\n * @precondition true\n * @postcondition exists greeting: returned(greeting) and logged(greeting)\n * @complexity O(1)\n * @throws Never - all errors are typed in the Effect error channel\n */\n// CHANGE: extract the composed program into a reusable Effect\n// WHY: keep the entrypoint as a thin platform runtime shell and make testing deterministic\n// QUOTE(TZ): \"\\u0414\\u0430 \\u0434\\u0430\\u0432\\u0430\\u0439 \\u0442\\u0430\\u043a \\u044d\\u0442\\u043e \\u0431\\u043e\\u043b\\u0435\\u0435 \\u043f\\u0440\\u0430\\u0432\\u0438\\u043b\\u044c\\u043d\\u0430\\u044f \\u0440\\u0435\\u0430\\u043b\\u0438\\u0437\\u0430\\u0446\\u0438\\u044f\"\n// REF: user-2025-12-19-platform-node\n// SOURCE: https://effect.website/docs/platform/runtime/ \"runMain helps you execute a main effect with built-in error handling, logging, and signal management.\"\n// FORMAT THEOREM: forall args in Argv: decode(args) = v -> log(formatGreeting(v))\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: exactly one log entry per successful parse\n// COMPLEXITY: O(1)/O(1)\nexport const program = pipe(\n readGreetingVariant,\n Effect.map((variant) => formatGreeting(variant)),\n Effect.tap(Console.log)\n)\n","import { NodeContext, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, pipe } from \"effect\"\n\nimport { program } from \"./program.js\"\n\n// CHANGE: run the program through the Node platform runtime with its layer\n// WHY: ensure effects execute under the platform runtime with proper teardown/logging behavior\n// QUOTE(TZ): \"\\u0414\\u0430 \\u0434\\u0430\\u0432\\u0430\\u0439 \\u0442\\u0430\\u043a \\u044d\\u0442\\u043e \\u0431\\u043e\\u043b\\u0435\\u0435 \\u043f\\u0440\\u0430\\u0432\\u0438\\u043b\\u044c\\u043d\\u0430\\u044f \\u0440\\u0435\\u0430\\u043b\\u0438\\u0437\\u0430\\u0446\\u0438\\u044f\"\n// REF: user-2025-12-19-platform-node\n// SOURCE: https://effect.website/docs/platform/runtime/ \"runMain helps you execute a main effect with built-in error handling, logging, and signal management.\"\n// FORMAT THEOREM: forall args in Argv: decode(args) = v -> runMain(program)\n// PURITY: SHELL\n// EFFECT: Effect\n// INVARIANT: program executed with NodeContext.layer\n// COMPLEXITY: O(1)/O(1)\nconst main = pipe(program, Effect.provide(NodeContext.layer))\n\nNodeRuntime.runMain(main)\n"],"names":["formatGreeting","variant","Match","name","cliSchema","S","toVariant","input","readGreetingVariant","pipe","Effect","args","program","Console","main","NodeContext","NodeRuntime"],"mappings":"+KAgBO,MAAMA,EAAkBC,GAC7BC,EAAM,MAAMD,CAAO,EAAE,KACnBC,EAAM,KAAK,CAAE,KAAM,QAAA,EAAY,IAAM,oBAAoB,EACzDA,EAAM,KAAK,CAAE,KAAM,OAAA,EAAW,CAAC,CAAE,KAAAC,CAAA,IAAW,UAAUA,CAAI,GAAG,EAC7DD,EAAM,UACR,EChBIE,EAAYC,EAAE,OAAO,CACzB,KAAMA,EAAE,aAAaA,EAAE,eAAgB,CAAE,QAAS,IAAM,QAAA,CAAU,CACpE,CAAC,EAIKC,EAAaC,GACjBA,EAAM,KAAK,gBAAkB,SACzB,CAAE,KAAM,UACR,CAAE,KAAM,QAAS,KAAMA,EAAM,IAAA,EAEtBC,EAAsBC,EACjCC,EAAO,KAAK,IAAM,QAAQ,KAAK,MAAM,CAAC,CAAC,EACvCA,EAAO,IAAKC,GAASA,EAAK,OAAS,GAAKA,EAAK,CAAC,IAAM,OAAY,CAAE,KAAMA,EAAK,CAAC,CAAA,EAAM,EAAE,EACtFD,EAAO,QAAQL,EAAE,cAAcD,CAAS,CAAC,EACzCM,EAAO,IAAKH,GAAUD,EAAUC,CAAK,CAAC,CACxC,ECOaK,EAAUH,EACrBD,EACAE,EAAO,IAAKT,GAAYD,EAAeC,CAAO,CAAC,EAC/CS,EAAO,IAAIG,EAAQ,GAAG,CACxB,ECjBMC,EAAOL,EAAKG,EAASF,EAAO,QAAQK,EAAY,KAAK,CAAC,EAE5DC,EAAY,QAAQF,CAAI"} \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index 84ed361..392febe 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -11,6 +11,10 @@ "default": "./dist/index.js" } }, + "files": [ + "dist", + "src" + ], "scripts": { "build": "vite build", "dev": "vite build --watch",