Skip to main content
Glama
bruToJson.js24.3 kB
import ohm from "ohm-js"; import _ from "lodash"; import { outdentString } from "../bruno-utils.js"; /** * A Bru file is made up of blocks. * There are two types of blocks * * 1. Dictionary Blocks - These are blocks that have key value pairs * ex: * headers { * content-type: application/json * } * * 2. Text Blocks - These are blocks that have text * ex: * body:json { * { * "username": "John Nash", * "password": "governingdynamics * } * */ const grammar = ohm.grammar(`Bru { BruFile = (meta | http | query | params | headers | auths | bodies | varsandassert | script | tests | docs)* auths = authawsv4 | authbasic | authbearer | authdigest | authNTLM | authOAuth2 | authwsse | authapikey bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body bodyforms = bodyformurlencoded | bodymultipart | bodyfile params = paramspath | paramsquery nl = "\\r"? "\\n" st = " " | "\\t" stnl = st | nl tagend = nl "}" optionalnl = ~tagend nl keychar = ~(tagend | st | nl | ":") any valuechar = ~(nl | tagend) any // Multiline text block surrounded by ''' multilinetextblockdelimiter = "'''" multilinetextblock = multilinetextblockdelimiter (~multilinetextblockdelimiter any)* multilinetextblockdelimiter // Dictionary Blocks dictionary = st* "{" pairlist? tagend pairlist = optionalnl* pair (~tagend stnl* pair)* (~tagend space)* pair = st* key st* ":" st* value st* key = keychar* value = multilinetextblock | valuechar* // Dictionary for Assert Block assertdictionary = st* "{" assertpairlist? tagend assertpairlist = optionalnl* assertpair (~tagend stnl* assertpair)* (~tagend space)* assertpair = st* assertkey st* ":" st* value st* assertkey = ~tagend assertkeychar* assertkeychar = ~(tagend | nl | ":") any // Text Blocks textblock = textline (~tagend nl textline)* textline = textchar* textchar = ~nl any meta = "meta" dictionary http = get | post | put | delete | patch | options | head | connect | trace get = "get" dictionary post = "post" dictionary put = "put" dictionary delete = "delete" dictionary patch = "patch" dictionary options = "options" dictionary head = "head" dictionary connect = "connect" dictionary trace = "trace" dictionary headers = "headers" dictionary query = "query" dictionary paramspath = "params:path" dictionary paramsquery = "params:query" dictionary varsandassert = varsreq | varsres | assert varsreq = "vars:pre-request" dictionary varsres = "vars:post-response" dictionary assert = "assert" assertdictionary authawsv4 = "auth:awsv4" dictionary authbasic = "auth:basic" dictionary authbearer = "auth:bearer" dictionary authdigest = "auth:digest" dictionary authNTLM = "auth:ntlm" dictionary authOAuth2 = "auth:oauth2" dictionary authwsse = "auth:wsse" dictionary authapikey = "auth:apikey" dictionary body = "body" st* "{" nl* textblock tagend bodyjson = "body:json" st* "{" nl* textblock tagend bodytext = "body:text" st* "{" nl* textblock tagend bodyxml = "body:xml" st* "{" nl* textblock tagend bodysparql = "body:sparql" st* "{" nl* textblock tagend bodygraphql = "body:graphql" st* "{" nl* textblock tagend bodygraphqlvars = "body:graphql:vars" st* "{" nl* textblock tagend bodyformurlencoded = "body:form-urlencoded" dictionary bodymultipart = "body:multipart-form" dictionary bodyfile = "body:file" dictionary script = scriptreq | scriptres scriptreq = "script:pre-request" st* "{" nl* textblock tagend scriptres = "script:post-response" st* "{" nl* textblock tagend tests = "tests" st* "{" nl* textblock tagend docs = "docs" st* "{" nl* textblock tagend }`); const mapPairListToKeyValPairs = (pairList = [], parseEnabled = true) => { if (!pairList.length) { return []; } return _.map(pairList[0], (pair) => { let name = _.keys(pair)[0]; let value = pair[name]; if (!parseEnabled) { return { name, value, }; } let enabled = true; if (name && name.length && name.charAt(0) === "~") { name = name.slice(1); enabled = false; } return { name, value, enabled, }; }); }; const mapRequestParams = (pairList = [], type) => { if (!pairList.length) { return []; } return _.map(pairList[0], (pair) => { let name = _.keys(pair)[0]; let value = pair[name]; let enabled = true; if (name && name.length && name.charAt(0) === "~") { name = name.slice(1); enabled = false; } return { name, value, enabled, type, }; }); }; const multipartExtractContentType = (pair) => { if (_.isString(pair.value)) { const match = pair.value.match(/^(.*?)\s*@contentType\((.*?)\)\s*$/); if (match != null && match.length > 2) { pair.value = match[1]; pair.contentType = match[2]; } else { pair.contentType = ""; } } }; const fileExtractContentType = (pair) => { if (_.isString(pair.value)) { const match = pair.value.match(/^(.*?)\s*@contentType\((.*?)\)\s*$/); if (match && match.length > 2) { pair.value = match[1].trim(); pair.contentType = match[2].trim(); } else { pair.contentType = ""; } } }; const mapPairListToKeyValPairsMultipart = ( pairList = [], parseEnabled = true ) => { const pairs = mapPairListToKeyValPairs(pairList, parseEnabled); return pairs.map((pair) => { pair.type = "text"; multipartExtractContentType(pair); if (pair.value.startsWith("@file(") && pair.value.endsWith(")")) { let filestr = pair.value.replace(/^@file\(/, "").replace(/\)$/, ""); pair.type = "file"; pair.value = filestr.split("|"); } return pair; }); }; const mapPairListToKeyValPairsFile = (pairList = [], parseEnabled = true) => { const pairs = mapPairListToKeyValPairs(pairList, parseEnabled); return pairs.map((pair) => { fileExtractContentType(pair); if (pair.value.startsWith("@file(") && pair.value.endsWith(")")) { let filePath = pair.value.replace(/^@file\(/, "").replace(/\)$/, ""); pair.filePath = filePath; pair.selected = pair.enabled; // Remove pair.value as it only contains the file path reference delete pair.value; // Remove pair.name as it is auto-generated (e.g., file1, file2, file3, etc.) delete pair.name; delete pair.enabled; } return pair; }); }; const concatArrays = (objValue, srcValue) => { if (_.isArray(objValue) && _.isArray(srcValue)) { return objValue.concat(srcValue); } }; const mapPairListToKeyValPair = (pairList = []) => { if (!pairList || !pairList.length) { return {}; } return _.merge({}, ...pairList[0]); }; const sem = grammar.createSemantics().addAttribute("ast", { BruFile(tags) { if (!tags || !tags.ast || !tags.ast.length) { return {}; } return _.reduce( tags.ast, (result, item) => { return _.mergeWith(result, item, concatArrays); }, {} ); }, dictionary(_1, _2, pairlist, _3) { return pairlist.ast; }, pairlist(_1, pair, _2, rest, _3) { return [pair.ast, ...rest.ast]; }, pair(_1, key, _2, _3, _4, value, _5) { let res = {}; res[key.ast] = value.ast ? value.ast.trim() : ""; return res; }, key(chars) { return chars.sourceString ? chars.sourceString.trim() : ""; }, value(chars) { try { let isMultiline = chars.sourceString?.startsWith(`'''`) && chars.sourceString?.endsWith(`'''`); if (isMultiline) { const multilineString = chars.sourceString?.replace(/^'''|'''$/g, ""); return multilineString .split("\n") .map((line) => line.slice(4)) .join("\n"); } return chars.sourceString ? chars.sourceString.trim() : ""; } catch (err) { console.error(err); } return chars.sourceString ? chars.sourceString.trim() : ""; }, assertdictionary(_1, _2, pairlist, _3) { return pairlist.ast; }, assertpairlist(_1, pair, _2, rest, _3) { return [pair.ast, ...rest.ast]; }, assertpair(_1, key, _2, _3, _4, value, _5) { let res = {}; res[key.ast] = value.ast ? value.ast.trim() : ""; return res; }, assertkey(chars) { return chars.sourceString ? chars.sourceString.trim() : ""; }, textblock(line, _1, rest) { return [line.ast, ...rest.ast].join("\n"); }, textline(chars) { return chars.sourceString; }, textchar(char) { return char.sourceString; }, nl(_1, _2) { return ""; }, st(_) { return ""; }, tagend(_1, _2) { return ""; }, _iter(...elements) { return elements.map((e) => e.ast); }, meta(_1, dictionary) { let meta = mapPairListToKeyValPair(dictionary.ast); if (!meta.seq) { meta.seq = 1; } if (!meta.type) { meta.type = "http"; } return { meta, }; }, get(_1, dictionary) { return { http: { method: "get", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, post(_1, dictionary) { return { http: { method: "post", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, put(_1, dictionary) { return { http: { method: "put", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, delete(_1, dictionary) { return { http: { method: "delete", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, patch(_1, dictionary) { return { http: { method: "patch", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, options(_1, dictionary) { return { http: { method: "options", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, head(_1, dictionary) { return { http: { method: "head", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, connect(_1, dictionary) { return { http: { method: "connect", ...mapPairListToKeyValPair(dictionary.ast), }, }; }, query(_1, dictionary) { return { params: mapRequestParams(dictionary.ast, "query"), }; }, paramspath(_1, dictionary) { return { params: mapRequestParams(dictionary.ast, "path"), }; }, paramsquery(_1, dictionary) { return { params: mapRequestParams(dictionary.ast, "query"), }; }, headers(_1, dictionary) { return { headers: mapPairListToKeyValPairs(dictionary.ast), }; }, authawsv4(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const accessKeyIdKey = _.find(auth, { name: "accessKeyId" }); const secretAccessKeyKey = _.find(auth, { name: "secretAccessKey" }); const sessionTokenKey = _.find(auth, { name: "sessionToken" }); const serviceKey = _.find(auth, { name: "service" }); const regionKey = _.find(auth, { name: "region" }); const profileNameKey = _.find(auth, { name: "profileName" }); const accessKeyId = accessKeyIdKey ? accessKeyIdKey.value : ""; const secretAccessKey = secretAccessKeyKey ? secretAccessKeyKey.value : ""; const sessionToken = sessionTokenKey ? sessionTokenKey.value : ""; const service = serviceKey ? serviceKey.value : ""; const region = regionKey ? regionKey.value : ""; const profileName = profileNameKey ? profileNameKey.value : ""; return { auth: { awsv4: { accessKeyId, secretAccessKey, sessionToken, service, region, profileName, }, }, }; }, authbasic(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const usernameKey = _.find(auth, { name: "username" }); const passwordKey = _.find(auth, { name: "password" }); const username = usernameKey ? usernameKey.value : ""; const password = passwordKey ? passwordKey.value : ""; return { auth: { basic: { username, password, }, }, }; }, authbearer(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const tokenKey = _.find(auth, { name: "token" }); const token = tokenKey ? tokenKey.value : ""; return { auth: { bearer: { token, }, }, }; }, authdigest(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const usernameKey = _.find(auth, { name: "username" }); const passwordKey = _.find(auth, { name: "password" }); const username = usernameKey ? usernameKey.value : ""; const password = passwordKey ? passwordKey.value : ""; return { auth: { digest: { username, password, }, }, }; }, authNTLM(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const usernameKey = _.find(auth, { name: "username" }); const passwordKey = _.find(auth, { name: "password" }); const domainKey = _.find(auth, { name: "domain" }); const username = usernameKey ? usernameKey.value : ""; const password = passwordKey ? passwordKey.value : ""; const domain = passwordKey ? domainKey.value : ""; return { auth: { ntlm: { username, password, domain, }, }, }; }, authOAuth2(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const grantTypeKey = _.find(auth, { name: "grant_type" }); const usernameKey = _.find(auth, { name: "username" }); const passwordKey = _.find(auth, { name: "password" }); const callbackUrlKey = _.find(auth, { name: "callback_url" }); const authorizationUrlKey = _.find(auth, { name: "authorization_url" }); const accessTokenUrlKey = _.find(auth, { name: "access_token_url" }); const refreshTokenUrlKey = _.find(auth, { name: "refresh_token_url" }); const clientIdKey = _.find(auth, { name: "client_id" }); const clientSecretKey = _.find(auth, { name: "client_secret" }); const scopeKey = _.find(auth, { name: "scope" }); const stateKey = _.find(auth, { name: "state" }); const pkceKey = _.find(auth, { name: "pkce" }); const credentialsPlacementKey = _.find(auth, { name: "credentials_placement", }); const credentialsIdKey = _.find(auth, { name: "credentials_id" }); const tokenPlacementKey = _.find(auth, { name: "token_placement" }); const tokenHeaderPrefixKey = _.find(auth, { name: "token_header_prefix" }); const tokenQueryKeyKey = _.find(auth, { name: "token_query_key" }); const autoFetchTokenKey = _.find(auth, { name: "auto_fetch_token" }); const autoRefreshTokenKey = _.find(auth, { name: "auto_refresh_token" }); return { auth: { oauth2: grantTypeKey?.value && grantTypeKey?.value == "password" ? { grantType: grantTypeKey ? grantTypeKey.value : "", accessTokenUrl: accessTokenUrlKey ? accessTokenUrlKey.value : "", refreshTokenUrl: refreshTokenUrlKey ? refreshTokenUrlKey.value : "", username: usernameKey ? usernameKey.value : "", password: passwordKey ? passwordKey.value : "", clientId: clientIdKey ? clientIdKey.value : "", clientSecret: clientSecretKey ? clientSecretKey.value : "", scope: scopeKey ? scopeKey.value : "", credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : "body", credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : "credentials", tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : "header", tokenHeaderPrefix: tokenHeaderPrefixKey?.value ? tokenHeaderPrefixKey.value : "Bearer", tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : "access_token", autoFetchToken: autoFetchTokenKey ? JSON.parse(autoFetchTokenKey?.value) : true, autoRefreshToken: autoRefreshTokenKey ? JSON.parse(autoRefreshTokenKey?.value) : true, } : grantTypeKey?.value && grantTypeKey?.value == "authorization_code" ? { grantType: grantTypeKey ? grantTypeKey.value : "", callbackUrl: callbackUrlKey ? callbackUrlKey.value : "", authorizationUrl: authorizationUrlKey ? authorizationUrlKey.value : "", accessTokenUrl: accessTokenUrlKey ? accessTokenUrlKey.value : "", refreshTokenUrl: refreshTokenUrlKey ? refreshTokenUrlKey.value : "", clientId: clientIdKey ? clientIdKey.value : "", clientSecret: clientSecretKey ? clientSecretKey.value : "", scope: scopeKey ? scopeKey.value : "", state: stateKey ? stateKey.value : "", pkce: pkceKey ? JSON.parse(pkceKey?.value || false) : false, credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : "body", credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : "credentials", tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : "header", tokenHeaderPrefix: tokenHeaderPrefixKey?.value ? tokenHeaderPrefixKey.value : "Bearer", tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : "access_token", autoFetchToken: autoFetchTokenKey ? JSON.parse(autoFetchTokenKey?.value) : true, autoRefreshToken: autoRefreshTokenKey ? JSON.parse(autoRefreshTokenKey?.value) : true, } : grantTypeKey?.value && grantTypeKey?.value == "client_credentials" ? { grantType: grantTypeKey ? grantTypeKey.value : "", accessTokenUrl: accessTokenUrlKey ? accessTokenUrlKey.value : "", refreshTokenUrl: refreshTokenUrlKey ? refreshTokenUrlKey.value : "", clientId: clientIdKey ? clientIdKey.value : "", clientSecret: clientSecretKey ? clientSecretKey.value : "", scope: scopeKey ? scopeKey.value : "", credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : "body", credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : "credentials", tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : "header", tokenHeaderPrefix: tokenHeaderPrefixKey?.value ? tokenHeaderPrefixKey.value : "Bearer", tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : "access_token", autoFetchToken: autoFetchTokenKey ? JSON.parse(autoFetchTokenKey?.value) : true, autoRefreshToken: autoRefreshTokenKey ? JSON.parse(autoRefreshTokenKey?.value) : true, } : {}, }, }; }, authwsse(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const userKey = _.find(auth, { name: "username" }); const secretKey = _.find(auth, { name: "password" }); const username = userKey ? userKey.value : ""; const password = secretKey ? secretKey.value : ""; return { auth: { wsse: { username, password, }, }, }; }, authapikey(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const findValueByName = (name) => { const item = _.find(auth, { name }); return item ? item.value : ""; }; const key = findValueByName("key"); const value = findValueByName("value"); const placement = findValueByName("placement"); return { auth: { apikey: { key, value, placement, }, }, }; }, bodyformurlencoded(_1, dictionary) { return { body: { formUrlEncoded: mapPairListToKeyValPairs(dictionary.ast), }, }; }, bodymultipart(_1, dictionary) { return { body: { multipartForm: mapPairListToKeyValPairsMultipart(dictionary.ast), }, }; }, bodyfile(_1, dictionary) { return { body: { file: mapPairListToKeyValPairsFile(dictionary.ast), }, }; }, body(_1, _2, _3, _4, textblock, _5) { return { http: { body: "json", }, body: { json: outdentString(textblock.sourceString), }, }; }, bodyjson(_1, _2, _3, _4, textblock, _5) { return { body: { json: outdentString(textblock.sourceString), }, }; }, bodytext(_1, _2, _3, _4, textblock, _5) { return { body: { text: outdentString(textblock.sourceString), }, }; }, bodyxml(_1, _2, _3, _4, textblock, _5) { return { body: { xml: outdentString(textblock.sourceString), }, }; }, bodysparql(_1, _2, _3, _4, textblock, _5) { return { body: { sparql: outdentString(textblock.sourceString), }, }; }, bodygraphql(_1, _2, _3, _4, textblock, _5) { return { body: { graphql: { query: outdentString(textblock.sourceString), }, }, }; }, bodygraphqlvars(_1, _2, _3, _4, textblock, _5) { return { body: { graphql: { variables: outdentString(textblock.sourceString), }, }, }; }, varsreq(_1, dictionary) { const vars = mapPairListToKeyValPairs(dictionary.ast); _.each(vars, (v) => { let name = v.name; if (name && name.length && name.charAt(0) === "@") { v.name = name.slice(1); v.local = true; } else { v.local = false; } }); return { vars: { req: vars, }, }; }, varsres(_1, dictionary) { const vars = mapPairListToKeyValPairs(dictionary.ast); _.each(vars, (v) => { let name = v.name; if (name && name.length && name.charAt(0) === "@") { v.name = name.slice(1); v.local = true; } else { v.local = false; } }); return { vars: { res: vars, }, }; }, assert(_1, dictionary) { return { assertions: mapPairListToKeyValPairs(dictionary.ast), }; }, scriptreq(_1, _2, _3, _4, textblock, _5) { return { script: { req: outdentString(textblock.sourceString), }, }; }, scriptres(_1, _2, _3, _4, textblock, _5) { return { script: { res: outdentString(textblock.sourceString), }, }; }, tests(_1, _2, _3, _4, textblock, _5) { return { tests: outdentString(textblock.sourceString), }; }, docs(_1, _2, _3, _4, textblock, _5) { return { docs: outdentString(textblock.sourceString), }; }, }); const parser = (input) => { const match = grammar.match(input); if (match.succeeded()) { return sem(match).ast; } else { throw new Error(match.message); } }; export default parser;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/djkz/bruno-api-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server