Skip to content

Releases: Julien-R44/tuyau

@tuyau/core@1.2.2

17 Mar 14:00
8a547b1

Choose a tag to compare

Patch Changes

  • 8ce6877: Fix TuyauPromise not being assignable to Promise<T>, which broke TanStack Query type inference.

    TuyauPromise implemented PromiseLike<T> but was missing [Symbol.toStringTag], so TypeScript did not consider it structurally compatible with Promise<T>. When users passed a tuyau call directly to TanStack Query's queryFn, the data type was inferred as TuyauPromise<Response> instead of Response:

    // Before: data was typed as TuyauPromise<User[]> instead of User[]
    const { data } = useQuery({
      queryKey: ["users"],
      queryFn: () => tuyau.request("users.index", {}),
    });

    TuyauPromise now implements the full Promise<T> interface, so it works seamlessly with TanStack Query and any other library expecting Promise<T>.

  • 8a29325: Fix body type not being recognized when using vine.group with .merge() in validators.

    Previously, validators like this would cause tuyau to lose the body type, resulting in 'body' does not exist in type 'BaseRequestOptions':

    const createSessionValidator = vine.create(
      vine
        .object({})
        .merge(
          vine.group([
            vine.group.if((data) => data.password, { password: vine.string() }),
            vine.group.if((data) => data.assertion, {
              assertion: webauthnAssertion,
            }),
          ]),
        ),
    );

    The tuyau client now correctly accepts both variants of the union:

    // Both calls are now properly typed
    await client.session.store({ body: { password: "secret" } });
    await client.session.store({
      body: { assertion: { id: "...", rawId: "..." } },
    });

@tuyau/core@1.2.0

07 Mar 00:07
49d84bb

Choose a tag to compare

Minor Changes

  • d6fcd0f: Support wildcard * route params in generated types

    Wildcard route parameters (e.g. /downloads/*) are now included in the generated params type as '*': ParamValue[], matching AdonisJS's runtime behavior where the wildcard captures all remaining URL segments as an array.

    Previously, the codegen only extracted named :param tokens and ignored wildcard tokens, which meant urlFor() and request() calls with * params would fail type checking despite working at runtime.

    // AdonisJS route
    router.get("/docs/:category/*", [DocsController, "show"]).as("docs.show");
    
    // Generated params type now includes '*'
    // params: { category: ParamValue; '*': ParamValue[] }
    
    // Client usage
    const tuyau = createTuyau({ baseUrl: "http://localhost:3333", registry });
    
    tuyau.request("docs.show", {
      params: { category: "guides", "*": ["sql", "orm", "query-builder"] },
    });
    
    tuyau.urlFor.get("docs.show", {
      category: "guides",
      "*": ["sql", "orm", "query-builder"],
    });

@tuyau/core@1.1.0

05 Mar 21:17
8560f35

Choose a tag to compare

Minor Changes

  • 077085b: Add responseType option to override content-type based response parsing

  • a2e531f: Add typed non-throwing error handling with .safe() and TuyauError

    • Add TuyauPromise, a thenable wrapper returned by client requests that preserves await, .catch(), and .finally() while adding .safe()
    • Add .safe() to return [data, null] on success or [null, error] on failure without throwing
    • Add TuyauError as the shared base class for client errors, with:
      • kind to distinguish 'http' and 'network' failures
      • status, response, rawResponse, and rawRequest
      • isStatus() to narrow typed HTTP error payloads without instanceof checks
    • Make TuyauHTTPError and TuyauNetworkError extend TuyauError
    • Extract non-2xx response payloads into generated route metadata so client errors can be typed from controller return unions
    • Add ExtractErrorResponse and ErrorResponseOf type utilities
    • Update Route.Error, Path.Error, RouteWithRegistry.Error, and PathWithRegistry.Error to expose the unified TuyauError contract
    • Keep thrown errors specific in try/catch: HTTP failures still throw TuyauHTTPError, and transport failures still throw TuyauNetworkError

    Basic usage

    // .safe() returns [data, null] on success, [null, error] on failure
    const [data, error] = await tuyau.api.auth
      .login({ body: { email: "foo@bar.com", password: "secret" } })
      .safe();
    
    if (error) {
      console.log(error.message); // "Request failed with status code 400: POST /auth/login"
      console.log(error.status); // 400
      return;
    }
    
    // data is fully typed here
    console.log(data.token);

    Narrowing errors with isStatus()

    const [data, error] = await tuyau.api.users..safe({ body: { name: 'John' } })
    
    if (error.isStatus(422)) {
      // error.response is narrowed to the 422 payload type
      console.log(error.response.errors)
    }
    
    if (error.isStatus(403)) {
      // error.response is narrowed to the 403 payload type
      console.log(error.response.message)
    }

    Network vs HTTP errors

    const [data, error] = await tuyau.api.users.index.safe();
    
    if (error.kind === "network") {
      // Server unreachable or client offline
      // error.status and error.response are undefined
      console.log("Network error:", error.message);
    }
    
    if (error.kind === "http") {
      // Server responded with a non-2xx status
      console.log(error.status, error.response);
    }
  • 3fcacf0: Add has() and current() methods for route introspection

    • tuyau.has('users.show') — checks if a route name exists in the registry
    • tuyau.current() — returns the current route name based on window.location
    • tuyau.current('users.show') — returns true if the current URL matches that route
    • tuyau.current('users.*') — supports * wildcards to match multiple routes
    • tuyau.current('users.show', { params: { id: 42 }, query: { foo: 'bar' } }) — additionally checks params and query
  • 6bbd80f: Auto-add typed 422 validation error response for routes using a validator. The generated error type uses SimpleError from @vinejs/vine/types. Can be customized via validationErrorType option or disabled with validationErrorType: false.

    Also adds isValidationError() as an alias for isStatus(422).

    const { data, error } = await tuyau.api.users.store.$post.safe({
      body: { email: "foo@bar.com", password: "secret" },
    });
    
    if (error.isValidationError()) {
      // error.response is typed as { errors: SimpleError[] }
      for (const err of error.response.errors) {
        console.log(err.field, err.message);
      }
    }

Patch Changes

  • 077085b: Handle objects in arrays and Date values in buildSearchParams