orpc-fn 0.1.0 → 0.3.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 (156) hide show
  1. package/README.md +70 -33
  2. package/dist/assemble-procedure.d.ts +13 -0
  3. package/dist/assemble-procedure.d.ts.map +1 -0
  4. package/dist/assemble-procedure.js +29 -0
  5. package/dist/assemble-procedure.js.map +1 -0
  6. package/dist/bound-call.d.ts +1 -1
  7. package/dist/bound-call.d.ts.map +1 -1
  8. package/dist/bound-call.js.map +1 -1
  9. package/dist/cli.d.ts +3 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +56 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/client.d.ts +4 -2
  14. package/dist/client.d.ts.map +1 -1
  15. package/dist/client.js +4 -2
  16. package/dist/client.js.map +1 -1
  17. package/dist/compatibility.d.ts +16 -0
  18. package/dist/compatibility.d.ts.map +1 -0
  19. package/dist/compatibility.js +4 -0
  20. package/dist/compatibility.js.map +1 -0
  21. package/dist/create-fn.d.ts +30 -34
  22. package/dist/create-fn.d.ts.map +1 -1
  23. package/dist/create-fn.js +177 -121
  24. package/dist/create-fn.js.map +1 -1
  25. package/dist/expo.d.ts +11 -25
  26. package/dist/expo.d.ts.map +1 -1
  27. package/dist/expo.js +25 -30
  28. package/dist/expo.js.map +1 -1
  29. package/dist/hono.d.ts +12 -4
  30. package/dist/hono.d.ts.map +1 -1
  31. package/dist/hono.js +29 -12
  32. package/dist/hono.js.map +1 -1
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -2
  36. package/dist/index.js.map +1 -1
  37. package/dist/json-schema.d.ts +2 -0
  38. package/dist/json-schema.d.ts.map +1 -1
  39. package/dist/json-schema.js +11 -0
  40. package/dist/json-schema.js.map +1 -1
  41. package/dist/live/codec.d.ts +6 -1
  42. package/dist/live/codec.d.ts.map +1 -1
  43. package/dist/live/codec.js +14 -3
  44. package/dist/live/codec.js.map +1 -1
  45. package/dist/live/fn-live.d.ts +67 -22
  46. package/dist/live/fn-live.d.ts.map +1 -1
  47. package/dist/live/fn-live.js +98 -16
  48. package/dist/live/fn-live.js.map +1 -1
  49. package/dist/live/index.d.ts +1 -1
  50. package/dist/live/index.d.ts.map +1 -1
  51. package/dist/live/index.js.map +1 -1
  52. package/dist/live/ioredis.d.ts +4 -1
  53. package/dist/live/ioredis.d.ts.map +1 -1
  54. package/dist/live/ioredis.js +21 -2
  55. package/dist/live/ioredis.js.map +1 -1
  56. package/dist/live/lifecycle.d.ts +7 -0
  57. package/dist/live/lifecycle.d.ts.map +1 -0
  58. package/dist/live/lifecycle.js +36 -0
  59. package/dist/live/lifecycle.js.map +1 -0
  60. package/dist/live/memory.d.ts.map +1 -1
  61. package/dist/live/memory.js +4 -0
  62. package/dist/live/memory.js.map +1 -1
  63. package/dist/live/pub-sub.d.ts +81 -13
  64. package/dist/live/pub-sub.d.ts.map +1 -1
  65. package/dist/live/pub-sub.js +437 -126
  66. package/dist/live/pub-sub.js.map +1 -1
  67. package/dist/live/redis-bun.d.ts +1 -1
  68. package/dist/live/redis-bun.d.ts.map +1 -1
  69. package/dist/live/redis-bun.js +3 -5
  70. package/dist/live/redis-bun.js.map +1 -1
  71. package/dist/live/redis.d.ts +2 -2
  72. package/dist/live/redis.d.ts.map +1 -1
  73. package/dist/live/redis.js +36 -9
  74. package/dist/live/redis.js.map +1 -1
  75. package/dist/live/transport.d.ts +5 -1
  76. package/dist/live/transport.d.ts.map +1 -1
  77. package/dist/live/transport.js.map +1 -1
  78. package/dist/logger.d.ts +2 -0
  79. package/dist/logger.d.ts.map +1 -1
  80. package/dist/logger.js +21 -0
  81. package/dist/logger.js.map +1 -1
  82. package/dist/mastra.d.ts.map +1 -1
  83. package/dist/mastra.js +16 -7
  84. package/dist/mastra.js.map +1 -1
  85. package/dist/mcp/core.d.ts +41 -0
  86. package/dist/mcp/core.d.ts.map +1 -0
  87. package/dist/mcp/core.js +61 -0
  88. package/dist/mcp/core.js.map +1 -0
  89. package/dist/mcp/sdk.d.ts +12 -0
  90. package/dist/mcp/sdk.d.ts.map +1 -0
  91. package/dist/mcp/sdk.js +14 -0
  92. package/dist/mcp/sdk.js.map +1 -0
  93. package/dist/mcp/server.d.ts +11 -0
  94. package/dist/mcp/server.d.ts.map +1 -0
  95. package/dist/mcp/server.js +13 -0
  96. package/dist/mcp/server.js.map +1 -0
  97. package/dist/mcp.d.ts +3 -2
  98. package/dist/mcp.d.ts.map +1 -1
  99. package/dist/mcp.js +22 -7
  100. package/dist/mcp.js.map +1 -1
  101. package/dist/meta.d.ts +15 -7
  102. package/dist/meta.d.ts.map +1 -1
  103. package/dist/meta.js +9 -2
  104. package/dist/meta.js.map +1 -1
  105. package/dist/otel.d.ts.map +1 -1
  106. package/dist/otel.js +77 -6
  107. package/dist/otel.js.map +1 -1
  108. package/dist/route-options.d.ts +22 -0
  109. package/dist/route-options.d.ts.map +1 -0
  110. package/dist/route-options.js +67 -0
  111. package/dist/route-options.js.map +1 -0
  112. package/dist/router.d.ts +13 -1
  113. package/dist/router.d.ts.map +1 -1
  114. package/dist/router.js +35 -0
  115. package/dist/router.js.map +1 -1
  116. package/dist/timing.d.ts +8 -0
  117. package/dist/timing.d.ts.map +1 -0
  118. package/dist/timing.js +4 -0
  119. package/dist/timing.js.map +1 -0
  120. package/dist/types.d.ts +96 -41
  121. package/dist/types.d.ts.map +1 -1
  122. package/dist/types.js +6 -2
  123. package/dist/types.js.map +1 -1
  124. package/package.json +28 -3
  125. package/src/assemble-procedure.ts +42 -0
  126. package/src/bound-call.ts +4 -3
  127. package/src/cli.ts +68 -0
  128. package/src/client.ts +9 -2
  129. package/src/compatibility.ts +39 -0
  130. package/src/create-fn.ts +295 -182
  131. package/src/expo.ts +49 -44
  132. package/src/hono.ts +36 -17
  133. package/src/index.ts +7 -2
  134. package/src/json-schema.ts +16 -0
  135. package/src/live/codec.ts +22 -3
  136. package/src/live/fn-live.ts +248 -69
  137. package/src/live/index.ts +2 -0
  138. package/src/live/ioredis.ts +22 -2
  139. package/src/live/lifecycle.ts +44 -0
  140. package/src/live/memory.ts +4 -0
  141. package/src/live/pub-sub.ts +619 -168
  142. package/src/live/redis-bun.ts +7 -5
  143. package/src/live/redis.ts +37 -11
  144. package/src/live/transport.ts +4 -1
  145. package/src/logger.ts +20 -0
  146. package/src/mastra.ts +15 -7
  147. package/src/mcp/core.ts +102 -0
  148. package/src/mcp/sdk.ts +36 -0
  149. package/src/mcp/server.ts +35 -0
  150. package/src/mcp.ts +48 -20
  151. package/src/meta.ts +40 -24
  152. package/src/otel.ts +77 -10
  153. package/src/route-options.ts +100 -0
  154. package/src/router.ts +58 -1
  155. package/src/timing.ts +5 -0
  156. package/src/types.ts +186 -62
package/dist/otel.js CHANGED
@@ -1,9 +1,16 @@
1
1
  import { isAsyncIteratorObject, onStreamEnd } from './stream.js';
2
2
  export function createTracing(otel) {
3
- const tracer = otel?.trace.getTracer('orpc-fn');
3
+ let tracer;
4
+ try {
5
+ tracer = otel?.trace.getTracer('orpc-fn');
6
+ }
7
+ catch {
8
+ /* Missing telemetry cannot prevent execution. */
9
+ }
4
10
  const end = (span, ...error) => {
5
11
  if (!span || !otel)
6
12
  return;
13
+ span = protectSpan(span);
7
14
  if (error.length === 0) {
8
15
  span.setStatus({ code: otel.SpanStatusCode.OK });
9
16
  }
@@ -23,11 +30,15 @@ export function createTracing(otel) {
23
30
  try {
24
31
  onSettled?.(span, ...error);
25
32
  }
33
+ catch {
34
+ /* Telemetry callbacks cannot replace the original result. */
35
+ }
26
36
  finally {
27
37
  end(span, ...error);
28
38
  }
29
39
  };
30
40
  const run = async (span, scope) => {
41
+ span = span ? protectSpan(span) : undefined;
31
42
  let result;
32
43
  try {
33
44
  result = await fn(span);
@@ -44,15 +55,56 @@ export function createTracing(otel) {
44
55
  };
45
56
  if (!tracer || !otel)
46
57
  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
- });
58
+ let started;
59
+ try {
60
+ return tracer.startActiveSpan(name, { kind: otel.SpanKind[kind] }, (span) => {
61
+ let active;
62
+ try {
63
+ active = otel.context.active();
64
+ }
65
+ catch {
66
+ /* Fall back to the span without scope restoration. */
67
+ }
68
+ started = run(span, (step) => {
69
+ let completed;
70
+ let failed;
71
+ try {
72
+ return otel.context.with(active, () => {
73
+ try {
74
+ const value = step();
75
+ completed = { value };
76
+ return value;
77
+ }
78
+ catch (error) {
79
+ failed = { error };
80
+ throw error;
81
+ }
82
+ });
83
+ }
84
+ catch {
85
+ if (completed)
86
+ return completed.value;
87
+ if (failed)
88
+ throw failed.error;
89
+ return step();
90
+ }
91
+ });
92
+ return started;
93
+ });
94
+ }
95
+ catch {
96
+ return started ?? run(undefined);
97
+ }
51
98
  },
52
99
  startSpan(name, kind, attributes) {
53
100
  if (!tracer || !otel)
54
101
  return undefined;
55
- return tracer.startSpan(name, { kind: otel.SpanKind[kind], attributes });
102
+ try {
103
+ return protectSpan(tracer.startSpan(name, { kind: otel.SpanKind[kind], attributes }));
104
+ }
105
+ catch {
106
+ return undefined;
107
+ }
56
108
  },
57
109
  end
58
110
  };
@@ -60,4 +112,23 @@ export function createTracing(otel) {
60
112
  export function errorMessageOf(error) {
61
113
  return error instanceof Error ? error.message : String(error);
62
114
  }
115
+ function protectSpan(span) {
116
+ return new Proxy(span, {
117
+ get(target, key) {
118
+ const value = Reflect.get(target, key, target);
119
+ if (typeof value !== 'function')
120
+ return value;
121
+ if (['setAttribute', 'setStatus', 'recordException', 'end'].includes(String(key)))
122
+ return (...args) => {
123
+ try {
124
+ return value.apply(target, args);
125
+ }
126
+ catch {
127
+ /* Ignore exporter failures. */
128
+ }
129
+ };
130
+ return value.bind(target);
131
+ }
132
+ });
133
+ }
63
134
  //# sourceMappingURL=otel.js.map
package/dist/otel.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"otel.js","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"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","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"]}
1
+ {"version":3,"file":"otel.js","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"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","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\tlet tracer: TracerLike<SpanLike> | undefined\n\ttry {\n\t\ttracer = otel?.trace.getTracer('orpc-fn')\n\t} catch {\n\t\t/* Missing telemetry cannot prevent execution. */\n\t}\n\tconst end: Tracing['end'] = (span, ...error) => {\n\t\tif (!span || !otel) return\n\t\tspan = protectSpan(span)\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} catch {\n\t\t\t\t\t/* Telemetry callbacks cannot replace the original result. */\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\tspan = span ? protectSpan(span) : undefined\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\tlet started: Promise<Awaited<ReturnType<typeof fn>>> | undefined\n\t\t\ttry {\n\t\t\t\treturn tracer.startActiveSpan(\n\t\t\t\t\tname,\n\t\t\t\t\t{ kind: otel.SpanKind[kind] },\n\t\t\t\t\t(span) => {\n\t\t\t\t\t\tlet active: unknown\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tactive = otel.context.active()\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* Fall back to the span without scope restoration. */\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstarted = run(span, (step) => {\n\t\t\t\t\t\t\tlet completed: { value: ReturnType<typeof step> } | undefined\n\t\t\t\t\t\t\tlet failed: { error: unknown } | undefined\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn otel.context.with(active as never, () => {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tconst value = step()\n\t\t\t\t\t\t\t\t\t\tcompleted = { value }\n\t\t\t\t\t\t\t\t\t\treturn value\n\t\t\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t\t\tfailed = { error }\n\t\t\t\t\t\t\t\t\t\tthrow error\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tif (completed) return completed.value\n\t\t\t\t\t\t\t\tif (failed) throw failed.error\n\t\t\t\t\t\t\t\treturn step()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn started\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t} catch {\n\t\t\t\treturn started ?? run(undefined)\n\t\t\t}\n\t\t},\n\t\tstartSpan(name, kind, attributes) {\n\t\t\tif (!tracer || !otel) return undefined\n\t\t\ttry {\n\t\t\t\treturn protectSpan(\n\t\t\t\t\ttracer.startSpan(name, { kind: otel.SpanKind[kind], attributes })\n\t\t\t\t)\n\t\t\t} catch {\n\t\t\t\treturn undefined\n\t\t\t}\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\nfunction protectSpan<T extends SpanLike>(span: T): T {\n\treturn new Proxy(span, {\n\t\tget(target, key) {\n\t\t\tconst value = Reflect.get(target, key, target)\n\t\t\tif (typeof value !== 'function') return value\n\t\t\tif (\n\t\t\t\t['setAttribute', 'setStatus', 'recordException', 'end'].includes(\n\t\t\t\t\tString(key)\n\t\t\t\t)\n\t\t\t)\n\t\t\t\treturn (...args: unknown[]) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn value.apply(target, args)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* Ignore exporter failures. */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\treturn value.bind(target)\n\t\t}\n\t})\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { type AnySchema, type ErrorMap } from '@orpc/contract';
2
+ type Resolver = (params: {
3
+ input: unknown;
4
+ context: unknown;
5
+ signal: AbortSignal | undefined;
6
+ }) => unknown;
7
+ /** Runtime validation for JavaScript callers and spread route options. */
8
+ export declare function normalizeRoute(options: Record<string, unknown>, isGuard: (key: string) => boolean): {
9
+ handler: (params: Record<PropertyKey, unknown>) => unknown;
10
+ input: AnySchema | undefined;
11
+ output: AnySchema | undefined;
12
+ name: string;
13
+ procedure: string | undefined;
14
+ meta: Record<string, unknown>;
15
+ stream: boolean;
16
+ errors: ErrorMap | undefined;
17
+ guardResolvers: Record<string, Resolver>;
18
+ route: Record<string, unknown>;
19
+ guardChecks: [string, unknown][];
20
+ };
21
+ export {};
22
+ //# sourceMappingURL=route-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-options.d.ts","sourceRoot":"","sources":["../src/route-options.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EACd,KAAK,QAAQ,EAEb,MAAM,gBAAgB,CAAA;AAGvB,KAAK,QAAQ,GAAG,CAAC,MAAM,EAAE;IACxB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,WAAW,GAAG,SAAS,CAAA;CAC/B,KAAK,OAAO,CAAA;AAab,0EAA0E;AAC1E,wBAAgB,cAAc,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO;aA4DZ,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,KAAK,OAAO;;;;;UAKpD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;YAEtB,QAAQ,GAAG,SAAS;oBACJ,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;;;EAI3D"}
@@ -0,0 +1,67 @@
1
+ import { getEventIteratorSchemaDetails } from '@orpc/contract';
2
+ import { ROUTE_OPTION_KEYS } from './types.js';
3
+ function schemaOption(schema, name) {
4
+ if (schema === undefined)
5
+ return undefined;
6
+ if (typeof schema !== 'object' ||
7
+ schema === null ||
8
+ !('~standard' in schema) ||
9
+ typeof schema['~standard']?.validate !== 'function')
10
+ throw new TypeError(`orpc-fn: ${name} must be a Standard Schema`);
11
+ return schema;
12
+ }
13
+ /** Runtime validation for JavaScript callers and spread route options. */
14
+ export function normalizeRoute(options, isGuard) {
15
+ const { handler, input: rawInput, output: rawOutput, name, procedure, meta: appMeta = {}, stream, errors, guardResolvers = {}, ...rest } = options;
16
+ if (typeof name !== 'string' || !name || typeof handler !== 'function')
17
+ throw new TypeError('orpc-fn: routes require a name and handler');
18
+ if (procedure !== undefined && typeof procedure !== 'string')
19
+ throw new TypeError('orpc-fn: procedure must be a configured key');
20
+ if (typeof appMeta !== 'object' || appMeta === null || Array.isArray(appMeta))
21
+ throw new TypeError('orpc-fn: meta must be an object');
22
+ if (stream !== undefined && typeof stream !== 'boolean')
23
+ throw new TypeError('orpc-fn: stream must be boolean');
24
+ if (typeof guardResolvers !== 'object' ||
25
+ guardResolvers === null ||
26
+ Array.isArray(guardResolvers))
27
+ throw new TypeError('orpc-fn: guardResolvers must be an object');
28
+ const input = schemaOption(rawInput, 'input');
29
+ const output = schemaOption(rawOutput, 'output');
30
+ const route = {};
31
+ const guardChecks = [];
32
+ for (const [option, value] of Object.entries(rest)) {
33
+ if (ROUTE_OPTION_KEYS.includes(option))
34
+ route[option] = value;
35
+ else if (isGuard(option)) {
36
+ if (value !== undefined)
37
+ guardChecks.push([option, value]);
38
+ }
39
+ else
40
+ throw new TypeError(`orpc-fn: unknown route option "${option}"; put application metadata in meta`);
41
+ }
42
+ const iterator = getEventIteratorSchemaDetails(output) !== undefined;
43
+ if (stream === false && iterator)
44
+ throw new TypeError('orpc-fn: an event iterator schema cannot declare stream: false');
45
+ for (const [guard, resolver] of Object.entries(guardResolvers)) {
46
+ if (!isGuard(guard))
47
+ throw new TypeError(`orpc-fn: unknown guard resolver "${guard}"`);
48
+ if (typeof resolver !== 'function')
49
+ throw new TypeError(`orpc-fn: guard resolver "${guard}" must be a function`);
50
+ if (guardChecks.some(([key]) => key === guard))
51
+ throw new TypeError(`orpc-fn: guard "${guard}" has both a value and resolver`);
52
+ }
53
+ return {
54
+ handler: handler,
55
+ input,
56
+ output,
57
+ name,
58
+ procedure,
59
+ meta: appMeta,
60
+ stream: stream === true || iterator,
61
+ errors: errors,
62
+ guardResolvers: guardResolvers,
63
+ route,
64
+ guardChecks
65
+ };
66
+ }
67
+ //# sourceMappingURL=route-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-options.js","sourceRoot":"","sources":["../src/route-options.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AnySchema,\n\ttype ErrorMap,\n\tgetEventIteratorSchemaDetails\n} from '@orpc/contract'\nimport { ROUTE_OPTION_KEYS } from './types.js'\n\ntype Resolver = (params: {\n\tinput: unknown\n\tcontext: unknown\n\tsignal: AbortSignal | undefined\n}) => unknown\nfunction schemaOption(schema: unknown, name: string): AnySchema | undefined {\n\tif (schema === undefined) return undefined\n\tif (\n\t\ttypeof schema !== 'object' ||\n\t\tschema === null ||\n\t\t!('~standard' in schema) ||\n\t\ttypeof (schema as AnySchema)['~standard']?.validate !== 'function'\n\t)\n\t\tthrow new TypeError(`orpc-fn: ${name} must be a Standard Schema`)\n\treturn schema as AnySchema\n}\n\n/** Runtime validation for JavaScript callers and spread route options. */\nexport function normalizeRoute(\n\toptions: Record<string, unknown>,\n\tisGuard: (key: string) => boolean\n) {\n\tconst {\n\t\thandler,\n\t\tinput: rawInput,\n\t\toutput: rawOutput,\n\t\tname,\n\t\tprocedure,\n\t\tmeta: appMeta = {},\n\t\tstream,\n\t\terrors,\n\t\tguardResolvers = {},\n\t\t...rest\n\t} = options\n\tif (typeof name !== 'string' || !name || typeof handler !== 'function')\n\t\tthrow new TypeError('orpc-fn: routes require a name and handler')\n\tif (procedure !== undefined && typeof procedure !== 'string')\n\t\tthrow new TypeError('orpc-fn: procedure must be a configured key')\n\tif (typeof appMeta !== 'object' || appMeta === null || Array.isArray(appMeta))\n\t\tthrow new TypeError('orpc-fn: meta must be an object')\n\tif (stream !== undefined && typeof stream !== 'boolean')\n\t\tthrow new TypeError('orpc-fn: stream must be boolean')\n\tif (\n\t\ttypeof guardResolvers !== 'object' ||\n\t\tguardResolvers === null ||\n\t\tArray.isArray(guardResolvers)\n\t)\n\t\tthrow new TypeError('orpc-fn: guardResolvers must be an object')\n\tconst input = schemaOption(rawInput, 'input')\n\tconst output = schemaOption(rawOutput, 'output')\n\tconst route: Record<string, unknown> = {}\n\tconst guardChecks: Array<[string, unknown]> = []\n\tfor (const [option, value] of Object.entries(rest)) {\n\t\tif ((ROUTE_OPTION_KEYS as readonly string[]).includes(option))\n\t\t\troute[option] = value\n\t\telse if (isGuard(option)) {\n\t\t\tif (value !== undefined) guardChecks.push([option, value])\n\t\t} else\n\t\t\tthrow new TypeError(\n\t\t\t\t`orpc-fn: unknown route option \"${option}\"; put application metadata in meta`\n\t\t\t)\n\t}\n\tconst iterator = getEventIteratorSchemaDetails(output) !== undefined\n\tif (stream === false && iterator)\n\t\tthrow new TypeError(\n\t\t\t'orpc-fn: an event iterator schema cannot declare stream: false'\n\t\t)\n\tfor (const [guard, resolver] of Object.entries(guardResolvers)) {\n\t\tif (!isGuard(guard))\n\t\t\tthrow new TypeError(`orpc-fn: unknown guard resolver \"${guard}\"`)\n\t\tif (typeof resolver !== 'function')\n\t\t\tthrow new TypeError(\n\t\t\t\t`orpc-fn: guard resolver \"${guard}\" must be a function`\n\t\t\t)\n\t\tif (guardChecks.some(([key]) => key === guard))\n\t\t\tthrow new TypeError(\n\t\t\t\t`orpc-fn: guard \"${guard}\" has both a value and resolver`\n\t\t\t)\n\t}\n\treturn {\n\t\thandler: handler as (params: Record<PropertyKey, unknown>) => unknown,\n\t\tinput,\n\t\toutput,\n\t\tname,\n\t\tprocedure,\n\t\tmeta: appMeta as Record<string, unknown>,\n\t\tstream: stream === true || iterator,\n\t\terrors: errors as ErrorMap | undefined,\n\t\tguardResolvers: guardResolvers as Record<string, Resolver>,\n\t\troute,\n\t\tguardChecks\n\t}\n}\n"]}
package/dist/router.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AnyRouter } from '@orpc/server';
1
+ import { type AnyRouter } from '@orpc/server';
2
2
  /**
3
3
  * Creates a router object from routes or nested routers.
4
4
  * Simply returns the routes as a const object for type inference.
@@ -10,4 +10,16 @@ import type { AnyRouter } from '@orpc/server';
10
10
  * ```
11
11
  */
12
12
  export declare function createRouter<T extends AnyRouter>(routes: T): Readonly<T>;
13
+ /** Serialize these paths to a frontend module; this helper belongs on the server. */
14
+ export declare function createStreamManifest(router: AnyRouter): readonly (readonly string[])[];
15
+ /** Resolve lazy routers before generating the same serializable path manifest. */
16
+ export declare function createStreamManifestAsync(router: AnyRouter): Promise<readonly (readonly string[])[]>;
17
+ /**
18
+ * Deterministic module source for a stream manifest: paths sorted, `json`
19
+ * as an array, `ts` as `export const <name>`. Used by `orpc-fn stream-manifest`.
20
+ */
21
+ export declare function renderStreamManifest(paths: readonly (readonly string[])[], options: {
22
+ format: 'json' | 'ts';
23
+ name?: string | undefined;
24
+ }): string;
13
25
  //# sourceMappingURL=router.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EAId,MAAM,cAAc,CAAA;AAGrB;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAExE;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,SAAS,GACf,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAahC;AAED,kFAAkF;AAClF,wBAAsB,yBAAyB,CAC9C,MAAM,EAAE,SAAS,GACf,OAAO,CAAC,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,CASzC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,EACrC,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC3D,MAAM,CAUR"}
package/dist/router.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { resolveContractProcedures, traverseContractProcedures } from '@orpc/server';
2
+ import { readFnMeta } from './meta.js';
1
3
  /**
2
4
  * Creates a router object from routes or nested routers.
3
5
  * Simply returns the routes as a const object for type inference.
@@ -11,4 +13,37 @@
11
13
  export function createRouter(routes) {
12
14
  return routes;
13
15
  }
16
+ /** Serialize these paths to a frontend module; this helper belongs on the server. */
17
+ export function createStreamManifest(router) {
18
+ const paths = [];
19
+ const unresolved = traverseContractProcedures({ router, path: [] }, ({ contract, path }) => {
20
+ if (readFnMeta(contract).stream)
21
+ paths.push([...path]);
22
+ });
23
+ if (unresolved.length)
24
+ throw new TypeError('orpc-fn: lazy routers require createStreamManifestAsync');
25
+ return paths;
26
+ }
27
+ /** Resolve lazy routers before generating the same serializable path manifest. */
28
+ export async function createStreamManifestAsync(router) {
29
+ const paths = [];
30
+ await resolveContractProcedures({ router, path: [] }, ({ contract, path }) => {
31
+ if (readFnMeta(contract).stream)
32
+ paths.push([...path]);
33
+ });
34
+ return paths;
35
+ }
36
+ /**
37
+ * Deterministic module source for a stream manifest: paths sorted, `json`
38
+ * as an array, `ts` as `export const <name>`. Used by `orpc-fn stream-manifest`.
39
+ */
40
+ export function renderStreamManifest(paths, options) {
41
+ const lines = paths
42
+ .map((path) => `\t[${path.map((segment) => JSON.stringify(segment)).join(', ')}]`)
43
+ .sort();
44
+ const array = lines.length ? `[\n${lines.join(',\n')}\n]` : '[]';
45
+ if (options.format === 'json')
46
+ return `${array}\n`;
47
+ return `// Generated by \`orpc-fn stream-manifest\`. Do not edit.\nexport const ${options.name ?? 'streamPaths'}: readonly (readonly string[])[] = ${array}\n`;
48
+ }
14
49
  //# sourceMappingURL=router.js.map
@@ -1 +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"]}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,yBAAyB,EACzB,0BAA0B,EAC1B,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAsB,MAAS;IAC1D,OAAO,MAAM,CAAA;AACd,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,oBAAoB,CACnC,MAAiB;IAEjB,MAAM,KAAK,GAAe,EAAE,CAAA;IAC5B,MAAM,UAAU,GAAG,0BAA0B,CAC5C,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,EACpB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,IAAI,UAAU,CAAC,QAAwB,CAAC,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IACvE,CAAC,CACD,CAAA;IACD,IAAI,UAAU,CAAC,MAAM;QACpB,MAAM,IAAI,SAAS,CAClB,yDAAyD,CACzD,CAAA;IACF,OAAO,KAAK,CAAA;AACb,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC9C,MAAiB;IAEjB,MAAM,KAAK,GAAe,EAAE,CAAA;IAC5B,MAAM,yBAAyB,CAC9B,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,EACpB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,IAAI,UAAU,CAAC,QAAwB,CAAC,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IACvE,CAAC,CACD,CAAA;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CACnC,KAAqC,EACrC,OAA6D;IAE7D,MAAM,KAAK,GAAG,KAAK;SACjB,GAAG,CACH,CAAC,IAAI,EAAE,EAAE,CACR,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACnE;SACA,IAAI,EAAE,CAAA;IACR,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;IAChE,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,GAAG,KAAK,IAAI,CAAA;IAClD,OAAO,2EAA2E,OAAO,CAAC,IAAI,IAAI,aAAa,sCAAsC,KAAK,IAAI,CAAA;AAC/J,CAAC","sourcesContent":["import {\n\ttype AnyRouter,\n\ttype AnyProcedure,\n\tresolveContractProcedures,\n\ttraverseContractProcedures\n} from '@orpc/server'\nimport { readFnMeta } from './meta.js'\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\n/** Serialize these paths to a frontend module; this helper belongs on the server. */\nexport function createStreamManifest(\n\trouter: AnyRouter\n): readonly (readonly string[])[] {\n\tconst paths: string[][] = []\n\tconst unresolved = traverseContractProcedures(\n\t\t{ router, path: [] },\n\t\t({ contract, path }) => {\n\t\t\tif (readFnMeta(contract as AnyProcedure).stream) paths.push([...path])\n\t\t}\n\t)\n\tif (unresolved.length)\n\t\tthrow new TypeError(\n\t\t\t'orpc-fn: lazy routers require createStreamManifestAsync'\n\t\t)\n\treturn paths\n}\n\n/** Resolve lazy routers before generating the same serializable path manifest. */\nexport async function createStreamManifestAsync(\n\trouter: AnyRouter\n): Promise<readonly (readonly string[])[]> {\n\tconst paths: string[][] = []\n\tawait resolveContractProcedures(\n\t\t{ router, path: [] },\n\t\t({ contract, path }) => {\n\t\t\tif (readFnMeta(contract as AnyProcedure).stream) paths.push([...path])\n\t\t}\n\t)\n\treturn paths\n}\n\n/**\n * Deterministic module source for a stream manifest: paths sorted, `json`\n * as an array, `ts` as `export const <name>`. Used by `orpc-fn stream-manifest`.\n */\nexport function renderStreamManifest(\n\tpaths: readonly (readonly string[])[],\n\toptions: { format: 'json' | 'ts'; name?: string | undefined }\n): string {\n\tconst lines = paths\n\t\t.map(\n\t\t\t(path) =>\n\t\t\t\t`\\t[${path.map((segment) => JSON.stringify(segment)).join(', ')}]`\n\t\t)\n\t\t.sort()\n\tconst array = lines.length ? `[\\n${lines.join(',\\n')}\\n]` : '[]'\n\tif (options.format === 'json') return `${array}\\n`\n\treturn `// Generated by \\`orpc-fn stream-manifest\\`. Do not edit.\\nexport const ${options.name ?? 'streamPaths'}: readonly (readonly string[])[] = ${array}\\n`\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /** Internal bridge shared by the execution middleware and HTTP adapters. */
2
+ export declare const REQUEST_TIMING: unique symbol;
3
+ export type InvocationTiming = {
4
+ procedureMs: number;
5
+ calls: number;
6
+ };
7
+ export declare const RAW_INPUT: unique symbol;
8
+ //# sourceMappingURL=timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../src/timing.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,eAAO,MAAM,cAAc,eAAkC,CAAA;AAC7D,MAAM,MAAM,gBAAgB,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAErE,eAAO,MAAM,SAAS,eAA6B,CAAA"}
package/dist/timing.js ADDED
@@ -0,0 +1,4 @@
1
+ /** Internal bridge shared by the execution middleware and HTTP adapters. */
2
+ export const REQUEST_TIMING = Symbol('orpc-fn.requestTiming');
3
+ export const RAW_INPUT = Symbol('orpc-fn.rawInput');
4
+ //# sourceMappingURL=timing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.js","sourceRoot":"","sources":["../src/timing.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAA;AAG7D,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAA","sourcesContent":["/** Internal bridge shared by the execution middleware and HTTP adapters. */\nexport const REQUEST_TIMING = Symbol('orpc-fn.requestTiming')\nexport type InvocationTiming = { procedureMs: number; calls: number }\n\nexport const RAW_INPUT = Symbol('orpc-fn.rawInput')\n"]}
package/dist/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- import type { AnySchema, ErrorMap, InferSchemaInput, InferSchemaOutput, Meta, Schema } from '@orpc/contract';
1
+ import type { AnySchema, ErrorMap, InferSchemaInput, InferSchemaOutput, MergedErrorMap, Schema } from '@orpc/contract';
2
2
  import type { AnyProcedure, DecoratedProcedure, Procedure, Route } from '@orpc/server';
3
- import type { ZodType, z } from 'zod';
3
+ import type { createORPCErrorConstructorMap } from '@orpc/server';
4
+ import type { StoredFnMeta } from './meta.js';
5
+ import type { NativeErrors, NativeMeta } from './compatibility.js';
4
6
  import type { BoundCall } from './bound-call.js';
5
7
  import type { FnLogger } from './logger.js';
6
8
  import type { SpanLike } from './otel.js';
@@ -28,13 +30,13 @@ export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omi
28
30
  /** `fn()` options passed to oRPC's `.route()`; `RouteConfig` is derived from it. */
29
31
  export declare const ROUTE_OPTION_KEYS: readonly ["path", "method", "summary", "description", "deprecated", "successStatus", "successDescription", "inputStructure", "outputStructure", "tags"];
30
32
  /**
31
- * `fn()` option names owned by the route itself. Guard and meta keys must not
33
+ * `fn()` option names owned by the route itself. Guard keys must not
32
34
  * reuse them: such an option would be read as the route option and the guard
33
35
  * would never run.
34
36
  */
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"];
37
+ export declare const RESERVED_OPTION_KEYS: readonly ["path", "method", "summary", "description", "deprecated", "successStatus", "successDescription", "inputStructure", "outputStructure", "tags", "name", "procedure", "input", "output", "handler", "live", "operationId", "meta", "stream", "errors", "guardResolvers"];
36
38
  export type ReservedOptionKey = (typeof RESERVED_OPTION_KEYS)[number];
37
- /** @internal Builds one route from a `procedures` builder; shared by `fn` and `createPubSub`. */
39
+ /** @internal Builds one route from a selected native procedure builder. */
38
40
  export type BuildProcedure = (options: {
39
41
  name: string;
40
42
  procedure: string | undefined;
@@ -42,25 +44,33 @@ export type BuildProcedure = (options: {
42
44
  meta: Record<string, unknown>;
43
45
  input?: AnySchema | undefined;
44
46
  output?: AnySchema | undefined;
47
+ stream?: boolean;
48
+ errors?: ErrorMap;
45
49
  handler: (options: {
46
50
  input: unknown;
47
- context: Record<string, unknown>;
51
+ context: Record<string | symbol, unknown>;
48
52
  signal?: AbortSignal;
53
+ lastEventId?: string;
54
+ errors: ReturnType<typeof createORPCErrorConstructorMap>;
49
55
  }) => unknown;
50
56
  }) => AnyProcedure;
51
57
  /** Turns every key of `T` found in `K` into a readable type error. */
52
58
  export type RejectKeys<T, K extends PropertyKey, TMessage extends string> = {
53
59
  [P in Extract<keyof T, K>]: TMessage;
54
60
  };
61
+ /** Params every handler receives; extras may not reuse these names. */
62
+ type HandlerParamKey = 'input' | 'context' | 'call' | 'signal' | 'span' | 'logger' | 'errors' | 'lastEventId';
55
63
  /**
56
64
  * Handler extras must have known keys: an index signature would claim every
57
65
  * handler param. Intersected into `createFn`'s options to reject it.
58
66
  */
59
- export type FiniteExtras<T> = string extends keyof T ? {
67
+ export type FiniteExtras<T> = T extends unknown ? string extends keyof T ? {
60
68
  extras: 'orpc-fn: extras need known keys, not an index signature';
61
69
  } : number extends keyof T ? {
62
70
  extras: 'orpc-fn: extras need known keys, not an index signature';
63
- } : unknown;
71
+ } : Extract<keyof T, HandlerParamKey> extends never ? unknown : {
72
+ extras: 'orpc-fn: extras cannot shadow handler parameters';
73
+ } : never;
64
74
  type ProcedureOf<TBuilder> = TBuilder extends {
65
75
  handler(handler: never): infer TProcedure;
66
76
  } ? TProcedure : never;
@@ -68,16 +78,8 @@ type ProcedureOf<TBuilder> = TBuilder extends {
68
78
  export type InitialContextOf<TBuilder> = ProcedureContext<ProcedureOf<TBuilder>>;
69
79
  /** Context the handler sees, after the builder's middleware. */
70
80
  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
+ export type ErrorMapOf<TBuilder> = NativeErrors<ProcedureOf<TBuilder>>;
82
+ type MetaOf<TBuilder> = NativeMeta<ProcedureOf<TBuilder>>;
81
83
  /**
82
84
  * Everything `createFn` inferred from its options. Route types read from it;
83
85
  * apps rarely spell it out.
@@ -86,6 +88,7 @@ export type FnDefinition = {
86
88
  procedures: Record<string, BuilderLike>;
87
89
  default: string;
88
90
  extras: object;
91
+ extrasByProcedure?: Partial<Record<string, object>>;
89
92
  guards: object;
90
93
  meta: object;
91
94
  tag: string;
@@ -114,11 +117,15 @@ export type GuardParams<TContext, TMeta> = {
114
117
  * `fn({...})`; the guard runs only on routes that set it. Throw (usually an
115
118
  * `ORPCError`) to refuse the call.
116
119
  */
117
- export type Guard<TValue, TParams> = (value: TValue, params: TParams) => unknown;
120
+ export type Guard<TValue, TParams> = (value: TValue, params: TParams) => MaybePromise<void | boolean>;
118
121
  export type GuardOptions<TGuards> = {
119
122
  [K in keyof TGuards]?: TGuards[K] extends (value: infer TValue, params: never) => unknown ? TValue : never;
120
123
  };
121
- export type HandlerParams<TDef extends FnDefinition, TInput, TKey extends ProcedureKey<TDef>> = DistributiveOmit<TDef['extras'], 'input' | 'context' | 'call' | 'signal' | 'span' | 'logger'> & {
124
+ /** Match object spread: scoped extras replace shared keys, preserving unions. */
125
+ type MergeExtras<TShared, TScoped> = TShared extends unknown ? TScoped extends unknown ? Omit<TShared, keyof TScoped> & TScoped : never : never;
126
+ export type HandlerParams<TDef extends FnDefinition, TInput, TKey extends ProcedureKey<TDef>, TErrors extends ErrorMap = Record<never, never>> = DistributiveOmit<MergeExtras<TDef['extras'], TDef extends {
127
+ extrasByProcedure: infer E;
128
+ } ? TKey extends keyof E ? E[TKey] : object : object>, HandlerParamKey> & {
122
129
  input: TInput;
123
130
  context: FnContext<TDef, TKey>;
124
131
  /** Calls another procedure with this context and signal, in a child span. */
@@ -128,6 +135,8 @@ export type HandlerParams<TDef extends FnDefinition, TInput, TKey extends Proced
128
135
  /** Active OpenTelemetry span, undefined when `createFn({ otel })` is not set. */
129
136
  span: TDef['span'] | undefined;
130
137
  logger: TDef['logger'];
138
+ lastEventId: string | undefined;
139
+ errors: ReturnType<typeof createORPCErrorConstructorMap<MergedErrorMap<ErrorMapOf<TDef['procedures'][TKey]>, TErrors>>>;
131
140
  };
132
141
  /** Route options shared by every `fn()` overload. */
133
142
  export type RouteConfig<TTag extends string = string> = Pick<Route, Exclude<(typeof ROUTE_OPTION_KEYS)[number], 'tags'>> & {
@@ -135,7 +144,18 @@ export type RouteConfig<TTag extends string = string> = Pick<Route, Exclude<(typ
135
144
  name: string;
136
145
  tags?: TTag[];
137
146
  };
138
- export type FnRouteOptions<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>> = RouteConfig<TDef['tag']> & GuardOptions<TDef['guards']> & TDef['meta'] & ProcedureOption<TDef, TKey>;
147
+ export type FnRouteOptions<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>, TInput = unknown, TErrors extends ErrorMap = Record<never, never>> = RouteConfig<TDef['tag']> & GuardOptions<TDef['guards']> & {
148
+ meta?: TDef['meta'];
149
+ stream?: boolean;
150
+ errors?: TErrors;
151
+ guardResolvers?: {
152
+ [K in keyof TDef['guards']]?: (params: {
153
+ input: TInput;
154
+ context: FnContext<TDef, TKey>;
155
+ signal: AbortSignal | undefined;
156
+ }) => MaybePromise<GuardOptions<TDef['guards']>[K]>;
157
+ };
158
+ } & ProcedureOption<TDef, TKey>;
139
159
  /**
140
160
  * Which `createFn({ procedures })` builder a route uses. Optional when
141
161
  * `createFn` has a `default`; required on every route when it has none.
@@ -145,40 +165,42 @@ export type ProcedureOption<TDef extends FnDefinition, TKey extends ProcedureKey
145
165
  } : {
146
166
  procedure?: TKey;
147
167
  };
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;
168
+ export type FnProcedure<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>, TInputSchema extends Schema<unknown, unknown>, TOutputSchema extends Schema<unknown, unknown>, TErrors extends ErrorMap = Record<never, never>> = DecoratedProcedure<InitialContextOf<TDef['procedures'][TKey]>, FnContext<TDef, TKey>, TInputSchema, TOutputSchema, MergedErrorMap<ErrorMapOf<TDef['procedures'][TKey]>, TErrors>, MetaOf<TDef['procedures'][TKey]> & {
169
+ 'orpc-fn': StoredFnMeta<TDef['meta'], TKey>;
170
+ }>;
171
+ type InputOf<T> = [T] extends [AnySchema] ? InferSchemaOutput<T> : unknown;
172
+ type ReturnOf<TOut, TReturn> = [TOut] extends [AnySchema] ? InferSchemaInput<TOut> : TReturn;
151
173
  /** `fn()`: four overloads for input/output schema × inferred. */
152
174
  export interface Fn<TDef extends FnDefinition> {
153
- <TInput extends ZodType, TOut extends ZodType, TKey extends ProcedureKey<TDef> = TDef['default']>(options: {
175
+ <TInput extends AnySchema, TOut extends AnySchema, TKey extends ProcedureKey<TDef> = TDef['default'], TErrors extends ErrorMap = Record<never, never>>(options: {
154
176
  input: TInput;
155
177
  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: {
178
+ handler: (params: HandlerParams<TDef, InferSchemaOutput<TInput>, NoInfer<TKey>, TErrors>) => MaybePromise<InferSchemaInput<TOut>>;
179
+ } & FnRouteOptions<TDef, TKey, InferSchemaOutput<TInput>, TErrors>): FnProcedure<TDef, TKey, TInput, TOut, TErrors>;
180
+ <TInput extends AnySchema, TReturn, TKey extends ProcedureKey<TDef> = TDef['default'], TErrors extends ErrorMap = Record<never, never>>(options: {
159
181
  input: TInput;
160
182
  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: {
183
+ handler: (params: HandlerParams<TDef, InferSchemaOutput<TInput>, NoInfer<TKey>, TErrors>) => MaybePromise<TReturn>;
184
+ } & FnRouteOptions<TDef, TKey, InferSchemaOutput<TInput>, TErrors>): FnProcedure<TDef, TKey, TInput, Schema<TReturn, TReturn>, TErrors>;
185
+ <TOut extends AnySchema, TKey extends ProcedureKey<TDef> = TDef['default'], TErrors extends ErrorMap = Record<never, never>>(options: {
164
186
  input?: undefined;
165
187
  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: {
188
+ handler: (params: HandlerParams<TDef, unknown, NoInfer<TKey>, TErrors>) => MaybePromise<InferSchemaInput<TOut>>;
189
+ } & FnRouteOptions<TDef, TKey, unknown, TErrors>): FnProcedure<TDef, TKey, Schema<unknown, unknown>, TOut, TErrors>;
190
+ <TReturn, TKey extends ProcedureKey<TDef> = TDef['default'], TErrors extends ErrorMap = Record<never, never>>(options: {
169
191
  input?: undefined;
170
192
  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: {
193
+ handler: (params: HandlerParams<TDef, unknown, NoInfer<TKey>, TErrors>) => MaybePromise<TReturn>;
194
+ } & FnRouteOptions<TDef, TKey, unknown, TErrors>): FnProcedure<TDef, TKey, Schema<unknown, unknown>, Schema<TReturn, TReturn>, TErrors>;
195
+ <TReturn, TInput extends AnySchema | undefined = undefined, TOut extends AnySchema | undefined = undefined, TKey extends ProcedureKey<TDef> = TDef['default'], TErrors extends ErrorMap = Record<never, never>>(options: {
174
196
  input?: TInput;
175
197
  output?: TOut;
176
- handler: (params: HandlerParams<TDef, InputOf<TInput>, NoInfer<TKey>>) => MaybePromise<ReturnOf<TOut, TReturn>>;
177
- } & FnRouteOptions<TDef, TKey>): FnProcedure<TDef, TKey, [
198
+ handler: (params: HandlerParams<TDef, InputOf<TInput>, NoInfer<TKey>, TErrors>) => MaybePromise<ReturnOf<TOut, TReturn>>;
199
+ } & FnRouteOptions<TDef, TKey, InputOf<TInput>, TErrors>): FnProcedure<TDef, TKey, [
178
200
  TInput
179
- ] extends [ZodType] ? TInput : Schema<unknown, unknown>, [
201
+ ] extends [AnySchema] ? TInput : Schema<unknown, unknown>, [
180
202
  TOut
181
- ] extends [ZodType] ? TOut : Schema<TReturn, TReturn>>;
203
+ ] extends [AnySchema] ? TOut : Schema<TReturn, TReturn>, TErrors>;
182
204
  }
183
205
  /** What `fn.completed` reports, and what `onCompleted` receives. */
184
206
  export type FnCompletedEvent<TContext = unknown, TSpan = SpanLike> = {
@@ -186,11 +208,44 @@ export type FnCompletedEvent<TContext = unknown, TSpan = SpanLike> = {
186
208
  procedure: string;
187
209
  durationMs: number;
188
210
  success: boolean;
211
+ /** False when authentication or input validation prevented handler execution. */
212
+ handlerStarted: boolean;
189
213
  error: unknown;
190
214
  input: unknown;
191
215
  context: TContext;
192
216
  span: TSpan | undefined;
193
217
  meta: Record<string, unknown>;
194
218
  };
219
+ /** Factory hooks retain the builder key/context relationship. */
220
+ export type ProcedureHookParams<TProcedures extends Record<string, BuilderLike>, TExtra = object> = {
221
+ [K in keyof TProcedures & string]: TExtra & {
222
+ procedure: K;
223
+ context: CurrentContextOf<TProcedures[K]>;
224
+ };
225
+ }[keyof TProcedures & string];
226
+ export type CompletedHookEvent<TProcedures extends Record<string, BuilderLike>, TSpan> = {
227
+ [K in keyof TProcedures & string]: Omit<FnCompletedEvent<unknown, TSpan>, 'context' | 'procedure' | 'handlerStarted'> & {
228
+ procedure: K;
229
+ } & ({
230
+ handlerStarted: true;
231
+ context: CurrentContextOf<TProcedures[K]>;
232
+ } | {
233
+ handlerStarted: false;
234
+ context: InitialContextOf<TProcedures[K]>;
235
+ });
236
+ }[keyof TProcedures & string];
237
+ /**
238
+ * Intersected into each scoped extras function's return type: rejects index
239
+ * signatures and handler-parameter names at the offending property. A
240
+ * top-level conditional over the whole map broke inference when zero-argument
241
+ * and context-reading functions were mixed.
242
+ */
243
+ export type ScopedExtrasGuard<T> = (string extends keyof T ? {
244
+ 'orpc-fn: scoped extras need finite keys': never;
245
+ } : number extends keyof T ? {
246
+ 'orpc-fn: scoped extras need finite keys': never;
247
+ } : unknown) & {
248
+ [K in HandlerParamKey]?: never;
249
+ };
195
250
  export {};
196
251
  //# sourceMappingURL=types.d.ts.map