> ## Documentation Index
> Fetch the complete documentation index at: https://xspec.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Requirement Markers and text() in TypeScript

> Understand how requirement markers record graph edges, how text() retrieves node content at runtime, the rules governing both, and how code locations are attributed.

xspec gives you two ways to create a living connection between your implementation code and your specification: **markers**, which record a `references` edge in the dependency graph, and the **`text()` function**, which retrieves a node's requirement text as a string at runtime and records an `embeds` edge. Both are valid TypeScript — no custom runtime or loader is required — and both are the primary mechanism by which xspec builds its queryable graph of coverage and impact.

## Markers

A marker is a bare node expression used as an expression statement — that is, appearing on its own line in statement position, not assigned, not passed anywhere:

```ts theme={null}
import AUTH, { text } from "../specs/AUTH.xspec"

export function login(email: string, password: string): boolean {
  AUTH.auth.login.valid          // marker: records a `references` edge
  return email.includes("@") && password.length > 0
}
```

At runtime, this is a harmless property read on a plain object — it has no side effects and requires no xspec tooling to execute. The dependency graph edge is recorded at build time by `xspec build`, not at runtime.

In the graph, the marker records a `references` edge from the **code location** of the enclosing function to the node `AUTH.auth.login.valid`. This edge participates in coverage analysis (which requirements are referenced by your code) and impact analysis (which code is affected when a requirement changes).

<Note>
  Placing a marker on the **root node** (e.g. `AUTH` alone as a statement) records a graph edge, but the root node never participates in coverage paths. Marker to root is valid but has no coverage effect.
</Note>

## The `text()` function

When you need the actual requirement text as a string at runtime — to display it, include it in a prompt, or embed it in output — use the `text()` function imported from the same spec module:

```ts theme={null}
const requirement = text(AUTH.auth.login.valid)
```

`text(node)` returns the node's full subtree text as a `string`. It also records an **`embeds` edge** from the calling code location to the node, indicating that this code site directly embeds the requirement content.

`text()` is the **only** way to obtain requirement text at runtime. You cannot read text off a node directly, and the string-path form that is available in MDX is not valid in TypeScript:

```ts theme={null}
// ❌ TypeScript build error — string form is MDX-only
text("auth.login")
```

Always pass a fully-qualified node expression as the argument.

## Rules for using spec bindings

xspec enforces a strict set of rules about how node bindings and the `text` binding may be used in TypeScript. These rules exist so that the static analyzer can reliably track all edges and so that rename and move refactors work correctly.

### Permitted uses

* **A node expression as a marker** — alone as an expression statement.
* **A node expression as the sole argument to `text()`** — where `text` is the binding imported from the same module.
* **Dot access** and **string-literal bracket access** to traverse the node tree.

### Invalid uses (build errors)

```ts theme={null}
// ❌ Optional chaining
AUTH.auth?.login.valid

// ❌ Parenthesised node
(AUTH.auth.login.valid)

// ❌ Non-null assertion
AUTH.auth.login.valid!

// ❌ Computed (non-literal) bracket index
AUTH.auth[segmentName]

// ❌ Assigning a node to a variable
const node = AUTH.auth.login.valid

// ❌ Destructuring
const { auth } = AUTH

// ❌ Storing in a data structure
const nodes = [AUTH.auth.login.valid]

// ❌ Passing to a function other than its own text()
track(AUTH.auth.login.valid)

// ❌ Re-exporting
export { AUTH }
```

<Warning>
  Any of the above patterns will be reported as a build error by `xspec build`. They are also structurally ambiguous for graph analysis, so xspec cannot infer the intended edges from them.
</Warning>

### Shadowing

If a local declaration shadows an imported spec binding, xspec treats the identifier as an ordinary local variable and does not record any graph edge:

```ts theme={null}
import AUTH from "../specs/AUTH.xspec"

function example() {
  const AUTH = getSomethingElse()  // shadows the import
  AUTH.auth.login.valid            // NOT a spec reference — no edge recorded
}
```

### Type-only references

Using a node in a type position — for example `typeof AUTH.auth.login` — is unrestricted. Type references record nothing in the graph and are not rewritten by rename or move operations.

## Module branding and aliasing `text`

Each spec module's node types are **branded to that module**. You cannot pass a node from one module to another module's `text` function — this is both a TypeScript type error and a runtime throw:

```ts theme={null}
import AUTH, { text as authText } from "../specs/AUTH.xspec"
import BILLING, { text as billingText } from "../specs/BILLING.xspec"

authText(AUTH.auth.login.valid)      // ✅ correct
billingText(AUTH.auth.login.valid)   // ❌ type error AND runtime throw
authText(BILLING.invoice.create)     // ❌ type error AND runtime throw
```

When your file imports multiple spec modules, alias each module's `text` export to avoid ambiguity and to make the pairing explicit, as shown above.

## Code locations and edge attribution

Every edge recorded in the graph is attributed to a **code location** — either a whole file or a named unit within a file.

A file-level location looks like:

```
src/auth.ts
```

A named-unit location appends a `#` and the unit's qualified name:

```
src/auth.ts#LoginService.validate
```

### What counts as a named unit

The following constructs create named units:

* Function and class **declarations**
* Class **members** with identifier names: methods, getters, setters, and function-valued properties
* `const`, `let`, or `var` **bindings** whose initializer is a function or class expression
* **Namespaces** — `namespace A.B` yields the unit name `A.B`
* **Default exports** — an anonymous default export uses the unit name `default`

A marker or `text()` call is attributed to the **innermost enclosing named unit**. If no named unit encloses the reference, it is attributed to the file as a whole.

### Disambiguation

When the same unit name appears more than once in a file (overloaded functions, repeated class names in different scopes), xspec disambiguates in document order:

```
src/auth.ts#validate       // first occurrence
src/auth.ts#validate@2     // second occurrence
src/auth.ts#validate@3     // third occurrence
```

This means renaming a unit in your source will update the attributed location across the graph — another reason to keep spec references inside properly named functions and classes rather than at the top level of a file.
