@openstax/ts-utils 1.1.34 → 1.1.36

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.
Files changed (27) hide show
  1. package/dist/cjs/services/authProvider/decryption.d.ts +1 -4
  2. package/dist/cjs/services/authProvider/index.d.ts +5 -1
  3. package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +3 -3
  4. package/dist/cjs/services/versionedDocumentStore/dynamodb.d.ts +4 -0
  5. package/dist/cjs/services/versionedDocumentStore/dynamodb.js +27 -0
  6. package/dist/cjs/services/versionedDocumentStore/file-system.d.ts +4 -0
  7. package/dist/cjs/services/versionedDocumentStore/file-system.js +22 -0
  8. package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -0
  9. package/dist/esm/services/authProvider/decryption.d.ts +1 -4
  10. package/dist/esm/services/authProvider/index.d.ts +5 -1
  11. package/dist/esm/services/authProvider/utils/decryptAndVerify.js +3 -3
  12. package/dist/esm/services/versionedDocumentStore/dynamodb.d.ts +4 -0
  13. package/dist/esm/services/versionedDocumentStore/dynamodb.js +27 -0
  14. package/dist/esm/services/versionedDocumentStore/file-system.d.ts +4 -0
  15. package/dist/esm/services/versionedDocumentStore/file-system.js +22 -0
  16. package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -0
  17. package/package.json +1 -2
  18. package/dist/cjs/config.d.ts +0 -27
  19. package/dist/cjs/config.js +0 -140
  20. package/dist/cjs/routing.d.ts +0 -107
  21. package/dist/cjs/routing.js +0 -247
  22. package/dist/cjs/tsconfig.withoutspecs.cjs.tsbuildinfo +0 -1
  23. package/dist/esm/config.d.ts +0 -27
  24. package/dist/esm/config.js +0 -127
  25. package/dist/esm/routing.d.ts +0 -107
  26. package/dist/esm/routing.js +0 -208
  27. package/dist/esm/tsconfig.withoutspecs.esm.tsbuildinfo +0 -1
@@ -1,140 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.lambdaParameterConfig = exports.awsParameterConfig = exports.awsAccountConfig = exports.replaceConfig = exports.stubConfig = exports.envConfig = exports.ENV_BUILD_CONFIGS = exports.resolveConfigValue = void 0;
7
- const client_ssm_1 = require("@aws-sdk/client-ssm");
8
- const client_sts_1 = require("@aws-sdk/client-sts");
9
- const node_fetch_1 = __importDefault(require("node-fetch"));
10
- const assertions_1 = require("./assertions");
11
- const securityTokenService_1 = require("./aws/securityTokenService");
12
- const ssmService_1 = require("./aws/ssmService");
13
- const guards_1 = require("./guards");
14
- /*
15
- * resolves a config value into a string, to be used inside of things that are provided configurations
16
- * */
17
- const resolveConfigValue = async (provider) => {
18
- return typeof provider === 'function'
19
- ? await provider()
20
- : provider;
21
- };
22
- exports.resolveConfigValue = resolveConfigValue;
23
- /*
24
- * ===========
25
- * re-usable config providers
26
- * ===========
27
- * */
28
- /*
29
- * uses a config from the process environment.
30
- *
31
- * there are two modes for this, by default it expects the value to be provided at build time,
32
- * and there is access built in here for webpack to provide the configs. if you pass `runtime`
33
- * as the second argument it skips the build logic and will try to literally pull it from
34
- * `process.env` when the config is used at runtime
35
- *
36
- * the value is read from the environment when something tries to use it, not when `envConfig` is called.
37
- *
38
- * eg:
39
- * const config = {
40
- * configValue: envConfig('environment_variable_name'),
41
- * };
42
- * */
43
- exports.ENV_BUILD_CONFIGS = [];
44
- const envConfig = (name, type = 'build', defaultValue) => {
45
- if (type === 'build') {
46
- exports.ENV_BUILD_CONFIGS.push(name);
47
- }
48
- return () => {
49
- /*global __PROCESS_ENV*/
50
- // @ts-ignore - hack to get around the way webpack/define works
51
- // - https://github.com/webpack/webpack/issues/14800
52
- // - https://github.com/webpack/webpack/issues/5392
53
- const envs = { ...process.env, ...(typeof __PROCESS_ENV !== 'undefined' ? __PROCESS_ENV : {}) };
54
- return (0, assertions_1.assertDefined)((0, guards_1.ifDefined)(envs[name], defaultValue), `expected to find environment variable with name: ${name}`);
55
- };
56
- };
57
- exports.envConfig = envConfig;
58
- /*
59
- * stub, mostly for testing. sometimes it helps please typescript to use this if you have
60
- * two configs you want to have the same type but one is a fixed string and one is a complicated provider
61
- *
62
- * eg:
63
- * const config = {
64
- * configValue: stubConfig('just-a-string'),
65
- * };
66
- * */
67
- const stubConfig = (configValue) => configValue;
68
- exports.stubConfig = stubConfig;
69
- const replaceConfig = (base, replacements) => {
70
- return async () => {
71
- const resolved = await Promise.all(Object.entries(replacements)
72
- .map(async ([token, replacement]) => [token, await (0, exports.resolveConfigValue)(replacement)]));
73
- return resolved.reduce((result, [token, replacement]) => result.replace(token, replacement), await (0, exports.resolveConfigValue)(base));
74
- };
75
- };
76
- exports.replaceConfig = replaceConfig;
77
- /*
78
- * uses a different given value depending on which aws account the code is deployed in.
79
- * this resolves the given value recursively, so its composable with more configProviders in the
80
- * conditional cases.
81
- *
82
- * eg:
83
- * const config = {
84
- * configValue: awsAccountConfig({sandbox: 'inTheSandboxConfig', production: 'inProductionConfig'}),
85
- * configValue: awsAccountConfig({sandbox: envConfig('an_environment_variable'), production: 'inProductionConfig'}),
86
- * };
87
- * */
88
- const awsAccountConfig = (config) => {
89
- const production = (0, exports.envConfig)('PRODUCTION_AWS');
90
- const sandbox = (0, exports.envConfig)('SANDBOX_AWS');
91
- return async () => {
92
- const identity = await (0, securityTokenService_1.securityTokenService)().send(new client_sts_1.GetCallerIdentityCommand({}));
93
- switch (identity.Account) {
94
- case await (0, exports.resolveConfigValue)(sandbox):
95
- return await (0, exports.resolveConfigValue)((0, assertions_1.assertDefined)(config.sandbox, 'a sandbox config was not provided'));
96
- case await (0, exports.resolveConfigValue)(production):
97
- return await (0, exports.resolveConfigValue)((0, assertions_1.assertDefined)(config.production, 'a production config was not provided'));
98
- default:
99
- throw new Error('unknown aws account');
100
- }
101
- };
102
- };
103
- exports.awsAccountConfig = awsAccountConfig;
104
- const awsParameterConfig = (parameterName) => {
105
- return async () => {
106
- const command = new client_ssm_1.GetParameterCommand({ Name: await (0, exports.resolveConfigValue)(parameterName), WithDecryption: true });
107
- // send() throws ParameterNotFound if the parameter is missing,
108
- // so it's not clear what missing Parameter or Value mean
109
- const response = await (0, ssmService_1.ssmService)().send(command);
110
- const parameter = (0, assertions_1.assertDefined)(response.Parameter, `aws GetParameter response missing Parameter key for ${parameterName}"`);
111
- return (0, assertions_1.assertDefined)(parameter.Value, `aws GetParameter response missing Parameter.Value key for ${parameterName}"`);
112
- };
113
- };
114
- exports.awsParameterConfig = awsParameterConfig;
115
- const lambdaExtensionUrl = 'http://localhost:2773';
116
- let lambdaExtensionReadyPromise;
117
- // NOTE: Can only be used during in AWS Lambda
118
- // The AWS Parameters and Secrets Lambda Extension Layer must be included in the Lambda function
119
- const lambdaParameterConfig = (parameterName) => async () => {
120
- const token = await (0, exports.resolveConfigValue)((0, exports.envConfig)('AWS_SESSION_TOKEN', 'runtime'));
121
- const name = await (0, exports.resolveConfigValue)(parameterName);
122
- if (!lambdaExtensionReadyPromise) {
123
- // This request will return 400 Bad Request,
124
- // but we only care that it'll block until the extension is ready
125
- lambdaExtensionReadyPromise = (0, node_fetch_1.default)(lambdaExtensionUrl);
126
- }
127
- await lambdaExtensionReadyPromise;
128
- const resp = await (0, node_fetch_1.default)(
129
- // Port 2773 is the default port for the extension
130
- `${lambdaExtensionUrl}/systemsmanager/parameters/get?name=${name}&withDecryption=true`, { headers: { 'X-Aws-Parameters-Secrets-Token': token } });
131
- if (resp.ok) {
132
- const response = await resp.json();
133
- const parameter = (0, assertions_1.assertDefined)(response.Parameter, `aws GetParameter response missing Parameter key for ${name}"`);
134
- return (0, assertions_1.assertDefined)(parameter.Value, `aws GetParameter response missing Parameter.Value key for ${name}"`);
135
- }
136
- else {
137
- throw new Error(`HTTP Error Response ${resp.status} ${resp.statusText} while fetching parameter ${name}`);
138
- }
139
- };
140
- exports.lambdaParameterConfig = lambdaParameterConfig;
@@ -1,107 +0,0 @@
1
- import { Track } from './profile';
2
- export declare type QueryParams = Record<string, string | undefined | string[] | null>;
3
- export declare type RouteParams = {
4
- [key: string]: string;
5
- };
6
- export declare type AnyRoute<R> = R extends Route<infer N, infer P, infer Sa, infer Sr, infer Ri, infer Ro> ? Route<N, P, Sa, Sr, Ri, Ro> : never;
7
- export declare type AnySpecificRoute<R, Sa, Ri, Ro> = R extends Route<infer N, infer P, Sa, infer Sr, Ri, Ro> & infer E ? Route<N, P, Sa, Sr, Ri, Ro> & E : never;
8
- export declare type OutputForRoute<R> = R extends Route<any, any, any, any, any, infer Ro> ? Ro : never;
9
- export declare type ParamsForRoute<R> = R extends Route<any, infer P, any, any, any, any> ? P : never;
10
- export declare type RequestServicesForRoute<R> = R extends Route<any, any, any, infer Sr, any, any> ? Sr : never;
11
- export declare type ParamsForRouteOrEmpty<R> = ParamsForRoute<R> extends undefined ? {} : Exclude<ParamsForRoute<R>, undefined>;
12
- export declare type RouteMatchRecord<R> = R extends AnyRoute<R> ? {
13
- route: R;
14
- params: ParamsForRoute<R>;
15
- } : never;
16
- export declare type PayloadForRoute<R> = RequestServicesForRoute<R> extends {
17
- payload: any;
18
- } ? RequestServicesForRoute<R>['payload'] : undefined;
19
- declare type RequestServiceProvider<Sa, Sr, Ri> = (app: Sa) => <R>(middleware: {
20
- request: Ri;
21
- profile: Track;
22
- }, match: RouteMatchRecord<R>) => Sr;
23
- declare type RouteHandler<P, Sr, Ro> = (params: P, request: Sr) => Ro;
24
- declare type Route<N extends string, P extends RouteParams | undefined, Sa, Sr, Ri, Ro> = (Sr extends undefined ? {
25
- requestServiceProvider?: RequestServiceProvider<Sa, Sr, Ri> | undefined;
26
- } : {
27
- requestServiceProvider: RequestServiceProvider<Sa, Sr, Ri>;
28
- }) & {
29
- name: N;
30
- path: string;
31
- handler: (params: P, request: Sr) => Ro;
32
- };
33
- declare type CreateRouteConfig<Sa, Sr, Ri, N extends string, Ex> = (Sr extends undefined ? {
34
- requestServiceProvider?: RequestServiceProvider<Sa, Sr, Ri> | undefined;
35
- } : {
36
- requestServiceProvider: RequestServiceProvider<Sa, Sr, Ri>;
37
- }) & {
38
- name: N;
39
- path: string;
40
- } & Ex;
41
- export interface CreateRoute<Sa, Ri, Ex> {
42
- <N extends string, Ro, Sr extends unknown | undefined = undefined, P extends RouteParams | undefined = undefined>(config: CreateRouteConfig<Sa, Sr, Ri, N, Ex> & {
43
- handler: RouteHandler<P, Sr, Ro>;
44
- }): Route<N, P, Sa, Sr, Ri, Ro> & Ex;
45
- <N extends string, Ro, Sr extends unknown | undefined, P extends RouteParams | undefined = undefined>(config: CreateRouteConfig<Sa, Sr, Ri, N, Ex>, handler: RouteHandler<P, Sr, Ro>): Route<N, P, Sa, Sr, Ri, Ro> & Ex;
46
- }
47
- export declare const makeCreateRoute: <Sa, Ri, Ex = {}>() => CreateRoute<Sa, Ri, Ex>;
48
- export declare const makeRenderRouteUrl: <Ru extends {
49
- path: string;
50
- }>() => <R extends Ru>(route: R, params: ParamsForRoute<R>, query?: QueryParams) => string;
51
- export declare const renderAnyRouteUrl: <R extends any>(route: R, params: ParamsForRoute<R>, query?: QueryParams) => string;
52
- declare type RequestPathExtractor<Ri> = (request: Ri) => string;
53
- declare type RequestRouteMatcher<Ri, R> = (request: Ri, route: R) => boolean;
54
- declare type RequestResponder<Sa, Ri, Ro> = {
55
- (services: Sa): (request: Ri) => Ro | undefined;
56
- <RoF>(services: Sa, responseMiddleware: (app: Sa) => (response: Ro | undefined, request: {
57
- request: Ri;
58
- profile: Track;
59
- }) => RoF): (request: Ri) => RoF;
60
- };
61
- export declare const makeGetRequestResponder: <Sa, Ru, Ri, Ro>() => ({ routes, pathExtractor, routeMatcher, errorHandler }: {
62
- routes: () => AnySpecificRoute<Ru, Sa, Ri, Ro>[];
63
- pathExtractor: RequestPathExtractor<Ri>;
64
- routeMatcher?: RequestRouteMatcher<Ri, AnySpecificRoute<Ru, Sa, Ri, Ro>> | undefined;
65
- errorHandler?: ((e: Error) => Ro) | undefined;
66
- }) => RequestResponder<Sa, Ri, Ro>;
67
- export declare type HttpHeaders = {
68
- [key: string]: string | undefined | string[];
69
- };
70
- export declare type JsonCompatibleValue = string | number | null | undefined | boolean;
71
- export declare type JsonCompatibleArray = Array<JsonCompatibleValue | JsonCompatibleStruct | JsonCompatibleStruct>;
72
- export declare type JsonCompatibleStruct = {
73
- [key: string]: JsonCompatibleStruct | JsonCompatibleValue | JsonCompatibleArray;
74
- };
75
- export declare type ApiResponse<S extends number, T> = {
76
- isBase64Encoded?: boolean;
77
- statusCode: S;
78
- data: T;
79
- body: string;
80
- headers?: {
81
- [key: string]: string;
82
- };
83
- };
84
- export declare const apiJsonResponse: <S extends number, T extends JsonCompatibleStruct>(statusCode: S, data: T, headers?: HttpHeaders | undefined) => ApiResponse<S, T>;
85
- export declare const apiTextResponse: <S extends number>(statusCode: S, data: string, headers?: HttpHeaders | undefined) => ApiResponse<S, string>;
86
- export declare const apiHtmlResponse: <S extends number>(statusCode: S, data: string, headers?: HttpHeaders | undefined) => ApiResponse<S, string>;
87
- export declare enum METHOD {
88
- GET = "GET",
89
- HEAD = "HEAD",
90
- POST = "POST",
91
- PUT = "PUT",
92
- PATCH = "PATCH",
93
- DELETE = "DELETE",
94
- OPTIONS = "OPTIONS"
95
- }
96
- export declare const getHeader: (headers: HttpHeaders, name: string) => string | undefined;
97
- export declare const getRequestBody: (request: {
98
- headers: HttpHeaders;
99
- body?: string | undefined;
100
- }) => any;
101
- export declare const unsafePayloadValidator: <T>() => (input: any) => input is T;
102
- export declare const requestPayloadProvider: <T>(validator: (input: any) => input is T) => () => <M extends {
103
- request: Parameters<typeof getRequestBody>[0];
104
- }>(requestServices: M) => M & {
105
- payload: T;
106
- };
107
- export {};
@@ -1,247 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.requestPayloadProvider = exports.unsafePayloadValidator = exports.getRequestBody = exports.getHeader = exports.METHOD = exports.apiHtmlResponse = exports.apiTextResponse = exports.apiJsonResponse = exports.makeGetRequestResponder = exports.renderAnyRouteUrl = exports.makeRenderRouteUrl = exports.makeCreateRoute = void 0;
30
- const pathToRegexp = __importStar(require("path-to-regexp"));
31
- const query_string_1 = __importDefault(require("query-string"));
32
- const assertions_1 = require("./assertions");
33
- const errors_1 = require("./errors");
34
- const guards_1 = require("./guards");
35
- const profile_1 = require("./profile");
36
- const _1 = require(".");
37
- /*
38
- * route definition helper. the only required params of the route are the name, path, and handler. other params
39
- * can be added to the type and then later used in the routeMatcher. when defining the `createRoute` method, only
40
- * the request input format is defined, the result format is derived from the routes.
41
- *
42
- * eg:
43
- * export const createRoute = makeCreateRoute<AppServices, ApiRouteRequest, {
44
- * method: METHOD;
45
- * }>();
46
- *
47
- * eg when defining requestServiceProvider in line, the types have a hard time, it helps to put in another argument:
48
- * export const exampleRoute = createRoute({name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
49
- * requestServiceProvider: requestServiceProvider({
50
- * cookieAuthMiddleware,
51
- * documentStoreMiddleware,
52
- * }},
53
- * async(params: {key: string}, services) => {
54
- * const result = await services.myDocumentStore.getItem(params.key);
55
- *
56
- * if (!result) {
57
- * throw new NotFoundError('requested item not found');
58
- * }
59
- *
60
- * return apiJsonResponse(200, result);
61
- * }
62
- * );
63
- *
64
- * eg when using a pre-existing provider variable the types work better:
65
- * export const exampleRoute = createRoute({name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
66
- * requestServiceProvider,
67
- * handler: async(params: {key: string}, services) => {
68
- * const result = await services.myDocumentStore.getItem(params.key);
69
- *
70
- * if (!result) {
71
- * throw new NotFoundError('requested item not found');
72
- * }
73
- *
74
- * return apiJsonResponse(200, result);
75
- * }
76
- * });
77
- */
78
- const makeCreateRoute = () => (...args) => {
79
- return (args.length === 1
80
- ? args[0]
81
- : { ...args[0], handler: args[1] });
82
- };
83
- exports.makeCreateRoute = makeCreateRoute;
84
- /* begin reverse routing utils */
85
- const makeRenderRouteUrl = () => (route, params, query = {}) => {
86
- const getPathForParams = pathToRegexp.compile(route.path, { encode: encodeURIComponent });
87
- const search = query_string_1.default.stringify(query);
88
- const path = getPathForParams(params) + (search ? `?${search}` : '');
89
- return path;
90
- };
91
- exports.makeRenderRouteUrl = makeRenderRouteUrl;
92
- exports.renderAnyRouteUrl = (0, exports.makeRenderRouteUrl)();
93
- const bindRoute = (services, appBinder, pathExtractor, matcher) => (route) => {
94
- const getParamsFromPath = pathToRegexp.match(route.path, { decode: decodeURIComponent });
95
- const boundServiceProvider = route.requestServiceProvider && appBinder(services, route.requestServiceProvider);
96
- return (request, profile) => {
97
- const path = pathExtractor(request);
98
- const match = getParamsFromPath(path);
99
- if ((!matcher || matcher(request, route)) && match) {
100
- return profile.track(route.name, routeProfile => () => route.handler(match.params, boundServiceProvider ? boundServiceProvider({ request, profile: routeProfile }, { route, params: match.params }) : undefined));
101
- }
102
- };
103
- };
104
- /*
105
- * here among other things we're specifying a generic response format that the response and error handling middleware can use,
106
- * if any routes have responses that don't adhere to this it'll complain about it.
107
- *
108
- * eg:
109
- * export const getRequestResponder = makeGetRequestResponder<AppServices, TRoutes, ApiRouteRequest, Promise<ApiRouteResponse>>()({
110
- * routes: apiRoutes, // the route definitions
111
- * pathExtractor, // how to get the path out of the request format
112
- * routeMatcher, // logic for matching route (if there is any in addition to the path matching)
113
- * errorHandler, // any special error handling
114
- * });
115
- *
116
- * eg an lambda entrypoint:
117
- * export const handler: (request: APIGatewayProxyEventV2) => Promise<ApiRouteResponse> =
118
- * getRequestResponder(
119
- * lambdaServices, // the AppServices for this entrypoint
120
- * lambdaMiddleware // environment specific response middleware (like cors)
121
- * );
122
- */
123
- const makeGetRequestResponder = () => ({ routes, pathExtractor, routeMatcher, errorHandler }) => (services, responseMiddleware) => {
124
- const appBinderImpl = (app, middleware) => middleware(app, appBinder);
125
- const appBinder = (0, _1.memoize)(appBinderImpl);
126
- const boundRoutes = routes().map(bindRoute(services, appBinder, pathExtractor, routeMatcher));
127
- const boundResponseMiddleware = responseMiddleware ? responseMiddleware(services) : undefined;
128
- // *note* this opaque promise guard is less generic than i hoped so
129
- // i'm leaving it here instead of the guards file.
130
- //
131
- // its less than ideal because it enforces that the handlers return
132
- // the same type as the parent promise, usually a handler can be either a
133
- // promise or a non-promise value and the promise figures it out, but those
134
- // types are getting complicated quickly here.
135
- const isPromise = (thing) => thing instanceof Promise;
136
- return (request) => {
137
- const { end, ...profile } = (0, profile_1.createProfile)(new Date().toISOString()).start();
138
- try {
139
- const executor = (0, _1.mapFind)(boundRoutes, (route) => route(request, profile));
140
- if (executor) {
141
- const result = boundResponseMiddleware ?
142
- boundResponseMiddleware(executor(), { request, profile }) : executor();
143
- if (isPromise(result) && errorHandler) {
144
- const errorHandlerWithMiddleware = (e) => boundResponseMiddleware ?
145
- boundResponseMiddleware(errorHandler(e), { request, profile }) : errorHandler(e);
146
- return result.catch(errorHandlerWithMiddleware);
147
- }
148
- else {
149
- return result;
150
- }
151
- }
152
- else if (boundResponseMiddleware) {
153
- return boundResponseMiddleware(undefined, { request, profile });
154
- }
155
- }
156
- catch (e) {
157
- if (errorHandler && e instanceof Error) {
158
- return boundResponseMiddleware ? boundResponseMiddleware(errorHandler(e), { request, profile }) : errorHandler(e);
159
- }
160
- throw e;
161
- }
162
- return undefined;
163
- };
164
- };
165
- exports.makeGetRequestResponder = makeGetRequestResponder;
166
- const apiJsonResponse = (statusCode, data, headers) => ({ statusCode, data, body: JSON.stringify(data), headers: { ...headers, 'content-type': 'application/json' } });
167
- exports.apiJsonResponse = apiJsonResponse;
168
- const apiTextResponse = (statusCode, data, headers) => ({ statusCode, data, body: data, headers: { ...headers, 'content-type': 'text/plain' } });
169
- exports.apiTextResponse = apiTextResponse;
170
- const apiHtmlResponse = (statusCode, data, headers) => ({ statusCode, data, body: data, headers: { ...headers, 'content-type': 'text/html' } });
171
- exports.apiHtmlResponse = apiHtmlResponse;
172
- var METHOD;
173
- (function (METHOD) {
174
- METHOD["GET"] = "GET";
175
- METHOD["HEAD"] = "HEAD";
176
- METHOD["POST"] = "POST";
177
- METHOD["PUT"] = "PUT";
178
- METHOD["PATCH"] = "PATCH";
179
- METHOD["DELETE"] = "DELETE";
180
- METHOD["OPTIONS"] = "OPTIONS";
181
- })(METHOD = exports.METHOD || (exports.METHOD = {}));
182
- /* utils and middleware for loading request payload (must follow this pattern for `PayloadForRoute` to work) */
183
- // use this to support case insensitive header keys
184
- // note if there are multiple headers of the same value, this only returns the first value
185
- const getHeader = (headers, name) => {
186
- const key = Object.keys(headers).find(header => header.toLowerCase() === name.toLowerCase());
187
- const value = key ? headers[key] : undefined;
188
- return value instanceof Array
189
- ? value[0]
190
- : value;
191
- };
192
- exports.getHeader = getHeader;
193
- const getRequestBody = (request) => {
194
- if ((0, exports.getHeader)(request.headers, 'content-type') !== 'application/json') {
195
- throw new errors_1.InvalidRequestError('unknown content type: ' + (0, exports.getHeader)(request.headers, 'content-type'));
196
- }
197
- if (!request.body) {
198
- return {};
199
- }
200
- try {
201
- return JSON.parse(request.body);
202
- }
203
- catch (error) {
204
- // Since the body is provided by the user, invalid JSON in the body is an invalid request
205
- // We return the message which tells them why the JSON is invalid, but no backtrace
206
- throw new errors_1.InvalidRequestError((0, assertions_1.assertErrorInstanceOf)(error, SyntaxError).message);
207
- }
208
- };
209
- exports.getRequestBody = getRequestBody;
210
- // stub validator because writing validators is annoying
211
- const unsafePayloadValidator = () => (input) => {
212
- return (0, guards_1.isPlainObject)(input) && Object.keys(input).length > 0;
213
- };
214
- exports.unsafePayloadValidator = unsafePayloadValidator;
215
- /*
216
- * the given validator is a guard, which provides the correct type this helper loads the body, runs the validator, throws if it isn't valid, or returns it as
217
- * the correct type if it is valid.
218
- *
219
- * this accomplishes a few things:
220
- * - establishes type of payload for route body logic
221
- * - validates the payload for route logic
222
- * - establishes type of payload for client logic calling this route
223
- *
224
- * eg:
225
- * export const exampleRoute = createRoute({name: 'exampleRoute', method: METHOD.POST, path: '/example/:id',
226
- * requestServiceProvider: composeServiceMiddleware(
227
- * requestServiceProvider, // previously compiled middleware can be re-composed if you have something to add
228
- * requestPayloadProvider(validatePayload)
229
- * )},
230
- * async(params: {id: string}, services) => {
231
- * const result = await services.myDocumentStore.putItem({
232
- * ...services.payload,
233
- * id: params.id,
234
- * });
235
- * return apiJsonResponse(201, result);
236
- * }
237
- * );
238
- * */
239
- const requestPayloadProvider = (validator) => () => (requestServices) => {
240
- const payload = (0, exports.getRequestBody)(requestServices.request);
241
- // for more precise error messages, throw your own InvalidRequestError from your validator function
242
- if (!validator(payload)) {
243
- throw new errors_1.InvalidRequestError();
244
- }
245
- return { ...requestServices, payload };
246
- };
247
- exports.requestPayloadProvider = requestPayloadProvider;