Skip to content

Mapping values

Values flow between Tasks and Wires. shape transforms a value between them — picking a piece of it, deriving a new one from it, or merging several into one. Its input is one or several sources. The transform lives in shape, not in fn.

shape works in both run.context and enabled.context.

import { compose, createTask, shape } from "@grlt-hub/app-compose"
const user = createTask({
name: "user",
run: {
fn: () => ({ name: "John", role: "admin" }),
},
})
const greeting = createTask({
name: "greeting",
run: {
// run.context
context: {
name: shape(user.result, (u) => u.name.toUpperCase()),
},
fn: ({ name }) => console.log(`Welcome back, ${name}`),
},
})
const adminPanel = createTask({
name: "admin-panel",
run: {
fn: () => console.log("Admin panel ready"),
},
enabled: {
// enabled.context
context: {
isAdmin: shape(user.result, (u) => u.role === "admin"),
},
fn: ({ isAdmin }) => isAdmin,
},
})
compose()
.step(user)
.step([greeting, adminPanel])
.run()

shape works in createWire.from.

import { compose, createTask, createWire, shape, tag } from "@grlt-hub/app-compose"
const userName = tag<string>("userName")
const user = createTask({
name: "user",
run: {
fn: () => ({ name: "John", role: "admin" }),
},
})
const greeting = createTask({
name: "greeting",
run: {
context: {
name: userName.value,
},
fn: ({ name }) => console.log(`Welcome back, ${name}`),
},
})
const userNameWire = createWire({
from: shape(user.result, (u) => u.name.toUpperCase()),
to: userName,
})
compose()
.step(user)
.step(userNameWire)
.step(greeting)
.run()

Pass an array or an object instead of a single source — either works:

import { compose, createTask, shape } from "@grlt-hub/app-compose"
const firstName = createTask({
name: "first-name",
run: { fn: () => "John" },
})
const lastName = createTask({
name: "last-name",
run: { fn: () => "Doe" },
})
const fullNameShape = shape(
{
first: firstName.result,
last: lastName.result,
},
(v) => `${v.first} ${v.last}`,
)
// as an array
// shape(
// [firstName.result, lastName.result],
// ([first, last]) => `${first} ${last}`,
// )
const fullName = createTask({
name: "full-name",
run: {
context: { fullName: fullNameShape },
fn: console.log,
},
})
compose()
.step([firstName, lastName])
.step(fullName)
.run()

When shape fails, the downstream Task skips. Two cases:

  • The source Task fails
  • The shape callback throws an error

The rest of the composition runs as usual.