Managing Tags
When creating Tags, it’s easy to slip into one of two extremes.
One Tag per Task — every shared value becomes a duplicate.
const dashboardTag = tag<{ user: User widgets: Widget[]}>("dashboard")
const dashboard = createTask({ name: "dashboard", run: { context: dashboardTag.value, fn: () => {} },})
const profileTag = tag<{ user: User settings: Setting[]}>("profile")
const profile = createTask({ name: "profile", run: { context: profileTag.value, fn: () => {} },})One Tag per value — every new value means a new Tag and a new wire.
const dashboardUser = tag<User>("dashboard::user")const dashboardWidgets = tag<Widget[]>("dashboard::widgets")
const dashboard = createTask({ name: "dashboard", run: { context: { user: dashboardUser.value, widgets: dashboardWidgets.value }, fn: () => {}, },})
const profileUser = tag<User>("profile::user")const profileSettings = tag<Setting[]>("profile::settings")
const profile = createTask({ name: "profile", run: { context: { user: profileUser.value, settings: profileSettings.value }, fn: () => {}, },})The right count sits between them.
The hybrid: one shared Tag, separate Tags for what differs.
import {compose, createTask, createWire, tag, literal} from "@app-compose/core"
const userTag = tag<User>("user")
const dashboardTag = tag<{ widgets: Widget[] }>("dashboard")const dashboard = createTask({ name: "dashboard", run: { context: { user: userTag.value, widgets: dashboardTag.value.widgets, }, fn: console.log, },})
const profileTag = tag<{ settings: Setting[] }>("profile")const profile = createTask({ name: "profile", run: { context: { user: userTag.value, settings: profileTag.value.settings, }, fn: console.log, },})
compose() .step([ createWire({ from: literal(1), to: userTag }), createWire({ from: { widgets: literal([]) }, to: dashboardTag }), createWire({ from: { settings: literal([]) }, to: profileTag }), ]) .step([dashboard, profile]) .run()
type User = unknowntype Widget = unknowntype Setting = unknownDeep context types
Section titled “Deep context types”When a Task’s context type is deeply nested, a Tag often carries the whole type minus a few internal fields. Writing that subtype by hand is painful — and it goes stale the moment the source changes. Use OmitDeep from type-fest.
import type { OmitDeep } from "type-fest"
type DashboardCtx = { user: { id: string } navigation: { sidebarApi: SidebarApi title: string } widgets: Widgets[]}
const dashboardTag = tag<OmitDeep<DashboardCtx, "navigation.sidebarApi">>("dashboard")// same as { user: { id: string }; navigation: { title: string }; widgets: Widgets[] }