[object Object]

← back to Robet Site

round2 item (2): JSON-LD structured data (Person + ScheduleAction → booking URL) + validity test (9/9); CSP-exempt data block

19f4d34aae884dcafbbbacecaabac4245408ffbd · 2026-06-29 23:30:54 -0700 · Steve

Files touched

Diff

commit 19f4d34aae884dcafbbbacecaabac4245408ffbd
Author: Steve <steve@designerwallcoverings.com>
Date:   Mon Jun 29 23:30:54 2026 -0700

    round2 item (2): JSON-LD structured data (Person + ScheduleAction → booking URL) + validity test (9/9); CSP-exempt data block
---
 public/index.html  | 25 +++++++++++++++++++++++++
 tests/api.test.mjs | 10 ++++++++++
 2 files changed, 35 insertions(+)

diff --git a/public/index.html b/public/index.html
index 9c47736..6e37d60 100644
--- a/public/index.html
+++ b/public/index.html
@@ -25,6 +25,31 @@
   <meta name="twitter:description" content="Pick a time, tell me what you need, get a straight answer." />
   <meta name="twitter:image" content="https://robert.agentabrams.com/og.png" />
 
+  <!-- structured data (data block; not executed → exempt from CSP script-src) -->
+  <script type="application/ld+json">
+  {
+    "@context": "https://schema.org",
+    "@type": "Person",
+    "name": "Robert",
+    "url": "https://robert.agentabrams.com/",
+    "image": "https://robert.agentabrams.com/og.png",
+    "email": "info@designerwallcoverings.com",
+    "description": "Book a meeting with Robert. Pick a time, get a straight answer.",
+    "potentialAction": {
+      "@type": "ScheduleAction",
+      "name": "Book a meeting",
+      "target": {
+        "@type": "EntryPoint",
+        "urlTemplate": "https://venturacorridor.com/book?owner=info@designerwallcoverings.com",
+        "actionPlatform": [
+          "https://schema.org/DesktopWebPlatform",
+          "https://schema.org/MobileWebPlatform"
+        ]
+      }
+    }
+  }
+  </script>
+
   <link rel="stylesheet" href="/styles.css" />
 </head>
 <body>
diff --git a/tests/api.test.mjs b/tests/api.test.mjs
index 0064db3..3deb6aa 100644
--- a/tests/api.test.mjs
+++ b/tests/api.test.mjs
@@ -85,6 +85,16 @@ test('contact: honeypot + validation + rate limit', async () => {
   assert.ok(saw429, 'rate limit should trigger 429 on burst');
 });
 
+test('homepage embeds valid JSON-LD with a booking action', async () => {
+  const html = await (await fetch(`${BASE}/`)).text();
+  const m = html.match(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/);
+  assert.ok(m, 'ld+json block present');
+  const j = JSON.parse(m[1]); // throws if invalid → test fails
+  assert.equal(j['@type'], 'Person');
+  assert.equal(j.potentialAction['@type'], 'ScheduleAction');
+  assert.match(j.potentialAction.target.urlTemplate, /venturacorridor\.com\/book/);
+});
+
 test('malformed JSON body → clean JSON error, not HTML stack', async () => {
   const r = await fetch(`${BASE}/api/contact`, {
     method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{bad json',

← de5f5ab loop ledger: round2 item (3) done + deployed  ·  back to Robet Site  ·  loop ledger: round2 item (2) done + deployed 7a5ae6e →