@temporary-name/server 1.9.3-alpha.65222302f1b71807a849530b3fe0fa0326d3c1a2 → 1.9.3-alpha.6a735c9469d995338b7d51335ea70485e18a4dca

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