@signal-kernel/vue
v0.2.4
Published
Thin Vue adapter for exposing signal-kernel graph values and async resources as readonly refs.
Maintainers
Readme
@signal-kernel/vue is a thin rendering adapter for Vue applications that need
to expose an existing signal-kernel graph as readonly refs. It connects Vue
scopes to graph values without moving graph ownership, business logic, or async
lifecycle into Vue.
Core signals, computed values, effects, batching, and invalidation semantics
remain owned by @signal-kernel/core.
Install
pnpm add @signal-kernel/vue @signal-kernel/core @signal-kernel/async-runtimevue is a peer dependency and is expected to already exist in the Vue application.
Core Bridge
import { computed, signal } from "@signal-kernel/core";
import { useKernelValue } from "@signal-kernel/vue";
const count = signal(0);
const doubled = computed(() => count.get() * 2);
export function useCounter() {
const value = useKernelValue(count);
const label = useKernelValue(doubled);
function increment() {
count.set(count.peek() + 1);
}
return { value, label, increment };
}useKernelValue() is the preferred bridge for a single readable signal-kernel graph value. It accepts values that expose get() and peek(), including signals and computed values.
useSignalValue() and useComputedValue() remain available as compatibility aliases when a call site wants a signal-specific or computed-specific readability hint.
Reading Multiple Values
Use useReactive() to read an existing reactive scope from Vue. Derived graph logic should still live in computed() or other runtime primitives.
import { computed, signal } from "@signal-kernel/core";
import { useReactive } from "@signal-kernel/vue";
const count = signal(1);
const doubled = computed(() => count.get() * 2);
const status = signal("idle");
export function useDashboard() {
return useReactive(() => ({
count: count.get(),
doubled: doubled.get(),
status: status.get(),
}));
}Async Bridge
import { signal } from "@signal-kernel/core";
import { createResource } from "@signal-kernel/async-runtime";
import { useResource } from "@signal-kernel/vue";
const userId = signal("1");
const userResource = createResource({
input: userId.get,
run: async (id, ctx) => {
const response = await fetch(`/api/users/${id}`, {
signal: ctx.signal,
});
return response.json() as Promise<{ name: string }>;
},
});
export function useUserView() {
const user = useResource(userResource);
return {
value: user.value,
status: user.status,
error: user.error,
reload: user.reload,
cancel: user.cancel,
};
}Resource helpers consume resource tuples created by @signal-kernel/async-runtime. They observe value and metadata getters so metadata-only transitions update Vue refs. They do not add caching, retry, cancellation, or Suspense policy.
Stopping a consumer scope only removes the Vue subscriptions created by the adapter. It does not call resource.meta.cancel() or resource.meta.dispose(), because the resource may be shared by other consumers. Application code may explicitly connect resource.meta.dispose() to a scope that truly owns the resource.
When a manual resource exposes runnable metadata, useResource() preserves that metadata type on resource.meta, so resource.meta.run(input) remains available after passing through the Vue adapter.
Boundary
Use Vue event handlers or composable actions to write to graph values. Use computed() for graph derivation. Use Vue lifecycle APIs for imperative component lifecycle work such as DOM APIs, browser subscriptions, focus management, and third-party widgets.
