@temporary-name/server 1.9.3-alpha.5537ef74244620c125352bc9356d8c0ae804f10d → 1.9.3-alpha.59a0ffc2afa5d7ac78321380832e510591ebe734

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