@controller-contract/server 0.1.1 → 0.2.1

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