@specglass/core 0.0.2

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 (95) hide show
  1. package/dist/config/defaults.d.ts +4 -0
  2. package/dist/config/defaults.d.ts.map +1 -0
  3. package/dist/config/defaults.js +31 -0
  4. package/dist/config/defaults.js.map +1 -0
  5. package/dist/config/define-config.d.ts +17 -0
  6. package/dist/config/define-config.d.ts.map +1 -0
  7. package/dist/config/define-config.js +55 -0
  8. package/dist/config/define-config.js.map +1 -0
  9. package/dist/config/loader.d.ts +11 -0
  10. package/dist/config/loader.d.ts.map +1 -0
  11. package/dist/config/loader.js +74 -0
  12. package/dist/config/loader.js.map +1 -0
  13. package/dist/config/schema.d.ts +366 -0
  14. package/dist/config/schema.d.ts.map +1 -0
  15. package/dist/config/schema.js +109 -0
  16. package/dist/config/schema.js.map +1 -0
  17. package/dist/content/frontmatter-schema.d.ts +53 -0
  18. package/dist/content/frontmatter-schema.d.ts.map +1 -0
  19. package/dist/content/frontmatter-schema.js +27 -0
  20. package/dist/content/frontmatter-schema.js.map +1 -0
  21. package/dist/content/mdx-loader.d.ts +93 -0
  22. package/dist/content/mdx-loader.d.ts.map +1 -0
  23. package/dist/content/mdx-loader.js +97 -0
  24. package/dist/content/mdx-loader.js.map +1 -0
  25. package/dist/content/openapi-loader.d.ts +40 -0
  26. package/dist/content/openapi-loader.d.ts.map +1 -0
  27. package/dist/content/openapi-loader.js +58 -0
  28. package/dist/content/openapi-loader.js.map +1 -0
  29. package/dist/content/rehype-code-blocks.d.ts +15 -0
  30. package/dist/content/rehype-code-blocks.d.ts.map +1 -0
  31. package/dist/content/rehype-code-blocks.js +84 -0
  32. package/dist/content/rehype-code-blocks.js.map +1 -0
  33. package/dist/errors/specglass-error.d.ts +12 -0
  34. package/dist/errors/specglass-error.d.ts.map +1 -0
  35. package/dist/errors/specglass-error.js +19 -0
  36. package/dist/errors/specglass-error.js.map +1 -0
  37. package/dist/index.d.ts +26 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +26 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/integration.d.ts +41 -0
  42. package/dist/integration.d.ts.map +1 -0
  43. package/dist/integration.js +276 -0
  44. package/dist/integration.js.map +1 -0
  45. package/dist/navigation/builder.d.ts +24 -0
  46. package/dist/navigation/builder.d.ts.map +1 -0
  47. package/dist/navigation/builder.js +169 -0
  48. package/dist/navigation/builder.js.map +1 -0
  49. package/dist/navigation/index.d.ts +7 -0
  50. package/dist/navigation/index.d.ts.map +1 -0
  51. package/dist/navigation/index.js +6 -0
  52. package/dist/navigation/index.js.map +1 -0
  53. package/dist/navigation/meta-parser.d.ts +9 -0
  54. package/dist/navigation/meta-parser.d.ts.map +1 -0
  55. package/dist/navigation/meta-parser.js +59 -0
  56. package/dist/navigation/meta-parser.js.map +1 -0
  57. package/dist/navigation/meta-schema.d.ts +77 -0
  58. package/dist/navigation/meta-schema.d.ts.map +1 -0
  59. package/dist/navigation/meta-schema.js +31 -0
  60. package/dist/navigation/meta-schema.js.map +1 -0
  61. package/dist/navigation/watcher.d.ts +44 -0
  62. package/dist/navigation/watcher.d.ts.map +1 -0
  63. package/dist/navigation/watcher.js +81 -0
  64. package/dist/navigation/watcher.js.map +1 -0
  65. package/dist/openapi/parser.d.ts +24 -0
  66. package/dist/openapi/parser.d.ts.map +1 -0
  67. package/dist/openapi/parser.js +53 -0
  68. package/dist/openapi/parser.js.map +1 -0
  69. package/dist/openapi/transformer.d.ts +19 -0
  70. package/dist/openapi/transformer.d.ts.map +1 -0
  71. package/dist/openapi/transformer.js +294 -0
  72. package/dist/openapi/transformer.js.map +1 -0
  73. package/dist/openapi/types.d.ts +109 -0
  74. package/dist/openapi/types.d.ts.map +1 -0
  75. package/dist/openapi/types.js +11 -0
  76. package/dist/openapi/types.js.map +1 -0
  77. package/dist/openapi/utils.d.ts +26 -0
  78. package/dist/openapi/utils.d.ts.map +1 -0
  79. package/dist/openapi/utils.js +34 -0
  80. package/dist/openapi/utils.js.map +1 -0
  81. package/dist/types/config.d.ts +7 -0
  82. package/dist/types/config.d.ts.map +1 -0
  83. package/dist/types/config.js +2 -0
  84. package/dist/types/config.js.map +1 -0
  85. package/dist/types/navigation.d.ts +62 -0
  86. package/dist/types/navigation.d.ts.map +1 -0
  87. package/dist/types/navigation.js +2 -0
  88. package/dist/types/navigation.js.map +1 -0
  89. package/dist/virtual/modules.d.ts +34 -0
  90. package/dist/virtual/modules.d.ts.map +1 -0
  91. package/dist/virtual/modules.js +37 -0
  92. package/dist/virtual/modules.js.map +1 -0
  93. package/package.json +51 -0
  94. package/src/pages/[...slug].astro +55 -0
  95. package/src/pages/api-reference/[...slug].astro +67 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Structured error class for all user-facing specglass errors.
3
+ * Provides actionable context: error code, file path, line number, and remediation hint.
4
+ */
5
+ export class SpecglassError extends Error {
6
+ code;
7
+ filePath;
8
+ line;
9
+ hint;
10
+ constructor(message, code, filePath, line, hint) {
11
+ super(message);
12
+ this.code = code;
13
+ this.filePath = filePath;
14
+ this.line = line;
15
+ this.hint = hint;
16
+ this.name = "SpecglassError";
17
+ }
18
+ }
19
+ //# sourceMappingURL=specglass-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specglass-error.js","sourceRoot":"","sources":["../../src/errors/specglass-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAGrB;IACA;IACA;IACA;IALlB,YACE,OAAe,EACC,IAAY,EACZ,QAAiB,EACjB,IAAa,EACb,IAAa;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAC;QALC,SAAI,GAAJ,IAAI,CAAQ;QACZ,aAAQ,GAAR,QAAQ,CAAS;QACjB,SAAI,GAAJ,IAAI,CAAS;QACb,SAAI,GAAJ,IAAI,CAAS;QAG7B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ export { defineConfig } from "./config/define-config.js";
2
+ export { loadConfig } from "./config/loader.js";
3
+ export { configSchema } from "./config/schema.js";
4
+ export { DEFAULT_CONFIG } from "./config/defaults.js";
5
+ export { specglassIntegration, COMPONENT_IMPORTS } from "./integration.js";
6
+ export { VIRTUAL_MODULE_ID, NAVIGATION_MODULE_ID, OPENAPI_DATA_MODULE_ID, generateConfigModuleContent, generateNavigationModuleContent, generateOpenApiDataModuleContent, } from "./virtual/modules.js";
7
+ export { frontmatterSchema } from "./content/frontmatter-schema.js";
8
+ export type { Frontmatter } from "./content/frontmatter-schema.js";
9
+ export { defineDocsCollection, getSlugFromFilePath, wrapFrontmatterError, } from "./content/mdx-loader.js";
10
+ export type { DocsCollectionOptions } from "./content/mdx-loader.js";
11
+ export type { SpecglassConfig, SpecglassUserConfig } from "./types/config.js";
12
+ export type { NavItem, NavigationTree, MetaJsonEntry, MetaJsonEntryValue, } from "./types/navigation.js";
13
+ export { buildNavigationTree, toTitleCase } from "./navigation/builder.js";
14
+ export { parseMetaJson } from "./navigation/meta-parser.js";
15
+ export { metaJsonSchema, metaJsonEntryValueSchema, metaJsonEntryObjectSchema, } from "./navigation/meta-schema.js";
16
+ export type { MetaJsonSchemaInput } from "./navigation/meta-schema.js";
17
+ export { createNavigationWatcher, isNavigationRelevant, } from "./navigation/watcher.js";
18
+ export type { NavigationWatcherOptions } from "./navigation/watcher.js";
19
+ export { SpecglassError } from "./errors/specglass-error.js";
20
+ export { parseOpenApiSpec } from "./openapi/parser.js";
21
+ export { transformSpec } from "./openapi/transformer.js";
22
+ export { buildEndpointSlug, buildEndpointId } from "./openapi/utils.js";
23
+ export type { ApiEndpoint, ApiEndpointError, ApiExample, ApiMediaType, ApiParameter, ApiRequestBody, ApiResponse, ApiSchema, ApiSecurityRequirement, ParsedOpenApiSpec, } from "./openapi/types.js";
24
+ export { loadOpenApiContent, } from "./content/openapi-loader.js";
25
+ export type { OpenApiLoaderConfig, OpenApiContentEntry, OpenApiLoaderResult, } from "./content/openapi-loader.js";
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG3E,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,+BAA+B,EAC/B,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGrE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EACV,OAAO,EACP,cAAc,EACd,aAAa,EACb,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAGxE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACxE,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,WAAW,EACX,SAAS,EACT,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,GACnB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ // @specglass/core — Public API
2
+ // Config system
3
+ export { defineConfig } from "./config/define-config.js";
4
+ export { loadConfig } from "./config/loader.js";
5
+ export { configSchema } from "./config/schema.js";
6
+ export { DEFAULT_CONFIG } from "./config/defaults.js";
7
+ // Integration
8
+ export { specglassIntegration, COMPONENT_IMPORTS } from "./integration.js";
9
+ // Virtual modules
10
+ export { VIRTUAL_MODULE_ID, NAVIGATION_MODULE_ID, OPENAPI_DATA_MODULE_ID, generateConfigModuleContent, generateNavigationModuleContent, generateOpenApiDataModuleContent, } from "./virtual/modules.js";
11
+ // Content
12
+ export { frontmatterSchema } from "./content/frontmatter-schema.js";
13
+ export { defineDocsCollection, getSlugFromFilePath, wrapFrontmatterError, } from "./content/mdx-loader.js";
14
+ // Navigation
15
+ export { buildNavigationTree, toTitleCase } from "./navigation/builder.js";
16
+ export { parseMetaJson } from "./navigation/meta-parser.js";
17
+ export { metaJsonSchema, metaJsonEntryValueSchema, metaJsonEntryObjectSchema, } from "./navigation/meta-schema.js";
18
+ export { createNavigationWatcher, isNavigationRelevant, } from "./navigation/watcher.js";
19
+ // Errors
20
+ export { SpecglassError } from "./errors/specglass-error.js";
21
+ // OpenAPI
22
+ export { parseOpenApiSpec } from "./openapi/parser.js";
23
+ export { transformSpec } from "./openapi/transformer.js";
24
+ export { buildEndpointSlug, buildEndpointId } from "./openapi/utils.js";
25
+ export { loadOpenApiContent, } from "./content/openapi-loader.js";
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,cAAc;AACd,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,kBAAkB;AAClB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,+BAA+B,EAC/B,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAE9B,UAAU;AACV,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAYjC,aAAa;AACb,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAGjC,SAAS;AACT,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,UAAU;AACV,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAaxE,OAAO,EACL,kBAAkB,GACnB,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Component import paths for auto-import.
3
+ *
4
+ * These components are automatically available in MDX files without
5
+ * explicit import statements. The paths resolve to Astro components
6
+ * in `@specglass/theme-default`.
7
+ */
8
+ export declare const COMPONENT_IMPORTS: readonly ["@specglass/theme-default/components/Callout.astro", "@specglass/theme-default/components/Tabs.astro", "@specglass/theme-default/components/TabItem.astro", "@specglass/theme-default/components/CodeBlock.astro", "@specglass/theme-default/components/CodeBlockGroup.astro", "@specglass/theme-default/components/CodeTabs.astro", "@specglass/theme-default/components/Card.astro"];
9
+ /**
10
+ * Components that site owners can override by placing a file at
11
+ * `src/components/overrides/<ComponentName>.astro` in their project.
12
+ *
13
+ * This is the third tier of the three-tier customization system:
14
+ * config options → CSS variables → component overrides.
15
+ */
16
+ export declare const OVERRIDABLE_COMPONENTS: readonly ["Header", "Footer", "Sidebar", "Logo"];
17
+ /**
18
+ * The specglass Astro integration.
19
+ *
20
+ * Accepts a pre-validated SpecglassConfig (resolved at CLI time via jiti)
21
+ * and injects it as a virtual module so theme components can import it
22
+ * at render time via `import { config } from 'virtual:specglass/config'`.
23
+ *
24
+ * Also injects a remark plugin that auto-imports built-in MDX components
25
+ * (Callout, Tabs, TabItem, CodeBlock, Card) into all MDX files.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { defineConfig } from "astro/config";
30
+ * import mdx from "@astrojs/mdx";
31
+ * import { specglassIntegration } from "@specglass/core";
32
+ *
33
+ * export default defineConfig({
34
+ * integrations: [specglassIntegration({ config }), mdx()],
35
+ * });
36
+ * ```
37
+ */
38
+ export declare const specglassIntegration: (options: {
39
+ config?: any;
40
+ }) => import("astro").AstroIntegration & {};
41
+ //# sourceMappingURL=integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAuBA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,kYAQpB,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,kDAKzB,CAAC;AA2CX;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB;;2CA6N/B,CAAC"}
@@ -0,0 +1,276 @@
1
+ import { defineIntegration, addVirtualImports, addVitePlugin, } from "astro-integration-kit";
2
+ import { z } from "zod";
3
+ import { join, parse } from "node:path";
4
+ import { existsSync } from "node:fs";
5
+ import { parse as parseJs } from "acorn";
6
+ import { VIRTUAL_MODULE_ID, NAVIGATION_MODULE_ID, OPENAPI_DATA_MODULE_ID, generateConfigModuleContent, generateNavigationModuleContent, generateOpenApiDataModuleContent, } from "./virtual/modules.js";
7
+ import { buildNavigationTree } from "./navigation/builder.js";
8
+ import { createNavigationWatcher } from "./navigation/watcher.js";
9
+ import { rehypeCodeBlocks } from "./content/rehype-code-blocks.js";
10
+ import { loadOpenApiContent } from "./content/openapi-loader.js";
11
+ /**
12
+ * Component import paths for auto-import.
13
+ *
14
+ * These components are automatically available in MDX files without
15
+ * explicit import statements. The paths resolve to Astro components
16
+ * in `@specglass/theme-default`.
17
+ */
18
+ export const COMPONENT_IMPORTS = [
19
+ "@specglass/theme-default/components/Callout.astro",
20
+ "@specglass/theme-default/components/Tabs.astro",
21
+ "@specglass/theme-default/components/TabItem.astro",
22
+ "@specglass/theme-default/components/CodeBlock.astro",
23
+ "@specglass/theme-default/components/CodeBlockGroup.astro",
24
+ "@specglass/theme-default/components/CodeTabs.astro",
25
+ "@specglass/theme-default/components/Card.astro",
26
+ ];
27
+ /**
28
+ * Components that site owners can override by placing a file at
29
+ * `src/components/overrides/<ComponentName>.astro` in their project.
30
+ *
31
+ * This is the third tier of the three-tier customization system:
32
+ * config options → CSS variables → component overrides.
33
+ */
34
+ export const OVERRIDABLE_COMPONENTS = [
35
+ "Header",
36
+ "Footer",
37
+ "Sidebar",
38
+ "Logo",
39
+ ];
40
+ /**
41
+ * Build import statements for the auto-import remark plugin.
42
+ */
43
+ function buildImportStatements(imports) {
44
+ return imports
45
+ .map((path) => {
46
+ const name = parse(path).name.replaceAll(/[^\w\d]/g, "");
47
+ return `import ${name} from ${JSON.stringify(path)};`;
48
+ })
49
+ .join("\n");
50
+ }
51
+ /**
52
+ * Create a remark plugin that injects component imports into MDX files.
53
+ * This is the same approach used by astro-auto-import internally.
54
+ */
55
+ function createAutoImportPlugin(imports) {
56
+ const js = buildImportStatements(imports);
57
+ const parsed = parseJs(js, { ecmaVersion: "latest", sourceType: "module" });
58
+ const importsNode = {
59
+ type: "mdxjsEsm",
60
+ value: "",
61
+ data: {
62
+ estree: {
63
+ ...parsed,
64
+ type: "Program",
65
+ sourceType: "module",
66
+ },
67
+ },
68
+ };
69
+ return function remarkAutoImport() {
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ return function (tree, vfile) {
72
+ if (!vfile.basename?.endsWith(".md")) {
73
+ tree.children.unshift(importsNode);
74
+ }
75
+ };
76
+ };
77
+ }
78
+ /**
79
+ * The specglass Astro integration.
80
+ *
81
+ * Accepts a pre-validated SpecglassConfig (resolved at CLI time via jiti)
82
+ * and injects it as a virtual module so theme components can import it
83
+ * at render time via `import { config } from 'virtual:specglass/config'`.
84
+ *
85
+ * Also injects a remark plugin that auto-imports built-in MDX components
86
+ * (Callout, Tabs, TabItem, CodeBlock, Card) into all MDX files.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * import { defineConfig } from "astro/config";
91
+ * import mdx from "@astrojs/mdx";
92
+ * import { specglassIntegration } from "@specglass/core";
93
+ *
94
+ * export default defineConfig({
95
+ * integrations: [specglassIntegration({ config }), mdx()],
96
+ * });
97
+ * ```
98
+ */
99
+ export const specglassIntegration = defineIntegration({
100
+ name: "specglass",
101
+ optionsSchema: z.object({
102
+ config: z.any().transform((val) => val),
103
+ }),
104
+ setup({ name, options }) {
105
+ /** Content directory path, resolved in astro:config:setup. */
106
+ let contentDir = "";
107
+ /**
108
+ * Current navigation module content.
109
+ * Updated by the file watcher during dev and used by the
110
+ * navigation HMR Vite plugin to serve the latest navigation data.
111
+ */
112
+ let currentNavContent = "";
113
+ return {
114
+ hooks: {
115
+ "astro:config:setup": async (params) => {
116
+ // Build navigation tree from content directory
117
+ contentDir = join(params.config.root.pathname, "src", "content", "docs");
118
+ let navTree;
119
+ try {
120
+ navTree = await buildNavigationTree(contentDir);
121
+ }
122
+ catch {
123
+ navTree = { items: [] };
124
+ }
125
+ currentNavContent = generateNavigationModuleContent(navTree);
126
+ // Parse OpenAPI specs if configured
127
+ let openApiModuleContent = generateOpenApiDataModuleContent();
128
+ const openApiConfig = options.config.openapi;
129
+ if (openApiConfig?.specs && openApiConfig.specs.length > 0) {
130
+ try {
131
+ const result = await loadOpenApiContent(openApiConfig);
132
+ openApiModuleContent = generateOpenApiDataModuleContent(result.specs);
133
+ }
134
+ catch (error) {
135
+ console.warn(`[specglass] Warning: OpenAPI loading failed: ${error instanceof Error ? error.message : "Unknown error"}`);
136
+ }
137
+ }
138
+ addVirtualImports(params, {
139
+ name,
140
+ imports: {
141
+ [VIRTUAL_MODULE_ID]: generateConfigModuleContent(options.config),
142
+ [NAVIGATION_MODULE_ID]: currentNavContent,
143
+ [OPENAPI_DATA_MODULE_ID]: openApiModuleContent,
144
+ },
145
+ });
146
+ // Add a higher-priority Vite plugin that serves the latest
147
+ // navigation content dynamically. The addVirtualImports plugin
148
+ // captures content as a static string, so on HMR invalidation
149
+ // it would re-serve stale data. This plugin overrides that.
150
+ const resolvedNavId = `\0${NAVIGATION_MODULE_ID}`;
151
+ addVitePlugin(params, {
152
+ warnDuplicated: false,
153
+ plugin: {
154
+ name: "specglass-nav-hmr",
155
+ enforce: "pre",
156
+ load(id) {
157
+ if (id === resolvedNavId) {
158
+ return currentNavContent;
159
+ }
160
+ },
161
+ },
162
+ });
163
+ // Component override resolution (Tier 3 of three-tier customization)
164
+ // Check for user override files and set up Vite aliases
165
+ const srcDir = join(params.config.root.pathname, "src");
166
+ const overrideDir = join(srcDir, "components", "overrides");
167
+ const overrideAliases = {};
168
+ for (const component of OVERRIDABLE_COMPONENTS) {
169
+ const overridePath = join(overrideDir, `${component}.astro`);
170
+ if (existsSync(overridePath)) {
171
+ overrideAliases[`@specglass/theme-default/components/${component}.astro`] = overridePath;
172
+ }
173
+ }
174
+ if (Object.keys(overrideAliases).length > 0) {
175
+ addVitePlugin(params, {
176
+ warnDuplicated: false,
177
+ plugin: {
178
+ name: "specglass-component-overrides",
179
+ enforce: "pre",
180
+ resolveId(id) {
181
+ if (id in overrideAliases) {
182
+ return overrideAliases[id];
183
+ }
184
+ },
185
+ },
186
+ });
187
+ }
188
+ // Inject auto-import remark plugin directly into markdown config.
189
+ // This avoids integration ordering issues — the plugin is added
190
+ // to the remark pipeline regardless of where mdx() appears.
191
+ params.updateConfig({
192
+ markdown: {
193
+ remarkPlugins: [createAutoImportPlugin(COMPONENT_IMPORTS)],
194
+ rehypePlugins: [rehypeCodeBlocks],
195
+ },
196
+ });
197
+ params.injectRoute({
198
+ pattern: "/[...slug]",
199
+ entrypoint: "@specglass/core/pages/[...slug].astro",
200
+ });
201
+ params.injectRoute({
202
+ pattern: "/api-reference/[...slug]",
203
+ entrypoint: "@specglass/core/pages/api-reference/[...slug].astro",
204
+ });
205
+ },
206
+ "astro:server:setup": ({ server }) => {
207
+ // Guard: contentDir must be set by astro:config:setup first
208
+ if (!contentDir) {
209
+ return;
210
+ }
211
+ // The resolved virtual module ID uses Vite's \0 prefix convention
212
+ const resolvedNavModuleId = `\0${NAVIGATION_MODULE_ID}`;
213
+ // The HMR channel — Vite 6 renamed server.ws → server.hot
214
+ const hot = server.hot ?? server.ws;
215
+ const watcher = createNavigationWatcher({
216
+ contentDir,
217
+ onNavigationUpdate: (tree) => {
218
+ // Update the cached module content (read by specglass-nav-hmr plugin)
219
+ currentNavContent = generateNavigationModuleContent(tree);
220
+ // Invalidate the navigation virtual module in Vite's module graph
221
+ const mod = server.moduleGraph.getModuleById(resolvedNavModuleId);
222
+ if (mod) {
223
+ server.moduleGraph.invalidateModule(mod);
224
+ // Send HMR update to trigger client-side re-render
225
+ hot.send({
226
+ type: "full-reload",
227
+ path: "*",
228
+ });
229
+ }
230
+ },
231
+ });
232
+ // Listen to the Vite/chokidar file watcher for all change events
233
+ server.watcher.on("all", (event, filePath) => {
234
+ watcher.handleFileChange(filePath);
235
+ });
236
+ // Clean up when the server closes
237
+ server.httpServer?.on("close", () => {
238
+ watcher.cleanup();
239
+ });
240
+ },
241
+ "astro:build:done": async ({ dir, logger }) => {
242
+ // Run Pagefind indexing on the built output
243
+ // Architecture: Content → Page generation → Pagefind indexing (build pipeline ordering)
244
+ try {
245
+ const pagefind = await import("pagefind");
246
+ const { index } = await pagefind.createIndex({
247
+ forceLanguage: "en",
248
+ });
249
+ if (!index) {
250
+ logger.warn("Pagefind: Failed to create search index");
251
+ return;
252
+ }
253
+ // Index all HTML files in the Astro output directory
254
+ const outputPath = dir.pathname;
255
+ const { page_count, errors } = await index.addDirectory({
256
+ path: outputPath,
257
+ });
258
+ if (errors && errors.length > 0) {
259
+ logger.warn(`Pagefind: Indexing completed with errors: ${errors.join(", ")}`);
260
+ }
261
+ // Write index files to the output directory
262
+ await index.writeFiles({
263
+ outputPath: join(outputPath, "pagefind"),
264
+ });
265
+ logger.info(`Pagefind: Indexed ${page_count} pages → ${outputPath}pagefind/`);
266
+ await pagefind.close();
267
+ }
268
+ catch (error) {
269
+ logger.warn(`Pagefind: Search indexing failed — ${error instanceof Error ? error.message : String(error)}`);
270
+ }
271
+ },
272
+ },
273
+ };
274
+ },
275
+ });
276
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { NavigationTree } from "../types/navigation.js";
2
+ /**
3
+ * Convert a kebab-case filename into Title Case.
4
+ *
5
+ * Examples:
6
+ * "getting-started" → "Getting Started"
7
+ * "api-reference" → "Api Reference"
8
+ */
9
+ export declare function toTitleCase(kebab: string): string;
10
+ /**
11
+ * Build a navigation tree by recursively walking a content directory.
12
+ *
13
+ * - Reads `_meta.json` in each directory for ordering and labels
14
+ * - Falls back to alphabetical ordering when no `_meta.json` exists
15
+ * - Unlisted items are appended alphabetically after listed ones
16
+ * - Hidden items (`hidden: true`) are excluded from the tree
17
+ * - External links (`href`) are included without requiring a file
18
+ *
19
+ * @param contentDir - Absolute path to the content directory root
20
+ * @param slugPrefix - Path prefix for slug generation (used during recursion)
21
+ * @returns The complete navigation tree
22
+ */
23
+ export declare function buildNavigationTree(contentDir: string, slugPrefix?: string): Promise<NavigationTree>;
24
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/navigation/builder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAW,cAAc,EAAiB,MAAM,wBAAwB,CAAC;AAErF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAW,GACtB,OAAO,CAAC,cAAc,CAAC,CAGzB"}
@@ -0,0 +1,169 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { join, parse } from "node:path";
3
+ import { parseMetaJson } from "./meta-parser.js";
4
+ /**
5
+ * Convert a kebab-case filename into Title Case.
6
+ *
7
+ * Examples:
8
+ * "getting-started" → "Getting Started"
9
+ * "api-reference" → "Api Reference"
10
+ */
11
+ export function toTitleCase(kebab) {
12
+ return kebab
13
+ .split("-")
14
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
15
+ .join(" ");
16
+ }
17
+ /**
18
+ * Build a navigation tree by recursively walking a content directory.
19
+ *
20
+ * - Reads `_meta.json` in each directory for ordering and labels
21
+ * - Falls back to alphabetical ordering when no `_meta.json` exists
22
+ * - Unlisted items are appended alphabetically after listed ones
23
+ * - Hidden items (`hidden: true`) are excluded from the tree
24
+ * - External links (`href`) are included without requiring a file
25
+ *
26
+ * @param contentDir - Absolute path to the content directory root
27
+ * @param slugPrefix - Path prefix for slug generation (used during recursion)
28
+ * @returns The complete navigation tree
29
+ */
30
+ export async function buildNavigationTree(contentDir, slugPrefix = "") {
31
+ const items = await buildItems(contentDir, slugPrefix);
32
+ return { items };
33
+ }
34
+ /**
35
+ * Recursively build NavItem[] for a single directory.
36
+ */
37
+ async function buildItems(dirPath, slugPrefix) {
38
+ // Read directory entries
39
+ let entries;
40
+ try {
41
+ entries = await readdir(dirPath);
42
+ }
43
+ catch {
44
+ return [];
45
+ }
46
+ // Classify entries into files and directories
47
+ const files = [];
48
+ const dirs = [];
49
+ for (const entry of entries) {
50
+ if (entry === "_meta.json")
51
+ continue;
52
+ const fullPath = join(dirPath, entry);
53
+ const info = await stat(fullPath);
54
+ if (info.isDirectory()) {
55
+ dirs.push(entry);
56
+ }
57
+ else if (entry.endsWith(".mdx") || entry.endsWith(".md")) {
58
+ // Skip index files — they are section landing pages, not sidebar items
59
+ const baseName = parse(entry).name;
60
+ if (baseName !== "index") {
61
+ files.push(entry);
62
+ }
63
+ }
64
+ }
65
+ // Parse _meta.json for ordering
66
+ const meta = await parseMetaJson(dirPath);
67
+ if (meta === null) {
68
+ // No valid _meta.json — alphabetical fallback
69
+ return buildAlphabetical(files, dirs, dirPath, slugPrefix);
70
+ }
71
+ // Build items in _meta.json order, then append unlisted
72
+ const result = [];
73
+ const listedKeys = new Set(meta.map((m) => m.key));
74
+ for (const entry of meta) {
75
+ if (entry.hidden)
76
+ continue;
77
+ // External link — no file needed
78
+ if (entry.href) {
79
+ result.push({
80
+ title: entry.title,
81
+ slug: "",
82
+ type: "external-link",
83
+ href: entry.href,
84
+ ...(entry.icon !== undefined && { icon: entry.icon }),
85
+ });
86
+ continue;
87
+ }
88
+ // Check if entry matches a directory
89
+ if (dirs.includes(entry.key)) {
90
+ const children = await buildItems(join(dirPath, entry.key), slugPrefix ? `${slugPrefix}/${entry.key}` : entry.key);
91
+ result.push({
92
+ title: entry.title,
93
+ slug: slugPrefix ? `${slugPrefix}/${entry.key}` : entry.key,
94
+ type: "section",
95
+ children,
96
+ ...(entry.collapsed !== undefined && { collapsed: entry.collapsed }),
97
+ ...(entry.icon !== undefined && { icon: entry.icon }),
98
+ });
99
+ continue;
100
+ }
101
+ // Check if entry matches a file
102
+ const matchingFile = files.find((f) => parse(f).name === entry.key);
103
+ if (matchingFile) {
104
+ const baseName = parse(matchingFile).name;
105
+ result.push({
106
+ title: entry.title,
107
+ slug: slugPrefix ? `${slugPrefix}/${baseName}` : baseName,
108
+ type: "page",
109
+ ...(entry.icon !== undefined && { icon: entry.icon }),
110
+ });
111
+ }
112
+ // If no file or directory matches, skip silently (meta references a non-existent item)
113
+ }
114
+ // Append unlisted files alphabetically
115
+ const unlistedFiles = files
116
+ .filter((f) => !listedKeys.has(parse(f).name))
117
+ .sort();
118
+ for (const file of unlistedFiles) {
119
+ const baseName = parse(file).name;
120
+ result.push({
121
+ title: toTitleCase(baseName),
122
+ slug: slugPrefix ? `${slugPrefix}/${baseName}` : baseName,
123
+ type: "page",
124
+ });
125
+ }
126
+ // Append unlisted directories alphabetically
127
+ const unlistedDirs = dirs.filter((d) => !listedKeys.has(d)).sort();
128
+ for (const dir of unlistedDirs) {
129
+ const children = await buildItems(join(dirPath, dir), slugPrefix ? `${slugPrefix}/${dir}` : dir);
130
+ result.push({
131
+ title: toTitleCase(dir),
132
+ slug: slugPrefix ? `${slugPrefix}/${dir}` : dir,
133
+ type: "section",
134
+ children,
135
+ });
136
+ }
137
+ return result;
138
+ }
139
+ /**
140
+ * Build NavItem[] from files and dirs in alphabetical order (no _meta.json).
141
+ */
142
+ async function buildAlphabetical(files, dirs, dirPath, slugPrefix) {
143
+ const allEntries = [
144
+ ...files.map((f) => ({ name: parse(f).name, isDir: false })),
145
+ ...dirs.map((d) => ({ name: d, isDir: true })),
146
+ ].sort((a, b) => a.name.localeCompare(b.name));
147
+ const result = [];
148
+ for (const entry of allEntries) {
149
+ const slug = slugPrefix ? `${slugPrefix}/${entry.name}` : entry.name;
150
+ if (entry.isDir) {
151
+ const children = await buildItems(join(dirPath, entry.name), slug);
152
+ result.push({
153
+ title: toTitleCase(entry.name),
154
+ slug,
155
+ type: "section",
156
+ children,
157
+ });
158
+ }
159
+ else {
160
+ result.push({
161
+ title: toTitleCase(entry.name),
162
+ slug,
163
+ type: "page",
164
+ });
165
+ }
166
+ }
167
+ return result;
168
+ }
169
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/navigation/builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { buildNavigationTree, toTitleCase } from "./builder.js";
2
+ export { parseMetaJson } from "./meta-parser.js";
3
+ export { metaJsonSchema, metaJsonEntryValueSchema, metaJsonEntryObjectSchema, } from "./meta-schema.js";
4
+ export type { MetaJsonSchemaInput } from "./meta-schema.js";
5
+ export { createNavigationWatcher, isNavigationRelevant, } from "./watcher.js";
6
+ export type { NavigationWatcherOptions } from "./watcher.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC"}