@farming-labs/docs 0.1.1-beta.2 → 0.1.1
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/dist/api-reference-wh4_pwG8.mjs +802 -0
- package/dist/cli/index.d.mts +8 -1
- package/dist/cli/index.mjs +117 -8
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -0
- package/dist/mcp.d.mts +86 -0
- package/dist/mcp.mjs +485 -0
- package/dist/server.d.mts +3 -2
- package/dist/server.mjs +3 -801
- package/dist/{types-DpijZMth.d.mts → types-Bd3kyFF1.d.mts} +52 -1
- package/package.json +10 -3
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, join, relative } from "node:path";
|
|
3
|
+
import { getHtmlDocument } from "@scalar/core/libs/html-rendering";
|
|
4
|
+
|
|
5
|
+
//#region src/api-reference.ts
|
|
6
|
+
const NEXT_ROUTE_FILE_RE = /^route\.(ts|tsx|js|jsx)$/;
|
|
7
|
+
const SVELTE_ROUTE_FILE_RE = /^\+server\.(ts|js)$/;
|
|
8
|
+
const ASTRO_ROUTE_FILE_RE = /^[^.].*\.(ts|js|mts|mjs)$/;
|
|
9
|
+
const NUXT_ROUTE_FILE_RE = /^[^.].*\.(ts|js|mts|mjs)$/;
|
|
10
|
+
const TANSTACK_ROUTE_FILE_RE = /\.(ts|tsx|js|jsx)$/;
|
|
11
|
+
const METHOD_RE = /export\s+(?:async\s+function|function|const)\s+(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD|ALL)\b/g;
|
|
12
|
+
const METHOD_NAMES = [
|
|
13
|
+
"GET",
|
|
14
|
+
"POST",
|
|
15
|
+
"PUT",
|
|
16
|
+
"PATCH",
|
|
17
|
+
"DELETE",
|
|
18
|
+
"OPTIONS",
|
|
19
|
+
"HEAD"
|
|
20
|
+
];
|
|
21
|
+
function normalizePathSegment(value) {
|
|
22
|
+
return value.replace(/^\/+|\/+$/g, "");
|
|
23
|
+
}
|
|
24
|
+
function resolveApiReferenceConfig(value) {
|
|
25
|
+
if (value === true) return {
|
|
26
|
+
enabled: true,
|
|
27
|
+
path: "api-reference",
|
|
28
|
+
specUrl: void 0,
|
|
29
|
+
routeRoot: "api",
|
|
30
|
+
exclude: []
|
|
31
|
+
};
|
|
32
|
+
if (!value) return {
|
|
33
|
+
enabled: false,
|
|
34
|
+
path: "api-reference",
|
|
35
|
+
specUrl: void 0,
|
|
36
|
+
routeRoot: "api",
|
|
37
|
+
exclude: []
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
enabled: value.enabled !== false,
|
|
41
|
+
path: normalizePathSegment(value.path ?? "api-reference"),
|
|
42
|
+
specUrl: normalizeRemoteSpecUrl(value.specUrl),
|
|
43
|
+
routeRoot: normalizePathSegment(value.routeRoot ?? "api") || "api",
|
|
44
|
+
exclude: normalizeApiReferenceExcludes(value.exclude)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function normalizeRemoteSpecUrl(value) {
|
|
48
|
+
const trimmed = value?.trim();
|
|
49
|
+
if (!trimmed) return void 0;
|
|
50
|
+
return trimmed;
|
|
51
|
+
}
|
|
52
|
+
function buildApiReferencePageTitle(config, title = "API Reference") {
|
|
53
|
+
const template = config.metadata?.titleTemplate;
|
|
54
|
+
if (!template) return title;
|
|
55
|
+
return template.replace("%s", title);
|
|
56
|
+
}
|
|
57
|
+
function buildApiReferenceScalarCss(config) {
|
|
58
|
+
const theme = resolveTheme(config);
|
|
59
|
+
const colors = theme?.ui?.colors;
|
|
60
|
+
const typography = theme?.ui?.typography?.font?.style;
|
|
61
|
+
const layout = theme?.ui?.layout;
|
|
62
|
+
const radius = resolveApiReferenceRadius(theme);
|
|
63
|
+
const primary = colors?.primary ?? "#6366f1";
|
|
64
|
+
const border = colors?.border ?? "#2a2a2a";
|
|
65
|
+
const muted = colors?.muted ?? "#64748b";
|
|
66
|
+
const background = colors?.background ?? "#ffffff";
|
|
67
|
+
const card = colors?.card ?? background;
|
|
68
|
+
const foreground = colors?.foreground ?? inferApiReferenceForeground(theme, background);
|
|
69
|
+
const primaryForeground = colors?.primaryForeground ?? inferContrastingForeground(primary, theme, background);
|
|
70
|
+
const sidebarWidth = layout?.sidebarWidth ?? 280;
|
|
71
|
+
const sans = typography?.sans ?? "\"Geist\", \"Inter\", \"Segoe UI\", sans-serif";
|
|
72
|
+
const mono = typography?.mono ?? "\"Geist Mono\", \"SFMono-Regular\", \"Menlo\", monospace";
|
|
73
|
+
const isPixelBorder = theme?.name?.includes("pixel-border");
|
|
74
|
+
return `
|
|
75
|
+
:root {
|
|
76
|
+
--scalar-font: ${sans};
|
|
77
|
+
--scalar-font-code: ${mono};
|
|
78
|
+
--scalar-radius: ${radius};
|
|
79
|
+
--scalar-radius-sm: calc(${radius} + 2px);
|
|
80
|
+
--scalar-theme-primary: ${primary};
|
|
81
|
+
--scalar-theme-border: ${border};
|
|
82
|
+
--scalar-theme-muted: ${muted};
|
|
83
|
+
--scalar-theme-background: ${background};
|
|
84
|
+
--scalar-theme-card: ${card};
|
|
85
|
+
--scalar-theme-foreground: ${foreground};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.dark-mode {
|
|
89
|
+
--scalar-background-1: color-mix(
|
|
90
|
+
in srgb,
|
|
91
|
+
var(--scalar-theme-background) 96%,
|
|
92
|
+
var(--scalar-theme-primary) 4%
|
|
93
|
+
);
|
|
94
|
+
--scalar-background-2: color-mix(in srgb, var(--scalar-theme-card) 94%, var(--scalar-theme-primary) 6%);
|
|
95
|
+
--scalar-background-3: color-mix(
|
|
96
|
+
in srgb,
|
|
97
|
+
var(--scalar-theme-card) 90%,
|
|
98
|
+
var(--scalar-theme-foreground) 10%
|
|
99
|
+
);
|
|
100
|
+
--scalar-color-1: var(--scalar-theme-foreground);
|
|
101
|
+
--scalar-color-2: color-mix(in srgb, var(--scalar-theme-foreground) 72%, transparent);
|
|
102
|
+
--scalar-color-3: color-mix(in srgb, var(--scalar-theme-foreground) 52%, transparent);
|
|
103
|
+
--scalar-color-accent: var(--scalar-theme-primary);
|
|
104
|
+
--scalar-sidebar-background-1: var(--scalar-background-1);
|
|
105
|
+
--scalar-sidebar-background-2: var(--scalar-background-2);
|
|
106
|
+
--scalar-sidebar-color-1: var(--scalar-color-1);
|
|
107
|
+
--scalar-sidebar-color-2: var(--scalar-color-2);
|
|
108
|
+
--scalar-sidebar-search-background: var(--scalar-background-2);
|
|
109
|
+
--scalar-sidebar-search-border-color: var(--scalar-border-color);
|
|
110
|
+
--scalar-sidebar-search-color: var(--scalar-color-2);
|
|
111
|
+
--scalar-sidebar-color-active: var(--scalar-theme-primary);
|
|
112
|
+
--scalar-sidebar-item-active-background: color-mix(
|
|
113
|
+
in srgb,
|
|
114
|
+
var(--scalar-theme-primary) 7%,
|
|
115
|
+
transparent
|
|
116
|
+
);
|
|
117
|
+
--scalar-border-color: ${isPixelBorder ? "color-mix(in srgb, var(--scalar-theme-foreground) 10%, transparent)" : "color-mix(in srgb, var(--scalar-theme-border) 14%, rgba(255, 255, 255, 0.02))"};
|
|
118
|
+
--scalar-button-1: var(--scalar-theme-primary);
|
|
119
|
+
--scalar-button-1-color: ${primaryForeground};
|
|
120
|
+
--scalar-button-1-hover: color-mix(in srgb, var(--scalar-theme-primary) 88%, white 12%);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.light-mode {
|
|
124
|
+
--scalar-background-1: var(--scalar-theme-background);
|
|
125
|
+
--scalar-background-2: color-mix(in srgb, var(--scalar-theme-card) 92%, white 8%);
|
|
126
|
+
--scalar-background-3: color-mix(in srgb, var(--scalar-theme-card) 84%, black 4%);
|
|
127
|
+
--scalar-color-1: var(--scalar-theme-foreground);
|
|
128
|
+
--scalar-color-2: var(--scalar-theme-muted);
|
|
129
|
+
--scalar-color-3: color-mix(in srgb, var(--scalar-theme-muted) 78%, white 22%);
|
|
130
|
+
--scalar-color-accent: var(--scalar-theme-primary);
|
|
131
|
+
--scalar-sidebar-background-1: var(--scalar-background-1);
|
|
132
|
+
--scalar-sidebar-background-2: var(--scalar-background-2);
|
|
133
|
+
--scalar-sidebar-color-1: var(--scalar-color-1);
|
|
134
|
+
--scalar-sidebar-color-2: var(--scalar-color-2);
|
|
135
|
+
--scalar-sidebar-search-background: var(--scalar-background-2);
|
|
136
|
+
--scalar-sidebar-search-border-color: var(--scalar-border-color);
|
|
137
|
+
--scalar-sidebar-search-color: var(--scalar-color-2);
|
|
138
|
+
--scalar-sidebar-color-active: var(--scalar-theme-primary);
|
|
139
|
+
--scalar-sidebar-item-active-background: color-mix(
|
|
140
|
+
in srgb,
|
|
141
|
+
var(--scalar-theme-primary) 5%,
|
|
142
|
+
transparent
|
|
143
|
+
);
|
|
144
|
+
--scalar-border-color: ${isPixelBorder ? "color-mix(in srgb, var(--scalar-theme-foreground) 14%, transparent)" : "color-mix(in srgb, var(--scalar-theme-border) 30%, white 70%)"};
|
|
145
|
+
--scalar-button-1: var(--scalar-theme-primary);
|
|
146
|
+
--scalar-button-1-color: ${primaryForeground};
|
|
147
|
+
--scalar-button-1-hover: color-mix(in srgb, var(--scalar-theme-primary) 88%, black 12%);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
body {
|
|
151
|
+
background: var(--scalar-background-1);
|
|
152
|
+
color: var(--scalar-color-1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.t-doc__sidebar {
|
|
156
|
+
width: min(${sidebarWidth}px, 100vw);
|
|
157
|
+
border-right: 1px solid var(--scalar-border-color);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.scalar-card,
|
|
161
|
+
.t-doc__sidebar,
|
|
162
|
+
.references-layout .reference-layout__content .request-card,
|
|
163
|
+
.references-layout .reference-layout__content .response-card,
|
|
164
|
+
.references-layout .reference-layout__content .scalar-card-header,
|
|
165
|
+
.references-layout .reference-layout__content .scalar-card-footer,
|
|
166
|
+
.references-layout .reference-layout__content .section,
|
|
167
|
+
.references-layout .reference-layout__content .section-container {
|
|
168
|
+
border-color: var(--scalar-border-color) !important;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.t-doc__sidebar,
|
|
172
|
+
.t-doc__sidebar * {
|
|
173
|
+
font-family: var(--scalar-font);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.t-doc__sidebar .sidebar-search {
|
|
177
|
+
margin: 0.5rem 0 1rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.t-doc__sidebar .sidebar-search input {
|
|
181
|
+
border-radius: var(--scalar-radius-sm);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.t-doc__sidebar .sidebar-item,
|
|
185
|
+
.t-doc__sidebar .sidebar-heading {
|
|
186
|
+
border-radius: var(--scalar-radius-sm);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.t-doc__sidebar .sidebar-group-label {
|
|
190
|
+
font-size: 0.72rem;
|
|
191
|
+
letter-spacing: 0.08em;
|
|
192
|
+
text-transform: uppercase;
|
|
193
|
+
color: var(--scalar-color-3);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.t-doc__sidebar .sidebar-item--active {
|
|
197
|
+
font-weight: 600;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.scalar-card,
|
|
201
|
+
.references-layout .reference-layout__content .request-card,
|
|
202
|
+
.references-layout .reference-layout__content .response-card {
|
|
203
|
+
border-radius: var(--scalar-radius);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.references-layout .reference-layout__content {
|
|
207
|
+
padding-top: 1.5rem;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.references-layout .section-content,
|
|
211
|
+
.references-layout .section-flare {
|
|
212
|
+
background: transparent;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.references-layout .reference-layout__content,
|
|
216
|
+
.references-layout .reference-layout__content * {
|
|
217
|
+
font-family: var(--scalar-font);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.references-layout code,
|
|
221
|
+
.references-layout pre,
|
|
222
|
+
.references-layout .scalar-codeblock {
|
|
223
|
+
font-family: var(--scalar-font-code);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.references-layout,
|
|
227
|
+
.references-layout * {
|
|
228
|
+
color: inherit;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.references-layout .reference-layout__content .introduction,
|
|
232
|
+
.references-layout .reference-layout__content .section,
|
|
233
|
+
.references-layout .reference-layout__content .section-container,
|
|
234
|
+
.references-layout .reference-layout__content .operation-details,
|
|
235
|
+
.references-layout .reference-layout__content .markdown,
|
|
236
|
+
.references-layout .reference-layout__content .markdown *,
|
|
237
|
+
.references-layout .reference-layout__content .property,
|
|
238
|
+
.references-layout .reference-layout__content .property *,
|
|
239
|
+
.references-layout .reference-layout__content .parameter-item,
|
|
240
|
+
.references-layout .reference-layout__content .parameter-item *,
|
|
241
|
+
.references-layout .reference-layout__content .response-card,
|
|
242
|
+
.references-layout .reference-layout__content .response-card *,
|
|
243
|
+
.references-layout .reference-layout__content .request-card,
|
|
244
|
+
.references-layout .reference-layout__content .request-card * {
|
|
245
|
+
color: var(--scalar-color-1);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.references-layout a,
|
|
249
|
+
.references-layout button {
|
|
250
|
+
color: var(--scalar-color-1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
${isPixelBorder ? buildPixelBorderScalarCss() : ""}
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
256
|
+
function buildPixelBorderScalarCss() {
|
|
257
|
+
return `
|
|
258
|
+
.t-doc__sidebar,
|
|
259
|
+
.references-layout .reference-layout__content {
|
|
260
|
+
background-image:
|
|
261
|
+
repeating-linear-gradient(
|
|
262
|
+
-45deg,
|
|
263
|
+
color-mix(in srgb, var(--scalar-theme-border) 12%, transparent),
|
|
264
|
+
color-mix(in srgb, var(--scalar-theme-border) 12%, transparent) 1px,
|
|
265
|
+
transparent 1px,
|
|
266
|
+
transparent 8px
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.t-doc__sidebar .sidebar-group-label,
|
|
271
|
+
.t-doc__sidebar .sidebar-item,
|
|
272
|
+
.references-layout .reference-layout__content .section-header {
|
|
273
|
+
font-family: var(--scalar-font-code);
|
|
274
|
+
text-transform: uppercase;
|
|
275
|
+
letter-spacing: 0.06em;
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
}
|
|
279
|
+
function inferApiReferenceForeground(theme, background) {
|
|
280
|
+
if (looksDarkTheme(theme, background)) return "#f5f5f4";
|
|
281
|
+
return "#1b1b1b";
|
|
282
|
+
}
|
|
283
|
+
function resolveApiReferenceRadius(theme) {
|
|
284
|
+
if (theme?.ui?.radius) return theme.ui.radius;
|
|
285
|
+
const name = theme?.name?.toLowerCase() ?? "";
|
|
286
|
+
if (name.includes("pixel-border") || name.includes("darksharp")) return "0px";
|
|
287
|
+
return "var(--radius, 0.75rem)";
|
|
288
|
+
}
|
|
289
|
+
function inferContrastingForeground(value, theme, background) {
|
|
290
|
+
const rgb = parseCssColor(value);
|
|
291
|
+
if (rgb) return relativeLuminance(rgb) > .58 ? "#0b0b0b" : "#ffffff";
|
|
292
|
+
if (theme?.name?.includes("pixel-border")) return "#0b0b0b";
|
|
293
|
+
if (looksDarkTheme(theme, background)) return "#0b0b0b";
|
|
294
|
+
return "#ffffff";
|
|
295
|
+
}
|
|
296
|
+
function looksDarkTheme(theme, background) {
|
|
297
|
+
const name = theme?.name?.toLowerCase() ?? "";
|
|
298
|
+
if (name.includes("pixel-border") || name.includes("darksharp")) return true;
|
|
299
|
+
const rgb = parseCssColor(background);
|
|
300
|
+
if (!rgb) return false;
|
|
301
|
+
return relativeLuminance(rgb) < .45;
|
|
302
|
+
}
|
|
303
|
+
function parseCssColor(value) {
|
|
304
|
+
const normalized = value.trim().toLowerCase();
|
|
305
|
+
if (normalized.startsWith("#")) return parseHexColor(normalized);
|
|
306
|
+
const rgbMatch = normalized.match(/^rgba?\((.+)\)$/);
|
|
307
|
+
if (rgbMatch) {
|
|
308
|
+
const [r, g, b] = rgbMatch[1].split(",").slice(0, 3).map((part) => Number.parseFloat(part.trim()));
|
|
309
|
+
if ([
|
|
310
|
+
r,
|
|
311
|
+
g,
|
|
312
|
+
b
|
|
313
|
+
].every((channel) => Number.isFinite(channel))) return [
|
|
314
|
+
r,
|
|
315
|
+
g,
|
|
316
|
+
b
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
const hslMatch = normalized.match(/^hsla?\((.+)\)$/);
|
|
320
|
+
if (hslMatch) {
|
|
321
|
+
const [h, s, l] = hslMatch[1].split(/[\s,/]+/).filter(Boolean).slice(0, 3);
|
|
322
|
+
const hue = Number.parseFloat(h);
|
|
323
|
+
const saturation = Number.parseFloat(s.replace("%", ""));
|
|
324
|
+
const lightness = Number.parseFloat(l.replace("%", ""));
|
|
325
|
+
if ([
|
|
326
|
+
hue,
|
|
327
|
+
saturation,
|
|
328
|
+
lightness
|
|
329
|
+
].every((channel) => Number.isFinite(channel))) return hslToRgb(hue, saturation / 100, lightness / 100);
|
|
330
|
+
}
|
|
331
|
+
const oklchMatch = normalized.match(/^oklch\((.+)\)$/);
|
|
332
|
+
if (oklchMatch) {
|
|
333
|
+
const [l, c, h] = oklchMatch[1].split(/[\s/]+/).filter(Boolean);
|
|
334
|
+
const lightness = Number.parseFloat(l);
|
|
335
|
+
const chroma = Number.parseFloat(c);
|
|
336
|
+
const hue = Number.parseFloat(h);
|
|
337
|
+
if ([
|
|
338
|
+
lightness,
|
|
339
|
+
chroma,
|
|
340
|
+
hue
|
|
341
|
+
].every((channel) => Number.isFinite(channel))) return oklchToRgb(lightness, chroma, hue);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function oklchToRgb(l, c, h) {
|
|
345
|
+
const hue = h * Math.PI / 180;
|
|
346
|
+
const a = Math.cos(hue) * c;
|
|
347
|
+
const b = Math.sin(hue) * c;
|
|
348
|
+
const l_ = l + .3963377774 * a + .2158037573 * b;
|
|
349
|
+
const m_ = l - .1055613458 * a - .0638541728 * b;
|
|
350
|
+
const s_ = l - .0894841775 * a - 1.291485548 * b;
|
|
351
|
+
const l3 = l_ ** 3;
|
|
352
|
+
const m3 = m_ ** 3;
|
|
353
|
+
const s3 = s_ ** 3;
|
|
354
|
+
const linearR = 4.0767416621 * l3 - 3.3077115913 * m3 + .2309699292 * s3;
|
|
355
|
+
const linearG = -1.2684380046 * l3 + 2.6097574011 * m3 - .3413193965 * s3;
|
|
356
|
+
const linearB = -.0041960863 * l3 - .7034186147 * m3 + 1.707614701 * s3;
|
|
357
|
+
return [
|
|
358
|
+
srgbFromLinear(linearR),
|
|
359
|
+
srgbFromLinear(linearG),
|
|
360
|
+
srgbFromLinear(linearB)
|
|
361
|
+
];
|
|
362
|
+
}
|
|
363
|
+
function srgbFromLinear(value) {
|
|
364
|
+
const normalized = value <= .0031308 ? value * 12.92 : 1.055 * value ** (1 / 2.4) - .055;
|
|
365
|
+
return Math.max(0, Math.min(255, Math.round(normalized * 255)));
|
|
366
|
+
}
|
|
367
|
+
function parseHexColor(value) {
|
|
368
|
+
const raw = value.slice(1);
|
|
369
|
+
if (raw.length === 3) return [
|
|
370
|
+
Number.parseInt(raw[0] + raw[0], 16),
|
|
371
|
+
Number.parseInt(raw[1] + raw[1], 16),
|
|
372
|
+
Number.parseInt(raw[2] + raw[2], 16)
|
|
373
|
+
];
|
|
374
|
+
if (raw.length === 6 || raw.length === 8) return [
|
|
375
|
+
Number.parseInt(raw.slice(0, 2), 16),
|
|
376
|
+
Number.parseInt(raw.slice(2, 4), 16),
|
|
377
|
+
Number.parseInt(raw.slice(4, 6), 16)
|
|
378
|
+
];
|
|
379
|
+
}
|
|
380
|
+
function hslToRgb(h, s, l) {
|
|
381
|
+
const hue = (h % 360 + 360) % 360 / 360;
|
|
382
|
+
if (s === 0) {
|
|
383
|
+
const gray = Math.round(l * 255);
|
|
384
|
+
return [
|
|
385
|
+
gray,
|
|
386
|
+
gray,
|
|
387
|
+
gray
|
|
388
|
+
];
|
|
389
|
+
}
|
|
390
|
+
const q = l < .5 ? l * (1 + s) : l + s - l * s;
|
|
391
|
+
const p = 2 * l - q;
|
|
392
|
+
const convert = (channel) => {
|
|
393
|
+
let t = channel;
|
|
394
|
+
if (t < 0) t += 1;
|
|
395
|
+
if (t > 1) t -= 1;
|
|
396
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
397
|
+
if (t < 1 / 2) return q;
|
|
398
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
399
|
+
return p;
|
|
400
|
+
};
|
|
401
|
+
return [
|
|
402
|
+
Math.round(convert(hue + 1 / 3) * 255),
|
|
403
|
+
Math.round(convert(hue) * 255),
|
|
404
|
+
Math.round(convert(hue - 1 / 3) * 255)
|
|
405
|
+
];
|
|
406
|
+
}
|
|
407
|
+
function relativeLuminance([r, g, b]) {
|
|
408
|
+
const normalize = (channel) => {
|
|
409
|
+
const value = channel / 255;
|
|
410
|
+
return value <= .03928 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4;
|
|
411
|
+
};
|
|
412
|
+
return .2126 * normalize(r) + .7152 * normalize(g) + .0722 * normalize(b);
|
|
413
|
+
}
|
|
414
|
+
function buildApiReferenceOpenApiDocument(config, options) {
|
|
415
|
+
if (resolveApiReferenceConfig(config.apiReference).specUrl) return buildUnavailableOpenApiDocument(config, `Remote OpenAPI specs require the async API reference builder. Use the framework route helper or buildApiReferenceOpenApiDocumentAsync().`);
|
|
416
|
+
const routes = buildApiReferenceRoutes(config, options);
|
|
417
|
+
return buildOpenApiDocumentFromRoutes(config, options.framework, routes);
|
|
418
|
+
}
|
|
419
|
+
async function buildApiReferenceOpenApiDocumentAsync(config, options) {
|
|
420
|
+
const apiReference = resolveApiReferenceConfig(config.apiReference);
|
|
421
|
+
if (!apiReference.specUrl) return buildApiReferenceOpenApiDocument(config, options);
|
|
422
|
+
try {
|
|
423
|
+
return normalizeRemoteOpenApiDocument(await fetchRemoteOpenApiDocument(apiReference.specUrl), config);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
return buildUnavailableOpenApiDocument(config, `Unable to load the remote OpenAPI JSON. ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function buildOpenApiDocumentFromRoutes(config, framework, routes) {
|
|
429
|
+
const tags = Array.from(new Set(routes.map((route) => route.tag))).map((name) => ({
|
|
430
|
+
name,
|
|
431
|
+
description: `${name} endpoints`
|
|
432
|
+
}));
|
|
433
|
+
return {
|
|
434
|
+
openapi: "3.1.0",
|
|
435
|
+
info: {
|
|
436
|
+
title: "API Reference",
|
|
437
|
+
description: config.metadata?.description ?? `Generated API reference for ${framework}.`,
|
|
438
|
+
version: "0.0.0"
|
|
439
|
+
},
|
|
440
|
+
servers: [{ url: "/" }],
|
|
441
|
+
tags,
|
|
442
|
+
paths: buildOpenApiPaths(routes)
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function buildApiReferenceHtmlDocument(config, options) {
|
|
446
|
+
return buildApiReferenceHtmlDocumentFromDocument(config, options, buildApiReferenceOpenApiDocument(config, options));
|
|
447
|
+
}
|
|
448
|
+
async function buildApiReferenceHtmlDocumentAsync(config, options) {
|
|
449
|
+
return buildApiReferenceHtmlDocumentFromDocument(config, options, await buildApiReferenceOpenApiDocumentAsync(config, options));
|
|
450
|
+
}
|
|
451
|
+
function buildApiReferenceHtmlDocumentFromDocument(config, options, document) {
|
|
452
|
+
const apiReference = resolveApiReferenceConfig(config.apiReference);
|
|
453
|
+
const title = options.title ?? "API Reference";
|
|
454
|
+
return getHtmlDocument({
|
|
455
|
+
pageTitle: buildApiReferencePageTitle(config, title),
|
|
456
|
+
title,
|
|
457
|
+
content: () => document,
|
|
458
|
+
theme: "deepSpace",
|
|
459
|
+
layout: "modern",
|
|
460
|
+
customCss: buildApiReferenceScalarCss(config),
|
|
461
|
+
pathRouting: { basePath: `/${apiReference.path}` },
|
|
462
|
+
showSidebar: true,
|
|
463
|
+
defaultOpenFirstTag: true,
|
|
464
|
+
tagsSorter: "alpha",
|
|
465
|
+
operationsSorter: "alpha",
|
|
466
|
+
operationTitleSource: "summary",
|
|
467
|
+
defaultHttpClient: {
|
|
468
|
+
targetKey: "shell",
|
|
469
|
+
clientKey: "curl"
|
|
470
|
+
},
|
|
471
|
+
documentDownloadType: "json"
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
async function fetchRemoteOpenApiDocument(specUrl) {
|
|
475
|
+
let url;
|
|
476
|
+
try {
|
|
477
|
+
url = new URL(specUrl);
|
|
478
|
+
} catch {
|
|
479
|
+
throw new Error("`apiReference.specUrl` must be an absolute URL.");
|
|
480
|
+
}
|
|
481
|
+
const response = await fetch(url, { headers: { accept: "application/json" } });
|
|
482
|
+
if (!response.ok) throw new Error(`Received ${response.status} ${response.statusText}`.trim());
|
|
483
|
+
const body = await response.text();
|
|
484
|
+
if (!body.trim()) throw new Error("The remote endpoint returned an empty response.");
|
|
485
|
+
let parsed;
|
|
486
|
+
try {
|
|
487
|
+
parsed = JSON.parse(body);
|
|
488
|
+
} catch {
|
|
489
|
+
throw new Error("The remote endpoint did not return valid JSON.");
|
|
490
|
+
}
|
|
491
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("The remote endpoint returned a JSON value instead of an OpenAPI object.");
|
|
492
|
+
if (!("openapi" in parsed) && !("swagger" in parsed)) throw new Error("The remote JSON does not look like an OpenAPI document.");
|
|
493
|
+
return parsed;
|
|
494
|
+
}
|
|
495
|
+
function normalizeRemoteOpenApiDocument(document, config) {
|
|
496
|
+
const info = document.info && typeof document.info === "object" && !Array.isArray(document.info) ? document.info : {};
|
|
497
|
+
return {
|
|
498
|
+
...document,
|
|
499
|
+
info: {
|
|
500
|
+
title: "API Reference",
|
|
501
|
+
version: "0.0.0",
|
|
502
|
+
...info,
|
|
503
|
+
description: typeof info.description === "string" && info.description.trim() ? info.description : config.metadata?.description
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function buildUnavailableOpenApiDocument(config, description) {
|
|
508
|
+
return {
|
|
509
|
+
openapi: "3.1.0",
|
|
510
|
+
info: {
|
|
511
|
+
title: "API Reference",
|
|
512
|
+
description,
|
|
513
|
+
version: "0.0.0"
|
|
514
|
+
},
|
|
515
|
+
servers: [{ url: "/" }],
|
|
516
|
+
tags: [{
|
|
517
|
+
name: "Unavailable",
|
|
518
|
+
description: config.metadata?.description ?? "OpenAPI spec could not be loaded."
|
|
519
|
+
}],
|
|
520
|
+
paths: {}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function buildApiReferenceRoutes(config, options) {
|
|
524
|
+
const apiReference = resolveApiReferenceConfig(config.apiReference);
|
|
525
|
+
if (!apiReference.enabled) return [];
|
|
526
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
527
|
+
switch (options.framework) {
|
|
528
|
+
case "next": return buildFileConventionRoutes({
|
|
529
|
+
rootDir,
|
|
530
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, getNextAppDir(rootDir)),
|
|
531
|
+
routePathBase: toRouteBase(apiReference.routeRoot, getNextAppDir(rootDir)),
|
|
532
|
+
isRouteFile: (name) => NEXT_ROUTE_FILE_RE.test(name),
|
|
533
|
+
toRouteSegments: (relativeFile) => relativeFile.split("/").slice(0, -1).filter(Boolean),
|
|
534
|
+
exclude: apiReference.exclude
|
|
535
|
+
});
|
|
536
|
+
case "sveltekit": return buildFileConventionRoutes({
|
|
537
|
+
rootDir,
|
|
538
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, "src/routes"),
|
|
539
|
+
routePathBase: toRouteBase(apiReference.routeRoot, "src/routes"),
|
|
540
|
+
isRouteFile: (name) => SVELTE_ROUTE_FILE_RE.test(name),
|
|
541
|
+
toRouteSegments: (relativeFile) => relativeFile.split("/").slice(0, -1).filter(Boolean),
|
|
542
|
+
exclude: apiReference.exclude
|
|
543
|
+
});
|
|
544
|
+
case "astro": return buildFileConventionRoutes({
|
|
545
|
+
rootDir,
|
|
546
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, "src/pages"),
|
|
547
|
+
routePathBase: toRouteBase(apiReference.routeRoot, "src/pages"),
|
|
548
|
+
isRouteFile: (name) => ASTRO_ROUTE_FILE_RE.test(name),
|
|
549
|
+
toRouteSegments: (relativeFile) => routeSegmentsFromEndpointFile(relativeFile),
|
|
550
|
+
exclude: apiReference.exclude
|
|
551
|
+
});
|
|
552
|
+
case "nuxt": return buildFileConventionRoutes({
|
|
553
|
+
rootDir,
|
|
554
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, "server"),
|
|
555
|
+
routePathBase: toRouteBase(apiReference.routeRoot, "server"),
|
|
556
|
+
isRouteFile: (name) => NUXT_ROUTE_FILE_RE.test(name),
|
|
557
|
+
toRouteSegments: (relativeFile) => routeSegmentsFromEndpointFile(stripNuxtMethodSuffix(relativeFile)),
|
|
558
|
+
exclude: apiReference.exclude,
|
|
559
|
+
getMethods: (source, file) => extractNuxtMethods(source, file)
|
|
560
|
+
});
|
|
561
|
+
case "tanstack-start": return buildTanstackRoutes(rootDir, apiReference);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function buildFileConventionRoutes({ rootDir, sourceDir, routePathBase, isRouteFile, toRouteSegments, exclude, getMethods = extractMethods }) {
|
|
565
|
+
const files = scanRouteFiles(sourceDir, isRouteFile);
|
|
566
|
+
const routes = [];
|
|
567
|
+
for (const file of files) {
|
|
568
|
+
const source = readFileSync(file, "utf-8");
|
|
569
|
+
const methods = getMethods(source, file);
|
|
570
|
+
if (methods.length === 0) continue;
|
|
571
|
+
const relativeFile = relative(sourceDir, file).replace(/\\/g, "/");
|
|
572
|
+
const routeSegments = toRouteSegments(relativeFile);
|
|
573
|
+
const routePath = buildRoutePath(routePathBase, routeSegments);
|
|
574
|
+
if (shouldExcludeRoute(exclude, routePath, relativeFile, routeSegments.join("/"))) continue;
|
|
575
|
+
routes.push(createApiReferenceRoute({
|
|
576
|
+
rootDir,
|
|
577
|
+
file,
|
|
578
|
+
source,
|
|
579
|
+
methods,
|
|
580
|
+
routePath
|
|
581
|
+
}));
|
|
582
|
+
}
|
|
583
|
+
return routes.sort((a, b) => a.routePath.localeCompare(b.routePath));
|
|
584
|
+
}
|
|
585
|
+
function buildTanstackRoutes(rootDir, apiReference) {
|
|
586
|
+
const routesDir = join(rootDir, "src", "routes");
|
|
587
|
+
const files = scanRouteFiles(routesDir, (name) => TANSTACK_ROUTE_FILE_RE.test(name));
|
|
588
|
+
const routeBase = `/${normalizePathSegment(apiReference.routeRoot)}`;
|
|
589
|
+
const routes = [];
|
|
590
|
+
for (const file of files) {
|
|
591
|
+
const source = readFileSync(file, "utf-8");
|
|
592
|
+
if (!source.includes("createFileRoute(") || !source.includes("handlers")) continue;
|
|
593
|
+
const pathMatch = source.match(/createFileRoute\(\s*["'`]([^"'`]+)["'`]\s*\)/);
|
|
594
|
+
if (!pathMatch) continue;
|
|
595
|
+
const routePath = normalizeTanstackRoutePath(pathMatch[1]);
|
|
596
|
+
if (!routePath.startsWith(routeBase)) continue;
|
|
597
|
+
const methods = extractTanstackMethods(source);
|
|
598
|
+
if (methods.length === 0) continue;
|
|
599
|
+
const relativeFile = relative(routesDir, file).replace(/\\/g, "/");
|
|
600
|
+
if (shouldExcludeRoute(apiReference.exclude, routePath, relativeFile, relativeFile)) continue;
|
|
601
|
+
routes.push(createApiReferenceRoute({
|
|
602
|
+
rootDir,
|
|
603
|
+
file,
|
|
604
|
+
source,
|
|
605
|
+
methods,
|
|
606
|
+
routePath
|
|
607
|
+
}));
|
|
608
|
+
}
|
|
609
|
+
return routes.sort((a, b) => a.routePath.localeCompare(b.routePath));
|
|
610
|
+
}
|
|
611
|
+
function createApiReferenceRoute({ rootDir, file, source, methods, routePath }) {
|
|
612
|
+
const docBlock = extractDocBlock(source);
|
|
613
|
+
const pathSegments = routePath.split("/").filter(Boolean);
|
|
614
|
+
const titleSegment = pathSegments[pathSegments.length - 1] ?? "overview";
|
|
615
|
+
const tagSegment = pathSegments[0] ?? "general";
|
|
616
|
+
const title = humanizeSegment(titleSegment);
|
|
617
|
+
return {
|
|
618
|
+
title,
|
|
619
|
+
summary: docBlock.summary ?? `${title} endpoint`,
|
|
620
|
+
description: docBlock.description,
|
|
621
|
+
routePath,
|
|
622
|
+
sourceFile: relative(rootDir, file).replace(/\\/g, "/"),
|
|
623
|
+
methods,
|
|
624
|
+
tag: humanizeSegment(tagSegment),
|
|
625
|
+
parameters: buildPathParameters(routePath)
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function buildPathParameters(routePath) {
|
|
629
|
+
const parameters = [];
|
|
630
|
+
for (const segment of routePath.split("/")) {
|
|
631
|
+
const match = segment.match(/^\{(.+)\}$/);
|
|
632
|
+
if (!match) continue;
|
|
633
|
+
parameters.push({
|
|
634
|
+
name: match[1],
|
|
635
|
+
in: "path",
|
|
636
|
+
required: true,
|
|
637
|
+
description: `${humanizeSegment(match[1])} path parameter.`,
|
|
638
|
+
schema: { type: "string" }
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
return parameters;
|
|
642
|
+
}
|
|
643
|
+
function buildOpenApiPaths(routes) {
|
|
644
|
+
const paths = {};
|
|
645
|
+
for (const route of routes) {
|
|
646
|
+
const pathItem = {};
|
|
647
|
+
for (const method of route.methods) pathItem[method.toLowerCase()] = {
|
|
648
|
+
tags: [route.tag],
|
|
649
|
+
summary: route.summary,
|
|
650
|
+
description: route.description ?? route.summary,
|
|
651
|
+
operationId: createOperationId(route, method),
|
|
652
|
+
...route.parameters.length > 0 ? { parameters: route.parameters } : {},
|
|
653
|
+
...buildRequestBody(method) ? { requestBody: buildRequestBody(method) } : {},
|
|
654
|
+
responses: buildResponses(method),
|
|
655
|
+
"x-farming-labs-source": route.sourceFile
|
|
656
|
+
};
|
|
657
|
+
paths[route.routePath] = pathItem;
|
|
658
|
+
}
|
|
659
|
+
return paths;
|
|
660
|
+
}
|
|
661
|
+
function buildRequestBody(method) {
|
|
662
|
+
if (![
|
|
663
|
+
"POST",
|
|
664
|
+
"PUT",
|
|
665
|
+
"PATCH"
|
|
666
|
+
].includes(method)) return void 0;
|
|
667
|
+
return {
|
|
668
|
+
required: method === "POST",
|
|
669
|
+
content: { "application/json": {
|
|
670
|
+
schema: {
|
|
671
|
+
type: "object",
|
|
672
|
+
additionalProperties: true
|
|
673
|
+
},
|
|
674
|
+
example: { example: true }
|
|
675
|
+
} }
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function buildResponses(method) {
|
|
679
|
+
return { "200": {
|
|
680
|
+
description: method === "DELETE" ? "Resource removed successfully." : "Request completed successfully.",
|
|
681
|
+
content: { "application/json": {
|
|
682
|
+
schema: {
|
|
683
|
+
type: "object",
|
|
684
|
+
additionalProperties: true
|
|
685
|
+
},
|
|
686
|
+
example: { ok: true }
|
|
687
|
+
} }
|
|
688
|
+
} };
|
|
689
|
+
}
|
|
690
|
+
function createOperationId(route, method) {
|
|
691
|
+
return `${method.toLowerCase()}_${route.routePath.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
692
|
+
}
|
|
693
|
+
function resolveTheme(config) {
|
|
694
|
+
return config.theme;
|
|
695
|
+
}
|
|
696
|
+
function normalizeApiReferenceExcludes(values) {
|
|
697
|
+
return (values ?? []).map(normalizeExcludeMatcher).filter(Boolean);
|
|
698
|
+
}
|
|
699
|
+
function normalizeExcludeMatcher(value) {
|
|
700
|
+
return value.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").replace(/\.(ts|tsx|js|jsx|mjs|mts)$/i, "").replace(/\/route$/i, "").replace(/\/\+server$/i, "").replace(/\/index$/i, "").replace(/\.(get|post|put|patch|delete|options|head)$/i, "");
|
|
701
|
+
}
|
|
702
|
+
function shouldExcludeRoute(excludes, routePath, relativeFile, relativeDir) {
|
|
703
|
+
if (excludes.length === 0) return false;
|
|
704
|
+
const candidates = new Set([
|
|
705
|
+
normalizeExcludeMatcher(routePath),
|
|
706
|
+
normalizeExcludeMatcher(routePath.replace(/^\/+/, "")),
|
|
707
|
+
normalizeExcludeMatcher(relativeFile),
|
|
708
|
+
normalizeExcludeMatcher(relativeDir)
|
|
709
|
+
]);
|
|
710
|
+
return excludes.some((entry) => candidates.has(entry));
|
|
711
|
+
}
|
|
712
|
+
function scanRouteFiles(dir, isRouteFile) {
|
|
713
|
+
if (!existsSync(dir)) return [];
|
|
714
|
+
const results = [];
|
|
715
|
+
for (const name of readdirSync(dir)) {
|
|
716
|
+
const full = join(dir, name);
|
|
717
|
+
if (statSync(full).isDirectory()) {
|
|
718
|
+
results.push(...scanRouteFiles(full, isRouteFile));
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
if (isRouteFile(name)) results.push(full);
|
|
722
|
+
}
|
|
723
|
+
return results;
|
|
724
|
+
}
|
|
725
|
+
function resolveRootedDir(rootDir, routeRoot, defaultRoot) {
|
|
726
|
+
const normalized = normalizePathSegment(routeRoot) || "api";
|
|
727
|
+
if (normalized === defaultRoot || normalized.startsWith(`${defaultRoot}/`)) return join(rootDir, ...normalized.split("/"));
|
|
728
|
+
return join(rootDir, ...defaultRoot.split("/"), ...normalized.split("/"));
|
|
729
|
+
}
|
|
730
|
+
function toRouteBase(routeRoot, defaultRoot) {
|
|
731
|
+
const normalized = normalizePathSegment(routeRoot) || "api";
|
|
732
|
+
return `/${normalizePathSegment(normalized === defaultRoot || normalized.startsWith(`${defaultRoot}/`) ? normalized.slice(defaultRoot.length).replace(/^\/+/, "") : normalized)}`;
|
|
733
|
+
}
|
|
734
|
+
function getNextAppDir(rootDir) {
|
|
735
|
+
if (existsSync(join(rootDir, "src", "app"))) return "src/app";
|
|
736
|
+
return "app";
|
|
737
|
+
}
|
|
738
|
+
function routeSegmentsFromEndpointFile(relativeFile) {
|
|
739
|
+
const segments = relativeFile.split("/");
|
|
740
|
+
const name = (segments.pop() ?? "").replace(/\.(ts|js|mts|mjs)$/i, "");
|
|
741
|
+
if (name !== "index") segments.push(name);
|
|
742
|
+
return segments.filter(Boolean);
|
|
743
|
+
}
|
|
744
|
+
function stripNuxtMethodSuffix(relativeFile) {
|
|
745
|
+
return relativeFile.replace(/\.(get|post|put|patch|delete|options|head)(?=\.(ts|js|mts|mjs)$)/i, "");
|
|
746
|
+
}
|
|
747
|
+
function buildRoutePath(basePath, rawSegments) {
|
|
748
|
+
const segments = rawSegments.filter(Boolean).map((segment) => endpointSegmentFromConvention(segment)).join("/");
|
|
749
|
+
const path = [normalizePathSegment(basePath), segments].filter(Boolean).join("/");
|
|
750
|
+
return path ? `/${path}` : "/";
|
|
751
|
+
}
|
|
752
|
+
function endpointSegmentFromConvention(value) {
|
|
753
|
+
if (value.startsWith("[[...") && value.endsWith("]]")) return `{${value.slice(5, -2)}}`;
|
|
754
|
+
if (value.startsWith("[...") && value.endsWith("]")) return `{${value.slice(4, -1)}}`;
|
|
755
|
+
if (value.startsWith("[") && value.endsWith("]")) return `{${value.slice(1, -1)}}`;
|
|
756
|
+
return value;
|
|
757
|
+
}
|
|
758
|
+
function normalizeTanstackRoutePath(value) {
|
|
759
|
+
return `/${value.replace(/^\/+|\/+$/g, "").split("/").map((segment) => segment.startsWith("$") ? `{${segment.slice(1)}}` : segment).filter(Boolean).join("/")}`;
|
|
760
|
+
}
|
|
761
|
+
function extractDocBlock(source) {
|
|
762
|
+
const match = source.match(/\/\*\*([\s\S]*?)\*\//);
|
|
763
|
+
if (!match) return {};
|
|
764
|
+
const lines = match[1].split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim()).filter(Boolean).filter((line) => !line.startsWith("@"));
|
|
765
|
+
if (lines.length === 0) return {};
|
|
766
|
+
return {
|
|
767
|
+
summary: lines[0],
|
|
768
|
+
description: lines.slice(1).join(" ")
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
function extractMethods(source) {
|
|
772
|
+
const methods = /* @__PURE__ */ new Set();
|
|
773
|
+
for (const match of source.matchAll(METHOD_RE)) {
|
|
774
|
+
if (match[1] === "ALL") {
|
|
775
|
+
METHOD_NAMES.forEach((method) => methods.add(method));
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
methods.add(match[1]);
|
|
779
|
+
}
|
|
780
|
+
return Array.from(methods);
|
|
781
|
+
}
|
|
782
|
+
function extractNuxtMethods(source, file) {
|
|
783
|
+
const methods = extractMethods(source);
|
|
784
|
+
if (methods.length > 0) return methods;
|
|
785
|
+
const suffix = basename(file).match(/\.(get|post|put|patch|delete|options|head)\.(ts|js|mts|mjs)$/i);
|
|
786
|
+
if (suffix) return [suffix[1].toUpperCase()];
|
|
787
|
+
if (/defineEventHandler|eventHandler/.test(source)) return ["GET"];
|
|
788
|
+
return [];
|
|
789
|
+
}
|
|
790
|
+
function extractTanstackMethods(source) {
|
|
791
|
+
const methods = /* @__PURE__ */ new Set();
|
|
792
|
+
const handlersMatch = source.match(/handlers\s*:\s*\{([\s\S]*?)\}/m);
|
|
793
|
+
if (!handlersMatch) return [];
|
|
794
|
+
for (const match of handlersMatch[1].matchAll(/\b(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\s*:/g)) methods.add(match[1]);
|
|
795
|
+
return Array.from(methods);
|
|
796
|
+
}
|
|
797
|
+
function humanizeSegment(value) {
|
|
798
|
+
return value.replace(/^\{/, "").replace(/\}$/, "").replace(/^\[\[?\.{3}/, "").replace(/^\[/, "").replace(/\]\]?$/, "").replace(/^\$/, "").replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
//#endregion
|
|
802
|
+
export { buildApiReferencePageTitle as a, buildApiReferenceOpenApiDocumentAsync as i, buildApiReferenceHtmlDocumentAsync as n, buildApiReferenceScalarCss as o, buildApiReferenceOpenApiDocument as r, resolveApiReferenceConfig as s, buildApiReferenceHtmlDocument as t };
|