@orpc/server 0.0.0-next.ef3ba82 → 0.0.0-next.fd1db03

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/dist/chunk-6A7XHEBH.js +189 -0
  2. package/dist/fetch.js +292 -73
  3. package/dist/index.js +405 -280
  4. package/dist/src/builder.d.ts +28 -41
  5. package/dist/src/fetch/composite-handler.d.ts +8 -0
  6. package/dist/src/fetch/index.d.ts +6 -2
  7. package/dist/src/fetch/orpc-handler.d.ts +20 -0
  8. package/dist/src/fetch/orpc-payload-codec.d.ts +16 -0
  9. package/dist/src/fetch/orpc-procedure-matcher.d.ts +12 -0
  10. package/dist/src/fetch/super-json.d.ts +12 -0
  11. package/dist/src/fetch/types.d.ts +15 -33
  12. package/dist/src/hidden.d.ts +6 -0
  13. package/dist/src/implementer-chainable.d.ts +10 -0
  14. package/dist/src/index.d.ts +12 -3
  15. package/dist/src/lazy-decorated.d.ts +10 -0
  16. package/dist/src/lazy-utils.d.ts +4 -0
  17. package/dist/src/lazy.d.ts +18 -0
  18. package/dist/src/middleware-decorated.d.ts +8 -0
  19. package/dist/src/middleware.d.ts +4 -6
  20. package/dist/src/procedure-builder.d.ts +16 -24
  21. package/dist/src/procedure-client.d.ts +34 -0
  22. package/dist/src/procedure-decorated.d.ts +14 -0
  23. package/dist/src/procedure-implementer.d.ts +14 -13
  24. package/dist/src/procedure.d.ts +19 -24
  25. package/dist/src/router-builder.d.ts +25 -17
  26. package/dist/src/router-client.d.ts +25 -0
  27. package/dist/src/router-implementer.d.ts +19 -17
  28. package/dist/src/router.d.ts +12 -15
  29. package/dist/src/types.d.ts +9 -4
  30. package/dist/src/utils.d.ts +1 -0
  31. package/package.json +7 -11
  32. package/dist/chunk-CVLK2PBB.js +0 -189
  33. package/dist/src/fetch/handle.d.ts +0 -6
  34. package/dist/src/fetch/handler.d.ts +0 -2
  35. package/dist/src/procedure-caller.d.ts +0 -18
  36. package/dist/src/router-caller.d.ts +0 -21
@@ -0,0 +1,189 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/utils.ts
8
+ function mergeContext(a, b) {
9
+ if (!a)
10
+ return b;
11
+ if (!b)
12
+ return a;
13
+ return {
14
+ ...a,
15
+ ...b
16
+ };
17
+ }
18
+
19
+ // src/procedure.ts
20
+ import { isContractProcedure } from "@orpc/contract";
21
+ var Procedure = class {
22
+ "~type" = "Procedure";
23
+ "~orpc";
24
+ constructor(def) {
25
+ this["~orpc"] = def;
26
+ }
27
+ };
28
+ function isProcedure(item) {
29
+ if (item instanceof Procedure) {
30
+ return true;
31
+ }
32
+ return (typeof item === "object" || typeof item === "function") && item !== null && "~type" in item && item["~type"] === "Procedure" && "~orpc" in item && typeof item["~orpc"] === "object" && item["~orpc"] !== null && "contract" in item["~orpc"] && isContractProcedure(item["~orpc"].contract) && "handler" in item["~orpc"] && typeof item["~orpc"].handler === "function";
33
+ }
34
+
35
+ // src/lazy.ts
36
+ var LAZY_LOADER_SYMBOL = Symbol("ORPC_LAZY_LOADER");
37
+ function lazy(loader) {
38
+ return {
39
+ [LAZY_LOADER_SYMBOL]: loader
40
+ };
41
+ }
42
+ function isLazy(item) {
43
+ return (typeof item === "object" || typeof item === "function") && item !== null && LAZY_LOADER_SYMBOL in item && typeof item[LAZY_LOADER_SYMBOL] === "function";
44
+ }
45
+ function unlazy(lazied) {
46
+ return isLazy(lazied) ? lazied[LAZY_LOADER_SYMBOL]() : Promise.resolve({ default: lazied });
47
+ }
48
+ function flatLazy(lazied) {
49
+ const flattenLoader = async () => {
50
+ let current = await unlazy(lazied);
51
+ while (true) {
52
+ if (!isLazy(current.default)) {
53
+ break;
54
+ }
55
+ current = await unlazy(current.default);
56
+ }
57
+ return current;
58
+ };
59
+ return lazy(flattenLoader);
60
+ }
61
+
62
+ // src/procedure-client.ts
63
+ import { executeWithHooks, value } from "@orpc/shared";
64
+ import { ORPCError } from "@orpc/shared/error";
65
+ function createProcedureClient(options) {
66
+ return async (...[input, callerOptions]) => {
67
+ const path = options.path ?? [];
68
+ const { default: procedure } = await unlazy(options.procedure);
69
+ const context = await value(options.context);
70
+ const meta = {
71
+ path,
72
+ procedure,
73
+ signal: callerOptions?.signal
74
+ };
75
+ const executeWithValidation = async () => {
76
+ const validInput = await validateInput(procedure, input);
77
+ const output = await executeMiddlewareChain(
78
+ procedure,
79
+ validInput,
80
+ context,
81
+ meta
82
+ );
83
+ return validateOutput(procedure, output);
84
+ };
85
+ return executeWithHooks({
86
+ hooks: options,
87
+ input,
88
+ context,
89
+ meta,
90
+ execute: executeWithValidation
91
+ });
92
+ };
93
+ }
94
+ async function validateInput(procedure, input) {
95
+ const schema = procedure["~orpc"].contract["~orpc"].InputSchema;
96
+ if (!schema)
97
+ return input;
98
+ const result = await schema["~standard"].validate(input);
99
+ if (result.issues) {
100
+ throw new ORPCError({
101
+ message: "Input validation failed",
102
+ code: "BAD_REQUEST",
103
+ issues: result.issues
104
+ });
105
+ }
106
+ return result.value;
107
+ }
108
+ async function validateOutput(procedure, output) {
109
+ const schema = procedure["~orpc"].contract["~orpc"].OutputSchema;
110
+ if (!schema)
111
+ return output;
112
+ const result = await schema["~standard"].validate(output);
113
+ if (result.issues) {
114
+ throw new ORPCError({
115
+ message: "Output validation failed",
116
+ code: "INTERNAL_SERVER_ERROR",
117
+ issues: result.issues
118
+ });
119
+ }
120
+ return result.value;
121
+ }
122
+ async function executeMiddlewareChain(procedure, input, context, meta) {
123
+ const middlewares = procedure["~orpc"].middlewares ?? [];
124
+ let currentMidIndex = 0;
125
+ let currentContext = context;
126
+ const next = async (nextOptions) => {
127
+ const mid = middlewares[currentMidIndex];
128
+ currentMidIndex += 1;
129
+ currentContext = mergeContext(currentContext, nextOptions.context);
130
+ if (mid) {
131
+ return await mid(input, currentContext, {
132
+ ...meta,
133
+ next,
134
+ output: (output) => ({ output, context: void 0 })
135
+ });
136
+ }
137
+ const result = {
138
+ output: await procedure["~orpc"].handler(input, currentContext, meta),
139
+ context: currentContext
140
+ };
141
+ return result;
142
+ };
143
+ return (await next({})).output;
144
+ }
145
+
146
+ // src/router.ts
147
+ function getRouterChild(router, ...path) {
148
+ let current = router;
149
+ for (let i = 0; i < path.length; i++) {
150
+ const segment = path[i];
151
+ if (!current) {
152
+ return void 0;
153
+ }
154
+ if (isProcedure(current)) {
155
+ return void 0;
156
+ }
157
+ if (!isLazy(current)) {
158
+ current = current[segment];
159
+ continue;
160
+ }
161
+ const lazied = current;
162
+ const rest = path.slice(i);
163
+ const newLazy = lazy(async () => {
164
+ const unwrapped = await unlazy(lazied);
165
+ if (!unwrapped.default) {
166
+ return unwrapped;
167
+ }
168
+ const next = getRouterChild(unwrapped.default, ...rest);
169
+ return { default: next };
170
+ });
171
+ return flatLazy(newLazy);
172
+ }
173
+ return current;
174
+ }
175
+
176
+ export {
177
+ __export,
178
+ mergeContext,
179
+ Procedure,
180
+ isProcedure,
181
+ LAZY_LOADER_SYMBOL,
182
+ lazy,
183
+ isLazy,
184
+ unlazy,
185
+ flatLazy,
186
+ createProcedureClient,
187
+ getRouterChild
188
+ };
189
+ //# sourceMappingURL=chunk-6A7XHEBH.js.map
package/dist/fetch.js CHANGED
@@ -1,105 +1,324 @@
1
1
  import {
2
- createProcedureCaller,
3
- isProcedure
4
- } from "./chunk-CVLK2PBB.js";
2
+ __export,
3
+ createProcedureClient,
4
+ getRouterChild,
5
+ isProcedure,
6
+ unlazy
7
+ } from "./chunk-6A7XHEBH.js";
5
8
 
6
- // src/fetch/handle.ts
9
+ // src/fetch/composite-handler.ts
10
+ var CompositeHandler = class {
11
+ constructor(handlers) {
12
+ this.handlers = handlers;
13
+ }
14
+ async fetch(request, ...opt) {
15
+ for (const handler of this.handlers) {
16
+ if (handler.condition(request)) {
17
+ return handler.fetch(request, ...opt);
18
+ }
19
+ }
20
+ return new Response("None of the handlers can handle the request.", {
21
+ status: 404
22
+ });
23
+ }
24
+ };
25
+
26
+ // src/fetch/orpc-handler.ts
27
+ import { executeWithHooks, ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE, trim as trim2 } from "@orpc/shared";
28
+ import { ORPCError as ORPCError2 } from "@orpc/shared/error";
29
+
30
+ // src/fetch/orpc-payload-codec.ts
31
+ import { findDeepMatches, set } from "@orpc/shared";
7
32
  import { ORPCError } from "@orpc/shared/error";
8
- async function handleFetchRequest(options) {
9
- for (const handler of options.handlers) {
10
- const response = await handler(options);
11
- if (response) {
12
- return response;
33
+
34
+ // src/fetch/super-json.ts
35
+ var super_json_exports = {};
36
+ __export(super_json_exports, {
37
+ deserialize: () => deserialize,
38
+ serialize: () => serialize
39
+ });
40
+
41
+ // ../../node_modules/.pnpm/is-what@5.0.2/node_modules/is-what/dist/getType.js
42
+ function getType(payload) {
43
+ return Object.prototype.toString.call(payload).slice(8, -1);
44
+ }
45
+
46
+ // ../../node_modules/.pnpm/is-what@5.0.2/node_modules/is-what/dist/isPlainObject.js
47
+ function isPlainObject(payload) {
48
+ if (getType(payload) !== "Object")
49
+ return false;
50
+ const prototype = Object.getPrototypeOf(payload);
51
+ return !!prototype && prototype.constructor === Object && prototype === Object.prototype;
52
+ }
53
+
54
+ // src/fetch/super-json.ts
55
+ function serialize(value, segments = [], meta = []) {
56
+ if (typeof value === "bigint") {
57
+ meta.push(["bigint", segments]);
58
+ return { data: value.toString(), meta };
59
+ }
60
+ if (value instanceof Date) {
61
+ meta.push(["date", segments]);
62
+ const data = Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
63
+ return { data, meta };
64
+ }
65
+ if (Number.isNaN(value)) {
66
+ meta.push(["nan", segments]);
67
+ return { data: "NaN", meta };
68
+ }
69
+ if (value instanceof RegExp) {
70
+ meta.push(["regexp", segments]);
71
+ return { data: value.toString(), meta };
72
+ }
73
+ if (value instanceof URL) {
74
+ meta.push(["url", segments]);
75
+ return { data: value.toString(), meta };
76
+ }
77
+ if (isPlainObject(value)) {
78
+ const data = {};
79
+ for (const k in value) {
80
+ data[k] = serialize(value[k], [...segments, k], meta).data;
13
81
  }
82
+ return { data, meta };
83
+ }
84
+ if (Array.isArray(value)) {
85
+ const data = value.map((v, i) => {
86
+ if (v === void 0) {
87
+ meta.push(["undefined", [...segments, i]]);
88
+ return null;
89
+ }
90
+ return serialize(v, [...segments, i], meta).data;
91
+ });
92
+ return { data, meta };
93
+ }
94
+ if (value instanceof Set) {
95
+ const result = serialize(Array.from(value), segments, meta);
96
+ meta.push(["set", segments]);
97
+ return result;
98
+ }
99
+ if (value instanceof Map) {
100
+ const result = serialize(Array.from(value.entries()), segments, meta);
101
+ meta.push(["map", segments]);
102
+ return result;
14
103
  }
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"
104
+ return { data: value, meta };
105
+ }
106
+ function deserialize({
107
+ data,
108
+ meta
109
+ }) {
110
+ if (meta.length === 0) {
111
+ return data;
112
+ }
113
+ const ref = { data };
114
+ for (const [type, segments] of meta) {
115
+ let currentRef = ref;
116
+ let preSegment = "data";
117
+ for (let i = 0; i < segments.length; i++) {
118
+ currentRef = currentRef[preSegment];
119
+ preSegment = segments[i];
120
+ }
121
+ switch (type) {
122
+ case "nan":
123
+ currentRef[preSegment] = Number.NaN;
124
+ break;
125
+ case "bigint":
126
+ currentRef[preSegment] = BigInt(currentRef[preSegment]);
127
+ break;
128
+ case "date":
129
+ currentRef[preSegment] = new Date(currentRef[preSegment]);
130
+ break;
131
+ case "regexp": {
132
+ const [, pattern, flags] = currentRef[preSegment].match(/^\/(.*)\/([a-z]*)$/);
133
+ currentRef[preSegment] = new RegExp(pattern, flags);
134
+ break;
135
+ }
136
+ case "url":
137
+ currentRef[preSegment] = new URL(currentRef[preSegment]);
138
+ break;
139
+ case "undefined":
140
+ currentRef[preSegment] = void 0;
141
+ break;
142
+ case "map":
143
+ currentRef[preSegment] = new Map(currentRef[preSegment]);
144
+ break;
145
+ case "set":
146
+ currentRef[preSegment] = new Set(currentRef[preSegment]);
147
+ break;
148
+ /* v8 ignore next 3 */
149
+ default: {
150
+ const _expected = type;
151
+ }
20
152
  }
21
- });
153
+ }
154
+ return ref.data;
22
155
  }
23
156
 
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) {
157
+ // src/fetch/orpc-payload-codec.ts
158
+ var ORPCPayloadCodec = class {
159
+ /**
160
+ * If method is GET, the payload will be encoded as query string.
161
+ * If method is GET and payload contain file, the method will be fallback to fallbackMethod. (fallbackMethod = GET will force to use GET method)
162
+ */
163
+ encode(payload, method = "POST", fallbackMethod = "POST") {
164
+ const { data, meta } = serialize(payload);
165
+ const { maps, values } = findDeepMatches((v) => v instanceof Blob, data);
166
+ if (method === "GET" && (values.length === 0 || fallbackMethod === "GET")) {
167
+ const query = new URLSearchParams({
168
+ data: JSON.stringify(data),
169
+ meta: JSON.stringify(meta)
170
+ });
171
+ return {
172
+ query,
173
+ method: "GET"
174
+ };
175
+ }
176
+ const nonGETMethod = method === "GET" ? fallbackMethod : method;
177
+ if (values.length > 0) {
178
+ const form = new FormData();
179
+ if (data !== void 0) {
180
+ form.append("data", JSON.stringify(data));
181
+ }
182
+ form.append("meta", JSON.stringify(meta));
183
+ form.append("maps", JSON.stringify(maps));
184
+ for (const i in values) {
185
+ const value = values[i];
186
+ form.append(i, value);
187
+ }
188
+ return {
189
+ body: form,
190
+ method: nonGETMethod
191
+ };
192
+ }
193
+ return {
194
+ body: JSON.stringify({ data, meta }),
195
+ headers: new Headers({
196
+ "content-type": "application/json"
197
+ }),
198
+ method: nonGETMethod
199
+ };
200
+ }
201
+ async decode(re) {
202
+ try {
203
+ if ("method" in re && re.method === "GET") {
204
+ const url = new URL(re.url);
205
+ const query = url.searchParams;
206
+ const data = JSON.parse(query.getAll("data").at(-1));
207
+ const meta = JSON.parse(query.getAll("meta").at(-1));
208
+ return deserialize({
209
+ data,
210
+ meta
211
+ });
212
+ }
213
+ if (re.headers.get("content-type")?.startsWith("multipart/form-data")) {
214
+ const form = await re.formData();
215
+ const rawData = form.get("data");
216
+ const rawMeta = form.get("meta");
217
+ const rawMaps = form.get("maps");
218
+ let data = JSON.parse(rawData);
219
+ const meta = JSON.parse(rawMeta);
220
+ const maps = JSON.parse(rawMaps);
221
+ for (const i in maps) {
222
+ data = set(data, maps[i], form.get(i));
223
+ }
224
+ return deserialize({
225
+ data,
226
+ meta
227
+ });
228
+ }
229
+ const json = await re.json();
230
+ return deserialize(json);
231
+ } catch (e) {
232
+ throw new ORPCError({
233
+ code: "BAD_REQUEST",
234
+ message: "Cannot parse request/response. Please check the request/response body and Content-Type header.",
235
+ cause: e
236
+ });
237
+ }
238
+ }
239
+ };
240
+
241
+ // src/fetch/orpc-procedure-matcher.ts
242
+ import { trim } from "@orpc/shared";
243
+ var ORPCProcedureMatcher = class {
244
+ constructor(router) {
245
+ this.router = router;
246
+ }
247
+ async match(pathname) {
248
+ const path = trim(pathname, "/").split("/").map(decodeURIComponent);
249
+ const match = getRouterChild(this.router, ...path);
250
+ const { default: maybeProcedure } = await unlazy(match);
251
+ if (!isProcedure(maybeProcedure)) {
34
252
  return void 0;
35
253
  }
36
- const context = await value(options.context);
37
- const handler = async () => {
38
- const url = new URL(options.request.url);
39
- const pathname = `/${trim(url.pathname.replace(options.prefix ?? "", ""), "/")}`;
40
- const match = resolveORPCRouter(options.router, pathname);
254
+ return {
255
+ procedure: maybeProcedure,
256
+ path
257
+ };
258
+ }
259
+ };
260
+
261
+ // src/fetch/orpc-handler.ts
262
+ var ORPCHandler = class {
263
+ constructor(router, options) {
264
+ this.router = router;
265
+ this.options = options;
266
+ this.procedureMatcher = options?.procedureMatcher ?? new ORPCProcedureMatcher(router);
267
+ this.payloadCodec = options?.payloadCodec ?? new ORPCPayloadCodec();
268
+ }
269
+ procedureMatcher;
270
+ payloadCodec;
271
+ condition(request) {
272
+ return Boolean(request.headers.get(ORPC_HANDLER_HEADER)?.includes(ORPC_HANDLER_VALUE));
273
+ }
274
+ async fetch(request, ...[options]) {
275
+ const context = options?.context;
276
+ const execute = async () => {
277
+ const url = new URL(request.url);
278
+ const pathname = `/${trim2(url.pathname.replace(options?.prefix ?? "", ""), "/")}`;
279
+ const match = await this.procedureMatcher.match(pathname);
41
280
  if (!match) {
42
281
  throw new ORPCError2({ code: "NOT_FOUND", message: "Not found" });
43
282
  }
44
- const input = await deserializeRequest(options.request);
45
- const caller = createProcedureCaller({
283
+ const input = await this.payloadCodec.decode(request);
284
+ const client = createProcedureClient({
46
285
  context,
47
286
  procedure: match.procedure,
48
287
  path: match.path
49
288
  });
50
- const output = await caller(input);
51
- const { body, headers } = serializer.serialize(output);
52
- return new Response(body, {
53
- status: 200,
54
- headers
55
- });
289
+ const output = await client(input, { signal: options?.signal });
290
+ const { body, headers } = this.payloadCodec.encode(output);
291
+ return new Response(body, { headers });
56
292
  };
57
293
  try {
58
- return await options.hooks?.(
294
+ return await executeWithHooks({
59
295
  context,
60
- { next: handler, response: (response) => response }
61
- ) ?? await handler();
296
+ execute,
297
+ input: request,
298
+ hooks: this.options,
299
+ meta: {
300
+ signal: options?.signal
301
+ }
302
+ });
62
303
  } catch (e) {
63
304
  const error = e instanceof ORPCError2 ? e : new ORPCError2({
64
305
  code: "INTERNAL_SERVER_ERROR",
65
306
  message: "Internal server error",
66
307
  cause: e
67
308
  });
68
- const { body, headers } = serializer.serialize(error.toJSON());
309
+ const { body, headers } = this.payloadCodec.encode(error.toJSON());
69
310
  return new Response(body, {
70
- status: error.status,
71
- headers
311
+ headers,
312
+ status: error.status
72
313
  });
73
314
  }
74
- };
75
- }
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;
90
- }
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
315
  }
101
- }
316
+ };
102
317
  export {
103
- createORPCHandler,
104
- handleFetchRequest
318
+ CompositeHandler,
319
+ ORPCHandler,
320
+ ORPCPayloadCodec,
321
+ ORPCProcedureMatcher,
322
+ super_json_exports as SuperJSON
105
323
  };
324
+ //# sourceMappingURL=fetch.js.map