yatta/frameworks
Bindings for Vue, Solid, Svelte, Angular, Qwik, and plain pages.
30 exported symbols and 39 members, read from src/frameworks/index.ts.
Construct
bindSubscribable
functionBinds a call and hands back a start/stop pair.
The store's subscription is established here so no adapter has to remember to.
bindSubscribable<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, args: TArgs | undefined, onChange: () => void, options?: { enabled?: boolean; key?: string; signal?: AbortSignal; }): Subscribable<T>callKey
functionThe key a call is stored under.
Both the route and the arguments, so two calls cannot share a value by accident and the same call always does.
callKey<TArgs>(method: unknown, args: TArgs | undefined): stringcreateAngularClient
functionBuilds an Angular client whose calls drive Angular signals.
createAngularClient(api: DirectMethods<never>, store: CallStore): YattaAngularClientcreateDomCall
functionNo framework at all — a plain page, a script tag, a worker, a Web Component.
The same contract the other adapters implement, so a page with no framework behaves identically to one inside a component tree: same caching, same de-duplication, same rollback. ```ts const call = createDomCall(store, api.getUser, { params: { id } }); const off = call.subscribe((state) => render(state.data)); ```
createDomCall<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, args?: TArgs, options?: { enabled?: boolean; }): DomCall<T>const call = createDomCall(store, api.getUser, { params: { id } });const off = call.subscribe((state) => render(state.data));readSvelte
functionReads a Svelte call from plain TypeScript, with no template.
Provided because the store contract needs a subscription to be read from outside a component, and without this there is no non-template way to do it. Inside a component, use <code>$user</code> instead.
readSvelte<T>(call: SvelteReadable<T>): TroutePrefix
functionThe prefix a target's cache keys start with.
`getUser` and `getUser:` both become `getUser:`. A plain string with no colon is treated as a route name rather than as a literal key, because "invalidate this exact key" is almost never what a caller means when they are naming a route.
routePrefix(target: InvalidationTarget): stringrunMutation
async functionThe optimistic-write-then-request sequence, shared so rollback cannot differ.
runMutation<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, args: TArgs | undefined, options: { optimistic?: { key: string; value: unknown | ((args: TArgs) => unknown); }; invalidates?: InvalidationTarget[]; }, hooks: { onPending: (pending: boolean) => void; onSuccess: (result: T) => void; onError: (error: Error) => void; }): Promise<T | undefined>useCall
functionVue 3.
`shallowRef` rather than `ref`. A response is a plain object, and a deep reactive proxy around it means every read goes through a proxy — and an optimistic rollback would then mutate the proxy instead of restoring what was there. ```vue <script setup lang="ts"> const { state, reload } = useCall(api.getUser, { params: { id: props.id } }); const user = computed(() => state.value.data); </script> ```
useCall<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, args?: TArgs, options?: { enabled?: boolean; }): VueCall<T><script setup lang="ts">const { state, reload } = useCall(api.getUser, { params: { id: props.id } });const user = computed(() => state.value.data);</script>useMutation
functionuseMutation<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, options?: { optimistic?: { key: string; value: unknown | ((args: TArgs) => unknown); }; invalidates?: string[]; onSuccess?: (result: T, args: TArgs) => void; onError?: (error: Error, args: TArgs) => void; }): VueMutation<T>useQwikCall
functionQwik.
A signal and a resource, rather than a subscription. Qwik containers are rendered on the server, serialized, and *resumed* in the browser without re-running the component — so anything held in a closure is gone, and the only thing that survives is serialized state. A resource is exactly that. Must be called during a component's render. That is a Qwik requirement rather than one this binding adds.
useQwikCall<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, args: TArgs | undefined, options?: { enabled?: boolean; }): QwikCall<T>useSolidCall
functionSolid.
The closest fit of the group: Solid's reactivity is push-based, which is what the store's subscription already is, so a signal write is the whole adapter. ```tsx const user = useCall(api.getUser, { params: { id } }); return <Show when={user().data}>{u => <span>{u().name}</span>}</Show>; ```
useSolidCall<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, args?: TArgs, options?: { enabled?: boolean; }): SolidCall<T>const user = useCall(api.getUser, { params: { id } });return <Show when={user().data}>{u => <span>{u().name}</span>}</Show>;useSvelteCall
functionuseSvelteCall<TArgs, T>(store: CallStore, method: (args?: TArgs) => Promise<T>, args?: TArgs, options?: { enabled?: boolean; key?: string; }): SvelteCall<T>YattaAngularClient
classAngular.
A plain class rather than a service decorator, so it can be constructed in a factory provider or built by hand in a test. Angular's own `signal` is used, not an RxJS subject: a signal is readable straight from a template with no async pipe, and this keeps RxJS out of the dependency list. ```ts
class YattaAngularClient2 members
callcall<TArgs, T>(method: (args?: TArgs) => Promise<T>, args?: TArgs, options?: { enabled?: boolean; }): AngularCall<T>useRoutesuseRoutes(): DirectMethods<never>
Types
AngularCall
interfaceinterface AngularCall<T>4 members
statestate(): CallState<T>An Angular signal of the call's state.
reloadreload(): voidsetset(updater: T | ((previous: T | undefined) => T)): voiddestroydestroy(): voidCall from `ngOnDestroy`.
DomCall
interfaceinterface DomCall<T>5 members
statepropertystate: CallState<T>subscribesubscribe(onChange: (state: CallState<T>) => void): () => voidTold on every change. The whole of a no-framework re-render.
reloadreload(): voidsetset(updater: T | ((previous: T | undefined) => T)): voiddestroydestroy(): void
MutationOptions
interfaceThe options every mutation binding accepts.
interface MutationOptions<TArgs, T>4 members
optimisticpropertyoptimistic?: OptimisticWrite<TArgs>invalidatespropertyinvalidates?: InvalidationTarget[]Route methods, or key prefixes.
onSuccesspropertyonSuccess?: (result: T, args: TArgs) => voidonErrorpropertyonError?: (error: Error, args: TArgs) => void
OptimisticWrite
interfaceKeys an optimistic write and rollback operate on.
interface OptimisticWrite<TArgs>2 members
keypropertykey: stringvaluepropertyvalue: unknown | ((args: TArgs) => unknown)
QwikCall
interfaceinterface QwikCall<T>3 members
keypropertykey: stringThe cache key, which is also how the call is tracked across a resume.
statepropertystate: { value: CallState<T>; }A Qwik signal of the call's state.
reloadreload(): void
SolidCall
interfaceinterface SolidCall<T>4 members
reloadreload(): voidsetset(updater: T | ((previous: T | undefined) => T)): voiddestroydestroy(): void[call]call(): CallState<T>A signal accessor. Call it in JSX and Solid tracks it.
Subscribable
interfaceSubscribes a callback to a bound call.
Returned rather than done inside the constructor of the holder, because a binding is created during a framework's render and doing work there is what causes a request per render.
interface Subscribable<T>4 members
statepropertystate: CallState<T>reloadreload(): voidsetset(updater: T | ((previous: T | undefined) => T)): voiddestroydestroy(): void
SvelteCall
interfaceSvelte.
Returns a **Svelte store**, not an object with getters. Getters on a plain object are not reactive in Svelte. It compiles components and only tracks reads of `$state` or a store's `subscribe`; a getter on a plain object is invisible to it, so the earlier version here — getters plus an empty change callback — would render once and then never update again. The store contract is the one thing Svelte actually subscribes to, and `$user` works on it directly. ```svelte <script lang="ts"> import { useSvelteCall } from "yatta/frameworks"; const user = useSvelteCall(store, api.getUser, { params: { id: data.id } }); </script> {$user.data?.name} ``` Also usable outside a component: `user.subscribe(fn)` works in plain TypeScript.
interface SvelteCall<T><script lang="ts"> import { useSvelteCall } from "yatta/frameworks"; const user = useSvelteCall(store, api.getUser, { params: { id: data.id } });</script> {$user.data?.name}3 members
reloadreload(): voidsetset(updater: T | ((previous: T | undefined) => T)): voiddestroydestroy(): void
SvelteReadable
interfaceThe Svelte store contract, declared here so Svelte is not a build-time dependency.
interface SvelteReadable<T>1 member
subscribesubscribe(run: (value: T) => void): () => void
VueCall
interfaceinterface VueCall<T>4 members
statepropertystate: { value: CallState<T>; }A `ShallowRef`. Read `.value`.
reloadreload(): voidsetset(updater: T | ((previous: T | undefined) => T)): voiddestroydestroy(): void
VueMutation
interfaceinterface VueMutation<T>3 members
pendingpropertypending: { value: boolean; }errorpropertyerror: { value: Error | undefined; }callcall(args: unknown): Promise<T | undefined>
InvalidationTarget
typeWhat a mutation can invalidate.
A route method, so `api.getUser` invalidates every `getUser` key whatever its arguments. A mutation on a user has to invalidate `getUser` for every id, and listing each id means writing down every id that was ever fetched — which is wrong the moment a component asks for an id nobody predicted. A string is accepted too, read as a prefix when it ends in ":".
type InvalidationTarget = string | ((...args: never[]) => unknown)Other
bind
unknownbindmakeBinding
unknownmakeBindingmethodName
unknownmethodNamestableStringify
unknownstableStringifywatchCall
unknownwatchCallAuthConfig or JobPayload — declaring your schema once is enough for the rest to follow. See Typed keys.