@ts-contract/core 1.0.0-alpha.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/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/lib/contract-builder.d.ts +18 -0
- package/dist/lib/contract-builder.d.ts.map +1 -0
- package/dist/lib/contract-builder.js +46 -0
- package/dist/lib/dsl.d.ts +33 -0
- package/dist/lib/dsl.d.ts.map +1 -0
- package/dist/lib/dsl.js +10 -0
- package/dist/lib/http-types.d.ts +20 -0
- package/dist/lib/http-types.d.ts.map +1 -0
- package/dist/lib/http-types.js +21 -0
- package/dist/lib/inference-utils.d.ts +59 -0
- package/dist/lib/inference-utils.d.ts.map +1 -0
- package/dist/lib/inference-utils.js +1 -0
- package/dist/lib/plugin-types.d.ts +34 -0
- package/dist/lib/plugin-types.d.ts.map +1 -0
- package/dist/lib/plugin-types.js +1 -0
- package/dist/lib/schema-types.d.ts +15 -0
- package/dist/lib/schema-types.d.ts.map +1 -0
- package/dist/lib/schema-types.js +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthew Brimmer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ContractDef } from './dsl';
|
|
2
|
+
import type { ContractPlugin, ApplyPlugins } from './plugin-types';
|
|
3
|
+
/**
|
|
4
|
+
* Builder that accumulates plugins and resolves them on .build().
|
|
5
|
+
*/
|
|
6
|
+
declare class ContractBuilder<C extends ContractDef, Ps extends readonly ContractPlugin[]> {
|
|
7
|
+
private readonly contract;
|
|
8
|
+
private readonly plugins;
|
|
9
|
+
constructor(contract: C, plugins: [...Ps]);
|
|
10
|
+
use<P extends ContractPlugin>(plugin: P): ContractBuilder<C, [...Ps, P]>;
|
|
11
|
+
build(): ApplyPlugins<C, Ps>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a contract builder that lets you compose plugins onto routes.
|
|
15
|
+
*/
|
|
16
|
+
export declare const initContract: <C extends ContractDef>(contract: C) => ContractBuilder<C, []>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=contract-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-builder.d.ts","sourceRoot":"","sources":["../../src/lib/contract-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAoBnE;;GAEG;AACH,cAAM,eAAe,CACnB,CAAC,SAAS,WAAW,EACrB,EAAE,SAAS,SAAS,cAAc,EAAE;IAGlC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC;IAGnC,GAAG,CAAC,CAAC,SAAS,cAAc,EAAE,MAAM,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IAIxE,KAAK,IAAI,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC;CAU7B;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,WAAW,EAChD,UAAU,CAAC,KACV,eAAe,CAAC,CAAC,EAAE,EAAE,CAEvB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isRouteDef } from './dsl';
|
|
2
|
+
/**
|
|
3
|
+
* Recursively walk a contract tree, applying a mapper function to each RouteDef.
|
|
4
|
+
*/
|
|
5
|
+
function mapRoutes(contract, mapper) {
|
|
6
|
+
const result = {};
|
|
7
|
+
for (const [key, value] of Object.entries(contract)) {
|
|
8
|
+
if (isRouteDef(value)) {
|
|
9
|
+
result[key] = mapper(value);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
result[key] = mapRoutes(value, mapper);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builder that accumulates plugins and resolves them on .build().
|
|
19
|
+
*/
|
|
20
|
+
class ContractBuilder {
|
|
21
|
+
contract;
|
|
22
|
+
plugins;
|
|
23
|
+
constructor(contract, plugins) {
|
|
24
|
+
this.contract = contract;
|
|
25
|
+
this.plugins = plugins;
|
|
26
|
+
}
|
|
27
|
+
use(plugin) {
|
|
28
|
+
return new ContractBuilder(this.contract, [...this.plugins, plugin]);
|
|
29
|
+
}
|
|
30
|
+
build() {
|
|
31
|
+
const plugins = this.plugins;
|
|
32
|
+
return mapRoutes(this.contract, (route) => {
|
|
33
|
+
const extended = {};
|
|
34
|
+
for (const plugin of plugins) {
|
|
35
|
+
Object.assign(extended, plugin.route(route));
|
|
36
|
+
}
|
|
37
|
+
return extended;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a contract builder that lets you compose plugins onto routes.
|
|
43
|
+
*/
|
|
44
|
+
export const initContract = (contract) => {
|
|
45
|
+
return new ContractBuilder(contract, []);
|
|
46
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Method, HttpStatusCodes } from './http-types';
|
|
2
|
+
import type { SchemaProtocol } from './schema-types';
|
|
3
|
+
/**
|
|
4
|
+
* Route definition
|
|
5
|
+
*/
|
|
6
|
+
export type RouteDef = {
|
|
7
|
+
method: Method;
|
|
8
|
+
path: string;
|
|
9
|
+
pathParams?: SchemaProtocol<any>;
|
|
10
|
+
query?: SchemaProtocol<any>;
|
|
11
|
+
headers?: Record<string, SchemaProtocol<any>>;
|
|
12
|
+
body?: SchemaProtocol<any>;
|
|
13
|
+
responses: Partial<Record<HttpStatusCodes, SchemaProtocol<any>>>;
|
|
14
|
+
summary?: string;
|
|
15
|
+
metadata?: Record<string, string | number | boolean>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Contract definition - maps route names to route definitions or nested contracts
|
|
19
|
+
*/
|
|
20
|
+
export interface ContractDef {
|
|
21
|
+
[key: string]: RouteDef | ContractDef;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Type guard to check if a value is a RouteDef (has method and path)
|
|
25
|
+
*/
|
|
26
|
+
export declare const isRouteDef: (value: RouteDef | ContractDef) => value is RouteDef;
|
|
27
|
+
/**
|
|
28
|
+
* Creates a new contract
|
|
29
|
+
*
|
|
30
|
+
* @returns {Contract}
|
|
31
|
+
*/
|
|
32
|
+
export declare const createContract: <C extends ContractDef>(contract: C) => C;
|
|
33
|
+
//# sourceMappingURL=dsl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsl.d.ts","sourceRoot":"","sources":["../../src/lib/dsl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACtD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;CACvC;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,QAAQ,GAAG,WAAW,KAAG,KAAK,IAAI,QAC9B,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,WAAW,EAAE,UAAU,CAAC,KAAG,CAC1D,CAAC"}
|
package/dist/lib/dsl.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard to check if a value is a RouteDef (has method and path)
|
|
3
|
+
*/
|
|
4
|
+
export const isRouteDef = (value) => 'method' in value && 'path' in value;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new contract
|
|
7
|
+
*
|
|
8
|
+
* @returns {Contract}
|
|
9
|
+
*/
|
|
10
|
+
export const createContract = (contract) => contract;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP methods
|
|
3
|
+
*/
|
|
4
|
+
export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
|
|
5
|
+
export declare const methods: readonly ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
|
|
6
|
+
export type HttpSuccessStatusCode = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226;
|
|
7
|
+
/**
|
|
8
|
+
* Successful HTTP status codes
|
|
9
|
+
*/
|
|
10
|
+
export declare const successfulHttpStatusCodes: readonly [200, 201, 202, 203, 204, 205, 206, 207, 208, 226];
|
|
11
|
+
/**
|
|
12
|
+
* All HTTP status codes
|
|
13
|
+
*/
|
|
14
|
+
export type HttpStatusCodes = 100 | 101 | 102 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 507 | 511;
|
|
15
|
+
export declare const httpStatusCodes: readonly [100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 307, 308, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 428, 429, 431, 451, 500, 501, 502, 503, 504, 505, 507, 511];
|
|
16
|
+
/**
|
|
17
|
+
* Error HTTP status codes (all except successful ones)
|
|
18
|
+
*/
|
|
19
|
+
export type ErrorHttpStatusCodes = Exclude<HttpStatusCodes, HttpSuccessStatusCode>;
|
|
20
|
+
//# sourceMappingURL=http-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-types.d.ts","sourceRoot":"","sources":["../../src/lib/http-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,GACd,KAAK,GACL,MAAM,GACN,KAAK,GACL,QAAQ,GACR,OAAO,GACP,SAAS,GACT,MAAM,CAAC;AAEX,eAAO,MAAM,OAAO,uEAQkB,CAAC;AAEvC,MAAM,MAAM,qBAAqB,GAC7B,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,CAAC;AAER;;GAEG;AACH,eAAO,MAAM,yBAAyB,6DAEe,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,CAAC;AAER,eAAO,MAAM,eAAe,6SAKmB,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,CACxC,eAAe,EACf,qBAAqB,CACtB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const methods = [
|
|
2
|
+
'GET',
|
|
3
|
+
'POST',
|
|
4
|
+
'PUT',
|
|
5
|
+
'DELETE',
|
|
6
|
+
'PATCH',
|
|
7
|
+
'OPTIONS',
|
|
8
|
+
'HEAD',
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Successful HTTP status codes
|
|
12
|
+
*/
|
|
13
|
+
export const successfulHttpStatusCodes = [
|
|
14
|
+
200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
|
|
15
|
+
];
|
|
16
|
+
export const httpStatusCodes = [
|
|
17
|
+
100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301,
|
|
18
|
+
302, 303, 304, 305, 307, 308, 400, 401, 402, 403, 404, 405, 406, 407, 408,
|
|
19
|
+
409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
|
|
20
|
+
424, 428, 429, 431, 451, 500, 501, 502, 503, 504, 505, 507, 511,
|
|
21
|
+
];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { RouteDef } from './dsl';
|
|
2
|
+
import type { InferSchema, StandardSchemaV1 } from './schema-types';
|
|
3
|
+
import type { HttpStatusCodes } from './http-types';
|
|
4
|
+
type InferOrUndefined<T> = T extends StandardSchemaV1<unknown, infer Out> ? Out : undefined;
|
|
5
|
+
/**
|
|
6
|
+
* Infer path parameters from a route
|
|
7
|
+
*/
|
|
8
|
+
export type InferPathParams<R extends RouteDef> = InferOrUndefined<R['pathParams']>;
|
|
9
|
+
/**
|
|
10
|
+
* Infer query parameters from a route
|
|
11
|
+
*/
|
|
12
|
+
export type InferQuery<R extends RouteDef> = InferOrUndefined<R['query']>;
|
|
13
|
+
/**
|
|
14
|
+
* Infer request body from a route
|
|
15
|
+
*/
|
|
16
|
+
export type InferBody<R extends RouteDef> = InferOrUndefined<R['body']>;
|
|
17
|
+
/**
|
|
18
|
+
* Infer request headers from a route
|
|
19
|
+
*/
|
|
20
|
+
export type InferHeaders<R extends RouteDef> = R['headers'] extends Record<string, StandardSchemaV1> ? {
|
|
21
|
+
[K in keyof R['headers']]: InferSchema<R['headers'][K]>;
|
|
22
|
+
} : undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Infer all response schemas as a mapped object keyed by status code
|
|
25
|
+
*/
|
|
26
|
+
export type InferResponseMap<R extends RouteDef> = {
|
|
27
|
+
[K in keyof R['responses']]: InferSchema<R['responses'][K]>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Infer a specific response body from a route by status code
|
|
31
|
+
*/
|
|
32
|
+
export type InferResponseBody<R extends RouteDef, S extends HttpStatusCodes> = R['responses'][S] extends StandardSchemaV1<unknown, infer Out> ? Out : never;
|
|
33
|
+
/**
|
|
34
|
+
* Infer all responses as a discriminated union of { status, body }
|
|
35
|
+
*/
|
|
36
|
+
export type InferResponses<R extends RouteDef> = {
|
|
37
|
+
[K in keyof R['responses'] & HttpStatusCodes]: R['responses'][K] extends StandardSchemaV1<unknown, infer Out> ? {
|
|
38
|
+
status: K;
|
|
39
|
+
body: Out;
|
|
40
|
+
} : never;
|
|
41
|
+
}[keyof R['responses'] & HttpStatusCodes];
|
|
42
|
+
/**
|
|
43
|
+
* Merge path, query, body, and header parameters into a single type
|
|
44
|
+
*/
|
|
45
|
+
export type MergeArgs<P, Q, B, H> = (P extends undefined ? object : {
|
|
46
|
+
params: P;
|
|
47
|
+
}) & (Q extends undefined ? object : {
|
|
48
|
+
query: Q;
|
|
49
|
+
}) & (B extends undefined ? object : {
|
|
50
|
+
body: B;
|
|
51
|
+
}) & (H extends undefined ? object : {
|
|
52
|
+
headers: H;
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Infer all arguments from a route
|
|
56
|
+
*/
|
|
57
|
+
export type InferArgs<R extends RouteDef> = MergeArgs<InferPathParams<R>, InferQuery<R>, InferBody<R>, InferHeaders<R>>;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=inference-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inference-utils.d.ts","sourceRoot":"","sources":["../../src/lib/inference-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RouteDef, ContractDef } from './dsl';
|
|
2
|
+
/**
|
|
3
|
+
* Plugin type registry — plugins register their per-route return types here
|
|
4
|
+
* via declaration merging.
|
|
5
|
+
*/
|
|
6
|
+
export interface PluginTypeRegistry<R> {
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A contract plugin that adds utility methods to each route.
|
|
10
|
+
*/
|
|
11
|
+
export interface ContractPlugin<Name extends string = string> {
|
|
12
|
+
name: Name;
|
|
13
|
+
route: (route: RouteDef) => Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Look up a plugin's return type from the registry for a given route.
|
|
17
|
+
*/
|
|
18
|
+
type PluginReturnFor<Name extends string, R extends RouteDef> = Name extends keyof PluginTypeRegistry<R> ? PluginTypeRegistry<R>[Name] : object;
|
|
19
|
+
/**
|
|
20
|
+
* Merge return types from all plugins in a tuple for a given route.
|
|
21
|
+
*/
|
|
22
|
+
export type MergePluginReturns<R extends RouteDef, Ps extends readonly ContractPlugin[]> = Ps extends readonly [
|
|
23
|
+
infer First extends ContractPlugin,
|
|
24
|
+
...infer Rest extends ContractPlugin[]
|
|
25
|
+
] ? PluginReturnFor<First['name'], R> & MergePluginReturns<R, Rest> : object;
|
|
26
|
+
/**
|
|
27
|
+
* Recursively apply plugins to a contract, producing a mapped type
|
|
28
|
+
* where each RouteDef is replaced with the merged plugin return types.
|
|
29
|
+
*/
|
|
30
|
+
export type ApplyPlugins<C, Ps extends readonly ContractPlugin[]> = {
|
|
31
|
+
[K in keyof C]: C[K] extends RouteDef ? MergePluginReturns<C[K], Ps> : C[K] extends ContractDef ? ApplyPlugins<C[K], Ps> : never;
|
|
32
|
+
};
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=plugin-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-types.d.ts","sourceRoot":"","sources":["../../src/lib/plugin-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEnD;;;GAGG;AAEH,MAAM,WAAW,kBAAkB,CAAC,CAAC;CAAI;AAEzC;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IAC1D,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,KAAK,eAAe,CAClB,IAAI,SAAS,MAAM,EACnB,CAAC,SAAS,QAAQ,IAChB,IAAI,SAAS,MAAM,kBAAkB,CAAC,CAAC,CAAC,GACxC,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAC3B,MAAM,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,kBAAkB,CAC5B,CAAC,SAAS,QAAQ,EAClB,EAAE,SAAS,SAAS,cAAc,EAAE,IAClC,EAAE,SAAS,SAAS;IACtB,MAAM,KAAK,SAAS,cAAc;IAClC,GAAG,MAAM,IAAI,SAAS,cAAc,EAAE;CACvC,GACG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,GAC/D,MAAM,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,EAAE,SAAS,SAAS,cAAc,EAAE,IAAI;KACjE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GACjC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAC5B,CAAC,CAAC,CAAC,CAAC,SAAS,WAAW,GACtB,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GACtB,KAAK;CACZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
export type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
|
+
/**
|
|
4
|
+
* Schema protocol — alias for StandardSchemaV1
|
|
5
|
+
*/
|
|
6
|
+
export type SchemaProtocol<TOutput = unknown> = StandardSchemaV1<unknown, TOutput>;
|
|
7
|
+
/**
|
|
8
|
+
* Infer the output type from a Standard Schema
|
|
9
|
+
*/
|
|
10
|
+
export type InferSchema<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : never;
|
|
11
|
+
/**
|
|
12
|
+
* Extract path parameters from a path string
|
|
13
|
+
*/
|
|
14
|
+
export type ExtractPathParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}` ? Param | ExtractPathParams<`/${Rest}`> : T extends `${string}:${infer Param}` ? Param : never;
|
|
15
|
+
//# sourceMappingURL=schema-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-types.d.ts","sourceRoot":"","sources":["../../src/lib/schema-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,OAAO,GAAG,OAAO,IAAI,gBAAgB,CAC9D,OAAO,EACP,OAAO,CACR,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,gBAAgB,GACnD,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,GAC/B,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAC5C,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GAC9C,KAAK,GAAG,iBAAiB,CAAC,IAAI,IAAI,EAAE,CAAC,GACrC,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK,EAAE,GAClC,KAAK,GACL,KAAK,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ts-contract/core",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "Contract definitions, type inference helpers, and plugin system for ts-contract",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
"./package.json": "./package.json",
|
|
11
|
+
".": {
|
|
12
|
+
"@ts-contract/source": "./src/index.ts",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"!**/*.tsbuildinfo"
|
|
21
|
+
],
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/mbrimmer83/ts-contract.git",
|
|
25
|
+
"directory": "packages/core"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/mbrimmer83/ts-contract#readme",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/mbrimmer83/ts-contract/issues"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"typescript",
|
|
33
|
+
"contract",
|
|
34
|
+
"api",
|
|
35
|
+
"schema",
|
|
36
|
+
"validation",
|
|
37
|
+
"type-safe"
|
|
38
|
+
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@standard-schema/spec": "1.1.0",
|
|
44
|
+
"tslib": "^2.3.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -b tsconfig.lib.json",
|
|
48
|
+
"test": "vitest run --config vitest.config.mts",
|
|
49
|
+
"lint": "eslint ."
|
|
50
|
+
}
|
|
51
|
+
}
|