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

@contentrain/query

v5.1.5

Published

Optional type-safe query SDK for Contentrain — generated TypeScript client for platform-independent JSON content

Downloads

942

Readme

@contentrain/query

npm version Agent Skills GitHub source Docs

Optional type-safe generated query SDK for Contentrain.

Start here:

Contentrain stores content as plain JSON and Markdown in a git-backed .contentrain/ directory. Any platform that reads JSON can consume this content directly. This package adds a TypeScript convenience layer that turns content models into a generated JS/TS client with:

  • exact TypeScript types from your models
  • zero-dependency query runtime
  • Node #contentrain subpath imports
  • ESM and CommonJS output
  • framework-agnostic usage across app and server environments

🚀 Install

pnpm add @contentrain/query

Requirements:

  • Node.js 22+
  • a Contentrain project with .contentrain/config.json

✨ What This Package Provides

@contentrain/query has two roles:

  1. Generator

    • reads .contentrain/config.json, models, and content files
    • writes .contentrain/client/
    • injects #contentrain imports into your package.json
  2. Base runtime

    • exports low-level runtime classes for framework SDK authors
    • exports createContentrainClient(projectRoot?) for loading a generated client module

🚀 Quick Start

Generate a client:

npx contentrain-query generate

This writes:

.contentrain/client/
  index.mjs
  index.cjs
  index.d.ts
  data/*

It also updates your package.json with:

{
  "imports": {
    "#contentrain": {
      "types": "./.contentrain/client/index.d.ts",
      "import": "./.contentrain/client/index.mjs",
      "require": "./.contentrain/client/index.cjs",
      "default": "./.contentrain/client/index.mjs"
    }
  }
}

Then use the generated client in your app:

import { query, singleton, dictionary, document } from '#contentrain'

const posts = query('blog-post')
  .locale('en')
  .where('status', 'published')
  .sort('title')
  .all()

const hero = singleton('hero').locale('en').get()
const messages = dictionary('error-messages').locale('en').get()
const article = document('blog-article').locale('en').bySlug('welcome-post')

📦 Generated Client API

The generated client exposes four entry points:

query(model)

For collection models.

Supported methods:

  • locale(lang)
  • where(field, value) — equality shorthand
  • where(field, op, value) — operators: eq, ne, gt, gte, lt, lte, in, contains
  • sort(field, order?)
  • limit(n)
  • offset(n)
  • include(...fields)
  • count()
  • first()
  • all()

Where operator examples:

query('plans').where('slug', 'ne', 'free').all()
query('plans').where('price', 'gte', 10).where('price', 'lte', 50).all()
query('starters').where('framework', 'in', ['nuxt', 'next']).all()
query('blog').where('title', 'contains', 'Guide').count()

singleton(model)

For singleton models.

Supported methods:

  • locale(lang)
  • include(...fields)
  • get()

dictionary(model)

For dictionary models.

Supported methods:

  • locale(lang)
  • get()
  • get(key)

document(model)

For markdown/document models.

Supported methods:

  • locale(lang)
  • where(field, value) — equality shorthand
  • where(field, op, value) — same operators as query()
  • include(...fields)
  • bySlug(slug)
  • count()
  • first()
  • all()

🔗 Relations

Generated clients support relation resolution via include(...).

Examples:

const posts = query('blog-post')
  .locale('en')
  .include('author', 'tags')
  .all()

const settings = singleton('site-settings')
  .locale('en')
  .include('featured_post')
  .get()

🧱 Framework SDK Authors

The package root exports runtime primitives and an async loader:

import { createContentrainClient } from '@contentrain/query'

const client = await createContentrainClient(process.cwd())
const posts = client.query('blog-post').locale('en').all()

Public root exports:

  • QueryBuilder, SingletonAccessor, DictionaryAccessor, DocumentQuery — runtime classes
  • createContentrainClient — local generated client loader
  • createContentrain — CDN client factory
  • MediaAccessor — CDN media manifest reader
  • FormsClient — CDN forms API client
  • ConversationClient — Conversation API client
  • ContentrainError — HTTP error class for CDN mode
  • applyWhere — shared where filter helper

CDN Transport

For apps that fetch content from Contentrain Studio CDN (SSR, serverless, mobile):

import { createContentrain } from '@contentrain/query/cdn'

const client = createContentrain({
  projectId: '350696e8-...',
  apiKey: 'crn_live_xxx',
  // baseUrl: 'https://studio.contentrain.io/api/cdn/v1'  (default)
})

// All CDN queries are async
const posts = await client.collection('faq').locale('en').all()
const hero  = await client.singleton('hero').locale('en').get()
const t     = await client.dictionary('ui').locale('en').get()
const doc   = await client.document('docs').locale('en').bySlug('intro')

CDN collection queries support extended operators:

const filtered = await client.collection('faq')
  .locale('en')
  .where('order', 'gt', 5)
  .where('category', 'in', ['general', 'billing'])
  .sort('order', 'desc')
  .limit(10)
  .all()

CDN collection queries support count() and entry metadata:

const total = await client.collection('faq').locale('en').count()

// Enrich entries with _meta (status, publish_at, expire_at)
const posts = await client.collection('blog')
  .locale('en')
  .withMeta()
  .all()
// posts[0]._meta → { status: 'published', publish_at: '...', ... }

Media

Access the media manifest and resolve asset variant URLs:

const media = client.media()
const assets = await media.list()           // All assets with paths
const asset  = await media.asset('hero.jpg') // Single asset

// Resolve variant URL
const thumbUrl = media.url(asset, 'thumb')  // Full CDN URL
const original = media.url(asset)           // Original URL

// Asset metadata
asset.meta.width      // 1920
asset.meta.blurhash   // 'LEHV6nWB...'
asset.meta.alt        // 'Hero image'

Forms

Fetch form schema and submit data from external sites:

const form = client.form()

// Get form field configuration
const config = await form.config('contact')
// config.fields → [{ id: 'name', type: 'string', required: true }, ...]

// Submit form data
const result = await form.submit('contact', {
  name: 'Alice',
  email: '[email protected]',
  message: 'Hello!',
}, { captchaToken: 'tok_xxx' })
// result → { success: true, message: 'Thank you!' }

Conversation API

Send messages to the AI content agent and manage conversation history:

const conv = client.conversation()

// Send a message — returns complete response with tool results
const response = await conv.send('Create a new blog post about Vue 4')
response.conversationId   // 'conv-abc123'
response.message          // 'I created the blog post...'
response.toolResults      // [{ id: 't-1', name: 'save_content', result: {...} }]
response.usage            // { inputTokens: 150, outputTokens: 80 }

// Continue a conversation
const followUp = await conv.send('Now translate it to Turkish', {
  conversationId: response.conversationId,
})

// Provide UI context
await conv.send('Update the hero section', {
  context: { activeModelId: 'hero', activeLocale: 'en' },
})

// Fetch conversation history
const history = await conv.history('conv-abc123', { limit: 50 })
history.messages  // [{ id, role, content, createdAt }, ...]

Metadata Endpoints

const manifest = await client.manifest()
const models   = await client.models()
const model    = await client.model('faq')

CDN vs Local

| Aspect | Local (#contentrain) | CDN (createContentrain()) | |--------|----------------------|---------------------------| | Data source | Bundled .mjs files | HTTP fetch from CDN | | Return type | Sync (T[]) | Async (Promise<T[]>) | | Auth | None | API key required | | Caching | In-memory (embedded) | ETag-based HTTP cache | | Use case | SSG, build-time | SSR, client-side, serverless |

CommonJS Usage

Generated clients support CommonJS through init():

const clientModule = require('#contentrain')
const client = await clientModule.init()

const hero = client.singleton('hero').get()

🛠 Generation Commands

Via the contentrain CLI (recommended for most users):

contentrain generate                # Generate once
contentrain generate --watch        # Regenerate on model/content changes
contentrain generate --json         # Machine-readable JSON for CI

Via contentrain-query (programmatic / build tool flows):

npx contentrain-query generate
npx contentrain-query generate --watch
npx contentrain-query generate --root /path/to/project

Or from TypeScript:

import { generate } from '@contentrain/query/generate'

const result = await generate({ projectRoot: process.cwd() })
console.log(result.generatedFiles.length)

📤 Package Exports

Main package:

  • @contentrain/query — runtime classes + createContentrain() CDN factory

Generator entry:

  • @contentrain/query/generate — programmatic generation API

CDN transport:

  • @contentrain/query/cdn — CDN client with HttpTransport, async query classes, MediaAccessor, FormsClient, ConversationClient

🧠 Design Constraints

This package intentionally:

  • generates into .contentrain/client/ instead of node_modules
  • uses package.json#imports instead of custom alias plugins
  • ships zero-dependency runtime classes
  • keeps the runtime framework-agnostic
  • treats generated client output as the primary consumer surface

🛠 Development

From the monorepo root:

pnpm --filter @contentrain/query build
pnpm --filter @contentrain/query test
pnpm --filter @contentrain/query typecheck
pnpm exec oxlint packages/sdk/js/src packages/sdk/js/tests

Agent Skill (embedded)

This package ships an embedded Agent Skill at skills/contentrain-query/SKILL.md. AI coding agents can discover and load it for type-safe SDK usage guidance, including:

  • QueryBuilder, SingletonAccessor, DictionaryAccessor, DocumentQuery APIs
  • Local mode vs CDN mode differences
  • Framework-specific bundler configuration (Vite, Next.js, Nuxt, SvelteKit, Metro)

The skill is available via the @contentrain/query/skills/* subpath export.

🔗 Related Packages

  • contentrain — CLI that runs project initialization, validation, serve, and generation flows
  • @contentrain/mcp — local-first MCP server and core content workflow engine
  • @contentrain/types — shared schema and model definitions
  • @contentrain/rules — agent rules and prompts

📚 Documentation

Full documentation at ai.contentrain.io/packages/sdk.

📄 License

MIT