@orpc/openapi 0.17.0 → 0.18.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/chunk-UPDKQRQG.js +665 -0
- package/dist/fetch.js +27 -693
- package/dist/index.js +39 -16
- package/dist/src/fetch/bracket-notation.d.ts +84 -0
- package/dist/src/fetch/index.d.ts +9 -3
- package/dist/src/fetch/input-builder-full.d.ts +11 -0
- package/dist/src/fetch/input-builder-simple.d.ts +6 -0
- package/dist/src/fetch/openapi-handler-server.d.ts +7 -0
- package/dist/src/fetch/openapi-handler-serverless.d.ts +7 -0
- package/dist/src/fetch/openapi-handler.d.ts +28 -0
- package/dist/src/fetch/openapi-payload-codec.d.ts +13 -0
- package/dist/src/fetch/openapi-procedure-matcher.d.ts +19 -0
- package/dist/src/fetch/schema-coercer.d.ts +10 -0
- package/dist/src/generator.d.ts +2 -0
- package/dist/src/utils.d.ts +2 -2
- package/package.json +9 -7
- package/dist/chunk-CMRY2Z4J.js +0 -54
- package/dist/src/fetch/base-handler.d.ts +0 -13
- package/dist/src/fetch/server-handler.d.ts +0 -3
- package/dist/src/fetch/serverless-handler.d.ts +0 -3
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
// src/fetch/bracket-notation.ts
|
|
2
|
+
import { isPlainObject } from "@orpc/shared";
|
|
3
|
+
function serialize(payload, parentKey = "") {
|
|
4
|
+
if (!Array.isArray(payload) && !isPlainObject(payload))
|
|
5
|
+
return [["", payload]];
|
|
6
|
+
const result = [];
|
|
7
|
+
function helper(value, path) {
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
value.forEach((item, index) => {
|
|
10
|
+
helper(item, [...path, String(index)]);
|
|
11
|
+
});
|
|
12
|
+
} else if (isPlainObject(value)) {
|
|
13
|
+
for (const [key, val] of Object.entries(value)) {
|
|
14
|
+
helper(val, [...path, key]);
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
result.push([stringifyPath(path), value]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
helper(payload, parentKey ? [parentKey] : []);
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
function deserialize(entities) {
|
|
24
|
+
if (entities.length === 0) {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
const isRootArray = entities.every(([path]) => path === "");
|
|
28
|
+
const result = isRootArray ? [] : {};
|
|
29
|
+
const arrayPushPaths = /* @__PURE__ */ new Set();
|
|
30
|
+
for (const [path, _] of entities) {
|
|
31
|
+
const segments = parsePath(path);
|
|
32
|
+
const base = segments.slice(0, -1).join(".");
|
|
33
|
+
const last = segments[segments.length - 1];
|
|
34
|
+
if (last === "") {
|
|
35
|
+
arrayPushPaths.add(base);
|
|
36
|
+
} else {
|
|
37
|
+
arrayPushPaths.delete(base);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function setValue(obj, segments, value, fullPath) {
|
|
41
|
+
const [first, ...rest_] = segments;
|
|
42
|
+
if (Array.isArray(obj) && first === "") {
|
|
43
|
+
;
|
|
44
|
+
obj.push(value);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const objAsRecord = obj;
|
|
48
|
+
if (rest_.length === 0) {
|
|
49
|
+
objAsRecord[first] = value;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const rest = rest_;
|
|
53
|
+
if (rest[0] === "") {
|
|
54
|
+
const pathToCheck = segments.slice(0, -1).join(".");
|
|
55
|
+
if (rest.length === 1 && arrayPushPaths.has(pathToCheck)) {
|
|
56
|
+
if (!(first in objAsRecord)) {
|
|
57
|
+
objAsRecord[first] = [];
|
|
58
|
+
}
|
|
59
|
+
if (Array.isArray(objAsRecord[first])) {
|
|
60
|
+
;
|
|
61
|
+
objAsRecord[first].push(value);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!(first in objAsRecord)) {
|
|
66
|
+
objAsRecord[first] = {};
|
|
67
|
+
}
|
|
68
|
+
const target = objAsRecord[first];
|
|
69
|
+
target[""] = value;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!(first in objAsRecord)) {
|
|
73
|
+
objAsRecord[first] = {};
|
|
74
|
+
}
|
|
75
|
+
setValue(
|
|
76
|
+
objAsRecord[first],
|
|
77
|
+
rest,
|
|
78
|
+
value,
|
|
79
|
+
fullPath
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
for (const [path, value] of entities) {
|
|
83
|
+
const segments = parsePath(path);
|
|
84
|
+
setValue(result, segments, value, path);
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
function escapeSegment(segment) {
|
|
89
|
+
return segment.replace(/[\\[\]]/g, (match) => {
|
|
90
|
+
switch (match) {
|
|
91
|
+
case "\\":
|
|
92
|
+
return "\\\\";
|
|
93
|
+
case "[":
|
|
94
|
+
return "\\[";
|
|
95
|
+
case "]":
|
|
96
|
+
return "\\]";
|
|
97
|
+
default:
|
|
98
|
+
return match;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function stringifyPath(path) {
|
|
103
|
+
const [first, ...rest] = path;
|
|
104
|
+
const firstSegment = escapeSegment(first);
|
|
105
|
+
const base = first === "" ? "" : firstSegment;
|
|
106
|
+
return rest.reduce(
|
|
107
|
+
(result, segment) => `${result}[${escapeSegment(segment)}]`,
|
|
108
|
+
base
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function parsePath(path) {
|
|
112
|
+
if (path === "")
|
|
113
|
+
return [""];
|
|
114
|
+
const result = [];
|
|
115
|
+
let currentSegment = "";
|
|
116
|
+
let inBracket = false;
|
|
117
|
+
let bracketContent = "";
|
|
118
|
+
let backslashCount = 0;
|
|
119
|
+
for (let i = 0; i < path.length; i++) {
|
|
120
|
+
const char = path[i];
|
|
121
|
+
if (char === "\\") {
|
|
122
|
+
backslashCount++;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (backslashCount > 0) {
|
|
126
|
+
const literalBackslashes = "\\".repeat(Math.floor(backslashCount / 2));
|
|
127
|
+
if (char === "[" || char === "]") {
|
|
128
|
+
if (backslashCount % 2 === 1) {
|
|
129
|
+
if (inBracket) {
|
|
130
|
+
bracketContent += literalBackslashes + char;
|
|
131
|
+
} else {
|
|
132
|
+
currentSegment += literalBackslashes + char;
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
if (inBracket) {
|
|
136
|
+
bracketContent += literalBackslashes;
|
|
137
|
+
} else {
|
|
138
|
+
currentSegment += literalBackslashes;
|
|
139
|
+
}
|
|
140
|
+
if (char === "[" && !inBracket) {
|
|
141
|
+
if (currentSegment !== "" || result.length === 0) {
|
|
142
|
+
result.push(currentSegment);
|
|
143
|
+
}
|
|
144
|
+
inBracket = true;
|
|
145
|
+
bracketContent = "";
|
|
146
|
+
currentSegment = "";
|
|
147
|
+
} else if (char === "]" && inBracket) {
|
|
148
|
+
result.push(bracketContent);
|
|
149
|
+
inBracket = false;
|
|
150
|
+
bracketContent = "";
|
|
151
|
+
} else {
|
|
152
|
+
if (inBracket) {
|
|
153
|
+
bracketContent += char;
|
|
154
|
+
} else {
|
|
155
|
+
currentSegment += char;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
const allBackslashes = "\\".repeat(backslashCount);
|
|
161
|
+
if (inBracket) {
|
|
162
|
+
bracketContent += allBackslashes + char;
|
|
163
|
+
} else {
|
|
164
|
+
currentSegment += allBackslashes + char;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
backslashCount = 0;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (char === "[" && !inBracket) {
|
|
171
|
+
if (currentSegment !== "" || result.length === 0) {
|
|
172
|
+
result.push(currentSegment);
|
|
173
|
+
}
|
|
174
|
+
inBracket = true;
|
|
175
|
+
bracketContent = "";
|
|
176
|
+
currentSegment = "";
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (char === "]" && inBracket) {
|
|
180
|
+
result.push(bracketContent);
|
|
181
|
+
inBracket = false;
|
|
182
|
+
bracketContent = "";
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (inBracket) {
|
|
186
|
+
bracketContent += char;
|
|
187
|
+
} else {
|
|
188
|
+
currentSegment += char;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (backslashCount > 0) {
|
|
192
|
+
const remainingBackslashes = "\\".repeat(backslashCount);
|
|
193
|
+
if (inBracket) {
|
|
194
|
+
bracketContent += remainingBackslashes;
|
|
195
|
+
} else {
|
|
196
|
+
currentSegment += remainingBackslashes;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (inBracket) {
|
|
200
|
+
if (currentSegment !== "" || result.length === 0) {
|
|
201
|
+
result.push(currentSegment);
|
|
202
|
+
}
|
|
203
|
+
result.push(`[${bracketContent}`);
|
|
204
|
+
} else if (currentSegment !== "" || result.length === 0) {
|
|
205
|
+
result.push(currentSegment);
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/fetch/input-builder-full.ts
|
|
211
|
+
var InputBuilderFull = class {
|
|
212
|
+
build(params, query, headers, body) {
|
|
213
|
+
return {
|
|
214
|
+
params,
|
|
215
|
+
query,
|
|
216
|
+
headers,
|
|
217
|
+
body
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/fetch/input-builder-simple.ts
|
|
223
|
+
import { isPlainObject as isPlainObject2 } from "@orpc/shared";
|
|
224
|
+
var InputBuilderSimple = class {
|
|
225
|
+
build(params, payload) {
|
|
226
|
+
if (Object.keys(params).length === 0) {
|
|
227
|
+
return payload;
|
|
228
|
+
}
|
|
229
|
+
if (!isPlainObject2(payload)) {
|
|
230
|
+
return params;
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
...params,
|
|
234
|
+
...payload
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/fetch/openapi-handler.ts
|
|
240
|
+
import { createProcedureClient, ORPCError as ORPCError2 } from "@orpc/server";
|
|
241
|
+
import { executeWithHooks, ORPC_HANDLER_HEADER, trim } from "@orpc/shared";
|
|
242
|
+
|
|
243
|
+
// src/fetch/openapi-payload-codec.ts
|
|
244
|
+
import { ORPCError } from "@orpc/server";
|
|
245
|
+
import { findDeepMatches, isPlainObject as isPlainObject3 } from "@orpc/shared";
|
|
246
|
+
import cd from "content-disposition";
|
|
247
|
+
import { safeParse } from "fast-content-type-parse";
|
|
248
|
+
import wcmatch from "wildcard-match";
|
|
249
|
+
var OpenAPIPayloadCodec = class {
|
|
250
|
+
encode(payload, accept) {
|
|
251
|
+
const typeMatchers = (accept?.split(",").map(safeParse) ?? [{ type: "*/*" }]).map(({ type }) => wcmatch(type));
|
|
252
|
+
if (payload instanceof Blob) {
|
|
253
|
+
const contentType = payload.type || "application/octet-stream";
|
|
254
|
+
if (typeMatchers.some((isMatch) => isMatch(contentType))) {
|
|
255
|
+
const headers = new Headers({
|
|
256
|
+
"Content-Type": contentType
|
|
257
|
+
});
|
|
258
|
+
if (payload instanceof File && payload.name) {
|
|
259
|
+
headers.append("Content-Disposition", cd(payload.name));
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
body: payload,
|
|
263
|
+
headers
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const handledPayload = this.serialize(payload);
|
|
268
|
+
const hasBlobs = findDeepMatches((v) => v instanceof Blob, handledPayload).values.length > 0;
|
|
269
|
+
const isExpectedMultipartFormData = typeMatchers.some(
|
|
270
|
+
(isMatch) => isMatch("multipart/form-data")
|
|
271
|
+
);
|
|
272
|
+
if (hasBlobs && isExpectedMultipartFormData) {
|
|
273
|
+
return this.encodeAsFormData(handledPayload);
|
|
274
|
+
}
|
|
275
|
+
if (typeMatchers.some((isMatch) => isMatch("application/json"))) {
|
|
276
|
+
return this.encodeAsJSON(handledPayload);
|
|
277
|
+
}
|
|
278
|
+
if (typeMatchers.some(
|
|
279
|
+
(isMatch) => isMatch("application/x-www-form-urlencoded")
|
|
280
|
+
)) {
|
|
281
|
+
return this.encodeAsURLSearchParams(handledPayload);
|
|
282
|
+
}
|
|
283
|
+
if (isExpectedMultipartFormData) {
|
|
284
|
+
return this.encodeAsFormData(handledPayload);
|
|
285
|
+
}
|
|
286
|
+
throw new ORPCError({
|
|
287
|
+
code: "NOT_ACCEPTABLE",
|
|
288
|
+
message: `Unsupported content-type: ${accept}`
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
encodeAsJSON(payload) {
|
|
292
|
+
if (payload === void 0) {
|
|
293
|
+
return {
|
|
294
|
+
body: void 0,
|
|
295
|
+
headers: new Headers({
|
|
296
|
+
"content-type": "application/json"
|
|
297
|
+
})
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
body: JSON.stringify(payload),
|
|
302
|
+
headers: new Headers({
|
|
303
|
+
"content-type": "application/json"
|
|
304
|
+
})
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
encodeAsFormData(payload) {
|
|
308
|
+
const form = new FormData();
|
|
309
|
+
for (const [path, value] of serialize(payload)) {
|
|
310
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
311
|
+
form.append(path, value.toString());
|
|
312
|
+
} else if (value === null) {
|
|
313
|
+
form.append(path, "null");
|
|
314
|
+
} else if (value instanceof Date) {
|
|
315
|
+
form.append(
|
|
316
|
+
path,
|
|
317
|
+
Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString()
|
|
318
|
+
);
|
|
319
|
+
} else if (value instanceof Blob) {
|
|
320
|
+
form.append(path, value);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
body: form
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
encodeAsURLSearchParams(payload) {
|
|
328
|
+
const params = new URLSearchParams();
|
|
329
|
+
for (const [path, value] of serialize(payload)) {
|
|
330
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
331
|
+
params.append(path, value.toString());
|
|
332
|
+
} else if (value === null) {
|
|
333
|
+
params.append(path, "null");
|
|
334
|
+
} else if (value instanceof Date) {
|
|
335
|
+
params.append(
|
|
336
|
+
path,
|
|
337
|
+
Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString()
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
body: params.toString(),
|
|
343
|
+
headers: new Headers({
|
|
344
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
345
|
+
})
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
serialize(payload) {
|
|
349
|
+
if (payload instanceof Set)
|
|
350
|
+
return this.serialize([...payload]);
|
|
351
|
+
if (payload instanceof Map)
|
|
352
|
+
return this.serialize([...payload.entries()]);
|
|
353
|
+
if (Array.isArray(payload)) {
|
|
354
|
+
return payload.map((v) => v === void 0 ? "undefined" : this.serialize(v));
|
|
355
|
+
}
|
|
356
|
+
if (Number.isNaN(payload))
|
|
357
|
+
return "NaN";
|
|
358
|
+
if (typeof payload === "bigint")
|
|
359
|
+
return payload.toString();
|
|
360
|
+
if (payload instanceof Date && Number.isNaN(payload.getTime())) {
|
|
361
|
+
return "Invalid Date";
|
|
362
|
+
}
|
|
363
|
+
if (payload instanceof RegExp)
|
|
364
|
+
return payload.toString();
|
|
365
|
+
if (payload instanceof URL)
|
|
366
|
+
return payload.toString();
|
|
367
|
+
if (!isPlainObject3(payload))
|
|
368
|
+
return payload;
|
|
369
|
+
return Object.keys(payload).reduce(
|
|
370
|
+
(carry, key) => {
|
|
371
|
+
const val = payload[key];
|
|
372
|
+
carry[key] = this.serialize(val);
|
|
373
|
+
return carry;
|
|
374
|
+
},
|
|
375
|
+
{}
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
async decode(re) {
|
|
379
|
+
if (re instanceof Headers || re instanceof URLSearchParams || re instanceof FormData) {
|
|
380
|
+
return deserialize([...re.entries()]);
|
|
381
|
+
}
|
|
382
|
+
const contentType = re.headers.get("content-type");
|
|
383
|
+
const contentDisposition = re.headers.get("content-disposition");
|
|
384
|
+
const fileName = contentDisposition ? cd.parse(contentDisposition).parameters.filename : void 0;
|
|
385
|
+
if (fileName) {
|
|
386
|
+
const blob2 = await re.blob();
|
|
387
|
+
const file = new File([blob2], fileName, {
|
|
388
|
+
type: blob2.type
|
|
389
|
+
});
|
|
390
|
+
return file;
|
|
391
|
+
}
|
|
392
|
+
if (!contentType || contentType.startsWith("application/json")) {
|
|
393
|
+
if (!re.body) {
|
|
394
|
+
return void 0;
|
|
395
|
+
}
|
|
396
|
+
return await re.json();
|
|
397
|
+
}
|
|
398
|
+
if (contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
399
|
+
const params = new URLSearchParams(await re.text());
|
|
400
|
+
return this.decode(params);
|
|
401
|
+
}
|
|
402
|
+
if (contentType.startsWith("text/")) {
|
|
403
|
+
const text = await re.text();
|
|
404
|
+
return text;
|
|
405
|
+
}
|
|
406
|
+
if (contentType.startsWith("multipart/form-data")) {
|
|
407
|
+
const form = await re.formData();
|
|
408
|
+
return this.decode(form);
|
|
409
|
+
}
|
|
410
|
+
const blob = await re.blob();
|
|
411
|
+
return new File([blob], "blob", {
|
|
412
|
+
type: blob.type
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// src/fetch/openapi-procedure-matcher.ts
|
|
418
|
+
import { getLazyRouterPrefix, getRouterChild, isProcedure as isProcedure2, unlazy } from "@orpc/server";
|
|
419
|
+
import { mapValues } from "@orpc/shared";
|
|
420
|
+
|
|
421
|
+
// src/utils.ts
|
|
422
|
+
import { isContractProcedure } from "@orpc/contract";
|
|
423
|
+
import { getRouterContract, isLazy, isProcedure } from "@orpc/server";
|
|
424
|
+
function forEachContractProcedure(options, callback, result = [], isCurrentRouterContract = false) {
|
|
425
|
+
const hiddenContract = getRouterContract(options.router);
|
|
426
|
+
if (!isCurrentRouterContract && hiddenContract) {
|
|
427
|
+
return forEachContractProcedure(
|
|
428
|
+
{
|
|
429
|
+
path: options.path,
|
|
430
|
+
router: hiddenContract
|
|
431
|
+
},
|
|
432
|
+
callback,
|
|
433
|
+
result,
|
|
434
|
+
true
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
if (isLazy(options.router)) {
|
|
438
|
+
result.push({
|
|
439
|
+
router: options.router,
|
|
440
|
+
path: options.path
|
|
441
|
+
});
|
|
442
|
+
} else if (isProcedure(options.router)) {
|
|
443
|
+
callback({
|
|
444
|
+
contract: options.router["~orpc"].contract,
|
|
445
|
+
path: options.path
|
|
446
|
+
});
|
|
447
|
+
} else if (isContractProcedure(options.router)) {
|
|
448
|
+
callback({
|
|
449
|
+
contract: options.router,
|
|
450
|
+
path: options.path
|
|
451
|
+
});
|
|
452
|
+
} else {
|
|
453
|
+
for (const key in options.router) {
|
|
454
|
+
forEachContractProcedure(
|
|
455
|
+
{
|
|
456
|
+
router: options.router[key],
|
|
457
|
+
path: [...options.path, key]
|
|
458
|
+
},
|
|
459
|
+
callback,
|
|
460
|
+
result
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
function standardizeHTTPPath(path) {
|
|
467
|
+
return `/${path.replace(/\/{2,}/g, "/").replace(/^\/|\/$/g, "")}`;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/fetch/openapi-procedure-matcher.ts
|
|
471
|
+
var OpenAPIProcedureMatcher = class {
|
|
472
|
+
constructor(hono, router) {
|
|
473
|
+
this.hono = hono;
|
|
474
|
+
this.router = router;
|
|
475
|
+
this.pendingRouters = [{ path: [], router }];
|
|
476
|
+
}
|
|
477
|
+
pendingRouters;
|
|
478
|
+
async match(method, pathname) {
|
|
479
|
+
await this.handlePendingRouters(pathname);
|
|
480
|
+
const [matches, paramStash] = this.hono.match(method, pathname);
|
|
481
|
+
const [match] = matches.sort((a, b) => {
|
|
482
|
+
const slashCountA = a[0][0].split("/").length;
|
|
483
|
+
const slashCountB = b[0][0].split("/").length;
|
|
484
|
+
if (slashCountA !== slashCountB) {
|
|
485
|
+
return slashCountB - slashCountA;
|
|
486
|
+
}
|
|
487
|
+
const paramsCountA = Object.keys(a[1]).length;
|
|
488
|
+
const paramsCountB = Object.keys(b[1]).length;
|
|
489
|
+
return paramsCountA - paramsCountB;
|
|
490
|
+
});
|
|
491
|
+
if (!match) {
|
|
492
|
+
return void 0;
|
|
493
|
+
}
|
|
494
|
+
const path = match[0][1];
|
|
495
|
+
const params = paramStash ? mapValues(
|
|
496
|
+
match[1],
|
|
497
|
+
// if paramStash is defined, then match[1] is ParamIndexMap
|
|
498
|
+
(v) => paramStash[v]
|
|
499
|
+
) : match[1];
|
|
500
|
+
const { default: maybeProcedure } = await unlazy(getRouterChild(this.router, ...path));
|
|
501
|
+
if (!isProcedure2(maybeProcedure)) {
|
|
502
|
+
return void 0;
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
path,
|
|
506
|
+
procedure: maybeProcedure,
|
|
507
|
+
params: { ...params }
|
|
508
|
+
// normalize params from hono
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
add(path, router) {
|
|
512
|
+
const lazies = forEachContractProcedure({ path, router }, ({ path: path2, contract }) => {
|
|
513
|
+
const method = contract["~orpc"].route?.method ?? "POST";
|
|
514
|
+
const httpPath = contract["~orpc"].route?.path ? this.convertOpenAPIPathToRouterPath(contract["~orpc"].route?.path) : `/${path2.map(encodeURIComponent).join("/")}`;
|
|
515
|
+
this.hono.add(method, httpPath, [httpPath, path2]);
|
|
516
|
+
});
|
|
517
|
+
this.pendingRouters.push(...lazies);
|
|
518
|
+
}
|
|
519
|
+
async handlePendingRouters(pathname) {
|
|
520
|
+
const newPendingLazyRouters = [];
|
|
521
|
+
for (const item of this.pendingRouters) {
|
|
522
|
+
const lazyPrefix = getLazyRouterPrefix(item.router);
|
|
523
|
+
if (lazyPrefix && !pathname.startsWith(lazyPrefix) && !pathname.startsWith(`/${item.path.map(encodeURIComponent).join("/")}`)) {
|
|
524
|
+
newPendingLazyRouters.push(item);
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const { default: router } = await unlazy(item.router);
|
|
528
|
+
this.add(item.path, router);
|
|
529
|
+
}
|
|
530
|
+
this.pendingRouters = newPendingLazyRouters;
|
|
531
|
+
}
|
|
532
|
+
convertOpenAPIPathToRouterPath(path) {
|
|
533
|
+
return standardizeHTTPPath(path).replace(/\{([^}]+)\}/g, ":$1");
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// src/fetch/schema-coercer.ts
|
|
538
|
+
var CompositeSchemaCoercer = class {
|
|
539
|
+
constructor(coercers) {
|
|
540
|
+
this.coercers = coercers;
|
|
541
|
+
}
|
|
542
|
+
coerce(schema, value) {
|
|
543
|
+
let current = value;
|
|
544
|
+
for (const coercer of this.coercers) {
|
|
545
|
+
current = coercer.coerce(schema, current);
|
|
546
|
+
}
|
|
547
|
+
return current;
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// src/fetch/openapi-handler.ts
|
|
552
|
+
var OpenAPIHandler = class {
|
|
553
|
+
constructor(hono, router, options) {
|
|
554
|
+
this.options = options;
|
|
555
|
+
this.procedureMatcher = options?.procedureMatcher ?? new OpenAPIProcedureMatcher(hono, router);
|
|
556
|
+
this.payloadCodec = options?.payloadCodec ?? new OpenAPIPayloadCodec();
|
|
557
|
+
this.inputBuilderSimple = options?.inputBuilderSimple ?? new InputBuilderSimple();
|
|
558
|
+
this.inputBuilderFull = options?.inputBuilderFull ?? new InputBuilderFull();
|
|
559
|
+
this.compositeSchemaCoercer = new CompositeSchemaCoercer(options?.schemaCoercers ?? []);
|
|
560
|
+
}
|
|
561
|
+
procedureMatcher;
|
|
562
|
+
payloadCodec;
|
|
563
|
+
inputBuilderSimple;
|
|
564
|
+
inputBuilderFull;
|
|
565
|
+
compositeSchemaCoercer;
|
|
566
|
+
condition(request) {
|
|
567
|
+
return request.headers.get(ORPC_HANDLER_HEADER) === null;
|
|
568
|
+
}
|
|
569
|
+
async fetch(request, ...[options]) {
|
|
570
|
+
const context = options?.context;
|
|
571
|
+
const headers = request.headers;
|
|
572
|
+
const accept = headers.get("Accept") || void 0;
|
|
573
|
+
const execute = async () => {
|
|
574
|
+
const url = new URL(request.url);
|
|
575
|
+
const pathname = `/${trim(url.pathname.replace(options?.prefix ?? "", ""), "/")}`;
|
|
576
|
+
const query = url.searchParams;
|
|
577
|
+
const customMethod = request.method === "POST" ? query.get("method")?.toUpperCase() : void 0;
|
|
578
|
+
const method = customMethod || request.method;
|
|
579
|
+
const match = await this.procedureMatcher.match(method, pathname);
|
|
580
|
+
if (!match) {
|
|
581
|
+
throw new ORPCError2({ code: "NOT_FOUND", message: "Not found" });
|
|
582
|
+
}
|
|
583
|
+
const decodedPayload = request.method === "GET" ? await this.payloadCodec.decode(query) : await this.payloadCodec.decode(request);
|
|
584
|
+
const input = this.inputBuilderSimple.build(match.params, decodedPayload);
|
|
585
|
+
const coercedInput = this.compositeSchemaCoercer.coerce(match.procedure["~orpc"].contract["~orpc"].InputSchema, input);
|
|
586
|
+
const client = createProcedureClient({
|
|
587
|
+
context,
|
|
588
|
+
procedure: match.procedure,
|
|
589
|
+
path: match.path
|
|
590
|
+
});
|
|
591
|
+
const output = await client(coercedInput, { signal: options?.signal });
|
|
592
|
+
const { body, headers: headers2 } = this.payloadCodec.encode(output);
|
|
593
|
+
return new Response(body, { headers: headers2 });
|
|
594
|
+
};
|
|
595
|
+
try {
|
|
596
|
+
return await executeWithHooks({
|
|
597
|
+
context,
|
|
598
|
+
execute,
|
|
599
|
+
input: request,
|
|
600
|
+
hooks: this.options,
|
|
601
|
+
meta: {
|
|
602
|
+
signal: options?.signal
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
} catch (e) {
|
|
606
|
+
const error = this.convertToORPCError(e);
|
|
607
|
+
try {
|
|
608
|
+
const { body, headers: headers2 } = this.payloadCodec.encode(error.toJSON(), accept);
|
|
609
|
+
return new Response(body, {
|
|
610
|
+
status: error.status,
|
|
611
|
+
headers: headers2
|
|
612
|
+
});
|
|
613
|
+
} catch (e2) {
|
|
614
|
+
const error2 = this.convertToORPCError(e2);
|
|
615
|
+
const { body, headers: headers2 } = this.payloadCodec.encode(error2.toJSON());
|
|
616
|
+
return new Response(body, {
|
|
617
|
+
status: error2.status,
|
|
618
|
+
headers: headers2
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
convertToORPCError(e) {
|
|
624
|
+
return e instanceof ORPCError2 ? e : new ORPCError2({
|
|
625
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
626
|
+
message: "Internal server error",
|
|
627
|
+
cause: e
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
// src/fetch/openapi-handler-server.ts
|
|
633
|
+
import { TrieRouter } from "hono/router/trie-router";
|
|
634
|
+
var OpenAPIServerHandler = class extends OpenAPIHandler {
|
|
635
|
+
constructor(router, options) {
|
|
636
|
+
super(new TrieRouter(), router, options);
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
// src/fetch/openapi-handler-serverless.ts
|
|
641
|
+
import { LinearRouter } from "hono/router/linear-router";
|
|
642
|
+
var OpenAPIServerlessHandler = class extends OpenAPIHandler {
|
|
643
|
+
constructor(router, options) {
|
|
644
|
+
super(new LinearRouter(), router, options);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
export {
|
|
649
|
+
serialize,
|
|
650
|
+
deserialize,
|
|
651
|
+
escapeSegment,
|
|
652
|
+
stringifyPath,
|
|
653
|
+
parsePath,
|
|
654
|
+
InputBuilderFull,
|
|
655
|
+
InputBuilderSimple,
|
|
656
|
+
OpenAPIPayloadCodec,
|
|
657
|
+
forEachContractProcedure,
|
|
658
|
+
standardizeHTTPPath,
|
|
659
|
+
OpenAPIProcedureMatcher,
|
|
660
|
+
CompositeSchemaCoercer,
|
|
661
|
+
OpenAPIHandler,
|
|
662
|
+
OpenAPIServerHandler,
|
|
663
|
+
OpenAPIServerlessHandler
|
|
664
|
+
};
|
|
665
|
+
//# sourceMappingURL=chunk-UPDKQRQG.js.map
|