Skip to content

roastery-cms/seedbed

Repository files navigation

@roastery/seedbed

Shared utilities, use cases, DTOs, and domain abstractions for the Roastery CMS ecosystem — reusable building blocks for entity lookup, pagination, slug validation, and plugin extensibility.

Checked with Biome

Overview

seedbed provides cross-cutting application concerns shared across Roastery services:

  • FindEntityByTypeUseCase — Generic use case that resolves an entity by UUID or slug, auto-detecting the input type.
  • GetNumberOfPagesService — Pagination calculator with configurable items per page.
  • SlugUniquenessCheckerService — Domain service to verify slug availability.
  • DTOs — Schema-validated Data Transfer Objects for common query parameters (ID/slug lookup, pagination).
  • Plugin interface — Generic contract for extending services with plugins.
  • Constants — Shared configuration values for pagination limits and cache expiration.

Technologies

Tool Purpose
@roastery/terroir Schema validation, exception hierarchy, and TypeBox re-exports
@roastery/beans Entity abstractions, UUID utilities, and collection schemas
tsup Bundling to ESM + CJS with .d.ts generation
Bun Runtime, test runner, and package manager
Knip Unused exports and dependency detection
Husky + commitlint Git hooks and conventional commit enforcement

Installation

Install the package and its peer dependencies:

bun add @roastery/seedbed @roastery/terroir @roastery/beans typescript

Local development (link)

If you're developing seedbed alongside another project, you can link it locally:

# Inside the seedbed directory
bun run setup  # builds and registers the link

# Inside your consuming project
bun link @roastery/seedbed

FindEntityByTypeUseCase

Generic use case that resolves an entity by its identifier. It auto-detects whether the input is a UUID or slug and delegates to the appropriate repository method.

import { FindEntityByTypeUseCase } from "@roastery/seedbed/application/use-cases";
import type { ICanReadId, ICanReadSlug } from "@roastery/seedbed/domain/types/repositories";

class PostRepository implements ICanReadId<PostDTO, Post>, ICanReadSlug<PostDTO, Post> {
  async findById(id: string): Promise<Post | null> { /* ... */ }
  async findBySlug(slug: string): Promise<Post | null> { /* ... */ }
}

const repository = new PostRepository();
const findPost = new FindEntityByTypeUseCase(repository);

// Resolves by UUID
const post = await findPost.run("550e8400-e29b-41d4-a716-446655440000", "Post");

// Resolves by slug
const post = await findPost.run("my-first-post", "Post");

// Throws ResourceNotFoundException if not found

GetNumberOfPagesService

Calculates the total number of pages for a paginated result set.

import { GetNumberOfPagesService } from "@roastery/seedbed/application/services";

GetNumberOfPagesService.run(100, 10); // 10
GetNumberOfPagesService.run(101, 10); // 11
GetNumberOfPagesService.run(100);     // 8 (default: 14 items per page)
GetNumberOfPagesService.run(0, 10);   // 0

SlugUniquenessCheckerService

Domain service that checks whether a slug is available for use.

import { SlugUniquenessCheckerService } from "@roastery/seedbed/domain/services";

const checker = new SlugUniquenessCheckerService(repository);

await checker.run("my-post");       // true (available)
await checker.run("existing-post"); // false (taken)

DTOs

Schema-validated Data Transfer Objects for common query parameters.

IdOrSlugDTO

Accepts either a UUID or a slug as a path/query parameter:

import { IdOrSlugDTO } from "@roastery/seedbed/presentation/dtos";

// { "id-or-slug": "550e8400-e29b-41d4-a716-446655440000" }
// { "id-or-slug": "my-cool-post" }

PaginationDTO

Pagination parameter with a minimum value of 1:

import { PaginationDTO } from "@roastery/seedbed/presentation/dtos";

// { page: 1 }

Plugin interface

Generic contract for building extensible plugin systems:

import type { IPlugin } from "@roastery/seedbed/presentation/types";

interface MyPluginValue {
  handler: () => void;
}

const myPlugin: IPlugin<MyPluginValue> = {
  name: "my-plugin",
  value: { handler: () => console.log("Hello!") },
};

Constants

import { MAX_ITEMS_PER_QUERY, CACHE_EXPIRATION_TIME } from "@roastery/seedbed/constants";
Constant Value Description
MAX_ITEMS_PER_QUERY 14 Default items per page for pagination
CACHE_EXPIRATION_TIME.SAFE 3600 1 hour (seconds)
CACHE_EXPIRATION_TIME.LOW_UPDATES 86400 24 hours (seconds)
CACHE_EXPIRATION_TIME.HIGH_UPDATES 900 15 minutes (seconds)

Utilities

detectEntry

Detects whether a string is a UUID or a slug:

import { detectEntry } from "@roastery/seedbed/application/utils";

detectEntry("550e8400-e29b-41d4-a716-446655440000"); // "UUID"
detectEntry("my-cool-post");                          // "SLUG"

Repository interfaces

Granular interfaces for read operations, allowing repositories to implement only what they need:

import type { ICanReadId, ICanReadSlug } from "@roastery/seedbed/domain/types/repositories";

// Find by UUID
interface ICanReadId<SchemaType, EntityType> {
  findById(id: string): Promise<EntityType | null>;
}

// Find by slug
interface ICanReadSlug<SchemaType, EntityType> {
  findBySlug(slug: string): Promise<EntityType | null>;
}

Exports reference

// Application — Use Cases
import { FindEntityByTypeUseCase } from "@roastery/seedbed/application/use-cases";

// Application — Services
import { GetNumberOfPagesService } from "@roastery/seedbed/application/services";

// Application — Utilities
import { detectEntry } from "@roastery/seedbed/application/utils";

// Application — Types
import type { EntryActions, ValueTypes, ICountItems } from "@roastery/seedbed/application/types";

// Domain — Repository Interfaces
import type { ICanReadId, ICanReadSlug } from "@roastery/seedbed/domain/types/repositories";

// Domain — Services
import { SlugUniquenessCheckerService } from "@roastery/seedbed/domain/services";

// Presentation — DTOs
import { IdOrSlugDTO, PaginationDTO } from "@roastery/seedbed/presentation/dtos";

// Presentation — Types
import type { IPlugin } from "@roastery/seedbed/presentation/types";

// Constants
import { MAX_ITEMS_PER_QUERY, CACHE_EXPIRATION_TIME } from "@roastery/seedbed/constants";

Development

# Run tests
bun run test:unit

# Run tests with coverage
bun run test:coverage

# Build for distribution
bun run build

# Check for unused exports and dependencies
bun run knip

# Full setup (build + bun link)
bun run setup

License

MIT

About

Shared utilities, use cases, DTOs, and domain abstractions for the Roastery CMS ecosystem.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors