[@layout]: ../docs.layout.md

# Queries

A query fence renders a live list, table, or component from Sources or Blocks that match. Results respect the reader's access.

````md
```query
from: .
type: reference
sort:
  - by: meta.title
```
````

```query
from: .
type: reference
sort:
  - by: meta.title
```


---

## List two lessons

In a [Quickstart project](../start/first-site.guide.md), create
`paths/lessons/contrast.lesson.md`:

```md
---
level: beginner
---

# Contrast

Compare a dark heading with a pale one.

---

<!--
id: practice
type: exercise
-->

## Compare two headings

Choose the heading you can read more easily.
```

Create `paths/lessons/spacing.lesson.md`:

```md
---
level: beginner
---

# Spacing

Give a heading room above and below it.

[Review contrast](./contrast.lesson.md).
```

Create `paths/lessons/index.md`:

````md
# Design lessons

```query
from: .
type: lesson
sort:
  - by: meta.title
```
````

Run `bun run dev` and open `/lessons`. The list contains **Contrast**, then
**Spacing**, each linked to its lesson. Change Spacing's heading to “Breathing
room”: the live list updates and sorts it first. Restore the heading for the
examples below. Core's standard Plugins provide queries; no package is needed.

---

## Show matching fields in a table

Replace the query in `paths/lessons/index.md` with:

````md
```query
from: .
type: lesson
where:
  level: beginner
view:
  type: table
  columns: [meta.title, level]
```
````

The same two lessons appear with their title and `beginner` level. Change the
filter to `advanced` to get no results. Queries only return readable Sources;
a missing row may be an access boundary rather than an absent file.

---

## Contract

| Key         | Meaning                                                                          |
| ----------- | -------------------------------------------------------------------------------- |
| `query`     | `sources` (default), `blocks`, or a plugin query such as `plugin.name`.          |
| `from`      | Folder to search. `.` is this folder; `/` is the root; `~/` is the Actor home.   |
| `type`      | Source or Block type.                                                            |
| `where`     | Map of authored data fields to required values.                                  |
| `sort`      | List of `by` fields with optional `direction: asc` or `desc`. `meta.title` sorts by title. |
| `limit`     | Maximum number of results.                                                       |
| `has`       | Registered prop name, or a list, that results must declare.                      |
| `recursive` | `false` limits results to the `from` folder itself.                              |
| `links`     | `to:` or `from:` a Source path, `this.source`, or `this.block`.                  |
| `view`      | `list` (default) or `type: table` with `columns`.                                |
| `component` | Imported Markdown tag or authored Plugin tag that renders each result.               |


---

## Component results

Create `paths/lesson-cards.components.md`:

````md
<!-- componentName: lesson-entry -->

# Lesson entry

A compact link with the lesson's authored level.

```html
<article>
  <a href="{{ href }}">{{ title }}</a>
  <span>{{ level }}</span>
</article>
```
````

Replace `paths/lessons/index.md` with:

````md
[@cards]: ../lesson-cards.components.md

# Design lessons

```query
from: .
type: lesson
sort:
  - by: meta.title
component: lesson-entry
```
````

The two results now render as linked lesson titles with their `beginner` level.
The component and dataset are ordinary Sources you can inspect and edit. For a
wrapper with slotted query children, see the complete
[bookcase and shelf components](../../examples/bookcase/index.demo.md).

---

## Result component attributes


Use `component: book-card` for an imported Markdown component, or an installed
Plugin's authored tag such as `component: x-books-card`. Markdown result
components can contain Plugin components.

Each result supplies string attributes from its scalar data, top-level fields,
and metadata, in that order; later fields take precedence. For Source results,
`href` is the public route and `title` comes from metadata.

Source and Block results also supply `source`, an exact canonical reference
(including the fragment for a Block), and `label`, defaulting to the target's
title. These let the same component render an individual
[rich reference](./links.reference.md) or a query result. Relative target
images resolve beside their owning Source. Named Plugin query results keep
their own field meanings.

Query components prepare with each row's actual attributes, then render for the
current viewer. Static export runs the same query and components anonymously.
Actor-dependent results require a host that admits that Actor, including a local
Actor in a browser publication.

---

## Query Blocks and links

Add these Blocks to `paths/lessons/index.md` after the previous example:

````md
---

## Practice

```query
query: blocks
from: .
type: exercise
```

---

## Lessons that link to Contrast

```query
from: .
type: lesson
links:
  to: ./contrast.lesson.md
```
````

Practice links to **Compare two headings** inside Contrast; the link query lists
**Spacing**, whose body links to Contrast. `links.from` instead selects targets
linked from its anchor. Use `this.source` or `this.block` when the authoring Source
or Block is the anchor. Keep exact Block ids stable when querying links to them.

---

## View bindings

Use a named Markdown reference to read one Source through the current View:

```md
[profile]: ~/profile.profile.md

Hello, {{ $profile.display-name ?? "Guest" }}.

<x-source-read name="profile">
  <p>{{ $profile.display-name }}</p>
  <slot name="fallback"><p>Add your profile to introduce yourself.</p></slot>
</x-source-read>
```

`~/` resolves within the current Actor's Home. Relative references resolve beside
their declaring Source; a fragment selects a Block. An unavailable or unreadable
target uses the literal default or fallback slot. Only the selected slot renders
and performs reads. A reference that is never consumed performs no read.

Bindings work in Markdown prose, literate HTML templates, and scalar HTML or
component attributes. Shared compilation keeps the instruction; each admitted
View resolves its own values and live dependencies. Returned scalar values are
HTML-escaped. Missing fields and non-scalar values render empty. `??` supplies a
string, number, or boolean only when the value is null or absent; `false`, `0`, and
empty text are preserved. Code examples, comments, and scripts remain literal.
Bindings do not evaluate JavaScript, loop over arrays, or embed a Source body.

A typed query can be called directly:

```md
{{ $input.responses(source=this.source, response="reflection").values.idea ?? "No saved idea" }}
```

Named arguments accept scalar literals, `this.source`, and `this.block`. A query
binding is singular: no rows means unavailable, one row supplies the value, and
multiple rows produce an authoring diagnostic. Binding declarations cannot use
`limit` to hide ambiguity. Query fences remain the collection presentation form.

For reuse or an availability wrapper, put the same request in Source topmatter:

```yaml
reads:
  answer:
    query: input.responses
    input:
      source: this.source
      response: reflection
```

```md
{{ $answer.values.idea ?? "No saved idea" }}

<x-source-read name="answer">
  <p>{{ $answer.values.idea }}</p>
  <slot name="fallback"><p>Save your reflection first.</p></slot>
</x-source-read>
```

A Block inherits its Source's local `reads`. A Block entry replaces the whole
inherited definition; `false` disables it. Representation selectors follow the
represented Source or Block in layouts, query results, and rich references.
Projected slots retain their caller's scope. Undefined names, invalid query
inputs, duplicate declarations, and ambiguous results diagnose errors; they do
not silently become missing data.

---

## Repository-wide bindings

An installed Plugin can export a Source or query descriptor:

```ts
import { definePlugin } from "@pathmx/core"

export default definePlugin({
  id: "workshop",
  bindings: {
    profile: { source: "~/profile.profile.md" },
  },
})
```

This makes `$profile` available throughout the repository without local references.
Exported names are unique and reserved: local `reads` and Markdown references
cannot shadow them, and a binding cannot use an installed Plugin's id. Plugins
supply declarative Source/query definitions, not callbacks or another state store.
Unused exports perform no reads. Operation identifiers consistently use
`plugin.member` for Queries, Actions, and Events; component tags retain their
`x-plugin-member` HTML form.

---

## Notes

- `pmx lint` reports an invalid fence, an unknown component, or a `links` target that does not exist.
- `pmx query sources|blocks|plugin.name` runs the same query from the terminal; see the [CLI](../cli.reference.md).

## Related

- [Components](./components.reference.md) · [Blocks](./blocks.reference.md)
- [A bookcase from Markdown](../../examples/bookcase/index.demo.md) renders query results as book cards.


---

## Call a named Plugin query

Server code passes a Plugin query's arguments inside `input`. With the Path
Plugin installed and a readable `/course.path`, use:

```ts
const steps = await view.query("path.steps", {
  input: { path: "/course.path" },
})
```

Top-level request fields belong to Core's query envelope. Passing `{ path: ... }`
at the top level is rejected. The admitted View still controls what the query
can read; changing `input` does not grant another Actor's authority.
