UNPKG

@jay-js/system

Version:

A powerful and flexible TypeScript library for UI, state management, lazy loading, routing and managing draggable elements in modern web applications.

205 lines 7.18 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { state } from "../state/core/state.js"; import { queryCache } from "./cache.js"; import { defaultRetryDelay, executeWithRetry } from "./utils.js"; /** * Default mutation options */ const DEFAULT_OPTIONS = { retry: false, retryDelay: defaultRetryDelay, }; /** * Creates a reactive mutation for write operations (POST/PUT/DELETE) * * Unlike queries, mutations don't cache results and are designed for write operations. * They support optimistic updates with automatic rollback on error. * * @param fetcher Async function to perform mutation * @param options Mutation configuration options * @returns Mutation store with reactive states and control methods * * @example * Basic usage: * ```typescript * const createUser = mutation(async (user: User, signal) => { * const res = await fetch('/api/users', { * method: 'POST', * body: JSON.stringify(user), * signal, * }); * return res.json(); * }); * * // Execute mutation * await createUser.mutate({ name: 'John', email: 'john@example.com' }); * ``` * * @example * With optimistic updates: * ```typescript * const updateUser = mutation( * async (user: User, signal) => { * const res = await fetch(`/api/users/${user.id}`, { * method: 'PUT', * body: JSON.stringify(user), * signal, * }); * return res.json(); * }, * { * onMutate: async (newUser) => { * // Snapshot current state for rollback * const previousUser = queryCache.get<User>(`user-${newUser.id}`); * * // Optimistically update cache * if (previousUser) { * queryCache.set(`user-${newUser.id}`, newUser, 300000); * } * * return { previousUser }; * }, * onError: (err, variables, context) => { * // Rollback on error * if (context?.previousUser) { * queryCache.set(`user-${variables.id}`, context.previousUser.data, 300000); * } * }, * onSuccess: () => { * // Invalidate related queries * queryCache.delete('users'); * }, * invalidateQueries: ['users', 'user-list'], * } * ); * ``` */ export function mutation(fetcher, options = {}) { const opts = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options); const internalState = state({ data: null, error: null, isLoading: false, isError: false, isSuccess: false, isIdle: true, status: "idle", }); let currentController = null; const mutate = (variables) => __awaiter(this, void 0, void 0, function* () { if (currentController) { currentController.abort(); } currentController = new AbortController(); internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isLoading: true, isIdle: false, isError: false, status: "loading" }); }); let context; try { if (options.onMutate) { context = yield options.onMutate(variables); } const data = yield executeWithRetry((signal) => fetcher(variables, signal), opts.retry, opts.retryDelay, currentController.signal); internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { data: data, error: null, isError: false, isSuccess: true, status: "success" }); }); if (options.onSuccess) { yield options.onSuccess(data, variables, context); } if (options.invalidateQueries) { for (const key of options.invalidateQueries) { queryCache.delete(key); } } if (options.invalidatePattern) { queryCache.invalidatePattern(options.invalidatePattern); } if (options.invalidateIf) { queryCache.invalidateQueries(options.invalidateIf); } if (options.onSettled) { yield options.onSettled(data, null, variables, context); } return data; } catch (error) { const err = error; internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { error: err, isError: true, isSuccess: false, status: "error" }); }); if (options.onError) { yield options.onError(err, variables, context); } if (options.onSettled) { yield options.onSettled(undefined, err, variables, context); } throw err; } finally { //internalState.value.isLoading = false; internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isLoading: false }); }); currentController = null; } }); const mutateAsync = (variables) => __awaiter(this, void 0, void 0, function* () { try { return yield mutate(variables); } catch (_a) { return undefined; } }); const reset = () => { internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { data: null, error: null, isLoading: false, isError: false, isSuccess: false, isIdle: true, status: "idle" }); }); }; const cancel = () => { if (currentController) { currentController.abort(); currentController = null; } //internalState.value.isLoading = false; internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isLoading: false }); }); }; return { get data() { return internalState.value.data; }, get error() { return internalState.value.error; }, get isLoading() { return internalState.value.isLoading; }, get isError() { return internalState.value.isError; }, get isSuccess() { return internalState.value.isSuccess; }, get isIdle() { return internalState.value.isIdle; }, get status() { return internalState.value.status; }, mutate, mutateAsync, reset, cancel, }; } //# sourceMappingURL=mutation.js.map