@orpc/server
Advanced tools
Comparing version 0.11.0 to 0.12.0
@@ -6,630 +6,45 @@ import { | ||
// src/adapters/fetch.ts | ||
import { | ||
ORPC_HEADER, | ||
ORPC_HEADER_VALUE, | ||
standardizeHTTPPath | ||
} from "@orpc/contract"; | ||
import { | ||
get, | ||
isPlainObject, | ||
mapValues, | ||
trim, | ||
value | ||
} from "@orpc/shared"; | ||
// src/fetch/handle.ts | ||
import { ORPCError } from "@orpc/shared/error"; | ||
import { | ||
OpenAPIDeserializer, | ||
OpenAPISerializer, | ||
ORPCDeserializer, | ||
ORPCSerializer, | ||
zodCoerce | ||
} from "@orpc/transformer"; | ||
// ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router.js | ||
var METHOD_NAME_ALL = "ALL"; | ||
var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built."; | ||
var UnsupportedPathError = class extends Error { | ||
}; | ||
// ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/utils/url.js | ||
var checkOptionalParameter = (path) => { | ||
if (!path.match(/\:.+\?$/)) { | ||
return null; | ||
async function handleFetchRequest(options) { | ||
for (const handler of options.handlers) { | ||
const response = await handler(options); | ||
if (response) { | ||
return response; | ||
} | ||
} | ||
const segments = path.split("/"); | ||
const results = []; | ||
let basePath = ""; | ||
segments.forEach((segment) => { | ||
if (segment !== "" && !/\:/.test(segment)) { | ||
basePath += "/" + segment; | ||
} else if (/\:/.test(segment)) { | ||
if (/\?/.test(segment)) { | ||
if (results.length === 0 && basePath === "") { | ||
results.push("/"); | ||
} else { | ||
results.push(basePath); | ||
} | ||
const optionalSegment = segment.replace("?", ""); | ||
basePath += "/" + optionalSegment; | ||
results.push(basePath); | ||
} else { | ||
basePath += "/" + segment; | ||
} | ||
const error = new ORPCError({ code: "NOT_FOUND", message: "Not found" }); | ||
return new Response(JSON.stringify(error.toJSON()), { | ||
status: error.status, | ||
headers: { | ||
"Content-Type": "application/json" | ||
} | ||
}); | ||
return results.filter((v, i, a) => a.indexOf(v) === i); | ||
}; | ||
// ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/linear-router/router.js | ||
var emptyParams = /* @__PURE__ */ Object.create(null); | ||
var splitPathRe = /\/(:\w+(?:{(?:(?:{[\d,]+})|[^}])+})?)|\/[^\/\?]+|(\?)/g; | ||
var splitByStarRe = /\*/; | ||
var LinearRouter = class { | ||
name = "LinearRouter"; | ||
routes = []; | ||
add(method, path, handler) { | ||
; | ||
(checkOptionalParameter(path) || [path]).forEach((p) => { | ||
this.routes.push([method, p, handler]); | ||
}); | ||
} | ||
match(method, path) { | ||
const handlers = []; | ||
ROUTES_LOOP: | ||
for (let i = 0, len = this.routes.length; i < len; i++) { | ||
const [routeMethod, routePath, handler] = this.routes[i]; | ||
if (routeMethod !== method && routeMethod !== METHOD_NAME_ALL) { | ||
continue; | ||
} | ||
if (routePath === "*" || routePath === "/*") { | ||
handlers.push([handler, emptyParams]); | ||
continue; | ||
} | ||
const hasStar = routePath.indexOf("*") !== -1; | ||
const hasLabel = routePath.indexOf(":") !== -1; | ||
if (!hasStar && !hasLabel) { | ||
if (routePath === path || routePath + "/" === path) { | ||
handlers.push([handler, emptyParams]); | ||
} | ||
} else if (hasStar && !hasLabel) { | ||
const endsWithStar = routePath.charCodeAt(routePath.length - 1) === 42; | ||
const parts = (endsWithStar ? routePath.slice(0, -2) : routePath).split(splitByStarRe); | ||
const lastIndex = parts.length - 1; | ||
for (let j = 0, pos = 0, len2 = parts.length; j < len2; j++) { | ||
const part = parts[j]; | ||
const index = path.indexOf(part, pos); | ||
if (index !== pos) { | ||
continue ROUTES_LOOP; | ||
} | ||
pos += part.length; | ||
if (j === lastIndex) { | ||
if (!endsWithStar && pos !== path.length && !(pos === path.length - 1 && path.charCodeAt(pos) === 47)) { | ||
continue ROUTES_LOOP; | ||
} | ||
} else { | ||
const index2 = path.indexOf("/", pos); | ||
if (index2 === -1) { | ||
continue ROUTES_LOOP; | ||
} | ||
pos = index2; | ||
} | ||
} | ||
handlers.push([handler, emptyParams]); | ||
} else if (hasLabel && !hasStar) { | ||
const params = /* @__PURE__ */ Object.create(null); | ||
const parts = routePath.match(splitPathRe); | ||
const lastIndex = parts.length - 1; | ||
for (let j = 0, pos = 0, len2 = parts.length; j < len2; j++) { | ||
if (pos === -1 || pos >= path.length) { | ||
continue ROUTES_LOOP; | ||
} | ||
const part = parts[j]; | ||
if (part.charCodeAt(1) === 58) { | ||
let name = part.slice(2); | ||
let value2; | ||
if (name.charCodeAt(name.length - 1) === 125) { | ||
const openBracePos = name.indexOf("{"); | ||
const pattern = name.slice(openBracePos + 1, -1); | ||
const restPath = path.slice(pos + 1); | ||
const match = new RegExp(pattern, "d").exec(restPath); | ||
if (!match || match.indices[0][0] !== 0 || match.indices[0][1] === 0) { | ||
continue ROUTES_LOOP; | ||
} | ||
name = name.slice(0, openBracePos); | ||
value2 = restPath.slice(...match.indices[0]); | ||
pos += match.indices[0][1] + 1; | ||
} else { | ||
let endValuePos = path.indexOf("/", pos + 1); | ||
if (endValuePos === -1) { | ||
if (pos + 1 === path.length) { | ||
continue ROUTES_LOOP; | ||
} | ||
endValuePos = path.length; | ||
} | ||
value2 = path.slice(pos + 1, endValuePos); | ||
pos = endValuePos; | ||
} | ||
params[name] ||= value2; | ||
} else { | ||
const index = path.indexOf(part, pos); | ||
if (index !== pos) { | ||
continue ROUTES_LOOP; | ||
} | ||
pos += part.length; | ||
} | ||
if (j === lastIndex) { | ||
if (pos !== path.length && !(pos === path.length - 1 && path.charCodeAt(pos) === 47)) { | ||
continue ROUTES_LOOP; | ||
} | ||
} | ||
} | ||
handlers.push([handler, params]); | ||
} else if (hasLabel && hasStar) { | ||
throw new UnsupportedPathError(); | ||
} | ||
} | ||
return [handlers]; | ||
} | ||
}; | ||
// ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/reg-exp-router/node.js | ||
var LABEL_REG_EXP_STR = "[^/]+"; | ||
var ONLY_WILDCARD_REG_EXP_STR = ".*"; | ||
var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)"; | ||
var PATH_ERROR = Symbol(); | ||
var regExpMetaChars = new Set(".\\+*[^]$()"); | ||
function compareKey(a, b) { | ||
if (a.length === 1) { | ||
return b.length === 1 ? a < b ? -1 : 1 : -1; | ||
} | ||
if (b.length === 1) { | ||
return 1; | ||
} | ||
if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) { | ||
return 1; | ||
} else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) { | ||
return -1; | ||
} | ||
if (a === LABEL_REG_EXP_STR) { | ||
return 1; | ||
} else if (b === LABEL_REG_EXP_STR) { | ||
return -1; | ||
} | ||
return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length; | ||
} | ||
var Node = class { | ||
index; | ||
varIndex; | ||
children = /* @__PURE__ */ Object.create(null); | ||
insert(tokens, index, paramMap, context, pathErrorCheckOnly) { | ||
if (tokens.length === 0) { | ||
if (this.index !== void 0) { | ||
throw PATH_ERROR; | ||
} | ||
if (pathErrorCheckOnly) { | ||
return; | ||
} | ||
this.index = index; | ||
return; | ||
} | ||
const [token, ...restTokens] = tokens; | ||
const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); | ||
let node; | ||
if (pattern) { | ||
const name = pattern[1]; | ||
let regexpStr = pattern[2] || LABEL_REG_EXP_STR; | ||
if (name && pattern[2]) { | ||
regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:"); | ||
if (/\((?!\?:)/.test(regexpStr)) { | ||
throw PATH_ERROR; | ||
} | ||
} | ||
node = this.children[regexpStr]; | ||
if (!node) { | ||
if (Object.keys(this.children).some( | ||
(k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR | ||
)) { | ||
throw PATH_ERROR; | ||
} | ||
if (pathErrorCheckOnly) { | ||
return; | ||
} | ||
node = this.children[regexpStr] = new Node(); | ||
if (name !== "") { | ||
node.varIndex = context.varIndex++; | ||
} | ||
} | ||
if (!pathErrorCheckOnly && name !== "") { | ||
paramMap.push([name, node.varIndex]); | ||
} | ||
} else { | ||
node = this.children[token]; | ||
if (!node) { | ||
if (Object.keys(this.children).some( | ||
(k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR | ||
)) { | ||
throw PATH_ERROR; | ||
} | ||
if (pathErrorCheckOnly) { | ||
return; | ||
} | ||
node = this.children[token] = new Node(); | ||
} | ||
} | ||
node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly); | ||
} | ||
buildRegExpStr() { | ||
const childKeys = Object.keys(this.children).sort(compareKey); | ||
const strList = childKeys.map((k) => { | ||
const c = this.children[k]; | ||
return (typeof c.varIndex === "number" ? `(${k})@${c.varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr(); | ||
}); | ||
if (typeof this.index === "number") { | ||
strList.unshift(`#${this.index}`); | ||
} | ||
if (strList.length === 0) { | ||
return ""; | ||
} | ||
if (strList.length === 1) { | ||
return strList[0]; | ||
} | ||
return "(?:" + strList.join("|") + ")"; | ||
} | ||
}; | ||
// ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/reg-exp-router/trie.js | ||
var Trie = class { | ||
context = { varIndex: 0 }; | ||
root = new Node(); | ||
insert(path, index, pathErrorCheckOnly) { | ||
const paramAssoc = []; | ||
const groups = []; | ||
for (let i = 0; ; ) { | ||
let replaced = false; | ||
path = path.replace(/\{[^}]+\}/g, (m) => { | ||
const mark = `@\\${i}`; | ||
groups[i] = [mark, m]; | ||
i++; | ||
replaced = true; | ||
return mark; | ||
}); | ||
if (!replaced) { | ||
break; | ||
} | ||
// src/fetch/handler.ts | ||
import { ORPC_HEADER, ORPC_HEADER_VALUE } from "@orpc/contract"; | ||
import { trim, value } from "@orpc/shared"; | ||
import { ORPCError as ORPCError2 } from "@orpc/shared/error"; | ||
import { ORPCDeserializer, ORPCSerializer } from "@orpc/transformer"; | ||
var serializer = new ORPCSerializer(); | ||
var deserializer = new ORPCDeserializer(); | ||
function createORPCHandler() { | ||
return async (options) => { | ||
if (options.request.headers.get(ORPC_HEADER) !== ORPC_HEADER_VALUE) { | ||
return void 0; | ||
} | ||
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []; | ||
for (let i = groups.length - 1; i >= 0; i--) { | ||
const [mark] = groups[i]; | ||
for (let j = tokens.length - 1; j >= 0; j--) { | ||
if (tokens[j].indexOf(mark) !== -1) { | ||
tokens[j] = tokens[j].replace(mark, groups[i][1]); | ||
break; | ||
} | ||
} | ||
} | ||
this.root.insert(tokens, index, paramAssoc, this.context, pathErrorCheckOnly); | ||
return paramAssoc; | ||
} | ||
buildRegExp() { | ||
let regexp = this.root.buildRegExpStr(); | ||
if (regexp === "") { | ||
return [/^$/, [], []]; | ||
} | ||
let captureIndex = 0; | ||
const indexReplacementMap = []; | ||
const paramReplacementMap = []; | ||
regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => { | ||
if (typeof handlerIndex !== "undefined") { | ||
indexReplacementMap[++captureIndex] = Number(handlerIndex); | ||
return "$()"; | ||
} | ||
if (typeof paramIndex !== "undefined") { | ||
paramReplacementMap[Number(paramIndex)] = ++captureIndex; | ||
return ""; | ||
} | ||
return ""; | ||
}); | ||
return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap]; | ||
} | ||
}; | ||
// ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/reg-exp-router/router.js | ||
var emptyParam = []; | ||
var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)]; | ||
var wildcardRegExpCache = /* @__PURE__ */ Object.create(null); | ||
function buildWildcardRegExp(path) { | ||
return wildcardRegExpCache[path] ??= new RegExp( | ||
path === "*" ? "" : `^${path.replace( | ||
/\/\*$|([.\\+*[^\]$()])/g, | ||
(_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)" | ||
)}$` | ||
); | ||
} | ||
function clearWildcardRegExpCache() { | ||
wildcardRegExpCache = /* @__PURE__ */ Object.create(null); | ||
} | ||
function buildMatcherFromPreprocessedRoutes(routes) { | ||
const trie = new Trie(); | ||
const handlerData = []; | ||
if (routes.length === 0) { | ||
return nullMatcher; | ||
} | ||
const routesWithStaticPathFlag = routes.map( | ||
(route) => [!/\*|\/:/.test(route[0]), ...route] | ||
).sort( | ||
([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length | ||
); | ||
const staticMap = /* @__PURE__ */ Object.create(null); | ||
for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) { | ||
const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i]; | ||
if (pathErrorCheckOnly) { | ||
staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam]; | ||
} else { | ||
j++; | ||
} | ||
let paramAssoc; | ||
try { | ||
paramAssoc = trie.insert(path, j, pathErrorCheckOnly); | ||
} catch (e) { | ||
throw e === PATH_ERROR ? new UnsupportedPathError(path) : e; | ||
} | ||
if (pathErrorCheckOnly) { | ||
continue; | ||
} | ||
handlerData[j] = handlers.map(([h, paramCount]) => { | ||
const paramIndexMap = /* @__PURE__ */ Object.create(null); | ||
paramCount -= 1; | ||
for (; paramCount >= 0; paramCount--) { | ||
const [key, value2] = paramAssoc[paramCount]; | ||
paramIndexMap[key] = value2; | ||
} | ||
return [h, paramIndexMap]; | ||
}); | ||
} | ||
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp(); | ||
for (let i = 0, len = handlerData.length; i < len; i++) { | ||
for (let j = 0, len2 = handlerData[i].length; j < len2; j++) { | ||
const map = handlerData[i][j]?.[1]; | ||
if (!map) { | ||
continue; | ||
} | ||
const keys = Object.keys(map); | ||
for (let k = 0, len3 = keys.length; k < len3; k++) { | ||
map[keys[k]] = paramReplacementMap[map[keys[k]]]; | ||
} | ||
} | ||
} | ||
const handlerMap = []; | ||
for (const i in indexReplacementMap) { | ||
handlerMap[i] = handlerData[indexReplacementMap[i]]; | ||
} | ||
return [regexp, handlerMap, staticMap]; | ||
} | ||
function findMiddleware(middleware, path) { | ||
if (!middleware) { | ||
return void 0; | ||
} | ||
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) { | ||
if (buildWildcardRegExp(k).test(path)) { | ||
return [...middleware[k]]; | ||
} | ||
} | ||
return void 0; | ||
} | ||
var RegExpRouter = class { | ||
name = "RegExpRouter"; | ||
middleware; | ||
routes; | ||
constructor() { | ||
this.middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) }; | ||
this.routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) }; | ||
} | ||
add(method, path, handler) { | ||
const { middleware, routes } = this; | ||
if (!middleware || !routes) { | ||
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT); | ||
} | ||
if (!middleware[method]) { | ||
; | ||
[middleware, routes].forEach((handlerMap) => { | ||
handlerMap[method] = /* @__PURE__ */ Object.create(null); | ||
Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => { | ||
handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]]; | ||
}); | ||
}); | ||
} | ||
if (path === "/*") { | ||
path = "*"; | ||
} | ||
const paramCount = (path.match(/\/:/g) || []).length; | ||
if (/\*$/.test(path)) { | ||
const re = buildWildcardRegExp(path); | ||
if (method === METHOD_NAME_ALL) { | ||
Object.keys(middleware).forEach((m) => { | ||
middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || []; | ||
}); | ||
} else { | ||
middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || []; | ||
} | ||
Object.keys(middleware).forEach((m) => { | ||
if (method === METHOD_NAME_ALL || method === m) { | ||
Object.keys(middleware[m]).forEach((p) => { | ||
re.test(p) && middleware[m][p].push([handler, paramCount]); | ||
}); | ||
} | ||
}); | ||
Object.keys(routes).forEach((m) => { | ||
if (method === METHOD_NAME_ALL || method === m) { | ||
Object.keys(routes[m]).forEach( | ||
(p) => re.test(p) && routes[m][p].push([handler, paramCount]) | ||
); | ||
} | ||
}); | ||
return; | ||
} | ||
const paths = checkOptionalParameter(path) || [path]; | ||
for (let i = 0, len = paths.length; i < len; i++) { | ||
const path2 = paths[i]; | ||
Object.keys(routes).forEach((m) => { | ||
if (method === METHOD_NAME_ALL || method === m) { | ||
routes[m][path2] ||= [ | ||
...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || [] | ||
]; | ||
routes[m][path2].push([handler, paramCount - len + i + 1]); | ||
} | ||
}); | ||
} | ||
} | ||
match(method, path) { | ||
clearWildcardRegExpCache(); | ||
const matchers = this.buildAllMatchers(); | ||
this.match = (method2, path2) => { | ||
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL]; | ||
const staticMatch = matcher[2][path2]; | ||
if (staticMatch) { | ||
return staticMatch; | ||
} | ||
const match = path2.match(matcher[0]); | ||
const context = await value(options.context); | ||
const handler = async () => { | ||
const url = new URL(options.request.url); | ||
const pathname = `/${trim(url.pathname.replace(options.prefix ?? "", ""), "/")}`; | ||
const match = resolveORPCRouter(options.router, pathname); | ||
if (!match) { | ||
return [[], emptyParam]; | ||
throw new ORPCError2({ code: "NOT_FOUND", message: "Not found" }); | ||
} | ||
const index = match.indexOf("", 1); | ||
return [matcher[1][index], match]; | ||
}; | ||
return this.match(method, path); | ||
} | ||
buildAllMatchers() { | ||
const matchers = /* @__PURE__ */ Object.create(null); | ||
[...Object.keys(this.routes), ...Object.keys(this.middleware)].forEach((method) => { | ||
matchers[method] ||= this.buildMatcher(method); | ||
}); | ||
this.middleware = this.routes = void 0; | ||
return matchers; | ||
} | ||
buildMatcher(method) { | ||
const routes = []; | ||
let hasOwnRoute = method === METHOD_NAME_ALL; | ||
[this.middleware, this.routes].forEach((r) => { | ||
const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : []; | ||
if (ownRoute.length !== 0) { | ||
hasOwnRoute ||= true; | ||
routes.push(...ownRoute); | ||
} else if (method !== METHOD_NAME_ALL) { | ||
routes.push( | ||
...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]]) | ||
); | ||
} | ||
}); | ||
if (!hasOwnRoute) { | ||
return null; | ||
} else { | ||
return buildMatcherFromPreprocessedRoutes(routes); | ||
} | ||
} | ||
}; | ||
// src/adapters/fetch.ts | ||
function createFetchHandler(options) { | ||
const routing = options.serverless ? new LinearRouter() : new RegExpRouter(); | ||
const addRouteRecursively = (router, basePath) => { | ||
for (const key in router) { | ||
const currentPath = [...basePath, key]; | ||
const item = router[key]; | ||
if (isProcedure(item)) { | ||
if (item.zz$p.contract.zz$cp.path) { | ||
const method = item.zz$p.contract.zz$cp.method ?? "POST"; | ||
const path = openAPIPathToRouterPath(item.zz$p.contract.zz$cp.path); | ||
routing.add(method, path, [currentPath, item]); | ||
} | ||
} else { | ||
addRouteRecursively(item, currentPath); | ||
} | ||
} | ||
}; | ||
addRouteRecursively(options.router, []); | ||
return async (requestOptions) => { | ||
const isORPCTransformer = requestOptions.request.headers.get(ORPC_HEADER) === ORPC_HEADER_VALUE; | ||
const accept = requestOptions.request.headers.get("Accept") || void 0; | ||
const serializer = isORPCTransformer ? new ORPCSerializer() : new OpenAPISerializer({ accept }); | ||
const context = await value(requestOptions.context); | ||
const handler = async () => { | ||
const url = new URL(requestOptions.request.url); | ||
const pathname = `/${trim(url.pathname.replace(requestOptions.prefix ?? "", ""), "/")}`; | ||
let path; | ||
let procedure; | ||
let params; | ||
if (isORPCTransformer) { | ||
path = trim(pathname, "/").split("/").map(decodeURIComponent); | ||
const val = get(options.router, path); | ||
if (isProcedure(val)) { | ||
procedure = val; | ||
} | ||
} else { | ||
const customMethod = requestOptions.request.method === "POST" ? url.searchParams.get("method")?.toUpperCase() : void 0; | ||
const method = customMethod || requestOptions.request.method; | ||
const [matches, params_] = routing.match(method, pathname); | ||
const [match] = matches.sort((a, b) => { | ||
return Object.keys(a[1]).length - Object.keys(b[1]).length; | ||
}); | ||
if (match) { | ||
path = match[0][0]; | ||
procedure = match[0][1]; | ||
if (params_) { | ||
params = mapValues( | ||
match[1], | ||
(v) => params_[v] | ||
); | ||
} else { | ||
params = match[1]; | ||
} | ||
} | ||
if (!path || !procedure) { | ||
path = trim(pathname, "/").split("/").map(decodeURIComponent); | ||
const val = get(options.router, path); | ||
if (isProcedure(val)) { | ||
procedure = val; | ||
} | ||
} | ||
} | ||
if (!path || !procedure) { | ||
throw new ORPCError({ code: "NOT_FOUND", message: "Not found" }); | ||
} | ||
const deserializer = isORPCTransformer ? new ORPCDeserializer() : new OpenAPIDeserializer({ | ||
schema: procedure.zz$p.contract.zz$cp.InputSchema | ||
}); | ||
const input_ = await (async () => { | ||
try { | ||
return await deserializer.deserialize(requestOptions.request); | ||
} catch (e) { | ||
throw new ORPCError({ | ||
code: "BAD_REQUEST", | ||
message: "Cannot parse request. Please check the request body and Content-Type header.", | ||
cause: e | ||
}); | ||
} | ||
})(); | ||
const input = (() => { | ||
if (!params || Object.keys(params).length === 0) { | ||
return input_; | ||
} | ||
const coercedParams = procedure.zz$p.contract.zz$cp.InputSchema ? zodCoerce( | ||
procedure.zz$p.contract.zz$cp.InputSchema, | ||
{ ...params }, | ||
{ | ||
bracketNotation: true | ||
} | ||
) : params; | ||
if (!isPlainObject(input_)) { | ||
return coercedParams; | ||
} | ||
return { | ||
...coercedParams, | ||
...input_ | ||
}; | ||
})(); | ||
const input = await deserializeRequest(options.request); | ||
const caller = createProcedureCaller({ | ||
context, | ||
procedure, | ||
path | ||
procedure: match.procedure, | ||
path: match.path | ||
}); | ||
@@ -644,39 +59,49 @@ const output = await caller(input); | ||
try { | ||
return await options.hooks?.(context, { | ||
next: handler, | ||
response: (response) => response | ||
}) ?? await handler(); | ||
return await options.hooks?.( | ||
context, | ||
{ next: handler, response: (response) => response } | ||
) ?? await handler(); | ||
} catch (e) { | ||
const error = toORPCError(e); | ||
try { | ||
const { body, headers } = serializer.serialize(error.toJSON()); | ||
return new Response(body, { | ||
status: error.status, | ||
headers | ||
}); | ||
} catch (e2) { | ||
const error2 = toORPCError(e2); | ||
const { body, headers } = new OpenAPISerializer().serialize( | ||
error2.toJSON() | ||
); | ||
return new Response(body, { | ||
status: error2.status, | ||
headers | ||
}); | ||
} | ||
const error = e instanceof ORPCError2 ? e : new ORPCError2({ | ||
code: "INTERNAL_SERVER_ERROR", | ||
message: "Internal server error", | ||
cause: e | ||
}); | ||
const { body, headers } = serializer.serialize(error.toJSON()); | ||
return new Response(body, { | ||
status: error.status, | ||
headers | ||
}); | ||
} | ||
}; | ||
} | ||
function openAPIPathToRouterPath(path) { | ||
return standardizeHTTPPath(path).replace(/\{([^}]+)\}/g, ":$1"); | ||
function resolveORPCRouter(router, pathname) { | ||
const path = trim(pathname, "/").split("/").map(decodeURIComponent); | ||
let current = router; | ||
for (const segment of path) { | ||
if ((typeof current !== "object" || current === null) && typeof current !== "function") { | ||
current = void 0; | ||
break; | ||
} | ||
current = current[segment]; | ||
} | ||
return isProcedure(current) ? { | ||
procedure: current, | ||
path | ||
} : void 0; | ||
} | ||
function toORPCError(e) { | ||
return e instanceof ORPCError ? e : new ORPCError({ | ||
code: "INTERNAL_SERVER_ERROR", | ||
message: "Internal server error", | ||
cause: e | ||
}); | ||
async function deserializeRequest(request) { | ||
try { | ||
return await deserializer.deserialize(request); | ||
} catch (e) { | ||
throw new ORPCError2({ | ||
code: "BAD_REQUEST", | ||
message: "Cannot parse request. Please check the request body and Content-Type header.", | ||
cause: e | ||
}); | ||
} | ||
} | ||
export { | ||
createFetchHandler | ||
createORPCHandler, | ||
handleFetchRequest | ||
}; |
{ | ||
"name": "@orpc/server", | ||
"type": "module", | ||
"version": "0.11.0", | ||
"version": "0.12.0", | ||
"license": "MIT", | ||
@@ -23,3 +23,3 @@ "homepage": "https://orpc.unnoq.com", | ||
"./fetch": { | ||
"types": "./dist/src/adapters/fetch.d.ts", | ||
"types": "./dist/src/fetch/index.d.ts", | ||
"import": "./dist/fetch.js", | ||
@@ -38,14 +38,14 @@ "default": "./dist/fetch.js" | ||
"zod": ">=3.23.0", | ||
"@orpc/zod": "0.11.0" | ||
"@orpc/zod": "0.12.0" | ||
}, | ||
"dependencies": { | ||
"@orpc/contract": "0.11.0", | ||
"@orpc/transformer": "0.11.0", | ||
"@orpc/shared": "0.11.0" | ||
"@orpc/contract": "0.12.0", | ||
"@orpc/transformer": "0.12.0", | ||
"@orpc/shared": "0.12.0" | ||
}, | ||
"devDependencies": { | ||
"hono": "^4.6.3" | ||
"@orpc/openapi": "0.12.0" | ||
}, | ||
"scripts": { | ||
"build": "tsup --clean --entry.index=src/index.ts --entry.fetch=src/adapters/fetch.ts --format=esm --onSuccess='tsc -b --noCheck'", | ||
"build": "tsup --clean --entry.index=src/index.ts --entry.fetch=src/fetch/index.ts --format=esm --onSuccess='tsc -b --noCheck'", | ||
"build:watch": "pnpm run build --watch", | ||
@@ -52,0 +52,0 @@ "type:check": "tsc -b" |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
22
0
43152
1003
+ Added@orpc/contract@0.12.0(transitive)
+ Added@orpc/shared@0.12.0(transitive)
+ Added@orpc/transformer@0.12.0(transitive)
+ Added@orpc/zod@0.12.0(transitive)
- Removed@orpc/contract@0.11.0(transitive)
- Removed@orpc/shared@0.11.0(transitive)
- Removed@orpc/transformer@0.11.0(transitive)
- Removed@orpc/zod@0.11.0(transitive)
Updated@orpc/contract@0.12.0
Updated@orpc/shared@0.12.0
Updated@orpc/transformer@0.12.0