UNPKG

mappersmith

Version:

It is a lightweight rest client for node.js and the browser

1 lines 14.5 kB
{"version":3,"sources":["../../src/request.ts"],"sourcesContent":["import { MethodDescriptor } from './method-descriptor'\nimport { toQueryString, lowerCaseObjectKeys, assign } from './utils/index'\nimport type {\n Auth,\n Body,\n Headers,\n NestedParam,\n NestedParamArray,\n Primitive,\n RequestParams,\n} from './types'\n\nconst REGEXP_DYNAMIC_SEGMENT = /{([^}?]+)\\??}/\nconst REGEXP_OPTIONAL_DYNAMIC_SEGMENT = /\\/?{([^}?]+)\\?}/g\nconst REGEXP_TRAILING_SLASH = /\\/$/\n\nexport type RequestContext = Record<string, unknown>\n\n/**\n * Removes the object type without removing Record types in the union\n */\nexport type ExcludeObject<T> = T extends object ? (object extends T ? never : T) : T\n\n/**\n * @typedef Request\n * @param {MethodDescriptor} methodDescriptor\n * @param {RequestParams} requestParams, defaults to an empty object ({})\n * @param {RequestContext} request context store, defaults to an empty object ({})\n */\nexport class Request {\n public methodDescriptor: MethodDescriptor\n public requestParams: RequestParams\n private requestContext: RequestContext\n\n constructor(\n methodDescriptor: MethodDescriptor,\n requestParams: RequestParams = {},\n requestContext: RequestContext = {}\n ) {\n this.methodDescriptor = methodDescriptor\n this.requestParams = requestParams\n this.requestContext = requestContext\n }\n\n private isParam(key: string) {\n return (\n key !== this.methodDescriptor.headersAttr &&\n key !== this.methodDescriptor.bodyAttr &&\n key !== this.methodDescriptor.authAttr &&\n key !== this.methodDescriptor.timeoutAttr &&\n key !== this.methodDescriptor.hostAttr &&\n key !== this.methodDescriptor.signalAttr &&\n key !== this.methodDescriptor.pathAttr\n )\n }\n\n public params() {\n const params = assign({}, this.methodDescriptor.params, this.requestParams)\n\n return Object.keys(params).reduce((obj, key) => {\n if (this.isParam(key)) {\n obj[key] = params[key]\n }\n return obj\n }, {} as RequestParams)\n }\n\n /**\n * Returns the request context; a key value object.\n * Useful to pass information from upstream middleware to a downstream one.\n */\n public context<T extends RequestContext = RequestContext>() {\n return this.requestContext as T\n }\n\n /**\n * Returns the HTTP method in lowercase\n */\n public method() {\n return this.methodDescriptor.method.toLowerCase()\n }\n\n /**\n * Returns host name without trailing slash\n * Example: 'http://example.org'\n */\n public host() {\n const { allowResourceHostOverride, hostAttr, host } = this.methodDescriptor\n const originalHost = allowResourceHostOverride\n ? this.requestParams[hostAttr] || host || ''\n : host || ''\n\n if (typeof originalHost === 'string') {\n return originalHost.replace(REGEXP_TRAILING_SLASH, '')\n }\n\n return ''\n }\n\n /**\n * Returns path with parameters and leading slash.\n * Example: '/some/path?param1=true'\n *\n * @throws {Error} if any dynamic segment is missing.\n * Example:\n * Imagine the path '/some/{name}', the error will be similar to:\n * '[Mappersmith] required parameter missing (name), \"/some/{name}\" cannot be resolved'\n */\n public path() {\n const { pathAttr: mdPathAttr, path: mdPath } = this.methodDescriptor\n const originalPath = (this.requestParams[mdPathAttr] as RequestParams['path']) || mdPath || ''\n const params = this.params()\n\n let path: string\n if (typeof originalPath === 'function') {\n path = originalPath(params)\n if (typeof path !== 'string') {\n throw new Error(\n `[Mappersmith] method descriptor function did not return a string, params=${JSON.stringify(\n params\n )}`\n )\n }\n } else {\n path = originalPath\n }\n\n // RegExp with 'g'-flag is stateful, therefore defining it locally\n const regexp = new RegExp(REGEXP_DYNAMIC_SEGMENT, 'g')\n\n const dynamicSegmentKeys = []\n let match\n while ((match = regexp.exec(path)) !== null) {\n dynamicSegmentKeys.push(match[1])\n }\n\n for (const key of dynamicSegmentKeys) {\n const pattern = new RegExp(`{${key}\\\\??}`, 'g')\n const value = params[key]\n if (value != null && typeof value !== 'object') {\n path = path.replace(pattern, this.methodDescriptor.parameterEncoder(value))\n delete params[key]\n }\n }\n\n path = path.replace(REGEXP_OPTIONAL_DYNAMIC_SEGMENT, '')\n\n const missingDynamicSegmentMatch = path.match(REGEXP_DYNAMIC_SEGMENT)\n if (missingDynamicSegmentMatch) {\n throw new Error(\n `[Mappersmith] required parameter missing (${missingDynamicSegmentMatch[1]}), \"${path}\" cannot be resolved`\n )\n }\n\n const aliasedParams = Object.keys(params).reduce(\n (aliased, key) => {\n const aliasedKey = this.methodDescriptor.queryParamAlias[key] || key\n const value = params[key]\n if (value != null) {\n /**\n * Here we use `ExcludeObject` to surgically remove the `object` type from `value`.\n * We need it as `object` is too broad to be useful, whereas `value` is also typed\n * as NestedParam, which is the correct shape for param objects.\n */\n aliased[aliasedKey] = value as ExcludeObject<typeof value>\n }\n return aliased\n },\n {} as Record<string, Primitive | NestedParam | NestedParamArray>\n )\n\n const queryString = toQueryString(aliasedParams, this.methodDescriptor.parameterEncoder)\n if (typeof queryString === 'string' && queryString.length !== 0) {\n const hasQuery = path.includes('?')\n path += `${hasQuery ? '&' : '?'}${queryString}`\n }\n\n // https://www.rfc-editor.org/rfc/rfc1738#section-3.3\n if (path[0] !== '/' && path.length > 0) {\n path = `/${path}`\n }\n\n return path\n }\n\n /**\n * Returns the template path, without params, before interpolation.\n * If path is a function, returns the result of request.path()\n * Example: '/some/{param}/path'\n */\n public pathTemplate() {\n const path = this.methodDescriptor.path\n\n const prependSlash = (str: string) => (str[0] !== '/' ? `/${str}` : str)\n\n if (typeof path === 'function') {\n return prependSlash(path(this.params()))\n }\n\n return prependSlash(path)\n }\n\n /**\n * Returns the full URL\n * Example: http://example.org/some/path?param1=true\n *\n */\n public url() {\n return `${this.host()}${this.path()}`\n }\n\n /**\n * Returns an object with the headers. Header names are converted to\n * lowercase\n */\n public headers() {\n const headerAttr = this.methodDescriptor.headersAttr\n const headers = (this.requestParams[headerAttr] || {}) as Headers\n\n if (typeof headers === 'function') {\n return headers\n }\n\n const mergedHeaders = { ...this.methodDescriptor.headers, ...headers } as Headers\n return lowerCaseObjectKeys(mergedHeaders) as Headers\n }\n\n /**\n * Utility method to get a header value by name\n */\n public header<T extends string | number | boolean>(name: string): T | undefined {\n const key = name.toLowerCase()\n\n if (key in this.headers()) {\n return this.headers()[key] as T\n }\n\n return undefined\n }\n\n public body() {\n return this.requestParams[this.methodDescriptor.bodyAttr] as Body | undefined\n }\n\n public auth() {\n return this.requestParams[this.methodDescriptor.authAttr] as Auth | undefined\n }\n\n public timeout() {\n return this.requestParams[this.methodDescriptor.timeoutAttr] as number | undefined\n }\n\n public signal() {\n return this.requestParams[this.methodDescriptor.signalAttr] as AbortSignal | undefined\n }\n\n /**\n * Enhances current request returning a new Request\n * @param {RequestParams} extras\n * @param {Object} extras.auth - it will replace the current auth\n * @param {String|Object} extras.body - it will replace the current body\n * @param {Headers} extras.headers - it will be merged with current headers\n * @param {String} extras.host - it will replace the current timeout\n * @param {RequestParams} extras.params - it will be merged with current params\n * @param {Number} extras.timeout - it will replace the current timeout\n * @param {Object} requestContext - Use to pass information between different middleware.\n */\n public enhance(extras: RequestParams, requestContext?: RequestContext) {\n const authKey = this.methodDescriptor.authAttr\n const bodyKey = this.methodDescriptor.bodyAttr\n const headerKey = this.methodDescriptor.headersAttr\n const hostKey = this.methodDescriptor.hostAttr\n const timeoutKey = this.methodDescriptor.timeoutAttr\n const pathKey = this.methodDescriptor.pathAttr\n const signalKey = this.methodDescriptor.signalAttr\n\n // Note: The result of merging an instance of RequestParams with instance of Params\n // is simply a RequestParams with even more [param: string]'s on it.\n const requestParams: RequestParams = assign({}, this.requestParams, extras.params)\n\n const headers = this.requestParams[headerKey] as Headers | undefined\n const mergedHeaders = assign({}, headers, extras.headers)\n requestParams[headerKey] = mergedHeaders\n\n extras.auth && (requestParams[authKey] = extras.auth)\n extras.body && (requestParams[bodyKey] = extras.body)\n extras.host && (requestParams[hostKey] = extras.host)\n extras.timeout && (requestParams[timeoutKey] = extras.timeout)\n extras.path && (requestParams[pathKey] = extras.path)\n extras.signal && (requestParams[signalKey] = extras.signal)\n\n const nextContext = { ...this.requestContext, ...requestContext }\n\n return new Request(this.methodDescriptor, requestParams, nextContext)\n }\n\n /**\n * Is the request expecting a binary response?\n */\n public isBinary() {\n return this.methodDescriptor.binary\n }\n}\n\nexport default Request\n"],"mappings":"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","names":[]}