ts-procedures 9.2.0 → 9.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ All notable changes to **ts-procedures** are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## Unreleased
8
+
9
+ ## 9.3.0 — 2026-06-25
10
+
11
+ ### Improved
12
+ - **DX: autocomplete for HTTP procedure schema channels.** `CreateHttp` and `CreateHttpStream` now surface `THttpReqInput` (and its alias `APIInput`) / `THttpResInput` inside `TCreateHttpConfig` so IDEs offer `pathParams | query | body | headers` (and `body | headers` for res) when writing `schema: { req: { ... }, res: { ... } }`. A small `HttpSchemaInput` helper keeps the public bag generics (`TReq`/`TRes`) while making literal keys visible. `satisfies APIInput` continues to work for extra checking. Same shapes are reused by `CreateHttpStream` for consistency.
13
+
7
14
  ## 9.2.0 — 2026-06-19
8
15
 
9
16
  Driven by downstream codegen feedback. Three classes of non-compiling generated
@@ -183,26 +183,15 @@ Registers a unary HTTP procedure (v8+). First-class surface for REST-style route
183
183
  ```typescript
184
184
  function CreateHttp<
185
185
  TName extends string,
186
- TReq extends { pathParams?: unknown; query?: unknown; body?: unknown; headers?: unknown } = {},
187
- TRes extends { body?: unknown; headers?: unknown } = {},
186
+ TReq extends THttpReqInput | undefined = undefined,
187
+ TRes extends THttpResInput | undefined = undefined,
188
188
  TErrorKey extends string = string
189
189
  >(
190
190
  name: TName,
191
- config: {
192
- path: string
193
- method: HttpMethod
194
- successStatus?: number
195
- scope?: string
196
- errors?: TErrorKey[]
197
- description?: string
198
- schema: {
199
- req?: TReq
200
- res?: TRes
201
- }
202
- },
191
+ config: TCreateHttpConfig<TReq, TRes, TErrorKey>,
203
192
  handler: (
204
193
  ctx: TContext & TLocalContext,
205
- req: { [K in keyof TReq]: Infer<TReq[K]> }
194
+ req: HttpReq<TReq>
206
195
  ) => Promise<HttpReturn<TRes>>
207
196
  ): {
208
197
  [K in TName]: typeof handler
@@ -215,7 +204,7 @@ function CreateHttp<
215
204
 
216
205
  - `config.path` — Route path. Supports Hono-style path params (`:id`).
217
206
  - `config.method` — HTTP method.
218
- - `config.schema.req` — Per-channel input schemas. Channels: `pathParams`, `query`, `body`, `headers`. Each channel independently validated.
207
+ - `config.schema.req` — Per-channel input schemas. The keys `pathParams`, `query`, `body`, `headers` are surfaced by `THttpReqInput` (and `APIInput`) for autocomplete and `satisfies` checks. Each channel independently validated.
219
208
  - `config.schema.res.body` — Response body schema (documentation; not runtime-validated).
220
209
  - `config.schema.res.headers` — When declared, handler **must** return `{ body, headers }` instead of the body bare. The builder reads `headers` and forwards them to the HTTP response.
221
210
  - `config.errors` — Taxonomy keys this route may emit (populates DocEnvelope for codegen).
@@ -1010,26 +999,40 @@ interface StreamHttpRouteDoc extends RPCConfig {
1010
999
  }
1011
1000
  ```
1012
1001
 
1013
- ### TCreateHttpConfig (v8)
1002
+ ### TCreateHttpConfig
1014
1003
 
1015
1004
  Config type for `CreateHttp`. HTTP fields are first-class — no `APIConfig` type parameter needed on the factory.
1016
1005
 
1006
+ The `schema.req` and `schema.res` use special input shapes (`THttpReqInput`, `THttpResInput`) so that object literals get autocomplete for the channel keys (`pathParams`, `query`, `body`, `headers`). `TReq` / `TRes` carry the user's concrete schema types for inference.
1007
+
1017
1008
  ```typescript
1018
- type TCreateHttpConfig<TReq, TRes, TErrorKey extends string = string> = {
1009
+ type TCreateHttpConfig<
1010
+ TReq extends THttpReqInput | undefined = undefined,
1011
+ TRes extends THttpResInput | undefined = undefined,
1012
+ TErrorKey extends string = string,
1013
+ > = {
1019
1014
  path: string
1020
1015
  method: HttpMethod
1021
1016
  successStatus?: number
1022
1017
  scope?: string
1023
1018
  errors?: TErrorKey[]
1024
1019
  description?: string
1025
- schema: {
1026
- req?: TReq // per-channel input: { pathParams?, query?, body?, headers? }
1027
- res?: TRes // structured response: { body?, headers? }
1020
+ schema?: {
1021
+ req?: HttpSchemaInput<THttpReqInput, TReq>
1022
+ res?: HttpSchemaInput<THttpResInput, TRes>
1028
1023
  }
1029
1024
  }
1030
1025
  ```
1031
1026
 
1032
- ### APIHttpRouteDoc (v8)
1027
+ `APIInput` (re-exported from `ts-procedures/http`) is an alias for `THttpReqInput` and is the recommended type for `satisfies` checks on `req`:
1028
+
1029
+ ```ts
1030
+ req: { pathParams: ..., query: ... } satisfies APIInput
1031
+ ```
1032
+
1033
+ See also `THttpReqInput`, `THttpResInput`, `HttpSchemaInput`, and `APIInput`.
1034
+
1035
+ ### APIHttpRouteDoc
1033
1036
 
1034
1037
  ```typescript
1035
1038
  interface APIHttpRouteDoc {
@@ -1,23 +1,13 @@
1
1
  import type { Infer, Prettify } from '../schema/json-schema.js';
2
2
  import type { FactoryRuntime } from './internal.js';
3
- import type { HttpMethod, ProcedureResult, TStreamContext } from './types.js';
4
- type HttpReq<TReq> = TReq extends Record<string, unknown> ? Prettify<{
5
- [K in keyof TReq]: Infer<TReq[K]>;
6
- }> : undefined;
7
- export declare function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext, any>): <TName extends string, TReq extends Record<string, unknown> | undefined, TYieldType, TReturnType = void, TResHeaders = undefined, TErrorKey extends string = string>(name: TName, config: {
3
+ import type { HttpMethod, HttpReq, ProcedureResult, THttpReqInput, THttpStreamSchema, TStreamContext } from './types.js';
4
+ export declare function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext, any>): <TName extends string, TReq extends THttpReqInput | undefined, TYieldType, TReturnType = void, TResHeaders = undefined, TErrorKey extends string = string>(name: TName, config: {
8
5
  path: string;
9
6
  method: HttpMethod;
10
7
  scope?: string;
11
8
  errors?: TErrorKey[];
12
9
  description?: string;
13
- schema: {
14
- req?: TReq;
15
- yield?: TYieldType;
16
- returnType?: TReturnType;
17
- res?: TResHeaders extends undefined ? undefined : {
18
- headers: TResHeaders;
19
- };
20
- };
10
+ schema: THttpStreamSchema<TReq, TYieldType, TReturnType, TResHeaders>;
21
11
  validateYields?: boolean;
22
12
  }, handler: TResHeaders extends undefined ? (ctx: Prettify<TContext & TStreamContext>, req: HttpReq<TReq>) => AsyncGenerator<Infer<TYieldType>, Infer<TReturnType> | void, unknown> : (ctx: Prettify<TContext & TStreamContext>, req: HttpReq<TReq>) => Promise<{
23
13
  headers: Infer<TResHeaders>;
@@ -47,4 +37,3 @@ export declare function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<T
47
37
  name: TName;
48
38
  kind: "http-stream";
49
39
  }>;
50
- export {};
@@ -1 +1 @@
1
- {"version":3,"file":"create-http-stream.js","sourceRoot":"","sources":["../../src/core/create-http-stream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-http-stream.js","sourceRoot":"","sources":["../../src/core/create-http-stream.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { Infer, Prettify } from '../schema/json-schema.js';
2
2
  import type { FactoryRuntime } from './internal.js';
3
- import type { ProcedureResult, TCreateHttpConfig, TLocalContext } from './types.js';
3
+ import type { HttpReq, ProcedureResult, TCreateHttpConfig, THttpReqInput, THttpResInput, TLocalContext } from './types.js';
4
4
  /**
5
5
  * The handler return shape is gated by the DECLARED response schema, not by
6
6
  * duck-typing the returned value — so domain objects that happen to have
@@ -22,13 +22,7 @@ export type HttpReturn<TRes> = TRes extends {
22
22
  } : TRes extends {
23
23
  body: infer B;
24
24
  } ? Infer<B> : void;
25
- type HttpReq<TReq> = TReq extends Record<string, unknown> ? Prettify<{
26
- [K in keyof TReq]: Infer<TReq[K]>;
27
- }> : undefined;
28
- export declare function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContext, any>): <TName extends string, TReq extends Record<string, unknown> | undefined = undefined, TRes extends {
29
- body?: unknown;
30
- headers?: unknown;
31
- } | undefined = undefined, TErrorKey extends string = string>(name: TName, config: TCreateHttpConfig<TReq, TRes, TErrorKey>, handler: (ctx: Prettify<TContext & TLocalContext>, req: HttpReq<TReq>) => Promise<HttpReturn<TRes>>) => ProcedureResult<TName, (ctx: Prettify<TContext>, req: HttpReq<TReq>) => Promise<HttpReturn<TRes>>, {
25
+ export declare function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContext, any>): <TName extends string, TReq extends THttpReqInput | undefined = undefined, TRes extends THttpResInput | undefined = undefined, TErrorKey extends string = string>(name: TName, config: TCreateHttpConfig<TReq, TRes, TErrorKey>, handler: (ctx: Prettify<TContext & TLocalContext>, req: HttpReq<TReq>) => Promise<HttpReturn<TRes>>) => ProcedureResult<TName, (ctx: Prettify<TContext>, req: HttpReq<TReq>) => Promise<HttpReturn<TRes>>, {
32
26
  path: string;
33
27
  method: import("./types.js").HttpMethod;
34
28
  successStatus?: number;
@@ -48,4 +42,3 @@ export declare function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContex
48
42
  name: TName;
49
43
  kind: "http";
50
44
  }>;
51
- export {};
@@ -1 +1 @@
1
- {"version":3,"file":"create-http.js","sourceRoot":"","sources":["../../src/core/create-http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-http.js","sourceRoot":"","sources":["../../src/core/create-http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,104 @@
1
+ import { Type } from 'typebox';
2
+ import { describe, expectTypeOf, test } from 'vitest';
3
+ import { Procedures } from './procedures.js';
4
+ // ---------------------------------------------------------------------------
5
+ // Type tests for HTTP input shape autocomplete / DX contract
6
+ // These guard the shapes that power IDE autocomplete for schema.req / schema.res.
7
+ // ---------------------------------------------------------------------------
8
+ describe('THttpReqInput / THttpResInput / APIInput', () => {
9
+ test('APIInput accepts only the four known channels', () => {
10
+ // Valid
11
+ const good = {
12
+ pathParams: Type.Object({ id: Type.String() }),
13
+ query: Type.Object({}),
14
+ body: Type.Object({}),
15
+ headers: Type.Object({}),
16
+ };
17
+ expectTypeOf(good).toEqualTypeOf();
18
+ // Invalid channel should not be assignable
19
+ // @ts-expect-error - unknown channel name
20
+ const _bad = { wrong: Type.Object({}) };
21
+ });
22
+ test('THttpResInput shape', () => {
23
+ const resShape = {
24
+ body: Type.Object({ ok: Type.Boolean() }),
25
+ headers: Type.Object({ 'x-foo': Type.String() }),
26
+ };
27
+ expectTypeOf(resShape).toEqualTypeOf();
28
+ });
29
+ });
30
+ describe('TCreateHttpConfig shapes for literals', () => {
31
+ test('config literal gets the channel keys from the shape (autocomplete contract)', () => {
32
+ const cfg = {
33
+ path: '/x',
34
+ method: 'get',
35
+ schema: {
36
+ req: {
37
+ // These keys should be suggested by the IDE
38
+ pathParams: Type.Object({ id: Type.String() }),
39
+ query: undefined,
40
+ },
41
+ res: {
42
+ body: Type.Object({}),
43
+ },
44
+ },
45
+ };
46
+ expectTypeOf(cfg).toEqualTypeOf();
47
+ });
48
+ test('using satisfies APIInput on req is valid and preserves specific types', () => {
49
+ const req = {
50
+ pathParams: Type.Object({ id: Type.String() }),
51
+ query: Type.Object({ q: Type.Optional(Type.String()) }),
52
+ };
53
+ const cfg = {
54
+ path: '/y',
55
+ method: 'get',
56
+ schema: { req },
57
+ };
58
+ // The assignment succeeded, proving the shape accepted the keys
59
+ // (specific inference of TReq from literal is a bonus)
60
+ });
61
+ });
62
+ describe('handler inference via HttpReq / HttpReturn', () => {
63
+ const procs = Procedures();
64
+ test('full req shape produces correctly typed handler param', () => {
65
+ const { Get } = procs.CreateHttp('Get', {
66
+ path: '/u/:id',
67
+ method: 'get',
68
+ schema: {
69
+ req: {
70
+ pathParams: Type.Object({ id: Type.String() }),
71
+ query: Type.Object({ verbose: Type.Optional(Type.Boolean()) }),
72
+ },
73
+ },
74
+ }, async (_ctx, req) => {
75
+ expectTypeOf(req).toEqualTypeOf();
76
+ // no res schema => void
77
+ return undefined;
78
+ });
79
+ expectTypeOf().toEqualTypeOf();
80
+ });
81
+ test('res with headers produces { body, headers } return shape', () => {
82
+ const { WithHeaders } = procs.CreateHttp('WithHeaders', {
83
+ path: '/h',
84
+ method: 'get',
85
+ schema: {
86
+ res: {
87
+ body: Type.Object({ data: Type.String() }),
88
+ headers: Type.Object({ 'x-trace': Type.String() }),
89
+ },
90
+ },
91
+ }, async () => ({ body: { data: 'hi' }, headers: { 'x-trace': 'abc' } }));
92
+ expectTypeOf().toEqualTypeOf();
93
+ });
94
+ test('no schema.res yields void (204-style)', () => {
95
+ const { Logout } = procs.CreateHttp('Logout', { path: '/logout', method: 'post' }, async () => undefined);
96
+ expectTypeOf().toEqualTypeOf();
97
+ });
98
+ });
99
+ // Smoke test that HttpReq and HttpSchemaInput are exported and usable
100
+ test('HttpReq and HttpSchemaInput are exported and usable', () => {
101
+ expectTypeOf().toHaveProperty('pathParams');
102
+ expectTypeOf().toHaveProperty('query');
103
+ });
104
+ //# sourceMappingURL=create-http.test-d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-http.test-d.js","sourceRoot":"","sources":["../../src/core/create-http.test-d.ts"],"names":[],"mappings":"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"}
@@ -70,10 +70,7 @@ export declare function Procedures<TContext = TNoContextProvided, TExtendedConfi
70
70
  yield?: import("../schema/compile.js").Validate;
71
71
  } | undefined;
72
72
  } & TExtendedConfig>;
73
- CreateHttp: <TName extends string, TReq extends Record<string, unknown> | undefined = undefined, TRes extends {
74
- body?: unknown;
75
- headers?: unknown;
76
- } | undefined = undefined, TErrorKey extends string = string>(name: TName, config: import("./types.js").TCreateHttpConfig<TReq, TRes, TErrorKey>, handler: (ctx: TContext & import("./types.js").TLocalContext extends infer T ? { [Key in keyof T]: T[Key]; } : never, req: TReq extends Record<string, unknown> ? { [Key_1 in keyof { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }]: { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }[Key_1]; } : undefined) => Promise<import("./create-http.js").HttpReturn<TRes>>) => import("./types.js").ProcedureResult<TName, (ctx: { [Key in keyof TContext]: TContext[Key]; }, req: TReq extends Record<string, unknown> ? { [Key_1 in keyof { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }]: { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }[Key_1]; } : undefined) => Promise<import("./create-http.js").HttpReturn<TRes>>, {
73
+ CreateHttp: <TName extends string, TReq extends import("./types.js").THttpReqInput | undefined = undefined, TRes extends import("./types.js").THttpResInput | undefined = undefined, TErrorKey extends string = string>(name: TName, config: import("./types.js").TCreateHttpConfig<TReq, TRes, TErrorKey>, handler: (ctx: TContext & import("./types.js").TLocalContext extends infer T ? { [Key in keyof T]: T[Key]; } : never, req: import("./types.js").HttpReq<TReq>) => Promise<import("./create-http.js").HttpReturn<TRes>>) => import("./types.js").ProcedureResult<TName, (ctx: { [Key in keyof TContext]: TContext[Key]; }, req: import("./types.js").HttpReq<TReq>) => Promise<import("./create-http.js").HttpReturn<TRes>>, {
77
74
  path: string;
78
75
  method: import("./types.js").HttpMethod;
79
76
  successStatus?: number;
@@ -93,29 +90,22 @@ export declare function Procedures<TContext = TNoContextProvided, TExtendedConfi
93
90
  name: TName;
94
91
  kind: "http";
95
92
  }>;
96
- CreateHttpStream: <TName extends string, TReq extends Record<string, unknown> | undefined, TYieldType, TReturnType = void, TResHeaders = undefined, TErrorKey extends string = string>(name: TName, config: {
93
+ CreateHttpStream: <TName extends string, TReq extends import("./types.js").THttpReqInput | undefined, TYieldType, TReturnType = void, TResHeaders = undefined, TErrorKey extends string = string>(name: TName, config: {
97
94
  path: string;
98
95
  method: import("./types.js").HttpMethod;
99
96
  scope?: string;
100
97
  errors?: TErrorKey[] | undefined;
101
98
  description?: string;
102
- schema: {
103
- req?: TReq | undefined;
104
- yield?: TYieldType | undefined;
105
- returnType?: TReturnType | undefined;
106
- res?: (TResHeaders extends undefined ? undefined : {
107
- headers: TResHeaders;
108
- }) | undefined;
109
- };
99
+ schema: import("./types.js").THttpStreamSchema<TReq, TYieldType, TReturnType, TResHeaders>;
110
100
  validateYields?: boolean;
111
101
  }, handler: TResHeaders extends undefined ? (ctx: TContext & import("./types.js").TLocalContext & {
112
102
  signal: AbortSignal;
113
- } extends infer T ? { [Key in keyof T]: T[Key]; } : never, req: TReq extends Record<string, unknown> ? { [Key_1 in keyof { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }]: { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }[Key_1]; } : undefined) => AsyncGenerator<import("../exports.js").Infer<TYieldType>, void | import("../exports.js").Infer<TReturnType>, unknown> : (ctx: TContext & import("./types.js").TLocalContext & {
103
+ } extends infer T ? { [Key in keyof T]: T[Key]; } : never, req: import("./types.js").HttpReq<TReq>) => AsyncGenerator<import("../exports.js").Infer<TYieldType>, void | import("../exports.js").Infer<TReturnType>, unknown> : (ctx: TContext & import("./types.js").TLocalContext & {
114
104
  signal: AbortSignal;
115
- } extends infer T ? { [Key in keyof T]: T[Key]; } : never, req: TReq extends Record<string, unknown> ? { [Key_1 in keyof { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }]: { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }[Key_1]; } : undefined) => Promise<{
105
+ } extends infer T ? { [Key in keyof T]: T[Key]; } : never, req: import("./types.js").HttpReq<TReq>) => Promise<{
116
106
  headers: import("../exports.js").Infer<TResHeaders>;
117
107
  stream: AsyncGenerator<import("../exports.js").Infer<TYieldType>, void | import("../exports.js").Infer<TReturnType>, unknown>;
118
- }>) => import("./types.js").ProcedureResult<TName, (ctx: { [Key in keyof TContext]: TContext[Key]; }, req: TReq extends Record<string, unknown> ? { [Key_1 in keyof { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }]: { [K in keyof TReq]: import("../exports.js").Infer<TReq[K]>; }[Key_1]; } : undefined) => Promise<{
108
+ }>) => import("./types.js").ProcedureResult<TName, (ctx: { [Key in keyof TContext]: TContext[Key]; }, req: import("./types.js").HttpReq<TReq>) => Promise<{
119
109
  stream: AsyncGenerator<import("../exports.js").Infer<TYieldType>, void | import("../exports.js").Infer<TReturnType>, unknown>;
120
110
  initialHeaders?: Record<string, string>;
121
111
  }>, {
@@ -1,7 +1,7 @@
1
1
  import type * as AJV from 'ajv';
2
2
  import type { ProcedureError } from './errors.js';
3
3
  import type { SchemaAdapter } from '../schema/adapter.js';
4
- import type { TJSONSchema } from '../schema/json-schema.js';
4
+ import type { Infer, Prettify, TJSONSchema } from '../schema/json-schema.js';
5
5
  import type { Validate } from '../schema/compile.js';
6
6
  export type TNoContextProvided = unknown;
7
7
  /**
@@ -61,7 +61,52 @@ export type TStreamProcedureRegistration<TContext = unknown, TExtendedConfig = u
61
61
  handler: (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>;
62
62
  };
63
63
  export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head';
64
- export type TCreateHttpConfig<TReq, TRes, TErrorKey extends string = string> = {
64
+ /**
65
+ * Canonical shape for `schema.req` channels.
66
+ * Used both to constrain generics and to provide autocomplete when writing
67
+ * `CreateHttp` / `CreateHttpStream` config objects.
68
+ *
69
+ * Note: RPC `Create` / `CreateStream` use a flatter `schema: { params?, yieldType?, returnType? }`
70
+ * directly (no extra container generic), so they don't need an equivalent named input shape.
71
+ */
72
+ export type THttpReqInput = {
73
+ pathParams?: unknown;
74
+ query?: unknown;
75
+ body?: unknown;
76
+ headers?: unknown;
77
+ };
78
+ /**
79
+ * Canonical shape for `schema.res` on CreateHttp (CreateHttpStream only supports `headers`).
80
+ */
81
+ export type THttpResInput = {
82
+ body?: unknown;
83
+ headers?: unknown;
84
+ };
85
+ /**
86
+ * @internal Helper to surface literal channel keys for autocomplete
87
+ * while still threading the user's concrete generic schemas through for inference.
88
+ */
89
+ export type HttpSchemaInput<Base, T> = Base & (T extends undefined ? {} : T);
90
+ /**
91
+ * @internal Shared helper for typing the destructured `req` argument passed to
92
+ * CreateHttp / CreateHttpStream handlers. Mirrors the per-channel Infer.
93
+ */
94
+ export type HttpReq<TReq> = TReq extends Record<string, unknown> ? Prettify<{
95
+ [K in keyof TReq]: Infer<TReq[K]>;
96
+ }> : undefined;
97
+ /**
98
+ * Shape used for the `schema` option of `CreateHttpStream`.
99
+ * Reuses the same req input shape as CreateHttp for autocomplete consistency.
100
+ */
101
+ export type THttpStreamSchema<TReq extends THttpReqInput | undefined = undefined, TYield = unknown, TReturn = void, TResHeaders = undefined> = {
102
+ req?: HttpSchemaInput<THttpReqInput, TReq>;
103
+ yield?: TYield;
104
+ returnType?: TReturn;
105
+ res?: TResHeaders extends undefined ? undefined : {
106
+ headers: TResHeaders;
107
+ };
108
+ };
109
+ export type TCreateHttpConfig<TReq extends THttpReqInput | undefined = undefined, TRes extends THttpResInput | undefined = undefined, TErrorKey extends string = string> = {
65
110
  path: string;
66
111
  method: HttpMethod;
67
112
  successStatus?: number;
@@ -70,8 +115,8 @@ export type TCreateHttpConfig<TReq, TRes, TErrorKey extends string = string> = {
70
115
  description?: string;
71
116
  /** Optional — a no-input, no-output route (e.g. a 204 logout) may omit it. */
72
117
  schema?: {
73
- req?: TReq;
74
- res?: TRes;
118
+ req?: HttpSchemaInput<THttpReqInput, TReq>;
119
+ res?: HttpSchemaInput<THttpResInput, TRes>;
75
120
  };
76
121
  };
77
122
  export type THttpProcedureRegistration<TContext = unknown> = {
@@ -12,7 +12,7 @@
12
12
  * - `ts-procedures/codegen` — client code generation (also: npx ts-procedures-codegen)
13
13
  */
14
14
  export { Procedures } from './core/procedures.js';
15
- export type { ProceduresOptions, ProcedureKind, AnyProcedureRegistration, TProcedureRegistration, TStreamProcedureRegistration, THttpProcedureRegistration, THttpStreamProcedureRegistration, TCreateHttpConfig, TLocalContext, TStreamContext, TNoContextProvided, HttpMethod, } from './core/types.js';
15
+ export type { ProceduresOptions, ProcedureKind, AnyProcedureRegistration, TProcedureRegistration, TStreamProcedureRegistration, THttpProcedureRegistration, THttpStreamProcedureRegistration, TCreateHttpConfig, THttpReqInput, THttpResInput, THttpStreamSchema, HttpSchemaInput, HttpReq, TLocalContext, TStreamContext, TNoContextProvided, HttpMethod, } from './core/types.js';
16
16
  export type { HttpReturn } from './core/create-http.js';
17
17
  export { ProcedureError, ProcedureValidationError, ProcedureRegistrationError, ProcedureYieldValidationError, } from './core/errors.js';
18
18
  export type { ProcedureErrorKind } from './core/errors.js';
@@ -1 +1 @@
1
- {"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8DAA8D;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAgBjD,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAKvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE1D,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AASlF,uCAAuC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA"}
1
+ {"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8DAA8D;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAqBjD,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAKvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE1D,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AASlF,uCAAuC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA"}
@@ -6,7 +6,7 @@
6
6
  * guarantees byte-identical generated output vs v8. Do not change field names,
7
7
  * optionality, or nesting without re-validating the codegen goldens.
8
8
  */
9
- import type { ProcedureKind } from '../core/types.js';
9
+ import type { ProcedureKind, THttpReqInput } from '../core/types.js';
10
10
  import type { ErrorTaxonomy } from './errors/taxonomy.js';
11
11
  /**
12
12
  * @typeParam TErrorKey - Union of valid taxonomy keys. Defaults to `string`
@@ -85,6 +85,9 @@ export interface APIHttpRouteDoc {
85
85
  * Constrains `schema.req` channel names to valid HTTP input sources.
86
86
  * Use with `satisfies` to catch typos at compile time:
87
87
  *
88
+ * The same shape also powers autocomplete inside `schema.req` when using
89
+ * CreateHttp / CreateHttpStream.
90
+ *
88
91
  * @example
89
92
  * schema: {
90
93
  * req: {
@@ -93,17 +96,8 @@ export interface APIHttpRouteDoc {
93
96
  * } satisfies APIInput
94
97
  * }
95
98
  */
96
- export type APIInput<T extends {
97
- pathParams?: unknown;
98
- query?: unknown;
99
- body?: unknown;
100
- headers?: unknown;
101
- } = {
102
- pathParams?: unknown;
103
- query?: unknown;
104
- body?: unknown;
105
- headers?: unknown;
106
- }> = T;
99
+ export type APIInput<T extends THttpReqInput = THttpReqInput> = T;
100
+ export type { THttpReqInput, THttpResInput } from '../core/types.js';
107
101
  export interface StreamHttpRouteDoc extends RPCConfig {
108
102
  kind: 'stream';
109
103
  name: string;
package/docs/core.md CHANGED
@@ -74,6 +74,7 @@ const { UpdateUser } = CreateHttp(
74
74
  **Rules:**
75
75
  - `schema.req` and `schema.params` are **mutually exclusive** — `schema.params` belongs to RPC procedures (`Create`/`CreateStream`), `schema.req` to HTTP procedures. Using `schema.params` on `CreateHttp` throws `ProcedureRegistrationError`.
76
76
  - Valid channels are `pathParams`, `query`, `body`, and `headers`; each is validated independently with per-channel error messages.
77
+ - The channel names are provided by `THttpReqInput` (and its alias `APIInput` from `ts-procedures/http`) so that IDEs offer autocomplete when writing `schema: { req: { ... } }`. Use `satisfies APIInput` for additional typo protection on the keys.
77
78
  - `schema.res.body` documents the response body (drives codegen); omit `schema.res` entirely for 204-style no-content routes.
78
79
  - Factory-level `http.pathPrefix` / `http.scope` defaults apply; per-route config wins.
79
80
  - For HTTP streaming routes, use `CreateHttpStream` — same `schema.req` channels plus `schema.yield`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-procedures",
3
- "version": "9.2.0",
3
+ "version": "9.3.0",
4
4
  "description": "A TypeScript RPC framework that creates type-safe, schema-validated procedure calls with a single function definition. Define your procedures once and get full type inference, runtime validation, and framework integration hooks.",
5
5
  "main": "build/exports.js",
6
6
  "types": "build/exports.d.ts",
@@ -11,11 +11,15 @@ import {
11
11
  validateReqChannels,
12
12
  } from './internal.js'
13
13
  import type { FactoryRuntime } from './internal.js'
14
- import type { HttpMethod, ProcedureResult, TStreamContext, THttpStreamProcedureRegistration } from './types.js'
15
-
16
- type HttpReq<TReq> = TReq extends Record<string, unknown>
17
- ? Prettify<{ [K in keyof TReq]: Infer<TReq[K]> }>
18
- : undefined
14
+ import type {
15
+ HttpMethod,
16
+ HttpReq,
17
+ ProcedureResult,
18
+ THttpReqInput,
19
+ THttpStreamSchema,
20
+ TStreamContext,
21
+ THttpStreamProcedureRegistration,
22
+ } from './types.js'
19
23
 
20
24
  export function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext, any>) {
21
25
  /**
@@ -29,7 +33,7 @@ export function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext,
29
33
  */
30
34
  return function CreateHttpStream<
31
35
  TName extends string,
32
- TReq extends Record<string, unknown> | undefined,
36
+ TReq extends THttpReqInput | undefined,
33
37
  TYieldType,
34
38
  TReturnType = void,
35
39
  TResHeaders = undefined,
@@ -42,12 +46,7 @@ export function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext,
42
46
  scope?: string
43
47
  errors?: TErrorKey[]
44
48
  description?: string
45
- schema: {
46
- req?: TReq
47
- yield?: TYieldType
48
- returnType?: TReturnType
49
- res?: TResHeaders extends undefined ? undefined : { headers: TResHeaders }
50
- }
49
+ schema: THttpStreamSchema<TReq, TYieldType, TReturnType, TResHeaders>
51
50
  validateYields?: boolean
52
51
  },
53
52
  handler: TResHeaders extends undefined
@@ -0,0 +1,154 @@
1
+ import { Type } from 'typebox'
2
+ import { describe, expectTypeOf, test } from 'vitest'
3
+
4
+ import { Procedures } from './procedures.js'
5
+ import type { HttpReturn } from './create-http.js'
6
+ import type {
7
+ HttpReq,
8
+ HttpSchemaInput,
9
+ TCreateHttpConfig,
10
+ THttpReqInput,
11
+ THttpResInput,
12
+ } from './types.js'
13
+ import type { APIInput } from '../server/types.js'
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Type tests for HTTP input shape autocomplete / DX contract
17
+ // These guard the shapes that power IDE autocomplete for schema.req / schema.res.
18
+ // ---------------------------------------------------------------------------
19
+
20
+ describe('THttpReqInput / THttpResInput / APIInput', () => {
21
+ test('APIInput accepts only the four known channels', () => {
22
+ // Valid
23
+ const good: APIInput = {
24
+ pathParams: Type.Object({ id: Type.String() }),
25
+ query: Type.Object({}),
26
+ body: Type.Object({}),
27
+ headers: Type.Object({}),
28
+ }
29
+ expectTypeOf(good).toEqualTypeOf<APIInput>()
30
+
31
+ // Invalid channel should not be assignable
32
+ // @ts-expect-error - unknown channel name
33
+ const _bad: APIInput = { wrong: Type.Object({}) as any }
34
+ })
35
+
36
+ test('THttpResInput shape', () => {
37
+ const resShape: THttpResInput = {
38
+ body: Type.Object({ ok: Type.Boolean() }),
39
+ headers: Type.Object({ 'x-foo': Type.String() }),
40
+ }
41
+ expectTypeOf(resShape).toEqualTypeOf<THttpResInput>()
42
+ })
43
+ })
44
+
45
+ describe('TCreateHttpConfig shapes for literals', () => {
46
+ test('config literal gets the channel keys from the shape (autocomplete contract)', () => {
47
+ const cfg: TCreateHttpConfig = {
48
+ path: '/x',
49
+ method: 'get',
50
+ schema: {
51
+ req: {
52
+ // These keys should be suggested by the IDE
53
+ pathParams: Type.Object({ id: Type.String() }),
54
+ query: undefined,
55
+ },
56
+ res: {
57
+ body: Type.Object({}),
58
+ },
59
+ },
60
+ }
61
+ expectTypeOf(cfg).toEqualTypeOf<TCreateHttpConfig>()
62
+ })
63
+
64
+ test('using satisfies APIInput on req is valid and preserves specific types', () => {
65
+ const req = {
66
+ pathParams: Type.Object({ id: Type.String() }),
67
+ query: Type.Object({ q: Type.Optional(Type.String()) }),
68
+ } satisfies APIInput
69
+
70
+ const cfg: TCreateHttpConfig<typeof req> = {
71
+ path: '/y',
72
+ method: 'get',
73
+ schema: { req },
74
+ }
75
+
76
+ // The assignment succeeded, proving the shape accepted the keys
77
+ // (specific inference of TReq from literal is a bonus)
78
+ })
79
+ })
80
+
81
+ describe('handler inference via HttpReq / HttpReturn', () => {
82
+ const procs = Procedures()
83
+
84
+ test('full req shape produces correctly typed handler param', () => {
85
+ const { Get } = procs.CreateHttp(
86
+ 'Get',
87
+ {
88
+ path: '/u/:id',
89
+ method: 'get',
90
+ schema: {
91
+ req: {
92
+ pathParams: Type.Object({ id: Type.String() }),
93
+ query: Type.Object({ verbose: Type.Optional(Type.Boolean()) }),
94
+ },
95
+ },
96
+ },
97
+ async (_ctx, req) => {
98
+ expectTypeOf(req).toEqualTypeOf<{
99
+ pathParams: { id: string }
100
+ query: { verbose?: boolean }
101
+ }>()
102
+ // no res schema => void
103
+ return undefined
104
+ }
105
+ )
106
+
107
+ type Handler = typeof Get
108
+ expectTypeOf<Parameters<Handler>[1]>().toEqualTypeOf<{
109
+ pathParams: { id: string }
110
+ query: { verbose?: boolean }
111
+ }>()
112
+ })
113
+
114
+ test('res with headers produces { body, headers } return shape', () => {
115
+ const { WithHeaders } = procs.CreateHttp(
116
+ 'WithHeaders',
117
+ {
118
+ path: '/h',
119
+ method: 'get',
120
+ schema: {
121
+ res: {
122
+ body: Type.Object({ data: Type.String() }),
123
+ headers: Type.Object({ 'x-trace': Type.String() }),
124
+ },
125
+ },
126
+ },
127
+ async () => ({ body: { data: 'hi' }, headers: { 'x-trace': 'abc' } })
128
+ )
129
+
130
+ type R = Awaited<ReturnType<typeof WithHeaders>>
131
+ expectTypeOf<R>().toEqualTypeOf<{ body: { data: string }; headers: { 'x-trace': string } }>()
132
+ })
133
+
134
+ test('no schema.res yields void (204-style)', () => {
135
+ const { Logout } = procs.CreateHttp(
136
+ 'Logout',
137
+ { path: '/logout', method: 'post' },
138
+ async () => undefined
139
+ )
140
+
141
+ type R = Awaited<ReturnType<typeof Logout>>
142
+ expectTypeOf<R>().toEqualTypeOf<void>()
143
+ })
144
+ })
145
+
146
+ // Smoke test that HttpReq and HttpSchemaInput are exported and usable
147
+ test('HttpReq and HttpSchemaInput are exported and usable', () => {
148
+ type ExampleReq = HttpSchemaInput<THttpReqInput, { pathParams: { id: string } }>
149
+ expectTypeOf<ExampleReq>().toHaveProperty('pathParams')
150
+
151
+ // HttpReq resolves through Infer; we just check it doesn't error and has shape
152
+ type H = HttpReq<{ query: { q?: string } }>
153
+ expectTypeOf<H>().toHaveProperty('query')
154
+ })
@@ -10,7 +10,15 @@ import {
10
10
  validateReqChannels,
11
11
  } from './internal.js'
12
12
  import type { FactoryRuntime } from './internal.js'
13
- import type { ProcedureResult, TCreateHttpConfig, TLocalContext, THttpProcedureRegistration } from './types.js'
13
+ import type {
14
+ HttpReq,
15
+ ProcedureResult,
16
+ TCreateHttpConfig,
17
+ THttpProcedureRegistration,
18
+ THttpReqInput,
19
+ THttpResInput,
20
+ TLocalContext,
21
+ } from './types.js'
14
22
 
15
23
  /**
16
24
  * The handler return shape is gated by the DECLARED response schema, not by
@@ -26,10 +34,6 @@ export type HttpReturn<TRes> =
26
34
  : TRes extends { body: infer B } ? Infer<B>
27
35
  : void
28
36
 
29
- type HttpReq<TReq> = TReq extends Record<string, unknown>
30
- ? Prettify<{ [K in keyof TReq]: Infer<TReq[K]> }>
31
- : undefined
32
-
33
37
  export function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContext, any>) {
34
38
  /**
35
39
  * Defines a REST-shaped HTTP procedure with per-channel input validation
@@ -40,8 +44,8 @@ export function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContext, any>)
40
44
  */
41
45
  return function CreateHttp<
42
46
  TName extends string,
43
- TReq extends Record<string, unknown> | undefined = undefined,
44
- TRes extends { body?: unknown; headers?: unknown } | undefined = undefined,
47
+ TReq extends THttpReqInput | undefined = undefined,
48
+ TRes extends THttpResInput | undefined = undefined,
45
49
  TErrorKey extends string = string,
46
50
  >(
47
51
  name: TName,
package/src/core/types.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type * as AJV from 'ajv'
2
2
  import type { ProcedureError } from './errors.js'
3
3
  import type { SchemaAdapter } from '../schema/adapter.js'
4
- import type { TJSONSchema } from '../schema/json-schema.js'
4
+ import type { Infer, Prettify, TJSONSchema } from '../schema/json-schema.js'
5
5
  import type { Validate } from '../schema/compile.js'
6
6
 
7
7
  export type TNoContextProvided = unknown
@@ -69,7 +69,64 @@ export type TStreamProcedureRegistration<TContext = unknown, TExtendedConfig = u
69
69
 
70
70
  export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
71
71
 
72
- export type TCreateHttpConfig<TReq, TRes, TErrorKey extends string = string> = {
72
+ /**
73
+ * Canonical shape for `schema.req` channels.
74
+ * Used both to constrain generics and to provide autocomplete when writing
75
+ * `CreateHttp` / `CreateHttpStream` config objects.
76
+ *
77
+ * Note: RPC `Create` / `CreateStream` use a flatter `schema: { params?, yieldType?, returnType? }`
78
+ * directly (no extra container generic), so they don't need an equivalent named input shape.
79
+ */
80
+ export type THttpReqInput = {
81
+ pathParams?: unknown
82
+ query?: unknown
83
+ body?: unknown
84
+ headers?: unknown
85
+ }
86
+
87
+ /**
88
+ * Canonical shape for `schema.res` on CreateHttp (CreateHttpStream only supports `headers`).
89
+ */
90
+ export type THttpResInput = {
91
+ body?: unknown
92
+ headers?: unknown
93
+ }
94
+
95
+ /**
96
+ * @internal Helper to surface literal channel keys for autocomplete
97
+ * while still threading the user's concrete generic schemas through for inference.
98
+ */
99
+ export type HttpSchemaInput<Base, T> = Base & (T extends undefined ? {} : T)
100
+
101
+ /**
102
+ * @internal Shared helper for typing the destructured `req` argument passed to
103
+ * CreateHttp / CreateHttpStream handlers. Mirrors the per-channel Infer.
104
+ */
105
+ export type HttpReq<TReq> = TReq extends Record<string, unknown>
106
+ ? Prettify<{ [K in keyof TReq]: Infer<TReq[K]> }>
107
+ : undefined
108
+
109
+ /**
110
+ * Shape used for the `schema` option of `CreateHttpStream`.
111
+ * Reuses the same req input shape as CreateHttp for autocomplete consistency.
112
+ */
113
+ export type THttpStreamSchema<
114
+ TReq extends THttpReqInput | undefined = undefined,
115
+ TYield = unknown,
116
+ TReturn = void,
117
+ TResHeaders = undefined,
118
+ > = {
119
+ req?: HttpSchemaInput<THttpReqInput, TReq>
120
+ yield?: TYield
121
+ returnType?: TReturn
122
+ res?: TResHeaders extends undefined ? undefined : { headers: TResHeaders }
123
+ }
124
+
125
+ export type TCreateHttpConfig<
126
+ TReq extends THttpReqInput | undefined = undefined,
127
+ TRes extends THttpResInput | undefined = undefined,
128
+ TErrorKey extends string = string,
129
+ > = {
73
130
  path: string
74
131
  method: HttpMethod
75
132
  successStatus?: number
@@ -78,8 +135,10 @@ export type TCreateHttpConfig<TReq, TRes, TErrorKey extends string = string> = {
78
135
  description?: string
79
136
  /** Optional — a no-input, no-output route (e.g. a 204 logout) may omit it. */
80
137
  schema?: {
81
- req?: TReq
82
- res?: TRes
138
+ // HttpSchemaInput surfaces the channel keys for IDE autocomplete / excess-property checking
139
+ // while the generic part carries the caller's concrete schema (for Infer + handler typing).
140
+ req?: HttpSchemaInput<THttpReqInput, TReq>
141
+ res?: HttpSchemaInput<THttpResInput, TRes>
83
142
  }
84
143
  }
85
144
 
package/src/exports.ts CHANGED
@@ -23,6 +23,11 @@ export type {
23
23
  THttpProcedureRegistration,
24
24
  THttpStreamProcedureRegistration,
25
25
  TCreateHttpConfig,
26
+ THttpReqInput,
27
+ THttpResInput,
28
+ THttpStreamSchema,
29
+ HttpSchemaInput,
30
+ HttpReq,
26
31
  TLocalContext,
27
32
  TStreamContext,
28
33
  TNoContextProvided,
@@ -6,7 +6,7 @@
6
6
  * guarantees byte-identical generated output vs v8. Do not change field names,
7
7
  * optionality, or nesting without re-validating the codegen goldens.
8
8
  */
9
- import type { ProcedureKind } from '../core/types.js'
9
+ import type { ProcedureKind, THttpReqInput } from '../core/types.js'
10
10
  import type { ErrorTaxonomy } from './errors/taxonomy.js'
11
11
 
12
12
  /**
@@ -97,6 +97,9 @@ export interface APIHttpRouteDoc {
97
97
  * Constrains `schema.req` channel names to valid HTTP input sources.
98
98
  * Use with `satisfies` to catch typos at compile time:
99
99
  *
100
+ * The same shape also powers autocomplete inside `schema.req` when using
101
+ * CreateHttp / CreateHttpStream.
102
+ *
100
103
  * @example
101
104
  * schema: {
102
105
  * req: {
@@ -105,17 +108,10 @@ export interface APIHttpRouteDoc {
105
108
  * } satisfies APIInput
106
109
  * }
107
110
  */
108
- export type APIInput<T extends {
109
- pathParams?: unknown
110
- query?: unknown
111
- body?: unknown
112
- headers?: unknown
113
- } = {
114
- pathParams?: unknown
115
- query?: unknown
116
- body?: unknown
117
- headers?: unknown
118
- }> = T
111
+ export type APIInput<T extends THttpReqInput = THttpReqInput> = T
112
+
113
+ // Re-export the canonical input shapes (useful for advanced typing or custom wrappers)
114
+ export type { THttpReqInput, THttpResInput } from '../core/types.js'
119
115
 
120
116
  export interface StreamHttpRouteDoc extends RPCConfig {
121
117
  kind: 'stream'