diff --git a/.github/brand/logo-dark.svg b/.github/brand/logo-dark.svg
new file mode 100644
index 0000000..6ba2693
--- /dev/null
+++ b/.github/brand/logo-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/.github/brand/logo-light.svg b/.github/brand/logo-light.svg
new file mode 100644
index 0000000..6c3252a
--- /dev/null
+++ b/.github/brand/logo-light.svg
@@ -0,0 +1 @@
+restloom logo
\ No newline at end of file
diff --git a/.github/brand/social-preview.png b/.github/brand/social-preview.png
new file mode 100644
index 0000000..3b5f8cd
Binary files /dev/null and b/.github/brand/social-preview.png differ
diff --git a/README.md b/README.md
index fec6421..16ca206 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# restloom
+
+
+
+
+
+
A REST API generated from your database's catalog. Name the schemas or tables to serve, and restloom reads their columns, types, keys and relations, and answers queries like this one:
diff --git a/conformance/dialects-doc.test.mjs b/conformance/dialects-doc.test.mjs
new file mode 100644
index 0000000..8c449e5
--- /dev/null
+++ b/conformance/dialects-doc.test.mjs
@@ -0,0 +1,36 @@
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { readFileSync } from 'node:fs';
+import { postgres } from '../packages/postgres/dist/index.js';
+import { mysql } from '../packages/mysql/dist/index.js';
+import { mssql } from '../packages/mssql/dist/index.js';
+import { sqlite } from '../packages/sqlite/dist/index.js';
+
+// The table in docs/dialects.md is what readers compare databases by, on the
+// site too. It must say what each dialect's capabilities say.
+const DIALECTS = {
+ '@crudy/restloom-postgres': postgres({}),
+ '@crudy/restloom-mysql': mysql({}),
+ '@crudy/restloom-mssql': mssql({}),
+ '@crudy/restloom-sqlite': sqlite({ pragma() {} }),
+};
+
+const rows = readFileSync(new URL('../docs/dialects.md', import.meta.url), 'utf8')
+ .split('\n')
+ .filter((line) => line.startsWith('| `@crudy/restloom-'))
+ .map((line) => line.split('|').slice(1, -1).map((cell) => cell.trim()));
+
+test('docs/dialects.md lists every dialect', () => {
+ assert.deepEqual(rows.map((r) => r[0].replaceAll('`', '')).sort(), Object.keys(DIALECTS).sort());
+});
+
+for (const [pkg, dialect] of Object.entries(DIALECTS)) {
+ test(`docs/dialects.md says what ${pkg} can do`, () => {
+ const [, , , written, estimated, rowSecurity, procedures] = rows.find((r) => r[0] === `\`${pkg}\``);
+ const c = dialect.capabilities;
+ assert.equal(/RETURNING|OUTPUT/.test(written), c.returning, 'written keys');
+ assert.equal(estimated !== 'no', c.estimatedCount, 'estimated count');
+ assert.equal(rowSecurity !== 'no', c.rowSecurity, 'row security');
+ assert.equal(procedures !== 'none', c.procedures, 'procedures');
+ });
+}