Skip to content

Commit 17328ff

Browse files
committed
refactor(app): enforce effect-only analyzer and remove promise client
1 parent 6528297 commit 17328ff

17 files changed

Lines changed: 128 additions & 1875 deletions

packages/app/eslint.config.mts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,26 @@ export default defineConfig(
209209
message:
210210
"Запрещены Promise.* — используй комбинаторы Effect (all, forEach, etc.).",
211211
},
212+
{
213+
selector: "CallExpression[callee.object.object.name='globalThis'][callee.object.property.name='Promise']",
214+
message:
215+
"Запрещены globalThis.Promise.* — используй комбинаторы Effect.",
216+
},
217+
{
218+
selector: "NewExpression[callee.object.name='globalThis'][callee.property.name='Promise']",
219+
message:
220+
"Запрещён globalThis.Promise — используй Effect.async / Effect.tryPromise.",
221+
},
222+
{
223+
selector: "TSTypeReference[typeName.name='Promise'], TSTypeReference[typeName.name='PromiseLike']",
224+
message:
225+
"Запрещены Promise/PromiseLike в типах — используй Effect.Effect<A, E, R>.",
226+
},
227+
{
228+
selector: "TSTypeReference[typeName.type='TSQualifiedName'][typeName.left.name='globalThis'][typeName.right.name='Promise']",
229+
message:
230+
"Запрещён globalThis.Promise в типах — используй Effect.Effect<A, E, R>.",
231+
},
212232
{
213233
selector: "CallExpression[callee.property.name='push'] > SpreadElement.arguments",
214234
message: "Do not use spread arguments in Array.push",
@@ -232,6 +252,18 @@ export default defineConfig(
232252
"Запрещён Promise<T> — используй Effect.Effect<T, E, R>.",
233253
suggest: ["Effect.Effect<T, E, R>"],
234254
},
255+
PromiseLike: {
256+
message:
257+
"Запрещён PromiseLike<T> — используй Effect.Effect<T, E, R>.",
258+
},
259+
"PromiseLike<*>": {
260+
message:
261+
"Запрещён PromiseLike<T> — используй Effect.Effect<T, E, R>.",
262+
},
263+
"globalThis.Promise": {
264+
message:
265+
"Запрещён globalThis.Promise<T> — используй Effect.Effect<T, E, R>.",
266+
},
235267
},
236268
},
237269
],
@@ -312,6 +344,30 @@ export default defineConfig(
312344
selector: 'CallExpression[callee.name="require"]',
313345
message: "Avoid using require(). Use ES6 imports instead.",
314346
},
347+
{
348+
selector: "NewExpression[callee.name='Promise']",
349+
message: "Запрещён new Promise — используй Effect.async / Effect.tryPromise.",
350+
},
351+
{
352+
selector: "CallExpression[callee.object.name='Promise']",
353+
message: "Запрещены Promise.* — используй комбинаторы Effect.",
354+
},
355+
{
356+
selector: "CallExpression[callee.object.object.name='globalThis'][callee.object.property.name='Promise']",
357+
message: "Запрещены globalThis.Promise.* — используй комбинаторы Effect.",
358+
},
359+
{
360+
selector: "NewExpression[callee.object.name='globalThis'][callee.property.name='Promise']",
361+
message: "Запрещён globalThis.Promise — используй Effect.async / Effect.tryPromise.",
362+
},
363+
{
364+
selector: "TSTypeReference[typeName.name='Promise'], TSTypeReference[typeName.name='PromiseLike']",
365+
message: "Запрещены Promise/PromiseLike в типах — используй Effect.Effect<A, E, R>.",
366+
},
367+
{
368+
selector: "TSTypeReference[typeName.type='TSQualifiedName'][typeName.left.name='globalThis'][typeName.right.name='Promise']",
369+
message: "Запрещён globalThis.Promise в типах — используй Effect.Effect<A, E, R>.",
370+
},
315371
],
316372
'@typescript-eslint/no-restricted-types': 'off',
317373
// Axiom type casting functions intentionally use single-use type parameters
@@ -336,6 +392,30 @@ export default defineConfig(
336392
selector: 'CallExpression[callee.name="require"]',
337393
message: "Avoid using require(). Use ES6 imports instead.",
338394
},
395+
{
396+
selector: "NewExpression[callee.name='Promise']",
397+
message: "Запрещён new Promise — используй Effect.async / Effect.tryPromise.",
398+
},
399+
{
400+
selector: "CallExpression[callee.object.name='Promise']",
401+
message: "Запрещены Promise.* — используй комбинаторы Effect.",
402+
},
403+
{
404+
selector: "CallExpression[callee.object.object.name='globalThis'][callee.object.property.name='Promise']",
405+
message: "Запрещены globalThis.Promise.* — используй комбинаторы Effect.",
406+
},
407+
{
408+
selector: "NewExpression[callee.object.name='globalThis'][callee.property.name='Promise']",
409+
message: "Запрещён globalThis.Promise — используй Effect.async / Effect.tryPromise.",
410+
},
411+
{
412+
selector: "TSTypeReference[typeName.name='Promise'], TSTypeReference[typeName.name='PromiseLike']",
413+
message: "Запрещены Promise/PromiseLike в типах — используй Effect.Effect<A, E, R>.",
414+
},
415+
{
416+
selector: "TSTypeReference[typeName.type='TSQualifiedName'][typeName.left.name='globalThis'][typeName.right.name='Promise']",
417+
message: "Запрещён globalThis.Promise в типах — используй Effect.Effect<A, E, R>.",
418+
},
339419
],
340420
'@typescript-eslint/no-restricted-types': 'off',
341421
},

packages/app/eslint.effect-ts-check.config.mjs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@ const restrictedSyntaxBase = [
7979
selector: "CallExpression[callee.object.name='Promise']",
8080
message: "Avoid Promise.*. Use Effect combinators."
8181
},
82+
{
83+
selector: "CallExpression[callee.object.object.name='globalThis'][callee.object.property.name='Promise']",
84+
message: "Avoid globalThis.Promise.*. Use Effect combinators."
85+
},
86+
{
87+
selector: "NewExpression[callee.object.name='globalThis'][callee.property.name='Promise']",
88+
message: "Avoid globalThis.Promise. Use Effect.async / Effect.tryPromise."
89+
},
90+
{
91+
selector: "TSTypeReference[typeName.name='Promise'], TSTypeReference[typeName.name='PromiseLike']",
92+
message: "Avoid Promise/PromiseLike types. Use Effect.Effect<A, E, R>."
93+
},
94+
{
95+
selector: "TSTypeReference[typeName.type='TSQualifiedName'][typeName.left.name='globalThis'][typeName.right.name='Promise']",
96+
message: "Avoid globalThis.Promise type. Use Effect.Effect<A, E, R>."
97+
},
8298
{
8399
selector: "CallExpression[callee.name='require']",
84100
message: "Avoid require(). Use ES module imports."
@@ -178,6 +194,15 @@ export default tseslint.config(
178194
},
179195
"Promise<*>": {
180196
message: "Avoid Promise<T>. Use Effect.Effect<T, E, R>."
197+
},
198+
PromiseLike: {
199+
message: "Avoid PromiseLike<T>. Use Effect.Effect<T, E, R>."
200+
},
201+
"PromiseLike<*>": {
202+
message: "Avoid PromiseLike<T>. Use Effect.Effect<T, E, R>."
203+
},
204+
"globalThis.Promise": {
205+
message: "Avoid globalThis.Promise<T>. Use Effect.Effect<T, E, R>."
181206
}
182207
}
183208
}],

packages/app/src/index.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
1-
// CHANGE: Make openapi-effect a drop-in replacement for openapi-fetch (Promise API), with an opt-in Effect API.
2-
// WHY: Consumer projects must be able to swap openapi-fetch -> openapi-effect with near-zero code changes.
3-
// QUOTE(ТЗ): "openapi-effect должен почти 1 в 1 заменяться с openapi-fetch" / "Просто добавлять effect поведение"
1+
// CHANGE: Expose Effect-only public API
2+
// WHY: Enforce Effect-first paradigm and remove Promise-based client surface
43
// SOURCE: n/a
54
// PURITY: SHELL (re-exports)
65
// COMPLEXITY: O(1)
76

8-
// Promise-based client (openapi-fetch compatible)
9-
export { default } from "./shell/api-client/promise-client/index.js"
10-
export { createClient } from "./shell/api-client/promise-client/index.js"
11-
export * from "./shell/api-client/promise-client/index.js"
12-
13-
// Effect-based client (opt-in)
147
export * as FetchHttpClient from "@effect/platform/FetchHttpClient"
158

16-
// Strict Effect client (advanced)
179
export type * from "./core/api-client/index.js"
1810
export { assertNever } from "./core/api-client/index.js"
1911

2012
export type {
13+
ClientOptions,
2114
DispatchersFor,
2215
StrictApiClient,
2316
StrictApiClientWithDispatchers
@@ -26,7 +19,7 @@ export type {
2619
export type { Decoder, Dispatcher, RawResponse, StrictClient, StrictRequestInit } from "./shell/api-client/index.js"
2720

2821
export {
29-
createClient as createClientStrict,
22+
createClient,
3023
createClientEffect,
3124
createDispatcher,
3225
createStrictClient,
@@ -38,5 +31,7 @@ export {
3831
unexpectedStatus
3932
} from "./shell/api-client/index.js"
4033

34+
export { createClientEffect as default } from "./shell/api-client/index.js"
35+
4136
// Generated dispatchers (auto-generated from OpenAPI schema)
4237
export * from "./generated/index.js"

packages/app/src/shell/api-client/create-client-types.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,26 @@ import type {
2222
ResponsesFor
2323
} from "../../core/api-client/strict-types.js"
2424
import type { Dispatcher } from "../../core/axioms.js"
25-
import type { ClientOptions as OpenapiFetchClientOptions } from "./promise-client/types.js"
25+
26+
export type HeadersOptions =
27+
| Required<RequestInit>["headers"]
28+
| Record<
29+
string,
30+
| string
31+
| number
32+
| boolean
33+
| ReadonlyArray<string | number | boolean>
34+
| null
35+
| undefined
36+
>
2637

2738
/**
28-
* Client configuration options
29-
*
30-
* @pure - immutable configuration
39+
* Effect client configuration options
3140
*/
32-
export type ClientOptions = OpenapiFetchClientOptions
41+
export interface ClientOptions extends Omit<RequestInit, "body" | "headers" | "method"> {
42+
readonly baseUrl?: string
43+
readonly headers?: HeadersOptions
44+
}
3345

3446
// CHANGE: Add dispatcher map type for auto-dispatching clients
3547
// WHY: Enable creating clients that infer dispatcher from path+method without per-call parameter

0 commit comments

Comments
 (0)