jamdesk 1.1.25 → 1.1.26

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.
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from 'vitest';
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { validateConfig } from '../../lib/docs-config.js';
@@ -67,4 +67,43 @@ describe('validate command integration', () => {
67
67
  });
68
68
  });
69
69
  });
70
+ describe('validate command — missing navigation pages', () => {
71
+ let originalCwd;
72
+ let exitSpy;
73
+ let logSpy;
74
+ let errSpy;
75
+ beforeEach(() => {
76
+ originalCwd = process.cwd();
77
+ exitSpy = vi.spyOn(process, 'exit').mockImplementation(((code) => {
78
+ throw new Error(`__exit__${code ?? 0}`);
79
+ }));
80
+ logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
81
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
82
+ });
83
+ afterEach(() => {
84
+ process.chdir(originalCwd);
85
+ exitSpy.mockRestore();
86
+ logSpy.mockRestore();
87
+ errSpy.mockRestore();
88
+ });
89
+ it('emits a missing_page warning for a page without an MDX file', async () => {
90
+ process.chdir(path.join(fixturesDir, 'missing-nav-pages'));
91
+ // Dynamic import so the process.exit/console spies in beforeEach are
92
+ // installed before validate.ts's module side effects run.
93
+ const { validate } = await import('../../commands/validate.js');
94
+ try {
95
+ await validate({ verbose: false, skipMdx: true });
96
+ }
97
+ catch (e) {
98
+ // validate() calls process.exit on completion — catch the thrown marker
99
+ expect(e.message).toMatch(/^__exit__/);
100
+ }
101
+ const output = [
102
+ ...logSpy.mock.calls.map((c) => c.join(' ')),
103
+ ...errSpy.mock.calls.map((c) => c.join(' ')),
104
+ ].join('\n');
105
+ expect(output).toContain('missing-page.mdx');
106
+ expect(output.toLowerCase()).toContain('missing navigation');
107
+ });
108
+ });
70
109
  //# sourceMappingURL=validate.integration.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/validate.integration.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/validate.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=templates-consistency.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates-consistency.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/templates-consistency.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { validateNavigationPages } from '../../lib/navigation-validator.js';
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const templatesDir = path.resolve(__dirname, '../../../templates');
9
+ describe('bundled init templates', () => {
10
+ it('contains a docs.json', () => {
11
+ expect(fs.existsSync(path.join(templatesDir, 'docs.json'))).toBe(true);
12
+ });
13
+ it('every page referenced in docs.json navigation exists as an MDX or MD file', async () => {
14
+ const config = JSON.parse(fs.readFileSync(path.join(templatesDir, 'docs.json'), 'utf-8'));
15
+ const warnings = await validateNavigationPages(config, templatesDir);
16
+ expect(warnings).toEqual([]);
17
+ });
18
+ });
19
+ //# sourceMappingURL=templates-consistency.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates-consistency.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/templates-consistency.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAE5E,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAEnE,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAC/D,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACrE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAUD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA4MtE"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAUD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA+MtE"}
@@ -10,7 +10,7 @@ import { validateConfig } from '../lib/docs-config.js';
10
10
  import { output } from '../lib/output.js';
11
11
  import { validateOpenApiSpec, formatErrorForCli, clearSpecCache } from '../lib/openapi.js';
12
12
  import { normalizeConfig } from '../lib/normalize-config.js';
13
- import { validateNavigationFiles } from '../lib/navigation-validator.js';
13
+ import { validateNavigationPages } from '../lib/navigation-validator.js';
14
14
  import { extractAllPages } from '../lib/mdx-validator.js';
15
15
  import { validateBrandingAssets } from '../lib/validate-branding.js';
16
16
  import { ensureDependencies } from '../lib/deps.js';
@@ -39,13 +39,16 @@ export async function validate(options) {
39
39
  console.log('');
40
40
  }
41
41
  }
42
- // Step 2: Validate navigation files exist
42
+ // Step 2: Validate navigation pages exist
43
43
  if (result.config) {
44
- const missingFiles = await validateNavigationFiles(result.config, projectDir);
45
- if (missingFiles.length > 0) {
44
+ const navWarnings = await validateNavigationPages(result.config, projectDir);
45
+ if (navWarnings.length > 0) {
46
46
  console.log('');
47
- output.warn('Missing navigation files:');
48
- missingFiles.forEach((f) => console.log(` - ${f}`));
47
+ output.warn('Missing navigation pages:');
48
+ for (const w of navWarnings) {
49
+ const detail = w.message ? ` — ${w.message}` : '';
50
+ console.log(` - ${w.file}${detail}`);
51
+ }
49
52
  console.log('');
50
53
  output.hint('Create these files or remove them from docs.json navigation');
51
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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"}
@@ -6,26 +6,48 @@
6
6
  *
7
7
  * Used by both CLI (validate command) and build-service (build-time warnings).
8
8
  */
9
- interface PageObject {
10
- page?: string;
11
- title?: string;
12
- }
13
- interface NavigationGroup {
14
- group?: string;
15
- pages?: (string | PageObject | NavigationGroup)[];
16
- }
17
- interface DocsConfig {
18
- navigation?: NavigationGroup[];
19
- }
20
9
  /**
21
- * Extract all page paths from navigation config.
22
- * Skips external URLs (http:// and https://).
10
+ * Structural copy of `BuildWarning` from ./status-reporter.ts. Kept local so
11
+ * this file can be copied byte-identically to all four tracked locations
12
+ * (build-service/shared, functions/src/shared, cli/vendored/shared,
13
+ * cli/src/lib) — the CLI src/lib copy has no sibling status-reporter.ts, and
14
+ * an `import type { BuildWarning } from './status-reporter.js'` would fail to
15
+ * resolve under the CLI's NodeNext tsconfig. TypeScript structural typing
16
+ * means callers in build-service/functions can still assign these to their
17
+ * imported BuildWarning[] without issue. Keep the shape in sync with
18
+ * builder/shared/status-reporter.ts.
23
19
  */
24
- export declare function extractNavigationPages(navigation: NavigationGroup[]): string[];
20
+ type BuildWarning = {
21
+ type: 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page';
22
+ file: string;
23
+ line?: number;
24
+ link?: string;
25
+ message?: string;
26
+ };
25
27
  /**
26
- * Validate that all navigation pages exist as MDX files.
27
- * Returns array of missing file paths.
28
+ * Extract all local page paths referenced anywhere in a navigation config.
29
+ *
30
+ * Walks every nav container shape (groups, tabs, anchors, dropdowns,
31
+ * products, versions, languages, global) recursively. Skips external URLs.
32
+ * Deduplicates — a page referenced from multiple containers is returned once.
33
+ *
34
+ * Input may be the full `navigation` object from docs.json, or any nested
35
+ * container. Returns a sorted unique list.
36
+ */
37
+ export declare function extractNavigationPages(nav: unknown): string[];
38
+ /**
39
+ * Boundary type for `validateNavigationPages` — any object with an optional
40
+ * `navigation` key. Callers' stricter `DocsConfig` types assign here.
41
+ */
42
+ export type NavValidationInput = {
43
+ navigation?: unknown;
44
+ [key: string]: unknown;
45
+ };
46
+ /**
47
+ * Validate that all pages referenced in docs.json navigation exist on disk
48
+ * as either `.mdx` or `.md` files. Returns a BuildWarning[] suitable for
49
+ * merging into `reporter.reportCompletion({ warnings })`.
28
50
  */
29
- export declare function validateNavigationFiles(config: DocsConfig, projectDir: string): Promise<string[]>;
51
+ export declare function validateNavigationPages(config: NavValidationInput | null | undefined, projectDir: string): Promise<BuildWarning[]>;
30
52
  export {};
31
53
  //# sourceMappingURL=navigation-validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-validator.d.ts","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,UAAU,GAAG,eAAe,CAAC,EAAE,CAAC;CACnD;AAED,UAAU,UAAU;IAClB,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAcD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CA4B9E;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBnB"}
1
+ {"version":3,"file":"navigation-validator.d.ts","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;;;;GAUG;AACH,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,cAAc,CAAC;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA6BF;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CA0C7D;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAElF;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,kBAAkB,GAAG,IAAI,GAAG,SAAS,EAC7C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAwBzB"}
@@ -21,55 +21,84 @@ async function fileExists(filePath) {
21
21
  }
22
22
  }
23
23
  /**
24
- * Extract all page paths from navigation config.
25
- * Skips external URLs (http:// and https://).
24
+ * Extract all local page paths referenced anywhere in a navigation config.
25
+ *
26
+ * Walks every nav container shape (groups, tabs, anchors, dropdowns,
27
+ * products, versions, languages, global) recursively. Skips external URLs.
28
+ * Deduplicates — a page referenced from multiple containers is returned once.
29
+ *
30
+ * Input may be the full `navigation` object from docs.json, or any nested
31
+ * container. Returns a sorted unique list.
26
32
  */
27
- export function extractNavigationPages(navigation) {
28
- const pages = [];
29
- function processPages(items) {
33
+ export function extractNavigationPages(nav) {
34
+ if (!nav || typeof nav !== 'object')
35
+ return [];
36
+ const seen = new Set();
37
+ function addPage(value) {
38
+ if (typeof value !== 'string')
39
+ return;
40
+ if (value.startsWith('http://') || value.startsWith('https://'))
41
+ return;
42
+ seen.add(value);
43
+ }
44
+ function walkItems(items) {
45
+ if (!Array.isArray(items))
46
+ return;
30
47
  for (const item of items) {
31
48
  if (typeof item === 'string') {
32
- // Skip external URLs
33
- if (!item.startsWith('http://') && !item.startsWith('https://')) {
34
- pages.push(item);
35
- }
49
+ addPage(item);
36
50
  }
37
51
  else if (item && typeof item === 'object') {
38
- if ('page' in item && typeof item.page === 'string') {
39
- pages.push(item.page);
40
- }
41
- if ('pages' in item && Array.isArray(item.pages)) {
42
- processPages(item.pages);
43
- }
52
+ walkContainer(item);
44
53
  }
45
54
  }
46
55
  }
47
- for (const group of navigation) {
48
- if (group.pages) {
49
- processPages(group.pages);
50
- }
56
+ function walkContainer(container) {
57
+ if (!container || typeof container !== 'object')
58
+ return;
59
+ // `page` property on an item (e.g., { page: "foo", title: "..." })
60
+ if ('page' in container)
61
+ addPage(container.page);
62
+ walkItems(container.pages);
63
+ walkItems(container.groups);
64
+ walkItems(container.tabs);
65
+ walkItems(container.anchors);
66
+ walkItems(container.dropdowns);
67
+ walkItems(container.products);
68
+ walkItems(container.versions);
69
+ walkItems(container.languages);
70
+ if (container.global)
71
+ walkContainer(container.global);
51
72
  }
52
- return pages;
73
+ walkContainer(nav);
74
+ return Array.from(seen).sort();
53
75
  }
54
76
  /**
55
- * Validate that all navigation pages exist as MDX files.
56
- * Returns array of missing file paths.
77
+ * Validate that all pages referenced in docs.json navigation exist on disk
78
+ * as either `.mdx` or `.md` files. Returns a BuildWarning[] suitable for
79
+ * merging into `reporter.reportCompletion({ warnings })`.
57
80
  */
58
- export async function validateNavigationFiles(config, projectDir) {
59
- if (!config.navigation || !Array.isArray(config.navigation)) {
81
+ export async function validateNavigationPages(config, projectDir) {
82
+ if (!config || typeof config !== 'object')
60
83
  return [];
61
- }
62
84
  const pagePaths = extractNavigationPages(config.navigation);
63
- const missing = [];
64
- for (const pagePath of pagePaths) {
65
- const mdxPath = path.join(projectDir, `${pagePath}.mdx`);
66
- const mdPath = path.join(projectDir, `${pagePath}.md`);
67
- const mdxExists = await fileExists(mdxPath);
68
- const mdExists = await fileExists(mdPath);
69
- if (!mdxExists && !mdExists) {
70
- missing.push(`${pagePath}.mdx`);
71
- }
72
- }
73
- return missing;
85
+ if (pagePaths.length === 0)
86
+ return [];
87
+ // Short-circuits to `.md` only if `.mdx` doesn't exist, avoiding a second
88
+ // stat when the first hits.
89
+ const results = await Promise.all(pagePaths.map(async (pagePath) => {
90
+ const mdxExists = await fileExists(path.join(projectDir, `${pagePath}.mdx`));
91
+ if (mdxExists)
92
+ return { pagePath, exists: true };
93
+ const mdExists = await fileExists(path.join(projectDir, `${pagePath}.md`));
94
+ return { pagePath, exists: mdExists };
95
+ }));
96
+ return results
97
+ .filter((r) => !r.exists)
98
+ .map(({ pagePath }) => ({
99
+ type: 'missing_page',
100
+ file: `${pagePath}.mdx`,
101
+ message: `Page "${pagePath}" is referenced in navigation but no ${pagePath}.mdx or ${pagePath}.md file exists`,
102
+ }));
74
103
  }
75
104
  //# sourceMappingURL=navigation-validator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-validator.js","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAgBxB;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,QAAgB;IACxC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAA6B;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,SAAS,YAAY,CAAC,KAAgD;QACpE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,qBAAqB;gBACrB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;oBAChE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC5C,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACpD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxB,CAAC;gBACD,IAAI,OAAO,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBACjD,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAkB,EAClB,UAAkB;IAElB,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5D,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,QAAQ,MAAM,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QAEvD,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;QAE1C,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,MAAM,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"navigation-validator.js","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jamdesk",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
4
4
  "description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
5
5
  "keywords": [
6
6
  "jamdesk",
@@ -214,9 +214,13 @@ export interface TabConfig {
214
214
  }
215
215
 
216
216
  /**
217
- * Anchor configuration (top-level navigation sections)
218
- * @deprecated Use TabConfig instead. Anchors in navigation are deprecated.
219
- * For external links, use the top-level `anchors` field with ExternalAnchorConfig.
217
+ * Legacy anchor configuration (top-level navigation sections).
218
+ *
219
+ * Retained so NavigationConfig / DropdownConfig / ProductConfig /
220
+ * VersionConfig / LanguageConfig can continue to type customer configs
221
+ * that still carry the legacy shape. New configs should use TabConfig
222
+ * for internal sections and top-level ExternalAnchorConfig for external
223
+ * links; `validateConfig` rejects `navigation.anchors` at build time.
220
224
  */
221
225
  export interface AnchorConfig {
222
226
  anchor: string;
@@ -792,9 +796,15 @@ export interface DocsConfig {
792
796
  // Required fields
793
797
  theme: ThemeName;
794
798
  name: string;
795
- colors: ColorsConfig;
796
799
  navigation: NavigationConfig;
797
800
 
801
+ // Boundary escape hatch — docs.json is user-authored and may carry fields
802
+ // not represented in this interface. Unknown keys read as `unknown`.
803
+ [key: string]: unknown;
804
+
805
+ // Optional — themes provide defaults at render time
806
+ colors?: ColorsConfig;
807
+
798
808
  // Optional fields
799
809
  description?: string;
800
810
  favicon?: Favicon;
@@ -12,7 +12,7 @@ import type { DocsConfig } from './docs-types.js';
12
12
  export interface ExtractedHighlights {
13
13
  siteName: string;
14
14
  theme: 'jam' | 'nebula' | 'pulsar';
15
- primaryColor: string;
15
+ primaryColor?: string;
16
16
  seoIndexable: boolean;
17
17
  analyticsIntegrations: string[];
18
18
  apiPlaygroundType: 'interactive' | 'simple' | 'hidden' | null;
@@ -118,7 +118,7 @@ export function extractConfigHighlights(config: DocsConfig, pageCount: number =
118
118
  return {
119
119
  siteName: config.name,
120
120
  theme: config.theme,
121
- primaryColor: config.colors.primary,
121
+ primaryColor: config.colors?.primary,
122
122
  seoIndexable,
123
123
  analyticsIntegrations,
124
124
  apiPlaygroundType,
@@ -19,7 +19,7 @@ const Ajv = (AjvModule as any).default || AjvModule;
19
19
  import JSON5 from 'json5';
20
20
  import fs from 'fs';
21
21
  import path from 'path';
22
- import { LIB_DIR } from './paths.js';
22
+ import { BUILD_SERVICE_DIR } from './paths.js';
23
23
 
24
24
  // Types
25
25
  export interface DocsConfig {
@@ -76,8 +76,11 @@ export async function getSchema(): Promise<Schema | null> {
76
76
  return cachedSchema;
77
77
  }
78
78
 
79
- // Try to load bundled schema first
80
- const bundledSchemaPath = path.join(LIB_DIR, '../schema/docs-schema.json');
79
+ // Try to load bundled schema first. Resolve from BUILD_SERVICE_DIR, not
80
+ // LIB_DIR in compiled CJS `__dirname` is `dist/lib`, so the old
81
+ // `LIB_DIR/../schema` path pointed at `dist/schema` (doesn't exist) and
82
+ // Cloud Run silently fell back to fetching the live marketing schema.
83
+ const bundledSchemaPath = path.join(BUILD_SERVICE_DIR, 'schema/docs-schema.json');
81
84
  if (fs.existsSync(bundledSchemaPath)) {
82
85
  const schemaContent = fs.readFileSync(bundledSchemaPath, 'utf-8');
83
86
  cachedSchema = JSON.parse(schemaContent) as Schema;
@@ -117,6 +120,97 @@ export function parseConfig(content: string): DocsConfig {
117
120
  }
118
121
  }
119
122
 
123
+ /**
124
+ * Filter out noise from anyOf validation errors.
125
+ *
126
+ * The master schema uses a 3-branch anyOf (one per theme) and Ajv emits errors
127
+ * from every branch it tried, producing messages like `must be "nebula"` when
128
+ * the user typed `theme: "jam"`. This helper:
129
+ *
130
+ * 1. Groups errors by their `#/anyOf/<N>` branch.
131
+ * 2. Finds the matching branch by the absence of a `/theme` const error
132
+ * (a passing branch can't emit a theme mismatch).
133
+ * 3. Drops all `/theme` const errors (just noise from wrong branches).
134
+ * 4. Drops all top-level `anyOf` meta-errors (never actionable).
135
+ * 5. Keeps only errors from the matching branch, plus errors not anchored
136
+ * to any anyOf branch (e.g., `#/definitions/...`).
137
+ * 6. Deduplicates by (instancePath, keyword, params).
138
+ *
139
+ * Ported from `builder/cli/src/lib/docs-config.ts`, which has used this logic
140
+ * in production since the CLI shipped. The `filterAnyOfNoise` bodies match;
141
+ * `formatValidationErrors` deliberately differs — build-service adds an
142
+ * `effectiveErrors` fallback the CLI version doesn't need.
143
+ */
144
+ export function filterAnyOfNoise(errors: ErrorObject[]): ErrorObject[] {
145
+ // First pass: identify the correct theme branch based on which branch
146
+ // does NOT have a const error for /theme
147
+ const branchErrors = new Map<string, ErrorObject[]>();
148
+ for (const err of errors) {
149
+ const match = err.schemaPath.match(/#\/anyOf\/(\d+)/);
150
+ if (match) {
151
+ const branch = match[1];
152
+ if (!branchErrors.has(branch)) {
153
+ branchErrors.set(branch, []);
154
+ }
155
+ branchErrors.get(branch)!.push(err);
156
+ }
157
+ }
158
+
159
+ // Find the branch that matches the user's theme (no theme const error)
160
+ let matchingBranch: string | null = null;
161
+ for (const [branch, errs] of branchErrors) {
162
+ const hasThemeConstError = errs.some((e) =>
163
+ e.keyword === 'const' && e.instancePath === '/theme',
164
+ );
165
+ if (!hasThemeConstError) {
166
+ matchingBranch = branch;
167
+ break;
168
+ }
169
+ }
170
+
171
+ // Filter to only show errors from the matching branch
172
+ // Also filter out generic "anyOf" errors which are not actionable
173
+ const filtered = errors.filter((err) => {
174
+ // Always filter out theme const errors (just noise from wrong branches)
175
+ if (err.keyword === 'const' && err.instancePath === '/theme') {
176
+ return false;
177
+ }
178
+
179
+ // Filter out generic anyOf errors (not actionable, user needs specific errors)
180
+ if (err.keyword === 'anyOf') {
181
+ return false;
182
+ }
183
+
184
+ // If we identified a matching branch, only show errors from that branch
185
+ // (or errors not from any anyOf branch)
186
+ if (matchingBranch !== null) {
187
+ const match = err.schemaPath.match(/#\/anyOf\/(\d+)/);
188
+ if (match && match[1] !== matchingBranch) {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ return true;
194
+ });
195
+
196
+ return deduplicateErrors(filtered);
197
+ }
198
+
199
+ /**
200
+ * Remove duplicate errors that share path + keyword + params.
201
+ */
202
+ function deduplicateErrors(errors: ErrorObject[]): ErrorObject[] {
203
+ const seen = new Set<string>();
204
+ return errors.filter((err) => {
205
+ const key = `${err.instancePath}|${err.keyword}|${JSON.stringify(err.params)}`;
206
+ if (seen.has(key)) {
207
+ return false;
208
+ }
209
+ seen.add(key);
210
+ return true;
211
+ });
212
+ }
213
+
120
214
  /**
121
215
  * Format validation errors into user-friendly messages
122
216
  */
@@ -124,8 +218,16 @@ export function formatValidationErrors(errors: ErrorObject[] | null | undefined)
124
218
  if (!errors || errors.length === 0) {
125
219
  return 'Unknown validation error';
126
220
  }
127
-
128
- const messages = errors.slice(0, 3).map(err => {
221
+
222
+ // Fall back to raw errors if the filter empties the list — prevents
223
+ // degrading to "Unknown validation error" for a config Ajv rejected.
224
+ // Safe only because validateConfig pre-checks `config.theme` before Ajv,
225
+ // so a fully-noise error list is unreachable in practice. A new caller
226
+ // that skips the theme pre-check would break this invariant.
227
+ const filteredErrors = filterAnyOfNoise(errors);
228
+ const effectiveErrors = filteredErrors.length > 0 ? filteredErrors : errors;
229
+
230
+ const messages = effectiveErrors.slice(0, 3).map(err => {
129
231
  const path = err.instancePath || 'root';
130
232
 
131
233
  switch (err.keyword) {
@@ -150,10 +252,10 @@ export function formatValidationErrors(errors: ErrorObject[] | null | undefined)
150
252
  }
151
253
  });
152
254
 
153
- if (errors.length > 3) {
154
- messages.push(`...and ${errors.length - 3} more error(s)`);
255
+ if (effectiveErrors.length > 3) {
256
+ messages.push(`...and ${effectiveErrors.length - 3} more error(s)`);
155
257
  }
156
-
258
+
157
259
  return messages.join('. ');
158
260
  }
159
261
 
@@ -210,9 +312,13 @@ export async function validateConfig(configPath: string, docsPath?: string): Pro
210
312
  };
211
313
  }
212
314
 
213
- // Validate navigation has content
315
+ // Validate navigation has content. navigation.anchors is a deprecated
316
+ // field that we reject below, but it still counts as "has content" for
317
+ // this gate so legacy configs get the precise migration error rather
318
+ // than the generic "must have at least one of" message.
214
319
  const nav = config.navigation;
215
- const hasContent = nav.anchors || nav.tabs || nav.groups || nav.pages ||
320
+ const legacyAnchors = (nav as { anchors?: unknown }).anchors;
321
+ const hasContent = legacyAnchors || nav.tabs || nav.groups || nav.pages ||
216
322
  nav.products || nav.dropdowns || nav.versions || nav.languages;
217
323
  if (!hasContent) {
218
324
  return {
@@ -220,7 +326,7 @@ export async function validateConfig(configPath: string, docsPath?: string): Pro
220
326
  error: 'Invalid docs.json: Navigation must have at least one of: anchors, tabs, groups, pages, products, dropdowns, versions, or languages.',
221
327
  };
222
328
  }
223
-
329
+
224
330
  // Validate theme
225
331
  const validThemes = ['jam', 'nebula', 'pulsar'];
226
332
  if (config.theme && !validThemes.includes(config.theme)) {
@@ -230,8 +336,7 @@ export async function validateConfig(configPath: string, docsPath?: string): Pro
230
336
  };
231
337
  }
232
338
 
233
- // Build error for navigation.anchors (no longer supported)
234
- if (nav.anchors) {
339
+ if (legacyAnchors) {
235
340
  return {
236
341
  valid: false,
237
342
  error: 'Invalid docs.json: "anchors" is not supported within "navigation". Use "tabs" instead. Run `jamdesk migrate` to update your docs.json.',
@@ -1604,7 +1604,6 @@
1604
1604
  "required": [
1605
1605
  "theme",
1606
1606
  "name",
1607
- "colors",
1608
1607
  "navigation"
1609
1608
  ],
1610
1609
  "additionalProperties": false
@@ -1726,7 +1725,6 @@
1726
1725
  "required": [
1727
1726
  "theme",
1728
1727
  "name",
1729
- "colors",
1730
1728
  "navigation"
1731
1729
  ],
1732
1730
  "additionalProperties": false
@@ -1848,7 +1846,6 @@
1848
1846
  "required": [
1849
1847
  "theme",
1850
1848
  "name",
1851
- "colors",
1852
1849
  "navigation"
1853
1850
  ],
1854
1851
  "additionalProperties": false
@@ -10,18 +10,38 @@
10
10
  import fs from 'fs';
11
11
  import path from 'path';
12
12
 
13
- interface PageObject {
14
- page?: string;
15
- title?: string;
16
- }
17
-
18
- interface NavigationGroup {
19
- group?: string;
20
- pages?: (string | PageObject | NavigationGroup)[];
21
- }
13
+ /**
14
+ * Structural copy of `BuildWarning` from ./status-reporter.ts. Kept local so
15
+ * this file can be copied byte-identically to all four tracked locations
16
+ * (build-service/shared, functions/src/shared, cli/vendored/shared,
17
+ * cli/src/lib) — the CLI src/lib copy has no sibling status-reporter.ts, and
18
+ * an `import type { BuildWarning } from './status-reporter.js'` would fail to
19
+ * resolve under the CLI's NodeNext tsconfig. TypeScript structural typing
20
+ * means callers in build-service/functions can still assign these to their
21
+ * imported BuildWarning[] without issue. Keep the shape in sync with
22
+ * builder/shared/status-reporter.ts.
23
+ */
24
+ type BuildWarning = {
25
+ type: 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page';
26
+ file: string;
27
+ line?: number;
28
+ link?: string;
29
+ message?: string;
30
+ };
22
31
 
23
- interface DocsConfig {
24
- navigation?: NavigationGroup[];
32
+ /**
33
+ * Minimal shape for anything that can nest pages.
34
+ */
35
+ interface NavContainer {
36
+ pages?: unknown[];
37
+ groups?: unknown[];
38
+ tabs?: unknown[];
39
+ anchors?: unknown[];
40
+ dropdowns?: unknown[];
41
+ products?: unknown[];
42
+ versions?: unknown[];
43
+ languages?: unknown[];
44
+ global?: NavContainer;
25
45
  }
26
46
 
27
47
  /**
@@ -37,65 +57,95 @@ async function fileExists(filePath: string): Promise<boolean> {
37
57
  }
38
58
 
39
59
  /**
40
- * Extract all page paths from navigation config.
41
- * Skips external URLs (http:// and https://).
60
+ * Extract all local page paths referenced anywhere in a navigation config.
61
+ *
62
+ * Walks every nav container shape (groups, tabs, anchors, dropdowns,
63
+ * products, versions, languages, global) recursively. Skips external URLs.
64
+ * Deduplicates — a page referenced from multiple containers is returned once.
65
+ *
66
+ * Input may be the full `navigation` object from docs.json, or any nested
67
+ * container. Returns a sorted unique list.
42
68
  */
43
- export function extractNavigationPages(navigation: NavigationGroup[]): string[] {
44
- const pages: string[] = [];
69
+ export function extractNavigationPages(nav: unknown): string[] {
70
+ if (!nav || typeof nav !== 'object') return [];
71
+
72
+ const seen = new Set<string>();
73
+
74
+ function addPage(value: unknown): void {
75
+ if (typeof value !== 'string') return;
76
+ if (value.startsWith('http://') || value.startsWith('https://')) return;
77
+ seen.add(value);
78
+ }
45
79
 
46
- function processPages(items: (string | PageObject | NavigationGroup)[]): void {
80
+ function walkItems(items: unknown): void {
81
+ if (!Array.isArray(items)) return;
47
82
  for (const item of items) {
48
83
  if (typeof item === 'string') {
49
- // Skip external URLs
50
- if (!item.startsWith('http://') && !item.startsWith('https://')) {
51
- pages.push(item);
52
- }
84
+ addPage(item);
53
85
  } else if (item && typeof item === 'object') {
54
- if ('page' in item && typeof item.page === 'string') {
55
- pages.push(item.page);
56
- }
57
- if ('pages' in item && Array.isArray(item.pages)) {
58
- processPages(item.pages);
59
- }
86
+ walkContainer(item as NavContainer);
60
87
  }
61
88
  }
62
89
  }
63
90
 
64
- for (const group of navigation) {
65
- if (group.pages) {
66
- processPages(group.pages);
67
- }
91
+ function walkContainer(container: NavContainer): void {
92
+ if (!container || typeof container !== 'object') return;
93
+
94
+ // `page` property on an item (e.g., { page: "foo", title: "..." })
95
+ if ('page' in container) addPage((container as { page?: unknown }).page);
96
+
97
+ walkItems(container.pages);
98
+ walkItems(container.groups);
99
+ walkItems(container.tabs);
100
+ walkItems(container.anchors);
101
+ walkItems(container.dropdowns);
102
+ walkItems(container.products);
103
+ walkItems(container.versions);
104
+ walkItems(container.languages);
105
+ if (container.global) walkContainer(container.global);
68
106
  }
69
107
 
70
- return pages;
108
+ walkContainer(nav as NavContainer);
109
+
110
+ return Array.from(seen).sort();
71
111
  }
72
112
 
73
113
  /**
74
- * Validate that all navigation pages exist as MDX files.
75
- * Returns array of missing file paths.
114
+ * Boundary type for `validateNavigationPages` any object with an optional
115
+ * `navigation` key. Callers' stricter `DocsConfig` types assign here.
76
116
  */
77
- export async function validateNavigationFiles(
78
- config: DocsConfig,
79
- projectDir: string
80
- ): Promise<string[]> {
81
- if (!config.navigation || !Array.isArray(config.navigation)) {
82
- return [];
83
- }
84
-
85
- const pagePaths = extractNavigationPages(config.navigation);
86
- const missing: string[] = [];
117
+ export type NavValidationInput = { navigation?: unknown; [key: string]: unknown };
87
118
 
88
- for (const pagePath of pagePaths) {
89
- const mdxPath = path.join(projectDir, `${pagePath}.mdx`);
90
- const mdPath = path.join(projectDir, `${pagePath}.md`);
119
+ /**
120
+ * Validate that all pages referenced in docs.json navigation exist on disk
121
+ * as either `.mdx` or `.md` files. Returns a BuildWarning[] suitable for
122
+ * merging into `reporter.reportCompletion({ warnings })`.
123
+ */
124
+ export async function validateNavigationPages(
125
+ config: NavValidationInput | null | undefined,
126
+ projectDir: string,
127
+ ): Promise<BuildWarning[]> {
128
+ if (!config || typeof config !== 'object') return [];
91
129
 
92
- const mdxExists = await fileExists(mdxPath);
93
- const mdExists = await fileExists(mdPath);
130
+ const pagePaths = extractNavigationPages(config.navigation);
131
+ if (pagePaths.length === 0) return [];
94
132
 
95
- if (!mdxExists && !mdExists) {
96
- missing.push(`${pagePath}.mdx`);
97
- }
98
- }
133
+ // Short-circuits to `.md` only if `.mdx` doesn't exist, avoiding a second
134
+ // stat when the first hits.
135
+ const results = await Promise.all(
136
+ pagePaths.map(async (pagePath) => {
137
+ const mdxExists = await fileExists(path.join(projectDir, `${pagePath}.mdx`));
138
+ if (mdxExists) return { pagePath, exists: true };
139
+ const mdExists = await fileExists(path.join(projectDir, `${pagePath}.md`));
140
+ return { pagePath, exists: mdExists };
141
+ }),
142
+ );
99
143
 
100
- return missing;
144
+ return results
145
+ .filter((r) => !r.exists)
146
+ .map(({ pagePath }) => ({
147
+ type: 'missing_page',
148
+ file: `${pagePath}.mdx`,
149
+ message: `Page "${pagePath}" is referenced in navigation but no ${pagePath}.mdx or ${pagePath}.md file exists`,
150
+ }));
101
151
  }
@@ -22,7 +22,7 @@ export interface ProgressUpdate {
22
22
  }
23
23
 
24
24
  /** Warning types that can occur during builds (non-blocking) */
25
- export type BuildWarningType = 'broken_link' | 'auto_migrate' | 'missing_asset';
25
+ export type BuildWarningType = 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page';
26
26
 
27
27
  /** Build warning structure */
28
28
  export interface BuildWarning {