### Install Packages Source: https://github.com/djgrant/piq/blob/main/docs/packages/index.md Use this command to install the core piqit package and its resolvers. ```bash npm install piqit @piqit/resolvers ``` -------------------------------- ### Install piq and dependencies Source: https://github.com/djgrant/piq/blob/main/docs/guide/index.md Install the core piqit library and necessary resolvers using npm. ```bash npm install piqit @piqit/resolvers zod ``` -------------------------------- ### Install piqit Package Source: https://github.com/djgrant/piq/blob/main/docs/packages/piqit.md Install the piqit package using npm. ```bash npm install piqit ``` -------------------------------- ### Install @piqit/resolvers Source: https://github.com/djgrant/piq/blob/main/docs/packages/resolvers.md Install the package using npm. This command is for the Node.js package manager. ```bash npm install @piqit/resolvers ``` -------------------------------- ### Import piq Library Source: https://github.com/djgrant/piq/blob/main/docs/reference/api.md Import necessary functions from the piqit library to start building queries. ```APIDOC ## Import piq Library ### Description Import the `piq` and `from` functions from the `piqit` library. ### Code Example ```typescript import { piq, from } from 'piqit' ``` ``` -------------------------------- ### Import piq Functions Source: https://github.com/djgrant/piq/blob/main/docs/reference/api.md Import the necessary functions 'piq' and 'from' from the 'piqit' library to start building queries. ```typescript import { piq, from } from 'piqit' ``` -------------------------------- ### Primary API Usage Example Source: https://github.com/djgrant/piq/blob/main/docs/packages/piqit.md Demonstrates the primary API usage for building and executing a query. Ensure `select()` is called before `exec()`. ```typescript import { piq } from 'piqit' const rows = await piq .from(postsResolver) .scan({ year: '2024' }) .filter({ status: 'published' }) .select('params.slug', 'frontmatter.title') .exec() ``` -------------------------------- ### piq.from - Create a Query from a Resolver Source: https://context7.com/djgrant/piq/llms.txt The `piq.from()` function initializes a query builder instance using a provided resolver. This is the starting point for all queries, returning a chainable QueryBuilder. ```APIDOC ## piq.from - Create a Query from a Resolver ### Description The `piq.from()` function creates a new query builder instance from a resolver, which is the entry point for all queries. It returns a chainable QueryBuilder that supports scan, filter, select, and execution methods. ### Request Example ```typescript import { piq } from 'piqit' import { fileMarkdown } from '@piqit/resolvers' import { z } from 'zod' // Create a resolver for markdown posts const posts = fileMarkdown({ base: 'content/posts', path: '{year}/{slug}.md', frontmatter: z.object({ title: z.string(), status: z.enum(['draft', 'published']), tags: z.array(z.string()).default([]), }), body: { html: true, headings: true } }) // Create and execute a query const results = await piq.from(posts) .scan({ year: '2024' }) .filter({ status: 'published' }) .select('params.slug', 'frontmatter.title', 'body.html') .exec() // Results are FLAT with field names from last path segment: // [{ slug: 'hello-world', title: 'Hello World', html: '
Content...
' }] ``` ``` -------------------------------- ### Quick Start: Querying Published Posts Source: https://github.com/djgrant/piq/blob/main/README.md Set up a resolver for markdown posts and query for published posts from a specific year, selecting specific fields. Results are flattened. ```typescript import { piq } from 'piqit' import { fileMarkdown } from '@piqit/resolvers' import { z } from 'zod' // Create a resolver for markdown posts const posts = fileMarkdown({ base: 'content/posts', path: '{year}/{slug}.md', frontmatter: z.object({ title: z.string(), status: z.enum(['draft', 'published']), tags: z.array(z.string()), }), body: { html: true, headings: true } }) // Query for published posts from 2024 const results = await piq.from(posts) .scan({ year: '2024' }) .filter({ status: 'published' }) .select('params.slug', 'frontmatter.title', 'body.html') .exec() // Results are FLAT - field names from the last path segment: // [{ slug: 'hello-world', title: 'Hello World', html: '...' }] ``` -------------------------------- ### Fluent API: Query Construction Source: https://github.com/djgrant/piq/blob/main/README.md Demonstrates the method chaining for constructing a query, starting with 'from' and progressing through 'scan', 'filter', 'select', and 'exec'. ```typescript piq.from(posts) .scan({ year: '2024' }) // enumerate by pattern (cheap) .filter({ status: 'published' }) // narrow by criteria (costs I/O) .select('params.slug', 'frontmatter.title') // what to read .exec() // execute ``` -------------------------------- ### scan() Method: Finding Items by Pattern Source: https://github.com/djgrant/piq/blob/main/README.md Examples of using the scan() method to find items by pattern, including finding all posts from a specific year, a specific post, or all posts. ```typescript // Find all posts from 2024 piq.from(posts).scan({ year: '2024' }) // Find a specific post piq.from(posts).scan({ year: '2024', slug: 'hello-world' }) // Find all posts (empty constraints) piq.from(posts).scan({}) ``` -------------------------------- ### Creating a QueryBuilder Source: https://github.com/djgrant/piq/blob/main/docs/reference/api.md Demonstrates how to initialize a QueryBuilder instance using `piq.from()` or the helper export `from()`. ```APIDOC ## Creating a QueryBuilder ### Description Initialize a `QueryBuilder` instance using a resolver function. This can be done via `piq.from(resolver)` or the equivalent helper export `from(resolver)`. ### Entry Points #### `piq.from(resolver)` Creates a `QueryBuilder` from a resolver. ```typescript const query = piq.from(postsResolver) ``` #### `from(resolver)` Equivalent helper export. ```typescript const query = from(postsResolver) ``` ``` -------------------------------- ### Get Single QueryBuilder with single() Source: https://github.com/djgrant/piq/blob/main/docs/reference/api.md Obtain a 'SingleQueryBuilder' instance using the 'single()' method, which is used for retrieving a single result. ```typescript const single = piq.from(postsResolver) .scan({ year: '2024', slug: 'hello-world' }) .select('params.slug') .single() ``` -------------------------------- ### Build and check @piqit/resolvers Source: https://github.com/djgrant/piq/blob/main/docs/packages/resolvers.md Commands to build and check the @piqit/resolvers package within a workspace environment using Bun. ```bash bun run --filter @piqit/resolvers build bun run --filter @piqit/resolvers check ``` -------------------------------- ### Build and Check piqit Workspace Source: https://github.com/djgrant/piq/blob/main/docs/packages/piqit.md Commands to build and check the piqit package within a workspace environment using bun. ```bash bun run --filter piqit build ``` ```bash bun run --filter piqit check ``` -------------------------------- ### filter() Method: Narrowing Results by Criteria Source: https://github.com/djgrant/piq/blob/main/README.md Example of using the filter() method to narrow down results based on document-level criteria, typically used after scan(). ```typescript // Find published posts from 2024 piq.from(posts) .scan({ year: '2024' }) .filter({ status: 'published' }) ``` -------------------------------- ### Build and Deploy Commands Source: https://github.com/djgrant/piq/blob/main/DEPLOY.md These bash commands are used to build the project's static assets and API worker, and to deploy the site to Cloudflare Pages. Development servers for VitePress and Astro can be run separately. ```bash # Build both sites and merge pok site build # Build and deploy to Cloudflare Pages pok deploy # Development (run separately) pok docs dev # VitePress dev server at localhost:5173 pok website dev # Astro dev server at localhost:4321 ``` -------------------------------- ### Flatten Select Output Source: https://github.com/djgrant/piq/blob/main/docs/guide/concepts.md Select paths are namespaced, but the output is flattened by the final segment. This example shows selecting 'params.slug' and 'frontmatter.title' into a flattened object. ```typescript .select('params.slug', 'frontmatter.title') // { slug, title } ``` -------------------------------- ### Create Cloudflare Pages Project Source: https://github.com/djgrant/piq/blob/main/DEPLOY.md This command initializes a new Cloudflare Pages project. You will need to configure the custom domain and DNS settings in the Cloudflare dashboard afterward. ```bash npx wrangler pages project create piq ``` -------------------------------- ### Retrieve Single Result with single() Source: https://github.com/djgrant/piq/blob/main/README.md Use single() to get the first result or undefined. For cases where exactly one result is expected, use execOrThrow() to throw an error if no results are found. ```typescript // Returns first result or undefined const post = await piq.from(posts) .scan({ year: '2024', slug: 'hello-world' }) .select('params.slug', 'frontmatter.title') .single() .exec() // Throws if no results const post = await piq.from(posts) .scan({ year: '2024', slug: 'hello-world' }) .select('params.slug') .single() .execOrThrow() ``` -------------------------------- ### Configure fileMarkdown resolver Source: https://github.com/djgrant/piq/blob/main/docs/packages/resolvers.md Set up the fileMarkdown resolver with a base directory, path pattern, frontmatter schema, and body processing options. Bun runtime is required. ```typescript const posts = fileMarkdown({ base: 'content/posts', path: '{year}/{slug}.md', frontmatter: z.object({ title: z.string(), status: z.enum(['draft', 'published']), }), body: { html: true, headings: true }, }) ``` -------------------------------- ### Parse Markdown Body Content Source: https://context7.com/djgrant/piq/llms.txt Parses markdown body to extract raw text, HTML, and headings. `extractHeadings` gets heading metadata, `markdownToHtml` converts markdown to HTML, and `slugify` creates URL-safe slugs. ```typescript import { parseMarkdownBody, extractHeadings, markdownToHtml, slugify, readMarkdownBody, readParsedBody } from '@piqit/resolvers' const content = `--- title: Test --- # Welcome This is **bold** and _italic_ text. ## Getting Started Some content here. ### Installation \`\`\`bash npm install piqit \`\`\` ` // Parse body with options const body = parseMarkdownBody(content, { raw: true, html: true, headings: true }) // { // raw: '# Welcome\n\nThis is **bold**...', // html: '
This is bold...
', // headings: [ // { depth: 1, text: 'Welcome', slug: 'welcome' }, // { depth: 2, text: 'Getting Started', slug: 'getting-started' }, // { depth: 3, text: 'Installation', slug: 'installation' } // ] // } // Extract just headings const headings = extractHeadings('# Title\n## Section\n### Subsection') // [{ depth: 1, text: 'Title', slug: 'title' }, ...] // Convert markdown to HTML const html = markdownToHtml('**bold** and _italic_') // 'bold and italic
' // Create URL-safe slug const slug = slugify('Hello World!') // 'hello-world' // Read body from file const rawBody = await readMarkdownBody('content/post.md') const parsedBody = await readParsedBody('content/post.md', { html: true }) ``` -------------------------------- ### Create Query from Resolver with piq.from Source: https://context7.com/djgrant/piq/llms.txt Use `piq.from()` to create a query builder instance from a resolver. This is the entry point for all queries and returns a chainable QueryBuilder. ```typescript import { piq } from 'piqit' import { fileMarkdown } from '@piqit/resolvers' import { z } from 'zod' // Create a resolver for markdown posts const posts = fileMarkdown({ base: 'content/posts', path: '{year}/{slug}.md', frontmatter: z.object({ title: z.string(), status: z.enum(['draft', 'published']), tags: z.array(z.string()).default([]), }), body: { html: true, headings: true } }) // Create and execute a query const results = await piq.from(posts) .scan({ year: '2024' }) .filter({ status: 'published' }) .select('params.slug', 'frontmatter.title', 'body.html') .exec() // Results are FLAT with field names from last path segment: // [{ slug: 'hello-world', title: 'Hello World', html: 'Content...
' }] ``` -------------------------------- ### Execute a basic piq query Source: https://github.com/djgrant/piq/blob/main/docs/guide/index.md Run a query against the configured posts resolver, scanning for a specific year, filtering by status, and selecting desired fields. Results are flattened with the last path segment as the key. ```typescript import { piq } from 'piqit' import { posts } from './posts-resolver' const results = await piq .from(posts) .scan({ year: '2024' }) .filter({ status: 'published' }) .select('params.slug', 'frontmatter.title', 'body.html') .exec() // [{ slug: 'hello-world', title: 'Hello World', html: '...' }
```
--------------------------------
### Select All Fields with Wildcards
Source: https://github.com/djgrant/piq/blob/main/README.md
Use the wildcard '*' to select all fields from a specific namespace. This is convenient for retrieving all parameters or frontmatter data at once.
```typescript
.select('params.*')
// Result: { slug: string; year: string } (all params fields)
.select('params.*', 'frontmatter.*')
// Result: { slug, year, title, status, tags, ... }
```
--------------------------------
### Query with Namespace Wildcards
Source: https://github.com/djgrant/piq/blob/main/docs/guide/recipes.md
Retrieves all fields from 'params' and 'frontmatter' namespaces for posts in a specific year. Prefer explicit fields for stable production contracts.
```typescript
const rows = await piq
.from(postsResolver)
.scan({ year: '2024' })
.select('params.*', 'frontmatter.*')
.exec()
```
--------------------------------
### Utility APIs
Source: https://github.com/djgrant/piq/blob/main/docs/packages/resolvers.md
Helper functions for various tasks including path pattern compilation, frontmatter parsing, and markdown processing.
```APIDOC
## Utility APIs
### Path Patterns
- `compilePattern(pattern)`: Compiles a path pattern string.
- `createParamsSchema(pattern)`: Creates a Zod schema for path parameters.
### Frontmatter
- `parseFrontmatter(content)`: Parses frontmatter from a string.
- `readFrontmatter(filePath)`: Reads and parses frontmatter from a file.
- `readFrontmatterWithOffset(filePath, offset)`: Reads and parses frontmatter with a specified offset.
### Markdown Processing
- `parseMarkdownBody(markdown)`: Parses markdown content.
- `extractHeadings(markdown)`: Extracts headings from markdown.
- `slugify(text)`: Converts text to a URL-friendly slug.
- `markdownToHtml(markdown)`: Converts markdown to HTML.
```
--------------------------------
### Configure Static Content Resolver
Source: https://github.com/djgrant/piq/blob/main/docs/reference/resolvers.md
Create a static-data resolver for edge runtimes using an array of data. This resolver filters parameters and frontmatter by equality and returns only selected namespaces.
```typescript
import { staticContent } from '@piqit/resolvers/edge'
const postsResolver = staticContent(postsArray)
```
--------------------------------
### Create Filesystem Markdown Resolver with fileMarkdown()
Source: https://context7.com/djgrant/piq/llms.txt
The fileMarkdown() function generates a resolver for querying markdown files from the filesystem. It supports path patterns with placeholders, frontmatter validation using StandardSchema-compatible libraries like Zod, and configurable body parsing options for raw markdown, HTML, and heading extraction.
```typescript
import { fileMarkdown } from '@piqit/resolvers'
import { z } from 'zod'
// Create resolver with all options
const posts = fileMarkdown({
// Base directory for finding files (absolute or relative to cwd)
base: 'content/posts',
// Path pattern with {param} placeholders - params available as params.*
path: '{year}/{slug}.md',
// Frontmatter schema using any StandardSchema-compatible library
frontmatter: z.object({
title: z.string(),
status: z.enum(['draft', 'published']),
tags: z.array(z.string()).default([]),
date: z.string(),
}),
// Body parsing options
body: {
raw: true, // Include raw markdown string
html: true, // Include HTML conversion
headings: true // Extract headings array with depth, text, slug
}
})
// Path pattern examples:
// '{year}/{slug}.md' -> matches 2024/hello-world.md
// '{category}/{year}/{slug}.md' -> matches tech/2024/my-post.md
// 'TASK-{num}-{title}.md' -> matches TASK-123-build-feature.md
// Query the resolver
const results = await piq.from(posts)
.scan({ year: '2024' })
.select('params.slug', 'frontmatter.title', 'body.headings')
.exec()
// [{ slug: 'hello-world', title: '...', headings: [{ depth: 1, text: '...', slug: '...' }] }]
```
--------------------------------
### Create staticContent resolver
Source: https://github.com/djgrant/piq/blob/main/docs/packages/resolvers.md
Instantiate a staticContent resolver with precompiled data. This is intended for edge or runtime-safe execution without filesystem dependencies.
```typescript
const postsResolver = staticContent(precompiledPosts)
```
--------------------------------
### Execute Query with exec()
Source: https://github.com/djgrant/piq/blob/main/README.md
Use exec() to retrieve all results from a query as an array. This is suitable for smaller result sets.
```typescript
const results = await piq.from(posts)
.scan({ year: '2024' })
.select('params.slug')
.exec()
// results: Array<{ slug: string }>
```
--------------------------------
### Query Detail Page Post
Source: https://github.com/djgrant/piq/blob/main/docs/guide/recipes.md
Retrieves a single post by year and slug, selecting its title, HTML body, and headings. Throws an error if the post is not found.
```typescript
const post = await piq
.from(postsResolver)
.scan({ year: '2024', slug: 'hello-world' })
.select('frontmatter.title', 'body.html', 'body.headings')
.single()
.execOrThrow()
```
--------------------------------
### Define a fileMarkdown resolver
Source: https://github.com/djgrant/piq/blob/main/docs/guide/index.md
Configure a resolver to read markdown files from a specified directory, extracting frontmatter and HTML body content. Ensure Zod is used for schema validation.
```typescript
import { fileMarkdown } from '@piqit/resolvers'
import { z } from 'zod'
export const posts = fileMarkdown({
base: 'content/posts',
path: '{year}/{slug}.md',
frontmatter: z.object({
title: z.string(),
status: z.enum(['draft', 'published']),
tags: z.array(z.string()).default([]),
}),
body: { html: true, headings: true },
})
```
--------------------------------
### The Select API
Source: https://github.com/djgrant/piq/blob/main/README.md
Defines how to select fields from query results using dotted-string paths.
```APIDOC
## The Select API
Select uses dotted-string paths that map to the resolver's namespaced output. Results are **flat**—the last segment of each path becomes the property name.
### Namespaces
For the `fileMarkdown` resolver:
- `params` — extracted from path patterns (free, from scan)
- `frontmatter` — YAML metadata (light I/O)
- `body` — parsed content (heavy I/O)
### Variadic Strings
The simplest form—list the fields you want:
```typescript
.select('params.slug', 'params.year', 'frontmatter.title', 'body.html')
// Result type inferred as:
// { slug: string; year: string; title: string; html: string }
```
The field name is the last segment of each path.
### Object Form (Aliasing)
When you need custom property names or have naming collisions:
```typescript
.select({
postSlug: 'params.slug',
postTitle: 'frontmatter.title',
content: 'body.html'
})
// Result: { postSlug: string; postTitle: string; content: string }
```
### Wildcards
Select all fields from a namespace:
```typescript
.select('params.*')
// Result: { slug: string; year: string } (all params fields)
.select('params.*', 'frontmatter.*')
// Result: { slug, year, title, status, tags, ... }
```
### Collision Detection
If two paths have the same final segment, TypeScript reports a compile-time error:
```typescript
// ERROR: 'title' appears in both paths
.select('params.title', 'frontmatter.title')
// FIX: use object form to alias
.select({ paramTitle: 'params.title', frontmatterTitle: 'frontmatter.title' })
```
This prevents runtime surprises where one field overwrites another.
```
--------------------------------
### Execute Query with exec()
Source: https://github.com/djgrant/piq/blob/main/docs/reference/api.md
Run the query and retrieve all rows using the 'exec()' method. This method throws an error if 'select()' has not been called.
```typescript
const rows = await piq.from(postsResolver)
.scan({ year: '2024' })
.select('params.slug')
.exec()
```
--------------------------------
### fileMarkdown Resolver
Source: https://github.com/djgrant/piq/blob/main/docs/reference/resolvers.md
Filesystem resolver for markdown documents with YAML frontmatter. It allows fetching and parsing markdown files based on a specified path structure and frontmatter schema.
```APIDOC
## fileMarkdown(options)
### Description
Filesystem resolver for markdown documents with YAML frontmatter.
### Method
`fileMarkdown` (Function)
### Parameters
#### Options
- **base** (string) - The base directory for resolving files.
- **path** (string) - The path pattern for files, supporting `{param}` placeholders. Example: `{year}/{slug}.md`.
- **frontmatter** (StandardSchema) - Zod schema for validating YAML frontmatter.
- **body** (object) - Optional configuration for body parsing.
- **raw** (boolean) - Whether to include the raw body content.
- **html** (boolean) - Whether to parse the body into HTML.
- **headings** (boolean) - Whether to extract headings from the body.
### Query Behavior
- `scan` maps to path parameters derived from the `path` pattern.
- `filter` checks frontmatter values by strict equality.
- Resolvers read only the data needed for selected namespaces.
- `params.*` does not require file content reads.
- `frontmatter.*` reads and parses frontmatter.
- `body.*` parses the body according to requested fields.
- For multi-param segments, matching is left-to-right, allowing trailing params to include additional separators.
### Runtime
This resolver uses Bun APIs (`Bun.Glob`, `Bun.file`) and is intended for Bun environments.
### Request Example
```typescript
import { fileMarkdown } from '@piqit/resolvers'
import { z } from 'zod'
const posts = fileMarkdown({
base: 'content/posts',
path: '{year}/{slug}.md',
frontmatter: z.object({
title: z.string(),
status: z.enum(['draft', 'published']),
tags: z.array(z.string()),
}),
body: { raw: true, html: true, headings: true },
})
```
```
--------------------------------
### Type Utilities for Path Validation
Source: https://context7.com/djgrant/piq/llms.txt
Leverage piqit's type utilities for compile-time validation of select paths, collision detection, and result type inference. These enhance TypeScript integration.
```typescript
import type {
SelectablePaths,
GetFieldName,
GetPathValue,
ExpandWildcard,
HasCollision,
Undot,
UndotWithAliases,
Resolver,
QuerySpec,
StandardSchema,
Infer
} from 'piqit'
// SelectablePaths extracts valid dot-paths from a result type
type Result = { params: { slug: string; year: string }; frontmatter: { title: string } }
type Paths = SelectablePaths