@cogenta/cli 0.3.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 +3856 -0
- package/dist/admin-assets/assets/index-CxZHGwVA.css +1 -0
- package/dist/admin-assets/assets/index-fYXyHpn8.js +1090 -0
- package/dist/admin-assets/assets/vips-_dmTUXFO.wasm +0 -0
- package/dist/admin-assets/assets/vips-es6-DrQTTS8Q.js +7 -0
- package/dist/admin-assets/branding/logo-cogenta-dark.png +0 -0
- package/dist/admin-assets/branding/logo-cogenta-icon.png +0 -0
- package/dist/admin-assets/branding/logo-cogenta-light.png +0 -0
- package/dist/admin-assets/branding/logo-cogenta-small.png +0 -0
- package/dist/admin-assets/branding/logo-cogenta-transparent.png +0 -0
- package/dist/admin-assets/branding/logo-cogenta.png +0 -0
- package/dist/admin-assets/favicon.png +0 -0
- package/dist/admin-assets/fonts/ibm-plex-mono-400.woff2 +0 -0
- package/dist/admin-assets/fonts/ibm-plex-mono-500.woff2 +0 -0
- package/dist/admin-assets/fonts/ibm-plex-mono-600.woff2 +0 -0
- package/dist/admin-assets/fonts/ibm-plex-mono-700.woff2 +0 -0
- package/dist/admin-assets/fonts/ibm-plex-sans-var.woff2 +0 -0
- package/dist/admin-assets/fonts/jetbrains-mono-var.woff2 +0 -0
- package/dist/admin-assets/fonts/space-grotesk-var.woff2 +0 -0
- package/dist/admin-assets/index.html +3 -2
- package/dist/bin.js +6 -0
- package/dist/bin.js.map +1 -1
- package/dist/commands/admin-assets.d.ts +1 -0
- package/dist/commands/admin-assets.d.ts.map +1 -1
- package/dist/commands/admin-assets.js +19 -1
- package/dist/commands/admin-assets.js.map +1 -1
- package/dist/commands/agent-runtime.d.ts +151 -0
- package/dist/commands/agent-runtime.d.ts.map +1 -0
- package/dist/commands/agent-runtime.js +519 -0
- package/dist/commands/agent-runtime.js.map +1 -0
- package/dist/commands/assistant.d.ts +89 -3
- package/dist/commands/assistant.d.ts.map +1 -1
- package/dist/commands/assistant.js +206 -39
- package/dist/commands/assistant.js.map +1 -1
- package/dist/commands/audit-integrity-alert.d.ts +23 -0
- package/dist/commands/audit-integrity-alert.d.ts.map +1 -0
- package/dist/commands/audit-integrity-alert.js +33 -0
- package/dist/commands/audit-integrity-alert.js.map +1 -0
- package/dist/commands/backup.d.ts +78 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +238 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/channels.d.ts +47 -0
- package/dist/commands/channels.d.ts.map +1 -0
- package/dist/commands/channels.js +277 -0
- package/dist/commands/channels.js.map +1 -0
- package/dist/commands/default-logo.d.ts +27 -0
- package/dist/commands/default-logo.d.ts.map +1 -0
- package/dist/commands/default-logo.js +30 -0
- package/dist/commands/default-logo.js.map +1 -0
- package/dist/commands/doctor.d.ts +5 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +74 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.d.ts +22 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +182 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/forms-page.d.ts +75 -0
- package/dist/commands/forms-page.d.ts.map +1 -0
- package/dist/commands/forms-page.js +191 -0
- package/dist/commands/forms-page.js.map +1 -0
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +13 -1
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/mcp.d.ts +35 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +277 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/roles.d.ts +22 -0
- package/dist/commands/roles.d.ts.map +1 -0
- package/dist/commands/roles.js +79 -0
- package/dist/commands/roles.js.map +1 -0
- package/dist/commands/search-page.d.ts +15 -0
- package/dist/commands/search-page.d.ts.map +1 -1
- package/dist/commands/search-page.js +22 -16
- package/dist/commands/search-page.js.map +1 -1
- package/dist/commands/seo.d.ts +92 -4
- package/dist/commands/seo.d.ts.map +1 -1
- package/dist/commands/seo.js +168 -4
- package/dist/commands/seo.js.map +1 -1
- package/dist/commands/serve.d.ts +489 -6
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +3883 -164
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/site-plan.d.ts +27 -1
- package/dist/commands/site-plan.d.ts.map +1 -1
- package/dist/commands/site-plan.js +71 -4
- package/dist/commands/site-plan.js.map +1 -1
- package/dist/commands/term-archive-page.d.ts +94 -0
- package/dist/commands/term-archive-page.d.ts.map +1 -0
- package/dist/commands/term-archive-page.js +195 -0
- package/dist/commands/term-archive-page.js.map +1 -0
- package/dist/commands/theme-css.d.ts +22 -7
- package/dist/commands/theme-css.d.ts.map +1 -1
- package/dist/commands/theme-css.js +33 -8
- package/dist/commands/theme-css.js.map +1 -1
- package/dist/commands/theme-registry.d.ts +94 -0
- package/dist/commands/theme-registry.d.ts.map +1 -0
- package/dist/commands/theme-registry.js +130 -0
- package/dist/commands/theme-registry.js.map +1 -0
- package/dist/commands/theme-render.d.ts +360 -2
- package/dist/commands/theme-render.d.ts.map +1 -1
- package/dist/commands/theme-render.js +984 -29
- package/dist/commands/theme-render.js.map +1 -1
- package/dist/commands/theme-wiring.d.ts +107 -0
- package/dist/commands/theme-wiring.d.ts.map +1 -0
- package/dist/commands/theme-wiring.js +295 -0
- package/dist/commands/theme-wiring.js.map +1 -0
- package/dist/commands/tools.d.ts +71 -0
- package/dist/commands/tools.d.ts.map +1 -0
- package/dist/commands/tools.js +330 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commands/update.d.ts +25 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +193 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/users.d.ts.map +1 -1
- package/dist/commands/users.js +6 -36
- package/dist/commands/users.js.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +146 -4
- package/dist/index.js.map +1 -1
- package/dist/invite-mail.d.ts +39 -0
- package/dist/invite-mail.d.ts.map +1 -0
- package/dist/invite-mail.js +39 -0
- package/dist/invite-mail.js.map +1 -0
- package/dist/reset-mail.d.ts +39 -0
- package/dist/reset-mail.d.ts.map +1 -0
- package/dist/reset-mail.js +45 -0
- package/dist/reset-mail.js.map +1 -0
- package/dist/update/apply.d.ts +54 -0
- package/dist/update/apply.d.ts.map +1 -0
- package/dist/update/apply.js +77 -0
- package/dist/update/apply.js.map +1 -0
- package/dist/update/changelog-risk.d.ts +20 -0
- package/dist/update/changelog-risk.d.ts.map +1 -0
- package/dist/update/changelog-risk.js +55 -0
- package/dist/update/changelog-risk.js.map +1 -0
- package/dist/update/check.d.ts +60 -0
- package/dist/update/check.d.ts.map +1 -0
- package/dist/update/check.js +56 -0
- package/dist/update/check.js.map +1 -0
- package/dist/update/contract-risk.d.ts +55 -0
- package/dist/update/contract-risk.d.ts.map +1 -0
- package/dist/update/contract-risk.js +75 -0
- package/dist/update/contract-risk.js.map +1 -0
- package/dist/update/history.d.ts +30 -0
- package/dist/update/history.d.ts.map +1 -0
- package/dist/update/history.js +38 -0
- package/dist/update/history.js.map +1 -0
- package/dist/update/index.d.ts +28 -0
- package/dist/update/index.d.ts.map +1 -0
- package/dist/update/index.js +11 -0
- package/dist/update/index.js.map +1 -0
- package/dist/update/list-restore-points.d.ts +20 -0
- package/dist/update/list-restore-points.d.ts.map +1 -0
- package/dist/update/list-restore-points.js +35 -0
- package/dist/update/list-restore-points.js.map +1 -0
- package/dist/update/npm-registry.d.ts +11 -0
- package/dist/update/npm-registry.d.ts.map +1 -0
- package/dist/update/npm-registry.js +74 -0
- package/dist/update/npm-registry.js.map +1 -0
- package/dist/update/restore-point.d.ts +26 -0
- package/dist/update/restore-point.d.ts.map +1 -0
- package/dist/update/restore-point.js +23 -0
- package/dist/update/restore-point.js.map +1 -0
- package/dist/update/tar.d.ts +27 -0
- package/dist/update/tar.d.ts.map +1 -0
- package/dist/update/tar.js +56 -0
- package/dist/update/tar.js.map +1 -0
- package/dist/update/version-compare.d.ts +17 -0
- package/dist/update/version-compare.d.ts.map +1 -0
- package/dist/update/version-compare.js +42 -0
- package/dist/update/version-compare.js.map +1 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +18 -0
- package/dist/version.js.map +1 -0
- package/package.json +33 -13
- package/dist/admin-assets/assets/index-Buwdj1V2.js +0 -71
- package/dist/admin-assets/assets/index-Csef0SiA.css +0 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { createDatabaseRegistry, createLogger, isCogentaError, loadConfig, } from '@cogenta/core';
|
|
5
|
+
import { createRolePermissionStore, serialiseRolePermissionExport } from '@cogenta/schema';
|
|
6
|
+
import { loadSchemaModule } from './serve.js';
|
|
7
|
+
const DEFAULT_EXPORT_FILE = 'cogenta.role-permissions.json';
|
|
8
|
+
const USAGE = `Usage
|
|
9
|
+
cogenta roles export [--out <path>] Freeze the role permission override table into a file
|
|
10
|
+
|
|
11
|
+
Reads every row an admin has written from the appearance-of-permissions
|
|
12
|
+
screen (or a previous "roles export") and writes it as JSON — a
|
|
13
|
+
point-in-time copy for a site that wants its permission overrides versioned
|
|
14
|
+
in git too, alongside cogenta.schema.*. Re-running this command overwrites
|
|
15
|
+
the file with the table's current state; it never writes back to the table.
|
|
16
|
+
`;
|
|
17
|
+
function reportCogentaError(error, stderr) {
|
|
18
|
+
if (isCogentaError(error)) {
|
|
19
|
+
stderr(`${error.code}: ${error.message}\n`);
|
|
20
|
+
if (error.hint !== undefined)
|
|
21
|
+
stderr(`${error.hint}\n`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
stderr(`${error instanceof Error ? error.stack : String(error)}\n`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function runExport(options) {
|
|
28
|
+
const { out, stderr } = options;
|
|
29
|
+
const env = options.env ?? process.env;
|
|
30
|
+
const logger = options.logger ?? createLogger();
|
|
31
|
+
let projectRoot;
|
|
32
|
+
let loaded;
|
|
33
|
+
try {
|
|
34
|
+
loaded = await loadConfig({
|
|
35
|
+
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
36
|
+
env,
|
|
37
|
+
});
|
|
38
|
+
projectRoot = loaded.path === null ? (options.cwd ?? process.cwd()) : dirname(loaded.path);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
reportCogentaError(error, stderr);
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
const selection = await createDatabaseRegistry({ logger }).select(loaded.config.database);
|
|
45
|
+
try {
|
|
46
|
+
const { collections, taxonomies } = await loadSchemaModule(projectRoot);
|
|
47
|
+
const store = createRolePermissionStore({ db: selection.instance, collections, taxonomies });
|
|
48
|
+
const records = await store.list();
|
|
49
|
+
const document = serialiseRolePermissionExport(records);
|
|
50
|
+
const path = options.file ?? join(projectRoot, DEFAULT_EXPORT_FILE);
|
|
51
|
+
await writeFile(path, `${JSON.stringify(document, null, 2)}\n`, 'utf8');
|
|
52
|
+
out.heading('Role permission overrides exported');
|
|
53
|
+
out.ok(`${records.length} override${records.length === 1 ? '' : 's'} written to ${path}`);
|
|
54
|
+
if (records.length === 0) {
|
|
55
|
+
out.detail('The database table holds no override yet — every role permission is still read from cogenta.schema.*.');
|
|
56
|
+
}
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
reportCogentaError(error, stderr);
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await selection.dispose();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export async function runRoles(options) {
|
|
68
|
+
const { subcommand, out, stderr } = options;
|
|
69
|
+
if (subcommand === undefined) {
|
|
70
|
+
out.line(USAGE);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
if (subcommand === 'export')
|
|
74
|
+
return runExport(options);
|
|
75
|
+
stderr(`Unknown "cogenta roles" subcommand: "${subcommand}".\n`);
|
|
76
|
+
out.line(USAGE);
|
|
77
|
+
return 2;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.js","sourceRoot":"","sources":["../../src/commands/roles.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { AccessContext, ContentGateway, SearchRouter } from '@cogenta/api';
|
|
2
|
+
import type { MediaAsset } from '@cogenta/render';
|
|
2
3
|
import { type CollectionDefinition } from '@cogenta/schema';
|
|
4
|
+
import type { SeoRenderDefaults } from './seo.js';
|
|
5
|
+
import { type BrandingSettings, type PageChromeMenus, type SiteIdentityMedia } from './theme-render.js';
|
|
3
6
|
/**
|
|
4
7
|
* `GET /search?q=…` — the public half of L10 task 3.
|
|
5
8
|
*
|
|
@@ -31,6 +34,18 @@ export interface SearchPageOptions {
|
|
|
31
34
|
};
|
|
32
35
|
/** The joined skin+theme stylesheet `cogenta serve` serves at `STYLESHEET_PATH` (`theme-render.ts`'s `joinStyles`). `null` when neither could be loaded — served unstyled rather than refused. */
|
|
33
36
|
readonly styles: string | null;
|
|
37
|
+
/** Same menu wiring the rest of the public site uses (`theme-render.ts`). Absent renders an empty header/footer nav. */
|
|
38
|
+
readonly menus?: PageChromeMenus;
|
|
39
|
+
/** Same live branding read the rest of the public site uses (`theme-render.ts`). Absent means full Cogenta credit. */
|
|
40
|
+
readonly branding?: () => Promise<BrandingSettings>;
|
|
41
|
+
/** Same live active-theme read the rest of the public site uses (`theme-render.ts`). Absent renders with the default theme. */
|
|
42
|
+
readonly activeTheme?: () => Promise<string | null>;
|
|
43
|
+
/** Same live SEO settings read the rest of the public site uses (`theme-render.ts`) — only the search-verification meta tags apply here (fiche 50 task 2). */
|
|
44
|
+
readonly seo?: () => Promise<SeoRenderDefaults>;
|
|
45
|
+
/** Same live site-identity read every other public page uses (audit T01) — the logo and favicon belong on this page too, or the search results look like a different site. */
|
|
46
|
+
readonly identity?: () => Promise<SiteIdentityMedia>;
|
|
47
|
+
/** Same batch media loader (`theme-render.ts`). Needed only to resolve the identity above; absent means the site name in text. */
|
|
48
|
+
readonly loadMedia?: (ids: readonly string[]) => Promise<ReadonlyMap<string, MediaAsset>>;
|
|
34
49
|
}
|
|
35
50
|
/**
|
|
36
51
|
* The whole page. Returns HTML; the caller decides the status code (always
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-page.d.ts","sourceRoot":"","sources":["../../src/commands/search-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC/E,OAAO,EAAa,KAAK,oBAAoB,EAAkB,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"search-page.d.ts","sourceRoot":"","sources":["../../src/commands/search-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAa,KAAK,oBAAoB,EAAkB,MAAM,iBAAiB,CAAA;AAEtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAA;AAE1B;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;QACpB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;QACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;KAC/B,CAAA;IACD,kMAAkM;IAClM,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,wHAAwH;IACxH,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,sHAAsH;IACtH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACnD,+HAA+H;IAC/H,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACnD,8JAA8J;IAC9J,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC/C,8KAA8K;IAC9K,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACpD,kIAAkI;IAClI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAA;CAC1F;AA+ED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CA4DjB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildPath } from '@cogenta/schema';
|
|
2
2
|
import { escapeHtmlAttribute, escapeHtmlText } from '@cogenta/seo';
|
|
3
|
-
import {
|
|
3
|
+
import { renderPageChrome, } from './theme-render.js';
|
|
4
4
|
/**
|
|
5
5
|
* Turns hits into links.
|
|
6
6
|
*
|
|
@@ -82,23 +82,29 @@ export async function renderSearchPage(query, options, context) {
|
|
|
82
82
|
: trimmed.length === 0
|
|
83
83
|
? ''
|
|
84
84
|
: resultList(results);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
// The real site chrome (`renderPageChrome`, `theme-render.ts`) — the same
|
|
86
|
+
// skip link, header and footer every collection page renders, not a
|
|
87
|
+
// second, thinner `<html>` shell of this file's own (L20 audit, points
|
|
88
|
+
// 8-9): the stylesheet was always linked here, but the markup its
|
|
89
|
+
// selectors (`.cg-site-header`, the skip link, …) target was never on the
|
|
90
|
+
// page, which is what made it look unstyled.
|
|
91
|
+
return renderPageChrome({
|
|
92
|
+
site: options.site,
|
|
93
|
+
locale: options.site.defaultLocale,
|
|
94
|
+
styles: options.styles,
|
|
95
|
+
headHtml: `<title>${heading} — ${escapeHtmlText(options.site.name)}</title>
|
|
96
|
+
<meta name="robots" content="noindex, follow" />`,
|
|
97
|
+
bodyHtml: `<main class="cg-main" id="cg-main">
|
|
96
98
|
<h1 class="cg-page__title">${heading}</h1>
|
|
97
99
|
${searchForm(trimmed)}
|
|
98
100
|
${main}
|
|
99
|
-
</main
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
</main>`,
|
|
102
|
+
...(options.menus === undefined ? {} : { menus: options.menus }),
|
|
103
|
+
...(options.branding === undefined ? {} : { branding: options.branding }),
|
|
104
|
+
...(options.activeTheme === undefined ? {} : { activeTheme: options.activeTheme }),
|
|
105
|
+
...(options.seo === undefined ? {} : { seo: options.seo }),
|
|
106
|
+
...(options.identity === undefined ? {} : { identity: options.identity }),
|
|
107
|
+
...(options.loadMedia === undefined ? {} : { loadMedia: options.loadMedia }),
|
|
108
|
+
}, context);
|
|
103
109
|
}
|
|
104
110
|
//# sourceMappingURL=search-page.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-page.js","sourceRoot":"","sources":["../../src/commands/search-page.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"search-page.js","sourceRoot":"","sources":["../../src/commands/search-page.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/seo.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AccessContext, type ContentGateway } from '@cogenta/api';
|
|
2
|
-
import type
|
|
3
|
-
import { type HreflangAlternate, type SeoImage, type SeoResource, type SeoSite, type SitemapFile } from '@cogenta/seo';
|
|
2
|
+
import { type CollectionDefinition, type ContentEntry, type SiteSettingsStore } from '@cogenta/schema';
|
|
3
|
+
import { type HreflangAlternate, type SeoImage, type SeoResource, type SeoSite, type SitemapCollectionOverride, type SitemapFile, type SitemapUrl } from '@cogenta/seo';
|
|
4
4
|
/**
|
|
5
5
|
* `@cogenta/seo`, actually wired to the server (L10, tasks 1-2).
|
|
6
6
|
*
|
|
@@ -18,6 +18,41 @@ export interface SiteIdentity {
|
|
|
18
18
|
readonly locales: readonly string[];
|
|
19
19
|
readonly defaultLocale: string;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* The live-editable half of `@cogenta/seo`'s output (fiche 21 task 3): what
|
|
23
|
+
* an admin set on the merged SEO screen's Général/Sitemap/Réseaux sociaux
|
|
24
|
+
* tabs, read straight from `@cogenta/schema`'s `seo.*` site settings — never
|
|
25
|
+
* cached at server startup, the same "read fresh on every request" contract
|
|
26
|
+
* `ThemeRenderOptions.homePath` already uses. Every field is the empty/absent
|
|
27
|
+
* state when nobody has set anything, so a site that never opens this screen
|
|
28
|
+
* renders exactly as it did before this fiche.
|
|
29
|
+
*/
|
|
30
|
+
export interface SeoRenderDefaults {
|
|
31
|
+
readonly titleTemplate: string;
|
|
32
|
+
readonly collectionTitleTemplates: Readonly<Record<string, string>>;
|
|
33
|
+
readonly defaultMetaDescription: string;
|
|
34
|
+
readonly twitterHandle: string;
|
|
35
|
+
readonly defaultSocialImageUrl: string;
|
|
36
|
+
readonly sitemapCollectionSettings: Readonly<Record<string, SitemapCollectionOverride>>;
|
|
37
|
+
/**
|
|
38
|
+
* Fiche 50 task 2 — rendered into `<meta name="google-site-verification">`
|
|
39
|
+
* by `siteVerificationMetaTags` below. Empty means the tag is omitted
|
|
40
|
+
* entirely. Already forced to `''` by `readSeoRenderDefaults` when the
|
|
41
|
+
* fiche 70 task 3 gate (`seo.searchVerificationEnabled`) is off — every
|
|
42
|
+
* consumer of this field sees the gated value, never the raw setting, so
|
|
43
|
+
* there is exactly one place this decision is made.
|
|
44
|
+
*/
|
|
45
|
+
readonly googleSiteVerification: string;
|
|
46
|
+
/** Same shape, rendered into `<meta name="msvalidate.01">` — Bing Webmaster Tools' own meta-tag verification. Same gating as `googleSiteVerification`. */
|
|
47
|
+
readonly bingSiteVerification: string;
|
|
48
|
+
/**
|
|
49
|
+
* Fiche 50 task 4 — an admin's own robots.txt lines, merged in by
|
|
50
|
+
* `renderRobots` below. Already forced to `''` by `readSeoRenderDefaults`
|
|
51
|
+
* when `seo.robotsCustomRulesEnabled` (fiche 70 task 3) is off — the saved
|
|
52
|
+
* text is never lost, only its effect on the served document.
|
|
53
|
+
*/
|
|
54
|
+
readonly robotsCustomRules: string;
|
|
55
|
+
}
|
|
21
56
|
/**
|
|
22
57
|
* The site as the SEO layer sees it.
|
|
23
58
|
*
|
|
@@ -26,8 +61,45 @@ export interface SiteIdentity {
|
|
|
26
61
|
* and `matchPath` then resolves `/blog/hello` as well as `/en/blog/hello`.
|
|
27
62
|
* Saying otherwise would emit canonicals that redirect to themselves — the
|
|
28
63
|
* exact failure `SeoSite`'s own doc comment warns about.
|
|
64
|
+
*
|
|
65
|
+
* `seo` is optional and, when present, only ever *adds* to what the bare
|
|
66
|
+
* `SiteIdentity` already says: an empty `defaultMetaDescription`/
|
|
67
|
+
* `twitterHandle` is left out entirely rather than passed as `''`, so a page
|
|
68
|
+
* that supplies its own description or the Twitter Card's absence is never
|
|
69
|
+
* overridden by a setting nobody actually set.
|
|
70
|
+
*/
|
|
71
|
+
export declare function seoSiteFor(site: SiteIdentity, seo?: SeoRenderDefaults | null): SeoSite;
|
|
72
|
+
/**
|
|
73
|
+
* `<meta>` tags proving domain ownership to Google Search Console and Bing
|
|
74
|
+
* Webmaster Tools (fiche 50 task 2) — the meta-tag verification method both
|
|
75
|
+
* offer as an alternative to DNS/file verification, deliberately the only
|
|
76
|
+
* one this codebase implements: no OAuth flow, no Search Console/Webmaster
|
|
77
|
+
* API call, no second secret to hold (R1/R7). An admin pastes the token the
|
|
78
|
+
* provider's own verification page already shows them; this only renders
|
|
79
|
+
* it, HTML-escaped like every other attribute value `@cogenta/seo` emits.
|
|
80
|
+
*/
|
|
81
|
+
export declare function siteVerificationMetaTags(seo: SeoRenderDefaults | null | undefined): string;
|
|
82
|
+
/**
|
|
83
|
+
* The live `seo.*` site settings (fiche 21 task 3), read fresh — never
|
|
84
|
+
* cached at server startup, the same contract `ThemeRenderOptions.homePath`
|
|
85
|
+
* already keeps for `reading.homePath`. A key nobody has ever written comes
|
|
86
|
+
* back as its registry default (an empty string, or `{}`), the same as the
|
|
87
|
+
* settings store itself does for any other group.
|
|
88
|
+
*/
|
|
89
|
+
export declare function readSeoRenderDefaults(store: SiteSettingsStore): Promise<SeoRenderDefaults>;
|
|
90
|
+
/**
|
|
91
|
+
* The three off-by-default indexing extras (fiche 50 tasks 3 and 5) —
|
|
92
|
+
* separate from `SeoRenderDefaults` above because none of the three feeds a
|
|
93
|
+
* page's `<head>`: IndexNow is pinged from the content-write path, and
|
|
94
|
+
* `llms.txt` is a route toggle, not a per-page render decision.
|
|
29
95
|
*/
|
|
30
|
-
export
|
|
96
|
+
export interface SeoOperationalSettings {
|
|
97
|
+
readonly indexNowEnabled: boolean;
|
|
98
|
+
readonly indexNowKey: string;
|
|
99
|
+
readonly llmsTxtEnabled: boolean;
|
|
100
|
+
}
|
|
101
|
+
/** Read fresh, same "no restart" contract as `readSeoRenderDefaults`. */
|
|
102
|
+
export declare function readSeoOperationalSettings(store: SiteSettingsStore): Promise<SeoOperationalSettings>;
|
|
31
103
|
/**
|
|
32
104
|
* Every published entry of every routed collection, as SEO resources.
|
|
33
105
|
*
|
|
@@ -59,6 +131,8 @@ export interface HeadOptions {
|
|
|
59
131
|
readonly noindex?: boolean;
|
|
60
132
|
/** Overrides the derived title. Used for pages that are not one entry. */
|
|
61
133
|
readonly title?: string;
|
|
134
|
+
/** The admin-editable title templates and default social image (fiche 21 task 3). Absent behaves exactly as before that fiche. */
|
|
135
|
+
readonly seo?: SeoRenderDefaults | null;
|
|
62
136
|
}
|
|
63
137
|
/**
|
|
64
138
|
* The whole `<head>` an entry earns: title, description, canonical,
|
|
@@ -79,10 +153,24 @@ export declare function renderSeoHead(site: SeoSite, resource: SeoResource, opti
|
|
|
79
153
|
* `/sitemap-N.xml` from the same array, so a site that grows past 50 000 URLs
|
|
80
154
|
* starts serving an index without anybody changing a route.
|
|
81
155
|
*/
|
|
82
|
-
export declare function buildSitemapFiles(site: SeoSite, resources: readonly SeoResource[]
|
|
156
|
+
export declare function buildSitemapFiles(site: SeoSite, resources: readonly SeoResource[], collectionOverrides?: Readonly<Record<string, SitemapCollectionOverride>>,
|
|
157
|
+
/**
|
|
158
|
+
* URLs a crawler should know about that are not entries — today, the
|
|
159
|
+
* taxonomy term archives (audit 2026-09-01, 04-taxonomies-menus.md T01).
|
|
160
|
+
*
|
|
161
|
+
* A separate parameter rather than a synthetic `SeoResource`, because a
|
|
162
|
+
* term genuinely is not one: it has no `status`, no `publishedAt` and no
|
|
163
|
+
* translation family, and faking those three to get it through
|
|
164
|
+
* `indexableResources` would put a lie in the middle of the SEO pipeline
|
|
165
|
+
* so that a URL could come out the other end. These arrive already
|
|
166
|
+
* decided-upon and are appended verbatim.
|
|
167
|
+
*/
|
|
168
|
+
extraUrls?: readonly SitemapUrl[]): readonly SitemapFile[];
|
|
83
169
|
export interface RobotsRenderOptions {
|
|
84
170
|
/** False on a staging or preview host: blocks every crawler outright. */
|
|
85
171
|
readonly allowIndexing?: boolean;
|
|
172
|
+
/** An admin's own robots.txt lines (fiche 50 task 4), merged in by `renderRobotsTxt`. */
|
|
173
|
+
readonly customRules?: string;
|
|
86
174
|
}
|
|
87
175
|
export declare function renderRobots(site: SeoSite, options?: RobotsRenderOptions): string;
|
|
88
176
|
//# sourceMappingURL=seo.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../src/commands/seo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAa,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAEjF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../src/commands/seo.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/seo.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ANONYMOUS } from '@cogenta/api';
|
|
2
2
|
import { isCogentaError } from '@cogenta/core';
|
|
3
|
-
import {
|
|
3
|
+
import { SITE_SETTINGS_SITE_SCOPE, } from '@cogenta/schema';
|
|
4
|
+
import { absoluteUrl, alternatesFor, buildJsonLd, buildMetaTags, buildSitemap, escapeHtmlAttribute, groupTranslationFamilies, renderJsonLdScript, renderMetaTags, renderRobotsTxt, sitemapUrlsFor, } from '@cogenta/seo';
|
|
4
5
|
/**
|
|
5
6
|
* The site as the SEO layer sees it.
|
|
6
7
|
*
|
|
@@ -9,14 +10,154 @@ import { alternatesFor, buildJsonLd, buildMetaTags, buildSitemap, groupTranslati
|
|
|
9
10
|
* and `matchPath` then resolves `/blog/hello` as well as `/en/blog/hello`.
|
|
10
11
|
* Saying otherwise would emit canonicals that redirect to themselves — the
|
|
11
12
|
* exact failure `SeoSite`'s own doc comment warns about.
|
|
13
|
+
*
|
|
14
|
+
* `seo` is optional and, when present, only ever *adds* to what the bare
|
|
15
|
+
* `SiteIdentity` already says: an empty `defaultMetaDescription`/
|
|
16
|
+
* `twitterHandle` is left out entirely rather than passed as `''`, so a page
|
|
17
|
+
* that supplies its own description or the Twitter Card's absence is never
|
|
18
|
+
* overridden by a setting nobody actually set.
|
|
12
19
|
*/
|
|
13
|
-
export function seoSiteFor(site) {
|
|
20
|
+
export function seoSiteFor(site, seo) {
|
|
14
21
|
return {
|
|
15
22
|
baseUrl: site.url,
|
|
16
23
|
name: site.name,
|
|
17
24
|
defaultLocale: site.defaultLocale,
|
|
18
25
|
locales: site.locales,
|
|
19
26
|
unprefixedDefaultLocale: true,
|
|
27
|
+
...(seo?.defaultMetaDescription ? { description: seo.defaultMetaDescription } : {}),
|
|
28
|
+
...(seo?.twitterHandle ? { twitterSite: seo.twitterHandle } : {}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** `seo.defaultSocialImageUrl`, resolved to an absolute URL a social crawler can fetch without a session. */
|
|
32
|
+
function fallbackImageFor(site, seo) {
|
|
33
|
+
const url = seo?.defaultSocialImageUrl;
|
|
34
|
+
if (url === undefined || url === '')
|
|
35
|
+
return undefined;
|
|
36
|
+
return { url: url.startsWith('http') ? url : absoluteUrl(site, url) };
|
|
37
|
+
}
|
|
38
|
+
const EMPTY_SEO_DEFAULTS = {
|
|
39
|
+
titleTemplate: '',
|
|
40
|
+
collectionTitleTemplates: {},
|
|
41
|
+
defaultMetaDescription: '',
|
|
42
|
+
twitterHandle: '',
|
|
43
|
+
defaultSocialImageUrl: '',
|
|
44
|
+
sitemapCollectionSettings: {},
|
|
45
|
+
googleSiteVerification: '',
|
|
46
|
+
bingSiteVerification: '',
|
|
47
|
+
robotsCustomRules: '',
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* `<meta>` tags proving domain ownership to Google Search Console and Bing
|
|
51
|
+
* Webmaster Tools (fiche 50 task 2) — the meta-tag verification method both
|
|
52
|
+
* offer as an alternative to DNS/file verification, deliberately the only
|
|
53
|
+
* one this codebase implements: no OAuth flow, no Search Console/Webmaster
|
|
54
|
+
* API call, no second secret to hold (R1/R7). An admin pastes the token the
|
|
55
|
+
* provider's own verification page already shows them; this only renders
|
|
56
|
+
* it, HTML-escaped like every other attribute value `@cogenta/seo` emits.
|
|
57
|
+
*/
|
|
58
|
+
export function siteVerificationMetaTags(seo) {
|
|
59
|
+
const tags = [];
|
|
60
|
+
if (seo?.googleSiteVerification) {
|
|
61
|
+
tags.push(`<meta name="google-site-verification" content="${escapeHtmlAttribute(seo.googleSiteVerification)}" />`);
|
|
62
|
+
}
|
|
63
|
+
if (seo?.bingSiteVerification) {
|
|
64
|
+
tags.push(`<meta name="msvalidate.01" content="${escapeHtmlAttribute(seo.bingSiteVerification)}" />`);
|
|
65
|
+
}
|
|
66
|
+
return tags.join('\n');
|
|
67
|
+
}
|
|
68
|
+
function stringSetting(value, fallback) {
|
|
69
|
+
return typeof value === 'string' ? value : fallback;
|
|
70
|
+
}
|
|
71
|
+
function recordSetting(value, fallback) {
|
|
72
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
73
|
+
? value
|
|
74
|
+
: fallback;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* `seo.sitemapCollectionSettings` is stored with `''` sentinels for "no
|
|
78
|
+
* hint" (so an empty text/select input round-trips instead of becoming
|
|
79
|
+
* `NaN`/`undefined`) — `@cogenta/seo`'s own `SitemapCollectionOverride`
|
|
80
|
+
* has no such sentinel, so this is the one place the two shapes meet.
|
|
81
|
+
*/
|
|
82
|
+
function toSitemapOverrides(raw) {
|
|
83
|
+
const overrides = {};
|
|
84
|
+
for (const [name, entry] of Object.entries(raw)) {
|
|
85
|
+
overrides[name] = {
|
|
86
|
+
included: entry.included,
|
|
87
|
+
...(entry.changefreq === '' ? {} : { changefreq: entry.changefreq }),
|
|
88
|
+
...(entry.priority === '' ? {} : { priority: entry.priority }),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return overrides;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The live `seo.*` site settings (fiche 21 task 3), read fresh — never
|
|
95
|
+
* cached at server startup, the same contract `ThemeRenderOptions.homePath`
|
|
96
|
+
* already keeps for `reading.homePath`. A key nobody has ever written comes
|
|
97
|
+
* back as its registry default (an empty string, or `{}`), the same as the
|
|
98
|
+
* settings store itself does for any other group.
|
|
99
|
+
*/
|
|
100
|
+
export async function readSeoRenderDefaults(store) {
|
|
101
|
+
const [titleTemplate, collectionTitleTemplates, defaultMetaDescription, sitemapCollectionSettings, twitterHandle, defaultSocialImageUrl, googleSiteVerification, bingSiteVerification, robotsCustomRules, searchVerificationEnabled, robotsCustomRulesEnabled,] = await Promise.all([
|
|
102
|
+
store.get('seo.titleTemplate', SITE_SETTINGS_SITE_SCOPE),
|
|
103
|
+
store.get('seo.collectionTitleTemplates', SITE_SETTINGS_SITE_SCOPE),
|
|
104
|
+
store.get('seo.defaultMetaDescription', SITE_SETTINGS_SITE_SCOPE),
|
|
105
|
+
store.get('seo.sitemapCollectionSettings', SITE_SETTINGS_SITE_SCOPE),
|
|
106
|
+
store.get('seo.twitterHandle', SITE_SETTINGS_SITE_SCOPE),
|
|
107
|
+
store.get('seo.defaultSocialImageUrl', SITE_SETTINGS_SITE_SCOPE),
|
|
108
|
+
store.get('seo.googleSiteVerification', SITE_SETTINGS_SITE_SCOPE),
|
|
109
|
+
store.get('seo.bingSiteVerification', SITE_SETTINGS_SITE_SCOPE),
|
|
110
|
+
store.get('seo.robotsCustomRules', SITE_SETTINGS_SITE_SCOPE),
|
|
111
|
+
// Fiche 70 task 3's own two gates. Read here, applied below, so every
|
|
112
|
+
// consumer of `SeoRenderDefaults` — `theme-render.ts`, `serve.ts`'s
|
|
113
|
+
// `/robots.txt` route, `SeoDiagnostics` — sees the gated value without
|
|
114
|
+
// having to know the gate exists (single source of truth, no duplicate
|
|
115
|
+
// check anywhere downstream).
|
|
116
|
+
store.get('seo.searchVerificationEnabled', SITE_SETTINGS_SITE_SCOPE),
|
|
117
|
+
store.get('seo.robotsCustomRulesEnabled', SITE_SETTINGS_SITE_SCOPE),
|
|
118
|
+
]);
|
|
119
|
+
// Both gates default to `true` (the registry's own default) — an install
|
|
120
|
+
// that has never touched this setting keeps behaving exactly as it did
|
|
121
|
+
// before this fiche.
|
|
122
|
+
const verificationEnabled = searchVerificationEnabled?.value !== false;
|
|
123
|
+
const customRulesEnabled = robotsCustomRulesEnabled?.value !== false;
|
|
124
|
+
return {
|
|
125
|
+
titleTemplate: stringSetting(titleTemplate?.value, EMPTY_SEO_DEFAULTS.titleTemplate),
|
|
126
|
+
collectionTitleTemplates: recordSetting(collectionTitleTemplates?.value, EMPTY_SEO_DEFAULTS.collectionTitleTemplates),
|
|
127
|
+
defaultMetaDescription: stringSetting(defaultMetaDescription?.value, EMPTY_SEO_DEFAULTS.defaultMetaDescription),
|
|
128
|
+
twitterHandle: stringSetting(twitterHandle?.value, EMPTY_SEO_DEFAULTS.twitterHandle),
|
|
129
|
+
defaultSocialImageUrl: stringSetting(defaultSocialImageUrl?.value, EMPTY_SEO_DEFAULTS.defaultSocialImageUrl),
|
|
130
|
+
sitemapCollectionSettings: toSitemapOverrides(recordSetting(sitemapCollectionSettings?.value, {})),
|
|
131
|
+
googleSiteVerification: verificationEnabled
|
|
132
|
+
? stringSetting(googleSiteVerification?.value, EMPTY_SEO_DEFAULTS.googleSiteVerification)
|
|
133
|
+
: '',
|
|
134
|
+
bingSiteVerification: verificationEnabled
|
|
135
|
+
? stringSetting(bingSiteVerification?.value, EMPTY_SEO_DEFAULTS.bingSiteVerification)
|
|
136
|
+
: '',
|
|
137
|
+
robotsCustomRules: customRulesEnabled
|
|
138
|
+
? stringSetting(robotsCustomRules?.value, EMPTY_SEO_DEFAULTS.robotsCustomRules)
|
|
139
|
+
: '',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const EMPTY_SEO_OPERATIONAL_SETTINGS = {
|
|
143
|
+
indexNowEnabled: false,
|
|
144
|
+
indexNowKey: '',
|
|
145
|
+
llmsTxtEnabled: false,
|
|
146
|
+
};
|
|
147
|
+
function booleanSetting(value, fallback) {
|
|
148
|
+
return typeof value === 'boolean' ? value : fallback;
|
|
149
|
+
}
|
|
150
|
+
/** Read fresh, same "no restart" contract as `readSeoRenderDefaults`. */
|
|
151
|
+
export async function readSeoOperationalSettings(store) {
|
|
152
|
+
const [indexNowEnabled, indexNowKey, llmsTxtEnabled] = await Promise.all([
|
|
153
|
+
store.get('seo.indexNowEnabled', SITE_SETTINGS_SITE_SCOPE),
|
|
154
|
+
store.get('seo.indexNowKey', SITE_SETTINGS_SITE_SCOPE),
|
|
155
|
+
store.get('seo.llmsTxtEnabled', SITE_SETTINGS_SITE_SCOPE),
|
|
156
|
+
]);
|
|
157
|
+
return {
|
|
158
|
+
indexNowEnabled: booleanSetting(indexNowEnabled?.value, EMPTY_SEO_OPERATIONAL_SETTINGS.indexNowEnabled),
|
|
159
|
+
indexNowKey: stringSetting(indexNowKey?.value, EMPTY_SEO_OPERATIONAL_SETTINGS.indexNowKey),
|
|
160
|
+
llmsTxtEnabled: booleanSetting(llmsTxtEnabled?.value, EMPTY_SEO_OPERATIONAL_SETTINGS.llmsTxtEnabled),
|
|
20
161
|
};
|
|
21
162
|
}
|
|
22
163
|
/** How many entries one collection contributes to a sitemap before it is cut off. */
|
|
@@ -114,11 +255,18 @@ export async function alternatesForEntry(site, collection, entry, gateway, conte
|
|
|
114
255
|
* this file.
|
|
115
256
|
*/
|
|
116
257
|
export function renderSeoHead(site, resource, options = {}) {
|
|
258
|
+
const fallbackImage = fallbackImageFor(site, options.seo);
|
|
259
|
+
const collectionTitleTemplates = options.seo?.collectionTitleTemplates;
|
|
117
260
|
const tags = buildMetaTags(site, resource, {
|
|
118
261
|
...(options.alternates === undefined ? {} : { alternates: options.alternates }),
|
|
119
262
|
...(options.media === undefined ? {} : { resolvers: { media: options.media } }),
|
|
120
263
|
...(options.noindex === undefined ? {} : { noindex: options.noindex }),
|
|
121
264
|
...(options.title === undefined ? {} : { title: options.title }),
|
|
265
|
+
...(options.seo?.titleTemplate ? { titleTemplate: options.seo.titleTemplate } : {}),
|
|
266
|
+
...(collectionTitleTemplates !== undefined && Object.keys(collectionTitleTemplates).length > 0
|
|
267
|
+
? { collectionTitleTemplates }
|
|
268
|
+
: {}),
|
|
269
|
+
...(fallbackImage === undefined ? {} : { fallbackImage }),
|
|
122
270
|
});
|
|
123
271
|
const parts = [renderMetaTags(tags)];
|
|
124
272
|
const graph = buildJsonLd(site, resource, {
|
|
@@ -138,8 +286,23 @@ export function renderSeoHead(site, resource, options = {}) {
|
|
|
138
286
|
* `/sitemap-N.xml` from the same array, so a site that grows past 50 000 URLs
|
|
139
287
|
* starts serving an index without anybody changing a route.
|
|
140
288
|
*/
|
|
141
|
-
export function buildSitemapFiles(site, resources
|
|
142
|
-
|
|
289
|
+
export function buildSitemapFiles(site, resources, collectionOverrides,
|
|
290
|
+
/**
|
|
291
|
+
* URLs a crawler should know about that are not entries — today, the
|
|
292
|
+
* taxonomy term archives (audit 2026-09-01, 04-taxonomies-menus.md T01).
|
|
293
|
+
*
|
|
294
|
+
* A separate parameter rather than a synthetic `SeoResource`, because a
|
|
295
|
+
* term genuinely is not one: it has no `status`, no `publishedAt` and no
|
|
296
|
+
* translation family, and faking those three to get it through
|
|
297
|
+
* `indexableResources` would put a lie in the middle of the SEO pipeline
|
|
298
|
+
* so that a URL could come out the other end. These arrive already
|
|
299
|
+
* decided-upon and are appended verbatim.
|
|
300
|
+
*/
|
|
301
|
+
extraUrls) {
|
|
302
|
+
return buildSitemap(site, [
|
|
303
|
+
...sitemapUrlsFor(site, resources, collectionOverrides === undefined ? {} : { collectionOverrides }),
|
|
304
|
+
...(extraUrls ?? []),
|
|
305
|
+
]);
|
|
143
306
|
}
|
|
144
307
|
export function renderRobots(site, options = {}) {
|
|
145
308
|
return renderRobotsTxt({
|
|
@@ -150,6 +313,7 @@ export function renderRobots(site, options = {}) {
|
|
|
150
313
|
// spend requests discovering that.
|
|
151
314
|
groups: [{ userAgent: '*', allow: ['/'], disallow: ['/admin', '/api/'] }],
|
|
152
315
|
...(options.allowIndexing === undefined ? {} : { allowIndexing: options.allowIndexing }),
|
|
316
|
+
...(options.customRules === undefined ? {} : { customRules: options.customRules }),
|
|
153
317
|
});
|
|
154
318
|
}
|
|
155
319
|
//# sourceMappingURL=seo.js.map
|
package/dist/commands/seo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo.js","sourceRoot":"","sources":["../../src/commands/seo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,SAAS,EAAuB,MAAM,cAAc,CAAA;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"seo.js","sourceRoot":"","sources":["../../src/commands/seo.ts"],"names":[],"mappings":"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"}
|