@controller-contract/server 0.1.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/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kenny G Perez
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.
@@ -0,0 +1,40 @@
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 };
@@ -0,0 +1,67 @@
1
+ import { n as output } from "./index-CgtO_soW.mjs";
2
+ import { ControllerContract, InputChannel, Method, NoInput, OutputFail, OutputMonads, ParamsFrom, Prettify } from "@controller-contract/core";
3
+ import { StandardSchemaV1 } from "@standard-schema/spec";
4
+
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> {
35
+ readonly method: Method;
36
+ readonly path: string;
37
+ readonly handler: OutputHandler<TContext, string, any, OutputMonads>;
38
+ constructor(method: Method, path: string, handler: OutputHandler<TContext, string, any, OutputMonads>);
39
+ }
40
+ declare class AdapterWithSchemaBlueprint<TContext> {
41
+ readonly method: Method;
42
+ readonly path: string;
43
+ readonly channel: InputChannel;
44
+ 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>);
47
+ }
48
+ //#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>;
66
+ //#endregion
67
+ export { AdapterBlueprint, AdapterWithSchemaBlueprint, AdaptorFn, controllerContractBuilderFactory };
package/dist/index.mjs ADDED
@@ -0,0 +1,73 @@
1
+ //#region src/types/adapter.ts
2
+ var AdapterBlueprint = class {
3
+ method;
4
+ path;
5
+ handler;
6
+ constructor(method, path, handler) {
7
+ this.method = method;
8
+ this.path = path;
9
+ this.handler = handler;
10
+ }
11
+ };
12
+ var AdapterWithSchemaBlueprint = class {
13
+ method;
14
+ path;
15
+ channel;
16
+ schema;
17
+ handler;
18
+ constructor(method, path, channel, schema, handler) {
19
+ this.method = method;
20
+ this.path = path;
21
+ this.channel = channel;
22
+ this.schema = schema;
23
+ this.handler = handler;
24
+ }
25
+ };
26
+ //#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
+ }
35
+ //#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
+ }
42
+ //#endregion
43
+ //#region src/steps/endpoint.ts
44
+ function endpointStep(adaptor) {
45
+ return { endpoint(method, path) {
46
+ return {
47
+ ...outputStep(adaptor, method, path),
48
+ ...inputStep(adaptor, method, path)
49
+ };
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);
71
+ }
72
+ //#endregion
73
+ export { AdapterBlueprint, AdapterWithSchemaBlueprint, controllerContractBuilderFactory };
@@ -0,0 +1,2 @@
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 };
@@ -0,0 +1,60 @@
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/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@controller-contract/server",
3
+ "type": "module",
4
+ "version": "0.1.1",
5
+ "author": "kennychulo",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.mjs",
9
+ "types": "./dist/index.d.ts",
10
+ "files": [
11
+ "dist",
12
+ "src",
13
+ "LICENSE",
14
+ "package.json"
15
+ ],
16
+ "exports": {
17
+ ".": "./dist/index.mjs",
18
+ "./utils": "./dist/utils/index.mjs",
19
+ "./package.json": "./package.json"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "22.19.19",
26
+ "tsdown": "0.22.0",
27
+ "typescript": "6.0.3"
28
+ },
29
+ "dependencies": {
30
+ "@controller-contract/core": "0.1.1",
31
+ "@standard-schema/spec": "1.1.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsdown",
35
+ "release": "pnpm publish"
36
+ }
37
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
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
+ }
@@ -0,0 +1,15 @@
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
+ }
@@ -0,0 +1,16 @@
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
+ }
@@ -0,0 +1,30 @@
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
+ }
@@ -0,0 +1,25 @@
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
+ }
@@ -0,0 +1,59 @@
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>;
@@ -0,0 +1,7 @@
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
+ }
@@ -0,0 +1,2 @@
1
+ export * from './cast';
2
+ export * from './output';
@@ -0,0 +1,55 @@
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
+ };