Skip to content

Releases: stamat/poops

v1.9.5

Choose a tag to compare

@stamat stamat released this 29 Jul 00:19

Added

  • A glob-matched index.* is named after its directory. This one is for
    building libraries of components. One directory per component is the obvious
    way to lay a library out — the accordion's markup, styles and script live
    together, and each one is called index. Point a glob at them and the naming
    falls apart, differently for each pipeline. Styles compiled every match to
    <out>/index.css, so the last directory to build won and the rest were
    overwritten in silence. Scripts fared better but not well: esbuild nests entry
    points from different directories under their common ancestor, so you got
    dist/accordion/index.js where you wanted dist/accordion.js.

    {
      "scripts": { "in": "src/elements/*/index.{js,mjs,cjs,jsx,ts,tsx}", "out": "dist/js/" },
      "styles":  { "in": "src/elements/*/index.{scss,sass,css}",         "out": "dist/css/" }
    }
    src/elements/accordion/index.scss  →  dist/css/accordion.css
    src/elements/accordion/index.ts    →  dist/js/accordion.js
    src/elements/tabs/index.scss       →  dist/css/tabs.css
    src/elements/tabs/index.ts         →  dist/js/tabs.js
    

    Two globs, and the whole library builds to a flat set of bundles named after
    the components — add a directory, get a bundle, no config change.

    The rename only applies to entries a glob matched. A literal
    "in": "src/index.js" still writes dist/index.js, and
    "in": "src/scss/index.scss" still writes dist/index.css — you named that
    entry point yourself, and moving it to src.js or scss.css because of a
    rule about globs would be a rename you never asked for. Same for an explicit
    out file path: that always wins.

    The name is placed relative to the glob's static prefix — everything
    before its first magic segment — which is what keeps the flat case flat
    without making same-named components collide. src/elements/*/index.scss
    has the prefix src/elements, shared by every match, so nothing is left to
    nest under. Widen the glob and the part it no longer pins down is kept:

    "src/*/accordion/index.scss"
    
    src/blocks/accordion/index.scss    →  dist/css/blocks/accordion.css
    src/elements/accordion/index.scss  →  dist/css/elements/accordion.css
    

    Two accordion directories, two stylesheets, no overwrite — and no config
    to keep in sync, because the prefix is read off the pattern rather than off
    whatever happened to match. Add or remove a component and the layout of the
    rest doesn't move.

  • Brace patterns count as globs. hasMagic doesn't treat braces as magic by
    default, so a pattern with alternates but no wildcard —
    src/elements/accordion/index.{scss,sass,css} — was read as a literal file
    path, and failed with Entry does not exist: naming a file that was never
    going to exist. It now resolves as the glob it obviously is, which is what
    makes "whichever extension this component happens to use" expressible for a
    single component and not only across a *.

    The watcher learned the same thing. A brace pattern in a copy, images or
    markup in is now matched as a glob when deciding whether a changed file
    belongs to that task, instead of falling through to a path-segment compare
    that could never match it.

Full Changelog: v1.9.4...v1.9.5

v1.9.4

Choose a tag to compare

@stamat stamat released this 28 Jul 20:45

Fixed

  • Watcher events are coalesced — a multi-file burst triggers one rebuild, not
    one per file.
    Chokidar fires one event per written file, but a single build
    rarely writes a single file. A styles entry with sourcemaps and minify on
    writes three — .css, .css.map, .min.css — and a post-compile exec
    step that rewrites the output makes it four. If those land in a directory
    another instance watches (a library's dist/ inside a docs site's copy
    source, the setup --quiet was added for), every one of those events ran
    the full branch: five Copied N paths passes and three or four style
    recompiles per save, all doing the same work on the same files.

    The copy and style branches now collect events over a trailing 300ms window
    — sized to outlive the 150ms awaitWriteFinish settle between files of one
    burst — and run once when the burst goes quiet. The window keeps the paths,
    so the per-file behavior survives: a css-only burst still hot-swaps each
    stylesheet in place, anything else still escalates to one full reload.

    Browser refreshes were already folded this way — reload() has debounced
    since the livereload server stopped fs-watching. Now the work feeding the
    refresh is folded too. One save, one compile, one copy, one refresh.

Full Changelog: v1.9.3...v1.9.4

v1.9.3

Choose a tag to compare

@stamat stamat released this 28 Jul 20:20

Added

  • --quiet / -q hides the banner. Running one Poops instance, the header
    and the address block are the useful part of startup. Running several at once
    — a library build in the repo root and its docs site under site/, each with
    its own poops.json — they stop being useful: three headers, three terminal
    bells, and two Local server blocks whose ports you already know, scrolling
    past before the first compile line lands.

    poops -q & poops -q -c site/poops.json

    What -q removes is exactly the startup furniture:

    💩 Poops — v1.9.3         ← header, and its terminal bell
    -----------------
    🏠 Local server: …        ← the address block
    🛜  Network     : …
    🔃 LiveReload  : …
    

    Everything else prints as before — [style] Compiled:, [markup] Compiled:,
    warnings, errors, the non-zero exit on a failed build. The flag is deliberately
    not a log level: in a parallel run the compile lines are the one thing you're
    watching, and the tags already tell you which stage spoke.

    It composes with the other flags, so it fits a CI build the same way it fits a
    split terminal:

    poops --build --quiet --base-url /blog

    Ports are still resolved and still auto-incremented when one is taken — -q
    only stops them being announced. If you need to see which port an instance
    landed on, drop the flag for that one instance and keep it on the rest.

Fixed

  • justMinified no longer throws ENOENT on watch rebuilds. The
    post-minify step always deleted the unminified output, but watch rebuilds
    hand the compiled code to the minifier in memory — the file was never on
    disk, and every rebuild of a justMinified entry printed an unlink
    ENOENT stack trace. Harmless but loud. The delete now only runs when the
    file actually exists.

Full Changelog: v1.9.2...v1.9.3

v1.9.2

Choose a tag to compare

@stamat stamat released this 28 Jul 19:11

Fixed

  • The dev server resolves extensionless URLs. GitHub Pages serves /a/b
    from a/b.html without touching the URL. The local server didn't, so a link
    written as /changelog/v1.9.1 worked in production and 404'd on
    localhost:4040 — the one place you'd have caught it. Both agree now:

    /a/b  →  a/b.html          200, URL stays /a/b
    /a/b  →  a/b/index.html    301 to /a/b/, then the index
    

    The directory redirect was already there; the file fallback is the new part,
    and it only fires when neither a file nor a directory matches. Relative
    assets on those pages need nothing special — an extensionless URL sits at
    the same depth as the file behind it, so ../css/styles.min.css resolves
    the same for /changelog/v1.9.1 and /changelog/v1.9.1.html.

  • 404.html loads its assets at any depth. The 404 page is the one file
    served from a path it doesn't live at: it sits at your site root but answers
    for /a/b/c/anything. Its relative asset paths — ./css/styles.min.css
    then resolved against /a/b/c/, so a miss at the root rendered fine and a
    miss two levels down rendered unstyled. The server now pins them:

    <head>
      <base href="/" />
      <meta charset="utf-8" />
    </head>

    Injected only when the page doesn't already declare its own <base>, and
    only in the response — your built 404.html is untouched on disk, which
    matters when you publish under a project path like /poops/.

  • serve.base: "/" no longer 404s the whole site. The server keeps every
    request inside its base directory by resolving the path and checking it
    still starts with that base. A base of / joins to <cwd>/ — with the
    trailing separator — so the check compared against <cwd>// and nothing
    ever matched. Every URL, including /, came back 404. The base is now
    normalized before anything is joined to it, so a trailing separator means
    what you'd expect. Traversal attempts are still rejected the same way.

Full Changelog: v1.9.1...v1.9.2

v1.9.1

Choose a tag to compare

@stamat stamat released this 27 Jul 14:15

Fixed

  • includePaths no longer breaks the markup glob. Top-level includePaths
    is a sass/esbuild load path, but it was also folded into the exclude list the
    markup compiler globs with — and that list fills a single extglob segment:

    !(node_modules|.git|.svn|.hg|_*)/**/*.+(md|html)
    

    Any entry with a separator in it made the whole pattern match nothing. A site
    that legitimately needs "includePaths": ["../node_modules"] — node_modules
    at the repo root, docs built from a subdirectory — compiled zero pages, exited
    0, and said so only as Compiled: 0 file. Entries with a separator are now
    filtered out of the excludes; bare directory names still exclude as before.

Changed

  • The example docs consume poops-docs-theme
    instead of local copies.
    The docs layout, nav partial, stylesheet, and
    script are now an npm dependency — the first real user of the package
    templates added in v1.9.0. Front matter points at the
    package, and the theme's sources compile straight out of node_modules:

    {
      "scripts": [{ "in": "node_modules/poops-docs-theme/src/docs.ts", "out": "example/dist/js/docs.js" }],
      "styles":  [{ "in": "node_modules/poops-docs-theme/scss/docs.scss", "out": "example/dist/css/docs.css" }]
    }
    ---
    layout: poops-docs-theme/docs
    ---

    Four files left the repo and nothing about the docs changed on screen — which
    was the point.

Full Changelog: v1.9.0...v1.9.1

v1.9.0

Choose a tag to compare

@stamat stamat released this 27 Jul 12:02

Added

  • Package templates resolve from node_modules. A layout or partial can now
    live in an installed npm package and be referenced by package name, so a
    shared theme ships as a dependency instead of files copied into every project.
    Anything with a / is resolved from the consumer's node_modules; a bare
    name (no /) stays project-only, so the common path never touches the
    resolver.

    {% raw %}{% extends "my-theme/layout.html" %}
    {% block content %}
      <h1>{{ page.title }}</h1>
    {% endblock %}{% endraw %}

    Or from front matter, so the page carries no template syntax at all:

    ---
    layout: my-theme/layout
    ---
    • Nunjucks and Liquid both. The Nunjucks loader falls back to
      require.resolve for pkg/template.html; the Liquid engine adds every
      ancestor node_modules on the path to its include roots — so hoisted,
      scoped, and pnpm installs all resolve, and liquidjs's containment guard
      stays intact.
    • Project templates always win. Package roots are appended last, so a
      same-named template in your own project shadows the package one.
    • Bundled filters stay global. toc, breadcrumb, og, canonical, …
      are engine-global, so package templates use them with no extra wiring.

    A theme package must not restrict subpaths with exports (or must map its
    templates explicitly, e.g. "exports": { "./*": "./*" }), and should
    reference its own partials relatively — {% raw %}{% import "./nav.html" as nav %}{% endraw %},
    not the bare name. See Templating HTML → Templates from an npm package.

Full Changelog: v1.8.0...v1.9.0

v1.8.0

Choose a tag to compare

@stamat stamat released this 27 Jul 09:15

Added

  • Native node:http dev server. The local server no longer depends on
    connect + serve-static, and free-port selection no longer depends on
    portscanner. Three dependencies gone (five packages, still 0 audit
    vulnerabilities). The replacement handler keeps the behavior you rely on and
    adds a few things the old stack didn't:
    • Range request support — single-range 206 Partial Content (and 416
      for unsatisfiable ranges), so <video>/<audio> seeking works against the
      dev server.
    • directory → index.html, with a /dir/dir/ redirect,
    • path-traversal containment and null-byte rejection,
    • 405 for non-GET/HEAD methods,
    • your existing 404.html fallback, unchanged.
  • Unknown config-key warnings. A typo in a top-level poops.json key
    (stlyes, marckup, …) was silently ignored before. Startup now warns per
    unknown key so the most common config mistake surfaces immediately.
  • Declared Node floor. package.json now sets
    "engines": { "node": ">=20" }, and CI runs a Node 20/22/24 × ubuntu/windows
    matrix so the real floor is tested, not assumed.

Changed

  • Search keywords are Unicode-aware. extractKeywords no longer strips all
    non-Latin characters — non-English sites now get real keywords instead of
    empty ones (regex switched to \p{L}\p{N} with the u flag).

Fixed

  • Liquid {% raw %}{% image %}{% endraw %} no longer breaks on commas in
    values.
    A standard responsive sizes value —
    sizes: "(max-width: 600px) 100vw, 50vw" — was split mid-value by a naive
    split(','), producing broken attributes. Argument parsing is now
    quote-aware, so the documented responsive-image examples work in Liquid as
    they already did in Nunjucks.
  • Index artifacts write to subdirectory outputs. Search index, sitemap, nav
    and robots.txt now mkdir -p their target directory first, so
    "output": "meta/nav.json" no longer throws ENOENT.
  • Path matching respects segment boundaries. An in: "src" config no longer
    false-matches mysrc2/file.js or dist/src-maps/x. This previously could
    skip a rebuild — or, on the copy path, delete the wrong output file.
  • Windows dev fixes. CSS hot-swap paths are now posix-normalized (live CSS
    swap stopped degrading on Windows), and npm test is cross-platform (no more
    POSIX-only NODE_OPTIONS= prefix).
  • Better data-file diagnostics. A YAML/JSON parse error now logs the real
    message instead of a misleading "Data file not found", and two data files
    with the same basename warn before the global collides.
  • No reactor rebuild loop. The chokidar watcher now ignores the
    *.reactor-tmp-* / *.reactor-bundle-* scratch files, closing a potential
    self-sustaining rebuild loop.
  • Clean startup failures. Errors during startServer() and the
    livereload-only path now exit with a message instead of surfacing as an
    unhandled promise rejection (a hard crash on current Node).
  • Free-port selection can't return a busy port. When every candidate port in
    the range is occupied, the server now exits with a clear "no free port in
    range" error instead of crashing later with EADDRINUSE.

Full Changelog: v1.7.1...v1.8.0

v1.7.1

Choose a tag to compare

@stamat stamat released this 26 Jul 23:22
  • page.filePath for "Edit on GitHub" links. Every page now carries
    page.filePath — its source file's path relative to your project root, with
    posix separators (e.g. src/markup/docs/index.md). That is exactly the path
    GitHub's editor expects, so an edit link is one line in your layout:

    {% raw %}{% set repoUrl = site.repo or package.homepage %}
    {% if page.filePath and repoUrl %}
    <a href="{{ repoUrl }}/edit/{{ site.branch or 'main' }}/{{ page.filePath }}">✏️ Edit this page on GitHub</a>
    {% endif %}{% endraw %}

    Set repo and branch in your site data; they fall back to
    package.homepage and main. The field mirrors the filePath already on
    collection items, so both read the same way. You can't rebuild this path from
    page.url — that's the output URL (.html, and index.md collapses to a
    directory), so it never reverses to the .md source.

Full Changelog: v1.7.0...v1.7.1

v1.7.0

Choose a tag to compare

@stamat stamat released this 26 Jul 22:06

Tags & categories — taxonomies

Declare which front-matter fields become taxonomies on the collection, alongside paginate/sort:

---
title: Changelog
collection: true
paginate: 10
taxonomies:
  - name: tags      # front-matter field to group on
    path: tag       # URL segment (defaults to name); use "tag" for a singular URL
    paginate: 5     # per-term page size (defaults to the collection's paginate)
---

Shorthand: a bare string (taxonomies: [tags, category]) uses the field name as the URL segment and inherits the collection's paginate. Poops then writes a landing page per term — changelog/tag/feature/, blog/category/release/ — paginated, listed in the sitemap, kept out of the search index and nav.

Term pages render with the collection's own index template — no extra file. Branch on activeTerm to show a term view, and build tag links anywhere from collection.taxonomies:

{% raw %}{% if changelog.activeTerm %}
  <h1>Tagged {{ changelog.activeTerm | humanize }}</h1>
  {% for post in changelog.pageItems %}
    <p><a href="{{ relativePathPrefix }}{{ post.url }}">{{ post.title }}</a></p>
  {% endfor %}
  {% pagination changelog %}
{% endif %}{% endraw %}

Full guide: Tags & categories.

Array-aware groupby

The groupby filter now splits array-valued fields: a post with tags: [js, css] lands under both the js and css groups (previously the whole array was one key). This is what makes multi-tag taxonomies work, and it's just as useful in a template:

{% raw %}{% for group in blog.items | groupby("tags") %}
  <h2>{{ group.key | humanize }}</h2>
{% endfor %}{% endraw %}

New humanize filter

The inverse of slugify: "static-site""Static Site". Handy for turning a slug or a raw tag into a display label. Available in both Nunjucks and Liquid.

Distinct titles for paginated & term pages

Paginated pages no longer all share the landing page's <title> (and its og/jsonld metadata). Pages 2..N get a — Page N suffix, and each term page gets a Tag: Feature title — so search engines and social cards see a distinct title per page.

Localizable pagination labels

The — Page N suffix and the {% raw %}{% pagination %}{% endraw %} tag's Previous/Next/of wording default to English but localize site-wide under site.pagination:

site:
  pagination:
    title: "{title} — Seite {n}"   # {title}, {n}, {total} tokens
    prev: Zurück
    next: Weiter
    of: von

Automatic breadcrumbs for term pages

The breadcrumb and jsonld filters resolve term pages to a Home › Collection › Tag: Term trail automatically — skipping the non-page tag/category URL segment and labelling the last crumb with the taxonomy. Nothing to configure.

Everything above is additive — existing sites build unchanged.

Full Changelog: v1.6.0...v1.7.0

v1.6.0

Choose a tag to compare

@stamat stamat released this 26 Jul 16:13

One release, several ways to stop hand-authoring the boilerplate search engines, social platforms, feed readers and LLMs consume — all driven from front matter you already write.

SEO metadata — og and jsonld

Two new filters generate the metadata that search engines, generative engines (GEO) and social platforms read — straight from your front matter, no per-page boilerplate. Drop both in your layout <head>:

{% raw %}{{ page | og(site) }}
{{ page | jsonld(site) }}{% endraw %}

Open Graph & Twitter cards — og

The og filter emits Open Graph and Twitter-card <meta> tags so links to your pages unfurl into rich previews in chat apps and social feeds. og:type is article when the page has a date, otherwise website. It pulls title, description, url (made absolute via site.url), image, site_name and locale from front matter and site data, adds article:published_time / article:modified_time / article:author for posts, and sets twitter:card to summary_large_image when there's an image. Attribute values are escaped.

Set an og object in front matter to add or override any tag:

---
title: My post
date: 2026-01-01
image: static/cover.jpg
og:
  "og:image:alt": Cover illustration
---

JSON-LD structured data — jsonld

The jsonld filter turns front matter into a schema.org JSON-LD <script type="application/ld+json"> block — structured data search and generative engines read to understand your content. Liquid uses the colon syntax: {% raw %}{{ page | jsonld: site }}{% endraw %}.

The @type auto-detects: BlogPosting when the page has a date, otherwise WebPage. These front-matter fields are used when present:

Front matter JSON-LD
title name / headline
description (or site.description) description
url (made absolute via site.url) url
date datePublished
updated (or date) dateModified
author string or { name } (or site.author) author (Person)
image image
lang (or site.lang) inLanguage
wordcount wordCount
site.title publisher (Organization)

Article-only fields (headline, dates, author, wordCount) are added only for the BlogPosting case. Front-matter values are escaped so a stray </script> in a title can't break out of the tag.

The same lang that drives inLanguage also declares the document language — wire {% raw %}<html lang="{{ page.lang or site.lang or 'en' }}">{% endraw %} in your layout so the markup and the structured data agree. site.lang sets the default; a page's front-matter lang overrides it.

Set a jsonld object in front matter — its keys merge over (and override) the generated defaults, including @type. Everything from a HowTo to an FAQPage to a Product:

---
title: How to brew coffee
date: 2026-01-01
jsonld:
  "@type": HowTo
  totalTime: PT5M
---

This very page emits both — an article Open Graph set and a BlogPosting JSON-LD block. View source and look in the <head>.

Breadcrumbs

Breadcrumbs land in two forms, both derived from a page's URL depth — no nav tree wiring, no per-page boilerplate.

The SEO half is automatic

The jsonld filter above now auto-appends a schema.org BreadcrumbList block on any nested page (its url has at least one folder). It's a Google breadcrumb rich result with zero extra markup:

{% raw %}{{ page | jsonld(site) }}{% endraw %}

The trail is the site root, each ancestor folder (humanized — docs/static-siteStatic Site), then the page itself. Item URLs are absolute, so it needs site.url (same requirement as canonical). Sits right alongside the existing homepage WebSite block.

A visible trail — breadcrumb

For a breadcrumb people can see and click, add the new breadcrumb filter in your body. Same crumbs, rendered as a {% raw %}<nav class="breadcrumb"><ol>{% endraw %}:

{% raw %}{{ page | breadcrumb(site, relativePathPrefix) }}{% endraw %}

Liquid uses the colon syntax: {% raw %}{{ page | breadcrumb: site, relativePathPrefix }}{% endraw %}.

Passing relativePathPrefix matters: the links resolve against the current page — localhost while you develop, your deployed path in production — instead of jumping to the absolute domain. It's the same convention the nav and header links already use. The last crumb is the current page, rendered as aria-current text rather than a link. Both the JSON-LD and the visible trail return nothing on the homepage or a single-crumb page. Style the .breadcrumb however you like.

Optional home crumb

The leading "Home" crumb is on by default. Drop it or rename it site-wide via site.breadcrumb, or per page in front matter — front matter wins:

# poops.json → markup.site
breadcrumb:
  home: false        # drop the leading "Home" crumb
  homeLabel: Start   # …or just rename it

With home: false, top-level pages fall to a single crumb and render nothing, while nested pages still show their folder trail — handy for a blog where you want Blog › Post, not Home › Blog › Post. Set breadcrumb: false on a page (or on site) to switch off both the visible trail and the JSON-LD entirely.

The docs pages and these changelog posts render a live trail — look just above the content, and view source for the BreadcrumbList in the <head>.

Auto RSS / Atom feeds from collections

Collections already know your posts, their dates and their descriptions. Now they can emit a subscription feed with a single config line — no hand-authored XML template to keep in sync.

Point it at a collection

{% raw %}{
  "markup": {
    "options": {
      "feed": { "collection": "changelog", "output": "changelog/feed.rss" }
    }
  }
}{% endraw %}

That writes changelog/feed.rss — the collection's posts newest-first by date, with the channel title, description, author and language taken from your site data. Item links, guids and the atom:link rel="self" are made absolute via site.url; each <description> uses the post's description, falling back to its auto-excerpt. Posts marked robots: noindex are left out, matching the sitemap.

Advertise it in your layout <head> so browsers and readers discover it:

{% raw %}<link rel="alternate" type="application/rss+xml" href="{{ site.url }}/changelog/feed.rss">{% endraw %}

Options

  • collection — the collection to feed from. Omit it to emit a feed for every collection.
  • output — a bare filename (default feed.xml) lands in the collection's own folder; a slashed path is written as-is.
  • type"rss" (default) or "atom".
  • limit — item cap, newest first (default 20).
  • title / description / author / lang — override the site defaults.
  • content — set true to embed each post's full article in the feed (see below).

Shorthand "feed": true (or a filename string) turns on RSS for every collection; an array of these objects generates several at once — say an RSS and an Atom for the same posts.

Full post content — content: true

By default items carry a <description> only. Set content: true and each post's whole article rides along — RSS <content:encoded>, Atom <content type="html"> — so readers show the full post without a round-trip to the site:

{% raw %}{ "feed": { "collection": "changelog", "output": "changelog/feed.rss", "content": true } }{% endraw %}

The HTML is the post's Markdown source rendered to article-body HTML — no layout, nav or footer chrome, just the content. Only .md/.markdown posts get it (a .njk/.liquid post has no clean body to extract and falls back to <description> alone). This very feed ships it — subscribe and read the whole changelog in your reader.

This changelog is a collection, so the feed you may already subscribe to at /changelog/feed.rss is now generated — the old hand-written template is gone.

An index for LLMs — llms.txt

The same page data that drives your sitemap now writes an llms.txt — a Markdown index of your pages that LLMs and generative engines read to understand a site. Point the option at a filename and it lands in your output dir:

{% raw %}{
  "markup": {
    "options": {
      "llms": { "output": "llms.txt", "full": true }
    }
  }
}{% endraw %}

You get an # H1 title, a > blockquote summary, then - [title](url): description links grouped by URL folder — the first folder becomes a ## section, a second nests as a ### subsection. Collection sections are ordered newest-first by date; site.url makes the links absolute. A string ("llms": "llms.txt") is the shorthand; the object form also takes title, description, sectionTitle and an intro path — a Markdown file dropped in verbatim for free-form context you author for LLMs.

The whole corpus — full: true

llms.txt is the map; full writes the territory. Set "full": true and Poops also emits a companion named after output with a -full suffix (llms.txtllms-full.txt) — or pass a filename to set it yourself — every page concatenated into one file an LLM can ingest whole. The file opens with a # Full Documentation Archive for {title} header, a one-line intro naming the site and a > blockquote of the description so a whole-file ingest starts with context, then each page becomes an # title (its own leading H1 if it has one) + URL: line + its body, joined by ---. Set fullIntro to a Markdown file path for your own preamble after that header — the full counterpar...

Read more