npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

amadaius

v1.3.9

Published

Amadaius is a simple and lightweight prompt-templating engine.

Readme

Amadaius

npm version

This package is maintained inside the monorepo at packages/amadaius and is built with pnpm and Turborepo.

Amadaius is a TypeScript/JavaScript library designed to simplify and streamline the process of creating text-based prompts for AI applications. By separating data validation and transformation and prompt structure, Amadaius ensures your prompts are robust, reusable, and easy to manage.

Amadaius leverages:

  • Zod for data validation and transformations.
  • Handlebars for templating.
  • Optional custom helpers that can be easily plugged in.

Why Use Amadaius?

  1. Separation of Concerns: Keep your prompt content and structure independent, making it easier to update, localize, and reuse templates.
  2. Validation and Transformation: Ensure your data is always in the correct format with Zod's powerful schema validation and enrichment features.
  3. Dynamic Templating: Use Handlebars for conditional logic, loops, and custom helpers to create flexible and adaptable prompts.
  4. Modular Template Composition: Build complex prompt templates seamlessly from smaller prompt templates.
  5. Incremental Application: Build complex prompts step-by-step with partial templates, allowing you to fill in data incrementally.
  6. Async Support: Handle asynchronous data fetching and transformations effortlessly.

TL;DR
Amadaius enables you to create prompts that are validated, enriched, and dynamically generated with minimal effort. It's ideal for building AI applications that require structured and reusable prompts.


Table of Contents

  1. Concepts
  2. Features
  3. Installation
  4. Basic Usage
  5. API Reference
  6. Release Process
  7. Contributing
  8. License

Concepts

Prompt Structure and Content

  • Prompt Structure: How the content of the prompt is laid out, defined using a Handlebars template string.
  • Prompt Content: The validated and enriched data provided to the template to populate the structure.

Validation and Enrichment with Zod

  • Validation: Ensures the data adheres to the expected shape and constraints.
  • Enrichment: Transforms or adds data using Zod's transform method.

Handlebars Helpers

Custom functions injected into templates to add dynamic behavior.

Separation of Concerns

Amadaius emphasizes keeping content (data) and structure (template) independent, enabling easier reuse and localization.

Modular Template Composition

Use the asSchema method to compose smaller prompt templates into larger, more complex templates, promoting modularity and reusability.


Features

  • Zod Validation: Ensure your template data is correct.
  • Handlebars-based Templating: Use Handlebars features like conditionals, loops, helpers, and comments.
  • Partial Prompt Templates: Build complex prompts incrementally, adding or overriding data fields at each step.
  • Asynchronous Support: Seamlessly handle asynchronous data transformations (buildAsync(data)).
  • Modular Composition: Combine smaller prompt templates into larger ones using asSchema().

Installation

# Using pnpm
pnpm add amadaius

# Using npm
npm install amadaius

Basic Usage

Creating a Prompt Template

  1. Define a schema describing the data your prompt needs.
  2. Define a template string (Handlebars syntax) that references properties in the schema.
  3. Create a PromptTemplate using promptTemplate(schema, templateStr, options).
import { promptTemplate } from "amadaius";
import { z } from "zod";

// Template references `{{topic}}`
const pt = promptTemplate(
  z.object({ topic: z.string() }),
  "Write a story about {{topic}}!",
);

// Provide data matching the schema
const { prompt, metadata } = pt.build({ topic: "dragons" });

console.log(prompt);
// -> "Write a story about dragons!"

console.log(metadata);
// -> {
//      type: "full",
//      templateStr: "Write a story about {{topic}}!",
//      data: { topic: "dragons" }
//    }

Validating and Transforming Data

Zod can do more than just type-check. You can refine, transform, and set default values. If data fails validation, an error is thrown.

import { promptTemplate } from "amadaius";
import { z } from "zod";

// Example of refining schema
const pt = promptTemplate(
  z
    .object({
      id: z.string(),
      name: z.string(),
    })
    .refine((data) => data.id.length === 10, {
      message: "User ID must be exactly 10 characters long",
    }),
  "Hello, {{name}}! Your ID is {{id}}.",
);

try {
  const { prompt } = pt.build({ id: "0123456789", name: "Alice" });
  console.log(prompt);
  // -> "Hello, Alice! Your ID is 0123456789."

  pt.build({ id: "short", name: "Invalid" }); // This will throw a Zod validation error
} catch (error) {
  console.error(error);
  // -> ZodError: User ID must be exactly 10 characters long
}

You can also transform data using Zod's transform method.

const pt = promptTemplate(
  z.string().transform((topic) => ({ topic })), // transforms a string into { topic },
  "Write a story about {{topic}}!",
);

// We can pass just a string; the schema transforms it into { topic }
const { prompt } = pt.build("dinosaurs");

console.log(prompt);
// -> "Write a story about dinosaurs!"

Composing Prompt Templates

You can convert a PromptTemplate into a Zod schema using asSchema(). This allows you to compose prompt templates together.

import { promptTemplate } from "amadaius";
import { z } from "zod";

// Define smaller prompt templates
const pt1 = promptTemplate(z.object({ name: z.string() }), "Hello, {{name}}!");

const pt2 = promptTemplate(z.object({ question: z.string() }), "{{question}}");

// Compose them into a single prompt
const { prompt } = promptTemplate(
  z.object({ greeting: pt1.asSchema(), request: pt2.asSchema() }),
  "{{greeting}} {{request}}",
).build({
  greeting: { name: "Alice" },
  request: { question: "What is your favorite color?" },
});

console.log(prompt);
// -> "Hello, Alice! What is your favorite color?"

Partial Templates

Sometimes you need to partially apply data to a template and fill in the rest later. You can convert a PromptTemplate into a PartialPromptTemplate using asPartial() and fill in data incrementally with partial(data).

import { promptTemplate } from "amadaius";
import { z } from "zod";

const pt = promptTemplate(
  z.object({
    persona: z.string(),
    message: z.string(),
  }),
  "You are {{persona}}. Respond to: {{message}}",
);

// Convert to partial template
const partialPt = pt.asPartial();
// Fill data in multiple steps
partialPt.partial({ persona: "a knowledgeable AI librarian" });
partialPt.partial({
  message: "What are the best science fiction books?",
});

// When you're ready, build the final string
const { prompt } = partialPt.build();

console.log(prompt);
// -> "You are a knowledgeable AI librarian. Respond to: What are the best science fiction books?"

You can also copy a PartialPromptTemplate to create a new instance with the same data.

const partialPtCopy = partialPt.copy();
const { prompt: copyPrompt } = partialPtCopy.build();
console.log(copyPrompt);
// -> "You are a knowledgeable AI librarian. Respond to: What are the best science fiction books?"
// partialPromptCopy shares the same partial data initially, then you can branch out

Custom Handlebars Helpers

You can add custom Handlebars helpers to your templates by passing them in the helpers option.

import { promptTemplate } from "amadaius";

const pt = promptTemplate(
  z.object({
    persona: z.string(),
    user_message: z.string(),
    tone: z.enum(["formal", "casual", "enthusiastic"]),
  }),
  `
  You are a helpful AI assistant who always follows the persona and tone specified below.
  Persona: {{persona}}

  User said: "{{transformTone user_message tone}}"

  Please respond to the user's message in a manner consistent with the persona and tone above.
  `,
  {
    helpers: {
      transformTone: (
        message: string,
        tone: "formal" | "casual" | "enthusiastic",
      ) => {
        switch (tone) {
          case "formal":
            return `Good day. I would like to bring to your attention: ${
              message.charAt(0).toUpperCase() + message.slice(1)
            }.`;
          case "casual":
            return `Hey! So basically: ${message}`;
          case "enthusiastic":
            return `Wow, check this out: ${message}!!!`;
          default:
            return message;
        }
      },
    },
  },
);

const { prompt } = pt.build({
  persona: "A knowledgeable librarian",
  user_message: "could you help me find a good science fiction book?",
  tone: "enthusiastic",
});

console.log(prompt);
// -> `You are a helpful AI assistant who always follows the persona and tone specified below.
//     Persona: A knowledgeable librarian
//
//    User said: "Wow, check this out: could you help me find a good science fiction book?!!!"
//
//    Please respond to the user's message in a manner consistent with the persona and tone above.
// `

Amadaius supports asynchronous data transformations using buildAsync(data).

import { promptTemplate } from "amadaius";

const asyncPt = promptTemplate(
  z
    .object({
      productNumber: z.number(),
    })
    .transform(async ({ productNumber }) => {
      await getProductData(productNumber);
      return {
        productNumber,
        productData: JSON.stringify(productData, null, 2),
      };
    }),
  "Act as an expert in creating product descriptions.\n\nProduct {{productNumber}}:\n\n{{productData}}\n\nCreate a product description based on the data provided.",
);

const { prompt } = await asyncPt.buildAsync({ productNumber: 1234 });

console.log(prompt);
// -> "Act as an expert in creating product descriptions.\n\nProduct 1234:\n\n{ ... }\n\nCreate a product description based on the data provided."

API Reference

promptTemplate(schema, templateStr, options?)

Creates a new PromptTemplate instance.

Signature

function promptTemplate<TSchema extends ZodType<any, any>>(
  schema: TSchema,
  templateStr: string,
  options?: PromptTemplateOptions,
): PromptTemplate<TSchema>;

Parameters

  • schema: A Zod schema describing the shape of the data needed by your template.
  • templateStr: A Handlebars template string.
  • options?: Optional configuration:
    • helpers?: Record<string, (...args: any) => any>: A key-value map of custom Handlebars helpers.

Returns

  • A new PromptTemplate instance.

Class: PromptTemplate<TSchema>

A fully specified prompt template. You create an instance of this class using promptTemplate.

build(data)

Builds the prompt string using the provided data.

Signature

build(data: z.input<TSchema>): PromptTemplateBuildResult<TSchema>;

Parameters

  • data: Data matching the schema.

Returns

An object containing:

  • prompt: The rendered template string
  • metadata: Object containing:
    • type: "full" | "partial"
    • templateStr: Original template string
    • data: The validated/transformed data
    • Optional fields: templateId, experimentId, version, description, custom

Throws

  • Zod validation errors if data doesn't match the schema.

buildAsync(data)

Builds the prompt string asynchronously using the provided data. This enables asynchronous data transformations (e.g., when using z.transform(async ...) in Zod).

Signature

async buildAsync(data: z.input<TSchema>): Promise<PromptTemplateBuildResult<TSchema>>;

Parameters

  • data: Data matching the schema.

Returns

A promise that resolves to an object containing:

  • prompt: The rendered template string
  • metadata: Object containing:
    • type: "full" | "partial"
    • templateStr: Original template string
    • data: The validated/transformed data
    • Optional fields: templateId, experimentId, version, description, custom

Throws

  • Zod validation errors if data doesn't match the schema.

asSchema()

Enables prompt template composition by converting the PromptTemplate into a zod schema with a built-in transform.

Signature

asSchema(): ZodType<z.input<TSchema>, string, unknown>;

Returns

  • A new Zod schema with a built-in transform method that converts the data into a built prompt.

asPartial()

Returns a PartialPromptTemplate based on a PromptTemplate, allowing you to partially apply data over multiple steps.

Signature

asPartial(): PartialPromptTemplate<TSchema>;

Returns

  • A new PartialPromptTemplate instance.

Class: PartialPromptTemplate<TSchema>

A template that can be progressively filled with data before finalising. It has the same underlying schema as the original PromptTemplate.

partial(data)

Partially applies data to the template.

Signature

partial(data: DeepPartial<z.input<TSchema>>): PartialPromptTemplate<TSchema>;

Parameters

  • data: A partial version of what TSchema expects. Each call merges with existing partial data.

Returns

  • The PartialPromptTemplate instance.

build()

Finalises the partial data, validates it with the original schema, and compiles the template into a string. Throws Zod validation error if the partial data is not sufficient or invalid.

Signature

build(): PromptTemplateBuildResult<TSchema>;

Returns

An object containing:

  • prompt: The rendered template string
  • metadata: Object containing:
    • type: "partial"
    • templateStr: Original template string
    • data: The validated/transformed data
    • Optional fields: templateId, experimentId, version, description, custom

Throws

  • Zod validation errors if the partial data doesn't match the schema.

buildAsync()

Finalises the partial data, validates it with the original schema, and compiles the template into a string asynchronously.

Signature

async buildAsync(): Promise<PromptTemplateBuildResult<TSchema>>;

Returns

A promise that resolves to an object containing:

  • prompt: The rendered template string
  • metadata: Object containing:
    • type: "partial"
    • templateStr: Original template string
    • data: The validated/transformed data
    • Optional fields: templateId, experimentId, version, description, custom

Throws

  • Zod validation errors if the partial data doesn't match the schema.

copy()

Creates a new PartialPromptTemplate instance with the same partial data. Useful for creating branches from a partially-applied template without interfering with each other's data.


Release Process

Releases are managed through two separate GitHub Actions workflows:

1. Release Workflow

The Release workflow handles versioning and creates the GitHub release. To create a new release:

  1. Go to the Actions tab in the GitHub repository
  2. Select the "Release" workflow from the left sidebar
  3. Click "Run workflow" and select the branch (usually main)
  4. Enter the version number in semver format (e.g., 1.2.3, 2.0.0, 1.2.3-beta.1)
  5. Click "Run workflow" to start the release process

The Release workflow will:

  • ✅ Build and verify the project
  • ✅ Check if the version already exists (prevents duplicate releases)
  • ✅ Bump the version in package.json and pnpm-lock.yaml
  • ✅ Create a git tag with the version
  • ✅ Push the changes and tag to the repository
  • ✅ Create a GitHub release
  • ✅ Automatically trigger the Publish workflow

2. Publish Workflow

The Publish workflow handles publishing to npm. It runs automatically after a successful Release workflow, but can also be manually triggered if needed (e.g., if the initial publish failed due to authentication issues). The workflow uses pnpm to install dependencies, build, test, and publish the package.

The Publish workflow will:

  • ✅ Build and verify the package
  • ✅ Verify npm authentication
  • ✅ Publish the package to npm

To manually trigger the Publish workflow:

  1. Go to the Actions tab
  2. Select the "Publish" workflow
  3. Click "Run workflow"
  4. Optionally specify a version (defaults to the version in package.json)

Note: Both workflows use npm's trusted publishing (OIDC) for secure authentication. To set it up:

  1. Go to https://www.npmjs.com/settings/YOUR_USERNAME/automation
  2. Click "Add GitHub Actions" or "Add Trusted Publisher"
  3. Enter the following details:
    • Organization/User: samueljacobs98
    • Repository: samueljacobs98/amadaius
    • Workflow filename: publish.yml (must match exactly, including case!)
    • Environment name: (leave blank)
  4. Grant publish permissions

⚠️ Important: The workflow filename must be exactly publish.yml for trusted publishing to work.

No NPM_TOKEN secret is needed! Trusted publishing is more secure and future-proof than classic tokens.


Contributing

Contributions are welcome! Please read the contribution guidelines first.

License

This project is licensed under the MIT License. See the LICENSE file for details.