@hale-bopp/valentino-engine 1.0.0 → 2.1.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/bin/commands/init.d.ts +3 -0
  3. package/dist/bin/commands/init.d.ts.map +1 -0
  4. package/dist/bin/commands/init.js +428 -0
  5. package/dist/bin/commands/init.js.map +1 -0
  6. package/dist/bin/commands/llms.d.ts +2 -0
  7. package/dist/bin/commands/llms.d.ts.map +1 -0
  8. package/dist/bin/commands/llms.js +84 -0
  9. package/dist/bin/commands/llms.js.map +1 -0
  10. package/dist/bin/commands/validate.d.ts.map +1 -1
  11. package/dist/bin/commands/validate.js +152 -11
  12. package/dist/bin/commands/validate.js.map +1 -1
  13. package/dist/bin/valentino.js +10 -0
  14. package/dist/bin/valentino.js.map +1 -1
  15. package/dist/core/animation.d.ts +18 -0
  16. package/dist/core/animation.d.ts.map +1 -0
  17. package/dist/core/animation.js +91 -0
  18. package/dist/core/animation.js.map +1 -0
  19. package/dist/core/encoding.d.ts +32 -0
  20. package/dist/core/encoding.d.ts.map +1 -0
  21. package/dist/core/encoding.js +112 -0
  22. package/dist/core/encoding.js.map +1 -0
  23. package/dist/core/extension-registry.d.ts +64 -0
  24. package/dist/core/extension-registry.d.ts.map +1 -0
  25. package/dist/core/extension-registry.js +62 -0
  26. package/dist/core/extension-registry.js.map +1 -0
  27. package/dist/core/guardrails-cms.d.ts +70 -0
  28. package/dist/core/guardrails-cms.d.ts.map +1 -0
  29. package/dist/core/guardrails-cms.js +299 -0
  30. package/dist/core/guardrails-cms.js.map +1 -0
  31. package/dist/core/hero-contract.js +2 -2
  32. package/dist/core/hero-contract.js.map +1 -1
  33. package/dist/core/hero-contract.test.js +9 -2
  34. package/dist/core/hero-contract.test.js.map +1 -1
  35. package/dist/core/llms-generator.d.ts +36 -0
  36. package/dist/core/llms-generator.d.ts.map +1 -0
  37. package/dist/core/llms-generator.js +95 -0
  38. package/dist/core/llms-generator.js.map +1 -0
  39. package/dist/core/media.d.ts +24 -0
  40. package/dist/core/media.d.ts.map +1 -0
  41. package/dist/core/media.js +16 -0
  42. package/dist/core/media.js.map +1 -0
  43. package/dist/core/page-status.d.ts +18 -0
  44. package/dist/core/page-status.d.ts.map +1 -0
  45. package/dist/core/page-status.js +33 -0
  46. package/dist/core/page-status.js.map +1 -0
  47. package/dist/core/redirects.d.ts +19 -0
  48. package/dist/core/redirects.d.ts.map +1 -0
  49. package/dist/core/redirects.js +17 -0
  50. package/dist/core/redirects.js.map +1 -0
  51. package/dist/core/rhythm.d.ts.map +1 -1
  52. package/dist/core/rhythm.js +20 -1
  53. package/dist/core/rhythm.js.map +1 -1
  54. package/dist/core/rhythm.test.js +6 -6
  55. package/dist/core/rhythm.test.js.map +1 -1
  56. package/dist/core/section-integrity.d.ts.map +1 -1
  57. package/dist/core/section-integrity.js +10 -0
  58. package/dist/core/section-integrity.js.map +1 -1
  59. package/dist/core/section-integrity.test.js +12 -0
  60. package/dist/core/section-integrity.test.js.map +1 -1
  61. package/dist/core/seo.d.ts +26 -0
  62. package/dist/core/seo.d.ts.map +1 -0
  63. package/dist/core/seo.js +49 -0
  64. package/dist/core/seo.js.map +1 -0
  65. package/dist/core/types.d.ts +30 -1
  66. package/dist/core/types.d.ts.map +1 -1
  67. package/dist/index.d.ts +20 -1
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +18 -0
  70. package/dist/index.js.map +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,62 @@
1
+ /** Create a fresh, empty registry. */
2
+ export function createExtensionRegistry() {
3
+ return {
4
+ sectionRenderers: new Map(),
5
+ guardrails: new Map(),
6
+ customStatuses: new Set(),
7
+ contentResolver: null,
8
+ mediaResolver: null,
9
+ editorPanels: new Map(),
10
+ };
11
+ }
12
+ // ---------------------------------------------------------------------------
13
+ // Registration API
14
+ // ---------------------------------------------------------------------------
15
+ /** Register a custom section renderer for a given section type. */
16
+ export function registerSectionRenderer(registry, sectionType, renderer) {
17
+ registry.sectionRenderers.set(sectionType, renderer);
18
+ }
19
+ /** Register a custom guardrail rule. */
20
+ export function registerGuardrail(registry, name, guardrail) {
21
+ registry.guardrails.set(name, guardrail);
22
+ }
23
+ /** Register a custom page status value beyond draft/published/scheduled. */
24
+ export function registerCustomStatus(registry, status) {
25
+ registry.customStatuses.add(status);
26
+ }
27
+ /** Set the content resolver (replaces any existing one). */
28
+ export function setContentResolver(registry, resolver) {
29
+ registry.contentResolver = resolver;
30
+ }
31
+ /** Set the media resolver (replaces any existing one). */
32
+ export function setMediaResolver(registry, resolver) {
33
+ registry.mediaResolver = resolver;
34
+ }
35
+ /** Register an editor panel for specific section types. */
36
+ export function registerEditorPanel(registry, panel) {
37
+ registry.editorPanels.set(panel.id, panel);
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Query API
41
+ // ---------------------------------------------------------------------------
42
+ /** Check if a section type has a custom renderer. */
43
+ export function hasCustomRenderer(registry, sectionType) {
44
+ return registry.sectionRenderers.has(sectionType);
45
+ }
46
+ /** Get a custom renderer for a section type (or undefined). */
47
+ export function getCustomRenderer(registry, sectionType) {
48
+ return registry.sectionRenderers.get(sectionType);
49
+ }
50
+ /** Run all custom guardrails and return combined warnings. */
51
+ export function runCustomGuardrails(registry, context) {
52
+ const warnings = [];
53
+ for (const guardrail of registry.guardrails.values()) {
54
+ warnings.push(...guardrail(context));
55
+ }
56
+ return warnings;
57
+ }
58
+ /** Get editor panels applicable to a given section type. */
59
+ export function getEditorPanels(registry, sectionType) {
60
+ return [...registry.editorPanels.values()].filter((p) => p.sectionTypes.includes(sectionType));
61
+ }
62
+ //# sourceMappingURL=extension-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-registry.js","sourceRoot":"","sources":["../../src/core/extension-registry.ts"],"names":[],"mappings":"AAsDA,sCAAsC;AACtC,MAAM,UAAU,uBAAuB;IACnC,OAAO;QACH,gBAAgB,EAAE,IAAI,GAAG,EAAE;QAC3B,UAAU,EAAE,IAAI,GAAG,EAAE;QACrB,cAAc,EAAE,IAAI,GAAG,EAAE;QACzB,eAAe,EAAE,IAAI;QACrB,aAAa,EAAE,IAAI;QACnB,YAAY,EAAE,IAAI,GAAG,EAAE;KAC1B,CAAC;AACN,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,mEAAmE;AACnE,MAAM,UAAU,uBAAuB,CACnC,QAA2B,EAC3B,WAAmB,EACnB,QAA+B;IAE/B,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,iBAAiB,CAC7B,QAA2B,EAC3B,IAAY,EACZ,SAA0B;IAE1B,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC7C,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAChC,QAA2B,EAC3B,MAAc;IAEd,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,kBAAkB,CAC9B,QAA2B,EAC3B,QAAyB;IAEzB,QAAQ,CAAC,eAAe,GAAG,QAAQ,CAAC;AACxC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,gBAAgB,CAC5B,QAA2B,EAC3B,QAAuB;IAEvB,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC;AACtC,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,mBAAmB,CAC/B,QAA2B,EAC3B,KAAqB;IAErB,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,qDAAqD;AACrD,MAAM,UAAU,iBAAiB,CAAC,QAA2B,EAAE,WAAmB;IAC9E,OAAO,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACtD,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,QAA2B,EAAE,WAAmB;IAC9E,OAAO,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACtD,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,mBAAmB,CAAC,QAA2B,EAAE,OAAyB;IACtF,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,KAAK,MAAM,SAAS,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACnD,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,eAAe,CAAC,QAA2B,EAAE,WAAmB;IAC5E,OAAO,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;AACnG,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * CMS Guardrails — Pure validation rules for CMS capabilities.
3
+ * Migrated from easyway-portal scripts/guardrails-cms.mjs (PBI #606).
4
+ * Extended with media, language, breadcrumb, and route checks (PBI #605).
5
+ * No I/O (no fs, no fetch) — works on pre-loaded data only.
6
+ */
7
+ import type { PagesManifestV1 } from './types.js';
8
+ import type { RedirectRule } from './redirects.js';
9
+ import type { SeoSpec } from './seo.js';
10
+ import type { MediaManifest } from './media.js';
11
+ export type CmsWarning = {
12
+ type: string;
13
+ severity: 'warning' | 'error';
14
+ file: string;
15
+ message: string;
16
+ };
17
+ /** Warn about draft pages without a scheduled publishAt date. */
18
+ export declare function checkDraftOrphans(manifest: PagesManifestV1): CmsWarning[];
19
+ /** Validate publishAt dates: format and coherence with status. */
20
+ export declare function checkPublishAtCoherence(manifest: PagesManifestV1): CmsWarning[];
21
+ /** Warn if maintenanceMode is active. */
22
+ export declare function checkMaintenanceModeLeak(manifest: PagesManifestV1): CmsWarning[];
23
+ /** Error if no "not-found" page exists in manifest. */
24
+ export declare function check404Exists(manifest: PagesManifestV1): CmsWarning[];
25
+ /** Validate redirect rules: target exists, no chains. */
26
+ export declare function checkRedirects(manifest: PagesManifestV1, rules: RedirectRule[]): CmsWarning[];
27
+ /**
28
+ * Check that navigable published pages have SEO metadata.
29
+ * Requires pre-loaded specs (the consumer handles I/O).
30
+ */
31
+ export declare function checkSeoCompleteness(manifest: PagesManifestV1, specsById: Map<string, {
32
+ seo?: SeoSpec;
33
+ }>): CmsWarning[];
34
+ /**
35
+ * Check that ogImage in page specs references an existing media key.
36
+ * Requires pre-loaded specs and media manifest.
37
+ */
38
+ export declare function checkOgImageExists(specsById: Map<string, {
39
+ seo?: SeoSpec;
40
+ }>, mediaManifest: MediaManifest): CmsWarning[];
41
+ /**
42
+ * Find media assets not referenced by any page spec.
43
+ * Scans all spec sections for media keys in known fields.
44
+ */
45
+ export declare function checkMediaOrphans(specsById: Map<string, {
46
+ seo?: SeoSpec;
47
+ sections?: Array<Record<string, any>>;
48
+ }>, mediaManifest: MediaManifest): CmsWarning[];
49
+ /** Warn about media assets without alt text (accessibility). */
50
+ export declare function checkMediaMissingAlt(mediaManifest: MediaManifest): CmsWarning[];
51
+ /** Warn about pages with deep routes (3+ segments) that may need intermediate breadcrumb pages. */
52
+ export declare function checkBreadcrumbDepth(manifest: PagesManifestV1): CmsWarning[];
53
+ /**
54
+ * Check language coverage: keys in one language map missing from another.
55
+ * Accepts flattened key sets (consumer handles the JSON loading/flattening).
56
+ */
57
+ export declare function checkLanguageCoverage(contentByLang: Map<string, Set<string>>): CmsWarning[];
58
+ /** Error if manifest has duplicate routes. */
59
+ export declare function checkDuplicateRoutes(manifest: PagesManifestV1): CmsWarning[];
60
+ /** Run all pure CMS guardrails. Returns combined warnings. */
61
+ export declare function collectPureCmsWarnings(manifest: PagesManifestV1, options?: {
62
+ redirectRules?: RedirectRule[];
63
+ specsById?: Map<string, {
64
+ seo?: SeoSpec;
65
+ sections?: Array<Record<string, any>>;
66
+ }>;
67
+ mediaManifest?: MediaManifest;
68
+ contentByLang?: Map<string, Set<string>>;
69
+ }): CmsWarning[];
70
+ //# sourceMappingURL=guardrails-cms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails-cms.d.ts","sourceRoot":"","sources":["../../src/core/guardrails-cms.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAc,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,MAAM,UAAU,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,EAAE,CAazE;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,EAAE,CAyB/E;AAED,yCAAyC;AACzC,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,EAAE,CAUhF;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,EAAE,CAWtE;AAED,yDAAyD;AACzD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,CAyB7F;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAChC,QAAQ,EAAE,eAAe,EACzB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAC1C,UAAU,EAAE,CAsBd;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAC9B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACzC,aAAa,EAAE,aAAa,GAC7B,UAAU,EAAE,CAmBd;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;CAAE,CAAC,EAChF,aAAa,EAAE,aAAa,GAC7B,UAAU,EAAE,CAiCd;AAED,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,aAAa,GAAG,UAAU,EAAE,CAa/E;AAED,mGAAmG;AACnG,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,EAAE,CAmB5E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACjC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GACxC,UAAU,EAAE,CAkCd;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,EAAE,CAkB5E;AAED,8DAA8D;AAC9D,wBAAgB,sBAAsB,CAClC,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE;IACN,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,GAAG,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;KAAE,CAAC,CAAC;IAClF,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAC5C,GACF,UAAU,EAAE,CAed"}
@@ -0,0 +1,299 @@
1
+ /** Warn about draft pages without a scheduled publishAt date. */
2
+ export function checkDraftOrphans(manifest) {
3
+ const warnings = [];
4
+ for (const page of manifest.pages) {
5
+ if (page.status !== 'draft')
6
+ continue;
7
+ if (page.publishAt)
8
+ continue;
9
+ warnings.push({
10
+ type: 'cms-draft-orphan',
11
+ severity: 'warning',
12
+ file: 'pages.manifest.json',
13
+ message: `Page "${page.id}" is draft without publishAt — consider setting a publish date or removing it`,
14
+ });
15
+ }
16
+ return warnings;
17
+ }
18
+ /** Validate publishAt dates: format and coherence with status. */
19
+ export function checkPublishAtCoherence(manifest) {
20
+ const warnings = [];
21
+ const now = new Date();
22
+ for (const page of manifest.pages) {
23
+ if (!page.publishAt)
24
+ continue;
25
+ const d = new Date(page.publishAt);
26
+ if (isNaN(d.getTime())) {
27
+ warnings.push({
28
+ type: 'cms-publishat-invalid',
29
+ severity: 'error',
30
+ file: 'pages.manifest.json',
31
+ message: `Page "${page.id}" has invalid publishAt: "${page.publishAt}"`,
32
+ });
33
+ continue;
34
+ }
35
+ if (d < now && page.status !== 'published' && page.status !== undefined) {
36
+ warnings.push({
37
+ type: 'cms-publishat-past',
38
+ severity: 'warning',
39
+ file: 'pages.manifest.json',
40
+ message: `Page "${page.id}" has publishAt in the past (${page.publishAt}) but status is "${page.status}" — should be "published"?`,
41
+ });
42
+ }
43
+ }
44
+ return warnings;
45
+ }
46
+ /** Warn if maintenanceMode is active. */
47
+ export function checkMaintenanceModeLeak(manifest) {
48
+ if (manifest.maintenanceMode === true) {
49
+ return [{
50
+ type: 'cms-maintenance-mode-active',
51
+ severity: 'warning',
52
+ file: 'pages.manifest.json',
53
+ message: 'maintenanceMode is TRUE — all pages will show maintenance screen in production. Intentional?',
54
+ }];
55
+ }
56
+ return [];
57
+ }
58
+ /** Error if no "not-found" page exists in manifest. */
59
+ export function check404Exists(manifest) {
60
+ const has404 = manifest.pages.some((p) => p.id === 'not-found');
61
+ if (!has404) {
62
+ return [{
63
+ type: 'cms-no-404-page',
64
+ severity: 'error',
65
+ file: 'pages.manifest.json',
66
+ message: 'No "not-found" page in manifest — users hitting invalid URLs will see plain text',
67
+ }];
68
+ }
69
+ return [];
70
+ }
71
+ /** Validate redirect rules: target exists, no chains. */
72
+ export function checkRedirects(manifest, rules) {
73
+ const warnings = [];
74
+ if (!rules.length)
75
+ return warnings;
76
+ const validRoutes = new Set(manifest.pages.map((p) => p.route));
77
+ for (const rule of rules) {
78
+ if (!validRoutes.has(rule.to)) {
79
+ warnings.push({
80
+ type: 'cms-redirect-target-missing',
81
+ severity: 'error',
82
+ file: 'redirects.json',
83
+ message: `Redirect "${rule.from}" → "${rule.to}" targets a non-existent route`,
84
+ });
85
+ }
86
+ const chainTarget = rules.find((r) => r.from === rule.to);
87
+ if (chainTarget) {
88
+ warnings.push({
89
+ type: 'cms-redirect-chain',
90
+ severity: 'warning',
91
+ file: 'redirects.json',
92
+ message: `Redirect chain: "${rule.from}" → "${rule.to}" → "${chainTarget.to}"`,
93
+ });
94
+ }
95
+ }
96
+ return warnings;
97
+ }
98
+ /**
99
+ * Check that navigable published pages have SEO metadata.
100
+ * Requires pre-loaded specs (the consumer handles I/O).
101
+ */
102
+ export function checkSeoCompleteness(manifest, specsById) {
103
+ const warnings = [];
104
+ const skipIds = new Set(['not-found', 'maintenance', 'editor', 'templates']);
105
+ for (const page of manifest.pages) {
106
+ if (!page.nav)
107
+ continue;
108
+ if (page.status === 'draft')
109
+ continue;
110
+ if (page.id.startsWith('pag-work-'))
111
+ continue;
112
+ if (skipIds.has(page.id))
113
+ continue;
114
+ const spec = specsById.get(page.id);
115
+ if (!spec)
116
+ continue;
117
+ if (!spec.seo || (!spec.seo.metaTitle && !spec.seo.metaDescription)) {
118
+ warnings.push({
119
+ type: 'cms-seo-missing',
120
+ severity: 'warning',
121
+ file: page.spec,
122
+ message: `Page "${page.id}" has navigation but no SEO block — add seo.metaTitle and seo.metaDescription`,
123
+ });
124
+ }
125
+ }
126
+ return warnings;
127
+ }
128
+ /**
129
+ * Check that ogImage in page specs references an existing media key.
130
+ * Requires pre-loaded specs and media manifest.
131
+ */
132
+ export function checkOgImageExists(specsById, mediaManifest) {
133
+ const warnings = [];
134
+ const mediaKeys = new Set(mediaManifest.assets.map((a) => a.key));
135
+ for (const [pageId, spec] of specsById) {
136
+ const ogImage = spec.seo?.ogImage;
137
+ if (!ogImage)
138
+ continue;
139
+ // Skip if it looks like a path/URL (only validate media key references)
140
+ if (ogImage.includes('/') || ogImage.includes('.'))
141
+ continue;
142
+ if (!mediaKeys.has(ogImage)) {
143
+ warnings.push({
144
+ type: 'cms-og-image-missing',
145
+ severity: 'warning',
146
+ file: `/pages/${pageId}.json`,
147
+ message: `Page "${pageId}" has seo.ogImage="${ogImage}" but no matching media asset found`,
148
+ });
149
+ }
150
+ }
151
+ return warnings;
152
+ }
153
+ /**
154
+ * Find media assets not referenced by any page spec.
155
+ * Scans all spec sections for media keys in known fields.
156
+ */
157
+ export function checkMediaOrphans(specsById, mediaManifest) {
158
+ const warnings = [];
159
+ if (!mediaManifest.assets.length)
160
+ return warnings;
161
+ // Collect all referenced media keys from specs
162
+ const referencedKeys = new Set();
163
+ for (const [, spec] of specsById) {
164
+ // OG image
165
+ if (spec.seo?.ogImage)
166
+ referencedKeys.add(spec.seo.ogImage);
167
+ // Scan sections for common media fields
168
+ for (const section of spec.sections ?? []) {
169
+ const json = JSON.stringify(section);
170
+ for (const asset of mediaManifest.assets) {
171
+ if (json.includes(asset.key) || json.includes(asset.file)) {
172
+ referencedKeys.add(asset.key);
173
+ }
174
+ }
175
+ }
176
+ }
177
+ for (const asset of mediaManifest.assets) {
178
+ if (!referencedKeys.has(asset.key)) {
179
+ warnings.push({
180
+ type: 'cms-media-orphan',
181
+ severity: 'warning',
182
+ file: 'media.manifest.json',
183
+ message: `Media asset "${asset.key}" (${asset.file}) is not referenced by any page spec`,
184
+ });
185
+ }
186
+ }
187
+ return warnings;
188
+ }
189
+ /** Warn about media assets without alt text (accessibility). */
190
+ export function checkMediaMissingAlt(mediaManifest) {
191
+ const warnings = [];
192
+ for (const asset of mediaManifest.assets) {
193
+ if (!asset.alt || !asset.alt.trim()) {
194
+ warnings.push({
195
+ type: 'cms-media-missing-alt',
196
+ severity: 'warning',
197
+ file: 'media.manifest.json',
198
+ message: `Media asset "${asset.key}" has no alt text — accessibility issue`,
199
+ });
200
+ }
201
+ }
202
+ return warnings;
203
+ }
204
+ /** Warn about pages with deep routes (3+ segments) that may need intermediate breadcrumb pages. */
205
+ export function checkBreadcrumbDepth(manifest) {
206
+ const warnings = [];
207
+ const routes = new Set(manifest.pages.map((p) => p.route));
208
+ for (const page of manifest.pages) {
209
+ const segments = page.route.split('/').filter(Boolean);
210
+ if (segments.length < 3)
211
+ continue;
212
+ // Check if parent route exists
213
+ const parentRoute = '/' + segments.slice(0, -1).join('/');
214
+ if (!routes.has(parentRoute)) {
215
+ warnings.push({
216
+ type: 'cms-breadcrumb-depth',
217
+ severity: 'warning',
218
+ file: 'pages.manifest.json',
219
+ message: `Page "${page.id}" has deep route "${page.route}" but parent "${parentRoute}" doesn't exist — breadcrumb will have gaps`,
220
+ });
221
+ }
222
+ }
223
+ return warnings;
224
+ }
225
+ /**
226
+ * Check language coverage: keys in one language map missing from another.
227
+ * Accepts flattened key sets (consumer handles the JSON loading/flattening).
228
+ */
229
+ export function checkLanguageCoverage(contentByLang) {
230
+ const warnings = [];
231
+ const langs = [...contentByLang.keys()];
232
+ for (let i = 0; i < langs.length; i++) {
233
+ for (let j = i + 1; j < langs.length; j++) {
234
+ const langA = langs[i];
235
+ const langB = langs[j];
236
+ const keysA = contentByLang.get(langA);
237
+ const keysB = contentByLang.get(langB);
238
+ for (const key of keysA) {
239
+ if (!keysB.has(key)) {
240
+ warnings.push({
241
+ type: 'cms-language-coverage',
242
+ severity: 'warning',
243
+ file: `content/${langA}.json`,
244
+ message: `Key "${key}" exists in ${langA.toUpperCase()} but not in ${langB.toUpperCase()}`,
245
+ });
246
+ }
247
+ }
248
+ for (const key of keysB) {
249
+ if (!keysA.has(key)) {
250
+ warnings.push({
251
+ type: 'cms-language-coverage',
252
+ severity: 'warning',
253
+ file: `content/${langB}.json`,
254
+ message: `Key "${key}" exists in ${langB.toUpperCase()} but not in ${langA.toUpperCase()}`,
255
+ });
256
+ }
257
+ }
258
+ }
259
+ }
260
+ return warnings;
261
+ }
262
+ /** Error if manifest has duplicate routes. */
263
+ export function checkDuplicateRoutes(manifest) {
264
+ const warnings = [];
265
+ const seen = new Map();
266
+ for (const page of manifest.pages) {
267
+ const existing = seen.get(page.route);
268
+ if (existing) {
269
+ warnings.push({
270
+ type: 'cms-duplicate-route',
271
+ severity: 'error',
272
+ file: 'pages.manifest.json',
273
+ message: `Route "${page.route}" is claimed by both "${existing}" and "${page.id}"`,
274
+ });
275
+ }
276
+ else {
277
+ seen.set(page.route, page.id);
278
+ }
279
+ }
280
+ return warnings;
281
+ }
282
+ /** Run all pure CMS guardrails. Returns combined warnings. */
283
+ export function collectPureCmsWarnings(manifest, options) {
284
+ return [
285
+ ...checkDraftOrphans(manifest),
286
+ ...checkPublishAtCoherence(manifest),
287
+ ...checkMaintenanceModeLeak(manifest),
288
+ ...check404Exists(manifest),
289
+ ...checkDuplicateRoutes(manifest),
290
+ ...checkBreadcrumbDepth(manifest),
291
+ ...checkRedirects(manifest, options?.redirectRules ?? []),
292
+ ...(options?.specsById ? checkSeoCompleteness(manifest, options.specsById) : []),
293
+ ...(options?.specsById && options?.mediaManifest ? checkOgImageExists(options.specsById, options.mediaManifest) : []),
294
+ ...(options?.specsById && options?.mediaManifest ? checkMediaOrphans(options.specsById, options.mediaManifest) : []),
295
+ ...(options?.mediaManifest ? checkMediaMissingAlt(options.mediaManifest) : []),
296
+ ...(options?.contentByLang ? checkLanguageCoverage(options.contentByLang) : []),
297
+ ];
298
+ }
299
+ //# sourceMappingURL=guardrails-cms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails-cms.js","sourceRoot":"","sources":["../../src/core/guardrails-cms.ts"],"names":[],"mappings":"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"}
@@ -18,10 +18,10 @@ export function probeHeroContract(hero) {
18
18
  ctaCount++;
19
19
  if (hero.ctaTertiary)
20
20
  ctaCount++;
21
- if (ctaCount > 3) {
21
+ if (ctaCount > 2) {
22
22
  warnings.push({
23
23
  rule: 'cta-discipline',
24
- message: `Hero has ${ctaCount} CTAs — maximum is 3.`,
24
+ message: `Hero has ${ctaCount} CTAs — maximum recommended is 2 (primary + secondary).`,
25
25
  });
26
26
  }
27
27
  // Rule 2: Single decorative source
@@ -1 +1 @@
1
- {"version":3,"file":"hero-contract.js","sourceRoot":"","sources":["../../src/core/hero-contract.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IAC/C,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAE3C,sCAAsC;IACtC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,IAAI,CAAC,GAAG;QAAE,QAAQ,EAAE,CAAC;IACzB,IAAI,IAAI,CAAC,YAAY;QAAE,QAAQ,EAAE,CAAC;IAClC,IAAI,IAAI,CAAC,WAAW;QAAE,QAAQ,EAAE,CAAC;IACjC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,YAAY,QAAQ,uBAAuB;SACvD,CAAC,CAAC;IACP,CAAC;IAED,mCAAmC;IACnC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC;IAC9C,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,KAAK,MAAM,CAAC;IACpG,IAAI,cAAc,IAAI,cAAc,EAAE,CAAC;QACnC,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,0BAA0B;YAChC,OAAO,EAAE,iFAAiF;SAC7F,CAAC,CAAC;IACP,CAAC;IAED,4CAA4C;IAC5C,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,IAAI,CAAC,QAAQ;QAAE,WAAW,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,UAAU;QAAE,WAAW,EAAE,CAAC;IACnC,IAAI,IAAI,CAAC,UAAU;QAAE,WAAW,EAAE,CAAC;IACnC,IAAI,IAAI,CAAC,UAAU;QAAE,WAAW,EAAE,CAAC;IACnC,IAAI,IAAI,CAAC,QAAQ;QAAE,WAAW,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,cAAc;QAAE,WAAW,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,mBAAmB;QAAE,WAAW,EAAE,CAAC;IAC5C,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,YAAY,WAAW,2DAA2D;SAC9F,CAAC,CAAC;IACP,CAAC;IAED,2BAA2B;IAC3B,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,oBAAoB;YAC1B,OAAO,EAAE,wBAAwB,IAAI,CAAC,UAAU,CAAC,MAAM,oCAAoC;SAC9F,CAAC,CAAC;IACP,CAAC;IAED,OAAO;QACH,KAAK,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC;QAC5B,QAAQ;KACX,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"hero-contract.js","sourceRoot":"","sources":["../../src/core/hero-contract.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IAC/C,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAE3C,sCAAsC;IACtC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,IAAI,CAAC,GAAG;QAAE,QAAQ,EAAE,CAAC;IACzB,IAAI,IAAI,CAAC,YAAY;QAAE,QAAQ,EAAE,CAAC;IAClC,IAAI,IAAI,CAAC,WAAW;QAAE,QAAQ,EAAE,CAAC;IACjC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,YAAY,QAAQ,yDAAyD;SACzF,CAAC,CAAC;IACP,CAAC;IAED,mCAAmC;IACnC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC;IAC9C,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,KAAK,MAAM,CAAC;IACpG,IAAI,cAAc,IAAI,cAAc,EAAE,CAAC;QACnC,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,0BAA0B;YAChC,OAAO,EAAE,iFAAiF;SAC7F,CAAC,CAAC;IACP,CAAC;IAED,4CAA4C;IAC5C,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,IAAI,CAAC,QAAQ;QAAE,WAAW,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,UAAU;QAAE,WAAW,EAAE,CAAC;IACnC,IAAI,IAAI,CAAC,UAAU;QAAE,WAAW,EAAE,CAAC;IACnC,IAAI,IAAI,CAAC,UAAU;QAAE,WAAW,EAAE,CAAC;IACnC,IAAI,IAAI,CAAC,QAAQ;QAAE,WAAW,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,cAAc;QAAE,WAAW,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,mBAAmB;QAAE,WAAW,EAAE,CAAC;IAC5C,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,YAAY,WAAW,2DAA2D;SAC9F,CAAC,CAAC;IACP,CAAC;IAED,2BAA2B;IAC3B,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,oBAAoB;YAC1B,OAAO,EAAE,wBAAwB,IAAI,CAAC,UAAU,CAAC,MAAM,oCAAoC;SAC9F,CAAC,CAAC;IACP,CAAC;IAED,OAAO;QACH,KAAK,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC;QAC5B,QAAQ;KACX,CAAC;AACN,CAAC"}
@@ -9,14 +9,21 @@ describe('probeHeroContract', () => {
9
9
  expect(result.valid).toBe(true);
10
10
  expect(result.warnings).toHaveLength(0);
11
11
  });
12
- it('passes with 3 CTAs', () => {
12
+ it('passes with 2 CTAs', () => {
13
13
  const result = probeHeroContract(makeHero({
14
14
  cta: { labelKey: 'c1', action: { type: 'link', href: '/' } },
15
15
  ctaSecondary: { labelKey: 'c2', action: { type: 'noop' } },
16
- ctaTertiary: { labelKey: 'c3', action: { type: 'noop' } },
17
16
  }));
18
17
  expect(result.valid).toBe(true);
19
18
  });
19
+ it('warns with 3 CTAs', () => {
20
+ const result = probeHeroContract(makeHero({
21
+ cta: { labelKey: 'c1', action: { type: 'link', href: '/' } },
22
+ ctaSecondary: { labelKey: 'c2', action: { type: 'noop' } },
23
+ ctaTertiary: { labelKey: 'c3', action: { type: 'noop' } },
24
+ }));
25
+ expect(result.warnings).toContainEqual(expect.objectContaining({ rule: 'cta-discipline' }));
26
+ });
20
27
  it('warns on single-decorative-source violation', () => {
21
28
  const result = probeHeroContract(makeHero({
22
29
  visualAssetPath: '/img/hero.jpg',
@@ -1 +1 @@
1
- {"version":3,"file":"hero-contract.test.js","sourceRoot":"","sources":["../../src/core/hero-contract.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hero-contract.test.js","sourceRoot":"","sources":["../../src/core/hero-contract.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * LLMs.txt Generator — Pure functions to produce llms.txt and llms-full.txt
3
+ * from a PagesManifest and optional content/spec data.
4
+ * No I/O — the consumer provides the loaded data.
5
+ *
6
+ * llms.txt = L0 Matrioska (max ~50 lines, site index for LLM agents)
7
+ * llms-full.txt = L1 Matrioska (detailed per-page breakdown)
8
+ *
9
+ * Follows the emerging llms.txt standard (like robots.txt for AI crawlers).
10
+ */
11
+ import type { PagesManifestV1, PageSpecV1 } from './types.js';
12
+ export type LlmsGeneratorOptions = {
13
+ /** Site name displayed in the header */
14
+ siteName: string;
15
+ /** One-line tagline / description */
16
+ tagline?: string;
17
+ /** Base URL (e.g. https://example.com) — used in llms-full.txt links */
18
+ baseUrl?: string;
19
+ /** Capabilities bullet points */
20
+ capabilities?: string[];
21
+ /** Content resolver: given a content key, returns the string value (or undefined) */
22
+ resolveContent?: (key: string) => string | undefined;
23
+ /** Pre-loaded page specs by page id (for llms-full.txt section details) */
24
+ specsById?: Map<string, PageSpecV1>;
25
+ };
26
+ /**
27
+ * Generate llms.txt — compact site index (L0 Matrioska).
28
+ * Max ~50 lines: header, pages list, capabilities.
29
+ */
30
+ export declare function generateLlmsTxt(manifest: PagesManifestV1, options: LlmsGeneratorOptions): string;
31
+ /**
32
+ * Generate llms-full.txt — detailed per-page breakdown (L1 Matrioska).
33
+ * Includes section types, SEO metadata, and page purpose.
34
+ */
35
+ export declare function generateLlmsFullTxt(manifest: PagesManifestV1, options: LlmsGeneratorOptions): string;
36
+ //# sourceMappingURL=llms-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms-generator.d.ts","sourceRoot":"","sources":["../../src/core/llms-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG9D,MAAM,MAAM,oBAAoB,GAAG;IAC/B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qFAAqF;IACrF,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACrD,2EAA2E;IAC3E,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,oBAAoB,GAC9B,MAAM,CAwCR;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAC/B,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,oBAAoB,GAC9B,MAAM,CAuDR"}