@rubytech/create-maxy-code 0.1.118 → 0.1.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +95 -4
- package/payload/platform/package-lock.json +141 -125
- package/payload/platform/plugins/aeo/PLUGIN.md +1 -1
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js +121 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js +129 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.js +171 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts +27 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js +274 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js +38 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts +48 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js +254 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts +25 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js +78 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts +18 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js +56 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts +37 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js +94 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts +12 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +34 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -1
- package/payload/platform/plugins/docs/references/aeo.md +2 -2
- package/payload/platform/plugins/docs/references/visitor-graph.md +82 -0
- package/payload/platform/plugins/memory/references/schema-base.md +44 -0
- package/payload/server/{chunk-FJCI6X3H.js → chunk-HCYM5FLU.js} +2 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/privacy.html +66 -0
- package/payload/server/public/v.js +191 -0
- package/payload/server/server.js +473 -85
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-level AEO (Answer Engine Optimisation) heuristics. Eight checks
|
|
3
|
+
* run against raw HTML; each returns a status and, when the check fails,
|
|
4
|
+
* one concrete rewrite suggestion the operator can apply.
|
|
5
|
+
*
|
|
6
|
+
* The HTML parser is a deliberate single-pass regex scanner: AEO audits
|
|
7
|
+
* run on pages the platform itself renders, so we know they are
|
|
8
|
+
* server-side-generated, well-formed enough for regex inspection, and
|
|
9
|
+
* not gated on script-injected content. Bringing in a full DOM parser
|
|
10
|
+
* (jsdom, cheerio) would dwarf the rest of the plugin and pull in
|
|
11
|
+
* Node-version-coupled native deps. If we later need to audit
|
|
12
|
+
* third-party arbitrary HTML, swap the parser then.
|
|
13
|
+
*/
|
|
14
|
+
const H1_RX = /<h1\b[^>]*>([\s\S]*?)<\/h1>/gi;
|
|
15
|
+
const JSONLD_RX = /<script[^>]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
16
|
+
const META_DESC_RX = /<meta\b[^>]*name=["']description["'][^>]*content=["']([^"']*)["']/i;
|
|
17
|
+
const META_DESC_RX_REV = /<meta\b[^>]*content=["']([^"']*)["'][^>]*name=["']description["']/i;
|
|
18
|
+
const CANONICAL_RX = /<link\b[^>]*rel=["']canonical["'][^>]*href=["']([^"']*)["']/i;
|
|
19
|
+
const OG_TITLE_RX = /<meta\b[^>]*property=["']og:title["']/i;
|
|
20
|
+
const OG_DESC_RX = /<meta\b[^>]*property=["']og:description["']/i;
|
|
21
|
+
const HEADING_RX = /<(h[1-6])\b[^>]*>([\s\S]*?)<\/\1>/gi;
|
|
22
|
+
const TAG_STRIP = /<[^>]+>/g;
|
|
23
|
+
function stripTags(html) {
|
|
24
|
+
return html.replace(TAG_STRIP, "").replace(/\s+/g, " ").trim();
|
|
25
|
+
}
|
|
26
|
+
function readMetaDescription(html) {
|
|
27
|
+
const m = html.match(META_DESC_RX) ?? html.match(META_DESC_RX_REV);
|
|
28
|
+
return m ? m[1] : null;
|
|
29
|
+
}
|
|
30
|
+
function checkH1(html) {
|
|
31
|
+
const matches = [...html.matchAll(H1_RX)];
|
|
32
|
+
if (matches.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
name: "h1-present",
|
|
35
|
+
status: "fail",
|
|
36
|
+
detail: "no <h1> found",
|
|
37
|
+
suggestion: "Add one <h1> at the top of the page naming the entity or question this page answers.",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (matches.length > 1) {
|
|
41
|
+
return {
|
|
42
|
+
name: "h1-present",
|
|
43
|
+
status: "warn",
|
|
44
|
+
detail: `${matches.length} <h1> elements (expected 1)`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
name: "h1-present",
|
|
49
|
+
status: "pass",
|
|
50
|
+
detail: `1 <h1>: "${stripTags(matches[0][1]).slice(0, 80)}"`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function checkJsonLd(html) {
|
|
54
|
+
const matches = [...html.matchAll(JSONLD_RX)];
|
|
55
|
+
if (matches.length === 0) {
|
|
56
|
+
return {
|
|
57
|
+
name: "jsonld-present",
|
|
58
|
+
status: "fail",
|
|
59
|
+
detail: "no JSON-LD <script> block",
|
|
60
|
+
suggestion: "Emit a schema.org JSON-LD block via aeo-emit-jsonld and inline it in <head>.",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
let valid = 0;
|
|
64
|
+
for (const m of matches) {
|
|
65
|
+
try {
|
|
66
|
+
JSON.parse(m[1]);
|
|
67
|
+
valid += 1;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// counted below
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (valid === 0) {
|
|
74
|
+
return {
|
|
75
|
+
name: "jsonld-present",
|
|
76
|
+
status: "fail",
|
|
77
|
+
detail: `${matches.length} JSON-LD block(s) but none parse`,
|
|
78
|
+
suggestion: "Fix the JSON-LD payload — JSON.parse failed on every block.",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (valid < matches.length) {
|
|
82
|
+
return {
|
|
83
|
+
name: "jsonld-present",
|
|
84
|
+
status: "warn",
|
|
85
|
+
detail: `${valid}/${matches.length} JSON-LD blocks parse`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
name: "jsonld-present",
|
|
90
|
+
status: "pass",
|
|
91
|
+
detail: `${valid} JSON-LD block(s)`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function checkStructuredAnswer(html) {
|
|
95
|
+
const h1Matches = [...html.matchAll(H1_RX)];
|
|
96
|
+
if (h1Matches.length === 0) {
|
|
97
|
+
return {
|
|
98
|
+
name: "structured-answer",
|
|
99
|
+
status: "fail",
|
|
100
|
+
detail: "no <h1> — cannot locate structured answer position",
|
|
101
|
+
suggestion: "Add an <h1> followed by a single ≤280-character paragraph that answers the page question directly.",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const h1 = h1Matches[0];
|
|
105
|
+
const after = html.slice((h1.index ?? 0) + h1[0].length);
|
|
106
|
+
const pMatch = after.match(/<p\b[^>]*>([\s\S]*?)<\/p>/i);
|
|
107
|
+
if (!pMatch) {
|
|
108
|
+
return {
|
|
109
|
+
name: "structured-answer",
|
|
110
|
+
status: "fail",
|
|
111
|
+
detail: "no <p> after <h1>",
|
|
112
|
+
suggestion: "Add one ≤280-character <p> immediately after the <h1> that states the answer in one sentence.",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const text = stripTags(pMatch[1]);
|
|
116
|
+
if (text.length === 0) {
|
|
117
|
+
return {
|
|
118
|
+
name: "structured-answer",
|
|
119
|
+
status: "fail",
|
|
120
|
+
detail: "first <p> after <h1> is empty",
|
|
121
|
+
suggestion: "Fill the first <p> after <h1> with a ≤280-character direct answer.",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (text.length > 280) {
|
|
125
|
+
return {
|
|
126
|
+
name: "structured-answer",
|
|
127
|
+
status: "warn",
|
|
128
|
+
detail: `first <p> after <h1> is ${text.length} chars (target ≤280)`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
name: "structured-answer",
|
|
133
|
+
status: "pass",
|
|
134
|
+
detail: `first <p> after <h1> is ${text.length} chars`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function checkFaqSection(html) {
|
|
138
|
+
const lower = html.toLowerCase();
|
|
139
|
+
const hasFaqJsonLd = /["']@type["']\s*:\s*["']faqpage["']/i.test(html);
|
|
140
|
+
const hasFaqText = /\bfrequently asked questions\b/i.test(lower) ||
|
|
141
|
+
/\bfaqs?\b/i.test(lower);
|
|
142
|
+
if (hasFaqJsonLd) {
|
|
143
|
+
return {
|
|
144
|
+
name: "faq-section",
|
|
145
|
+
status: "pass",
|
|
146
|
+
detail: "FAQPage JSON-LD present",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (hasFaqText) {
|
|
150
|
+
return {
|
|
151
|
+
name: "faq-section",
|
|
152
|
+
status: "warn",
|
|
153
|
+
detail: "FAQ text but no FAQPage JSON-LD",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
name: "faq-section",
|
|
158
|
+
status: "fail",
|
|
159
|
+
detail: "no FAQ section",
|
|
160
|
+
suggestion: "Add a FAQ section with FAQPage JSON-LD listing the 3–5 questions customers most often ask.",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function checkMetaDescription(html) {
|
|
164
|
+
const desc = readMetaDescription(html);
|
|
165
|
+
if (!desc) {
|
|
166
|
+
return {
|
|
167
|
+
name: "meta-description",
|
|
168
|
+
status: "fail",
|
|
169
|
+
detail: "no <meta name=\"description\">",
|
|
170
|
+
suggestion: "Add <meta name=\"description\"> with an 80–160 character summary of the page's answer.",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const len = desc.trim().length;
|
|
174
|
+
if (len < 80 || len > 160) {
|
|
175
|
+
return {
|
|
176
|
+
name: "meta-description",
|
|
177
|
+
status: "warn",
|
|
178
|
+
detail: `description is ${len} chars (target 80–160)`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
name: "meta-description",
|
|
183
|
+
status: "pass",
|
|
184
|
+
detail: `description is ${len} chars`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function checkCanonical(html) {
|
|
188
|
+
const m = html.match(CANONICAL_RX);
|
|
189
|
+
if (!m) {
|
|
190
|
+
return {
|
|
191
|
+
name: "canonical-url",
|
|
192
|
+
status: "fail",
|
|
193
|
+
detail: "no <link rel=\"canonical\">",
|
|
194
|
+
suggestion: "Add <link rel=\"canonical\" href=\"<page-url>\"> in <head>.",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
name: "canonical-url",
|
|
199
|
+
status: "pass",
|
|
200
|
+
detail: `canonical=${m[1]}`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function checkOgTags(html) {
|
|
204
|
+
const hasTitle = OG_TITLE_RX.test(html);
|
|
205
|
+
const hasDesc = OG_DESC_RX.test(html);
|
|
206
|
+
if (hasTitle && hasDesc) {
|
|
207
|
+
return {
|
|
208
|
+
name: "og-tags",
|
|
209
|
+
status: "pass",
|
|
210
|
+
detail: "og:title + og:description present",
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const missing = [];
|
|
214
|
+
if (!hasTitle)
|
|
215
|
+
missing.push("og:title");
|
|
216
|
+
if (!hasDesc)
|
|
217
|
+
missing.push("og:description");
|
|
218
|
+
return {
|
|
219
|
+
name: "og-tags",
|
|
220
|
+
status: "fail",
|
|
221
|
+
detail: `missing: ${missing.join(", ")}`,
|
|
222
|
+
suggestion: `Add <meta property="${missing.join('"> and <meta property="')}"> in <head>.`,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function checkHeadingHierarchy(html) {
|
|
226
|
+
const levels = [];
|
|
227
|
+
for (const m of html.matchAll(HEADING_RX)) {
|
|
228
|
+
levels.push(parseInt(m[1].slice(1), 10));
|
|
229
|
+
}
|
|
230
|
+
if (levels.length === 0) {
|
|
231
|
+
return {
|
|
232
|
+
name: "heading-hierarchy",
|
|
233
|
+
status: "fail",
|
|
234
|
+
detail: "no headings on the page",
|
|
235
|
+
suggestion: "Add a semantic heading structure starting with <h1> and stepping down by one level.",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
for (let i = 1; i < levels.length; i += 1) {
|
|
239
|
+
const prev = levels[i - 1];
|
|
240
|
+
const cur = levels[i];
|
|
241
|
+
if (cur > prev + 1) {
|
|
242
|
+
return {
|
|
243
|
+
name: "heading-hierarchy",
|
|
244
|
+
status: "fail",
|
|
245
|
+
detail: `heading skip: h${prev} → h${cur}`,
|
|
246
|
+
suggestion: `Replace the h${cur} after the h${prev} with an h${prev + 1} (or insert an intermediate heading).`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
name: "heading-hierarchy",
|
|
252
|
+
status: "pass",
|
|
253
|
+
detail: `${levels.length} headings, no skips`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const ALL_CHECKS = [
|
|
257
|
+
checkH1,
|
|
258
|
+
checkJsonLd,
|
|
259
|
+
checkStructuredAnswer,
|
|
260
|
+
checkFaqSection,
|
|
261
|
+
checkMetaDescription,
|
|
262
|
+
checkCanonical,
|
|
263
|
+
checkOgTags,
|
|
264
|
+
checkHeadingHierarchy,
|
|
265
|
+
];
|
|
266
|
+
export function auditHtml(html) {
|
|
267
|
+
const heuristics = ALL_CHECKS.map((fn) => fn(html));
|
|
268
|
+
const passes = heuristics.filter((r) => r.status === "pass").length;
|
|
269
|
+
const warns = heuristics.filter((r) => r.status === "warn").length;
|
|
270
|
+
const total = heuristics.length;
|
|
271
|
+
const score = Math.round((100 * (passes + 0.5 * warns)) / total);
|
|
272
|
+
return { score, heuristics };
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=audit-heuristics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-heuristics.js","sourceRoot":"","sources":["../../src/lib/audit-heuristics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neo4j.d.ts","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAqBtD,wBAAgB,SAAS,IAAI,MAAM,CAclC;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import neo4j from "neo4j-driver";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
let driver = null;
|
|
5
|
+
function readPassword() {
|
|
6
|
+
if (process.env.NEO4J_PASSWORD)
|
|
7
|
+
return process.env.NEO4J_PASSWORD;
|
|
8
|
+
const passwordFile = resolve(process.env.PLATFORM_ROOT ?? resolve(import.meta.dirname, "../../../.."), "config/.neo4j-password");
|
|
9
|
+
try {
|
|
10
|
+
return readFileSync(passwordFile, "utf-8").trim();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error(`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function getDriver() {
|
|
17
|
+
if (!driver) {
|
|
18
|
+
const uri = process.env.NEO4J_URI;
|
|
19
|
+
if (!uri) {
|
|
20
|
+
throw new Error("[aeo] NEO4J_URI unset — refusing to default to bolt://localhost:7687");
|
|
21
|
+
}
|
|
22
|
+
const user = process.env.NEO4J_USER ?? "neo4j";
|
|
23
|
+
const password = readPassword();
|
|
24
|
+
console.error(`[aeo] resolved neo4j_uri=${uri}`);
|
|
25
|
+
driver = neo4j.driver(uri, neo4j.auth.basic(user, password));
|
|
26
|
+
}
|
|
27
|
+
return driver;
|
|
28
|
+
}
|
|
29
|
+
export function getSession() {
|
|
30
|
+
return getDriver().session();
|
|
31
|
+
}
|
|
32
|
+
export async function closeDriver() {
|
|
33
|
+
if (driver) {
|
|
34
|
+
await driver.close();
|
|
35
|
+
driver = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=neo4j.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neo4j.js","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAO,KAA0B,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,SAAS,YAAY;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAClE,MAAM,YAAY,GAAG,OAAO,CAC1B,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EACxE,wBAAwB,CACzB,CAAC;IACF,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yCAAyC,YAAY,gCAAgC,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;QAC/C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,4BAA4B,GAAG,EAAE,CAAC,CAAC;QACjD,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps Neo4j node labels to schema.org types and builds JSON-LD payloads
|
|
3
|
+
* suitable for inline `<script type="application/ld+json">` blocks on
|
|
4
|
+
* rendered pages.
|
|
5
|
+
*
|
|
6
|
+
* Coverage matches Task 354 spec §1 — the seven page types that the
|
|
7
|
+
* platform's typed nodes cover natively. Labels outside this set fall
|
|
8
|
+
* through to a generic `schema:Thing` projection; callers should treat
|
|
9
|
+
* a generic result as a doc gap, not a success.
|
|
10
|
+
*
|
|
11
|
+
* The mapper is deliberately strict: every emitted JSON-LD object names
|
|
12
|
+
* `@context` and `@type` and carries `@id` when an `elementId` is
|
|
13
|
+
* available. Missing required properties for the chosen type produce a
|
|
14
|
+
* `MissingPropertyError`; callers surface this rather than emitting an
|
|
15
|
+
* invalid block.
|
|
16
|
+
*/
|
|
17
|
+
export type JsonLdValue = string | number | boolean | null | JsonLdObject | JsonLdValue[];
|
|
18
|
+
export interface JsonLdObject {
|
|
19
|
+
"@context"?: string;
|
|
20
|
+
"@type": string;
|
|
21
|
+
"@id"?: string;
|
|
22
|
+
[property: string]: JsonLdValue | undefined;
|
|
23
|
+
}
|
|
24
|
+
export type SupportedPageType = "Organization" | "LocalBusiness" | "Person" | "Service" | "Product" | "Article" | "FAQPage" | "RealEstateListing" | "Event";
|
|
25
|
+
/** Inputs accepted by the mapper. */
|
|
26
|
+
export interface EntityInput {
|
|
27
|
+
/** Source Neo4j label (or one of `SupportedPageType` directly). */
|
|
28
|
+
label: string;
|
|
29
|
+
/** Optional Neo4j elementId — emitted as `@id` when present. */
|
|
30
|
+
elementId?: string;
|
|
31
|
+
/** Flat property bag from the node. */
|
|
32
|
+
properties: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
export declare class MissingPropertyError extends Error {
|
|
35
|
+
readonly schemaType: SupportedPageType | "Thing";
|
|
36
|
+
readonly missing: string[];
|
|
37
|
+
constructor(schemaType: SupportedPageType | "Thing", missing: string[]);
|
|
38
|
+
}
|
|
39
|
+
export declare function resolveSchemaType(label: string): SupportedPageType | "Thing";
|
|
40
|
+
/**
|
|
41
|
+
* Build a JSON-LD object from a typed entity. Throws
|
|
42
|
+
* `MissingPropertyError` when the resolved schema.org type's required
|
|
43
|
+
* properties are not satisfied.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildJsonLd(input: EntityInput): JsonLdObject;
|
|
46
|
+
/** Render a JSON-LD object as a `<script>` block string. */
|
|
47
|
+
export declare function renderJsonLdScript(obj: JsonLdObject): string;
|
|
48
|
+
//# sourceMappingURL=schema-mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-mapping.d.ts","sourceRoot":"","sources":["../../src/lib/schema-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,YAAY,GACZ,WAAW,EAAE,CAAC;AAElB,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,MAAM,iBAAiB,GACzB,cAAc,GACd,eAAe,GACf,QAAQ,GACR,SAAS,GACT,SAAS,GACT,SAAS,GACT,SAAS,GACT,mBAAmB,GACnB,OAAO,CAAC;AAEZ,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,UAAU,EAAE,iBAAiB,GAAG,OAAO;aACvC,OAAO,EAAE,MAAM,EAAE;gBADjB,UAAU,EAAE,iBAAiB,GAAG,OAAO,EACvC,OAAO,EAAE,MAAM,EAAE;CAOpC;AA4BD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,GACZ,iBAAiB,GAAG,OAAO,CAE7B;AAyLD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CA0B5D;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps Neo4j node labels to schema.org types and builds JSON-LD payloads
|
|
3
|
+
* suitable for inline `<script type="application/ld+json">` blocks on
|
|
4
|
+
* rendered pages.
|
|
5
|
+
*
|
|
6
|
+
* Coverage matches Task 354 spec §1 — the seven page types that the
|
|
7
|
+
* platform's typed nodes cover natively. Labels outside this set fall
|
|
8
|
+
* through to a generic `schema:Thing` projection; callers should treat
|
|
9
|
+
* a generic result as a doc gap, not a success.
|
|
10
|
+
*
|
|
11
|
+
* The mapper is deliberately strict: every emitted JSON-LD object names
|
|
12
|
+
* `@context` and `@type` and carries `@id` when an `elementId` is
|
|
13
|
+
* available. Missing required properties for the chosen type produce a
|
|
14
|
+
* `MissingPropertyError`; callers surface this rather than emitting an
|
|
15
|
+
* invalid block.
|
|
16
|
+
*/
|
|
17
|
+
export class MissingPropertyError extends Error {
|
|
18
|
+
schemaType;
|
|
19
|
+
missing;
|
|
20
|
+
constructor(schemaType, missing) {
|
|
21
|
+
super(`JSON-LD ${schemaType} requires ${missing.join(", ")} but received none`);
|
|
22
|
+
this.schemaType = schemaType;
|
|
23
|
+
this.missing = missing;
|
|
24
|
+
this.name = "MissingPropertyError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Label-to-schema.org-type resolution.
|
|
29
|
+
*
|
|
30
|
+
* - `LocalBusiness` keeps its own schema type (it's a schema.org subtype
|
|
31
|
+
* of `Organization`, but is more specific and SEO-relevant when set).
|
|
32
|
+
* - `Article` covers the platform's narrative `CreativeWork` label when
|
|
33
|
+
* it has article-shaped fields (`headline` or `title`).
|
|
34
|
+
* - `FAQPage` is recognised by the `FAQPage` label OR a `Question`
|
|
35
|
+
* collection — the caller picks.
|
|
36
|
+
* - `RealEstateListing` is the schema.org subtype of `Product` used by
|
|
37
|
+
* estate-agent verticals.
|
|
38
|
+
*/
|
|
39
|
+
const LABEL_TO_SCHEMA_TYPE = {
|
|
40
|
+
Organization: "Organization",
|
|
41
|
+
LocalBusiness: "LocalBusiness",
|
|
42
|
+
Person: "Person",
|
|
43
|
+
Service: "Service",
|
|
44
|
+
Product: "Product",
|
|
45
|
+
CreativeWork: "Article",
|
|
46
|
+
Article: "Article",
|
|
47
|
+
FAQPage: "FAQPage",
|
|
48
|
+
RealEstateListing: "RealEstateListing",
|
|
49
|
+
Listing: "RealEstateListing",
|
|
50
|
+
Event: "Event",
|
|
51
|
+
};
|
|
52
|
+
export function resolveSchemaType(label) {
|
|
53
|
+
return LABEL_TO_SCHEMA_TYPE[label] ?? "Thing";
|
|
54
|
+
}
|
|
55
|
+
function readStr(v) {
|
|
56
|
+
if (typeof v !== "string")
|
|
57
|
+
return undefined;
|
|
58
|
+
const trimmed = v.trim();
|
|
59
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
60
|
+
}
|
|
61
|
+
function readNum(v) {
|
|
62
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
63
|
+
return v;
|
|
64
|
+
if (typeof v === "string" && v.trim().length > 0) {
|
|
65
|
+
const n = Number(v);
|
|
66
|
+
return Number.isFinite(n) ? n : undefined;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const TYPE_SPECS = {
|
|
71
|
+
Organization: {
|
|
72
|
+
required: ["name"],
|
|
73
|
+
rename: { website: "url", phone: "telephone" },
|
|
74
|
+
},
|
|
75
|
+
LocalBusiness: {
|
|
76
|
+
required: ["name"],
|
|
77
|
+
rename: { website: "url", phone: "telephone" },
|
|
78
|
+
enrich: (props, out) => {
|
|
79
|
+
const street = readStr(props.streetAddress);
|
|
80
|
+
const locality = readStr(props.addressLocality);
|
|
81
|
+
const postalCode = readStr(props.postalCode);
|
|
82
|
+
if (street || locality || postalCode) {
|
|
83
|
+
out.address = {
|
|
84
|
+
"@type": "PostalAddress",
|
|
85
|
+
...(street ? { streetAddress: street } : {}),
|
|
86
|
+
...(locality ? { addressLocality: locality } : {}),
|
|
87
|
+
...(postalCode ? { postalCode } : {}),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
Person: {
|
|
93
|
+
required: ["givenName", "familyName"],
|
|
94
|
+
rename: { phone: "telephone" },
|
|
95
|
+
enrich: (props, out) => {
|
|
96
|
+
const given = readStr(props.givenName);
|
|
97
|
+
const family = readStr(props.familyName);
|
|
98
|
+
if (given && family)
|
|
99
|
+
out.name = `${given} ${family}`;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
Service: {
|
|
103
|
+
required: ["name"],
|
|
104
|
+
enrich: (props, out) => {
|
|
105
|
+
const price = readNum(props.price);
|
|
106
|
+
const currency = readStr(props.priceCurrency);
|
|
107
|
+
if (price !== undefined) {
|
|
108
|
+
out.offers = {
|
|
109
|
+
"@type": "Offer",
|
|
110
|
+
price,
|
|
111
|
+
...(currency ? { priceCurrency: currency } : { priceCurrency: "GBP" }),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
Product: {
|
|
117
|
+
required: ["name"],
|
|
118
|
+
enrich: (props, out) => {
|
|
119
|
+
const price = readNum(props.price);
|
|
120
|
+
const currency = readStr(props.priceCurrency);
|
|
121
|
+
if (price !== undefined) {
|
|
122
|
+
out.offers = {
|
|
123
|
+
"@type": "Offer",
|
|
124
|
+
price,
|
|
125
|
+
...(currency ? { priceCurrency: currency } : { priceCurrency: "GBP" }),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
Article: {
|
|
131
|
+
required: ["title"],
|
|
132
|
+
rename: { title: "headline", abstract: "description", body: "articleBody" },
|
|
133
|
+
},
|
|
134
|
+
FAQPage: {
|
|
135
|
+
required: ["name"],
|
|
136
|
+
enrich: (props, out) => {
|
|
137
|
+
const questions = Array.isArray(props.questions)
|
|
138
|
+
? props.questions
|
|
139
|
+
: [];
|
|
140
|
+
const mainEntity = [];
|
|
141
|
+
for (const q of questions) {
|
|
142
|
+
const name = readStr(q.name);
|
|
143
|
+
const answer = readStr(q.acceptedAnswer);
|
|
144
|
+
if (!name || !answer)
|
|
145
|
+
continue;
|
|
146
|
+
mainEntity.push({
|
|
147
|
+
"@type": "Question",
|
|
148
|
+
name,
|
|
149
|
+
acceptedAnswer: { "@type": "Answer", text: answer },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (mainEntity.length > 0)
|
|
153
|
+
out.mainEntity = mainEntity;
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
RealEstateListing: {
|
|
157
|
+
required: ["name"],
|
|
158
|
+
rename: { title: "name", abstract: "description" },
|
|
159
|
+
enrich: (props, out) => {
|
|
160
|
+
const price = readNum(props.price);
|
|
161
|
+
const currency = readStr(props.priceCurrency);
|
|
162
|
+
if (price !== undefined) {
|
|
163
|
+
out.offers = {
|
|
164
|
+
"@type": "Offer",
|
|
165
|
+
price,
|
|
166
|
+
...(currency ? { priceCurrency: currency } : { priceCurrency: "GBP" }),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const street = readStr(props.streetAddress);
|
|
170
|
+
const locality = readStr(props.addressLocality);
|
|
171
|
+
const postalCode = readStr(props.postalCode);
|
|
172
|
+
if (street || locality || postalCode) {
|
|
173
|
+
out.address = {
|
|
174
|
+
"@type": "PostalAddress",
|
|
175
|
+
...(street ? { streetAddress: street } : {}),
|
|
176
|
+
...(locality ? { addressLocality: locality } : {}),
|
|
177
|
+
...(postalCode ? { postalCode } : {}),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
Event: {
|
|
183
|
+
required: ["name", "startDate"],
|
|
184
|
+
rename: {},
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
/** Plain properties always copied verbatim when present. */
|
|
188
|
+
const COMMON_KEYS = [
|
|
189
|
+
"name",
|
|
190
|
+
"description",
|
|
191
|
+
"url",
|
|
192
|
+
"image",
|
|
193
|
+
"email",
|
|
194
|
+
"telephone",
|
|
195
|
+
"givenName",
|
|
196
|
+
"familyName",
|
|
197
|
+
"jobTitle",
|
|
198
|
+
"headline",
|
|
199
|
+
"articleBody",
|
|
200
|
+
"datePublished",
|
|
201
|
+
"dateModified",
|
|
202
|
+
"startDate",
|
|
203
|
+
"endDate",
|
|
204
|
+
"location",
|
|
205
|
+
];
|
|
206
|
+
function applyCommon(props, out, rename = {}) {
|
|
207
|
+
const renameInverse = {};
|
|
208
|
+
for (const [from, to] of Object.entries(rename))
|
|
209
|
+
renameInverse[from] = to;
|
|
210
|
+
for (const key of COMMON_KEYS) {
|
|
211
|
+
const v = props[key];
|
|
212
|
+
if (typeof v === "string" || typeof v === "number") {
|
|
213
|
+
out[key] = v;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
for (const [from, to] of Object.entries(renameInverse)) {
|
|
217
|
+
const v = props[from];
|
|
218
|
+
if (typeof v === "string" || typeof v === "number") {
|
|
219
|
+
out[to] = v;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Build a JSON-LD object from a typed entity. Throws
|
|
225
|
+
* `MissingPropertyError` when the resolved schema.org type's required
|
|
226
|
+
* properties are not satisfied.
|
|
227
|
+
*/
|
|
228
|
+
export function buildJsonLd(input) {
|
|
229
|
+
const schemaType = resolveSchemaType(input.label);
|
|
230
|
+
const out = {
|
|
231
|
+
"@context": "https://schema.org",
|
|
232
|
+
"@type": schemaType,
|
|
233
|
+
};
|
|
234
|
+
if (input.elementId)
|
|
235
|
+
out["@id"] = input.elementId;
|
|
236
|
+
if (schemaType === "Thing") {
|
|
237
|
+
applyCommon(input.properties, out);
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
const spec = TYPE_SPECS[schemaType];
|
|
241
|
+
applyCommon(input.properties, out, spec.rename);
|
|
242
|
+
spec.enrich?.(input.properties, out);
|
|
243
|
+
const renamedRequired = spec.required.map((k) => spec.rename?.[k] ?? k);
|
|
244
|
+
const missing = renamedRequired.filter((k) => out[k] === undefined);
|
|
245
|
+
if (missing.length > 0) {
|
|
246
|
+
throw new MissingPropertyError(schemaType, missing);
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
/** Render a JSON-LD object as a `<script>` block string. */
|
|
251
|
+
export function renderJsonLdScript(obj) {
|
|
252
|
+
return `<script type="application/ld+json">\n${JSON.stringify(obj, null, 2)}\n</script>`;
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=schema-mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-mapping.js","sourceRoot":"","sources":["../../src/lib/schema-mapping.ts"],"names":[],"mappings":"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"}
|