[object Object]

← back to build-pages

opensearch: /opensearch.xml descriptor + autodiscovery <link rel=search> in shell + 4 smoke

89a5046913336ddd8139eaa45205a04a883e0a21 · 2026-05-13 19:44:52 -0700 · SteveStudio2

Files touched

Diff

commit 89a5046913336ddd8139eaa45205a04a883e0a21
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 19:44:52 2026 -0700

    opensearch: /opensearch.xml descriptor + autodiscovery <link rel=search> in shell + 4 smoke
---
 server.js     | 20 ++++++++++++++++++++
 test/smoke.js |  8 ++++++++
 2 files changed, 28 insertions(+)

diff --git a/server.js b/server.js
index 4b8341e..9abdca5 100644
--- a/server.js
+++ b/server.js
@@ -189,6 +189,7 @@ function shell(title, body, headExtra = '', meta = {}) {
   <meta name="theme-color" content="#faf9f6" media="(prefers-color-scheme: light)">
   <meta name="theme-color" content="#0d0c0a" media="(prefers-color-scheme: dark)">
   <link rel="canonical" href="${esc(canonical)}">
+  <link rel="search" type="application/opensearchdescription+xml" title="build-pages" href="/opensearch.xml">
   <meta property="og:title" content="${esc(title)}">
   <meta property="og:description" content="${esc(desc)}">
   <meta property="og:type" content="website">
@@ -1209,6 +1210,25 @@ app.get('/api/projects/:slug', async (req, res, next) => {
   } catch (e) { next(e); }
 });
 
+// /opensearch.xml — OpenSearch 1.1 description so browsers (Chrome, Firefox,
+// Safari Tech Preview) can register projects.agentabrams.com as a custom
+// search engine. Triggered by autodiscovery <link rel="search"> in the shell.
+app.get('/opensearch.xml', (_req, res) => {
+  const base = 'https://projects.agentabrams.com';
+  const xml = `<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+  <ShortName>build-pages</ShortName>
+  <Description>Search every commit across Steve Abrams' projects.</Description>
+  <InputEncoding>UTF-8</InputEncoding>
+  <Image height="32" width="32" type="image/svg+xml">${base}/favicon.svg</Image>
+  <Url type="text/html" method="get" template="${base}/?q={searchTerms}"/>
+  <Url type="application/json" method="get" template="${base}/api/search?q={searchTerms}&amp;limit=50"/>
+</OpenSearchDescription>
+`;
+  res.setHeader('Cache-Control', 'public, max-age=86400');
+  res.type('application/opensearchdescription+xml').send(xml);
+});
+
 // /search — convention alias that 302s to home with ?q= prefilled. Lets folks
 // who guess "/search" land on the right surface.
 app.get('/search', (req, res) => {
diff --git a/test/smoke.js b/test/smoke.js
index a62bc8f..a385334 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -220,6 +220,14 @@ function check(name, cond, hint = '') {
   check('search: 302',             r.status === 302);
   check('search: redirects to /?q', /\/\?q=whisper/.test(r.headers['location'] || ''));
 
+  // 12c. OpenSearch — autodiscovery + descriptor
+  r = await fetch('/opensearch.xml');
+  check('opensearch: 200',         r.status === 200);
+  check('opensearch: descriptor',  /<OpenSearchDescription/.test(r.body));
+  check('opensearch: html url',    /template=".+\/\?q=\{searchTerms\}/.test(r.body));
+  r = await fetch('/');
+  check('home: opensearch autodisc', /rel="search" type="application\/opensearchdescription\+xml"/.test(r.body));
+
   // 13. branded 404 on unknown route
   r = await fetch('/xyz-no-such-route');
   check('404: status 404',         r.status === 404);

← eff2d00 static pages: Cache-Control max-age=3600 SWR=86400 on /about  ·  back to build-pages  ·  favicon: /favicon.svg + /favicon.ico routes serve real brand 9419dd0 →