@farming-labs/docs 0.1.1 → 0.1.3
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/cli/index.mjs +62 -3643
- package/dist/config-CSywk3ou.mjs +95 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +4 -1
- package/dist/init-C7kgy5hD.mjs +3277 -0
- package/dist/mcp-aXyV1jPp.mjs +46 -0
- package/dist/mcp.d.mts +3 -2
- package/dist/mcp.mjs +39 -37
- package/dist/search-BS6C5N1i.mjs +671 -0
- package/dist/search-ChhShKMO.mjs +99 -0
- package/dist/search-KzREATdM.d.mts +21 -0
- package/dist/server.d.mts +3 -2
- package/dist/server.mjs +3 -2
- package/dist/{types-Bd3kyFF1.d.mts → types-BAulrjlV.d.mts} +149 -1
- package/dist/upgrade-J_kkv-ti.mjs +138 -0
- package/dist/utils-CRhME2g-.mjs +145 -0
- package/package.json +1 -1
- /package/dist/{api-reference-wh4_pwG8.mjs → api-reference-DlfH-Y9c.mjs} +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, t as buildDocsSearchDocuments } from "./search-BS6C5N1i.mjs";
|
|
2
|
+
import "./api-reference-DlfH-Y9c.mjs";
|
|
3
|
+
import { createFilesystemDocsMcpSource } from "./mcp.mjs";
|
|
4
|
+
import "./server.mjs";
|
|
5
|
+
import { a as readStringProperty, n as loadProjectEnv, o as resolveDocsConfigPath, s as resolveDocsContentDir } from "./config-CSywk3ou.mjs";
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
|
|
9
|
+
//#region src/cli/search.ts
|
|
10
|
+
function getEnvValue(loadedEnv, key) {
|
|
11
|
+
return process.env[key] ?? loadedEnv[key];
|
|
12
|
+
}
|
|
13
|
+
function resolveSearchSyncProvider(options, loadedEnv) {
|
|
14
|
+
if (options.typesense && options.algolia) throw new Error("Use only one provider flag: --typesense or --algolia.");
|
|
15
|
+
if (options.typesense) return "typesense";
|
|
16
|
+
if (options.algolia) return "algolia";
|
|
17
|
+
if (options.provider === "typesense" || options.provider === "algolia") return options.provider;
|
|
18
|
+
if (options.provider) throw new Error(`Unsupported search provider: ${options.provider}.`);
|
|
19
|
+
if (options.baseUrl || getEnvValue(loadedEnv, "TYPESENSE_URL")) return "typesense";
|
|
20
|
+
if (options.appId || getEnvValue(loadedEnv, "ALGOLIA_APP_ID")) return "algolia";
|
|
21
|
+
throw new Error("Could not determine a search provider. Use --typesense, --algolia, or --provider <name>.");
|
|
22
|
+
}
|
|
23
|
+
function resolveTypesenseSyncConfig(options, loadedEnv) {
|
|
24
|
+
const baseUrl = options.baseUrl ?? getEnvValue(loadedEnv, "TYPESENSE_URL") ?? getEnvValue(loadedEnv, "TYPESENSE_BASE_URL");
|
|
25
|
+
const collection = options.collection ?? getEnvValue(loadedEnv, "TYPESENSE_COLLECTION") ?? "docs";
|
|
26
|
+
const apiKey = options.apiKey ?? getEnvValue(loadedEnv, "TYPESENSE_SEARCH_API_KEY") ?? getEnvValue(loadedEnv, "TYPESENSE_API_KEY");
|
|
27
|
+
const adminApiKey = options.adminApiKey ?? getEnvValue(loadedEnv, "TYPESENSE_ADMIN_API_KEY") ?? getEnvValue(loadedEnv, "TYPESENSE_API_KEY");
|
|
28
|
+
const mode = options.mode === "hybrid" ? "hybrid" : "keyword";
|
|
29
|
+
const ollamaModel = options.ollamaModel ?? getEnvValue(loadedEnv, "TYPESENSE_OLLAMA_MODEL");
|
|
30
|
+
const ollamaBaseUrl = options.ollamaBaseUrl ?? getEnvValue(loadedEnv, "TYPESENSE_OLLAMA_BASE_URL");
|
|
31
|
+
if (!baseUrl) throw new Error("Missing Typesense base URL. Set TYPESENSE_URL or pass --base-url.");
|
|
32
|
+
if (!apiKey) throw new Error("Missing Typesense API key. Set TYPESENSE_API_KEY or pass --api-key.");
|
|
33
|
+
if (!adminApiKey) throw new Error("Missing Typesense admin-capable key for sync. Set TYPESENSE_ADMIN_API_KEY or pass --admin-api-key.");
|
|
34
|
+
return {
|
|
35
|
+
provider: "typesense",
|
|
36
|
+
baseUrl,
|
|
37
|
+
collection,
|
|
38
|
+
apiKey,
|
|
39
|
+
adminApiKey,
|
|
40
|
+
mode,
|
|
41
|
+
...mode === "hybrid" ? ollamaModel ? { embeddings: {
|
|
42
|
+
provider: "ollama",
|
|
43
|
+
model: ollamaModel,
|
|
44
|
+
baseUrl: ollamaBaseUrl
|
|
45
|
+
} } : (() => {
|
|
46
|
+
throw new Error("Typesense hybrid sync needs an embeddings model. Set TYPESENSE_OLLAMA_MODEL or pass --ollama-model.");
|
|
47
|
+
})() : {}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function resolveAlgoliaSyncConfig(options, loadedEnv) {
|
|
51
|
+
const appId = options.appId ?? getEnvValue(loadedEnv, "ALGOLIA_APP_ID");
|
|
52
|
+
const indexName = options.indexName ?? getEnvValue(loadedEnv, "ALGOLIA_INDEX_NAME") ?? "docs";
|
|
53
|
+
const adminApiKey = options.adminApiKey ?? getEnvValue(loadedEnv, "ALGOLIA_ADMIN_API_KEY");
|
|
54
|
+
const searchApiKey = options.searchApiKey ?? getEnvValue(loadedEnv, "ALGOLIA_SEARCH_API_KEY") ?? adminApiKey;
|
|
55
|
+
if (!appId) throw new Error("Missing Algolia app id. Set ALGOLIA_APP_ID or pass --app-id.");
|
|
56
|
+
if (!adminApiKey) throw new Error("Missing Algolia admin API key for sync. Set ALGOLIA_ADMIN_API_KEY or pass --admin-api-key.");
|
|
57
|
+
if (!searchApiKey) throw new Error("Missing Algolia search API key. Set ALGOLIA_SEARCH_API_KEY or pass --search-api-key.");
|
|
58
|
+
return {
|
|
59
|
+
provider: "algolia",
|
|
60
|
+
appId,
|
|
61
|
+
indexName,
|
|
62
|
+
searchApiKey,
|
|
63
|
+
adminApiKey
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async function syncSearch(options = {}) {
|
|
67
|
+
const rootDir = process.cwd();
|
|
68
|
+
const configContent = readFileSync(resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
|
|
69
|
+
const loadedEnv = loadProjectEnv(rootDir);
|
|
70
|
+
const provider = resolveSearchSyncProvider(options, loadedEnv);
|
|
71
|
+
const entry = readStringProperty(configContent, "entry") ?? "docs";
|
|
72
|
+
const contentDir = resolveDocsContentDir(rootDir, configContent, entry);
|
|
73
|
+
const source = createFilesystemDocsMcpSource({
|
|
74
|
+
rootDir,
|
|
75
|
+
entry,
|
|
76
|
+
contentDir,
|
|
77
|
+
siteTitle: "Documentation"
|
|
78
|
+
});
|
|
79
|
+
const pages = await source.getPages();
|
|
80
|
+
const documents = buildDocsSearchDocuments(pages);
|
|
81
|
+
const context = {
|
|
82
|
+
pages,
|
|
83
|
+
documents,
|
|
84
|
+
siteTitle: source.siteTitle
|
|
85
|
+
};
|
|
86
|
+
if (documents.length === 0) throw new Error(`No docs content was found under ${contentDir}.`);
|
|
87
|
+
if (provider === "typesense") {
|
|
88
|
+
const config = resolveTypesenseSyncConfig(options, loadedEnv);
|
|
89
|
+
await createTypesenseSearchAdapter(config).index?.(context);
|
|
90
|
+
console.log(pc.green(`Synced ${documents.length} docs search documents to Typesense collection "${config.collection}".`));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const config = resolveAlgoliaSyncConfig(options, loadedEnv);
|
|
94
|
+
await createAlgoliaSearchAdapter(config).index?.(context);
|
|
95
|
+
console.log(pc.green(`Synced ${documents.length} docs search documents to Algolia index "${config.indexName}".`));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
//#endregion
|
|
99
|
+
export { syncSearch };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { E as DocsSearchSourcePage, N as McpDocsSearchConfig, Z as TypesenseDocsSearchConfig, b as DocsSearchConfig, g as DocsSearchAdapter, n as AlgoliaDocsSearchConfig, s as CustomDocsSearchConfig, v as DocsSearchAdapterFactory, w as DocsSearchResult, x as DocsSearchDocument, y as DocsSearchChunkingConfig } from "./types-BAulrjlV.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/search.d.ts
|
|
4
|
+
declare function buildDocsSearchDocuments(pages: DocsSearchSourcePage[], chunking?: DocsSearchChunkingConfig): DocsSearchDocument[];
|
|
5
|
+
declare function createSimpleSearchAdapter(): DocsSearchAdapter;
|
|
6
|
+
declare function createTypesenseSearchAdapter(config: TypesenseDocsSearchConfig): DocsSearchAdapter;
|
|
7
|
+
declare function resolveSearchRequestConfig(search: boolean | DocsSearchConfig | undefined, requestUrl?: string): boolean | DocsSearchConfig | undefined;
|
|
8
|
+
declare function createMcpSearchAdapter(config: McpDocsSearchConfig): DocsSearchAdapter;
|
|
9
|
+
declare function createAlgoliaSearchAdapter(config: AlgoliaDocsSearchConfig): DocsSearchAdapter;
|
|
10
|
+
declare function performDocsSearch(options: {
|
|
11
|
+
pages: DocsSearchSourcePage[];
|
|
12
|
+
query: string;
|
|
13
|
+
search?: boolean | DocsSearchConfig;
|
|
14
|
+
locale?: string;
|
|
15
|
+
pathname?: string;
|
|
16
|
+
siteTitle?: string;
|
|
17
|
+
limit?: number;
|
|
18
|
+
}): Promise<DocsSearchResult[]>;
|
|
19
|
+
declare function createCustomSearchAdapter(adapter: DocsSearchAdapter | DocsSearchAdapterFactory): CustomDocsSearchConfig;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { createSimpleSearchAdapter as a, resolveSearchRequestConfig as c, createMcpSearchAdapter as i, createAlgoliaSearchAdapter as n, createTypesenseSearchAdapter as o, createCustomSearchAdapter as r, performDocsSearch as s, buildDocsSearchDocuments as t };
|
package/dist/server.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as DocsSearchQuery, E as DocsSearchSourcePage, N as McpDocsSearchConfig, _ as DocsSearchAdapterContext, b as DocsSearchConfig, c as DocsConfig, g as DocsSearchAdapter, v as DocsSearchAdapterFactory, w as DocsSearchResult, x as DocsSearchDocument } from "./types-BAulrjlV.mjs";
|
|
2
|
+
import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-KzREATdM.mjs";
|
|
2
3
|
import { DocsMcpHttpHandlers, DocsMcpNavigationNode, DocsMcpNavigationTree, DocsMcpPage, DocsMcpResolvedConfig, DocsMcpSource, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, normalizeDocsMcpRoute, resolveDocsMcpConfig, runDocsMcpStdio } from "./mcp.mjs";
|
|
3
4
|
|
|
4
5
|
//#region src/api-reference.d.ts
|
|
@@ -36,4 +37,4 @@ declare function buildApiReferenceOpenApiDocumentAsync(config: DocsConfig, optio
|
|
|
36
37
|
declare function buildApiReferenceHtmlDocument(config: DocsConfig, options: BuildApiReferenceHtmlOptions): string;
|
|
37
38
|
declare function buildApiReferenceHtmlDocumentAsync(config: DocsConfig, options: BuildApiReferenceHtmlOptions): Promise<string>;
|
|
38
39
|
//#endregion
|
|
39
|
-
export { type ApiReferenceFramework, type ApiReferenceRoute, type DocsMcpHttpHandlers, type DocsMcpNavigationNode, type DocsMcpNavigationTree, type DocsMcpPage, type DocsMcpResolvedConfig, type DocsMcpSource, type ResolvedApiReferenceConfig, buildApiReferenceHtmlDocument, buildApiReferenceHtmlDocumentAsync, buildApiReferenceOpenApiDocument, buildApiReferenceOpenApiDocumentAsync, buildApiReferencePageTitle, buildApiReferenceScalarCss, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, normalizeDocsMcpRoute, resolveApiReferenceConfig, resolveDocsMcpConfig, runDocsMcpStdio };
|
|
40
|
+
export { type ApiReferenceFramework, type ApiReferenceRoute, type DocsMcpHttpHandlers, type DocsMcpNavigationNode, type DocsMcpNavigationTree, type DocsMcpPage, type DocsMcpResolvedConfig, type DocsMcpSource, type DocsSearchAdapter, type DocsSearchAdapterContext, type DocsSearchAdapterFactory, type DocsSearchConfig, type DocsSearchDocument, type DocsSearchQuery, type DocsSearchResult, type DocsSearchSourcePage, type McpDocsSearchConfig, type ResolvedApiReferenceConfig, buildApiReferenceHtmlDocument, buildApiReferenceHtmlDocumentAsync, buildApiReferenceOpenApiDocument, buildApiReferenceOpenApiDocumentAsync, buildApiReferencePageTitle, buildApiReferenceScalarCss, buildDocsSearchDocuments, createAlgoliaSearchAdapter, createCustomSearchAdapter, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, createMcpSearchAdapter, createSimpleSearchAdapter, createTypesenseSearchAdapter, normalizeDocsMcpRoute, performDocsSearch, resolveApiReferenceConfig, resolveDocsMcpConfig, resolveSearchRequestConfig, runDocsMcpStdio };
|
package/dist/server.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-BS6C5N1i.mjs";
|
|
2
|
+
import { a as buildApiReferencePageTitle, i as buildApiReferenceOpenApiDocumentAsync, n as buildApiReferenceHtmlDocumentAsync, o as buildApiReferenceScalarCss, r as buildApiReferenceOpenApiDocument, s as resolveApiReferenceConfig, t as buildApiReferenceHtmlDocument } from "./api-reference-DlfH-Y9c.mjs";
|
|
2
3
|
import { createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, normalizeDocsMcpRoute, resolveDocsMcpConfig, runDocsMcpStdio } from "./mcp.mjs";
|
|
3
4
|
|
|
4
|
-
export { buildApiReferenceHtmlDocument, buildApiReferenceHtmlDocumentAsync, buildApiReferenceOpenApiDocument, buildApiReferenceOpenApiDocumentAsync, buildApiReferencePageTitle, buildApiReferenceScalarCss, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, normalizeDocsMcpRoute, resolveApiReferenceConfig, resolveDocsMcpConfig, runDocsMcpStdio };
|
|
5
|
+
export { buildApiReferenceHtmlDocument, buildApiReferenceHtmlDocumentAsync, buildApiReferenceOpenApiDocument, buildApiReferenceOpenApiDocumentAsync, buildApiReferencePageTitle, buildApiReferenceScalarCss, buildDocsSearchDocuments, createAlgoliaSearchAdapter, createCustomSearchAdapter, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, createMcpSearchAdapter, createSimpleSearchAdapter, createTypesenseSearchAdapter, normalizeDocsMcpRoute, performDocsSearch, resolveApiReferenceConfig, resolveDocsMcpConfig, resolveSearchRequestConfig, runDocsMcpStdio };
|
|
@@ -614,6 +614,144 @@ interface DocsMcpConfig {
|
|
|
614
614
|
/** Fine-grained tool toggles. Omitted tools stay enabled. */
|
|
615
615
|
tools?: DocsMcpToolsConfig;
|
|
616
616
|
}
|
|
617
|
+
type DocsSearchResultType = "page" | "heading" | "text";
|
|
618
|
+
interface DocsSearchSourcePage {
|
|
619
|
+
title: string;
|
|
620
|
+
url: string;
|
|
621
|
+
content: string;
|
|
622
|
+
description?: string;
|
|
623
|
+
rawContent?: string;
|
|
624
|
+
type?: "page" | "api" | "code" | "changelog";
|
|
625
|
+
locale?: string;
|
|
626
|
+
framework?: string;
|
|
627
|
+
version?: string;
|
|
628
|
+
tags?: string[];
|
|
629
|
+
}
|
|
630
|
+
interface DocsSearchDocument {
|
|
631
|
+
id: string;
|
|
632
|
+
url: string;
|
|
633
|
+
title: string;
|
|
634
|
+
content: string;
|
|
635
|
+
description?: string;
|
|
636
|
+
section?: string;
|
|
637
|
+
type: DocsSearchResultType;
|
|
638
|
+
locale?: string;
|
|
639
|
+
framework?: string;
|
|
640
|
+
version?: string;
|
|
641
|
+
tags?: string[];
|
|
642
|
+
}
|
|
643
|
+
interface DocsSearchResult {
|
|
644
|
+
id: string;
|
|
645
|
+
url: string;
|
|
646
|
+
content: string;
|
|
647
|
+
description?: string;
|
|
648
|
+
type: DocsSearchResultType;
|
|
649
|
+
score?: number;
|
|
650
|
+
section?: string;
|
|
651
|
+
}
|
|
652
|
+
interface DocsSearchQuery {
|
|
653
|
+
query: string;
|
|
654
|
+
limit?: number;
|
|
655
|
+
locale?: string;
|
|
656
|
+
pathname?: string;
|
|
657
|
+
}
|
|
658
|
+
interface DocsSearchAdapterContext {
|
|
659
|
+
pages: DocsSearchSourcePage[];
|
|
660
|
+
documents: DocsSearchDocument[];
|
|
661
|
+
locale?: string;
|
|
662
|
+
pathname?: string;
|
|
663
|
+
siteTitle?: string;
|
|
664
|
+
}
|
|
665
|
+
interface DocsSearchAdapter {
|
|
666
|
+
name: string;
|
|
667
|
+
index?(context: DocsSearchAdapterContext): Promise<void>;
|
|
668
|
+
search(query: DocsSearchQuery, context: DocsSearchAdapterContext): Promise<DocsSearchResult[]>;
|
|
669
|
+
}
|
|
670
|
+
type DocsSearchAdapterFactory = (context: DocsSearchAdapterContext) => DocsSearchAdapter | Promise<DocsSearchAdapter>;
|
|
671
|
+
interface DocsSearchChunkingConfig {
|
|
672
|
+
/**
|
|
673
|
+
* How docs content should be chunked before searching.
|
|
674
|
+
*
|
|
675
|
+
* - `"page"` keeps one search document per page
|
|
676
|
+
* - `"section"` splits pages by headings and improves precision
|
|
677
|
+
*
|
|
678
|
+
* @default "section"
|
|
679
|
+
*/
|
|
680
|
+
strategy?: "page" | "section";
|
|
681
|
+
}
|
|
682
|
+
interface DocsSearchEmbeddingsConfig {
|
|
683
|
+
/**
|
|
684
|
+
* Embeddings provider used for hybrid / semantic search.
|
|
685
|
+
* The initial built-in provider is Ollama for local or self-hosted setups.
|
|
686
|
+
*/
|
|
687
|
+
provider: "ollama";
|
|
688
|
+
/** Embedding model id, e.g. `embeddinggemma`. */
|
|
689
|
+
model: string;
|
|
690
|
+
/** Base URL of the embedding API. @default "http://127.0.0.1:11434" */
|
|
691
|
+
baseUrl?: string;
|
|
692
|
+
}
|
|
693
|
+
interface SimpleDocsSearchConfig {
|
|
694
|
+
provider?: "simple";
|
|
695
|
+
enabled?: boolean;
|
|
696
|
+
maxResults?: number;
|
|
697
|
+
chunking?: DocsSearchChunkingConfig;
|
|
698
|
+
}
|
|
699
|
+
interface AlgoliaDocsSearchConfig {
|
|
700
|
+
provider: "algolia";
|
|
701
|
+
enabled?: boolean;
|
|
702
|
+
appId: string;
|
|
703
|
+
indexName: string;
|
|
704
|
+
searchApiKey: string;
|
|
705
|
+
adminApiKey?: string;
|
|
706
|
+
maxResults?: number;
|
|
707
|
+
syncOnSearch?: boolean;
|
|
708
|
+
chunking?: DocsSearchChunkingConfig;
|
|
709
|
+
}
|
|
710
|
+
interface TypesenseDocsSearchConfig {
|
|
711
|
+
provider: "typesense";
|
|
712
|
+
enabled?: boolean;
|
|
713
|
+
baseUrl: string;
|
|
714
|
+
collection: string;
|
|
715
|
+
apiKey: string;
|
|
716
|
+
adminApiKey?: string;
|
|
717
|
+
maxResults?: number;
|
|
718
|
+
syncOnSearch?: boolean;
|
|
719
|
+
queryBy?: string[];
|
|
720
|
+
mode?: "keyword" | "hybrid";
|
|
721
|
+
embeddings?: DocsSearchEmbeddingsConfig;
|
|
722
|
+
chunking?: DocsSearchChunkingConfig;
|
|
723
|
+
}
|
|
724
|
+
interface McpDocsSearchConfig {
|
|
725
|
+
provider: "mcp";
|
|
726
|
+
enabled?: boolean;
|
|
727
|
+
/**
|
|
728
|
+
* Streamable HTTP MCP endpoint. Relative paths like `/api/docs/mcp` are resolved
|
|
729
|
+
* against the current docs API request URL.
|
|
730
|
+
*/
|
|
731
|
+
endpoint: string;
|
|
732
|
+
/**
|
|
733
|
+
* Optional extra headers passed to the MCP endpoint on initialize/tool calls.
|
|
734
|
+
*/
|
|
735
|
+
headers?: Record<string, string>;
|
|
736
|
+
/**
|
|
737
|
+
* MCP tool name used for search. Defaults to `search_docs`.
|
|
738
|
+
*/
|
|
739
|
+
toolName?: string;
|
|
740
|
+
/**
|
|
741
|
+
* Override the MCP protocol version header when needed.
|
|
742
|
+
*/
|
|
743
|
+
protocolVersion?: string;
|
|
744
|
+
maxResults?: number;
|
|
745
|
+
chunking?: DocsSearchChunkingConfig;
|
|
746
|
+
}
|
|
747
|
+
interface CustomDocsSearchConfig {
|
|
748
|
+
provider: "custom";
|
|
749
|
+
enabled?: boolean;
|
|
750
|
+
adapter: DocsSearchAdapter | DocsSearchAdapterFactory;
|
|
751
|
+
maxResults?: number;
|
|
752
|
+
chunking?: DocsSearchChunkingConfig;
|
|
753
|
+
}
|
|
754
|
+
type DocsSearchConfig = SimpleDocsSearchConfig | AlgoliaDocsSearchConfig | McpDocsSearchConfig | TypesenseDocsSearchConfig | CustomDocsSearchConfig;
|
|
617
755
|
interface LastUpdatedConfig {
|
|
618
756
|
/**
|
|
619
757
|
* Whether to show the "Last updated" date.
|
|
@@ -1353,6 +1491,16 @@ interface DocsConfig {
|
|
|
1353
1491
|
* ```
|
|
1354
1492
|
*/
|
|
1355
1493
|
pageActions?: PageActionsConfig;
|
|
1494
|
+
/**
|
|
1495
|
+
* Built-in docs search configuration.
|
|
1496
|
+
*
|
|
1497
|
+
* - `true` or omitted → use the built-in simple search
|
|
1498
|
+
* - `{ provider: "typesense", ... }` → use Typesense
|
|
1499
|
+
* - `{ provider: "algolia", ... }` → use Algolia
|
|
1500
|
+
* - `{ provider: "custom", adapter }` → use a custom adapter
|
|
1501
|
+
* - `false` → disable docs search
|
|
1502
|
+
*/
|
|
1503
|
+
search?: boolean | DocsSearchConfig;
|
|
1356
1504
|
/**
|
|
1357
1505
|
* Configuration for the "Last updated" date display.
|
|
1358
1506
|
*
|
|
@@ -1481,4 +1629,4 @@ interface DocsConfig {
|
|
|
1481
1629
|
og?: OGConfig;
|
|
1482
1630
|
}
|
|
1483
1631
|
//#endregion
|
|
1484
|
-
export {
|
|
1632
|
+
export { UIConfig as $, GithubConfig as A, PageFrontmatter as B, DocsSearchQuery as C, DocsTheme as D, DocsSearchSourcePage as E, OpenDocsConfig as F, SidebarFolderNode as G, PageTwitter as H, OpenDocsProvider as I, SidebarTree as J, SidebarNode as K, OpenGraphImage as L, LlmsTxtConfig as M, McpDocsSearchConfig as N, FeedbackConfig as O, OGConfig as P, TypographyConfig as Q, OrderingItem as R, DocsSearchEmbeddingsConfig as S, DocsSearchResultType as T, SidebarComponentProps as U, PageOpenGraph as V, SidebarConfig as W, ThemeToggleConfig as X, SimpleDocsSearchConfig as Y, TypesenseDocsSearchConfig as Z, DocsSearchAdapterContext as _, CodeBlockCopyData as a, DocsSearchConfig as b, DocsConfig as c, DocsI18nConfig as d, DocsMcpConfig as f, DocsSearchAdapter as g, DocsNav as h, BreadcrumbConfig as i, LastUpdatedConfig as j, FontStyle as k, DocsFeedbackData as l, DocsMetadata as m, AlgoliaDocsSearchConfig as n, CopyMarkdownConfig as o, DocsMcpToolsConfig as p, SidebarPageNode as q, ApiReferenceConfig as r, CustomDocsSearchConfig as s, AIConfig as t, DocsFeedbackValue as u, DocsSearchAdapterFactory as v, DocsSearchResult as w, DocsSearchDocument as x, DocsSearchChunkingConfig as y, PageActionsConfig as z };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { c as installCommand, i as detectPackageManagerFromLockfile, o as exec, s as fileExists, t as detectFramework } from "./utils-CRhME2g-.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
|
|
6
|
+
//#region src/cli/upgrade.ts
|
|
7
|
+
/**
|
|
8
|
+
* Upgrade @farming-labs/* packages to latest.
|
|
9
|
+
* Detects framework from package.json by default, or use --framework (next, tanstack-start, nuxt, sveltekit, astro).
|
|
10
|
+
*/
|
|
11
|
+
const PRESETS = [
|
|
12
|
+
"next",
|
|
13
|
+
"tanstack-start",
|
|
14
|
+
"nuxt",
|
|
15
|
+
"sveltekit",
|
|
16
|
+
"astro"
|
|
17
|
+
];
|
|
18
|
+
const PACKAGES_BY_FRAMEWORK = {
|
|
19
|
+
nextjs: [
|
|
20
|
+
"@farming-labs/docs",
|
|
21
|
+
"@farming-labs/theme",
|
|
22
|
+
"@farming-labs/next"
|
|
23
|
+
],
|
|
24
|
+
"tanstack-start": [
|
|
25
|
+
"@farming-labs/docs",
|
|
26
|
+
"@farming-labs/theme",
|
|
27
|
+
"@farming-labs/tanstack-start"
|
|
28
|
+
],
|
|
29
|
+
nuxt: [
|
|
30
|
+
"@farming-labs/docs",
|
|
31
|
+
"@farming-labs/nuxt",
|
|
32
|
+
"@farming-labs/nuxt-theme"
|
|
33
|
+
],
|
|
34
|
+
sveltekit: [
|
|
35
|
+
"@farming-labs/docs",
|
|
36
|
+
"@farming-labs/svelte",
|
|
37
|
+
"@farming-labs/svelte-theme"
|
|
38
|
+
],
|
|
39
|
+
astro: [
|
|
40
|
+
"@farming-labs/docs",
|
|
41
|
+
"@farming-labs/astro",
|
|
42
|
+
"@farming-labs/astro-theme"
|
|
43
|
+
]
|
|
44
|
+
};
|
|
45
|
+
function presetFromFramework(fw) {
|
|
46
|
+
return fw === "nextjs" ? "next" : fw;
|
|
47
|
+
}
|
|
48
|
+
function frameworkFromPreset(preset) {
|
|
49
|
+
return preset === "next" ? "nextjs" : preset;
|
|
50
|
+
}
|
|
51
|
+
/** Return package list for a framework (for testing and CLI). */
|
|
52
|
+
function getPackagesForFramework(framework) {
|
|
53
|
+
return PACKAGES_BY_FRAMEWORK[framework];
|
|
54
|
+
}
|
|
55
|
+
/** Build the install command for upgrade (for testing). */
|
|
56
|
+
function buildUpgradeCommand(framework, tag, pm) {
|
|
57
|
+
const packagesWithTag = PACKAGES_BY_FRAMEWORK[framework].map((name) => `${name}@${tag}`);
|
|
58
|
+
return `${installCommand(pm)} ${packagesWithTag.join(" ")}`;
|
|
59
|
+
}
|
|
60
|
+
async function upgrade(options = {}) {
|
|
61
|
+
const cwd = process.cwd();
|
|
62
|
+
const tag = options.tag ?? "latest";
|
|
63
|
+
p.intro(pc.bgCyan(pc.black(" @farming-labs/docs upgrade ")));
|
|
64
|
+
if (!fileExists(path.join(cwd, "package.json"))) {
|
|
65
|
+
p.log.error("No package.json found in the current directory. Run this from your project root.");
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
let framework = null;
|
|
69
|
+
let preset;
|
|
70
|
+
if (options.framework) {
|
|
71
|
+
const raw = options.framework.toLowerCase().trim();
|
|
72
|
+
const normalized = raw === "nextjs" ? "next" : raw;
|
|
73
|
+
if (!PRESETS.includes(normalized)) {
|
|
74
|
+
p.log.error(`Invalid framework ${pc.cyan(options.framework)}. Use one of: ${PRESETS.map((t) => pc.cyan(t)).join(", ")}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
preset = normalized;
|
|
78
|
+
framework = frameworkFromPreset(preset);
|
|
79
|
+
} else {
|
|
80
|
+
const detected = detectFramework(cwd);
|
|
81
|
+
if (!detected) {
|
|
82
|
+
p.log.error("Could not detect a supported framework (Next.js, TanStack Start, Nuxt, SvelteKit, Astro). Use " + pc.cyan("--framework <next|tanstack-start|nuxt|sveltekit|astro>") + " to specify.");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
framework = detected;
|
|
86
|
+
preset = presetFromFramework(framework);
|
|
87
|
+
}
|
|
88
|
+
let pm = detectPackageManagerFromLockfile(cwd);
|
|
89
|
+
if (pm) p.log.info(`Detected ${pc.cyan(pm)} from lockfile`);
|
|
90
|
+
else {
|
|
91
|
+
const pmAnswer = await p.select({
|
|
92
|
+
message: "Which package manager do you want to use for this upgrade?",
|
|
93
|
+
options: [
|
|
94
|
+
{
|
|
95
|
+
value: "pnpm",
|
|
96
|
+
label: "pnpm",
|
|
97
|
+
hint: "Use pnpm add"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
value: "npm",
|
|
101
|
+
label: "npm",
|
|
102
|
+
hint: "Use npm add"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
value: "yarn",
|
|
106
|
+
label: "yarn",
|
|
107
|
+
hint: "Use yarn add"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
value: "bun",
|
|
111
|
+
label: "bun",
|
|
112
|
+
hint: "Use bun add"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
if (p.isCancel(pmAnswer)) {
|
|
117
|
+
p.outro(pc.red("Upgrade cancelled."));
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
120
|
+
pm = pmAnswer;
|
|
121
|
+
p.log.info(`Using ${pc.cyan(pm)} as package manager`);
|
|
122
|
+
}
|
|
123
|
+
const cmd = buildUpgradeCommand(framework, tag, pm);
|
|
124
|
+
const packages = getPackagesForFramework(framework);
|
|
125
|
+
p.log.step(`Upgrading ${preset} docs packages to ${tag}...`);
|
|
126
|
+
p.log.message(pc.dim(packages.join(", ")));
|
|
127
|
+
try {
|
|
128
|
+
exec(cmd, cwd);
|
|
129
|
+
p.log.success(`Packages upgraded to ${tag}.`);
|
|
130
|
+
p.outro(pc.green("Done. Run your dev server to confirm everything works."));
|
|
131
|
+
} catch {
|
|
132
|
+
p.log.error("Upgrade failed. Try running manually:\n " + pc.cyan(cmd));
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
export { upgrade };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execSync, spawn } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/utils.ts
|
|
6
|
+
function detectFramework(cwd) {
|
|
7
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
8
|
+
if (!fs.existsSync(pkgPath)) return null;
|
|
9
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
10
|
+
const allDeps = {
|
|
11
|
+
...pkg.dependencies,
|
|
12
|
+
...pkg.devDependencies
|
|
13
|
+
};
|
|
14
|
+
if (allDeps["next"]) return "nextjs";
|
|
15
|
+
if (allDeps["@tanstack/react-start"]) return "tanstack-start";
|
|
16
|
+
if (allDeps["@sveltejs/kit"]) return "sveltekit";
|
|
17
|
+
if (allDeps["astro"]) return "astro";
|
|
18
|
+
if (allDeps["nuxt"]) return "nuxt";
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function detectPackageManagerFromLockfile(cwd) {
|
|
22
|
+
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
23
|
+
if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) return "bun";
|
|
24
|
+
if (fs.existsSync(path.join(cwd, "yarn.lock"))) return "yarn";
|
|
25
|
+
if (fs.existsSync(path.join(cwd, "package-lock.json"))) return "npm";
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function installCommand(pm) {
|
|
29
|
+
return pm === "yarn" ? "yarn add" : `${pm} add`;
|
|
30
|
+
}
|
|
31
|
+
function devInstallCommand(pm) {
|
|
32
|
+
if (pm === "yarn") return "yarn add -D";
|
|
33
|
+
if (pm === "npm") return "npm install -D";
|
|
34
|
+
return `${pm} add -D`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Write a file, creating parent directories as needed.
|
|
38
|
+
* Returns true if the file was written, false if it already existed and was skipped.
|
|
39
|
+
*/
|
|
40
|
+
function writeFileSafe(filePath, content, overwrite = false) {
|
|
41
|
+
if (fs.existsSync(filePath) && !overwrite) return false;
|
|
42
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
43
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if a file exists.
|
|
48
|
+
*/
|
|
49
|
+
function fileExists(filePath) {
|
|
50
|
+
return fs.existsSync(filePath);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Read a file, returning null if it does not exist.
|
|
54
|
+
*/
|
|
55
|
+
function readFileSafe(filePath) {
|
|
56
|
+
if (!fs.existsSync(filePath)) return null;
|
|
57
|
+
return fs.readFileSync(filePath, "utf-8");
|
|
58
|
+
}
|
|
59
|
+
/** Common locations where global CSS files live in Next.js / SvelteKit projects. */
|
|
60
|
+
const GLOBAL_CSS_CANDIDATES = [
|
|
61
|
+
"app/globals.css",
|
|
62
|
+
"app/global.css",
|
|
63
|
+
"src/app/globals.css",
|
|
64
|
+
"src/app/global.css",
|
|
65
|
+
"src/app.css",
|
|
66
|
+
"src/styles/app.css",
|
|
67
|
+
"styles/globals.css",
|
|
68
|
+
"styles/global.css",
|
|
69
|
+
"src/styles/globals.css",
|
|
70
|
+
"src/styles/global.css",
|
|
71
|
+
"assets/css/main.css",
|
|
72
|
+
"assets/main.css"
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* Find existing global CSS files in the project.
|
|
76
|
+
* Returns relative paths that exist.
|
|
77
|
+
*/
|
|
78
|
+
function detectGlobalCssFiles(cwd) {
|
|
79
|
+
return GLOBAL_CSS_CANDIDATES.filter((rel) => fs.existsSync(path.join(cwd, rel)));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Detect whether the Next.js project uses `app` or `src/app` for the App Router.
|
|
83
|
+
* Returns the directory that exists; if both exist, prefers src/app; if neither, returns null.
|
|
84
|
+
*/
|
|
85
|
+
function detectNextAppDir(cwd) {
|
|
86
|
+
const hasSrcApp = fs.existsSync(path.join(cwd, "src", "app"));
|
|
87
|
+
const hasApp = fs.existsSync(path.join(cwd, "app"));
|
|
88
|
+
if (hasSrcApp) return "src/app";
|
|
89
|
+
if (hasApp) return "app";
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Run a shell command synchronously, inheriting stdio.
|
|
94
|
+
*/
|
|
95
|
+
function exec(command, cwd) {
|
|
96
|
+
execSync(command, {
|
|
97
|
+
cwd,
|
|
98
|
+
stdio: "inherit"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Spawn a process and wait for a specific string in stdout,
|
|
103
|
+
* then resolve with the child process (still running).
|
|
104
|
+
*/
|
|
105
|
+
function spawnAndWaitFor(command, args, cwd, waitFor, timeoutMs = 6e4) {
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const child = spawn(command, args, {
|
|
108
|
+
cwd,
|
|
109
|
+
stdio: [
|
|
110
|
+
"ignore",
|
|
111
|
+
"pipe",
|
|
112
|
+
"pipe"
|
|
113
|
+
],
|
|
114
|
+
shell: true
|
|
115
|
+
});
|
|
116
|
+
let output = "";
|
|
117
|
+
const timer = setTimeout(() => {
|
|
118
|
+
child.kill();
|
|
119
|
+
reject(/* @__PURE__ */ new Error(`Timed out waiting for "${waitFor}" after ${timeoutMs}ms`));
|
|
120
|
+
}, timeoutMs);
|
|
121
|
+
child.stdout?.on("data", (data) => {
|
|
122
|
+
const text = data.toString();
|
|
123
|
+
output += text;
|
|
124
|
+
process.stdout.write(text);
|
|
125
|
+
if (output.includes(waitFor)) {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
resolve(child);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
child.stderr?.on("data", (data) => {
|
|
131
|
+
process.stderr.write(data.toString());
|
|
132
|
+
});
|
|
133
|
+
child.on("error", (err) => {
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
reject(err);
|
|
136
|
+
});
|
|
137
|
+
child.on("close", (code) => {
|
|
138
|
+
clearTimeout(timer);
|
|
139
|
+
if (!output.includes(waitFor)) reject(/* @__PURE__ */ new Error(`Process exited with code ${code} before "${waitFor}" appeared`));
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//#endregion
|
|
145
|
+
export { devInstallCommand as a, installCommand as c, writeFileSafe as d, detectPackageManagerFromLockfile as i, readFileSafe as l, detectGlobalCssFiles as n, exec as o, detectNextAppDir as r, fileExists as s, detectFramework as t, spawnAndWaitFor as u };
|
package/package.json
CHANGED
|
File without changes
|