@aeptus/aexeo-emdash 0.1.1 → 0.1.2

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 CHANGED
@@ -6,6 +6,38 @@ and the project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.2] - 2026-04-28
10
+
11
+ Functional patch. Three bugs flagged by the aeptus web team after
12
+ testing 0.1.1 against emdash 0.8.0; same `wasm/` as `0.1.0` (no
13
+ rule-engine change).
14
+
15
+ **Compatibility:** verified against emdash `0.7.0` and `0.8.0`.
16
+
17
+ ### Fixed
18
+
19
+ - **Duplicate "SEO findings" sidebar entry.** The descriptor and
20
+ the runtime admin both declared `/` AND `/findings` as adminPages
21
+ with the same label, so emdash rendered two duplicate links.
22
+ Drop the `/` entry — root-URL navigation
23
+ (`/admin/plugins/<id>/`) still works because the dispatcher in
24
+ `src/configured.ts` aliases the empty page name to the findings
25
+ page.
26
+
27
+ ### Added
28
+
29
+ - **`seogeoPlugin({ collections })` option** for configured mode.
30
+ Lets sites with non-default collection slugs override which
31
+ collections the Refresh button sweeps. Defaults to
32
+ `["posts", "pages"]` when omitted (the slugs from
33
+ `@emdash-cms/template-blog-cloudflare`). Pointing at a missing
34
+ slug is non-fatal — the bridge returns empty and the sweep
35
+ records the missing collection in the Refresh summary's `errors`.
36
+ - The Refresh sweep now threads the resolved collection list
37
+ through `handleAdminRoute` → `handleRefresh` →
38
+ `evaluateAndPersistAll` so the runtime honors what the descriptor
39
+ passed.
40
+
9
41
  ## [0.1.1] - 2026-04-28
10
42
 
11
43
  Documentation-only patch. No runtime changes — same `dist/` and
@@ -68,6 +100,7 @@ First public release.
68
100
  `astro.config.mjs` for the WASM import to resolve to a
69
101
  precompiled `WebAssembly.Module`. Not optional.
70
102
 
71
- [Unreleased]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.1.1...HEAD
103
+ [Unreleased]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.1.2...HEAD
104
+ [0.1.2]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.1.1...aexeo-emdash-v0.1.2
72
105
  [0.1.1]: https://github.com/schiste/Aexeo/compare/aexeo-emdash-v0.1.0...aexeo-emdash-v0.1.1
73
106
  [0.1.0]: https://github.com/schiste/Aexeo/releases/tag/aexeo-emdash-v0.1.0
@@ -1,5 +1,12 @@
1
1
  export interface ConfiguredPluginOptions {
2
+ /**
3
+ * emdash collections the Refresh button sweeps. Comes through from
4
+ * the descriptor's `options` field — see `seogeoPlugin({ collections })`
5
+ * in src/index.ts. When undefined, falls back to DEFAULT_COLLECTIONS
6
+ * (`["posts", "pages"]`).
7
+ */
8
+ collections?: readonly string[];
2
9
  }
3
- export declare function createPlugin(_options?: ConfiguredPluginOptions): unknown;
10
+ export declare function createPlugin(options?: ConfiguredPluginOptions): unknown;
4
11
  export default createPlugin;
5
12
  //# sourceMappingURL=configured.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"configured.d.ts","sourceRoot":"","sources":["../src/configured.ts"],"names":[],"mappings":"AA4EA,MAAM,WAAW,uBAAuB;CAIvC;AAED,wBAAgB,YAAY,CAAC,QAAQ,GAAE,uBAA4B,GAAG,OAAO,CAiC5E;AAID,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"configured.d.ts","sourceRoot":"","sources":["../src/configured.ts"],"names":[],"mappings":"AA6EA,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAmD3E;AAID,eAAe,YAAY,CAAC"}
@@ -357,24 +357,33 @@ var inProcessEvaluator = async (documents) => {
357
357
  return { ok: false, reason: "wasm_error", detail };
358
358
  }
359
359
  };
360
- function createPlugin(_options = {}) {
360
+ function createPlugin(options = {}) {
361
+ const collections = options.collections === void 0 ? DEFAULT_COLLECTIONS : [...options.collections];
361
362
  return definePlugin({
362
363
  id: "aexeo-seogeo",
363
364
  version: "0.0.1",
364
365
  capabilities: ["read:content"],
365
366
  hooks: {
367
+ // afterSave processes one saved document at a time — collections
368
+ // list isn't needed here. The hook always runs regardless of
369
+ // which collection the document came from.
366
370
  "content:afterSave": (event, ctx) => handleAfterSaveConfigured(event, ctx, inProcessEvaluator)
367
371
  },
368
372
  routes: {
369
- admin: { handler: handleAdminRoute }
373
+ // The Refresh sweep (handleAdminRoute → handleRefresh) is the
374
+ // one consumer of `collections`. Bind the resolved list here so
375
+ // every request handles the same set without re-reading options.
376
+ admin: {
377
+ handler: (ctx) => handleAdminRoute(ctx, collections)
378
+ }
370
379
  },
371
380
  admin: {
372
381
  pages: [
373
- // emdash's /admin/plugins/<id> root navigates to the plugin's
374
- // "/" page — without it, clicking the plugin in the meta list
375
- // produces a "Not Found". Alias "/" to the findings page so
376
- // the entry-point lands somewhere useful.
377
- { path: "/", label: "SEO findings" },
382
+ // The root URL `/admin/plugins/<id>/` routes through the same
383
+ // dispatcher (handlePageLoad treats "" and "findings" as
384
+ // aliases). Listing "/" alongside "/findings" here would create
385
+ // duplicate "SEO findings" sidebar entries — that was the
386
+ // visible 0.1.0 / 0.1.1 bug fixed in 0.1.2.
378
387
  { path: "/findings", label: "SEO findings" },
379
388
  { path: "/document", label: "Document SEO" }
380
389
  ],
@@ -385,7 +394,7 @@ function createPlugin(_options = {}) {
385
394
  });
386
395
  }
387
396
  var configured_default = createPlugin;
388
- async function handleAdminRoute(ctx) {
397
+ async function handleAdminRoute(ctx, collections) {
389
398
  const body = ctx.input;
390
399
  if (body.type === "page_load") {
391
400
  return handlePageLoad(ctx, body.page);
@@ -395,7 +404,7 @@ async function handleAdminRoute(ctx) {
395
404
  return renderDocumentPanel(ctx, body.value);
396
405
  }
397
406
  if (body.action_id === "refresh_findings") {
398
- return handleRefresh(ctx);
407
+ return handleRefresh(ctx, collections);
399
408
  }
400
409
  if (body.action_id.startsWith("filter:")) {
401
410
  return renderFindingsPage(ctx);
@@ -426,11 +435,12 @@ async function handlePageLoad(ctx, page) {
426
435
  }
427
436
  return notFound(page);
428
437
  }
429
- async function handleRefresh(ctx) {
438
+ async function handleRefresh(ctx, collections) {
430
439
  let summary;
431
440
  try {
432
441
  summary = await evaluateAndPersistAll(ctx, {
433
- evaluator: inProcessEvaluator
442
+ evaluator: inProcessEvaluator,
443
+ collections
434
444
  });
435
445
  } catch (err) {
436
446
  const detail = err instanceof Error ? err.message : String(err);
package/dist/index.d.ts CHANGED
@@ -18,6 +18,27 @@ export interface ConfiguredPluginDescriptor {
18
18
  }[];
19
19
  }
20
20
  export interface SeogeoPluginOptions {
21
+ /**
22
+ * emdash collections the plugin sweeps when an admin clicks
23
+ * Refresh on the findings page. Order doesn't matter; the sweep
24
+ * iterates each collection sequentially and groups findings by
25
+ * document route. The per-document `content:afterSave` hook always
26
+ * runs regardless of this list — saving a document in any
27
+ * collection triggers an evaluation for that one document.
28
+ *
29
+ * Defaults to `["posts", "pages"]` (the slugs the
30
+ * `@emdash-cms/template-blog-cloudflare` template ships with).
31
+ * Override when your schema uses different collection slugs:
32
+ *
33
+ * seogeoPlugin({ collections: ["posts", "guides", "products"] })
34
+ *
35
+ * Pointing this at a slug that doesn't exist in your schema is
36
+ * non-fatal: the bridge's `content.list` returns empty and the
37
+ * sweep records the missing collection in the Refresh summary's
38
+ * `errors` field. Safe to ship a superset for a project that may
39
+ * add collections later.
40
+ */
41
+ collections?: readonly string[];
21
42
  }
22
43
  export declare function seogeoPlugin(options?: SeogeoPluginOptions): ConfiguredPluginDescriptor;
23
44
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAWtB,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,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,mBAAmB;CAGnC;AAED,wBAAgB,YAAY,CAC1B,OAAO,GAAE,mBAAwB,GAChC,0BAA0B,CA0B5B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAWtB,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,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,mBAAmB;IAClC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED,wBAAgB,YAAY,CAC1B,OAAO,GAAE,mBAAwB,GAChC,0BAA0B,CAsC5B"}
package/dist/index.js CHANGED
@@ -57,17 +57,27 @@ function seogeoPlugin(options = {}) {
57
57
  // time — must match the package.json `exports["./configured"]`
58
58
  // entry.
59
59
  entrypoint: "@aeptus/aexeo-emdash/configured",
60
- options: { ...options },
60
+ // emdash's astro integration JSON-serializes `options` and emits
61
+ // an importer that does `createPlugin(<options>)` at boot.
62
+ // Anything we put here is what the runtime entry's createPlugin
63
+ // receives. Spread to a plain object so readonly arrays survive
64
+ // the structured-clone semantics of that codegen.
65
+ options: {
66
+ ...options.collections === void 0 ? {} : { collections: [...options.collections] }
67
+ },
61
68
  capabilities: [
62
69
  "read:content",
63
70
  "read:schema",
64
71
  "kv:seogeo-baselines"
65
72
  ],
73
+ // adminPages drives the admin sidebar — one nav entry per item.
74
+ // Don't list "/" alongside "/findings": both would render as
75
+ // duplicate "SEO findings" links in the sidebar (0.1.0 / 0.1.1
76
+ // shipped with this duplicate; fixed in 0.1.2). The root URL
77
+ // /admin/plugins/<id>/ still routes correctly because the
78
+ // dispatcher in src/configured.ts treats "" and "findings" as
79
+ // the same page.
66
80
  adminPages: [
67
- // "/" alias is required so emdash's /admin/plugins/<id> root
68
- // doesn't 404 — that page is where the meta /admin/plugins
69
- // list links to when an operator clicks the plugin entry.
70
- { path: "/", label: "SEO findings" },
71
81
  { path: "/findings", label: "SEO findings" },
72
82
  { path: "/document", label: "Document SEO" }
73
83
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeptus/aexeo-emdash",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Aexeo seogeo SEO/GEO content evaluator as an emdash plugin",
5
5
  "license": "MIT",
6
6
  "author": "Aexeo",