@aeptus/aexeo-emdash 0.2.0 → 0.4.0
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/CHANGELOG.md +116 -1
- package/dist/admin/Facts.d.ts +3 -0
- package/dist/admin/Facts.d.ts.map +1 -0
- package/dist/admin.d.ts +2 -0
- package/dist/admin.d.ts.map +1 -1
- package/dist/admin.js +327 -49
- package/dist/configured.d.ts +8 -0
- package/dist/configured.d.ts.map +1 -1
- package/dist/configured.js +447 -109
- package/dist/data-route.d.ts +2 -0
- package/dist/data-route.d.ts.map +1 -1
- package/dist/facts-route.d.ts +7 -0
- package/dist/facts-route.d.ts.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/plugin.d.ts +5 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/sandbox-entry.js +6 -2
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/suppressions.d.ts +10 -0
- package/dist/suppressions.d.ts.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/wasm-init.d.ts +3 -1
- package/dist/wasm-init.d.ts.map +1 -1
- package/package.json +4 -3
- package/wasm/aexeo_emdash_bridge.d.ts +16 -1
- package/wasm/aexeo_emdash_bridge.js +1 -1
- package/wasm/aexeo_emdash_bridge_bg.d.ts +12 -3
- package/wasm/aexeo_emdash_bridge_bg.js +65 -2
- package/wasm/aexeo_emdash_bridge_bg.wasm +0 -0
- package/wasm/aexeo_emdash_bridge_bg.wasm.d.ts +13 -11
package/dist/configured.js
CHANGED
|
@@ -73,7 +73,7 @@ function isPortableTextArray(value) {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// src/plugin.ts
|
|
76
|
-
async function handleAfterSaveConfigured(event, ctx, evaluator) {
|
|
76
|
+
async function handleAfterSaveConfigured(event, ctx, evaluator, suppressionFilter) {
|
|
77
77
|
const adapted = adaptContentItem(event.content);
|
|
78
78
|
const document = adapted.document;
|
|
79
79
|
const { kv, log } = ctx;
|
|
@@ -89,14 +89,16 @@ async function handleAfterSaveConfigured(event, ctx, evaluator) {
|
|
|
89
89
|
const pageFindings = result.findings.filter(
|
|
90
90
|
(finding) => finding.scope === "page"
|
|
91
91
|
);
|
|
92
|
+
const filtered = suppressionFilter === void 0 ? pageFindings : suppressionFilter.apply(document.route, pageFindings);
|
|
92
93
|
await kv.set(findingsKey(document.route), {
|
|
93
94
|
route: document.route,
|
|
94
|
-
findings:
|
|
95
|
+
findings: filtered
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
var DEFAULT_COLLECTIONS = ["posts", "pages"];
|
|
98
99
|
async function evaluateAndPersistAll(ctx, options) {
|
|
99
100
|
const collections = options.collections ?? DEFAULT_COLLECTIONS;
|
|
101
|
+
const suppressionFilter = options.suppressionFilter;
|
|
100
102
|
const { kv, log } = ctx;
|
|
101
103
|
const summary = {
|
|
102
104
|
documentsScanned: 0,
|
|
@@ -154,11 +156,14 @@ async function evaluateAndPersistAll(ctx, options) {
|
|
|
154
156
|
list.push(finding);
|
|
155
157
|
findingsByRoute.set(bucket, list);
|
|
156
158
|
}
|
|
159
|
+
let totalAfterSuppression = 0;
|
|
157
160
|
for (const [route, findings] of findingsByRoute) {
|
|
158
|
-
|
|
161
|
+
const filtered = suppressionFilter === void 0 ? findings : suppressionFilter.apply(route, findings);
|
|
162
|
+
await kv.set(findingsKey(route), { route, findings: filtered });
|
|
159
163
|
summary.routesUpdated += 1;
|
|
164
|
+
totalAfterSuppression += filtered.length;
|
|
160
165
|
}
|
|
161
|
-
summary.totalFindings =
|
|
166
|
+
summary.totalFindings = totalAfterSuppression;
|
|
162
167
|
return summary;
|
|
163
168
|
}
|
|
164
169
|
function findingsKey(route) {
|
|
@@ -169,6 +174,11 @@ function documentKey(route) {
|
|
|
169
174
|
const normalized = route === "" || route === "/" ? "/" : route;
|
|
170
175
|
return `document:${normalized}`;
|
|
171
176
|
}
|
|
177
|
+
var FACTS_KEY = "facts:current";
|
|
178
|
+
async function readStoredFacts(kv) {
|
|
179
|
+
const stored = await kv.get(FACTS_KEY);
|
|
180
|
+
return stored === void 0 ? null : stored;
|
|
181
|
+
}
|
|
172
182
|
async function readAllDocuments(kv) {
|
|
173
183
|
return (await readAllStoredDocuments(kv)).map((s) => s.document);
|
|
174
184
|
}
|
|
@@ -206,97 +216,6 @@ async function readFindings(kv, route) {
|
|
|
206
216
|
return stored.findings;
|
|
207
217
|
}
|
|
208
218
|
|
|
209
|
-
// src/data-route.ts
|
|
210
|
-
async function handleDataRoute(ctx, options) {
|
|
211
|
-
let summary = null;
|
|
212
|
-
if (options.refresh) {
|
|
213
|
-
summary = await evaluateAndPersistAll(ctx, {
|
|
214
|
-
collections: options.collections,
|
|
215
|
-
evaluator: options.evaluator
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
const payload = await buildFindingsPayload(ctx);
|
|
219
|
-
return { payload, summary };
|
|
220
|
-
}
|
|
221
|
-
async function buildFindingsPayload(ctx) {
|
|
222
|
-
const stored = await readAllStoredDocuments(ctx.kv);
|
|
223
|
-
const documents = /* @__PURE__ */ new Map();
|
|
224
|
-
for (const entry of stored) {
|
|
225
|
-
documents.set(entry.document.route, entry);
|
|
226
|
-
}
|
|
227
|
-
const entries = await ctx.kv.list(
|
|
228
|
-
"findings:"
|
|
229
|
-
);
|
|
230
|
-
const findings = [];
|
|
231
|
-
const sitewide = [];
|
|
232
|
-
const perRoute = /* @__PURE__ */ new Map();
|
|
233
|
-
for (const entry of entries) {
|
|
234
|
-
if (entry.value === null) continue;
|
|
235
|
-
const route = entry.key.replace(/^findings:/, "");
|
|
236
|
-
if (route === "*") {
|
|
237
|
-
sitewide.push(...entry.value.findings);
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
let bucket = perRoute.get(route);
|
|
241
|
-
if (bucket === void 0) {
|
|
242
|
-
bucket = { findings: [], errors: 0, warnings: 0 };
|
|
243
|
-
perRoute.set(route, bucket);
|
|
244
|
-
}
|
|
245
|
-
for (const finding of entry.value.findings) {
|
|
246
|
-
bucket.findings.push(finding);
|
|
247
|
-
if (finding.severity === "error") bucket.errors += 1;
|
|
248
|
-
else if (finding.severity === "warning") bucket.warnings += 1;
|
|
249
|
-
findings.push({ ...finding, route });
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
findings.sort((a, b) => severityRank(a.severity) - severityRank(b.severity));
|
|
253
|
-
const siteUrl = ctx.site?.url ?? "";
|
|
254
|
-
const routes = [];
|
|
255
|
-
for (const [route, bucket] of perRoute) {
|
|
256
|
-
const stored2 = documents.get(route);
|
|
257
|
-
routes.push({
|
|
258
|
-
route,
|
|
259
|
-
title: stored2?.meta.title ?? route,
|
|
260
|
-
status: stored2?.meta.status ?? "",
|
|
261
|
-
collection: stored2?.meta.collection ?? "",
|
|
262
|
-
id: stored2?.meta.id ?? "",
|
|
263
|
-
editUrl: stored2 ? buildEditUrl(stored2.meta) : "",
|
|
264
|
-
publishedUrl: stored2 ? buildPublishedUrl(stored2.meta, route, siteUrl) : "",
|
|
265
|
-
findingCount: bucket.findings.length,
|
|
266
|
-
errorCount: bucket.errors,
|
|
267
|
-
warningCount: bucket.warnings
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
routes.sort((a, b) => b.errorCount - a.errorCount || a.route.localeCompare(b.route));
|
|
271
|
-
return {
|
|
272
|
-
routes,
|
|
273
|
-
findings,
|
|
274
|
-
totals: {
|
|
275
|
-
routes: routes.length,
|
|
276
|
-
findings: findings.length,
|
|
277
|
-
errors: findings.filter((f) => f.severity === "error").length,
|
|
278
|
-
warnings: findings.filter((f) => f.severity === "warning").length,
|
|
279
|
-
documentsIndexed: documents.size
|
|
280
|
-
},
|
|
281
|
-
sitewideFindings: sitewide
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
function severityRank(severity) {
|
|
285
|
-
if (severity === "error") return 0;
|
|
286
|
-
if (severity === "warning") return 1;
|
|
287
|
-
return 2;
|
|
288
|
-
}
|
|
289
|
-
function buildEditUrl(meta) {
|
|
290
|
-
if (meta.collection === "" || meta.id === "") return "";
|
|
291
|
-
return `/_emdash/admin/content/${encodeURIComponent(meta.collection)}/${encodeURIComponent(meta.id)}`;
|
|
292
|
-
}
|
|
293
|
-
function buildPublishedUrl(meta, route, siteUrl) {
|
|
294
|
-
if (meta.status !== "published") return "";
|
|
295
|
-
if (siteUrl === "") return "";
|
|
296
|
-
const trimmed = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
|
|
297
|
-
return `${trimmed}${route}`;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
219
|
// wasm/aexeo_emdash_bridge_bg.js
|
|
301
220
|
var aexeo_emdash_bridge_bg_exports = {};
|
|
302
221
|
__export(aexeo_emdash_bridge_bg_exports, {
|
|
@@ -304,7 +223,9 @@ __export(aexeo_emdash_bridge_bg_exports, {
|
|
|
304
223
|
__wbg_set_wasm: () => __wbg_set_wasm,
|
|
305
224
|
__wbindgen_init_externref_table: () => __wbindgen_init_externref_table,
|
|
306
225
|
evaluateDocuments: () => evaluateDocuments,
|
|
307
|
-
|
|
226
|
+
generateFactsPrompt: () => generateFactsPrompt,
|
|
227
|
+
scoreIntelligence: () => scoreIntelligence,
|
|
228
|
+
validateFactsManifest: () => validateFactsManifest
|
|
308
229
|
});
|
|
309
230
|
function evaluateDocuments(documents_json, config_json) {
|
|
310
231
|
let deferred4_0;
|
|
@@ -329,13 +250,13 @@ function evaluateDocuments(documents_json, config_json) {
|
|
|
329
250
|
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
|
|
330
251
|
}
|
|
331
252
|
}
|
|
332
|
-
function
|
|
253
|
+
function generateFactsPrompt(documents_json) {
|
|
333
254
|
let deferred3_0;
|
|
334
255
|
let deferred3_1;
|
|
335
256
|
try {
|
|
336
257
|
const ptr0 = passStringToWasm0(documents_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
337
258
|
const len0 = WASM_VECTOR_LEN;
|
|
338
|
-
const ret = wasm.
|
|
259
|
+
const ret = wasm.generateFactsPrompt(ptr0, len0);
|
|
339
260
|
var ptr2 = ret[0];
|
|
340
261
|
var len2 = ret[1];
|
|
341
262
|
if (ret[3]) {
|
|
@@ -350,6 +271,52 @@ function scoreIntelligence(documents_json) {
|
|
|
350
271
|
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
351
272
|
}
|
|
352
273
|
}
|
|
274
|
+
function scoreIntelligence(documents_json, manifest_json) {
|
|
275
|
+
let deferred4_0;
|
|
276
|
+
let deferred4_1;
|
|
277
|
+
try {
|
|
278
|
+
const ptr0 = passStringToWasm0(documents_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
279
|
+
const len0 = WASM_VECTOR_LEN;
|
|
280
|
+
var ptr1 = isLikeNone(manifest_json) ? 0 : passStringToWasm0(manifest_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
281
|
+
var len1 = WASM_VECTOR_LEN;
|
|
282
|
+
const ret = wasm.scoreIntelligence(ptr0, len0, ptr1, len1);
|
|
283
|
+
var ptr3 = ret[0];
|
|
284
|
+
var len3 = ret[1];
|
|
285
|
+
if (ret[3]) {
|
|
286
|
+
ptr3 = 0;
|
|
287
|
+
len3 = 0;
|
|
288
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
289
|
+
}
|
|
290
|
+
deferred4_0 = ptr3;
|
|
291
|
+
deferred4_1 = len3;
|
|
292
|
+
return getStringFromWasm0(ptr3, len3);
|
|
293
|
+
} finally {
|
|
294
|
+
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function validateFactsManifest(manifest_json, documents_json) {
|
|
298
|
+
let deferred4_0;
|
|
299
|
+
let deferred4_1;
|
|
300
|
+
try {
|
|
301
|
+
const ptr0 = passStringToWasm0(manifest_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
302
|
+
const len0 = WASM_VECTOR_LEN;
|
|
303
|
+
const ptr1 = passStringToWasm0(documents_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
304
|
+
const len1 = WASM_VECTOR_LEN;
|
|
305
|
+
const ret = wasm.validateFactsManifest(ptr0, len0, ptr1, len1);
|
|
306
|
+
var ptr3 = ret[0];
|
|
307
|
+
var len3 = ret[1];
|
|
308
|
+
if (ret[3]) {
|
|
309
|
+
ptr3 = 0;
|
|
310
|
+
len3 = 0;
|
|
311
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
312
|
+
}
|
|
313
|
+
deferred4_0 = ptr3;
|
|
314
|
+
deferred4_1 = len3;
|
|
315
|
+
return getStringFromWasm0(ptr3, len3);
|
|
316
|
+
} finally {
|
|
317
|
+
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
353
320
|
function __wbg_Error_960c155d3d49e4c2(arg0, arg1) {
|
|
354
321
|
const ret = Error(getStringFromWasm0(arg0, arg1));
|
|
355
322
|
return ret;
|
|
@@ -465,11 +432,349 @@ async function evaluateDocuments2(documentsJson, configJson) {
|
|
|
465
432
|
ensureInitialized();
|
|
466
433
|
return evaluateDocuments(documentsJson, configJson);
|
|
467
434
|
}
|
|
468
|
-
async function scoreIntelligence2(documentsJson) {
|
|
435
|
+
async function scoreIntelligence2(documentsJson, manifestJson) {
|
|
469
436
|
ensureInitialized();
|
|
470
|
-
return scoreIntelligence(documentsJson);
|
|
437
|
+
return scoreIntelligence(documentsJson, manifestJson ?? void 0);
|
|
438
|
+
}
|
|
439
|
+
async function generateFactsPrompt2(documentsJson) {
|
|
440
|
+
ensureInitialized();
|
|
441
|
+
return generateFactsPrompt(documentsJson);
|
|
442
|
+
}
|
|
443
|
+
async function validateFactsManifest2(manifestJson, documentsJson) {
|
|
444
|
+
ensureInitialized();
|
|
445
|
+
return validateFactsManifest(manifestJson, documentsJson);
|
|
471
446
|
}
|
|
472
447
|
|
|
448
|
+
// src/data-route.ts
|
|
449
|
+
async function handleDataRoute(ctx, options) {
|
|
450
|
+
let summary = null;
|
|
451
|
+
if (options.refresh) {
|
|
452
|
+
summary = await evaluateAndPersistAll(ctx, {
|
|
453
|
+
collections: options.collections,
|
|
454
|
+
evaluator: options.evaluator,
|
|
455
|
+
...options.suppressionFilter === void 0 ? {} : { suppressionFilter: options.suppressionFilter }
|
|
456
|
+
});
|
|
457
|
+
await persistManifestStateFindings(ctx);
|
|
458
|
+
}
|
|
459
|
+
const payload = await buildFindingsPayload(ctx);
|
|
460
|
+
return { payload, summary };
|
|
461
|
+
}
|
|
462
|
+
async function buildFindingsPayload(ctx) {
|
|
463
|
+
const stored = await readAllStoredDocuments(ctx.kv);
|
|
464
|
+
const documents = /* @__PURE__ */ new Map();
|
|
465
|
+
for (const entry of stored) {
|
|
466
|
+
documents.set(entry.document.route, entry);
|
|
467
|
+
}
|
|
468
|
+
const entries = await ctx.kv.list(
|
|
469
|
+
"findings:"
|
|
470
|
+
);
|
|
471
|
+
const findings = [];
|
|
472
|
+
const sitewide = [];
|
|
473
|
+
const perRoute = /* @__PURE__ */ new Map();
|
|
474
|
+
for (const entry of entries) {
|
|
475
|
+
if (entry.value === null) continue;
|
|
476
|
+
const route = entry.key.replace(/^findings:/, "");
|
|
477
|
+
if (route === "*") {
|
|
478
|
+
sitewide.push(...entry.value.findings);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
let bucket = perRoute.get(route);
|
|
482
|
+
if (bucket === void 0) {
|
|
483
|
+
bucket = { findings: [], errors: 0, warnings: 0 };
|
|
484
|
+
perRoute.set(route, bucket);
|
|
485
|
+
}
|
|
486
|
+
for (const finding of entry.value.findings) {
|
|
487
|
+
bucket.findings.push(finding);
|
|
488
|
+
if (finding.severity === "error") bucket.errors += 1;
|
|
489
|
+
else if (finding.severity === "warning") bucket.warnings += 1;
|
|
490
|
+
findings.push({ ...finding, route });
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
findings.sort((a, b) => severityRank(a.severity) - severityRank(b.severity));
|
|
494
|
+
const siteUrl = ctx.site?.url ?? "";
|
|
495
|
+
const routes = [];
|
|
496
|
+
for (const [route, bucket] of perRoute) {
|
|
497
|
+
const stored2 = documents.get(route);
|
|
498
|
+
routes.push({
|
|
499
|
+
route,
|
|
500
|
+
title: stored2?.meta.title ?? route,
|
|
501
|
+
status: stored2?.meta.status ?? "",
|
|
502
|
+
collection: stored2?.meta.collection ?? "",
|
|
503
|
+
id: stored2?.meta.id ?? "",
|
|
504
|
+
editUrl: stored2 ? buildEditUrl(stored2.meta) : "",
|
|
505
|
+
publishedUrl: stored2 ? buildPublishedUrl(stored2.meta, route, siteUrl) : "",
|
|
506
|
+
findingCount: bucket.findings.length,
|
|
507
|
+
errorCount: bucket.errors,
|
|
508
|
+
warningCount: bucket.warnings
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
routes.sort((a, b) => b.errorCount - a.errorCount || a.route.localeCompare(b.route));
|
|
512
|
+
const cached = await ctx.kv.get(
|
|
513
|
+
MANIFEST_FINDINGS_KEY
|
|
514
|
+
);
|
|
515
|
+
if (cached !== void 0 && cached !== null) {
|
|
516
|
+
sitewide.push(...cached.findings);
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
routes,
|
|
520
|
+
findings,
|
|
521
|
+
totals: {
|
|
522
|
+
routes: routes.length,
|
|
523
|
+
findings: findings.length,
|
|
524
|
+
errors: findings.filter((f) => f.severity === "error").length,
|
|
525
|
+
warnings: findings.filter((f) => f.severity === "warning").length,
|
|
526
|
+
documentsIndexed: documents.size
|
|
527
|
+
},
|
|
528
|
+
sitewideFindings: sitewide
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
var MANIFEST_FINDINGS_KEY = "meta:facts-findings";
|
|
532
|
+
async function persistManifestStateFindings(ctx) {
|
|
533
|
+
const findings = await deriveManifestStateFindings(ctx);
|
|
534
|
+
await ctx.kv.set(MANIFEST_FINDINGS_KEY, { findings });
|
|
535
|
+
}
|
|
536
|
+
async function deriveManifestStateFindings(ctx) {
|
|
537
|
+
const manifest = await readStoredFacts(ctx.kv);
|
|
538
|
+
if (manifest === null) {
|
|
539
|
+
return [
|
|
540
|
+
{
|
|
541
|
+
rule_id: "FACTS001",
|
|
542
|
+
message: "No truth manifest authored yet. Open the Truth manifest page to generate an LLM authoring prompt and save your facts.json.",
|
|
543
|
+
severity: "warning",
|
|
544
|
+
path: "",
|
|
545
|
+
line: 0,
|
|
546
|
+
column: 0,
|
|
547
|
+
scope: "sitewide",
|
|
548
|
+
suggestion: null
|
|
549
|
+
}
|
|
550
|
+
];
|
|
551
|
+
}
|
|
552
|
+
const documents = await readAllDocuments(ctx.kv);
|
|
553
|
+
if (documents.length === 0) {
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
556
|
+
try {
|
|
557
|
+
const raw = await validateFactsManifest2(
|
|
558
|
+
JSON.stringify(manifest),
|
|
559
|
+
JSON.stringify(documents)
|
|
560
|
+
);
|
|
561
|
+
const result = JSON.parse(raw);
|
|
562
|
+
const out = [];
|
|
563
|
+
if (!result.validation.valid || result.validation.errors.length > 0) {
|
|
564
|
+
out.push({
|
|
565
|
+
rule_id: "FACTS001",
|
|
566
|
+
message: `Truth manifest fails shape validation: ${result.validation.errors.slice(0, 3).join("; ")}`,
|
|
567
|
+
severity: "error",
|
|
568
|
+
path: "",
|
|
569
|
+
line: 0,
|
|
570
|
+
column: 0,
|
|
571
|
+
scope: "sitewide",
|
|
572
|
+
suggestion: null
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
const errCount = result.assessment.mismatches.filter(
|
|
576
|
+
(m) => m.severity === "error"
|
|
577
|
+
).length;
|
|
578
|
+
const warnCount = result.assessment.mismatches.length - errCount;
|
|
579
|
+
if (result.assessment.mismatches.length > 0) {
|
|
580
|
+
out.push({
|
|
581
|
+
rule_id: "FACTS003",
|
|
582
|
+
message: `Truth manifest disagrees with on-page schema.org: ${errCount} errors, ${warnCount} warnings. Open the Truth manifest page to review and refresh.`,
|
|
583
|
+
severity: errCount > 0 ? "error" : "warning",
|
|
584
|
+
path: "",
|
|
585
|
+
line: 0,
|
|
586
|
+
column: 0,
|
|
587
|
+
scope: "sitewide",
|
|
588
|
+
suggestion: null
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
return out;
|
|
592
|
+
} catch {
|
|
593
|
+
return [];
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function severityRank(severity) {
|
|
597
|
+
if (severity === "error") return 0;
|
|
598
|
+
if (severity === "warning") return 1;
|
|
599
|
+
return 2;
|
|
600
|
+
}
|
|
601
|
+
function buildEditUrl(meta) {
|
|
602
|
+
if (meta.collection === "" || meta.id === "") return "";
|
|
603
|
+
return `/_emdash/admin/content/${encodeURIComponent(meta.collection)}/${encodeURIComponent(meta.id)}`;
|
|
604
|
+
}
|
|
605
|
+
function buildPublishedUrl(meta, route, siteUrl) {
|
|
606
|
+
if (meta.status !== "published") return "";
|
|
607
|
+
if (siteUrl === "") return "";
|
|
608
|
+
const trimmed = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
|
|
609
|
+
return `${trimmed}${route}`;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/facts-route.ts
|
|
613
|
+
async function handleFactsRoute(ctx) {
|
|
614
|
+
const body = ctx.input ?? {};
|
|
615
|
+
switch (body.kind) {
|
|
616
|
+
case "data":
|
|
617
|
+
return await handleData(ctx);
|
|
618
|
+
case "prompt":
|
|
619
|
+
return await handlePrompt(ctx);
|
|
620
|
+
case "validate":
|
|
621
|
+
return await handleValidate(ctx, body);
|
|
622
|
+
case "save":
|
|
623
|
+
return await handleSave(ctx, body);
|
|
624
|
+
default:
|
|
625
|
+
return {
|
|
626
|
+
error: {
|
|
627
|
+
code: "unknown_kind",
|
|
628
|
+
message: `unknown facts route kind: ${String(body.kind)}`
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async function handleData(ctx) {
|
|
634
|
+
const manifest = await readStoredFacts(ctx.kv);
|
|
635
|
+
return {
|
|
636
|
+
data: {
|
|
637
|
+
manifest,
|
|
638
|
+
// The presence flag lets the React component branch on
|
|
639
|
+
// "show authoring CTA" vs "show 'manifest stored' state" without
|
|
640
|
+
// having to introspect the manifest shape.
|
|
641
|
+
present: manifest !== null
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
async function handlePrompt(ctx) {
|
|
646
|
+
const documents = await readAllDocuments(ctx.kv);
|
|
647
|
+
if (documents.length === 0) {
|
|
648
|
+
return {
|
|
649
|
+
error: {
|
|
650
|
+
code: "no_documents",
|
|
651
|
+
message: "no documents indexed yet \u2014 click Refresh on the Findings page first, then come back to author the manifest"
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
const prompt = await generateFactsPrompt2(JSON.stringify(documents));
|
|
656
|
+
return { data: { prompt } };
|
|
657
|
+
}
|
|
658
|
+
async function handleValidate(ctx, body) {
|
|
659
|
+
if (typeof body.manifest_json !== "string" || body.manifest_json.length === 0) {
|
|
660
|
+
return {
|
|
661
|
+
error: {
|
|
662
|
+
code: "missing_manifest",
|
|
663
|
+
message: "manifest_json field is required"
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
const documents = await readAllDocuments(ctx.kv);
|
|
668
|
+
try {
|
|
669
|
+
const raw = await validateFactsManifest2(
|
|
670
|
+
body.manifest_json,
|
|
671
|
+
JSON.stringify(documents)
|
|
672
|
+
);
|
|
673
|
+
return { data: JSON.parse(raw) };
|
|
674
|
+
} catch (error) {
|
|
675
|
+
return {
|
|
676
|
+
error: {
|
|
677
|
+
code: "validate_failed",
|
|
678
|
+
message: error instanceof Error ? error.message : String(error)
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
async function handleSave(ctx, body) {
|
|
684
|
+
if (typeof body.manifest_json !== "string" || body.manifest_json.length === 0) {
|
|
685
|
+
return {
|
|
686
|
+
error: {
|
|
687
|
+
code: "missing_manifest",
|
|
688
|
+
message: "manifest_json field is required"
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
const documents = await readAllDocuments(ctx.kv);
|
|
693
|
+
let validation;
|
|
694
|
+
try {
|
|
695
|
+
const raw = await validateFactsManifest2(
|
|
696
|
+
body.manifest_json,
|
|
697
|
+
JSON.stringify(documents)
|
|
698
|
+
);
|
|
699
|
+
const parsed2 = JSON.parse(raw);
|
|
700
|
+
validation = parsed2.validation;
|
|
701
|
+
} catch (error) {
|
|
702
|
+
return {
|
|
703
|
+
error: {
|
|
704
|
+
code: "validate_failed",
|
|
705
|
+
message: error instanceof Error ? error.message : String(error)
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
if (!validation.valid || validation.errors.length > 0) {
|
|
710
|
+
return {
|
|
711
|
+
error: {
|
|
712
|
+
code: "validation_failed",
|
|
713
|
+
message: `manifest does not pass shape validation: ${validation.errors.slice(0, 3).join("; ")}`
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
let parsed;
|
|
718
|
+
try {
|
|
719
|
+
parsed = JSON.parse(body.manifest_json);
|
|
720
|
+
} catch (error) {
|
|
721
|
+
return {
|
|
722
|
+
error: {
|
|
723
|
+
code: "parse_failed",
|
|
724
|
+
message: error instanceof Error ? error.message : String(error)
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
await ctx.kv.set(FACTS_KEY, parsed);
|
|
729
|
+
await persistManifestStateFindings(ctx);
|
|
730
|
+
return { data: { saved: true } };
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// src/suppressions.ts
|
|
734
|
+
var NOOP_FILTER = {
|
|
735
|
+
apply: (_route, findings) => [...findings]
|
|
736
|
+
};
|
|
737
|
+
function compileSuppressions(rules) {
|
|
738
|
+
if (rules === void 0 || rules.length === 0) {
|
|
739
|
+
return NOOP_FILTER;
|
|
740
|
+
}
|
|
741
|
+
rules.forEach((rule, index) => {
|
|
742
|
+
const hasRoute = rule.routePattern !== void 0 && rule.routePattern.length > 0;
|
|
743
|
+
const hasRuleIds = rule.ruleIds !== void 0 && rule.ruleIds.length > 0;
|
|
744
|
+
if (!hasRoute && !hasRuleIds) {
|
|
745
|
+
throw new Error(
|
|
746
|
+
`seogeoPlugin: suppressions[${index}] is empty \u2014 set routePattern, ruleIds, or both`
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
const compiled = rules.map((rule) => ({
|
|
751
|
+
matchRoute: rule.routePattern === void 0 ? matchAlways : globToMatcher(rule.routePattern),
|
|
752
|
+
ruleIds: rule.ruleIds === void 0 || rule.ruleIds.length === 0 ? null : new Set(rule.ruleIds)
|
|
753
|
+
}));
|
|
754
|
+
return {
|
|
755
|
+
apply(route, findings) {
|
|
756
|
+
return findings.filter((finding) => !compiled.some((rule) => {
|
|
757
|
+
if (!rule.matchRoute(route)) return false;
|
|
758
|
+
if (rule.ruleIds !== null && !rule.ruleIds.has(finding.rule_id)) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
return true;
|
|
762
|
+
}));
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
var matchAlways = () => true;
|
|
767
|
+
function globToMatcher(pattern) {
|
|
768
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
769
|
+
const DOUBLE_STAR_TOKEN = "";
|
|
770
|
+
let body = escaped.replace(/\*\*/g, DOUBLE_STAR_TOKEN).replace(/\*/g, "[^/]*").replace(new RegExp(DOUBLE_STAR_TOKEN, "g"), ".*").replace(/\?/g, "[^/]");
|
|
771
|
+
const re = new RegExp(`^${body}$`);
|
|
772
|
+
return (route) => re.test(route);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/version.ts
|
|
776
|
+
var PACKAGE_VERSION = "0.4.0";
|
|
777
|
+
|
|
473
778
|
// src/configured.ts
|
|
474
779
|
import { definePlugin } from "emdash";
|
|
475
780
|
var inProcessEvaluator = async (documents) => {
|
|
@@ -491,15 +796,21 @@ var inProcessEvaluator = async (documents) => {
|
|
|
491
796
|
};
|
|
492
797
|
function createPlugin(options = {}) {
|
|
493
798
|
const collections = options.collections === void 0 ? DEFAULT_COLLECTIONS : [...options.collections];
|
|
799
|
+
const suppressionFilter = compileSuppressions(options.suppressions);
|
|
494
800
|
return definePlugin({
|
|
495
801
|
id: "aexeo-seogeo",
|
|
496
|
-
version:
|
|
802
|
+
version: PACKAGE_VERSION,
|
|
497
803
|
capabilities: ["read:content"],
|
|
498
804
|
hooks: {
|
|
499
805
|
// afterSave processes one saved document at a time — collections
|
|
500
806
|
// list isn't needed here. The hook always runs regardless of
|
|
501
807
|
// which collection the document came from.
|
|
502
|
-
"content:afterSave": (event, ctx) => handleAfterSaveConfigured(
|
|
808
|
+
"content:afterSave": (event, ctx) => handleAfterSaveConfigured(
|
|
809
|
+
event,
|
|
810
|
+
ctx,
|
|
811
|
+
inProcessEvaluator,
|
|
812
|
+
suppressionFilter
|
|
813
|
+
)
|
|
503
814
|
},
|
|
504
815
|
routes: {
|
|
505
816
|
// The Refresh sweep (handleAdminRoute → handleRefresh) is the
|
|
@@ -519,15 +830,24 @@ function createPlugin(options = {}) {
|
|
|
519
830
|
handler: async (ctx) => (await handleDataRoute(ctx, {
|
|
520
831
|
collections,
|
|
521
832
|
evaluator: inProcessEvaluator,
|
|
522
|
-
refresh: false
|
|
833
|
+
refresh: false,
|
|
834
|
+
suppressionFilter
|
|
523
835
|
})).payload
|
|
524
836
|
},
|
|
525
837
|
refresh: {
|
|
526
838
|
handler: (ctx) => handleDataRoute(ctx, {
|
|
527
839
|
collections,
|
|
528
840
|
evaluator: inProcessEvaluator,
|
|
529
|
-
refresh: true
|
|
841
|
+
refresh: true,
|
|
842
|
+
suppressionFilter
|
|
530
843
|
})
|
|
844
|
+
},
|
|
845
|
+
// /facts route serves the truth-manifest authoring UI. It
|
|
846
|
+
// multiplexes four operations (data / prompt / validate / save)
|
|
847
|
+
// onto one POST endpoint via a "kind" body field — see
|
|
848
|
+
// facts-route.ts for the full contract.
|
|
849
|
+
facts: {
|
|
850
|
+
handler: (ctx) => handleFactsRoute(ctx)
|
|
531
851
|
}
|
|
532
852
|
},
|
|
533
853
|
admin: {
|
|
@@ -538,7 +858,8 @@ function createPlugin(options = {}) {
|
|
|
538
858
|
// duplicate "SEO findings" sidebar entries — that was the
|
|
539
859
|
// visible 0.1.0 / 0.1.1 bug fixed in 0.1.2.
|
|
540
860
|
{ path: "/findings", label: "SEO findings" },
|
|
541
|
-
{ path: "/document", label: "Document SEO" }
|
|
861
|
+
{ path: "/document", label: "Document SEO" },
|
|
862
|
+
{ path: "/facts", label: "Truth manifest" }
|
|
542
863
|
],
|
|
543
864
|
widgets: [
|
|
544
865
|
{ id: "seogeo-score", size: "third", title: "SEO score" }
|
|
@@ -733,14 +1054,16 @@ async function renderScoreWidget(ctx) {
|
|
|
733
1054
|
]
|
|
734
1055
|
};
|
|
735
1056
|
}
|
|
736
|
-
const
|
|
1057
|
+
const facts = await readStoredFacts(ctx.kv);
|
|
1058
|
+
const score = await scoreLocally(documents, facts);
|
|
1059
|
+
const truthLabel = formatTruthLabel(score);
|
|
737
1060
|
const blocks = [
|
|
738
1061
|
{
|
|
739
1062
|
type: "stats",
|
|
740
1063
|
items: [
|
|
741
1064
|
{ label: "Overall", value: `${score.overall_score}` },
|
|
742
1065
|
{ label: "Citation", value: `${score.citation_readiness_score}` },
|
|
743
|
-
{ label:
|
|
1066
|
+
{ label: truthLabel, value: `${score.truth_consistency_score}` },
|
|
744
1067
|
{ label: "Answers", value: `${score.answer_pack_score}` }
|
|
745
1068
|
]
|
|
746
1069
|
}
|
|
@@ -757,10 +1080,25 @@ async function renderScoreWidget(ctx) {
|
|
|
757
1080
|
}
|
|
758
1081
|
return { blocks };
|
|
759
1082
|
}
|
|
760
|
-
async function scoreLocally(documents) {
|
|
761
|
-
const
|
|
1083
|
+
async function scoreLocally(documents, manifest = null) {
|
|
1084
|
+
const manifestJson = manifest === null ? null : JSON.stringify(manifest);
|
|
1085
|
+
const raw = await scoreIntelligence2(JSON.stringify(documents), manifestJson);
|
|
762
1086
|
return JSON.parse(raw);
|
|
763
1087
|
}
|
|
1088
|
+
function formatTruthLabel(score) {
|
|
1089
|
+
switch (score.structured_truth_source) {
|
|
1090
|
+
case "schema_and_manifest":
|
|
1091
|
+
return "Truth (manifest+schema)";
|
|
1092
|
+
case "manifest":
|
|
1093
|
+
return "Truth (manifest only)";
|
|
1094
|
+
case "schema":
|
|
1095
|
+
return "Truth (schema only)";
|
|
1096
|
+
case "none":
|
|
1097
|
+
return "Truth (no signal)";
|
|
1098
|
+
default:
|
|
1099
|
+
return "Truth";
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
764
1102
|
function topBlockersLine(score) {
|
|
765
1103
|
const top = score.blockers.slice(0, 3).map((b) => b.message);
|
|
766
1104
|
if (top.length === 0) return "No blockers identified.";
|
package/dist/data-route.d.ts
CHANGED
|
@@ -39,10 +39,12 @@ interface DataRouteOptions {
|
|
|
39
39
|
collections: readonly string[];
|
|
40
40
|
evaluator: EvaluatorFn;
|
|
41
41
|
refresh: boolean;
|
|
42
|
+
suppressionFilter?: import("./suppressions.js").SuppressionFilter;
|
|
42
43
|
}
|
|
43
44
|
export declare function handleDataRoute(ctx: SandboxCtx, options: DataRouteOptions): Promise<{
|
|
44
45
|
payload: FindingsPayload;
|
|
45
46
|
summary: RefreshSummary | null;
|
|
46
47
|
}>;
|
|
48
|
+
export declare function persistManifestStateFindings(ctx: SandboxCtx): Promise<void>;
|
|
47
49
|
export {};
|
|
48
50
|
//# sourceMappingURL=data-route.d.ts.map
|