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 +7 -0
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +25 -22
- package/build/core/create-http-stream.d.ts +3 -14
- package/build/core/create-http-stream.js.map +1 -1
- package/build/core/create-http.d.ts +2 -9
- package/build/core/create-http.js.map +1 -1
- package/build/core/create-http.test-d.d.ts +1 -0
- package/build/core/create-http.test-d.js +104 -0
- package/build/core/create-http.test-d.js.map +1 -0
- package/build/core/procedures.d.ts +6 -16
- package/build/core/types.d.ts +49 -4
- package/build/exports.d.ts +1 -1
- package/build/exports.js.map +1 -1
- package/build/server/types.d.ts +6 -12
- package/docs/core.md +1 -0
- package/package.json +1 -1
- package/src/core/create-http-stream.ts +11 -12
- package/src/core/create-http.test-d.ts +154 -0
- package/src/core/create-http.ts +11 -7
- package/src/core/types.ts +63 -4
- package/src/exports.ts +5 -0
- package/src/server/types.ts +8 -12
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
|
|
187
|
-
TRes extends
|
|
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:
|
|
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.
|
|
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
|
|
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<
|
|
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
|
|
1027
|
-
res?: TRes
|
|
1020
|
+
schema?: {
|
|
1021
|
+
req?: HttpSchemaInput<THttpReqInput, TReq>
|
|
1022
|
+
res?: HttpSchemaInput<THttpResInput, TRes>
|
|
1028
1023
|
}
|
|
1029
1024
|
}
|
|
1030
1025
|
```
|
|
1031
1026
|
|
|
1032
|
-
|
|
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
|
-
|
|
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":"AAAA,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAE3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAC5E,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,eAAe,CAAA;
|
|
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
|
-
|
|
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":"AAAA,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAE3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAC5E,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,eAAe,CAAA;
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
}>, {
|
package/build/core/types.d.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
|
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
|
-
|
|
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> = {
|
package/build/exports.d.ts
CHANGED
|
@@ -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';
|
package/build/exports.js.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/build/server/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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 {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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
|
+
})
|
package/src/core/create-http.ts
CHANGED
|
@@ -10,7 +10,15 @@ import {
|
|
|
10
10
|
validateReqChannels,
|
|
11
11
|
} from './internal.js'
|
|
12
12
|
import type { FactoryRuntime } from './internal.js'
|
|
13
|
-
import type {
|
|
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
|
|
44
|
-
TRes extends
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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,
|
package/src/server/types.ts
CHANGED
|
@@ -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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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'
|