docs: re-index on build, and gate pages against the document

Two things the build was carrying quietly.

post-build has thrown MODULE_NOT_FOUND on every build since March, when Orama
was replaced by Hanzo Search and lib/orama was deleted — but it kept calling
`await import('./update-orama-index.ts')`, and the throw landed in a catch that
only logs. Green build, and docs.hanzo.ai's search index has not been rebuilt
since. Call the indexer the repo actually has, statically, so a missing one is
a type error rather than a runtime surprise. Indexing stays non-fatal: a search
outage must not stop a deploy — it just must not hide either.

check-endpoints now runs at the end of pre-build. Generated pages that name an
endpoint the document does not have fail the build; authored prose that does is
counted and listed, because reconciling it is an editorial call. Today: 546
mentions checked, generated pages clean, 153 in authored prose — most of them
products hanzoai/openapi has since dropped as authored-but-served-nowhere.

vitest's projects list gains apps/docs (not apps/*, since apps/platform/e2e is
Playwright's) so the SDK-naming tests that pin the four surfaces actually run in
CI instead of sitting in a directory the runner never visits.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-07-31 08:42:52 -07:00
co-authored by Hanzo Dev
parent aa589203eb
commit ab4ef1287e
4 changed files with 28 additions and 23 deletions
+9 -2
View File
@@ -3,11 +3,18 @@ import { updateSearchIndexes } from './update-hanzo-index';
env.loadEnvConfig(process.cwd());
// Push the freshly-built page set into Hanzo Search.
//
// This used to `await import('./update-orama-index.ts')`, which has thrown
// MODULE_NOT_FOUND on every build since Orama was replaced by Hanzo Search and
// lib/orama was deleted. The throw landed in the catch below, so the build went
// green and the index quietly stopped being updated. One indexer, imported
// statically — a missing one is now a type error, not a runtime surprise.
async function main() {
const { updateSearchIndexes } = await import('./update-orama-index.ts');
await Promise.all([updateSearchIndexes()]);
await updateSearchIndexes();
}
// Indexing is downstream of the export: a search outage must not stop a deploy.
await main().catch((e) => {
console.error('Failed to run post build script', e);
});
+16
View File
@@ -4,6 +4,7 @@ import { genFlowPages } from './gen-flow-pages';
import { syncCliCommands } from './sync-cli-commands';
import { syncProjectDocs } from './sync-project-docs';
import { sanitizeMdx } from './sanitize-mdx';
import { checkEndpoints } from './check-endpoints';
async function main() {
// The document first, then its projections: the reference (one page per
@@ -22,6 +23,21 @@ async function main() {
// docs (angle-bracket autolinks, mis-nested JSX wrappers) so every page
// compiles instead of falling back to the error boundary.
sanitizeMdx();
// Last: no page may claim an endpoint the document does not have. A generated
// page that does is a bug in a generator above and fails the build; authored
// prose that does is reported, because the fix there is someone's editorial
// call, not the build's.
const { checked, unknown } = checkEndpoints();
const generated = unknown.filter(([f]) => f.startsWith('openapi/') || f.startsWith('start/'));
if (generated.length) {
for (const [f, p] of generated.slice(0, 20)) console.error(` ${f}: ${p}`);
throw new Error(`${generated.length} generated pages name endpoints the document does not have`);
}
console.log(
`[endpoints] ${checked} mentions checked, generated pages clean, ` +
`${unknown.length} in authored prose to reconcile`,
);
}
await main().catch((e) => {
-20
View File
@@ -1,20 +0,0 @@
import { type OramaDocument, sync } from '@hanzo/docs-core/search/orama-cloud';
import fs from 'node:fs/promises';
import { DataSourceId, isAdmin, orama } from '../lib/orama/client.ts';
export async function updateSearchIndexes(): Promise<void> {
if (!isAdmin) {
console.log('no private API key for Orama found, skipping');
return;
}
const content = await fs.readFile('.next/server/app/static.json.body');
const records = JSON.parse(content.toString()) as OramaDocument[];
await sync(orama, {
index: DataSourceId,
documents: records,
});
console.log(`search updated: ${records.length} records`);
}
+3 -1
View File
@@ -2,6 +2,8 @@ import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
projects: ['packages/*'],
// `apps/docs` and not `apps/*`: apps/platform/e2e holds Playwright specs,
// which are not vitest's to run.
projects: ['packages/*', 'apps/docs'],
},
});