@shadow-shard-tools/docs-core 1.0.18 → 1.0.20

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 (144) hide show
  1. package/README.md +60 -1
  2. package/dist/cjs/configs/clientConfig.js +30 -112
  3. package/dist/cjs/configs/index.browser.js +16 -0
  4. package/dist/cjs/configs/sstDocsConfigShared.js +169 -101
  5. package/dist/cjs/data/buildTree.js +48 -10
  6. package/dist/cjs/data/fsDataProvider.browser.js +10 -0
  7. package/dist/cjs/data/index.browser.js +19 -0
  8. package/dist/cjs/data/index.js +6 -1
  9. package/dist/cjs/data/loadAllCategories.js +15 -5
  10. package/dist/cjs/data/loadAllItems.js +19 -7
  11. package/dist/cjs/data/loadProducts.js +6 -0
  12. package/dist/cjs/data/loadVersionData.fs.js +31 -0
  13. package/dist/cjs/data/loadVersionData.js +130 -7
  14. package/dist/cjs/index.browser.js +22 -0
  15. package/dist/cjs/themes/index.js +5 -1
  16. package/dist/cjs/themes/styleThemeSchema.js +36 -0
  17. package/dist/cjs/types/DataDiagnostic.js +2 -0
  18. package/dist/cjs/types/Product.js +2 -0
  19. package/dist/cjs/utilities/index.browser.js +53 -0
  20. package/dist/cjs/utilities/path/pathExists.browser.js +6 -0
  21. package/dist/cjs/utilities/path/pathExists.js +1 -1
  22. package/dist/cjs/utilities/path/resolveAgainstProjectRoot.browser.js +6 -0
  23. package/dist/cjs/utilities/path/resolveDataPath.browser.js +6 -0
  24. package/dist/cjs/utilities/path/resolveDataPath.js +1 -1
  25. package/dist/cjs/utilities/string/withBasePath.js +37 -9
  26. package/dist/cjs/utilities/system/logger.browser.js +39 -0
  27. package/dist/cjs/utilities/system/logger.js +26 -6
  28. package/dist/configs/clientConfig.d.ts.map +1 -1
  29. package/dist/configs/clientConfig.js +30 -112
  30. package/dist/configs/clientConfig.js.map +1 -1
  31. package/dist/configs/index.browser.d.ts +4 -0
  32. package/dist/configs/index.browser.d.ts.map +1 -0
  33. package/dist/configs/index.browser.js +4 -0
  34. package/dist/configs/index.browser.js.map +1 -0
  35. package/dist/configs/sstDocsConfig.browser.d.ts.map +1 -1
  36. package/dist/configs/sstDocsConfig.browser.js.map +1 -1
  37. package/dist/configs/sstDocsConfig.d.ts.map +1 -1
  38. package/dist/configs/sstDocsConfig.js.map +1 -1
  39. package/dist/configs/sstDocsConfigShared.d.ts +8 -0
  40. package/dist/configs/sstDocsConfigShared.d.ts.map +1 -1
  41. package/dist/configs/sstDocsConfigShared.js +168 -97
  42. package/dist/configs/sstDocsConfigShared.js.map +1 -1
  43. package/dist/data/buildTree.d.ts +10 -2
  44. package/dist/data/buildTree.d.ts.map +1 -1
  45. package/dist/data/buildTree.js +48 -10
  46. package/dist/data/buildTree.js.map +1 -1
  47. package/dist/data/fsDataProvider.browser.d.ts +6 -0
  48. package/dist/data/fsDataProvider.browser.d.ts.map +1 -0
  49. package/dist/data/fsDataProvider.browser.js +7 -0
  50. package/dist/data/fsDataProvider.browser.js.map +1 -0
  51. package/dist/data/fsDataProvider.d.ts.map +1 -1
  52. package/dist/data/fsDataProvider.js.map +1 -1
  53. package/dist/data/index.browser.d.ts +8 -0
  54. package/dist/data/index.browser.d.ts.map +1 -0
  55. package/dist/data/index.browser.js +8 -0
  56. package/dist/data/index.browser.js.map +1 -0
  57. package/dist/data/index.d.ts +3 -1
  58. package/dist/data/index.d.ts.map +1 -1
  59. package/dist/data/index.js +3 -1
  60. package/dist/data/index.js.map +1 -1
  61. package/dist/data/loadAllCategories.d.ts +10 -1
  62. package/dist/data/loadAllCategories.d.ts.map +1 -1
  63. package/dist/data/loadAllCategories.js +15 -5
  64. package/dist/data/loadAllCategories.js.map +1 -1
  65. package/dist/data/loadAllItems.d.ts +10 -1
  66. package/dist/data/loadAllItems.d.ts.map +1 -1
  67. package/dist/data/loadAllItems.js +19 -7
  68. package/dist/data/loadAllItems.js.map +1 -1
  69. package/dist/data/loadProducts.d.ts +4 -0
  70. package/dist/data/loadProducts.d.ts.map +1 -0
  71. package/dist/data/loadProducts.js +4 -0
  72. package/dist/data/loadProducts.js.map +1 -0
  73. package/dist/data/loadVersionData.d.ts +37 -1
  74. package/dist/data/loadVersionData.d.ts.map +1 -1
  75. package/dist/data/loadVersionData.fs.d.ts +3 -0
  76. package/dist/data/loadVersionData.fs.d.ts.map +1 -0
  77. package/dist/data/loadVersionData.fs.js +26 -0
  78. package/dist/data/loadVersionData.fs.js.map +1 -0
  79. package/dist/data/loadVersionData.js +127 -7
  80. package/dist/data/loadVersionData.js.map +1 -1
  81. package/dist/index.browser.d.ts +7 -0
  82. package/dist/index.browser.d.ts.map +1 -0
  83. package/dist/index.browser.js +7 -0
  84. package/dist/index.browser.js.map +1 -0
  85. package/dist/themes/index.d.ts +1 -0
  86. package/dist/themes/index.d.ts.map +1 -1
  87. package/dist/themes/index.js +1 -0
  88. package/dist/themes/index.js.map +1 -1
  89. package/dist/themes/styleThemeSchema.d.ts +6 -0
  90. package/dist/themes/styleThemeSchema.d.ts.map +1 -0
  91. package/dist/themes/styleThemeSchema.js +32 -0
  92. package/dist/themes/styleThemeSchema.js.map +1 -0
  93. package/dist/themes/themeRegistry.d.ts.map +1 -1
  94. package/dist/themes/themeRegistry.js.map +1 -1
  95. package/dist/types/ClientVisibleSstDocsConfig.d.ts +1 -0
  96. package/dist/types/ClientVisibleSstDocsConfig.d.ts.map +1 -1
  97. package/dist/types/DataDiagnostic.d.ts +8 -0
  98. package/dist/types/DataDiagnostic.d.ts.map +1 -0
  99. package/dist/types/DataDiagnostic.js +2 -0
  100. package/dist/types/DataDiagnostic.js.map +1 -0
  101. package/dist/types/HtmlGeneratorSettings.d.ts +1 -1
  102. package/dist/types/HtmlGeneratorSettings.d.ts.map +1 -1
  103. package/dist/types/Product.d.ts +5 -0
  104. package/dist/types/Product.d.ts.map +1 -0
  105. package/dist/types/Product.js +2 -0
  106. package/dist/types/Product.js.map +1 -0
  107. package/dist/types/SstDocsConfig.d.ts +2 -0
  108. package/dist/types/SstDocsConfig.d.ts.map +1 -1
  109. package/dist/types/index.d.ts +2 -0
  110. package/dist/types/index.d.ts.map +1 -1
  111. package/dist/utilities/index.browser.d.ts +23 -0
  112. package/dist/utilities/index.browser.d.ts.map +1 -0
  113. package/dist/utilities/index.browser.js +29 -0
  114. package/dist/utilities/index.browser.js.map +1 -0
  115. package/dist/utilities/path/pathExists.browser.d.ts +2 -0
  116. package/dist/utilities/path/pathExists.browser.d.ts.map +1 -0
  117. package/dist/utilities/path/pathExists.browser.js +4 -0
  118. package/dist/utilities/path/pathExists.browser.js.map +1 -0
  119. package/dist/utilities/path/pathExists.js +1 -1
  120. package/dist/utilities/path/pathExists.js.map +1 -1
  121. package/dist/utilities/path/resolveAgainstProjectRoot.browser.d.ts +2 -0
  122. package/dist/utilities/path/resolveAgainstProjectRoot.browser.d.ts.map +1 -0
  123. package/dist/utilities/path/resolveAgainstProjectRoot.browser.js +4 -0
  124. package/dist/utilities/path/resolveAgainstProjectRoot.browser.js.map +1 -0
  125. package/dist/utilities/path/resolveAgainstProjectRoot.d.ts.map +1 -1
  126. package/dist/utilities/path/resolveAgainstProjectRoot.js.map +1 -1
  127. package/dist/utilities/path/resolveDataPath.browser.d.ts +2 -0
  128. package/dist/utilities/path/resolveDataPath.browser.d.ts.map +1 -0
  129. package/dist/utilities/path/resolveDataPath.browser.js +4 -0
  130. package/dist/utilities/path/resolveDataPath.browser.js.map +1 -0
  131. package/dist/utilities/path/resolveDataPath.d.ts.map +1 -1
  132. package/dist/utilities/path/resolveDataPath.js +1 -1
  133. package/dist/utilities/path/resolveDataPath.js.map +1 -1
  134. package/dist/utilities/string/withBasePath.d.ts.map +1 -1
  135. package/dist/utilities/string/withBasePath.js +37 -6
  136. package/dist/utilities/string/withBasePath.js.map +1 -1
  137. package/dist/utilities/system/logger.browser.d.ts +3 -0
  138. package/dist/utilities/system/logger.browser.d.ts.map +1 -0
  139. package/dist/utilities/system/logger.browser.js +36 -0
  140. package/dist/utilities/system/logger.browser.js.map +1 -0
  141. package/dist/utilities/system/logger.d.ts.map +1 -1
  142. package/dist/utilities/system/logger.js +26 -6
  143. package/dist/utilities/system/logger.js.map +1 -1
  144. package/package.json +181 -9
package/README.md CHANGED
@@ -25,11 +25,69 @@ import { CODE_LANGUAGE_CONFIG } from "@shadow-shard-tools/docs-core/configs/inde
25
25
  Everything ships as native ESM with TypeScript declarations in `dist/`. Install with `npm install @shadow-shard-tools/docs-core` (or link locally) and cherry-pick the helpers and contracts you need.
26
26
  CommonJS consumers can `require` the same subpaths via the conditional exports (built into `dist/cjs`).
27
27
 
28
+ ## API map (common imports)
29
+ | Domain | Import from | Common exports |
30
+ | --- | --- | --- |
31
+ | Configs | `@shadow-shard-tools/docs-core/configs` | `loadSstDocsConfig`, `buildClientVisibleConfig`, `serializeClientConfigForBrowser`, `exposeClientConfig`, `readClientConfig`, `CODE_LANGUAGE_CONFIG` |
32
+ | Data | `@shadow-shard-tools/docs-core/data` | `loadVersions`, `loadVersionData`, `buildTree`, `FsDataProvider`, `HttpDataProvider` |
33
+ | Themes | `@shadow-shard-tools/docs-core/themes` | `defaultTheme`, `getThemePreset`, `AVAILABLE_THEME_PRESET_NAMES`, `StyleTheme` |
34
+ | Utilities | `@shadow-shard-tools/docs-core/utilities` | `slugify`, `withBasePath`, `normalizeSystemPath`, `isValidImageUrl`, `createLogger` |
35
+ | Types | `@shadow-shard-tools/docs-core/types` | `Content`, `Version`, `StyleTheme`, `SstDocsConfigFile`, `ResolvedSstDocsConfig`, `ClientVisibleSstDocsConfig` |
36
+
37
+ ## Quickstart recipes
38
+ ### Load config and expose client config
39
+ ```ts
40
+ import {
41
+ buildClientVisibleConfig,
42
+ exposeClientConfig,
43
+ loadSstDocsConfig,
44
+ readClientConfig,
45
+ serializeClientConfigForBrowser,
46
+ } from "@shadow-shard-tools/docs-core/configs";
47
+
48
+ const config = await loadSstDocsConfig();
49
+ const clientConfig = buildClientVisibleConfig(config);
50
+
51
+ // Server-side: embed into HTML (pretty for readability while debugging)
52
+ const clientScript = serializeClientConfigForBrowser(clientConfig, { pretty: true });
53
+ // -> <script>{clientScript}</script>
54
+
55
+ // Browser-side: attach and read from the global name (__SST_DOCS_CONFIG__ by default)
56
+ exposeClientConfig(clientConfig);
57
+ const cfg = readClientConfig();
58
+ ```
59
+
60
+ ### Load docs from FS/HTTP and build a tree
61
+ ```ts
62
+ import path from "node:path";
63
+ import {
64
+ FsDataProvider,
65
+ HttpDataProvider,
66
+ loadVersionData,
67
+ loadVersions,
68
+ } from "@shadow-shard-tools/docs-core/data";
69
+
70
+ // Local filesystem
71
+ const fsProvider = new FsDataProvider();
72
+ const dataRoot = path.resolve("./public/SST-Docs/data");
73
+ const versions = await loadVersions(fsProvider, dataRoot);
74
+ const versionRoot = `${dataRoot}/${versions[0]?.version ?? "current"}`;
75
+ const { tree, items, standaloneDocs } = await loadVersionData(fsProvider, versionRoot);
76
+
77
+ // Remote HTTP (e.g., CDN-hosted data)
78
+ const httpProvider = new HttpDataProvider();
79
+ const remoteRoot = "https://cdn.example.com/SST-Docs/data";
80
+ const remoteVersions = await loadVersions(httpProvider, remoteRoot);
81
+ const remoteVersionRoot = `${remoteRoot}/${remoteVersions[0]?.version ?? "current"}`;
82
+ const { tree: remoteTree } = await loadVersionData(httpProvider, remoteVersionRoot);
83
+ ```
84
+
28
85
  ### Config example
29
- Create `sst-docs.config.json` in your project root:
86
+ Create `sst-docs.config.json` in your project root. Set `PRODUCT_VERSIONING` to `true` when your data root contains product subfolders (see below); leave it `false` to use the single-product layout.
30
87
  ```json
31
88
  {
32
89
  "FS_DATA_PATH": "./public/SST-Docs/data",
90
+ "PRODUCT_VERSIONING": true,
33
91
  "HEADER_BRANDING": {
34
92
  "logoText": "SST Docs"
35
93
  },
@@ -40,6 +98,7 @@ Create `sst-docs.config.json` in your project root:
40
98
  }
41
99
  }
42
100
  ```
101
+ When product versioning is enabled, place a `products.json` file at the data root and nest per-product versions under each product folder, e.g. `public/SST-Docs/data/Product_1/versions.json` and `public/SST-Docs/data/Product_1/v1.0/index.json`.
43
102
  Load and expose a browser-friendly subset:
44
103
  ```ts
45
104
  import { loadSstDocsConfig, buildClientVisibleConfig, serializeClientConfigForBrowser } from "@shadow-shard-tools/docs-core/configs";
@@ -5,10 +5,30 @@ exports.buildClientVisibleConfig = buildClientVisibleConfig;
5
5
  exports.serializeClientConfigForBrowser = serializeClientConfigForBrowser;
6
6
  exports.exposeClientConfig = exposeClientConfig;
7
7
  exports.readClientConfig = readClientConfig;
8
+ const zod_1 = require("zod");
9
+ const styleThemeSchema_js_1 = require("../themes/styleThemeSchema.js");
8
10
  exports.DEFAULT_CLIENT_CONFIG_GLOBAL = "__SST_DOCS_CONFIG__";
11
+ const headerBrandingSchema = zod_1.z
12
+ .object({
13
+ logoSrc: zod_1.z.string().optional(),
14
+ logoAlt: zod_1.z.string().optional(),
15
+ logoText: zod_1.z.string().optional(),
16
+ })
17
+ .strict()
18
+ .optional()
19
+ .default({});
20
+ const clientConfigSchema = zod_1.z
21
+ .object({
22
+ PUBLIC_DATA_PATH: zod_1.z.string(),
23
+ PRODUCT_VERSIONING: zod_1.z.boolean().default(false),
24
+ HEADER_BRANDING: headerBrandingSchema,
25
+ HTML_GENERATOR_THEME: styleThemeSchema_js_1.styleThemeSchema.optional(),
26
+ })
27
+ .strict();
9
28
  function buildClientVisibleConfig(config) {
10
29
  return {
11
30
  PUBLIC_DATA_PATH: config.PUBLIC_DATA_PATH,
31
+ PRODUCT_VERSIONING: config.PRODUCT_VERSIONING,
12
32
  HEADER_BRANDING: config.HEADER_BRANDING,
13
33
  HTML_GENERATOR_THEME: config.HTML_GENERATOR_SETTINGS?.THEME,
14
34
  };
@@ -39,124 +59,22 @@ function readClientConfig(globalName = exports.DEFAULT_CLIENT_CONFIG_GLOBAL) {
39
59
  return assertClientConfig(value, globalName);
40
60
  }
41
61
  function normalizeAndValidateClientConfigSource(source) {
42
- const normalized = isClientConfig(source)
62
+ const normalized = isClientConfigShape(source)
43
63
  ? source
44
64
  : buildClientVisibleConfig(source);
45
65
  return assertClientConfig(normalized);
46
66
  }
47
67
  function assertClientConfig(value, globalName = exports.DEFAULT_CLIENT_CONFIG_GLOBAL) {
48
- if (!isClientConfig(value)) {
49
- throw new Error(`Client config "${globalName}" is missing or malformed on globalThis.`);
50
- }
51
- return value;
52
- }
53
- function isClientConfig(value) {
54
- if (!value || typeof value !== "object") {
55
- return false;
56
- }
57
- const candidate = value;
58
- const hasPublicPath = typeof candidate.PUBLIC_DATA_PATH === "string";
59
- const hasBranding = isHeaderBranding(candidate.HEADER_BRANDING);
60
- const hasTheme = candidate.HTML_GENERATOR_THEME === undefined ||
61
- isStyleTheme(candidate.HTML_GENERATOR_THEME);
62
- return hasPublicPath && hasBranding && hasTheme;
63
- }
64
- function isHeaderBranding(value) {
65
- if (value === undefined)
66
- return true;
67
- if (!value || typeof value !== "object")
68
- return false;
69
- const record = value;
70
- return (["logoSrc", "logoAlt", "logoText"].every((key) => key in record ? typeof record[key] === "string" || record[key] === undefined : true));
68
+ const parsed = clientConfigSchema.safeParse(value);
69
+ if (parsed.success)
70
+ return parsed.data;
71
+ const message = parsed.error.issues
72
+ .map((issue) => `${issue.path.join(".") || "config"}: ${issue.message}`)
73
+ .join("; ");
74
+ throw new Error(`Client config "${globalName}" is missing or malformed on globalThis. ${message}`);
71
75
  }
72
- function isStyleTheme(value) {
76
+ function isClientConfigShape(value) {
73
77
  if (!value || typeof value !== "object")
74
78
  return false;
75
- const theme = value;
76
- const stringProps = (obj, keys) => !!obj &&
77
- typeof obj === "object" &&
78
- keys.every((k) => typeof obj[k] === "string");
79
- return (typeof theme.input === "string" &&
80
- stringProps(theme.text, [
81
- "logoText",
82
- "documentTitle",
83
- "breadcrumb",
84
- "titleLevel1",
85
- "titleLevel2",
86
- "titleLevel3",
87
- "titleAnchor",
88
- "general",
89
- "alternative",
90
- "list",
91
- "math",
92
- ]) &&
93
- stringProps(theme.hints, ["text", "key"]) &&
94
- stringProps(theme.divider, ["border", "gradient", "text"]) &&
95
- stringProps(theme.buttons, ["common", "small", "tabSmall", "tabSmallActive"]) &&
96
- stringProps(theme.dropdown, ["container", "item", "itemActive"]) &&
97
- stringProps(theme.messageBox, [
98
- "info",
99
- "warning",
100
- "error",
101
- "success",
102
- "neutral",
103
- "quote",
104
- ]) &&
105
- stringProps(theme.table, ["cornerCell", "headers", "rows", "border", "empty"]) &&
106
- stringProps(theme.code, ["header", "language", "lines", "empty"]) &&
107
- stringProps(theme.audioPlayer, [
108
- "container",
109
- "playButton",
110
- "time",
111
- "slider",
112
- "sliderThumb",
113
- "sliderTrackColor",
114
- "sliderFillColor",
115
- ]) &&
116
- stringProps(theme.chart, [
117
- "legendLabelColor",
118
- "tooltipBg",
119
- "tooltipTitleColor",
120
- "tooltipBodyColor",
121
- "tooltipBorderColor",
122
- "axisTickColor",
123
- "gridLineColor",
124
- ]) &&
125
- stringProps(theme.graph, ["background", "defaultCurve"]) &&
126
- stringProps(theme.searchModal, [
127
- "resultBackground",
128
- "resultEmptyInputText",
129
- "resultNoResultText",
130
- "header",
131
- "footer",
132
- "borders",
133
- "item",
134
- "selectedItem",
135
- "itemHeaderText",
136
- "itemFoundSectionText",
137
- "itemTags",
138
- ]) &&
139
- stringProps(theme.navigation, [
140
- "row",
141
- "rowActive",
142
- "rowFocused",
143
- "rowHover",
144
- "hideOrShowHintsText",
145
- ]) &&
146
- stringProps(theme.category, [
147
- "empty",
148
- "cardBody",
149
- "cardHeaderText",
150
- "cardDescriptionText",
151
- ]) &&
152
- stringProps(theme.sections, [
153
- "siteBackground",
154
- "siteBorders",
155
- "headerBackground",
156
- "headerMobileBackground",
157
- "sidebarBackground",
158
- "documentHeaderBackground",
159
- "contentBackground",
160
- ]) &&
161
- stringProps(theme.header, ["mobileNavigationToggle", "mobileMenuToggle"]));
79
+ return clientConfigSchema.safeParse(value).success;
162
80
  }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeClientConfigForBrowser = exports.readClientConfig = exports.exposeClientConfig = exports.DEFAULT_CLIENT_CONFIG_GLOBAL = exports.buildClientVisibleConfig = exports.SST_DOCS_CONFIG_FILENAME = exports.SstDocsConfigError = exports.resolvePublicDataPath = exports.derivePublicDataPath = exports.CODE_LANGUAGE_CONFIG = void 0;
4
+ var codeLanguagesConfig_js_1 = require("./codeLanguagesConfig.js");
5
+ Object.defineProperty(exports, "CODE_LANGUAGE_CONFIG", { enumerable: true, get: function () { return codeLanguagesConfig_js_1.CODE_LANGUAGE_CONFIG; } });
6
+ var sstDocsConfigShared_js_1 = require("./sstDocsConfigShared.js");
7
+ Object.defineProperty(exports, "derivePublicDataPath", { enumerable: true, get: function () { return sstDocsConfigShared_js_1.derivePublicDataPath; } });
8
+ Object.defineProperty(exports, "resolvePublicDataPath", { enumerable: true, get: function () { return sstDocsConfigShared_js_1.resolvePublicDataPath; } });
9
+ Object.defineProperty(exports, "SstDocsConfigError", { enumerable: true, get: function () { return sstDocsConfigShared_js_1.SstDocsConfigError; } });
10
+ Object.defineProperty(exports, "SST_DOCS_CONFIG_FILENAME", { enumerable: true, get: function () { return sstDocsConfigShared_js_1.SST_DOCS_CONFIG_FILENAME; } });
11
+ var clientConfig_js_1 = require("./clientConfig.js");
12
+ Object.defineProperty(exports, "buildClientVisibleConfig", { enumerable: true, get: function () { return clientConfig_js_1.buildClientVisibleConfig; } });
13
+ Object.defineProperty(exports, "DEFAULT_CLIENT_CONFIG_GLOBAL", { enumerable: true, get: function () { return clientConfig_js_1.DEFAULT_CLIENT_CONFIG_GLOBAL; } });
14
+ Object.defineProperty(exports, "exposeClientConfig", { enumerable: true, get: function () { return clientConfig_js_1.exposeClientConfig; } });
15
+ Object.defineProperty(exports, "readClientConfig", { enumerable: true, get: function () { return clientConfig_js_1.readClientConfig; } });
16
+ Object.defineProperty(exports, "serializeClientConfigForBrowser", { enumerable: true, get: function () { return clientConfig_js_1.serializeClientConfigForBrowser; } });
@@ -1,16 +1,16 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SstDocsConfigError = exports.SST_DOCS_CONFIG_FILENAME = void 0;
3
+ exports.SST_DOCS_CONFIG_REFERENCE = exports.SstDocsConfigError = exports.SST_DOCS_CONFIG_FILENAME = void 0;
7
4
  exports.parseConfigContents = parseConfigContents;
8
5
  exports.derivePublicDataPath = derivePublicDataPath;
9
6
  exports.resolvePublicDataPath = resolvePublicDataPath;
7
+ const zod_1 = require("zod");
10
8
  const themeRegistry_js_1 = require("../themes/themeRegistry.js");
11
- const node_path_1 = __importDefault(require("node:path"));
12
9
  const normalizeSystemPath_js_1 = require("../utilities/string/normalizeSystemPath.js");
13
10
  exports.SST_DOCS_CONFIG_FILENAME = "sst-docs.config.json";
11
+ const DEFAULT_FS_DATA_PATH = "./public/SST-Docs/data";
12
+ const DEFAULT_PRODUCT_VERSIONING = false;
13
+ const DEFAULT_OUTPUT_DIRECTORY = "./dist/html";
14
14
  class SstDocsConfigError extends Error {
15
15
  constructor(message) {
16
16
  super(message);
@@ -18,6 +18,119 @@ class SstDocsConfigError extends Error {
18
18
  }
19
19
  }
20
20
  exports.SstDocsConfigError = SstDocsConfigError;
21
+ exports.SST_DOCS_CONFIG_REFERENCE = [
22
+ {
23
+ key: "FS_DATA_PATH",
24
+ type: "string",
25
+ required: true,
26
+ default: DEFAULT_FS_DATA_PATH,
27
+ description: "Absolute or project-relative path to the docs data root (contains versions.json).",
28
+ },
29
+ {
30
+ key: "PRODUCT_VERSIONING",
31
+ type: "boolean",
32
+ required: false,
33
+ default: `${DEFAULT_PRODUCT_VERSIONING}`,
34
+ description: "Toggle product-level versioning for docs data.",
35
+ },
36
+ {
37
+ key: "HEADER_BRANDING.logoSrc",
38
+ type: "string",
39
+ required: false,
40
+ description: "Optional logo image path/URL shown in the header.",
41
+ },
42
+ {
43
+ key: "HEADER_BRANDING.logoAlt",
44
+ type: "string",
45
+ required: false,
46
+ description: "Optional alt text for the logo image.",
47
+ },
48
+ {
49
+ key: "HEADER_BRANDING.logoText",
50
+ type: "string",
51
+ required: false,
52
+ description: "Optional text fallback when no logo image is provided.",
53
+ },
54
+ {
55
+ key: "HTML_GENERATOR_SETTINGS.OUTPUT_DIRECTORY",
56
+ type: "string",
57
+ required: true,
58
+ default: DEFAULT_OUTPUT_DIRECTORY,
59
+ description: "Where the HTML generator writes the output bundle.",
60
+ },
61
+ {
62
+ key: "HTML_GENERATOR_SETTINGS.THEME",
63
+ type: "string",
64
+ required: true,
65
+ default: themeRegistry_js_1.DEFAULT_THEME_PRESET,
66
+ description: `Theme preset name. Options: ${themeRegistry_js_1.AVAILABLE_THEME_PRESET_NAMES.join(", ")}.`,
67
+ },
68
+ {
69
+ key: "HTML_GENERATOR_SETTINGS.SEPARATE_BUILD_FOR_HTML_GENERATOR",
70
+ type: "boolean",
71
+ required: false,
72
+ default: "false",
73
+ description: "Whether to run a separate build for the HTML generator.",
74
+ },
75
+ ];
76
+ const optionalTrimmedString = zod_1.z
77
+ .string()
78
+ .trim()
79
+ .transform((value) => (value.length > 0 ? value : undefined))
80
+ .optional();
81
+ const HeaderBrandingSchema = zod_1.z
82
+ .object({
83
+ logoSrc: optionalTrimmedString,
84
+ logoAlt: optionalTrimmedString,
85
+ logoText: optionalTrimmedString,
86
+ })
87
+ .strict()
88
+ .transform((value) => value);
89
+ const HtmlGeneratorSettingsSchema = zod_1.z
90
+ .object({
91
+ OUTPUT_DIRECTORY: zod_1.z
92
+ .string()
93
+ .trim()
94
+ .min(1, {
95
+ message: `Set HTML_GENERATOR_SETTINGS.OUTPUT_DIRECTORY (default: "${DEFAULT_OUTPUT_DIRECTORY}")`,
96
+ })
97
+ .transform((val) => (0, normalizeSystemPath_js_1.normalizeSystemPath)(val)),
98
+ THEME: zod_1.z
99
+ .string()
100
+ .trim()
101
+ .min(1, {
102
+ message: `Set HTML_GENERATOR_SETTINGS.THEME (default: "${themeRegistry_js_1.DEFAULT_THEME_PRESET}")`,
103
+ })
104
+ .transform((value, ctx) => {
105
+ const normalized = value.toLowerCase();
106
+ if (!(0, themeRegistry_js_1.isThemePresetName)(normalized)) {
107
+ const options = themeRegistry_js_1.AVAILABLE_THEME_PRESET_NAMES.join(", ");
108
+ ctx.addIssue({
109
+ code: zod_1.z.ZodIssueCode.custom,
110
+ message: `HTML_GENERATOR_SETTINGS.THEME must be one of: ${options} (default: "${themeRegistry_js_1.DEFAULT_THEME_PRESET}")`,
111
+ });
112
+ return zod_1.z.NEVER;
113
+ }
114
+ return cloneTheme((0, themeRegistry_js_1.getThemePreset)(normalized));
115
+ }),
116
+ SEPARATE_BUILD_FOR_HTML_GENERATOR: zod_1.z.boolean().default(false),
117
+ })
118
+ .strict()
119
+ .transform((value) => value);
120
+ const ConfigSchema = zod_1.z
121
+ .object({
122
+ FS_DATA_PATH: zod_1.z
123
+ .string()
124
+ .trim()
125
+ .min(1, {
126
+ message: `Provide FS_DATA_PATH (default: "${DEFAULT_FS_DATA_PATH}")`,
127
+ })
128
+ .transform((val) => (0, normalizeSystemPath_js_1.normalizeSystemPath)(val)),
129
+ PRODUCT_VERSIONING: zod_1.z.boolean().default(DEFAULT_PRODUCT_VERSIONING),
130
+ HEADER_BRANDING: HeaderBrandingSchema.optional().transform((value) => value ?? {}),
131
+ HTML_GENERATOR_SETTINGS: HtmlGeneratorSettingsSchema.optional(),
132
+ })
133
+ .strict();
21
134
  function parseConfigContents(contents, sourcePath) {
22
135
  let parsed;
23
136
  try {
@@ -26,11 +139,22 @@ function parseConfigContents(contents, sourcePath) {
26
139
  catch (error) {
27
140
  throw new SstDocsConfigError(`Failed to parse ${exports.SST_DOCS_CONFIG_FILENAME}: ${error.message}`);
28
141
  }
29
- return normalizeConfig(parsed, sourcePath);
142
+ const result = ConfigSchema.safeParse(parsed);
143
+ if (!result.success) {
144
+ throw new SstDocsConfigError(formatConfigValidationError(sourcePath, result.error));
145
+ }
146
+ const resolved = result.data;
147
+ return {
148
+ FS_DATA_PATH: resolved.FS_DATA_PATH,
149
+ PUBLIC_DATA_PATH: derivePublicDataPath(resolved.FS_DATA_PATH),
150
+ PRODUCT_VERSIONING: resolved.PRODUCT_VERSIONING,
151
+ HEADER_BRANDING: resolved.HEADER_BRANDING,
152
+ HTML_GENERATOR_SETTINGS: resolved.HTML_GENERATOR_SETTINGS,
153
+ };
30
154
  }
31
155
  function derivePublicDataPath(fsDataPath) {
32
156
  const normalizedFsPath = (0, normalizeSystemPath_js_1.normalizeSystemPath)(fsDataPath.trim());
33
- const posixPath = node_path_1.default.posix.normalize(normalizedFsPath || "/");
157
+ const posixPath = normalizePosixPath(normalizedFsPath || "/");
34
158
  const segments = posixPath.split("/").filter(Boolean);
35
159
  const publicIndex = segments.indexOf("public");
36
160
  const publicSegments = publicIndex >= 0 ? segments.slice(publicIndex + 1) : segments;
@@ -57,14 +181,14 @@ function resolvePublicDataPath(baseUrl, config) {
57
181
  }
58
182
  function normalizePublicPath(value) {
59
183
  const normalized = (0, normalizeSystemPath_js_1.normalizeSystemPath)(value.trim());
60
- const posixPath = node_path_1.default.posix.normalize(normalized || "/");
184
+ const posixPath = normalizePosixPath(normalized || "/");
61
185
  const withLeading = posixPath.startsWith("/") ? posixPath : `/${posixPath}`;
62
186
  return withLeading.endsWith("/") ? withLeading : `${withLeading}/`;
63
187
  }
64
188
  function joinUrlPaths(base, child) {
65
189
  const normalizedBase = normalizePublicPath(base);
66
190
  const normalizedChild = normalizePublicPath(child).replace(/^\/+/, "");
67
- const joined = node_path_1.default.posix.join(normalizedBase, normalizedChild);
191
+ const joined = normalizePosixPath(`${normalizedBase}${normalizedChild ? `/${normalizedChild}` : ""}`);
68
192
  return joined.endsWith("/") ? joined : `${joined}/`;
69
193
  }
70
194
  function isAbsoluteUrl(value) {
@@ -76,99 +200,43 @@ function isAbsoluteUrl(value) {
76
200
  return false;
77
201
  }
78
202
  }
79
- function normalizeConfig(value, sourcePath) {
80
- if (!value || typeof value !== "object") {
81
- throw new SstDocsConfigError(`Invalid configuration in ${sourcePath}: expected an object`);
82
- }
83
- const raw = value;
84
- if (typeof raw.FS_DATA_PATH !== "string") {
85
- throw new SstDocsConfigError(`Missing or invalid FS_DATA_PATH in ${sourcePath}`);
86
- }
87
- const fsDataPath = (0, normalizeSystemPath_js_1.normalizeSystemPath)(raw.FS_DATA_PATH.trim());
88
- if (fsDataPath.length === 0) {
89
- throw new SstDocsConfigError(`FS_DATA_PATH cannot be empty in ${sourcePath}`);
90
- }
91
- return {
92
- FS_DATA_PATH: fsDataPath,
93
- PUBLIC_DATA_PATH: derivePublicDataPath(fsDataPath),
94
- HEADER_BRANDING: normalizeHeaderBranding(raw.HEADER_BRANDING, sourcePath),
95
- HTML_GENERATOR_SETTINGS: normalizeHtmlGeneratorSettings(raw.HTML_GENERATOR_SETTINGS, sourcePath),
96
- };
97
- }
98
- function normalizeHeaderBranding(value, sourcePath) {
99
- if (value === undefined) {
100
- return {};
101
- }
102
- if (!value || typeof value !== "object") {
103
- throw new SstDocsConfigError(`HEADER_BRANDING must be an object in ${sourcePath}`);
104
- }
105
- const { logoSrc, logoAlt, logoText } = value;
106
- return {
107
- logoSrc: normalizeOptionalString(logoSrc, "logoSrc", sourcePath),
108
- logoAlt: normalizeOptionalString(logoAlt, "logoAlt", sourcePath),
109
- logoText: normalizeOptionalString(logoText, "logoText", sourcePath),
110
- };
111
- }
112
- function normalizeOptionalString(value, key, sourcePath) {
113
- if (value === undefined || value === null)
114
- return undefined;
115
- if (typeof value !== "string") {
116
- throw new SstDocsConfigError(`HEADER_BRANDING.${key} must be a string in ${sourcePath}`);
117
- }
118
- const trimmed = value.trim();
119
- return trimmed.length > 0 ? trimmed : undefined;
120
- }
121
- function normalizeHtmlGeneratorSettings(value, sourcePath) {
122
- if (value === undefined) {
123
- return undefined;
124
- }
125
- if (!value || typeof value !== "object" || Array.isArray(value)) {
126
- throw new SstDocsConfigError(`HTML_GENERATOR_SETTINGS must be an object in ${sourcePath}`);
127
- }
128
- const { OUTPUT_DIRECTORY, THEME, SEPARATE_BUILD_FOR_HTML_GENERATOR, } = value;
129
- if (typeof OUTPUT_DIRECTORY !== "string") {
130
- throw new SstDocsConfigError(`HTML_GENERATOR_SETTINGS.OUTPUT_DIRECTORY must be a string in ${sourcePath}`);
131
- }
132
- const outputDirectory = (0, normalizeSystemPath_js_1.normalizeSystemPath)(OUTPUT_DIRECTORY.trim());
133
- if (outputDirectory.length === 0) {
134
- throw new SstDocsConfigError(`HTML_GENERATOR_SETTINGS.OUTPUT_DIRECTORY cannot be empty in ${sourcePath}`);
135
- }
136
- if (THEME === undefined) {
137
- throw new SstDocsConfigError(`HTML_GENERATOR_SETTINGS.THEME must be provided in ${sourcePath}`);
138
- }
139
- if (SEPARATE_BUILD_FOR_HTML_GENERATOR !== undefined &&
140
- typeof SEPARATE_BUILD_FOR_HTML_GENERATOR !== "boolean") {
141
- throw new SstDocsConfigError(`HTML_GENERATOR_SETTINGS.SEPARATE_BUILD_FOR_HTML_GENERATOR must be a boolean in ${sourcePath}`);
142
- }
143
- const resolvedTheme = resolveThemeConfiguration(THEME, sourcePath);
144
- return {
145
- OUTPUT_DIRECTORY: outputDirectory,
146
- THEME: resolvedTheme,
147
- SEPARATE_BUILD_FOR_HTML_GENERATOR: SEPARATE_BUILD_FOR_HTML_GENERATOR ?? false,
148
- };
203
+ function cloneTheme(theme) {
204
+ return JSON.parse(JSON.stringify(theme));
149
205
  }
150
- function resolveThemeConfiguration(value, sourcePath) {
151
- if (typeof value !== "string") {
152
- throw new SstDocsConfigError(`HTML_GENERATOR_SETTINGS.THEME must be a string preset in ${sourcePath}`);
153
- }
154
- const themeName = ensureThemePresetName(value, sourcePath, "HTML_GENERATOR_SETTINGS.THEME");
155
- return cloneTheme((0, themeRegistry_js_1.getThemePreset)(themeName));
206
+ function formatConfigValidationError(sourcePath, error) {
207
+ const lines = error.issues.map((issue) => {
208
+ const path = issue.path.join(".") || "config";
209
+ const defaultValue = defaultValueFor(path);
210
+ const hint = defaultValue ? ` (default: ${defaultValue})` : "";
211
+ return `${path}: ${issue.message}${hint}`;
212
+ });
213
+ return `Invalid configuration in ${sourcePath}:\n- ${lines.join("\n- ")}`;
156
214
  }
157
- function ensureThemePresetName(raw, sourcePath, propertyPath) {
158
- if (raw === undefined || raw === null) {
159
- return themeRegistry_js_1.DEFAULT_THEME_PRESET;
160
- }
161
- if (typeof raw !== "string") {
162
- const options = themeRegistry_js_1.AVAILABLE_THEME_PRESET_NAMES.join(", ");
163
- throw new SstDocsConfigError(`${propertyPath} must be one of: ${options} in ${sourcePath}`);
164
- }
165
- const normalized = raw.trim().toLowerCase();
166
- if (!(0, themeRegistry_js_1.isThemePresetName)(normalized)) {
167
- const options = themeRegistry_js_1.AVAILABLE_THEME_PRESET_NAMES.join(", ");
168
- throw new SstDocsConfigError(`Unknown ${propertyPath} "${raw}" in ${sourcePath}. Supported presets: ${options}`);
169
- }
170
- return normalized;
215
+ function defaultValueFor(path) {
216
+ return exports.SST_DOCS_CONFIG_REFERENCE.find((entry) => entry.key === path)?.default;
171
217
  }
172
- function cloneTheme(theme) {
173
- return JSON.parse(JSON.stringify(theme));
218
+ function normalizePosixPath(value) {
219
+ const sanitized = value.replace(/\\/g, "/");
220
+ const hasLeadingSlash = sanitized.startsWith("/");
221
+ const parts = sanitized.split("/");
222
+ const stack = [];
223
+ for (const part of parts) {
224
+ if (!part || part === ".")
225
+ continue;
226
+ if (part === "..") {
227
+ if (stack.length > 0 && stack[stack.length - 1] !== "..") {
228
+ stack.pop();
229
+ }
230
+ else if (!hasLeadingSlash) {
231
+ stack.push("..");
232
+ }
233
+ continue;
234
+ }
235
+ stack.push(part);
236
+ }
237
+ const normalized = stack.join("/");
238
+ if (hasLeadingSlash) {
239
+ return `/${normalized}`;
240
+ }
241
+ return normalized || ".";
174
242
  }