@controller-contract/server 0.1.1 → 0.2.3
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/dist/index.d.mts +101 -55
- package/dist/index.mjs +129 -54
- package/package.json +2 -6
- package/src/classes/ControllerContractBuilder.ts +24 -0
- package/src/classes/ControllerContractBuilderFactory.ts +10 -0
- package/src/classes/ControllerContractSpec.ts +25 -0
- package/src/classes/PostEndpointStep.ts +49 -0
- package/src/classes/PostInjectStep.ts +19 -0
- package/src/classes/PostInputStep.ts +57 -0
- package/src/index.ts +2 -23
- package/src/types/index.ts +24 -0
- package/src/utils/index.ts +1 -2
- package/src/utils/outputs.ts +55 -0
- package/dist/index-CgtO_soW.d.mts +0 -40
- package/dist/utils/index.d.mts +0 -2
- package/dist/utils/index.mjs +0 -60
- package/src/steps/endpoint.ts +0 -15
- package/src/steps/input.ts +0 -16
- package/src/steps/output.ts +0 -30
- package/src/types/adapter.ts +0 -25
- package/src/types/builder-step.ts +0 -59
- package/src/utils/cast.ts +0 -7
- package/src/utils/output.ts +0 -55
package/dist/index.d.mts
CHANGED
|
@@ -1,67 +1,113 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ControllerContract, InputChannel, Method, NoInput, OutputFail, OutputMonads, ParamsFrom, Prettify } from "@controller-contract/core";
|
|
1
|
+
import { ControllerContractProduct, ExitOutput, FailOutput, InputChannel, Method, NextOutput, NoInput, Output, ParamsFrom, PassOutput } from "@controller-contract/core";
|
|
3
2
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
4
3
|
|
|
5
|
-
//#region src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
declare namespace BuilderStep {
|
|
10
|
-
/**
|
|
11
|
-
* Always the first step
|
|
12
|
-
*/
|
|
13
|
-
interface Endpoint<TContext> {
|
|
14
|
-
endpoint<const TMethod extends Method, const TPath extends string>(method: TMethod, path: TPath): BuilderStep.Output<TContext, TMethod, TPath, NoInput, never> & BuilderStep.Input<TContext, TMethod, TPath>;
|
|
15
|
-
}
|
|
16
|
-
interface Input<TContext, TMethod extends Method, TPath extends string> {
|
|
17
|
-
input<TInput>(channel: InputChannel, schema: StandardSchemaV1<TInput>): BuilderStep.Output<TContext, TMethod, TPath, TInput, OutputFail<Extract<keyof TInput, string>>>;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Always the last step
|
|
21
|
-
*/
|
|
22
|
-
interface Output<TContext, TMethod extends Method, TPath extends string, TInput, TOutput extends OutputMonads> {
|
|
23
|
-
output<TFnOutput extends OutputMonads>(handler: OutputHandler<TContext, TPath, TInput, TFnOutput>): ControllerContract<TMethod, TPath, TInput, TOutput | TFnOutput>;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
type OutputHandler<TContext, TPath extends string, TInput, TOutput extends OutputMonads> = (context: Prettify<TContext & {
|
|
27
|
-
params: ParamsFrom<TPath>;
|
|
28
|
-
input: TInput;
|
|
29
|
-
output: typeof output;
|
|
30
|
-
}>) => TOutput | Promise<TOutput>;
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region src/types/adapter.d.ts
|
|
33
|
-
type AdaptorFn<TContext> = (blueprint: AdapterBlueprint<TContext> | AdapterWithSchemaBlueprint<TContext>) => void;
|
|
34
|
-
declare class AdapterBlueprint<TContext> {
|
|
4
|
+
//#region src/classes/ControllerContractSpec.d.ts
|
|
5
|
+
interface NullaryControllerContractSpec<TContext, TInject> {
|
|
6
|
+
readonly type: 'nullary';
|
|
7
|
+
readonly injectable: TInject;
|
|
35
8
|
readonly method: Method;
|
|
36
9
|
readonly path: string;
|
|
37
|
-
readonly
|
|
38
|
-
constructor(method: Method, path: string, handler: OutputHandler<TContext, string, any, OutputMonads>);
|
|
10
|
+
readonly outputFn: OutputFn<TContext, string, Output>;
|
|
39
11
|
}
|
|
40
|
-
|
|
12
|
+
interface SanitizedControllerContractSpec<TContext, TInject> {
|
|
13
|
+
readonly type: 'sanitized';
|
|
14
|
+
readonly injectable: TInject;
|
|
41
15
|
readonly method: Method;
|
|
42
16
|
readonly path: string;
|
|
43
17
|
readonly channel: InputChannel;
|
|
44
18
|
readonly schema: StandardSchemaV1;
|
|
45
|
-
readonly
|
|
46
|
-
|
|
19
|
+
readonly outputFn: SanitizedOutputFn<TContext, string, unknown, Output>;
|
|
20
|
+
}
|
|
21
|
+
type ControllerContractSpec<TContext, TInject> = NullaryControllerContractSpec<TContext, TInject> | SanitizedControllerContractSpec<TContext, TInject>;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/utils/outputs.d.ts
|
|
24
|
+
declare namespace outputs {
|
|
25
|
+
function next<T>(data: T, code?: number): NextOutput<T>;
|
|
26
|
+
function pass<T>(data: T, code?: number): PassOutput<T>;
|
|
27
|
+
function fail<const T extends string>(data: FailOutput<T>['data'], code?: number): FailOutput<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Used to escape the controller contract state machine, useful for redirects.
|
|
30
|
+
*/
|
|
31
|
+
function exit(): ExitOutput;
|
|
47
32
|
}
|
|
33
|
+
declare function rootOnly(message: string): FailOutput<'root'>['data'];
|
|
34
|
+
declare const StandardOutputs: Readonly<{
|
|
35
|
+
OK: PassOutput<{
|
|
36
|
+
key: "root";
|
|
37
|
+
message: string;
|
|
38
|
+
}[]>;
|
|
39
|
+
Created: PassOutput<{
|
|
40
|
+
key: "root";
|
|
41
|
+
message: string;
|
|
42
|
+
}[]>;
|
|
43
|
+
Accepted: PassOutput<{
|
|
44
|
+
key: "root";
|
|
45
|
+
message: string;
|
|
46
|
+
}[]>;
|
|
47
|
+
BadRequest: FailOutput<"root">;
|
|
48
|
+
Unauthorized: FailOutput<"root">;
|
|
49
|
+
Forbidden: FailOutput<"root">;
|
|
50
|
+
NotFound: FailOutput<"root">;
|
|
51
|
+
InternalServerError: FailOutput<"root">;
|
|
52
|
+
BadGateway: FailOutput<"root">;
|
|
53
|
+
}>;
|
|
48
54
|
//#endregion
|
|
49
|
-
//#region src/index.d.ts
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
//#region src/types/index.d.ts
|
|
56
|
+
type IntegratorFn<TContext, TInject, TProduct> = (spec: ControllerContractSpec<TContext, TInject>) => TProduct;
|
|
57
|
+
type Prettify<T> = { [K in keyof T]: T[K] } & {};
|
|
58
|
+
type OutputFn<TContext, TPath extends string, TOutput> = (context: Prettify<TContext & {
|
|
59
|
+
params: ParamsFrom<TPath>;
|
|
60
|
+
outputs: typeof outputs;
|
|
61
|
+
}>) => Promise<TOutput>;
|
|
62
|
+
type SanitizedOutputFn<TContext, TPath extends string, TInput, TOutput> = OutputFn<TContext & {
|
|
63
|
+
input: TInput;
|
|
64
|
+
}, TPath, TOutput>;
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/classes/PostInputStep.d.ts
|
|
67
|
+
declare class PostInputStep<TContext, TInject, TProduct, const TMethod extends Method, const TPath extends string, const TChannel extends InputChannel, TInput, TOutputFromInput extends Output> {
|
|
68
|
+
private readonly injectable;
|
|
69
|
+
private readonly integrator;
|
|
70
|
+
private readonly method;
|
|
71
|
+
private readonly path;
|
|
72
|
+
private readonly channel;
|
|
73
|
+
private readonly schema;
|
|
74
|
+
constructor(injectable: TInject, integrator: IntegratorFn<TContext, TInject, TProduct>, method: TMethod, path: TPath, channel: TChannel, schema: StandardSchemaV1<TInput>);
|
|
75
|
+
output<TOutput extends Output>(outputFn: SanitizedOutputFn<TContext, TPath, TInput, TOutput>): ControllerContractProduct<TMethod, TPath, ParamsFrom<TPath>, TChannel extends "query" ? TInput : NoInput, TChannel extends "body" ? TInput : NoInput, TOutputFromInput | TOutput, TProduct>;
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/classes/PostEndpointStep.d.ts
|
|
79
|
+
declare class PostEndpointStep<TContext, TInject, TProduct, const TMethod extends Method, const TPath extends string> {
|
|
80
|
+
private readonly injectable;
|
|
81
|
+
private readonly integrator;
|
|
82
|
+
private readonly method;
|
|
83
|
+
private readonly path;
|
|
84
|
+
constructor(injectable: TInject, integrator: IntegratorFn<TContext, TInject, TProduct>, method: TMethod, path: TPath);
|
|
85
|
+
input<const TChannel extends InputChannel, TInput>(channel: TChannel, schema: StandardSchemaV1<TInput>): PostInputStep<TContext, TInject, TProduct, TMethod, TPath, TChannel, TInput, FailOutput<Extract<keyof TInput, string>>>;
|
|
86
|
+
output<TOutput extends Output>(outputFn: OutputFn<TContext, TPath, TOutput>): ControllerContractProduct<TMethod, TPath, ParamsFrom<TPath>, NoInput, NoInput, TOutput, TProduct>;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/classes/PostInjectStep.d.ts
|
|
90
|
+
declare class PostInjectStep<TContext, TInject, TProduct> {
|
|
91
|
+
private readonly injectable;
|
|
92
|
+
private readonly integrator;
|
|
93
|
+
constructor(injectable: TInject, integrator: IntegratorFn<TContext, TInject, TProduct>);
|
|
94
|
+
endpoint<const TMethod extends Method, const TPath extends string>(method: TMethod, path: TPath): PostEndpointStep<TContext, TInject, TProduct, TMethod, TPath>;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/classes/ControllerContractBuilder.d.ts
|
|
98
|
+
declare class ControllerContractBuilder<TContext, TInject, TProduct> {
|
|
99
|
+
private readonly injectableDefaultValue;
|
|
100
|
+
private readonly integrator;
|
|
101
|
+
constructor(injectableDefaultValue: TInject, integrator: IntegratorFn<TContext, TInject, TProduct>);
|
|
102
|
+
inject(injectable: TInject): PostInjectStep<TContext, TInject, TProduct>;
|
|
103
|
+
endpoint<const TMethod extends Method, const TPath extends string>(method: TMethod, path: TPath): PostEndpointStep<TContext, TInject, TProduct, TMethod, TPath>;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/classes/ControllerContractBuilderFactory.d.ts
|
|
107
|
+
declare class ControllerContractBuilderFactory<TContext, TInject> {
|
|
108
|
+
private readonly injectableDefaultValue;
|
|
109
|
+
constructor(injectableDefaultValue: TInject);
|
|
110
|
+
createBuilder<TProduct>(integrator: IntegratorFn<TContext, TInject, TProduct>): ControllerContractBuilder<TContext, TInject, TProduct>;
|
|
111
|
+
}
|
|
66
112
|
//#endregion
|
|
67
|
-
export {
|
|
113
|
+
export { ControllerContractBuilderFactory, StandardOutputs, outputs, rootOnly };
|
package/dist/index.mjs
CHANGED
|
@@ -1,73 +1,148 @@
|
|
|
1
|
-
//#region src/
|
|
2
|
-
var
|
|
1
|
+
//#region src/classes/PostInputStep.ts
|
|
2
|
+
var PostInputStep = class {
|
|
3
|
+
injectable;
|
|
4
|
+
integrator;
|
|
3
5
|
method;
|
|
4
6
|
path;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
channel;
|
|
8
|
+
schema;
|
|
9
|
+
constructor(injectable, integrator, method, path, channel, schema) {
|
|
10
|
+
this.injectable = injectable;
|
|
11
|
+
this.integrator = integrator;
|
|
7
12
|
this.method = method;
|
|
8
13
|
this.path = path;
|
|
9
|
-
this.
|
|
14
|
+
this.channel = channel;
|
|
15
|
+
this.schema = schema;
|
|
16
|
+
}
|
|
17
|
+
output(outputFn) {
|
|
18
|
+
this.integrator({
|
|
19
|
+
type: "sanitized",
|
|
20
|
+
injectable: this.injectable,
|
|
21
|
+
method: this.method,
|
|
22
|
+
path: this.path,
|
|
23
|
+
channel: this.channel,
|
|
24
|
+
schema: this.schema,
|
|
25
|
+
outputFn
|
|
26
|
+
});
|
|
27
|
+
return {};
|
|
10
28
|
}
|
|
11
29
|
};
|
|
12
|
-
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/classes/PostEndpointStep.ts
|
|
32
|
+
var PostEndpointStep = class {
|
|
33
|
+
injectable;
|
|
34
|
+
integrator;
|
|
13
35
|
method;
|
|
14
36
|
path;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
constructor(method, path, channel, schema, handler) {
|
|
37
|
+
constructor(injectable, integrator, method, path) {
|
|
38
|
+
this.injectable = injectable;
|
|
39
|
+
this.integrator = integrator;
|
|
19
40
|
this.method = method;
|
|
20
41
|
this.path = path;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
this.
|
|
42
|
+
}
|
|
43
|
+
input(channel, schema) {
|
|
44
|
+
return new PostInputStep(this.injectable, this.integrator, this.method, this.path, channel, schema);
|
|
45
|
+
}
|
|
46
|
+
output(outputFn) {
|
|
47
|
+
this.integrator({
|
|
48
|
+
type: "nullary",
|
|
49
|
+
method: this.method,
|
|
50
|
+
path: this.path,
|
|
51
|
+
injectable: this.injectable,
|
|
52
|
+
outputFn
|
|
53
|
+
});
|
|
54
|
+
return {};
|
|
24
55
|
}
|
|
25
56
|
};
|
|
26
57
|
//#endregion
|
|
27
|
-
//#region src/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
58
|
+
//#region src/classes/PostInjectStep.ts
|
|
59
|
+
var PostInjectStep = class {
|
|
60
|
+
injectable;
|
|
61
|
+
integrator;
|
|
62
|
+
constructor(injectable, integrator) {
|
|
63
|
+
this.injectable = injectable;
|
|
64
|
+
this.integrator = integrator;
|
|
65
|
+
}
|
|
66
|
+
endpoint(method, path) {
|
|
67
|
+
return new PostEndpointStep(this.injectable, this.integrator, method, path);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
35
70
|
//#endregion
|
|
36
|
-
//#region src/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
71
|
+
//#region src/classes/ControllerContractBuilder.ts
|
|
72
|
+
var ControllerContractBuilder = class {
|
|
73
|
+
injectableDefaultValue;
|
|
74
|
+
integrator;
|
|
75
|
+
constructor(injectableDefaultValue, integrator) {
|
|
76
|
+
this.injectableDefaultValue = injectableDefaultValue;
|
|
77
|
+
this.integrator = integrator;
|
|
78
|
+
}
|
|
79
|
+
inject(injectable) {
|
|
80
|
+
return new PostInjectStep(injectable, this.integrator);
|
|
81
|
+
}
|
|
82
|
+
endpoint(method, path) {
|
|
83
|
+
return new PostEndpointStep(this.injectableDefaultValue, this.integrator, method, path);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
42
86
|
//#endregion
|
|
43
|
-
//#region src/
|
|
44
|
-
|
|
45
|
-
|
|
87
|
+
//#region src/classes/ControllerContractBuilderFactory.ts
|
|
88
|
+
var ControllerContractBuilderFactory = class {
|
|
89
|
+
injectableDefaultValue;
|
|
90
|
+
constructor(injectableDefaultValue) {
|
|
91
|
+
this.injectableDefaultValue = injectableDefaultValue;
|
|
92
|
+
}
|
|
93
|
+
createBuilder(integrator) {
|
|
94
|
+
return new ControllerContractBuilder(this.injectableDefaultValue, integrator);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/utils/outputs.ts
|
|
99
|
+
let outputs;
|
|
100
|
+
(function(_outputs) {
|
|
101
|
+
function next(data, code = 100) {
|
|
46
102
|
return {
|
|
47
|
-
|
|
48
|
-
|
|
103
|
+
type: "next",
|
|
104
|
+
code,
|
|
105
|
+
data
|
|
49
106
|
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
107
|
+
}
|
|
108
|
+
_outputs.next = next;
|
|
109
|
+
function pass(data, code = 200) {
|
|
110
|
+
return {
|
|
111
|
+
type: "pass",
|
|
112
|
+
code,
|
|
113
|
+
data
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
_outputs.pass = pass;
|
|
117
|
+
function fail(data, code = 400) {
|
|
118
|
+
return {
|
|
119
|
+
type: "fail",
|
|
120
|
+
code,
|
|
121
|
+
data
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
_outputs.fail = fail;
|
|
125
|
+
function exit() {
|
|
126
|
+
return { type: "exit" };
|
|
127
|
+
}
|
|
128
|
+
_outputs.exit = exit;
|
|
129
|
+
})(outputs || (outputs = {}));
|
|
130
|
+
function rootOnly(message) {
|
|
131
|
+
return [{
|
|
132
|
+
key: "root",
|
|
133
|
+
message
|
|
134
|
+
}];
|
|
71
135
|
}
|
|
136
|
+
const StandardOutputs = Object.freeze({
|
|
137
|
+
OK: outputs.pass(rootOnly("ok"), 200),
|
|
138
|
+
Created: outputs.pass(rootOnly("Created"), 201),
|
|
139
|
+
Accepted: outputs.pass(rootOnly("Accepted"), 202),
|
|
140
|
+
BadRequest: outputs.fail(rootOnly("Bad Request"), 400),
|
|
141
|
+
Unauthorized: outputs.fail(rootOnly("Unauthorized"), 401),
|
|
142
|
+
Forbidden: outputs.fail(rootOnly("Forbidden"), 403),
|
|
143
|
+
NotFound: outputs.fail(rootOnly("Not Found"), 404),
|
|
144
|
+
InternalServerError: outputs.fail(rootOnly("Internal Server Error"), 500),
|
|
145
|
+
BadGateway: outputs.fail(rootOnly("Bad Gateway"), 502)
|
|
146
|
+
});
|
|
72
147
|
//#endregion
|
|
73
|
-
export {
|
|
148
|
+
export { ControllerContractBuilderFactory, StandardOutputs, outputs, rootOnly };
|
package/package.json
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@controller-contract/server",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"author": "kennychulo",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"main": "dist/index.js",
|
|
8
|
-
"module": "dist/index.mjs",
|
|
9
|
-
"types": "./dist/index.d.ts",
|
|
10
7
|
"files": [
|
|
11
8
|
"dist",
|
|
12
9
|
"src",
|
|
@@ -15,7 +12,6 @@
|
|
|
15
12
|
],
|
|
16
13
|
"exports": {
|
|
17
14
|
".": "./dist/index.mjs",
|
|
18
|
-
"./utils": "./dist/utils/index.mjs",
|
|
19
15
|
"./package.json": "./package.json"
|
|
20
16
|
},
|
|
21
17
|
"publishConfig": {
|
|
@@ -27,7 +23,7 @@
|
|
|
27
23
|
"typescript": "6.0.3"
|
|
28
24
|
},
|
|
29
25
|
"dependencies": {
|
|
30
|
-
"@controller-contract/core": "0.
|
|
26
|
+
"@controller-contract/core": "0.2.7",
|
|
31
27
|
"@standard-schema/spec": "1.1.0"
|
|
32
28
|
},
|
|
33
29
|
"scripts": {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Method } from '@controller-contract/core';
|
|
2
|
+
import type { IntegratorFn } from '../types';
|
|
3
|
+
import { PostEndpointStep } from './PostEndpointStep';
|
|
4
|
+
import { PostInjectStep } from './PostInjectStep';
|
|
5
|
+
|
|
6
|
+
export class ControllerContractBuilder<TContext, TInject, TProduct> {
|
|
7
|
+
constructor(
|
|
8
|
+
private readonly injectableDefaultValue: TInject,
|
|
9
|
+
private readonly integrator: IntegratorFn<TContext, TInject, TProduct>,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
inject(injectable: TInject) {
|
|
13
|
+
return new PostInjectStep<TContext, TInject, TProduct>(injectable, this.integrator);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
endpoint<const TMethod extends Method, const TPath extends string>(method: TMethod, path: TPath) {
|
|
17
|
+
return new PostEndpointStep<TContext, TInject, TProduct, TMethod, TPath>(
|
|
18
|
+
this.injectableDefaultValue,
|
|
19
|
+
this.integrator,
|
|
20
|
+
method,
|
|
21
|
+
path,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IntegratorFn } from '../types';
|
|
2
|
+
import { ControllerContractBuilder } from './ControllerContractBuilder';
|
|
3
|
+
|
|
4
|
+
export class ControllerContractBuilderFactory<TContext, TInject> {
|
|
5
|
+
constructor(private readonly injectableDefaultValue: TInject) {}
|
|
6
|
+
|
|
7
|
+
createBuilder<TProduct>(integrator: IntegratorFn<TContext, TInject, TProduct>) {
|
|
8
|
+
return new ControllerContractBuilder<TContext, TInject, TProduct>(this.injectableDefaultValue, integrator);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { InputChannel, Method, Output } from '@controller-contract/core';
|
|
2
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
|
+
import type { OutputFn, SanitizedOutputFn } from '../types';
|
|
4
|
+
|
|
5
|
+
export interface NullaryControllerContractSpec<TContext, TInject> {
|
|
6
|
+
readonly type: 'nullary';
|
|
7
|
+
readonly injectable: TInject;
|
|
8
|
+
readonly method: Method;
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly outputFn: OutputFn<TContext, string, Output>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SanitizedControllerContractSpec<TContext, TInject> {
|
|
14
|
+
readonly type: 'sanitized';
|
|
15
|
+
readonly injectable: TInject;
|
|
16
|
+
readonly method: Method;
|
|
17
|
+
readonly path: string;
|
|
18
|
+
readonly channel: InputChannel;
|
|
19
|
+
readonly schema: StandardSchemaV1;
|
|
20
|
+
readonly outputFn: SanitizedOutputFn<TContext, string, unknown, Output>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ControllerContractSpec<TContext, TInject> =
|
|
24
|
+
| NullaryControllerContractSpec<TContext, TInject>
|
|
25
|
+
| SanitizedControllerContractSpec<TContext, TInject>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ControllerContractProduct,
|
|
3
|
+
FailOutput,
|
|
4
|
+
InputChannel,
|
|
5
|
+
Method,
|
|
6
|
+
NoInput,
|
|
7
|
+
Output,
|
|
8
|
+
ParamsFrom,
|
|
9
|
+
} from '@controller-contract/core';
|
|
10
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
11
|
+
import type { IntegratorFn, OutputFn } from '../types';
|
|
12
|
+
import type { NullaryControllerContractSpec } from './ControllerContractSpec';
|
|
13
|
+
import { PostInputStep } from './PostInputStep';
|
|
14
|
+
|
|
15
|
+
export class PostEndpointStep<TContext, TInject, TProduct, const TMethod extends Method, const TPath extends string> {
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly injectable: TInject,
|
|
18
|
+
private readonly integrator: IntegratorFn<TContext, TInject, TProduct>,
|
|
19
|
+
private readonly method: TMethod,
|
|
20
|
+
private readonly path: TPath,
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
input<const TChannel extends InputChannel, TInput>(channel: TChannel, schema: StandardSchemaV1<TInput>) {
|
|
24
|
+
return new PostInputStep<
|
|
25
|
+
TContext,
|
|
26
|
+
TInject,
|
|
27
|
+
TProduct,
|
|
28
|
+
TMethod,
|
|
29
|
+
TPath,
|
|
30
|
+
TChannel,
|
|
31
|
+
TInput,
|
|
32
|
+
FailOutput<Extract<keyof TInput, string>>
|
|
33
|
+
>(this.injectable, this.integrator, this.method, this.path, channel, schema);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
output<TOutput extends Output>(outputFn: OutputFn<TContext, TPath, TOutput>) {
|
|
37
|
+
const product = this.integrator(<NullaryControllerContractSpec<TContext, TInject>>{
|
|
38
|
+
type: 'nullary',
|
|
39
|
+
method: this.method,
|
|
40
|
+
path: this.path,
|
|
41
|
+
injectable: this.injectable,
|
|
42
|
+
outputFn,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return <ControllerContractProduct<TMethod, TPath, ParamsFrom<TPath>, NoInput, NoInput, TOutput, TProduct>>{
|
|
46
|
+
...() => product,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Method } from '@controller-contract/core';
|
|
2
|
+
import type { IntegratorFn } from '../types';
|
|
3
|
+
import { PostEndpointStep } from './PostEndpointStep';
|
|
4
|
+
|
|
5
|
+
export class PostInjectStep<TContext, TInject, TProduct> {
|
|
6
|
+
constructor(
|
|
7
|
+
private readonly injectable: TInject,
|
|
8
|
+
private readonly integrator: IntegratorFn<TContext, TInject, TProduct>,
|
|
9
|
+
) {}
|
|
10
|
+
|
|
11
|
+
endpoint<const TMethod extends Method, const TPath extends string>(method: TMethod, path: TPath) {
|
|
12
|
+
return new PostEndpointStep<TContext, TInject, TProduct, TMethod, TPath>(
|
|
13
|
+
this.injectable,
|
|
14
|
+
this.integrator,
|
|
15
|
+
method,
|
|
16
|
+
path,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ControllerContractProduct,
|
|
3
|
+
InputChannel,
|
|
4
|
+
Method,
|
|
5
|
+
NoInput,
|
|
6
|
+
Output,
|
|
7
|
+
ParamsFrom,
|
|
8
|
+
} from '@controller-contract/core';
|
|
9
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
10
|
+
import type { IntegratorFn, SanitizedOutputFn } from '../types';
|
|
11
|
+
import type { SanitizedControllerContractSpec } from './ControllerContractSpec';
|
|
12
|
+
|
|
13
|
+
export class PostInputStep<
|
|
14
|
+
TContext,
|
|
15
|
+
TInject,
|
|
16
|
+
TProduct,
|
|
17
|
+
const TMethod extends Method,
|
|
18
|
+
const TPath extends string,
|
|
19
|
+
const TChannel extends InputChannel,
|
|
20
|
+
TInput,
|
|
21
|
+
TOutputFromInput extends Output,
|
|
22
|
+
> {
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly injectable: TInject,
|
|
25
|
+
private readonly integrator: IntegratorFn<TContext, TInject, TProduct>,
|
|
26
|
+
private readonly method: TMethod,
|
|
27
|
+
private readonly path: TPath,
|
|
28
|
+
private readonly channel: TChannel,
|
|
29
|
+
private readonly schema: StandardSchemaV1<TInput>,
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
output<TOutput extends Output>(outputFn: SanitizedOutputFn<TContext, TPath, TInput, TOutput>) {
|
|
33
|
+
const product = this.integrator(<SanitizedControllerContractSpec<TContext, TInject>>{
|
|
34
|
+
type: 'sanitized',
|
|
35
|
+
injectable: this.injectable,
|
|
36
|
+
method: this.method,
|
|
37
|
+
path: this.path,
|
|
38
|
+
channel: this.channel,
|
|
39
|
+
schema: this.schema,
|
|
40
|
+
outputFn,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return <
|
|
44
|
+
ControllerContractProduct<
|
|
45
|
+
TMethod,
|
|
46
|
+
TPath,
|
|
47
|
+
ParamsFrom<TPath>,
|
|
48
|
+
TChannel extends 'query' ? TInput : NoInput,
|
|
49
|
+
TChannel extends 'body' ? TInput : NoInput,
|
|
50
|
+
TOutputFromInput | TOutput,
|
|
51
|
+
TProduct
|
|
52
|
+
>
|
|
53
|
+
>{
|
|
54
|
+
...() => product,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,23 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export * from './types/adapter';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* This factory function provides the inteface for you to write the integration between
|
|
8
|
-
* `contract-controller` and your http library/framework of choice.
|
|
9
|
-
*
|
|
10
|
-
* When youre done writing your adaptor you can use the product in your code base.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* // lib/cc.ts
|
|
14
|
-
* export const cc = controllerContractFactory(() => {
|
|
15
|
-
* // your code
|
|
16
|
-
* })
|
|
17
|
-
*
|
|
18
|
-
* // api/users.ts
|
|
19
|
-
* export const getUsers = cc.endpoint().input('...', ...).output(() => { ... });
|
|
20
|
-
*/
|
|
21
|
-
export function controllerContractBuilderFactory<TContext>(adaptor: AdaptorFn<TContext>) {
|
|
22
|
-
return endpointStep<TContext>(adaptor);
|
|
23
|
-
}
|
|
1
|
+
export * from './classes/ControllerContractBuilderFactory';
|
|
2
|
+
export * from './utils';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ParamsFrom } from '@controller-contract/core';
|
|
2
|
+
import type { ControllerContractSpec } from '../classes/ControllerContractSpec';
|
|
3
|
+
import type { outputs } from '../utils';
|
|
4
|
+
|
|
5
|
+
export type IntegratorFn<TContext, TInject, TProduct> = (spec: ControllerContractSpec<TContext, TInject>) => TProduct;
|
|
6
|
+
|
|
7
|
+
export type Prettify<T> = {
|
|
8
|
+
[K in keyof T]: T[K];
|
|
9
|
+
} & {};
|
|
10
|
+
|
|
11
|
+
export type OutputFn<TContext, TPath extends string, TOutput> = (
|
|
12
|
+
context: Prettify<
|
|
13
|
+
TContext & {
|
|
14
|
+
params: ParamsFrom<TPath>;
|
|
15
|
+
outputs: typeof outputs;
|
|
16
|
+
}
|
|
17
|
+
>,
|
|
18
|
+
) => Promise<TOutput>;
|
|
19
|
+
|
|
20
|
+
export type SanitizedOutputFn<TContext, TPath extends string, TInput, TOutput> = OutputFn<
|
|
21
|
+
TContext & { input: TInput },
|
|
22
|
+
TPath,
|
|
23
|
+
TOutput
|
|
24
|
+
>;
|
package/src/utils/index.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './output';
|
|
1
|
+
export * from './outputs';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ExitOutput, FailOutput, NextOutput, PassOutput } from '@controller-contract/core';
|
|
2
|
+
|
|
3
|
+
export namespace outputs {
|
|
4
|
+
export function next<T>(data: T, code = 100): NextOutput<T> {
|
|
5
|
+
return {
|
|
6
|
+
type: 'next',
|
|
7
|
+
code,
|
|
8
|
+
data,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function pass<T>(data: T, code = 200): PassOutput<T> {
|
|
13
|
+
return {
|
|
14
|
+
type: 'pass',
|
|
15
|
+
code,
|
|
16
|
+
data,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function fail<const T extends string>(data: FailOutput<T>['data'], code = 400): FailOutput<T> {
|
|
21
|
+
return {
|
|
22
|
+
type: 'fail',
|
|
23
|
+
code,
|
|
24
|
+
data,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Used to escape the controller contract state machine, useful for redirects.
|
|
30
|
+
*/
|
|
31
|
+
export function exit(): ExitOutput {
|
|
32
|
+
return {
|
|
33
|
+
type: 'exit',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function rootOnly(message: string): FailOutput<'root'>['data'] {
|
|
39
|
+
return [{ key: 'root', message }];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const StandardOutputs = Object.freeze({
|
|
43
|
+
// 200
|
|
44
|
+
OK: outputs.pass(rootOnly('ok'), 200),
|
|
45
|
+
Created: outputs.pass(rootOnly('Created'), 201),
|
|
46
|
+
Accepted: outputs.pass(rootOnly('Accepted'), 202),
|
|
47
|
+
// 400
|
|
48
|
+
BadRequest: outputs.fail(rootOnly('Bad Request'), 400),
|
|
49
|
+
Unauthorized: outputs.fail(rootOnly('Unauthorized'), 401),
|
|
50
|
+
Forbidden: outputs.fail(rootOnly('Forbidden'), 403),
|
|
51
|
+
NotFound: outputs.fail(rootOnly('Not Found'), 404),
|
|
52
|
+
// 500
|
|
53
|
+
InternalServerError: outputs.fail(rootOnly('Internal Server Error'), 500),
|
|
54
|
+
BadGateway: outputs.fail(rootOnly('Bad Gateway'), 502),
|
|
55
|
+
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { OutputExit, OutputFail, OutputNext, OutputPass, ParamsFrom } from "@controller-contract/core";
|
|
2
|
-
|
|
3
|
-
//#region src/utils/cast.d.ts
|
|
4
|
-
declare namespace cast {
|
|
5
|
-
function params<T>(rawParams: T): ParamsFrom<string>;
|
|
6
|
-
}
|
|
7
|
-
//#endregion
|
|
8
|
-
//#region src/utils/output.d.ts
|
|
9
|
-
declare namespace output {
|
|
10
|
-
function next<T>(data: T, code?: number): OutputNext<T>;
|
|
11
|
-
function pass<T>(data: T, code?: number): OutputPass<T>;
|
|
12
|
-
function fail<const T extends string>(data: OutputFail<T>['data'], code?: number): OutputFail<T>;
|
|
13
|
-
/**
|
|
14
|
-
* Used to escape the controller contract state machine, useful for redirects.
|
|
15
|
-
*/
|
|
16
|
-
function exit(): OutputExit;
|
|
17
|
-
}
|
|
18
|
-
declare function rootOnly(message: string): OutputFail<'root'>['data'];
|
|
19
|
-
declare const StandardOutput: {
|
|
20
|
-
OK: OutputPass<{
|
|
21
|
-
key: "root";
|
|
22
|
-
message: string;
|
|
23
|
-
}[]>;
|
|
24
|
-
Created: OutputPass<{
|
|
25
|
-
key: "root";
|
|
26
|
-
message: string;
|
|
27
|
-
}[]>;
|
|
28
|
-
Accepted: OutputPass<{
|
|
29
|
-
key: "root";
|
|
30
|
-
message: string;
|
|
31
|
-
}[]>;
|
|
32
|
-
BadRequest: OutputFail<"root">;
|
|
33
|
-
Unauthorized: OutputFail<"root">;
|
|
34
|
-
Forbidden: OutputFail<"root">;
|
|
35
|
-
NotFound: OutputFail<"root">;
|
|
36
|
-
InternalServerError: OutputFail<"root">;
|
|
37
|
-
BadGateway: OutputFail<"root">;
|
|
38
|
-
};
|
|
39
|
-
//#endregion
|
|
40
|
-
export { cast as i, output as n, rootOnly as r, StandardOutput as t };
|
package/dist/utils/index.d.mts
DELETED
package/dist/utils/index.mjs
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
//#region src/utils/cast.ts
|
|
2
|
-
let cast;
|
|
3
|
-
(function(_cast) {
|
|
4
|
-
function params(rawParams) {
|
|
5
|
-
return rawParams;
|
|
6
|
-
}
|
|
7
|
-
_cast.params = params;
|
|
8
|
-
})(cast || (cast = {}));
|
|
9
|
-
//#endregion
|
|
10
|
-
//#region src/utils/output.ts
|
|
11
|
-
let output;
|
|
12
|
-
(function(_output) {
|
|
13
|
-
function next(data, code = 100) {
|
|
14
|
-
return {
|
|
15
|
-
type: "next",
|
|
16
|
-
code,
|
|
17
|
-
data
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
_output.next = next;
|
|
21
|
-
function pass(data, code = 200) {
|
|
22
|
-
return {
|
|
23
|
-
type: "pass",
|
|
24
|
-
code,
|
|
25
|
-
data
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
_output.pass = pass;
|
|
29
|
-
function fail(data, code = 400) {
|
|
30
|
-
return {
|
|
31
|
-
type: "fail",
|
|
32
|
-
code,
|
|
33
|
-
data
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
_output.fail = fail;
|
|
37
|
-
function exit() {
|
|
38
|
-
return { type: "exit" };
|
|
39
|
-
}
|
|
40
|
-
_output.exit = exit;
|
|
41
|
-
})(output || (output = {}));
|
|
42
|
-
function rootOnly(message) {
|
|
43
|
-
return [{
|
|
44
|
-
key: "root",
|
|
45
|
-
message
|
|
46
|
-
}];
|
|
47
|
-
}
|
|
48
|
-
const StandardOutput = {
|
|
49
|
-
OK: output.pass(rootOnly("ok"), 200),
|
|
50
|
-
Created: output.pass(rootOnly("Created"), 201),
|
|
51
|
-
Accepted: output.pass(rootOnly("Accepted"), 202),
|
|
52
|
-
BadRequest: output.fail(rootOnly("Bad Request"), 400),
|
|
53
|
-
Unauthorized: output.fail(rootOnly("Unauthorized"), 401),
|
|
54
|
-
Forbidden: output.fail(rootOnly("Forbidden"), 403),
|
|
55
|
-
NotFound: output.fail(rootOnly("Not Found"), 404),
|
|
56
|
-
InternalServerError: output.fail(rootOnly("Internal Server Error"), 500),
|
|
57
|
-
BadGateway: output.fail(rootOnly("Bad Gateway"), 502)
|
|
58
|
-
};
|
|
59
|
-
//#endregion
|
|
60
|
-
export { StandardOutput, cast, output, rootOnly };
|
package/src/steps/endpoint.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { AdaptorFn } from '../types/adapter';
|
|
2
|
-
import type { BuilderStep } from '../types/builder-step';
|
|
3
|
-
import { inputStep } from './input';
|
|
4
|
-
import { outputStep } from './output';
|
|
5
|
-
|
|
6
|
-
export function endpointStep<TContext>(adaptor: AdaptorFn<TContext>): BuilderStep.Endpoint<TContext> {
|
|
7
|
-
return {
|
|
8
|
-
endpoint(method, path) {
|
|
9
|
-
return {
|
|
10
|
-
...outputStep(adaptor, method, path),
|
|
11
|
-
...inputStep(adaptor, method, path),
|
|
12
|
-
};
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
}
|
package/src/steps/input.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Method } from '@controller-contract/core';
|
|
2
|
-
import type { AdaptorFn } from '../types/adapter';
|
|
3
|
-
import type { BuilderStep } from '../types/builder-step';
|
|
4
|
-
import { outputStep } from './output';
|
|
5
|
-
|
|
6
|
-
export function inputStep<TContext, const TMethod extends Method, const TPath extends string>(
|
|
7
|
-
adaptor: AdaptorFn<TContext>,
|
|
8
|
-
method: TMethod,
|
|
9
|
-
path: TPath,
|
|
10
|
-
): BuilderStep.Input<TContext, TMethod, TPath> {
|
|
11
|
-
return {
|
|
12
|
-
input(channel, schema) {
|
|
13
|
-
return outputStep(adaptor, method, path, channel, schema);
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
}
|
package/src/steps/output.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { InputChannel, Method, OutputMonads } from '@controller-contract/core';
|
|
2
|
-
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
|
-
import { AdapterBlueprint, AdapterWithSchemaBlueprint, type AdaptorFn } from '../types/adapter';
|
|
4
|
-
import type { BuilderStep } from '../types/builder-step';
|
|
5
|
-
|
|
6
|
-
export function outputStep<
|
|
7
|
-
TContext,
|
|
8
|
-
const TMethod extends Method,
|
|
9
|
-
const TPath extends string,
|
|
10
|
-
TInput,
|
|
11
|
-
TOutput extends OutputMonads,
|
|
12
|
-
>(
|
|
13
|
-
adaptor: AdaptorFn<TContext>,
|
|
14
|
-
method: TMethod,
|
|
15
|
-
path: TPath,
|
|
16
|
-
channel?: InputChannel,
|
|
17
|
-
schema?: StandardSchemaV1<TInput>,
|
|
18
|
-
): BuilderStep.Output<TContext, TMethod, TPath, TInput, TOutput> {
|
|
19
|
-
return {
|
|
20
|
-
output(handler) {
|
|
21
|
-
if (channel && schema) {
|
|
22
|
-
adaptor(new AdapterWithSchemaBlueprint(method, path, channel, schema, handler));
|
|
23
|
-
} else {
|
|
24
|
-
adaptor(new AdapterBlueprint(method, path, handler));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {};
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
package/src/types/adapter.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { InputChannel, Method, OutputMonads } from '@controller-contract/core';
|
|
2
|
-
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
|
-
import type { OutputHandler } from './builder-step';
|
|
4
|
-
|
|
5
|
-
export type AdaptorFn<TContext> = (
|
|
6
|
-
blueprint: AdapterBlueprint<TContext> | AdapterWithSchemaBlueprint<TContext>,
|
|
7
|
-
) => void;
|
|
8
|
-
|
|
9
|
-
export class AdapterBlueprint<TContext> {
|
|
10
|
-
constructor(
|
|
11
|
-
readonly method: Method,
|
|
12
|
-
readonly path: string,
|
|
13
|
-
readonly handler: OutputHandler<TContext, string, any, OutputMonads>,
|
|
14
|
-
) {}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class AdapterWithSchemaBlueprint<TContext> {
|
|
18
|
-
constructor(
|
|
19
|
-
readonly method: Method,
|
|
20
|
-
readonly path: string,
|
|
21
|
-
readonly channel: InputChannel,
|
|
22
|
-
readonly schema: StandardSchemaV1,
|
|
23
|
-
readonly handler: OutputHandler<TContext, string, any, OutputMonads>,
|
|
24
|
-
) {}
|
|
25
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ControllerContract,
|
|
3
|
-
InputChannel,
|
|
4
|
-
Method,
|
|
5
|
-
NoInput,
|
|
6
|
-
OutputFail,
|
|
7
|
-
OutputMonads,
|
|
8
|
-
ParamsFrom,
|
|
9
|
-
Prettify,
|
|
10
|
-
} from '@controller-contract/core';
|
|
11
|
-
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
12
|
-
import type { output } from '../utils';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* All the possible steps are defined below.
|
|
16
|
-
*/
|
|
17
|
-
export namespace BuilderStep {
|
|
18
|
-
/**
|
|
19
|
-
* Always the first step
|
|
20
|
-
*/
|
|
21
|
-
export interface Endpoint<TContext> {
|
|
22
|
-
endpoint<const TMethod extends Method, const TPath extends string>(
|
|
23
|
-
method: TMethod,
|
|
24
|
-
path: TPath,
|
|
25
|
-
): BuilderStep.Output<TContext, TMethod, TPath, NoInput, never> & BuilderStep.Input<TContext, TMethod, TPath>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface Input<TContext, TMethod extends Method, TPath extends string> {
|
|
29
|
-
input<TInput>(
|
|
30
|
-
channel: InputChannel,
|
|
31
|
-
schema: StandardSchemaV1<TInput>,
|
|
32
|
-
): BuilderStep.Output<TContext, TMethod, TPath, TInput, OutputFail<Extract<keyof TInput, string>>>;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Always the last step
|
|
37
|
-
*/
|
|
38
|
-
export interface Output<
|
|
39
|
-
TContext,
|
|
40
|
-
TMethod extends Method,
|
|
41
|
-
TPath extends string,
|
|
42
|
-
TInput,
|
|
43
|
-
TOutput extends OutputMonads,
|
|
44
|
-
> {
|
|
45
|
-
output<TFnOutput extends OutputMonads>(
|
|
46
|
-
handler: OutputHandler<TContext, TPath, TInput, TFnOutput>,
|
|
47
|
-
): ControllerContract<TMethod, TPath, TInput, TOutput | TFnOutput>;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type OutputHandler<TContext, TPath extends string, TInput, TOutput extends OutputMonads> = (
|
|
52
|
-
context: Prettify<
|
|
53
|
-
TContext & {
|
|
54
|
-
params: ParamsFrom<TPath>;
|
|
55
|
-
input: TInput;
|
|
56
|
-
output: typeof output;
|
|
57
|
-
}
|
|
58
|
-
>,
|
|
59
|
-
) => TOutput | Promise<TOutput>;
|
package/src/utils/cast.ts
DELETED
package/src/utils/output.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { OutputExit, OutputFail, OutputNext, OutputPass } from '@controller-contract/core';
|
|
2
|
-
|
|
3
|
-
export namespace output {
|
|
4
|
-
export function next<T>(data: T, code = 100): OutputNext<T> {
|
|
5
|
-
return {
|
|
6
|
-
type: 'next',
|
|
7
|
-
code,
|
|
8
|
-
data,
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function pass<T>(data: T, code = 200): OutputPass<T> {
|
|
13
|
-
return {
|
|
14
|
-
type: 'pass',
|
|
15
|
-
code,
|
|
16
|
-
data,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function fail<const T extends string>(data: OutputFail<T>['data'], code = 400): OutputFail<T> {
|
|
21
|
-
return {
|
|
22
|
-
type: 'fail',
|
|
23
|
-
code,
|
|
24
|
-
data,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Used to escape the controller contract state machine, useful for redirects.
|
|
30
|
-
*/
|
|
31
|
-
export function exit(): OutputExit {
|
|
32
|
-
return {
|
|
33
|
-
type: 'exit',
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function rootOnly(message: string): OutputFail<'root'>['data'] {
|
|
39
|
-
return [{ key: 'root', message }];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const StandardOutput = {
|
|
43
|
-
// 200
|
|
44
|
-
OK: output.pass(rootOnly('ok'), 200),
|
|
45
|
-
Created: output.pass(rootOnly('Created'), 201),
|
|
46
|
-
Accepted: output.pass(rootOnly('Accepted'), 202),
|
|
47
|
-
// 400
|
|
48
|
-
BadRequest: output.fail(rootOnly('Bad Request'), 400),
|
|
49
|
-
Unauthorized: output.fail(rootOnly('Unauthorized'), 401),
|
|
50
|
-
Forbidden: output.fail(rootOnly('Forbidden'), 403),
|
|
51
|
-
NotFound: output.fail(rootOnly('Not Found'), 404),
|
|
52
|
-
// 500
|
|
53
|
-
InternalServerError: output.fail(rootOnly('Internal Server Error'), 500),
|
|
54
|
-
BadGateway: output.fail(rootOnly('Bad Gateway'), 502),
|
|
55
|
-
};
|