@flowwright/plugin-cache
Two helpers that pair a cache.restore and cache.save around a body, with the key
derived from the files that determine the contents.
npm install --save-dev @flowwright/plugin-cache
API
| Export | Kind | Signature |
|---|---|---|
withCache | step helper | (opts: WithCacheOptions, body: () => Promise<void>) => Promise<void> |
cachedStage | stage builder | (name, opts: CachedStageOptions, body) => StageDefinition |
WithCacheOptions: path (required), keyFiles (required, string or array),
prefix (default: a slug of path). CachedStageOptions is that plus every
StageOptions key except run.
Example
import { pipeline, stage, sh } from "@flowwright/core";
import { cachedStage } from "@flowwright/plugin-cache";
export default pipeline({
name: "acme-api",
stages: [
cachedStage("Install", { id: "install", path: "node_modules", keyFiles: "package-lock.json" }, async () => {
await sh(["npm", "ci"]);
}),
stage("Test", {
needs: ["install"],
run: async () => {
await sh(["npm", "test"]);
},
}),
],
});
What it composes down to
withCache is four lines:
const key = `${prefix}-${await hashFiles(keyFiles)}`;
await cache.restore(path, { key });
await body();
await cache.save(path, { key });
So cachedStage("Install", { path: "node_modules", keyFiles: "package-lock.json" }, body)
is a stage whose cacheKeys are a restore/save pair around whatever body records,
keyed node-modules-<hash of package-lock.json>. The prefix is path slugified —
node_modules → node-modules, .venv → venv — unless you pass prefix.
Every StageOptions key except run passes straight through, so id, needs,
container and the rest work as usual.
Gotchas
The body always runs. A cache hit doesn't skip anything — it just means the files
are already there, so npm ci finds its work done and exits quickly. There's no
"restore succeeded, skip the install" branch, and there can't be: the body was
recorded before any cache was consulted. See
Artifacts, cache and stash.
Keys are write-once. Saving to an existing key is a no-op forever. That's why the
key contains a hash — change the lockfile and you get a new key. A hand-written
prefix with static keyFiles will serve its first contents indefinitely.
A keyFiles pattern that matches nothing still produces a key — the hash of an
empty set, which every other empty pattern also produces. Check the key in
flow explain once.
plugin-node and plugin-python don't use this package. They hand-roll a
<pm>-deps-<hash> key against the core primitives, so their cache entries and this
one never share. See the overview.