Interface: ArtifactStore
Defined in: packages/core/src/artifact.types.ts:188
The storage contract behind publishing: implement it over Postgres, S3, or anything else, and every part of Nubbin that publishes, rolls back or checks routes works against it. Two kinds of state sit behind it — artifacts keyed by content hash, written once and never changed, and one pointer per route, which is the only thing that moves.
Callers order the two: write the artifact, then publish the route at its hash. An
implementation holds up its half by making absence a value rather than a failure, by taking a
repeated write as a no-op and a repeated publish as an ordinary one so a retried publish
succeeds, and by writing each pointer whole — two publishes racing for one route must leave one
of them intact, never a blend. @nubbin/store-fs is the reference implementation, and
runArtifactStoreContract from @nubbin/store-fs/testing is the suite every implementation is
expected to pass — call it with a factory for your store and the guarantees above are executed
rather than read. It needs vitest, which the package declares as an optional peer.
Example
import { parseMatchKind } from "@nubbin/core";
import type { ArtifactStore } from "@nubbin/core";
const store: ArtifactStore = {
read: async (hash) => (await db.artifact(hash)) ?? null,
write: async (artifact) => { await db.putArtifactIfAbsent(artifact.hash, artifact); },
manifest: async () => ({ routes: await db.pointers(), generatedAt: new Date().toISOString() }),
pointer: async (route) => (await db.pointer(route)) ?? null,
publish: async (route, hash) => { await db.movePointer(route, hash, parseMatchKind(route)); },
unpublish: async (route) => { await db.deletePointer(route); },
};
Methods
history()?
optionalhistory(route):Promise<PointerMove[]>
Defined in: packages/core/src/artifact.types.ts:291
Every move publish made at this route, oldest first, surviving unpublish. Optional
because a write-only blob store is still a valid adapter — a caller degrades with a
message rather than requiring it.
Parameters
route
string
The route whose moves are read.
Returns
Promise<PointerMove[]>
One entry per publish, oldest first, and an empty array for a route that has never
been published. A store that keeps no history omits the method rather than returning [],
so a caller can tell "never published" from "not recorded".
Example
const moves = (await store.history?.("/pricing")) ?? [];
moves.at(-1)?.hash; // what it points at now
manifest()
manifest():
Promise<Manifest>
Defined in: packages/core/src/artifact.types.ts:226
Lists every published route.
Returns
Promise<Manifest>
A snapshot of every pointer the store holds, with the time it was taken. An empty
routes is a store with nothing published, not a failure.
Example
const { routes } = await store.manifest();
pointer()
pointer(
route):Promise<RoutePointer|null>
Defined in: packages/core/src/artifact.types.ts:240
Reads the pointer for one route.
Parameters
route
string
The route as it was published, matched exactly — /pricing does not find
/pricing/, and a param or prefix pointer is found by its pattern, not by a path it
would match.
Returns
Promise<RoutePointer | null>
The pointer, or null when nothing is published at that route.
Example
const pointer = await store.pointer("/guides/[city]");
publish()
publish(
route,hash):Promise<void>
Defined in: packages/core/src/artifact.types.ts:262
Points a route at an artifact already in the store — the publish, and the rollback. The
implementation derives matchKind with parseMatchKind and stamps updatedAt itself.
Parameters
route
string
The route to serve. It is validated on the way through parseMatchKind.
hash
string
The artifact to serve there. It has to be written first.
Returns
Promise<void>
Nothing. Publishing the same route and hash twice succeeds and leaves the route where it already pointed, which is what makes a retry safe — it is not a no-op. The pointer is rewritten and a store keeping history records a second move, since what is deduplicated is the artifact rather than the act of publishing.
Throws
NubbinError with code NubbinIssueCode.InvalidRoute when the route is not
addressable, from parseMatchKind. An implementation also rejects a hash it holds no
artifact for, so no pointer can name one that was never written — @nubbin/store-fs refuses
that with NubbinIssueCode.ArtifactNotStored.
Example
await store.write(artifact);
await store.publish("/pricing", artifact.hash);
read()
read(
hash):Promise<Artifact|null>
Defined in: packages/core/src/artifact.types.ts:200
Reads one artifact by its content hash.
Parameters
hash
string
The hash of an artifact already written.
Returns
Promise<Artifact | null>
The artifact as written, or null when the store holds nothing at that hash.
Absence is a value here — an unknown hash never throws.
Example
const artifact = await store.read("4a162726");
unpublish()
unpublish(
route):Promise<void>
Defined in: packages/core/src/artifact.types.ts:275
Takes a route offline by removing its pointer. The artifact stays, so republishing it is
another publish at the same hash.
Parameters
route
string
The route to stop serving.
Returns
Promise<void>
Nothing. Unpublishing a route that has no pointer is a no-op.
Example
await store.unpublish("/pricing");
write()
write(
artifact):Promise<void>
Defined in: packages/core/src/artifact.types.ts:214
Stores one artifact under its own hash. Write before publishing the route at it.
Parameters
artifact
A compiled artifact. artifact.hash is the key; nothing else is read.
Returns
Promise<void>
Nothing. Writing a hash the store already holds is a no-op, so a publish retried after a timeout succeeds on its second attempt.
Throws
Whatever the underlying storage raises when the write itself fails.
Example
await store.write(compile(version, catalog, registry, "/pricing").artifact);