@farming-labs/docs 0.2.60 → 0.2.62

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 (51) hide show
  1. package/README.md +1 -1
  2. package/package.json +11 -1
  3. package/dist/agent-BFqyqEnC.mjs +0 -4547
  4. package/dist/agent-DlxriaTs.mjs +0 -624
  5. package/dist/agent-evals-kJs2Y9xR.mjs +0 -2144
  6. package/dist/agent-export-BgUaiW8f.mjs +0 -869
  7. package/dist/agent-scope-CCaIY1aK.mjs +0 -283
  8. package/dist/agents-Djh-HXih.mjs +0 -219
  9. package/dist/analytics-Bx44lg6d.mjs +0 -177
  10. package/dist/cli/index.d.mts +0 -15
  11. package/dist/cli/index.mjs +0 -452
  12. package/dist/client/react.d.mts +0 -45
  13. package/dist/client/react.mjs +0 -223
  14. package/dist/cloud-BH_sHX64.mjs +0 -1615
  15. package/dist/cloud-analytics-CSyFE6SS.mjs +0 -132
  16. package/dist/cloud-ask-ai-B2WnG4fF.d.mts +0 -23
  17. package/dist/cloud-ask-ai-hnJfj8-X.mjs +0 -382
  18. package/dist/code-blocks-qe0T8-xe.mjs +0 -871
  19. package/dist/codeblocks-Bq67u32v.mjs +0 -250
  20. package/dist/config-DASewQ0x.mjs +0 -363
  21. package/dist/dev-DgY5xGl9.mjs +0 -1333
  22. package/dist/docs-cloud-server.d.mts +0 -70
  23. package/dist/docs-cloud-server.mjs +0 -310
  24. package/dist/doctor-CO1VMcF_.mjs +0 -1906
  25. package/dist/downgrade-BZs86NVr.mjs +0 -184
  26. package/dist/errors-CVqZ3kOO.mjs +0 -20
  27. package/dist/golden-evaluations-BN9u2wxw.mjs +0 -1483
  28. package/dist/i18n-CAlj1ADU.mjs +0 -40
  29. package/dist/index.d.mts +0 -1099
  30. package/dist/index.mjs +0 -9
  31. package/dist/init-Bd_k06bR.mjs +0 -1233
  32. package/dist/mcp-B_yXL5G5.mjs +0 -137
  33. package/dist/mcp.d.mts +0 -287
  34. package/dist/mcp.mjs +0 -4135
  35. package/dist/metadata-BDuewuzq.mjs +0 -237
  36. package/dist/package-version-qik_4J6C.mjs +0 -128
  37. package/dist/reading-time-BkEft6SD.mjs +0 -741
  38. package/dist/review-NC-sOdXn.mjs +0 -665
  39. package/dist/robots-DskPvGPw.mjs +0 -178
  40. package/dist/robots-ltltiLJF.mjs +0 -197
  41. package/dist/search-C1JitPwi.d.mts +0 -397
  42. package/dist/search-D57JXQLj.mjs +0 -1758
  43. package/dist/search-o4Ud6OXv.mjs +0 -102
  44. package/dist/server.d.mts +0 -341
  45. package/dist/server.mjs +0 -11
  46. package/dist/sitemap-Cq-Yj_iA.mjs +0 -247
  47. package/dist/sitemap-server-C1ibVKOy.mjs +0 -1137
  48. package/dist/templates-DNw15P-x.mjs +0 -2373
  49. package/dist/types-XHABMh_f.d.mts +0 -3248
  50. package/dist/upgrade-BCJTCW3O.mjs +0 -56
  51. package/dist/utils-6UCLxv4B.mjs +0 -225
@@ -1,741 +0,0 @@
1
- import { Gt as API_CATALOG_PROFILE_URI, Nt as DEFAULT_SITEMAP_XML_ROUTE, Qt as DEFAULT_API_CATALOG_ROUTE, S as DEFAULT_MCP_WELL_KNOWN_ROUTE, Ut as AGENT_SKILLS_DISCOVERY_SCHEMA_URI, Wt as API_CATALOG_MEDIA_TYPE, Yt as DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, f as DEFAULT_DOCS_CONFIG_ROUTE, h as DEFAULT_LLMS_FULL_TXT_ROUTE, i as DEFAULT_AGENT_FEEDBACK_ROUTE, jt as DEFAULT_SITEMAP_MD_ROUTE, m as DEFAULT_DOCS_DIAGNOSTICS_ROUTE, qt as DEFAULT_AGENT_SKILLS_INDEX_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_LLMS_TXT_ROUTE, w as DEFAULT_SKILL_MD_ROUTE } from "./agent-BFqyqEnC.mjs";
2
- import { n as DEFAULT_ROBOTS_TXT_ROUTE } from "./robots-ltltiLJF.mjs";
3
- import matter from "gray-matter";
4
-
5
- //#region src/define-docs.ts
6
- /**
7
- * Define docs configuration. Validates and returns the config.
8
- */
9
- function defineDocs(config) {
10
- return {
11
- entry: config.entry ?? "docs",
12
- docsPath: config.docsPath,
13
- contentDir: config.contentDir,
14
- i18n: config.i18n,
15
- theme: config.theme,
16
- nav: config.nav,
17
- github: config.github,
18
- themeToggle: config.themeToggle,
19
- breadcrumb: config.breadcrumb,
20
- sidebar: config.sidebar,
21
- components: config.components,
22
- analytics: config.analytics,
23
- telemetry: config.telemetry,
24
- cloud: config.cloud,
25
- observability: config.observability,
26
- onCopyClick: config.onCopyClick,
27
- codeBlocks: config.codeBlocks,
28
- feedback: config.feedback,
29
- search: config.search,
30
- mcp: config.mcp,
31
- icons: config.icons,
32
- pageActions: config.pageActions,
33
- lastUpdated: config.lastUpdated,
34
- readingTime: config.readingTime,
35
- llmsTxt: config.llmsTxt,
36
- sitemap: config.sitemap,
37
- robots: config.robots,
38
- ai: config.ai,
39
- ordering: config.ordering,
40
- metadata: config.metadata,
41
- og: config.og,
42
- changelog: config.changelog,
43
- apiReference: config.apiReference,
44
- agent: config.agent,
45
- review: config.review
46
- };
47
- }
48
-
49
- //#endregion
50
- //#region src/changelog.ts
51
- function normalizePathSegment(value, fallback) {
52
- return (value ?? fallback).trim().replace(/^\/+|\/+$/g, "") || fallback;
53
- }
54
- function normalizeContentDir(value) {
55
- const trimmed = value?.trim();
56
- if (!trimmed) return "changelog";
57
- return trimmed.replace(/\/+$/, "") || "changelog";
58
- }
59
- function resolveChangelogConfig(value) {
60
- if (value === false || value === void 0) return {
61
- enabled: false,
62
- path: "changelog",
63
- contentDir: "changelog",
64
- title: "Changelog",
65
- description: void 0,
66
- search: true
67
- };
68
- if (value === true) return {
69
- enabled: true,
70
- path: "changelog",
71
- contentDir: "changelog",
72
- title: "Changelog",
73
- description: void 0,
74
- search: true
75
- };
76
- return {
77
- enabled: value.enabled !== false,
78
- path: normalizePathSegment(value.path, "changelog"),
79
- contentDir: normalizeContentDir(value.contentDir),
80
- title: value.title?.trim() || "Changelog",
81
- description: value.description?.trim() || void 0,
82
- search: value.search !== false,
83
- actionsComponent: value.actionsComponent
84
- };
85
- }
86
-
87
- //#endregion
88
- //#region src/utils.ts
89
- /**
90
- * Deep merge utility for theme overrides.
91
- * Merges objects recursively; later values override earlier ones.
92
- */
93
- function deepMerge(target, ...sources) {
94
- if (!sources.length) return target;
95
- const source = sources.shift();
96
- if (!source) return target;
97
- const result = { ...target };
98
- for (const key of Object.keys(source)) {
99
- const sourceVal = source[key];
100
- const targetVal = result[key];
101
- if (sourceVal && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal && typeof targetVal === "object" && !Array.isArray(targetVal)) result[key] = deepMerge(targetVal, sourceVal);
102
- else if (sourceVal !== void 0) result[key] = sourceVal;
103
- }
104
- if (sources.length) return deepMerge(result, ...sources);
105
- return result;
106
- }
107
-
108
- //#endregion
109
- //#region src/create-theme.ts
110
- /**
111
- * Create a theme preset factory.
112
- *
113
- * Returns a function that accepts optional overrides and deep-merges them
114
- * with the base theme defaults. This is the same pattern used by the
115
- * built-in `fumadocs()`, `darksharp()`, and `pixelBorder()` presets.
116
- *
117
- * @param baseTheme - The default theme configuration
118
- * @returns A factory function `(overrides?) => DocsTheme`
119
- *
120
- * @example
121
- * ```ts
122
- * import { createTheme } from "@farming-labs/docs";
123
- *
124
- * export const myTheme = createTheme({
125
- * name: "my-theme",
126
- * ui: {
127
- * colors: { primary: "#6366f1" },
128
- * layout: { contentWidth: 800 },
129
- * },
130
- * });
131
- * ```
132
- */
133
- function createTheme(baseTheme) {
134
- return function themeFactory(overrides = {}) {
135
- const merged = deepMerge(baseTheme, overrides);
136
- if (overrides.ui?.colors) merged._userColorOverrides = { ...overrides.ui.colors };
137
- return merged;
138
- };
139
- }
140
- /**
141
- * Extend an existing theme preset with additional defaults.
142
- *
143
- * Useful when you want to build on top of an existing theme (e.g. fumadocs)
144
- * rather than starting from scratch.
145
- *
146
- * @example
147
- * ```ts
148
- * import { extendTheme } from "@farming-labs/docs";
149
- * import { fumadocs } from "@farming-labs/theme/default";
150
- *
151
- * // Start with fumadocs defaults, override some values
152
- * export const myTheme = extendTheme(fumadocs(), {
153
- * name: "my-custom-fumadocs",
154
- * ui: { colors: { primary: "#22c55e" } },
155
- * });
156
- * ```
157
- */
158
- function extendTheme(baseTheme, extensions) {
159
- return deepMerge(baseTheme, extensions);
160
- }
161
-
162
- //#endregion
163
- //#region src/http-link.ts
164
- /** Split an HTTP list without treating delimiters inside quotes or URI references as separators. */
165
- function splitHttpList(value, delimiter) {
166
- const parts = [];
167
- let start = 0;
168
- let quoted = false;
169
- let escaped = false;
170
- let angleDepth = 0;
171
- for (let index = 0; index < value.length; index += 1) {
172
- const character = value[index];
173
- if (escaped) {
174
- escaped = false;
175
- continue;
176
- }
177
- if (quoted && character === "\\") {
178
- escaped = true;
179
- continue;
180
- }
181
- if (character === "\"") {
182
- quoted = !quoted;
183
- continue;
184
- }
185
- if (!quoted && character === "<") angleDepth += 1;
186
- if (!quoted && character === ">" && angleDepth > 0) angleDepth -= 1;
187
- if (!quoted && angleDepth === 0 && character === delimiter) {
188
- parts.push(value.slice(start, index).trim());
189
- start = index + 1;
190
- }
191
- }
192
- parts.push(value.slice(start).trim());
193
- return parts.filter(Boolean);
194
- }
195
- function unquoteHttpValue(value) {
196
- const trimmed = value.trim();
197
- if (!trimmed.startsWith("\"") || !trimmed.endsWith("\"")) return trimmed;
198
- return trimmed.slice(1, -1).replace(/\\(.)/g, "$1");
199
- }
200
- /** Parse Link header values while preserving the target/relation association. */
201
- function parseHttpLinkHeader(header) {
202
- if (!header) return [];
203
- const links = [];
204
- for (const value of splitHttpList(header, ",")) {
205
- const target = value.match(/^\s*<([^>]*)>/);
206
- if (!target) continue;
207
- const relations = [];
208
- for (const rawParameter of splitHttpList(value.slice(target[0].length), ";")) {
209
- const separator = rawParameter.indexOf("=");
210
- if (separator < 0 || rawParameter.slice(0, separator).trim().toLowerCase() !== "rel") continue;
211
- relations.push(...unquoteHttpValue(rawParameter.slice(separator + 1)).toLowerCase().split(/\s+/));
212
- }
213
- links.push({
214
- href: target[1],
215
- relations: relations.filter(Boolean)
216
- });
217
- }
218
- return links;
219
- }
220
- function httpLinkMatchesExpectation(links, expectation, responseUrl) {
221
- let expectedUrl;
222
- try {
223
- expectedUrl = new URL(expectation.href, responseUrl).toString();
224
- } catch {
225
- return false;
226
- }
227
- return links.some((link) => {
228
- try {
229
- return new URL(link.href, responseUrl).toString() === expectedUrl && link.relations.includes(expectation.rel.toLowerCase());
230
- } catch {
231
- return false;
232
- }
233
- });
234
- }
235
- function httpLinkHeaderHasTargetRelation(header, href, rel, responseUrl) {
236
- return httpLinkMatchesExpectation(parseHttpLinkHeader(header), {
237
- href,
238
- rel
239
- }, responseUrl);
240
- }
241
-
242
- //#endregion
243
- //#region src/agent-conformance.ts
244
- const DOCS_AGENT_CONTRACT_VERSION = "1.1";
245
- function normalizeOrigin(value) {
246
- return value.replace(/\/+$/, "");
247
- }
248
- function normalizeEntry(value) {
249
- return value.replace(/^\/+|\/+$/g, "") || "docs";
250
- }
251
- /**
252
- * The framework-neutral public surface every first-party adapter must implement.
253
- * Adapter tests consume this list so new routes and invariants are added once.
254
- */
255
- function createDocsAgentContractCases(options = {}) {
256
- const origin = normalizeOrigin(options.origin ?? "http://localhost");
257
- const entry = normalizeEntry(options.entry ?? "docs");
258
- const locale = options.locale === void 0 ? "fr" : options.locale;
259
- const url = (route) => `${origin}${route.startsWith("/") ? route : `/${route}`}`;
260
- const markdown = ["text/markdown"];
261
- const json = ["application/json"];
262
- const cases = [
263
- {
264
- surface: "discovery",
265
- request: { url: url(DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE) },
266
- expect: {
267
- statuses: [200],
268
- contentTypes: json,
269
- linkRelations: [{
270
- href: DEFAULT_API_CATALOG_ROUTE,
271
- rel: "api-catalog"
272
- }, {
273
- href: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
274
- rel: "service-meta"
275
- }]
276
- }
277
- },
278
- {
279
- surface: "api-catalog",
280
- request: { url: url(DEFAULT_API_CATALOG_ROUTE) },
281
- expect: {
282
- statuses: [200],
283
- contentTypes: [`${API_CATALOG_MEDIA_TYPE}; profile="${API_CATALOG_PROFILE_URI}"`],
284
- bodyIncludes: [
285
- "linkset",
286
- DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
287
- DEFAULT_AGENT_SKILLS_INDEX_ROUTE
288
- ],
289
- linkRelations: [{
290
- href: DEFAULT_API_CATALOG_ROUTE,
291
- rel: "api-catalog"
292
- }]
293
- }
294
- },
295
- {
296
- surface: "api-catalog-head",
297
- request: {
298
- url: url(DEFAULT_API_CATALOG_ROUTE),
299
- init: { method: "HEAD" }
300
- },
301
- expect: {
302
- statuses: [200],
303
- contentTypes: [`${API_CATALOG_MEDIA_TYPE}; profile="${API_CATALOG_PROFILE_URI}"`],
304
- bodyEmpty: true,
305
- linkRelations: [{
306
- href: DEFAULT_API_CATALOG_ROUTE,
307
- rel: "api-catalog"
308
- }]
309
- }
310
- },
311
- {
312
- surface: "agent-skills-index",
313
- request: { url: url(DEFAULT_AGENT_SKILLS_INDEX_ROUTE) },
314
- expect: {
315
- statuses: [200],
316
- contentTypes: json,
317
- bodyIncludes: [
318
- "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
319
- `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/docs/SKILL.md`,
320
- "sha256:"
321
- ],
322
- linkRelations: [{
323
- href: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/docs/SKILL.md`,
324
- rel: "item"
325
- }, {
326
- href: DEFAULT_API_CATALOG_ROUTE,
327
- rel: "api-catalog"
328
- }]
329
- }
330
- },
331
- {
332
- surface: "agent-skills-index-head",
333
- request: {
334
- url: url(DEFAULT_AGENT_SKILLS_INDEX_ROUTE),
335
- init: { method: "HEAD" }
336
- },
337
- expect: {
338
- statuses: [200],
339
- contentTypes: json,
340
- bodyEmpty: true,
341
- linkRelations: [{
342
- href: DEFAULT_API_CATALOG_ROUTE,
343
- rel: "api-catalog"
344
- }]
345
- }
346
- },
347
- {
348
- surface: "agent-skill",
349
- request: { url: url(`${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/docs/SKILL.md`) },
350
- expect: {
351
- statuses: [200],
352
- contentTypes: markdown,
353
- bodyIncludes: ["name: docs"],
354
- linkRelations: [{
355
- href: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
356
- rel: "collection"
357
- }, {
358
- href: DEFAULT_API_CATALOG_ROUTE,
359
- rel: "api-catalog"
360
- }]
361
- }
362
- },
363
- {
364
- surface: "agent-skill-head",
365
- request: {
366
- url: url(`${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/docs/SKILL.md`),
367
- init: { method: "HEAD" }
368
- },
369
- expect: {
370
- statuses: [200],
371
- contentTypes: markdown,
372
- bodyEmpty: true,
373
- linkRelations: [{
374
- href: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
375
- rel: "collection"
376
- }, {
377
- href: DEFAULT_API_CATALOG_ROUTE,
378
- rel: "api-catalog"
379
- }]
380
- }
381
- },
382
- {
383
- surface: "config",
384
- request: { url: url(DEFAULT_DOCS_CONFIG_ROUTE) },
385
- expect: {
386
- statuses: [200],
387
- contentTypes: json,
388
- bodyIncludes: ["docs-config-map.v1"]
389
- }
390
- },
391
- {
392
- surface: "diagnostics",
393
- request: { url: url(DEFAULT_DOCS_DIAGNOSTICS_ROUTE) },
394
- expect: {
395
- statuses: [200],
396
- contentTypes: json,
397
- bodyIncludes: ["docs-diagnostics.v1"]
398
- }
399
- },
400
- {
401
- surface: "feedback-schema",
402
- request: { url: url(`${DEFAULT_AGENT_FEEDBACK_ROUTE}/schema`) },
403
- expect: {
404
- statuses: [200],
405
- contentTypes: ["application/schema+json"]
406
- }
407
- },
408
- {
409
- surface: "markdown",
410
- request: { url: url(`/${entry}.md`) },
411
- expect: {
412
- statuses: [200],
413
- contentTypes: markdown,
414
- bodyIncludes: ["Introduction"]
415
- }
416
- },
417
- {
418
- surface: "markdown-accept",
419
- request: {
420
- url: url(`/${entry}`),
421
- init: { headers: { Accept: "text/markdown" } }
422
- },
423
- expect: {
424
- statuses: [200],
425
- contentTypes: markdown,
426
- bodyIncludes: ["Introduction"]
427
- }
428
- },
429
- {
430
- surface: "markdown-missing",
431
- request: { url: url(`/${entry}/missing.md`) },
432
- expect: {
433
- statuses: [200, 404],
434
- contentTypes: markdown,
435
- bodyIncludes: ["not found"]
436
- }
437
- },
438
- {
439
- surface: "llms",
440
- request: { url: url(DEFAULT_LLMS_TXT_ROUTE) },
441
- expect: {
442
- statuses: [200],
443
- contentTypes: ["text/plain"],
444
- bodyIncludes: ["Introduction"]
445
- }
446
- },
447
- {
448
- surface: "llms-full",
449
- request: { url: url(DEFAULT_LLMS_FULL_TXT_ROUTE) },
450
- expect: {
451
- statuses: [200],
452
- contentTypes: ["text/plain"],
453
- bodyIncludes: ["Introduction"]
454
- }
455
- },
456
- {
457
- surface: "agents",
458
- request: { url: url(DEFAULT_AGENTS_MD_ROUTE) },
459
- expect: {
460
- statuses: [200],
461
- contentTypes: markdown,
462
- bodyIncludes: ["AGENTS.md"]
463
- }
464
- },
465
- {
466
- surface: "skill",
467
- request: { url: url(DEFAULT_SKILL_MD_ROUTE) },
468
- expect: {
469
- statuses: [200],
470
- contentTypes: markdown,
471
- bodyIncludes: ["name:"]
472
- }
473
- },
474
- {
475
- surface: "sitemap-xml",
476
- request: { url: url(DEFAULT_SITEMAP_XML_ROUTE) },
477
- expect: {
478
- statuses: [200],
479
- contentTypes: ["application/xml", "text/xml"],
480
- bodyIncludes: ["<urlset"]
481
- }
482
- },
483
- {
484
- surface: "sitemap-markdown",
485
- request: { url: url(DEFAULT_SITEMAP_MD_ROUTE) },
486
- expect: {
487
- statuses: [200],
488
- contentTypes: markdown,
489
- bodyIncludes: ["Introduction"]
490
- }
491
- },
492
- {
493
- surface: "robots",
494
- request: { url: url(DEFAULT_ROBOTS_TXT_ROUTE) },
495
- expect: {
496
- statuses: [200],
497
- contentTypes: ["text/plain"],
498
- bodyIncludes: ["User-agent:"]
499
- }
500
- },
501
- {
502
- surface: "mcp",
503
- request: {
504
- url: url(DEFAULT_MCP_WELL_KNOWN_ROUTE),
505
- init: {
506
- method: "POST",
507
- headers: {
508
- Accept: "application/json, text/event-stream",
509
- "Content-Type": "application/json",
510
- "MCP-Protocol-Version": "2025-11-25"
511
- },
512
- body: JSON.stringify({
513
- jsonrpc: "2.0",
514
- id: 1,
515
- method: "initialize",
516
- params: {
517
- protocolVersion: "2025-11-25",
518
- capabilities: {},
519
- clientInfo: {
520
- name: "agent-conformance",
521
- version: DOCS_AGENT_CONTRACT_VERSION
522
- }
523
- }
524
- })
525
- }
526
- },
527
- expect: {
528
- statuses: [200],
529
- contentTypes: ["application/json", "text/event-stream"],
530
- bodyIncludes: ["serverInfo", "protocolVersion"]
531
- }
532
- }
533
- ];
534
- if (locale) {
535
- const markdownMissingIndex = cases.findIndex((contractCase) => contractCase.surface === "markdown-missing");
536
- cases.splice(markdownMissingIndex, 0, {
537
- surface: "markdown-locale",
538
- request: { url: url(`/${entry}.md?lang=${encodeURIComponent(locale)}`) },
539
- expect: {
540
- statuses: [200],
541
- contentTypes: markdown,
542
- bodyIncludes: ["Bonjour"]
543
- }
544
- });
545
- }
546
- return cases;
547
- }
548
- function parseParameterizedValue(value) {
549
- const [rawValue, ...rawParameters] = splitHttpList(value, ";");
550
- const normalizedValue = rawValue?.trim().toLowerCase();
551
- if (!normalizedValue) return null;
552
- const parameters = /* @__PURE__ */ new Map();
553
- for (const rawParameter of rawParameters) {
554
- const separator = rawParameter.indexOf("=");
555
- if (separator < 0) continue;
556
- const name = rawParameter.slice(0, separator).trim().toLowerCase();
557
- if (!name) continue;
558
- parameters.set(name, unquoteHttpValue(rawParameter.slice(separator + 1)));
559
- }
560
- return {
561
- value: normalizedValue,
562
- parameters
563
- };
564
- }
565
- function matchesContentType(actual, expected) {
566
- const parsedActual = parseParameterizedValue(actual);
567
- if (!parsedActual) return false;
568
- return expected.some((value) => {
569
- const parsedExpected = parseParameterizedValue(value);
570
- if (!parsedExpected || parsedExpected.value !== parsedActual.value) return false;
571
- return [...parsedExpected.parameters].every(([name, expectedValue]) => parsedActual.parameters.get(name) === expectedValue);
572
- });
573
- }
574
- function asRecord(value) {
575
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
576
- }
577
- function isNonEmptyString(value) {
578
- return typeof value === "string" && value.trim().length > 0;
579
- }
580
- async function sha256Utf8(content) {
581
- const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(content));
582
- return [...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join("");
583
- }
584
- async function validateAgentSkillsIndex(content, indexUrl, handle) {
585
- const issues = [];
586
- let parsed;
587
- try {
588
- parsed = JSON.parse(content);
589
- } catch {
590
- return ["Agent Skills index did not contain valid JSON"];
591
- }
592
- const root = asRecord(parsed);
593
- if (root?.$schema !== AGENT_SKILLS_DISCOVERY_SCHEMA_URI) issues.push(`Agent Skills index did not declare ${AGENT_SKILLS_DISCOVERY_SCHEMA_URI}`);
594
- if (!Array.isArray(root?.skills) || root.skills.length === 0) {
595
- issues.push("Agent Skills index did not declare any skills");
596
- return issues;
597
- }
598
- const seenNames = /* @__PURE__ */ new Set();
599
- const origin = new URL(indexUrl).origin;
600
- for (const rawSkill of root.skills) {
601
- const skill = asRecord(rawSkill);
602
- const name = skill?.name;
603
- const description = skill?.description;
604
- const artifactRoute = skill?.url;
605
- const digest = skill?.digest;
606
- if (!isNonEmptyString(name) || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(name) || name.length > 64 || skill?.type !== "skill-md" || !isNonEmptyString(description) || description.length > 1024 || !isNonEmptyString(artifactRoute) || typeof digest !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(digest)) {
607
- issues.push("Agent Skills index contained an invalid skill entry");
608
- continue;
609
- }
610
- if (seenNames.has(name)) {
611
- issues.push(`Agent Skills index declared duplicate skill ${JSON.stringify(name)}`);
612
- continue;
613
- }
614
- seenNames.add(name);
615
- let artifactUrl;
616
- try {
617
- artifactUrl = new URL(artifactRoute, indexUrl);
618
- } catch {
619
- issues.push(`Agent Skills artifact URL for ${JSON.stringify(name)} was invalid`);
620
- continue;
621
- }
622
- const expectedPath = `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/${name}/SKILL.md`;
623
- if (artifactUrl.origin !== origin || !artifactUrl.pathname.endsWith(expectedPath)) {
624
- issues.push(`Agent Skills artifact URL for ${JSON.stringify(name)} did not resolve to same-origin ${expectedPath}`);
625
- continue;
626
- }
627
- try {
628
- const response = await handle(new Request(artifactUrl), "agent-skill");
629
- const artifact = await response.text();
630
- if (response.status !== 200) issues.push(`Agent Skills artifact ${JSON.stringify(artifactRoute)} returned status ${response.status}`);
631
- const contentType = response.headers.get("content-type") ?? "";
632
- if (!matchesContentType(contentType, ["text/markdown"])) issues.push(`Agent Skills artifact ${JSON.stringify(artifactRoute)} returned content-type ${contentType || "<missing>"}`);
633
- const actualDigest = `sha256:${await sha256Utf8(artifact)}`;
634
- if (actualDigest !== digest) issues.push(`Agent Skills artifact ${JSON.stringify(artifactRoute)} digest ${actualDigest} did not match ${digest}`);
635
- } catch (error) {
636
- issues.push(`Agent Skills artifact ${JSON.stringify(artifactRoute)} failed: ${error instanceof Error ? error.message : String(error)}`);
637
- }
638
- }
639
- return issues;
640
- }
641
- async function runDocsAgentConformance(options) {
642
- const cases = createDocsAgentContractCases(options);
643
- const results = [];
644
- for (const contractCase of cases) {
645
- const issues = [];
646
- try {
647
- const response = await options.handle(new Request(contractCase.request.url, contractCase.request.init), contractCase.surface);
648
- const contentType = response.headers.get("content-type") ?? "";
649
- const body = await response.text();
650
- if (!contractCase.expect.statuses.includes(response.status)) issues.push(`expected status ${contractCase.expect.statuses.join(" or ")}, received ${response.status}`);
651
- if (!matchesContentType(contentType, contractCase.expect.contentTypes)) issues.push(`expected content-type ${contractCase.expect.contentTypes.join(" or ")}, received ${contentType || "<missing>"}`);
652
- for (const requiredText of contractCase.expect.bodyIncludes ?? []) if (!body.toLowerCase().includes(requiredText.toLowerCase())) issues.push(`response body did not include ${JSON.stringify(requiredText)}`);
653
- if (contractCase.expect.bodyEmpty && body !== "") issues.push(`expected an empty response body, received ${body.length} characters`);
654
- if (contractCase.surface === "agent-skills-index") issues.push(...await validateAgentSkillsIndex(body, contractCase.request.url, options.handle));
655
- for (const [header, requiredValues] of Object.entries(contractCase.expect.headerIncludes ?? {})) {
656
- const actual = response.headers.get(header) ?? "";
657
- for (const requiredValue of requiredValues) if (!actual.toLowerCase().includes(requiredValue.toLowerCase())) issues.push(`response header ${JSON.stringify(header)} did not include ${JSON.stringify(requiredValue)}`);
658
- }
659
- const parsedLinks = parseHttpLinkHeader(response.headers.get("link"));
660
- for (const expectation of contractCase.expect.linkRelations ?? []) if (!httpLinkMatchesExpectation(parsedLinks, expectation, contractCase.request.url)) issues.push(`response Link header did not include ${JSON.stringify(expectation.href)} with rel=${JSON.stringify(expectation.rel)} in the same link-value`);
661
- results.push({
662
- surface: contractCase.surface,
663
- passed: issues.length === 0,
664
- status: response.status,
665
- contentType,
666
- issues
667
- });
668
- } catch (error) {
669
- issues.push(error instanceof Error ? error.message : String(error));
670
- results.push({
671
- surface: contractCase.surface,
672
- passed: false,
673
- issues
674
- });
675
- }
676
- }
677
- return {
678
- adapter: options.adapter,
679
- contractVersion: DOCS_AGENT_CONTRACT_VERSION,
680
- passed: results.every((result) => result.passed),
681
- cases: results
682
- };
683
- }
684
-
685
- //#endregion
686
- //#region src/reading-time.ts
687
- function hasExplicitReadingTime(frontmatter) {
688
- return Object.prototype.hasOwnProperty.call(frontmatter ?? {}, "readingTime");
689
- }
690
- function normalizeWordsPerMinute(wordsPerMinute) {
691
- if (typeof wordsPerMinute !== "number" || !Number.isFinite(wordsPerMinute)) return 220;
692
- return Math.max(1, Math.floor(wordsPerMinute));
693
- }
694
- function stripNonReadingContent(content, options) {
695
- const includeCode = options?.includeCode === true;
696
- return (includeCode ? content.replace(/^(`{3,}|~{3,})[^\n]*$/gm, " ").replace(/`([^`\n]+)`/g, " $1 ") : content.replace(/(`{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/(~{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/`[^`\n]+`/g, " ")).replace(/!\[[^\]]*\]\([^)]+\)/g, " ").replace(/\[([^\]]+)\]\([^)]+\)/g, " $1 ").replace(/<[^>]+>/g, " ").replace(includeCode ? /[{}]/g : /\{[^{}]*\}/g, " ").replace(/https?:\/\/\S+/g, " ").replace(/[#>*_~|]/g, " ");
697
- }
698
- function estimateReadingTimeMinutes(content, wordsPerMinute, options) {
699
- const wordCount = stripNonReadingContent(content, options).match(/\b[\p{L}\p{N}][\p{L}\p{N}'’-]*\b/gu)?.length ?? 0;
700
- return Math.max(1, Math.ceil(wordCount / normalizeWordsPerMinute(wordsPerMinute)));
701
- }
702
- function resolveReadingTimeOptions(readingTime) {
703
- if (readingTime === true) return {
704
- enabled: true,
705
- format: "long",
706
- includeCode: false
707
- };
708
- if (readingTime === false || readingTime === void 0 || readingTime === null) return {
709
- enabled: false,
710
- format: "long",
711
- includeCode: false
712
- };
713
- if (typeof readingTime !== "object") return {
714
- enabled: false,
715
- format: "long",
716
- includeCode: false
717
- };
718
- return {
719
- enabled: readingTime.enabled !== false,
720
- wordsPerMinute: typeof readingTime.wordsPerMinute === "number" && Number.isFinite(readingTime.wordsPerMinute) ? readingTime.wordsPerMinute : void 0,
721
- format: readingTime.format === "short" ? "short" : "long",
722
- includeCode: readingTime.includeCode === true
723
- };
724
- }
725
- function resolveReadingTimeFromContent(frontmatter, content, wordsPerMinute, options) {
726
- const pageData = frontmatter ?? {};
727
- if (pageData.readingTime === false) return null;
728
- if (typeof pageData.readingTime === "number" && Number.isFinite(pageData.readingTime)) return Math.max(1, Math.ceil(pageData.readingTime));
729
- return estimateReadingTimeMinutes(content, wordsPerMinute, options);
730
- }
731
- function resolvePageReadingTime(frontmatter, content, options) {
732
- if (!(options?.enabledByDefault ?? false) && !hasExplicitReadingTime(frontmatter)) return;
733
- return resolveReadingTimeFromContent(frontmatter, content, options?.wordsPerMinute, { includeCode: options?.includeCode });
734
- }
735
- function resolveReadingTimeFromSource(source, wordsPerMinute, options) {
736
- const { data, content } = matter(source);
737
- return resolveReadingTimeFromContent(data, content, wordsPerMinute, options);
738
- }
739
-
740
- //#endregion
741
- export { resolveReadingTimeOptions as a, runDocsAgentConformance as c, extendTheme as d, deepMerge as f, resolveReadingTimeFromSource as i, httpLinkHeaderHasTargetRelation as l, defineDocs as m, resolvePageReadingTime as n, DOCS_AGENT_CONTRACT_VERSION as o, resolveChangelogConfig as p, resolveReadingTimeFromContent as r, createDocsAgentContractCases as s, estimateReadingTimeMinutes as t, createTheme as u };