Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/query-counts-harness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"emdash": patch
"@emdash-cms/cloudflare": patch
---

Adds opt-in query instrumentation for performance regression testing. Setting `EMDASH_QUERY_LOG=1` causes the Kysely log hook to emit `[emdash-query-log]`-prefixed NDJSON on stdout for every DB query executed inside a request, tagged with the route, method, and an `X-Perf-Phase` header value. Zero runtime overhead when the flag is unset — the log option is only attached to Kysely when enabled.

Also exposes the helpers at `emdash/database/instrumentation` so first-party adapters (e.g. `@emdash-cms/cloudflare`) can wire the same hook into their per-request Kysely instances.
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ jobs:
- run: pnpm run --filter emdash-demo --filter @emdash-cms/demo-cloudflare typecheck
- run: pnpm typecheck:templates

query-counts:
name: Query Counts
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run --filter emdash... build
- run: node scripts/query-counts.mjs --target sqlite
- run: node scripts/query-counts.mjs --target d1

lint:
name: Lint
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,5 @@ __screenshots__/
# Downloaded test data (fetched on demand in CI)
examples/wp-theme-unit-test/
.opencode

.perf-query-counts
34 changes: 34 additions & 0 deletions fixtures/perf-site/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import cloudflare from "@astrojs/cloudflare";
import node from "@astrojs/node";
import react from "@astrojs/react";
import { d1, r2 } from "@emdash-cms/cloudflare";
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";

const target = process.env.EMDASH_FIXTURE_TARGET ?? "sqlite";

const sqliteIntegration = emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
});

const d1Integration = emdash({
database: d1({ binding: "DB", session: "auto" }),
storage: r2({ binding: "MEDIA" }),
});

export default defineConfig({
output: "server",
adapter:
target === "d1"
? cloudflare()
: node({
mode: "standalone",
}),
integrations: [react(), target === "d1" ? d1Integration : sqliteIntegration],
devToolbar: { enabled: false },
});
39 changes: 39 additions & 0 deletions fixtures/perf-site/emdash-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Generated by EmDash on dev server start
// Do not edit manually

/// <reference types="emdash/locals" />

import type { ContentBylineCredit, PortableTextBlock } from "emdash";

export interface Page {
id: string;
slug: string | null;
status: string;
title: string;
content?: PortableTextBlock[];
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}

export interface Post {
id: string;
slug: string | null;
status: string;
title: string;
featured_image?: { id: string; src?: string; alt?: string; width?: number; height?: number };
content?: PortableTextBlock[];
excerpt?: string;
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}

declare module "emdash" {
interface EmDashCollections {
pages: Page;
posts: Post;
}
}
40 changes: 40 additions & 0 deletions fixtures/perf-site/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@emdash-cms/fixture-perf-site",
"version": "0.0.0",
"private": true,
"type": "module",
"description": "Fixture site for query-count perf snapshots. Runs under sqlite+node or d1+cloudflare based on EMDASH_FIXTURE_TARGET.",
"emdash": {
"seed": "seed/seed.json"
},
"scripts": {
"dev": "astro dev",
"build": "astro build",
"bootstrap": "emdash init && emdash seed",
"typecheck": "astro check"
},
"dependencies": {
"@astrojs/cloudflare": "catalog:",
"@astrojs/node": "catalog:",
"@astrojs/react": "catalog:",
"@emdash-cms/cloudflare": "workspace:*",
"astro": "catalog:",
"better-sqlite3": "catalog:",
"emdash": "workspace:*",
"kysely": "^0.27.0",
"react": "catalog:",
"react-dom": "catalog:"
},
"devDependencies": {
"@astrojs/check": "catalog:",
"@cloudflare/workers-types": "catalog:",
"wrangler": "catalog:"
},
"pnpm": {
"onlyBuiltDependencies": [
"better-sqlite3",
"esbuild",
"workerd"
]
}
}
Loading
Loading