tailwindcss-docs-mcp 1.0.0
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/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/auto-index.d.ts +21 -0
- package/dist/auto-index.d.ts.map +1 -0
- package/dist/auto-index.js +27 -0
- package/dist/auto-index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/chunker.d.ts +61 -0
- package/dist/pipeline/chunker.d.ts.map +1 -0
- package/dist/pipeline/chunker.js +162 -0
- package/dist/pipeline/chunker.js.map +1 -0
- package/dist/pipeline/embedder.d.ts +57 -0
- package/dist/pipeline/embedder.d.ts.map +1 -0
- package/dist/pipeline/embedder.js +108 -0
- package/dist/pipeline/embedder.js.map +1 -0
- package/dist/pipeline/fetcher.d.ts +63 -0
- package/dist/pipeline/fetcher.d.ts.map +1 -0
- package/dist/pipeline/fetcher.js +128 -0
- package/dist/pipeline/fetcher.js.map +1 -0
- package/dist/pipeline/parser.d.ts +73 -0
- package/dist/pipeline/parser.d.ts.map +1 -0
- package/dist/pipeline/parser.js +127 -0
- package/dist/pipeline/parser.js.map +1 -0
- package/dist/server.d.ts +47 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +141 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/database.d.ts +90 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +342 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/search.d.ts +84 -0
- package/dist/storage/search.d.ts.map +1 -0
- package/dist/storage/search.js +165 -0
- package/dist/storage/search.js.map +1 -0
- package/dist/tools/check-status.d.ts +35 -0
- package/dist/tools/check-status.d.ts.map +1 -0
- package/dist/tools/check-status.js +40 -0
- package/dist/tools/check-status.js.map +1 -0
- package/dist/tools/fetch-docs.d.ts +42 -0
- package/dist/tools/fetch-docs.d.ts.map +1 -0
- package/dist/tools/fetch-docs.js +98 -0
- package/dist/tools/fetch-docs.js.map +1 -0
- package/dist/tools/list-utilities.d.ts +49 -0
- package/dist/tools/list-utilities.d.ts.map +1 -0
- package/dist/tools/list-utilities.js +63 -0
- package/dist/tools/list-utilities.js.map +1 -0
- package/dist/tools/search-docs.d.ts +44 -0
- package/dist/tools/search-docs.d.ts.map +1 -0
- package/dist/tools/search-docs.js +53 -0
- package/dist/tools/search-docs.js.map +1 -0
- package/dist/utils/categories.d.ts +25 -0
- package/dist/utils/categories.d.ts.map +1 -0
- package/dist/utils/categories.js +240 -0
- package/dist/utils/categories.js.map +1 -0
- package/dist/utils/config.d.ts +39 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +37 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/query-expansion.d.ts +28 -0
- package/dist/utils/query-expansion.d.ts.map +1 -0
- package/dist/utils/query-expansion.js +147 -0
- package/dist/utils/query-expansion.js.map +1 -0
- package/dist/utils/similarity.d.ts +12 -0
- package/dist/utils/similarity.d.ts.map +1 -0
- package/dist/utils/similarity.js +28 -0
- package/dist/utils/similarity.js.map +1 -0
- package/package.json +59 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { handleCheckStatus } from "./tools/check-status.js";
|
|
6
|
+
import { handleFetchDocs } from "./tools/fetch-docs.js";
|
|
7
|
+
import { formatUtilitiesList, handleListUtilities } from "./tools/list-utilities.js";
|
|
8
|
+
import { formatSearchResults, handleSearchDocs } from "./tools/search-docs.js";
|
|
9
|
+
const { version: SERVER_VERSION } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
10
|
+
/**
|
|
11
|
+
* MCP tool names exposed by this server.
|
|
12
|
+
*/
|
|
13
|
+
export const TOOL_NAMES = {
|
|
14
|
+
FETCH_DOCS: "fetch_docs",
|
|
15
|
+
SEARCH_DOCS: "search_docs",
|
|
16
|
+
LIST_UTILITIES: "list_utilities",
|
|
17
|
+
CHECK_STATUS: "check_status",
|
|
18
|
+
};
|
|
19
|
+
export const EMBEDDER_STATUS_MESSAGES = {
|
|
20
|
+
pending: "Embedding model is initializing. Please wait a moment and try again.",
|
|
21
|
+
downloading: "Embedding model is downloading (~27 MB). Please wait and try again.",
|
|
22
|
+
ready: "Embedding model is ready.",
|
|
23
|
+
failed: "Embedding model failed to load. Check server logs for details.",
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Create and configure the MCP server with all tool handlers.
|
|
27
|
+
*
|
|
28
|
+
* The server starts immediately even if the embedder is not yet available.
|
|
29
|
+
* Tools that require the embedder (fetch_docs, search_docs) will throw
|
|
30
|
+
* an error if called before the model has finished loading.
|
|
31
|
+
*/
|
|
32
|
+
export async function createServer(deps, transport) {
|
|
33
|
+
const { config, db } = deps;
|
|
34
|
+
let embedder = deps.embedder;
|
|
35
|
+
let embedderStatus = deps.embedder ? "ready" : "pending";
|
|
36
|
+
function requireEmbedder() {
|
|
37
|
+
if (!embedder) {
|
|
38
|
+
throw new Error(EMBEDDER_STATUS_MESSAGES[embedderStatus]);
|
|
39
|
+
}
|
|
40
|
+
return embedder;
|
|
41
|
+
}
|
|
42
|
+
function toolError(error) {
|
|
43
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
46
|
+
isError: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const server = new McpServer({
|
|
50
|
+
name: "tailwindcss-docs-mcp",
|
|
51
|
+
version: SERVER_VERSION,
|
|
52
|
+
});
|
|
53
|
+
// Register fetch_docs
|
|
54
|
+
server.tool(TOOL_NAMES.FETCH_DOCS, "Download and index Tailwind CSS documentation for local semantic search. Only needs to be run once per version. Re-run with force=true to refresh.", {
|
|
55
|
+
version: z.enum(["v3", "v4"]).optional().describe("Tailwind CSS major version (default: v4)"),
|
|
56
|
+
force: z
|
|
57
|
+
.boolean()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Force re-download and re-index even if already cached (default: false)"),
|
|
60
|
+
}, async (params) => {
|
|
61
|
+
try {
|
|
62
|
+
const result = await handleFetchDocs(params, config, db, requireEmbedder());
|
|
63
|
+
return { content: [{ type: "text", text: result.message }] };
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return toolError(error);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Register search_docs
|
|
70
|
+
server.tool(TOOL_NAMES.SEARCH_DOCS, "Search Tailwind CSS documentation using natural language. Returns the most relevant documentation snippets with code examples. Requires fetch_docs to be run first.", {
|
|
71
|
+
query: z
|
|
72
|
+
.string()
|
|
73
|
+
.describe("Natural language search query (e.g., 'how to add responsive padding', 'dark mode configuration', 'grid layout with gaps')"),
|
|
74
|
+
version: z
|
|
75
|
+
.enum(["v3", "v4"])
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Tailwind CSS major version to search (default: v4)"),
|
|
78
|
+
limit: z
|
|
79
|
+
.number()
|
|
80
|
+
.min(1)
|
|
81
|
+
.max(20)
|
|
82
|
+
.optional()
|
|
83
|
+
.describe("Maximum number of results to return (default: 5)"),
|
|
84
|
+
}, async (params) => {
|
|
85
|
+
try {
|
|
86
|
+
const result = await handleSearchDocs(params, db, requireEmbedder(), config.defaultVersion);
|
|
87
|
+
const text = formatSearchResults(result);
|
|
88
|
+
return { content: [{ type: "text", text }] };
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return toolError(error);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// Register list_utilities (does not require embedder)
|
|
95
|
+
server.tool(TOOL_NAMES.LIST_UTILITIES, "List all Tailwind CSS utility categories with descriptions. Useful for discovering what utilities are available.", {
|
|
96
|
+
category: z
|
|
97
|
+
.string()
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("Filter by category (e.g., 'Layout', 'Spacing', 'Typography', 'Flexbox & Grid'). Omit to list all categories."),
|
|
100
|
+
version: z.enum(["v3", "v4"]).optional().describe("Tailwind CSS major version (default: v4)"),
|
|
101
|
+
}, (params) => {
|
|
102
|
+
try {
|
|
103
|
+
const result = handleListUtilities(params, db, config.defaultVersion);
|
|
104
|
+
const text = formatUtilitiesList(result);
|
|
105
|
+
return { content: [{ type: "text", text }] };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return toolError(error);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// Register check_status (does not require embedder)
|
|
112
|
+
server.tool(TOOL_NAMES.CHECK_STATUS, "Check the current index status for Tailwind CSS documentation. Shows which versions are indexed, chunk counts, and when they were last updated.", {
|
|
113
|
+
version: z
|
|
114
|
+
.enum(["v3", "v4"])
|
|
115
|
+
.optional()
|
|
116
|
+
.describe("Check specific version (v3 or v4). Omit to check all."),
|
|
117
|
+
}, (params) => {
|
|
118
|
+
try {
|
|
119
|
+
const result = handleCheckStatus(params, db, embedderStatus);
|
|
120
|
+
return { content: [{ type: "text", text: result.message }] };
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return toolError(error);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// Connect via provided transport or default to stdio
|
|
127
|
+
await server.connect(transport ?? new StdioServerTransport());
|
|
128
|
+
return {
|
|
129
|
+
setEmbedder(e) {
|
|
130
|
+
embedder = e;
|
|
131
|
+
embedderStatus = "ready";
|
|
132
|
+
},
|
|
133
|
+
setEmbedderStatus(status) {
|
|
134
|
+
embedderStatus = status;
|
|
135
|
+
},
|
|
136
|
+
getEmbedderStatus() {
|
|
137
|
+
return embedderStatus;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/E,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,KAAK,CAC5C,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAC5C,CAAC;AAEzB;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,UAAU,EAAE,YAAY;IACxB,WAAW,EAAE,aAAa;IAC1B,cAAc,EAAE,gBAAgB;IAChC,YAAY,EAAE,cAAc;CACpB,CAAC;AA6BX,MAAM,CAAC,MAAM,wBAAwB,GAAmC;IACtE,OAAO,EAAE,sEAAsE;IAC/E,WAAW,EAAE,qEAAqE;IAClF,KAAK,EAAE,2BAA2B;IAClC,MAAM,EAAE,gEAAgE;CACzE,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAgB,EAAE,SAAqB;IACxE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;IAC5B,IAAI,QAAQ,GAAoB,IAAI,CAAC,QAAQ,CAAC;IAC9C,IAAI,cAAc,GAAmB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAEzE,SAAS,eAAe;QACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,cAAc,CAAC,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,SAAS,SAAS,CAAC,KAAc;QAC/B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YAC/D,OAAO,EAAE,IAAa;SACvB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,cAAc;KACxB,CAAC,CAAC;IAEH,sBAAsB;IACtB,MAAM,CAAC,IAAI,CACT,UAAU,CAAC,UAAU,EACrB,oJAAoJ,EACpJ;QACE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;QAC7F,KAAK,EAAE,CAAC;aACL,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,wEAAwE,CAAC;KACtF,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;YAC5E,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,uBAAuB;IACvB,MAAM,CAAC,IAAI,CACT,UAAU,CAAC,WAAW,EACtB,qKAAqK,EACrK;QACE,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,CACP,2HAA2H,CAC5H;QACH,OAAO,EAAE,CAAC;aACP,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;aAClB,QAAQ,EAAE;aACV,QAAQ,CAAC,oDAAoD,CAAC;QACjE,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,EAAE,CAAC;aACP,QAAQ,EAAE;aACV,QAAQ,CAAC,kDAAkD,CAAC;KAChE,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;YAC5F,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;YACzC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,sDAAsD;IACtD,MAAM,CAAC,IAAI,CACT,UAAU,CAAC,cAAc,EACzB,kHAAkH,EAClH;QACE,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,8GAA8G,CAC/G;QACH,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;KAC9F,EACD,CAAC,MAAM,EAAE,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;YACtE,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;YACzC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,oDAAoD;IACpD,MAAM,CAAC,IAAI,CACT,UAAU,CAAC,YAAY,EACvB,iJAAiJ,EACjJ;QACE,OAAO,EAAE,CAAC;aACP,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;aAClB,QAAQ,EAAE;aACV,QAAQ,CAAC,uDAAuD,CAAC;KACrE,EACD,CAAC,MAAM,EAAE,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,EAAE,EAAE,EAAE,cAAc,CAAC,CAAC;YAC7D,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,qDAAqD;IACrD,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,IAAI,oBAAoB,EAAE,CAAC,CAAC;IAE9D,OAAO;QACL,WAAW,CAAC,CAAW;YACrB,QAAQ,GAAG,CAAC,CAAC;YACb,cAAc,GAAG,OAAO,CAAC;QAC3B,CAAC;QACD,iBAAiB,CAAC,MAAsB;YACtC,cAAc,GAAG,MAAM,CAAC;QAC1B,CAAC;QACD,iBAAiB;YACf,OAAO,cAAc,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Chunk } from "../pipeline/chunker.js";
|
|
2
|
+
import type { CleanDocument } from "../pipeline/parser.js";
|
|
3
|
+
import type { Config, TailwindVersion } from "../utils/config.js";
|
|
4
|
+
/**
|
|
5
|
+
* A document row from the `docs` table.
|
|
6
|
+
*/
|
|
7
|
+
export interface DocRow {
|
|
8
|
+
id: number;
|
|
9
|
+
slug: string;
|
|
10
|
+
title: string;
|
|
11
|
+
description: string | null;
|
|
12
|
+
url: string;
|
|
13
|
+
version: string;
|
|
14
|
+
fetched_at: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A chunk row from the `chunks` table.
|
|
18
|
+
*/
|
|
19
|
+
export interface ChunkRow {
|
|
20
|
+
id: number;
|
|
21
|
+
doc_id: number;
|
|
22
|
+
heading: string;
|
|
23
|
+
content: string;
|
|
24
|
+
content_hash: string;
|
|
25
|
+
embedding: Buffer | null;
|
|
26
|
+
url: string;
|
|
27
|
+
token_count: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Index status from the `index_status` table.
|
|
31
|
+
*/
|
|
32
|
+
export interface IndexStatus {
|
|
33
|
+
version: string;
|
|
34
|
+
doc_count: number;
|
|
35
|
+
chunk_count: number;
|
|
36
|
+
embedding_model: string;
|
|
37
|
+
embedding_dimensions: number;
|
|
38
|
+
indexed_at: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The database interface for storing and retrieving documentation.
|
|
42
|
+
*/
|
|
43
|
+
export interface Database {
|
|
44
|
+
/** Insert or update a document */
|
|
45
|
+
upsertDoc(doc: CleanDocument): number;
|
|
46
|
+
/** Insert a chunk with its embedding */
|
|
47
|
+
upsertChunk(chunk: Chunk, docId: number, embedding: Float32Array | null): void;
|
|
48
|
+
/** Get a document by slug and version */
|
|
49
|
+
getDoc(slug: string, version: TailwindVersion): DocRow | undefined;
|
|
50
|
+
/** Get all chunks for a document */
|
|
51
|
+
getChunksForDoc(docId: number): ChunkRow[];
|
|
52
|
+
/** Get all chunks with embeddings for a version (for in-memory search) */
|
|
53
|
+
getAllChunksWithEmbeddings(version: TailwindVersion): ChunkRow[];
|
|
54
|
+
/** Get chunk by content hash (for incremental re-indexing) */
|
|
55
|
+
getChunkByHash(contentHash: string, docId: number): ChunkRow | undefined;
|
|
56
|
+
/** Delete orphaned chunks not in the given hash set */
|
|
57
|
+
deleteOrphanedChunks(docId: number, validHashes: Set<string>): number;
|
|
58
|
+
/** Delete all data for a version */
|
|
59
|
+
deleteVersion(version: TailwindVersion): void;
|
|
60
|
+
/** Update index status after indexing */
|
|
61
|
+
updateIndexStatus(version: TailwindVersion, model: string, dimensions: number): void;
|
|
62
|
+
/** Get index status for a version */
|
|
63
|
+
getIndexStatus(version?: TailwindVersion): IndexStatus[];
|
|
64
|
+
/** Get a document by ID */
|
|
65
|
+
getDocById(id: number): DocRow | undefined;
|
|
66
|
+
/** Search chunks using FTS5 full-text search, ordered by BM25 relevance */
|
|
67
|
+
searchFts(query: string, version: TailwindVersion, limit: number): ChunkRow[];
|
|
68
|
+
/** Close the database connection */
|
|
69
|
+
close(): void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a database instance.
|
|
73
|
+
*
|
|
74
|
+
* Uses `bun:sqlite` when running under Bun, falls back to `better-sqlite3`
|
|
75
|
+
* for Node.js. The runtime detection is automatic.
|
|
76
|
+
*/
|
|
77
|
+
export declare function createDatabase(config: Config): Promise<Database>;
|
|
78
|
+
/**
|
|
79
|
+
* Convert a Float32Array to a Buffer for BLOB storage.
|
|
80
|
+
*/
|
|
81
|
+
export declare function embeddingToBlob(embedding: Float32Array): Buffer;
|
|
82
|
+
/**
|
|
83
|
+
* Convert a BLOB Buffer back to a Float32Array.
|
|
84
|
+
*
|
|
85
|
+
* Copies through Uint8Array to guarantee 4-byte alignment — a Buffer's
|
|
86
|
+
* byteOffset may not be aligned, which would cause Float32Array to throw
|
|
87
|
+
* a RangeError on construction.
|
|
88
|
+
*/
|
|
89
|
+
export declare function blobToEmbedding(blob: Buffer): Float32Array;
|
|
90
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* SQL schema for the documentation database.
|
|
5
|
+
*/
|
|
6
|
+
const SCHEMA = `
|
|
7
|
+
-- Metadata
|
|
8
|
+
CREATE TABLE IF NOT EXISTS docs (
|
|
9
|
+
id INTEGER PRIMARY KEY,
|
|
10
|
+
slug TEXT NOT NULL,
|
|
11
|
+
title TEXT NOT NULL,
|
|
12
|
+
description TEXT,
|
|
13
|
+
url TEXT NOT NULL,
|
|
14
|
+
version TEXT NOT NULL,
|
|
15
|
+
fetched_at TEXT NOT NULL,
|
|
16
|
+
UNIQUE(slug, version)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
-- Chunks with text content and embeddings
|
|
20
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
21
|
+
id INTEGER PRIMARY KEY,
|
|
22
|
+
doc_id INTEGER NOT NULL REFERENCES docs(id),
|
|
23
|
+
heading TEXT NOT NULL,
|
|
24
|
+
content TEXT NOT NULL,
|
|
25
|
+
content_hash TEXT NOT NULL,
|
|
26
|
+
embedding BLOB,
|
|
27
|
+
url TEXT NOT NULL,
|
|
28
|
+
token_count INTEGER NOT NULL,
|
|
29
|
+
UNIQUE(doc_id, heading, content_hash)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
-- Full-text search for keyword queries (exact class names like px-4, grid-cols-3)
|
|
33
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
|
|
34
|
+
heading, content,
|
|
35
|
+
content='chunks',
|
|
36
|
+
content_rowid='id',
|
|
37
|
+
tokenize = "unicode61 tokenchars '-'"
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
-- Index status
|
|
41
|
+
CREATE TABLE IF NOT EXISTS index_status (
|
|
42
|
+
version TEXT PRIMARY KEY,
|
|
43
|
+
doc_count INTEGER,
|
|
44
|
+
chunk_count INTEGER,
|
|
45
|
+
embedding_model TEXT,
|
|
46
|
+
embedding_dimensions INTEGER,
|
|
47
|
+
indexed_at TEXT NOT NULL
|
|
48
|
+
);
|
|
49
|
+
`;
|
|
50
|
+
/**
|
|
51
|
+
* FTS5 triggers to keep chunks_fts in sync with the chunks table.
|
|
52
|
+
*
|
|
53
|
+
* These triggers fire on INSERT, DELETE, and UPDATE on the chunks table
|
|
54
|
+
* and mirror changes into the FTS5 content-sync'd virtual table.
|
|
55
|
+
*/
|
|
56
|
+
const FTS_TRIGGERS = `
|
|
57
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON chunks BEGIN
|
|
58
|
+
INSERT INTO chunks_fts(rowid, heading, content) VALUES (new.id, new.heading, new.content);
|
|
59
|
+
END;
|
|
60
|
+
|
|
61
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ad AFTER DELETE ON chunks BEGIN
|
|
62
|
+
INSERT INTO chunks_fts(chunks_fts, rowid, heading, content) VALUES ('delete', old.id, old.heading, old.content);
|
|
63
|
+
END;
|
|
64
|
+
|
|
65
|
+
CREATE TRIGGER IF NOT EXISTS chunks_au AFTER UPDATE ON chunks BEGIN
|
|
66
|
+
INSERT INTO chunks_fts(chunks_fts, rowid, heading, content) VALUES ('delete', old.id, old.heading, old.content);
|
|
67
|
+
INSERT INTO chunks_fts(rowid, heading, content) VALUES (new.id, new.heading, new.content);
|
|
68
|
+
END;
|
|
69
|
+
`;
|
|
70
|
+
/**
|
|
71
|
+
* Open a SQLite database using bun:sqlite, wrapping it in SqliteDb.
|
|
72
|
+
*/
|
|
73
|
+
async function openBunSqlite(dbPath) {
|
|
74
|
+
const mod = (await import("bun:sqlite"));
|
|
75
|
+
const BunDatabase = mod.default ?? mod.Database;
|
|
76
|
+
if (!BunDatabase)
|
|
77
|
+
throw new Error("bun:sqlite module has no Database export");
|
|
78
|
+
const db = new BunDatabase(dbPath);
|
|
79
|
+
// Enable WAL mode for better concurrent read performance
|
|
80
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
81
|
+
// Enforce foreign key constraints at the database level
|
|
82
|
+
db.exec("PRAGMA foreign_keys=ON");
|
|
83
|
+
// Wait up to 5s for locks instead of failing immediately with SQLITE_BUSY
|
|
84
|
+
db.exec("PRAGMA busy_timeout=5000");
|
|
85
|
+
// Cache compiled statements to avoid re-calling db.query() on every operation.
|
|
86
|
+
const stmtCache = new Map();
|
|
87
|
+
function stmt(sql) {
|
|
88
|
+
let s = stmtCache.get(sql);
|
|
89
|
+
if (!s) {
|
|
90
|
+
s = db.query(sql);
|
|
91
|
+
stmtCache.set(sql, s);
|
|
92
|
+
}
|
|
93
|
+
return s;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
exec(sql) {
|
|
97
|
+
db.exec(sql);
|
|
98
|
+
},
|
|
99
|
+
queryGet(sql, ...params) {
|
|
100
|
+
const result = stmt(sql).get(...params);
|
|
101
|
+
// bun:sqlite returns null for no match; normalize to undefined
|
|
102
|
+
return (result ?? undefined);
|
|
103
|
+
},
|
|
104
|
+
queryAll(sql, ...params) {
|
|
105
|
+
return stmt(sql).all(...params);
|
|
106
|
+
},
|
|
107
|
+
queryRun(sql, ...params) {
|
|
108
|
+
stmt(sql).run(...params);
|
|
109
|
+
// bun:sqlite doesn't return changes from .run(), so we query it.
|
|
110
|
+
// Safe: bun:sqlite is synchronous and single-threaded — no write can
|
|
111
|
+
// interleave between .run() and this SELECT within the same connection.
|
|
112
|
+
const info = stmt("SELECT changes() as changes, last_insert_rowid() as lastInsertRowid").get();
|
|
113
|
+
return info;
|
|
114
|
+
},
|
|
115
|
+
close() {
|
|
116
|
+
stmtCache.clear();
|
|
117
|
+
db.close();
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Open a SQLite database using better-sqlite3, wrapping it in SqliteDb.
|
|
123
|
+
*/
|
|
124
|
+
async function openBetterSqlite3(dbPath) {
|
|
125
|
+
const mod = await import("better-sqlite3");
|
|
126
|
+
const BetterSqlite3 = mod.default;
|
|
127
|
+
const db = new BetterSqlite3(dbPath);
|
|
128
|
+
// Enable WAL mode for better concurrent read performance
|
|
129
|
+
db.pragma("journal_mode=WAL");
|
|
130
|
+
// Enforce foreign key constraints at the database level
|
|
131
|
+
db.pragma("foreign_keys=ON");
|
|
132
|
+
// Wait up to 5s for locks instead of failing immediately with SQLITE_BUSY
|
|
133
|
+
db.pragma("busy_timeout=5000");
|
|
134
|
+
// Cache compiled statements to avoid re-calling db.prepare() on every operation.
|
|
135
|
+
// Use `any` for the cached statement type because better-sqlite3's Statement
|
|
136
|
+
// generic overloads are incompatible with unknown[] spread args at the type level.
|
|
137
|
+
// biome-ignore lint/suspicious/noExplicitAny: better-sqlite3 Statement generics
|
|
138
|
+
const stmtCache = new Map();
|
|
139
|
+
function stmt(sql) {
|
|
140
|
+
let s = stmtCache.get(sql);
|
|
141
|
+
if (!s) {
|
|
142
|
+
s = db.prepare(sql);
|
|
143
|
+
stmtCache.set(sql, s);
|
|
144
|
+
}
|
|
145
|
+
return s;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
exec(sql) {
|
|
149
|
+
db.exec(sql);
|
|
150
|
+
},
|
|
151
|
+
queryGet(sql, ...params) {
|
|
152
|
+
return stmt(sql).get(...params);
|
|
153
|
+
},
|
|
154
|
+
queryAll(sql, ...params) {
|
|
155
|
+
return stmt(sql).all(...params);
|
|
156
|
+
},
|
|
157
|
+
queryRun(sql, ...params) {
|
|
158
|
+
const result = stmt(sql).run(...params);
|
|
159
|
+
return {
|
|
160
|
+
changes: result.changes,
|
|
161
|
+
lastInsertRowid: Number(result.lastInsertRowid),
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
close() {
|
|
165
|
+
stmtCache.clear();
|
|
166
|
+
db.close();
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Detect runtime and open the appropriate SQLite driver.
|
|
172
|
+
*
|
|
173
|
+
* Tries bun:sqlite first (zero-dependency when running under Bun),
|
|
174
|
+
* then falls back to better-sqlite3 (Node.js).
|
|
175
|
+
*/
|
|
176
|
+
async function openDatabase(dbPath) {
|
|
177
|
+
let bunError;
|
|
178
|
+
try {
|
|
179
|
+
return await openBunSqlite(dbPath);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
bunError = error;
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
return await openBetterSqlite3(dbPath);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
throw new Error(`Failed to open SQLite database. bun:sqlite: ${bunError instanceof Error ? bunError.message : String(bunError)}; better-sqlite3: ${error instanceof Error ? error.message : String(error)}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create a database instance.
|
|
193
|
+
*
|
|
194
|
+
* Uses `bun:sqlite` when running under Bun, falls back to `better-sqlite3`
|
|
195
|
+
* for Node.js. The runtime detection is automatic.
|
|
196
|
+
*/
|
|
197
|
+
export async function createDatabase(config) {
|
|
198
|
+
// Ensure the database directory exists (skip for in-memory databases)
|
|
199
|
+
if (config.dbPath !== ":memory:") {
|
|
200
|
+
mkdirSync(dirname(config.dbPath), { recursive: true });
|
|
201
|
+
}
|
|
202
|
+
const db = await openDatabase(config.dbPath);
|
|
203
|
+
// Run schema DDL and FTS triggers
|
|
204
|
+
db.exec(SCHEMA);
|
|
205
|
+
db.exec(FTS_TRIGGERS);
|
|
206
|
+
return {
|
|
207
|
+
upsertDoc(doc) {
|
|
208
|
+
db.queryRun(`INSERT INTO docs (slug, title, description, url, version, fetched_at)
|
|
209
|
+
VALUES (?, ?, ?, ?, ?, datetime('now'))
|
|
210
|
+
ON CONFLICT(slug, version) DO UPDATE SET
|
|
211
|
+
title = excluded.title,
|
|
212
|
+
description = excluded.description,
|
|
213
|
+
url = excluded.url,
|
|
214
|
+
fetched_at = excluded.fetched_at`, doc.slug, doc.title, doc.description, doc.url, doc.version);
|
|
215
|
+
// ON CONFLICT UPDATE does not change lastInsertRowid reliably,
|
|
216
|
+
// so always query the actual id by unique key.
|
|
217
|
+
const row = db.queryGet("SELECT id FROM docs WHERE slug = ? AND version = ?", doc.slug, doc.version);
|
|
218
|
+
if (!row) {
|
|
219
|
+
throw new Error(`Failed to upsert doc: ${doc.slug} (${doc.version})`);
|
|
220
|
+
}
|
|
221
|
+
return row.id;
|
|
222
|
+
},
|
|
223
|
+
upsertChunk(chunk, docId, embedding) {
|
|
224
|
+
const blob = embedding ? embeddingToBlob(embedding) : null;
|
|
225
|
+
db.queryRun(`INSERT INTO chunks (doc_id, heading, content, content_hash, embedding, url, token_count)
|
|
226
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
227
|
+
ON CONFLICT(doc_id, heading, content_hash) DO UPDATE SET
|
|
228
|
+
content = excluded.content,
|
|
229
|
+
embedding = excluded.embedding,
|
|
230
|
+
url = excluded.url,
|
|
231
|
+
token_count = excluded.token_count`, docId, chunk.heading, chunk.content, chunk.id, blob, chunk.url, chunk.tokenCount);
|
|
232
|
+
},
|
|
233
|
+
getDoc(slug, version) {
|
|
234
|
+
return db.queryGet("SELECT * FROM docs WHERE slug = ? AND version = ?", slug, version);
|
|
235
|
+
},
|
|
236
|
+
getChunksForDoc(docId) {
|
|
237
|
+
return db.queryAll("SELECT * FROM chunks WHERE doc_id = ?", docId);
|
|
238
|
+
},
|
|
239
|
+
getAllChunksWithEmbeddings(version) {
|
|
240
|
+
return db.queryAll(`SELECT c.* FROM chunks c
|
|
241
|
+
JOIN docs d ON c.doc_id = d.id
|
|
242
|
+
WHERE d.version = ? AND c.embedding IS NOT NULL`, version);
|
|
243
|
+
},
|
|
244
|
+
getChunkByHash(contentHash, docId) {
|
|
245
|
+
return db.queryGet("SELECT * FROM chunks WHERE content_hash = ? AND doc_id = ?", contentHash, docId);
|
|
246
|
+
},
|
|
247
|
+
deleteOrphanedChunks(docId, validHashes) {
|
|
248
|
+
if (validHashes.size === 0) {
|
|
249
|
+
// Delete all chunks for this doc
|
|
250
|
+
const result = db.queryRun("DELETE FROM chunks WHERE doc_id = ?", docId);
|
|
251
|
+
return result.changes;
|
|
252
|
+
}
|
|
253
|
+
// Use fixed SQL strings to avoid statement cache pollution.
|
|
254
|
+
// A dynamic IN (?, ?, ...) clause creates a unique cached statement
|
|
255
|
+
// per hash-set size. Instead, SELECT all + delete non-matching individually.
|
|
256
|
+
const all = db.queryAll("SELECT id, content_hash FROM chunks WHERE doc_id = ?", docId);
|
|
257
|
+
let deleted = 0;
|
|
258
|
+
for (const row of all) {
|
|
259
|
+
if (!validHashes.has(row.content_hash)) {
|
|
260
|
+
db.queryRun("DELETE FROM chunks WHERE id = ?", row.id);
|
|
261
|
+
deleted++;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return deleted;
|
|
265
|
+
},
|
|
266
|
+
deleteVersion(version) {
|
|
267
|
+
// Delete chunks first (referencing docs), then docs
|
|
268
|
+
db.queryRun(`DELETE FROM chunks WHERE doc_id IN (
|
|
269
|
+
SELECT id FROM docs WHERE version = ?
|
|
270
|
+
)`, version);
|
|
271
|
+
db.queryRun("DELETE FROM docs WHERE version = ?", version);
|
|
272
|
+
db.queryRun("DELETE FROM index_status WHERE version = ?", version);
|
|
273
|
+
},
|
|
274
|
+
updateIndexStatus(version, model, dimensions) {
|
|
275
|
+
const docCount = db.queryGet("SELECT COUNT(*) as count FROM docs WHERE version = ?", version);
|
|
276
|
+
const chunkCount = db.queryGet("SELECT COUNT(*) as count FROM chunks WHERE doc_id IN (SELECT id FROM docs WHERE version = ?)", version);
|
|
277
|
+
db.queryRun(`INSERT INTO index_status (version, doc_count, chunk_count, embedding_model, embedding_dimensions, indexed_at)
|
|
278
|
+
VALUES (?, ?, ?, ?, ?, datetime('now'))
|
|
279
|
+
ON CONFLICT(version) DO UPDATE SET
|
|
280
|
+
doc_count = excluded.doc_count,
|
|
281
|
+
chunk_count = excluded.chunk_count,
|
|
282
|
+
embedding_model = excluded.embedding_model,
|
|
283
|
+
embedding_dimensions = excluded.embedding_dimensions,
|
|
284
|
+
indexed_at = excluded.indexed_at`, version, docCount?.count ?? 0, chunkCount?.count ?? 0, model, dimensions);
|
|
285
|
+
},
|
|
286
|
+
getIndexStatus(version) {
|
|
287
|
+
if (version) {
|
|
288
|
+
return db.queryAll("SELECT * FROM index_status WHERE version = ?", version);
|
|
289
|
+
}
|
|
290
|
+
return db.queryAll("SELECT * FROM index_status");
|
|
291
|
+
},
|
|
292
|
+
getDocById(id) {
|
|
293
|
+
return db.queryGet("SELECT * FROM docs WHERE id = ?", id);
|
|
294
|
+
},
|
|
295
|
+
searchFts(query, version, limit) {
|
|
296
|
+
try {
|
|
297
|
+
// Escape FTS5 special characters by double-quoting each token
|
|
298
|
+
const escaped = query
|
|
299
|
+
.split(/\s+/)
|
|
300
|
+
.filter((t) => t.length > 0)
|
|
301
|
+
.map((t) => `"${t.replace(/"/g, '""')}"`)
|
|
302
|
+
.join(" OR ");
|
|
303
|
+
if (!escaped)
|
|
304
|
+
return [];
|
|
305
|
+
return db.queryAll(`SELECT c.* FROM chunks_fts f
|
|
306
|
+
JOIN chunks c ON c.id = f.rowid
|
|
307
|
+
JOIN docs d ON c.doc_id = d.id
|
|
308
|
+
WHERE chunks_fts MATCH ? AND d.version = ?
|
|
309
|
+
ORDER BY bm25(chunks_fts)
|
|
310
|
+
LIMIT ?`, escaped, version, limit);
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
console.warn("[tailwindcss-docs-mcp] FTS5 search failed:", error);
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
close() {
|
|
318
|
+
db.close();
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Convert a Float32Array to a Buffer for BLOB storage.
|
|
324
|
+
*/
|
|
325
|
+
export function embeddingToBlob(embedding) {
|
|
326
|
+
// Copy via Uint8Array to decouple from the source ArrayBuffer,
|
|
327
|
+
// symmetric with blobToEmbedding which also copies.
|
|
328
|
+
return Buffer.from(new Uint8Array(embedding.buffer, embedding.byteOffset, embedding.byteLength));
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Convert a BLOB Buffer back to a Float32Array.
|
|
332
|
+
*
|
|
333
|
+
* Copies through Uint8Array to guarantee 4-byte alignment — a Buffer's
|
|
334
|
+
* byteOffset may not be aligned, which would cause Float32Array to throw
|
|
335
|
+
* a RangeError on construction.
|
|
336
|
+
*/
|
|
337
|
+
export function blobToEmbedding(blob) {
|
|
338
|
+
const aligned = new Uint8Array(blob.byteLength);
|
|
339
|
+
aligned.set(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
|
|
340
|
+
return new Float32Array(aligned.buffer);
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
|