jamdesk 1.1.136 → 1.1.138

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 (56) hide show
  1. package/dist/__tests__/unit/config-suggestions.test.d.ts +2 -0
  2. package/dist/__tests__/unit/config-suggestions.test.d.ts.map +1 -0
  3. package/dist/__tests__/unit/config-suggestions.test.js +44 -0
  4. package/dist/__tests__/unit/config-suggestions.test.js.map +1 -0
  5. package/dist/__tests__/unit/deploy.test.js +54 -1
  6. package/dist/__tests__/unit/deploy.test.js.map +1 -1
  7. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts +2 -0
  8. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts.map +1 -0
  9. package/dist/__tests__/unit/dev-workspace-symlinks.test.js +112 -0
  10. package/dist/__tests__/unit/dev-workspace-symlinks.test.js.map +1 -0
  11. package/dist/__tests__/unit/language-filter.test.d.ts +2 -0
  12. package/dist/__tests__/unit/language-filter.test.d.ts.map +1 -0
  13. package/dist/__tests__/unit/language-filter.test.js +166 -0
  14. package/dist/__tests__/unit/language-filter.test.js.map +1 -0
  15. package/dist/__tests__/unit/loading-page.test.js +8 -0
  16. package/dist/__tests__/unit/loading-page.test.js.map +1 -1
  17. package/dist/__tests__/unit/tarball.test.js +25 -1
  18. package/dist/__tests__/unit/tarball.test.js.map +1 -1
  19. package/dist/commands/deploy.d.ts +1 -0
  20. package/dist/commands/deploy.d.ts.map +1 -1
  21. package/dist/commands/deploy.js +9 -1
  22. package/dist/commands/deploy.js.map +1 -1
  23. package/dist/commands/dev.d.ts.map +1 -1
  24. package/dist/commands/dev.js +6 -0
  25. package/dist/commands/dev.js.map +1 -1
  26. package/dist/commands/validate.d.ts.map +1 -1
  27. package/dist/commands/validate.js +13 -0
  28. package/dist/commands/validate.js.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/lib/config-suggestions.d.ts +33 -0
  32. package/dist/lib/config-suggestions.d.ts.map +1 -0
  33. package/dist/lib/config-suggestions.js +45 -0
  34. package/dist/lib/config-suggestions.js.map +1 -0
  35. package/dist/lib/language-filter.d.ts +31 -0
  36. package/dist/lib/language-filter.d.ts.map +1 -0
  37. package/dist/lib/language-filter.js +14 -0
  38. package/dist/lib/language-filter.js.map +1 -0
  39. package/dist/lib/loading-page.d.ts.map +1 -1
  40. package/dist/lib/loading-page.js +9 -1
  41. package/dist/lib/loading-page.js.map +1 -1
  42. package/dist/lib/output.d.ts +7 -0
  43. package/dist/lib/output.d.ts.map +1 -1
  44. package/dist/lib/output.js +9 -0
  45. package/dist/lib/output.js.map +1 -1
  46. package/dist/lib/tarball.d.ts +8 -0
  47. package/dist/lib/tarball.d.ts.map +1 -1
  48. package/dist/lib/tarball.js +13 -0
  49. package/dist/lib/tarball.js.map +1 -1
  50. package/package.json +1 -1
  51. package/vendored/lib/build/error-parser.ts +33 -0
  52. package/vendored/lib/config-suggestions.ts +71 -0
  53. package/vendored/lib/r2-cleanup.ts +57 -4
  54. package/vendored/shared/status-reporter.ts +34 -1
  55. package/vendored/shared/types.ts +1 -0
  56. package/vendored/workspace-package-lock.json +3 -3
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Config Suggestions (CLI)
3
+ *
4
+ * Detects optional-but-recommended docs.json fields the user hasn't set
5
+ * (favicon, description, logo) and returns suggestions with docs links.
6
+ * Softer than warnings: printed in a separate dim "Suggestions" section by
7
+ * `validate` and `dev`; never affects exit codes.
8
+ *
9
+ * Independent twin of build-service/lib/config-suggestions.ts (same
10
+ * precedent as validate-branding.ts). Keep messages/links aligned.
11
+ */
12
+ const BRANDING_DOCS_URL = 'https://jamdesk.com/docs/customization/branding';
13
+ /** A branding image field counts as set when it carries at least one path. */
14
+ function hasImageValue(value) {
15
+ if (!value)
16
+ return false;
17
+ if (typeof value === 'string')
18
+ return value.trim() !== '';
19
+ return Boolean(value.light?.trim() || value.dark?.trim());
20
+ }
21
+ export function suggestConfigImprovements(config) {
22
+ const suggestions = [];
23
+ if (!hasImageValue(config.favicon)) {
24
+ suggestions.push({
25
+ message: 'Add a favicon so browser tabs and bookmarks show your brand icon instead of a generic page icon.',
26
+ link: `${BRANDING_DOCS_URL}#favicon`,
27
+ });
28
+ }
29
+ if (!config.description || config.description.trim() === '') {
30
+ suggestions.push({
31
+ message: 'Add a site description to improve how your docs appear in search results and social shares.',
32
+ // #site-name is deliberate: description is documented inside the docs
33
+ // page's "Site Name" section (no #description anchor exists yet).
34
+ link: `${BRANDING_DOCS_URL}#site-name`,
35
+ });
36
+ }
37
+ if (!hasImageValue(config.logo)) {
38
+ suggestions.push({
39
+ message: 'Add a logo so your site header shows your brand instead of the default Jamdesk logo.',
40
+ link: `${BRANDING_DOCS_URL}#logo`,
41
+ });
42
+ }
43
+ return suggestions;
44
+ }
45
+ //# sourceMappingURL=config-suggestions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-suggestions.js","sourceRoot":"","sources":["../../src/lib/config-suggestions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,MAAM,iBAAiB,GAAG,iDAAiD,CAAC;AAE5E,8EAA8E;AAC9E,SAAS,aAAa,CAAC,KAA6D;IAClF,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC1D,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAwB;IAChE,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACnC,WAAW,CAAC,IAAI,CAAC;YACf,OAAO,EAAE,kGAAkG;YAC3G,IAAI,EAAE,GAAG,iBAAiB,UAAU;SACrC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC5D,WAAW,CAAC,IAAI,CAAC;YACf,OAAO,EAAE,6FAA6F;YACtG,sEAAsE;YACtE,kEAAkE;YAClE,IAAI,EAAE,GAAG,iBAAiB,YAAY;SACvC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,WAAW,CAAC,IAAI,CAAC;YACf,OAAO,EAAE,sFAAsF;YAC/F,IAAI,EAAE,GAAG,iBAAiB,OAAO;SAClC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Picks which `<lang>/` directories under projectDir to keep as workspace
3
+ * symlinks and which to skip. Skipping non-active language directories
4
+ * shrinks Turbopack's filesystem scan and dropped cold compile from ~67s
5
+ * to ~12s on jamdesk-docs (commit 90d781b4).
6
+ *
7
+ * The dev server still reads content from the user's source tree via
8
+ * JAMDESK_PROJECTS_DIR, so the language picker keeps showing every
9
+ * language and clicks always 200 OK — the workspace symlinks are a
10
+ * Turbopack-only performance layer.
11
+ *
12
+ * Default rules:
13
+ * 1. Language with `default: true`
14
+ * 2. First language in `navigation.languages[]`
15
+ */
16
+ export interface LanguageFilter {
17
+ active: string | null;
18
+ skip: Set<string>;
19
+ }
20
+ interface NavigationLanguageEntry {
21
+ language?: string;
22
+ default?: boolean;
23
+ }
24
+ interface MinimalConfig {
25
+ navigation?: {
26
+ languages?: NavigationLanguageEntry[];
27
+ };
28
+ }
29
+ export declare function getActiveLanguageFilter(config: MinimalConfig): LanguageFilter;
30
+ export {};
31
+ //# sourceMappingURL=language-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-filter.d.ts","sourceRoot":"","sources":["../../src/lib/language-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,UAAU,uBAAuB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE;QACX,SAAS,CAAC,EAAE,uBAAuB,EAAE,CAAC;KACvC,CAAC;CACH;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,CAc7E"}
@@ -0,0 +1,14 @@
1
+ export function getActiveLanguageFilter(config) {
2
+ const validEntries = (config.navigation?.languages ?? []).filter((l) => typeof l.language === 'string');
3
+ if (validEntries.length === 0) {
4
+ return { active: null, skip: new Set() };
5
+ }
6
+ const codes = validEntries.map((l) => l.language);
7
+ const active = validEntries.find((l) => l.default)?.language ?? validEntries[0].language;
8
+ if (codes.length === 1) {
9
+ return { active, skip: new Set() };
10
+ }
11
+ const skip = new Set(codes.filter((c) => c !== active));
12
+ return { active, skip };
13
+ }
14
+ //# sourceMappingURL=language-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-filter.js","sourceRoot":"","sources":["../../src/lib/language-filter.ts"],"names":[],"mappings":"AA+BA,MAAM,UAAU,uBAAuB,CAAC,MAAqB;IAC3D,MAAM,YAAY,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAC9D,CAAC,CAAC,EAAgD,EAAE,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CACpF,CAAC;IACF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;IAC7C,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"loading-page.d.ts","sourceRoot":"","sources":["../../src/lib/loading-page.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAiNtE"}
1
+ {"version":3,"file":"loading-page.d.ts","sourceRoot":"","sources":["../../src/lib/loading-page.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAUD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAkNtE"}
@@ -2,6 +2,13 @@
2
2
  * Generates a self-contained HTML loading page shown while Next.js compiles.
3
3
  * Inline CSS + JS, zero external requests.
4
4
  */
5
+ /**
6
+ * Jamdesk purple mark, shared by the body logo and the favicon data URI.
7
+ * The favicon must be declared explicitly: without one the browser reuses
8
+ * whatever icon it last cached for localhost:<port> (often another docs
9
+ * CLI's), since /favicon.ico requests get this HTML page back while loading.
10
+ */
11
+ const JAMDESK_MARK_SVG = `<svg viewBox="0 0 2289 2038" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#jd)"><path d="M1129.57 1311.33L874.802 891.18C871.197 885.234 865.147 881.59 858.194 881.554C767.557 881.077 136.784 877.9 113.489 881.612C88.3871 885.611 73.8331 886.702 53.4424 900.608C33.0517 914.514 15.9545 933.404 6.68471 963.597C-2.5851 993.79 -2.17416 1016.09 7.66949 1048.58C17.5131 1081.08 524.5 1921.11 545.5 1952.61C566.5 1984.11 583.272 2010.33 616.006 2023.4C648.739 2036.48 695.461 2035.4 724.5 2023.4C753.539 2011.4 772 1979.11 791 1952.61C808.575 1928.1 1088.41 1408.59 1130.28 1330.82C1133.63 1324.58 1133.24 1317.39 1129.57 1311.33Z" fill="#D6C2FF"/><path d="M2166.5 885.611C2131.07 879.853 1511.26 884.752 1418.96 885.515C1411.63 885.576 1405.35 889.635 1401.91 896.098L809.142 2007.78C802.048 2021.08 811.223 2037.15 826.3 2037.18C987.697 2037.52 1690.61 2038.88 1710.98 2037.11C1734 2035.11 1756.5 2027.11 1774 2013.11C1791.5 1999.11 1803.5 1976.11 1821 1953.11C1838.5 1930.11 2246.48 1130.65 2259.79 1099.11C2273.1 1067.57 2286.88 1046.31 2288.29 1012.11C2289.7 977.909 2277.12 955.19 2252 926.611C2226.88 898.033 2204.79 891.835 2166.5 885.611Z" fill="#AD85FF"/><path d="M501.001 57.6116C483.367 88.4712 161.381 679.447 85.4473 818.837C78.1875 832.164 87.9925 848.112 103.169 848.112H2171.04C2186.72 848.112 2196.44 831.097 2188.15 817.777C2100.22 676.343 1733.69 87.1557 1709.5 54.6116C1682 17.6118 1638 3.11141 1593 3.11126C1548 3.11111 621.001 -3.88914 582.001 3.11146C543.001 10.112 521.001 22.6116 501.001 57.6116Z" fill="#9966FF"/></g><defs><clipPath id="jd"><rect width="2289" height="2038" fill="white"/></clipPath></defs></svg>`;
5
12
  export function getLoadingPageHtml(options) {
6
13
  const { projectName, targetUrl, statusEndpoint } = options;
7
14
  return `<!DOCTYPE html>
@@ -10,6 +17,7 @@ export function getLoadingPageHtml(options) {
10
17
  <meta charset="utf-8">
11
18
  <meta name="viewport" content="width=device-width, initial-scale=1">
12
19
  <title>Building ${escapeHtml(projectName)} — Jamdesk</title>
20
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,${encodeURIComponent(JAMDESK_MARK_SVG)}">
13
21
  <style>
14
22
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
15
23
 
@@ -140,7 +148,7 @@ export function getLoadingPageHtml(options) {
140
148
  <div class="glow"></div>
141
149
  <div class="container content">
142
150
  <div class="logo" role="img" aria-label="Jamdesk">
143
- <svg viewBox="0 0 2289 2038" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#jd)"><path d="M1129.57 1311.33L874.802 891.18C871.197 885.234 865.147 881.59 858.194 881.554C767.557 881.077 136.784 877.9 113.489 881.612C88.3871 885.611 73.8331 886.702 53.4424 900.608C33.0517 914.514 15.9545 933.404 6.68471 963.597C-2.5851 993.79 -2.17416 1016.09 7.66949 1048.58C17.5131 1081.08 524.5 1921.11 545.5 1952.61C566.5 1984.11 583.272 2010.33 616.006 2023.4C648.739 2036.48 695.461 2035.4 724.5 2023.4C753.539 2011.4 772 1979.11 791 1952.61C808.575 1928.1 1088.41 1408.59 1130.28 1330.82C1133.63 1324.58 1133.24 1317.39 1129.57 1311.33Z" fill="#D6C2FF"/><path d="M2166.5 885.611C2131.07 879.853 1511.26 884.752 1418.96 885.515C1411.63 885.576 1405.35 889.635 1401.91 896.098L809.142 2007.78C802.048 2021.08 811.223 2037.15 826.3 2037.18C987.697 2037.52 1690.61 2038.88 1710.98 2037.11C1734 2035.11 1756.5 2027.11 1774 2013.11C1791.5 1999.11 1803.5 1976.11 1821 1953.11C1838.5 1930.11 2246.48 1130.65 2259.79 1099.11C2273.1 1067.57 2286.88 1046.31 2288.29 1012.11C2289.7 977.909 2277.12 955.19 2252 926.611C2226.88 898.033 2204.79 891.835 2166.5 885.611Z" fill="#AD85FF"/><path d="M501.001 57.6116C483.367 88.4712 161.381 679.447 85.4473 818.837C78.1875 832.164 87.9925 848.112 103.169 848.112H2171.04C2186.72 848.112 2196.44 831.097 2188.15 817.777C2100.22 676.343 1733.69 87.1557 1709.5 54.6116C1682 17.6118 1638 3.11141 1593 3.11126C1548 3.11111 621.001 -3.88914 582.001 3.11146C543.001 10.112 521.001 22.6116 501.001 57.6116Z" fill="#9966FF"/></g><defs><clipPath id="jd"><rect width="2289" height="2038" fill="white"/></clipPath></defs></svg>
151
+ ${JAMDESK_MARK_SVG}
144
152
  </div>
145
153
  <h1 id="heading">Building Your Site</h1>
146
154
  <p class="project-name">${escapeHtml(projectName)}</p>
@@ -1 +1 @@
1
- {"version":3,"file":"loading-page.js","sourceRoot":"","sources":["../../src/lib/loading-page.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,UAAU,kBAAkB,CAAC,OAA2B;IAC5D,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAE3D,OAAO;;;;;oBAKW,UAAU,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAsIb,UAAU,CAAC,WAAW,CAAC;;;;;;;;;;;;;wBAa7B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;6BACpB,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAoDnD,CAAC;AACT,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"loading-page.js","sourceRoot":"","sources":["../../src/lib/loading-page.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,wnDAAwnD,CAAC;AAElpD,MAAM,UAAU,kBAAkB,CAAC,OAA2B;IAC5D,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAE3D,OAAO;;;;;oBAKW,UAAU,CAAC,WAAW,CAAC;mEACwB,kBAAkB,CAAC,gBAAgB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAmI/F,gBAAgB;;;8BAGM,UAAU,CAAC,WAAW,CAAC;;;;;;;;;;;;;wBAa7B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;6BACpB,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAoDnD,CAAC;AACT,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
@@ -8,6 +8,12 @@ declare function header(text: string): void;
8
8
  declare function warn(text: string): void;
9
9
  declare function info(text: string): void;
10
10
  declare function hint(text: string): void;
11
+ /**
12
+ * Suggestion headline — softer than warn (cyan ◆, not yellow ⚠).
13
+ * BMP glyph on purpose, NOT an emoji: Windows conhost renders astral-plane
14
+ * emoji (💡) inconsistently, and this CLI has dedicated Windows CI.
15
+ */
16
+ declare function suggest(text: string): void;
11
17
  /**
12
18
  * A blank-line-framed group of hint lines, printed under a headline a spinner
13
19
  * already failed (`spin.fail(...)`). An empty string is a blank separator.
@@ -30,6 +36,7 @@ export declare const output: {
30
36
  info: typeof info;
31
37
  hint: typeof hint;
32
38
  hintBlock: typeof hintBlock;
39
+ suggest: typeof suggest;
33
40
  migrationNotice: typeof migrationNotice;
34
41
  spinner: typeof spinner;
35
42
  check: typeof success;
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AACA,OAAY,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAEpC,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEnC;AAED,iBAAS,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAajC;AAED,uEAAuE;AACvE,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAElC;AAED,YAAY,EAAE,GAAG,EAAE,CAAC;AAEpB,iBAAS,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAElC;AAED,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEhC;AAED,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEhC;AAED,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEhC;AAED;;;;;GAKG;AACH,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAOxC;AAED;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAO5C;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;CAYlB,CAAC"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AACA,OAAY,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAEpC,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEnC;AAED,iBAAS,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAajC;AAED,uEAAuE;AACvE,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAElC;AAED,YAAY,EAAE,GAAG,EAAE,CAAC;AAEpB,iBAAS,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAElC;AAED,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEhC;AAED,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEhC;AAED,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEhC;AAED;;;;GAIG;AACH,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEnC;AAED;;;;;GAKG;AACH,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAOxC;AAED;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAO5C;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;CAalB,CAAC"}
@@ -34,6 +34,14 @@ function info(text) {
34
34
  function hint(text) {
35
35
  console.log(chalk.gray(` ${text}`));
36
36
  }
37
+ /**
38
+ * Suggestion headline — softer than warn (cyan ◆, not yellow ⚠).
39
+ * BMP glyph on purpose, NOT an emoji: Windows conhost renders astral-plane
40
+ * emoji (💡) inconsistently, and this CLI has dedicated Windows CI.
41
+ */
42
+ function suggest(text) {
43
+ console.log(chalk.cyan(`◆ ${text}`));
44
+ }
37
45
  /**
38
46
  * A blank-line-framed group of hint lines, printed under a headline a spinner
39
47
  * already failed (`spin.fail(...)`). An empty string is a blank separator.
@@ -72,6 +80,7 @@ export const output = {
72
80
  info,
73
81
  hint,
74
82
  hintBlock,
83
+ suggest,
75
84
  migrationNotice,
76
85
  spinner,
77
86
  check: success,
@@ -1 +1 @@
1
- {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,14 @@ export declare function checkForSecrets(files: string[]): string[];
15
15
  * Applies .gitignore rules and always-excluded patterns.
16
16
  */
17
17
  export declare function collectFiles(dir: string): Promise<string[]>;
18
+ /**
19
+ * Count content MDX pages in a collected file list. Mirrors the build
20
+ * service's collectMdxFiles() semantics (glob '**\/*.mdx' ignoring
21
+ * snippets\/**; collectFiles already excludes node_modules and .git).
22
+ * A deploy with zero content MDX publishes a site with no pages — the
23
+ * 2026-06-09 empty-manifest prune-skip incident shipped exactly that.
24
+ */
25
+ export declare function countContentMdx(files: string[]): number;
18
26
  export interface TarballResult {
19
27
  buffer: Buffer;
20
28
  fileCount: number;
@@ -1 +1 @@
1
- {"version":3,"file":"tarball.d.ts","sourceRoot":"","sources":["../../src/lib/tarball.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoCH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAKzD;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAqCjE;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAuCvE"}
1
+ {"version":3,"file":"tarball.d.ts","sourceRoot":"","sources":["../../src/lib/tarball.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoCH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAKzD;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAqCjE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAKvD;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAuCvE"}
@@ -86,6 +86,19 @@ export async function collectFiles(dir) {
86
86
  await walk(dir);
87
87
  return files.sort();
88
88
  }
89
+ /**
90
+ * Count content MDX pages in a collected file list. Mirrors the build
91
+ * service's collectMdxFiles() semantics (glob '**\/*.mdx' ignoring
92
+ * snippets\/**; collectFiles already excludes node_modules and .git).
93
+ * A deploy with zero content MDX publishes a site with no pages — the
94
+ * 2026-06-09 empty-manifest prune-skip incident shipped exactly that.
95
+ */
96
+ export function countContentMdx(files) {
97
+ return files.filter((file) => {
98
+ const posixPath = file.split(/[\\/]/).join('/');
99
+ return posixPath.endsWith('.mdx') && !posixPath.startsWith('snippets/');
100
+ }).length;
101
+ }
89
102
  /**
90
103
  * Create a gzipped tarball of the docs directory.
91
104
  * Respects .gitignore and excludes sensitive files.
@@ -1 +1 @@
1
- {"version":3,"file":"tarball.js","sourceRoot":"","sources":["../../src/lib/tarball.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tarball.js","sourceRoot":"","sources":["../../src/lib/tarball.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.136",
3
+ "version": "1.1.138",
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",
@@ -7,6 +7,14 @@ export function generateErrorRef(buildId: string): string {
7
7
  return `ERR-${buildId.substring(0, 8).toUpperCase()}`;
8
8
  }
9
9
 
10
+ /**
11
+ * Stable prefix of the 0-MDX guard error thrown in build.ts (copy_content
12
+ * phase). Defined here — not in build.ts — so build.ts imports it without
13
+ * creating an import cycle; the parser branch below matches on it, so the
14
+ * throw and the matcher can never drift apart.
15
+ */
16
+ export const NO_MDX_CONTENT_ERROR = 'No MDX content files found';
17
+
10
18
  /**
11
19
  * Extract file path from error output using various patterns.
12
20
  * @param pageToFileMap - Optional mapping of URL paths to MDX files
@@ -140,6 +148,31 @@ export function parseErrorDetails(
140
148
  };
141
149
  }
142
150
 
151
+ // Empty-content guard (thrown in build.ts copy_content phase): the build
152
+ // collected zero .mdx files. Matched on the guard's stable message prefix.
153
+ // The --allow-empty hint is CLI-only: git-build owners never ran `jamdesk
154
+ // deploy` and CANNOT pass the flag (queueBuild scopes allowEmpty to
155
+ // tarball builds), and old CLI versions reject it as an unknown option —
156
+ // so lead with the real fix and offer the flag only where it can work.
157
+ if (message.includes(NO_MDX_CONTENT_ERROR)) {
158
+ const escapeHint = repoFullName
159
+ ? 'If you believe an empty site is intentional, contact support@jamdesk.com.'
160
+ : 'If an empty deploy is intentional, update to the latest CLI (`npm i -g jamdesk`) and re-run with `jamdesk deploy --allow-empty`.';
161
+ return {
162
+ type: 'config_error',
163
+ message: 'No content pages found',
164
+ details: message,
165
+ suggestion:
166
+ 'The build collected zero .mdx content files, so it would publish an empty site.\n\n' +
167
+ 'Fix: add at least one .mdx page and rebuild.\n\n' +
168
+ 'Common causes:\n' +
169
+ '• Running `jamdesk deploy` from the wrong directory (no .mdx files next to docs.json)\n' +
170
+ '• A docs path setting pointing at a folder without MDX content\n' +
171
+ '• Pages excluded by .gitignore\n\n' +
172
+ escapeHint,
173
+ };
174
+ }
175
+
143
176
  // Configuration validation errors (from validate phase)
144
177
  if (message.includes('Missing docs.json') || message.includes('Invalid docs.json')) {
145
178
  const monorepoMatch = message.match(/Missing docs.json at '([^']+)'/);
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Config Suggestions
3
+ *
4
+ * Detects optional-but-recommended docs.json fields the customer hasn't set
5
+ * (favicon, description, logo) and returns suggestion-tier BuildWarnings
6
+ * (types in SUGGESTION_TYPES — see shared/status-reporter.ts for how each
7
+ * surface treats them; never emailed, excluded from warningCount).
8
+ *
9
+ * Distinct from validate-branding-assets.ts: that warns when a field IS set
10
+ * but the referenced file is missing; this suggests when the field is absent.
11
+ *
12
+ * The CLI has an independent twin (cli/src/lib/config-suggestions.ts) —
13
+ * same precedent as validate-branding.ts. Keep messages/links aligned.
14
+ */
15
+
16
+ import type { DocsConfig } from './docs-types.js';
17
+ import type { BuildWarning } from '../shared/status-reporter.js';
18
+
19
+ const BRANDING_DOCS_URL = 'https://jamdesk.com/docs/customization/branding';
20
+
21
+ type ImageValue = string | { light?: string; dark?: string; href?: string } | undefined;
22
+
23
+ /** A branding image field counts as set when it carries at least one path. */
24
+ function hasImageValue(value: ImageValue): boolean {
25
+ if (!value) return false;
26
+ if (typeof value === 'string') return value.trim() !== '';
27
+ return Boolean(value.light?.trim() || value.dark?.trim());
28
+ }
29
+
30
+ interface SuggestionDef {
31
+ type: BuildWarning['type'];
32
+ message: string;
33
+ link: string;
34
+ isMissing: (config: DocsConfig) => boolean;
35
+ }
36
+
37
+ const SUGGESTION_DEFS: SuggestionDef[] = [
38
+ {
39
+ type: 'missing_favicon',
40
+ message: 'Add a favicon so browser tabs and bookmarks show your brand icon instead of a generic page icon.',
41
+ link: `${BRANDING_DOCS_URL}#favicon`,
42
+ isMissing: (c) => !hasImageValue(c.favicon as ImageValue),
43
+ },
44
+ {
45
+ type: 'missing_description',
46
+ message: 'Add a site description to improve how your docs appear in search results and social shares.',
47
+ // #site-name is deliberate: description is documented inside the docs
48
+ // page's "Site Name" section (no #description anchor exists yet).
49
+ link: `${BRANDING_DOCS_URL}#site-name`,
50
+ isMissing: (c) => !c.description || c.description.trim() === '',
51
+ },
52
+ {
53
+ type: 'missing_logo',
54
+ message: 'Add a logo so your site header shows your brand instead of the default Jamdesk logo.',
55
+ link: `${BRANDING_DOCS_URL}#logo`,
56
+ isMissing: (c) => !hasImageValue(c.logo as ImageValue),
57
+ },
58
+ ];
59
+
60
+ /**
61
+ * Suggest optional config improvements. Bounded output (max 3 entries), so
62
+ * it can be appended to allWarnings without interacting with per-type caps.
63
+ */
64
+ export function suggestConfigImprovements(config: DocsConfig): BuildWarning[] {
65
+ return SUGGESTION_DEFS.filter((def) => def.isMissing(config)).map((def) => ({
66
+ type: def.type,
67
+ file: 'docs.json',
68
+ message: def.message,
69
+ link: def.link,
70
+ }));
71
+ }
@@ -87,7 +87,7 @@ export async function deleteRemovedR2Objects(
87
87
  * cached-redis.ts: accepts any truthy value except '0'/'false'. Trims because
88
88
  * Vercel/Cloud Run env values can pick up trailing whitespace.
89
89
  */
90
- function envFlag(name: string): boolean {
90
+ export function envFlag(name: string): boolean {
91
91
  const v = process.env[name]?.trim().toLowerCase();
92
92
  if (!v) return false;
93
93
  return v !== '0' && v !== 'false';
@@ -120,9 +120,25 @@ export interface PrunableManifest {
120
120
 
121
121
  export type PruneSkipReason = 'disabled' | 'empty-manifest' | 'mass-delete' | 'stale-build';
122
122
 
123
+ /**
124
+ * Per-category context for a skipped prune, logged for instant triage — the
125
+ * 2026-06-10 mass-delete investigation needed manifest archaeology to recover
126
+ * these numbers. removedCounts = keys that WOULD have been deleted
127
+ * (post-exclusion); only computable for mass-delete, which runs after the
128
+ * key diff. Timestamps only for stale-build.
129
+ */
130
+ export interface PruneSkipDetail {
131
+ oldCounts: { content: number; snippets: number; openapi: number };
132
+ newCounts: { content: number; snippets: number; openapi: number };
133
+ removedCounts?: { content: number; snippets: number; openapi: number };
134
+ oldCommitTimestamp?: string;
135
+ newCommitTimestamp?: string;
136
+ }
137
+
123
138
  export interface ContentPruneResult extends CleanupResult {
124
139
  skipped: boolean;
125
140
  skipReason?: PruneSkipReason;
141
+ skipDetail?: PruneSkipDetail;
126
142
  }
127
143
 
128
144
  /**
@@ -204,6 +220,12 @@ async function batchDeleteKeys(keys: string[]): Promise<CleanupResult> {
204
220
  return { deleted, failed };
205
221
  }
206
222
 
223
+ const categoryCounts = (m: PrunableManifest) => ({
224
+ content: Object.keys(m.files ?? {}).length,
225
+ snippets: Object.keys(m.snippets ?? {}).length,
226
+ openapi: Object.keys(m.openapi ?? {}).length,
227
+ });
228
+
207
229
  /**
208
230
  * Prune content MDX, snippets, and OpenAPI specs that the new build dropped.
209
231
  *
@@ -245,7 +267,13 @@ export async function pruneRemovedContent(
245
267
  return { deleted: [], failed: [], skipped: true, skipReason: 'disabled' };
246
268
  }
247
269
  if (Object.keys(newManifest.files ?? {}).length === 0) {
248
- return { deleted: [], failed: [], skipped: true, skipReason: 'empty-manifest' };
270
+ return {
271
+ deleted: [],
272
+ failed: [],
273
+ skipped: true,
274
+ skipReason: 'empty-manifest',
275
+ skipDetail: { oldCounts: categoryCounts(oldManifest), newCounts: categoryCounts(newManifest) },
276
+ };
249
277
  }
250
278
 
251
279
  // Stale-build guard: skip the prune when THIS build's commit is strictly OLDER
@@ -273,7 +301,18 @@ export async function pruneRemovedContent(
273
301
  const bothParseable = !Number.isNaN(newCommitMs) && !Number.isNaN(oldCommitMs);
274
302
  const neitherFutureDated = newCommitMs <= nowMs && oldCommitMs <= nowMs;
275
303
  if (bothParseable && neitherFutureDated && newCommitMs < oldCommitMs) {
276
- return { deleted: [], failed: [], skipped: true, skipReason: 'stale-build' };
304
+ return {
305
+ deleted: [],
306
+ failed: [],
307
+ skipped: true,
308
+ skipReason: 'stale-build',
309
+ skipDetail: {
310
+ oldCounts: categoryCounts(oldManifest),
311
+ newCounts: categoryCounts(newManifest),
312
+ oldCommitTimestamp: oldManifest.commitTimestamp,
313
+ newCommitTimestamp: newManifest.commitTimestamp,
314
+ },
315
+ };
277
316
  }
278
317
 
279
318
  // Never prune a snippet still imported by surviving content, nor any key that
@@ -309,7 +348,21 @@ export async function pruneRemovedContent(
309
348
  isMassDelete(snippetKeys.length, Object.keys(oldManifest.snippets ?? {}).length) ||
310
349
  isMassDelete(openapiKeys.length, Object.keys(oldManifest.openapi ?? {}).length);
311
350
  if (massDelete && !envFlag('JD_PRUNE_ALLOW_MASS')) {
312
- return { deleted: [], failed: [], skipped: true, skipReason: 'mass-delete' };
351
+ return {
352
+ deleted: [],
353
+ failed: [],
354
+ skipped: true,
355
+ skipReason: 'mass-delete',
356
+ skipDetail: {
357
+ oldCounts: categoryCounts(oldManifest),
358
+ newCounts: categoryCounts(newManifest),
359
+ removedCounts: {
360
+ content: contentKeys.length,
361
+ snippets: snippetKeys.length,
362
+ openapi: openapiKeys.length,
363
+ },
364
+ },
365
+ };
313
366
  }
314
367
 
315
368
  const { deleted, failed } = await batchDeleteKeys([...contentKeys, ...snippetKeys, ...openapiKeys]);
@@ -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' | 'missing_page' | 'missing_openapi_ref' | 'inline_code_on_api_page' | 'invalid_openapi_spec' | 'missing_snippet' | 'risky_expression';
25
+ export type BuildWarningType = 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page' | 'missing_openapi_ref' | 'inline_code_on_api_page' | 'invalid_openapi_spec' | 'missing_snippet' | 'risky_expression' | 'missing_favicon' | 'missing_description' | 'missing_logo';
26
26
 
27
27
  /** Build warning structure */
28
28
  export interface BuildWarning {
@@ -33,6 +33,39 @@ export interface BuildWarning {
33
33
  message?: string;
34
34
  }
35
35
 
36
+ /**
37
+ * Suggestion-tier warning types — softer than warnings. A warning means
38
+ * "something you wrote is broken"; a suggestion means "your site works but
39
+ * an optional docs.json field (favicon, description, logo) is unset and
40
+ * setting it would improve branding/SEO". Suggestions ride the same
41
+ * warnings[] pipeline but every surface treats them differently:
42
+ * - CLI validate/dev: separate dim "Suggestions" section
43
+ * - Dashboard BuildsList: DISPLAYED_SUGGESTION_TYPES section, excluded
44
+ * from the orange warning badge
45
+ * - Email: NEVER sent — EMAILABLE_TYPES (dashboard/functions) is a
46
+ * fail-closed allowlist that excludes them
47
+ * - warningCount (Firestore): excluded via
48
+ * countWarningsExcludingSuggestions below. NOTE: the mobile apps
49
+ * (jamdesk-mobile, separate repo) display warningCount verbatim —
50
+ * keep the exclusion here, not in each consumer.
51
+ * `link` on a suggestion warning carries a docs URL ("Learn more"), not a
52
+ * broken link target.
53
+ */
54
+ export const SUGGESTION_TYPES: ReadonlySet<BuildWarningType> = new Set([
55
+ 'missing_favicon',
56
+ 'missing_description',
57
+ 'missing_logo',
58
+ ]);
59
+
60
+ /**
61
+ * Warning count for user-facing "N warnings" displays (Firestore
62
+ * warningCount, editor toast). Suggestions are excluded so a build whose
63
+ * only findings are unset optional config doesn't read as "3 warnings".
64
+ */
65
+ export function countWarningsExcludingSuggestions(warnings: BuildWarning[]): number {
66
+ return warnings.filter((w) => !SUGGESTION_TYPES.has(w.type)).length;
67
+ }
68
+
36
69
  /** File statistics from the build process */
37
70
  export interface FileStats {
38
71
  sourceFiles: number;
@@ -19,6 +19,7 @@ export interface BuildParams {
19
19
  commitSha?: string; // Optional for CLI builds
20
20
  commitMessage?: string; // Optional for CLI builds
21
21
  fullRebuild?: boolean; // If true, forces full R2 upload (ignores manifest)
22
+ allowEmpty?: boolean; // CLI --allow-empty: permit a 0-MDX build (default false)
22
23
  docsPath?: string | null; // Monorepo: path to docs.json directory (e.g., "jamdesk")
23
24
  domain?: string; // Project's custom domain or subdomain (e.g., "docs.acme.com" or "acme.jamdesk.app")
24
25
  showBranding?: boolean; // Show "Powered by Jamdesk" in footer (defaults to true)
@@ -1932,9 +1932,9 @@
1932
1932
  }
1933
1933
  },
1934
1934
  "node_modules/@types/node": {
1935
- "version": "25.9.2",
1936
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
1937
- "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
1935
+ "version": "25.9.3",
1936
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz",
1937
+ "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==",
1938
1938
  "license": "MIT",
1939
1939
  "dependencies": {
1940
1940
  "undici-types": ">=7.24.0 <7.24.7"