@orpc/server 0.0.0-next.d137cdf → 0.0.0-next.d42488d

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