Keep bunext sitemap alive while sitemap exports query the DB

Bun's SQL client (mariadb/mysql adapters, Bun 1.3.14) doesn't keep the
event loop alive for a query on an idle connection, so a second dynamic
route's sitemap export querying the DB made the command exit silently.
Hold the loop open with a timer during generation, and log what a stuck
run is waiting on.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-15 09:00:40 +01:00
co-authored by Claude Opus 5.5
parent 23a309005a
commit 40c17f903e
7 changed files with 153 additions and 37 deletions
+39 -13
View File
@@ -18,6 +18,14 @@ export const SitemapMaxBytesPerFile = 50 * 1024 * 1024;
export const SitemapDefaultChunkSize = 10_000;
const SitemapXmlHeader = `<?xml version="1.0" encoding="UTF-8"?>`;
const SitemapXmlns = `xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"`;
/**
* What sitemap generation is currently waiting on, so `bunext sitemap`
* can name it when a `sitemap` export or `additional_paths` never settles.
*/
let pending_sitemap_task;
export function grabPendingSitemapTask() {
return pending_sitemap_task;
}
/**
* # Generate a sitemap.xml string for all pages
* Dynamic routes are only included through a `sitemap` export in their
@@ -169,9 +177,19 @@ export async function grabSitemapEntries(params) {
: undefined),
});
}
const additional_paths = typeof params?.additional_paths == "function"
? await params.additional_paths()
: params?.additional_paths;
let additional_paths;
if (typeof params?.additional_paths == "function") {
pending_sitemap_task = "`sitemap.additional_paths` in the config";
try {
additional_paths = await params.additional_paths();
}
finally {
pending_sitemap_task = undefined;
}
}
else {
additional_paths = params?.additional_paths;
}
entries.push(...(additional_paths || []).map(toSitemapEntry));
const seen = new Set();
const final_entries = [];
@@ -204,18 +222,26 @@ async function grabPageSitemapPaths({ file_path, route, }) {
const final_server_path = server_ctx?.local_path
? path.join(ROOT_DIR, server_ctx.path)
: server_file_path;
// Bust the import cache in development so edits to the server file
// are picked up without a restart.
const server_module = await import(isDevelopment()
? `${final_server_path}?t=${Date.now()}`
: final_server_path);
if (typeof server_module.sitemap != "function")
return;
const relative_server_path = grabRelativePath(server_file_path);
try {
return await server_module.sitemap({ route });
pending_sitemap_task = `importing ${relative_server_path}`;
// Bust the import cache in development so edits to the server file
// are picked up without a restart.
const server_module = await import(isDevelopment()
? `${final_server_path}?t=${Date.now()}`
: final_server_path);
if (typeof server_module.sitemap != "function")
return;
pending_sitemap_task = `the \`sitemap\` export of ${relative_server_path}`;
try {
return await server_module.sitemap({ route });
}
catch (error) {
throw new Error(`\`sitemap\` export of ${relative_server_path} failed: ${error?.message || error}`);
}
}
catch (error) {
throw new Error(`\`sitemap\` export of ${grabRelativePath(server_file_path)} failed: ${error?.message || error}`);
finally {
pending_sitemap_task = undefined;
}
}
function toSitemapEntry(path) {