@temporary-name/server 1.9.3-alpha.6ef4729e23affbe6454d37025d1dfc4d998b0649 → 1.9.3-alpha.70ac9623e5fa2a8af7954e34ef97cee968e5cad9

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.
Files changed (45) hide show
  1. package/dist/adapters/aws-lambda/index.d.mts +11 -7
  2. package/dist/adapters/aws-lambda/index.d.ts +11 -7
  3. package/dist/adapters/aws-lambda/index.mjs +11 -4
  4. package/dist/adapters/fetch/index.d.mts +15 -87
  5. package/dist/adapters/fetch/index.d.ts +15 -87
  6. package/dist/adapters/fetch/index.mjs +22 -161
  7. package/dist/adapters/node/index.d.mts +15 -64
  8. package/dist/adapters/node/index.d.ts +15 -64
  9. package/dist/adapters/node/index.mjs +20 -126
  10. package/dist/handler/index.d.mts +28 -0
  11. package/dist/handler/index.d.ts +28 -0
  12. package/dist/handler/index.mjs +8 -0
  13. package/dist/helpers/index.mjs +3 -29
  14. package/dist/index.d.mts +373 -546
  15. package/dist/index.d.ts +373 -546
  16. package/dist/index.mjs +547 -470
  17. package/dist/openapi/index.d.mts +185 -0
  18. package/dist/openapi/index.d.ts +185 -0
  19. package/dist/openapi/index.mjs +782 -0
  20. package/dist/shared/server.BGG3eatg.mjs +315 -0
  21. package/dist/shared/server.BJw16psR.d.mts +39 -0
  22. package/dist/shared/server.BM9lK_Yv.mjs +523 -0
  23. package/dist/shared/server.BpMVFPn5.d.ts +39 -0
  24. package/dist/shared/server.C1RJffw4.mjs +30 -0
  25. package/dist/shared/server.CjPiuQYH.d.mts +51 -0
  26. package/dist/shared/server.CjPiuQYH.d.ts +51 -0
  27. package/dist/shared/server.CmNVzZVe.mjs +156 -0
  28. package/dist/shared/server.DX_j7xyF.d.mts +805 -0
  29. package/dist/shared/server.DX_j7xyF.d.ts +805 -0
  30. package/dist/shared/server.X8F6e8eV.mjs +499 -0
  31. package/package.json +20 -31
  32. package/dist/adapters/standard/index.d.mts +0 -16
  33. package/dist/adapters/standard/index.d.ts +0 -16
  34. package/dist/adapters/standard/index.mjs +0 -101
  35. package/dist/plugins/index.d.mts +0 -160
  36. package/dist/plugins/index.d.ts +0 -160
  37. package/dist/plugins/index.mjs +0 -288
  38. package/dist/shared/server.BEQrAa3A.mjs +0 -207
  39. package/dist/shared/server.Bo94xDTv.d.mts +0 -73
  40. package/dist/shared/server.Btxrgkj5.d.ts +0 -73
  41. package/dist/shared/server.C1YnHvvf.d.mts +0 -192
  42. package/dist/shared/server.C1YnHvvf.d.ts +0 -192
  43. package/dist/shared/server.D6K9uoPI.mjs +0 -35
  44. package/dist/shared/server.DZ5BIITo.mjs +0 -9
  45. package/dist/shared/server.X0YaZxSJ.mjs +0 -13
package/dist/index.mjs CHANGED
@@ -1,137 +1,82 @@
1
- import { isContractProcedure, mergeErrorMap, mergeMeta, mergeRoute, mergePrefix, enhanceRoute, mergeTags, getContractRouter } from '@temporary-name/contract';
2
- export { ValidationError, eventIterator, type, validateORPCError } from '@temporary-name/contract';
3
- import { onError, resolveMaybeOptionalOptions } from '@temporary-name/shared';
4
- export { AsyncIteratorClass, EventPublisher, ORPCError, asyncIteratorToStream as eventIteratorToStream, isDefinedError, onError, onFinish, onStart, onSuccess, safe, streamToAsyncIteratorClass as streamToEventIterator } from '@temporary-name/shared';
5
- import { g as gatingContext } from './shared/server.D6K9uoPI.mjs';
6
- import { c as createProcedureClient, i as isLazy, g as getLazyMeta, l as lazy, u as unlazy } from './shared/server.BEQrAa3A.mjs';
7
- export { L as LAZY_SYMBOL, a as createORPCErrorConstructorMap, m as mergeCurrentContext, b as middlewareOutputFn } from './shared/server.BEQrAa3A.mjs';
1
+ import { assertNever, splitFirst, onError, isObject, toArray, guard, resolveMaybeOptionalOptions, isTypescriptObject, isPropertyKey } from '@temporary-name/shared';
2
+ export { AsyncIteratorClass, EventPublisher, asyncIteratorToStream as eventIteratorToStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@temporary-name/shared';
3
+ import * as z from '@temporary-name/zod';
4
+ import { SchemaClass, object, core, gatingContext } from '@temporary-name/zod';
5
+ export { z as baseZ };
6
+ import { U as UnauthorizedError, P as Procedure, m as mergePrefix, a as mergeTags, b as enhanceRouter, C as Contract, p as parseEndpointDefinition, c as mergeRoute, d as prefixRoute, f as addMiddleware, l as lazyInternal, h as getLazyMeta, u as unlazy, j as isProcedure } from './shared/server.X8F6e8eV.mjs';
7
+ export { A as APIError, B as BadRequestError, F as ForbiddenError, I as InternalServerError, L as LAZY_SYMBOL, N as NotFoundError, V as ValidationError, x as createAccessibleLazyRouter, e as encodeError, D as endpointRegex, g as getDynamicParams, w as getRouter, i as isAPIErrorStatus, E as isDevelopment, o as isLazy, q as isStartWithMiddlewares, n as lazy, k as makeErrors, v as mergeMiddlewares, r as resolveContractProcedures, s as standardizeHTTPPath, t as toAPIError, y as traverseContractProcedures, z as unlazyRouter } from './shared/server.X8F6e8eV.mjs';
8
+ import { g as getCookie } from './shared/server.C1RJffw4.mjs';
9
+ import { c as createProcedureClient } from './shared/server.CmNVzZVe.mjs';
10
+ export { m as mergeCurrentContext, a as middlewareOutputFn } from './shared/server.CmNVzZVe.mjs';
11
+ import { J as JsonSchemaXNativeType } from './shared/server.BM9lK_Yv.mjs';
12
+ export { b as JSON_SCHEMA_INPUT_REGISTRY, c as JSON_SCHEMA_OUTPUT_REGISTRY, a as JSON_SCHEMA_REGISTRY, Z as ZodToJsonSchemaConverter, e as eventIterator, g as getEventIteratorSchemaDetails } from './shared/server.BM9lK_Yv.mjs';
8
13
  export { getEventMeta, withEventMeta } from '@temporary-name/standard-server';
9
- import 'node:async_hooks';
10
- import 'zod';
14
+ import 'cookie';
15
+ import '@temporary-name/server/openapi';
11
16
  import 'zod/v4/core';
12
17
 
13
- function decorateMiddleware(middleware) {
14
- const decorated = ((...args) => middleware(...args));
15
- decorated.mapInput = (mapInput) => {
16
- const mapped = decorateMiddleware(
17
- (options, input, ...rest) => middleware(options, mapInput(input), ...rest)
18
- );
19
- return mapped;
20
- };
21
- decorated.concat = (concatMiddleware, mapInput) => {
22
- const mapped = mapInput ? decorateMiddleware(concatMiddleware).mapInput(mapInput) : concatMiddleware;
23
- const concatted = decorateMiddleware((options, input, output, ...rest) => {
24
- const merged = middleware(
25
- {
26
- ...options,
27
- next: (...[nextOptions1]) => mapped(
28
- {
29
- ...options,
30
- context: { ...options.context, ...nextOptions1?.context },
31
- next: (...[nextOptions2]) => options.next({ context: { ...nextOptions1?.context, ...nextOptions2?.context } })
32
- },
33
- input,
34
- output,
35
- ...rest
36
- )
37
- },
38
- input,
39
- output,
40
- ...rest
41
- );
42
- return merged;
43
- });
44
- return concatted;
45
- };
46
- return decorated;
47
- }
48
-
49
- function isStartWithMiddlewares(middlewares, compare) {
50
- if (compare.length > middlewares.length) {
51
- return false;
18
+ function validateTokenPrefix(prefix, token) {
19
+ if (prefix && !token.startsWith(prefix)) {
20
+ throw new UnauthorizedError({ message: `Invalid auth token. It must start with "${prefix}"` });
52
21
  }
53
- for (let i = 0; i < middlewares.length; i++) {
54
- if (compare[i] === void 0) {
55
- return true;
22
+ }
23
+ function validateTokenAuth(config, token, options) {
24
+ const { tokenPrefix, validate } = config;
25
+ validateTokenPrefix(tokenPrefix, token);
26
+ return validate(token, options);
27
+ }
28
+ function runAuth(config, options) {
29
+ switch (config.type) {
30
+ case "header": {
31
+ const authHeader = options.request.headers.get(config.name);
32
+ return authHeader ? validateTokenAuth(config, authHeader, options) : void 0;
33
+ }
34
+ case "query": {
35
+ const { name, tokenPrefix, validate } = config;
36
+ const query = options.request.url.searchParams;
37
+ const token = query.get(name);
38
+ if (!token) return void 0;
39
+ validateTokenPrefix(tokenPrefix, token);
40
+ return validate(token, options);
41
+ }
42
+ case "cookie": {
43
+ const cookie = getCookie(options.request.headers, config.name);
44
+ return cookie ? validateTokenAuth(config, cookie, options) : void 0;
45
+ }
46
+ case "bearer": {
47
+ const authHeader = options.request.headers.get("Authorization");
48
+ if (!authHeader) return void 0;
49
+ const [authType, bearer] = splitFirst(authHeader, " ");
50
+ return authType === "Bearer" ? validateTokenAuth(config, bearer, options) : void 0;
51
+ }
52
+ case "basic": {
53
+ const authHeader = options.request.headers.get("Authorization");
54
+ if (!authHeader) return void 0;
55
+ const [authType, encoded] = splitFirst(authHeader, " ");
56
+ if (authType !== "Basic") return void 0;
57
+ const decoded = Buffer.from(encoded, "base64").toString("utf-8");
58
+ const [username, password] = splitFirst(decoded, ":");
59
+ validateTokenPrefix(config.tokenPrefix, password);
60
+ return config.validate(username, password, options);
56
61
  }
57
- if (middlewares[i] !== compare[i]) {
62
+ case "none": {
58
63
  return false;
59
64
  }
65
+ default: {
66
+ assertNever(config);
67
+ }
60
68
  }
61
- return true;
62
- }
63
- function mergeMiddlewares(first, second, options) {
64
- if (options.dedupeLeading && isStartWithMiddlewares(second, first)) {
65
- return second;
66
- }
67
- return [...first, ...second];
68
69
  }
69
- function addMiddleware(middlewares, addition) {
70
- return [...middlewares, addition];
70
+ function authDescription(config) {
71
+ const { type } = config;
72
+ return type === "basic" ? "a Basic Authentication header" : type === "bearer" ? "a Bearer Authentication header" : type === "header" ? `a header named "${config.name}"` : type === "query" ? `a query parameter named "${config.name}"` : type === "cookie" ? `a cookie named "${config.name}"` : type === "none" ? "no authentication" : assertNever(type);
71
73
  }
72
74
 
73
- class Procedure {
74
- /**
75
- * This property holds the defined options.
76
- */
77
- "~orpc";
78
- constructor(def) {
79
- this["~orpc"] = def;
80
- }
81
- }
82
- function isProcedure(item) {
83
- if (item instanceof Procedure) {
84
- return true;
85
- }
86
- return isContractProcedure(item) && "middlewares" in item["~orpc"] && "inputValidationIndex" in item["~orpc"] && "outputValidationIndex" in item["~orpc"] && "handler" in item["~orpc"];
75
+ function mergeMeta(meta1, meta2) {
76
+ return { ...meta1, ...meta2 };
87
77
  }
88
78
 
89
79
  class DecoratedProcedure extends Procedure {
90
- /**
91
- * Adds type-safe custom errors.
92
- * The provided errors are spared-merged with any existing errors.
93
- *
94
- * @see {@link https://orpc.unnoq.com/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
95
- */
96
- errors(errors) {
97
- return new DecoratedProcedure({
98
- ...this["~orpc"],
99
- errorMap: mergeErrorMap(this["~orpc"].errorMap, errors)
100
- });
101
- }
102
- /**
103
- * Sets or updates the metadata.
104
- * The provided metadata is spared-merged with any existing metadata.
105
- *
106
- * @see {@link https://orpc.unnoq.com/docs/metadata Metadata Docs}
107
- */
108
- meta(meta) {
109
- return new DecoratedProcedure({
110
- ...this["~orpc"],
111
- meta: mergeMeta(this["~orpc"].meta, meta)
112
- });
113
- }
114
- /**
115
- * Sets or updates the route definition.
116
- * The provided route is spared-merged with any existing route.
117
- * This option is typically relevant when integrating with OpenAPI.
118
- *
119
- * @see {@link https://orpc.unnoq.com/docs/openapi/routing OpenAPI Routing Docs}
120
- * @see {@link https://orpc.unnoq.com/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
121
- */
122
- route(route) {
123
- return new DecoratedProcedure({
124
- ...this["~orpc"],
125
- route: mergeRoute(this["~orpc"].route, route)
126
- });
127
- }
128
- use(middleware, mapInput) {
129
- const mapped = mapInput ? decorateMiddleware(middleware).mapInput(mapInput) : middleware;
130
- return new DecoratedProcedure({
131
- ...this["~orpc"],
132
- middlewares: addMiddleware(this["~orpc"].middlewares, mapped)
133
- });
134
- }
135
80
  /**
136
81
  * Make this procedure callable (works like a function while still being a procedure).
137
82
  *
@@ -150,244 +95,76 @@ class DecoratedProcedure extends Procedure {
150
95
  }
151
96
  }
152
97
 
153
- const HIDDEN_ROUTER_CONTRACT_SYMBOL = Symbol("ORPC_HIDDEN_ROUTER_CONTRACT");
154
- function setHiddenRouterContract(router, contract) {
155
- return new Proxy(router, {
156
- get(target, key) {
157
- if (key === HIDDEN_ROUTER_CONTRACT_SYMBOL) {
158
- return contract;
159
- }
160
- return Reflect.get(target, key);
161
- }
162
- });
163
- }
164
- function getHiddenRouterContract(router) {
165
- return router[HIDDEN_ROUTER_CONTRACT_SYMBOL];
166
- }
167
-
168
- function getRouter(router, path) {
169
- let current = router;
170
- for (let i = 0; i < path.length; i++) {
171
- const segment = path[i];
172
- if (!current) {
173
- return void 0;
174
- }
175
- if (isProcedure(current)) {
176
- return void 0;
177
- }
178
- if (!isLazy(current)) {
179
- current = current[segment];
180
- continue;
181
- }
182
- const lazied = current;
183
- const rest = path.slice(i);
184
- return lazy(async () => {
185
- const unwrapped = await unlazy(lazied);
186
- const next = getRouter(unwrapped.default, rest);
187
- return unlazy(next);
188
- }, getLazyMeta(lazied));
189
- }
190
- return current;
191
- }
192
- function createAccessibleLazyRouter(lazied) {
193
- const recursive = new Proxy(lazied, {
194
- get(target, key) {
195
- if (typeof key !== "string") {
196
- return Reflect.get(target, key);
197
- }
198
- const next = getRouter(lazied, [key]);
199
- return createAccessibleLazyRouter(next);
200
- }
201
- });
202
- return recursive;
203
- }
204
- function enhanceRouter(router, options) {
205
- if (isLazy(router)) {
206
- const laziedMeta = getLazyMeta(router);
207
- const enhancedPrefix = laziedMeta?.prefix ? mergePrefix(options.prefix, laziedMeta?.prefix) : options.prefix;
208
- const enhanced2 = lazy(
209
- async () => {
210
- const { default: unlaziedRouter } = await unlazy(router);
211
- const enhanced3 = enhanceRouter(unlaziedRouter, options);
212
- return unlazy(enhanced3);
213
- },
214
- {
215
- ...laziedMeta,
216
- prefix: enhancedPrefix
217
- }
218
- );
219
- const accessible = createAccessibleLazyRouter(enhanced2);
220
- return accessible;
221
- }
222
- if (isProcedure(router)) {
223
- const newMiddlewares = mergeMiddlewares(options.middlewares, router["~orpc"].middlewares, {
224
- dedupeLeading: options.dedupeLeadingMiddlewares
225
- });
226
- const newMiddlewareAdded = newMiddlewares.length - router["~orpc"].middlewares.length;
227
- const enhanced2 = new Procedure({
228
- ...router["~orpc"],
229
- route: enhanceRoute(router["~orpc"].route, options),
230
- errorMap: mergeErrorMap(options.errorMap, router["~orpc"].errorMap),
231
- middlewares: newMiddlewares,
232
- inputValidationIndex: router["~orpc"].inputValidationIndex + newMiddlewareAdded,
233
- outputValidationIndex: router["~orpc"].outputValidationIndex + newMiddlewareAdded
234
- });
235
- return enhanced2;
236
- }
237
- const enhanced = {};
238
- for (const key in router) {
239
- enhanced[key] = enhanceRouter(router[key], options);
240
- }
241
- return enhanced;
242
- }
243
- function traverseContractProcedures(options, callback, lazyOptions = []) {
244
- let currentRouter = options.router;
245
- const hiddenContract = getHiddenRouterContract(options.router);
246
- if (hiddenContract !== void 0) {
247
- currentRouter = hiddenContract;
248
- }
249
- if (isLazy(currentRouter)) {
250
- lazyOptions.push({
251
- router: currentRouter,
252
- path: options.path
253
- });
254
- } else if (isContractProcedure(currentRouter)) {
255
- callback({
256
- contract: currentRouter,
257
- path: options.path
258
- });
259
- } else {
260
- for (const key in currentRouter) {
261
- traverseContractProcedures(
262
- {
263
- router: currentRouter[key],
264
- path: [...options.path, key]
265
- },
266
- callback,
267
- lazyOptions
268
- );
269
- }
270
- }
271
- return lazyOptions;
272
- }
273
- async function resolveContractProcedures(options, callback) {
274
- const pending = [options];
275
- for (const options2 of pending) {
276
- const lazyOptions = traverseContractProcedures(options2, callback);
277
- for (const options3 of lazyOptions) {
278
- const { default: router } = await unlazy(options3.router);
279
- pending.push({
280
- router,
281
- path: options3.path
282
- });
283
- }
284
- }
285
- }
286
- async function unlazyRouter(router) {
287
- if (isProcedure(router)) {
288
- return router;
289
- }
290
- const unlazied = {};
291
- for (const key in router) {
292
- const item = router[key];
293
- const { default: unlaziedRouter } = await unlazy(item);
294
- unlazied[key] = await unlazyRouter(unlaziedRouter);
295
- }
296
- return unlazied;
297
- }
98
+ const initialSchemas = {
99
+ pathSchema: z.strictObject({}),
100
+ querySchema: z.strictObject({}),
101
+ bodySchema: z.strictObject({}),
102
+ outputSchema: z.unknown()
103
+ };
298
104
 
299
- class Builder {
300
- /**
301
- * This property holds the defined options.
302
- */
303
- "~orpc";
105
+ class ProcedureBuilder extends Contract {
106
+ z;
304
107
  constructor(def) {
305
- this["~orpc"] = def;
108
+ super(def);
109
+ this.z = new SchemaClass();
306
110
  }
307
111
  /**
308
- * Set or override the initial context.
309
- *
310
- * @see {@link https://orpc.unnoq.com/docs/context Context Docs}
311
- */
312
- $context() {
313
- return this;
314
- }
315
- /**
316
- * Creates a middleware.
317
- *
318
- * @see {@link https://orpc.unnoq.com/docs/middleware Middleware Docs}
319
- */
320
- middleware(middleware) {
321
- return decorateMiddleware(middleware);
322
- }
323
- /**
324
- * Adds type-safe custom errors.
325
- * The provided errors are spared-merged with any existing errors.
326
- *
327
- * @see {@link https://orpc.unnoq.com/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
328
- */
329
- errors(errors) {
330
- return new Builder({
331
- ...this["~orpc"],
332
- errorMap: mergeErrorMap(this["~orpc"].errorMap, errors)
333
- });
334
- }
335
- /**
336
- * Uses a middleware to modify the context or improve the pipeline.
337
- *
338
- * @info Supports both normal middleware and inline middleware implementations.
339
- * @note The current context must be satisfy middleware dependent-context
340
- * @see {@link https://orpc.unnoq.com/docs/middleware Middleware Docs}
341
- */
342
- use(middleware) {
343
- return new Builder({
344
- ...this["~orpc"],
345
- middlewares: addMiddleware(this["~orpc"].middlewares, middleware)
346
- });
347
- }
348
- useGating(gates, isGateEnabled) {
349
- return this.use(({ next, context }) => {
350
- return gatingContext.run(
351
- (gate) => isGateEnabled(gate, context),
352
- () => next({ context: { isGateEnabled } })
353
- );
354
- });
355
- }
356
- /**
357
- * Sets or updates the metadata.
358
- * The provided metadata is spared-merged with any existing metadata.
112
+ * Sets or updates the metadata for the contract.
113
+ * The provided metadata is spared-merged with any existing metadata in the contract.
359
114
  *
360
115
  * @see {@link https://orpc.unnoq.com/docs/metadata Metadata Docs}
361
116
  */
362
117
  meta(meta) {
363
- return new Builder({
118
+ return new ProcedureBuilder({
364
119
  ...this["~orpc"],
365
120
  meta: mergeMeta(this["~orpc"].meta, meta)
366
121
  });
367
122
  }
123
+ endpoint(stringsOrEndpoint, ...values) {
124
+ const { method, path, pathSchema } = parseEndpointDefinition(stringsOrEndpoint, values);
125
+ const { prefix } = this["~orpc"];
126
+ const route = { method, path };
127
+ return new ProcedureBuilder({
128
+ ...this["~orpc"],
129
+ route: mergeRoute(this["~orpc"].route, prefix ? prefixRoute(route, prefix) : route),
130
+ schemas: {
131
+ ...this["~orpc"].schemas,
132
+ pathSchema
133
+ }
134
+ });
135
+ }
368
136
  /**
369
- * Sets or updates the route definition.
370
- * The provided route is spared-merged with any existing route.
137
+ * Sets or updates the route definition for the contract.
138
+ * The provided route is spared-merged with any existing route in the contract.
371
139
  * This option is typically relevant when integrating with OpenAPI.
372
140
  *
373
141
  * @see {@link https://orpc.unnoq.com/docs/openapi/routing OpenAPI Routing Docs}
374
142
  * @see {@link https://orpc.unnoq.com/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
375
143
  */
376
- route(route) {
377
- return new Builder({
144
+ endpointOpts(route) {
145
+ const { prefix } = this["~orpc"];
146
+ return new ProcedureBuilder({
378
147
  ...this["~orpc"],
379
- route: mergeRoute(this["~orpc"].route, route)
148
+ route: mergeRoute(this["~orpc"].route, prefix ? prefixRoute(route, prefix) : route)
380
149
  });
381
150
  }
382
- /**
383
- * Defines the input validation schema.
384
- *
385
- * @see {@link https://orpc.unnoq.com/docs/procedure#input-output-validation Input Validation Docs}
386
- */
387
- input(schema) {
388
- return new Builder({
151
+ query(schema) {
152
+ return new ProcedureBuilder({
389
153
  ...this["~orpc"],
390
- inputSchema: schema,
154
+ schemas: {
155
+ ...this["~orpc"].schemas,
156
+ querySchema: schema instanceof core.$ZodType ? schema : object(schema)
157
+ },
158
+ inputValidationIndex: this["~orpc"].middlewares.length
159
+ });
160
+ }
161
+ body(schema) {
162
+ return new ProcedureBuilder({
163
+ ...this["~orpc"],
164
+ schemas: {
165
+ ...this["~orpc"].schemas,
166
+ bodySchema: schema instanceof core.$ZodType ? schema : object(schema)
167
+ },
391
168
  inputValidationIndex: this["~orpc"].middlewares.length
392
169
  });
393
170
  }
@@ -397,23 +174,79 @@ class Builder {
397
174
  * @see {@link https://orpc.unnoq.com/docs/procedure#input-output-validation Output Validation Docs}
398
175
  */
399
176
  output(schema) {
400
- return new Builder({
177
+ return new ProcedureBuilder({
401
178
  ...this["~orpc"],
402
- outputSchema: schema,
179
+ schemas: {
180
+ ...this["~orpc"].schemas,
181
+ outputSchema: schema instanceof core.$ZodType ? schema : object(schema)
182
+ },
403
183
  outputValidationIndex: this["~orpc"].middlewares.length
404
184
  });
405
185
  }
186
+ use(middleware) {
187
+ return new this.constructor({
188
+ ...this["~orpc"],
189
+ middlewares: addMiddleware(this["~orpc"].middlewares, middleware)
190
+ });
191
+ }
192
+ useErrors(errorMap) {
193
+ return new this.constructor({
194
+ ...this["~orpc"],
195
+ errorMap
196
+ });
197
+ }
198
+ // The default of false for ValidatedAuthContext is used when you pass in the type 'none'. We use false
199
+ // because we can't use null or undefined (see ValidatedAuthContext) but we still want it to be falsy.
200
+ useAuth(config) {
201
+ const middleware = baseApi.$context().middleware(async (options) => {
202
+ const { next, context } = options;
203
+ if (context.auth) return next();
204
+ const auth = await runAuth(config, options);
205
+ if (auth === void 0) {
206
+ const { authConfigs } = options.procedure["~orpc"];
207
+ if (context.auth !== false && config === authConfigs.at(-1)) {
208
+ let authDescriptions = authConfigs.map(authDescription).join(", ");
209
+ if (authConfigs.length > 1) {
210
+ authDescriptions = `one of: ${authDescriptions}`;
211
+ }
212
+ throw new UnauthorizedError({
213
+ message: `Authentication required. You must provide ${authDescriptions}`
214
+ });
215
+ }
216
+ return next();
217
+ }
218
+ return next({ context: { auth } });
219
+ });
220
+ return new this.constructor({
221
+ ...this["~orpc"],
222
+ authConfigs: [...this["~orpc"].authConfigs, config],
223
+ middlewares: addMiddleware(this["~orpc"].middlewares, middleware)
224
+ });
225
+ }
226
+ useGating(gates, isGateEnabled) {
227
+ return this.use(({ next, context }) => {
228
+ return gatingContext.run(
229
+ (gate) => isGateEnabled(gate, context),
230
+ () => next({ context: { isGateEnabled } })
231
+ );
232
+ });
233
+ }
406
234
  /**
407
235
  * Defines the handler of the procedure.
408
236
  *
409
237
  * @see {@link https://orpc.unnoq.com/docs/procedure Procedure Docs}
410
238
  */
411
239
  handler(handler) {
240
+ if (this["~orpc"].schemas.outputSchema === initialSchemas.outputSchema) {
241
+ throw new Error("You must call .output() before calling .handler()");
242
+ }
412
243
  return new DecoratedProcedure({
413
244
  ...this["~orpc"],
414
245
  handler
415
246
  });
416
247
  }
248
+ }
249
+ class BuilderWithMiddlewares extends ProcedureBuilder {
417
250
  /**
418
251
  * Prefixes all procedures in the router.
419
252
  * The provided prefix is post-appended to any existing router prefix.
@@ -423,7 +256,7 @@ class Builder {
423
256
  * @see {@link https://orpc.unnoq.com/docs/openapi/routing#route-prefixes OpenAPI Route Prefixes Docs}
424
257
  */
425
258
  prefix(prefix) {
426
- return new Builder({
259
+ return new this.constructor({
427
260
  ...this["~orpc"],
428
261
  prefix: mergePrefix(this["~orpc"].prefix, prefix)
429
262
  });
@@ -435,7 +268,7 @@ class Builder {
435
268
  * @see {@link https://orpc.unnoq.com/docs/openapi/openapi-specification#operation-metadata OpenAPI Operation Metadata Docs}
436
269
  */
437
270
  tag(...tags) {
438
- return new Builder({
271
+ return new this.constructor({
439
272
  ...this["~orpc"],
440
273
  tags: mergeTags(this["~orpc"].tags, tags)
441
274
  });
@@ -448,123 +281,383 @@ class Builder {
448
281
  router(router) {
449
282
  return enhanceRouter(router, this["~orpc"]);
450
283
  }
284
+ }
285
+
286
+ function decorateMiddleware(middleware) {
287
+ const decorated = ((...args) => middleware(...args));
288
+ decorated.mapInput = (mapInput) => {
289
+ const mapped = decorateMiddleware(
290
+ (options, input, ...rest) => middleware(options, mapInput(input), ...rest)
291
+ );
292
+ return mapped;
293
+ };
294
+ return decorated;
295
+ }
296
+
297
+ class Builder extends BuilderWithMiddlewares {
451
298
  /**
452
- * Create a lazy router
453
- * And applies all of the previously defined options to the specified router.
299
+ * Set or override the initial context.
454
300
  *
455
- * @see {@link https://orpc.unnoq.com/docs/router#extending-router Extending Router Docs}
301
+ * @see {@link https://orpc.unnoq.com/docs/context Context Docs}
456
302
  */
457
- lazyRoute(loader) {
458
- return enhanceRouter(lazy(loader), this["~orpc"]);
303
+ $context() {
304
+ return this;
305
+ }
306
+ /**
307
+ * Creates a middleware.
308
+ *
309
+ * @see {@link https://orpc.unnoq.com/docs/middleware Middleware Docs}
310
+ */
311
+ middleware(middleware) {
312
+ return decorateMiddleware(middleware);
459
313
  }
460
314
  }
461
- function createApiBuilder(opts = {}) {
462
- const base = new Builder({
463
- route: {},
464
- meta: opts.meta ?? {},
465
- errorMap: {},
466
- inputValidationIndex: 0,
467
- outputValidationIndex: 0,
468
- middlewares: [
469
- onError((error, options) => {
470
- console.dir(error, { depth: null });
471
- })
472
- ],
473
- // NB: this is a relic from orpc -- I'm not convinced there's a need for this (or if there is, that it's
474
- // the best solution). For now I've removed the interface to configure it externally.
475
- dedupeLeadingMiddlewares: true
476
- });
477
- return base;
478
- }
479
- const os = createApiBuilder();
315
+ const baseApi = new Builder({
316
+ route: {},
317
+ meta: {},
318
+ errorMap: {},
319
+ inputValidationIndex: 0,
320
+ outputValidationIndex: 0,
321
+ middlewares: [
322
+ onError((error, _options) => {
323
+ console.dir(error, { depth: null });
324
+ })
325
+ ],
326
+ schemas: initialSchemas,
327
+ // NB: this is a relic from orpc -- I'm not convinced there's a need for this (or if there is, that it's
328
+ // the best solution). For now I've removed the interface to configure it externally.
329
+ dedupeLeadingMiddlewares: true,
330
+ authConfigs: []
331
+ });
480
332
 
481
- function implementerInternal(contract, middlewares) {
482
- if (isContractProcedure(contract)) {
483
- const impl2 = new Builder({
484
- ...contract["~orpc"],
485
- middlewares,
486
- inputValidationIndex: middlewares.length,
487
- outputValidationIndex: middlewares.length,
488
- dedupeLeadingMiddlewares: true
489
- });
490
- return impl2;
491
- }
492
- const impl = new Proxy(contract, {
493
- get: (target, key) => {
494
- if (typeof key !== "string") {
495
- return Reflect.get(target, key);
496
- }
497
- let method;
498
- if (key === "middleware") {
499
- method = (mid) => decorateMiddleware(mid);
500
- } else if (key === "use") {
501
- method = (mid) => {
502
- return implementerInternal(contract, addMiddleware(middlewares, mid));
503
- };
504
- } else if (key === "router") {
505
- method = (router) => {
506
- const adapted = enhanceRouter(router, {
507
- middlewares,
508
- errorMap: {},
509
- prefix: void 0,
510
- tags: void 0,
511
- dedupeLeadingMiddlewares: true
512
- });
513
- return setHiddenRouterContract(adapted, contract);
514
- };
515
- } else if (key === "lazyRoute") {
516
- method = (loader) => {
517
- const adapted = enhanceRouter(lazy(loader), {
518
- middlewares,
519
- errorMap: {},
520
- prefix: void 0,
521
- tags: void 0,
522
- dedupeLeadingMiddlewares: true
523
- });
524
- return setHiddenRouterContract(adapted, contract);
525
- };
333
+ const FLEXIBLE_DATE_FORMAT_REGEX = /^[^-]+-[^-]+-[^-]+$/;
334
+ class experimental_JsonSchemaCoercer {
335
+ coerce(schema, value, options = {}) {
336
+ const [, coerced] = this.#coerce(schema, value, options);
337
+ return coerced;
338
+ }
339
+ #coerce(schema, originalValue, options) {
340
+ if (typeof schema === "boolean") {
341
+ return [schema, originalValue];
342
+ }
343
+ if (Array.isArray(schema.type)) {
344
+ return this.#coerce(
345
+ {
346
+ anyOf: schema.type.map((type) => ({ ...schema, type }))
347
+ },
348
+ originalValue,
349
+ options
350
+ );
351
+ }
352
+ let coerced = originalValue;
353
+ let satisfied = true;
354
+ if (typeof schema.$ref === "string") {
355
+ const refSchema = options?.components?.[schema.$ref];
356
+ if (refSchema !== void 0) {
357
+ const [subSatisfied, subCoerced] = this.#coerce(refSchema, coerced, options);
358
+ coerced = subCoerced;
359
+ satisfied = subSatisfied;
526
360
  }
527
- const next = getContractRouter(target, [key]);
528
- if (!next) {
529
- return method ?? next;
361
+ }
362
+ const enumValues = schema.const !== void 0 ? [schema.const] : schema.enum;
363
+ if (enumValues !== void 0 && !enumValues.includes(coerced)) {
364
+ if (typeof coerced === "string") {
365
+ const numberValue = this.#stringToNumber(coerced);
366
+ if (enumValues.includes(numberValue)) {
367
+ coerced = numberValue;
368
+ } else {
369
+ const booleanValue = this.#stringToBoolean(coerced);
370
+ if (enumValues.includes(booleanValue)) {
371
+ coerced = booleanValue;
372
+ } else {
373
+ satisfied = false;
374
+ }
375
+ }
376
+ } else {
377
+ satisfied = false;
530
378
  }
531
- const nextImpl = implementerInternal(next, middlewares);
532
- if (method) {
533
- return new Proxy(method, {
534
- get(_, key2) {
535
- return Reflect.get(nextImpl, key2);
379
+ }
380
+ if (typeof schema.type === "string") {
381
+ switch (schema.type) {
382
+ case "null": {
383
+ if (coerced !== null) {
384
+ satisfied = false;
385
+ }
386
+ break;
387
+ }
388
+ case "string": {
389
+ if (typeof coerced !== "string") {
390
+ satisfied = false;
536
391
  }
537
- });
392
+ break;
393
+ }
394
+ case "number": {
395
+ if (typeof coerced === "string") {
396
+ coerced = this.#stringToNumber(coerced);
397
+ }
398
+ if (typeof coerced !== "number") {
399
+ satisfied = false;
400
+ }
401
+ break;
402
+ }
403
+ case "integer": {
404
+ if (typeof coerced === "string") {
405
+ coerced = this.#stringToInteger(coerced);
406
+ }
407
+ if (typeof coerced !== "number" || !Number.isInteger(coerced)) {
408
+ satisfied = false;
409
+ }
410
+ break;
411
+ }
412
+ case "boolean": {
413
+ if (typeof coerced === "string") {
414
+ coerced = this.#stringToBoolean(coerced);
415
+ }
416
+ if (typeof coerced !== "boolean") {
417
+ satisfied = false;
418
+ }
419
+ break;
420
+ }
421
+ case "array": {
422
+ if (Array.isArray(coerced)) {
423
+ const prefixItemSchemas = "prefixItems" in schema ? toArray(schema.prefixItems) : Array.isArray(schema.items) ? schema.items : [];
424
+ const itemSchema = Array.isArray(schema.items) ? schema.additionalItems : schema.items;
425
+ let shouldUseCoercedItems = false;
426
+ const coercedItems = coerced.map((item, i) => {
427
+ const subSchema = prefixItemSchemas[i] ?? itemSchema;
428
+ if (subSchema === void 0) {
429
+ satisfied = false;
430
+ return item;
431
+ }
432
+ const [subSatisfied, subCoerced] = this.#coerce(subSchema, item, options);
433
+ if (!subSatisfied) {
434
+ satisfied = false;
435
+ }
436
+ if (subCoerced !== item) {
437
+ shouldUseCoercedItems = true;
438
+ }
439
+ return subCoerced;
440
+ });
441
+ if (coercedItems.length < prefixItemSchemas.length) {
442
+ satisfied = false;
443
+ }
444
+ if (shouldUseCoercedItems) {
445
+ coerced = coercedItems;
446
+ }
447
+ } else {
448
+ satisfied = false;
449
+ }
450
+ break;
451
+ }
452
+ case "object": {
453
+ if (Array.isArray(coerced)) {
454
+ coerced = { ...coerced };
455
+ }
456
+ if (isObject(coerced)) {
457
+ let shouldUseCoercedItems = false;
458
+ const coercedItems = {};
459
+ const patternProperties = Object.entries(schema.patternProperties ?? {}).map(
460
+ ([key, value]) => [new RegExp(key), value]
461
+ );
462
+ for (const key in coerced) {
463
+ const value = coerced[key];
464
+ const subSchema = schema.properties?.[key] ?? patternProperties.find(([pattern]) => pattern.test(key))?.[1] ?? schema.additionalProperties;
465
+ if (value === void 0 && !schema.required?.includes(key)) {
466
+ coercedItems[key] = value;
467
+ } else if (subSchema === void 0) {
468
+ coercedItems[key] = value;
469
+ satisfied = false;
470
+ } else {
471
+ const [subSatisfied, subCoerced] = this.#coerce(subSchema, value, options);
472
+ coercedItems[key] = subCoerced;
473
+ if (!subSatisfied) {
474
+ satisfied = false;
475
+ }
476
+ if (subCoerced !== value) {
477
+ shouldUseCoercedItems = true;
478
+ }
479
+ }
480
+ }
481
+ if (schema.required?.some((key) => !Object.hasOwn(coercedItems, key))) {
482
+ satisfied = false;
483
+ }
484
+ if (shouldUseCoercedItems) {
485
+ coerced = coercedItems;
486
+ }
487
+ } else {
488
+ satisfied = false;
489
+ }
490
+ break;
491
+ }
538
492
  }
539
- return nextImpl;
540
493
  }
541
- });
542
- return impl;
543
- }
544
- function implement(contract) {
545
- const implInternal = implementerInternal(contract, []);
546
- const impl = new Proxy(implInternal, {
547
- get: (target, key) => {
548
- let method;
549
- if (key === "$context") {
550
- method = () => impl;
494
+ if ("x-native-type" in schema && typeof schema["x-native-type"] === "string") {
495
+ switch (schema["x-native-type"]) {
496
+ case JsonSchemaXNativeType.Date: {
497
+ if (typeof coerced === "string") {
498
+ coerced = this.#stringToDate(coerced);
499
+ }
500
+ if (!(coerced instanceof Date)) {
501
+ satisfied = false;
502
+ }
503
+ break;
504
+ }
505
+ case JsonSchemaXNativeType.BigInt: {
506
+ switch (typeof coerced) {
507
+ case "string":
508
+ coerced = this.#stringToBigInt(coerced);
509
+ break;
510
+ case "number":
511
+ coerced = this.#numberToBigInt(coerced);
512
+ break;
513
+ }
514
+ if (typeof coerced !== "bigint") {
515
+ satisfied = false;
516
+ }
517
+ break;
518
+ }
519
+ case JsonSchemaXNativeType.RegExp: {
520
+ if (typeof coerced === "string") {
521
+ coerced = this.#stringToRegExp(coerced);
522
+ }
523
+ if (!(coerced instanceof RegExp)) {
524
+ satisfied = false;
525
+ }
526
+ break;
527
+ }
528
+ case JsonSchemaXNativeType.Url: {
529
+ if (typeof coerced === "string") {
530
+ coerced = this.#stringToURL(coerced);
531
+ }
532
+ if (!(coerced instanceof URL)) {
533
+ satisfied = false;
534
+ }
535
+ break;
536
+ }
537
+ case JsonSchemaXNativeType.Set: {
538
+ if (Array.isArray(coerced)) {
539
+ coerced = this.#arrayToSet(coerced);
540
+ }
541
+ if (!(coerced instanceof Set)) {
542
+ satisfied = false;
543
+ }
544
+ break;
545
+ }
546
+ case JsonSchemaXNativeType.Map: {
547
+ if (Array.isArray(coerced)) {
548
+ coerced = this.#arrayToMap(coerced);
549
+ }
550
+ if (!(coerced instanceof Map)) {
551
+ satisfied = false;
552
+ }
553
+ break;
554
+ }
551
555
  }
552
- const next = Reflect.get(target, key);
553
- if (!method || !next || typeof next !== "function" && typeof next !== "object") {
554
- return method || next;
556
+ }
557
+ if (schema.allOf) {
558
+ for (const subSchema of schema.allOf) {
559
+ const [subSatisfied, subCoerced] = this.#coerce(subSchema, coerced, options);
560
+ coerced = subCoerced;
561
+ if (!subSatisfied) {
562
+ satisfied = false;
563
+ }
555
564
  }
556
- return new Proxy(method, {
557
- get(_, key2) {
558
- return Reflect.get(next, key2);
565
+ }
566
+ for (const key of ["anyOf", "oneOf"]) {
567
+ if (schema[key]) {
568
+ let bestOptions;
569
+ for (const subSchema of schema[key]) {
570
+ const [subSatisfied, subCoerced] = this.#coerce(subSchema, coerced, options);
571
+ if (subSatisfied) {
572
+ if (!bestOptions || subCoerced === coerced) {
573
+ bestOptions = { coerced: subCoerced, satisfied: subSatisfied };
574
+ }
575
+ if (subCoerced === coerced) {
576
+ break;
577
+ }
578
+ }
559
579
  }
560
- });
580
+ coerced = bestOptions ? bestOptions.coerced : coerced;
581
+ satisfied = bestOptions ? bestOptions.satisfied : false;
582
+ }
583
+ }
584
+ if (typeof schema.not !== "undefined") {
585
+ const [notSatisfied] = this.#coerce(schema.not, coerced, options);
586
+ if (notSatisfied) {
587
+ satisfied = false;
588
+ }
589
+ }
590
+ return [satisfied, coerced];
591
+ }
592
+ #stringToNumber(value) {
593
+ const num = Number.parseFloat(value);
594
+ if (Number.isNaN(num) || num !== Number(value)) {
595
+ return value;
596
+ }
597
+ return num;
598
+ }
599
+ #stringToInteger(value) {
600
+ const num = Number.parseInt(value);
601
+ if (Number.isNaN(num) || num !== Number(value)) {
602
+ return value;
603
+ }
604
+ return num;
605
+ }
606
+ #stringToBoolean(value) {
607
+ const lower = value.toLowerCase();
608
+ if (lower === "false" || lower === "off") {
609
+ return false;
610
+ }
611
+ if (lower === "true" || lower === "on") {
612
+ return true;
613
+ }
614
+ return value;
615
+ }
616
+ #stringToBigInt(value) {
617
+ return guard(() => BigInt(value)) ?? value;
618
+ }
619
+ #numberToBigInt(value) {
620
+ return guard(() => BigInt(value)) ?? value;
621
+ }
622
+ #stringToDate(value) {
623
+ const date = new Date(value);
624
+ if (Number.isNaN(date.getTime()) || !FLEXIBLE_DATE_FORMAT_REGEX.test(value)) {
625
+ return value;
626
+ }
627
+ return date;
628
+ }
629
+ #stringToRegExp(value) {
630
+ const match = value.match(/^\/(.*)\/([a-z]*)$/);
631
+ if (match) {
632
+ const [, pattern, flags] = match;
633
+ return guard(() => new RegExp(pattern, flags)) ?? value;
561
634
  }
562
- });
563
- return impl;
635
+ return value;
636
+ }
637
+ #stringToURL(value) {
638
+ return guard(() => new URL(value)) ?? value;
639
+ }
640
+ #arrayToSet(value) {
641
+ const set = new Set(value);
642
+ if (set.size !== value.length) {
643
+ return value;
644
+ }
645
+ return set;
646
+ }
647
+ #arrayToMap(value) {
648
+ if (value.some((item) => !Array.isArray(item) || item.length !== 2)) {
649
+ return value;
650
+ }
651
+ const result = new Map(value);
652
+ if (result.size !== value.length) {
653
+ return value;
654
+ }
655
+ return result;
656
+ }
564
657
  }
565
658
 
566
659
  function createAssertedLazyProcedure(lazied) {
567
- const lazyProcedure = lazy(async () => {
660
+ const lazyProcedure = lazyInternal(async () => {
568
661
  const { default: maybeProcedure } = await unlazy(lazied);
569
662
  if (!isProcedure(maybeProcedure)) {
570
663
  throw new Error(`
@@ -577,42 +670,26 @@ function createAssertedLazyProcedure(lazied) {
577
670
  }, getLazyMeta(lazied));
578
671
  return lazyProcedure;
579
672
  }
580
- function createContractedProcedure(procedure, contract) {
581
- return new Procedure({
582
- ...procedure["~orpc"],
583
- errorMap: contract["~orpc"].errorMap,
584
- route: contract["~orpc"].route,
585
- meta: contract["~orpc"].meta
586
- });
587
- }
588
673
  function call(procedure, input, ...rest) {
589
674
  const options = resolveMaybeOptionalOptions(rest);
590
675
  return createProcedureClient(procedure, options)(input, options);
591
676
  }
592
677
 
593
- function createRouterClient(router, ...rest) {
594
- const options = resolveMaybeOptionalOptions(rest);
595
- if (isProcedure(router)) {
596
- const caller = createProcedureClient(router, options);
597
- return caller;
598
- }
599
- const procedureCaller = isLazy(router) ? createProcedureClient(createAssertedLazyProcedure(router), options) : {};
600
- const recursive = new Proxy(procedureCaller, {
601
- get(target, key) {
602
- if (typeof key !== "string") {
603
- return Reflect.get(target, key);
604
- }
605
- const next = getRouter(router, [key]);
606
- if (!next) {
607
- return Reflect.get(target, key);
608
- }
609
- return createRouterClient(next, {
610
- ...rest[0],
611
- path: [...rest[0]?.path ?? [], key]
612
- });
678
+ function isSchemaIssue(issue) {
679
+ if (!isTypescriptObject(issue) || typeof issue.message !== "string") {
680
+ return false;
681
+ }
682
+ if (issue.path !== void 0) {
683
+ if (!Array.isArray(issue.path)) {
684
+ return false;
613
685
  }
614
- });
615
- return recursive;
686
+ if (!issue.path.every(
687
+ (segment) => isPropertyKey(segment) || isTypescriptObject(segment) && isPropertyKey(segment.key)
688
+ )) {
689
+ return false;
690
+ }
691
+ }
692
+ return true;
616
693
  }
617
694
 
618
- export { Builder, DecoratedProcedure, Procedure, addMiddleware, call, createAccessibleLazyRouter, createApiBuilder, createAssertedLazyProcedure, createContractedProcedure, createProcedureClient, createRouterClient, decorateMiddleware, enhanceRouter, getHiddenRouterContract, getLazyMeta, getRouter, implement, implementerInternal, isLazy, isProcedure, isStartWithMiddlewares, lazy, mergeMiddlewares, os, resolveContractProcedures, setHiddenRouterContract, traverseContractProcedures, unlazy, unlazyRouter };
695
+ export { Builder, BuilderWithMiddlewares, Contract, DecoratedProcedure, JsonSchemaXNativeType, Procedure, ProcedureBuilder, UnauthorizedError, addMiddleware, baseApi, call, createAssertedLazyProcedure, createProcedureClient, decorateMiddleware, enhanceRouter, experimental_JsonSchemaCoercer, getLazyMeta, initialSchemas, isProcedure, isSchemaIssue, lazyInternal, mergeMeta, parseEndpointDefinition, unlazy };