ts-procedures 1.0.0 → 1.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/README.md +13 -3
- package/build/errors.d.ts +16 -0
- package/build/errors.js +37 -0
- package/build/errors.js.map +1 -0
- package/build/exports.d.ts +6 -0
- package/build/exports.js +7 -0
- package/build/exports.js.map +1 -0
- package/build/implementations/http/client/index.d.ts +1 -0
- package/build/implementations/http/client/index.js +2 -0
- package/build/implementations/http/client/index.js.map +1 -0
- package/build/implementations/http/express/example/factories.d.ts +97 -0
- package/build/implementations/http/express/example/factories.js +4 -0
- package/build/implementations/http/express/example/factories.js.map +1 -0
- package/build/implementations/http/express/example/procedures/auth.d.ts +1 -0
- package/build/implementations/http/express/example/procedures/auth.js +22 -0
- package/build/implementations/http/express/example/procedures/auth.js.map +1 -0
- package/build/implementations/http/express/example/procedures/users.d.ts +1 -0
- package/build/implementations/http/express/example/procedures/users.js +30 -0
- package/build/implementations/http/express/example/procedures/users.js.map +1 -0
- package/build/implementations/http/express/example/server.d.ts +3 -0
- package/build/implementations/http/express/example/server.js +49 -0
- package/build/implementations/http/express/example/server.js.map +1 -0
- package/build/implementations/http/express/example/server.test.d.ts +1 -0
- package/build/implementations/http/express/example/server.test.js +110 -0
- package/build/implementations/http/express/example/server.test.js.map +1 -0
- package/build/implementations/http/express/index.d.ts +34 -0
- package/build/implementations/http/express/index.js +75 -0
- package/build/implementations/http/express/index.js.map +1 -0
- package/build/implementations/http/express/index.test.d.ts +1 -0
- package/build/implementations/http/express/index.test.js +329 -0
- package/build/implementations/http/express/index.test.js.map +1 -0
- package/build/index.d.ts +71 -0
- package/build/index.js +80 -0
- package/build/index.js.map +1 -0
- package/build/index.test.d.ts +1 -0
- package/build/index.test.js +249 -0
- package/build/index.test.js.map +1 -0
- package/build/schema/compute-schema.d.ts +23 -0
- package/build/schema/compute-schema.js +28 -0
- package/build/schema/compute-schema.js.map +1 -0
- package/build/schema/compute-schema.test.d.ts +1 -0
- package/build/schema/compute-schema.test.js +107 -0
- package/build/schema/compute-schema.test.js.map +1 -0
- package/build/schema/extract-json-schema.d.ts +2 -0
- package/build/schema/extract-json-schema.js +12 -0
- package/build/schema/extract-json-schema.js.map +1 -0
- package/build/schema/extract-json-schema.test.d.ts +1 -0
- package/build/schema/extract-json-schema.test.js +23 -0
- package/build/schema/extract-json-schema.test.js.map +1 -0
- package/build/schema/parser.d.ts +21 -0
- package/build/schema/parser.js +71 -0
- package/build/schema/parser.js.map +1 -0
- package/build/schema/parser.test.d.ts +1 -0
- package/build/schema/parser.test.js +102 -0
- package/build/schema/parser.test.js.map +1 -0
- package/build/schema/resolve-schema-lib.d.ts +12 -0
- package/build/schema/resolve-schema-lib.js +11 -0
- package/build/schema/resolve-schema-lib.js.map +1 -0
- package/build/schema/resolve-schema-lib.test.d.ts +1 -0
- package/build/schema/resolve-schema-lib.test.js +17 -0
- package/build/schema/resolve-schema-lib.test.js.map +1 -0
- package/build/schema/types.d.ts +7 -0
- package/build/schema/types.js +2 -0
- package/build/schema/types.js.map +1 -0
- package/package.json +22 -9
- package/src/implementations/http/client/index.ts +0 -0
- package/src/implementations/http/express/README.md +351 -0
- package/src/implementations/http/express/example/factories.ts +25 -0
- package/src/implementations/http/express/example/procedures/auth.ts +24 -0
- package/src/implementations/http/express/example/procedures/users.ts +32 -0
- package/src/implementations/http/express/example/server.test.ts +133 -0
- package/src/implementations/http/express/example/server.ts +67 -0
- package/src/implementations/http/express/index.test.ts +526 -0
- package/src/implementations/http/express/index.ts +108 -0
- package/src/index.test.ts +4 -2
- package/src/index.ts +9 -17
- package/src/schema/parser.ts +5 -4
- package/src/schema/types.ts +0 -1
package/README.md
CHANGED
|
@@ -312,6 +312,16 @@ const { Create } = Procedures<{ req: Request; res: Response }>({
|
|
|
312
312
|
// Procedures are automatically registered as /rpc/GetUser, /rpc/CreateUser, etc.
|
|
313
313
|
```
|
|
314
314
|
|
|
315
|
+
### Express Utility
|
|
316
|
+
|
|
317
|
+
`ts-procedures` includes a built-in Express integration that handles route registration, parameter extraction, and validation automatically.
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import { registerExpressRoutes } from 'ts-procedures/express'
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
See [Express Integration Guide](src/implementations/http/express/README.md) for complete setup instructions.
|
|
324
|
+
|
|
315
325
|
### Introspection with getProcedures()
|
|
316
326
|
|
|
317
327
|
Access all registered procedures for documentation or routing:
|
|
@@ -326,8 +336,8 @@ Create('ListUsers', { schema: { params: Type.Object({}) } }, async () => {})
|
|
|
326
336
|
const procedures = getProcedures()
|
|
327
337
|
|
|
328
338
|
// Generate OpenAPI spec
|
|
329
|
-
for (const
|
|
330
|
-
console.log(`${name}:`, config.schema)
|
|
339
|
+
for (const config of procedures) {
|
|
340
|
+
console.log(`${config.name}:`, config.schema)
|
|
331
341
|
}
|
|
332
342
|
```
|
|
333
343
|
|
|
@@ -403,7 +413,7 @@ Creates a procedure factory.
|
|
|
403
413
|
|
|
404
414
|
**Returns:**
|
|
405
415
|
- `Create` - Function to define procedures
|
|
406
|
-
- `getProcedures()` - Returns `
|
|
416
|
+
- `getProcedures()` - Returns `Array` of all registered procedures
|
|
407
417
|
|
|
408
418
|
### Create(name, config, handler)
|
|
409
419
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TSchemaValidationError } from './schema/parser.js';
|
|
2
|
+
export declare class ProcedureError extends Error {
|
|
3
|
+
readonly procedureName: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly meta?: object | undefined;
|
|
6
|
+
constructor(procedureName: string, message: string, meta?: object | undefined);
|
|
7
|
+
}
|
|
8
|
+
export declare class ProcedureValidationError extends ProcedureError {
|
|
9
|
+
readonly procedureName: string;
|
|
10
|
+
readonly errors?: TSchemaValidationError[] | undefined;
|
|
11
|
+
constructor(procedureName: string, message: string, errors?: TSchemaValidationError[] | undefined);
|
|
12
|
+
}
|
|
13
|
+
export declare class ProcedureRegistrationError extends Error {
|
|
14
|
+
readonly procedureName: string;
|
|
15
|
+
constructor(procedureName: string, message: string);
|
|
16
|
+
}
|
package/build/errors.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class ProcedureError extends Error {
|
|
2
|
+
procedureName;
|
|
3
|
+
message;
|
|
4
|
+
meta;
|
|
5
|
+
constructor(procedureName, message, meta) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.procedureName = procedureName;
|
|
8
|
+
this.message = message;
|
|
9
|
+
this.meta = meta;
|
|
10
|
+
this.name = 'ProcedureError';
|
|
11
|
+
// https://www.dannyguo.com/blog/how-to-fix-instanceof-not-working-for-custom-errors-in-typescript/
|
|
12
|
+
Object.setPrototypeOf(this, ProcedureError.prototype);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class ProcedureValidationError extends ProcedureError {
|
|
16
|
+
procedureName;
|
|
17
|
+
errors;
|
|
18
|
+
constructor(procedureName, message, errors) {
|
|
19
|
+
super(procedureName, message);
|
|
20
|
+
this.procedureName = procedureName;
|
|
21
|
+
this.errors = errors;
|
|
22
|
+
this.name = 'ProcedureValidationError';
|
|
23
|
+
// https://www.dannyguo.com/blog/how-to-fix-instanceof-not-working-for-custom-errors-in-typescript/
|
|
24
|
+
Object.setPrototypeOf(this, ProcedureValidationError.prototype);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class ProcedureRegistrationError extends Error {
|
|
28
|
+
procedureName;
|
|
29
|
+
constructor(procedureName, message) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.procedureName = procedureName;
|
|
32
|
+
this.name = 'ProcedureRegistrationError';
|
|
33
|
+
// https://www.dannyguo.com/blog/how-to-fix-instanceof-not-working-for-custom-errors-in-typescript/
|
|
34
|
+
Object.setPrototypeOf(this, ProcedureRegistrationError.prototype);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,cAAe,SAAQ,KAAK;IAE5B;IACA;IACA;IAHX,YACW,aAAqB,EACrB,OAAe,EACf,IAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAA;QAJL,kBAAa,GAAb,aAAa,CAAQ;QACrB,YAAO,GAAP,OAAO,CAAQ;QACf,SAAI,GAAJ,IAAI,CAAS;QAGtB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;QAE5B,mGAAmG;QACnG,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;IACvD,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,cAAc;IAE/C;IAEA;IAHX,YACW,aAAqB,EAC9B,OAAe,EACN,MAAiC;QAE1C,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;QAJpB,kBAAa,GAAb,aAAa,CAAQ;QAErB,WAAM,GAAN,MAAM,CAA2B;QAG1C,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAA;QAEtC,mGAAmG;QACnG,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,wBAAwB,CAAC,SAAS,CAAC,CAAA;IACjE,CAAC;CACF;AAED,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAC9B;IAArB,YAAqB,aAAqB,EAAE,OAAe;QACzD,KAAK,CAAC,OAAO,CAAC,CAAA;QADK,kBAAa,GAAb,aAAa,CAAQ;QAExC,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAA;QAExC,mGAAmG;QACnG,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAA;IACnE,CAAC;CACF"}
|
package/build/exports.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './index.js';
|
|
2
|
+
export * from './errors.js';
|
|
3
|
+
export * from './schema/extract-json-schema.js';
|
|
4
|
+
export * from './schema/parser.js';
|
|
5
|
+
export * from './schema/resolve-schema-lib.js';
|
|
6
|
+
export * from './schema/types.js';
|
|
7
|
+
//# sourceMappingURL=exports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,iCAAiC,CAAA;AAC/C,cAAc,oBAAoB,CAAA;AAClC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,mBAAmB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/implementations/http/client/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export interface ProceduresContext {
|
|
2
|
+
headers: Record<string, string>;
|
|
3
|
+
ipAddress: string;
|
|
4
|
+
requestId: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ProtectedContext extends ProceduresContext {
|
|
7
|
+
userId: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PublicContext extends ProceduresContext {
|
|
10
|
+
userId?: undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare const ProtectedFactory: {
|
|
13
|
+
getProcedures: () => import("../../../../index.js").TProcedureRegistration<ProtectedContext, HTTPRouteConfig>[];
|
|
14
|
+
Create: <TName extends string, TParams, TReturnType>(name: TName, config: {
|
|
15
|
+
description?: string;
|
|
16
|
+
schema?: {
|
|
17
|
+
params?: TParams | undefined;
|
|
18
|
+
returnType?: TReturnType | undefined;
|
|
19
|
+
} | undefined;
|
|
20
|
+
} & HTTPRouteConfig, handler: (ctx: {
|
|
21
|
+
userId: string;
|
|
22
|
+
headers: Record<string, string>;
|
|
23
|
+
ipAddress: string;
|
|
24
|
+
requestId: string;
|
|
25
|
+
error: (message: string, meta?: object) => import("../../../../errors.js").ProcedureError;
|
|
26
|
+
}, params: import("../../../../exports.js").TSchemaLib<TParams>) => Promise<import("../../../../exports.js").TSchemaLib<TReturnType>>) => { [K in TName]: (ctx: {
|
|
27
|
+
userId: string;
|
|
28
|
+
headers: Record<string, string>;
|
|
29
|
+
ipAddress: string;
|
|
30
|
+
requestId: string;
|
|
31
|
+
}, params: import("../../../../exports.js").TSchemaLib<TParams>) => Promise<import("../../../../exports.js").TSchemaLib<TReturnType>>; } & {
|
|
32
|
+
procedure: (ctx: {
|
|
33
|
+
userId: string;
|
|
34
|
+
headers: Record<string, string>;
|
|
35
|
+
ipAddress: string;
|
|
36
|
+
requestId: string;
|
|
37
|
+
}, params: import("../../../../exports.js").TSchemaLib<TParams>) => Promise<import("../../../../exports.js").TSchemaLib<TReturnType>>;
|
|
38
|
+
info: {
|
|
39
|
+
name: TName;
|
|
40
|
+
description?: string;
|
|
41
|
+
schema: {
|
|
42
|
+
params?: TParams | undefined;
|
|
43
|
+
returnType?: TReturnType | undefined;
|
|
44
|
+
};
|
|
45
|
+
validation?: {
|
|
46
|
+
params?: ((params: any) => {
|
|
47
|
+
errors?: any[];
|
|
48
|
+
}) | undefined;
|
|
49
|
+
} | undefined;
|
|
50
|
+
} & HTTPRouteConfig;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export declare const PublicFactory: {
|
|
54
|
+
getProcedures: () => import("../../../../index.js").TProcedureRegistration<PublicContext, HTTPRouteConfig>[];
|
|
55
|
+
Create: <TName extends string, TParams, TReturnType>(name: TName, config: {
|
|
56
|
+
description?: string;
|
|
57
|
+
schema?: {
|
|
58
|
+
params?: TParams | undefined;
|
|
59
|
+
returnType?: TReturnType | undefined;
|
|
60
|
+
} | undefined;
|
|
61
|
+
} & HTTPRouteConfig, handler: (ctx: {
|
|
62
|
+
userId?: undefined | undefined;
|
|
63
|
+
headers: Record<string, string>;
|
|
64
|
+
ipAddress: string;
|
|
65
|
+
requestId: string;
|
|
66
|
+
error: (message: string, meta?: object) => import("../../../../errors.js").ProcedureError;
|
|
67
|
+
}, params: import("../../../../exports.js").TSchemaLib<TParams>) => Promise<import("../../../../exports.js").TSchemaLib<TReturnType>>) => { [K in TName]: (ctx: {
|
|
68
|
+
userId?: undefined | undefined;
|
|
69
|
+
headers: Record<string, string>;
|
|
70
|
+
ipAddress: string;
|
|
71
|
+
requestId: string;
|
|
72
|
+
}, params: import("../../../../exports.js").TSchemaLib<TParams>) => Promise<import("../../../../exports.js").TSchemaLib<TReturnType>>; } & {
|
|
73
|
+
procedure: (ctx: {
|
|
74
|
+
userId?: undefined | undefined;
|
|
75
|
+
headers: Record<string, string>;
|
|
76
|
+
ipAddress: string;
|
|
77
|
+
requestId: string;
|
|
78
|
+
}, params: import("../../../../exports.js").TSchemaLib<TParams>) => Promise<import("../../../../exports.js").TSchemaLib<TReturnType>>;
|
|
79
|
+
info: {
|
|
80
|
+
name: TName;
|
|
81
|
+
description?: string;
|
|
82
|
+
schema: {
|
|
83
|
+
params?: TParams | undefined;
|
|
84
|
+
returnType?: TReturnType | undefined;
|
|
85
|
+
};
|
|
86
|
+
validation?: {
|
|
87
|
+
params?: ((params: any) => {
|
|
88
|
+
errors?: any[];
|
|
89
|
+
}) | undefined;
|
|
90
|
+
} | undefined;
|
|
91
|
+
} & HTTPRouteConfig;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
export interface HTTPRouteConfig {
|
|
95
|
+
path: string;
|
|
96
|
+
method: 'get' | 'post' | 'put' | 'delete' | 'patch';
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factories.js","sourceRoot":"","sources":["../../../../../src/implementations/http/express/example/factories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAiBjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,EAAoC,CAAA;AAE9E,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,EAAiC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PublicFactory } from '../factories.js';
|
|
2
|
+
import { Type } from 'typebox';
|
|
3
|
+
PublicFactory.Create('Authenticate', {
|
|
4
|
+
path: '/authenticate',
|
|
5
|
+
method: 'post',
|
|
6
|
+
schema: {
|
|
7
|
+
params: Type.Object({
|
|
8
|
+
username: Type.String({ minLength: 3 }),
|
|
9
|
+
password: Type.String({ minLength: 6 }),
|
|
10
|
+
}),
|
|
11
|
+
returnType: Type.Object({
|
|
12
|
+
token: Type.String(),
|
|
13
|
+
})
|
|
14
|
+
},
|
|
15
|
+
description: 'Authenticate as a user and obtain a token',
|
|
16
|
+
}, async (ctx, params) => {
|
|
17
|
+
// In a real implementation, you would verify user credentials here, ie: ctx.services.userService.authenticate(params.username, params.password)
|
|
18
|
+
return {
|
|
19
|
+
token: 'fake-jwt-token',
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../../src/implementations/http/express/example/procedures/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE;IACnC,IAAI,EAAE,eAAe;IACrB,MAAM,EAAE,MAAM;IACd,MAAM,EAAE;QACN,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;YAClB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;YACvC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAG,CAAC;SACzC,CAAC;QACF,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;YACtB,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;SACrB,CAAC;KACH;IACD,WAAW,EAAE,2CAA2C;CACvD,EACD,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;IACpB,gJAAgJ;IAEhJ,OAAO;QACL,KAAK,EAAE,gBAAgB;KACxB,CAAA;AACL,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ProtectedFactory } from '../factories.js';
|
|
2
|
+
import { Type } from 'typebox';
|
|
3
|
+
ProtectedFactory.Create('Get User Profile', {
|
|
4
|
+
path: '/users/user-profile/:id',
|
|
5
|
+
method: 'get',
|
|
6
|
+
schema: {
|
|
7
|
+
params: Type.Object({
|
|
8
|
+
// Our router in this example will map :id to this param
|
|
9
|
+
id: Type.String(),
|
|
10
|
+
}),
|
|
11
|
+
returnType: Type.Object({
|
|
12
|
+
user: Type.Object({
|
|
13
|
+
id: Type.String(),
|
|
14
|
+
name: Type.String(),
|
|
15
|
+
email: Type.String(),
|
|
16
|
+
}),
|
|
17
|
+
})
|
|
18
|
+
},
|
|
19
|
+
description: 'Get the profile of a specific user',
|
|
20
|
+
}, async (ctx, params) => {
|
|
21
|
+
// In a real implementation, you would fetch the user profile from a database or service here, ie: ctx.services.userService.getUserProfile(params.userId)
|
|
22
|
+
return {
|
|
23
|
+
user: {
|
|
24
|
+
id: params.id,
|
|
25
|
+
name: 'Jane Doe',
|
|
26
|
+
email: 'test@gmail.com'
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=users.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../../../../../src/implementations/http/express/example/procedures/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B,gBAAgB,CAAC,MAAM,CAAC,kBAAkB,EAAE;IAC1C,IAAI,EAAE,yBAAyB;IAC/B,MAAM,EAAE,KAAK;IACb,MAAM,EAAE;QACN,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;YAClB,wDAAwD;YACxD,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE;SAClB,CAAC;QACF,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;YACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;gBAChB,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE;gBACjB,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE;gBACnB,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;aACrB,CAAC;SACH,CAAC;KACH;IACD,WAAW,EAAE,oCAAoC;CAChD,EACD,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;IACpB,yJAAyJ;IAEzJ,OAAO;QACL,IAAI,EAAE;YACJ,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,gBAAgB;SACxB;KACF,CAAA;AACL,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { PublicFactory, ProtectedFactory } from './factories.js';
|
|
3
|
+
import { registerExpressRoutes } from '../index.js';
|
|
4
|
+
// Import procedures for their side effects (they register with the factories)
|
|
5
|
+
import './procedures/auth.js';
|
|
6
|
+
import './procedures/users.js';
|
|
7
|
+
const authTokenService = (authToken) => {
|
|
8
|
+
// Dummy implementation - replace with real token validation logic
|
|
9
|
+
return {
|
|
10
|
+
isValid: authToken === 'valid-token',
|
|
11
|
+
userId: authToken === 'valid-token' ? 'user-123' : null,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export const app = express();
|
|
15
|
+
app.use(express.json());
|
|
16
|
+
registerExpressRoutes(app, {
|
|
17
|
+
getContext: async (req, res) => {
|
|
18
|
+
return {
|
|
19
|
+
ipAddress: '',
|
|
20
|
+
requestId: '',
|
|
21
|
+
headers: {},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}, PublicFactory.getProcedures());
|
|
25
|
+
registerExpressRoutes(app, {
|
|
26
|
+
getContext: async (req, res) => {
|
|
27
|
+
const authServiceResult = authTokenService(req.headers['authorization']?.toString() || '');
|
|
28
|
+
if (authServiceResult.isValid) {
|
|
29
|
+
return {
|
|
30
|
+
userId: authServiceResult.userId,
|
|
31
|
+
ipAddress: '',
|
|
32
|
+
requestId: '',
|
|
33
|
+
headers: {}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
res.status(401).json({
|
|
37
|
+
error: 'Unauthorized'
|
|
38
|
+
});
|
|
39
|
+
throw new Error('Unauthorized');
|
|
40
|
+
}
|
|
41
|
+
}, ProtectedFactory.getProcedures());
|
|
42
|
+
// Only start server when run directly (not when imported for testing)
|
|
43
|
+
const isMainModule = import.meta.url.endsWith(process.argv[1]?.replace(/\\/g, '/') || '');
|
|
44
|
+
if (isMainModule) {
|
|
45
|
+
app.listen(4841, () => {
|
|
46
|
+
console.log('Server is running on http://localhost:4841');
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../../../src/implementations/http/express/example/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAC7B,OAAO,EACL,aAAa,EACb,gBAAgB,EACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEnD,8EAA8E;AAC9E,OAAO,sBAAsB,CAAA;AAC7B,OAAO,uBAAuB,CAAA;AAE9B,MAAM,gBAAgB,GAAG,CAAC,SAAiB,EAAE,EAAE;IAC7C,kEAAkE;IAClE,OAAO;QACL,OAAO,EAAE,SAAS,KAAK,aAAa;QACpC,MAAM,EAAE,SAAS,KAAK,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;KACxD,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAA;AAC5B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;AAEvB,qBAAqB,CACnB,GAAG,EACH;IACE,UAAU,EAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC5B,OAAO;YACL,SAAS,EAAC,EAAE;YACZ,SAAS,EAAC,EAAE;YACZ,OAAO,EAAC,EAAE;SACX,CAAA;IACH,CAAC;CACF,EACD,aAAa,CAAC,aAAa,EAAE,CAC9B,CAAA;AAED,qBAAqB,CACnB,GAAG,EACH;IACE,UAAU,EAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC5B,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;QAE1F,IAAI,iBAAiB,CAAC,OAAO,EAAE,CAAC;YAC9B,OAAO;gBACL,MAAM,EAAC,iBAAiB,CAAC,MAAO;gBAChC,SAAS,EAAC,EAAE;gBACZ,SAAS,EAAC,EAAE;gBACZ,OAAO,EAAC,EAAE;aACX,CAAA;QACH,CAAC;QAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,cAAc;SACtB,CAAC,CAAA;QACF,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAA;IACjC,CAAC;CACF,EACD,gBAAgB,CAAC,aAAa,EAAE,CACjC,CAAA;AAED,sEAAsE;AACtE,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;AACzF,IAAI,YAAY,EAAE,CAAC;IACjB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import supertest from 'supertest';
|
|
3
|
+
import { app } from './server.js';
|
|
4
|
+
describe('Express Server Tests', () => {
|
|
5
|
+
describe('Public Endpoints', () => {
|
|
6
|
+
describe('POST /authenticate', () => {
|
|
7
|
+
it('should return token with valid credentials', async () => {
|
|
8
|
+
const response = await supertest(app)
|
|
9
|
+
.post('/authenticate')
|
|
10
|
+
.send({ username: 'john', password: 'secret123' });
|
|
11
|
+
expect(response.status).toBe(200);
|
|
12
|
+
expect(response.body).toEqual({ token: 'fake-jwt-token' });
|
|
13
|
+
});
|
|
14
|
+
it('should return 422 when username is missing', async () => {
|
|
15
|
+
const response = await supertest(app).post('/authenticate').send({ password: 'secret123' });
|
|
16
|
+
expect(response.status).toBe(422);
|
|
17
|
+
expect(response.body).toHaveProperty('error');
|
|
18
|
+
});
|
|
19
|
+
it('should return 422 when username is too short (minLength: 3)', async () => {
|
|
20
|
+
const response = await supertest(app)
|
|
21
|
+
.post('/authenticate')
|
|
22
|
+
.send({ username: 'ab', password: 'secret123' });
|
|
23
|
+
expect(response.status).toBe(422);
|
|
24
|
+
expect(response.body).toHaveProperty('error');
|
|
25
|
+
});
|
|
26
|
+
it('should return 422 when password is missing', async () => {
|
|
27
|
+
const response = await supertest(app).post('/authenticate').send({ username: 'john' });
|
|
28
|
+
expect(response.status).toBe(422);
|
|
29
|
+
expect(response.body).toHaveProperty('error');
|
|
30
|
+
});
|
|
31
|
+
it('should return 422 when password is too short (minLength: 6)', async () => {
|
|
32
|
+
const response = await supertest(app)
|
|
33
|
+
.post('/authenticate')
|
|
34
|
+
.send({ username: 'john', password: '12345' });
|
|
35
|
+
expect(response.status).toBe(422);
|
|
36
|
+
expect(response.body).toHaveProperty('error');
|
|
37
|
+
});
|
|
38
|
+
it('should return 422 when body is empty', async () => {
|
|
39
|
+
const response = await supertest(app).post('/authenticate').send({});
|
|
40
|
+
expect(response.status).toBe(422);
|
|
41
|
+
expect(response.body).toHaveProperty('error');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('Protected Endpoints', () => {
|
|
46
|
+
describe('GET /users/user-profile/:id', () => {
|
|
47
|
+
it('should return 401 when no authorization header is provided', async () => {
|
|
48
|
+
const response = await supertest(app).get('/users/user-profile/123');
|
|
49
|
+
expect(response.status).toBe(401);
|
|
50
|
+
expect(response.body).toEqual({ error: 'Unauthorized' });
|
|
51
|
+
});
|
|
52
|
+
it('should return 401 when authorization token is invalid', async () => {
|
|
53
|
+
const response = await supertest(app)
|
|
54
|
+
.get('/users/user-profile/123')
|
|
55
|
+
.set('Authorization', 'invalid-token');
|
|
56
|
+
expect(response.status).toBe(401);
|
|
57
|
+
expect(response.body).toEqual({ error: 'Unauthorized' });
|
|
58
|
+
});
|
|
59
|
+
it('should return user profile with valid token', async () => {
|
|
60
|
+
const response = await supertest(app)
|
|
61
|
+
.get('/users/user-profile/123')
|
|
62
|
+
.set('Authorization', 'valid-token');
|
|
63
|
+
expect(response.status).toBe(200);
|
|
64
|
+
expect(response.body).toEqual({
|
|
65
|
+
user: {
|
|
66
|
+
id: '123',
|
|
67
|
+
name: 'Jane Doe',
|
|
68
|
+
email: 'test@gmail.com',
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
it('should return user profile with different id from path', async () => {
|
|
73
|
+
const response = await supertest(app)
|
|
74
|
+
.get('/users/user-profile/abc')
|
|
75
|
+
.set('Authorization', 'valid-token');
|
|
76
|
+
expect(response.status).toBe(200);
|
|
77
|
+
expect(response.body).toEqual({
|
|
78
|
+
user: {
|
|
79
|
+
id: 'abc',
|
|
80
|
+
name: 'Jane Doe',
|
|
81
|
+
email: 'test@gmail.com',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('Response Structure', () => {
|
|
88
|
+
it('authenticate returns correct token structure', async () => {
|
|
89
|
+
const response = await supertest(app)
|
|
90
|
+
.post('/authenticate')
|
|
91
|
+
.send({ username: 'testuser', password: 'password123' });
|
|
92
|
+
expect(response.status).toBe(200);
|
|
93
|
+
expect(response.body).toHaveProperty('token');
|
|
94
|
+
expect(typeof response.body.token).toBe('string');
|
|
95
|
+
});
|
|
96
|
+
it('user profile returns correct nested user object with id, name, email', async () => {
|
|
97
|
+
const response = await supertest(app)
|
|
98
|
+
.get('/users/user-profile/test-id')
|
|
99
|
+
.set('Authorization', 'valid-token');
|
|
100
|
+
expect(response.status).toBe(200);
|
|
101
|
+
expect(response.body).toHaveProperty('user');
|
|
102
|
+
expect(response.body.user).toHaveProperty('id', 'test-id');
|
|
103
|
+
expect(response.body.user).toHaveProperty('name');
|
|
104
|
+
expect(response.body.user).toHaveProperty('email');
|
|
105
|
+
expect(typeof response.body.user.name).toBe('string');
|
|
106
|
+
expect(typeof response.body.user.email).toBe('string');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../../../../src/implementations/http/express/example/server.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { TProcedureRegistration } from '../../../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Maps path parameters from Express route to an object
|
|
5
|
+
*
|
|
6
|
+
* Path /users/:id with /users/123 => { id: '123' }
|
|
7
|
+
*
|
|
8
|
+
* @param path
|
|
9
|
+
* @param url
|
|
10
|
+
*/
|
|
11
|
+
export declare function mapPathParamsToObject(path: string, url: string): Record<string, string>;
|
|
12
|
+
/**
|
|
13
|
+
* A convenience function to register multiple procedures as Express routes.
|
|
14
|
+
*
|
|
15
|
+
* Provide the Express app, a context generator function, and an array of procedure registrations.
|
|
16
|
+
*
|
|
17
|
+
* @param app
|
|
18
|
+
* @param callbacks
|
|
19
|
+
* @param procedures
|
|
20
|
+
*/
|
|
21
|
+
export declare function registerExpressRoutes<ProceduresContext>(app: express.Application, callbacks: {
|
|
22
|
+
/** Get procedure factory context for handler */
|
|
23
|
+
getContext: (req: express.Request, res: express.Response) => Promise<ProceduresContext>;
|
|
24
|
+
/** Optional error handler for procedure handler errors */
|
|
25
|
+
onHandlerError?: (error: Error, req: express.Request, res: express.Response) => void;
|
|
26
|
+
/** Optional validation error handler */
|
|
27
|
+
onValidationError?: (errors: Array<{
|
|
28
|
+
message: string;
|
|
29
|
+
path: string[];
|
|
30
|
+
}>, req: express.Request, res: express.Response) => void;
|
|
31
|
+
}, procedures: Array<TProcedureRegistration<ProceduresContext, {
|
|
32
|
+
method: 'get' | 'post' | 'patch' | 'delete' | 'put';
|
|
33
|
+
path: string;
|
|
34
|
+
}>>): void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps path parameters from Express route to an object
|
|
3
|
+
*
|
|
4
|
+
* Path /users/:id with /users/123 => { id: '123' }
|
|
5
|
+
*
|
|
6
|
+
* @param path
|
|
7
|
+
* @param url
|
|
8
|
+
*/
|
|
9
|
+
export function mapPathParamsToObject(path, url) {
|
|
10
|
+
const urlObj = new URL(url, 'http://localhost'); // Base URL is required but irrelevant here
|
|
11
|
+
const pathSegments = path.split('/').filter(Boolean);
|
|
12
|
+
const urlSegments = urlObj.pathname.split('/').filter(Boolean);
|
|
13
|
+
const params = {};
|
|
14
|
+
pathSegments.forEach((segment, index) => {
|
|
15
|
+
if (segment.startsWith(':')) {
|
|
16
|
+
const paramName = segment.slice(1);
|
|
17
|
+
params[paramName] = urlSegments[index] || '';
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return params;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Combines path, query, and body parameters from an Express request into a single object
|
|
24
|
+
* @param req
|
|
25
|
+
*/
|
|
26
|
+
function getAllParamsFromExpressRequest(req) {
|
|
27
|
+
const pathParams = mapPathParamsToObject(req.route.path, req.url);
|
|
28
|
+
const queryParams = req.query || {};
|
|
29
|
+
const bodyParams = req.body || {};
|
|
30
|
+
return { ...pathParams, ...queryParams, ...bodyParams };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A convenience function to register multiple procedures as Express routes.
|
|
34
|
+
*
|
|
35
|
+
* Provide the Express app, a context generator function, and an array of procedure registrations.
|
|
36
|
+
*
|
|
37
|
+
* @param app
|
|
38
|
+
* @param callbacks
|
|
39
|
+
* @param procedures
|
|
40
|
+
*/
|
|
41
|
+
export function registerExpressRoutes(app, callbacks,
|
|
42
|
+
// The procedure extended config must include method and path
|
|
43
|
+
procedures) {
|
|
44
|
+
procedures.forEach(({ handler, config }) => {
|
|
45
|
+
const { method, path } = config;
|
|
46
|
+
app[method](path, async (req, res) => {
|
|
47
|
+
const context = await callbacks.getContext(req, res);
|
|
48
|
+
const allParams = getAllParamsFromExpressRequest(req);
|
|
49
|
+
if (config.validation?.params) {
|
|
50
|
+
const { errors } = config.validation.params(allParams);
|
|
51
|
+
if (errors && errors.length > 0) {
|
|
52
|
+
if (callbacks.onValidationError) {
|
|
53
|
+
callbacks.onValidationError(errors, req, res);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
res.status(422).json({ error: 'Validation Error', details: errors });
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const result = await handler(context, allParams);
|
|
63
|
+
res.json(result);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (callbacks.onHandlerError) {
|
|
67
|
+
callbacks.onHandlerError(error, req, res);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
res.status(500).json({ error: error.message });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/implementations/http/express/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|