UNPKG

elysia

Version:

Ergonomic Framework for Human

366 lines (365 loc) 13.4 kB
const hasReturn = (fn) => { const fnLiteral = typeof fn === "object" ? fn.fn.toString() : typeof fn === "string" ? fn.toString() : fn; const parenthesisEnd = fnLiteral.indexOf(")"); if (fnLiteral.charCodeAt(parenthesisEnd + 2) === 61 && fnLiteral.charCodeAt(parenthesisEnd + 5) !== 123) { return true; } return fnLiteral.includes("return"); }; const separateFunction = (code) => { if (code.startsWith("async")) code = code.slice(5); code = code.trimStart(); let index = -1; if (code.charCodeAt(0) === 40) { index = code.indexOf("=>", code.indexOf(")")); if (index !== -1) { let bracketEndIndex = index; while (bracketEndIndex > 0) if (code.charCodeAt(--bracketEndIndex) === 41) break; let body = code.slice(index + 2); if (body.charCodeAt(0) === 32) body = body.trimStart(); return [ code.slice(1, bracketEndIndex), body, { isArrowReturn: body.charCodeAt(0) !== 123 } ]; } } if (/^(\w+)=>/g.test(code)) { index = code.indexOf("=>"); if (index !== -1) { let body = code.slice(index + 2); if (body.charCodeAt(0) === 32) body = body.trimStart(); return [ code.slice(0, index), body, { isArrowReturn: body.charCodeAt(0) !== 123 } ]; } } if (code.startsWith("function")) { index = code.indexOf("("); const end = code.indexOf(")"); return [ code.slice(index + 1, end), code.slice(end + 2), { isArrowReturn: false } ]; } const start = code.indexOf("("); if (start !== -1) { const sep = code.indexOf("\n", 2); const parameter = code.slice(0, sep); const end = parameter.lastIndexOf(")") + 1; const body = code.slice(sep + 1); return [ parameter.slice(start, end), "{" + body, { isArrowReturn: false } ]; } const x = code.split("\n", 2); return [x[0], x[1], { isArrowReturn: false }]; }; const bracketPairRange = (parameter) => { const start = parameter.indexOf("{"); if (start === -1) return [-1, 0]; let end = start + 1; let deep = 1; for (; end < parameter.length; end++) { const char = parameter.charCodeAt(end); if (char === 123) deep++; else if (char === 125) deep--; if (deep === 0) break; } if (deep !== 0) return [0, parameter.length]; return [start, end + 1]; }; const bracketPairRangeReverse = (parameter) => { const end = parameter.lastIndexOf("}"); if (end === -1) return [-1, 0]; let start = end - 1; let deep = 1; for (; start >= 0; start--) { const char = parameter.charCodeAt(start); if (char === 125) deep++; else if (char === 123) deep--; if (deep === 0) break; } if (deep !== 0) return [-1, 0]; return [start, end + 1]; }; const removeColonAlias = (parameter) => { while (true) { const start = parameter.indexOf(":"); if (start === -1) break; let end = parameter.indexOf(",", start); if (end === -1) end = parameter.indexOf("}", start) - 1; if (end === -2) end = parameter.length; parameter = parameter.slice(0, start) + parameter.slice(end); } return parameter; }; const retrieveRootParamters = (parameter) => { let hasParenthesis = false; if (parameter.charCodeAt(0) === 40) parameter = parameter.slice(1, -1); if (parameter.charCodeAt(0) === 123) { hasParenthesis = true; parameter = parameter.slice(1, -1); } parameter = parameter.replace(/( |\t|\n)/g, "").trim(); let parameters = []; while (true) { let [start, end] = bracketPairRange(parameter); if (start === -1) break; parameters.push(parameter.slice(0, start - 1)); if (parameter.charCodeAt(end) === 44) end++; parameter = parameter.slice(end); } parameter = removeColonAlias(parameter); if (parameter) parameters = parameters.concat(parameter.split(",")); const newParameters = []; for (const p of parameters) { if (p.indexOf(",") === -1) { newParameters.push(p); continue; } for (const q of p.split(",")) newParameters.push(q.trim()); } parameters = newParameters; return { hasParenthesis, parameters }; }; const findParameterReference = (parameter, inference) => { const { parameters, hasParenthesis } = retrieveRootParamters(parameter); if (!inference.query && parameters.includes("query")) inference.query = true; if (!inference.headers && parameters.includes("headers")) inference.headers = true; if (!inference.body && parameters.includes("body")) inference.body = true; if (!inference.cookie && parameters.includes("cookie")) inference.cookie = true; if (!inference.set && parameters.includes("set")) inference.set = true; if (!inference.server && parameters.includes("server")) inference.server = true; if (!inference.request && parameters.includes("request")) inference.request = true; if (!inference.route && parameters.includes("route")) inference.route = true; if (hasParenthesis) return `{ ${parameters.join(", ")} }`; return parameters.join(", "); }; const findEndIndex = (type, content, index) => { const newLineIndex = content.indexOf(type + "\n", index); const newTabIndex = content.indexOf(type + " ", index); const commaIndex = content.indexOf(type + ",", index); const semicolonIndex = content.indexOf(type + ";", index); const emptyIndex = content.indexOf(type + " ", index); return [newLineIndex, newTabIndex, commaIndex, semicolonIndex, emptyIndex].filter((i) => i > 0).sort((a, b) => a - b)[0] || -1; }; const findEndQueryBracketIndex = (type, content, index) => { const bracketEndIndex = content.indexOf(type + "]", index); const singleQuoteIndex = content.indexOf(type + "'", index); const doubleQuoteIndex = content.indexOf(type + '"', index); return [bracketEndIndex, singleQuoteIndex, doubleQuoteIndex].filter((i) => i > 0).sort((a, b) => a - b)[0] || -1; }; const findAlias = (type, body, depth = 0) => { if (depth > 5) return []; const aliases = []; let content = body; while (true) { let index = findEndIndex(" = " + type, content); if (index === -1) index = findEndIndex("=" + type, content); if (index === -1) { let lastIndex = content.indexOf(" = " + type); if (lastIndex === -1) lastIndex = content.indexOf("=" + type); if (lastIndex + 3 + type.length !== content.length) break; index = lastIndex; } const part = content.slice(0, index); const lastPart = part.lastIndexOf(" "); let variable = part.slice(lastPart !== -1 ? lastPart + 1 : -1); if (variable === "}") { const [start, end] = bracketPairRangeReverse(part); aliases.push(removeColonAlias(content.slice(start, end))); content = content.slice(index + 3 + type.length); continue; } while (variable.charCodeAt(0) === 44) variable = variable.slice(1); while (variable.charCodeAt(0) === 9) variable = variable.slice(1); if (!variable.includes("(")) aliases.push(variable); content = content.slice(index + 3 + type.length); } for (const alias of aliases) { if (alias.charCodeAt(0) === 123) continue; const deepAlias = findAlias(alias, body); if (deepAlias.length > 0) aliases.push(...deepAlias); } return aliases; }; const extractMainParameter = (parameter) => { if (!parameter) return; if (parameter.charCodeAt(0) !== 123) return parameter; parameter = parameter.slice(2, -2); const hasComma = parameter.includes(","); if (!hasComma) { if (parameter.includes("...")) return parameter.slice(parameter.indexOf("...") + 3); return; } const spreadIndex = parameter.indexOf("..."); if (spreadIndex === -1) return; return parameter.slice(spreadIndex + 3).trimEnd(); }; const inferBodyReference = (code, aliases, inference) => { const access = (type, alias) => code.includes(alias + "." + type) || code.includes(alias + '["' + type + '"]') || code.includes(alias + "['" + type + "']"); for (const alias of aliases) { if (!alias) continue; if (alias.charCodeAt(0) === 123) { const parameters = retrieveRootParamters(alias).parameters; if (!inference.query && parameters.includes("query")) inference.query = true; if (!inference.headers && parameters.includes("headers")) inference.headers = true; if (!inference.body && parameters.includes("body")) inference.body = true; if (!inference.cookie && parameters.includes("cookie")) inference.cookie = true; if (!inference.set && parameters.includes("set")) inference.set = true; if (!inference.query && parameters.includes("server")) inference.server = true; if (!inference.request && parameters.includes("request")) inference.request = true; if (!inference.route && parameters.includes("route")) inference.route = true; continue; } if (!inference.query && access("query", alias)) inference.query = true; if (code.includes("return " + alias) || code.includes("return " + alias + ".query")) inference.query = true; if (!inference.headers && access("headers", alias)) inference.headers = true; if (!inference.body && access("body", alias)) inference.body = true; if (!inference.cookie && access("cookie", alias)) inference.cookie = true; if (!inference.set && access("set", alias)) inference.set = true; if (!inference.server && access("server", alias)) inference.server = true; if (inference.query && inference.headers && inference.body && inference.cookie && inference.set && inference.server && inference.server && inference.route) break; } return aliases; }; const removeDefaultParameter = (parameter) => { while (true) { const index = parameter.indexOf("="); if (index === -1) break; const commaIndex = parameter.indexOf(",", index); const bracketIndex = parameter.indexOf("}", index); const end = [commaIndex, bracketIndex].filter((i) => i > 0).sort((a, b) => a - b)[0] || -1; if (end === -1) { parameter = parameter.slice(0, index); break; } parameter = parameter.slice(0, index) + parameter.slice(end); } return parameter.split(",").map((i) => i.trim()).join(", "); }; const isContextPassToFunction = (context, body, inference) => { try { const captureFunction = new RegExp(`(?:\\w)\\((?:.*)?${context}`, "gs"); captureFunction.test(body); const nextChar = body.charCodeAt(captureFunction.lastIndex); if (nextChar === 41 || nextChar === 44) { inference.query = true; inference.headers = true; inference.body = true; inference.cookie = true; inference.set = true; inference.server = true; inference.route = true; inference.request = true; return true; } return false; } catch (error) { console.log( "[Sucrose] warning: unexpected isContextPassToFunction error, you may continue development as usual but please report the following to maintainers:" ); console.log("--- body ---"); console.log(body); console.log("--- context ---"); console.log(context); return true; } }; const sucrose = (lifeCycle, inference = { query: false, headers: false, body: false, cookie: false, set: false, server: false, request: false, route: false }) => { const events = []; if (lifeCycle.handler && typeof lifeCycle.handler === "function") events.push(lifeCycle.handler); if (lifeCycle.request?.length) events.push(...lifeCycle.request); if (lifeCycle.beforeHandle?.length) events.push(...lifeCycle.beforeHandle); if (lifeCycle.parse?.length) events.push(...lifeCycle.parse); if (lifeCycle.error?.length) events.push(...lifeCycle.error); if (lifeCycle.transform?.length) events.push(...lifeCycle.transform); if (lifeCycle.afterHandle?.length) events.push(...lifeCycle.afterHandle); if (lifeCycle.mapResponse?.length) events.push(...lifeCycle.mapResponse); if (lifeCycle.afterResponse?.length) events.push(...lifeCycle.afterResponse); for (const e of events) { if (!e) continue; const event = "fn" in e ? e.fn : e; if (typeof event !== "function") continue; const [parameter, body, { isArrowReturn }] = separateFunction( event.toString() ); const rootParameters = findParameterReference(parameter, inference); const mainParameter = extractMainParameter(rootParameters); if (mainParameter) { const aliases = findAlias(mainParameter, body.slice(1, -1)); aliases.splice(0, -1, mainParameter); let code = body; if (code.charCodeAt(0) === 123 && code.charCodeAt(body.length - 1) === 125) code = code.slice(1, -1); if (!isContextPassToFunction(mainParameter, code, inference)) inferBodyReference(code, aliases, inference); if (!inference.query && code.includes("return " + mainParameter + ".query")) inference.query = true; } if (inference.query && inference.headers && inference.body && inference.cookie && inference.set && inference.server && inference.request && inference.route) break; } return inference; }; export { bracketPairRange, bracketPairRangeReverse, extractMainParameter, findAlias, findParameterReference, hasReturn, inferBodyReference, isContextPassToFunction, removeColonAlias, removeDefaultParameter, retrieveRootParamters, separateFunction, sucrose };