@aeptus/aexeo-emdash 0.6.0 → 0.8.1
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 +144 -1
- package/dist/admin/Absorbability.d.ts +3 -0
- package/dist/admin/Absorbability.d.ts.map +1 -0
- package/dist/admin/Citability.d.ts +3 -0
- package/dist/admin/Citability.d.ts.map +1 -0
- package/dist/admin/EntityLegitimacy.d.ts +3 -0
- package/dist/admin/EntityLegitimacy.d.ts.map +1 -0
- package/dist/admin/EntityPresence.d.ts +3 -0
- package/dist/admin/EntityPresence.d.ts.map +1 -0
- package/dist/admin/PillarView.d.ts +7 -0
- package/dist/admin/PillarView.d.ts.map +1 -0
- package/dist/admin/Retrievability.d.ts +3 -0
- package/dist/admin/Retrievability.d.ts.map +1 -0
- package/dist/admin.d.ts +8 -0
- package/dist/admin.d.ts.map +1 -1
- package/dist/admin.js +652 -123
- package/dist/configured.d.ts.map +1 -1
- package/dist/configured.js +423 -16
- package/dist/data-route.d.ts +8 -0
- package/dist/data-route.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/presence-check.d.ts +22 -0
- package/dist/presence-check.d.ts.map +1 -0
- package/dist/presence-route.d.ts +7 -0
- package/dist/presence-route.d.ts.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/wasm/aexeo_emdash_bridge_bg.wasm +0 -0
package/dist/admin.js
CHANGED
|
@@ -1,16 +1,513 @@
|
|
|
1
|
-
// src/admin/
|
|
1
|
+
// src/admin/PillarView.tsx
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
// src/types.ts
|
|
5
|
+
function layerHumanLabel(layer) {
|
|
6
|
+
switch (layer) {
|
|
7
|
+
case "retrievability":
|
|
8
|
+
return "Retrievability";
|
|
9
|
+
case "citability":
|
|
10
|
+
return "Citability";
|
|
11
|
+
case "absorbability":
|
|
12
|
+
return "Absorbability";
|
|
13
|
+
case "entity_legitimacy":
|
|
14
|
+
return "Entity legitimacy";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function layerOneLineDescription(layer) {
|
|
18
|
+
switch (layer) {
|
|
19
|
+
case "retrievability":
|
|
20
|
+
return "Can the engine find the page at all? Robots, sitemap, internal links, machine-readable surfaces.";
|
|
21
|
+
case "citability":
|
|
22
|
+
return "Once retrieved, does it look worth citing? Structure, schema, evidence density, scannability.";
|
|
23
|
+
case "absorbability":
|
|
24
|
+
return "Does the answer actually use this content? Cite-ready evidence, mirrors, llms.txt.";
|
|
25
|
+
case "entity_legitimacy":
|
|
26
|
+
return "Does the entity exist strongly enough to be selected at all? Aexeo surfaces this layer; it does not fix it.";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/admin/PillarView.tsx
|
|
31
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
32
|
var PLUGIN_ID = "aexeo-emdash";
|
|
5
33
|
var API_BASE = "/_emdash/api";
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const [
|
|
11
|
-
|
|
34
|
+
function PillarView({
|
|
35
|
+
layer,
|
|
36
|
+
extraSlot
|
|
37
|
+
}) {
|
|
38
|
+
const [state, setState] = useState({
|
|
39
|
+
loading: true,
|
|
40
|
+
error: null,
|
|
41
|
+
payload: null
|
|
42
|
+
});
|
|
43
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
12
44
|
const [toast, setToast] = useState(null);
|
|
13
|
-
const
|
|
45
|
+
const load = useCallback(async () => {
|
|
46
|
+
setState((s) => ({ ...s, loading: true, error: null }));
|
|
47
|
+
try {
|
|
48
|
+
const res = await fetch(`${API_BASE}/plugins/${PLUGIN_ID}/data`, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: csrfHeaders(),
|
|
51
|
+
body: "{}"
|
|
52
|
+
});
|
|
53
|
+
const body = await res.json();
|
|
54
|
+
const payload2 = unwrap(body, res);
|
|
55
|
+
setState({ loading: false, error: null, payload: payload2 });
|
|
56
|
+
} catch (err) {
|
|
57
|
+
setState({
|
|
58
|
+
loading: false,
|
|
59
|
+
error: err instanceof Error ? err.message : String(err),
|
|
60
|
+
payload: null
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}, []);
|
|
64
|
+
const refresh = useCallback(async () => {
|
|
65
|
+
setRefreshing(true);
|
|
66
|
+
setToast(null);
|
|
67
|
+
try {
|
|
68
|
+
const res = await fetch(`${API_BASE}/plugins/${PLUGIN_ID}/refresh`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: csrfHeaders(),
|
|
71
|
+
body: "{}"
|
|
72
|
+
});
|
|
73
|
+
const body = await res.json();
|
|
74
|
+
const data = unwrap(body, res);
|
|
75
|
+
setState({ loading: false, error: null, payload: data.payload });
|
|
76
|
+
setToast(`Refreshed ${data.payload.totals.routes} routes`);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
setToast(
|
|
79
|
+
`Refresh failed: ${err instanceof Error ? err.message : String(err)}`
|
|
80
|
+
);
|
|
81
|
+
} finally {
|
|
82
|
+
setRefreshing(false);
|
|
83
|
+
}
|
|
84
|
+
}, []);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
void load();
|
|
87
|
+
}, [load]);
|
|
88
|
+
if (state.loading && state.payload === null) {
|
|
89
|
+
return /* @__PURE__ */ jsxs("div", { className: "p-4 text-sm text-kumo-subtle", children: [
|
|
90
|
+
"Loading ",
|
|
91
|
+
layerHumanLabel(layer),
|
|
92
|
+
" findings\u2026"
|
|
93
|
+
] });
|
|
94
|
+
}
|
|
95
|
+
if (state.error !== null) {
|
|
96
|
+
return /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
97
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: layerHumanLabel(layer) }),
|
|
98
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-2 rounded border border-red-500/40 bg-red-500/10 p-3 text-sm text-red-700", children: [
|
|
99
|
+
"Failed to load: ",
|
|
100
|
+
state.error
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
"button",
|
|
104
|
+
{
|
|
105
|
+
type: "button",
|
|
106
|
+
onClick: () => void load(),
|
|
107
|
+
className: "mt-3 rounded border border-kumo-line px-3 py-1.5 text-sm hover:bg-kumo-tint",
|
|
108
|
+
children: "Retry"
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
] });
|
|
112
|
+
}
|
|
113
|
+
const payload = state.payload;
|
|
114
|
+
if (payload === null) {
|
|
115
|
+
return /* @__PURE__ */ jsx("div", { className: "p-4 text-sm text-kumo-subtle", children: "No data." });
|
|
116
|
+
}
|
|
117
|
+
const breakdown = payload.layerBreakdown.find((b) => b.layer === layer);
|
|
118
|
+
const findings = payload.findings.filter(
|
|
119
|
+
(f) => (f.layers?.primary ?? "citability") === layer
|
|
120
|
+
);
|
|
121
|
+
const sitewide = payload.sitewideFindings.filter(
|
|
122
|
+
(f) => (f.layers?.primary ?? "citability") === layer
|
|
123
|
+
);
|
|
124
|
+
const routesByPath = /* @__PURE__ */ new Map();
|
|
125
|
+
for (const route of payload.routes) {
|
|
126
|
+
routesByPath.set(route.route, route);
|
|
127
|
+
}
|
|
128
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4", children: [
|
|
129
|
+
/* @__PURE__ */ jsxs("header", { className: "flex items-start justify-between gap-4", children: [
|
|
130
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
131
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: layerHumanLabel(layer) }),
|
|
132
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-kumo-subtle", children: layerOneLineDescription(layer) }),
|
|
133
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm", children: breakdown === void 0 || breakdown.total === 0 ? /* @__PURE__ */ jsx("span", { className: "text-kumo-subtle", children: "No findings on this layer." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
134
|
+
/* @__PURE__ */ jsx("strong", { children: breakdown.total }),
|
|
135
|
+
" findings \u2014",
|
|
136
|
+
" ",
|
|
137
|
+
breakdown.errors,
|
|
138
|
+
" errors, ",
|
|
139
|
+
breakdown.warnings,
|
|
140
|
+
" warnings."
|
|
141
|
+
] }) })
|
|
142
|
+
] }),
|
|
143
|
+
/* @__PURE__ */ jsx(
|
|
144
|
+
"button",
|
|
145
|
+
{
|
|
146
|
+
type: "button",
|
|
147
|
+
onClick: () => void refresh(),
|
|
148
|
+
disabled: refreshing,
|
|
149
|
+
className: "rounded bg-kumo-brand px-3 py-1.5 text-sm font-medium text-white hover:bg-kumo-brand/90 disabled:opacity-50",
|
|
150
|
+
children: refreshing ? "Refreshing\u2026" : "Refresh"
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
] }),
|
|
154
|
+
toast !== null && /* @__PURE__ */ jsx("div", { className: "rounded border border-kumo-line bg-kumo-tint p-3 text-sm", children: toast }),
|
|
155
|
+
extraSlot,
|
|
156
|
+
sitewide.length > 0 && /* @__PURE__ */ jsxs("section", { className: "rounded border border-kumo-line p-3", children: [
|
|
157
|
+
/* @__PURE__ */ jsx("h2", { className: "text-sm font-medium", children: "Sitewide findings" }),
|
|
158
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-2 space-y-1 text-sm", children: sitewide.map((finding, i) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
159
|
+
/* @__PURE__ */ jsx(SeverityBadge, { severity: finding.severity }),
|
|
160
|
+
/* @__PURE__ */ jsx("code", { className: "font-mono text-xs", children: finding.rule_id }),
|
|
161
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: finding.message }),
|
|
162
|
+
finding.layers?.secondaries.length ? /* @__PURE__ */ jsx(SecondariesChips, { layers: finding.layers.secondaries }) : null
|
|
163
|
+
] }, i)) })
|
|
164
|
+
] }),
|
|
165
|
+
findings.length === 0 ? sitewide.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-kumo-subtle", children: "All clean on this layer." }) : null : /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded border border-kumo-line", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-left text-sm", children: [
|
|
166
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-kumo-tint", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
167
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Route" }),
|
|
168
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Rule" }),
|
|
169
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Severity" }),
|
|
170
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Message" }),
|
|
171
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Also affects" })
|
|
172
|
+
] }) }),
|
|
173
|
+
/* @__PURE__ */ jsx("tbody", { children: findings.map((finding, i) => {
|
|
174
|
+
const routeInfo = routesByPath.get(finding.route);
|
|
175
|
+
return /* @__PURE__ */ jsxs("tr", { className: "border-t border-kumo-line", children: [
|
|
176
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 align-top", children: /* @__PURE__ */ jsx(RouteLinks, { finding, info: routeInfo }) }),
|
|
177
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 align-top", children: /* @__PURE__ */ jsx("code", { className: "rounded bg-kumo-tint px-1.5 py-0.5 font-mono text-xs", children: finding.rule_id }) }),
|
|
178
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 align-top", children: /* @__PURE__ */ jsx(SeverityBadge, { severity: finding.severity }) }),
|
|
179
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 align-top", children: finding.message }),
|
|
180
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 align-top", children: finding.layers?.secondaries.length ? /* @__PURE__ */ jsx(SecondariesChips, { layers: finding.layers.secondaries }) : /* @__PURE__ */ jsx("span", { className: "text-xs text-kumo-subtle", children: "\u2014" }) })
|
|
181
|
+
] }, i);
|
|
182
|
+
}) })
|
|
183
|
+
] }) })
|
|
184
|
+
] });
|
|
185
|
+
}
|
|
186
|
+
function RouteLinks({
|
|
187
|
+
finding,
|
|
188
|
+
info
|
|
189
|
+
}) {
|
|
190
|
+
if (info === void 0) {
|
|
191
|
+
return /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: finding.route });
|
|
192
|
+
}
|
|
193
|
+
return /* @__PURE__ */ jsxs("span", { className: "space-x-2", children: [
|
|
194
|
+
info.editUrl !== "" ? /* @__PURE__ */ jsx(
|
|
195
|
+
"a",
|
|
196
|
+
{
|
|
197
|
+
href: info.editUrl,
|
|
198
|
+
className: "font-mono text-xs text-kumo-link hover:underline",
|
|
199
|
+
children: finding.route
|
|
200
|
+
}
|
|
201
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: finding.route }),
|
|
202
|
+
info.publishedUrl !== "" && /* @__PURE__ */ jsx(
|
|
203
|
+
"a",
|
|
204
|
+
{
|
|
205
|
+
href: info.publishedUrl,
|
|
206
|
+
target: "_blank",
|
|
207
|
+
rel: "noopener noreferrer",
|
|
208
|
+
className: "text-xs text-kumo-link hover:underline",
|
|
209
|
+
children: "live \u2197"
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
info.status !== "" && info.status !== "published" && /* @__PURE__ */ jsx("span", { className: "text-xs text-kumo-subtle", children: info.status })
|
|
213
|
+
] });
|
|
214
|
+
}
|
|
215
|
+
function SecondariesChips({ layers }) {
|
|
216
|
+
return /* @__PURE__ */ jsx("span", { className: "space-x-1", children: layers.map((layer) => /* @__PURE__ */ jsx(
|
|
217
|
+
"span",
|
|
218
|
+
{
|
|
219
|
+
className: "inline-block rounded border border-kumo-line bg-kumo-tint px-1.5 py-0.5 text-xs text-kumo-subtle",
|
|
220
|
+
title: layerOneLineDescription(layer),
|
|
221
|
+
children: layerHumanLabel(layer).toLowerCase()
|
|
222
|
+
},
|
|
223
|
+
layer
|
|
224
|
+
)) });
|
|
225
|
+
}
|
|
226
|
+
function SeverityBadge({ severity }) {
|
|
227
|
+
const classes = severity === "error" ? "bg-red-500/10 text-red-700 border-red-500/40" : severity === "warning" ? "bg-yellow-500/10 text-yellow-800 border-yellow-500/40" : "bg-kumo-tint text-kumo-subtle border-kumo-line";
|
|
228
|
+
return /* @__PURE__ */ jsx(
|
|
229
|
+
"span",
|
|
230
|
+
{
|
|
231
|
+
className: `inline-block rounded border px-1.5 py-0.5 text-xs font-medium ${classes}`,
|
|
232
|
+
children: severity
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
function csrfHeaders() {
|
|
237
|
+
return {
|
|
238
|
+
"Content-Type": "application/json",
|
|
239
|
+
"X-EmDash-Request": "1"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function unwrap(body, res) {
|
|
243
|
+
if (body.error !== void 0) {
|
|
244
|
+
throw new Error(`${body.error.code}: ${body.error.message}`);
|
|
245
|
+
}
|
|
246
|
+
if (body.data === void 0) {
|
|
247
|
+
throw new Error(`Empty response (HTTP ${res.status})`);
|
|
248
|
+
}
|
|
249
|
+
return body.data;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/admin/Absorbability.tsx
|
|
253
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
254
|
+
function Absorbability() {
|
|
255
|
+
return /* @__PURE__ */ jsx2(PillarView, { layer: "absorbability" });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/admin/Citability.tsx
|
|
259
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
260
|
+
function Citability() {
|
|
261
|
+
return /* @__PURE__ */ jsx3(PillarView, { layer: "citability" });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/admin/EntityPresence.tsx
|
|
265
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
266
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
267
|
+
var PLUGIN_ID2 = "aexeo-emdash";
|
|
268
|
+
var API_BASE2 = "/_emdash/api";
|
|
269
|
+
var SOURCE_LABELS = {
|
|
270
|
+
wikipedia: "Wikipedia",
|
|
271
|
+
wikidata: "Wikidata",
|
|
272
|
+
github: "GitHub",
|
|
273
|
+
rdap: "Domain registration",
|
|
274
|
+
common_crawl: "Common Crawl"
|
|
275
|
+
};
|
|
276
|
+
var SOURCE_ORDER = [
|
|
277
|
+
"wikipedia",
|
|
278
|
+
"wikidata",
|
|
279
|
+
"github",
|
|
280
|
+
"rdap",
|
|
281
|
+
"common_crawl"
|
|
282
|
+
];
|
|
283
|
+
function EntityPresence() {
|
|
284
|
+
const [data, setData] = useState2(null);
|
|
285
|
+
const [loadError, setLoadError] = useState2(null);
|
|
286
|
+
const [busy, setBusy] = useState2(null);
|
|
287
|
+
const load = useCallback2(async () => {
|
|
288
|
+
setLoadError(null);
|
|
289
|
+
try {
|
|
290
|
+
const res = await callPresenceRoute("data");
|
|
291
|
+
setData(res);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
setLoadError(error instanceof Error ? error.message : String(error));
|
|
294
|
+
}
|
|
295
|
+
}, []);
|
|
296
|
+
const refresh = useCallback2(async () => {
|
|
297
|
+
setBusy("refresh");
|
|
298
|
+
setLoadError(null);
|
|
299
|
+
try {
|
|
300
|
+
const res = await callPresenceRoute("refresh");
|
|
301
|
+
setData(res);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
setLoadError(error instanceof Error ? error.message : String(error));
|
|
304
|
+
} finally {
|
|
305
|
+
setBusy(null);
|
|
306
|
+
}
|
|
307
|
+
}, []);
|
|
308
|
+
useEffect2(() => {
|
|
309
|
+
void load();
|
|
310
|
+
}, [load]);
|
|
311
|
+
if (loadError !== null) {
|
|
312
|
+
return /* @__PURE__ */ jsxs2("div", { className: "rounded border border-red-300 bg-red-50 p-3 text-sm text-red-900", children: [
|
|
313
|
+
"Failed to load presence data: ",
|
|
314
|
+
loadError
|
|
315
|
+
] });
|
|
316
|
+
}
|
|
317
|
+
if (data === null) {
|
|
318
|
+
return /* @__PURE__ */ jsx4("div", { className: "text-sm text-kumo-subtle", children: "Loading presence data\u2026" });
|
|
319
|
+
}
|
|
320
|
+
if (data.state === "no_manifest") {
|
|
321
|
+
return /* @__PURE__ */ jsx4("div", { className: "rounded border border-kumo-line bg-kumo-canvas p-3 text-sm", children: "Author the truth manifest first (above). The presence checks need at least an organization name and website." });
|
|
322
|
+
}
|
|
323
|
+
if (data.state === "no_organization") {
|
|
324
|
+
return /* @__PURE__ */ jsxs2("div", { className: "rounded border border-kumo-line bg-kumo-canvas p-3 text-sm", children: [
|
|
325
|
+
"The manifest exists but has no organization. Add an",
|
|
326
|
+
" ",
|
|
327
|
+
/* @__PURE__ */ jsx4("code", { children: "organization" }),
|
|
328
|
+
" entry with at least a ",
|
|
329
|
+
/* @__PURE__ */ jsx4("code", { children: "name" }),
|
|
330
|
+
" ",
|
|
331
|
+
"and ideally a ",
|
|
332
|
+
/* @__PURE__ */ jsx4("code", { children: "website" }),
|
|
333
|
+
", then refresh."
|
|
334
|
+
] });
|
|
335
|
+
}
|
|
336
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
|
|
337
|
+
/* @__PURE__ */ jsxs2("header", { className: "flex items-baseline justify-between gap-3", children: [
|
|
338
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
339
|
+
/* @__PURE__ */ jsx4("h3", { className: "text-sm font-medium", children: "Public web presence" }),
|
|
340
|
+
/* @__PURE__ */ jsxs2("p", { className: "mt-1 text-xs text-kumo-subtle", children: [
|
|
341
|
+
"What the open web shows about",
|
|
342
|
+
" ",
|
|
343
|
+
/* @__PURE__ */ jsx4("strong", { children: data.entityName ?? "this entity" }),
|
|
344
|
+
". Aexeo does not score this layer \u2014 it surfaces it. Click through each row to verify."
|
|
345
|
+
] })
|
|
346
|
+
] }),
|
|
347
|
+
/* @__PURE__ */ jsx4(
|
|
348
|
+
"button",
|
|
349
|
+
{
|
|
350
|
+
type: "button",
|
|
351
|
+
onClick: () => void refresh(),
|
|
352
|
+
disabled: busy !== null,
|
|
353
|
+
className: "rounded border border-kumo-line px-3 py-1 text-xs hover:bg-kumo-canvas disabled:opacity-50",
|
|
354
|
+
children: busy === "refresh" ? "Checking\u2026" : "Refresh"
|
|
355
|
+
}
|
|
356
|
+
)
|
|
357
|
+
] }),
|
|
358
|
+
data.state === "empty" && /* @__PURE__ */ jsxs2("div", { className: "rounded border border-kumo-line bg-kumo-canvas p-3 text-sm", children: [
|
|
359
|
+
"No checks have been run yet for",
|
|
360
|
+
" ",
|
|
361
|
+
/* @__PURE__ */ jsx4("strong", { children: data.entityName }),
|
|
362
|
+
". Hit Refresh to query the five sources."
|
|
363
|
+
] }),
|
|
364
|
+
data.state === "stale" && data.ageMinutes !== null && /* @__PURE__ */ jsxs2("div", { className: "rounded border border-amber-200 bg-amber-50 p-2 text-xs text-amber-900", children: [
|
|
365
|
+
"Cached results are ",
|
|
366
|
+
formatAge(data.ageMinutes),
|
|
367
|
+
" old. Hit Refresh to re-check."
|
|
368
|
+
] }),
|
|
369
|
+
data.state === "fresh" && data.ageMinutes !== null && /* @__PURE__ */ jsxs2("div", { className: "text-xs text-kumo-subtle", children: [
|
|
370
|
+
"Last checked ",
|
|
371
|
+
formatAge(data.ageMinutes),
|
|
372
|
+
" ago."
|
|
373
|
+
] }),
|
|
374
|
+
(data.state === "fresh" || data.state === "stale") && /* @__PURE__ */ jsx4("ul", { className: "space-y-2", children: orderResults(data.results).map((result) => /* @__PURE__ */ jsx4(SourceCard, { result }, result.source)) })
|
|
375
|
+
] });
|
|
376
|
+
}
|
|
377
|
+
function SourceCard({ result }) {
|
|
378
|
+
const label = SOURCE_LABELS[result.source] ?? result.source;
|
|
379
|
+
const tone = toneFor(result.status);
|
|
380
|
+
return /* @__PURE__ */ jsx4(
|
|
381
|
+
"li",
|
|
382
|
+
{
|
|
383
|
+
className: `rounded border p-3 ${tone.border} ${tone.bg} text-sm`,
|
|
384
|
+
children: /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-3", children: [
|
|
385
|
+
/* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
|
|
386
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
387
|
+
/* @__PURE__ */ jsx4("span", { className: `text-xs font-medium ${tone.icon}`, children: tone.symbol }),
|
|
388
|
+
/* @__PURE__ */ jsx4("span", { className: "font-medium", children: label }),
|
|
389
|
+
/* @__PURE__ */ jsx4("span", { className: `text-xs ${tone.statusText}`, children: statusLabel(result.status) })
|
|
390
|
+
] }),
|
|
391
|
+
result.label !== void 0 && /* @__PURE__ */ jsx4("div", { className: "mt-1 truncate", children: result.label }),
|
|
392
|
+
result.extra !== void 0 && /* @__PURE__ */ jsx4("div", { className: "mt-0.5 text-xs text-kumo-subtle", children: result.extra }),
|
|
393
|
+
result.error !== void 0 && /* @__PURE__ */ jsx4("div", { className: "mt-0.5 text-xs italic text-kumo-subtle", children: result.error })
|
|
394
|
+
] }),
|
|
395
|
+
result.url !== void 0 && /* @__PURE__ */ jsx4(
|
|
396
|
+
"a",
|
|
397
|
+
{
|
|
398
|
+
href: result.url,
|
|
399
|
+
target: "_blank",
|
|
400
|
+
rel: "noreferrer noopener",
|
|
401
|
+
className: "shrink-0 self-center text-xs underline hover:no-underline",
|
|
402
|
+
children: "Open \u2197"
|
|
403
|
+
}
|
|
404
|
+
)
|
|
405
|
+
] })
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
function toneFor(status) {
|
|
410
|
+
switch (status) {
|
|
411
|
+
case "found":
|
|
412
|
+
return {
|
|
413
|
+
border: "border-emerald-200",
|
|
414
|
+
bg: "bg-emerald-50",
|
|
415
|
+
icon: "text-emerald-700",
|
|
416
|
+
statusText: "text-emerald-700",
|
|
417
|
+
symbol: "\u2713"
|
|
418
|
+
};
|
|
419
|
+
case "not_found":
|
|
420
|
+
return {
|
|
421
|
+
border: "border-kumo-line",
|
|
422
|
+
bg: "bg-kumo-canvas",
|
|
423
|
+
icon: "text-kumo-subtle",
|
|
424
|
+
statusText: "text-kumo-subtle",
|
|
425
|
+
symbol: "\xB7"
|
|
426
|
+
};
|
|
427
|
+
case "unreachable":
|
|
428
|
+
return {
|
|
429
|
+
border: "border-amber-200",
|
|
430
|
+
bg: "bg-amber-50",
|
|
431
|
+
icon: "text-amber-800",
|
|
432
|
+
statusText: "text-amber-800",
|
|
433
|
+
symbol: "!"
|
|
434
|
+
};
|
|
435
|
+
case "skipped":
|
|
436
|
+
return {
|
|
437
|
+
border: "border-kumo-line",
|
|
438
|
+
bg: "bg-kumo-canvas",
|
|
439
|
+
icon: "text-kumo-subtle",
|
|
440
|
+
statusText: "text-kumo-subtle",
|
|
441
|
+
symbol: "\u2014"
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function statusLabel(status) {
|
|
446
|
+
switch (status) {
|
|
447
|
+
case "found":
|
|
448
|
+
return "found";
|
|
449
|
+
case "not_found":
|
|
450
|
+
return "no record";
|
|
451
|
+
case "unreachable":
|
|
452
|
+
return "couldn't reach";
|
|
453
|
+
case "skipped":
|
|
454
|
+
return "skipped";
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function orderResults(results) {
|
|
458
|
+
const known = SOURCE_ORDER.map(
|
|
459
|
+
(name) => results.find((r) => r.source === name) ?? null
|
|
460
|
+
).filter((r) => r !== null);
|
|
461
|
+
const unknown = results.filter(
|
|
462
|
+
(r) => !SOURCE_ORDER.includes(r.source)
|
|
463
|
+
);
|
|
464
|
+
return [...known, ...unknown];
|
|
465
|
+
}
|
|
466
|
+
function formatAge(minutes) {
|
|
467
|
+
if (minutes < 60) return `${minutes} min`;
|
|
468
|
+
const hours = Math.floor(minutes / 60);
|
|
469
|
+
if (hours < 24) {
|
|
470
|
+
const remMin = minutes % 60;
|
|
471
|
+
return remMin === 0 ? `${hours}h` : `${hours}h ${remMin}m`;
|
|
472
|
+
}
|
|
473
|
+
const days = Math.floor(hours / 24);
|
|
474
|
+
return `${days}d`;
|
|
475
|
+
}
|
|
476
|
+
async function callPresenceRoute(kind) {
|
|
477
|
+
const res = await fetch(`${API_BASE2}/plugins/${PLUGIN_ID2}/presence`, {
|
|
478
|
+
method: "POST",
|
|
479
|
+
headers: {
|
|
480
|
+
"Content-Type": "application/json",
|
|
481
|
+
// Required by emdash's catch-all plugin-route handler for any
|
|
482
|
+
// POST/PUT/PATCH/DELETE on a private (default) plugin route.
|
|
483
|
+
// Without it the host returns 403 CSRF_REJECTED before the
|
|
484
|
+
// plugin handler even runs. Same convention as the /facts and
|
|
485
|
+
// /data routes the other admin components use.
|
|
486
|
+
"X-EmDash-Request": "1"
|
|
487
|
+
},
|
|
488
|
+
body: JSON.stringify({ kind })
|
|
489
|
+
});
|
|
490
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
491
|
+
const body = await res.json();
|
|
492
|
+
if ("error" in body) {
|
|
493
|
+
throw new Error(body.error.message ?? "presence route returned error");
|
|
494
|
+
}
|
|
495
|
+
return body.data;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/admin/Facts.tsx
|
|
499
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useState as useState3 } from "react";
|
|
500
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
501
|
+
var PLUGIN_ID3 = "aexeo-emdash";
|
|
502
|
+
var API_BASE3 = "/_emdash/api";
|
|
503
|
+
function Facts() {
|
|
504
|
+
const [data, setData] = useState3(null);
|
|
505
|
+
const [loadError, setLoadError] = useState3(null);
|
|
506
|
+
const [draft, setDraft] = useState3("");
|
|
507
|
+
const [validation, setValidation] = useState3(null);
|
|
508
|
+
const [busy, setBusy] = useState3(null);
|
|
509
|
+
const [toast, setToast] = useState3(null);
|
|
510
|
+
const loadCurrent = useCallback3(async () => {
|
|
14
511
|
setLoadError(null);
|
|
15
512
|
try {
|
|
16
513
|
const res = await callRoute("data", {});
|
|
@@ -23,10 +520,10 @@ function Facts() {
|
|
|
23
520
|
setLoadError(err instanceof Error ? err.message : String(err));
|
|
24
521
|
}
|
|
25
522
|
}, []);
|
|
26
|
-
|
|
523
|
+
useEffect3(() => {
|
|
27
524
|
void loadCurrent();
|
|
28
525
|
}, [loadCurrent]);
|
|
29
|
-
const onCopyPrompt =
|
|
526
|
+
const onCopyPrompt = useCallback3(async () => {
|
|
30
527
|
setBusy("prompt");
|
|
31
528
|
setToast(null);
|
|
32
529
|
try {
|
|
@@ -43,7 +540,7 @@ function Facts() {
|
|
|
43
540
|
setBusy(null);
|
|
44
541
|
}
|
|
45
542
|
}, []);
|
|
46
|
-
const onValidate =
|
|
543
|
+
const onValidate = useCallback3(async () => {
|
|
47
544
|
setBusy("validate");
|
|
48
545
|
setToast(null);
|
|
49
546
|
setValidation(null);
|
|
@@ -60,7 +557,7 @@ function Facts() {
|
|
|
60
557
|
setBusy(null);
|
|
61
558
|
}
|
|
62
559
|
}, [draft]);
|
|
63
|
-
const onSave =
|
|
560
|
+
const onSave = useCallback3(async () => {
|
|
64
561
|
setBusy("save");
|
|
65
562
|
setToast(null);
|
|
66
563
|
try {
|
|
@@ -76,15 +573,15 @@ function Facts() {
|
|
|
76
573
|
}
|
|
77
574
|
}, [draft, loadCurrent]);
|
|
78
575
|
const validationOk = validation !== null && validation.validation.valid && validation.validation.errors.length === 0 && validation.assessment.mismatches.filter((m) => m.severity === "error").length === 0;
|
|
79
|
-
return /* @__PURE__ */
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
/* @__PURE__ */
|
|
82
|
-
/* @__PURE__ */
|
|
576
|
+
return /* @__PURE__ */ jsxs3("div", { className: "space-y-6 p-4", children: [
|
|
577
|
+
/* @__PURE__ */ jsxs3("header", { children: [
|
|
578
|
+
/* @__PURE__ */ jsx5("h1", { className: "text-xl font-semibold", children: "Truth manifest" }),
|
|
579
|
+
/* @__PURE__ */ jsxs3("p", { className: "mt-1 text-sm text-kumo-subtle", children: [
|
|
83
580
|
"Author a ",
|
|
84
|
-
/* @__PURE__ */
|
|
581
|
+
/* @__PURE__ */ jsx5("code", { children: "facts.json" }),
|
|
85
582
|
" using your LLM of choice. The plugin frames the question, validates the answer, and stores the result. Read",
|
|
86
583
|
" ",
|
|
87
|
-
/* @__PURE__ */
|
|
584
|
+
/* @__PURE__ */ jsx5(
|
|
88
585
|
"a",
|
|
89
586
|
{
|
|
90
587
|
href: "https://github.com/schiste/Aexeo/blob/main/docs/facts-manifest.md",
|
|
@@ -98,18 +595,18 @@ function Facts() {
|
|
|
98
595
|
"for the philosophy behind this flow."
|
|
99
596
|
] })
|
|
100
597
|
] }),
|
|
101
|
-
loadError !== null && /* @__PURE__ */
|
|
598
|
+
loadError !== null && /* @__PURE__ */ jsxs3("div", { className: "rounded border border-red-500/40 bg-red-500/10 p-3 text-sm text-red-700", children: [
|
|
102
599
|
"Failed to load manifest state: ",
|
|
103
600
|
loadError
|
|
104
601
|
] }),
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
/* @__PURE__ */
|
|
107
|
-
/* @__PURE__ */
|
|
602
|
+
/* @__PURE__ */ jsxs3("section", { className: "rounded border border-kumo-line p-3", children: [
|
|
603
|
+
/* @__PURE__ */ jsx5("h2", { className: "text-sm font-medium", children: "Status" }),
|
|
604
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-2 text-sm", children: data === null ? "Loading\u2026" : data.present ? "\u2713 A truth manifest is stored. Editing the JSON below replaces it on Save." : "No truth manifest stored. Generate the prompt below to author one." })
|
|
108
605
|
] }),
|
|
109
|
-
/* @__PURE__ */
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
-
/* @__PURE__ */
|
|
112
|
-
/* @__PURE__ */
|
|
606
|
+
/* @__PURE__ */ jsxs3("section", { className: "space-y-3 rounded border border-kumo-line p-3", children: [
|
|
607
|
+
/* @__PURE__ */ jsx5("h2", { className: "text-sm font-medium", children: "1. Generate authoring prompt" }),
|
|
608
|
+
/* @__PURE__ */ jsx5("p", { className: "text-sm text-kumo-subtle", children: "Copies a populated prompt to your clipboard. Paste it into Claude/GPT/etc., answer the LLM's questions, and bring the resulting JSON back here." }),
|
|
609
|
+
/* @__PURE__ */ jsx5(
|
|
113
610
|
"button",
|
|
114
611
|
{
|
|
115
612
|
type: "button",
|
|
@@ -120,9 +617,9 @@ function Facts() {
|
|
|
120
617
|
}
|
|
121
618
|
)
|
|
122
619
|
] }),
|
|
123
|
-
/* @__PURE__ */
|
|
124
|
-
/* @__PURE__ */
|
|
125
|
-
/* @__PURE__ */
|
|
620
|
+
/* @__PURE__ */ jsxs3("section", { className: "space-y-3 rounded border border-kumo-line p-3", children: [
|
|
621
|
+
/* @__PURE__ */ jsx5("h2", { className: "text-sm font-medium", children: "2. Paste & validate" }),
|
|
622
|
+
/* @__PURE__ */ jsx5(
|
|
126
623
|
"textarea",
|
|
127
624
|
{
|
|
128
625
|
value: draft,
|
|
@@ -133,8 +630,8 @@ function Facts() {
|
|
|
133
630
|
className: "w-full rounded border border-kumo-line bg-kumo-tint p-2 font-mono text-xs"
|
|
134
631
|
}
|
|
135
632
|
),
|
|
136
|
-
/* @__PURE__ */
|
|
137
|
-
/* @__PURE__ */
|
|
633
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
|
|
634
|
+
/* @__PURE__ */ jsx5(
|
|
138
635
|
"button",
|
|
139
636
|
{
|
|
140
637
|
type: "button",
|
|
@@ -144,7 +641,7 @@ function Facts() {
|
|
|
144
641
|
children: busy === "validate" ? "Validating\u2026" : "Validate"
|
|
145
642
|
}
|
|
146
643
|
),
|
|
147
|
-
/* @__PURE__ */
|
|
644
|
+
/* @__PURE__ */ jsx5(
|
|
148
645
|
"button",
|
|
149
646
|
{
|
|
150
647
|
type: "button",
|
|
@@ -157,8 +654,8 @@ function Facts() {
|
|
|
157
654
|
)
|
|
158
655
|
] })
|
|
159
656
|
] }),
|
|
160
|
-
toast !== null && /* @__PURE__ */
|
|
161
|
-
validation !== null && /* @__PURE__ */
|
|
657
|
+
toast !== null && /* @__PURE__ */ jsx5("div", { className: "rounded border border-kumo-line bg-kumo-tint p-3 text-sm", children: toast }),
|
|
658
|
+
validation !== null && /* @__PURE__ */ jsx5(ValidationReport, { result: validation })
|
|
162
659
|
] });
|
|
163
660
|
}
|
|
164
661
|
function ValidationReport({
|
|
@@ -170,13 +667,13 @@ function ValidationReport({
|
|
|
170
667
|
const warnMismatches = result.assessment.mismatches.filter(
|
|
171
668
|
(m) => m.severity === "warning"
|
|
172
669
|
);
|
|
173
|
-
return /* @__PURE__ */
|
|
174
|
-
/* @__PURE__ */
|
|
175
|
-
/* @__PURE__ */
|
|
176
|
-
/* @__PURE__ */
|
|
670
|
+
return /* @__PURE__ */ jsxs3("section", { className: "space-y-3 rounded border border-kumo-line p-3", children: [
|
|
671
|
+
/* @__PURE__ */ jsx5("h2", { className: "text-sm font-medium", children: "Validation result" }),
|
|
672
|
+
/* @__PURE__ */ jsxs3("div", { className: "text-sm", children: [
|
|
673
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
177
674
|
"Shape:",
|
|
178
675
|
" ",
|
|
179
|
-
/* @__PURE__ */
|
|
676
|
+
/* @__PURE__ */ jsx5("strong", { children: result.validation.valid ? "valid" : "invalid" }),
|
|
180
677
|
" ",
|
|
181
678
|
"\xB7 org",
|
|
182
679
|
" ",
|
|
@@ -192,14 +689,14 @@ function ValidationReport({
|
|
|
192
689
|
result.validation.forbidden_term_count,
|
|
193
690
|
" forbidden terms"
|
|
194
691
|
] }),
|
|
195
|
-
/* @__PURE__ */
|
|
692
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
196
693
|
"Score: ",
|
|
197
|
-
/* @__PURE__ */
|
|
694
|
+
/* @__PURE__ */ jsx5("strong", { children: result.assessment.score }),
|
|
198
695
|
"/",
|
|
199
696
|
result.assessment.score_ceiling,
|
|
200
697
|
" \xB7 source =",
|
|
201
698
|
" ",
|
|
202
|
-
/* @__PURE__ */
|
|
699
|
+
/* @__PURE__ */ jsx5("code", { children: result.assessment.structured_truth_source }),
|
|
203
700
|
" \xB7",
|
|
204
701
|
" ",
|
|
205
702
|
result.assessment.pages_analyzed,
|
|
@@ -209,23 +706,23 @@ function ValidationReport({
|
|
|
209
706
|
" with schema.org"
|
|
210
707
|
] })
|
|
211
708
|
] }),
|
|
212
|
-
result.validation.errors.length > 0 && /* @__PURE__ */
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
-
/* @__PURE__ */
|
|
709
|
+
result.validation.errors.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "rounded border border-red-500/40 bg-red-500/10 p-2 text-sm", children: [
|
|
710
|
+
/* @__PURE__ */ jsx5("strong", { children: "Schema errors:" }),
|
|
711
|
+
/* @__PURE__ */ jsx5("ul", { className: "mt-1 list-disc pl-5", children: result.validation.errors.map((e, i) => /* @__PURE__ */ jsx5("li", { children: e }, i)) })
|
|
215
712
|
] }),
|
|
216
|
-
result.validation.warnings.length > 0 && /* @__PURE__ */
|
|
217
|
-
/* @__PURE__ */
|
|
218
|
-
/* @__PURE__ */
|
|
713
|
+
result.validation.warnings.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "rounded border border-yellow-500/40 bg-yellow-500/10 p-2 text-sm", children: [
|
|
714
|
+
/* @__PURE__ */ jsx5("strong", { children: "Schema warnings:" }),
|
|
715
|
+
/* @__PURE__ */ jsx5("ul", { className: "mt-1 list-disc pl-5", children: result.validation.warnings.map((w, i) => /* @__PURE__ */ jsx5("li", { children: w }, i)) })
|
|
219
716
|
] }),
|
|
220
|
-
errMismatches.length > 0 && /* @__PURE__ */
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
-
/* @__PURE__ */
|
|
717
|
+
errMismatches.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "rounded border border-red-500/40 bg-red-500/10 p-2 text-sm", children: [
|
|
718
|
+
/* @__PURE__ */ jsx5("strong", { children: "Mismatches with site (errors):" }),
|
|
719
|
+
/* @__PURE__ */ jsx5(MismatchList, { items: errMismatches })
|
|
223
720
|
] }),
|
|
224
|
-
warnMismatches.length > 0 && /* @__PURE__ */
|
|
225
|
-
/* @__PURE__ */
|
|
226
|
-
/* @__PURE__ */
|
|
721
|
+
warnMismatches.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "rounded border border-yellow-500/40 bg-yellow-500/10 p-2 text-sm", children: [
|
|
722
|
+
/* @__PURE__ */ jsx5("strong", { children: "Mismatches with site (warnings):" }),
|
|
723
|
+
/* @__PURE__ */ jsx5(MismatchList, { items: warnMismatches })
|
|
227
724
|
] }),
|
|
228
|
-
result.validation.errors.length === 0 && result.assessment.mismatches.length === 0 && /* @__PURE__ */
|
|
725
|
+
result.validation.errors.length === 0 && result.assessment.mismatches.length === 0 && /* @__PURE__ */ jsxs3("div", { className: "rounded border border-green-500/40 bg-green-500/10 p-2 text-sm", children: [
|
|
229
726
|
"\u2713 Manifest is valid and agrees with on-page schema.org. Click",
|
|
230
727
|
" ",
|
|
231
728
|
"Save to persist."
|
|
@@ -235,18 +732,18 @@ function ValidationReport({
|
|
|
235
732
|
function MismatchList({
|
|
236
733
|
items
|
|
237
734
|
}) {
|
|
238
|
-
return /* @__PURE__ */
|
|
239
|
-
/* @__PURE__ */
|
|
735
|
+
return /* @__PURE__ */ jsx5("ul", { className: "mt-1 list-disc pl-5", children: items.map((m, i) => /* @__PURE__ */ jsxs3("li", { children: [
|
|
736
|
+
/* @__PURE__ */ jsx5("code", { className: "text-xs", children: m.route }),
|
|
240
737
|
" \xB7 ",
|
|
241
738
|
m.field,
|
|
242
739
|
": expected",
|
|
243
740
|
" ",
|
|
244
|
-
/* @__PURE__ */
|
|
741
|
+
/* @__PURE__ */ jsx5("code", { className: "text-xs", children: m.expected }),
|
|
245
742
|
", observed",
|
|
246
743
|
" ",
|
|
247
|
-
/* @__PURE__ */
|
|
744
|
+
/* @__PURE__ */ jsx5("code", { className: "text-xs", children: m.observed }),
|
|
248
745
|
" ",
|
|
249
|
-
/* @__PURE__ */
|
|
746
|
+
/* @__PURE__ */ jsxs3("span", { className: "text-xs text-kumo-subtle", children: [
|
|
250
747
|
"(",
|
|
251
748
|
m.source,
|
|
252
749
|
")"
|
|
@@ -254,7 +751,7 @@ function MismatchList({
|
|
|
254
751
|
] }, i)) });
|
|
255
752
|
}
|
|
256
753
|
async function callRoute(kind, extra) {
|
|
257
|
-
const res = await fetch(`${
|
|
754
|
+
const res = await fetch(`${API_BASE3}/plugins/${PLUGIN_ID3}/facts`, {
|
|
258
755
|
method: "POST",
|
|
259
756
|
headers: {
|
|
260
757
|
"Content-Type": "application/json",
|
|
@@ -272,29 +769,48 @@ async function callRoute(kind, extra) {
|
|
|
272
769
|
return body.data;
|
|
273
770
|
}
|
|
274
771
|
|
|
772
|
+
// src/admin/EntityLegitimacy.tsx
|
|
773
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
774
|
+
function EntityLegitimacy() {
|
|
775
|
+
return /* @__PURE__ */ jsx6(
|
|
776
|
+
PillarView,
|
|
777
|
+
{
|
|
778
|
+
layer: "entity_legitimacy",
|
|
779
|
+
extraSlot: /* @__PURE__ */ jsxs4("div", { className: "space-y-3", children: [
|
|
780
|
+
/* @__PURE__ */ jsxs4("section", { className: "rounded border border-kumo-line p-3", children: [
|
|
781
|
+
/* @__PURE__ */ jsx6("h2", { className: "text-sm font-medium", children: "Truth manifest" }),
|
|
782
|
+
/* @__PURE__ */ jsx6("p", { className: "mt-1 text-xs text-kumo-subtle", children: "The structured assertion of who the organization is, what the products are, and the terminology to use/avoid. This is the primary entity-legitimacy signal Aexeo can write \u2014 the rest comes from third-party presence Aexeo can only observe." }),
|
|
783
|
+
/* @__PURE__ */ jsx6("div", { className: "mt-3", children: /* @__PURE__ */ jsx6(Facts, {}) })
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ jsx6("section", { className: "rounded border border-kumo-line p-3", children: /* @__PURE__ */ jsx6(EntityPresence, {}) })
|
|
786
|
+
] })
|
|
787
|
+
}
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
|
|
275
791
|
// src/admin/Findings.tsx
|
|
276
|
-
import { useCallback as
|
|
277
|
-
import { Fragment, jsx as
|
|
278
|
-
var
|
|
279
|
-
var
|
|
792
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useState as useState4 } from "react";
|
|
793
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
794
|
+
var PLUGIN_ID4 = "aexeo-emdash";
|
|
795
|
+
var API_BASE4 = "/_emdash/api";
|
|
280
796
|
function Findings() {
|
|
281
|
-
const [state, setState] =
|
|
797
|
+
const [state, setState] = useState4({
|
|
282
798
|
loading: true,
|
|
283
799
|
error: null,
|
|
284
800
|
payload: null
|
|
285
801
|
});
|
|
286
|
-
const [refreshing, setRefreshing] =
|
|
287
|
-
const [toast, setToast] =
|
|
288
|
-
const load =
|
|
802
|
+
const [refreshing, setRefreshing] = useState4(false);
|
|
803
|
+
const [toast, setToast] = useState4(null);
|
|
804
|
+
const load = useCallback4(async () => {
|
|
289
805
|
setState((s) => ({ ...s, loading: true, error: null }));
|
|
290
806
|
try {
|
|
291
|
-
const res = await fetch(`${
|
|
807
|
+
const res = await fetch(`${API_BASE4}/plugins/${PLUGIN_ID4}/data`, {
|
|
292
808
|
method: "POST",
|
|
293
|
-
headers:
|
|
809
|
+
headers: csrfHeaders2(),
|
|
294
810
|
body: "{}"
|
|
295
811
|
});
|
|
296
812
|
const body = await res.json();
|
|
297
|
-
const payload2 =
|
|
813
|
+
const payload2 = unwrap2(body, res);
|
|
298
814
|
setState({ loading: false, error: null, payload: payload2 });
|
|
299
815
|
} catch (err) {
|
|
300
816
|
setState({
|
|
@@ -304,17 +820,17 @@ function Findings() {
|
|
|
304
820
|
});
|
|
305
821
|
}
|
|
306
822
|
}, []);
|
|
307
|
-
const refresh =
|
|
823
|
+
const refresh = useCallback4(async () => {
|
|
308
824
|
setRefreshing(true);
|
|
309
825
|
setToast(null);
|
|
310
826
|
try {
|
|
311
|
-
const res = await fetch(`${
|
|
827
|
+
const res = await fetch(`${API_BASE4}/plugins/${PLUGIN_ID4}/refresh`, {
|
|
312
828
|
method: "POST",
|
|
313
|
-
headers:
|
|
829
|
+
headers: csrfHeaders2(),
|
|
314
830
|
body: "{}"
|
|
315
831
|
});
|
|
316
832
|
const body = await res.json();
|
|
317
|
-
const data =
|
|
833
|
+
const data = unwrap2(body, res);
|
|
318
834
|
setState({ loading: false, error: null, payload: data.payload });
|
|
319
835
|
if (data.summary) {
|
|
320
836
|
const { documentsScanned, routesUpdated, totalFindings, errors } = data.summary;
|
|
@@ -334,20 +850,20 @@ function Findings() {
|
|
|
334
850
|
setRefreshing(false);
|
|
335
851
|
}
|
|
336
852
|
}, []);
|
|
337
|
-
|
|
853
|
+
useEffect4(() => {
|
|
338
854
|
void load();
|
|
339
855
|
}, [load]);
|
|
340
856
|
if (state.loading && state.payload === null) {
|
|
341
|
-
return /* @__PURE__ */
|
|
857
|
+
return /* @__PURE__ */ jsx7("div", { className: "p-4 text-sm text-kumo-subtle", children: "Loading findings\u2026" });
|
|
342
858
|
}
|
|
343
859
|
if (state.error !== null) {
|
|
344
|
-
return /* @__PURE__ */
|
|
345
|
-
/* @__PURE__ */
|
|
346
|
-
/* @__PURE__ */
|
|
860
|
+
return /* @__PURE__ */ jsxs5("div", { className: "p-4", children: [
|
|
861
|
+
/* @__PURE__ */ jsx7("h1", { className: "text-xl font-semibold", children: "SEO findings" }),
|
|
862
|
+
/* @__PURE__ */ jsxs5("p", { className: "mt-2 rounded border border-red-500/40 bg-red-500/10 p-3 text-sm text-red-700", children: [
|
|
347
863
|
"Failed to load: ",
|
|
348
864
|
state.error
|
|
349
865
|
] }),
|
|
350
|
-
/* @__PURE__ */
|
|
866
|
+
/* @__PURE__ */ jsx7(
|
|
351
867
|
"button",
|
|
352
868
|
{
|
|
353
869
|
type: "button",
|
|
@@ -360,17 +876,17 @@ function Findings() {
|
|
|
360
876
|
}
|
|
361
877
|
const payload = state.payload;
|
|
362
878
|
if (payload === null) {
|
|
363
|
-
return /* @__PURE__ */
|
|
879
|
+
return /* @__PURE__ */ jsx7("div", { className: "p-4 text-sm text-kumo-subtle", children: "No data." });
|
|
364
880
|
}
|
|
365
881
|
const routesByPath = /* @__PURE__ */ new Map();
|
|
366
882
|
for (const route of payload.routes) {
|
|
367
883
|
routesByPath.set(route.route, route);
|
|
368
884
|
}
|
|
369
|
-
return /* @__PURE__ */
|
|
370
|
-
/* @__PURE__ */
|
|
371
|
-
/* @__PURE__ */
|
|
372
|
-
/* @__PURE__ */
|
|
373
|
-
/* @__PURE__ */
|
|
885
|
+
return /* @__PURE__ */ jsxs5("div", { className: "space-y-4 p-4", children: [
|
|
886
|
+
/* @__PURE__ */ jsxs5("header", { className: "flex items-center justify-between gap-4", children: [
|
|
887
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
888
|
+
/* @__PURE__ */ jsx7("h1", { className: "text-xl font-semibold", children: "SEO findings" }),
|
|
889
|
+
/* @__PURE__ */ jsx7("p", { className: "mt-1 text-sm text-kumo-subtle", children: payload.totals.findings === 0 ? /* @__PURE__ */ jsx7(Fragment2, { children: "No findings yet \u2014 click Refresh to evaluate the site." }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
374
890
|
payload.totals.findings,
|
|
375
891
|
" findings across ",
|
|
376
892
|
payload.totals.routes,
|
|
@@ -382,7 +898,7 @@ function Findings() {
|
|
|
382
898
|
" warnings."
|
|
383
899
|
] }) })
|
|
384
900
|
] }),
|
|
385
|
-
/* @__PURE__ */
|
|
901
|
+
/* @__PURE__ */ jsx7(
|
|
386
902
|
"button",
|
|
387
903
|
{
|
|
388
904
|
type: "button",
|
|
@@ -393,51 +909,51 @@ function Findings() {
|
|
|
393
909
|
}
|
|
394
910
|
)
|
|
395
911
|
] }),
|
|
396
|
-
toast !== null && /* @__PURE__ */
|
|
397
|
-
payload.sitewideFindings.length > 0 && /* @__PURE__ */
|
|
398
|
-
/* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
401
|
-
/* @__PURE__ */
|
|
402
|
-
/* @__PURE__ */
|
|
912
|
+
toast !== null && /* @__PURE__ */ jsx7("div", { className: "rounded border border-kumo-line bg-kumo-tint p-3 text-sm", children: toast }),
|
|
913
|
+
payload.sitewideFindings.length > 0 && /* @__PURE__ */ jsxs5("section", { className: "rounded border border-kumo-line p-3", children: [
|
|
914
|
+
/* @__PURE__ */ jsx7("h2", { className: "text-sm font-medium", children: "Sitewide" }),
|
|
915
|
+
/* @__PURE__ */ jsx7("ul", { className: "mt-2 space-y-1 text-sm", children: payload.sitewideFindings.map((finding, i) => /* @__PURE__ */ jsxs5("li", { className: "flex items-start gap-2", children: [
|
|
916
|
+
/* @__PURE__ */ jsx7(SeverityBadge2, { severity: finding.severity }),
|
|
917
|
+
/* @__PURE__ */ jsx7("code", { className: "font-mono text-xs", children: finding.rule_id }),
|
|
918
|
+
/* @__PURE__ */ jsx7("span", { className: "flex-1", children: finding.message })
|
|
403
919
|
] }, i)) })
|
|
404
920
|
] }),
|
|
405
|
-
payload.findings.length === 0 ? /* @__PURE__ */
|
|
406
|
-
/* @__PURE__ */
|
|
407
|
-
/* @__PURE__ */
|
|
408
|
-
/* @__PURE__ */
|
|
409
|
-
/* @__PURE__ */
|
|
410
|
-
/* @__PURE__ */
|
|
921
|
+
payload.findings.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "text-sm text-kumo-subtle", children: payload.totals.documentsIndexed === 0 ? "No documents indexed \u2014 click Refresh to do an initial sweep." : "All indexed documents are clean." }) : /* @__PURE__ */ jsx7("div", { className: "overflow-x-auto rounded border border-kumo-line", children: /* @__PURE__ */ jsxs5("table", { className: "w-full text-left text-sm", children: [
|
|
922
|
+
/* @__PURE__ */ jsx7("thead", { className: "bg-kumo-tint", children: /* @__PURE__ */ jsxs5("tr", { children: [
|
|
923
|
+
/* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Route" }),
|
|
924
|
+
/* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Rule" }),
|
|
925
|
+
/* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Severity" }),
|
|
926
|
+
/* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium text-kumo-subtle", children: "Message" })
|
|
411
927
|
] }) }),
|
|
412
|
-
/* @__PURE__ */
|
|
928
|
+
/* @__PURE__ */ jsx7("tbody", { children: payload.findings.map((finding, i) => {
|
|
413
929
|
const routeInfo = routesByPath.get(finding.route);
|
|
414
|
-
return /* @__PURE__ */
|
|
415
|
-
/* @__PURE__ */
|
|
416
|
-
/* @__PURE__ */
|
|
417
|
-
/* @__PURE__ */
|
|
418
|
-
/* @__PURE__ */
|
|
930
|
+
return /* @__PURE__ */ jsxs5("tr", { className: "border-t border-kumo-line", children: [
|
|
931
|
+
/* @__PURE__ */ jsx7("td", { className: "px-3 py-2 align-top", children: /* @__PURE__ */ jsx7(RouteLinks2, { finding, info: routeInfo }) }),
|
|
932
|
+
/* @__PURE__ */ jsx7("td", { className: "px-3 py-2 align-top", children: /* @__PURE__ */ jsx7("code", { className: "rounded bg-kumo-tint px-1.5 py-0.5 font-mono text-xs", children: finding.rule_id }) }),
|
|
933
|
+
/* @__PURE__ */ jsx7("td", { className: "px-3 py-2 align-top", children: /* @__PURE__ */ jsx7(SeverityBadge2, { severity: finding.severity }) }),
|
|
934
|
+
/* @__PURE__ */ jsx7("td", { className: "px-3 py-2 align-top", children: finding.message })
|
|
419
935
|
] }, i);
|
|
420
936
|
}) })
|
|
421
937
|
] }) })
|
|
422
938
|
] });
|
|
423
939
|
}
|
|
424
|
-
function
|
|
940
|
+
function RouteLinks2({
|
|
425
941
|
finding,
|
|
426
942
|
info
|
|
427
943
|
}) {
|
|
428
944
|
if (info === void 0) {
|
|
429
|
-
return /* @__PURE__ */
|
|
945
|
+
return /* @__PURE__ */ jsx7("span", { className: "font-mono text-xs", children: finding.route });
|
|
430
946
|
}
|
|
431
|
-
return /* @__PURE__ */
|
|
432
|
-
info.editUrl !== "" ? /* @__PURE__ */
|
|
947
|
+
return /* @__PURE__ */ jsxs5("span", { className: "space-x-2", children: [
|
|
948
|
+
info.editUrl !== "" ? /* @__PURE__ */ jsx7(
|
|
433
949
|
"a",
|
|
434
950
|
{
|
|
435
951
|
href: info.editUrl,
|
|
436
952
|
className: "font-mono text-xs text-kumo-link hover:underline",
|
|
437
953
|
children: finding.route
|
|
438
954
|
}
|
|
439
|
-
) : /* @__PURE__ */
|
|
440
|
-
info.publishedUrl !== "" && /* @__PURE__ */
|
|
955
|
+
) : /* @__PURE__ */ jsx7("span", { className: "font-mono text-xs", children: finding.route }),
|
|
956
|
+
info.publishedUrl !== "" && /* @__PURE__ */ jsx7(
|
|
441
957
|
"a",
|
|
442
958
|
{
|
|
443
959
|
href: info.publishedUrl,
|
|
@@ -447,12 +963,12 @@ function RouteLinks({
|
|
|
447
963
|
children: "live \u2197"
|
|
448
964
|
}
|
|
449
965
|
),
|
|
450
|
-
info.status !== "" && info.status !== "published" && /* @__PURE__ */
|
|
966
|
+
info.status !== "" && info.status !== "published" && /* @__PURE__ */ jsx7("span", { className: "text-xs text-kumo-subtle", children: info.status })
|
|
451
967
|
] });
|
|
452
968
|
}
|
|
453
|
-
function
|
|
969
|
+
function SeverityBadge2({ severity }) {
|
|
454
970
|
const classes = severity === "error" ? "bg-red-500/10 text-red-700 border-red-500/40" : severity === "warning" ? "bg-yellow-500/10 text-yellow-800 border-yellow-500/40" : "bg-kumo-tint text-kumo-subtle border-kumo-line";
|
|
455
|
-
return /* @__PURE__ */
|
|
971
|
+
return /* @__PURE__ */ jsx7(
|
|
456
972
|
"span",
|
|
457
973
|
{
|
|
458
974
|
className: `inline-block rounded border px-1.5 py-0.5 text-xs font-medium ${classes}`,
|
|
@@ -460,7 +976,7 @@ function SeverityBadge({ severity }) {
|
|
|
460
976
|
}
|
|
461
977
|
);
|
|
462
978
|
}
|
|
463
|
-
function
|
|
979
|
+
function csrfHeaders2() {
|
|
464
980
|
return {
|
|
465
981
|
"Content-Type": "application/json",
|
|
466
982
|
// emdash's apiFetch wrapper sets this on every mutation;
|
|
@@ -469,7 +985,7 @@ function csrfHeaders() {
|
|
|
469
985
|
"X-EmDash-Request": "1"
|
|
470
986
|
};
|
|
471
987
|
}
|
|
472
|
-
function
|
|
988
|
+
function unwrap2(body, res) {
|
|
473
989
|
if (body.error !== void 0) {
|
|
474
990
|
throw new Error(`${body.error.code}: ${body.error.message}`);
|
|
475
991
|
}
|
|
@@ -479,12 +995,25 @@ function unwrap(body, res) {
|
|
|
479
995
|
return body.data;
|
|
480
996
|
}
|
|
481
997
|
|
|
998
|
+
// src/admin/Retrievability.tsx
|
|
999
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1000
|
+
function Retrievability() {
|
|
1001
|
+
return /* @__PURE__ */ jsx8(PillarView, { layer: "retrievability" });
|
|
1002
|
+
}
|
|
1003
|
+
|
|
482
1004
|
// src/admin.tsx
|
|
483
1005
|
var pages = {
|
|
1006
|
+
// Four pillar pages — the canonical organization going forward.
|
|
1007
|
+
"/retrievability": Retrievability,
|
|
1008
|
+
"/citability": Citability,
|
|
1009
|
+
"/absorbability": Absorbability,
|
|
1010
|
+
"/entity-legitimacy": EntityLegitimacy,
|
|
1011
|
+
// /findings stays as a cross-pillar flat view for users who want
|
|
1012
|
+
// every finding in one list. Not removed because it's still useful
|
|
1013
|
+
// for triage — "show me everything that's wrong" is a real workflow.
|
|
484
1014
|
"/findings": Findings,
|
|
485
|
-
// /facts
|
|
486
|
-
//
|
|
487
|
-
// the React adminEntry. Same mechanism as /findings.
|
|
1015
|
+
// /facts kept as an alias so old bookmarks don't 404. Renders the
|
|
1016
|
+
// same Facts component the EntityLegitimacy page composes.
|
|
488
1017
|
"/facts": Facts
|
|
489
1018
|
};
|
|
490
1019
|
export {
|