Skip to content

Commit 8a1dab8

Browse files
committed
Website: hosting headers, one file for the coverage figures
- public/_headers caches content-hashed assets and DuckDB-WASM, now in a directory named for its version, as immutable; the engine revalidates. For hosts that read the file; GitHub Pages ignores it. - src/data/figures.json also holds the number of catalogued syntax forms (3,475, from coverage/syntax/baseline.json), which replaces the hard-coded 3,348. The llms.txt files are filled from it too. - npm run check:figures fails while any figure is still a placeholder.
1 parent 53ea383 commit 8a1dab8

11 files changed

Lines changed: 85 additions & 19 deletions

File tree

‎website/.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ dist/
44
public/playground/engine/
55
wasm/target/
66
public/playground/duckdb/
7+
public/playground/duckdb-version.js
78
public/playground/sqlquack.toml

‎website/README.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ that bundles DuckDB 2.0. Swap it for a stable 2.x release once one ships: change
2525
`package.json`, run `npm install`, and check that stage 4 of the playground reports the expected
2626
DuckDB version.
2727

28+
The headline coverage figures live in one file, `src/data/figures.json`: the syntax and
29+
translation percentages, their basis lines and the number of catalogued syntax forms. The homepage,
30+
`/docs/coverage/` and the `llms.txt` files all read it at build time. A value left as its own
31+
`{{NAME}}` token shows as a placeholder; `npm run build` warns about it and `npm run check:figures`
32+
fails until every figure is set, so run that before publishing.
33+
34+
`public/_headers` sets caching for hosts that read it, such as Cloudflare Pages and Netlify:
35+
content-hashed Astro assets and the version-named DuckDB-WASM directory are immutable for a year;
36+
the engine revalidates after five minutes. Both hosts serve Brotli on their own. GitHub Pages
37+
ignores the file and serves gzip with a 10-minute cache.
38+
2839
The logo uses the SQLQuack icon from `sqlstack-io/sqlstack` (`assets/icons/sqlquack.png`); the
2940
footer's SQLStack symbols are unchanged copies from the same repository.
3041

@@ -45,4 +56,5 @@ npm run build # static site in dist/, including the search index
4556
npm run preview # serve dist/
4657
python3 scripts/check_links.py dist
4758
npm run test:engine # the engine and its spans, after build_engine.sh
59+
npm run check:figures # fails while a coverage figure is still a placeholder
4860
```

‎website/package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"dev": "node scripts/prepare_playground.mjs && astro dev",
1010
"build": "node scripts/prepare_playground.mjs && astro build",
1111
"preview": "astro preview",
12-
"test:engine": "node --test tests/playground.test.mjs"
12+
"test:engine": "node --test tests/playground.test.mjs",
13+
"check:figures": "node scripts/check_figures.mjs"
1314
},
1415
"dependencies": {
1516
"@astrojs/starlight": "^0.42.4",

‎website/public/_headers‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Response headers for hosts that read a _headers file, such as Cloudflare Pages and Netlify. Both
2+
# compress with Brotli on their own, .wasm included, so no encoding is set here. GitHub Pages, which
3+
# the SQLClean and SQLBuild sites use, ignores this file: it serves gzip with a 10-minute cache.
4+
5+
# Content-hashed by Astro: a changed file gets a new name.
6+
/_astro/*
7+
Cache-Control: public, max-age=31536000, immutable
8+
9+
# DuckDB-WASM, in a directory named for its version: a new version gets a new path.
10+
/playground/duckdb/*
11+
Cache-Control: public, max-age=31536000, immutable
12+
13+
# The playground engine and pages keep their names across builds, so they revalidate.
14+
/playground/engine/*
15+
Cache-Control: public, max-age=300, must-revalidate
16+
17+
/fonts/*
18+
Cache-Control: public, max-age=2592000
19+
20+
/*
21+
X-Content-Type-Options: nosniff
22+
Referrer-Policy: strict-origin-when-cross-origin

‎website/public/index.html‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ <h3>{{TRANSLATION_PCT}} of Snowflake statements translate</h3>
275275
</div>
276276
<div class="item">
277277
<h3>{{SYNTAX_PCT}} of documented syntax verified</h3>
278-
<p>Out of 3,348 documented Snowflake syntax forms. {{SYNTAX_BASIS}}</p>
278+
<p>Out of {{SYNTAX_FORMS}} documented Snowflake syntax forms. {{SYNTAX_BASIS}}</p>
279279
</div>
280280
<div class="item">
281281
<h3>Rules checked against Snowflake</h3>

‎website/public/playground/playground.js‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ function engine(op, fields) {
2222
});
2323
}
2424

25-
// DuckDB-WASM, served from this site (scripts/prepare_playground.mjs copies it in at build time).
26-
const DUCKDB_WASM = new URL('./duckdb/', import.meta.url);
2725

2826
const SCHEMA = `{
2927
"ORDERS": [
@@ -177,6 +175,9 @@ let duckdb = null;
177175
let running = 0;
178176

179177
async function loadDuckDB() {
178+
// Served from this site, in a directory named for its version (scripts/prepare_playground.mjs).
179+
const { DUCKDB_DIR } = await import('./duckdb-version.js');
180+
const DUCKDB_WASM = new URL(DUCKDB_DIR, import.meta.url);
180181
const module = await import(new URL('duckdb-browser.mjs', DUCKDB_WASM).href);
181182
const worker = new Worker(new URL('duckdb-browser-base.worker.js', DUCKDB_WASM));
182183
const db = new module.AsyncDuckDB(new module.VoidLogger(), worker);

‎website/scripts/check_figures.mjs‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Fails while any headline figure in src/data/figures.json is still its {{NAME}} placeholder, or
2+
// while a built page in dist/ still shows one. Run before publishing: `npm run check:figures`.
3+
import { readFile, readdir } from 'node:fs/promises';
4+
import path from 'node:path';
5+
import { placeholders } from '../src/figures.mjs';
6+
7+
const problems = placeholders().map((name) => `src/data/figures.json: ${name} is unset`);
8+
async function scan(dir) {
9+
for (const entry of await readdir(dir, { withFileTypes: true }).catch(() => [])) {
10+
const full = path.join(dir, entry.name);
11+
if (entry.isDirectory()) await scan(full);
12+
else if (/\.(html|txt)$/.test(entry.name)) {
13+
const left = (await readFile(full, 'utf8')).match(/\{\{[A-Z\\_]+\}\}/g);
14+
if (left) problems.push(`${full}: ${[...new Set(left)].join(', ')}`);
15+
}
16+
}
17+
}
18+
await scan('dist');
19+
if (problems.length) {
20+
console.error(`placeholder figures remain:\n ${problems.join('\n ')}`);
21+
process.exit(1);
22+
}
23+
console.log('every figure is set');
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
// Prepares the playground's served files, ignored by Git and rebuilt by `npm run build`:
2-
// DuckDB-WASM from node_modules into public/playground/duckdb/, so the playground serves it from
3-
// the site rather than a CDN (its browser module bundled with its Apache Arrow import), and the
4-
// repository's policy file, which the page offers to load. Run from website/.
2+
// DuckDB-WASM from node_modules into public/playground/duckdb/<version>/, so the playground serves
3+
// it from the site rather than a CDN (its browser module bundled with its Apache Arrow import), with
4+
// duckdb-version.js naming that directory; and the repository's policy file, which the page offers
5+
// to load. The version in the path lets hosts cache DuckDB as immutable (public/_headers).
6+
// Run from website/.
57
import { build } from 'esbuild';
6-
import { copyFileSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
8+
import { copyFileSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
79
import { join } from 'node:path';
810

911
const root = 'node_modules/@duckdb/duckdb-wasm';
1012
const { version } = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
11-
const out = 'public/playground/duckdb';
13+
const out = `public/playground/duckdb/${version}`;
1214

13-
rmSync(out, { recursive: true, force: true });
15+
rmSync('public/playground/duckdb', { recursive: true, force: true });
1416
mkdirSync(out, { recursive: true });
1517
await build({
1618
entryPoints: [join(root, 'dist/duckdb-browser.mjs')],
@@ -23,5 +25,6 @@ await build({
2325
for (const file of ['duckdb-base.wasm', 'duckdb-browser-base.worker.js']) {
2426
copyFileSync(join(root, 'dist', file), join(out, file));
2527
}
28+
writeFileSync('public/playground/duckdb-version.js', `export const DUCKDB_DIR = './duckdb/${version}/';\n`);
2629
console.log(`duckdb-wasm ${version} vendored into ${out}`);
2730
copyFileSync('../sqlquack.toml', 'public/playground/sqlquack.toml');

‎website/src/content/docs/docs/coverage.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ None of them means every input is proven: a figure says what was checked, under
1111
| Measure | Figure | Basis |
1212
|---------|--------|-------|
1313
| Snowflake statements that translate | {figures.TRANSLATION_PCT} | {figures.TRANSLATION_BASIS} |
14-
| Documented Snowflake syntax forms verified | {figures.SYNTAX_PCT} of 3,348 | {figures.SYNTAX_BASIS} |
14+
| Documented Snowflake syntax forms verified | {figures.SYNTAX_PCT} of {figures.SYNTAX_FORMS} | {figures.SYNTAX_BASIS} |
1515

1616
## Translation
1717

@@ -23,7 +23,7 @@ against results recorded on Snowflake, not every possible input.
2323
## Syntax
2424

2525
The syntax figure is the share of documented Snowflake syntax forms that meet every obligation
26-
declared for them. The catalogue holds 3,348 forms, one per documented syntax block or variant.
26+
declared for them. The catalogue holds {figures.SYNTAX_FORMS} forms, one per documented syntax block or variant.
2727
A form counts as verified only when it has at least one required obligation, every required
2828
obligation is satisfied by a case recorded on Snowflake, and an approved review of its family is
2929
current. Anything missing, ambiguous or stale counts as not verified.

‎website/src/data/figures.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
2-
"_comment": "Headline coverage figures, used by the homepage and docs/coverage. A value left as its own {{NAME}} token renders as a visible placeholder, and the build warns about it.",
2+
"_comment": "Headline coverage figures: the only file to edit for them. The homepage, docs/coverage and llms.txt read it at build time. A value left as its own {{NAME}} token renders as a visible placeholder; `npm run build` warns, and `npm run check:figures` fails, while any is left.",
3+
"_sources": "SYNTAX_FORMS is the `forms` count in coverage/syntax/baseline.json. SYNTAX_PCT is the verified share of those forms, as `make syntax-coverage` reports it. TRANSLATION_PCT is the share of actual Snowflake statements that convert in full.",
34
"SYNTAX_PCT": "{{SYNTAX_PCT}}",
5+
"SYNTAX_FORMS": "3,475",
46
"SYNTAX_BASIS": "{{SYNTAX_BASIS}}",
57
"TRANSLATION_PCT": "{{TRANSLATION_PCT}}",
68
"TRANSLATION_BASIS": "{{TRANSLATION_BASIS}}"

0 commit comments

Comments
 (0)