Releases: wellcrafted-dev/wellcrafted
v0.34.1
Patch Changes
-
41b025a: Fix
trySyncandtryAsynctype inference for catch handlers returning union Err typesWhen a catch handler returned multiple Err variants (e.g.,
Err<A> | Err<B>), TypeScript could not infer the union, requiring explicit return type annotations. Replaced three overloads per function with a single generic signature that infers the full catch return type.
v0.34.0
v0.33.0
Minor Changes
-
1c6e597: BREAKING:
defineErrorsv2 — Rust-style namespaced errors with Err-by-default- Factories now return
Err<...>directly (no dualFooError/FooErrfactories) - Keys are short variant names (
Connection,Parse) instead ofConnectionError InferError<T>takes a single factory (typeof HttpError.Connection)InferErrors<T>replacesInferErrorUnion<T>for union extractionValidatedConfigprovides descriptive error when reservednamekey is used
- Factories now return
v0.32.0
Minor Changes
- 4539eb2: Redesign
createTaggedErrorbuilder: flat.withFields()API replaces nested.withContext()/.withCause(),.withMessage()is optional and seals the message (not in factory input type),messagerequired at call site when.withMessage()is absent. Removescontextnesting,causeas first-class field, andreasonconvention.
v0.31.0
Minor Changes
-
69bf59e: Breaking:
createTaggedErrornow requires.withMessage(fn)as a mandatory terminal builder step before factories are available.What Changed
.withMessage(fn)is now required: Factories (XxxError,XxxErr) are only returned after calling.withMessage(). Previously you could destructure factories after just.withContext()or.withCause().- Message is auto-computed: The
messagefield is no longer a required input at the factory call site — it is derived from the template function passed to.withMessage(). You can still passmessage:as an optional override. - Context type tightened:
contextconstraint changed fromRecord<string, unknown>toJsonObject(values must be JSON-serializable). - Strict builder/factory separation:
ErrorBuilderonly has chain methods (.withContext(),.withCause(),.withMessage()).FinalFactoriesonly has factory functions — no more mixing.
Migration
Before:
const { UserError, UserErr } = createTaggedError("UserError").withContext<{ userId: string; }>(); // ❌ Error: factories not available without .withMessage()
After:
const { UserError, UserErr } = createTaggedError("UserError") .withContext<{ userId: string }>() .withMessage(({ context }) => `User ${context.userId} failed`); // ✅ Factories now available
v0.29.1
Patch Changes
-
7df7aee: Fix Brand type to support hierarchical/stacked brands
Previously, stacking brands resulted in
neverbecause intersecting{ [brand]: 'A' }with{ [brand]: 'B' }collapsed the property tonever.Now brands use a nested object structure
{ [brand]: { [K in T]: true } }(following Effect-TS pattern), allowing brand stacking to work correctly:type AbsolutePath = string & Brand<"AbsolutePath">; type ProjectDir = AbsolutePath & Brand<"ProjectDir">; const projectDir = "/home/project" as ProjectDir; const abs: AbsolutePath = projectDir; // Works - child assignable to parent
v0.29.0
Minor Changes
-
1237ce4: BREAKING: Rename
resultQueryFntoqueryFnandresultMutationFntomutationFnThe
resultprefix was redundant since the TypeScript signature already encodes that these functions return Result types. This removes unnecessary Hungarian notation from the API.Migration:
// Before defineQuery({ queryKey: ["users"], resultQueryFn: () => getUsers(), }); defineMutation({ mutationKey: ["users", "create"], resultMutationFn: (input) => createUser(input), }); // After defineQuery({ queryKey: ["users"], queryFn: () => getUsers(), }); defineMutation({ mutationKey: ["users", "create"], mutationFn: (input) => createUser(input), });
v0.28.0
Minor Changes
-
7c41baf: feat(error): explicit opt-in for context and cause properties
Following Rust's thiserror pattern,
createTaggedErrornow uses explicit opt-in forcontextandcauseproperties. By default, errors only have{ name, message }.Breaking Change: Previously, all errors had optional
contextandcauseproperties by default. Now you must explicitly chain.withContext<T>()and/or.withCause<T>()to add these properties.Before (old behavior):
const { ApiError } = createTaggedError("ApiError"); // ApiError had: { name, message, context?: Record<string, unknown>, cause?: AnyTaggedError }
After (new behavior):
// Minimal error - only name and message const { ApiError } = createTaggedError("ApiError"); // ApiError has: { name, message } // With required context const { ApiError } = createTaggedError("ApiError").withContext<{ endpoint: string; }>(); // ApiError has: { name, message, context: { endpoint: string } } // With optional typed cause const { ApiError } = createTaggedError("ApiError").withCause< NetworkError | undefined >(); // ApiError has: { name, message, cause?: NetworkError }
Migration: To replicate the old permissive behavior, either specify the types explicitly:
const { FlexibleError } = createTaggedError("FlexibleError") .withContext<Record<string, unknown> | undefined>() .withCause<AnyTaggedError | undefined>();
Or use the new defaults by calling without generics:
const { FlexibleError } = createTaggedError("FlexibleError") .withContext() // Defaults to Record<string, unknown> | undefined .withCause(); // Defaults to AnyTaggedError | undefined
v0.27.0
Minor Changes
-
b917060: Replace
defineErrorwith fluentcreateTaggedErrorAPIThe
createTaggedErrorfunction now uses a fluent builder pattern for type constraints:// Simple usage (flexible mode) const { NetworkError, NetworkErr } = createTaggedError("NetworkError"); // Required context const { ApiError } = createTaggedError("ApiError").withContext<{ endpoint: string; status: number; }>(); // Optional typed context const { LogError } = createTaggedError("LogError").withContext< { file: string; line: number } | undefined >(); // Chaining both context and cause const { RepoError } = createTaggedError("RepoError") .withContext<{ entity: string }>() .withCause<DbError | undefined>();
Breaking changes:
defineErrorhas been removed (usecreateTaggedErrorinstead)- The old
createTaggedErrorgeneric overloads are removed in favor of the fluent API
v0.26.0
Minor Changes
-
5f0e7af: Make query and mutation definitions directly callable
Query and mutation definitions from
defineQueryanddefineMutationare now directly callable functions:// Queries - callable defaults to ensure() behavior const { data, error } = await userQuery(); // same as userQuery.ensure() // Mutations - callable defaults to execute() behavior const { data, error } = await createUser({ name: "John" }); // same as createUser.execute()
The explicit methods (
.ensure(),.fetch(),.execute()) remain available for when you need different behavior or prefer explicit code.Breaking change:
.optionsis now a property instead of a function. UpdatecreateQuery(query.options())tocreateQuery(query.options).