orpc-fn 0.1.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.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -0
  3. package/dist/bound-call.d.ts +23 -0
  4. package/dist/bound-call.d.ts.map +1 -0
  5. package/dist/bound-call.js +23 -0
  6. package/dist/bound-call.js.map +1 -0
  7. package/dist/client.d.ts +44 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +49 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/create-fn.d.ts +97 -0
  12. package/dist/create-fn.d.ts.map +1 -0
  13. package/dist/create-fn.js +224 -0
  14. package/dist/create-fn.js.map +1 -0
  15. package/dist/expected-client-error.d.ts +11 -0
  16. package/dist/expected-client-error.d.ts.map +1 -0
  17. package/dist/expected-client-error.js +21 -0
  18. package/dist/expected-client-error.js.map +1 -0
  19. package/dist/expo.d.ts +66 -0
  20. package/dist/expo.d.ts.map +1 -0
  21. package/dist/expo.js +74 -0
  22. package/dist/expo.js.map +1 -0
  23. package/dist/hono.d.ts +81 -0
  24. package/dist/hono.d.ts.map +1 -0
  25. package/dist/hono.js +138 -0
  26. package/dist/hono.js.map +1 -0
  27. package/dist/index.d.ts +10 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +8 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/json-schema.d.ts +47 -0
  32. package/dist/json-schema.d.ts.map +1 -0
  33. package/dist/json-schema.js +44 -0
  34. package/dist/json-schema.js.map +1 -0
  35. package/dist/live/codec.d.ts +9 -0
  36. package/dist/live/codec.d.ts.map +1 -0
  37. package/dist/live/codec.js +27 -0
  38. package/dist/live/codec.js.map +1 -0
  39. package/dist/live/fn-live.d.ts +155 -0
  40. package/dist/live/fn-live.d.ts.map +1 -0
  41. package/dist/live/fn-live.js +210 -0
  42. package/dist/live/fn-live.js.map +1 -0
  43. package/dist/live/index.d.ts +4 -0
  44. package/dist/live/index.d.ts.map +1 -0
  45. package/dist/live/index.js +5 -0
  46. package/dist/live/index.js.map +1 -0
  47. package/dist/live/ioredis.d.ts +29 -0
  48. package/dist/live/ioredis.d.ts.map +1 -0
  49. package/dist/live/ioredis.js +49 -0
  50. package/dist/live/ioredis.js.map +1 -0
  51. package/dist/live/memory.d.ts +10 -0
  52. package/dist/live/memory.d.ts.map +1 -0
  53. package/dist/live/memory.js +45 -0
  54. package/dist/live/memory.js.map +1 -0
  55. package/dist/live/pub-sub.d.ts +211 -0
  56. package/dist/live/pub-sub.d.ts.map +1 -0
  57. package/dist/live/pub-sub.js +529 -0
  58. package/dist/live/pub-sub.js.map +1 -0
  59. package/dist/live/redis-bun.d.ts +26 -0
  60. package/dist/live/redis-bun.d.ts.map +1 -0
  61. package/dist/live/redis-bun.js +59 -0
  62. package/dist/live/redis-bun.js.map +1 -0
  63. package/dist/live/redis.d.ts +45 -0
  64. package/dist/live/redis.d.ts.map +1 -0
  65. package/dist/live/redis.js +214 -0
  66. package/dist/live/redis.js.map +1 -0
  67. package/dist/live/transport.d.ts +35 -0
  68. package/dist/live/transport.d.ts.map +1 -0
  69. package/dist/live/transport.js +2 -0
  70. package/dist/live/transport.js.map +1 -0
  71. package/dist/logger.d.ts +15 -0
  72. package/dist/logger.d.ts.map +1 -0
  73. package/dist/logger.js +15 -0
  74. package/dist/logger.js.map +1 -0
  75. package/dist/mastra.d.ts +44 -0
  76. package/dist/mastra.d.ts.map +1 -0
  77. package/dist/mastra.js +78 -0
  78. package/dist/mastra.js.map +1 -0
  79. package/dist/mcp.d.ts +39 -0
  80. package/dist/mcp.d.ts.map +1 -0
  81. package/dist/mcp.js +38 -0
  82. package/dist/mcp.js.map +1 -0
  83. package/dist/meta.d.ts +35 -0
  84. package/dist/meta.d.ts.map +1 -0
  85. package/dist/meta.js +30 -0
  86. package/dist/meta.js.map +1 -0
  87. package/dist/otel.d.ts +66 -0
  88. package/dist/otel.d.ts.map +1 -0
  89. package/dist/otel.js +63 -0
  90. package/dist/otel.js.map +1 -0
  91. package/dist/router.d.ts +13 -0
  92. package/dist/router.d.ts.map +1 -0
  93. package/dist/router.js +14 -0
  94. package/dist/router.js.map +1 -0
  95. package/dist/stream.d.ts +13 -0
  96. package/dist/stream.d.ts.map +1 -0
  97. package/dist/stream.js +44 -0
  98. package/dist/stream.js.map +1 -0
  99. package/dist/types.d.ts +196 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +29 -0
  102. package/dist/types.js.map +1 -0
  103. package/package.json +157 -0
  104. package/src/bound-call.ts +50 -0
  105. package/src/client.ts +82 -0
  106. package/src/create-fn.ts +461 -0
  107. package/src/expected-client-error.ts +23 -0
  108. package/src/expo.ts +127 -0
  109. package/src/hono.ts +247 -0
  110. package/src/index.ts +45 -0
  111. package/src/json-schema.ts +82 -0
  112. package/src/live/codec.ts +31 -0
  113. package/src/live/fn-live.ts +470 -0
  114. package/src/live/index.ts +31 -0
  115. package/src/live/ioredis.ts +74 -0
  116. package/src/live/memory.ts +50 -0
  117. package/src/live/pub-sub.ts +828 -0
  118. package/src/live/redis-bun.ts +76 -0
  119. package/src/live/redis.ts +255 -0
  120. package/src/live/transport.ts +41 -0
  121. package/src/logger.ts +28 -0
  122. package/src/mastra.ts +140 -0
  123. package/src/mcp.ts +73 -0
  124. package/src/meta.ts +72 -0
  125. package/src/otel.ts +151 -0
  126. package/src/router.ts +15 -0
  127. package/src/stream.ts +54 -0
  128. package/src/types.ts +389 -0
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { type AnyProcedure, type AnyRouter, type TraverseContractProcedureCallbackOptions } from '@orpc/server';
2
+ import { rawInputSchema, type ToolSchema } from './json-schema.js';
3
+ import { toToolName } from './meta.js';
4
+ export { toToolName };
5
+ export { rawInputSchema as toolInputSchema };
6
+ export type { ToolSchema };
7
+ /** Picks procedures; also fits oRPC's `OpenAPIHandler({ filter })`. */
8
+ export type ProcedureFilter = (options: TraverseContractProcedureCallbackOptions) => boolean;
9
+ export type McpTool = {
10
+ /** Tool name: the sanitized `fn` name (`user.me` becomes `user-me`). */
11
+ name: string;
12
+ procedure: AnyProcedure;
13
+ config: {
14
+ description?: string;
15
+ /**
16
+ * Validates and coerces (date strings to `Date`) but returns the raw
17
+ * input: pass it to `call(procedure, args)`, which parses it.
18
+ */
19
+ inputSchema?: ToolSchema;
20
+ annotations: {
21
+ readOnlyHint: boolean;
22
+ };
23
+ };
24
+ };
25
+ /** Procedures whose route has `tag`: `listTools(router, { filter: hasTag('external') })`. */
26
+ export declare const hasTag: (tag: string) => ProcedureFilter;
27
+ /**
28
+ * MCP tool definitions for the procedures in `router` matching `filter`
29
+ * (default: all). Register them with any MCP server:
30
+ * `server.registerTool(tool.name, tool.config, run)`.
31
+ *
32
+ * `readOnly` sets `readOnlyHint`; it defaults to GET routes, since an MCP
33
+ * client treats anything else as a mutation and may ask for approval.
34
+ */
35
+ export declare function listTools(router: AnyRouter, options?: {
36
+ filter?: ProcedureFilter;
37
+ readOnly?: ProcedureFilter;
38
+ }): McpTool[];
39
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,wCAAwC,EAE7C,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EAAc,UAAU,EAAE,MAAM,WAAW,CAAA;AAElD,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,OAAO,EAAE,cAAc,IAAI,eAAe,EAAE,CAAA;AAC5C,YAAY,EAAE,UAAU,EAAE,CAAA;AAE1B,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,CAC7B,OAAO,EAAE,wCAAwC,KAC7C,OAAO,CAAA;AAEZ,MAAM,MAAM,OAAO,GAAG;IACrB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,YAAY,CAAA;IACvB,MAAM,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB;;;WAGG;QACH,WAAW,CAAC,EAAE,UAAU,CAAA;QACxB,WAAW,EAAE;YAAE,YAAY,EAAE,OAAO,CAAA;SAAE,CAAA;KACtC,CAAA;CACD,CAAA;AAED,6FAA6F;AAC7F,eAAO,MAAM,MAAM,QACZ,MAAM,KAAG,eAEsC,CAAA;AAKtD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACxB,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,EAAE,eAAe,CAAA;CAAO,GACpE,OAAO,EAAE,CAmBX"}
package/dist/mcp.js ADDED
@@ -0,0 +1,38 @@
1
+ import { traverseContractProcedures } from '@orpc/server';
2
+ import { rawInputSchema } from './json-schema.js';
3
+ import { readFnMeta, toToolName } from './meta.js';
4
+ export { toToolName };
5
+ export { rawInputSchema as toolInputSchema };
6
+ /** Procedures whose route has `tag`: `listTools(router, { filter: hasTag('external') })`. */
7
+ export const hasTag = (tag) => ({ contract }) => contract['~orpc'].route.tags?.includes(tag) ?? false;
8
+ const isGet = ({ contract }) => contract['~orpc'].route.method === 'GET';
9
+ /**
10
+ * MCP tool definitions for the procedures in `router` matching `filter`
11
+ * (default: all). Register them with any MCP server:
12
+ * `server.registerTool(tool.name, tool.config, run)`.
13
+ *
14
+ * `readOnly` sets `readOnlyHint`; it defaults to GET routes, since an MCP
15
+ * client treats anything else as a mutation and may ask for approval.
16
+ */
17
+ export function listTools(router, options = {}) {
18
+ const { filter, readOnly = isGet } = options;
19
+ const tools = [];
20
+ traverseContractProcedures({ router, path: [] }, (traversed) => {
21
+ if (filter && !filter(traversed))
22
+ return;
23
+ const procedure = traversed.contract;
24
+ const { inputSchema, route } = procedure['~orpc'];
25
+ const description = route.description ?? route.summary;
26
+ tools.push({
27
+ name: toToolName(readFnMeta(procedure).name ?? traversed.path.join('.')),
28
+ procedure,
29
+ config: {
30
+ ...(description === undefined ? {} : { description }),
31
+ ...(inputSchema ? { inputSchema: rawInputSchema(inputSchema) } : {}),
32
+ annotations: { readOnlyHint: readOnly(traversed) }
33
+ }
34
+ });
35
+ });
36
+ return tools;
37
+ }
38
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,0BAA0B,EAC1B,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,cAAc,EAAmB,MAAM,kBAAkB,CAAA;AAClE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAElD,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,OAAO,EAAE,cAAc,IAAI,eAAe,EAAE,CAAA;AAuB5C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,MAAM,GAClB,CAAC,GAAW,EAAmB,EAAE,CACjC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAChB,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAA;AAEtD,MAAM,KAAK,GAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAC/C,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,KAAK,CAAA;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CACxB,MAAiB,EACjB,OAAO,GAA6D,EAAE;IAEtE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,KAAK,EAAE,GAAG,OAAO,CAAA;IAC5C,MAAM,KAAK,GAAc,EAAE,CAAA;IAC3B,0BAA0B,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,SAAS,EAAE,EAAE;QAC9D,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;YAAE,OAAM;QACxC,MAAM,SAAS,GAAG,SAAS,CAAC,QAAwB,CAAA;QACpD,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;QACjD,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,OAAO,CAAA;QACtD,KAAK,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxE,SAAS;YACT,MAAM,EAAE;gBACP,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;gBACrD,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,WAAW,EAAE,EAAE,YAAY,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE;aAClD;SACD,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;IACF,OAAO,KAAK,CAAA;AACb,CAAC","sourcesContent":["import {\n\ttype AnyProcedure,\n\ttype AnyRouter,\n\ttype TraverseContractProcedureCallbackOptions,\n\ttraverseContractProcedures\n} from '@orpc/server'\nimport { rawInputSchema, type ToolSchema } from './json-schema.js'\nimport { readFnMeta, toToolName } from './meta.js'\n\nexport { toToolName }\nexport { rawInputSchema as toolInputSchema }\nexport type { ToolSchema }\n\n/** Picks procedures; also fits oRPC's `OpenAPIHandler({ filter })`. */\nexport type ProcedureFilter = (\n\toptions: TraverseContractProcedureCallbackOptions\n) => boolean\n\nexport type McpTool = {\n\t/** Tool name: the sanitized `fn` name (`user.me` becomes `user-me`). */\n\tname: string\n\tprocedure: AnyProcedure\n\tconfig: {\n\t\tdescription?: string\n\t\t/**\n\t\t * Validates and coerces (date strings to `Date`) but returns the raw\n\t\t * input: pass it to `call(procedure, args)`, which parses it.\n\t\t */\n\t\tinputSchema?: ToolSchema\n\t\tannotations: { readOnlyHint: boolean }\n\t}\n}\n\n/** Procedures whose route has `tag`: `listTools(router, { filter: hasTag('external') })`. */\nexport const hasTag =\n\t(tag: string): ProcedureFilter =>\n\t({ contract }) =>\n\t\tcontract['~orpc'].route.tags?.includes(tag) ?? false\n\nconst isGet: ProcedureFilter = ({ contract }) =>\n\tcontract['~orpc'].route.method === 'GET'\n\n/**\n * MCP tool definitions for the procedures in `router` matching `filter`\n * (default: all). Register them with any MCP server:\n * `server.registerTool(tool.name, tool.config, run)`.\n *\n * `readOnly` sets `readOnlyHint`; it defaults to GET routes, since an MCP\n * client treats anything else as a mutation and may ask for approval.\n */\nexport function listTools(\n\trouter: AnyRouter,\n\toptions: { filter?: ProcedureFilter; readOnly?: ProcedureFilter } = {}\n): McpTool[] {\n\tconst { filter, readOnly = isGet } = options\n\tconst tools: McpTool[] = []\n\ttraverseContractProcedures({ router, path: [] }, (traversed) => {\n\t\tif (filter && !filter(traversed)) return\n\t\tconst procedure = traversed.contract as AnyProcedure\n\t\tconst { inputSchema, route } = procedure['~orpc']\n\t\tconst description = route.description ?? route.summary\n\t\ttools.push({\n\t\t\tname: toToolName(readFnMeta(procedure).name ?? traversed.path.join('.')),\n\t\t\tprocedure,\n\t\t\tconfig: {\n\t\t\t\t...(description === undefined ? {} : { description }),\n\t\t\t\t...(inputSchema ? { inputSchema: rawInputSchema(inputSchema) } : {}),\n\t\t\t\tannotations: { readOnlyHint: readOnly(traversed) }\n\t\t\t}\n\t\t})\n\t})\n\treturn tools\n}\n"]}
package/dist/meta.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type { AnySchema } from '@orpc/contract';
2
+ import type { AnyProcedure } from '@orpc/server';
3
+ /**
4
+ * Key under which `fn()` stores its metadata in oRPC's procedure meta
5
+ * (`.meta()`). Living in oRPC's own meta means it survives everything that
6
+ * rebuilds a procedure (`os.router()`, `.use()` on a router, lazy routers),
7
+ * unlike a symbol property on the procedure object.
8
+ */
9
+ export declare const FN_META_KEY = "orpc-fn";
10
+ export type StoredFnMeta = {
11
+ name: string;
12
+ procedure: string;
13
+ meta: Record<string, unknown>;
14
+ };
15
+ export type FnMeta<TMeta extends object = Record<string, unknown>> = {
16
+ /** `fn({ name })`, also the OpenAPI operationId; undefined if not made by `fn()`. */
17
+ name: string | undefined;
18
+ /** The `procedures` key the route was built from. */
19
+ procedure: string | undefined;
20
+ /** The app's typed meta (`createFn({ meta })`) declared on this route. */
21
+ meta: Partial<TMeta>;
22
+ /** Route summary, falling back to description, falling back to ''. */
23
+ description: string;
24
+ summary: string | undefined;
25
+ method: string | undefined;
26
+ path: string | undefined;
27
+ tags: readonly string[];
28
+ inputSchema: AnySchema | undefined;
29
+ outputSchema: AnySchema | undefined;
30
+ };
31
+ /** Read what `fn()` (and oRPC) recorded on a procedure. */
32
+ export declare function readFnMeta<TMeta extends object = Record<string, unknown>>(procedure: AnyProcedure): FnMeta<TMeta>;
33
+ /** Tool-safe name (`a-z0-9-_`): `Users.Get` becomes `users-get`. */
34
+ export declare function toToolName(name: string): string;
35
+ //# sourceMappingURL=meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../src/meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,YAAY,CAAA;AAEpC,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,MAAM,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACpE,qFAAqF;IACrF,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,qDAAqD;IACrD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,0EAA0E;IAC1E,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IACpB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,WAAW,EAAE,SAAS,GAAG,SAAS,CAAA;IAClC,YAAY,EAAE,SAAS,GAAG,SAAS,CAAA;CACnC,CAAA;AAeD,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxE,SAAS,EAAE,YAAY,GACrB,MAAM,CAAC,KAAK,CAAC,CAgBf;AAED,oEAAoE;AACpE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
package/dist/meta.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Key under which `fn()` stores its metadata in oRPC's procedure meta
3
+ * (`.meta()`). Living in oRPC's own meta means it survives everything that
4
+ * rebuilds a procedure (`os.router()`, `.use()` on a router, lazy routers),
5
+ * unlike a symbol property on the procedure object.
6
+ */
7
+ export const FN_META_KEY = 'orpc-fn';
8
+ /** Read what `fn()` (and oRPC) recorded on a procedure. */
9
+ export function readFnMeta(procedure) {
10
+ const def = procedure['~orpc'] ?? {};
11
+ const stored = def.meta?.[FN_META_KEY];
12
+ const route = def.route ?? {};
13
+ return {
14
+ name: stored?.name,
15
+ procedure: stored?.procedure,
16
+ meta: (stored?.meta ?? {}),
17
+ description: route.summary ?? route.description ?? '',
18
+ summary: route.summary,
19
+ method: route.method,
20
+ path: route.path,
21
+ tags: route.tags ?? [],
22
+ inputSchema: def.inputSchema,
23
+ outputSchema: def.outputSchema
24
+ };
25
+ }
26
+ /** Tool-safe name (`a-z0-9-_`): `Users.Get` becomes `users-get`. */
27
+ export function toToolName(name) {
28
+ return name.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
29
+ }
30
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","sourceRoot":"","sources":["../src/meta.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAA;AAsCpC,2DAA2D;AAC3D,MAAM,UAAU,UAAU,CACzB,SAAuB;IAEvB,MAAM,GAAG,GAAI,SAA+C,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,CAA6B,CAAA;IAClE,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAA;IAC7B,OAAO;QACN,IAAI,EAAE,MAAM,EAAE,IAAI;QAClB,SAAS,EAAE,MAAM,EAAE,SAAS;QAC5B,IAAI,EAAE,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAmB;QAC5C,WAAW,EAAE,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,WAAW,IAAI,EAAE;QACrD,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;KAC9B,CAAA;AACF,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;AACvD,CAAC","sourcesContent":["import type { AnySchema } from '@orpc/contract'\nimport type { AnyProcedure } from '@orpc/server'\n\n/**\n * Key under which `fn()` stores its metadata in oRPC's procedure meta\n * (`.meta()`). Living in oRPC's own meta means it survives everything that\n * rebuilds a procedure (`os.router()`, `.use()` on a router, lazy routers),\n * unlike a symbol property on the procedure object.\n */\nexport const FN_META_KEY = 'orpc-fn'\n\nexport type StoredFnMeta = {\n\tname: string\n\tprocedure: string\n\tmeta: Record<string, unknown>\n}\n\nexport type FnMeta<TMeta extends object = Record<string, unknown>> = {\n\t/** `fn({ name })`, also the OpenAPI operationId; undefined if not made by `fn()`. */\n\tname: string | undefined\n\t/** The `procedures` key the route was built from. */\n\tprocedure: string | undefined\n\t/** The app's typed meta (`createFn({ meta })`) declared on this route. */\n\tmeta: Partial<TMeta>\n\t/** Route summary, falling back to description, falling back to ''. */\n\tdescription: string\n\tsummary: string | undefined\n\tmethod: string | undefined\n\tpath: string | undefined\n\ttags: readonly string[]\n\tinputSchema: AnySchema | undefined\n\toutputSchema: AnySchema | undefined\n}\n\ntype ProcedureDefinition = {\n\tmeta?: Record<string, unknown>\n\troute?: {\n\t\tsummary?: string\n\t\tdescription?: string\n\t\tmethod?: string\n\t\tpath?: string\n\t\ttags?: readonly string[]\n\t}\n\tinputSchema?: AnySchema\n\toutputSchema?: AnySchema\n}\n\n/** Read what `fn()` (and oRPC) recorded on a procedure. */\nexport function readFnMeta<TMeta extends object = Record<string, unknown>>(\n\tprocedure: AnyProcedure\n): FnMeta<TMeta> {\n\tconst def = (procedure as { '~orpc'?: ProcedureDefinition })['~orpc'] ?? {}\n\tconst stored = def.meta?.[FN_META_KEY] as StoredFnMeta | undefined\n\tconst route = def.route ?? {}\n\treturn {\n\t\tname: stored?.name,\n\t\tprocedure: stored?.procedure,\n\t\tmeta: (stored?.meta ?? {}) as Partial<TMeta>,\n\t\tdescription: route.summary ?? route.description ?? '',\n\t\tsummary: route.summary,\n\t\tmethod: route.method,\n\t\tpath: route.path,\n\t\ttags: route.tags ?? [],\n\t\tinputSchema: def.inputSchema,\n\t\toutputSchema: def.outputSchema\n\t}\n}\n\n/** Tool-safe name (`a-z0-9-_`): `Users.Get` becomes `users-get`. */\nexport function toToolName(name: string): string {\n\treturn name.toLowerCase().replace(/[^a-z0-9-_]/g, '-')\n}\n"]}
package/dist/otel.d.ts ADDED
@@ -0,0 +1,66 @@
1
+ /** Attribute values accepted by OpenTelemetry spans. */
2
+ export type AttributeValue = string | number | boolean;
3
+ /** The part of an OpenTelemetry `Span` this package uses. */
4
+ export interface SpanLike {
5
+ setAttribute(key: string, value: AttributeValue): unknown;
6
+ setStatus(status: {
7
+ code: number;
8
+ message?: string;
9
+ }): unknown;
10
+ recordException(exception: Error): unknown;
11
+ end(): unknown;
12
+ }
13
+ type SpanOptionsLike = {
14
+ kind?: number;
15
+ attributes?: Record<string, AttributeValue>;
16
+ };
17
+ interface TracerLike<TSpan> {
18
+ startActiveSpan<F extends (span: TSpan) => unknown>(name: string, options: SpanOptionsLike, fn: F): ReturnType<F>;
19
+ startSpan(name: string, options?: SpanOptionsLike): TSpan;
20
+ }
21
+ /**
22
+ * The part of `@opentelemetry/api` this package uses. Pass the module itself:
23
+ * `import * as otel from '@opentelemetry/api'` then `createFn({ otel })`.
24
+ * Without it no spans are created and handlers receive `span: undefined`.
25
+ */
26
+ export interface OtelApiLike<TSpan extends SpanLike = SpanLike> {
27
+ trace: {
28
+ getTracer(name: string): TracerLike<TSpan>;
29
+ };
30
+ SpanStatusCode: {
31
+ OK: number;
32
+ ERROR: number;
33
+ };
34
+ SpanKind: {
35
+ INTERNAL: number;
36
+ SERVER: number;
37
+ PRODUCER: number;
38
+ };
39
+ /** Restores a span's context for each step of a stream it returned. */
40
+ context: {
41
+ active(): unknown;
42
+ with<T>(context: never, fn: () => T): T;
43
+ };
44
+ }
45
+ /** The span type of an OpenTelemetry API, or `SpanLike` when absent. */
46
+ export type SpanOf<TOtel> = TOtel extends OtelApiLike<infer TSpan> ? TSpan : SpanLike;
47
+ /** Called once when a span's work is done, before the span ends. */
48
+ export type OnSettled = (span: SpanLike | undefined, ...error: [] | [unknown]) => void;
49
+ export type Tracing = {
50
+ /**
51
+ * Run `fn` inside an active span: OK when it resolves, ERROR when it throws,
52
+ * ended either way. When `fn` resolves to a stream, the span stays open -
53
+ * and active for each step of the stream - until the stream ends.
54
+ * `onSettled` runs once at that point, before the span ends. Without
55
+ * OpenTelemetry `span` is undefined and `onSettled` still runs.
56
+ */
57
+ inSpan<T>(name: string, kind: 'INTERNAL' | 'SERVER' | 'PRODUCER', fn: (span: SpanLike | undefined) => Promise<T>, onSettled?: OnSettled): Promise<T>;
58
+ /** Start a span that the caller ends with `end` (long-lived streams). */
59
+ startSpan(name: string, kind: 'SERVER', attributes: Record<string, AttributeValue>): SpanLike | undefined;
60
+ /** Set OK, or ERROR with `error`, and end the span. */
61
+ end(span: SpanLike | undefined, error?: unknown): void;
62
+ };
63
+ export declare function createTracing(otel: OtelApiLike | undefined): Tracing;
64
+ export declare function errorMessageOf(error: unknown): string;
65
+ export {};
66
+ //# sourceMappingURL=otel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"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"}
package/dist/otel.js ADDED
@@ -0,0 +1,63 @@
1
+ import { isAsyncIteratorObject, onStreamEnd } from './stream.js';
2
+ export function createTracing(otel) {
3
+ const tracer = otel?.trace.getTracer('orpc-fn');
4
+ const end = (span, ...error) => {
5
+ if (!span || !otel)
6
+ return;
7
+ if (error.length === 0) {
8
+ span.setStatus({ code: otel.SpanStatusCode.OK });
9
+ }
10
+ else {
11
+ span.setStatus({
12
+ code: otel.SpanStatusCode.ERROR,
13
+ message: errorMessageOf(error[0])
14
+ });
15
+ if (error[0] instanceof Error)
16
+ span.recordException(error[0]);
17
+ }
18
+ span.end();
19
+ };
20
+ return {
21
+ inSpan(name, kind, fn, onSettled) {
22
+ const settle = (span, ...error) => {
23
+ try {
24
+ onSettled?.(span, ...error);
25
+ }
26
+ finally {
27
+ end(span, ...error);
28
+ }
29
+ };
30
+ const run = async (span, scope) => {
31
+ let result;
32
+ try {
33
+ result = await fn(span);
34
+ }
35
+ catch (error) {
36
+ settle(span, error);
37
+ throw error;
38
+ }
39
+ if (isAsyncIteratorObject(result)) {
40
+ return onStreamEnd(result, (...error) => settle(span, ...error), scope);
41
+ }
42
+ settle(span);
43
+ return result;
44
+ };
45
+ if (!tracer || !otel)
46
+ return run(undefined);
47
+ return tracer.startActiveSpan(name, { kind: otel.SpanKind[kind] }, (span) => {
48
+ const active = otel.context.active();
49
+ return run(span, (step) => otel.context.with(active, step));
50
+ });
51
+ },
52
+ startSpan(name, kind, attributes) {
53
+ if (!tracer || !otel)
54
+ return undefined;
55
+ return tracer.startSpan(name, { kind: otel.SpanKind[kind], attributes });
56
+ },
57
+ end
58
+ };
59
+ }
60
+ export function errorMessageOf(error) {
61
+ return error instanceof Error ? error.message : String(error);
62
+ }
63
+ //# sourceMappingURL=otel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.js","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EACrB,WAAW,EAEX,MAAM,aAAa,CAAA;AAiFpB,MAAM,UAAU,aAAa,CAAC,IAA6B;IAC1D,MAAM,MAAM,GAAG,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;IAC/C,MAAM,GAAG,GAAmB,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE;QAC9C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAM;QAC1B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC,CAAA;QACjD,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,SAAS,CAAC;gBACd,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,KAAK;gBAC/B,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;aACjC,CAAC,CAAA;YACF,IAAI,KAAK,CAAC,CAAC,CAAC,YAAY,KAAK;gBAAE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9D,CAAC;QACD,IAAI,CAAC,GAAG,EAAE,CAAA;IACX,CAAC,CAAA;IACD,OAAO;QACN,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS;YAC/B,MAAM,MAAM,GAAG,CAAC,IAA0B,EAAE,GAAG,KAAqB,EAAE,EAAE;gBACvE,IAAI,CAAC;oBACJ,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAA;gBAC5B,CAAC;wBAAS,CAAC;oBACV,GAAG,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAA;gBACpB,CAAC;YACF,CAAC,CAAA;YACD,MAAM,GAAG,GAAG,KAAK,EAChB,IAA0B,EAC1B,KAAmB,EACuB,EAAE;gBAC5C,IAAI,MAAsC,CAAA;gBAC1C,IAAI,CAAC;oBACJ,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAA;gBACxB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;oBACnB,MAAM,KAAK,CAAA;gBACZ,CAAC;gBACD,IAAI,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnC,OAAO,WAAW,CACjB,MAAM,EACN,CAAC,GAAG,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,EACpC,KAAK,CACY,CAAA;gBACnB,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,CAAA;gBACZ,OAAO,MAAM,CAAA;YACd,CAAC,CAAA;YACD,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI;gBAAE,OAAO,GAAG,CAAC,SAAS,CAAC,CAAA;YAC3C,OAAO,MAAM,CAAC,eAAe,CAC5B,IAAI,EACJ,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAC7B,CAAC,IAAI,EAAE,EAAE;gBACR,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAA;gBACpC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAe,EAAE,IAAI,CAAC,CAAC,CAAA;YACrE,CAAC,CACD,CAAA;QACF,CAAC;QACD,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU;YAC/B,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI;gBAAE,OAAO,SAAS,CAAA;YACtC,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC,CAAA;QACzE,CAAC;QACD,GAAG;KACH,CAAA;AACF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC5C,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC9D,CAAC","sourcesContent":["import {\n\tisAsyncIteratorObject,\n\tonStreamEnd,\n\ttype StreamScope\n} from './stream.js'\n\n/** Attribute values accepted by OpenTelemetry spans. */\nexport type AttributeValue = string | number | boolean\n\n/** The part of an OpenTelemetry `Span` this package uses. */\nexport interface SpanLike {\n\tsetAttribute(key: string, value: AttributeValue): unknown\n\tsetStatus(status: { code: number; message?: string }): unknown\n\trecordException(exception: Error): unknown\n\tend(): unknown\n}\n\ntype SpanOptionsLike = {\n\tkind?: number\n\tattributes?: Record<string, AttributeValue>\n}\n\ninterface TracerLike<TSpan> {\n\tstartActiveSpan<F extends (span: TSpan) => unknown>(\n\t\tname: string,\n\t\toptions: SpanOptionsLike,\n\t\tfn: F\n\t): ReturnType<F>\n\tstartSpan(name: string, options?: SpanOptionsLike): TSpan\n}\n\n/**\n * The part of `@opentelemetry/api` this package uses. Pass the module itself:\n * `import * as otel from '@opentelemetry/api'` then `createFn({ otel })`.\n * Without it no spans are created and handlers receive `span: undefined`.\n */\nexport interface OtelApiLike<TSpan extends SpanLike = SpanLike> {\n\ttrace: { getTracer(name: string): TracerLike<TSpan> }\n\tSpanStatusCode: { OK: number; ERROR: number }\n\tSpanKind: {\n\t\tINTERNAL: number\n\t\tSERVER: number\n\t\tPRODUCER: number\n\t}\n\t/** Restores a span's context for each step of a stream it returned. */\n\tcontext: {\n\t\tactive(): unknown\n\t\twith<T>(context: never, fn: () => T): T\n\t}\n}\n\n/** The span type of an OpenTelemetry API, or `SpanLike` when absent. */\nexport type SpanOf<TOtel> =\n\tTOtel extends OtelApiLike<infer TSpan> ? TSpan : SpanLike\n\n/** Called once when a span's work is done, before the span ends. */\nexport type OnSettled = (\n\tspan: SpanLike | undefined,\n\t...error: [] | [unknown]\n) => void\n\nexport type Tracing = {\n\t/**\n\t * Run `fn` inside an active span: OK when it resolves, ERROR when it throws,\n\t * ended either way. When `fn` resolves to a stream, the span stays open -\n\t * and active for each step of the stream - until the stream ends.\n\t * `onSettled` runs once at that point, before the span ends. Without\n\t * OpenTelemetry `span` is undefined and `onSettled` still runs.\n\t */\n\tinSpan<T>(\n\t\tname: string,\n\t\tkind: 'INTERNAL' | 'SERVER' | 'PRODUCER',\n\t\tfn: (span: SpanLike | undefined) => Promise<T>,\n\t\tonSettled?: OnSettled\n\t): Promise<T>\n\t/** Start a span that the caller ends with `end` (long-lived streams). */\n\tstartSpan(\n\t\tname: string,\n\t\tkind: 'SERVER',\n\t\tattributes: Record<string, AttributeValue>\n\t): SpanLike | undefined\n\t/** Set OK, or ERROR with `error`, and end the span. */\n\tend(span: SpanLike | undefined, error?: unknown): void\n}\n\nexport function createTracing(otel: OtelApiLike | undefined): Tracing {\n\tconst tracer = otel?.trace.getTracer('orpc-fn')\n\tconst end: Tracing['end'] = (span, ...error) => {\n\t\tif (!span || !otel) return\n\t\tif (error.length === 0) {\n\t\t\tspan.setStatus({ code: otel.SpanStatusCode.OK })\n\t\t} else {\n\t\t\tspan.setStatus({\n\t\t\t\tcode: otel.SpanStatusCode.ERROR,\n\t\t\t\tmessage: errorMessageOf(error[0])\n\t\t\t})\n\t\t\tif (error[0] instanceof Error) span.recordException(error[0])\n\t\t}\n\t\tspan.end()\n\t}\n\treturn {\n\t\tinSpan(name, kind, fn, onSettled) {\n\t\t\tconst settle = (span: SpanLike | undefined, ...error: [] | [unknown]) => {\n\t\t\t\ttry {\n\t\t\t\t\tonSettled?.(span, ...error)\n\t\t\t\t} finally {\n\t\t\t\t\tend(span, ...error)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst run = async (\n\t\t\t\tspan: SpanLike | undefined,\n\t\t\t\tscope?: StreamScope\n\t\t\t): Promise<Awaited<ReturnType<typeof fn>>> => {\n\t\t\t\tlet result: Awaited<ReturnType<typeof fn>>\n\t\t\t\ttry {\n\t\t\t\t\tresult = await fn(span)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tsettle(span, error)\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\tif (isAsyncIteratorObject(result)) {\n\t\t\t\t\treturn onStreamEnd(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\t(...error) => settle(span, ...error),\n\t\t\t\t\t\tscope\n\t\t\t\t\t) as typeof result\n\t\t\t\t}\n\t\t\t\tsettle(span)\n\t\t\t\treturn result\n\t\t\t}\n\t\t\tif (!tracer || !otel) return run(undefined)\n\t\t\treturn tracer.startActiveSpan(\n\t\t\t\tname,\n\t\t\t\t{ kind: otel.SpanKind[kind] },\n\t\t\t\t(span) => {\n\t\t\t\t\tconst active = otel.context.active()\n\t\t\t\t\treturn run(span, (step) => otel.context.with(active as never, step))\n\t\t\t\t}\n\t\t\t)\n\t\t},\n\t\tstartSpan(name, kind, attributes) {\n\t\t\tif (!tracer || !otel) return undefined\n\t\t\treturn tracer.startSpan(name, { kind: otel.SpanKind[kind], attributes })\n\t\t},\n\t\tend\n\t}\n}\n\nexport function errorMessageOf(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error)\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { AnyRouter } from '@orpc/server';
2
+ /**
3
+ * Creates a router object from routes or nested routers.
4
+ * Simply returns the routes as a const object for type inference.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const items = createRouter({ getItem, updateItem })
9
+ * export const api = createRouter({ metabase, items })
10
+ * ```
11
+ */
12
+ export declare function createRouter<T extends AnyRouter>(routes: T): Readonly<T>;
13
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAExE"}
package/dist/router.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Creates a router object from routes or nested routers.
3
+ * Simply returns the routes as a const object for type inference.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * const items = createRouter({ getItem, updateItem })
8
+ * export const api = createRouter({ metabase, items })
9
+ * ```
10
+ */
11
+ export function createRouter(routes) {
12
+ return routes;
13
+ }
14
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAsB,MAAS;IAC1D,OAAO,MAAM,CAAA;AACd,CAAC","sourcesContent":["import type { AnyRouter } from '@orpc/server'\n\n/**\n * Creates a router object from routes or nested routers.\n * Simply returns the routes as a const object for type inference.\n *\n * @example\n * ```ts\n * const items = createRouter({ getItem, updateItem })\n * export const api = createRouter({ metabase, items })\n * ```\n */\nexport function createRouter<T extends AnyRouter>(routes: T): Readonly<T> {\n\treturn routes\n}\n"]}
@@ -0,0 +1,13 @@
1
+ /** A value oRPC streams to the client (an async generator or iterator). */
2
+ export declare function isAsyncIteratorObject(value: unknown): value is AsyncIterator<unknown> & AsyncIterable<unknown>;
3
+ /** Runs one step of a stream inside some context (e.g. its tracing span). */
4
+ export type StreamScope = <T>(step: () => T) => T;
5
+ /**
6
+ * Wraps a stream so `finish` runs once when it ends: after the last item,
7
+ * when it throws (with the error), or when the consumer stops early with
8
+ * `return()` - also before the first `next()`, which a plain async generator
9
+ * wrapper would miss. With `scope`, every `next`/`return`/`throw` runs inside
10
+ * it: a generator's body runs on those calls, long after it was created.
11
+ */
12
+ export declare function onStreamEnd<T>(stream: AsyncIterator<T> & AsyncIterable<T>, finish: (...error: [] | [unknown]) => void, scope?: StreamScope): AsyncIterator<T> & AsyncIterable<T>;
13
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,OAAO,GACZ,KAAK,IAAI,aAAa,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,CAQ1D;AAED,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;AAEjD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC5B,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,EAC3C,MAAM,EAAE,CAAC,GAAG,KAAK,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAC1C,KAAK,GAAE,WAA8B,GACnC,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CA0BrC"}
package/dist/stream.js ADDED
@@ -0,0 +1,44 @@
1
+ /** A value oRPC streams to the client (an async generator or iterator). */
2
+ export function isAsyncIteratorObject(value) {
3
+ return (typeof value === 'object' &&
4
+ value !== null &&
5
+ typeof value[Symbol.asyncIterator] ===
6
+ 'function' &&
7
+ typeof value.next === 'function');
8
+ }
9
+ /**
10
+ * Wraps a stream so `finish` runs once when it ends: after the last item,
11
+ * when it throws (with the error), or when the consumer stops early with
12
+ * `return()` - also before the first `next()`, which a plain async generator
13
+ * wrapper would miss. With `scope`, every `next`/`return`/`throw` runs inside
14
+ * it: a generator's body runs on those calls, long after it was created.
15
+ */
16
+ export function onStreamEnd(stream, finish, scope = (step) => step()) {
17
+ let finished = false;
18
+ const done = (...error) => {
19
+ if (finished)
20
+ return;
21
+ finished = true;
22
+ finish(...error);
23
+ };
24
+ const settle = async (step) => {
25
+ try {
26
+ const result = await step();
27
+ if (!result || result.done)
28
+ done();
29
+ return result ?? { done: true, value: undefined };
30
+ }
31
+ catch (error) {
32
+ done(error);
33
+ throw error;
34
+ }
35
+ };
36
+ const wrapped = {
37
+ next: (...args) => settle(() => scope(() => stream.next(...args))),
38
+ return: (value) => settle(() => scope(() => stream.return?.(value))).finally(() => done()),
39
+ throw: (error) => settle(() => scope(() => stream.throw?.(error) ?? Promise.reject(error))),
40
+ [Symbol.asyncIterator]: () => wrapped
41
+ };
42
+ return wrapped;
43
+ }
44
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,MAAM,UAAU,qBAAqB,CACpC,KAAc;IAEd,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAQ,KAAgC,CAAC,MAAM,CAAC,aAAa,CAAC;YAC7D,UAAU;QACX,OAAQ,KAAgC,CAAC,IAAI,KAAK,UAAU,CAC5D,CAAA;AACF,CAAC;AAKD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAC1B,MAA2C,EAC3C,MAA0C,EAC1C,KAAK,GAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;IAErC,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,MAAM,IAAI,GAAG,CAAC,GAAG,KAAqB,EAAE,EAAE;QACzC,IAAI,QAAQ;YAAE,OAAM;QACpB,QAAQ,GAAG,IAAI,CAAA;QACf,MAAM,CAAC,GAAG,KAAK,CAAC,CAAA;IACjB,CAAC,CAAA;IACD,MAAM,MAAM,GAAG,KAAK,EAAE,IAAkD,EAAE,EAAE;QAC3E,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,IAAI,EAAE,CAAA;YAC3B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI;gBAAE,IAAI,EAAE,CAAA;YAClC,OAAO,MAAM,IAAI,EAAE,IAAI,EAAE,IAAa,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QAC3D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,CAAA;YACX,MAAM,KAAK,CAAA;QACZ,CAAC;IACF,CAAC,CAAA;IACD,MAAM,OAAO,GAAwC;QACpD,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAClE,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;QACxE,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAChB,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1E,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO;KACrC,CAAA;IACD,OAAO,OAAO,CAAA;AACf,CAAC","sourcesContent":["/** A value oRPC streams to the client (an async generator or iterator). */\nexport function isAsyncIteratorObject(\n\tvalue: unknown\n): value is AsyncIterator<unknown> & AsyncIterable<unknown> {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as AsyncIterable<unknown>)[Symbol.asyncIterator] ===\n\t\t\t'function' &&\n\t\ttypeof (value as AsyncIterator<unknown>).next === 'function'\n\t)\n}\n\n/** Runs one step of a stream inside some context (e.g. its tracing span). */\nexport type StreamScope = <T>(step: () => T) => T\n\n/**\n * Wraps a stream so `finish` runs once when it ends: after the last item,\n * when it throws (with the error), or when the consumer stops early with\n * `return()` - also before the first `next()`, which a plain async generator\n * wrapper would miss. With `scope`, every `next`/`return`/`throw` runs inside\n * it: a generator's body runs on those calls, long after it was created.\n */\nexport function onStreamEnd<T>(\n\tstream: AsyncIterator<T> & AsyncIterable<T>,\n\tfinish: (...error: [] | [unknown]) => void,\n\tscope: StreamScope = (step) => step()\n): AsyncIterator<T> & AsyncIterable<T> {\n\tlet finished = false\n\tconst done = (...error: [] | [unknown]) => {\n\t\tif (finished) return\n\t\tfinished = true\n\t\tfinish(...error)\n\t}\n\tconst settle = async (step: () => Promise<IteratorResult<T>> | undefined) => {\n\t\ttry {\n\t\t\tconst result = await step()\n\t\t\tif (!result || result.done) done()\n\t\t\treturn result ?? { done: true as const, value: undefined }\n\t\t} catch (error) {\n\t\t\tdone(error)\n\t\t\tthrow error\n\t\t}\n\t}\n\tconst wrapped: AsyncIterator<T> & AsyncIterable<T> = {\n\t\tnext: (...args) => settle(() => scope(() => stream.next(...args))),\n\t\treturn: (value) =>\n\t\t\tsettle(() => scope(() => stream.return?.(value))).finally(() => done()),\n\t\tthrow: (error) =>\n\t\t\tsettle(() => scope(() => stream.throw?.(error) ?? Promise.reject(error))),\n\t\t[Symbol.asyncIterator]: () => wrapped\n\t}\n\treturn wrapped\n}\n"]}
@@ -0,0 +1,196 @@
1
+ import type { AnySchema, ErrorMap, InferSchemaInput, InferSchemaOutput, Meta, Schema } from '@orpc/contract';
2
+ import type { AnyProcedure, DecoratedProcedure, Procedure, Route } from '@orpc/server';
3
+ import type { ZodType, z } from 'zod';
4
+ import type { BoundCall } from './bound-call.js';
5
+ import type { FnLogger } from './logger.js';
6
+ import type { SpanLike } from './otel.js';
7
+ export type MaybePromise<T> = T | Promise<T>;
8
+ /**
9
+ * An oRPC builder without input or output schema: `os`, `os.$context<T>()`,
10
+ * `os.use(...)`. A schema set on the builder is rejected by `createFn`; set it
11
+ * on the route instead.
12
+ */
13
+ export type BuilderLike = {
14
+ route(route: any): unknown;
15
+ meta(meta: any): unknown;
16
+ input(schema: any): unknown;
17
+ output(schema: any): unknown;
18
+ handler(handler: any): Procedure<any, any, any, any, any, any>;
19
+ };
20
+ /** Input a procedure is called with (before its input schema runs). */
21
+ export type ProcedureInput<T> = T extends Procedure<infer _TInitial, infer _TCurrent, infer TInputSchema extends AnySchema, infer _TOut, infer _TErrors, infer _TMeta> ? InferSchemaInput<TInputSchema> : unknown;
22
+ /** What a procedure resolves to (after its output schema runs). */
23
+ export type ProcedureOutput<T> = T extends Procedure<infer _TInitial, infer _TCurrent, infer _TIn, infer TOutputSchema extends AnySchema, infer _TErrors, infer _TMeta> ? InferSchemaOutput<TOutputSchema> : unknown;
24
+ /** Context a procedure must be called with. */
25
+ export type ProcedureContext<T> = T extends Procedure<infer TContext, infer _TCurrent, infer _TIn, infer _TOut, infer _TErrors, infer _TMeta> ? TContext : never;
26
+ /** `Omit` that keeps each member of a union (handler params narrow by discriminant). */
27
+ export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
28
+ /** `fn()` options passed to oRPC's `.route()`; `RouteConfig` is derived from it. */
29
+ export declare const ROUTE_OPTION_KEYS: readonly ["path", "method", "summary", "description", "deprecated", "successStatus", "successDescription", "inputStructure", "outputStructure", "tags"];
30
+ /**
31
+ * `fn()` option names owned by the route itself. Guard and meta keys must not
32
+ * reuse them: such an option would be read as the route option and the guard
33
+ * would never run.
34
+ */
35
+ export declare const RESERVED_OPTION_KEYS: readonly ["path", "method", "summary", "description", "deprecated", "successStatus", "successDescription", "inputStructure", "outputStructure", "tags", "name", "procedure", "input", "output", "handler", "live", "operationId"];
36
+ export type ReservedOptionKey = (typeof RESERVED_OPTION_KEYS)[number];
37
+ /** @internal Builds one route from a `procedures` builder; shared by `fn` and `createPubSub`. */
38
+ export type BuildProcedure = (options: {
39
+ name: string;
40
+ procedure: string | undefined;
41
+ route: Record<string, unknown>;
42
+ meta: Record<string, unknown>;
43
+ input?: AnySchema | undefined;
44
+ output?: AnySchema | undefined;
45
+ handler: (options: {
46
+ input: unknown;
47
+ context: Record<string, unknown>;
48
+ signal?: AbortSignal;
49
+ }) => unknown;
50
+ }) => AnyProcedure;
51
+ /** Turns every key of `T` found in `K` into a readable type error. */
52
+ export type RejectKeys<T, K extends PropertyKey, TMessage extends string> = {
53
+ [P in Extract<keyof T, K>]: TMessage;
54
+ };
55
+ /**
56
+ * Handler extras must have known keys: an index signature would claim every
57
+ * handler param. Intersected into `createFn`'s options to reject it.
58
+ */
59
+ export type FiniteExtras<T> = string extends keyof T ? {
60
+ extras: 'orpc-fn: extras need known keys, not an index signature';
61
+ } : number extends keyof T ? {
62
+ extras: 'orpc-fn: extras need known keys, not an index signature';
63
+ } : unknown;
64
+ type ProcedureOf<TBuilder> = TBuilder extends {
65
+ handler(handler: never): infer TProcedure;
66
+ } ? TProcedure : never;
67
+ /** Context a route built from this builder is called with. */
68
+ export type InitialContextOf<TBuilder> = ProcedureContext<ProcedureOf<TBuilder>>;
69
+ /** Context the handler sees, after the builder's middleware. */
70
+ export type CurrentContextOf<TBuilder> = ProcedureOf<TBuilder> extends Procedure<infer _TInitial, infer TContext, infer _TIn, infer _TOut, infer _TErrors, infer _TMeta> ? TContext : never;
71
+ type ErrorMapOf<TBuilder> = ProcedureOf<TBuilder> extends {
72
+ '~orpc': {
73
+ errorMap: infer T extends ErrorMap;
74
+ };
75
+ } ? T : ErrorMap;
76
+ type MetaOf<TBuilder> = ProcedureOf<TBuilder> extends {
77
+ '~orpc': {
78
+ meta: infer T extends Meta;
79
+ };
80
+ } ? T : Meta;
81
+ /**
82
+ * Everything `createFn` inferred from its options. Route types read from it;
83
+ * apps rarely spell it out.
84
+ */
85
+ export type FnDefinition = {
86
+ procedures: Record<string, BuilderLike>;
87
+ default: string;
88
+ extras: object;
89
+ guards: object;
90
+ meta: object;
91
+ tag: string;
92
+ span: SpanLike;
93
+ logger: FnLogger;
94
+ };
95
+ export type ProcedureKey<TDef extends FnDefinition> = keyof TDef['procedures'] & string;
96
+ /** Context the handler of a `procedure: TKey` route receives. */
97
+ export type FnContext<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>> = CurrentContextOf<TDef['procedures'][TKey]>;
98
+ /** Union of every procedure's handler context: what guards and extras see. */
99
+ export type AnyFnContext<TDef extends FnDefinition> = {
100
+ [K in ProcedureKey<TDef>]: FnContext<TDef, K>;
101
+ }[ProcedureKey<TDef>];
102
+ export type GuardParams<TContext, TMeta> = {
103
+ context: TContext;
104
+ input: unknown;
105
+ /** Route name (operationId). */
106
+ name: string;
107
+ /** Procedures key the route was built from. */
108
+ procedure: string;
109
+ meta: Partial<TMeta>;
110
+ signal: AbortSignal | undefined;
111
+ };
112
+ /**
113
+ * A check run before the handler. Its first parameter becomes an option on
114
+ * `fn({...})`; the guard runs only on routes that set it. Throw (usually an
115
+ * `ORPCError`) to refuse the call.
116
+ */
117
+ export type Guard<TValue, TParams> = (value: TValue, params: TParams) => unknown;
118
+ export type GuardOptions<TGuards> = {
119
+ [K in keyof TGuards]?: TGuards[K] extends (value: infer TValue, params: never) => unknown ? TValue : never;
120
+ };
121
+ export type HandlerParams<TDef extends FnDefinition, TInput, TKey extends ProcedureKey<TDef>> = DistributiveOmit<TDef['extras'], 'input' | 'context' | 'call' | 'signal' | 'span' | 'logger'> & {
122
+ input: TInput;
123
+ context: FnContext<TDef, TKey>;
124
+ /** Calls another procedure with this context and signal, in a child span. */
125
+ call: BoundCall<FnContext<TDef, TKey>>;
126
+ /** Cancellation is inherited by nested calls; pass to interruptible I/O. */
127
+ signal: AbortSignal | undefined;
128
+ /** Active OpenTelemetry span, undefined when `createFn({ otel })` is not set. */
129
+ span: TDef['span'] | undefined;
130
+ logger: TDef['logger'];
131
+ };
132
+ /** Route options shared by every `fn()` overload. */
133
+ export type RouteConfig<TTag extends string = string> = Pick<Route, Exclude<(typeof ROUTE_OPTION_KEYS)[number], 'tags'>> & {
134
+ /** Required. Becomes the OpenAPI operationId and the span name (e.g. 'user.create'). */
135
+ name: string;
136
+ tags?: TTag[];
137
+ };
138
+ export type FnRouteOptions<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>> = RouteConfig<TDef['tag']> & GuardOptions<TDef['guards']> & TDef['meta'] & ProcedureOption<TDef, TKey>;
139
+ /**
140
+ * Which `createFn({ procedures })` builder a route uses. Optional when
141
+ * `createFn` has a `default`; required on every route when it has none.
142
+ */
143
+ export type ProcedureOption<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>> = [TDef['default']] extends [never] ? {
144
+ procedure: TKey;
145
+ } : {
146
+ procedure?: TKey;
147
+ };
148
+ export type FnProcedure<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>, TInputSchema extends Schema<unknown, unknown>, TOutputSchema extends Schema<unknown, unknown>> = DecoratedProcedure<InitialContextOf<TDef['procedures'][TKey]>, FnContext<TDef, TKey>, TInputSchema, TOutputSchema, ErrorMapOf<TDef['procedures'][TKey]>, MetaOf<TDef['procedures'][TKey]>>;
149
+ type InputOf<T> = [T] extends [ZodType] ? z.output<T> : unknown;
150
+ type ReturnOf<TOut, TReturn> = [TOut] extends [ZodType] ? z.input<TOut> : TReturn;
151
+ /** `fn()`: four overloads for input/output schema × inferred. */
152
+ export interface Fn<TDef extends FnDefinition> {
153
+ <TInput extends ZodType, TOut extends ZodType, TKey extends ProcedureKey<TDef> = TDef['default']>(options: {
154
+ input: TInput;
155
+ output: TOut;
156
+ handler: (params: HandlerParams<TDef, z.output<TInput>, NoInfer<TKey>>) => MaybePromise<z.input<TOut>>;
157
+ } & FnRouteOptions<TDef, TKey>): FnProcedure<TDef, TKey, TInput, TOut>;
158
+ <TInput extends ZodType, TReturn, TKey extends ProcedureKey<TDef> = TDef['default']>(options: {
159
+ input: TInput;
160
+ output?: undefined;
161
+ handler: (params: HandlerParams<TDef, z.output<TInput>, NoInfer<TKey>>) => MaybePromise<TReturn>;
162
+ } & FnRouteOptions<TDef, TKey>): FnProcedure<TDef, TKey, TInput, Schema<TReturn, TReturn>>;
163
+ <TOut extends ZodType, TKey extends ProcedureKey<TDef> = TDef['default']>(options: {
164
+ input?: undefined;
165
+ output: TOut;
166
+ handler: (params: HandlerParams<TDef, unknown, NoInfer<TKey>>) => MaybePromise<z.input<TOut>>;
167
+ } & FnRouteOptions<TDef, TKey>): FnProcedure<TDef, TKey, Schema<unknown, unknown>, TOut>;
168
+ <TReturn, TKey extends ProcedureKey<TDef> = TDef['default']>(options: {
169
+ input?: undefined;
170
+ output?: undefined;
171
+ handler: (params: HandlerParams<TDef, unknown, NoInfer<TKey>>) => MaybePromise<TReturn>;
172
+ } & FnRouteOptions<TDef, TKey>): FnProcedure<TDef, TKey, Schema<unknown, unknown>, Schema<TReturn, TReturn>>;
173
+ <TReturn, TInput extends ZodType | undefined = undefined, TOut extends ZodType | undefined = undefined, TKey extends ProcedureKey<TDef> = TDef['default']>(options: {
174
+ input?: TInput;
175
+ output?: TOut;
176
+ handler: (params: HandlerParams<TDef, InputOf<TInput>, NoInfer<TKey>>) => MaybePromise<ReturnOf<TOut, TReturn>>;
177
+ } & FnRouteOptions<TDef, TKey>): FnProcedure<TDef, TKey, [
178
+ TInput
179
+ ] extends [ZodType] ? TInput : Schema<unknown, unknown>, [
180
+ TOut
181
+ ] extends [ZodType] ? TOut : Schema<TReturn, TReturn>>;
182
+ }
183
+ /** What `fn.completed` reports, and what `onCompleted` receives. */
184
+ export type FnCompletedEvent<TContext = unknown, TSpan = SpanLike> = {
185
+ name: string;
186
+ procedure: string;
187
+ durationMs: number;
188
+ success: boolean;
189
+ error: unknown;
190
+ input: unknown;
191
+ context: TContext;
192
+ span: TSpan | undefined;
193
+ meta: Record<string, unknown>;
194
+ };
195
+ export {};
196
+ //# sourceMappingURL=types.d.ts.map