sourcey 2.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 (191) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +254 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +197 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/client/copy.js +21 -0
  8. package/dist/client/index.d.ts +7 -0
  9. package/dist/client/index.d.ts.map +1 -0
  10. package/dist/client/index.js +8 -0
  11. package/dist/client/index.js.map +1 -0
  12. package/dist/client/scroll-tracker.js +88 -0
  13. package/dist/client/search.js +164 -0
  14. package/dist/client/sidebar.js +54 -0
  15. package/dist/client/tabs.js +51 -0
  16. package/dist/client/theme-toggle.js +36 -0
  17. package/dist/components/App.d.ts +15 -0
  18. package/dist/components/App.d.ts.map +1 -0
  19. package/dist/components/App.js +13 -0
  20. package/dist/components/App.js.map +1 -0
  21. package/dist/components/layout/Head.d.ts +2 -0
  22. package/dist/components/layout/Head.d.ts.map +1 -0
  23. package/dist/components/layout/Head.js +29 -0
  24. package/dist/components/layout/Head.js.map +1 -0
  25. package/dist/components/layout/Header.d.ts +13 -0
  26. package/dist/components/layout/Header.d.ts.map +1 -0
  27. package/dist/components/layout/Header.js +38 -0
  28. package/dist/components/layout/Header.js.map +1 -0
  29. package/dist/components/layout/Page.d.ts +2 -0
  30. package/dist/components/layout/Page.d.ts.map +1 -0
  31. package/dist/components/layout/Page.js +29 -0
  32. package/dist/components/layout/Page.js.map +1 -0
  33. package/dist/components/layout/Sidebar.d.ts +9 -0
  34. package/dist/components/layout/Sidebar.d.ts.map +1 -0
  35. package/dist/components/layout/Sidebar.js +41 -0
  36. package/dist/components/layout/Sidebar.js.map +1 -0
  37. package/dist/components/layout/TableOfContents.d.ts +10 -0
  38. package/dist/components/layout/TableOfContents.d.ts.map +1 -0
  39. package/dist/components/layout/TableOfContents.js +12 -0
  40. package/dist/components/layout/TableOfContents.js.map +1 -0
  41. package/dist/components/openapi/CodeSamples.d.ts +11 -0
  42. package/dist/components/openapi/CodeSamples.d.ts.map +1 -0
  43. package/dist/components/openapi/CodeSamples.js +16 -0
  44. package/dist/components/openapi/CodeSamples.js.map +1 -0
  45. package/dist/components/openapi/Definition.d.ts +11 -0
  46. package/dist/components/openapi/Definition.d.ts.map +1 -0
  47. package/dist/components/openapi/Definition.js +12 -0
  48. package/dist/components/openapi/Definition.js.map +1 -0
  49. package/dist/components/openapi/EndpointBar.d.ts +12 -0
  50. package/dist/components/openapi/EndpointBar.d.ts.map +1 -0
  51. package/dist/components/openapi/EndpointBar.js +22 -0
  52. package/dist/components/openapi/EndpointBar.js.map +1 -0
  53. package/dist/components/openapi/Introduction.d.ts +6 -0
  54. package/dist/components/openapi/Introduction.d.ts.map +1 -0
  55. package/dist/components/openapi/Introduction.js +14 -0
  56. package/dist/components/openapi/Introduction.js.map +1 -0
  57. package/dist/components/openapi/Operation.d.ts +13 -0
  58. package/dist/components/openapi/Operation.d.ts.map +1 -0
  59. package/dist/components/openapi/Operation.js +23 -0
  60. package/dist/components/openapi/Operation.js.map +1 -0
  61. package/dist/components/openapi/Parameters.d.ts +7 -0
  62. package/dist/components/openapi/Parameters.d.ts.map +1 -0
  63. package/dist/components/openapi/Parameters.js +10 -0
  64. package/dist/components/openapi/Parameters.js.map +1 -0
  65. package/dist/components/openapi/RequestBody.d.ts +17 -0
  66. package/dist/components/openapi/RequestBody.d.ts.map +1 -0
  67. package/dist/components/openapi/RequestBody.js +27 -0
  68. package/dist/components/openapi/RequestBody.js.map +1 -0
  69. package/dist/components/openapi/Responses.d.ts +14 -0
  70. package/dist/components/openapi/Responses.d.ts.map +1 -0
  71. package/dist/components/openapi/Responses.js +60 -0
  72. package/dist/components/openapi/Responses.js.map +1 -0
  73. package/dist/components/openapi/Security.d.ts +14 -0
  74. package/dist/components/openapi/Security.d.ts.map +1 -0
  75. package/dist/components/openapi/Security.js +32 -0
  76. package/dist/components/openapi/Security.js.map +1 -0
  77. package/dist/components/openapi/Tags.d.ts +8 -0
  78. package/dist/components/openapi/Tags.d.ts.map +1 -0
  79. package/dist/components/openapi/Tags.js +10 -0
  80. package/dist/components/openapi/Tags.js.map +1 -0
  81. package/dist/components/schema/ExampleView.d.ts +11 -0
  82. package/dist/components/schema/ExampleView.d.ts.map +1 -0
  83. package/dist/components/schema/ExampleView.js +15 -0
  84. package/dist/components/schema/ExampleView.js.map +1 -0
  85. package/dist/components/schema/SchemaDatatype.d.ts +11 -0
  86. package/dist/components/schema/SchemaDatatype.d.ts.map +1 -0
  87. package/dist/components/schema/SchemaDatatype.js +36 -0
  88. package/dist/components/schema/SchemaDatatype.js.map +1 -0
  89. package/dist/components/schema/SchemaView.d.ts +14 -0
  90. package/dist/components/schema/SchemaView.d.ts.map +1 -0
  91. package/dist/components/schema/SchemaView.js +44 -0
  92. package/dist/components/schema/SchemaView.js.map +1 -0
  93. package/dist/components/ui/Badge.d.ts +11 -0
  94. package/dist/components/ui/Badge.d.ts.map +1 -0
  95. package/dist/components/ui/Badge.js +14 -0
  96. package/dist/components/ui/Badge.js.map +1 -0
  97. package/dist/components/ui/Markdown.d.ts +8 -0
  98. package/dist/components/ui/Markdown.d.ts.map +1 -0
  99. package/dist/components/ui/Markdown.js +13 -0
  100. package/dist/components/ui/Markdown.js.map +1 -0
  101. package/dist/components/ui/SectionLabel.d.ts +10 -0
  102. package/dist/components/ui/SectionLabel.d.ts.map +1 -0
  103. package/dist/components/ui/SectionLabel.js +9 -0
  104. package/dist/components/ui/SectionLabel.js.map +1 -0
  105. package/dist/config.d.ts +46 -0
  106. package/dist/config.d.ts.map +1 -0
  107. package/dist/config.js +102 -0
  108. package/dist/config.js.map +1 -0
  109. package/dist/core/converter.d.ts +9 -0
  110. package/dist/core/converter.d.ts.map +1 -0
  111. package/dist/core/converter.js +29 -0
  112. package/dist/core/converter.js.map +1 -0
  113. package/dist/core/loader.d.ts +7 -0
  114. package/dist/core/loader.d.ts.map +1 -0
  115. package/dist/core/loader.js +92 -0
  116. package/dist/core/loader.js.map +1 -0
  117. package/dist/core/markdown-loader.d.ts +29 -0
  118. package/dist/core/markdown-loader.d.ts.map +1 -0
  119. package/dist/core/markdown-loader.js +65 -0
  120. package/dist/core/markdown-loader.js.map +1 -0
  121. package/dist/core/navigation.d.ts +51 -0
  122. package/dist/core/navigation.d.ts.map +1 -0
  123. package/dist/core/navigation.js +108 -0
  124. package/dist/core/navigation.js.map +1 -0
  125. package/dist/core/normalizer.d.ts +7 -0
  126. package/dist/core/normalizer.d.ts.map +1 -0
  127. package/dist/core/normalizer.js +472 -0
  128. package/dist/core/normalizer.js.map +1 -0
  129. package/dist/core/parser.d.ts +10 -0
  130. package/dist/core/parser.d.ts.map +1 -0
  131. package/dist/core/parser.js +34 -0
  132. package/dist/core/parser.js.map +1 -0
  133. package/dist/core/search-indexer.d.ts +25 -0
  134. package/dist/core/search-indexer.d.ts.map +1 -0
  135. package/dist/core/search-indexer.js +72 -0
  136. package/dist/core/search-indexer.js.map +1 -0
  137. package/dist/core/types.d.ts +236 -0
  138. package/dist/core/types.d.ts.map +1 -0
  139. package/dist/core/types.js +7 -0
  140. package/dist/core/types.js.map +1 -0
  141. package/dist/dev-server.d.ts +17 -0
  142. package/dist/dev-server.d.ts.map +1 -0
  143. package/dist/dev-server.js +202 -0
  144. package/dist/dev-server.js.map +1 -0
  145. package/dist/index.d.ts +62 -0
  146. package/dist/index.d.ts.map +1 -0
  147. package/dist/index.js +166 -0
  148. package/dist/index.js.map +1 -0
  149. package/dist/renderer/context.d.ts +46 -0
  150. package/dist/renderer/context.d.ts.map +1 -0
  151. package/dist/renderer/context.js +22 -0
  152. package/dist/renderer/context.js.map +1 -0
  153. package/dist/renderer/html-builder.d.ts +37 -0
  154. package/dist/renderer/html-builder.d.ts.map +1 -0
  155. package/dist/renderer/html-builder.js +87 -0
  156. package/dist/renderer/html-builder.js.map +1 -0
  157. package/dist/renderer/static-renderer.d.ts +10 -0
  158. package/dist/renderer/static-renderer.d.ts.map +1 -0
  159. package/dist/renderer/static-renderer.js +17 -0
  160. package/dist/renderer/static-renderer.js.map +1 -0
  161. package/dist/themes/default/main.css +61 -0
  162. package/dist/themes/default/sourcey.css +425 -0
  163. package/dist/utils/code-samples.d.ts +8 -0
  164. package/dist/utils/code-samples.d.ts.map +1 -0
  165. package/dist/utils/code-samples.js +84 -0
  166. package/dist/utils/code-samples.js.map +1 -0
  167. package/dist/utils/example-generator.d.ts +12 -0
  168. package/dist/utils/example-generator.d.ts.map +1 -0
  169. package/dist/utils/example-generator.js +123 -0
  170. package/dist/utils/example-generator.js.map +1 -0
  171. package/dist/utils/highlighter.d.ts +10 -0
  172. package/dist/utils/highlighter.d.ts.map +1 -0
  173. package/dist/utils/highlighter.js +51 -0
  174. package/dist/utils/highlighter.js.map +1 -0
  175. package/dist/utils/html-id.d.ts +6 -0
  176. package/dist/utils/html-id.d.ts.map +1 -0
  177. package/dist/utils/html-id.js +11 -0
  178. package/dist/utils/html-id.js.map +1 -0
  179. package/dist/utils/http.d.ts +14 -0
  180. package/dist/utils/http.d.ts.map +1 -0
  181. package/dist/utils/http.js +97 -0
  182. package/dist/utils/http.js.map +1 -0
  183. package/dist/utils/markdown.d.ts +9 -0
  184. package/dist/utils/markdown.d.ts.map +1 -0
  185. package/dist/utils/markdown.js +18 -0
  186. package/dist/utils/markdown.js.map +1 -0
  187. package/dist/vite-plugin.d.ts +16 -0
  188. package/dist/vite-plugin.d.ts.map +1 -0
  189. package/dist/vite-plugin.js +92 -0
  190. package/dist/vite-plugin.js.map +1 -0
  191. package/package.json +78 -0
@@ -0,0 +1,51 @@
1
+ import type { NormalizedSpec } from "./types.js";
2
+ import type { NavigationTab } from "../config.js";
3
+ import type { MarkdownPage } from "./markdown-loader.js";
4
+ export interface SiteNavigation {
5
+ tabs: SiteTab[];
6
+ activeTabSlug: string;
7
+ activePageSlug: string;
8
+ }
9
+ export interface SiteTab {
10
+ label: string;
11
+ slug: string;
12
+ /** Relative URL to this tab's first page (from site root) */
13
+ href: string;
14
+ kind: "spec" | "docs";
15
+ groups: SiteNavGroup[];
16
+ }
17
+ export interface SiteNavGroup {
18
+ label: string;
19
+ items: SiteNavItem[];
20
+ }
21
+ export interface SiteNavItem {
22
+ label: string;
23
+ /** Relative URL to this item (from site root) */
24
+ href: string;
25
+ /** Unique ID for active-state matching */
26
+ id: string;
27
+ /** HTTP method for OpenAPI operations (colored dot in sidebar) */
28
+ method?: string;
29
+ }
30
+ /**
31
+ * Build a navigation tab from an OpenAPI spec.
32
+ * Extracts the same structure currently hardcoded in Sidebar.tsx:
33
+ * Introduction + Authentication → tag groups with operations → Models.
34
+ * All hrefs are #anchor links (single-page spec rendering).
35
+ */
36
+ export declare function buildNavFromSpec(spec: NormalizedSpec, tabSlug: string): SiteTab;
37
+ /**
38
+ * Build a navigation tab from markdown pages.
39
+ * Groups come from the config; items come from loaded page data.
40
+ */
41
+ export declare function buildNavFromPages(tabConfig: NavigationTab, pagesByPath: Map<string, MarkdownPage>): SiteTab;
42
+ /**
43
+ * Assemble a SiteNavigation from a list of tabs.
44
+ * Defaults to the first tab and first page as active.
45
+ */
46
+ export declare function buildSiteNavigation(tabs: SiteTab[]): SiteNavigation;
47
+ /**
48
+ * Create a copy of the navigation with a specific page marked as active.
49
+ */
50
+ export declare function withActivePage(nav: SiteNavigation, tabSlug: string, pageSlug: string): SiteNavigation;
51
+ //# sourceMappingURL=navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOzD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CA+CT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GACrC,OAAO,CA8BT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,cAAc,CASnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,cAAc,CAEhB"}
@@ -0,0 +1,108 @@
1
+ import { htmlId } from "../utils/html-id.js";
2
+ // ---------------------------------------------------------------------------
3
+ // Builders
4
+ // ---------------------------------------------------------------------------
5
+ /**
6
+ * Build a navigation tab from an OpenAPI spec.
7
+ * Extracts the same structure currently hardcoded in Sidebar.tsx:
8
+ * Introduction + Authentication → tag groups with operations → Models.
9
+ * All hrefs are #anchor links (single-page spec rendering).
10
+ */
11
+ export function buildNavFromSpec(spec, tabSlug) {
12
+ const basePath = `${tabSlug}/index.html`;
13
+ const groups = [];
14
+ // Intro group
15
+ const introItems = [
16
+ { label: "Introduction", href: `${basePath}#introduction`, id: "introduction" },
17
+ ];
18
+ if (Object.keys(spec.securitySchemes).length > 0) {
19
+ introItems.push({
20
+ label: "Authentication",
21
+ href: `${basePath}#authentication`,
22
+ id: "authentication",
23
+ });
24
+ }
25
+ groups.push({ label: "", items: introItems });
26
+ // Tag groups with operations
27
+ for (const tag of spec.tags) {
28
+ if (tag.hidden)
29
+ continue;
30
+ const items = tag.operations.map((op) => ({
31
+ label: op.summary ?? `${op.method.toUpperCase()} ${op.path}`,
32
+ href: `${basePath}#operation-${htmlId(op.path)}-${htmlId(op.method)}`,
33
+ id: `operation-${htmlId(op.path)}-${htmlId(op.method)}`,
34
+ method: op.method,
35
+ }));
36
+ groups.push({ label: tag.name, items });
37
+ }
38
+ // Models group
39
+ const schemaNames = Object.keys(spec.schemas);
40
+ if (schemaNames.length > 0) {
41
+ const items = schemaNames.map((name) => ({
42
+ label: name,
43
+ href: `${basePath}#definition-${htmlId(name)}`,
44
+ id: `definition-${htmlId(name)}`,
45
+ }));
46
+ groups.push({ label: "Models", items });
47
+ }
48
+ return {
49
+ label: tabSlug,
50
+ slug: tabSlug,
51
+ href: basePath,
52
+ kind: "spec",
53
+ groups,
54
+ };
55
+ }
56
+ /**
57
+ * Build a navigation tab from markdown pages.
58
+ * Groups come from the config; items come from loaded page data.
59
+ */
60
+ export function buildNavFromPages(tabConfig, pagesByPath) {
61
+ const groups = [];
62
+ if (tabConfig.groups) {
63
+ for (const groupConfig of tabConfig.groups) {
64
+ const items = [];
65
+ for (const pagePath of groupConfig.pages) {
66
+ const page = pagesByPath.get(pagePath);
67
+ if (!page)
68
+ continue;
69
+ items.push({
70
+ label: page.title,
71
+ href: `${tabConfig.slug}/${page.slug}.html`,
72
+ id: page.slug,
73
+ });
74
+ }
75
+ groups.push({ label: groupConfig.label, items });
76
+ }
77
+ }
78
+ // First page href for tab link
79
+ const firstItem = groups[0]?.items[0];
80
+ const href = firstItem?.href ?? `${tabConfig.slug}/`;
81
+ return {
82
+ label: tabConfig.label,
83
+ slug: tabConfig.slug,
84
+ href,
85
+ kind: "docs",
86
+ groups,
87
+ };
88
+ }
89
+ /**
90
+ * Assemble a SiteNavigation from a list of tabs.
91
+ * Defaults to the first tab and first page as active.
92
+ */
93
+ export function buildSiteNavigation(tabs) {
94
+ const firstTab = tabs[0];
95
+ const firstItem = firstTab?.groups[0]?.items[0];
96
+ return {
97
+ tabs,
98
+ activeTabSlug: firstTab?.slug ?? "",
99
+ activePageSlug: firstItem?.id ?? "",
100
+ };
101
+ }
102
+ /**
103
+ * Create a copy of the navigation with a specific page marked as active.
104
+ */
105
+ export function withActivePage(nav, tabSlug, pageSlug) {
106
+ return { ...nav, activeTabSlug: tabSlug, activePageSlug: pageSlug };
107
+ }
108
+ //# sourceMappingURL=navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ParsedSpec, NormalizedSpec } from "./types.js";
2
+ /**
3
+ * Transform a fully dereferenced OpenAPI 3.x document into a NormalizedSpec.
4
+ * This is the single point where spec-format-specific logic lives.
5
+ */
6
+ export declare function normalizeSpec(parsed: ParsedSpec): NormalizedSpec;
7
+ //# sourceMappingURL=normalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../src/core/normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,UAAU,EACV,cAAc,EAqBf,MAAM,YAAY,CAAC;AAMpB;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAoBhE"}
@@ -0,0 +1,472 @@
1
+ const HTTP_METHODS = [
2
+ "get", "post", "put", "delete", "patch", "options", "head", "trace",
3
+ ];
4
+ /**
5
+ * Transform a fully dereferenced OpenAPI 3.x document into a NormalizedSpec.
6
+ * This is the single point where spec-format-specific logic lives.
7
+ */
8
+ export function normalizeSpec(parsed) {
9
+ const doc = parsed.document;
10
+ const schemas = normalizeSchemas(doc.components?.schemas);
11
+ const securitySchemes = normalizeSecuritySchemes(doc.components?.securitySchemes);
12
+ const servers = normalizeServers(doc.servers);
13
+ const operations = normalizeOperations(doc.paths, doc.security);
14
+ const webhooks = normalizeWebhooks(doc.webhooks);
15
+ const tags = normalizeTags(doc.tags, operations);
16
+ return {
17
+ info: normalizeInfo(doc),
18
+ servers,
19
+ tags,
20
+ operations,
21
+ schemas,
22
+ securitySchemes,
23
+ webhooks,
24
+ externalDocs: normalizeExternalDocs(doc.externalDocs),
25
+ };
26
+ }
27
+ // ── Info ───────────────────────────────────────────────────────────
28
+ function normalizeInfo(doc) {
29
+ const info = doc.info ?? {};
30
+ return {
31
+ title: str(info.title, "Untitled API"),
32
+ version: str(info.version, "0.0.0"),
33
+ description: optStr(info.description),
34
+ termsOfService: optStr(info.termsOfService),
35
+ contact: info.contact
36
+ ? {
37
+ name: optStr(info.contact.name),
38
+ url: optStr(info.contact.url),
39
+ email: optStr(info.contact.email),
40
+ }
41
+ : undefined,
42
+ license: info.license
43
+ ? {
44
+ name: str(info.license.name, ""),
45
+ url: optStr(info.license.url),
46
+ identifier: optStr(info.license.identifier),
47
+ }
48
+ : undefined,
49
+ logo: optStr(info["x-logo"]),
50
+ };
51
+ }
52
+ // ── Servers ────────────────────────────────────────────────────────
53
+ function normalizeServers(servers) {
54
+ if (!servers?.length)
55
+ return [{ url: "/" }];
56
+ return servers.map((s) => ({
57
+ url: str(s.url, "/"),
58
+ description: optStr(s.description),
59
+ variables: s.variables
60
+ ? normalizeServerVariables(s.variables)
61
+ : undefined,
62
+ }));
63
+ }
64
+ function normalizeServerVariables(vars) {
65
+ const result = {};
66
+ for (const [key, v] of Object.entries(vars)) {
67
+ result[key] = {
68
+ default: str(v.default, ""),
69
+ description: optStr(v.description),
70
+ enum: Array.isArray(v.enum) ? v.enum.map(String) : undefined,
71
+ };
72
+ }
73
+ return result;
74
+ }
75
+ // ── Tags ───────────────────────────────────────────────────────────
76
+ function normalizeTags(rawTags, operations) {
77
+ // Build a map of defined tags
78
+ const tagMap = new Map();
79
+ if (rawTags) {
80
+ for (const t of rawTags) {
81
+ const name = str(t.name, "");
82
+ if (name) {
83
+ tagMap.set(name, {
84
+ name,
85
+ description: optStr(t.description),
86
+ externalDocs: normalizeExternalDocs(t.externalDocs),
87
+ operations: [],
88
+ hidden: t["x-sourcey-hide"] === true,
89
+ });
90
+ }
91
+ }
92
+ }
93
+ // Assign operations to tags, creating implicit tags as needed
94
+ for (const op of operations) {
95
+ if (op.hidden)
96
+ continue;
97
+ const opTags = op.tags.length > 0 ? op.tags : ["default"];
98
+ for (const tagName of opTags) {
99
+ if (!tagMap.has(tagName)) {
100
+ // Implicit tag creation (legacy behavior from preprocessor.js)
101
+ tagMap.set(tagName, {
102
+ name: tagName,
103
+ operations: [],
104
+ });
105
+ }
106
+ tagMap.get(tagName).operations.push(op);
107
+ }
108
+ }
109
+ // Filter out tags with no operations (unless they have a description)
110
+ return Array.from(tagMap.values()).filter((t) => t.operations.length > 0 || t.description);
111
+ }
112
+ // ── Operations ─────────────────────────────────────────────────────
113
+ function normalizeOperations(paths, globalSecurity) {
114
+ if (!paths)
115
+ return [];
116
+ const operations = [];
117
+ for (const [path, pathItem] of Object.entries(paths)) {
118
+ const pathParams = normalizeParameters(pathItem.parameters);
119
+ for (const method of HTTP_METHODS) {
120
+ const opRaw = pathItem[method];
121
+ if (!opRaw)
122
+ continue;
123
+ const opParams = normalizeParameters(opRaw.parameters);
124
+ // Merge path-level + operation-level params, deduping by name+in
125
+ const mergedParams = deduplicateParameters([...pathParams, ...opParams]);
126
+ const op = {
127
+ operationId: optStr(opRaw.operationId),
128
+ summary: optStr(opRaw.summary),
129
+ description: optStr(opRaw.description),
130
+ method,
131
+ path,
132
+ tags: Array.isArray(opRaw.tags) ? opRaw.tags.map(String) : [],
133
+ parameters: mergedParams,
134
+ requestBody: normalizeRequestBody(opRaw.requestBody),
135
+ responses: normalizeResponses(opRaw.responses),
136
+ security: normalizeSecurityRequirements(opRaw.security, globalSecurity),
137
+ deprecated: opRaw.deprecated === true,
138
+ servers: opRaw.servers
139
+ ? normalizeServers(opRaw.servers)
140
+ : undefined,
141
+ externalDocs: normalizeExternalDocs(opRaw.externalDocs),
142
+ callbacks: normalizeCallbacks(opRaw.callbacks),
143
+ hidden: opRaw["x-sourcey-hide"] === true,
144
+ codeSamples: normalizeCodeSamples(opRaw["x-sourcey-code-samples"], opRaw["x-code-samples"]),
145
+ };
146
+ operations.push(op);
147
+ }
148
+ }
149
+ return operations;
150
+ }
151
+ /**
152
+ * Deduplicate parameters by (name, in) — operation-level params
153
+ * override path-level params with the same identity.
154
+ */
155
+ function deduplicateParameters(params) {
156
+ const seen = new Map();
157
+ // Process in order: path-level first, then operation-level.
158
+ // Later entries (operation-level) override earlier ones (path-level).
159
+ for (const p of params) {
160
+ seen.set(`${p.in}:${p.name}`, p);
161
+ }
162
+ return Array.from(seen.values());
163
+ }
164
+ function normalizeParameters(params) {
165
+ if (!params)
166
+ return [];
167
+ return params.map((p) => ({
168
+ name: str(p.name, ""),
169
+ in: str(p.in, "query"),
170
+ description: optStr(p.description),
171
+ required: p.required === true,
172
+ deprecated: p.deprecated === true,
173
+ schema: p.schema
174
+ ? normalizeSchema(p.schema)
175
+ : undefined,
176
+ example: p.example,
177
+ examples: p.examples
178
+ ? normalizeExamples(p.examples)
179
+ : undefined,
180
+ }));
181
+ }
182
+ function normalizeRequestBody(body) {
183
+ if (!body)
184
+ return undefined;
185
+ return {
186
+ description: optStr(body.description),
187
+ required: body.required === true,
188
+ content: normalizeContent(body.content),
189
+ };
190
+ }
191
+ function normalizeContent(content) {
192
+ if (!content)
193
+ return {};
194
+ const result = {};
195
+ for (const [mediaType, value] of Object.entries(content)) {
196
+ result[mediaType] = {
197
+ schema: value.schema
198
+ ? normalizeSchema(value.schema)
199
+ : undefined,
200
+ example: value.example,
201
+ examples: value.examples
202
+ ? normalizeExamples(value.examples)
203
+ : undefined,
204
+ };
205
+ }
206
+ return result;
207
+ }
208
+ function normalizeResponses(responses) {
209
+ if (!responses)
210
+ return [];
211
+ return Object.entries(responses).map(([statusCode, r]) => ({
212
+ statusCode,
213
+ description: str(r.description, ""),
214
+ content: r.content
215
+ ? normalizeContent(r.content)
216
+ : undefined,
217
+ headers: r.headers
218
+ ? normalizeResponseHeaders(r.headers)
219
+ : undefined,
220
+ links: r.links
221
+ ? normalizeLinks(r.links)
222
+ : undefined,
223
+ }));
224
+ }
225
+ function normalizeResponseHeaders(headers) {
226
+ const result = {};
227
+ for (const [name, h] of Object.entries(headers)) {
228
+ result[name] = {
229
+ name,
230
+ in: "header",
231
+ description: optStr(h.description),
232
+ required: h.required === true,
233
+ deprecated: h.deprecated === true,
234
+ schema: h.schema
235
+ ? normalizeSchema(h.schema)
236
+ : undefined,
237
+ };
238
+ }
239
+ return result;
240
+ }
241
+ function normalizeLinks(links) {
242
+ const result = {};
243
+ for (const [name, l] of Object.entries(links)) {
244
+ result[name] = {
245
+ operationId: optStr(l.operationId),
246
+ operationRef: optStr(l.operationRef),
247
+ description: optStr(l.description),
248
+ parameters: l.parameters,
249
+ requestBody: l.requestBody,
250
+ server: l.server
251
+ ? normalizeServers([l.server])[0]
252
+ : undefined,
253
+ };
254
+ }
255
+ return result;
256
+ }
257
+ function normalizeCallbacks(callbacks) {
258
+ if (!callbacks)
259
+ return undefined;
260
+ const result = {};
261
+ for (const [expression, pathItem] of Object.entries(callbacks)) {
262
+ result[expression] = {
263
+ expression,
264
+ operations: normalizeOperations({ [expression]: pathItem }),
265
+ };
266
+ }
267
+ return result;
268
+ }
269
+ function normalizeSecurityRequirements(opSecurity, globalSecurity) {
270
+ // Operation security overrides global
271
+ return (opSecurity ?? globalSecurity ?? []);
272
+ }
273
+ function normalizeCodeSamples(sourceySamples, genericSamples) {
274
+ const samples = sourceySamples ?? genericSamples;
275
+ if (!samples?.length)
276
+ return undefined;
277
+ return samples.map((s) => ({
278
+ lang: str(s.lang, "text"),
279
+ label: optStr(s.label),
280
+ source: str(s.source, ""),
281
+ }));
282
+ }
283
+ // ── Schemas ────────────────────────────────────────────────────────
284
+ function normalizeSchemas(schemas) {
285
+ if (!schemas)
286
+ return {};
287
+ const result = {};
288
+ for (const [name, s] of Object.entries(schemas)) {
289
+ result[name] = normalizeSchema(s, name);
290
+ }
291
+ return result;
292
+ }
293
+ function normalizeSchema(schema, name) {
294
+ // Handle nullable: OpenAPI 3.0 uses x-nullable or nullable,
295
+ // OpenAPI 3.1 uses type: ["string", "null"]
296
+ let type = schema.type;
297
+ let nullable = schema.nullable === true || schema["x-nullable"] === true;
298
+ if (Array.isArray(type)) {
299
+ const nonNullTypes = type.filter((t) => t !== "null");
300
+ if (nonNullTypes.length < type.length) {
301
+ nullable = true;
302
+ }
303
+ type = nonNullTypes.length === 1 ? nonNullTypes[0] : nonNullTypes;
304
+ }
305
+ const result = {
306
+ name,
307
+ type,
308
+ format: optStr(schema.format),
309
+ title: optStr(schema.title),
310
+ description: optStr(schema.description),
311
+ default: schema.default,
312
+ enum: Array.isArray(schema.enum) ? schema.enum : undefined,
313
+ const: schema.const,
314
+ nullable,
315
+ readOnly: schema.readOnly === true,
316
+ writeOnly: schema.writeOnly === true,
317
+ deprecated: schema.deprecated === true,
318
+ example: schema.example,
319
+ examples: Array.isArray(schema.examples) ? schema.examples : undefined,
320
+ refName: optStr(schema["x-ref-name"]),
321
+ };
322
+ // Object properties
323
+ if (schema.properties) {
324
+ result.properties = {};
325
+ for (const [key, val] of Object.entries(schema.properties)) {
326
+ result.properties[key] = normalizeSchema(val);
327
+ }
328
+ }
329
+ if (schema.additionalProperties !== undefined) {
330
+ result.additionalProperties =
331
+ typeof schema.additionalProperties === "boolean"
332
+ ? schema.additionalProperties
333
+ : normalizeSchema(schema.additionalProperties);
334
+ }
335
+ if (Array.isArray(schema.required)) {
336
+ result.required = schema.required.map(String);
337
+ }
338
+ // Array items
339
+ if (schema.items) {
340
+ result.items = normalizeSchema(schema.items);
341
+ }
342
+ if (schema.minItems !== undefined)
343
+ result.minItems = Number(schema.minItems);
344
+ if (schema.maxItems !== undefined)
345
+ result.maxItems = Number(schema.maxItems);
346
+ if (schema.uniqueItems === true)
347
+ result.uniqueItems = true;
348
+ // Numeric constraints
349
+ if (schema.minimum !== undefined)
350
+ result.minimum = Number(schema.minimum);
351
+ if (schema.maximum !== undefined)
352
+ result.maximum = Number(schema.maximum);
353
+ if (schema.exclusiveMinimum !== undefined) {
354
+ result.exclusiveMinimum = schema.exclusiveMinimum;
355
+ }
356
+ if (schema.exclusiveMaximum !== undefined) {
357
+ result.exclusiveMaximum = schema.exclusiveMaximum;
358
+ }
359
+ if (schema.multipleOf !== undefined)
360
+ result.multipleOf = Number(schema.multipleOf);
361
+ // String constraints
362
+ if (schema.minLength !== undefined)
363
+ result.minLength = Number(schema.minLength);
364
+ if (schema.maxLength !== undefined)
365
+ result.maxLength = Number(schema.maxLength);
366
+ if (schema.pattern !== undefined)
367
+ result.pattern = String(schema.pattern);
368
+ // Composition
369
+ if (Array.isArray(schema.allOf)) {
370
+ result.allOf = schema.allOf.map((s) => normalizeSchema(s));
371
+ }
372
+ if (Array.isArray(schema.anyOf)) {
373
+ result.anyOf = schema.anyOf.map((s) => normalizeSchema(s));
374
+ }
375
+ if (Array.isArray(schema.oneOf)) {
376
+ result.oneOf = schema.oneOf.map((s) => normalizeSchema(s));
377
+ }
378
+ if (schema.not) {
379
+ result.not = normalizeSchema(schema.not);
380
+ }
381
+ if (schema.discriminator) {
382
+ const d = schema.discriminator;
383
+ result.discriminator = {
384
+ propertyName: str(d.propertyName, ""),
385
+ mapping: d.mapping,
386
+ };
387
+ }
388
+ // External docs
389
+ if (schema.externalDocs) {
390
+ result.externalDocs = normalizeExternalDocs(schema.externalDocs);
391
+ }
392
+ return result;
393
+ }
394
+ // ── Security Schemes ───────────────────────────────────────────────
395
+ function normalizeSecuritySchemes(schemes) {
396
+ if (!schemes)
397
+ return {};
398
+ const result = {};
399
+ for (const [name, s] of Object.entries(schemes)) {
400
+ const raw = s;
401
+ result[name] = {
402
+ type: str(raw.type, "apiKey"),
403
+ name: optStr(raw.name),
404
+ description: optStr(raw.description),
405
+ in: optStr(raw.in),
406
+ scheme: optStr(raw.scheme),
407
+ bearerFormat: optStr(raw.bearerFormat),
408
+ flows: raw.flows
409
+ ? normalizeOAuthFlows(raw.flows)
410
+ : undefined,
411
+ openIdConnectUrl: optStr(raw.openIdConnectUrl),
412
+ };
413
+ }
414
+ return result;
415
+ }
416
+ function normalizeOAuthFlows(flows) {
417
+ const result = {};
418
+ for (const flowType of [
419
+ "implicit",
420
+ "password",
421
+ "clientCredentials",
422
+ "authorizationCode",
423
+ ]) {
424
+ if (flows[flowType]) {
425
+ result[flowType] = normalizeOAuthFlow(flows[flowType]);
426
+ }
427
+ }
428
+ return result;
429
+ }
430
+ function normalizeOAuthFlow(flow) {
431
+ return {
432
+ authorizationUrl: optStr(flow.authorizationUrl),
433
+ tokenUrl: optStr(flow.tokenUrl),
434
+ refreshUrl: optStr(flow.refreshUrl),
435
+ scopes: (flow.scopes ?? {}),
436
+ };
437
+ }
438
+ // ── Webhooks ───────────────────────────────────────────────────────
439
+ function normalizeWebhooks(webhooks) {
440
+ if (!webhooks)
441
+ return [];
442
+ return normalizeOperations(webhooks);
443
+ }
444
+ // ── Shared ─────────────────────────────────────────────────────────
445
+ function normalizeExternalDocs(docs) {
446
+ if (!docs?.url)
447
+ return undefined;
448
+ return {
449
+ url: str(docs.url, ""),
450
+ description: optStr(docs.description),
451
+ };
452
+ }
453
+ function normalizeExamples(examples) {
454
+ const result = {};
455
+ for (const [name, e] of Object.entries(examples)) {
456
+ result[name] = {
457
+ summary: optStr(e.summary),
458
+ description: optStr(e.description),
459
+ value: e.value,
460
+ externalValue: optStr(e.externalValue),
461
+ };
462
+ }
463
+ return result;
464
+ }
465
+ // ── Utility helpers ────────────────────────────────────────────────
466
+ function str(value, fallback) {
467
+ return typeof value === "string" ? value : fallback;
468
+ }
469
+ function optStr(value) {
470
+ return typeof value === "string" ? value : undefined;
471
+ }
472
+ //# sourceMappingURL=normalizer.js.map