@orpc/server 0.11.0 → 0.12.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/dist/fetch.js
CHANGED
@@ -3,634 +3,49 @@ import {
|
|
3
3
|
isProcedure
|
4
4
|
} from "./chunk-CVLK2PBB.js";
|
5
5
|
|
6
|
-
// src/
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
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/
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
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(
|
557
|
-
const pathname = `/${trim(url.pathname.replace(
|
558
|
-
|
559
|
-
|
560
|
-
|
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
|
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?.(
|
644
|
-
|
645
|
-
response: (response) => response
|
646
|
-
|
58
|
+
return await options.hooks?.(
|
59
|
+
context,
|
60
|
+
{ next: handler, response: (response) => response }
|
61
|
+
) ?? await handler();
|
647
62
|
} catch (e) {
|
648
|
-
const error =
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
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
|
669
|
-
|
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
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
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
|
-
|
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>;
|
@@ -4,21 +4,12 @@ export interface FetchHandlerHooks {
|
|
4
4
|
next: () => Promise<Response>;
|
5
5
|
response: (response: Response) => Response;
|
6
6
|
}
|
7
|
-
export
|
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
|
-
*
|
9
|
+
* The `router` used for handling the request and routing,
|
15
10
|
*
|
16
|
-
* @default false
|
17
11
|
*/
|
18
|
-
|
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
|
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.
|
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/
|
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.
|
37
|
+
"@orpc/zod": "0.12.0"
|
38
38
|
},
|
39
39
|
"dependencies": {
|
40
|
-
"@orpc/contract": "0.
|
41
|
-
"@orpc/transformer": "0.
|
42
|
-
"@orpc/shared": "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
|
-
"
|
45
|
+
"@orpc/openapi": "0.12.0"
|
46
46
|
},
|
47
47
|
"scripts": {
|
48
|
-
"build": "tsup --clean --entry.index=src/index.ts --entry.fetch=src/
|
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
|
}
|