@arinoto/cdk-arch 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-container.d.ts +39 -12
- package/dist/api-container.d.ts.map +1 -1
- package/dist/api-container.js +13 -2
- package/dist/binding.d.ts +2 -2
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +1 -1
- package/dist/http-handler.d.ts +26 -5
- package/dist/http-handler.d.ts.map +1 -1
- package/dist/http-handler.js +31 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/package.json +1 -1
- package/src/api-container.ts +49 -14
- package/src/binding.ts +2 -2
- package/src/http-handler.ts +53 -11
- package/src/index.ts +2 -2
package/dist/api-container.d.ts
CHANGED
|
@@ -1,21 +1,48 @@
|
|
|
1
1
|
import { Construct } from 'constructs';
|
|
2
2
|
import { Function } from './function';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export interface RouteEntry {
|
|
3
|
+
/**
|
|
4
|
+
* A route entry that captures the handler's argument and return types.
|
|
5
|
+
*/
|
|
6
|
+
export interface RouteEntry<TArgs extends any[] = any[], TReturn = any> {
|
|
7
7
|
path: string;
|
|
8
|
-
handler: Function
|
|
8
|
+
handler: Function<TArgs, TReturn>;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Base type for route definitions - maps route names to route entries.
|
|
12
|
+
*/
|
|
13
|
+
export type ApiRoutes = {
|
|
14
|
+
[name: string]: RouteEntry;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Extract the handler signature from a Function type.
|
|
18
|
+
*/
|
|
19
|
+
export type HandlerOf<T> = T extends Function<infer Args, infer Return> ? (...args: Args) => Promise<Return> : never;
|
|
20
|
+
/**
|
|
21
|
+
* Extract handler signatures from all routes in an ApiRoutes type.
|
|
22
|
+
*/
|
|
23
|
+
export type RouteHandlers<TRoutes extends ApiRoutes> = {
|
|
24
|
+
[K in keyof TRoutes]: HandlerOf<TRoutes[K]['handler']>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Represents an API container that routes requests to functions.
|
|
28
|
+
*
|
|
29
|
+
* @typeParam TRoutes - The type of the routes object, preserving route names and handler signatures.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const api = new ApiContainer(arch, 'api', {
|
|
34
|
+
* hello: { path: 'GET /v1/api/hello/{name}', handler: helloFunction },
|
|
35
|
+
* hellos: { path: 'GET /v1/api/hellos', handler: hellosFunction }
|
|
36
|
+
* });
|
|
37
|
+
* // api has type ApiContainer<{ hello: RouteEntry<[string], string>, hellos: RouteEntry<[], Greeting[]> }>
|
|
38
|
+
* ```
|
|
12
39
|
*/
|
|
13
|
-
export declare class ApiContainer extends Construct {
|
|
14
|
-
readonly routes:
|
|
15
|
-
constructor(scope: Construct, id: string, routes?:
|
|
16
|
-
addRoute(name: string, path: string, handler: Function): void;
|
|
17
|
-
getRoute(name:
|
|
18
|
-
listRoutes(): string[];
|
|
40
|
+
export declare class ApiContainer<TRoutes extends ApiRoutes = ApiRoutes> extends Construct {
|
|
41
|
+
readonly routes: TRoutes;
|
|
42
|
+
constructor(scope: Construct, id: string, routes?: TRoutes);
|
|
43
|
+
addRoute<TArgs extends any[], TReturn>(name: string, path: string, handler: Function<TArgs, TReturn>): void;
|
|
44
|
+
getRoute<K extends keyof TRoutes & string>(name: K): TRoutes[K];
|
|
45
|
+
listRoutes(): (keyof TRoutes & string)[];
|
|
19
46
|
/**
|
|
20
47
|
* Returns a list of TBDFunctions that have not been overloaded.
|
|
21
48
|
* Use this to validate that all required implementations are provided.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-container.d.ts","sourceRoot":"","sources":["../src/api-container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,SAAS;
|
|
1
|
+
{"version":3,"file":"api-container.d.ts","sourceRoot":"","sources":["../src/api-container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,KAAK,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,OAAO,GAAG,GAAG;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;CAC5B,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,MAAM,IAAI,EAAE,MAAM,MAAM,CAAC,GACnE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,GAClC,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,OAAO,SAAS,SAAS,IAAI;KACpD,CAAC,IAAI,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;CACvD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAY,CAAC,OAAO,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,SAAS;IAChF,SAAgB,MAAM,EAAE,OAAO,CAAC;gBAEpB,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,GAAE,OAAuB;IAKzE,QAAQ,CAAC,KAAK,SAAS,GAAG,EAAE,EAAE,OAAO,EACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,GAChC,IAAI;IAIP,QAAQ,CAAC,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAQ/D,UAAU,IAAI,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE;IAIxC;;;OAGG;IACH,iBAAiB,IAAI,QAAQ,EAAE;CAKhC"}
|
package/dist/api-container.js
CHANGED
|
@@ -4,7 +4,18 @@ exports.ApiContainer = void 0;
|
|
|
4
4
|
const constructs_1 = require("constructs");
|
|
5
5
|
const function_1 = require("./function");
|
|
6
6
|
/**
|
|
7
|
-
* Represents an API container that routes requests to functions
|
|
7
|
+
* Represents an API container that routes requests to functions.
|
|
8
|
+
*
|
|
9
|
+
* @typeParam TRoutes - The type of the routes object, preserving route names and handler signatures.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const api = new ApiContainer(arch, 'api', {
|
|
14
|
+
* hello: { path: 'GET /v1/api/hello/{name}', handler: helloFunction },
|
|
15
|
+
* hellos: { path: 'GET /v1/api/hellos', handler: hellosFunction }
|
|
16
|
+
* });
|
|
17
|
+
* // api has type ApiContainer<{ hello: RouteEntry<[string], string>, hellos: RouteEntry<[], Greeting[]> }>
|
|
18
|
+
* ```
|
|
8
19
|
*/
|
|
9
20
|
class ApiContainer extends constructs_1.Construct {
|
|
10
21
|
constructor(scope, id, routes = {}) {
|
|
@@ -35,4 +46,4 @@ class ApiContainer extends constructs_1.Construct {
|
|
|
35
46
|
}
|
|
36
47
|
}
|
|
37
48
|
exports.ApiContainer = ApiContainer;
|
|
38
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLWNvbnRhaW5lci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9hcGktY29udGFpbmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDJDQUF1QztBQUN2Qyx5Q0FBbUQ7QUErQm5EOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFhLFlBQW9ELFNBQVEsc0JBQVM7SUFHaEYsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxTQUFrQixFQUFhO1FBQ3ZFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDakIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVELFFBQVEsQ0FDTixJQUFZLEVBQ1osSUFBWSxFQUNaLE9BQWlDO1FBRWhDLElBQUksQ0FBQyxNQUFvQixDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDO0lBQ3ZELENBQUM7SUFFRCxRQUFRLENBQW1DLElBQU87UUFDaEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDWCxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsSUFBSSw2QkFBNkIsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzlFLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxVQUFVO1FBQ1IsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQStCLENBQUM7SUFDaEUsQ0FBQztJQUVEOzs7T0FHRztJQUNILGlCQUFpQjtRQUNmLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO2FBQzlCLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUM7YUFDM0IsTUFBTSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxZQUFZLHNCQUFXLElBQUksQ0FBQyxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztJQUNsRSxDQUFDO0NBQ0Y7QUFyQ0Qsb0NBcUNDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBGdW5jdGlvbiwgVEJERnVuY3Rpb24gfSBmcm9tICcuL2Z1bmN0aW9uJztcblxuLyoqXG4gKiBBIHJvdXRlIGVudHJ5IHRoYXQgY2FwdHVyZXMgdGhlIGhhbmRsZXIncyBhcmd1bWVudCBhbmQgcmV0dXJuIHR5cGVzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJvdXRlRW50cnk8VEFyZ3MgZXh0ZW5kcyBhbnlbXSA9IGFueVtdLCBUUmV0dXJuID0gYW55PiB7XG4gIHBhdGg6IHN0cmluZztcbiAgaGFuZGxlcjogRnVuY3Rpb248VEFyZ3MsIFRSZXR1cm4+O1xufVxuXG4vKipcbiAqIEJhc2UgdHlwZSBmb3Igcm91dGUgZGVmaW5pdGlvbnMgLSBtYXBzIHJvdXRlIG5hbWVzIHRvIHJvdXRlIGVudHJpZXMuXG4gKi9cbmV4cG9ydCB0eXBlIEFwaVJvdXRlcyA9IHtcbiAgW25hbWU6IHN0cmluZ106IFJvdXRlRW50cnk7XG59XG5cbi8qKlxuICogRXh0cmFjdCB0aGUgaGFuZGxlciBzaWduYXR1cmUgZnJvbSBhIEZ1bmN0aW9uIHR5cGUuXG4gKi9cbmV4cG9ydCB0eXBlIEhhbmRsZXJPZjxUPiA9IFQgZXh0ZW5kcyBGdW5jdGlvbjxpbmZlciBBcmdzLCBpbmZlciBSZXR1cm4+XG4gID8gKC4uLmFyZ3M6IEFyZ3MpID0+IFByb21pc2U8UmV0dXJuPlxuICA6IG5ldmVyO1xuXG4vKipcbiAqIEV4dHJhY3QgaGFuZGxlciBzaWduYXR1cmVzIGZyb20gYWxsIHJvdXRlcyBpbiBhbiBBcGlSb3V0ZXMgdHlwZS5cbiAqL1xuZXhwb3J0IHR5cGUgUm91dGVIYW5kbGVyczxUUm91dGVzIGV4dGVuZHMgQXBpUm91dGVzPiA9IHtcbiAgW0sgaW4ga2V5b2YgVFJvdXRlc106IEhhbmRsZXJPZjxUUm91dGVzW0tdWydoYW5kbGVyJ10+O1xufTtcblxuLyoqXG4gKiBSZXByZXNlbnRzIGFuIEFQSSBjb250YWluZXIgdGhhdCByb3V0ZXMgcmVxdWVzdHMgdG8gZnVuY3Rpb25zLlxuICpcbiAqIEB0eXBlUGFyYW0gVFJvdXRlcyAtIFRoZSB0eXBlIG9mIHRoZSByb3V0ZXMgb2JqZWN0LCBwcmVzZXJ2aW5nIHJvdXRlIG5hbWVzIGFuZCBoYW5kbGVyIHNpZ25hdHVyZXMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IGFwaSA9IG5ldyBBcGlDb250YWluZXIoYXJjaCwgJ2FwaScsIHtcbiAqICAgaGVsbG86IHsgcGF0aDogJ0dFVCAvdjEvYXBpL2hlbGxvL3tuYW1lfScsIGhhbmRsZXI6IGhlbGxvRnVuY3Rpb24gfSxcbiAqICAgaGVsbG9zOiB7IHBhdGg6ICdHRVQgL3YxL2FwaS9oZWxsb3MnLCBoYW5kbGVyOiBoZWxsb3NGdW5jdGlvbiB9XG4gKiB9KTtcbiAqIC8vIGFwaSBoYXMgdHlwZSBBcGlDb250YWluZXI8eyBoZWxsbzogUm91dGVFbnRyeTxbc3RyaW5nXSwgc3RyaW5nPiwgaGVsbG9zOiBSb3V0ZUVudHJ5PFtdLCBHcmVldGluZ1tdPiB9PlxuICogYGBgXG4gKi9cbmV4cG9ydCBjbGFzcyBBcGlDb250YWluZXI8VFJvdXRlcyBleHRlbmRzIEFwaVJvdXRlcyA9IEFwaVJvdXRlcz4gZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgcm91dGVzOiBUUm91dGVzO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHJvdXRlczogVFJvdXRlcyA9IHt9IGFzIFRSb3V0ZXMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgIHRoaXMucm91dGVzID0gcm91dGVzO1xuICB9XG5cbiAgYWRkUm91dGU8VEFyZ3MgZXh0ZW5kcyBhbnlbXSwgVFJldHVybj4oXG4gICAgbmFtZTogc3RyaW5nLFxuICAgIHBhdGg6IHN0cmluZyxcbiAgICBoYW5kbGVyOiBGdW5jdGlvbjxUQXJncywgVFJldHVybj5cbiAgKTogdm9pZCB7XG4gICAgKHRoaXMucm91dGVzIGFzIEFwaVJvdXRlcylbbmFtZV0gPSB7IHBhdGgsIGhhbmRsZXIgfTtcbiAgfVxuXG4gIGdldFJvdXRlPEsgZXh0ZW5kcyBrZXlvZiBUUm91dGVzICYgc3RyaW5nPihuYW1lOiBLKTogVFJvdXRlc1tLXSB7XG4gICAgY29uc3QgZW50cnkgPSB0aGlzLnJvdXRlc1tuYW1lXTtcbiAgICBpZiAoIWVudHJ5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFJvdXRlICcke25hbWV9JyBub3QgZm91bmQgaW4gY29udGFpbmVyICcke3RoaXMubm9kZS5pZH0nYCk7XG4gICAgfVxuICAgIHJldHVybiBlbnRyeTtcbiAgfVxuXG4gIGxpc3RSb3V0ZXMoKTogKGtleW9mIFRSb3V0ZXMgJiBzdHJpbmcpW10ge1xuICAgIHJldHVybiBPYmplY3Qua2V5cyh0aGlzLnJvdXRlcykgYXMgKGtleW9mIFRSb3V0ZXMgJiBzdHJpbmcpW107XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhIGxpc3Qgb2YgVEJERnVuY3Rpb25zIHRoYXQgaGF2ZSBub3QgYmVlbiBvdmVybG9hZGVkLlxuICAgKiBVc2UgdGhpcyB0byB2YWxpZGF0ZSB0aGF0IGFsbCByZXF1aXJlZCBpbXBsZW1lbnRhdGlvbnMgYXJlIHByb3ZpZGVkLlxuICAgKi9cbiAgdmFsaWRhdGVPdmVybG9hZHMoKTogRnVuY3Rpb25bXSB7XG4gICAgcmV0dXJuIE9iamVjdC52YWx1ZXModGhpcy5yb3V0ZXMpXG4gICAgICAubWFwKGVudHJ5ID0+IGVudHJ5LmhhbmRsZXIpXG4gICAgICAuZmlsdGVyKGZuID0+IGZuIGluc3RhbmNlb2YgVEJERnVuY3Rpb24gJiYgIWZuLmhhc092ZXJsb2FkKCkpO1xuICB9XG59XG4iXX0=
|
package/dist/binding.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Construct } from 'constructs';
|
|
2
|
-
import { ApiContainer } from './api-container';
|
|
2
|
+
import { ApiContainer, ApiRoutes } from './api-container';
|
|
3
3
|
import { FunctionHandler } from './function';
|
|
4
4
|
/**
|
|
5
5
|
* Service discovery configuration for runtime
|
|
@@ -29,7 +29,7 @@ export declare class ArchitectureBinding {
|
|
|
29
29
|
* Optionally override function implementations with the overloads option.
|
|
30
30
|
* Overload keys must be route names registered via addRoute.
|
|
31
31
|
*/
|
|
32
|
-
bind(component: ApiContainer
|
|
32
|
+
bind<TRoutes extends ApiRoutes>(component: ApiContainer<TRoutes>, options: BindOptions): void;
|
|
33
33
|
getEndpoint(component: Construct): ServiceEndpoint | undefined;
|
|
34
34
|
getAllBindings(): Map<Construct, ServiceEndpoint>;
|
|
35
35
|
setLocal(component: Construct): void;
|
package/dist/binding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAY,eAAe,EAAE,MAAM,YAAY,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAA8C;IAC9D,OAAO,CAAC,eAAe,CAA6B;IAEpD;;;;OAIG;IACH,IAAI,CAAC,OAAO,SAAS,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAe7F,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS;IAI9D,cAAc,IAAI,GAAG,CAAC,SAAS,EAAE,eAAe,CAAC;IAIjD,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAIpC,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO;CAGvC;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
|
package/dist/binding.js
CHANGED
|
@@ -47,4 +47,4 @@ exports.ArchitectureBinding = ArchitectureBinding;
|
|
|
47
47
|
* Global binding registry
|
|
48
48
|
*/
|
|
49
49
|
exports.architectureBinding = new ArchitectureBinding();
|
|
50
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
50
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmluZGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9iaW5kaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLHlDQUF1RDtBQW9CdkQ7OztHQUdHO0FBQ0gsTUFBYSxtQkFBbUI7SUFBaEM7UUFDVSxhQUFRLEdBQW9DLElBQUksR0FBRyxFQUFFLENBQUM7UUFDdEQsb0JBQWUsR0FBbUIsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQXFDdEQsQ0FBQztJQW5DQzs7OztPQUlHO0lBQ0gsSUFBSSxDQUE0QixTQUFnQyxFQUFFLE9BQW9CO1FBQ3BGLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUUzRCxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxTQUFTLElBQUksRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLEVBQUUsRUFBRTtZQUNsRSxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDWCxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsSUFBSSw2QkFBNkIsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ25GLENBQUM7WUFDRCxJQUFJLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxZQUFZLG1CQUFRLENBQUMsRUFBRSxDQUFDO2dCQUN6QyxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsSUFBSSw2QkFBNkIsQ0FBQyxDQUFDO1lBQy9ELENBQUM7WUFDRCxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsQyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxXQUFXLENBQUMsU0FBb0I7UUFDOUIsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN0QyxDQUFDO0lBRUQsY0FBYztRQUNaLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBRUQsUUFBUSxDQUFDLFNBQW9CO1FBQzNCLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFRCxPQUFPLENBQUMsU0FBb0I7UUFDMUIsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUM3QyxDQUFDO0NBQ0Y7QUF2Q0Qsa0RBdUNDO0FBRUQ7O0dBRUc7QUFDVSxRQUFBLG1CQUFtQixHQUFHLElBQUksbUJBQW1CLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgQXBpQ29udGFpbmVyLCBBcGlSb3V0ZXMgfSBmcm9tICcuL2FwaS1jb250YWluZXInO1xuaW1wb3J0IHsgRnVuY3Rpb24sIEZ1bmN0aW9uSGFuZGxlciB9IGZyb20gJy4vZnVuY3Rpb24nO1xuXG4vKipcbiAqIFNlcnZpY2UgZGlzY292ZXJ5IGNvbmZpZ3VyYXRpb24gZm9yIHJ1bnRpbWVcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTZXJ2aWNlRW5kcG9pbnQge1xuICBiYXNlVXJsOiBzdHJpbmc7XG59XG5cbi8qKlxuICogQmluZGluZyBvcHRpb25zIGZvciBhbiBBcGlDb250YWluZXJcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCaW5kT3B0aW9ucyBleHRlbmRzIFNlcnZpY2VFbmRwb2ludCB7XG4gIC8qKlxuICAgKiBPdmVycmlkZSBmdW5jdGlvbiBpbXBsZW1lbnRhdGlvbnMuXG4gICAqIEtleXMgYXJlIGZ1bmN0aW9uIHByb3BlcnR5IG5hbWVzIG9uIHRoZSBjb250YWluZXIuXG4gICAqL1xuICBvdmVybG9hZHM/OiBSZWNvcmQ8c3RyaW5nLCBGdW5jdGlvbkhhbmRsZXI+O1xufVxuXG4vKipcbiAqIEJpbmRpbmcgYmV0d2VlbiBhcmNoaXRlY3R1cmFsIGNvbXBvbmVudHMgYW5kIHRoZWlyIHJ1bnRpbWUgZW5kcG9pbnRzLlxuICogU3VwcG9ydHMgZnVuY3Rpb24gb3ZlcmxvYWRpbmcgZm9yIHJlcGxhY2luZyBpbXBsZW1lbnRhdGlvbnMgYXQgcnVudGltZS5cbiAqL1xuZXhwb3J0IGNsYXNzIEFyY2hpdGVjdHVyZUJpbmRpbmcge1xuICBwcml2YXRlIGJpbmRpbmdzOiBNYXA8Q29uc3RydWN0LCBTZXJ2aWNlRW5kcG9pbnQ+ID0gbmV3IE1hcCgpO1xuICBwcml2YXRlIGxvY2FsQ29tcG9uZW50czogU2V0PENvbnN0cnVjdD4gPSBuZXcgU2V0KCk7XG5cbiAgLyoqXG4gICAqIEJpbmQgYW4gYXJjaGl0ZWN0dXJhbCBjb21wb25lbnQgdG8gYSBzZXJ2aWNlIGVuZHBvaW50LlxuICAgKiBPcHRpb25hbGx5IG92ZXJyaWRlIGZ1bmN0aW9uIGltcGxlbWVudGF0aW9ucyB3aXRoIHRoZSBvdmVybG9hZHMgb3B0aW9uLlxuICAgKiBPdmVybG9hZCBrZXlzIG11c3QgYmUgcm91dGUgbmFtZXMgcmVnaXN0ZXJlZCB2aWEgYWRkUm91dGUuXG4gICAqL1xuICBiaW5kPFRSb3V0ZXMgZXh0ZW5kcyBBcGlSb3V0ZXM+KGNvbXBvbmVudDogQXBpQ29udGFpbmVyPFRSb3V0ZXM+LCBvcHRpb25zOiBCaW5kT3B0aW9ucyk6IHZvaWQge1xuICAgIHRoaXMuYmluZGluZ3Muc2V0KGNvbXBvbmVudCwgeyBiYXNlVXJsOiBvcHRpb25zLmJhc2VVcmwgfSk7XG5cbiAgICBPYmplY3QuZW50cmllcyhvcHRpb25zLm92ZXJsb2FkcyA/PyB7fSkuZm9yRWFjaCgoW25hbWUsIGhhbmRsZXJdKSA9PiB7XG4gICAgICBjb25zdCByb3V0ZSA9IGNvbXBvbmVudC5nZXRSb3V0ZShuYW1lKTtcbiAgICAgIGlmICghcm91dGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBSb3V0ZSAnJHtuYW1lfScgbm90IGZvdW5kIGluIGNvbXBvbmVudCAnJHtjb21wb25lbnQubm9kZS5pZH0nYCk7XG4gICAgICB9XG4gICAgICBpZiAoIShyb3V0ZS5oYW5kbGVyIGluc3RhbmNlb2YgRnVuY3Rpb24pKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgUm91dGUgJyR7bmFtZX0nIGhhbmRsZXIgaXMgbm90IGEgRnVuY3Rpb25gKTtcbiAgICAgIH1cbiAgICAgIHJvdXRlLmhhbmRsZXIub3ZlcmxvYWQoaGFuZGxlcik7XG4gICAgfSk7XG4gIH1cblxuICBnZXRFbmRwb2ludChjb21wb25lbnQ6IENvbnN0cnVjdCk6IFNlcnZpY2VFbmRwb2ludCB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuYmluZGluZ3MuZ2V0KGNvbXBvbmVudCk7XG4gIH1cblxuICBnZXRBbGxCaW5kaW5ncygpOiBNYXA8Q29uc3RydWN0LCBTZXJ2aWNlRW5kcG9pbnQ+IHtcbiAgICByZXR1cm4gdGhpcy5iaW5kaW5ncztcbiAgfVxuXG4gIHNldExvY2FsKGNvbXBvbmVudDogQ29uc3RydWN0KTogdm9pZCB7XG4gICAgdGhpcy5sb2NhbENvbXBvbmVudHMuYWRkKGNvbXBvbmVudCk7XG4gIH1cblxuICBpc0xvY2FsKGNvbXBvbmVudDogQ29uc3RydWN0KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYWxDb21wb25lbnRzLmhhcyhjb21wb25lbnQpO1xuICB9XG59XG5cbi8qKlxuICogR2xvYmFsIGJpbmRpbmcgcmVnaXN0cnlcbiAqL1xuZXhwb3J0IGNvbnN0IGFyY2hpdGVjdHVyZUJpbmRpbmcgPSBuZXcgQXJjaGl0ZWN0dXJlQmluZGluZygpO1xuIl19
|
package/dist/http-handler.d.ts
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
import { ApiContainer } from './api-container';
|
|
2
|
-
import { FunctionHandler } from './function';
|
|
1
|
+
import { ApiContainer, ApiRoutes, RouteHandlers } from './api-container';
|
|
3
2
|
import { ServiceEndpoint } from './binding';
|
|
3
|
+
export type Fetcher = () => {
|
|
4
|
+
fetch: typeof fetch;
|
|
5
|
+
};
|
|
4
6
|
/**
|
|
5
7
|
* Create an HTTP handler for a route by name.
|
|
6
8
|
* Looks up the route path from the container's registry.
|
|
7
9
|
*/
|
|
8
|
-
export declare const httpHandler: (endpoint: ServiceEndpoint, container: ApiContainer
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
export declare const httpHandler: <TRoutes extends ApiRoutes, K extends keyof TRoutes & string>(endpoint: ServiceEndpoint, container: ApiContainer<TRoutes>, routeName: K, fetcher?: Fetcher) => RouteHandlers<TRoutes>[K];
|
|
11
|
+
/**
|
|
12
|
+
* Create HTTP bindings for multiple routes as a callable client.
|
|
13
|
+
* Returns a strongly-typed object where each route name maps to an async function
|
|
14
|
+
* with the same signature as the original handler.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam TRoutes - The routes type from the ApiContainer
|
|
17
|
+
* @typeParam K - The subset of route names to include in the client
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const api = new ApiContainer(arch, 'api', {
|
|
22
|
+
* hello: { path: 'GET /v1/api/hello/{name}', handler: helloFunction },
|
|
23
|
+
* hellos: { path: 'GET /v1/api/hellos', handler: hellosFunction }
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* const client = createHttpBindings(endpoint, api, ['hello', 'hellos']);
|
|
27
|
+
* await client.hello('John'); // (name: string) => Promise<string>
|
|
28
|
+
* await client.hellos(); // () => Promise<Greeting[]>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const createHttpBindings: <TRoutes extends ApiRoutes, K extends keyof TRoutes & string>(endpoint: ServiceEndpoint, container: ApiContainer<TRoutes>, routeNames: readonly K[], fetcher?: Fetcher) => Pick<RouteHandlers<TRoutes>, K>;
|
|
11
32
|
//# sourceMappingURL=http-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-handler.d.ts","sourceRoot":"","sources":["../src/http-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"http-handler.d.ts","sourceRoot":"","sources":["../src/http-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,MAAM,OAAO,GAAG,MAAM;IAAE,KAAK,EAAE,OAAO,KAAK,CAAA;CAAE,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,WAAW,GACtB,OAAO,SAAS,SAAS,EACzB,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,EAEhC,UAAU,eAAe,EACzB,WAAW,YAAY,CAAC,OAAO,CAAC,EAChC,WAAW,CAAC,EACZ,UAAS,OAAyB,KACjC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CA+B1B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,SAAS,SAAS,EACzB,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,EAEhC,UAAU,eAAe,EACzB,WAAW,YAAY,CAAC,OAAO,CAAC,EAChC,YAAY,SAAS,CAAC,EAAE,EACxB,UAAS,OAAyB,KACjC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAQhC,CAAC"}
|
package/dist/http-handler.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.httpHandler = void 0;
|
|
3
|
+
exports.createHttpBindings = exports.httpHandler = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Create an HTTP handler for a route by name.
|
|
6
6
|
* Looks up the route path from the container's registry.
|
|
7
7
|
*/
|
|
8
8
|
const httpHandler = (endpoint, container, routeName, fetcher = () => ({ fetch })) => {
|
|
9
9
|
const route = container.getRoute(routeName);
|
|
10
|
-
if (!route) {
|
|
11
|
-
throw new Error(`Route '${routeName}' not found in container '${container.node.id}'`);
|
|
12
|
-
}
|
|
13
10
|
const [method, path] = route.path.split(' ');
|
|
14
11
|
const pathParams = path.match(/\{(\w+)\}/g) || [];
|
|
15
|
-
|
|
12
|
+
const handler = async (...args) => {
|
|
16
13
|
const url = pathParams.reduce((u, param, i) => args[i] !== undefined ? u.replace(param, encodeURIComponent(String(args[i]))) : u, `${endpoint.baseUrl}${path}`);
|
|
17
14
|
const options = {
|
|
18
15
|
method: method || 'GET',
|
|
@@ -28,6 +25,34 @@ const httpHandler = (endpoint, container, routeName, fetcher = () => ({ fetch })
|
|
|
28
25
|
}
|
|
29
26
|
return response.json();
|
|
30
27
|
};
|
|
28
|
+
return handler;
|
|
31
29
|
};
|
|
32
30
|
exports.httpHandler = httpHandler;
|
|
33
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Create HTTP bindings for multiple routes as a callable client.
|
|
33
|
+
* Returns a strongly-typed object where each route name maps to an async function
|
|
34
|
+
* with the same signature as the original handler.
|
|
35
|
+
*
|
|
36
|
+
* @typeParam TRoutes - The routes type from the ApiContainer
|
|
37
|
+
* @typeParam K - The subset of route names to include in the client
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const api = new ApiContainer(arch, 'api', {
|
|
42
|
+
* hello: { path: 'GET /v1/api/hello/{name}', handler: helloFunction },
|
|
43
|
+
* hellos: { path: 'GET /v1/api/hellos', handler: hellosFunction }
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* const client = createHttpBindings(endpoint, api, ['hello', 'hellos']);
|
|
47
|
+
* await client.hello('John'); // (name: string) => Promise<string>
|
|
48
|
+
* await client.hellos(); // () => Promise<Greeting[]>
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
const createHttpBindings = (endpoint, container, routeNames, fetcher = () => ({ fetch })) => {
|
|
52
|
+
return routeNames.reduce((acc, name) => {
|
|
53
|
+
acc[name] = (0, exports.httpHandler)(endpoint, container, name, fetcher);
|
|
54
|
+
return acc;
|
|
55
|
+
}, {});
|
|
56
|
+
};
|
|
57
|
+
exports.createHttpBindings = createHttpBindings;
|
|
58
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2h0dHAtaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFNQTs7O0dBR0c7QUFDSSxNQUFNLFdBQVcsR0FBRyxDQUl6QixRQUF5QixFQUN6QixTQUFnQyxFQUNoQyxTQUFZLEVBQ1osVUFBbUIsR0FBRyxFQUFFLENBQUMsQ0FBQyxFQUFDLEtBQUssRUFBQyxDQUFDLEVBQ1AsRUFBRTtJQUM3QixNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBRTVDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0MsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUM7SUFFbEQsTUFBTSxPQUFPLEdBQUcsS0FBSyxFQUFFLEdBQUcsSUFBVyxFQUFFLEVBQUU7UUFDdkMsTUFBTSxHQUFHLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FDM0IsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsa0JBQWtCLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUNsRyxHQUFHLFFBQVEsQ0FBQyxPQUFPLEdBQUcsSUFBSSxFQUFFLENBQzdCLENBQUM7UUFFRixNQUFNLE9BQU8sR0FBZ0I7WUFDM0IsTUFBTSxFQUFFLE1BQU0sSUFBSSxLQUFLO1lBQ3ZCLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxrQkFBa0IsRUFBRTtZQUMvQyxHQUFHLENBQUMsQ0FBQyxNQUFNLEtBQUssTUFBTSxJQUFJLE1BQU0sS0FBSyxLQUFLLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLFVBQVUsQ0FBQyxNQUFNO2dCQUM1RSxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUU7Z0JBQ25ELENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDUixDQUFDO1FBRUYsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsRUFBRSxFQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUMsQ0FBQyxDQUFDO1FBQ3hELE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUVyRCxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ2pCLE1BQU0sSUFBSSxLQUFLLENBQUMsa0JBQWtCLEdBQUcsU0FBUyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxZQUFZLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQy9HLENBQUM7UUFFRCxPQUFPLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN6QixDQUFDLENBQUM7SUFFRixPQUFPLE9BQW9DLENBQUM7QUFDOUMsQ0FBQyxDQUFDO0FBdkNXLFFBQUEsV0FBVyxlQXVDdEI7QUFFRjs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNJLE1BQU0sa0JBQWtCLEdBQUcsQ0FJaEMsUUFBeUIsRUFDekIsU0FBZ0MsRUFDaEMsVUFBd0IsRUFDeEIsVUFBbUIsR0FBRyxFQUFFLENBQUMsQ0FBQyxFQUFDLEtBQUssRUFBQyxDQUFDLEVBQ0QsRUFBRTtJQUNuQyxPQUFPLFVBQVUsQ0FBQyxNQUFNLENBQ3RCLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO1FBQ1osR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUEsbUJBQVcsRUFBQyxRQUFRLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM1RCxPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsRUFDRCxFQUFxQyxDQUN0QyxDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBaEJXLFFBQUEsa0JBQWtCLHNCQWdCN0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBcGlDb250YWluZXIsIEFwaVJvdXRlcywgUm91dGVIYW5kbGVycyB9IGZyb20gJy4vYXBpLWNvbnRhaW5lcic7XG5pbXBvcnQgeyBGdW5jdGlvbkhhbmRsZXIgfSBmcm9tICcuL2Z1bmN0aW9uJztcbmltcG9ydCB7IFNlcnZpY2VFbmRwb2ludCB9IGZyb20gJy4vYmluZGluZyc7XG5cbmV4cG9ydCB0eXBlIEZldGNoZXIgPSAoKSA9PiB7IGZldGNoOiB0eXBlb2YgZmV0Y2ggfTtcblxuLyoqXG4gKiBDcmVhdGUgYW4gSFRUUCBoYW5kbGVyIGZvciBhIHJvdXRlIGJ5IG5hbWUuXG4gKiBMb29rcyB1cCB0aGUgcm91dGUgcGF0aCBmcm9tIHRoZSBjb250YWluZXIncyByZWdpc3RyeS5cbiAqL1xuZXhwb3J0IGNvbnN0IGh0dHBIYW5kbGVyID0gPFxuICBUUm91dGVzIGV4dGVuZHMgQXBpUm91dGVzLFxuICBLIGV4dGVuZHMga2V5b2YgVFJvdXRlcyAmIHN0cmluZ1xuPihcbiAgZW5kcG9pbnQ6IFNlcnZpY2VFbmRwb2ludCxcbiAgY29udGFpbmVyOiBBcGlDb250YWluZXI8VFJvdXRlcz4sXG4gIHJvdXRlTmFtZTogSyxcbiAgZmV0Y2hlcjogRmV0Y2hlciA9ICgpID0+ICh7ZmV0Y2h9KVxuKTogUm91dGVIYW5kbGVyczxUUm91dGVzPltLXSA9PiB7XG4gIGNvbnN0IHJvdXRlID0gY29udGFpbmVyLmdldFJvdXRlKHJvdXRlTmFtZSk7XG5cbiAgY29uc3QgW21ldGhvZCwgcGF0aF0gPSByb3V0ZS5wYXRoLnNwbGl0KCcgJyk7XG4gIGNvbnN0IHBhdGhQYXJhbXMgPSBwYXRoLm1hdGNoKC9cXHsoXFx3KylcXH0vZykgfHwgW107XG5cbiAgY29uc3QgaGFuZGxlciA9IGFzeW5jICguLi5hcmdzOiBhbnlbXSkgPT4ge1xuICAgIGNvbnN0IHVybCA9IHBhdGhQYXJhbXMucmVkdWNlKFxuICAgICAgKHUsIHBhcmFtLCBpKSA9PiBhcmdzW2ldICE9PSB1bmRlZmluZWQgPyB1LnJlcGxhY2UocGFyYW0sIGVuY29kZVVSSUNvbXBvbmVudChTdHJpbmcoYXJnc1tpXSkpKSA6IHUsXG4gICAgICBgJHtlbmRwb2ludC5iYXNlVXJsfSR7cGF0aH1gXG4gICAgKTtcblxuICAgIGNvbnN0IG9wdGlvbnM6IFJlcXVlc3RJbml0ID0ge1xuICAgICAgbWV0aG9kOiBtZXRob2QgfHwgJ0dFVCcsXG4gICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgICAgIC4uLigobWV0aG9kID09PSAnUE9TVCcgfHwgbWV0aG9kID09PSAnUFVUJykgJiYgYXJncy5sZW5ndGggPiBwYXRoUGFyYW1zLmxlbmd0aFxuICAgICAgICA/IHsgYm9keTogSlNPTi5zdHJpbmdpZnkoYXJnc1twYXRoUGFyYW1zLmxlbmd0aF0pIH1cbiAgICAgICAgOiB7fSlcbiAgICB9O1xuXG4gICAgY29uc29sZS5sb2coJ0hUVFAgaGFuZGxlci4gV2lsbCBmZXRjaCcsIHt1cmwsIG9wdGlvbnN9KTtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoZXIoKS5mZXRjaCh1cmwsIG9wdGlvbnMpO1xuXG4gICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBSZW1vdGUgY2FsbCB0byAke3VybH0gd2l0aCAke0pTT04uc3RyaW5naWZ5KG9wdGlvbnMpfSBmYWlsZWQ6ICR7SlNPTi5zdHJpbmdpZnkocmVzcG9uc2UpfWApO1xuICAgIH1cblxuICAgIHJldHVybiByZXNwb25zZS5qc29uKCk7XG4gIH07XG5cbiAgcmV0dXJuIGhhbmRsZXIgYXMgUm91dGVIYW5kbGVyczxUUm91dGVzPltLXTtcbn07XG5cbi8qKlxuICogQ3JlYXRlIEhUVFAgYmluZGluZ3MgZm9yIG11bHRpcGxlIHJvdXRlcyBhcyBhIGNhbGxhYmxlIGNsaWVudC5cbiAqIFJldHVybnMgYSBzdHJvbmdseS10eXBlZCBvYmplY3Qgd2hlcmUgZWFjaCByb3V0ZSBuYW1lIG1hcHMgdG8gYW4gYXN5bmMgZnVuY3Rpb25cbiAqIHdpdGggdGhlIHNhbWUgc2lnbmF0dXJlIGFzIHRoZSBvcmlnaW5hbCBoYW5kbGVyLlxuICpcbiAqIEB0eXBlUGFyYW0gVFJvdXRlcyAtIFRoZSByb3V0ZXMgdHlwZSBmcm9tIHRoZSBBcGlDb250YWluZXJcbiAqIEB0eXBlUGFyYW0gSyAtIFRoZSBzdWJzZXQgb2Ygcm91dGUgbmFtZXMgdG8gaW5jbHVkZSBpbiB0aGUgY2xpZW50XG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IGFwaSA9IG5ldyBBcGlDb250YWluZXIoYXJjaCwgJ2FwaScsIHtcbiAqICAgaGVsbG86IHsgcGF0aDogJ0dFVCAvdjEvYXBpL2hlbGxvL3tuYW1lfScsIGhhbmRsZXI6IGhlbGxvRnVuY3Rpb24gfSxcbiAqICAgaGVsbG9zOiB7IHBhdGg6ICdHRVQgL3YxL2FwaS9oZWxsb3MnLCBoYW5kbGVyOiBoZWxsb3NGdW5jdGlvbiB9XG4gKiB9KTtcbiAqXG4gKiBjb25zdCBjbGllbnQgPSBjcmVhdGVIdHRwQmluZGluZ3MoZW5kcG9pbnQsIGFwaSwgWydoZWxsbycsICdoZWxsb3MnXSk7XG4gKiBhd2FpdCBjbGllbnQuaGVsbG8oJ0pvaG4nKTsgIC8vIChuYW1lOiBzdHJpbmcpID0+IFByb21pc2U8c3RyaW5nPlxuICogYXdhaXQgY2xpZW50LmhlbGxvcygpOyAgICAgICAvLyAoKSA9PiBQcm9taXNlPEdyZWV0aW5nW10+XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNvbnN0IGNyZWF0ZUh0dHBCaW5kaW5ncyA9IDxcbiAgVFJvdXRlcyBleHRlbmRzIEFwaVJvdXRlcyxcbiAgSyBleHRlbmRzIGtleW9mIFRSb3V0ZXMgJiBzdHJpbmdcbj4oXG4gIGVuZHBvaW50OiBTZXJ2aWNlRW5kcG9pbnQsXG4gIGNvbnRhaW5lcjogQXBpQ29udGFpbmVyPFRSb3V0ZXM+LFxuICByb3V0ZU5hbWVzOiByZWFkb25seSBLW10sXG4gIGZldGNoZXI6IEZldGNoZXIgPSAoKSA9PiAoe2ZldGNofSlcbik6IFBpY2s8Um91dGVIYW5kbGVyczxUUm91dGVzPiwgSz4gPT4ge1xuICByZXR1cm4gcm91dGVOYW1lcy5yZWR1Y2UoXG4gICAgKGFjYywgbmFtZSkgPT4ge1xuICAgICAgYWNjW25hbWVdID0gaHR0cEhhbmRsZXIoZW5kcG9pbnQsIGNvbnRhaW5lciwgbmFtZSwgZmV0Y2hlcik7XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sXG4gICAge30gYXMgUGljazxSb3V0ZUhhbmRsZXJzPFRSb3V0ZXM+LCBLPlxuICApO1xufTtcbiJdfQ==
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { Construct } from 'constructs';
|
|
2
2
|
export { Architecture, ArchitectureDefinition, ComponentDefinition } from './architecture';
|
|
3
3
|
export { Function, TBDFunction, FunctionHandler } from './function';
|
|
4
|
-
export { ApiContainer, ApiRoutes, RouteEntry } from './api-container';
|
|
4
|
+
export { ApiContainer, ApiRoutes, RouteEntry, HandlerOf, RouteHandlers } from './api-container';
|
|
5
5
|
export { ArchitectureBinding, ServiceEndpoint, architectureBinding } from './binding';
|
|
6
|
-
export { httpHandler } from './http-handler';
|
|
6
|
+
export { httpHandler, createHttpBindings, Fetcher } from './http-handler';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.httpHandler = exports.architectureBinding = exports.ArchitectureBinding = exports.ApiContainer = exports.TBDFunction = exports.Function = exports.Architecture = exports.Construct = void 0;
|
|
3
|
+
exports.createHttpBindings = exports.httpHandler = exports.architectureBinding = exports.ArchitectureBinding = exports.ApiContainer = exports.TBDFunction = exports.Function = exports.Architecture = exports.Construct = void 0;
|
|
4
4
|
var constructs_1 = require("constructs");
|
|
5
5
|
Object.defineProperty(exports, "Construct", { enumerable: true, get: function () { return constructs_1.Construct; } });
|
|
6
6
|
var architecture_1 = require("./architecture");
|
|
@@ -15,4 +15,5 @@ Object.defineProperty(exports, "ArchitectureBinding", { enumerable: true, get: f
|
|
|
15
15
|
Object.defineProperty(exports, "architectureBinding", { enumerable: true, get: function () { return binding_1.architectureBinding; } });
|
|
16
16
|
var http_handler_1 = require("./http-handler");
|
|
17
17
|
Object.defineProperty(exports, "httpHandler", { enumerable: true, get: function () { return http_handler_1.httpHandler; } });
|
|
18
|
-
|
|
18
|
+
Object.defineProperty(exports, "createHttpBindings", { enumerable: true, get: function () { return http_handler_1.createHttpBindings; } });
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEseUNBQXVDO0FBQTlCLHVHQUFBLFNBQVMsT0FBQTtBQUNsQiwrQ0FBMkY7QUFBbEYsNEdBQUEsWUFBWSxPQUFBO0FBQ3JCLHVDQUFvRTtBQUEzRCxvR0FBQSxRQUFRLE9BQUE7QUFBRSx1R0FBQSxXQUFXLE9BQUE7QUFDOUIsaURBQWdHO0FBQXZGLDZHQUFBLFlBQVksT0FBQTtBQUNyQixxQ0FBc0Y7QUFBN0UsOEdBQUEsbUJBQW1CLE9BQUE7QUFBbUIsOEdBQUEsbUJBQW1CLE9BQUE7QUFDbEUsK0NBQTBFO0FBQWpFLDJHQUFBLFdBQVcsT0FBQTtBQUFFLGtIQUFBLGtCQUFrQixPQUFBIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5leHBvcnQgeyBBcmNoaXRlY3R1cmUsIEFyY2hpdGVjdHVyZURlZmluaXRpb24sIENvbXBvbmVudERlZmluaXRpb24gfSBmcm9tICcuL2FyY2hpdGVjdHVyZSc7XG5leHBvcnQgeyBGdW5jdGlvbiwgVEJERnVuY3Rpb24sIEZ1bmN0aW9uSGFuZGxlciB9IGZyb20gJy4vZnVuY3Rpb24nO1xuZXhwb3J0IHsgQXBpQ29udGFpbmVyLCBBcGlSb3V0ZXMsIFJvdXRlRW50cnksIEhhbmRsZXJPZiwgUm91dGVIYW5kbGVycyB9IGZyb20gJy4vYXBpLWNvbnRhaW5lcic7XG5leHBvcnQgeyBBcmNoaXRlY3R1cmVCaW5kaW5nLCBTZXJ2aWNlRW5kcG9pbnQsIGFyY2hpdGVjdHVyZUJpbmRpbmcgfSBmcm9tICcuL2JpbmRpbmcnO1xuZXhwb3J0IHsgaHR0cEhhbmRsZXIsIGNyZWF0ZUh0dHBCaW5kaW5ncywgRmV0Y2hlciB9IGZyb20gJy4vaHR0cC1oYW5kbGVyJztcbiJdfQ==
|
package/package.json
CHANGED
package/src/api-container.ts
CHANGED
|
@@ -1,31 +1,66 @@
|
|
|
1
1
|
import { Construct } from 'constructs';
|
|
2
2
|
import { Function, TBDFunction } from './function';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* A route entry that captures the handler's argument and return types.
|
|
6
|
+
*/
|
|
7
|
+
export interface RouteEntry<TArgs extends any[] = any[], TReturn = any> {
|
|
8
|
+
path: string;
|
|
9
|
+
handler: Function<TArgs, TReturn>;
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Base type for route definitions - maps route names to route entries.
|
|
14
|
+
*/
|
|
15
|
+
export type ApiRoutes = {
|
|
16
|
+
[name: string]: RouteEntry;
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
|
-
*
|
|
20
|
+
* Extract the handler signature from a Function type.
|
|
21
|
+
*/
|
|
22
|
+
export type HandlerOf<T> = T extends Function<infer Args, infer Return>
|
|
23
|
+
? (...args: Args) => Promise<Return>
|
|
24
|
+
: never;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Extract handler signatures from all routes in an ApiRoutes type.
|
|
28
|
+
*/
|
|
29
|
+
export type RouteHandlers<TRoutes extends ApiRoutes> = {
|
|
30
|
+
[K in keyof TRoutes]: HandlerOf<TRoutes[K]['handler']>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Represents an API container that routes requests to functions.
|
|
35
|
+
*
|
|
36
|
+
* @typeParam TRoutes - The type of the routes object, preserving route names and handler signatures.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const api = new ApiContainer(arch, 'api', {
|
|
41
|
+
* hello: { path: 'GET /v1/api/hello/{name}', handler: helloFunction },
|
|
42
|
+
* hellos: { path: 'GET /v1/api/hellos', handler: hellosFunction }
|
|
43
|
+
* });
|
|
44
|
+
* // api has type ApiContainer<{ hello: RouteEntry<[string], string>, hellos: RouteEntry<[], Greeting[]> }>
|
|
45
|
+
* ```
|
|
15
46
|
*/
|
|
16
|
-
export class ApiContainer extends Construct {
|
|
17
|
-
public readonly routes:
|
|
47
|
+
export class ApiContainer<TRoutes extends ApiRoutes = ApiRoutes> extends Construct {
|
|
48
|
+
public readonly routes: TRoutes;
|
|
18
49
|
|
|
19
|
-
constructor(scope: Construct, id: string, routes:
|
|
50
|
+
constructor(scope: Construct, id: string, routes: TRoutes = {} as TRoutes) {
|
|
20
51
|
super(scope, id);
|
|
21
52
|
this.routes = routes;
|
|
22
53
|
}
|
|
23
54
|
|
|
24
|
-
addRoute
|
|
25
|
-
|
|
55
|
+
addRoute<TArgs extends any[], TReturn>(
|
|
56
|
+
name: string,
|
|
57
|
+
path: string,
|
|
58
|
+
handler: Function<TArgs, TReturn>
|
|
59
|
+
): void {
|
|
60
|
+
(this.routes as ApiRoutes)[name] = { path, handler };
|
|
26
61
|
}
|
|
27
62
|
|
|
28
|
-
getRoute(name:
|
|
63
|
+
getRoute<K extends keyof TRoutes & string>(name: K): TRoutes[K] {
|
|
29
64
|
const entry = this.routes[name];
|
|
30
65
|
if (!entry) {
|
|
31
66
|
throw new Error(`Route '${name}' not found in container '${this.node.id}'`);
|
|
@@ -33,8 +68,8 @@ export class ApiContainer extends Construct {
|
|
|
33
68
|
return entry;
|
|
34
69
|
}
|
|
35
70
|
|
|
36
|
-
listRoutes()
|
|
37
|
-
return Object.keys(this.routes);
|
|
71
|
+
listRoutes(): (keyof TRoutes & string)[] {
|
|
72
|
+
return Object.keys(this.routes) as (keyof TRoutes & string)[];
|
|
38
73
|
}
|
|
39
74
|
|
|
40
75
|
/**
|
package/src/binding.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Construct } from 'constructs';
|
|
2
|
-
import { ApiContainer } from './api-container';
|
|
2
|
+
import { ApiContainer, ApiRoutes } from './api-container';
|
|
3
3
|
import { Function, FunctionHandler } from './function';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -33,7 +33,7 @@ export class ArchitectureBinding {
|
|
|
33
33
|
* Optionally override function implementations with the overloads option.
|
|
34
34
|
* Overload keys must be route names registered via addRoute.
|
|
35
35
|
*/
|
|
36
|
-
bind(component: ApiContainer
|
|
36
|
+
bind<TRoutes extends ApiRoutes>(component: ApiContainer<TRoutes>, options: BindOptions): void {
|
|
37
37
|
this.bindings.set(component, { baseUrl: options.baseUrl });
|
|
38
38
|
|
|
39
39
|
Object.entries(options.overloads ?? {}).forEach(([name, handler]) => {
|
package/src/http-handler.ts
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
import { ApiContainer } from './api-container';
|
|
1
|
+
import { ApiContainer, ApiRoutes, RouteHandlers } from './api-container';
|
|
2
2
|
import { FunctionHandler } from './function';
|
|
3
3
|
import { ServiceEndpoint } from './binding';
|
|
4
4
|
|
|
5
|
+
export type Fetcher = () => { fetch: typeof fetch };
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Create an HTTP handler for a route by name.
|
|
7
9
|
* Looks up the route path from the container's registry.
|
|
8
10
|
*/
|
|
9
|
-
export const httpHandler =
|
|
11
|
+
export const httpHandler = <
|
|
12
|
+
TRoutes extends ApiRoutes,
|
|
13
|
+
K extends keyof TRoutes & string
|
|
14
|
+
>(
|
|
10
15
|
endpoint: ServiceEndpoint,
|
|
11
|
-
container: ApiContainer
|
|
12
|
-
routeName:
|
|
13
|
-
fetcher:
|
|
14
|
-
):
|
|
16
|
+
container: ApiContainer<TRoutes>,
|
|
17
|
+
routeName: K,
|
|
18
|
+
fetcher: Fetcher = () => ({fetch})
|
|
19
|
+
): RouteHandlers<TRoutes>[K] => {
|
|
15
20
|
const route = container.getRoute(routeName);
|
|
16
|
-
if (!route) {
|
|
17
|
-
throw new Error(`Route '${routeName}' not found in container '${container.node.id}'`);
|
|
18
|
-
}
|
|
19
21
|
|
|
20
22
|
const [method, path] = route.path.split(' ');
|
|
21
23
|
const pathParams = path.match(/\{(\w+)\}/g) || [];
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
const handler = async (...args: any[]) => {
|
|
24
26
|
const url = pathParams.reduce(
|
|
25
27
|
(u, param, i) => args[i] !== undefined ? u.replace(param, encodeURIComponent(String(args[i]))) : u,
|
|
26
28
|
`${endpoint.baseUrl}${path}`
|
|
@@ -43,4 +45,44 @@ export const httpHandler = (
|
|
|
43
45
|
|
|
44
46
|
return response.json();
|
|
45
47
|
};
|
|
46
|
-
|
|
48
|
+
|
|
49
|
+
return handler as RouteHandlers<TRoutes>[K];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Create HTTP bindings for multiple routes as a callable client.
|
|
54
|
+
* Returns a strongly-typed object where each route name maps to an async function
|
|
55
|
+
* with the same signature as the original handler.
|
|
56
|
+
*
|
|
57
|
+
* @typeParam TRoutes - The routes type from the ApiContainer
|
|
58
|
+
* @typeParam K - The subset of route names to include in the client
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const api = new ApiContainer(arch, 'api', {
|
|
63
|
+
* hello: { path: 'GET /v1/api/hello/{name}', handler: helloFunction },
|
|
64
|
+
* hellos: { path: 'GET /v1/api/hellos', handler: hellosFunction }
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* const client = createHttpBindings(endpoint, api, ['hello', 'hellos']);
|
|
68
|
+
* await client.hello('John'); // (name: string) => Promise<string>
|
|
69
|
+
* await client.hellos(); // () => Promise<Greeting[]>
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export const createHttpBindings = <
|
|
73
|
+
TRoutes extends ApiRoutes,
|
|
74
|
+
K extends keyof TRoutes & string
|
|
75
|
+
>(
|
|
76
|
+
endpoint: ServiceEndpoint,
|
|
77
|
+
container: ApiContainer<TRoutes>,
|
|
78
|
+
routeNames: readonly K[],
|
|
79
|
+
fetcher: Fetcher = () => ({fetch})
|
|
80
|
+
): Pick<RouteHandlers<TRoutes>, K> => {
|
|
81
|
+
return routeNames.reduce(
|
|
82
|
+
(acc, name) => {
|
|
83
|
+
acc[name] = httpHandler(endpoint, container, name, fetcher);
|
|
84
|
+
return acc;
|
|
85
|
+
},
|
|
86
|
+
{} as Pick<RouteHandlers<TRoutes>, K>
|
|
87
|
+
);
|
|
88
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { Construct } from 'constructs';
|
|
2
2
|
export { Architecture, ArchitectureDefinition, ComponentDefinition } from './architecture';
|
|
3
3
|
export { Function, TBDFunction, FunctionHandler } from './function';
|
|
4
|
-
export { ApiContainer, ApiRoutes, RouteEntry } from './api-container';
|
|
4
|
+
export { ApiContainer, ApiRoutes, RouteEntry, HandlerOf, RouteHandlers } from './api-container';
|
|
5
5
|
export { ArchitectureBinding, ServiceEndpoint, architectureBinding } from './binding';
|
|
6
|
-
export { httpHandler } from './http-handler';
|
|
6
|
+
export { httpHandler, createHttpBindings, Fetcher } from './http-handler';
|