jamdesk 1.1.149 → 1.1.151
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.
- package/README.md +28 -0
- package/dist/__tests__/integration/fix-scan-integration.test.d.ts +2 -0
- package/dist/__tests__/integration/fix-scan-integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/fix-scan-integration.test.js +77 -0
- package/dist/__tests__/integration/fix-scan-integration.test.js.map +1 -0
- package/dist/__tests__/unit/ai-fix-client.test.d.ts +2 -0
- package/dist/__tests__/unit/ai-fix-client.test.d.ts.map +1 -0
- package/dist/__tests__/unit/ai-fix-client.test.js +64 -0
- package/dist/__tests__/unit/ai-fix-client.test.js.map +1 -0
- package/dist/__tests__/unit/docs-config.test.js +66 -0
- package/dist/__tests__/unit/docs-config.test.js.map +1 -1
- package/dist/__tests__/unit/fix-ai-fallback.test.d.ts +2 -0
- package/dist/__tests__/unit/fix-ai-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/unit/fix-ai-fallback.test.js +82 -0
- package/dist/__tests__/unit/fix-ai-fallback.test.js.map +1 -0
- package/dist/__tests__/unit/fix-engine-sync.test.d.ts +2 -0
- package/dist/__tests__/unit/fix-engine-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/fix-engine-sync.test.js +16 -0
- package/dist/__tests__/unit/fix-engine-sync.test.js.map +1 -0
- package/dist/__tests__/unit/fix.test.d.ts +2 -0
- package/dist/__tests__/unit/fix.test.d.ts.map +1 -0
- package/dist/__tests__/unit/fix.test.js +61 -0
- package/dist/__tests__/unit/fix.test.js.map +1 -0
- package/dist/__tests__/unit/frontmatter-write.test.d.ts +2 -0
- package/dist/__tests__/unit/frontmatter-write.test.d.ts.map +1 -0
- package/dist/__tests__/unit/frontmatter-write.test.js +31 -0
- package/dist/__tests__/unit/frontmatter-write.test.js.map +1 -0
- package/dist/__tests__/unit/heading-extractor-sync.test.d.ts +2 -0
- package/dist/__tests__/unit/heading-extractor-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/heading-extractor-sync.test.js +16 -0
- package/dist/__tests__/unit/heading-extractor-sync.test.js.map +1 -0
- package/dist/__tests__/unit/missing-description-scan.test.d.ts +2 -0
- package/dist/__tests__/unit/missing-description-scan.test.d.ts.map +1 -0
- package/dist/__tests__/unit/missing-description-scan.test.js +50 -0
- package/dist/__tests__/unit/missing-description-scan.test.js.map +1 -0
- package/dist/commands/fix.d.ts +97 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +348 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/ai-fix-client.d.ts +27 -0
- package/dist/lib/ai-fix-client.d.ts.map +1 -0
- package/dist/lib/ai-fix-client.js +45 -0
- package/dist/lib/ai-fix-client.js.map +1 -0
- package/dist/lib/deps.js +1 -1
- package/dist/lib/deps.js.map +1 -1
- package/dist/lib/docs-config.d.ts +1 -1
- package/dist/lib/docs-config.d.ts.map +1 -1
- package/dist/lib/docs-config.js +59 -4
- package/dist/lib/docs-config.js.map +1 -1
- package/dist/lib/fix-engine.d.ts +89 -0
- package/dist/lib/fix-engine.d.ts.map +1 -0
- package/dist/lib/fix-engine.js +236 -0
- package/dist/lib/fix-engine.js.map +1 -0
- package/dist/lib/frontmatter-write.d.ts +4 -0
- package/dist/lib/frontmatter-write.d.ts.map +1 -0
- package/dist/lib/frontmatter-write.js +10 -0
- package/dist/lib/frontmatter-write.js.map +1 -0
- package/dist/lib/heading-extractor.d.ts +63 -0
- package/dist/lib/heading-extractor.d.ts.map +1 -0
- package/dist/lib/heading-extractor.js +171 -0
- package/dist/lib/heading-extractor.js.map +1 -0
- package/dist/lib/missing-description-scan.d.ts +7 -0
- package/dist/lib/missing-description-scan.d.ts.map +1 -0
- package/dist/lib/missing-description-scan.js +28 -0
- package/dist/lib/missing-description-scan.js.map +1 -0
- package/package.json +3 -3
- package/vendored/components/mdx/ApiEndpoint.tsx +1 -1
- package/vendored/components/mdx/OpenApiEndpoint.tsx +7 -2
- package/vendored/components/navigation/SocialFooter.tsx +9 -1
- package/vendored/lib/build/error-parser.ts +6 -3
- package/vendored/lib/firestore-helpers.ts +2 -3
- package/vendored/lib/fix-engine.ts +293 -0
- package/vendored/lib/health-checks.ts +2 -2
- package/vendored/lib/json-ld.ts +98 -6
- package/vendored/lib/render-doc-page.tsx +8 -2
- package/vendored/lib/search.ts +2 -2
- package/vendored/lib/seo.ts +29 -13
- package/vendored/lib/static-artifacts.ts +2 -2
- package/vendored/lib/ui-strings.ts +6 -0
- package/vendored/lib/validate-config.ts +63 -4
- package/vendored/scripts/build-search-index.cjs +2 -2
- package/vendored/shared/types.ts +1 -0
- package/vendored/workspace-package-lock.json +36 -36
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic Doc-Warning Fix Engine.
|
|
3
|
+
*
|
|
4
|
+
* AUTO-SYNCED: This file is automatically copied from build-service by vendor.js.
|
|
5
|
+
* DO NOT EDIT directly - edit builder/build-service/lib/fix-engine.ts instead.
|
|
6
|
+
*/
|
|
7
|
+
/** A markdown heading entry (not a <Step> or <Update> component anchor). */
|
|
8
|
+
export function isMarkdownHeading(h) {
|
|
9
|
+
return h.stepNumber == null && !h.isUpdate;
|
|
10
|
+
}
|
|
11
|
+
/** True iff `candidate` is an exact heading id on `headings`. The canonical
|
|
12
|
+
* guard for any externally-sourced (e.g. AI) anchor suggestion — the engine
|
|
13
|
+
* itself uses the same `allIds.includes(...)` check inside fixBrokenLink's
|
|
14
|
+
* `emit`. Phase 2's AI tier MUST route every suggestion through this so a
|
|
15
|
+
* hallucinated fragment can never be written. Pure, dependency-free. */
|
|
16
|
+
export function isRealAnchor(candidate, headings) {
|
|
17
|
+
if (!candidate)
|
|
18
|
+
return false;
|
|
19
|
+
return headings.some((h) => h.id === candidate);
|
|
20
|
+
}
|
|
21
|
+
/** Substring appended to the skip reason when the deterministic engine defers an
|
|
22
|
+
* anchor to the AI tier. Exported so the AI-tier callers match on this constant
|
|
23
|
+
* instead of a re-typed literal — producer and consumer cannot drift. */
|
|
24
|
+
export const NEEDS_AI_MARKER = '(needs AI)';
|
|
25
|
+
// ---- Edit application -----------------------------------------------------
|
|
26
|
+
// A flagged href is always the target of a markdown link `](href)` or an
|
|
27
|
+
// `href="…"`/`href='…'` attribute, so in source it is bounded on the left by a
|
|
28
|
+
// delimiter — never by another URL/path character. A match whose preceding char
|
|
29
|
+
// IS a path character is the href appearing as a SUBSTRING inside a longer URL
|
|
30
|
+
// (e.g. `/setup#x` inside `/guide/setup#x`) and must be skipped, or the edit
|
|
31
|
+
// would corrupt an unrelated, valid link on the same line.
|
|
32
|
+
const URL_PATH_CHAR = /[A-Za-z0-9\-._~%/#?:@]/;
|
|
33
|
+
/** Index of the next occurrence of `href` at/after `from` that sits at a link
|
|
34
|
+
* boundary (not as a substring inside a longer path), or -1. */
|
|
35
|
+
function findHrefBoundary(line, href, from) {
|
|
36
|
+
let pos = line.indexOf(href, from);
|
|
37
|
+
while (pos !== -1) {
|
|
38
|
+
if (pos === 0 || !URL_PATH_CHAR.test(line[pos - 1]))
|
|
39
|
+
return pos;
|
|
40
|
+
pos = line.indexOf(href, pos + 1);
|
|
41
|
+
}
|
|
42
|
+
return -1;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Apply edits, replacing each edit's oldText with newText on its specific
|
|
46
|
+
* (1-indexed) source line. Line-scoped, so an identical href on another line
|
|
47
|
+
* (e.g. a code sample) is never touched. Within a line: oldText is matched at a
|
|
48
|
+
* link boundary (so a short href is never replaced inside a longer one),
|
|
49
|
+
* matching runs against the ORIGINAL line (so an edit whose newText contains its
|
|
50
|
+
* oldText is never re-matched), and edits apply right-to-left so earlier
|
|
51
|
+
* positions keep their indices. Identical oldTexts on one line consume
|
|
52
|
+
* successive occurrences. Throws if a line is out of range or an oldText is
|
|
53
|
+
* absent — a missing match means the source changed since the fix was derived,
|
|
54
|
+
* and a partial/wrong edit must never be written.
|
|
55
|
+
*/
|
|
56
|
+
export function applyEdits(content, edits) {
|
|
57
|
+
if (edits.length === 0)
|
|
58
|
+
return content;
|
|
59
|
+
const lines = content.split('\n');
|
|
60
|
+
const byLine = new Map();
|
|
61
|
+
for (const edit of edits) {
|
|
62
|
+
const list = byLine.get(edit.line);
|
|
63
|
+
if (list)
|
|
64
|
+
list.push(edit);
|
|
65
|
+
else
|
|
66
|
+
byLine.set(edit.line, [edit]);
|
|
67
|
+
}
|
|
68
|
+
for (const [line, lineEdits] of byLine) {
|
|
69
|
+
const idx = line - 1;
|
|
70
|
+
if (idx < 0 || idx >= lines.length) {
|
|
71
|
+
throw new Error(`applyEdits: line ${line} out of range (${lines.length} lines)`);
|
|
72
|
+
}
|
|
73
|
+
const src = lines[idx];
|
|
74
|
+
// Resolve every edit to a concrete [start,end) span on the ORIGINAL line.
|
|
75
|
+
const spans = [];
|
|
76
|
+
const nextFrom = new Map();
|
|
77
|
+
for (const edit of lineEdits) {
|
|
78
|
+
const from = nextFrom.get(edit.oldText) ?? 0;
|
|
79
|
+
const start = findHrefBoundary(src, edit.oldText, from);
|
|
80
|
+
if (start === -1) {
|
|
81
|
+
throw new Error(`applyEdits: oldText not found on line ${line}: ${JSON.stringify(edit.oldText)}`);
|
|
82
|
+
}
|
|
83
|
+
const end = start + edit.oldText.length;
|
|
84
|
+
nextFrom.set(edit.oldText, end);
|
|
85
|
+
spans.push({ start, end, newText: edit.newText });
|
|
86
|
+
}
|
|
87
|
+
// Apply right-to-left so an earlier span's indices survive later edits.
|
|
88
|
+
spans.sort((a, b) => b.start - a.start);
|
|
89
|
+
let out = src;
|
|
90
|
+
for (const s of spans)
|
|
91
|
+
out = out.slice(0, s.start) + s.newText + out.slice(s.end);
|
|
92
|
+
lines[idx] = out;
|
|
93
|
+
}
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
// ---- Distance / nearest-slug (ported from validate-links.cjs) -------------
|
|
97
|
+
const MAX_SUGGESTION_DISTANCE = 2;
|
|
98
|
+
export function levenshtein(a, b) {
|
|
99
|
+
if (a === b)
|
|
100
|
+
return 0;
|
|
101
|
+
if (a.length === 0)
|
|
102
|
+
return b.length;
|
|
103
|
+
if (b.length === 0)
|
|
104
|
+
return a.length;
|
|
105
|
+
let prev = new Array(b.length + 1);
|
|
106
|
+
let curr = new Array(b.length + 1);
|
|
107
|
+
for (let j = 0; j <= b.length; j++)
|
|
108
|
+
prev[j] = j;
|
|
109
|
+
for (let i = 0; i < a.length; i++) {
|
|
110
|
+
curr[0] = i + 1;
|
|
111
|
+
for (let j = 0; j < b.length; j++) {
|
|
112
|
+
const cost = a[i] === b[j] ? 0 : 1;
|
|
113
|
+
curr[j + 1] = Math.min(curr[j] + 1, prev[j + 1] + 1, prev[j] + cost);
|
|
114
|
+
}
|
|
115
|
+
[prev, curr] = [curr, prev];
|
|
116
|
+
}
|
|
117
|
+
return prev[b.length];
|
|
118
|
+
}
|
|
119
|
+
export function closestSlug(fragment, slugs) {
|
|
120
|
+
if (!fragment)
|
|
121
|
+
return null;
|
|
122
|
+
let best = null;
|
|
123
|
+
let bestDist = Infinity;
|
|
124
|
+
let secondDist = Infinity;
|
|
125
|
+
for (const slug of slugs) {
|
|
126
|
+
if (Math.abs(fragment.length - slug.length) > MAX_SUGGESTION_DISTANCE)
|
|
127
|
+
continue;
|
|
128
|
+
const d = levenshtein(fragment, slug);
|
|
129
|
+
if (d < bestDist) {
|
|
130
|
+
secondDist = bestDist;
|
|
131
|
+
bestDist = d;
|
|
132
|
+
best = slug;
|
|
133
|
+
}
|
|
134
|
+
else if (d < secondDist) {
|
|
135
|
+
secondDist = d;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return best !== null && bestDist >= 1 && bestDist <= MAX_SUGGESTION_DISTANCE && bestDist < secondDist
|
|
139
|
+
? best
|
|
140
|
+
: null;
|
|
141
|
+
}
|
|
142
|
+
// ---- Link + locale helpers ------------------------------------------------
|
|
143
|
+
export function parseHref(href) {
|
|
144
|
+
const i = href.indexOf('#');
|
|
145
|
+
if (i === -1)
|
|
146
|
+
return { path: href, fragment: null };
|
|
147
|
+
return { path: i === 0 ? null : href.slice(0, i), fragment: href.slice(i + 1) || null };
|
|
148
|
+
}
|
|
149
|
+
export function normalizeLinkPath(linkPath, hostAtDocs) {
|
|
150
|
+
let p = linkPath;
|
|
151
|
+
if (hostAtDocs && p.startsWith('/docs/'))
|
|
152
|
+
p = p.slice('/docs/'.length);
|
|
153
|
+
if (p.startsWith('/'))
|
|
154
|
+
p = p.slice(1);
|
|
155
|
+
return p.replace(/\.mdx$/, '');
|
|
156
|
+
}
|
|
157
|
+
/** The default-locale twin of a localized page path, or null if already default. */
|
|
158
|
+
export function defaultTwinPath(pagePath, locales) {
|
|
159
|
+
const segments = pagePath.split('/');
|
|
160
|
+
const first = segments[0];
|
|
161
|
+
if (first !== locales.defaultCode && locales.codes.includes(first)) {
|
|
162
|
+
return segments.slice(1).join('/');
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
// ---- Broken-link fixer ----------------------------------------------------
|
|
167
|
+
function skip(reason) {
|
|
168
|
+
return { status: 'skipped', edits: [], reason };
|
|
169
|
+
}
|
|
170
|
+
export const fixBrokenLink = (warning, ctx) => {
|
|
171
|
+
const { path: linkPath, fragment } = parseHref(warning.link);
|
|
172
|
+
if (!fragment)
|
|
173
|
+
return skip('not a fragment link — out of scope for phase 1');
|
|
174
|
+
if (!linkPath)
|
|
175
|
+
return skip('same-page fragment — out of scope for phase 1');
|
|
176
|
+
if (warning.line == null)
|
|
177
|
+
return skip('warning has no line — cannot place a safe edit');
|
|
178
|
+
const targetPage = ctx.resolveLinkPagePath(linkPath);
|
|
179
|
+
if (!targetPage)
|
|
180
|
+
return skip('target page could not be resolved');
|
|
181
|
+
const targetHeadings = ctx.headingsOf(targetPage);
|
|
182
|
+
if (!targetHeadings)
|
|
183
|
+
return skip('target page headings could not be read');
|
|
184
|
+
const allIds = targetHeadings.map((h) => h.id);
|
|
185
|
+
// (0) Re-derivation: may already be valid in current source.
|
|
186
|
+
if (allIds.includes(fragment))
|
|
187
|
+
return skip('already valid — fragment exists on target');
|
|
188
|
+
// Emit only a fragment that is a REAL anchor on the target (never point at a
|
|
189
|
+
// non-existent or wrong-kind id), reconstructing the href (no substring replace).
|
|
190
|
+
const emit = (candidate, reason) => {
|
|
191
|
+
if (!allIds.includes(candidate))
|
|
192
|
+
return skip('internal: candidate is not a real anchor — refusing');
|
|
193
|
+
return {
|
|
194
|
+
status: 'fixed',
|
|
195
|
+
edits: [{ file: warning.file, line: warning.line, oldText: warning.link, newText: linkPath + '#' + candidate }],
|
|
196
|
+
reason,
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
// (1) Cross-locale positional map FIRST (high precision for localized targets).
|
|
200
|
+
// Ordinals over MARKDOWN headings only (so <Step>/<Update> can't misalign them),
|
|
201
|
+
// but the emitted id is the target heading's REAL id (full-set dedup).
|
|
202
|
+
const twinPage = defaultTwinPath(targetPage, ctx.locales);
|
|
203
|
+
if (twinPage) {
|
|
204
|
+
const twinHeadings = ctx.headingsOf(twinPage);
|
|
205
|
+
if (twinHeadings) {
|
|
206
|
+
const twinMd = twinHeadings.filter(isMarkdownHeading);
|
|
207
|
+
const targetMd = targetHeadings.filter(isMarkdownHeading);
|
|
208
|
+
const ordinal = twinMd.findIndex((h) => h.id === fragment);
|
|
209
|
+
if (ordinal !== -1 && twinMd.length === targetMd.length && targetMd[ordinal]) {
|
|
210
|
+
return emit(targetMd[ordinal].id, `cross-locale positional map: #${fragment} → #${targetMd[ordinal].id}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// (2) Near-miss fallback (typos within the same language).
|
|
215
|
+
const near = closestSlug(fragment, allIds);
|
|
216
|
+
if (near)
|
|
217
|
+
return emit(near, `near-miss: #${fragment} → #${near}`);
|
|
218
|
+
return skip(`no confident deterministic fix — structures diverge or target is ambiguous ${NEEDS_AI_MARKER}`);
|
|
219
|
+
};
|
|
220
|
+
// ---- Registry + engine entry point ----------------------------------------
|
|
221
|
+
/** Fixer registry keyed by BuildWarningType. Phase 1: broken_link only. */
|
|
222
|
+
export const FIXERS = {
|
|
223
|
+
broken_link: fixBrokenLink,
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Run the registered fixer for a warning. Deterministic-first is implemented
|
|
227
|
+
* inside each fixer (exact strategies, then 'skipped' for AI cases). The AI
|
|
228
|
+
* fallback tier is wired in Phase 2; until then a 'skipped' result is terminal.
|
|
229
|
+
*/
|
|
230
|
+
export function runFixer(warning, ctx) {
|
|
231
|
+
const fixer = FIXERS[warning.type];
|
|
232
|
+
if (!fixer)
|
|
233
|
+
return skip(`no fixer registered for type "${warning.type}"`);
|
|
234
|
+
return fixer(warning, ctx);
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=fix-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-engine.js","sourceRoot":"","sources":["../../src/lib/fix-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Set a single frontmatter field, preserving the body. gray-matter round-trips
|
|
2
|
+
* via matter.stringify (already used elsewhere in the CLI dep tree). */
|
|
3
|
+
export declare function setFrontmatterField(source: string, key: string, value: string): string;
|
|
4
|
+
//# sourceMappingURL=frontmatter-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-write.d.ts","sourceRoot":"","sources":["../../src/lib/frontmatter-write.ts"],"names":[],"mappings":"AAGA;yEACyE;AACzE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAItF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// builder/cli/src/lib/frontmatter-write.ts
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
/** Set a single frontmatter field, preserving the body. gray-matter round-trips
|
|
4
|
+
* via matter.stringify (already used elsewhere in the CLI dep tree). */
|
|
5
|
+
export function setFrontmatterField(source, key, value) {
|
|
6
|
+
const parsed = matter(source);
|
|
7
|
+
const data = { ...parsed.data, [key]: value };
|
|
8
|
+
return matter.stringify(parsed.content, data);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=frontmatter-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-write.js","sourceRoot":"","sources":["../../src/lib/frontmatter-write.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC;yEACyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,GAAW,EAAE,KAAa;IAC5E,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;IAC9C,OAAO,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heading Extractor (slug ids matching rehype-slug / the real DOM).
|
|
3
|
+
*
|
|
4
|
+
* AUTO-SYNCED: This file is automatically copied from build-service by vendor.js.
|
|
5
|
+
* DO NOT EDIT directly - edit builder/build-service/lib/heading-extractor.ts instead.
|
|
6
|
+
*/
|
|
7
|
+
export interface HeadingInfo {
|
|
8
|
+
id: string;
|
|
9
|
+
text: string;
|
|
10
|
+
level: number;
|
|
11
|
+
line: number;
|
|
12
|
+
/** When the entry is a <Step> inside a <Steps> block, this is its 1-based index within the block. */
|
|
13
|
+
stepNumber?: number;
|
|
14
|
+
/** True when the entry came from an <Update label="..."> anchor (vs a markdown heading). */
|
|
15
|
+
isUpdate?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate a URL-friendly slug from heading text.
|
|
19
|
+
*
|
|
20
|
+
* Delegates to github-slugger — the SAME library `rehype-slug` uses to assign the
|
|
21
|
+
* real heading `id` attributes during MDX compile (wired in render-doc-page.tsx).
|
|
22
|
+
* Using it here keeps the "On this page" TOC, <Update>/<Step> anchors, and the
|
|
23
|
+
* link validator byte-for-byte aligned with the actual DOM ids — including for
|
|
24
|
+
* non-ASCII headings, where the previous `[^a-z0-9]+ -> '-'` implementation
|
|
25
|
+
* diverged (accents/apostrophes/underscores became dashes) and produced dead
|
|
26
|
+
* anchors (e.g. heading "Abrir cualquier página" → real id `abrir-cualquier-página`,
|
|
27
|
+
* but the old slug was `abrir-cualquier-p-gina`).
|
|
28
|
+
*
|
|
29
|
+
* Stateless: repeated calls with the same text return the same slug. For
|
|
30
|
+
* per-document uniqueness across duplicate headings, use a `GithubSlugger`
|
|
31
|
+
* instance (as extractHeadings does) — it mirrors rehype-slug's `-1`/`-2`
|
|
32
|
+
* suffixing exactly.
|
|
33
|
+
*
|
|
34
|
+
* Mirrored in scripts/validate-links.cjs (CJS — can't import the ESM-only
|
|
35
|
+
* github-slugger), where the algorithm + regex are replicated and guarded by a
|
|
36
|
+
* drift test (`validate-links-slug.test.ts`). Keep both in sync.
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateSlug(text: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Extract all heading slugs from MDX content.
|
|
41
|
+
* Includes markdown headings, <Update label="..."> component anchors,
|
|
42
|
+
* and <Step title="..."> entries inside <Steps> blocks (with per-block numbering).
|
|
43
|
+
* Skips content inside fenced code blocks.
|
|
44
|
+
*
|
|
45
|
+
* A single page-scoped `GithubSlugger` assigns ids so duplicate titles (across
|
|
46
|
+
* H2s, Update labels, and Steps) get `-1`, `-2`, ... suffixes in source order —
|
|
47
|
+
* the same algorithm rehype-slug applies to the real markdown-heading DOM ids.
|
|
48
|
+
*/
|
|
49
|
+
export declare function extractHeadings(content: string): HeadingInfo[];
|
|
50
|
+
/**
|
|
51
|
+
* Build a map of page path -> heading slug set.
|
|
52
|
+
* Keys are page paths without .mdx extension (matching docs.json navigation format).
|
|
53
|
+
* Skips API endpoint pages (they generate dynamic anchors at render time).
|
|
54
|
+
*
|
|
55
|
+
* Unlike buildHeadingMapFromFiles() in validate-links.cjs, this doesn't add dual
|
|
56
|
+
* keys for index pages. The CJS validator handles index resolution via
|
|
57
|
+
* getTargetHeadingSlugs() which derives the key from the filesystem path.
|
|
58
|
+
*
|
|
59
|
+
* @param fileContents - Map of relative file paths (with .mdx) to content.
|
|
60
|
+
* Keys come from build.ts fileContents Map (e.g., 'getting-started.mdx', 'api/auth.mdx').
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildHeadingMap(fileContents: Map<string, string>): Map<string, Set<string>>;
|
|
63
|
+
//# sourceMappingURL=heading-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heading-extractor.d.ts","sourceRoot":"","sources":["../../src/lib/heading-extractor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,qGAAqG;IACrG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAgF9D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAU1B"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heading Extractor (slug ids matching rehype-slug / the real DOM).
|
|
3
|
+
*
|
|
4
|
+
* AUTO-SYNCED: This file is automatically copied from build-service by vendor.js.
|
|
5
|
+
* DO NOT EDIT directly - edit builder/build-service/lib/heading-extractor.ts instead.
|
|
6
|
+
*/
|
|
7
|
+
import GithubSlugger, { slug as githubSlug } from 'github-slugger';
|
|
8
|
+
/**
|
|
9
|
+
* Generate a URL-friendly slug from heading text.
|
|
10
|
+
*
|
|
11
|
+
* Delegates to github-slugger — the SAME library `rehype-slug` uses to assign the
|
|
12
|
+
* real heading `id` attributes during MDX compile (wired in render-doc-page.tsx).
|
|
13
|
+
* Using it here keeps the "On this page" TOC, <Update>/<Step> anchors, and the
|
|
14
|
+
* link validator byte-for-byte aligned with the actual DOM ids — including for
|
|
15
|
+
* non-ASCII headings, where the previous `[^a-z0-9]+ -> '-'` implementation
|
|
16
|
+
* diverged (accents/apostrophes/underscores became dashes) and produced dead
|
|
17
|
+
* anchors (e.g. heading "Abrir cualquier página" → real id `abrir-cualquier-página`,
|
|
18
|
+
* but the old slug was `abrir-cualquier-p-gina`).
|
|
19
|
+
*
|
|
20
|
+
* Stateless: repeated calls with the same text return the same slug. For
|
|
21
|
+
* per-document uniqueness across duplicate headings, use a `GithubSlugger`
|
|
22
|
+
* instance (as extractHeadings does) — it mirrors rehype-slug's `-1`/`-2`
|
|
23
|
+
* suffixing exactly.
|
|
24
|
+
*
|
|
25
|
+
* Mirrored in scripts/validate-links.cjs (CJS — can't import the ESM-only
|
|
26
|
+
* github-slugger), where the algorithm + regex are replicated and guarded by a
|
|
27
|
+
* drift test (`validate-links-slug.test.ts`). Keep both in sync.
|
|
28
|
+
*/
|
|
29
|
+
export function generateSlug(text) {
|
|
30
|
+
return githubSlug(text);
|
|
31
|
+
}
|
|
32
|
+
const HEADING_REGEX = /^(#{1,6})\s+(.+)$/;
|
|
33
|
+
const FENCE_REGEX = /^(`{3,}|~{3,})/;
|
|
34
|
+
// Global + matchAll (like STEP_TITLE_REGEX below) so multiple inline <Update>
|
|
35
|
+
// tags on one line each get an anchor, and `[^>]*` so `label=` need not be the
|
|
36
|
+
// first attribute (e.g. `<Update date=".." label="..">`). Alternation (not a
|
|
37
|
+
// character class) keeps an apostrophe inside a double-quoted label from
|
|
38
|
+
// terminating the capture. MUST only be used with matchAll — direct test/exec
|
|
39
|
+
// would share lastIndex across calls and miscount.
|
|
40
|
+
const UPDATE_LABEL_REGEX = /<Update\s+[^>]*label=(?:"([^"]+)"|'([^']+)')/g;
|
|
41
|
+
const STEPS_OPEN_REGEX = /<Steps(\s|>)/;
|
|
42
|
+
const STEPS_CLOSE_REGEX = /<\/Steps>/;
|
|
43
|
+
// Global flag — we iterate with matchAll so authors who inline multiple
|
|
44
|
+
// <Step> tags on one line get every one numbered.
|
|
45
|
+
// Alternation (not a character class) keeps the capture from terminating on
|
|
46
|
+
// apostrophes inside double-quoted attribute values (e.g. `<Step title="You're Live">`).
|
|
47
|
+
// MUST only be used with matchAll — direct `test`/`exec` would share
|
|
48
|
+
// lastIndex across calls and miscount.
|
|
49
|
+
const STEP_TITLE_REGEX = /<Step\s+[^>]*title=(?:"([^"]+)"|'([^']+)')/g;
|
|
50
|
+
/**
|
|
51
|
+
* Extract all heading slugs from MDX content.
|
|
52
|
+
* Includes markdown headings, <Update label="..."> component anchors,
|
|
53
|
+
* and <Step title="..."> entries inside <Steps> blocks (with per-block numbering).
|
|
54
|
+
* Skips content inside fenced code blocks.
|
|
55
|
+
*
|
|
56
|
+
* A single page-scoped `GithubSlugger` assigns ids so duplicate titles (across
|
|
57
|
+
* H2s, Update labels, and Steps) get `-1`, `-2`, ... suffixes in source order —
|
|
58
|
+
* the same algorithm rehype-slug applies to the real markdown-heading DOM ids.
|
|
59
|
+
*/
|
|
60
|
+
export function extractHeadings(content) {
|
|
61
|
+
const headings = [];
|
|
62
|
+
const lines = content.split('\n');
|
|
63
|
+
let inCodeBlock = false;
|
|
64
|
+
let fencePattern = '';
|
|
65
|
+
// Boolean (not depth counter) — resets on every <Steps> open so an unclosed
|
|
66
|
+
// block can't stick the counter across subsequent blocks.
|
|
67
|
+
let inStepsBlock = false;
|
|
68
|
+
let stepCounter = 0;
|
|
69
|
+
// Page-scoped slugger so duplicate titles get -1, -2, ... suffixes in source
|
|
70
|
+
// order, matching rehype-slug's per-document GithubSlugger.
|
|
71
|
+
const slugger = new GithubSlugger();
|
|
72
|
+
for (let i = 0; i < lines.length; i++) {
|
|
73
|
+
const line = lines[i];
|
|
74
|
+
const fenceMatch = line.match(FENCE_REGEX);
|
|
75
|
+
if (fenceMatch) {
|
|
76
|
+
if (!inCodeBlock) {
|
|
77
|
+
inCodeBlock = true;
|
|
78
|
+
fencePattern = fenceMatch[1];
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
else if (line.startsWith(fencePattern)) {
|
|
82
|
+
inCodeBlock = false;
|
|
83
|
+
fencePattern = '';
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (inCodeBlock)
|
|
88
|
+
continue;
|
|
89
|
+
// <Steps> open resets the per-block counter unconditionally. Ordering
|
|
90
|
+
// matters: open-reset must run BEFORE step matching on the same line so
|
|
91
|
+
// `<Steps><Step title="A">...</Step>` numbers "A" as 1.
|
|
92
|
+
if (STEPS_OPEN_REGEX.test(line)) {
|
|
93
|
+
inStepsBlock = true;
|
|
94
|
+
stepCounter = 0;
|
|
95
|
+
}
|
|
96
|
+
const headingMatch = line.match(HEADING_REGEX);
|
|
97
|
+
if (headingMatch) {
|
|
98
|
+
const level = headingMatch[1].length;
|
|
99
|
+
const text = headingMatch[2].trim();
|
|
100
|
+
if (generateSlug(text)) {
|
|
101
|
+
const id = slugger.slug(text);
|
|
102
|
+
headings.push({ id, text, level, line: i + 1 });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const match of line.matchAll(UPDATE_LABEL_REGEX)) {
|
|
106
|
+
const text = match[1] ?? match[2];
|
|
107
|
+
if (!generateSlug(text))
|
|
108
|
+
continue;
|
|
109
|
+
const id = slugger.slug(text);
|
|
110
|
+
headings.push({ id, text, level: 2, line: i + 1, isUpdate: true });
|
|
111
|
+
}
|
|
112
|
+
if (inStepsBlock) {
|
|
113
|
+
for (const match of line.matchAll(STEP_TITLE_REGEX)) {
|
|
114
|
+
const text = match[1] ?? match[2];
|
|
115
|
+
if (!generateSlug(text))
|
|
116
|
+
continue;
|
|
117
|
+
stepCounter += 1;
|
|
118
|
+
const id = slugger.slug(text);
|
|
119
|
+
headings.push({
|
|
120
|
+
id,
|
|
121
|
+
text,
|
|
122
|
+
level: 3,
|
|
123
|
+
line: i + 1,
|
|
124
|
+
stepNumber: stepCounter,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// </Steps> close runs AFTER step matching so `</Steps>` on the same line
|
|
129
|
+
// as the last <Step> still picks up that step.
|
|
130
|
+
if (STEPS_CLOSE_REGEX.test(line)) {
|
|
131
|
+
inStepsBlock = false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return headings;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Check if MDX content has api: or openapi: in its frontmatter.
|
|
138
|
+
* API endpoint pages generate dynamic anchor IDs at render time (#param-*, etc.)
|
|
139
|
+
* that aren't in the MDX source, so fragment validation is skipped for them.
|
|
140
|
+
*/
|
|
141
|
+
function hasApiFrontmatter(content) {
|
|
142
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
143
|
+
if (!match)
|
|
144
|
+
return false;
|
|
145
|
+
return /^(openapi|api):/m.test(match[1]);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Build a map of page path -> heading slug set.
|
|
149
|
+
* Keys are page paths without .mdx extension (matching docs.json navigation format).
|
|
150
|
+
* Skips API endpoint pages (they generate dynamic anchors at render time).
|
|
151
|
+
*
|
|
152
|
+
* Unlike buildHeadingMapFromFiles() in validate-links.cjs, this doesn't add dual
|
|
153
|
+
* keys for index pages. The CJS validator handles index resolution via
|
|
154
|
+
* getTargetHeadingSlugs() which derives the key from the filesystem path.
|
|
155
|
+
*
|
|
156
|
+
* @param fileContents - Map of relative file paths (with .mdx) to content.
|
|
157
|
+
* Keys come from build.ts fileContents Map (e.g., 'getting-started.mdx', 'api/auth.mdx').
|
|
158
|
+
*/
|
|
159
|
+
export function buildHeadingMap(fileContents) {
|
|
160
|
+
const map = new Map();
|
|
161
|
+
for (const [filePath, content] of fileContents) {
|
|
162
|
+
if (hasApiFrontmatter(content))
|
|
163
|
+
continue;
|
|
164
|
+
const headings = extractHeadings(content);
|
|
165
|
+
const slugs = new Set(headings.map((h) => h.id));
|
|
166
|
+
const pagePath = filePath.replace(/\.mdx$/, '');
|
|
167
|
+
map.set(pagePath, slugs);
|
|
168
|
+
}
|
|
169
|
+
return map;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=heading-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heading-extractor.js","sourceRoot":"","sources":["../../src/lib/heading-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface MissingDescription {
|
|
2
|
+
file: string;
|
|
3
|
+
title: string;
|
|
4
|
+
pageText: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function scanMissingDescriptions(pagePaths: string[], readPage: (p: string) => string | null, resolveFile?: (p: string) => string | null): MissingDescription[];
|
|
7
|
+
//# sourceMappingURL=missing-description-scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-description-scan.d.ts","sourceRoot":"","sources":["../../src/lib/missing-description-scan.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;CAAE;AAEtF,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,EAKtC,WAAW,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAwB,GAC5D,kBAAkB,EAAE,CAiBtB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// builder/cli/src/lib/missing-description-scan.ts
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
export function scanMissingDescriptions(pagePaths, readPage,
|
|
4
|
+
// Map a nav page key to its actual on-disk relative path. A page reference like
|
|
5
|
+
// "api" may be backed by "api.mdx" OR "api/index.mdx"; the write target must be the
|
|
6
|
+
// real file, not a fabricated "api.mdx" (which would miss the dir-index page).
|
|
7
|
+
// Defaults to "<page>.mdx" for callers (and tests) where the key IS the file base.
|
|
8
|
+
resolveFile = (p) => `${p}.mdx`) {
|
|
9
|
+
const out = [];
|
|
10
|
+
for (const page of pagePaths) {
|
|
11
|
+
const src = readPage(page);
|
|
12
|
+
if (src == null)
|
|
13
|
+
continue;
|
|
14
|
+
const file = resolveFile(page);
|
|
15
|
+
if (file == null)
|
|
16
|
+
continue; // page resolved to no on-disk file — nowhere to write a description
|
|
17
|
+
const parsed = matter(src);
|
|
18
|
+
const desc = parsed.data.description?.trim();
|
|
19
|
+
if (desc)
|
|
20
|
+
continue; // already has one
|
|
21
|
+
const title = parsed.data.title?.trim() ||
|
|
22
|
+
parsed.content.match(/^#\s+(.+)$/m)?.[1]?.trim() ||
|
|
23
|
+
page.split('/').pop() || page;
|
|
24
|
+
out.push({ file, title, pageText: parsed.content.trim() });
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=missing-description-scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-description-scan.js","sourceRoot":"","sources":["../../src/lib/missing-description-scan.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,MAAM,MAAM,aAAa,CAAC;AAIjC,MAAM,UAAU,uBAAuB,CACrC,SAAmB,EACnB,QAAsC;AACtC,gFAAgF;AAChF,oFAAoF;AACpF,+EAA+E;AAC/E,mFAAmF;AACnF,cAA4C,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM;IAE7D,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,GAAG,IAAI,IAAI;YAAE,SAAS;QAC1B,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,IAAI,IAAI,IAAI;YAAE,SAAS,CAAC,oEAAoE;QAChG,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAI,MAAM,CAAC,IAAI,CAAC,WAAkC,EAAE,IAAI,EAAE,CAAC;QACrE,IAAI,IAAI;YAAE,SAAS,CAAC,kBAAkB;QACtC,MAAM,KAAK,GACR,MAAM,CAAC,IAAI,CAAC,KAA4B,EAAE,IAAI,EAAE;YACjD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;YAChD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;QAChC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.151",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
@@ -128,9 +128,9 @@
|
|
|
128
128
|
"devDependencies": {
|
|
129
129
|
"@mdx-js/mdx": "^3.1.1",
|
|
130
130
|
"@types/fs-extra": "^11.0.0",
|
|
131
|
-
"@types/node": "^25.
|
|
131
|
+
"@types/node": "^25.9.3",
|
|
132
132
|
"typescript": "^6.0.2",
|
|
133
|
-
"vitest": "^4.1.
|
|
133
|
+
"vitest": "^4.1.9"
|
|
134
134
|
},
|
|
135
135
|
"engines": {
|
|
136
136
|
"node": ">=20.0.0"
|
|
@@ -64,7 +64,7 @@ export function ApiEndpoint({ method, path, baseUrl = 'https://api.jamdesk.com/a
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
|
-
<div data-api-endpoint="true" className="flex items-center gap-3 px-3 py-1.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg-primary)] mb-4 not-prose">
|
|
67
|
+
<div data-api-endpoint="true" data-markdown-ignore className="flex items-center gap-3 px-3 py-1.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg-primary)] mb-4 not-prose">
|
|
68
68
|
{/* Method badge */}
|
|
69
69
|
<span className={`px-2.5 py-1 text-xs font-bold rounded-md ${colors.bg} ${colors.text}`}>
|
|
70
70
|
{method}
|
|
@@ -829,7 +829,7 @@ export function OpenApiEndpoint({
|
|
|
829
829
|
|
|
830
830
|
if (playgroundOnly) {
|
|
831
831
|
return (
|
|
832
|
-
<div className="openapi-endpoint">
|
|
832
|
+
<div className="openapi-endpoint" data-markdown-ignore>
|
|
833
833
|
<ApiEndpoint
|
|
834
834
|
method={method}
|
|
835
835
|
path={path}
|
|
@@ -842,7 +842,12 @@ export function OpenApiEndpoint({
|
|
|
842
842
|
}
|
|
843
843
|
|
|
844
844
|
return (
|
|
845
|
-
|
|
845
|
+
/* data-markdown-ignore: the endpoint UI is generated from the spec
|
|
846
|
+
and intentionally absent from the .md export (which points agents
|
|
847
|
+
at api-specs.zip). afdocs strips [data-markdown-ignore] before the
|
|
848
|
+
markdown-content-parity comparison, so OpenAPI pages don't false-
|
|
849
|
+
fail parity. */
|
|
850
|
+
<div className="openapi-endpoint" data-markdown-ignore>
|
|
846
851
|
{/* Deprecation Warning */}
|
|
847
852
|
{deprecated && (
|
|
848
853
|
<div className="flex items-center gap-2 p-3 mb-4 bg-amber-500/10 border border-amber-500/20 rounded-lg text-amber-600 dark:text-amber-400">
|
|
@@ -16,6 +16,14 @@ const WORDMARK_STYLE: CSSProperties = {
|
|
|
16
16
|
backgroundColor: 'currentColor',
|
|
17
17
|
WebkitMask: WORDMARK_MASK,
|
|
18
18
|
mask: WORDMARK_MASK,
|
|
19
|
+
// The wordmark SVG's viewBox runs from the j-dot (top) to the j-descender
|
|
20
|
+
// (bottom), so its typographic baseline sits ~76% down the box. Under the
|
|
21
|
+
// link's `items-baseline` the span's box-bottom lands on the text baseline,
|
|
22
|
+
// which leaves the glyphs ~3.3px ABOVE it (measured live). Nudge down by that
|
|
23
|
+
// much so the wordmark and "Powered by" share a baseline. Inline transform
|
|
24
|
+
// (not a Tailwind `translate-y-*` class) because the theme's CSS build purges
|
|
25
|
+
// arbitrary transform utilities — that silently dropped the previous fix.
|
|
26
|
+
transform: 'translateY(3.5px)',
|
|
19
27
|
};
|
|
20
28
|
|
|
21
29
|
interface SocialFooterProps {
|
|
@@ -155,7 +163,7 @@ function BrandingLink({ projectSlug }: { projectSlug?: string }) {
|
|
|
155
163
|
<span
|
|
156
164
|
role="img"
|
|
157
165
|
aria-label="Jamdesk"
|
|
158
|
-
className="inline-block
|
|
166
|
+
className="inline-block text-[var(--color-text-muted)]/85 group-hover:text-[var(--color-text-primary)]"
|
|
159
167
|
style={WORDMARK_STYLE}
|
|
160
168
|
/>
|
|
161
169
|
</a>
|