@fern-api/fdr-sdk 1.2.39-1829e82a64 → 1.2.39-2a1aa9df8f

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 (52) hide show
  1. package/dist/js/converters/index.js +4 -9
  2. package/dist/js/converters/index.js.map +1 -1
  3. package/dist/js/converters/index.mjs +4 -9
  4. package/dist/js/converters/index.mjs.map +1 -1
  5. package/dist/js/index.js +4 -9
  6. package/dist/js/index.js.map +1 -1
  7. package/dist/js/index.mjs +4 -9
  8. package/dist/js/index.mjs.map +1 -1
  9. package/dist/js/navigation/index.js +4 -9
  10. package/dist/js/navigation/index.js.map +1 -1
  11. package/dist/js/navigation/index.mjs +4 -9
  12. package/dist/js/navigation/index.mjs.map +1 -1
  13. package/dist/js/navigation/ledger-root-builder.js +14 -6
  14. package/dist/js/navigation/ledger-root-builder.js.map +1 -1
  15. package/dist/js/navigation/ledger-root-builder.mjs +14 -6
  16. package/dist/js/navigation/ledger-root-builder.mjs.map +1 -1
  17. package/dist/navigation/__test__/findNode.authedRedirect.test.d.ts +2 -0
  18. package/dist/navigation/__test__/findNode.authedRedirect.test.d.ts.map +1 -0
  19. package/dist/navigation/__test__/findNode.authedRedirect.test.js +184 -0
  20. package/dist/navigation/__test__/findNode.authedRedirect.test.js.map +1 -0
  21. package/dist/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.d.ts +2 -0
  22. package/dist/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.d.ts.map +1 -0
  23. package/dist/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.js +85 -0
  24. package/dist/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.js.map +1 -0
  25. package/dist/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.d.ts +2 -0
  26. package/dist/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.d.ts.map +1 -0
  27. package/dist/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.js +210 -0
  28. package/dist/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.js.map +1 -0
  29. package/dist/navigation/ledger-root-builder.d.ts +5 -1
  30. package/dist/navigation/ledger-root-builder.d.ts.map +1 -1
  31. package/dist/navigation/ledger-root-builder.js +27 -12
  32. package/dist/navigation/ledger-root-builder.js.map +1 -1
  33. package/dist/navigation/utils/findNode.d.ts +1 -0
  34. package/dist/navigation/utils/findNode.d.ts.map +1 -1
  35. package/dist/navigation/utils/findNode.js +3 -11
  36. package/dist/navigation/utils/findNode.js.map +1 -1
  37. package/dist/navigation/utils/followRedirect.d.ts.map +1 -1
  38. package/dist/navigation/utils/followRedirect.js +2 -0
  39. package/dist/navigation/utils/followRedirect.js.map +1 -1
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/dist/types/navigation/__test__/findNode.authedRedirect.test.d.ts +2 -0
  42. package/dist/types/navigation/__test__/findNode.authedRedirect.test.d.ts.map +1 -0
  43. package/dist/types/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.d.ts +2 -0
  44. package/dist/types/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.d.ts.map +1 -0
  45. package/dist/types/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.d.ts +2 -0
  46. package/dist/types/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.d.ts.map +1 -0
  47. package/dist/types/navigation/ledger-root-builder.d.ts +5 -1
  48. package/dist/types/navigation/ledger-root-builder.d.ts.map +1 -1
  49. package/dist/types/navigation/utils/findNode.d.ts +1 -0
  50. package/dist/types/navigation/utils/findNode.d.ts.map +1 -1
  51. package/dist/types/navigation/utils/followRedirect.d.ts.map +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=findNode.authedRedirect.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findNode.authedRedirect.test.d.ts","sourceRoot":"","sources":["../../../src/navigation/__test__/findNode.authedRedirect.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,184 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { FernNavigation } from "../..";
3
+ /**
4
+ * Build a minimal RootNode with a multi-product structure to test that
5
+ * findNode surfaces the `authed` flag on redirect results. This mirrors
6
+ * the RBAC multi-product fixture where one product (greenhouse) is
7
+ * admin-only and another (garden) is public.
8
+ *
9
+ * Only fields that `findNode` reads are populated; the rest are stubs.
10
+ * The entire tree is built as a plain object and cast to `RootNode`.
11
+ */
12
+ function buildMultiProductRoot(opts) {
13
+ return {
14
+ type: "root",
15
+ version: "v2",
16
+ id: "root",
17
+ slug: "",
18
+ title: "Test Docs",
19
+ icon: undefined,
20
+ hidden: undefined,
21
+ authed: undefined,
22
+ viewers: undefined,
23
+ orphaned: undefined,
24
+ featureFlags: undefined,
25
+ canonicalSlug: undefined,
26
+ collapsed: undefined,
27
+ pointsTo: "garden/garden-setup",
28
+ roles: undefined,
29
+ child: {
30
+ type: "productgroup",
31
+ id: "productgroup",
32
+ landingPage: undefined,
33
+ children: [
34
+ {
35
+ type: "product",
36
+ id: "product-garden",
37
+ productId: "product-garden",
38
+ slug: "garden",
39
+ title: "Garden",
40
+ icon: undefined,
41
+ hidden: undefined,
42
+ authed: opts.publicProductAuthed ? true : undefined,
43
+ viewers: undefined,
44
+ orphaned: undefined,
45
+ featureFlags: undefined,
46
+ canonicalSlug: undefined,
47
+ default: true,
48
+ subtitle: "",
49
+ image: undefined,
50
+ announcement: undefined,
51
+ pointsTo: "garden/garden-setup",
52
+ child: {
53
+ type: "unversioned",
54
+ id: "unversioned-garden",
55
+ landingPage: undefined,
56
+ child: {
57
+ type: "sidebarRoot",
58
+ id: "sidebar-garden",
59
+ children: [
60
+ {
61
+ type: "sidebarGroup",
62
+ id: "group-garden",
63
+ children: [
64
+ {
65
+ type: "page",
66
+ id: "page-garden-setup",
67
+ slug: "garden/garden-setup",
68
+ title: "Garden Setup",
69
+ icon: undefined,
70
+ hidden: undefined,
71
+ authed: opts.publicProductAuthed ? true : undefined,
72
+ viewers: undefined,
73
+ orphaned: undefined,
74
+ featureFlags: undefined,
75
+ canonicalSlug: undefined,
76
+ noindex: undefined,
77
+ pageId: "page-garden-setup",
78
+ availability: undefined
79
+ }
80
+ ]
81
+ }
82
+ ]
83
+ }
84
+ }
85
+ },
86
+ {
87
+ type: "product",
88
+ id: "product-greenhouse",
89
+ productId: "product-greenhouse",
90
+ slug: "greenhouse",
91
+ title: "Greenhouse",
92
+ icon: undefined,
93
+ hidden: undefined,
94
+ authed: opts.protectedProductAuthed ? true : undefined,
95
+ viewers: undefined,
96
+ orphaned: undefined,
97
+ featureFlags: undefined,
98
+ canonicalSlug: undefined,
99
+ default: false,
100
+ subtitle: "",
101
+ image: undefined,
102
+ announcement: undefined,
103
+ pointsTo: "greenhouse/greenhouse-setup",
104
+ child: {
105
+ type: "unversioned",
106
+ id: "unversioned-greenhouse",
107
+ landingPage: undefined,
108
+ child: {
109
+ type: "sidebarRoot",
110
+ id: "sidebar-greenhouse",
111
+ children: [
112
+ {
113
+ type: "sidebarGroup",
114
+ id: "group-greenhouse",
115
+ children: [
116
+ {
117
+ type: "page",
118
+ id: "page-greenhouse-setup",
119
+ slug: "greenhouse/greenhouse-setup",
120
+ title: "Greenhouse Setup",
121
+ icon: undefined,
122
+ hidden: undefined,
123
+ authed: opts.protectedProductAuthed ? true : undefined,
124
+ viewers: undefined,
125
+ orphaned: undefined,
126
+ featureFlags: undefined,
127
+ canonicalSlug: undefined,
128
+ noindex: undefined,
129
+ pageId: "page-greenhouse-setup",
130
+ availability: undefined
131
+ }
132
+ ]
133
+ }
134
+ ]
135
+ }
136
+ }
137
+ }
138
+ ]
139
+ }
140
+ };
141
+ }
142
+ describe("findNode — authed flag on redirect results", () => {
143
+ it("returns authed=true on redirect for a protected product structural URL", () => {
144
+ const root = buildMultiProductRoot({ protectedProductAuthed: true });
145
+ const result = FernNavigation.utils.findNode(root, FernNavigation.Slug("greenhouse"));
146
+ expect(result.type).toBe("redirect");
147
+ if (result.type === "redirect") {
148
+ expect(result.redirect).toBe("greenhouse/greenhouse-setup");
149
+ expect(result.authed).toBe(true);
150
+ }
151
+ });
152
+ it("returns authed=undefined on redirect for a public product structural URL", () => {
153
+ const root = buildMultiProductRoot({ protectedProductAuthed: false });
154
+ const result = FernNavigation.utils.findNode(root, FernNavigation.Slug("garden"));
155
+ expect(result.type).toBe("redirect");
156
+ if (result.type === "redirect") {
157
+ expect(result.redirect).toBe("garden/garden-setup");
158
+ expect(result.authed).toBeUndefined();
159
+ }
160
+ });
161
+ it("returns authed=undefined on root redirect", () => {
162
+ const root = buildMultiProductRoot({ protectedProductAuthed: true });
163
+ const result = FernNavigation.utils.findNode(root, FernNavigation.Slug(""));
164
+ expect(result.type).toBe("redirect");
165
+ if (result.type === "redirect") {
166
+ expect(result.redirect).toBe("garden/garden-setup");
167
+ expect(result.authed).toBeUndefined();
168
+ }
169
+ });
170
+ it("returns found (not redirect) for page slugs", () => {
171
+ const root = buildMultiProductRoot({ protectedProductAuthed: true });
172
+ const result = FernNavigation.utils.findNode(root, FernNavigation.Slug("greenhouse/greenhouse-setup"));
173
+ expect(result.type).toBe("found");
174
+ });
175
+ it("returns authed on notFound for unknown slug under protected product", () => {
176
+ const root = buildMultiProductRoot({ protectedProductAuthed: true });
177
+ const result = FernNavigation.utils.findNode(root, FernNavigation.Slug("greenhouse/nonexistent"));
178
+ expect(result.type).toBe("notFound");
179
+ if (result.type === "notFound") {
180
+ expect(result.authed).toBe(true);
181
+ }
182
+ });
183
+ });
184
+ //# sourceMappingURL=findNode.authedRedirect.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findNode.authedRedirect.test.js","sourceRoot":"","sources":["../../../src/navigation/__test__/findNode.authedRedirect.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ledger-root-builder.collapsedDeterminism.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-root-builder.collapsedDeterminism.test.d.ts","sourceRoot":"","sources":["../../../src/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildFullRootFromSegments } from "../ledger-root-builder.js";
3
+ /**
4
+ * Regression guard for the ledger `collapsed:false` flip
5
+ * (fern-platform#12357 follow-up).
6
+ *
7
+ * Root cause: the ledger segment read had no stable total `ORDER BY`, so two
8
+ * segments sharing a `sort_order` came back in arbitrary order; the builder's
9
+ * stable `.sort()` then preserved that arbitrary DB order, and the surviving
10
+ * section row is the one `sectionNode` reads `collapsed` from — flipping it
11
+ * across identical publishes. The DB fix adds a `seg.id` tiebreak; this test
12
+ * locks the complementary invariant on the builder side: its output must be
13
+ * independent of the order segments arrive in.
14
+ */
15
+ function pageRoute(fullPath, title) {
16
+ return { type: "markdown", fullPath, hidden: false, metadata: { title, pageId: `pg-${title}` } };
17
+ }
18
+ function segment(opts) {
19
+ return {
20
+ segmentId: opts.hash,
21
+ segmentHash: opts.hash,
22
+ section: opts.section,
23
+ locale: "en",
24
+ sortOrder: opts.sortOrder,
25
+ hidden: false,
26
+ metadata: opts.metadata,
27
+ product: null,
28
+ version: null,
29
+ variant: null,
30
+ tab: null
31
+ };
32
+ }
33
+ function findSectionByTitle(root, title) {
34
+ const found = [];
35
+ const walk = (nodes) => {
36
+ for (const n of nodes) {
37
+ found.push(n);
38
+ if (Array.isArray(n.children)) {
39
+ walk(n.children);
40
+ }
41
+ }
42
+ };
43
+ const sidebar = root.child.child;
44
+ walk(sidebar.children);
45
+ return found.find((n) => n.type === "section" && n.title === title);
46
+ }
47
+ // Two sibling guides sections under a tabRoot; "Advanced" carries collapsed:false.
48
+ // The two section segments deliberately share a sort_order to mimic the DB tie
49
+ // that previously surfaced in arbitrary order.
50
+ function buildWith(order) {
51
+ const root = segment({ hash: "seg-root", section: "", sortOrder: 0, metadata: { type: "tabRoot" } });
52
+ const getStarted = segment({
53
+ hash: "seg-gs",
54
+ section: "get-started",
55
+ sortOrder: 5,
56
+ metadata: { type: "section", title: "Get Started", childIndex: 0 }
57
+ });
58
+ const advanced = segment({
59
+ hash: "seg-adv",
60
+ section: "advanced",
61
+ sortOrder: 5,
62
+ metadata: { type: "section", title: "Advanced", collapsed: false, childIndex: 1 }
63
+ });
64
+ const segments = order === "forward" ? [root, getStarted, advanced] : [root, advanced, getStarted];
65
+ return buildFullRootFromSegments({
66
+ basePath: "docs",
67
+ title: "Test",
68
+ segments: segments,
69
+ navBySegment: new Map([
70
+ ["seg-root", [pageRoute("overview", "Overview")]],
71
+ ["seg-gs", [pageRoute("get-started/intro", "Intro")]],
72
+ ["seg-adv", [pageRoute("advanced/config", "Config")]]
73
+ ])
74
+ });
75
+ }
76
+ describe("buildFullRootFromSegments — collapsed:false is order-independent", () => {
77
+ it("resolves collapsed:false identically regardless of segment input order", () => {
78
+ const forward = findSectionByTitle(buildWith("forward"), "Advanced");
79
+ const reversed = findSectionByTitle(buildWith("reversed"), "Advanced");
80
+ expect(forward?.collapsed).toBe(false);
81
+ expect(reversed?.collapsed).toBe(false);
82
+ expect(forward?.collapsed).toBe(reversed?.collapsed);
83
+ });
84
+ });
85
+ //# sourceMappingURL=ledger-root-builder.collapsedDeterminism.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-root-builder.collapsedDeterminism.test.js","sourceRoot":"","sources":["../../../src/navigation/__test__/ledger-root-builder.collapsedDeterminism.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ledger-root-builder.skipSlugApiReference.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-root-builder.skipSlugApiReference.test.d.ts","sourceRoot":"","sources":["../../../src/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,210 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import * as FernNavigation from "../index.js";
3
+ import { buildFullRootFromSegments } from "../ledger-root-builder.js";
4
+ function segment(opts) {
5
+ return {
6
+ segmentId: opts.segmentHash,
7
+ segmentHash: opts.segmentHash,
8
+ section: opts.section,
9
+ locale: "en",
10
+ sortOrder: opts.sortOrder,
11
+ hidden: false,
12
+ metadata: opts.metadata,
13
+ product: null,
14
+ version: null,
15
+ variant: null,
16
+ tab: opts.tab != null
17
+ ? {
18
+ id: opts.tab.id,
19
+ displayName: opts.tab.displayName,
20
+ icon: null,
21
+ sortOrder: opts.tab.sortOrder ?? 0,
22
+ hidden: false,
23
+ viewers: [],
24
+ orphaned: false,
25
+ featureFlags: [],
26
+ metadata: {
27
+ slug: opts.tab.slug,
28
+ tab_type: "tab",
29
+ ...(opts.tab.pointsTo != null ? { pointsTo: opts.tab.pointsTo } : {})
30
+ }
31
+ }
32
+ : null
33
+ };
34
+ }
35
+ function restRoute(opts) {
36
+ return {
37
+ type: "rest",
38
+ fullPath: opts.fullPath,
39
+ hidden: false,
40
+ displaySortOrder: opts.displaySortOrder,
41
+ metadata: {
42
+ title: opts.title,
43
+ apiDefinitionId: "plant-api-def",
44
+ endpointId: opts.endpointId,
45
+ method: opts.method ?? "GET",
46
+ isResponseStream: false
47
+ }
48
+ };
49
+ }
50
+ function markdownRoute(opts) {
51
+ return {
52
+ type: "markdown",
53
+ fullPath: opts.fullPath,
54
+ hidden: false,
55
+ displaySortOrder: opts.displaySortOrder,
56
+ metadata: { title: opts.title, pageId: opts.pageId }
57
+ };
58
+ }
59
+ describe("buildFullRootFromSegments — skip-slug apiReference", () => {
60
+ const guidesTab = {
61
+ id: "tab:guides",
62
+ displayName: "Guides",
63
+ slug: "",
64
+ sortOrder: 0,
65
+ pointsTo: "welcome"
66
+ };
67
+ const apiTab = {
68
+ id: "tab:api-reference",
69
+ displayName: "API Reference",
70
+ slug: "api-reference",
71
+ sortOrder: 1,
72
+ pointsTo: "api-reference/list-plants"
73
+ };
74
+ it("skip-slug apiReference at section='' includes endpoint children in sidebar", () => {
75
+ const segments = [
76
+ // Guides tab section
77
+ segment({
78
+ segmentHash: "guides-section",
79
+ section: "get-started",
80
+ sortOrder: 0,
81
+ metadata: { type: "section", title: "Get Started", pointsTo: "welcome" },
82
+ tab: guidesTab
83
+ }),
84
+ // API Reference tab — apiReference with skip-slug (section="")
85
+ segment({
86
+ segmentHash: "api-ref",
87
+ section: "",
88
+ sortOrder: 1,
89
+ metadata: {
90
+ type: "apiReference",
91
+ title: "Plant API",
92
+ apiDefinitionId: "plant-api-def",
93
+ pointsTo: "api-reference/list-plants"
94
+ },
95
+ tab: apiTab
96
+ })
97
+ ];
98
+ const navBySegment = new Map([
99
+ [
100
+ "guides-section",
101
+ [
102
+ markdownRoute({
103
+ fullPath: "welcome",
104
+ title: "Welcome",
105
+ pageId: "pages/welcome.mdx",
106
+ displaySortOrder: 0
107
+ }),
108
+ markdownRoute({
109
+ fullPath: "quickstart",
110
+ title: "Quickstart",
111
+ pageId: "pages/quickstart.mdx",
112
+ displaySortOrder: 1
113
+ })
114
+ ]
115
+ ],
116
+ [
117
+ "api-ref",
118
+ [
119
+ restRoute({
120
+ fullPath: "api-reference/list-plants",
121
+ title: "List plants",
122
+ endpointId: "listPlants",
123
+ displaySortOrder: 0
124
+ }),
125
+ restRoute({
126
+ fullPath: "api-reference/get-plant",
127
+ title: "Get plant",
128
+ endpointId: "getPlant",
129
+ displaySortOrder: 1
130
+ })
131
+ ]
132
+ ]
133
+ ]);
134
+ const root = buildFullRootFromSegments({
135
+ basePath: "",
136
+ title: "Plant Docs",
137
+ segments,
138
+ navBySegment
139
+ });
140
+ FernNavigation.utils.mutableUpdatePointsTo(root);
141
+ // Navigate to the tabbed node
142
+ expect(root.child.type).toBe("unversioned");
143
+ const unversioned = root.child;
144
+ expect(unversioned.child.type).toBe("tabbed");
145
+ const tabbed = unversioned.child;
146
+ expect(tabbed.children).toHaveLength(2);
147
+ // Find the API Reference tab
148
+ const apiRefTab = tabbed.children.find((t) => t.type === "tab" && t.title === "API Reference");
149
+ expect(apiRefTab).toBeDefined();
150
+ // The sidebar root should have children (the apiReference node)
151
+ const sidebar = apiRefTab.child;
152
+ expect(sidebar.type).toBe("sidebarRoot");
153
+ expect(sidebar.children.length).toBeGreaterThan(0);
154
+ // The first child should be an apiReference node with endpoint children
155
+ const apiNode = sidebar.children[0];
156
+ expect(apiNode.type).toBe("apiReference");
157
+ expect(apiNode.children.length).toBe(2);
158
+ expect(apiNode.children.map((c) => [c.type, "title" in c ? c.title : ""])).toEqual([
159
+ ["endpoint", "List plants"],
160
+ ["endpoint", "Get plant"]
161
+ ]);
162
+ });
163
+ it("skip-slug apiReference endpoints are indexable (appear in sitemap)", () => {
164
+ const segments = [
165
+ segment({
166
+ segmentHash: "api-ref",
167
+ section: "",
168
+ sortOrder: 0,
169
+ metadata: {
170
+ type: "apiReference",
171
+ title: "Plant API",
172
+ apiDefinitionId: "plant-api-def",
173
+ pointsTo: "api-reference/list-plants"
174
+ },
175
+ tab: apiTab
176
+ })
177
+ ];
178
+ const navBySegment = new Map([
179
+ [
180
+ "api-ref",
181
+ [
182
+ restRoute({
183
+ fullPath: "api-reference/list-plants",
184
+ title: "List plants",
185
+ endpointId: "listPlants",
186
+ displaySortOrder: 0
187
+ }),
188
+ restRoute({
189
+ fullPath: "api-reference/get-plant",
190
+ title: "Get plant",
191
+ endpointId: "getPlant",
192
+ displaySortOrder: 1
193
+ })
194
+ ]
195
+ ]
196
+ ]);
197
+ const root = buildFullRootFromSegments({
198
+ basePath: "",
199
+ title: "Plant Docs",
200
+ segments,
201
+ navBySegment
202
+ });
203
+ FernNavigation.utils.mutableUpdatePointsTo(root);
204
+ const collector = FernNavigation.NodeCollector.collect(root);
205
+ const indexableSlugs = collector.indexablePageSlugs;
206
+ expect(indexableSlugs).toContain("api-reference/list-plants");
207
+ expect(indexableSlugs).toContain("api-reference/get-plant");
208
+ });
209
+ });
210
+ //# sourceMappingURL=ledger-root-builder.skipSlugApiReference.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-root-builder.skipSlugApiReference.test.js","sourceRoot":"","sources":["../../../src/navigation/__test__/ledger-root-builder.skipSlugApiReference.test.ts"],"names":[],"mappings":"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"}
@@ -101,7 +101,11 @@ export declare function apiLeafNodeFromNavRoute(route: NavRoute, basePath: strin
101
101
  * @internal Exported for the ledger loader's scoped explorer methods.
102
102
  */
103
103
  export declare function collectApiLeafNodesFromNav(routes: NavRoute[], basePath: string, segmentApiDefinitionId: string): ApiLeafNode[];
104
- /** Read the apiDefinitionId off a segment's metadata, or undefined. */
104
+ /**
105
+ * Read the apiDefinitionId off a segment's metadata, or undefined.
106
+ * The matched types (apiReference, apiPackage, changelog) are the
107
+ * subset of {@link StructuralSegmentType} that carry an apiDefinitionId.
108
+ */
105
109
  export declare function segmentApiDefinitionId(metadata: Segment["metadata"]): string | undefined;
106
110
  /**
107
111
  * Merge endpoint nodes that were originally part of an `endpointPair` in the V1
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-root-builder.d.ts","sourceRoot":"","sources":["../../src/navigation/ledger-root-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,KAAK,cAAc,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,KAAK,OAAO,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC7B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,6DAA6D;IAC7D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtC;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACpC;AAID;;;;;;GAMG;AACH,UAAU,YAAY;IAClB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,cAAc,CAAC,MAAM,CAAC;IAC/C,kFAAkF;IAClF,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,cAAc,CAAC,IAAI,CAAC;CAC9D;AAED,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAuBjE;AAmPD;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GACjB,cAAc,CAAC,YAAY,GAC3B,cAAc,CAAC,WAAW,GAC1B,cAAc,CAAC,aAAa,GAC5B,cAAc,CAAC,QAAQ,GACvB,cAAc,CAAC,WAAW,CAAC;AAOjC;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,MAAM,EAChB,sBAAsB,EAAE,MAAM,GAC/B,WAAW,GAAG,SAAS,CAGzB;AA+KD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,QAAQ,EAAE,EAClB,QAAQ,EAAE,MAAM,EAChB,sBAAsB,EAAE,MAAM,GAC/B,WAAW,EAAE,CAKf;AAED,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,SAAS,CASxF;AAgWD;;;;;;GAMG;AACH,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,WAAW,EAAE,EACxB,MAAM,EAAE,QAAQ,EAAE,GACnB,cAAc,CAAC,eAAe,EAAE,CAsElC;AAgiBD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,cAAc,CAAC,QAAQ,CAmF5F"}
1
+ {"version":3,"file":"ledger-root-builder.d.ts","sourceRoot":"","sources":["../../src/navigation/ledger-root-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,KAAK,cAAc,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,KAAK,OAAO,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AA6B9C,MAAM,WAAW,gBAAgB;IAC7B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,6DAA6D;IAC7D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtC;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACpC;AAID;;;;;;GAMG;AACH,UAAU,YAAY;IAClB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,cAAc,CAAC,MAAM,CAAC;IAC/C,kFAAkF;IAClF,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,cAAc,CAAC,IAAI,CAAC;CAC9D;AAED,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAuBjE;AA+OD;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GACjB,cAAc,CAAC,YAAY,GAC3B,cAAc,CAAC,WAAW,GAC1B,cAAc,CAAC,aAAa,GAC5B,cAAc,CAAC,QAAQ,GACvB,cAAc,CAAC,WAAW,CAAC;AAOjC;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,MAAM,EAChB,sBAAsB,EAAE,MAAM,GAC/B,WAAW,GAAG,SAAS,CAGzB;AA+KD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,QAAQ,EAAE,EAClB,QAAQ,EAAE,MAAM,EAChB,sBAAsB,EAAE,MAAM,GAC/B,WAAW,EAAE,CAKf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,SAAS,CASxF;AAgWD;;;;;;GAMG;AACH,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,WAAW,EAAE,EACxB,MAAM,EAAE,QAAQ,EAAE,GACnB,cAAc,CAAC,eAAe,EAAE,CAsElC;AAsiBD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,cAAc,CAAC,QAAQ,CAmF5F"}
@@ -1,4 +1,12 @@
1
1
  import * as FernNavigation from "./index.js";
2
+ const STRUCTURAL_SEGMENT_TYPES = new Set([
3
+ "section",
4
+ "apiReference",
5
+ "apiPackage",
6
+ "changelog"
7
+ ]);
8
+ const API_NODE_SEGMENT_TYPES = new Set(["apiReference", "apiPackage"]);
9
+ const INFRA_SEGMENT_TYPES = new Set(["files", "redirects"]);
2
10
  /** @internal Exported for testing only. */
3
11
  export function createBuildContext(basePath) {
4
12
  // Tracks how many times each id key has been handed out, so collisions get
@@ -109,10 +117,7 @@ function toViewers(viewers) {
109
117
  * cross-variant prev/next links to disclose restricted content.
110
118
  */
111
119
  function mergeVariantViewers(seg) {
112
- const segViewers = seg.metadata.type === "section" ||
113
- seg.metadata.type === "apiReference" ||
114
- seg.metadata.type === "apiPackage" ||
115
- seg.metadata.type === "changelog"
120
+ const segViewers = STRUCTURAL_SEGMENT_TYPES.has(seg.metadata.type)
116
121
  ? seg.metadata.viewers
117
122
  : undefined;
118
123
  const variantViewers = seg.variant?.viewers;
@@ -401,7 +406,11 @@ export function collectApiLeafNodesFromNav(routes, basePath, segmentApiDefinitio
401
406
  .map((route) => apiLeafNodeFromNavRouteInternal(ctx, route, segmentApiDefinitionId))
402
407
  .filter((node) => node != null);
403
408
  }
404
- /** Read the apiDefinitionId off a segment's metadata, or undefined. */
409
+ /**
410
+ * Read the apiDefinitionId off a segment's metadata, or undefined.
411
+ * The matched types (apiReference, apiPackage, changelog) are the
412
+ * subset of {@link StructuralSegmentType} that carry an apiDefinitionId.
413
+ */
405
414
  export function segmentApiDefinitionId(metadata) {
406
415
  switch (metadata.type) {
407
416
  case "apiReference":
@@ -962,7 +971,7 @@ function buildSectionTree(ctx, scopeSegs, parentPath, scopePrefix, changelogScop
962
971
  const type = metaStr(s.seg.metadata, "type");
963
972
  const childIndex = metaNum(s.seg.metadata, "childIndex");
964
973
  let node;
965
- if (type === "apiReference" || type === "apiPackage") {
974
+ if (API_NODE_SEGMENT_TYPES.has(type ?? "")) {
966
975
  node = apiReferenceNode(ctx, s, scopeSegs, scopePrefix);
967
976
  }
968
977
  else if (type === "changelog") {
@@ -1051,7 +1060,7 @@ function buildSidebarRootChildren(ctx, scopeSegs, scopePrefix, changelogScopePre
1051
1060
  //
1052
1061
  // hasRootSection = true
1053
1062
  // │
1054
- // ├─ (A) tabRoot / loose artifacts (sec=="", type!="section")
1063
+ // ├─ (A) tabRoot / loose artifacts (sec=="", !STRUCTURAL_SEGMENT_TYPES)
1055
1064
  // │ → emit pages/links inline at their displaySortOrder
1056
1065
  // │
1057
1066
  // ├─ (B) the root section itself (sec=="", type=="section")
@@ -1080,8 +1089,10 @@ function buildSidebarRootChildren(ctx, scopeSegs, scopePrefix, changelogScopePre
1080
1089
  if (type === "tabRoot" && metaBool(s.seg.metadata, "landingPage") === true) {
1081
1090
  continue;
1082
1091
  }
1083
- // (A) Loose artifacts: tabRoot or non-section segments at section path "".
1084
- if (type === "tabRoot" || (sec === "" && type !== "section")) {
1092
+ // (A) Loose artifacts: tabRoot or non-structural segments at section path "".
1093
+ // Structural types need their own node builders in (B)/(C) even when
1094
+ // their section path is "" (which happens with skip-slug: true).
1095
+ if (type === "tabRoot" || (sec === "" && !STRUCTURAL_SEGMENT_TYPES.has(type ?? ""))) {
1085
1096
  for (const r of s.nav) {
1086
1097
  const node = artifactToNode(ctx, r);
1087
1098
  if (node != null) {
@@ -1101,12 +1112,16 @@ function buildSidebarRootChildren(ctx, scopeSegs, scopePrefix, changelogScopePre
1101
1112
  // adopted by a root section (B).
1102
1113
  const parent = parentSectionPath(sec);
1103
1114
  const isRootLevel = parent === "" || (!sectionPaths.has(parent) && nearestExistingAncestor(sec, sectionPaths) === "");
1104
- if (isRootLevel && hasRootSection) {
1115
+ // Skip depth-0 sections already adopted by the root section wrapper (B),
1116
+ // but NOT structural types with sec="" (skip-slug). buildSectionTree
1117
+ // filters out sec="" segments (line 1249), so they wouldn't be adopted
1118
+ // and must be emitted here directly.
1119
+ if (isRootLevel && hasRootSection && sec !== "") {
1105
1120
  continue;
1106
1121
  }
1107
1122
  if (isRootLevel) {
1108
1123
  let node;
1109
- if (type === "apiReference" || type === "apiPackage") {
1124
+ if (API_NODE_SEGMENT_TYPES.has(type ?? "")) {
1110
1125
  node = apiReferenceNode(ctx, s, scopeSegs, scopePrefix);
1111
1126
  }
1112
1127
  else if (type === "changelog") {
@@ -1223,7 +1238,7 @@ export function buildFullRootFromSegments(options) {
1223
1238
  // but guard here too.
1224
1239
  .filter((seg) => {
1225
1240
  const t = metaStr(seg.metadata, "type");
1226
- return t !== "files" && t !== "redirects";
1241
+ return !INFRA_SEGMENT_TYPES.has(t ?? "");
1227
1242
  })
1228
1243
  .map((seg) => ({ seg, nav: navBySegment.get(seg.segmentHash) ?? [] }));
1229
1244
  const rootSlug = asSlug(sourceRootSlug ?? basePath);