orpc-fn 0.6.1 → 0.8.0

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.
package/README.md CHANGED
@@ -203,7 +203,7 @@ const tool = createMastraTool(getOrder, { requireApproval: true })
203
203
  // at run time: requestContext.set('orpcContext', context)
204
204
  ```
205
205
 
206
- Options: `id`, `description`, `requireApproval`, `allowMissingInputSchema`, `onExecuteFinish`, `contextKey` (default `'orpcContext'`). Execution goes through oRPC `call`, so middleware and guards apply.
206
+ Options: `id`, `description`, `requireApproval`, `allowMissingInputSchema`, `onExecuteFinish`, `contextKey` (default `'orpcContext'`), or `context: (requestContext) => …` to read the oRPC context yourself. Mastra writes plain request-context values into run snapshots and spans, so a context holding a session belongs in a slot that serializes to nothing (better-mastra's `defineRequestContextHolder`), read through `context`. Execution goes through oRPC `call`, so middleware and guards apply.
207
207
 
208
208
  The tool takes the procedure's raw input and returns its parsed output, and `InferToolInput`/`InferToolOutput`/`InferUITools` see exactly those types. Mastra validates the input (coercing date strings) but passes the raw value on, and the procedure parses that raw value: transforms/refinements must be pure: SDK validation and procedure execution may run them more than once, always on raw input. Agent tools accept objects and unions of object shapes. Other inputs and streams are rejected. Finish-hook failures preserve execution outcomes.
209
209
 
@@ -242,7 +242,7 @@ Sanitized tool-name collisions between distinct procedures throw; identical alia
242
242
 
243
243
  ## `orpc-fn/cli`
244
244
 
245
- Uses `hanzio/cli`, which orpc-fn depends on (`hanzio` ^3.3.0). Your `defineCli` must come from the same `hanzio` copy, so keep your own `hanzio` on a matching major.
245
+ Uses `hanzio/cli`, which orpc-fn depends on (`hanzio` ^3.4.0). Your `defineCli` must come from the same `hanzio` copy, so keep your own `hanzio` on a matching major.
246
246
 
247
247
  ```ts
248
248
  import { defineCli } from 'hanzio/cli'
@@ -288,6 +288,7 @@ Behaviour:
288
288
  - The response gets a `Server-Timing` header built from the request timing collector: app-recorded queue/auth/context metrics plus library-owned procedure time. Nested calls do not overwrite parent timing; batches aggregate their top-level procedure durations (which may overlap). `fn` does not mutate an arbitrary `context.timing` field. `formatServerTiming` and `finishOrpcHeaders` are available for composed Fetch adapters.
289
289
  - SSE responses get keep-alive comments (15 s) and anti-buffering headers.
290
290
  - RPC calls are batched (`BatchHandlerPlugin`).
291
+ - JSON and batch responses are gzipped when the client accepts it (`compress: false` turns it off). A streaming batch sends each result as it finishes, so it is flushed after every result instead of buffered; Hono's `compress()` skips event streams entirely. Live event streams are never compressed. A response that already has a `content-encoding` is left alone.
291
292
  - Scalar docs are served at the OpenAPI prefix.
292
293
  - `onError` sees only unexpected errors.
293
294
  - `openapi.filter`, `openapi.smartCoercion` and `openapi.spec` (for security schemes) cover an external API with its own auth.
package/dist/hono.d.ts CHANGED
@@ -51,6 +51,12 @@ export type MountOrpcOptions<E extends Env, R extends AnyRouter> = ContextOption
51
51
  };
52
52
  /** Add a Server-Timing header from the request timing. Default: true */
53
53
  serverTiming?: boolean;
54
+ /**
55
+ * gzip JSON responses and batch event streams for clients that accept it,
56
+ * flushing after every batch result. Other event streams are left alone.
57
+ * Default: true
58
+ */
59
+ compress?: boolean;
54
60
  /** SSE keep-alive comments every N ms (default 15000), or false. */
55
61
  sseKeepAlive?: boolean | number;
56
62
  /** Extra RPC plugins (e.g. `CORSPlugin`). */
@@ -1 +1 @@
1
- {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"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"}
package/dist/hono.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { Readable, pipeline } from 'node:stream';
2
+ import zlib from 'node:zlib';
1
3
  import { SmartCoercionPlugin } from '@orpc/json-schema';
2
4
  import { OpenAPIHandler } from '@orpc/openapi/fetch';
3
5
  import { OpenAPIReferencePlugin } from '@orpc/openapi/plugins';
@@ -60,9 +62,40 @@ export function finishOrpcHeaders(response, serverTiming) {
60
62
  }
61
63
  return headers;
62
64
  }
65
+ const NO_TRANSFORM = /(?:^|,)\s*no-transform\s*(?:,|$)/i;
66
+ /**
67
+ * JSON, and the event stream oRPC answers a streaming batch with (one event
68
+ * per result). Other event streams are live: they stay open and carry little.
69
+ */
70
+ function shouldCompress(request, response) {
71
+ if (request.method === 'HEAD')
72
+ return false;
73
+ if (!request.headers.get('accept-encoding')?.includes('gzip'))
74
+ return false;
75
+ if (response.headers.has('content-encoding'))
76
+ return false;
77
+ if (NO_TRANSFORM.test(response.headers.get('cache-control') ?? '')) {
78
+ return false;
79
+ }
80
+ const type = response.headers.get('content-type') ?? '';
81
+ if (type.startsWith('text/event-stream')) {
82
+ // BatchHandlerPlugin keys on this header too, whatever the URL.
83
+ return request.headers.has('x-orpc-batch');
84
+ }
85
+ return type.startsWith('application/json');
86
+ }
87
+ /**
88
+ * gzip that emits every chunk at once, so a fast batch result is not held
89
+ * back by a slow one. Cancelling the result cancels `body`.
90
+ */
91
+ function gzipFlushingEachChunk(body) {
92
+ const gzip = zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH });
93
+ pipeline(Readable.fromWeb(body), gzip, () => { });
94
+ return Readable.toWeb(gzip);
95
+ }
63
96
  /** Mount oRPC's RPC and OpenAPI handlers on a Hono app. */
64
97
  export function mountOrpc(app, options) {
65
- const { router, batch = true, serverTiming = true, sseKeepAlive = true } = options;
98
+ const { router, batch = true, serverTiming = true, sseKeepAlive = true, compress = true } = options;
66
99
  const isExpectedError = options.isExpectedError ?? isExpectedClientError;
67
100
  const keepAlive = sseKeepAlive === false
68
101
  ? { eventIteratorKeepAliveEnabled: false }
@@ -115,10 +148,18 @@ export function mountOrpc(app, options) {
115
148
  handler_ms: invocationTiming.procedureMs
116
149
  })
117
150
  : undefined;
118
- return c.newResponse(response.body, {
151
+ const responseHeaders = finishOrpcHeaders(response, timed);
152
+ let body = response.body;
153
+ if (body && compress && shouldCompress(c.req.raw, response)) {
154
+ body = gzipFlushingEachChunk(body);
155
+ responseHeaders.set('content-encoding', 'gzip');
156
+ responseHeaders.delete('content-length');
157
+ responseHeaders.append('vary', 'accept-encoding');
158
+ }
159
+ return c.newResponse(body, {
119
160
  // oRPC only produces valid HTTP status codes.
120
161
  status: response.status,
121
- headers: finishOrpcHeaders(response, timed)
162
+ headers: responseHeaders
122
163
  });
123
164
  });
124
165
  };
package/dist/hono.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hono.js","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { SmartCoercionPlugin } from '@orpc/json-schema'\nimport type { OpenAPIGeneratorGenerateOptions } from '@orpc/openapi'\nimport { OpenAPIHandler } from '@orpc/openapi/fetch'\nimport { OpenAPIReferencePlugin } from '@orpc/openapi/plugins'\nimport {\n\ttype AnyRouter,\n\ttype InferRouterInitialContext,\n\tonError as orpcOnError\n} from '@orpc/server'\nimport { RPCHandler } from '@orpc/server/fetch'\nimport { BatchHandlerPlugin } from '@orpc/server/plugins'\nimport type { StandardHandlerPlugin } from '@orpc/server/standard'\nimport { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'\nimport type { Context as HonoContext, Env, Hono } from 'hono'\nimport type { StatusCode } from 'hono/utils/http-status'\nimport { REQUEST_TIMING, type InvocationTiming } from './timing.js'\nimport { isExpectedClientError } from './expected-client-error.js'\nimport type { ProcedureFilter } from './mcp.js'\n\n/** Per-request timings in ms; `fn()` adds `handler_ms`. Sent as Server-Timing. */\nexport type RequestTiming = Record<string, number>\n\n/** What every request starts with; the default context when `context` is omitted. */\nexport type BaseContext = { headers: Headers; timing: RequestTiming }\n\n/**\n * Builds the router's initial context from the request. Return a `Response`\n * to answer (e.g. 401) instead.\n */\nexport type ContextFactory<E extends Env, R extends AnyRouter> = (\n\tc: HonoContext<E>,\n\tbase: BaseContext\n) =>\n\t| InferRouterInitialContext<R>\n\t| Response\n\t| Promise<InferRouterInitialContext<R> | Response>\n\n/** `context` is optional only when the base context already satisfies the router. */\ntype ContextOption<E extends Env, R extends AnyRouter> =\n\tBaseContext extends InferRouterInitialContext<R>\n\t\t? { context?: ContextFactory<E, R> }\n\t\t: { context: ContextFactory<E, R> }\n\ntype Path = `/${string}`\ntype Plugin<T extends object> = StandardHandlerPlugin<T>\n\nexport type MountOrpcOptions<\n\tE extends Env,\n\tR extends AnyRouter\n> = ContextOption<E, R> & {\n\trouter: R\n\t/** Mount the RPC handler (oRPC's `RPCLink`) here. Omit to skip it. */\n\trpcPrefix?: Path\n\t/** Mount the OpenAPI (REST) handler and its Scalar docs here. */\n\topenapi?: {\n\t\tprefix: Path\n\t\tinfo?: { title: string; version: string; description?: string }\n\t\t/** Docs page, relative to `prefix` (default '/'); the spec is at `/spec.json`. */\n\t\tdocsPath?: Path\n\t\t/** Only expose matching procedures (also filters the spec). */\n\t\tfilter?: ProcedureFilter\n\t\t/** Turn JSON strings back into `Date`s etc. for `z.date()` inputs. */\n\t\tsmartCoercion?: boolean\n\t\t/** Extra spec fields, e.g. `components.securitySchemes` and `security`. */\n\t\tspec?: Omit<OpenAPIGeneratorGenerateOptions, 'filter'>\n\t\tplugins?: Plugin<InferRouterInitialContext<R>>[]\n\t}\n\t/** Collapse parallel RPC calls into one HTTP request. Default: true (25 calls). */\n\tbatch?: boolean | { maxSize: number }\n\t/** Add a Server-Timing header from the request timing. Default: true */\n\tserverTiming?: boolean\n\t/** SSE keep-alive comments every N ms (default 15000), or false. */\n\tsseKeepAlive?: boolean | number\n\t/** Extra RPC plugins (e.g. `CORSPlugin`). */\n\tplugins?: Plugin<InferRouterInitialContext<R>>[]\n\t/** Rewrite request headers before they reach the context (see `normalizeExpoOrigin`). */\n\tnormalizeHeaders?: (headers: Headers) => Headers\n\t/**\n\t * Time spent before the handler ran, reported as `queue`. Default: time\n\t * since {@link markRequestArrival}, when the server marked the request.\n\t */\n\tqueueMs?: (request: Request) => number | undefined\n\t/**\n\t * Unexpected procedure errors (oRPC turns thrown errors into responses, so\n\t * Hono's `onError` never sees them). Expected client errors are skipped.\n\t */\n\tonError?: (error: unknown, request: { url: string; method: string }) => void\n\t/** Default: 4xx `ORPCError` or `AbortError`. */\n\tisExpectedError?: (error: unknown) => boolean\n}\n\nconst arrivals = new WeakMap<Request, number>()\n\n/**\n * Marks a request at the server's front door (`Bun.serve({ fetch })`), before\n * routing and middleware. `mountOrpc` reports the time until the procedure\n * runs as `queue_ms`: in-process queueing no handler timer can see.\n */\nexport function markRequestArrival(request: Request): void {\n\tarrivals.set(request, performance.now())\n}\n\n/** Milliseconds since {@link markRequestArrival}, or undefined when unmarked. */\nexport function msSinceRequestArrival(request: Request): number | undefined {\n\tconst arrivedAt = arrivals.get(request)\n\treturn arrivedAt === undefined ? undefined : performance.now() - arrivedAt\n}\n\n/**\n * Expo native sends `expo-origin` but not `origin`; copy it so cookie-based\n * auth (e.g. Better Auth) sees the origin.\n */\nexport function normalizeExpoOrigin(raw: Headers): Headers {\n\tconst expo = raw.get('expo-origin')\n\tif (!expo || raw.get('origin')) return raw\n\tconst headers = new Headers(raw)\n\theaders.set('origin', expo)\n\treturn headers\n}\n\nexport function formatServerTiming(\n\ttotalMs: number,\n\ttiming: Record<string, number | undefined>\n): string {\n\tconst parts = [`total;dur=${totalMs.toFixed(2)}`]\n\tfor (const [key, value] of Object.entries(timing)) {\n\t\tif (value !== undefined) {\n\t\t\tparts.push(`${key.replace(/_ms$/, '')};dur=${value.toFixed(2)}`)\n\t\t}\n\t}\n\treturn parts.join(', ')\n}\n\n/**\n * Response headers plus Server-Timing. For SSE, also disable proxy buffering\n * so keep-alive comments reach the edge (empty `:` lines are otherwise easy\n * for nginx/Fastly to hold until an idle kill). A copy: some responses (e.g.\n * redirects) have immutable headers.\n */\nexport function finishOrpcHeaders(\n\tresponse: Response,\n\tserverTiming: string | undefined\n): Headers {\n\tconst headers = new Headers(response.headers)\n\tif (serverTiming) {\n\t\theaders.set('Server-Timing', serverTiming)\n\t\theaders.append('Access-Control-Expose-Headers', 'Server-Timing')\n\t}\n\tif ((headers.get('content-type') ?? '').includes('text/event-stream')) {\n\t\theaders.set('Cache-Control', 'no-cache')\n\t\theaders.set('X-Accel-Buffering', 'no')\n\t}\n\treturn headers\n}\n\n/** Mount oRPC's RPC and OpenAPI handlers on a Hono app. */\nexport function mountOrpc<E extends Env, R extends AnyRouter>(\n\tapp: Hono<E>,\n\toptions: MountOrpcOptions<E, R>\n): void {\n\tconst {\n\t\trouter,\n\t\tbatch = true,\n\t\tserverTiming = true,\n\t\tsseKeepAlive = true\n\t} = options\n\tconst isExpectedError = options.isExpectedError ?? isExpectedClientError\n\tconst keepAlive =\n\t\tsseKeepAlive === false\n\t\t\t? { eventIteratorKeepAliveEnabled: false }\n\t\t\t: {\n\t\t\t\t\teventIteratorKeepAliveEnabled: true,\n\t\t\t\t\t// Explicit keep-alive under proxy idle timers (~30s); oRPC's default is 5s.\n\t\t\t\t\teventIteratorKeepAliveInterval:\n\t\t\t\t\t\tsseKeepAlive === true ? 15_000 : sseKeepAlive\n\t\t\t\t}\n\tconst report = (\n\t\terror: unknown,\n\t\t{ request }: { request: { url: URL; method: string } }\n\t) => {\n\t\ttry {\n\t\t\tif (isExpectedError(error)) return\n\t\t\toptions.onError?.(error, {\n\t\t\t\turl: request.url.pathname,\n\t\t\t\tmethod: request.method\n\t\t\t})\n\t\t} catch {\n\t\t\t/* Error reporting cannot replace transport outcomes. */\n\t\t}\n\t}\n\n\tconst mount = (\n\t\tprefix: Path,\n\t\thandler: Pick<RPCHandler<InferRouterInitialContext<R>>, 'handle'>\n\t) => {\n\t\tapp.use(`${prefix}/*`, async (c, next) => {\n\t\t\tconst timing: RequestTiming = {}\n\t\t\tconst queueMs = (options.queueMs ?? msSinceRequestArrival)(c.req.raw)\n\t\t\tif (queueMs !== undefined)\n\t\t\t\ttiming.queue_ms = Math.round(queueMs * 100) / 100\n\t\t\tconst started = performance.now()\n\t\t\tconst headers = options.normalizeHeaders\n\t\t\t\t? options.normalizeHeaders(c.req.raw.headers)\n\t\t\t\t: c.req.raw.headers\n\t\t\tconst context = options.context\n\t\t\t\t? await options.context(c, { headers, timing })\n\t\t\t\t: { headers, timing }\n\t\t\tif (context instanceof Response) return context\n\t\t\tconst invocationTiming: InvocationTiming = { procedureMs: 0, calls: 0 }\n\t\t\tconst { matched, response } = await handler.handle(c.req.raw, {\n\t\t\t\tprefix,\n\t\t\t\tcontext: {\n\t\t\t\t\t...context,\n\t\t\t\t\t[REQUEST_TIMING]: invocationTiming\n\t\t\t\t} as InferRouterInitialContext<R>\n\t\t\t})\n\t\t\tif (!matched) return next()\n\t\t\tconst timed = serverTiming\n\t\t\t\t? formatServerTiming(performance.now() - started, {\n\t\t\t\t\t\t...timing,\n\t\t\t\t\t\thandler_ms: invocationTiming.procedureMs\n\t\t\t\t\t})\n\t\t\t\t: undefined\n\t\t\treturn c.newResponse(response.body, {\n\t\t\t\t// oRPC only produces valid HTTP status codes.\n\t\t\t\tstatus: response.status as StatusCode,\n\t\t\t\theaders: finishOrpcHeaders(response, timed)\n\t\t\t})\n\t\t})\n\t}\n\n\tif (options.rpcPrefix) {\n\t\tconst plugins: Plugin<InferRouterInitialContext<R>>[] = [\n\t\t\t...(options.plugins ?? [])\n\t\t]\n\t\tif (batch) {\n\t\t\tplugins.push(\n\t\t\t\tnew BatchHandlerPlugin({\n\t\t\t\t\tmaxSize: batch === true ? 25 : batch.maxSize\n\t\t\t\t})\n\t\t\t)\n\t\t}\n\t\tmount(\n\t\t\toptions.rpcPrefix,\n\t\t\tnew RPCHandler(router, {\n\t\t\t\t...keepAlive,\n\t\t\t\tplugins,\n\t\t\t\tinterceptors: options.onError ? [orpcOnError(report)] : []\n\t\t\t})\n\t\t)\n\t}\n\n\tif (options.openapi) {\n\t\tconst { openapi } = options\n\t\tconst filter = openapi.filter ? { filter: openapi.filter } : {}\n\t\tconst converters = [new ZodToJsonSchemaConverter()]\n\t\tconst plugins: Plugin<InferRouterInitialContext<R>>[] = [\n\t\t\t...(openapi.plugins ?? [])\n\t\t]\n\t\tif (openapi.smartCoercion) {\n\t\t\tplugins.push(new SmartCoercionPlugin({ schemaConverters: converters }))\n\t\t}\n\t\tplugins.push(\n\t\t\tnew OpenAPIReferencePlugin({\n\t\t\t\tdocsProvider: 'scalar',\n\t\t\t\t...(openapi.docsPath ? { docsPath: openapi.docsPath } : {}),\n\t\t\t\tschemaConverters: converters,\n\t\t\t\tspecGenerateOptions: {\n\t\t\t\t\tinfo: openapi.info ?? { title: 'API', version: '0.0.0' },\n\t\t\t\t\tservers: [{ url: openapi.prefix }],\n\t\t\t\t\t...filter,\n\t\t\t\t\t...openapi.spec\n\t\t\t\t}\n\t\t\t})\n\t\t)\n\t\tmount(\n\t\t\topenapi.prefix,\n\t\t\tnew OpenAPIHandler(router, {\n\t\t\t\t...keepAlive,\n\t\t\t\t...filter,\n\t\t\t\tplugins,\n\t\t\t\tinterceptors: options.onError ? [orpcOnError(report)] : []\n\t\t\t})\n\t\t)\n\t}\n}\n"]}
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { Readable, pipeline } from 'node:stream'\nimport zlib from 'node:zlib'\nimport { SmartCoercionPlugin } from '@orpc/json-schema'\nimport type { OpenAPIGeneratorGenerateOptions } from '@orpc/openapi'\nimport { OpenAPIHandler } from '@orpc/openapi/fetch'\nimport { OpenAPIReferencePlugin } from '@orpc/openapi/plugins'\nimport {\n\ttype AnyRouter,\n\ttype InferRouterInitialContext,\n\tonError as orpcOnError\n} from '@orpc/server'\nimport { RPCHandler } from '@orpc/server/fetch'\nimport { BatchHandlerPlugin } from '@orpc/server/plugins'\nimport type { StandardHandlerPlugin } from '@orpc/server/standard'\nimport { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'\nimport type { Context as HonoContext, Env, Hono } from 'hono'\nimport type { StatusCode } from 'hono/utils/http-status'\nimport { REQUEST_TIMING, type InvocationTiming } from './timing.js'\nimport { isExpectedClientError } from './expected-client-error.js'\nimport type { ProcedureFilter } from './mcp.js'\n\n/** Per-request timings in ms; `fn()` adds `handler_ms`. Sent as Server-Timing. */\nexport type RequestTiming = Record<string, number>\n\n/** What every request starts with; the default context when `context` is omitted. */\nexport type BaseContext = { headers: Headers; timing: RequestTiming }\n\n/**\n * Builds the router's initial context from the request. Return a `Response`\n * to answer (e.g. 401) instead.\n */\nexport type ContextFactory<E extends Env, R extends AnyRouter> = (\n\tc: HonoContext<E>,\n\tbase: BaseContext\n) =>\n\t| InferRouterInitialContext<R>\n\t| Response\n\t| Promise<InferRouterInitialContext<R> | Response>\n\n/** `context` is optional only when the base context already satisfies the router. */\ntype ContextOption<E extends Env, R extends AnyRouter> =\n\tBaseContext extends InferRouterInitialContext<R>\n\t\t? { context?: ContextFactory<E, R> }\n\t\t: { context: ContextFactory<E, R> }\n\ntype Path = `/${string}`\ntype Plugin<T extends object> = StandardHandlerPlugin<T>\n\nexport type MountOrpcOptions<\n\tE extends Env,\n\tR extends AnyRouter\n> = ContextOption<E, R> & {\n\trouter: R\n\t/** Mount the RPC handler (oRPC's `RPCLink`) here. Omit to skip it. */\n\trpcPrefix?: Path\n\t/** Mount the OpenAPI (REST) handler and its Scalar docs here. */\n\topenapi?: {\n\t\tprefix: Path\n\t\tinfo?: { title: string; version: string; description?: string }\n\t\t/** Docs page, relative to `prefix` (default '/'); the spec is at `/spec.json`. */\n\t\tdocsPath?: Path\n\t\t/** Only expose matching procedures (also filters the spec). */\n\t\tfilter?: ProcedureFilter\n\t\t/** Turn JSON strings back into `Date`s etc. for `z.date()` inputs. */\n\t\tsmartCoercion?: boolean\n\t\t/** Extra spec fields, e.g. `components.securitySchemes` and `security`. */\n\t\tspec?: Omit<OpenAPIGeneratorGenerateOptions, 'filter'>\n\t\tplugins?: Plugin<InferRouterInitialContext<R>>[]\n\t}\n\t/** Collapse parallel RPC calls into one HTTP request. Default: true (25 calls). */\n\tbatch?: boolean | { maxSize: number }\n\t/** Add a Server-Timing header from the request timing. Default: true */\n\tserverTiming?: boolean\n\t/**\n\t * gzip JSON responses and batch event streams for clients that accept it,\n\t * flushing after every batch result. Other event streams are left alone.\n\t * Default: true\n\t */\n\tcompress?: boolean\n\t/** SSE keep-alive comments every N ms (default 15000), or false. */\n\tsseKeepAlive?: boolean | number\n\t/** Extra RPC plugins (e.g. `CORSPlugin`). */\n\tplugins?: Plugin<InferRouterInitialContext<R>>[]\n\t/** Rewrite request headers before they reach the context (see `normalizeExpoOrigin`). */\n\tnormalizeHeaders?: (headers: Headers) => Headers\n\t/**\n\t * Time spent before the handler ran, reported as `queue`. Default: time\n\t * since {@link markRequestArrival}, when the server marked the request.\n\t */\n\tqueueMs?: (request: Request) => number | undefined\n\t/**\n\t * Unexpected procedure errors (oRPC turns thrown errors into responses, so\n\t * Hono's `onError` never sees them). Expected client errors are skipped.\n\t */\n\tonError?: (error: unknown, request: { url: string; method: string }) => void\n\t/** Default: 4xx `ORPCError` or `AbortError`. */\n\tisExpectedError?: (error: unknown) => boolean\n}\n\nconst arrivals = new WeakMap<Request, number>()\n\n/**\n * Marks a request at the server's front door (`Bun.serve({ fetch })`), before\n * routing and middleware. `mountOrpc` reports the time until the procedure\n * runs as `queue_ms`: in-process queueing no handler timer can see.\n */\nexport function markRequestArrival(request: Request): void {\n\tarrivals.set(request, performance.now())\n}\n\n/** Milliseconds since {@link markRequestArrival}, or undefined when unmarked. */\nexport function msSinceRequestArrival(request: Request): number | undefined {\n\tconst arrivedAt = arrivals.get(request)\n\treturn arrivedAt === undefined ? undefined : performance.now() - arrivedAt\n}\n\n/**\n * Expo native sends `expo-origin` but not `origin`; copy it so cookie-based\n * auth (e.g. Better Auth) sees the origin.\n */\nexport function normalizeExpoOrigin(raw: Headers): Headers {\n\tconst expo = raw.get('expo-origin')\n\tif (!expo || raw.get('origin')) return raw\n\tconst headers = new Headers(raw)\n\theaders.set('origin', expo)\n\treturn headers\n}\n\nexport function formatServerTiming(\n\ttotalMs: number,\n\ttiming: Record<string, number | undefined>\n): string {\n\tconst parts = [`total;dur=${totalMs.toFixed(2)}`]\n\tfor (const [key, value] of Object.entries(timing)) {\n\t\tif (value !== undefined) {\n\t\t\tparts.push(`${key.replace(/_ms$/, '')};dur=${value.toFixed(2)}`)\n\t\t}\n\t}\n\treturn parts.join(', ')\n}\n\n/**\n * Response headers plus Server-Timing. For SSE, also disable proxy buffering\n * so keep-alive comments reach the edge (empty `:` lines are otherwise easy\n * for nginx/Fastly to hold until an idle kill). A copy: some responses (e.g.\n * redirects) have immutable headers.\n */\nexport function finishOrpcHeaders(\n\tresponse: Response,\n\tserverTiming: string | undefined\n): Headers {\n\tconst headers = new Headers(response.headers)\n\tif (serverTiming) {\n\t\theaders.set('Server-Timing', serverTiming)\n\t\theaders.append('Access-Control-Expose-Headers', 'Server-Timing')\n\t}\n\tif ((headers.get('content-type') ?? '').includes('text/event-stream')) {\n\t\theaders.set('Cache-Control', 'no-cache')\n\t\theaders.set('X-Accel-Buffering', 'no')\n\t}\n\treturn headers\n}\n\nconst NO_TRANSFORM = /(?:^|,)\\s*no-transform\\s*(?:,|$)/i\n\n/**\n * JSON, and the event stream oRPC answers a streaming batch with (one event\n * per result). Other event streams are live: they stay open and carry little.\n */\nfunction shouldCompress(request: Request, response: Response): boolean {\n\tif (request.method === 'HEAD') return false\n\tif (!request.headers.get('accept-encoding')?.includes('gzip')) return false\n\tif (response.headers.has('content-encoding')) return false\n\tif (NO_TRANSFORM.test(response.headers.get('cache-control') ?? '')) {\n\t\treturn false\n\t}\n\tconst type = response.headers.get('content-type') ?? ''\n\tif (type.startsWith('text/event-stream')) {\n\t\t// BatchHandlerPlugin keys on this header too, whatever the URL.\n\t\treturn request.headers.has('x-orpc-batch')\n\t}\n\treturn type.startsWith('application/json')\n}\n\n/**\n * gzip that emits every chunk at once, so a fast batch result is not held\n * back by a slow one. Cancelling the result cancels `body`.\n */\nfunction gzipFlushingEachChunk(\n\tbody: ReadableStream<Uint8Array>\n): ReadableStream<Uint8Array> {\n\tconst gzip = zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH })\n\tpipeline(Readable.fromWeb(body as never), gzip, () => {})\n\treturn Readable.toWeb(gzip) as unknown as ReadableStream<Uint8Array>\n}\n\n/** Mount oRPC's RPC and OpenAPI handlers on a Hono app. */\nexport function mountOrpc<E extends Env, R extends AnyRouter>(\n\tapp: Hono<E>,\n\toptions: MountOrpcOptions<E, R>\n): void {\n\tconst {\n\t\trouter,\n\t\tbatch = true,\n\t\tserverTiming = true,\n\t\tsseKeepAlive = true,\n\t\tcompress = true\n\t} = options\n\tconst isExpectedError = options.isExpectedError ?? isExpectedClientError\n\tconst keepAlive =\n\t\tsseKeepAlive === false\n\t\t\t? { eventIteratorKeepAliveEnabled: false }\n\t\t\t: {\n\t\t\t\t\teventIteratorKeepAliveEnabled: true,\n\t\t\t\t\t// Explicit keep-alive under proxy idle timers (~30s); oRPC's default is 5s.\n\t\t\t\t\teventIteratorKeepAliveInterval:\n\t\t\t\t\t\tsseKeepAlive === true ? 15_000 : sseKeepAlive\n\t\t\t\t}\n\tconst report = (\n\t\terror: unknown,\n\t\t{ request }: { request: { url: URL; method: string } }\n\t) => {\n\t\ttry {\n\t\t\tif (isExpectedError(error)) return\n\t\t\toptions.onError?.(error, {\n\t\t\t\turl: request.url.pathname,\n\t\t\t\tmethod: request.method\n\t\t\t})\n\t\t} catch {\n\t\t\t/* Error reporting cannot replace transport outcomes. */\n\t\t}\n\t}\n\n\tconst mount = (\n\t\tprefix: Path,\n\t\thandler: Pick<RPCHandler<InferRouterInitialContext<R>>, 'handle'>\n\t) => {\n\t\tapp.use(`${prefix}/*`, async (c, next) => {\n\t\t\tconst timing: RequestTiming = {}\n\t\t\tconst queueMs = (options.queueMs ?? msSinceRequestArrival)(c.req.raw)\n\t\t\tif (queueMs !== undefined)\n\t\t\t\ttiming.queue_ms = Math.round(queueMs * 100) / 100\n\t\t\tconst started = performance.now()\n\t\t\tconst headers = options.normalizeHeaders\n\t\t\t\t? options.normalizeHeaders(c.req.raw.headers)\n\t\t\t\t: c.req.raw.headers\n\t\t\tconst context = options.context\n\t\t\t\t? await options.context(c, { headers, timing })\n\t\t\t\t: { headers, timing }\n\t\t\tif (context instanceof Response) return context\n\t\t\tconst invocationTiming: InvocationTiming = { procedureMs: 0, calls: 0 }\n\t\t\tconst { matched, response } = await handler.handle(c.req.raw, {\n\t\t\t\tprefix,\n\t\t\t\tcontext: {\n\t\t\t\t\t...context,\n\t\t\t\t\t[REQUEST_TIMING]: invocationTiming\n\t\t\t\t} as InferRouterInitialContext<R>\n\t\t\t})\n\t\t\tif (!matched) return next()\n\t\t\tconst timed = serverTiming\n\t\t\t\t? formatServerTiming(performance.now() - started, {\n\t\t\t\t\t\t...timing,\n\t\t\t\t\t\thandler_ms: invocationTiming.procedureMs\n\t\t\t\t\t})\n\t\t\t\t: undefined\n\t\t\tconst responseHeaders = finishOrpcHeaders(response, timed)\n\t\t\tlet body = response.body\n\t\t\tif (body && compress && shouldCompress(c.req.raw, response)) {\n\t\t\t\tbody = gzipFlushingEachChunk(body)\n\t\t\t\tresponseHeaders.set('content-encoding', 'gzip')\n\t\t\t\tresponseHeaders.delete('content-length')\n\t\t\t\tresponseHeaders.append('vary', 'accept-encoding')\n\t\t\t}\n\t\t\treturn c.newResponse(body, {\n\t\t\t\t// oRPC only produces valid HTTP status codes.\n\t\t\t\tstatus: response.status as StatusCode,\n\t\t\t\theaders: responseHeaders\n\t\t\t})\n\t\t})\n\t}\n\n\tif (options.rpcPrefix) {\n\t\tconst plugins: Plugin<InferRouterInitialContext<R>>[] = [\n\t\t\t...(options.plugins ?? [])\n\t\t]\n\t\tif (batch) {\n\t\t\tplugins.push(\n\t\t\t\tnew BatchHandlerPlugin({\n\t\t\t\t\tmaxSize: batch === true ? 25 : batch.maxSize\n\t\t\t\t})\n\t\t\t)\n\t\t}\n\t\tmount(\n\t\t\toptions.rpcPrefix,\n\t\t\tnew RPCHandler(router, {\n\t\t\t\t...keepAlive,\n\t\t\t\tplugins,\n\t\t\t\tinterceptors: options.onError ? [orpcOnError(report)] : []\n\t\t\t})\n\t\t)\n\t}\n\n\tif (options.openapi) {\n\t\tconst { openapi } = options\n\t\tconst filter = openapi.filter ? { filter: openapi.filter } : {}\n\t\tconst converters = [new ZodToJsonSchemaConverter()]\n\t\tconst plugins: Plugin<InferRouterInitialContext<R>>[] = [\n\t\t\t...(openapi.plugins ?? [])\n\t\t]\n\t\tif (openapi.smartCoercion) {\n\t\t\tplugins.push(new SmartCoercionPlugin({ schemaConverters: converters }))\n\t\t}\n\t\tplugins.push(\n\t\t\tnew OpenAPIReferencePlugin({\n\t\t\t\tdocsProvider: 'scalar',\n\t\t\t\t...(openapi.docsPath ? { docsPath: openapi.docsPath } : {}),\n\t\t\t\tschemaConverters: converters,\n\t\t\t\tspecGenerateOptions: {\n\t\t\t\t\tinfo: openapi.info ?? { title: 'API', version: '0.0.0' },\n\t\t\t\t\tservers: [{ url: openapi.prefix }],\n\t\t\t\t\t...filter,\n\t\t\t\t\t...openapi.spec\n\t\t\t\t}\n\t\t\t})\n\t\t)\n\t\tmount(\n\t\t\topenapi.prefix,\n\t\t\tnew OpenAPIHandler(router, {\n\t\t\t\t...keepAlive,\n\t\t\t\t...filter,\n\t\t\t\tplugins,\n\t\t\t\tinterceptors: options.onError ? [orpcOnError(report)] : []\n\t\t\t})\n\t\t)\n\t}\n}\n"]}
package/dist/mastra.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { RequestContext } from '@mastra/core/request-context';
1
2
  import { type Tool } from '@mastra/core/tools';
2
3
  import { type AnyProcedure } from '@orpc/server';
3
4
  import { toToolName } from './meta.js';
@@ -22,6 +23,13 @@ export type CreateMastraToolOptions = {
22
23
  }) => void;
23
24
  /** Key holding the oRPC context in Mastra's `RequestContext`. Default 'orpcContext'. */
24
25
  contextKey?: string;
26
+ /**
27
+ * Reads the oRPC context from the run's request context, in place of
28
+ * `contextKey`. Mastra writes plain request-context values into run
29
+ * snapshots and spans, so an app whose context holds a session keeps it
30
+ * in a slot that serializes to nothing and reads it here.
31
+ */
32
+ context?: (requestContext: RequestContext) => unknown;
25
33
  };
26
34
  /** What the tool takes: the procedure's raw input, or `{}` without a schema. */
27
35
  export type MastraToolInput<TProc> = unknown extends ProcedureInput<TProc> ? Record<string, never> : ProcedureInput<TProc>;
@@ -1 +1 @@
1
- {"version":3,"file":"mastra.d.ts","sourceRoot":"","sources":["../src/mastra.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,cAAc,CAAA;AAOtD,OAAO,EAAc,UAAU,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjE,OAAO,EAAE,UAAU,EAAE,CAAA;AAErB,MAAM,MAAM,uBAAuB,GAAG;IACrC,kEAAkE;IAClE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QACzB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,MAAM,CAAA;QAClB,OAAO,EAAE,SAAS,GAAG,OAAO,CAAA;KAC5B,KAAK,IAAI,CAAA;IACV,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,CAAC,KAAK,IAChC,OAAO,SAAS,cAAc,CAAC,KAAK,CAAC,GAClC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACrB,cAAc,CAAC,KAAK,CAAC,CAAA;AAEzB,0EAA0E;AAC1E,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,YAAY,IAAI,IAAI,CACxD,eAAe,CAAC,KAAK,CAAC,EACtB,eAAe,CAAC,KAAK,CAAC,CACtB,CAAA;AAED,4EAA4E;AAC5E,KAAK,gBAAgB,CAAC,KAAK,IAC1B,eAAe,CAAC,KAAK,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnD,OAAO,GACP;IAAE,8CAA8C,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,CAAA;AAE7E;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,YAAY,EAC1D,SAAS,EAAE,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,EAC1C,OAAO,GAAE,uBAA4B,GACnC,UAAU,CAAC,KAAK,CAAC,CAkFnB"}
1
+ {"version":3,"file":"mastra.d.ts","sourceRoot":"","sources":["../src/mastra.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,cAAc,CAAA;AAOtD,OAAO,EAAc,UAAU,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjE,OAAO,EAAE,UAAU,EAAE,CAAA;AAErB,MAAM,MAAM,uBAAuB,GAAG;IACrC,kEAAkE;IAClE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QACzB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,MAAM,CAAA;QAClB,OAAO,EAAE,SAAS,GAAG,OAAO,CAAA;KAC5B,KAAK,IAAI,CAAA;IACV,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,cAAc,KAAK,OAAO,CAAA;CACrD,CAAA;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,CAAC,KAAK,IAChC,OAAO,SAAS,cAAc,CAAC,KAAK,CAAC,GAClC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACrB,cAAc,CAAC,KAAK,CAAC,CAAA;AAEzB,0EAA0E;AAC1E,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,YAAY,IAAI,IAAI,CACxD,eAAe,CAAC,KAAK,CAAC,EACtB,eAAe,CAAC,KAAK,CAAC,CACtB,CAAA;AAED,4EAA4E;AAC5E,KAAK,gBAAgB,CAAC,KAAK,IAC1B,eAAe,CAAC,KAAK,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnD,OAAO,GACP;IAAE,8CAA8C,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,CAAA;AAE7E;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,YAAY,EAC1D,SAAS,EAAE,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,EAC1C,OAAO,GAAE,uBAA4B,GACnC,UAAU,CAAC,KAAK,CAAC,CAwFnB"}
package/dist/mastra.js CHANGED
@@ -53,9 +53,14 @@ export function createMastraTool(procedure, options = {}) {
53
53
  : {}),
54
54
  execute: async (input, ctx) => {
55
55
  const requestContext = ctx?.requestContext;
56
- const context = requestContext?.get(contextKey);
56
+ const context = requestContext &&
57
+ (options.context
58
+ ? options.context(requestContext)
59
+ : requestContext.get(contextKey));
57
60
  if (!context) {
58
- throw new Error(`createMastraTool execution requires ${contextKey} in the Mastra request context`);
61
+ throw new Error(options.context
62
+ ? 'createMastraTool execution found no oRPC context through its context option'
63
+ : `createMastraTool execution requires ${contextKey} in the Mastra request context`);
59
64
  }
60
65
  const startedAt = performance.now();
61
66
  const finish = (outcome) => {
@@ -1 +1 @@
1
- {"version":3,"file":"mastra.js","sourceRoot":"","sources":["../src/mastra.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RequestContext } from '@mastra/core/request-context'\nimport { createTool, type Tool } from '@mastra/core/tools'\nimport { type AnyProcedure, call } from '@orpc/server'\nimport {\n\tisObjectJsonSchema,\n\tpassThroughOutputSchema,\n\trawInputSchema,\n\ttype ToolSchema\n} from './json-schema.js'\nimport { readFnMeta, toToolName } from './meta.js'\nimport type { ProcedureInput, ProcedureOutput } from './types.js'\n\nexport { toToolName }\n\nexport type CreateMastraToolOptions = {\n\t/** Override the tool id (defaults to the sanitized `fn` name). */\n\tid?: string\n\t/** Override the tool description (defaults to the route summary). */\n\tdescription?: string\n\t/** Gate execution behind the AI SDK tool-approval flow. */\n\trequireApproval?: boolean\n\t/**\n\t * Accept a procedure without an input schema (the tool then takes an empty\n\t * object). For no-input queries like `teams.list`.\n\t */\n\tallowMissingInputSchema?: boolean\n\tonExecuteFinish?: (event: {\n\t\ttoolId: string\n\t\tdurationMs: number\n\t\toutcome: 'success' | 'error'\n\t}) => void\n\t/** Key holding the oRPC context in Mastra's `RequestContext`. Default 'orpcContext'. */\n\tcontextKey?: string\n}\n\n/** What the tool takes: the procedure's raw input, or `{}` without a schema. */\nexport type MastraToolInput<TProc> =\n\tunknown extends ProcedureInput<TProc>\n\t\t? Record<string, never>\n\t\t: ProcedureInput<TProc>\n\n/** A Mastra tool typed by the procedure's raw input and parsed output. */\nexport type MastraTool<TProc extends AnyProcedure> = Tool<\n\tMastraToolInput<TProc>,\n\tProcedureOutput<TProc>\n>\n\n/** LLM tool parameters must be an object; other inputs are a type error. */\ntype ObjectInputCheck<TProc> =\n\tMastraToolInput<TProc> extends Record<string, unknown>\n\t\t? unknown\n\t\t: { 'orpc-fn: an agent tool needs an object input': ProcedureInput<TProc> }\n\n/**\n * Creates a Mastra tool from a procedure made with `fn()`. Execution calls\n * the procedure with the oRPC context stored in the Mastra request context\n * (`requestContext.set('orpcContext', context)`), so auth and guards apply.\n *\n * Mastra checks the input against the procedure's schema (with dates coerced\n * from strings) but passes the raw input on for the procedure to parse;\n * the output is the procedure's parsed result, passed through unchanged.\n */\nexport function createMastraTool<TProc extends AnyProcedure>(\n\tprocedure: TProc & ObjectInputCheck<TProc>,\n\toptions: CreateMastraToolOptions = {}\n): MastraTool<TProc> {\n\tconst meta = readFnMeta(procedure)\n\tif (meta.stream)\n\t\tthrow new TypeError('orpc-fn: streaming procedures cannot be Mastra tools')\n\tconst name = options.id ?? meta.name\n\tif (!name) {\n\t\tthrow new Error(\n\t\t\t'Procedure must be created with fn() to use createMastraTool'\n\t\t)\n\t}\n\tlet inputSchema: ToolSchema | undefined\n\tif (meta.inputSchema) {\n\t\tinputSchema = rawInputSchema(meta.inputSchema)\n\t\tif (!isObjectJsonSchema(inputSchema['~standard'].jsonSchema.input())) {\n\t\t\tthrow new Error(`${name} must take an object input to be an agent tool`)\n\t\t}\n\t} else if (!options.allowMissingInputSchema) {\n\t\tthrow new Error(\n\t\t\t`${name} must declare an input schema to be exposed as an agent tool`\n\t\t)\n\t}\n\n\tconst id = options.id ?? toToolName(name)\n\tconst contextKey = options.contextKey ?? 'orpcContext'\n\n\tconst tool = createTool({\n\t\tid,\n\t\tdescription: options.description ?? meta.description,\n\t\trequireApproval: options.requireApproval === true,\n\t\tinputSchema:\n\t\t\tinputSchema ??\n\t\t\t({\n\t\t\t\t'~standard': {\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tvendor: 'orpc-fn',\n\t\t\t\t\tvalidate: () => ({ value: {} }),\n\t\t\t\t\tjsonSchema: {\n\t\t\t\t\t\tinput: () => ({ type: 'object', properties: {} }),\n\t\t\t\t\t\toutput: () => ({ type: 'object', properties: {} })\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} satisfies ToolSchema),\n\t\t...(meta.outputSchema\n\t\t\t? { outputSchema: passThroughOutputSchema(meta.outputSchema) }\n\t\t\t: {}),\n\t\texecute: async (input, ctx) => {\n\t\t\tconst requestContext = ctx?.requestContext as RequestContext | undefined\n\t\t\tconst context = requestContext?.get(contextKey)\n\t\t\tif (!context) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`createMastraTool execution requires ${contextKey} in the Mastra request context`\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst startedAt = performance.now()\n\t\t\tconst finish = (outcome: 'success' | 'error') => {\n\t\t\t\ttry {\n\t\t\t\t\toptions.onExecuteFinish?.({\n\t\t\t\t\t\ttoolId: id,\n\t\t\t\t\t\tdurationMs: performance.now() - startedAt,\n\t\t\t\t\t\toutcome\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\t/* A telemetry hook cannot change tool execution. */\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst result = await call(\n\t\t\t\t\tprocedure,\n\t\t\t\t\tmeta.inputSchema ? input : undefined,\n\t\t\t\t\tctx?.abortSignal ? { context, signal: ctx.abortSignal } : { context }\n\t\t\t\t)\n\t\t\t\tfinish('success')\n\t\t\t\treturn result\n\t\t\t} catch (error) {\n\t\t\t\tfinish('error')\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t})\n\n\t// The runtime schemas are untyped `ToolSchema`s; `MastraTool` is the typed surface.\n\treturn tool as unknown as MastraTool<TProc>\n}\n"]}
1
+ {"version":3,"file":"mastra.js","sourceRoot":"","sources":["../src/mastra.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RequestContext } from '@mastra/core/request-context'\nimport { createTool, type Tool } from '@mastra/core/tools'\nimport { type AnyProcedure, call } from '@orpc/server'\nimport {\n\tisObjectJsonSchema,\n\tpassThroughOutputSchema,\n\trawInputSchema,\n\ttype ToolSchema\n} from './json-schema.js'\nimport { readFnMeta, toToolName } from './meta.js'\nimport type { ProcedureInput, ProcedureOutput } from './types.js'\n\nexport { toToolName }\n\nexport type CreateMastraToolOptions = {\n\t/** Override the tool id (defaults to the sanitized `fn` name). */\n\tid?: string\n\t/** Override the tool description (defaults to the route summary). */\n\tdescription?: string\n\t/** Gate execution behind the AI SDK tool-approval flow. */\n\trequireApproval?: boolean\n\t/**\n\t * Accept a procedure without an input schema (the tool then takes an empty\n\t * object). For no-input queries like `teams.list`.\n\t */\n\tallowMissingInputSchema?: boolean\n\tonExecuteFinish?: (event: {\n\t\ttoolId: string\n\t\tdurationMs: number\n\t\toutcome: 'success' | 'error'\n\t}) => void\n\t/** Key holding the oRPC context in Mastra's `RequestContext`. Default 'orpcContext'. */\n\tcontextKey?: string\n\t/**\n\t * Reads the oRPC context from the run's request context, in place of\n\t * `contextKey`. Mastra writes plain request-context values into run\n\t * snapshots and spans, so an app whose context holds a session keeps it\n\t * in a slot that serializes to nothing and reads it here.\n\t */\n\tcontext?: (requestContext: RequestContext) => unknown\n}\n\n/** What the tool takes: the procedure's raw input, or `{}` without a schema. */\nexport type MastraToolInput<TProc> =\n\tunknown extends ProcedureInput<TProc>\n\t\t? Record<string, never>\n\t\t: ProcedureInput<TProc>\n\n/** A Mastra tool typed by the procedure's raw input and parsed output. */\nexport type MastraTool<TProc extends AnyProcedure> = Tool<\n\tMastraToolInput<TProc>,\n\tProcedureOutput<TProc>\n>\n\n/** LLM tool parameters must be an object; other inputs are a type error. */\ntype ObjectInputCheck<TProc> =\n\tMastraToolInput<TProc> extends Record<string, unknown>\n\t\t? unknown\n\t\t: { 'orpc-fn: an agent tool needs an object input': ProcedureInput<TProc> }\n\n/**\n * Creates a Mastra tool from a procedure made with `fn()`. Execution calls\n * the procedure with the oRPC context stored in the Mastra request context\n * (`requestContext.set('orpcContext', context)`), so auth and guards apply.\n *\n * Mastra checks the input against the procedure's schema (with dates coerced\n * from strings) but passes the raw input on for the procedure to parse;\n * the output is the procedure's parsed result, passed through unchanged.\n */\nexport function createMastraTool<TProc extends AnyProcedure>(\n\tprocedure: TProc & ObjectInputCheck<TProc>,\n\toptions: CreateMastraToolOptions = {}\n): MastraTool<TProc> {\n\tconst meta = readFnMeta(procedure)\n\tif (meta.stream)\n\t\tthrow new TypeError('orpc-fn: streaming procedures cannot be Mastra tools')\n\tconst name = options.id ?? meta.name\n\tif (!name) {\n\t\tthrow new Error(\n\t\t\t'Procedure must be created with fn() to use createMastraTool'\n\t\t)\n\t}\n\tlet inputSchema: ToolSchema | undefined\n\tif (meta.inputSchema) {\n\t\tinputSchema = rawInputSchema(meta.inputSchema)\n\t\tif (!isObjectJsonSchema(inputSchema['~standard'].jsonSchema.input())) {\n\t\t\tthrow new Error(`${name} must take an object input to be an agent tool`)\n\t\t}\n\t} else if (!options.allowMissingInputSchema) {\n\t\tthrow new Error(\n\t\t\t`${name} must declare an input schema to be exposed as an agent tool`\n\t\t)\n\t}\n\n\tconst id = options.id ?? toToolName(name)\n\tconst contextKey = options.contextKey ?? 'orpcContext'\n\n\tconst tool = createTool({\n\t\tid,\n\t\tdescription: options.description ?? meta.description,\n\t\trequireApproval: options.requireApproval === true,\n\t\tinputSchema:\n\t\t\tinputSchema ??\n\t\t\t({\n\t\t\t\t'~standard': {\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tvendor: 'orpc-fn',\n\t\t\t\t\tvalidate: () => ({ value: {} }),\n\t\t\t\t\tjsonSchema: {\n\t\t\t\t\t\tinput: () => ({ type: 'object', properties: {} }),\n\t\t\t\t\t\toutput: () => ({ type: 'object', properties: {} })\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} satisfies ToolSchema),\n\t\t...(meta.outputSchema\n\t\t\t? { outputSchema: passThroughOutputSchema(meta.outputSchema) }\n\t\t\t: {}),\n\t\texecute: async (input, ctx) => {\n\t\t\tconst requestContext = ctx?.requestContext as RequestContext | undefined\n\t\t\tconst context =\n\t\t\t\trequestContext &&\n\t\t\t\t(options.context\n\t\t\t\t\t? options.context(requestContext)\n\t\t\t\t\t: requestContext.get(contextKey))\n\t\t\tif (!context) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\toptions.context\n\t\t\t\t\t\t? 'createMastraTool execution found no oRPC context through its context option'\n\t\t\t\t\t\t: `createMastraTool execution requires ${contextKey} in the Mastra request context`\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst startedAt = performance.now()\n\t\t\tconst finish = (outcome: 'success' | 'error') => {\n\t\t\t\ttry {\n\t\t\t\t\toptions.onExecuteFinish?.({\n\t\t\t\t\t\ttoolId: id,\n\t\t\t\t\t\tdurationMs: performance.now() - startedAt,\n\t\t\t\t\t\toutcome\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\t/* A telemetry hook cannot change tool execution. */\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst result = await call(\n\t\t\t\t\tprocedure,\n\t\t\t\t\tmeta.inputSchema ? input : undefined,\n\t\t\t\t\tctx?.abortSignal ? { context, signal: ctx.abortSignal } : { context }\n\t\t\t\t)\n\t\t\t\tfinish('success')\n\t\t\t\treturn result\n\t\t\t} catch (error) {\n\t\t\t\tfinish('error')\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t})\n\n\t// The runtime schemas are untyped `ToolSchema`s; `MastraTool` is the typed surface.\n\treturn tool as unknown as MastraTool<TProc>\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orpc-fn",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "Define a backend function once: a typed oRPC procedure with tracing, logging, guards, injected handler extras, nested calls and live queries over Redis.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -128,7 +128,7 @@
128
128
  "test:client-bundle": "bun scripts/check-client-bundle.ts"
129
129
  },
130
130
  "dependencies": {
131
- "hanzio": "^3.3.0"
131
+ "hanzio": "^3.4.0"
132
132
  },
133
133
  "peerDependencies": {
134
134
  "@mastra/core": "^1.51.0",
package/src/hono.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Readable, pipeline } from 'node:stream'
2
+ import zlib from 'node:zlib'
1
3
  import { SmartCoercionPlugin } from '@orpc/json-schema'
2
4
  import type { OpenAPIGeneratorGenerateOptions } from '@orpc/openapi'
3
5
  import { OpenAPIHandler } from '@orpc/openapi/fetch'
@@ -69,6 +71,12 @@ export type MountOrpcOptions<
69
71
  batch?: boolean | { maxSize: number }
70
72
  /** Add a Server-Timing header from the request timing. Default: true */
71
73
  serverTiming?: boolean
74
+ /**
75
+ * gzip JSON responses and batch event streams for clients that accept it,
76
+ * flushing after every batch result. Other event streams are left alone.
77
+ * Default: true
78
+ */
79
+ compress?: boolean
72
80
  /** SSE keep-alive comments every N ms (default 15000), or false. */
73
81
  sseKeepAlive?: boolean | number
74
82
  /** Extra RPC plugins (e.g. `CORSPlugin`). */
@@ -153,6 +161,39 @@ export function finishOrpcHeaders(
153
161
  return headers
154
162
  }
155
163
 
164
+ const NO_TRANSFORM = /(?:^|,)\s*no-transform\s*(?:,|$)/i
165
+
166
+ /**
167
+ * JSON, and the event stream oRPC answers a streaming batch with (one event
168
+ * per result). Other event streams are live: they stay open and carry little.
169
+ */
170
+ function shouldCompress(request: Request, response: Response): boolean {
171
+ if (request.method === 'HEAD') return false
172
+ if (!request.headers.get('accept-encoding')?.includes('gzip')) return false
173
+ if (response.headers.has('content-encoding')) return false
174
+ if (NO_TRANSFORM.test(response.headers.get('cache-control') ?? '')) {
175
+ return false
176
+ }
177
+ const type = response.headers.get('content-type') ?? ''
178
+ if (type.startsWith('text/event-stream')) {
179
+ // BatchHandlerPlugin keys on this header too, whatever the URL.
180
+ return request.headers.has('x-orpc-batch')
181
+ }
182
+ return type.startsWith('application/json')
183
+ }
184
+
185
+ /**
186
+ * gzip that emits every chunk at once, so a fast batch result is not held
187
+ * back by a slow one. Cancelling the result cancels `body`.
188
+ */
189
+ function gzipFlushingEachChunk(
190
+ body: ReadableStream<Uint8Array>
191
+ ): ReadableStream<Uint8Array> {
192
+ const gzip = zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH })
193
+ pipeline(Readable.fromWeb(body as never), gzip, () => {})
194
+ return Readable.toWeb(gzip) as unknown as ReadableStream<Uint8Array>
195
+ }
196
+
156
197
  /** Mount oRPC's RPC and OpenAPI handlers on a Hono app. */
157
198
  export function mountOrpc<E extends Env, R extends AnyRouter>(
158
199
  app: Hono<E>,
@@ -162,7 +203,8 @@ export function mountOrpc<E extends Env, R extends AnyRouter>(
162
203
  router,
163
204
  batch = true,
164
205
  serverTiming = true,
165
- sseKeepAlive = true
206
+ sseKeepAlive = true,
207
+ compress = true
166
208
  } = options
167
209
  const isExpectedError = options.isExpectedError ?? isExpectedClientError
168
210
  const keepAlive =
@@ -221,10 +263,18 @@ export function mountOrpc<E extends Env, R extends AnyRouter>(
221
263
  handler_ms: invocationTiming.procedureMs
222
264
  })
223
265
  : undefined
224
- return c.newResponse(response.body, {
266
+ const responseHeaders = finishOrpcHeaders(response, timed)
267
+ let body = response.body
268
+ if (body && compress && shouldCompress(c.req.raw, response)) {
269
+ body = gzipFlushingEachChunk(body)
270
+ responseHeaders.set('content-encoding', 'gzip')
271
+ responseHeaders.delete('content-length')
272
+ responseHeaders.append('vary', 'accept-encoding')
273
+ }
274
+ return c.newResponse(body, {
225
275
  // oRPC only produces valid HTTP status codes.
226
276
  status: response.status as StatusCode,
227
- headers: finishOrpcHeaders(response, timed)
277
+ headers: responseHeaders
228
278
  })
229
279
  })
230
280
  }
package/src/mastra.ts CHANGED
@@ -31,6 +31,13 @@ export type CreateMastraToolOptions = {
31
31
  }) => void
32
32
  /** Key holding the oRPC context in Mastra's `RequestContext`. Default 'orpcContext'. */
33
33
  contextKey?: string
34
+ /**
35
+ * Reads the oRPC context from the run's request context, in place of
36
+ * `contextKey`. Mastra writes plain request-context values into run
37
+ * snapshots and spans, so an app whose context holds a session keeps it
38
+ * in a slot that serializes to nothing and reads it here.
39
+ */
40
+ context?: (requestContext: RequestContext) => unknown
34
41
  }
35
42
 
36
43
  /** What the tool takes: the procedure's raw input, or `{}` without a schema. */
@@ -110,10 +117,16 @@ export function createMastraTool<TProc extends AnyProcedure>(
110
117
  : {}),
111
118
  execute: async (input, ctx) => {
112
119
  const requestContext = ctx?.requestContext as RequestContext | undefined
113
- const context = requestContext?.get(contextKey)
120
+ const context =
121
+ requestContext &&
122
+ (options.context
123
+ ? options.context(requestContext)
124
+ : requestContext.get(contextKey))
114
125
  if (!context) {
115
126
  throw new Error(
116
- `createMastraTool execution requires ${contextKey} in the Mastra request context`
127
+ options.context
128
+ ? 'createMastraTool execution found no oRPC context through its context option'
129
+ : `createMastraTool execution requires ${contextKey} in the Mastra request context`
117
130
  )
118
131
  }
119
132
  const startedAt = performance.now()