@aeptus/aexeo-emdash 0.4.0 → 0.6.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 +126 -11
- package/INSTALL.md +20 -20
- package/LICENSE +1 -1
- package/README.md +101 -6
- package/dist/admin.js +2 -2
- package/dist/configured.d.ts +12 -4
- package/dist/configured.d.ts.map +1 -1
- package/dist/configured.js +77 -25
- package/dist/index.d.ts +46 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -14
- package/dist/plugin.d.ts.map +1 -1
- package/dist/sandbox-entry.js +19 -13
- package/dist/sandbox.d.ts +2 -2
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/suppressions.d.ts +8 -1
- package/dist/suppressions.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +3 -3
- package/wasm/aexeo_emdash_bridge_bg.wasm +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,119 @@ and the project follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.6.0] - 2026-05-03
|
|
10
|
+
|
|
11
|
+
**Breaking change**: the `seogeo` → `aexeo` rename across the
|
|
12
|
+
codebase reaches the published API. Hosts on 0.5.x using the
|
|
13
|
+
sandboxed factory or the env-var fallback need to update their
|
|
14
|
+
imports and config. The configured factory was already named
|
|
15
|
+
`aexeoPlugin` in 0.5.x, so consumers using only that path are
|
|
16
|
+
unaffected.
|
|
17
|
+
|
|
18
|
+
**Compatibility:** verified against emdash `0.7.0` and `0.8.0`.
|
|
19
|
+
|
|
20
|
+
### Changed (breaking)
|
|
21
|
+
|
|
22
|
+
- **Sandboxed factory rename:** `seogeoPluginSandboxed(...)` is now
|
|
23
|
+
`aexeoPluginSandboxed(...)`. Update your import:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
// before (0.5.x)
|
|
27
|
+
import { seogeoPluginSandboxed } from "@aeptus/aexeo-emdash";
|
|
28
|
+
// after (0.6.0)
|
|
29
|
+
import { aexeoPluginSandboxed } from "@aeptus/aexeo-emdash";
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- **Sandboxed options type rename:** `SeogeoSandboxedOptions` →
|
|
33
|
+
`AexeoSandboxedOptions`. Same migration as above for any host
|
|
34
|
+
that referenced the type name explicitly.
|
|
35
|
+
|
|
36
|
+
- **Env var rename:** `SEOGEO_EVALUATOR_HOST` →
|
|
37
|
+
`AEXEO_EVALUATOR_HOST`. Update any CI / Cloudflare Pages build
|
|
38
|
+
config that sets the old name. The fallback chain is unchanged
|
|
39
|
+
otherwise (factory option > env var > none).
|
|
40
|
+
|
|
41
|
+
- **Plugin id rename:** the plugin's emdash id is now `aexeo-emdash`
|
|
42
|
+
(was `aexeo-seogeo`). This affects:
|
|
43
|
+
- admin URL paths: `/admin/plugins/aexeo-emdash/...` (was
|
|
44
|
+
`/admin/plugins/aexeo-seogeo/...`)
|
|
45
|
+
- any host code that referenced the plugin id explicitly (e.g.
|
|
46
|
+
in `usePluginPage(pluginId, ...)` or in plugin-bridge URLs)
|
|
47
|
+
- the React `adminEntry` URL convention is unchanged at the
|
|
48
|
+
HTTP route level (the React pages still mount under the new
|
|
49
|
+
plugin id automatically)
|
|
50
|
+
|
|
51
|
+
- **Widget id rename:** `seogeo-score` → `aexeo-score`. Hosts that
|
|
52
|
+
declared this widget id explicitly in their dashboard config need
|
|
53
|
+
to update.
|
|
54
|
+
|
|
55
|
+
### Notes for hosts upgrading from 0.5.x
|
|
56
|
+
|
|
57
|
+
The configured factory has been `aexeoPlugin` since 0.5.0 — sites
|
|
58
|
+
using only `aexeoPlugin({ ... })` need no code changes for this
|
|
59
|
+
release. The breaking changes above only affect:
|
|
60
|
+
|
|
61
|
+
1. Hosts using the sandboxed factory.
|
|
62
|
+
2. Hosts deploying with the `SEOGEO_EVALUATOR_HOST` env var.
|
|
63
|
+
3. Host-side code or workflows that hardcode the plugin id
|
|
64
|
+
(`aexeo-seogeo`) or widget id (`seogeo-score`).
|
|
65
|
+
|
|
66
|
+
Migration is a sed-and-rebuild for most hosts. The rename was the
|
|
67
|
+
last user-facing trace of the original `seogeo` codebase name —
|
|
68
|
+
0.6.0 closes the renaming pass that started across the workspace
|
|
69
|
+
in late April.
|
|
70
|
+
|
|
71
|
+
## [0.5.0] - 2026-05-01
|
|
72
|
+
|
|
73
|
+
Three follow-ups from real-world adoption feedback after 0.4.0:
|
|
74
|
+
|
|
75
|
+
- richer suppression selectors so editors can target by collection
|
|
76
|
+
and document status, not just route + rule
|
|
77
|
+
- ergonomic collections API (`includeCollections` /
|
|
78
|
+
`excludeCollections`) for sites whose schema is close to the
|
|
79
|
+
default but needs minor tweaks
|
|
80
|
+
- README now leads with the suppressions glob semantics so consumers
|
|
81
|
+
migrating from local patches don't have to dig through the
|
|
82
|
+
CHANGELOG to learn that `*` is single-segment and `**` is recursive
|
|
83
|
+
|
|
84
|
+
**Compatibility:** verified against emdash `0.7.0` and `0.8.0`.
|
|
85
|
+
|
|
86
|
+
### Added
|
|
87
|
+
|
|
88
|
+
- **`Suppression.collections`** — silence findings for documents in
|
|
89
|
+
the named emdash collections.
|
|
90
|
+
- **`Suppression.statuses`** — silence findings for documents in the
|
|
91
|
+
named statuses (`"draft"`, `"published"`, etc.). Useful for
|
|
92
|
+
preventing draft-stage findings from cluttering the dashboard.
|
|
93
|
+
- **`includeCollections`** factory option — adds slugs to the
|
|
94
|
+
default `["posts", "pages"]`. For sites that have the defaults
|
|
95
|
+
PLUS extras (e.g. blog adapter + `guides` and `faqs`).
|
|
96
|
+
- **`excludeCollections`** factory option — removes slugs from the
|
|
97
|
+
default. For sites that have most of the defaults but want to
|
|
98
|
+
skip one.
|
|
99
|
+
|
|
100
|
+
### Changed
|
|
101
|
+
|
|
102
|
+
- **`SuppressionFilter.apply` signature** — now takes a
|
|
103
|
+
`SuppressionContext` object (`{ route, collection?, status? }`)
|
|
104
|
+
instead of a bare route string. Direct callers of `apply` will
|
|
105
|
+
see a TypeScript error and need a one-line update; the public
|
|
106
|
+
factory API (`aexeoPlugin({ suppressions })`) is unchanged.
|
|
107
|
+
|
|
108
|
+
### Notes for hosts upgrading from 0.4.x
|
|
109
|
+
|
|
110
|
+
- No automatic behavior change. New selectors and options default
|
|
111
|
+
to undefined and are ignored unless the host opts in.
|
|
112
|
+
- Selector matching is **AND across selectors, OR across rules**.
|
|
113
|
+
A suppression with both `routePattern: "/fr-fr/**"` and
|
|
114
|
+
`collections: ["pages"]` only silences findings on French pages,
|
|
115
|
+
not on French blog posts.
|
|
116
|
+
- Sitewide findings (route `*`) ignore `collections` /
|
|
117
|
+
`statuses` selectors — those findings are inherently
|
|
118
|
+
cross-document and have no single collection or status to match.
|
|
119
|
+
Use `routePattern` (or omit it) and `ruleIds` for sitewide
|
|
120
|
+
suppressions.
|
|
121
|
+
|
|
9
122
|
## [0.4.0] - 2026-05-01
|
|
10
123
|
|
|
11
124
|
Adds upstreamed support for editor-workflow finding suppressions.
|
|
@@ -16,13 +129,13 @@ patch over the published plugin.
|
|
|
16
129
|
|
|
17
130
|
### Added
|
|
18
131
|
|
|
19
|
-
- **`
|
|
132
|
+
- **`aexeoPlugin({ suppressions })` option.** Each rule silences
|
|
20
133
|
findings matching the route pattern (glob) AND/OR a rule-id set.
|
|
21
134
|
Applied before findings are persisted to KV — suppressed findings
|
|
22
135
|
never reach the dashboard, /findings, or the per-document panel.
|
|
23
136
|
|
|
24
137
|
```ts
|
|
25
|
-
|
|
138
|
+
aexeoPlugin({
|
|
26
139
|
suppressions: [
|
|
27
140
|
{ routePattern: "/privacy", ruleIds: ["RULE001"] },
|
|
28
141
|
{ routePattern: "/fr-fr/**", ruleIds: ["RULE002"] },
|
|
@@ -50,7 +163,7 @@ patch over the published plugin.
|
|
|
50
163
|
and the filter is a no-op until the host opts in.
|
|
51
164
|
- Suppressions are plugin-only by design. The CLI `check` continues
|
|
52
165
|
to surface every finding; if you want to silence findings at the
|
|
53
|
-
CLI layer, use `
|
|
166
|
+
CLI layer, use `aexeo.toml`'s `[ignore]` block. The two surfaces
|
|
54
167
|
serve different audiences: the plugin is editorial workflow, the
|
|
55
168
|
CLI is build-gating.
|
|
56
169
|
|
|
@@ -144,10 +257,10 @@ Block Kit (no link needs there).
|
|
|
144
257
|
emdash's `usePluginPage` hook picks this up and renders the
|
|
145
258
|
React component in place of the Block Kit fallback.
|
|
146
259
|
- **Two new HTTP routes** the React component consumes:
|
|
147
|
-
- `POST /_emdash/api/plugins/aexeo-
|
|
260
|
+
- `POST /_emdash/api/plugins/aexeo-emdash/data` — read current
|
|
148
261
|
findings + per-route metadata + computed URLs (no
|
|
149
262
|
re-evaluation).
|
|
150
|
-
- `POST /_emdash/api/plugins/aexeo-
|
|
263
|
+
- `POST /_emdash/api/plugins/aexeo-emdash/refresh` — sweep the
|
|
151
264
|
configured collections, write findings to KV, return the same
|
|
152
265
|
payload as `/data` plus a refresh summary.
|
|
153
266
|
Both routes return JSON in the shape exported as `FindingsPayload`
|
|
@@ -192,7 +305,7 @@ rule-engine change).
|
|
|
192
305
|
|
|
193
306
|
### Added
|
|
194
307
|
|
|
195
|
-
- **`
|
|
308
|
+
- **`aexeoPlugin({ collections })` option** for configured mode.
|
|
196
309
|
Lets sites with non-default collection slugs override which
|
|
197
310
|
collections the Refresh button sweeps. Defaults to
|
|
198
311
|
`["posts", "pages"]` when omitted (the slugs from
|
|
@@ -230,15 +343,15 @@ First public release.
|
|
|
230
343
|
|
|
231
344
|
### Added
|
|
232
345
|
|
|
233
|
-
- `
|
|
346
|
+
- `aexeoPlugin()` factory for **configured mode** — the recommended
|
|
234
347
|
install path for first-party emdash sites. Plugin runs in-process
|
|
235
348
|
inside the host Worker. No sidecar deploy, no auth token, no admin
|
|
236
349
|
Setup page.
|
|
237
|
-
- `
|
|
350
|
+
- `aexeoPluginSandboxed({ evaluatorHost })` factory for **sandboxed
|
|
238
351
|
mode** — preserved for future third-party-deploy scenarios where
|
|
239
352
|
the plugin code shouldn't have full host access. Requires a
|
|
240
353
|
separately-deployed sidecar Worker (template at
|
|
241
|
-
`
|
|
354
|
+
`aexeo-crawl-worker` in the source repo).
|
|
242
355
|
- Block Kit admin pages: findings table, per-document panel,
|
|
243
356
|
intelligence-score dashboard widget. Refresh button on the
|
|
244
357
|
findings page sweeps every document in the configured collections.
|
|
@@ -246,7 +359,7 @@ First public release.
|
|
|
246
359
|
automatic re-evaluation of the changed document and update the
|
|
247
360
|
findings table without manual intervention.
|
|
248
361
|
- WASM-backed evaluator powered by the `aexeo-emdash-bridge` Rust
|
|
249
|
-
crate (same engine as the
|
|
362
|
+
crate (same engine as the Aexeo CLI). Compiles via the consumer's
|
|
250
363
|
Vite/Wrangler chain — `vite-plugin-wasm` is a peer-side runtime
|
|
251
364
|
dependency for Vite-driven Cloudflare deploys.
|
|
252
365
|
|
|
@@ -266,7 +379,9 @@ First public release.
|
|
|
266
379
|
`astro.config.mjs` for the WASM import to resolve to a
|
|
267
380
|
precompiled `WebAssembly.Module`. Not optional.
|
|
268
381
|
|
|
269
|
-
[Unreleased]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.
|
|
382
|
+
[Unreleased]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.6.0...HEAD
|
|
383
|
+
[0.6.0]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.5.0...aexeo-emdash-v0.6.0
|
|
384
|
+
[0.5.0]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.4.0...aexeo-emdash-v0.5.0
|
|
270
385
|
[0.4.0]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.3.0...aexeo-emdash-v0.4.0
|
|
271
386
|
[0.3.0]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.2.0...aexeo-emdash-v0.3.0
|
|
272
387
|
[0.2.0]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.1.2...aexeo-emdash-v0.2.0
|
package/INSTALL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Installing `@aeptus/aexeo-emdash`
|
|
2
2
|
|
|
3
|
-
This package adds Aexeo
|
|
3
|
+
This package adds Aexeo SEO/GEO content evaluation to an emdash
|
|
4
4
|
site. Findings show up as a Block Kit admin page; a sidebar widget
|
|
5
5
|
shows the site's current intelligence score; saves auto-evaluate the
|
|
6
6
|
changed document.
|
|
@@ -9,11 +9,11 @@ changed document.
|
|
|
9
9
|
|
|
10
10
|
The package exports two factories:
|
|
11
11
|
|
|
12
|
-
- **`
|
|
12
|
+
- **`aexeoPlugin()`** — **configured mode** (recommended). Plugin
|
|
13
13
|
runs in-process inside the host emdash Worker. No separate
|
|
14
14
|
deployment, no auth token, no admin Setup page. Use this for
|
|
15
15
|
first-party emdash sites.
|
|
16
|
-
- **`
|
|
16
|
+
- **`aexeoPluginSandboxed({ evaluatorHost })`** — **sandboxed
|
|
17
17
|
mode**. Plugin runs inside emdash's Worker Loader isolate; WASM
|
|
18
18
|
evaluation runs in a separate sidecar Worker the operator
|
|
19
19
|
deploys. Use this only when a third party hosts emdash and
|
|
@@ -42,7 +42,7 @@ npm install @aeptus/aexeo-emdash vite-plugin-wasm
|
|
|
42
42
|
`vite-plugin-wasm` is required because Vite's defaults treat `.wasm`
|
|
43
43
|
imports as static-asset URLs, but Cloudflare Workers can only run
|
|
44
44
|
WASM as precompiled `WebAssembly.Module` instances. The plugin
|
|
45
|
-
bridges the two; without it, the
|
|
45
|
+
bridges the two; without it, the Aexeo plugin's first call throws
|
|
46
46
|
"WASM module did not resolve to a WebAssembly.Module."
|
|
47
47
|
|
|
48
48
|
> **If installing from a private git remote or as a vendored
|
|
@@ -54,7 +54,7 @@ bridges the two; without it, the seogeo plugin's first call throws
|
|
|
54
54
|
```js
|
|
55
55
|
import cloudflare from "@astrojs/cloudflare";
|
|
56
56
|
import { d1, r2 } from "@emdash-cms/cloudflare";
|
|
57
|
-
import {
|
|
57
|
+
import { aexeoPlugin } from "@aeptus/aexeo-emdash";
|
|
58
58
|
import { defineConfig } from "astro/config";
|
|
59
59
|
import emdash from "emdash/astro";
|
|
60
60
|
import wasm from "vite-plugin-wasm";
|
|
@@ -68,7 +68,7 @@ export default defineConfig({
|
|
|
68
68
|
// workerd disallow runtime WebAssembly.instantiate from raw
|
|
69
69
|
// bytes, so the bundler has to do the compilation.
|
|
70
70
|
plugins: [wasm()],
|
|
71
|
-
// The
|
|
71
|
+
// The Aexeo plugin's WASM import confuses Vite's dep
|
|
72
72
|
// optimizer when it tries to pre-bundle the package.
|
|
73
73
|
optimizeDeps: { exclude: ["@aeptus/aexeo-emdash"] },
|
|
74
74
|
},
|
|
@@ -76,7 +76,7 @@ export default defineConfig({
|
|
|
76
76
|
emdash({
|
|
77
77
|
database: d1({ binding: "DB" }),
|
|
78
78
|
storage: r2({ binding: "MEDIA" }),
|
|
79
|
-
plugins: [
|
|
79
|
+
plugins: [aexeoPlugin()],
|
|
80
80
|
}),
|
|
81
81
|
],
|
|
82
82
|
});
|
|
@@ -96,7 +96,7 @@ no sidecar to deploy.
|
|
|
96
96
|
http://localhost:4321/_emdash/admin/plugins
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
The
|
|
99
|
+
The Aexeo plugin will appear in the list. Click it.
|
|
100
100
|
|
|
101
101
|
3. The findings page renders with a Refresh button. Click **Refresh**
|
|
102
102
|
— toast says "Refreshed N routes (M findings across K documents)"
|
|
@@ -151,7 +151,7 @@ token to rotate, no Setup page to revisit.
|
|
|
151
151
|
|
|
152
152
|
**Verify after a plugin update:**
|
|
153
153
|
|
|
154
|
-
1. Visit `/admin/plugins/aexeo-
|
|
154
|
+
1. Visit `/admin/plugins/aexeo-emdash/findings` and click **Refresh**.
|
|
155
155
|
Toast should show non-zero rules sweeping. (A zero count *can*
|
|
156
156
|
be valid for a clean site, but if you previously had findings
|
|
157
157
|
and now have none, suspect the upgrade.)
|
|
@@ -216,7 +216,7 @@ Treat anything outside that range as best-effort.
|
|
|
216
216
|
|
|
217
217
|
## Removing
|
|
218
218
|
|
|
219
|
-
Delete the dependency, the import, and the `
|
|
219
|
+
Delete the dependency, the import, and the `aexeoPlugin()` line in
|
|
220
220
|
`integrations:[emdash({plugins:[...]})]`. The KV keys the plugin
|
|
221
221
|
wrote remain in your KV namespace — purge with
|
|
222
222
|
`wrangler kv:key delete --prefix=findings:` and
|
|
@@ -243,26 +243,26 @@ Trade-offs vs. configured mode:
|
|
|
243
243
|
```js
|
|
244
244
|
// astro.config.mjs (sandboxed mode)
|
|
245
245
|
import { sandbox } from "@emdash-cms/cloudflare";
|
|
246
|
-
import {
|
|
246
|
+
import { aexeoPluginSandboxed } from "@aeptus/aexeo-emdash";
|
|
247
247
|
|
|
248
248
|
emdash({
|
|
249
249
|
database: d1({ binding: "DB" }),
|
|
250
250
|
storage: r2({ binding: "MEDIA" }),
|
|
251
251
|
sandboxed: [
|
|
252
|
-
|
|
252
|
+
aexeoPluginSandboxed({
|
|
253
253
|
// Public host of the deployed sidecar Worker.
|
|
254
|
-
evaluatorHost: "
|
|
254
|
+
evaluatorHost: "aexeo-crawl-worker.<subdomain>.workers.dev",
|
|
255
255
|
}),
|
|
256
256
|
],
|
|
257
257
|
sandboxRunner: sandbox(),
|
|
258
258
|
});
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
-
The sidecar Worker template lives at `packages/
|
|
261
|
+
The sidecar Worker template lives at `packages/aexeo-crawl-worker/`
|
|
262
262
|
in this repo. Per-site deploy:
|
|
263
263
|
|
|
264
264
|
```bash
|
|
265
|
-
cd
|
|
265
|
+
cd aexeo-crawl-worker
|
|
266
266
|
# Edit wrangler.toml: name, R2 bucket name, SITE_URL
|
|
267
267
|
npx wrangler login
|
|
268
268
|
npx wrangler r2 bucket create <bucket-name>
|
|
@@ -271,7 +271,7 @@ echo "$(openssl rand -hex 32)" | npx wrangler secret put EVAL_TOKEN
|
|
|
271
271
|
```
|
|
272
272
|
|
|
273
273
|
Then in the admin UI, visit
|
|
274
|
-
`/admin/plugins/aexeo-
|
|
274
|
+
`/admin/plugins/aexeo-emdash/setup` and paste the deployed URL +
|
|
275
275
|
the same token.
|
|
276
276
|
|
|
277
277
|
## Alternative install sources
|
|
@@ -316,7 +316,7 @@ present, double-check the package version is recent (≥ the version
|
|
|
316
316
|
that switched to direct .wasm imports — see git log for `Switch
|
|
317
317
|
configured-mode WASM from inlined bytes to direct .wasm import`).
|
|
318
318
|
|
|
319
|
-
**`
|
|
319
|
+
**`aexeo: WASM module did not resolve to a WebAssembly.Module`**
|
|
320
320
|
The bundler resolved the .wasm import but produced something other
|
|
321
321
|
than a Module (URL string, Uint8Array). Same fix as above: ensure
|
|
322
322
|
`vite-plugin-wasm` is loaded.
|
|
@@ -327,7 +327,7 @@ sandboxed two-arg ctx shape. Update; it's fixed in the version with
|
|
|
327
327
|
`Switch configured-mode WASM from inlined bytes to direct .wasm
|
|
328
328
|
import` in the commit log.
|
|
329
329
|
|
|
330
|
-
**`/admin/plugins` doesn't list
|
|
330
|
+
**`/admin/plugins` doesn't list Aexeo, but `/findings` works.** The
|
|
331
331
|
emdash version's plugins meta page may filter out plugins without a
|
|
332
332
|
`/` adminPage entry. The package declares one explicitly; if you see
|
|
333
333
|
this on a recent version, it's an emdash regression — file upstream.
|
|
@@ -344,7 +344,7 @@ Node adapter.
|
|
|
344
344
|
|
|
345
345
|
## What the plugin actually does
|
|
346
346
|
|
|
347
|
-
- **Findings page** (`/_emdash/admin/plugins/aexeo-
|
|
347
|
+
- **Findings page** (`/_emdash/admin/plugins/aexeo-emdash/findings`):
|
|
348
348
|
Block Kit table of every rule violation across the site. Filter by
|
|
349
349
|
severity. Click a route in the picker to drill into per-document
|
|
350
350
|
findings.
|
|
@@ -371,6 +371,6 @@ inline. Findings are stored in the host's plugin KV (the
|
|
|
371
371
|
`PluginContext.kv` accessor). The Refresh button does the same thing
|
|
372
372
|
but for every document in every configured collection. The WASM
|
|
373
373
|
itself is the same `aexeo-emdash-bridge` Rust crate that powers the
|
|
374
|
-
|
|
374
|
+
Aexeo CLI; it's compiled to WebAssembly via wasm-pack and imported
|
|
375
375
|
by the host's bundler (Vite + `vite-plugin-wasm`). No separate
|
|
376
376
|
service, no sidecar, no auth token.
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @aeptus/aexeo-emdash
|
|
2
2
|
|
|
3
|
-
Aexeo
|
|
3
|
+
Aexeo SEO/GEO content evaluator as an emdash plugin.
|
|
4
4
|
|
|
5
5
|
Adds a Findings admin page, per-document panel, and dashboard
|
|
6
6
|
intelligence-score widget to an emdash site. Saves auto-evaluate the
|
|
@@ -14,7 +14,7 @@ npm install @aeptus/aexeo-emdash vite-plugin-wasm
|
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
16
|
// astro.config.mjs
|
|
17
|
-
import {
|
|
17
|
+
import { aexeoPlugin } from "@aeptus/aexeo-emdash";
|
|
18
18
|
import wasm from "vite-plugin-wasm";
|
|
19
19
|
|
|
20
20
|
export default defineConfig({
|
|
@@ -26,7 +26,7 @@ export default defineConfig({
|
|
|
26
26
|
emdash({
|
|
27
27
|
database: d1({ binding: "DB" }),
|
|
28
28
|
storage: r2({ binding: "MEDIA" }),
|
|
29
|
-
plugins: [
|
|
29
|
+
plugins: [aexeoPlugin()],
|
|
30
30
|
}),
|
|
31
31
|
],
|
|
32
32
|
});
|
|
@@ -40,7 +40,7 @@ troubleshooting list, see [INSTALL.md](./INSTALL.md).
|
|
|
40
40
|
|
|
41
41
|
## What it does
|
|
42
42
|
|
|
43
|
-
- **Findings page** at `/_emdash/admin/plugins/aexeo-
|
|
43
|
+
- **Findings page** at `/_emdash/admin/plugins/aexeo-emdash/findings`:
|
|
44
44
|
rule violations across the site, filterable by severity.
|
|
45
45
|
- **Document panel** at `/document`: findings scoped to one document.
|
|
46
46
|
- **Dashboard widget**: top-line intelligence score (citation, truth,
|
|
@@ -59,13 +59,108 @@ troubleshooting list, see [INSTALL.md](./INSTALL.md).
|
|
|
59
59
|
bundler handles compilation at build time. `vite-plugin-wasm` is a
|
|
60
60
|
hard requirement on the consumer side.
|
|
61
61
|
|
|
62
|
+
## Configuring collections
|
|
63
|
+
|
|
64
|
+
The plugin sweeps `["posts", "pages"]` by default — the slugs the
|
|
65
|
+
`@emdash-cms/template-blog-cloudflare` template ships with. Three knobs
|
|
66
|
+
to override, in precedence order:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
// Full override — replace the default entirely.
|
|
70
|
+
// Use when your schema has none of the default slugs.
|
|
71
|
+
aexeoPlugin({ collections: ["blog_posts", "pages"] })
|
|
72
|
+
|
|
73
|
+
// Extend the default — add slugs alongside posts + pages.
|
|
74
|
+
aexeoPlugin({ includeCollections: ["guides", "faqs"] })
|
|
75
|
+
|
|
76
|
+
// Subtract from the default — remove a slug.
|
|
77
|
+
aexeoPlugin({ excludeCollections: ["posts"] })
|
|
78
|
+
|
|
79
|
+
// Combine include + exclude (subtract first, then add).
|
|
80
|
+
// `collections` ignores both — it's the explicit-list override.
|
|
81
|
+
aexeoPlugin({
|
|
82
|
+
excludeCollections: ["posts"],
|
|
83
|
+
includeCollections: ["blog_posts", "guides"],
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Pointing any of these at a slug that doesn't exist is non-fatal:
|
|
88
|
+
the bridge's `content.list` returns empty and the missing collection
|
|
89
|
+
shows up in the Refresh summary's `errors` field.
|
|
90
|
+
|
|
91
|
+
## Suppressing findings
|
|
92
|
+
|
|
93
|
+
When a finding is intentional (legal pages with non-standard SEO,
|
|
94
|
+
localized routes where a rule doesn't apply, draft documents whose
|
|
95
|
+
findings shouldn't surface yet), silence it with a suppression rule.
|
|
96
|
+
Suppressions are applied **before** findings are persisted to KV —
|
|
97
|
+
suppressed findings never reach the dashboard, `/findings`, or the
|
|
98
|
+
per-document panel.
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
aexeoPlugin({
|
|
102
|
+
suppressions: [
|
|
103
|
+
// route + rule
|
|
104
|
+
{ routePattern: "/privacy", ruleIds: ["RULE001"] },
|
|
105
|
+
|
|
106
|
+
// recursive route subtree
|
|
107
|
+
{ routePattern: "/fr-fr/**", ruleIds: ["RULE002"] },
|
|
108
|
+
|
|
109
|
+
// applies to every route
|
|
110
|
+
{ ruleIds: ["RULE999"] },
|
|
111
|
+
|
|
112
|
+
// by collection
|
|
113
|
+
{ collections: ["drafts"], ruleIds: ["RULE042"] },
|
|
114
|
+
|
|
115
|
+
// by document status
|
|
116
|
+
{ statuses: ["draft"] }, // silence ALL findings on drafts
|
|
117
|
+
],
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Glob syntax for `routePattern`
|
|
122
|
+
|
|
123
|
+
| Pattern | Matches |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `*` | any chars **except** `/` |
|
|
126
|
+
| `**` | any chars **including** `/` (recursive) |
|
|
127
|
+
| `?` | exactly one char except `/` |
|
|
128
|
+
|
|
129
|
+
Patterns are anchored. `/about` matches only `/about`, not
|
|
130
|
+
`/about/team` — use `/about/**` if you want a subtree.
|
|
131
|
+
|
|
132
|
+
> **Migrating from a local patch**: if your previous setup used
|
|
133
|
+
> looser patterns (e.g. one-asterisk-means-everything), update them
|
|
134
|
+
> on bump. `*` is intentionally single-segment so editors can scope
|
|
135
|
+
> rules to a level of the path hierarchy without recursive surprises.
|
|
136
|
+
|
|
137
|
+
### Selector semantics
|
|
138
|
+
|
|
139
|
+
A rule may set any combination of `routePattern`, `ruleIds`,
|
|
140
|
+
`collections`, and `statuses`. Matching is **AND across selectors,
|
|
141
|
+
OR across rules**: a finding is suppressed when at least one rule
|
|
142
|
+
matches it; a rule matches when every selector it sets matches the
|
|
143
|
+
finding's context.
|
|
144
|
+
|
|
145
|
+
Empty rules (`{}` with no selectors) are rejected at plugin
|
|
146
|
+
construction. That's a kill-switch for all findings everywhere
|
|
147
|
+
and almost certainly an editor mistake.
|
|
148
|
+
|
|
149
|
+
`collections` and `statuses` only meaningful for per-document
|
|
150
|
+
findings. Sitewide findings (cross-document audit issues) are
|
|
151
|
+
matched only by `routePattern` and `ruleIds`.
|
|
152
|
+
|
|
153
|
+
Suppressions are plugin-only by design. The CLI's `check` is the
|
|
154
|
+
canonical strict audit; if you want to silence findings at the CLI
|
|
155
|
+
layer, use `aexeo.toml`'s `[ignore]` block instead.
|
|
156
|
+
|
|
62
157
|
## Two factories
|
|
63
158
|
|
|
64
159
|
The package exports two factories for different trust contexts:
|
|
65
160
|
|
|
66
|
-
- `
|
|
161
|
+
- `aexeoPlugin()` — **configured mode** (recommended). In-process
|
|
67
162
|
plugin. Use for first-party emdash sites.
|
|
68
|
-
- `
|
|
163
|
+
- `aexeoPluginSandboxed({ evaluatorHost })` — **sandboxed mode**.
|
|
69
164
|
Requires a separately-deployed sidecar Worker. Use only when
|
|
70
165
|
shipping to third-party emdash sites that shouldn't trust the
|
|
71
166
|
plugin code with full host access.
|
package/dist/admin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/admin/Facts.tsx
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
-
var PLUGIN_ID = "aexeo-
|
|
4
|
+
var PLUGIN_ID = "aexeo-emdash";
|
|
5
5
|
var API_BASE = "/_emdash/api";
|
|
6
6
|
function Facts() {
|
|
7
7
|
const [data, setData] = useState(null);
|
|
@@ -275,7 +275,7 @@ async function callRoute(kind, extra) {
|
|
|
275
275
|
// src/admin/Findings.tsx
|
|
276
276
|
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
277
277
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
278
|
-
var PLUGIN_ID2 = "aexeo-
|
|
278
|
+
var PLUGIN_ID2 = "aexeo-emdash";
|
|
279
279
|
var API_BASE2 = "/_emdash/api";
|
|
280
280
|
function Findings() {
|
|
281
281
|
const [state, setState] = useState2({
|
package/dist/configured.d.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import type { Suppression } from "./suppressions.js";
|
|
2
2
|
export interface ConfiguredPluginOptions {
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* in src/index.ts. When undefined, falls back to DEFAULT_COLLECTIONS
|
|
7
|
-
* (`["posts", "pages"]`).
|
|
4
|
+
* Full override of the swept collections. See `aexeoPlugin` in
|
|
5
|
+
* src/index.ts for the full precedence story.
|
|
8
6
|
*/
|
|
9
7
|
collections?: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* Add to the default `["posts", "pages"]`. Ignored when
|
|
10
|
+
* `collections` is set.
|
|
11
|
+
*/
|
|
12
|
+
includeCollections?: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Remove from the default `["posts", "pages"]`. Applied before
|
|
15
|
+
* `includeCollections`. Ignored when `collections` is set.
|
|
16
|
+
*/
|
|
17
|
+
excludeCollections?: readonly string[];
|
|
10
18
|
/**
|
|
11
19
|
* Editor-workflow suppressions. See `Suppression` in suppressions.ts
|
|
12
20
|
* for the rule shape and matching semantics. Compiled once at plugin
|
package/dist/configured.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configured.d.ts","sourceRoot":"","sources":["../src/configured.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"configured.d.ts","sourceRoot":"","sources":["../src/configured.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAmFrD,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEhC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CACvC;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAyG3E;AAID,eAAe,YAAY,CAAC"}
|
package/dist/configured.js
CHANGED
|
@@ -89,7 +89,14 @@ async function handleAfterSaveConfigured(event, ctx, evaluator, suppressionFilte
|
|
|
89
89
|
const pageFindings = result.findings.filter(
|
|
90
90
|
(finding) => finding.scope === "page"
|
|
91
91
|
);
|
|
92
|
-
const filtered = suppressionFilter === void 0 ? pageFindings : suppressionFilter.apply(
|
|
92
|
+
const filtered = suppressionFilter === void 0 ? pageFindings : suppressionFilter.apply(
|
|
93
|
+
{
|
|
94
|
+
route: document.route,
|
|
95
|
+
collection: adapted.meta.collection,
|
|
96
|
+
status: adapted.meta.status
|
|
97
|
+
},
|
|
98
|
+
pageFindings
|
|
99
|
+
);
|
|
93
100
|
await kv.set(findingsKey(document.route), {
|
|
94
101
|
route: document.route,
|
|
95
102
|
findings: filtered
|
|
@@ -140,7 +147,7 @@ async function evaluateAndPersistAll(ctx, options) {
|
|
|
140
147
|
}
|
|
141
148
|
const result = await options.evaluator(documents);
|
|
142
149
|
if (!result.ok) {
|
|
143
|
-
log?.error?.(`
|
|
150
|
+
log?.error?.(`aexeo evaluator failure (${result.reason})`, {
|
|
144
151
|
detail: result.detail
|
|
145
152
|
});
|
|
146
153
|
summary.errors.push(`${result.reason}: ${result.detail}`);
|
|
@@ -158,7 +165,13 @@ async function evaluateAndPersistAll(ctx, options) {
|
|
|
158
165
|
}
|
|
159
166
|
let totalAfterSuppression = 0;
|
|
160
167
|
for (const [route, findings] of findingsByRoute) {
|
|
161
|
-
const
|
|
168
|
+
const stored = adaptedByRoute.get(route);
|
|
169
|
+
const context = {
|
|
170
|
+
route,
|
|
171
|
+
...stored?.meta.collection === void 0 ? {} : { collection: stored.meta.collection },
|
|
172
|
+
...stored?.meta.status === void 0 ? {} : { status: stored.meta.status }
|
|
173
|
+
};
|
|
174
|
+
const filtered = suppressionFilter === void 0 ? findings : suppressionFilter.apply(context, findings);
|
|
162
175
|
await kv.set(findingsKey(route), { route, findings: filtered });
|
|
163
176
|
summary.routesUpdated += 1;
|
|
164
177
|
totalAfterSuppression += filtered.length;
|
|
@@ -418,7 +431,7 @@ function ensureInitialized() {
|
|
|
418
431
|
}
|
|
419
432
|
if (!(bridgeModule instanceof WebAssembly.Module)) {
|
|
420
433
|
throw new Error(
|
|
421
|
-
"
|
|
434
|
+
"aexeo: WASM module did not resolve to a WebAssembly.Module \u2014 the consumer's bundler isn't configured for .wasm imports. Add the .wasm loader to your Vite/wrangler config; see @aeptus/aexeo-emdash INSTALL.md for the recipe."
|
|
422
435
|
);
|
|
423
436
|
}
|
|
424
437
|
const instance = new WebAssembly.Instance(bridgeModule, {
|
|
@@ -732,7 +745,7 @@ async function handleSave(ctx, body) {
|
|
|
732
745
|
|
|
733
746
|
// src/suppressions.ts
|
|
734
747
|
var NOOP_FILTER = {
|
|
735
|
-
apply: (
|
|
748
|
+
apply: (_context, findings) => [...findings]
|
|
736
749
|
};
|
|
737
750
|
function compileSuppressions(rules) {
|
|
738
751
|
if (rules === void 0 || rules.length === 0) {
|
|
@@ -741,25 +754,41 @@ function compileSuppressions(rules) {
|
|
|
741
754
|
rules.forEach((rule, index) => {
|
|
742
755
|
const hasRoute = rule.routePattern !== void 0 && rule.routePattern.length > 0;
|
|
743
756
|
const hasRuleIds = rule.ruleIds !== void 0 && rule.ruleIds.length > 0;
|
|
744
|
-
|
|
757
|
+
const hasCollections = rule.collections !== void 0 && rule.collections.length > 0;
|
|
758
|
+
const hasStatuses = rule.statuses !== void 0 && rule.statuses.length > 0;
|
|
759
|
+
if (!hasRoute && !hasRuleIds && !hasCollections && !hasStatuses) {
|
|
745
760
|
throw new Error(
|
|
746
|
-
`
|
|
761
|
+
`aexeoPlugin: suppressions[${index}] is empty \u2014 set at least one of routePattern, ruleIds, collections, statuses`
|
|
747
762
|
);
|
|
748
763
|
}
|
|
749
764
|
});
|
|
750
765
|
const compiled = rules.map((rule) => ({
|
|
751
766
|
matchRoute: rule.routePattern === void 0 ? matchAlways : globToMatcher(rule.routePattern),
|
|
752
|
-
ruleIds: rule.ruleIds === void 0 || rule.ruleIds.length === 0 ? null : new Set(rule.ruleIds)
|
|
767
|
+
ruleIds: rule.ruleIds === void 0 || rule.ruleIds.length === 0 ? null : new Set(rule.ruleIds),
|
|
768
|
+
collections: rule.collections === void 0 || rule.collections.length === 0 ? null : new Set(rule.collections),
|
|
769
|
+
statuses: rule.statuses === void 0 || rule.statuses.length === 0 ? null : new Set(rule.statuses)
|
|
753
770
|
}));
|
|
754
771
|
return {
|
|
755
|
-
apply(
|
|
756
|
-
return findings.filter(
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
772
|
+
apply(context, findings) {
|
|
773
|
+
return findings.filter(
|
|
774
|
+
(finding) => !compiled.some((rule) => {
|
|
775
|
+
if (!rule.matchRoute(context.route)) return false;
|
|
776
|
+
if (rule.ruleIds !== null && !rule.ruleIds.has(finding.rule_id)) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
if (rule.collections !== null) {
|
|
780
|
+
if (context.collection === void 0 || !rule.collections.has(context.collection)) {
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (rule.statuses !== null) {
|
|
785
|
+
if (context.status === void 0 || !rule.statuses.has(context.status)) {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return true;
|
|
790
|
+
})
|
|
791
|
+
);
|
|
763
792
|
}
|
|
764
793
|
};
|
|
765
794
|
}
|
|
@@ -767,16 +796,35 @@ var matchAlways = () => true;
|
|
|
767
796
|
function globToMatcher(pattern) {
|
|
768
797
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
769
798
|
const DOUBLE_STAR_TOKEN = "";
|
|
770
|
-
|
|
799
|
+
const body = escaped.replace(/\*\*/g, DOUBLE_STAR_TOKEN).replace(/\*/g, "[^/]*").replace(new RegExp(DOUBLE_STAR_TOKEN, "g"), ".*").replace(/\?/g, "[^/]");
|
|
771
800
|
const re = new RegExp(`^${body}$`);
|
|
772
801
|
return (route) => re.test(route);
|
|
773
802
|
}
|
|
774
803
|
|
|
775
804
|
// src/version.ts
|
|
776
|
-
var PACKAGE_VERSION = "0.
|
|
805
|
+
var PACKAGE_VERSION = "0.6.0";
|
|
777
806
|
|
|
778
807
|
// src/configured.ts
|
|
779
808
|
import { definePlugin } from "emdash";
|
|
809
|
+
function resolveCollections(explicit, include, exclude) {
|
|
810
|
+
if (explicit !== void 0) {
|
|
811
|
+
return [...explicit];
|
|
812
|
+
}
|
|
813
|
+
const excludeSet = new Set(exclude ?? []);
|
|
814
|
+
const base = DEFAULT_COLLECTIONS.filter(
|
|
815
|
+
(slug) => !excludeSet.has(slug)
|
|
816
|
+
);
|
|
817
|
+
if (include === void 0 || include.length === 0) {
|
|
818
|
+
return base;
|
|
819
|
+
}
|
|
820
|
+
const result = [...base];
|
|
821
|
+
for (const slug of include) {
|
|
822
|
+
if (!result.includes(slug)) {
|
|
823
|
+
result.push(slug);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return result;
|
|
827
|
+
}
|
|
780
828
|
var inProcessEvaluator = async (documents) => {
|
|
781
829
|
try {
|
|
782
830
|
const raw = await evaluateDocuments2(JSON.stringify(documents));
|
|
@@ -795,10 +843,14 @@ var inProcessEvaluator = async (documents) => {
|
|
|
795
843
|
}
|
|
796
844
|
};
|
|
797
845
|
function createPlugin(options = {}) {
|
|
798
|
-
const collections =
|
|
846
|
+
const collections = resolveCollections(
|
|
847
|
+
options.collections,
|
|
848
|
+
options.includeCollections,
|
|
849
|
+
options.excludeCollections
|
|
850
|
+
);
|
|
799
851
|
const suppressionFilter = compileSuppressions(options.suppressions);
|
|
800
852
|
return definePlugin({
|
|
801
|
-
id: "aexeo-
|
|
853
|
+
id: "aexeo-emdash",
|
|
802
854
|
version: PACKAGE_VERSION,
|
|
803
855
|
capabilities: ["read:content"],
|
|
804
856
|
hooks: {
|
|
@@ -820,8 +872,8 @@ function createPlugin(options = {}) {
|
|
|
820
872
|
handler: (ctx) => handleAdminRoute(ctx, collections)
|
|
821
873
|
},
|
|
822
874
|
// JSON data endpoint for the React adminEntry. Two routes:
|
|
823
|
-
// POST /_emdash/api/plugins/aexeo-
|
|
824
|
-
// POST /_emdash/api/plugins/aexeo-
|
|
875
|
+
// POST /_emdash/api/plugins/aexeo-emdash/data — read current findings
|
|
876
|
+
// POST /_emdash/api/plugins/aexeo-emdash/refresh — sweep + read
|
|
825
877
|
// Returning JSON (not Block Kit blocks) lets the React
|
|
826
878
|
// <Findings/> component render proper <a href> links to the
|
|
827
879
|
// emdash edit page and the public site, which Block Kit can't
|
|
@@ -862,7 +914,7 @@ function createPlugin(options = {}) {
|
|
|
862
914
|
{ path: "/facts", label: "Truth manifest" }
|
|
863
915
|
],
|
|
864
916
|
widgets: [
|
|
865
|
-
{ id: "
|
|
917
|
+
{ id: "aexeo-score", size: "third", title: "SEO score" }
|
|
866
918
|
]
|
|
867
919
|
}
|
|
868
920
|
});
|
|
@@ -901,7 +953,7 @@ async function handlePageLoad(ctx, page) {
|
|
|
901
953
|
if (normalized === "" || normalized === "findings") {
|
|
902
954
|
return renderFindingsPage(ctx);
|
|
903
955
|
}
|
|
904
|
-
if (normalized === "widget:
|
|
956
|
+
if (normalized === "widget:aexeo-score") {
|
|
905
957
|
return renderScoreWidget(ctx);
|
|
906
958
|
}
|
|
907
959
|
if (normalized === "document") {
|
|
@@ -1049,7 +1101,7 @@ async function renderScoreWidget(ctx) {
|
|
|
1049
1101
|
{ type: "header", text: "SEO score" },
|
|
1050
1102
|
{
|
|
1051
1103
|
type: "context",
|
|
1052
|
-
text: "No documents indexed \u2014 click Refresh on the
|
|
1104
|
+
text: "No documents indexed \u2014 click Refresh on the Aexeo findings page."
|
|
1053
1105
|
}
|
|
1054
1106
|
]
|
|
1055
1107
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type { PluginAdminPage, PluginAdminWidget, SandboxedPluginDescriptor,
|
|
1
|
+
export { aexeoPluginSandboxed } from "./sandbox.js";
|
|
2
|
+
export type { PluginAdminPage, PluginAdminWidget, SandboxedPluginDescriptor, AexeoSandboxedOptions, } from "./sandbox.js";
|
|
3
3
|
export interface ConfiguredPluginDescriptor {
|
|
4
4
|
id: string;
|
|
5
5
|
version: string;
|
|
@@ -18,7 +18,7 @@ export interface ConfiguredPluginDescriptor {
|
|
|
18
18
|
title?: string;
|
|
19
19
|
}[];
|
|
20
20
|
}
|
|
21
|
-
export interface
|
|
21
|
+
export interface AexeoPluginOptions {
|
|
22
22
|
/**
|
|
23
23
|
* emdash collections the plugin sweeps when an admin clicks
|
|
24
24
|
* Refresh on the findings page. Order doesn't matter; the sweep
|
|
@@ -29,24 +29,57 @@ export interface SeogeoPluginOptions {
|
|
|
29
29
|
*
|
|
30
30
|
* Defaults to `["posts", "pages"]` (the slugs the
|
|
31
31
|
* `@emdash-cms/template-blog-cloudflare` template ships with).
|
|
32
|
-
*
|
|
32
|
+
* Three knobs, in precedence order:
|
|
33
33
|
*
|
|
34
|
-
*
|
|
34
|
+
* - `collections` — full override. When set, replaces the
|
|
35
|
+
* default entirely. Use this when your schema has none of the
|
|
36
|
+
* default slugs (e.g. a site with `blog_posts` and `pages`).
|
|
35
37
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* aexeoPlugin({ collections: ["blog_posts", "pages"] })
|
|
39
|
+
*
|
|
40
|
+
* - `includeCollections` — extend the default. Adds slugs to
|
|
41
|
+
* the existing default. Use this when your schema has the
|
|
42
|
+
* default slugs PLUS extras.
|
|
43
|
+
*
|
|
44
|
+
* aexeoPlugin({ includeCollections: ["guides", "faqs"] })
|
|
45
|
+
*
|
|
46
|
+
* - `excludeCollections` — subtract from the default. Removes
|
|
47
|
+
* slugs from the existing default. Use this when your schema
|
|
48
|
+
* has the defaults but you want to skip one.
|
|
49
|
+
*
|
|
50
|
+
* aexeoPlugin({ excludeCollections: ["posts"] })
|
|
51
|
+
*
|
|
52
|
+
* `includeCollections` and `excludeCollections` may be combined
|
|
53
|
+
* (subtract first, then add). `collections` ignores both — it's
|
|
54
|
+
* the explicit-list override.
|
|
55
|
+
*
|
|
56
|
+
* Pointing any of these at a slug that doesn't exist in your
|
|
57
|
+
* schema is non-fatal: the bridge's `content.list` returns empty
|
|
58
|
+
* and the sweep records the missing collection in the Refresh
|
|
59
|
+
* summary's `errors` field. Safe to ship a superset for a project
|
|
60
|
+
* that may add collections later.
|
|
41
61
|
*/
|
|
42
62
|
collections?: readonly string[];
|
|
63
|
+
/**
|
|
64
|
+
* Slugs to add on top of the default `["posts", "pages"]`. Ignored
|
|
65
|
+
* when `collections` is set (which fully overrides the default).
|
|
66
|
+
* See `collections` for the full precedence story.
|
|
67
|
+
*/
|
|
68
|
+
includeCollections?: readonly string[];
|
|
69
|
+
/**
|
|
70
|
+
* Slugs to remove from the default `["posts", "pages"]`. Applied
|
|
71
|
+
* before `includeCollections`, so adding a slug back via include
|
|
72
|
+
* is a no-op (it was never removed). Ignored when `collections`
|
|
73
|
+
* is set.
|
|
74
|
+
*/
|
|
75
|
+
excludeCollections?: readonly string[];
|
|
43
76
|
/**
|
|
44
77
|
* Editor-workflow suppressions. Each rule silences findings that
|
|
45
78
|
* match the route pattern (glob) AND/OR are in the rule-id set.
|
|
46
79
|
* Applied before findings are persisted to KV — suppressed findings
|
|
47
80
|
* never reach the dashboard, /findings, or the per-document panel.
|
|
48
81
|
*
|
|
49
|
-
*
|
|
82
|
+
* aexeoPlugin({
|
|
50
83
|
* suppressions: [
|
|
51
84
|
* { routePattern: "/privacy", ruleIds: ["RULE001"] },
|
|
52
85
|
* { routePattern: "/fr-fr/**", ruleIds: ["RULE002"] },
|
|
@@ -63,7 +96,7 @@ export interface SeogeoPluginOptions {
|
|
|
63
96
|
*
|
|
64
97
|
* Suppressions are plugin-only by design. The CLI's `check` is the
|
|
65
98
|
* canonical strict audit; if you want to silence findings at the
|
|
66
|
-
* CLI layer, use `
|
|
99
|
+
* CLI layer, use `aexeo.toml`'s `[ignore]` block instead. The two
|
|
67
100
|
* surfaces serve different audiences: the plugin is editorial
|
|
68
101
|
* workflow, the CLI is build-gating.
|
|
69
102
|
*/
|
|
@@ -71,5 +104,5 @@ export interface SeogeoPluginOptions {
|
|
|
71
104
|
}
|
|
72
105
|
export type { Suppression } from "./suppressions.js";
|
|
73
106
|
import type { Suppression } from "./suppressions.js";
|
|
74
|
-
export declare function
|
|
107
|
+
export declare function aexeoPlugin(options?: AexeoPluginOptions): ConfiguredPluginDescriptor;
|
|
75
108
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAYtB,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAOnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,UAAU,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,YAAY,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACxE;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACH,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEhC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEvC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,YAAY,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CACvC;AAED,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,0BAA0B,CAqE5B"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ var baseCapabilities = [
|
|
|
6
6
|
"write:artifacts:public/llms-full.txt",
|
|
7
7
|
"write:artifacts:public/facts.json",
|
|
8
8
|
"write:artifacts:public/*.md.txt",
|
|
9
|
-
"kv:
|
|
9
|
+
"kv:aexeo-baselines"
|
|
10
10
|
];
|
|
11
11
|
function buildCapabilities(evaluatorHost) {
|
|
12
12
|
if (evaluatorHost === null) {
|
|
@@ -23,13 +23,13 @@ function buildAllowedHosts(evaluatorHost) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// src/version.ts
|
|
26
|
-
var PACKAGE_VERSION = "0.
|
|
26
|
+
var PACKAGE_VERSION = "0.6.0";
|
|
27
27
|
|
|
28
28
|
// src/sandbox.ts
|
|
29
|
-
function
|
|
30
|
-
const evaluatorHost = options.evaluatorHost ?? process.env.
|
|
29
|
+
function aexeoPluginSandboxed(options = {}) {
|
|
30
|
+
const evaluatorHost = options.evaluatorHost ?? process.env.AEXEO_EVALUATOR_HOST ?? null;
|
|
31
31
|
return {
|
|
32
|
-
id: "aexeo-
|
|
32
|
+
id: "aexeo-emdash",
|
|
33
33
|
version: PACKAGE_VERSION,
|
|
34
34
|
// Subpath export of this same package; must match package.json
|
|
35
35
|
// `exports["./sandbox"]`.
|
|
@@ -37,7 +37,7 @@ function seogeoPluginSandboxed(options = {}) {
|
|
|
37
37
|
format: "standard",
|
|
38
38
|
capabilities: buildCapabilities(evaluatorHost),
|
|
39
39
|
allowedHosts: buildAllowedHosts(evaluatorHost),
|
|
40
|
-
// Pages mount at /admin/plugins/aexeo-
|
|
40
|
+
// Pages mount at /admin/plugins/aexeo-emdash/<path>. The page name
|
|
41
41
|
// (without leading slash) is what the sandbox entry receives in
|
|
42
42
|
// `body.page` when emdash POSTs the page_load interaction.
|
|
43
43
|
adminPages: [
|
|
@@ -46,15 +46,15 @@ function seogeoPluginSandboxed(options = {}) {
|
|
|
46
46
|
{ path: "/setup", label: "Setup" }
|
|
47
47
|
],
|
|
48
48
|
adminWidgets: [
|
|
49
|
-
{ id: "
|
|
49
|
+
{ id: "aexeo-score", size: "third", title: "SEO score" }
|
|
50
50
|
]
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// src/index.ts
|
|
55
|
-
function
|
|
55
|
+
function aexeoPlugin(options = {}) {
|
|
56
56
|
return {
|
|
57
|
-
id: "aexeo-
|
|
57
|
+
id: "aexeo-emdash",
|
|
58
58
|
version: PACKAGE_VERSION,
|
|
59
59
|
// Subpath import resolved by the consumer's bundler at build
|
|
60
60
|
// time — must match the package.json `exports["./configured"]`
|
|
@@ -76,12 +76,21 @@ function seogeoPlugin(options = {}) {
|
|
|
76
76
|
// the structured-clone semantics of that codegen.
|
|
77
77
|
options: {
|
|
78
78
|
...options.collections === void 0 ? {} : { collections: [...options.collections] },
|
|
79
|
-
...options.
|
|
79
|
+
...options.includeCollections === void 0 ? {} : { includeCollections: [...options.includeCollections] },
|
|
80
|
+
...options.excludeCollections === void 0 ? {} : { excludeCollections: [...options.excludeCollections] },
|
|
81
|
+
...options.suppressions === void 0 ? {} : {
|
|
82
|
+
suppressions: options.suppressions.map((rule) => ({
|
|
83
|
+
...rule,
|
|
84
|
+
...rule.ruleIds === void 0 ? {} : { ruleIds: [...rule.ruleIds] },
|
|
85
|
+
...rule.collections === void 0 ? {} : { collections: [...rule.collections] },
|
|
86
|
+
...rule.statuses === void 0 ? {} : { statuses: [...rule.statuses] }
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
80
89
|
},
|
|
81
90
|
capabilities: [
|
|
82
91
|
"read:content",
|
|
83
92
|
"read:schema",
|
|
84
|
-
"kv:
|
|
93
|
+
"kv:aexeo-baselines"
|
|
85
94
|
],
|
|
86
95
|
// adminPages drives the admin sidebar — one nav entry per item.
|
|
87
96
|
// Don't list "/" alongside "/findings": both would render as
|
|
@@ -95,11 +104,11 @@ function seogeoPlugin(options = {}) {
|
|
|
95
104
|
{ path: "/document", label: "Document SEO" }
|
|
96
105
|
],
|
|
97
106
|
adminWidgets: [
|
|
98
|
-
{ id: "
|
|
107
|
+
{ id: "aexeo-score", size: "third", title: "SEO score" }
|
|
99
108
|
]
|
|
100
109
|
};
|
|
101
110
|
}
|
|
102
111
|
export {
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
aexeoPlugin,
|
|
113
|
+
aexeoPluginSandboxed
|
|
105
114
|
};
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/sandbox-entry.js
CHANGED
|
@@ -131,7 +131,7 @@ async function evaluateAndPersistAll(ctx, options) {
|
|
|
131
131
|
}
|
|
132
132
|
const result = await options.evaluator(documents);
|
|
133
133
|
if (!result.ok) {
|
|
134
|
-
log?.error?.(`
|
|
134
|
+
log?.error?.(`aexeo evaluator failure (${result.reason})`, {
|
|
135
135
|
detail: result.detail
|
|
136
136
|
});
|
|
137
137
|
summary.errors.push(`${result.reason}: ${result.detail}`);
|
|
@@ -149,7 +149,13 @@ async function evaluateAndPersistAll(ctx, options) {
|
|
|
149
149
|
}
|
|
150
150
|
let totalAfterSuppression = 0;
|
|
151
151
|
for (const [route, findings] of findingsByRoute2) {
|
|
152
|
-
const
|
|
152
|
+
const stored = adaptedByRoute.get(route);
|
|
153
|
+
const context = {
|
|
154
|
+
route,
|
|
155
|
+
...stored?.meta.collection === void 0 ? {} : { collection: stored.meta.collection },
|
|
156
|
+
...stored?.meta.status === void 0 ? {} : { status: stored.meta.status }
|
|
157
|
+
};
|
|
158
|
+
const filtered = suppressionFilter === void 0 ? findings : suppressionFilter.apply(context, findings);
|
|
153
159
|
await kv.set(findingsKey(route), { route, findings: filtered });
|
|
154
160
|
summary.routesUpdated += 1;
|
|
155
161
|
totalAfterSuppression += filtered.length;
|
|
@@ -364,8 +370,8 @@ function failure(reason, rejected) {
|
|
|
364
370
|
|
|
365
371
|
// src/mcp.ts
|
|
366
372
|
var checkTool = {
|
|
367
|
-
name: "
|
|
368
|
-
description: "Evaluate a list of EmdashDocument values against the
|
|
373
|
+
name: "aexeo.check",
|
|
374
|
+
description: "Evaluate a list of EmdashDocument values against the Aexeo rule engine and return the stable Finding contract. Mirrors the CLI's `aexeo-cli check` surface so agents, the admin UI, and the CI gate all see the same rule ids.",
|
|
369
375
|
inputSchema: {
|
|
370
376
|
type: "object",
|
|
371
377
|
properties: {
|
|
@@ -375,7 +381,7 @@ var checkTool = {
|
|
|
375
381
|
},
|
|
376
382
|
configJson: {
|
|
377
383
|
type: "string",
|
|
378
|
-
description: "Optional JSON-serialized
|
|
384
|
+
description: "Optional JSON-serialized Aexeo config. When absent the evaluator uses Config::default(), matching a cold CLI run."
|
|
379
385
|
}
|
|
380
386
|
},
|
|
381
387
|
required: ["documents"]
|
|
@@ -402,8 +408,8 @@ var checkTool = {
|
|
|
402
408
|
}
|
|
403
409
|
};
|
|
404
410
|
var indexNowSubmitTool = {
|
|
405
|
-
name: "
|
|
406
|
-
description: "Submit a list of URLs to the IndexNow freshness endpoint. Mirrors the
|
|
411
|
+
name: "aexeo.indexnow.submit",
|
|
412
|
+
description: "Submit a list of URLs to the IndexNow freshness endpoint. Mirrors the aexeo-cli `indexnow submit` contract: the IndexNow protocol requires every submitted URL to belong to the configured site host. URLs that do not match the host are returned in `rejected` and never sent over the wire.",
|
|
407
413
|
inputSchema: {
|
|
408
414
|
type: "object",
|
|
409
415
|
properties: {
|
|
@@ -435,7 +441,7 @@ function definePlugin(plugin) {
|
|
|
435
441
|
}
|
|
436
442
|
async function handleAdminRoute(input, ctx) {
|
|
437
443
|
ctx.log?.info?.(
|
|
438
|
-
`
|
|
444
|
+
`aexeo route: type=${input.input?.type} page=${input.input?.page ?? ""}`
|
|
439
445
|
);
|
|
440
446
|
const dispatch = { body: input.input, kv: ctx.kv, ctx };
|
|
441
447
|
if (dispatch.body.type === "page_load") {
|
|
@@ -474,7 +480,7 @@ async function handlePageLoad(ctx, page) {
|
|
|
474
480
|
if (normalized === "findings") {
|
|
475
481
|
return renderFindingsPage(ctx);
|
|
476
482
|
}
|
|
477
|
-
if (normalized === "widget:
|
|
483
|
+
if (normalized === "widget:aexeo-score") {
|
|
478
484
|
return renderScoreWidget(ctx);
|
|
479
485
|
}
|
|
480
486
|
if (normalized === "document") {
|
|
@@ -504,7 +510,7 @@ async function handleRefresh(ctx) {
|
|
|
504
510
|
return {
|
|
505
511
|
ok: false,
|
|
506
512
|
reason: "config_missing",
|
|
507
|
-
detail: "sidecar not configured \u2014 open the
|
|
513
|
+
detail: "sidecar not configured \u2014 open the Aexeo Setup page and enter your evaluator URL and token"
|
|
508
514
|
};
|
|
509
515
|
}
|
|
510
516
|
return evaluateViaSidecar(
|
|
@@ -763,10 +769,10 @@ function documentFindingsTable(findings) {
|
|
|
763
769
|
async function renderSetupPage(ctx, options = {}) {
|
|
764
770
|
const existing = await readSidecarConfig(ctx.kv);
|
|
765
771
|
const blocks = [
|
|
766
|
-
{ type: "header", text: "
|
|
772
|
+
{ type: "header", text: "Aexeo setup" },
|
|
767
773
|
{
|
|
768
774
|
type: "context",
|
|
769
|
-
text: "Paste the URL and auth token of your deployed
|
|
775
|
+
text: "Paste the URL and auth token of your deployed aexeo-crawl-worker. These are stored in plugin KV and read at runtime \u2014 no rebuild required after a change. Rotate the token here whenever you redeploy the sidecar with a new secret."
|
|
770
776
|
}
|
|
771
777
|
];
|
|
772
778
|
if (options.bannerError !== void 0) {
|
|
@@ -790,7 +796,7 @@ async function renderSetupPage(ctx, options = {}) {
|
|
|
790
796
|
type: "text_input",
|
|
791
797
|
action_id: "evaluator_url",
|
|
792
798
|
label: "Sidecar URL",
|
|
793
|
-
placeholder: "https://
|
|
799
|
+
placeholder: "https://aexeo-crawl-worker.<subdomain>.workers.dev",
|
|
794
800
|
initial_value: options.initialUrl ?? existing?.url ?? ""
|
|
795
801
|
},
|
|
796
802
|
{
|
package/dist/sandbox.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ export interface SandboxedPluginDescriptor {
|
|
|
18
18
|
adminWidgets?: readonly PluginAdminWidget[];
|
|
19
19
|
allowedHosts?: readonly string[];
|
|
20
20
|
}
|
|
21
|
-
export interface
|
|
21
|
+
export interface AexeoSandboxedOptions {
|
|
22
22
|
evaluatorHost?: string;
|
|
23
23
|
}
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function aexeoPluginSandboxed(options?: AexeoSandboxedOptions): SandboxedPluginDescriptor;
|
|
25
25
|
//# sourceMappingURL=sandbox.d.ts.map
|
package/dist/sandbox.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAGnB,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAGxC,YAAY,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC5C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAeD,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAGnB,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAGxC,YAAY,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC5C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAeD,MAAM,WAAW,qBAAqB;IAgBpC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,qBAA0B,GAClC,yBAAyB,CAwB3B"}
|
package/dist/suppressions.d.ts
CHANGED
|
@@ -2,9 +2,16 @@ import type { Finding } from "./types.js";
|
|
|
2
2
|
export interface Suppression {
|
|
3
3
|
routePattern?: string;
|
|
4
4
|
ruleIds?: readonly string[];
|
|
5
|
+
collections?: readonly string[];
|
|
6
|
+
statuses?: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
export interface SuppressionContext {
|
|
9
|
+
route: string;
|
|
10
|
+
collection?: string;
|
|
11
|
+
status?: string;
|
|
5
12
|
}
|
|
6
13
|
export interface SuppressionFilter {
|
|
7
|
-
apply(
|
|
14
|
+
apply(context: SuppressionContext, findings: readonly Finding[]): Finding[];
|
|
8
15
|
}
|
|
9
16
|
export declare function compileSuppressions(rules: readonly Suppression[] | undefined): SuppressionFilter;
|
|
10
17
|
//# sourceMappingURL=suppressions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suppressions.d.ts","sourceRoot":"","sources":["../src/suppressions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"suppressions.d.ts","sourceRoot":"","sources":["../src/suppressions.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAsB1C,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAOD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,WAAW,iBAAiB;IAChC,KAAK,CACH,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,OAAO,EAAE,CAAC;CACd;AASD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,GACxC,iBAAiB,CAyEnB"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "0.
|
|
1
|
+
export declare const PACKAGE_VERSION = "0.6.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeptus/aexeo-emdash",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Aexeo
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Aexeo SEO/GEO content evaluator as an emdash plugin",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"author": "
|
|
6
|
+
"author": "Aeptus",
|
|
7
7
|
"homepage": "https://github.com/schiste/Aexeo/tree/main/packages/aexeo-emdash",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
Binary file
|