@zoja/sdk
v4.2.0
Published
Zoja SDK — fetch prompt versions at runtime
Maintainers
Readme
📝 Zoja
TypeScript SDK for fetching and hydrating prompts from Zoja at runtime. Zero dependencies.
Install
npm install @zoja/sdkQuick start
import { ZojaClient } from "@zoja/sdk";
const zoja = new ZojaClient({ apiKey: "zj_..." });
const prompt = await zoja.getPrompt("welcome-email");
console.log(prompt.text);Template variables
Write prompts with {{variable}} placeholders in the Zoja dashboard, then hydrate them at runtime by calling setVariables on the returned prompt:
// Prompt text in dashboard:
// "Hello {{name}}, welcome to {{company}}!"
const prompt = await zoja.getPrompt("welcome-email");
prompt.setVariables({ name: "Alice", company: "Acme" });
console.log(prompt.text);
// "Hello Alice, welcome to Acme!"Variables accumulate across calls and can be overwritten. Unmatched placeholders are left as-is by default, so partial hydration is safe.
Handling unused variables
Call removeUnusedVariables once you're done setting variables to clean up any {{var}} left unfilled:
prompt.setVariables({ name: "Alice" });
prompt.setVariables({ company: "Acme" });
prompt.removeUnusedVariables("remove-line");"remove-line"— drop the entire line containing the unused variable"remove-sentence"— drop the surrounding sentence"remove-variable"— strip just the{{token}}
The handling sticks for subsequent setVariables calls, and recomputation always starts from the original text and the accumulated variable map. Filling a previously unused variable restores the surrounding text.
Preloading
Use loadPrompt to warm the cache at startup. Later getPrompt calls for the same slug resolve instantly from cache:
// At app startup
await zoja.loadPrompt("welcome-email");
await zoja.loadPrompt("support-reply");
// Later, when needed — no network call
const prompt = await zoja.getPrompt("welcome-email");
prompt.setVariables({ name: "Alice" });Caching
By default, prompts are cached in-process using MemoryCache. Cache lifetime per prompt comes from config.cache_ttl_ms (default: 5 minutes).
For multi-instance deployments, pass a custom cache adapter that implements the PromptCache interface — get/set/delete, plus an optional clear. The SDK serializes prompts to plain JSON (CachedPromptData), so any external store works:
import { ZojaClient, type PromptCache, type CachedPromptData } from "@zoja/sdk";
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL!);
const redisCache: PromptCache = {
async get(slug) {
const raw = await redis.get(`zoja:${slug}`);
return raw ? (JSON.parse(raw) as CachedPromptData) : null;
},
async set(slug, data, ttlMs) {
await redis.set(`zoja:${slug}`, JSON.stringify(data), "PX", ttlMs);
},
async delete(slug) {
await redis.del(`zoja:${slug}`);
},
};
const zoja = new ZojaClient({ apiKey: "zj_...", cache: redisCache });Each method may be sync or async — return whichever fits your store.
API
new ZojaClient({ apiKey, cache? })
Create a client. The API key is generated in the Zoja dashboard under Keys and is scoped to a project + environment. cache defaults to an in-memory MemoryCache — see Caching to plug in Redis or another backend.
client.getPrompt<TConfig>(slug)
Fetch a prompt by slug. Returns a Prompt<TConfig> object.
- Results are cached in memory. TTL is read from
config.cache_ttl_mson the prompt (default: 5 minutes). - Pass a generic
TConfigto type the prompt'sconfigfield.
client.loadPrompt(slug)
Fetch and cache a prompt without returning it. Use this to preload prompts you'll need later.
client.invalidate(slug)
Evict a specific slug from the cache.
client.clearCache()
Clear the entire cache.
Prompt<TConfig>
Returned by getPrompt. Holds the fetched text and config.
prompt.text— current text, updated bysetVariablesandremoveUnusedVariablesprompt.config— config object from the dashboard, typed asTConfigprompt.setVariables(vars)— accumulate variables and recomputetextprompt.removeUnusedVariables(handling)— strip leftover{{vars}}using"remove-line","remove-sentence", or"remove-variable"
Error handling
All errors are thrown as ZojaError with code and message fields:
import { ZojaError } from "@zoja/sdk";
try {
await zoja.getPrompt("nonexistent");
} catch (err) {
if (err instanceof ZojaError) {
console.log(err.code); // "PROMPT_NOT_FOUND"
console.log(err.message); // "No prompt found with slug 'nonexistent'"
}
}License
MIT
