@openstax/ts-utils 1.0.6 → 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/dist/config.d.ts CHANGED
@@ -24,3 +24,4 @@ export declare const awsAccountConfig: <C extends string>(config: {
24
24
  production: ConfigValueProvider<C>;
25
25
  }) => ConfigValueProvider<C>;
26
26
  export declare const awsParameterConfig: (parameterName: ConfigValueProvider<string>) => ConfigValueProvider<string>;
27
+ export declare const lambdaParameterConfig: (parameterName: ConfigValueProvider<string>) => ConfigValueProvider<string>;
package/dist/config.js CHANGED
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.awsParameterConfig = exports.awsAccountConfig = exports.replaceConfig = exports.stubConfig = exports.envConfig = exports.ENV_BUILD_CONFIGS = exports.resolveConfigValue = void 0;
6
+ exports.lambdaParameterConfig = exports.awsParameterConfig = exports.awsAccountConfig = exports.replaceConfig = exports.stubConfig = exports.envConfig = exports.ENV_BUILD_CONFIGS = exports.resolveConfigValue = void 0;
4
7
  const client_ssm_1 = require("@aws-sdk/client-ssm");
5
8
  const client_sts_1 = require("@aws-sdk/client-sts");
9
+ const node_fetch_1 = __importDefault(require("node-fetch"));
6
10
  const assertions_1 = require("./assertions");
7
11
  const securityTokenService_1 = require("./aws/securityTokenService");
8
12
  const ssmService_1 = require("./aws/ssmService");
@@ -108,3 +112,29 @@ const awsParameterConfig = (parameterName) => {
108
112
  };
109
113
  };
110
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;
package/dist/fetch.d.ts CHANGED
@@ -53,7 +53,7 @@ export declare type FetchConfig = {
53
53
  declare type Headers = {
54
54
  get: (name: string) => string | null;
55
55
  };
56
- declare type Response = {
56
+ export declare type Response = {
57
57
  status: number;
58
58
  headers: Headers;
59
59
  json: () => Promise<any>;
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ declare type HashCompoundValue = Array<HashValue> | {
10
10
  };
11
11
  export declare const hashValue: (value: HashValue) => string;
12
12
  export declare const once: <F extends (...args: any[]) => any>(fn: F) => F;
13
+ export declare const memoize: <F extends (...args: any[]) => any>(fn: F) => F;
13
14
  export declare const roundToPrecision: (num: number, places: number) => number;
14
15
  export declare const getCommonProperties: <T1 extends {}, T2 extends {}>(thing1: T1, thing2: T2) => (keyof T1 & keyof T2)[];
15
16
  export declare const merge: <T extends {}[]>(...[thing1, ...tail]: T) => UnionToIntersection<T[number]>;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tuple = exports.merge = exports.getCommonProperties = exports.roundToPrecision = exports.once = exports.hashValue = exports.mapFind = exports.fnIf = exports.putKeyValue = exports.getKeyValueOr = exports.getKeyValue = void 0;
3
+ exports.tuple = exports.merge = exports.getCommonProperties = exports.roundToPrecision = exports.memoize = exports.once = exports.hashValue = exports.mapFind = exports.fnIf = exports.putKeyValue = exports.getKeyValueOr = exports.getKeyValue = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  const guards_1 = require("./guards");
6
6
  /*
@@ -72,6 +72,34 @@ const once = (fn) => {
72
72
  return ((...args) => result || (result = fn(...args)));
73
73
  };
74
74
  exports.once = once;
75
+ /*
76
+ * memoizes a function with any number of arguments
77
+ */
78
+ const memoize = (fn) => {
79
+ const cache = {};
80
+ const resolveCache = (cacheLayer, [first, ...rest]) => {
81
+ if (!first) {
82
+ return cacheLayer;
83
+ }
84
+ const layers = first instanceof Object
85
+ ? (cacheLayer.weakLayers = (cacheLayer.weakLayers || (typeof WeakMap === 'undefined' ? undefined : new WeakMap())))
86
+ : (cacheLayer.strongLayers = (cacheLayer.strongLayers || new Map()));
87
+ // argument is an object and WeakMap is not supported
88
+ if (!layers) {
89
+ return {};
90
+ }
91
+ const layer = layers.get(first) || {};
92
+ if (!layers.has(first)) {
93
+ layers.set(first, layer);
94
+ }
95
+ return resolveCache(layer, rest);
96
+ };
97
+ return ((...args) => {
98
+ const thisCache = resolveCache(cache, args);
99
+ return thisCache.result = (thisCache.result || fn(...args));
100
+ });
101
+ };
102
+ exports.memoize = memoize;
75
103
  /*
76
104
  * rounds number to given number of places
77
105
  *
@@ -1,5 +1,5 @@
1
1
  import { TupleExtends } from './types';
2
- export declare type MiddlewareProvider<Sa, M, A extends any[], R> = (app: Sa) => (middleware: M, ...args: A) => R;
2
+ export declare type MiddlewareProvider<Sa, M, A extends any[], R> = (app: Sa, appBinder?: ((app: Sa, provider: MiddlewareProvider<Sa, M, A, R>) => (middleware: M, ...args: A) => R)) => (middleware: M, ...args: A) => R;
3
3
  export declare type MiddlewareTransformProvider<Sa, M, A extends any[], R> = (app: Sa) => (middleware: M, ...args: A) => Omit<M, keyof R> & R;
4
4
  export declare type MiddlewareInput<S> = S extends MiddlewareProvider<any, infer M, any, any> ? M : never;
5
5
  export declare type ServiceMiddlewareProviderResult<M, S> = [M] extends [never] ? never : [M] extends [MiddlewareInput<S>] ? S extends MiddlewareTransformProvider<any, M, any, infer R> ? R extends M ? R : Omit<M, keyof R> & R : S extends MiddlewareProvider<any, M, any, infer R> ? R : never : never;
@@ -29,8 +29,8 @@ exports.makeComposeMiddleware = void 0;
29
29
  * // this helps typescript figure out what is going on
30
30
  * eg: const myThing: myType = makeComposeMiddleware()(...);
31
31
  * */
32
- const makeComposeMiddleware = () => (...chain) => (app) => {
33
- const boundChain = chain.map(provider => provider(app));
32
+ const makeComposeMiddleware = () => (...chain) => (app, appBinder) => {
33
+ const boundChain = chain.map(provider => appBinder ? appBinder(app, provider) : provider(app));
34
34
  return (middleware, ...args) => {
35
35
  return boundChain.reduce((result, provider) => provider(result, ...args), middleware);
36
36
  };
package/dist/routing.d.ts CHANGED
@@ -92,8 +92,11 @@ export declare const getRequestBody: (request: {
92
92
  body?: string | undefined;
93
93
  }) => any;
94
94
  export declare const unsafePayloadValidator: <T>() => (input: any) => input is T;
95
- export declare const requestPayloadProvider: <T>(validator: (input: any) => input is T) => () => <M extends {
96
- request: Parameters<typeof getRequestBody>[0];
95
+ export declare const requestPayloadProvider: <T>(validator: (input: any) => input is T) => () => <R extends {
96
+ headers: HttpHeaders;
97
+ body?: string | undefined;
98
+ }, M extends {
99
+ request: R;
97
100
  }>(requestServices: M) => M & {
98
101
  payload: T;
99
102
  };
package/dist/routing.js CHANGED
@@ -89,9 +89,9 @@ const makeRenderRouteUrl = () => (route, params, query = {}) => {
89
89
  };
90
90
  exports.makeRenderRouteUrl = makeRenderRouteUrl;
91
91
  exports.renderAnyRouteUrl = (0, exports.makeRenderRouteUrl)();
92
- const bindRoute = (services, pathExtractor, matcher) => (route) => {
92
+ const bindRoute = (services, appBinder, pathExtractor, matcher) => (route) => {
93
93
  const getParamsFromPath = pathToRegexp.match(route.path, { decode: decodeURIComponent });
94
- const boundServiceProvider = route.requestServiceProvider && route.requestServiceProvider(services);
94
+ const boundServiceProvider = route.requestServiceProvider && appBinder(services, route.requestServiceProvider);
95
95
  return (request) => {
96
96
  const path = pathExtractor(request);
97
97
  const match = getParamsFromPath(path);
@@ -120,7 +120,9 @@ const bindRoute = (services, pathExtractor, matcher) => (route) => {
120
120
  * );
121
121
  */
122
122
  const makeGetRequestResponder = () => ({ routes, pathExtractor, routeMatcher, errorHandler }) => (services, responseMiddleware) => {
123
- const boundRoutes = routes().map(bindRoute(services, pathExtractor, routeMatcher));
123
+ const appBinderImpl = (app, middleware) => middleware(app, appBinder);
124
+ const appBinder = (0, _1.memoize)(appBinderImpl);
125
+ const boundRoutes = routes().map(bindRoute(services, appBinder, pathExtractor, routeMatcher));
124
126
  const boundResponseMiddleware = responseMiddleware ? responseMiddleware(services) : undefined;
125
127
  // *note* this opaque promise guard is less generic than i hoped so
126
128
  // i'm leaving it here instead of the guards file.
@@ -1,5 +1,5 @@
1
1
  import { ConfigProviderForConfig } from '../../config';
2
- import { ConfigForFetch, GenericFetch } from '../../fetch';
2
+ import { ConfigForFetch, GenericFetch, Response } from '../../fetch';
3
3
  import { AnyRoute, ApiResponse, OutputForRoute, ParamsForRoute, PayloadForRoute, QueryParams } from '../../routing';
4
4
  import { UnwrapPromise } from '../../types';
5
5
  declare type TResponsePayload<R> = R extends ApiResponse<any, infer P> ? P : never;
@@ -41,6 +41,7 @@ declare type MapRoutesToConfig<Ru> = [Ru] extends [AnyRoute<Ru>] ? {
41
41
  method: string;
42
42
  };
43
43
  } : never;
44
+ export declare const loadResponse: (response: Response) => () => Promise<any>;
44
45
  interface MakeApiGateway<F> {
45
46
  <Ru>(config: ConfigProviderForConfig<{
46
47
  apiBase: string;
@@ -48,14 +49,7 @@ interface MakeApiGateway<F> {
48
49
  getAuthorizedFetchConfig: () => ConfigForFetch<F>;
49
50
  }): MapRoutesToClient<Ru>;
50
51
  }
51
- export declare const createApiGateway: <F extends GenericFetch<import("../../fetch").FetchConfig, {
52
- status: number;
53
- headers: {
54
- get: (name: string) => string | null;
55
- };
56
- json: () => Promise<any>;
57
- text: () => Promise<string>;
58
- }>>(initializer: {
52
+ export declare const createApiGateway: <F extends GenericFetch<import("../../fetch").FetchConfig, Response>>(initializer: {
59
53
  fetch: F;
60
54
  }) => MakeApiGateway<F>;
61
55
  export {};
@@ -26,11 +26,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.createApiGateway = void 0;
29
+ exports.createApiGateway = exports.loadResponse = void 0;
30
30
  const pathToRegexp = __importStar(require("path-to-regexp"));
31
31
  const query_string_1 = __importDefault(require("query-string"));
32
32
  const __1 = require("../..");
33
33
  const config_1 = require("../../config");
34
+ const loadResponse = (response) => () => {
35
+ const contentType = response.headers.get('content-type');
36
+ switch (contentType) {
37
+ case 'text/plain':
38
+ return response.text();
39
+ case 'application/json':
40
+ return response.json();
41
+ default:
42
+ throw new Error(`unknown content type ${contentType}`);
43
+ }
44
+ };
45
+ exports.loadResponse = loadResponse;
34
46
  const makeRouteClient = (initializer, config, route, authProvider) => {
35
47
  const renderUrl = async ({ params, query }) => {
36
48
  const apiBase = await (0, config_1.resolveConfigValue)(config.apiBase);
@@ -55,9 +67,9 @@ const makeRouteClient = (initializer, config, route, authProvider) => {
55
67
  if (!status.includes(response.status)) {
56
68
  throw new Error('unexpected response from api');
57
69
  }
58
- return { status: response.status, load: () => response.json() };
70
+ return { status: response.status, load: (0, exports.loadResponse)(response) };
59
71
  },
60
- load: () => response.json(),
72
+ load: (0, exports.loadResponse)(response),
61
73
  }));
62
74
  };
63
75
  routeClient.renderUrl = renderUrl;
@@ -66,14 +66,7 @@ export declare const exercisesGateway: <C extends string = "exercises">(initiali
66
66
  }; }) => {
67
67
  searchDigest: (query: string, page?: number, per_page?: number) => Promise<string>;
68
68
  get: (uuid: string) => Promise<Exercise | undefined>;
69
- request: (method: METHOD, path: string, query?: object | undefined) => Promise<{
70
- status: number;
71
- headers: {
72
- get: (name: string) => string | null;
73
- };
74
- json: () => Promise<any>;
75
- text: () => Promise<string>;
76
- }>;
69
+ request: (method: METHOD, path: string, query?: object | undefined) => Promise<import("../../fetch").Response>;
77
70
  search: (query: string, page?: number, per_page?: number) => Promise<ExercisesSearchResultsWithDigest>;
78
71
  };
79
72
  export declare type ExercisesGateway = ReturnType<ReturnType<typeof exercisesGateway>>;