Skip to content

Releases: dodopayments/dualmark

v0.10.0 — @dualmark/netlify + @dualmark/nuxt adapters, api-reference converter, pluggable tokenizer

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 18 Jun 12:18
a7ade1f

The AEO infrastructure your marketing site is missing. This release adds two new edge/framework adapters, a new OpenAPI-aware converter, and a pluggable token estimator across every adapter. All @dualmark/* packages are version-linked and ship at 0.10.0.

Highlights

New: Netlify adapter — @dualmark/netlify@0.10.0

Run Dualmark on Netlify Edge Functions (Deno runtime). Serves clean markdown twins to AI bots at the edge via content negotiation.

bun add @dualmark/netlify @dualmark/core

See examples/netlify-edge.

New: Nuxt integration — @dualmark/nuxt@0.10.0

First-class Nuxt support with a content-negotiation middleware, a pluggable tokenizer, compareOptions, request-param validation, and h3 v2 compatibility. Generates .md twins and llms.txt from your content.

bun add @dualmark/nuxt @dualmark/core @dualmark/converters

New: OpenAPI-aware api-reference converter — @dualmark/converters@0.10.0

A new api-reference converter with a fromOpenAPI helper turns your OpenAPI spec into clean, AI-readable markdown API reference pages. Available through the Astro, Next.js, and SvelteKit adapters.

New: Pluggable token estimator — @dualmark/core@0.10.0

Bring your own tokenizer for accurate X-Markdown-Tokens counts:

  • estimateTokens(text, { tokenizer }) accepts an inline override function
  • New exports from core: TokenEstimator type, setTokenEstimator, resetTokenEstimator
  • A tokenizer option is now wired into every adapter config: Astro, Cloudflare, Deno, Next.js, SvelteKit, Vercel
  • The Astro adapter also accepts module-path strings (e.g. "./src/aeo-tokenizer.ts") for tokenizers that close over external state like js-tiktoken

Changes by package

Package Version Type Change
@dualmark/netlify 0.10.0 minor New Netlify Edge Functions adapter
@dualmark/nuxt 0.10.0 minor New Nuxt integration
@dualmark/converters 0.10.0 minor New OpenAPI-aware api-reference converter (fromOpenAPI)
@dualmark/core 0.10.0 minor Pluggable token estimator (tokenizer option)
@dualmark/astro 0.10.0 minor api-reference converter + pluggable tokenizer (function or module-path)
@dualmark/nextjs 0.10.0 minor api-reference converter + pluggable tokenizer
@dualmark/sveltekit 0.10.0 minor api-reference converter + pluggable tokenizer
@dualmark/cloudflare 0.10.0 minor Pluggable tokenizer
@dualmark/deno 0.10.0 minor Pluggable tokenizer
@dualmark/vercel 0.10.0 minor Pluggable tokenizer
@dualmark/cli 0.10.0 patch Bump for @dualmark/core@0.10.0

Eight end-to-end adapters now ship: Astro, Next.js, SvelteKit, Cloudflare, Deno, Vercel, Netlify, Nuxt. Plus a new examples/sanity-astro showing Sanity CMS → dual-marked Astro pages.

Full Changelog: v0.9.0...v0.10.0

v0.9.0 — @dualmark/sveltekit + @dualmark/vercel adapters

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 04 Jun 14:18
663c10c

The AEO infrastructure your marketing site is missing. This release adds two new edge/framework adapters and improves Astro's config diagnostics.

Highlights

New: SvelteKit adapter — @dualmark/sveltekit@0.9.0

First-class SvelteKit support. Generates .md endpoints from your collections, static pages, and parameterized routes, emits /llms.txt, and ships a createDualmarkHandle() server hook that serves clean markdown to AI bots via content negotiation.

bun add @dualmark/sveltekit @dualmark/core @dualmark/converters

Verified at 125/125 conformance under vite dev. See examples/sveltekit-blog.

New: Vercel Edge adapter — @dualmark/vercel@0.9.0

Drop-in Vercel Edge Middleware that serves pre-built .md twins to AI bots at the edge — wraps any upstream handler with createAEOMiddleware(). Includes subrequest-loop protection, trailing-slash enforcement, Link rel="alternate" injection, redirects, and onAIRequest/onMiss lifecycle hooks.

bun add @dualmark/vercel @dualmark/core

Verified at 120/125 under next dev. See examples/vercel-edge-full.

Improved: Astro config diagnostics — @dualmark/astro@0.9.0

Config validation errors now include the offending file path (e.g. [astro.config.mjs] Dualmark config error: siteUrl is required), so you know exactly which file to fix.

Changes by package

Package Version Type Change
@dualmark/sveltekit 0.9.0 minor New SvelteKit adapter (#32)
@dualmark/vercel 0.9.0 minor New Vercel Edge adapter (closes #8)
@dualmark/astro 0.9.0 patch File paths in config validation errors (#50)

Six end-to-end adapters now ship: Astro, Next.js, SvelteKit, Cloudflare, Deno, Vercel.

Full Changelog: v0.8.0...v0.9.0

v0.8.0 — @dualmark/deno + status-page converter + CLI v1.0 JSON

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 30 May 07:11

Highlights

Three things land in this release:

  • New package: @dualmark/deno — a Deno Deploy edge adapter that wraps any Deno fetch handler. AI bots get the markdown twin at the edge; lifecycle hooks (analytics/telemetry) are scheduled on info.completed. examples/deno-deploy scores a perfect 125/125 under deno run.
  • New converter: status-page — a built-in converter for public uptime/status pages (component health + incidents). Drop converter: "status-page" into an Astro or Next.js collection and status pages get the same battle-tested markdown layout as the rest.
  • Stable dualmark verify --json@dualmark/cli now emits the public AEO Spec v1.0 JSON contract instead of the internal report shape. See the migration note below.

No breaking API changes for the framework adapters. Drop-in upgrade from 0.7.x. The whole @dualmark/* family moves to 0.8.0 together (linked versioning).

What's new

@dualmark/deno@0.8.0 (new)

  • Deno Deploy edge adapter (6a6bf8a) — wraps any upstream Deno fetch handler, serves markdown to known AI bots at the edge, and schedules lifecycle hooks on info.completed.
  • 23 tests pass. Integration docs included.

@dualmark/converters@0.8.0

  • New: statusPageConverter (dd0512c, closes #12) for uptime/status pages — public component health plus incident history, with the standard brand-footer + breadcrumb conventions.
  • Registered as the built-in status-page resolver in @dualmark/astro and @dualmark/nextjs — no config beyond converter: "status-page".
  • Separators in the status converter now render conditionally (b2d925e).

@dualmark/cli@0.8.0

  • verify --json now emits the AEO Spec v1.0 JSON contract (65ab17e) and enforces --json mutual exclusivity with --quiet/--color. Required-check failures still exit non-zero.

    Migration note: --json previously emitted the internal VerifyReport shape (mdUrl, maxScore, passed[], failed[], skippedNegotiation). It now emits the v1.0 public schema (url, markdownUrl, score, max, level, skippedNegotiation, durationMs, checks[]) with checks in canonical evaluation order. Update any CI parsing that read the old field names.

@dualmark/core@0.8.0, @dualmark/astro@0.8.0, @dualmark/nextjs@0.8.0, @dualmark/cloudflare@0.8.0

Version bumps to keep the linked @dualmark/* family in lockstep (and to register the status-page resolver in astro / nextjs). No API changes.

Upgrade

# Astro
bun add @dualmark/astro@^0.8.0

# Next.js
bun add @dualmark/nextjs@^0.8.0

# Cloudflare
bun add @dualmark/cloudflare@^0.8.0

# Deno (new!)
bun add @dualmark/deno@^0.8.0

# CLI
bun add -g @dualmark/cli@^0.8.0

To use the new status-page converter:

// astro.config.mjs
dualmark({
  siteUrl: "https://yourcompany.com",
  collections: {
    status: { converter: "status-page" }, // ← new
  },
});

Verified

  • ✅ All package tests pass across @dualmark/* (build 7/7, test 7/7, typecheck 7/7)
  • ✅ Conformance E2E: examples/deno-deploy 125/125, examples/astro-cloudflare-full 125/125, examples/nextjs-app-router 120/125

v0.7.0 — More AI bots + integration converter

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 20 May 19:05
e048a2c

Highlights

Two features land together in this release:

  • Broader AI bot coverage — the crawler registry now detects DeepSeek, Anthropic's new Claude-SearchBot and Claude-User, Meta's ExternalFetcher, and Perplexity-User. If any of these hit your site, they now get the markdown twin instead of HTML soup.
  • integrationConverter — a new built-in converter for marketplace-style "Connect X to Y" pages (vendor, categories, capabilities, optional setup, pricing, requirements). Drop converter: "integration" into an Astro or Next.js collection and integration pages get the same battle-tested markdown layout as blog posts.

No breaking changes. Drop-in upgrade from 0.6.x.

What's new

@dualmark/core@0.7.0

  • Extend the AI Agent Registry with 5 new bots (6155878):
    • DeepSeekBot (DeepSeek)
    • Claude-SearchBot (Anthropic — search-time fetcher)
    • Claude-User (Anthropic — claude.ai on-demand fetch)
    • Meta-ExternalFetcher (Meta — user-action fetch from Meta AI)
    • Perplexity-User (Perplexity — user-action fetch)
  • Tighten the Meta-ExternalFetcher UA pattern to avoid false positives (0377a0e) and classify its purpose as user-action rather than training (951f373) — matches Meta's published bot semantics.
  • AEO spec AI Agent Registry table is synced with the new entries.

@dualmark/converters@0.7.0

  • New: integrationConverter (dbd8ef4) for marketplace integration pages. Handles:
    • Vendor and integration metadata
    • Categories + capabilities
    • Optional setup steps, pricing, and requirements blocks
    • Same brand-footer + breadcrumb conventions as the other converters
  • Registered as the built-in integration resolver in both @dualmark/astro and @dualmark/nextjs — no config beyond converter: "integration".
  • Refactor: integration categories are now a structured field with a brandFooter test (42cc5f8).

@dualmark/astro@0.7.0, @dualmark/nextjs@0.7.0, @dualmark/cli@0.7.0, @dualmark/cloudflare@0.7.0

Version bumps to pick up the new core + converters (and the integration resolver registration in astro / nextjs). No API changes.

Upgrade

# Astro
bun add @dualmark/astro@^0.7.0

# Next.js
bun add @dualmark/nextjs@^0.7.0

# Cloudflare
bun add @dualmark/cloudflare@^0.7.0

# CLI
bun add -g @dualmark/cli@^0.7.0

To use the new integration converter:

// astro.config.mjs
dualmark({
  siteUrl: "https://yourcompany.com",
  collections: {
    integrations: { converter: "integration" },  // ← new
  },
});

Verified

  • ✅ All 313 tests pass across @dualmark/* packages
  • ✅ Typecheck clean
  • ✅ Conformance E2E: examples/astro-cloudflare-full still 125/125, examples/nextjs-app-router 120/125

Full changelogs

Full diff: v0.6.0...v0.7.0

v0.6.0 — Astro 6 security upgrade

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 13 May 09:32
05a9a79

Highlights

Security release. Bumps the `astro` peer dependency to `^6.1.10` to resolve two upstream advisories that were never backported to Astro 5:

If you ship `@dualmark/astro` on a public site, upgrade.

Breaking changes

  • `@dualmark/astro` now requires `astro@^6.1.10`. Astro 5 is no longer a supported peer. The integration uses only stable hooks (`astro:config:setup`, `injectRoute`, `addMiddleware`) and the Content Layer API — there are no source changes inside `@dualmark/astro` itself, but you'll need to run Astro's v5 → v6 migration guide on your own site.
  • `engines.node` is now `>=22.12.0` across every published `@dualmark/*` package, matching Astro 6's hard Node 22 requirement.

Upgrade

```bash

1. Make sure you're on Node 22+

node --version # → v22.x.x

2. Upgrade @dualmark/astro and astro together

bun add @dualmark/astro@^0.6.0 astro@^6.1.10

or: pnpm add / npm install / yarn add equivalents

3. Run Astro's official 5 → 6 codemod / migration

https://docs.astro.build/en/guides/upgrade-to/v6/

```

The other five `@dualmark/*` packages (`core`, `converters`, `cli`, `cloudflare`, `nextjs`) are unchanged and remain at `0.5.2` — no Node bump on their published metadata, no peer-dep changes. If you don't use `@dualmark/astro`, this release is a no-op for you.

Verified end-to-end

The conformance E2E suite (`.github/workflows/conformance.yml`) ran clean against Astro 6 on every push since the upgrade landed:

  • ✅ `examples/astro-blog` — boots under `astro dev`, `dualmark verify` passes
  • ✅ `examples/astro-cloudflare-full` — boots under `wrangler dev`, full 125/125 conformance
  • ✅ All 313 unit/integration tests across `@dualmark/*` pass on Astro 6.3.1 (resolved from `^6.1.10`)

Full changelogs

v0.5.2 — npm provenance + Astro catalog auto-discovery

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 12 May 11:50
a16b370

Highlights

Supply-chain hardening — every `@dualmark/*` tarball is now signed with npm provenance attestation. Each package on npmjs.com gets a verified "Provenance" badge tying the artifact to this exact GitHub Actions workflow run and commit SHA. Consumers can verify with:

```bash
npm audit signatures
```

No API changes inside any package — this is a release-pipeline upgrade.

Changes

`@dualmark/astro` 0.5.2

  • ✨ Added `seo`, `performance`, `optimization` keywords to enable auto-discovery by the Astro Integrations catalog. `@dualmark/astro` will appear under "Performance + SEO" on the next weekly catalog sync (no runtime change).

All `@dualmark/*` packages 0.5.2

  • 🔐 Release pipeline switched from `bun publish` to `bun pm pack` + `npm publish --provenance` (because bun 1.3.5 doesn't yet support `--provenance` — see oven-sh/bun#15601).
  • 📝 `CONTRIBUTING.md` updated with the new release flow + how to verify provenance.

Full changelogs

v0.5.1 — @dualmark/nextjs

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 10 May 03:45

@dualmark/nextjs@0.5.1

Patch Changes

  • fix(nextjs): withDualmark() rejecting typed NextConfig from next.config.ts (#26).

    The internal NextConfigShape constraint had an [key: string]: unknown index
    signature, which TypeScript treats as a structural demand on the input. Next.js's
    NextConfig is a closed interface with no top-level index signature, so any
    caller passing a typed next.config.ts hit:

    Type 'NextConfig' is not assignable to type 'NextConfigShape'.
      Index signature for type 'string' is missing in type 'NextConfig'.
    

    The constraint was unnecessary — the function only reads transpilePackages
    and spreads the remaining config, neither of which need an index signature.
    Removing it unblocks typed configs on Next 14, 15, and 16. Runtime behavior
    is unchanged.

  • docs(nextjs): Next.js 16 compatibility — package metadata, docs, and example (#27).

    • Drop the "Next.js 15" reference from the package description; the adapter
      works with the Next.js App Router on 14, 15, and 16.
    • Bump the next devDependency from ^15.0.0 to ^16.2.6 (test/build
      toolchain only — the peerDependencies range is unchanged).
    • Update README to document the Next.js 16 proxy.ts file convention,
      with a note that Next ≤15 should keep using middleware.ts (body is
      identical). No runtime behavior change.

    The reference example at examples/nextjs-app-router was migrated to
    Next.js 16 in the same change and still scores 120/125 under next dev.

Other packages

@dualmark/core, @dualmark/converters, @dualmark/astro, @dualmark/cloudflare, @dualmark/cli are unchanged at 0.5.0 (no changesets touched them this cycle).

Full Changelog: v0.5.0...v0.5.1

v0.5.0 — @dualmark/nextjs

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 07 May 11:03

New: `@dualmark/nextjs` — first-class Next.js 15 App Router adapter

Closes #4. Same one-line install as `@dualmark/astro`, with a small surface area:

  • `withDualmark(nextConfig, options)` — wraps `next.config.mjs`
  • `createDualmarkMiddleware(options)` — drop-in `middleware.ts`
  • `createDualmarkRouteHandler(options)` — catch-all markdown twin route handler with `generateStaticParams`
  • `createLlmsTxtHandler(options)` — `/llms.txt` route handler

The `collections` / `staticPages` / `parameterizedRoutes` config shape mirrors `@dualmark/astro` so users can copy their config across frameworks. All 12 built-in converters (`blog`, `case-study`, `changelog`, `compare`, `docs`, `feature`, `glossary`, `legal`, `pricing`, `pseo`, `tool`, `video`) work identically. Tree-shakeable, ESM + CJS, zero runtime deps beyond `@dualmark/core` + `@dualmark/converters`. 47 vitest tests cover config validation, middleware negotiation, route dispatch, and `generateStaticParams`.

Install

```bash
bun add @dualmark/nextjs @dualmark/core @dualmark/converters
```

Minimal setup

```ts
// middleware.ts
import { createDualmarkMiddleware } from "@dualmark/nextjs";

export default createDualmarkMiddleware({ siteUrl: "https://example.com\" });

export const config = {
matcher: [
{
source: "/((?!_next/|favicon.ico|md/).*)",
missing: [{ type: "header", key: "next-router-prefetch" }],
},
],
};
```

```ts
// app/md/[...path]/route.ts
import { createDualmarkRouteHandler } from "@dualmark/nextjs";
import { POSTS } from "@/lib/posts";

const handler = createDualmarkRouteHandler({
siteUrl: "https://example.com\",
collections: {
blog: { converter: "blog", getEntries: () => POSTS.map(toEntry) },
},
});

export const dynamic = "force-static";
export const GET = handler.GET;
export const generateStaticParams = handler.generateStaticParams;
```

That's it. Bot UAs get markdown, browsers get HTML with `Link rel="alternate"`, direct `.md` URLs serve markdown.

Migration from manual `@dualmark/core` setup

If you wired `@dualmark/core` into Next.js by hand before this release, the migration is mechanical:

Before (`@dualmark/core` only) After (`@dualmark/nextjs`)
Hand-rolled `middleware.ts` with `detectAIBot` + `negotiateFormat` + manual rewrite `createDualmarkMiddleware({ siteUrl })`
Hand-rolled `app/md/[...path]/route.ts` with `if`-chains `createDualmarkRouteHandler({ siteUrl, collections, staticPages, parameterizedRoutes })`
Hand-rolled `app/llms.txt/route.ts` calling `renderLlmsTxt` `createLlmsTxtHandler({ brandName, sections })`
Manual `transpilePackages: [...]` `withDualmark(nextConfig, options)`

The internal namespace, response headers, and conformance score are unchanged. Full guide: docs.dualmark.dev/docs/integrations/nextjs.

Reference example

`examples/nextjs-app-router` is now built on the new package — ~50 lines instead of ~120 hand-rolled, same 120/125 conformance score under both `next dev` and `next start`. The CI `nextjs-app-router` job verifies this on every PR.

Coordinated patch bumps

The linked `@dualmark/*` changeset group means every package gets a coordinated version bump:

  • `@dualmark/nextjs` 0.5.0 (new)
  • `@dualmark/core` 0.3.1 → 0.5.0
  • `@dualmark/converters` 0.3.1 → 0.5.0
  • `@dualmark/astro` 0.3.1 → 0.5.0
  • `@dualmark/cloudflare` 0.3.1 → 0.5.0
  • `@dualmark/cli` 0.3.1 → 0.5.0

No source-level changes to the other five packages — just version metadata bumps so internal `workspace:*` deps resolve cleanly when consumers install `@dualmark/nextjs`.

Verified

  • Full monorepo: 313 tests across 6 packages, build + test + typecheck green on CI
  • `examples/nextjs-app-router` E2E in CI: `next dev` + `dualmark verify` → 120/125
  • Local verification: `next dev` and `next start` both score 120/125
  • Docs site dogfood (`apps/docs`): every doc page ≥ 105/125

Full PR · Issue #4

v0.3.1 — Hotfix: workspace deps + landing polish

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 05 May 20:45
bd68f5e

v0.3.1 — Hotfix: workspace dep resolution + landing visual polish

Patch release fixing one critical packaging bug introduced upstream by bun publish and one set of landing-page visual inconsistencies.

Strongly recommended upgrade for anyone on 0.3.0 — fresh installs of 0.3.0 resolve mismatched workspace versions due to a bun publish lockfile-metadata bug.


Critical: stale @dualmark/core pin in 0.3.0 tarballs

@dualmark/cli@0.3.0, @dualmark/astro@0.3.0, @dualmark/cloudflare@0.3.0, and @dualmark/converters@0.3.0 were published with a stale @dualmark/core dependency pin of "0.2.1" instead of "0.3.0". Running bun add @dualmark/cli@0.3.0 in a fresh project resolves @dualmark/core@0.2.1 alongside it — a mixed-version install. APIs are byte-equivalent across 0.2.1 and 0.3.0 so it does not crash, but the dependency graph is incorrect.

Root cause (full RCA): bun publish reads workspace dependency versions from bun.lock metadata cached at the last install time, not from the current packages/*/package.json files. When changeset version bumps versions but does not regenerate the lockfile, bun publish rewrites workspace:* deps to the previous version. bun install --frozen-lockfile does NOT detect this drift. Tracked upstream as oven-sh/bun#20477 (PR #26797 still open as of bun 1.3.5).

Two-layer fix (defense in depth):

  1. The version-packages script now chains bun install --lockfile-only after changeset version, so the version PR commits a refreshed lockfile.
  2. release.yml runs the same command after checkout/setup as a self-healing safety net for tagged releases.

Verification: bun pm pack on packages/cli with the refreshed lockfile correctly emits "@dualmark/core": "0.3.1" in the packed package.json.


Landing-page visual polish

  • Hero: removed full-viewport border-b that produced a stray horizontal hairline below the terminal demo.
  • Page rails: promoted z-index from 0 to 10 so rails render above the hero BeamsBackground gradients (previously the bottom radial fade obscured rails inside the hero).
  • Navbar: scoped the bottom border to its inner max-w-7xl column so it terminates at the rails instead of running full-viewport.
  • Section dividers: shared <Section> component now renders a top hairline between the rails (via new divider prop, default true). Every landing-page section transition has a clean ┬ intersection at both rail crossings, giving the page consistent vertical rhythm.

No package source-code changes; landing fixes affect apps/docs/ only.


Commits in this release


Migration

  • From 0.3.0: bun update @dualmark/* (or your equivalent). No code changes required. Strongly recommended.
  • From 0.2.x: see the v0.3.0 changelog for the relicense + identity changes; this 0.3.1 release rolls those forward with the dep fix.

Verified

  • 266 tests pass across 5 packages
  • typecheck 10/10 green
  • All 5 packages publish at 0.3.1 with correctly resolved cross-package dependencies ("@dualmark/core": "0.3.1").

License

Apache 2.0. See LICENSE and NOTICE.

v0.3.0

Choose a tag to compare

@aagarwal1012 aagarwal1012 released this 05 May 20:11
76b65c3

v0.3.0 — Landing rewrite, Apache 2.0 relicense, /play polish

This is a meta-release. No package source-code changes ship in this version, but we're cutting a minor bump (rather than a patch) because the project's identity, license, and surface have changed substantively in this window. All 5 @dualmark/* packages move 0.2.1 → 0.3.0 in lockstep.

If you're already on 0.2.1 and don't care about the relicense announcement, you can skip this version with no functional impact. The published tarballs are functionally byte-equivalent to 0.2.1.

Highlights

License: MIT → Apache 2.0

  • Apache 2.0 includes an explicit patent grant that matters for infrastructure libraries touching standard-tracking work (content negotiation, AI bot UA detection, llms.txt).
  • Existing 0.2.x installs are unaffected. Downstream redistributors and forks should now reference Apache 2.0 + the new NOTICE file.

Landing page restructured for problem-first positioning

  • New tagline: "ChatGPT cites your competitor — that's an infrastructure problem."
  • 7-section narrative: Hero → PlaygroundTeaser → Transform → CaseStudy → ConformanceDemo → Adapters → CTA.
  • New Dodo Payments case study with a real customer proof point: 5× lift in AI agent traffic over 2 months.
  • New page rails — Vercel-style vertical hairlines framing the content column on every page.
  • Cut three weak sections (TrustStrip, Architecture diagram, standalone Converters page) and folded Converters into Adapters as an inline chip strip.
  • Promoted the playground teaser to second-position so visitors can score their own site immediately.
  • Reframed the conformance section from "Verify CLI" to "Catch regressions in CI" with a real GitHub Actions workflow snippet.

Playground (/play) layout aligned with the new landing

  • Page rails now render on /play.
  • Hero gradient + grid background contained within max-w-7xl overflow-hidden so they stop at rail edges instead of bleeding past.
  • Content column widened from max-w-5xl to max-w-7xl.
  • New ?url= query-param support — landing teaser deeplinks straight into a live score.

SEO/AEO surfaces aligned

Every search snippet, social card, and AI crawler artifact now carries the same problem-first hook as the landing:

  • <head> metadata.description and twitter.description
  • JSON-LD Organization.description
  • llms.txt brand description (the primary AI agent discovery artifact)
  • raw/index.md blockquote (markdown twin of the landing)
  • Fumadocs content/docs/index.mdx frontmatter

Documentation pass

  • README test-count claims corrected: 266 across 5 packages (was incorrectly 253). Breakdown: @dualmark/core 167, @dualmark/astro 35, @dualmark/converters 24, @dualmark/cloudflare 23, @dualmark/cli 17.
  • All npx @dualmark/cli invocations unified to bunx @dualmark/cli (matches the project's bun-first tooling).
  • Customer proof point injected into the Next.js and Cloudflare integration docs where conformance scores are discussed.
  • CONTRIBUTING.md release flow updated to match the actual release.yml (which uses bun publish per package, not changeset publish).

Migration

  • Code: none required.
  • License notices: redistributors should update LICENSE references from MIT to Apache 2.0 and include the NOTICE file.
  • CLI: prefer bunx @dualmark/cli verify <url> for consistency. npx @dualmark/cli verify <url> still works.

Verified

  • 266 tests pass across 5 packages
  • bun run typecheck 10/10
  • Build green; landing page zero horizontal overflow at 1440 / 768 / 390 px
  • Playground layout parity verified at all three widths

Atomic commit history (this release window)

76b65c3 chore: version packages (#2)
f1a0eb0 chore: changeset for 0.3.0 — landing rewrite, Apache 2.0, /play polish
1433a41 docs: fix stale claims, version drift, and unify CLI invocation on bunx
3eeead7 fix(seo): align metadata with problem-first positioning
621063c feat(play): align layout with new landing — rails, contained background, max-w-7xl
ba9603a refactor(landing): tighten narrative, add page rails
5d37806 feat(landing): add Dodo Payments case study and playground teaser
6f24c68 feat(play): support `?url=` query param for deeplinking
26def03 feat(landing): restructure for problem-first OSS positioning
deb9b20 chore(license): relicense from MIT to Apache 2.0

Full changelog: v0.2.1...v0.3.0