@orpc/server 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/fetch.js CHANGED
@@ -3,634 +3,49 @@ import {
3
3
  isProcedure
4
4
  } from "./chunk-CVLK2PBB.js";
5
5
 
6
- // src/adapters/fetch.ts
7
- import {
8
- ORPC_HEADER,
9
- ORPC_HEADER_VALUE,
10
- standardizeHTTPPath
11
- } from "@orpc/contract";
12
- import {
13
- get,
14
- isPlainObject,
15
- mapValues,
16
- trim,
17
- value
18
- } from "@orpc/shared";
6
+ // src/fetch/handle.ts
19
7
  import { ORPCError } from "@orpc/shared/error";
20
- import {
21
- OpenAPIDeserializer,
22
- OpenAPISerializer,
23
- ORPCDeserializer,
24
- ORPCSerializer,
25
- zodCoerce
26
- } from "@orpc/transformer";
27
-
28
- // ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router.js
29
- var METHOD_NAME_ALL = "ALL";
30
- var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
31
- var UnsupportedPathError = class extends Error {
32
- };
33
-
34
- // ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/utils/url.js
35
- var checkOptionalParameter = (path) => {
36
- if (!path.match(/\:.+\?$/)) {
37
- return null;
38
- }
39
- const segments = path.split("/");
40
- const results = [];
41
- let basePath = "";
42
- segments.forEach((segment) => {
43
- if (segment !== "" && !/\:/.test(segment)) {
44
- basePath += "/" + segment;
45
- } else if (/\:/.test(segment)) {
46
- if (/\?/.test(segment)) {
47
- if (results.length === 0 && basePath === "") {
48
- results.push("/");
49
- } else {
50
- results.push(basePath);
51
- }
52
- const optionalSegment = segment.replace("?", "");
53
- basePath += "/" + optionalSegment;
54
- results.push(basePath);
55
- } else {
56
- basePath += "/" + segment;
57
- }
8
+ async function handleFetchRequest(options) {
9
+ for (const handler of options.handlers) {
10
+ const response = await handler(options);
11
+ if (response) {
12
+ return response;
58
13
  }
59
- });
60
- return results.filter((v, i, a) => a.indexOf(v) === i);
61
- };
62
-
63
- // ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/linear-router/router.js
64
- var emptyParams = /* @__PURE__ */ Object.create(null);
65
- var splitPathRe = /\/(:\w+(?:{(?:(?:{[\d,]+})|[^}])+})?)|\/[^\/\?]+|(\?)/g;
66
- var splitByStarRe = /\*/;
67
- var LinearRouter = class {
68
- name = "LinearRouter";
69
- routes = [];
70
- add(method, path, handler) {
71
- ;
72
- (checkOptionalParameter(path) || [path]).forEach((p) => {
73
- this.routes.push([method, p, handler]);
74
- });
75
- }
76
- match(method, path) {
77
- const handlers = [];
78
- ROUTES_LOOP:
79
- for (let i = 0, len = this.routes.length; i < len; i++) {
80
- const [routeMethod, routePath, handler] = this.routes[i];
81
- if (routeMethod !== method && routeMethod !== METHOD_NAME_ALL) {
82
- continue;
83
- }
84
- if (routePath === "*" || routePath === "/*") {
85
- handlers.push([handler, emptyParams]);
86
- continue;
87
- }
88
- const hasStar = routePath.indexOf("*") !== -1;
89
- const hasLabel = routePath.indexOf(":") !== -1;
90
- if (!hasStar && !hasLabel) {
91
- if (routePath === path || routePath + "/" === path) {
92
- handlers.push([handler, emptyParams]);
93
- }
94
- } else if (hasStar && !hasLabel) {
95
- const endsWithStar = routePath.charCodeAt(routePath.length - 1) === 42;
96
- const parts = (endsWithStar ? routePath.slice(0, -2) : routePath).split(splitByStarRe);
97
- const lastIndex = parts.length - 1;
98
- for (let j = 0, pos = 0, len2 = parts.length; j < len2; j++) {
99
- const part = parts[j];
100
- const index = path.indexOf(part, pos);
101
- if (index !== pos) {
102
- continue ROUTES_LOOP;
103
- }
104
- pos += part.length;
105
- if (j === lastIndex) {
106
- if (!endsWithStar && pos !== path.length && !(pos === path.length - 1 && path.charCodeAt(pos) === 47)) {
107
- continue ROUTES_LOOP;
108
- }
109
- } else {
110
- const index2 = path.indexOf("/", pos);
111
- if (index2 === -1) {
112
- continue ROUTES_LOOP;
113
- }
114
- pos = index2;
115
- }
116
- }
117
- handlers.push([handler, emptyParams]);
118
- } else if (hasLabel && !hasStar) {
119
- const params = /* @__PURE__ */ Object.create(null);
120
- const parts = routePath.match(splitPathRe);
121
- const lastIndex = parts.length - 1;
122
- for (let j = 0, pos = 0, len2 = parts.length; j < len2; j++) {
123
- if (pos === -1 || pos >= path.length) {
124
- continue ROUTES_LOOP;
125
- }
126
- const part = parts[j];
127
- if (part.charCodeAt(1) === 58) {
128
- let name = part.slice(2);
129
- let value2;
130
- if (name.charCodeAt(name.length - 1) === 125) {
131
- const openBracePos = name.indexOf("{");
132
- const pattern = name.slice(openBracePos + 1, -1);
133
- const restPath = path.slice(pos + 1);
134
- const match = new RegExp(pattern, "d").exec(restPath);
135
- if (!match || match.indices[0][0] !== 0 || match.indices[0][1] === 0) {
136
- continue ROUTES_LOOP;
137
- }
138
- name = name.slice(0, openBracePos);
139
- value2 = restPath.slice(...match.indices[0]);
140
- pos += match.indices[0][1] + 1;
141
- } else {
142
- let endValuePos = path.indexOf("/", pos + 1);
143
- if (endValuePos === -1) {
144
- if (pos + 1 === path.length) {
145
- continue ROUTES_LOOP;
146
- }
147
- endValuePos = path.length;
148
- }
149
- value2 = path.slice(pos + 1, endValuePos);
150
- pos = endValuePos;
151
- }
152
- params[name] ||= value2;
153
- } else {
154
- const index = path.indexOf(part, pos);
155
- if (index !== pos) {
156
- continue ROUTES_LOOP;
157
- }
158
- pos += part.length;
159
- }
160
- if (j === lastIndex) {
161
- if (pos !== path.length && !(pos === path.length - 1 && path.charCodeAt(pos) === 47)) {
162
- continue ROUTES_LOOP;
163
- }
164
- }
165
- }
166
- handlers.push([handler, params]);
167
- } else if (hasLabel && hasStar) {
168
- throw new UnsupportedPathError();
169
- }
170
- }
171
- return [handlers];
172
14
  }
173
- };
174
-
175
- // ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/reg-exp-router/node.js
176
- var LABEL_REG_EXP_STR = "[^/]+";
177
- var ONLY_WILDCARD_REG_EXP_STR = ".*";
178
- var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
179
- var PATH_ERROR = Symbol();
180
- var regExpMetaChars = new Set(".\\+*[^]$()");
181
- function compareKey(a, b) {
182
- if (a.length === 1) {
183
- return b.length === 1 ? a < b ? -1 : 1 : -1;
184
- }
185
- if (b.length === 1) {
186
- return 1;
187
- }
188
- if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
189
- return 1;
190
- } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
191
- return -1;
192
- }
193
- if (a === LABEL_REG_EXP_STR) {
194
- return 1;
195
- } else if (b === LABEL_REG_EXP_STR) {
196
- return -1;
197
- }
198
- return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
199
- }
200
- var Node = class {
201
- index;
202
- varIndex;
203
- children = /* @__PURE__ */ Object.create(null);
204
- insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
205
- if (tokens.length === 0) {
206
- if (this.index !== void 0) {
207
- throw PATH_ERROR;
208
- }
209
- if (pathErrorCheckOnly) {
210
- return;
211
- }
212
- this.index = index;
213
- return;
214
- }
215
- const [token, ...restTokens] = tokens;
216
- const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
217
- let node;
218
- if (pattern) {
219
- const name = pattern[1];
220
- let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
221
- if (name && pattern[2]) {
222
- regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
223
- if (/\((?!\?:)/.test(regexpStr)) {
224
- throw PATH_ERROR;
225
- }
226
- }
227
- node = this.children[regexpStr];
228
- if (!node) {
229
- if (Object.keys(this.children).some(
230
- (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
231
- )) {
232
- throw PATH_ERROR;
233
- }
234
- if (pathErrorCheckOnly) {
235
- return;
236
- }
237
- node = this.children[regexpStr] = new Node();
238
- if (name !== "") {
239
- node.varIndex = context.varIndex++;
240
- }
241
- }
242
- if (!pathErrorCheckOnly && name !== "") {
243
- paramMap.push([name, node.varIndex]);
244
- }
245
- } else {
246
- node = this.children[token];
247
- if (!node) {
248
- if (Object.keys(this.children).some(
249
- (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
250
- )) {
251
- throw PATH_ERROR;
252
- }
253
- if (pathErrorCheckOnly) {
254
- return;
255
- }
256
- node = this.children[token] = new Node();
257
- }
15
+ const error = new ORPCError({ code: "NOT_FOUND", message: "Not found" });
16
+ return new Response(JSON.stringify(error.toJSON()), {
17
+ status: error.status,
18
+ headers: {
19
+ "Content-Type": "application/json"
258
20
  }
259
- node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
260
- }
261
- buildRegExpStr() {
262
- const childKeys = Object.keys(this.children).sort(compareKey);
263
- const strList = childKeys.map((k) => {
264
- const c = this.children[k];
265
- return (typeof c.varIndex === "number" ? `(${k})@${c.varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
266
- });
267
- if (typeof this.index === "number") {
268
- strList.unshift(`#${this.index}`);
269
- }
270
- if (strList.length === 0) {
271
- return "";
272
- }
273
- if (strList.length === 1) {
274
- return strList[0];
275
- }
276
- return "(?:" + strList.join("|") + ")";
277
- }
278
- };
279
-
280
- // ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/reg-exp-router/trie.js
281
- var Trie = class {
282
- context = { varIndex: 0 };
283
- root = new Node();
284
- insert(path, index, pathErrorCheckOnly) {
285
- const paramAssoc = [];
286
- const groups = [];
287
- for (let i = 0; ; ) {
288
- let replaced = false;
289
- path = path.replace(/\{[^}]+\}/g, (m) => {
290
- const mark = `@\\${i}`;
291
- groups[i] = [mark, m];
292
- i++;
293
- replaced = true;
294
- return mark;
295
- });
296
- if (!replaced) {
297
- break;
298
- }
299
- }
300
- const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
301
- for (let i = groups.length - 1; i >= 0; i--) {
302
- const [mark] = groups[i];
303
- for (let j = tokens.length - 1; j >= 0; j--) {
304
- if (tokens[j].indexOf(mark) !== -1) {
305
- tokens[j] = tokens[j].replace(mark, groups[i][1]);
306
- break;
307
- }
308
- }
309
- }
310
- this.root.insert(tokens, index, paramAssoc, this.context, pathErrorCheckOnly);
311
- return paramAssoc;
312
- }
313
- buildRegExp() {
314
- let regexp = this.root.buildRegExpStr();
315
- if (regexp === "") {
316
- return [/^$/, [], []];
317
- }
318
- let captureIndex = 0;
319
- const indexReplacementMap = [];
320
- const paramReplacementMap = [];
321
- regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => {
322
- if (typeof handlerIndex !== "undefined") {
323
- indexReplacementMap[++captureIndex] = Number(handlerIndex);
324
- return "$()";
325
- }
326
- if (typeof paramIndex !== "undefined") {
327
- paramReplacementMap[Number(paramIndex)] = ++captureIndex;
328
- return "";
329
- }
330
- return "";
331
- });
332
- return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap];
333
- }
334
- };
335
-
336
- // ../../node_modules/.pnpm/hono@4.6.6/node_modules/hono/dist/router/reg-exp-router/router.js
337
- var emptyParam = [];
338
- var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
339
- var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
340
- function buildWildcardRegExp(path) {
341
- return wildcardRegExpCache[path] ??= new RegExp(
342
- path === "*" ? "" : `^${path.replace(
343
- /\/\*$|([.\\+*[^\]$()])/g,
344
- (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
345
- )}$`
346
- );
347
- }
348
- function clearWildcardRegExpCache() {
349
- wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
350
- }
351
- function buildMatcherFromPreprocessedRoutes(routes) {
352
- const trie = new Trie();
353
- const handlerData = [];
354
- if (routes.length === 0) {
355
- return nullMatcher;
356
- }
357
- const routesWithStaticPathFlag = routes.map(
358
- (route) => [!/\*|\/:/.test(route[0]), ...route]
359
- ).sort(
360
- ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
361
- );
362
- const staticMap = /* @__PURE__ */ Object.create(null);
363
- for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
364
- const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
365
- if (pathErrorCheckOnly) {
366
- staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
367
- } else {
368
- j++;
369
- }
370
- let paramAssoc;
371
- try {
372
- paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
373
- } catch (e) {
374
- throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
375
- }
376
- if (pathErrorCheckOnly) {
377
- continue;
378
- }
379
- handlerData[j] = handlers.map(([h, paramCount]) => {
380
- const paramIndexMap = /* @__PURE__ */ Object.create(null);
381
- paramCount -= 1;
382
- for (; paramCount >= 0; paramCount--) {
383
- const [key, value2] = paramAssoc[paramCount];
384
- paramIndexMap[key] = value2;
385
- }
386
- return [h, paramIndexMap];
387
- });
388
- }
389
- const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
390
- for (let i = 0, len = handlerData.length; i < len; i++) {
391
- for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
392
- const map = handlerData[i][j]?.[1];
393
- if (!map) {
394
- continue;
395
- }
396
- const keys = Object.keys(map);
397
- for (let k = 0, len3 = keys.length; k < len3; k++) {
398
- map[keys[k]] = paramReplacementMap[map[keys[k]]];
399
- }
400
- }
401
- }
402
- const handlerMap = [];
403
- for (const i in indexReplacementMap) {
404
- handlerMap[i] = handlerData[indexReplacementMap[i]];
405
- }
406
- return [regexp, handlerMap, staticMap];
407
- }
408
- function findMiddleware(middleware, path) {
409
- if (!middleware) {
410
- return void 0;
411
- }
412
- for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
413
- if (buildWildcardRegExp(k).test(path)) {
414
- return [...middleware[k]];
415
- }
416
- }
417
- return void 0;
21
+ });
418
22
  }
419
- var RegExpRouter = class {
420
- name = "RegExpRouter";
421
- middleware;
422
- routes;
423
- constructor() {
424
- this.middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
425
- this.routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
426
- }
427
- add(method, path, handler) {
428
- const { middleware, routes } = this;
429
- if (!middleware || !routes) {
430
- throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
431
- }
432
- if (!middleware[method]) {
433
- ;
434
- [middleware, routes].forEach((handlerMap) => {
435
- handlerMap[method] = /* @__PURE__ */ Object.create(null);
436
- Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
437
- handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
438
- });
439
- });
440
- }
441
- if (path === "/*") {
442
- path = "*";
443
- }
444
- const paramCount = (path.match(/\/:/g) || []).length;
445
- if (/\*$/.test(path)) {
446
- const re = buildWildcardRegExp(path);
447
- if (method === METHOD_NAME_ALL) {
448
- Object.keys(middleware).forEach((m) => {
449
- middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
450
- });
451
- } else {
452
- middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
453
- }
454
- Object.keys(middleware).forEach((m) => {
455
- if (method === METHOD_NAME_ALL || method === m) {
456
- Object.keys(middleware[m]).forEach((p) => {
457
- re.test(p) && middleware[m][p].push([handler, paramCount]);
458
- });
459
- }
460
- });
461
- Object.keys(routes).forEach((m) => {
462
- if (method === METHOD_NAME_ALL || method === m) {
463
- Object.keys(routes[m]).forEach(
464
- (p) => re.test(p) && routes[m][p].push([handler, paramCount])
465
- );
466
- }
467
- });
468
- return;
469
- }
470
- const paths = checkOptionalParameter(path) || [path];
471
- for (let i = 0, len = paths.length; i < len; i++) {
472
- const path2 = paths[i];
473
- Object.keys(routes).forEach((m) => {
474
- if (method === METHOD_NAME_ALL || method === m) {
475
- routes[m][path2] ||= [
476
- ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
477
- ];
478
- routes[m][path2].push([handler, paramCount - len + i + 1]);
479
- }
480
- });
481
- }
482
- }
483
- match(method, path) {
484
- clearWildcardRegExpCache();
485
- const matchers = this.buildAllMatchers();
486
- this.match = (method2, path2) => {
487
- const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
488
- const staticMatch = matcher[2][path2];
489
- if (staticMatch) {
490
- return staticMatch;
491
- }
492
- const match = path2.match(matcher[0]);
493
- if (!match) {
494
- return [[], emptyParam];
495
- }
496
- const index = match.indexOf("", 1);
497
- return [matcher[1][index], match];
498
- };
499
- return this.match(method, path);
500
- }
501
- buildAllMatchers() {
502
- const matchers = /* @__PURE__ */ Object.create(null);
503
- [...Object.keys(this.routes), ...Object.keys(this.middleware)].forEach((method) => {
504
- matchers[method] ||= this.buildMatcher(method);
505
- });
506
- this.middleware = this.routes = void 0;
507
- return matchers;
508
- }
509
- buildMatcher(method) {
510
- const routes = [];
511
- let hasOwnRoute = method === METHOD_NAME_ALL;
512
- [this.middleware, this.routes].forEach((r) => {
513
- const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
514
- if (ownRoute.length !== 0) {
515
- hasOwnRoute ||= true;
516
- routes.push(...ownRoute);
517
- } else if (method !== METHOD_NAME_ALL) {
518
- routes.push(
519
- ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])
520
- );
521
- }
522
- });
523
- if (!hasOwnRoute) {
524
- return null;
525
- } else {
526
- return buildMatcherFromPreprocessedRoutes(routes);
527
- }
528
- }
529
- };
530
23
 
531
- // src/adapters/fetch.ts
532
- function createFetchHandler(options) {
533
- const routing = options.serverless ? new LinearRouter() : new RegExpRouter();
534
- const addRouteRecursively = (router, basePath) => {
535
- for (const key in router) {
536
- const currentPath = [...basePath, key];
537
- const item = router[key];
538
- if (isProcedure(item)) {
539
- if (item.zz$p.contract.zz$cp.path) {
540
- const method = item.zz$p.contract.zz$cp.method ?? "POST";
541
- const path = openAPIPathToRouterPath(item.zz$p.contract.zz$cp.path);
542
- routing.add(method, path, [currentPath, item]);
543
- }
544
- } else {
545
- addRouteRecursively(item, currentPath);
546
- }
547
- }
548
- };
549
- addRouteRecursively(options.router, []);
550
- return async (requestOptions) => {
551
- const isORPCTransformer = requestOptions.request.headers.get(ORPC_HEADER) === ORPC_HEADER_VALUE;
552
- const accept = requestOptions.request.headers.get("Accept") || void 0;
553
- const serializer = isORPCTransformer ? new ORPCSerializer() : new OpenAPISerializer({ accept });
554
- const context = await value(requestOptions.context);
24
+ // src/fetch/handler.ts
25
+ import { ORPC_HEADER, ORPC_HEADER_VALUE } from "@orpc/contract";
26
+ import { trim, value } from "@orpc/shared";
27
+ import { ORPCError as ORPCError2 } from "@orpc/shared/error";
28
+ import { ORPCDeserializer, ORPCSerializer } from "@orpc/transformer";
29
+ var serializer = new ORPCSerializer();
30
+ var deserializer = new ORPCDeserializer();
31
+ function createORPCHandler() {
32
+ return async (options) => {
33
+ if (options.request.headers.get(ORPC_HEADER) !== ORPC_HEADER_VALUE) {
34
+ return void 0;
35
+ }
36
+ const context = await value(options.context);
555
37
  const handler = async () => {
556
- const url = new URL(requestOptions.request.url);
557
- const pathname = `/${trim(url.pathname.replace(requestOptions.prefix ?? "", ""), "/")}`;
558
- let path;
559
- let procedure;
560
- let params;
561
- if (isORPCTransformer) {
562
- path = trim(pathname, "/").split("/").map(decodeURIComponent);
563
- const val = get(options.router, path);
564
- if (isProcedure(val)) {
565
- procedure = val;
566
- }
567
- } else {
568
- const customMethod = requestOptions.request.method === "POST" ? url.searchParams.get("method")?.toUpperCase() : void 0;
569
- const method = customMethod || requestOptions.request.method;
570
- const [matches, params_] = routing.match(method, pathname);
571
- const [match] = matches.sort((a, b) => {
572
- return Object.keys(a[1]).length - Object.keys(b[1]).length;
573
- });
574
- if (match) {
575
- path = match[0][0];
576
- procedure = match[0][1];
577
- if (params_) {
578
- params = mapValues(
579
- match[1],
580
- (v) => params_[v]
581
- );
582
- } else {
583
- params = match[1];
584
- }
585
- }
586
- if (!path || !procedure) {
587
- path = trim(pathname, "/").split("/").map(decodeURIComponent);
588
- const val = get(options.router, path);
589
- if (isProcedure(val)) {
590
- procedure = val;
591
- }
592
- }
593
- }
594
- if (!path || !procedure) {
595
- throw new ORPCError({ code: "NOT_FOUND", message: "Not found" });
38
+ const url = new URL(options.request.url);
39
+ const pathname = `/${trim(url.pathname.replace(options.prefix ?? "", ""), "/")}`;
40
+ const match = resolveORPCRouter(options.router, pathname);
41
+ if (!match) {
42
+ throw new ORPCError2({ code: "NOT_FOUND", message: "Not found" });
596
43
  }
597
- const deserializer = isORPCTransformer ? new ORPCDeserializer() : new OpenAPIDeserializer({
598
- schema: procedure.zz$p.contract.zz$cp.InputSchema
599
- });
600
- const input_ = await (async () => {
601
- try {
602
- return await deserializer.deserialize(requestOptions.request);
603
- } catch (e) {
604
- throw new ORPCError({
605
- code: "BAD_REQUEST",
606
- message: "Cannot parse request. Please check the request body and Content-Type header.",
607
- cause: e
608
- });
609
- }
610
- })();
611
- const input = (() => {
612
- if (!params || Object.keys(params).length === 0) {
613
- return input_;
614
- }
615
- const coercedParams = procedure.zz$p.contract.zz$cp.InputSchema ? zodCoerce(
616
- procedure.zz$p.contract.zz$cp.InputSchema,
617
- { ...params },
618
- {
619
- bracketNotation: true
620
- }
621
- ) : params;
622
- if (!isPlainObject(input_)) {
623
- return coercedParams;
624
- }
625
- return {
626
- ...coercedParams,
627
- ...input_
628
- };
629
- })();
44
+ const input = await deserializeRequest(options.request);
630
45
  const caller = createProcedureCaller({
631
46
  context,
632
- procedure,
633
- path
47
+ procedure: match.procedure,
48
+ path: match.path
634
49
  });
635
50
  const output = await caller(input);
636
51
  const { body, headers } = serializer.serialize(output);
@@ -640,41 +55,51 @@ function createFetchHandler(options) {
640
55
  });
641
56
  };
642
57
  try {
643
- return await options.hooks?.(context, {
644
- next: handler,
645
- response: (response) => response
646
- }) ?? await handler();
58
+ return await options.hooks?.(
59
+ context,
60
+ { next: handler, response: (response) => response }
61
+ ) ?? await handler();
647
62
  } catch (e) {
648
- const error = toORPCError(e);
649
- try {
650
- const { body, headers } = serializer.serialize(error.toJSON());
651
- return new Response(body, {
652
- status: error.status,
653
- headers
654
- });
655
- } catch (e2) {
656
- const error2 = toORPCError(e2);
657
- const { body, headers } = new OpenAPISerializer().serialize(
658
- error2.toJSON()
659
- );
660
- return new Response(body, {
661
- status: error2.status,
662
- headers
663
- });
664
- }
63
+ const error = e instanceof ORPCError2 ? e : new ORPCError2({
64
+ code: "INTERNAL_SERVER_ERROR",
65
+ message: "Internal server error",
66
+ cause: e
67
+ });
68
+ const { body, headers } = serializer.serialize(error.toJSON());
69
+ return new Response(body, {
70
+ status: error.status,
71
+ headers
72
+ });
665
73
  }
666
74
  };
667
75
  }
668
- function openAPIPathToRouterPath(path) {
669
- return standardizeHTTPPath(path).replace(/\{([^}]+)\}/g, ":$1");
76
+ function resolveORPCRouter(router, pathname) {
77
+ const path = trim(pathname, "/").split("/").map(decodeURIComponent);
78
+ let current = router;
79
+ for (const segment of path) {
80
+ if ((typeof current !== "object" || current === null) && typeof current !== "function") {
81
+ current = void 0;
82
+ break;
83
+ }
84
+ current = current[segment];
85
+ }
86
+ return isProcedure(current) ? {
87
+ procedure: current,
88
+ path
89
+ } : void 0;
670
90
  }
671
- function toORPCError(e) {
672
- return e instanceof ORPCError ? e : new ORPCError({
673
- code: "INTERNAL_SERVER_ERROR",
674
- message: "Internal server error",
675
- cause: e
676
- });
91
+ async function deserializeRequest(request) {
92
+ try {
93
+ return await deserializer.deserialize(request);
94
+ } catch (e) {
95
+ throw new ORPCError2({
96
+ code: "BAD_REQUEST",
97
+ message: "Cannot parse request. Please check the request body and Content-Type header.",
98
+ cause: e
99
+ });
100
+ }
677
101
  }
678
102
  export {
679
- createFetchHandler
103
+ createORPCHandler,
104
+ handleFetchRequest
680
105
  };
@@ -0,0 +1,6 @@
1
+ import type { Router } from '../router';
2
+ import type { FetchHandler, FetchHandlerOptions } from './types';
3
+ export type HandleFetchRequestOptions<TRouter extends Router<any>> = FetchHandlerOptions<TRouter> & {
4
+ handlers: readonly [FetchHandler, ...FetchHandler[]];
5
+ };
6
+ export declare function handleFetchRequest<TRouter extends Router<any>>(options: HandleFetchRequestOptions<TRouter>): Promise<Response>;
@@ -0,0 +1,2 @@
1
+ import type { FetchHandler } from './types';
2
+ export declare function createORPCHandler(): FetchHandler;
@@ -0,0 +1,3 @@
1
+ export * from './handle';
2
+ export * from './handler';
3
+ export * from './types';
@@ -4,21 +4,12 @@ export interface FetchHandlerHooks {
4
4
  next: () => Promise<Response>;
5
5
  response: (response: Response) => Response;
6
6
  }
7
- export interface CreateFetchHandlerOptions<TRouter extends Router<any>> {
8
- router: TRouter;
9
- /**
10
- * Hooks for executing logics on lifecycle events.
11
- */
12
- hooks?: (context: TRouter extends Router<infer UContext> ? UContext : never, hooks: FetchHandlerHooks) => Promisable<Response>;
7
+ export type FetchHandlerOptions<TRouter extends Router<any>> = {
13
8
  /**
14
- * It will help improve the cold start time. But it will increase the performance.
9
+ * The `router` used for handling the request and routing,
15
10
  *
16
- * @default false
17
11
  */
18
- serverless?: boolean;
19
- }
20
- export declare function createFetchHandler<TRouter extends Router<any>>(options: CreateFetchHandlerOptions<TRouter>): FetchHandler<TRouter>;
21
- export type FetchHandlerOptions<TRouter extends Router<any>> = {
12
+ router: TRouter;
22
13
  /**
23
14
  * The request need to be handled.
24
15
  */
@@ -30,12 +21,14 @@ export type FetchHandlerOptions<TRouter extends Router<any>> = {
30
21
  * @example /api
31
22
  */
32
23
  prefix?: string;
24
+ /**
25
+ * Hooks for executing logics on lifecycle events.
26
+ */
27
+ hooks?: (context: TRouter extends Router<infer UContext> ? UContext : never, hooks: FetchHandlerHooks) => Promisable<Response>;
33
28
  } & PartialOnUndefinedDeep<{
34
29
  /**
35
30
  * The context used to handle the request.
36
31
  */
37
32
  context: Value<TRouter extends Router<infer UContext> ? UContext : never>;
38
33
  }>;
39
- export interface FetchHandler<TRouter extends Router<any>> {
40
- (options: FetchHandlerOptions<TRouter>): Promise<Response>;
41
- }
34
+ export type FetchHandler = <TRouter extends Router<any>>(options: FetchHandlerOptions<TRouter>) => Promise<Response | undefined>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@orpc/server",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.12.0",
5
5
  "license": "MIT",
6
6
  "homepage": "https://orpc.unnoq.com",
7
7
  "repository": {
@@ -20,7 +20,7 @@
20
20
  "default": "./dist/index.js"
21
21
  },
22
22
  "./fetch": {
23
- "types": "./dist/src/adapters/fetch.d.ts",
23
+ "types": "./dist/src/fetch/index.d.ts",
24
24
  "import": "./dist/fetch.js",
25
25
  "default": "./dist/fetch.js"
26
26
  },
@@ -34,18 +34,18 @@
34
34
  ],
35
35
  "peerDependencies": {
36
36
  "zod": ">=3.23.0",
37
- "@orpc/zod": "0.11.0"
37
+ "@orpc/zod": "0.12.0"
38
38
  },
39
39
  "dependencies": {
40
- "@orpc/contract": "0.11.0",
41
- "@orpc/transformer": "0.11.0",
42
- "@orpc/shared": "0.11.0"
40
+ "@orpc/contract": "0.12.0",
41
+ "@orpc/transformer": "0.12.0",
42
+ "@orpc/shared": "0.12.0"
43
43
  },
44
44
  "devDependencies": {
45
- "hono": "^4.6.3"
45
+ "@orpc/openapi": "0.12.0"
46
46
  },
47
47
  "scripts": {
48
- "build": "tsup --clean --entry.index=src/index.ts --entry.fetch=src/adapters/fetch.ts --format=esm --onSuccess='tsc -b --noCheck'",
48
+ "build": "tsup --clean --entry.index=src/index.ts --entry.fetch=src/fetch/index.ts --format=esm --onSuccess='tsc -b --noCheck'",
49
49
  "build:watch": "pnpm run build --watch",
50
50
  "type:check": "tsc -b"
51
51
  }