corvée(lint) applique des suggestions de lint Oxlint

This commit is contained in:
gcch 2026-04-29 10:45:49 +02:00
commit ab665599c9
6 changed files with 64 additions and 36 deletions

View file

@ -1,14 +1,16 @@
import { Console, Context, Effect, Layer, Match, pipe, Schedule, Schema, SchemaIssue } from "effect";
import { SchemaError } from "effect/Schema";
import type { SchemaError } from "effect/Schema";
import type {
HttpClientError,
} from "effect/unstable/http";
import {
FetchHttpClient,
HttpClient,
HttpClientError,
HttpClientRequest,
HttpClientResponse,
} from "effect/unstable/http";
import { HttpClientErrorSchema } from "effect/unstable/http/HttpClientError";
import type { CartProduct } from "../schemas/api.ts";
import type { CartProduct, GetProducts } from "../schemas/api.ts";
import { WooCommerceCart } from "../schemas/cart.ts";
/** Le nombre maximal d'essais pour une Requête. */
@ -16,20 +18,21 @@ const MAX_RETRIES = 3;
/** Le temps d'attente avant de réessayer une Requête. */
const RETRY_WAIT_TIME = "1 seconds";
type APIError = APIRequestError | APIResponseError;
type APIResponse<T> = T | WooCommerceError;
/** Décrit une Erreur survenue au traitement d'une `Request`. */
class APIRequestError extends Schema.TaggedErrorClass<APIRequestError>()("APIRequestError", {
message: Schema.String,
cause: Schema.Union([Schema.Defect, HttpClientErrorSchema]),
message: Schema.String,
}) {}
/** Décrit une Erreur survenue au traitement d'une `Response`. */
class APIResponseError extends Schema.TaggedErrorClass<APIResponseError>()("APIResponseError", {
message: Schema.String,
cause: Schema.Union([Schema.Defect, HttpClientErrorSchema]),
message: Schema.String,
}) {}
type APIError = APIRequestError | APIResponseError;
class WooCommerceErrorBody extends Schema.Class<WooCommerceErrorBody>("WooCommerceErrorBody")({
code: Schema.String,
data: Schema.Struct({
@ -37,13 +40,12 @@ class WooCommerceErrorBody extends Schema.Class<WooCommerceErrorBody>("WooCommer
}),
message: Schema.String,
}) {}
class WooCommerceError extends Schema.Class<WooCommerceError>("WooCommerceError")({
body: WooCommerceErrorBody,
status: Schema.Number,
}) {}
type APIResponse<T> = T | WooCommerceError;
/** Client `fetch` contenant les options et en-têtes de Requêtes pré-renseignées. */
const APIFetchClient = FetchHttpClient.layer.pipe(
Layer.provide(
@ -81,22 +83,22 @@ class APIClient extends Context.Service<APIClient>()("haikuatelier.fr/APIClient"
const matchAPIError = (error: HttpClientError.HttpClientError | SchemaError): APIError => {
if (error._tag === "SchemaError") {
return new APIRequestError({
message: `Erreur lors du parsage du corps de la Requête :${SchemaIssue.makeFormatterDefault()(error.issue)}`,
cause: error,
message: `Erreur lors du parsage du corps de la Requête :${SchemaIssue.makeFormatterDefault()(error.issue)}`,
});
} else {
return Match.typeTags<HttpClientError.HttpClientErrorReason, APIError>()({
DecodeError: cause => new APIResponseError({ cause, message: "Le corps de la Réponse ne peut être lu" }),
EmptyBodyError: cause => new APIResponseError({ cause, message: "Un corps vide ne peut être lu" }),
EncodeError: cause => new APIRequestError({ cause, message: "Le corps de la Requête ne peut être lu" }),
InvalidUrlError: cause => new APIRequestError({ cause, message: "L'URL de la Requête n'est pas valide" }),
StatusCodeError: cause =>
new APIResponseError({ cause, message: "Le code HTTP de la Réponse correspond à un échec" }),
TransportError: (cause): APIError =>
new APIRequestError({
cause,
message: "Un problème réseau empêche l'exécution de la Requête",
}),
EncodeError: cause => new APIRequestError({ cause, message: "Le corps de la Requête ne peut être lu" }),
InvalidUrlError: cause => new APIRequestError({ cause, message: "L'URL de la Requête n'est pas valide" }),
StatusCodeError: cause =>
new APIResponseError({ cause, message: "Le code HTTP de la Réponse correspond à un échec" }),
DecodeError: cause => new APIResponseError({ cause, message: "Le corps de la Réponse ne peut être lu" }),
EmptyBodyError: cause => new APIResponseError({ cause, message: "Un corps vide ne peut être lu" }),
})(error.reason);
}
};
@ -109,7 +111,7 @@ class APIClient extends Context.Service<APIClient>()("haikuatelier.fr/APIClient"
return yield* Effect.succeed(error);
});
const AddProductToCart = Effect.fn("AppClient.AddProductToCart")(
const AddProductToCart = Effect.fn("APIClient.AddProductToCart")(
function*(nonce: string, productToAdd: CartProduct): Effect.fn.Return<APIResponse<WooCommerceCart>, APIError> {
const request = pipe(
HttpClientRequest.post(`/wp-json/wc/store/cart/add-item`),
@ -132,7 +134,28 @@ class APIClient extends Context.Service<APIClient>()("haikuatelier.fr/APIClient"
},
);
return { AddProductToCart };
const GetProducts = Effect.fn("APIClient.GetProducts")(
function*(nonce: string, authString: string, queryParams: GetProducts) {
const request = pipe(
HttpClientRequest.get(`/wp-json/wc/store/products`),
HttpClientRequest.setHeader("Nonce", nonce),
HttpClientRequest.bearerToken(authString),
// Le corps de la Requête a été validée en amont, on peut utiliser Unsafe.
HttpClientRequest.setUrlParams(queryParams),
);
const response = yield* pipe(
haikuHTTPClient.execute(request),
Effect.flatMap(HttpClientResponse.schemaBodyJson(Schema.Unknown)),
Effect.mapError(error => matchAPIError(error)),
Effect.tapError(error => printErrorAsSuccinctMessage(error)),
);
return response;
},
);
return { AddProductToCart, GetProducts };
}),
}) {
static readonly Live = Layer.effect(this, this.make).pipe(Layer.provide(APIFetchClient));

View file

@ -31,7 +31,7 @@ const getAllSelectorFromParent =
pipe(
parent.querySelectorAll<E>(selector),
// Convertis NodeListOf en Array.
(xs: NodeListOf<E>) => Array.from<E>(xs),
(xs: NodeListOf<E>) => [...xs],
(xs: Array<E>) => Option.liftPredicate(FxArray.isReadonlyArrayNonEmpty)(xs),
);

View file

@ -0,0 +1,9 @@
import { Effect } from "effect";
import { ATTRIBUT_CHARGEMENT, ATTRIBUT_DESACTIVE } from "../../scripts/constantes/dom.ts";
const setLoadingState = Effect.fn("setLoadingState")(function*(element: HTMLElement, isLoading: boolean) {
element.toggleAttribute(ATTRIBUT_DESACTIVE, isLoading);
element.toggleAttribute(ATTRIBUT_CHARGEMENT, isLoading);
});
export { setLoadingState };

View file

@ -1,18 +1,18 @@
import { Context, Effect, flow, Layer, pipe, Schedule, Schema } from "effect";
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
import { HttpClientError } from "effect/unstable/http/HttpClientError";
class Todo extends Schema.Class<Todo>("Todo")({
userId: Schema.Number,
id: Schema.Number,
title: Schema.String,
completed: Schema.Boolean,
}) {}
import type { HttpClientError } from "effect/unstable/http/HttpClientError";
class FetchClientError extends Schema.TaggedErrorClass<FetchClientError>()("FetchClientError", {
cause: Schema.Defect,
}) {}
class Todo extends Schema.Class<Todo>("Todo")({
completed: Schema.Boolean,
id: Schema.Number,
title: Schema.String,
userId: Schema.Number,
}) {}
class FetchClientExample extends Context.Service<FetchClientExample, {
readonly allTodos: Effect.Effect<ReadonlyArray<Todo>, FetchClientError>;
createTodo(todo: Omit<Todo, "id">): Effect.Effect<Todo, FetchClientError>;
@ -75,8 +75,8 @@ class FetchClientExample extends Context.Service<FetchClientExample, {
return FetchClientExample.of({
allTodos,
getTodo,
createTodo,
getTodo,
});
}),
).pipe(

View file

@ -1,6 +1,6 @@
// oxlint-disable typescript/dot-notation
import type { SchemaError } from "effect/Schema";
// oxlint-disable typescript/dot-notation
import {
Array as FxArray,
Console,
@ -21,6 +21,7 @@ import type { APIError } from "../../scripts-effect/lib/api.ts";
import type { DetailEnsemble } from "./types.d.ts";
import { APIClient } from "../../scripts-effect/lib/api.ts";
import { setLoadingState } from "../../scripts-effect/lib/elements.ts";
import { CartProduct } from "../../scripts-effect/schemas/api.ts";
import { WooCommerceCart } from "../../scripts-effect/schemas/cart.ts";
import { Product, ProductVariation, ProductVariationAttribute } from "../../scripts-effect/schemas/product.ts";
@ -98,11 +99,6 @@ class ProductPageDOM extends Context.Service<ProductPageDOM>()(
);
});
const setLoadingState = Effect.fn("setLoadingState")(function*(element: HTMLElement, isLoading: boolean) {
element.toggleAttribute(ATTRIBUT_DESACTIVE, isLoading);
element.toggleAttribute(ATTRIBUT_CHARGEMENT, isLoading);
});
const detailButtonClickHandler = Effect.fn("detailButtonClickHandler")(
function*(evt: Event) {
// Empêche la pollution de l'historique de navigation
@ -232,7 +228,7 @@ class ProductPageDOM extends Context.Service<ProductPageDOM>()(
// Désactive les interactions le temps de la requête.
yield* setLoadingState(AddToCartButton, true);
yield* SubscriptionRef.set(AddToCartButtonText, "Adding Product...");
// lanceAnimationCycleLoading(AddToCartButton, 500);
// LanceAnimationCycleLoading(AddToCartButton, 500);
// Exécute la Requête auprès du backend.
const newCart = yield* API.AddProductToCart(PageStates.nonce, requestBody);

View file

@ -7,7 +7,7 @@ class ProductPageMessages extends Context.Service<ProductPageMessages>()("haikua
const { AddToCartButton } = yield* ProductPageElements;
const AddToCartButtonText = yield* SubscriptionRef.make("Add to cart");
// const AddToCartErrorText = yield* SubscriptionRef.make<Option.Option<string>>(Option.none());
// Const AddToCartErrorText = yield* SubscriptionRef.make<Option.Option<string>>(Option.none());
const initAddToCartButtonUpdates = Effect.fn("initAddToCartButtonUpdates")(function*() {
return yield* pipe(