httpsnippet-client-api 6.1.0 → 7.0.0-alpha.4

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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2022 ReadMe
1
+ Copyright © 2023 ReadMe
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of
4
4
  this software and associated documentation files (the “Software”), to deal in
package/dist/index.cjs ADDED
@@ -0,0 +1,219 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/index.ts
2
+ var _codebuilder = require('@readme/httpsnippet/helpers/code-builder');
3
+ var _contenttype = require('content-type'); var _contenttype2 = _interopRequireDefault(_contenttype);
4
+ var _oas = require('oas'); var _oas2 = _interopRequireDefault(_oas);
5
+ var _utils = require('oas/utils');
6
+ var _stringifyobject = require('stringify-object'); var _stringifyobject2 = _interopRequireDefault(_stringifyobject);
7
+ function stringify(obj, opts = {}) {
8
+ return _stringifyobject2.default.call(void 0, obj, { indent: " ", ...opts });
9
+ }
10
+ function buildAuthSnippet(authKey) {
11
+ if (Array.isArray(authKey)) {
12
+ const auth = [];
13
+ authKey.forEach((token, i) => {
14
+ if (token.length === 0 && authKey.length > 1 && i === authKey.length - 1) {
15
+ return;
16
+ }
17
+ auth.push(`'${token.replace(/'/g, "\\'")}'`);
18
+ });
19
+ return `sdk.auth(${auth.join(", ")});`;
20
+ }
21
+ return `sdk.auth('${authKey.replace(/'/g, "\\'")}');`;
22
+ }
23
+ function getAuthSources(operation) {
24
+ const matchers = {
25
+ header: {},
26
+ query: [],
27
+ cookie: []
28
+ };
29
+ if (operation.getSecurity().length === 0) {
30
+ return matchers;
31
+ }
32
+ Object.entries(operation.prepareSecurity()).forEach(([, schemes]) => {
33
+ schemes.forEach((scheme) => {
34
+ if (scheme.type === "http") {
35
+ if (scheme.scheme === "basic") {
36
+ matchers.header.authorization = "Basic";
37
+ } else if (scheme.scheme === "bearer") {
38
+ matchers.header.authorization = "Bearer";
39
+ }
40
+ } else if (scheme.type === "oauth2") {
41
+ matchers.header.authorization = "Bearer";
42
+ } else if (scheme.type === "apiKey") {
43
+ if (scheme.in === "query") {
44
+ matchers.query.push(scheme.name);
45
+ } else if (scheme.in === "header") {
46
+ matchers.header[scheme.name.toLowerCase()] = "*";
47
+ } else if (scheme.in === "cookie") {
48
+ matchers.cookie.push(scheme.name);
49
+ }
50
+ }
51
+ });
52
+ });
53
+ return matchers;
54
+ }
55
+ var client = {
56
+ info: {
57
+ key: "api",
58
+ title: "API",
59
+ link: "https://npm.im/api",
60
+ description: "Automatic SDK generation from an OpenAPI definition.",
61
+ extname: ".js"
62
+ },
63
+ convert: ({ cookiesObj, headersObj, postData, queryObj, url, ...source }, options) => {
64
+ const opts = {
65
+ ...options
66
+ };
67
+ if (!("apiDefinitionUri" in opts)) {
68
+ throw new Error("This HTTP Snippet client must have an `apiDefinitionUri` option supplied to it.");
69
+ } else if (!("apiDefinition" in opts)) {
70
+ throw new Error("This HTTP Snippet client must have an `apiDefinition` option supplied to it.");
71
+ }
72
+ const method = source.method.toLowerCase();
73
+ const oas = new (0, _oas2.default)(opts.apiDefinition);
74
+ const apiDefinition = oas.getDefinition();
75
+ const foundOperation = oas.findOperation(url, method);
76
+ if (!foundOperation) {
77
+ throw new Error(
78
+ `Unable to locate a matching operation in the supplied \`apiDefinition\` for: ${source.method} ${url}`
79
+ );
80
+ }
81
+ const operationSlugs = foundOperation.url.slugs;
82
+ const operation = oas.operation(foundOperation.url.nonNormalizedPath, method);
83
+ const operationPathParameters = operation.getParameters().filter((param) => param.in === "path");
84
+ const path = operation.path;
85
+ const authData = [];
86
+ const authSources = getAuthSources(operation);
87
+ const { blank, push, join } = new (0, _codebuilder.CodeBuilder)({ indent: opts.indent || " " });
88
+ push(`const sdk = require('api')('${opts.apiDefinitionUri}');`);
89
+ blank();
90
+ const configData = [];
91
+ if ((apiDefinition.servers || []).length > 1) {
92
+ const stockUrl = oas.url();
93
+ const baseUrl = url.replace(path, "");
94
+ if (baseUrl !== stockUrl) {
95
+ const serverVars = oas.splitVariables(baseUrl);
96
+ const serverUrl = serverVars ? oas.url(serverVars.selected, serverVars.variables) : baseUrl;
97
+ configData.push(`sdk.server('${serverUrl}');`);
98
+ }
99
+ }
100
+ let metadata = {};
101
+ Object.keys(queryObj).forEach((param) => {
102
+ if (authSources.query.includes(param)) {
103
+ authData.push(buildAuthSnippet(queryObj[param]));
104
+ return;
105
+ }
106
+ metadata[param] = queryObj[param];
107
+ });
108
+ Object.keys(cookiesObj).forEach((cookie) => {
109
+ if (authSources.cookie.includes(cookie)) {
110
+ authData.push(buildAuthSnippet(cookiesObj[cookie]));
111
+ return;
112
+ }
113
+ metadata[cookie] = cookiesObj[cookie];
114
+ });
115
+ Array.from(Object.entries(operationSlugs)).forEach(([param, value]) => {
116
+ const cleanedParam = param.substring(1);
117
+ const unsanitizedParam = operationPathParameters.find((p) => {
118
+ return p.name.includes("-") && p.name.replace(/-/g, "") === cleanedParam ? p.name : false;
119
+ });
120
+ if (unsanitizedParam) {
121
+ metadata[unsanitizedParam.name] = value;
122
+ } else {
123
+ metadata[cleanedParam] = value;
124
+ }
125
+ });
126
+ if (Object.keys(headersObj).length) {
127
+ const headers = headersObj;
128
+ const requestHeaders = {};
129
+ Object.keys(headers).forEach((header) => {
130
+ const headerLower = header.toLowerCase();
131
+ if (headerLower in authSources.header) {
132
+ const authScheme = authSources.header[headerLower];
133
+ if (authScheme === "*") {
134
+ authData.push(buildAuthSnippet(headers[header]));
135
+ } else {
136
+ let authKey = headers[header].replace(`${authSources.header[headerLower]} `, "");
137
+ if (authScheme.toLowerCase() === "basic") {
138
+ authKey = Buffer.from(authKey, "base64").toString("ascii");
139
+ authKey = authKey.split(":");
140
+ }
141
+ authData.push(buildAuthSnippet(authKey));
142
+ }
143
+ delete headers[header];
144
+ return;
145
+ } else if (headerLower === "content-type") {
146
+ const parsedContentType = _contenttype2.default.parse(headers[header]);
147
+ if (!Object.keys(parsedContentType.parameters).length) {
148
+ delete headers[header];
149
+ return;
150
+ }
151
+ } else if (headerLower === "accept") {
152
+ if (_utils.matchesMimeType.json(headers[header])) {
153
+ delete headers[header];
154
+ return;
155
+ }
156
+ }
157
+ requestHeaders[headerLower] = headers[header];
158
+ });
159
+ if (Object.keys(requestHeaders).length > 0) {
160
+ metadata = Object.assign(metadata, requestHeaders);
161
+ }
162
+ }
163
+ let body;
164
+ switch (postData.mimeType) {
165
+ case "application/x-www-form-urlencoded":
166
+ body = postData.paramsObj;
167
+ break;
168
+ case "application/json":
169
+ if (postData.jsonObj) {
170
+ body = postData.jsonObj;
171
+ }
172
+ break;
173
+ case "multipart/form-data":
174
+ if (postData.params) {
175
+ body = {};
176
+ if ("content-type" in metadata && metadata["content-type"].indexOf("multipart/form-data") === 0) {
177
+ delete metadata["content-type"];
178
+ }
179
+ postData.params.forEach((param) => {
180
+ if (param.fileName) {
181
+ body[param.name] = param.fileName;
182
+ } else {
183
+ body[param.name] = param.value;
184
+ }
185
+ });
186
+ }
187
+ break;
188
+ default:
189
+ if (postData.text) {
190
+ body = postData.text;
191
+ }
192
+ }
193
+ const args = [];
194
+ const accessor = operation.getOperationId({ camelCase: true });
195
+ const inlineCharacterLimit = typeof body !== "undefined" && Object.keys(metadata).length > 0 ? 40 : 80;
196
+ if (typeof body !== "undefined") {
197
+ args.push(stringify(body, { inlineCharacterLimit }));
198
+ }
199
+ if (Object.keys(metadata).length > 0) {
200
+ args.push(stringify(metadata, { inlineCharacterLimit }));
201
+ }
202
+ if (authData.length) {
203
+ push(authData.join("\n"));
204
+ }
205
+ if (configData.length) {
206
+ push(configData.join("\n"));
207
+ }
208
+ push(`sdk.${accessor}(${args.join(", ")})`);
209
+ push(".then(({ data }) => console.log(data))", 1);
210
+ push(".catch(err => console.error(err));", 1);
211
+ return join();
212
+ }
213
+ };
214
+ var src_default = client;
215
+
216
+
217
+ exports.default = src_default;
218
+
219
+ module.exports = exports.default//# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReducedHelperObject } from '@readme/httpsnippet/helpers/reducer';\nimport type { Client } from '@readme/httpsnippet/targets';\nimport type Operation from 'oas/operation';\nimport type { HttpMethods, OASDocument } from 'oas/rmoas.types';\n\nimport { CodeBuilder } from '@readme/httpsnippet/helpers/code-builder';\nimport contentType from 'content-type';\nimport Oas from 'oas';\nimport { matchesMimeType } from 'oas/utils';\nimport stringifyObject from 'stringify-object';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction stringify(obj: any, opts = {}) {\n return stringifyObject(obj, { indent: ' ', ...opts });\n}\n\nfunction buildAuthSnippet(authKey: string | string[]) {\n // Auth key will be an array for Basic auth cases.\n if (Array.isArray(authKey)) {\n const auth: string[] = [];\n authKey.forEach((token, i) => {\n // If the token part is the last part of the key and it's empty, don't add it to the snippet.\n if (token.length === 0 && authKey.length > 1 && i === authKey.length - 1) {\n return;\n }\n\n auth.push(`'${token.replace(/'/g, \"\\\\'\")}'`);\n });\n\n return `sdk.auth(${auth.join(', ')});`;\n }\n\n return `sdk.auth('${authKey.replace(/'/g, \"\\\\'\")}');`;\n}\n\nfunction getAuthSources(operation: Operation) {\n const matchers: { cookie: string[]; header: Record<string, string>; query: string[] } = {\n header: {},\n query: [],\n cookie: [],\n };\n\n if (operation.getSecurity().length === 0) {\n return matchers;\n }\n\n Object.entries(operation.prepareSecurity()).forEach(([, schemes]) => {\n schemes.forEach(scheme => {\n if (scheme.type === 'http') {\n if (scheme.scheme === 'basic') {\n matchers.header.authorization = 'Basic';\n } else if (scheme.scheme === 'bearer') {\n matchers.header.authorization = 'Bearer';\n }\n } else if (scheme.type === 'oauth2') {\n matchers.header.authorization = 'Bearer';\n } else if (scheme.type === 'apiKey') {\n if (scheme.in === 'query') {\n matchers.query.push(scheme.name);\n } else if (scheme.in === 'header') {\n // The way that this asterisk header matcher works is that since this `apiKey` goes in a\n // named header (`scheme.name`) because the header is the key, we're matching against the\n // entire header -- counter to the way that the HTTP basic matcher above works where we\n // match and extract the API key from everything after `Basic ` in the `Authorization`\n // header.\n matchers.header[scheme.name.toLowerCase()] = '*';\n } else if (scheme.in === 'cookie') {\n matchers.cookie.push(scheme.name);\n }\n }\n });\n });\n\n return matchers;\n}\n\ninterface APIOptions {\n apiDefinition: OASDocument;\n apiDefinitionUri: string;\n escapeBrackets?: boolean;\n indent?: string | false;\n}\n\nconst client: Client<APIOptions> = {\n info: {\n key: 'api',\n title: 'API',\n link: 'https://npm.im/api',\n description: 'Automatic SDK generation from an OpenAPI definition.',\n extname: '.js',\n },\n convert: ({ cookiesObj, headersObj, postData, queryObj, url, ...source }, options) => {\n const opts = {\n ...options,\n } as APIOptions;\n\n if (!('apiDefinitionUri' in opts)) {\n throw new Error('This HTTP Snippet client must have an `apiDefinitionUri` option supplied to it.');\n } else if (!('apiDefinition' in opts)) {\n throw new Error('This HTTP Snippet client must have an `apiDefinition` option supplied to it.');\n }\n\n const method = source.method.toLowerCase() as HttpMethods;\n const oas = new Oas(opts.apiDefinition);\n const apiDefinition = oas.getDefinition();\n const foundOperation = oas.findOperation(url, method);\n if (!foundOperation) {\n throw new Error(\n `Unable to locate a matching operation in the supplied \\`apiDefinition\\` for: ${source.method} ${url}`,\n );\n }\n\n const operationSlugs = foundOperation.url.slugs;\n const operation = oas.operation(foundOperation.url.nonNormalizedPath, method);\n const operationPathParameters = operation.getParameters().filter(param => param.in === 'path');\n const path = operation.path;\n const authData: string[] = [];\n const authSources = getAuthSources(operation);\n\n const { blank, push, join } = new CodeBuilder({ indent: opts.indent || ' ' });\n\n push(`const sdk = require('api')('${opts.apiDefinitionUri}');`);\n blank();\n\n // If we have multiple servers configured and our source URL differs from the stock URL that we\n // receive from our `oas` library then the URL either has server variables contained in it (that\n // don't match the defaults), or the OAS offers alternate server URLs and we should expose that\n // in the generated snippet.\n const configData = [];\n if ((apiDefinition.servers || []).length > 1) {\n const stockUrl = oas.url();\n const baseUrl = url.replace(path, '');\n if (baseUrl !== stockUrl) {\n const serverVars = oas.splitVariables(baseUrl);\n const serverUrl = serverVars ? oas.url(serverVars.selected, serverVars.variables) : baseUrl;\n\n configData.push(`sdk.server('${serverUrl}');`);\n }\n }\n\n let metadata: Record<string, string | string[]> = {};\n Object.keys(queryObj).forEach(param => {\n if (authSources.query.includes(param)) {\n authData.push(buildAuthSnippet(queryObj[param]));\n\n // If this query param is part of an auth source then we don't want it doubled up in the\n // snippet.\n return;\n }\n\n metadata[param] = queryObj[param];\n });\n\n Object.keys(cookiesObj).forEach(cookie => {\n if (authSources.cookie.includes(cookie)) {\n authData.push(buildAuthSnippet(cookiesObj[cookie]));\n\n // If this cookie is part of an auth source then we don't want it doubled up.\n return;\n }\n\n // Note that we may have the potential to overlap any cookie that also shares the name as\n // another metadata parameter. This problem is currently inherent to `api` and not this\n // snippet generator.\n metadata[cookie] = cookiesObj[cookie];\n });\n\n // If we have path parameters present we should add them into the metadata object.\n Array.from(Object.entries(operationSlugs)).forEach(([param, value]) => {\n // The keys in `operationSlugs` will always be prefixed with a `:` in the `oas` library so\n // we can safely do this substring here without asserting this context.\n const cleanedParam = param.substring(1);\n\n // If our incoming path slug out of `oas.findOperation()` has been sanitized and is missing\n // a hyphen, but there is a parameter in the OpenAPI definition that matches what our\n // hyphen-less slug is then we should use that for the snippet.\n const unsanitizedParam = operationPathParameters.find(p => {\n return p.name.includes('-') && p.name.replace(/-/g, '') === cleanedParam ? p.name : false;\n });\n\n if (unsanitizedParam) {\n metadata[unsanitizedParam.name] = value;\n } else {\n metadata[cleanedParam] = value;\n }\n });\n\n if (Object.keys(headersObj).length) {\n const headers = headersObj;\n const requestHeaders: ReducedHelperObject = {};\n\n Object.keys(headers).forEach(header => {\n // Headers in HTTPSnippet are case-insensitive so we need to add in some special handling to\n // make sure we're able to match them properly.\n const headerLower = header.toLowerCase();\n\n if (headerLower in authSources.header) {\n // If this header has been set up as an authentication header, let's remove it and add it\n // into our auth data so we can build up an `.auth()` snippet for the SDK.\n const authScheme = authSources.header[headerLower];\n if (authScheme === '*') {\n authData.push(buildAuthSnippet(headers[header]));\n } else {\n // @ts-expect-error `headers[header]` is typed improperly in HTTPSnippet.\n let authKey = headers[header].replace(`${authSources.header[headerLower]} `, '');\n if (authScheme.toLowerCase() === 'basic') {\n authKey = Buffer.from(authKey, 'base64').toString('ascii');\n authKey = authKey.split(':');\n }\n\n authData.push(buildAuthSnippet(authKey));\n }\n\n delete headers[header];\n return;\n } else if (headerLower === 'content-type') {\n // `Content-Type` headers are automatically added within the SDK so we can filter them out\n // if they don't have parameters attached to them.\n // @ts-expect-error `headers[header]` is typed improperly in HTTPSnippet.\n const parsedContentType = contentType.parse(headers[header]);\n if (!Object.keys(parsedContentType.parameters).length) {\n delete headers[header];\n return;\n }\n } else if (headerLower === 'accept') {\n // If the `Accept` header here is JSON-like header then we can remove it from the code\n // snippet because `api` natively supports and prioritizes JSON over any other mime type.\n if (matchesMimeType.json(headers[header] as string)) {\n delete headers[header];\n return;\n }\n }\n\n // If we haven't used our header anywhere else, or we've deleted it from the payload\n // because it'll be handled internally by `api` then we should add the lowercased version\n // of our header into the generated code snippet.\n requestHeaders[headerLower] = headers[header];\n });\n\n if (Object.keys(requestHeaders).length > 0) {\n metadata = Object.assign(metadata, requestHeaders);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let body: any;\n switch (postData.mimeType) {\n case 'application/x-www-form-urlencoded':\n body = postData.paramsObj;\n break;\n\n case 'application/json':\n if (postData.jsonObj) {\n body = postData.jsonObj;\n }\n break;\n\n case 'multipart/form-data':\n if (postData.params) {\n body = {};\n\n // If there's a `Content-Type` header present in the metadata, but it's for the\n // `multipart/form-data` request then dump it off the snippet. We shouldn't offload that\n // unnecessary bloat of multipart boundaries to the user, instead letting the SDK handle it\n // automatically.\n if ('content-type' in metadata && metadata['content-type'].indexOf('multipart/form-data') === 0) {\n delete metadata['content-type'];\n }\n\n postData.params.forEach(param => {\n if (param.fileName) {\n body[param.name] = param.fileName;\n } else {\n body[param.name] = param.value;\n }\n });\n }\n break;\n\n default:\n if (postData.text) {\n body = postData.text;\n }\n }\n\n const args = [];\n\n const accessor = operation.getOperationId({ camelCase: true });\n\n // If we're going to be rendering out body params and metadata we should cut their character\n // limit in half because we'll be rendering them in their own lines.\n const inlineCharacterLimit = typeof body !== 'undefined' && Object.keys(metadata).length > 0 ? 40 : 80;\n if (typeof body !== 'undefined') {\n args.push(stringify(body, { inlineCharacterLimit }));\n }\n\n if (Object.keys(metadata).length > 0) {\n args.push(stringify(metadata, { inlineCharacterLimit }));\n }\n\n if (authData.length) {\n push(authData.join('\\n'));\n }\n\n if (configData.length) {\n push(configData.join('\\n'));\n }\n\n push(`sdk.${accessor}(${args.join(', ')})`);\n push('.then(({ data }) => console.log(data))', 1);\n push('.catch(err => console.error(err));', 1);\n\n return join();\n },\n};\n\nexport default client;\n"]}
@@ -0,0 +1,12 @@
1
+ import { Client } from '@readme/httpsnippet/targets';
2
+ import { OASDocument } from 'oas/rmoas.types';
3
+
4
+ interface APIOptions {
5
+ apiDefinition: OASDocument;
6
+ apiDefinitionUri: string;
7
+ escapeBrackets?: boolean;
8
+ indent?: string | false;
9
+ }
10
+ declare const client: Client<APIOptions>;
11
+
12
+ export = client;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- import type { Client } from '@readme/httpsnippet/dist/targets/targets';
2
- import type { OASDocument } from 'oas/dist/rmoas.types';
3
- export interface APIOptions {
1
+ import { Client } from '@readme/httpsnippet/targets';
2
+ import { OASDocument } from 'oas/rmoas.types';
3
+
4
+ interface APIOptions {
4
5
  apiDefinition: OASDocument;
5
6
  apiDefinitionUri: string;
6
7
  escapeBrackets?: boolean;
7
8
  indent?: string | false;
8
9
  }
9
10
  declare const client: Client<APIOptions>;
10
- export default client;
11
+
12
+ export { client as default };
package/dist/index.js CHANGED
@@ -1,292 +1,218 @@
1
- "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
- var __rest = (this && this.__rest) || function (s, e) {
14
- var t = {};
15
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
16
- t[p] = s[p];
17
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
18
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
19
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
20
- t[p[i]] = s[p[i]];
21
- }
22
- return t;
23
- };
24
- var __importDefault = (this && this.__importDefault) || function (mod) {
25
- return (mod && mod.__esModule) ? mod : { "default": mod };
26
- };
27
- exports.__esModule = true;
28
- var code_builder_1 = require("@readme/httpsnippet/dist/helpers/code-builder");
29
- var content_type_1 = __importDefault(require("content-type"));
30
- var oas_1 = __importDefault(require("oas"));
31
- var stringify_object_1 = __importDefault(require("stringify-object"));
32
- function stringify(obj, opts) {
33
- if (opts === void 0) { opts = {}; }
34
- return (0, stringify_object_1["default"])(obj, __assign({ indent: ' ' }, opts));
1
+ // src/index.ts
2
+ import { CodeBuilder } from "@readme/httpsnippet/helpers/code-builder";
3
+ import contentType from "content-type";
4
+ import Oas from "oas";
5
+ import { matchesMimeType } from "oas/utils";
6
+ import stringifyObject from "stringify-object";
7
+ function stringify(obj, opts = {}) {
8
+ return stringifyObject(obj, { indent: " ", ...opts });
35
9
  }
36
10
  function buildAuthSnippet(authKey) {
37
- // Auth key will be an array for Basic auth cases.
38
- if (Array.isArray(authKey)) {
39
- var auth_1 = [];
40
- authKey.forEach(function (token, i) {
41
- // If the token part is the last part of the key and it's empty, don't add it to the snippet.
42
- if (token.length === 0 && authKey.length > 1 && i === authKey.length - 1) {
43
- return;
44
- }
45
- auth_1.push("'".concat(token.replace(/'/g, "\\'"), "'"));
46
- });
47
- return "sdk.auth(".concat(auth_1.join(', '), ");");
48
- }
49
- return "sdk.auth('".concat(authKey.replace(/'/g, "\\'"), "');");
11
+ if (Array.isArray(authKey)) {
12
+ const auth = [];
13
+ authKey.forEach((token, i) => {
14
+ if (token.length === 0 && authKey.length > 1 && i === authKey.length - 1) {
15
+ return;
16
+ }
17
+ auth.push(`'${token.replace(/'/g, "\\'")}'`);
18
+ });
19
+ return `sdk.auth(${auth.join(", ")});`;
20
+ }
21
+ return `sdk.auth('${authKey.replace(/'/g, "\\'")}');`;
50
22
  }
51
23
  function getAuthSources(operation) {
52
- var matchers = {
53
- header: {},
54
- query: [],
55
- cookie: []
56
- };
57
- if (operation.getSecurity().length === 0) {
58
- return matchers;
59
- }
60
- var security = operation.prepareSecurity();
61
- Object.keys(security).forEach(function (id) {
62
- security[id].forEach(function (scheme) {
63
- if (scheme.type === 'http') {
64
- if (scheme.scheme === 'basic') {
65
- matchers.header.authorization = 'Basic';
66
- }
67
- else if (scheme.scheme === 'bearer') {
68
- matchers.header.authorization = 'Bearer';
69
- }
70
- }
71
- else if (scheme.type === 'oauth2') {
72
- matchers.header.authorization = 'Bearer';
73
- }
74
- else if (scheme.type === 'apiKey') {
75
- if (scheme["in"] === 'query') {
76
- matchers.query.push(scheme.name);
77
- }
78
- else if (scheme["in"] === 'header') {
79
- // The way that this asterisk header matcher works is that since this `apiKey` goes in a
80
- // named header (`scheme.name`) because the header is the key, we're matching against the
81
- // entire header -- counter to the way that the HTTP basic matcher above works where we
82
- // match and extract the API key from everything after `Basic ` in the `Authorization`
83
- // header.
84
- matchers.header[scheme.name.toLowerCase()] = '*';
85
- }
86
- else if (scheme["in"] === 'cookie') {
87
- matchers.cookie.push(scheme.name);
88
- }
89
- }
90
- });
91
- });
24
+ const matchers = {
25
+ header: {},
26
+ query: [],
27
+ cookie: []
28
+ };
29
+ if (operation.getSecurity().length === 0) {
92
30
  return matchers;
93
- }
94
- var client = {
95
- info: {
96
- key: 'api',
97
- title: 'API',
98
- link: 'https://npm.im/api',
99
- description: 'Automatic SDK generation from an OpenAPI definition.'
100
- },
101
- convert: function (_a, options) {
102
- var cookiesObj = _a.cookiesObj, headersObj = _a.headersObj, postData = _a.postData, queryObj = _a.queryObj, url = _a.url, source = __rest(_a, ["cookiesObj", "headersObj", "postData", "queryObj", "url"]);
103
- var opts = __assign({}, options);
104
- if (!('apiDefinitionUri' in opts)) {
105
- throw new Error('This HTTP Snippet client must have an `apiDefinitionUri` option supplied to it.');
31
+ }
32
+ Object.entries(operation.prepareSecurity()).forEach(([, schemes]) => {
33
+ schemes.forEach((scheme) => {
34
+ if (scheme.type === "http") {
35
+ if (scheme.scheme === "basic") {
36
+ matchers.header.authorization = "Basic";
37
+ } else if (scheme.scheme === "bearer") {
38
+ matchers.header.authorization = "Bearer";
106
39
  }
107
- else if (!('apiDefinition' in opts)) {
108
- throw new Error('This HTTP Snippet client must have an `apiDefinition` option supplied to it.');
40
+ } else if (scheme.type === "oauth2") {
41
+ matchers.header.authorization = "Bearer";
42
+ } else if (scheme.type === "apiKey") {
43
+ if (scheme.in === "query") {
44
+ matchers.query.push(scheme.name);
45
+ } else if (scheme.in === "header") {
46
+ matchers.header[scheme.name.toLowerCase()] = "*";
47
+ } else if (scheme.in === "cookie") {
48
+ matchers.cookie.push(scheme.name);
109
49
  }
110
- var method = source.method.toLowerCase();
111
- var oas = new oas_1["default"](opts.apiDefinition);
112
- var apiDefinition = oas.getDefinition();
113
- var foundOperation = oas.findOperation(url, method);
114
- if (!foundOperation) {
115
- throw new Error("Unable to locate a matching operation in the supplied `apiDefinition` for: ".concat(source.method, " ").concat(url));
116
- }
117
- var operationSlugs = foundOperation.url.slugs;
118
- var operation = oas.operation(foundOperation.url.nonNormalizedPath, method);
119
- var operationPathParameters = operation.getParameters().filter(function (param) { return param["in"] === 'path'; });
120
- var path = operation.path;
121
- var authData = [];
122
- var authSources = getAuthSources(operation);
123
- var _b = new code_builder_1.CodeBuilder({ indent: opts.indent || ' ' }), blank = _b.blank, push = _b.push, join = _b.join;
124
- push("const sdk = require('api')('".concat(opts.apiDefinitionUri, "');"));
125
- blank();
126
- // If we have multiple servers configured and our source URL differs from the stock URL that we
127
- // receive from our `oas` library then the URL either has server variables contained in it (that
128
- // don't match the defaults), or the OAS offers alternate server URLs and we should expose that
129
- // in the generated snippet.
130
- var configData = [];
131
- if ((apiDefinition.servers || []).length > 1) {
132
- var stockUrl = oas.url();
133
- var baseUrl = url.replace(path, '');
134
- if (baseUrl !== stockUrl) {
135
- var serverVars = oas.splitVariables(baseUrl);
136
- var serverUrl = serverVars ? oas.url(serverVars.selected, serverVars.variables) : baseUrl;
137
- configData.push("sdk.server('".concat(serverUrl, "');"));
138
- }
139
- }
140
- var metadata = {};
141
- Object.keys(queryObj).forEach(function (param) {
142
- if (authSources.query.includes(param)) {
143
- authData.push(buildAuthSnippet(queryObj[param]));
144
- // If this query param is part of an auth source then we don't want it doubled up in the
145
- // snippet.
146
- return;
147
- }
148
- metadata[param] = queryObj[param];
149
- });
150
- Object.keys(cookiesObj).forEach(function (cookie) {
151
- if (authSources.cookie.includes(cookie)) {
152
- authData.push(buildAuthSnippet(cookiesObj[cookie]));
153
- // If this cookie is part of an auth source then we don't want it doubled up.
154
- return;
155
- }
156
- // Note that we may have the potential to overlap any cookie that also shares the name as
157
- // another metadata parameter. This problem is currently inherent to `api` and not this
158
- // snippet generator.
159
- metadata[cookie] = cookiesObj[cookie];
160
- });
161
- // If we have path parameters present we should add them into the metadata object.
162
- Array.from(Object.entries(operationSlugs)).forEach(function (_a) {
163
- var param = _a[0], value = _a[1];
164
- // The keys in `operationSlugs` will always be prefixed with a `:` in the `oas` library so
165
- // we can safely do this substring here without asserting this context.
166
- var cleanedParam = param.substring(1);
167
- // If our incoming path slug out of `oas.findOperation()` has been sanitized and is missing
168
- // a hyphen, but there is a parameter in the OpenAPI definition that matches what our
169
- // hyphen-less slug is then we should use that for the snippet.
170
- var unsanitizedParam = operationPathParameters.find(function (p) {
171
- return p.name.includes('-') && p.name.replace(/-/g, '') === cleanedParam ? p.name : false;
172
- });
173
- if (unsanitizedParam) {
174
- metadata[unsanitizedParam.name] = value;
175
- }
176
- else {
177
- metadata[cleanedParam] = value;
178
- }
179
- });
180
- if (Object.keys(headersObj).length) {
181
- var headers_1 = headersObj;
182
- var requestHeaders_1 = {};
183
- Object.keys(headers_1).forEach(function (header) {
184
- // Headers in HTTPSnippet are case-insensitive so we need to add in some special handling to
185
- // make sure we're able to match them properly.
186
- var headerLower = header.toLowerCase();
187
- if (headerLower in authSources.header) {
188
- // If this header has been set up as an authentication header, let's remove it and add it
189
- // into our auth data so we can build up an `.auth()` snippet for the SDK.
190
- var authScheme = authSources.header[headerLower];
191
- if (authScheme === '*') {
192
- authData.push(buildAuthSnippet(headers_1[header]));
193
- }
194
- else {
195
- // @ts-expect-error `headers[header]` is typed improperly in HTTPSnippet.
196
- var authKey = headers_1[header].replace("".concat(authSources.header[headerLower], " "), '');
197
- if (authScheme.toLowerCase() === 'basic') {
198
- authKey = Buffer.from(authKey, 'base64').toString('ascii');
199
- authKey = authKey.split(':');
200
- }
201
- authData.push(buildAuthSnippet(authKey));
202
- }
203
- delete headers_1[header];
204
- return;
205
- }
206
- else if (headerLower === 'content-type') {
207
- // `Content-Type` headers are automatically added within the SDK so we can filter them out
208
- // if they don't have parameters attached to them.
209
- // @ts-expect-error `headers[header]` is typed improperly in HTTPSnippet.
210
- var parsedContentType = content_type_1["default"].parse(headers_1[header]);
211
- if (!Object.keys(parsedContentType.parameters).length) {
212
- delete headers_1[header];
213
- return;
214
- }
215
- }
216
- else if (headerLower === 'accept') {
217
- // If the `Accept` header here is not the default or first `Accept` header for the
218
- // operations' request body then we should add it otherwise we can let the SDK handle it
219
- // itself.
220
- if (headers_1[header] === operation.getContentType()) {
221
- delete headers_1[header];
222
- return;
223
- }
224
- }
225
- // If we haven't used our header anywhere else, or we've deleted it from the payload
226
- // because it'll be handled internally by `api` then we should add the lowercased version
227
- // of our header into the generated code snippet.
228
- requestHeaders_1[headerLower] = headers_1[header];
229
- });
230
- if (Object.keys(requestHeaders_1).length > 0) {
231
- metadata = Object.assign(metadata, requestHeaders_1);
50
+ }
51
+ });
52
+ });
53
+ return matchers;
54
+ }
55
+ var client = {
56
+ info: {
57
+ key: "api",
58
+ title: "API",
59
+ link: "https://npm.im/api",
60
+ description: "Automatic SDK generation from an OpenAPI definition.",
61
+ extname: ".js"
62
+ },
63
+ convert: ({ cookiesObj, headersObj, postData, queryObj, url, ...source }, options) => {
64
+ const opts = {
65
+ ...options
66
+ };
67
+ if (!("apiDefinitionUri" in opts)) {
68
+ throw new Error("This HTTP Snippet client must have an `apiDefinitionUri` option supplied to it.");
69
+ } else if (!("apiDefinition" in opts)) {
70
+ throw new Error("This HTTP Snippet client must have an `apiDefinition` option supplied to it.");
71
+ }
72
+ const method = source.method.toLowerCase();
73
+ const oas = new Oas(opts.apiDefinition);
74
+ const apiDefinition = oas.getDefinition();
75
+ const foundOperation = oas.findOperation(url, method);
76
+ if (!foundOperation) {
77
+ throw new Error(
78
+ `Unable to locate a matching operation in the supplied \`apiDefinition\` for: ${source.method} ${url}`
79
+ );
80
+ }
81
+ const operationSlugs = foundOperation.url.slugs;
82
+ const operation = oas.operation(foundOperation.url.nonNormalizedPath, method);
83
+ const operationPathParameters = operation.getParameters().filter((param) => param.in === "path");
84
+ const path = operation.path;
85
+ const authData = [];
86
+ const authSources = getAuthSources(operation);
87
+ const { blank, push, join } = new CodeBuilder({ indent: opts.indent || " " });
88
+ push(`const sdk = require('api')('${opts.apiDefinitionUri}');`);
89
+ blank();
90
+ const configData = [];
91
+ if ((apiDefinition.servers || []).length > 1) {
92
+ const stockUrl = oas.url();
93
+ const baseUrl = url.replace(path, "");
94
+ if (baseUrl !== stockUrl) {
95
+ const serverVars = oas.splitVariables(baseUrl);
96
+ const serverUrl = serverVars ? oas.url(serverVars.selected, serverVars.variables) : baseUrl;
97
+ configData.push(`sdk.server('${serverUrl}');`);
98
+ }
99
+ }
100
+ let metadata = {};
101
+ Object.keys(queryObj).forEach((param) => {
102
+ if (authSources.query.includes(param)) {
103
+ authData.push(buildAuthSnippet(queryObj[param]));
104
+ return;
105
+ }
106
+ metadata[param] = queryObj[param];
107
+ });
108
+ Object.keys(cookiesObj).forEach((cookie) => {
109
+ if (authSources.cookie.includes(cookie)) {
110
+ authData.push(buildAuthSnippet(cookiesObj[cookie]));
111
+ return;
112
+ }
113
+ metadata[cookie] = cookiesObj[cookie];
114
+ });
115
+ Array.from(Object.entries(operationSlugs)).forEach(([param, value]) => {
116
+ const cleanedParam = param.substring(1);
117
+ const unsanitizedParam = operationPathParameters.find((p) => {
118
+ return p.name.includes("-") && p.name.replace(/-/g, "") === cleanedParam ? p.name : false;
119
+ });
120
+ if (unsanitizedParam) {
121
+ metadata[unsanitizedParam.name] = value;
122
+ } else {
123
+ metadata[cleanedParam] = value;
124
+ }
125
+ });
126
+ if (Object.keys(headersObj).length) {
127
+ const headers = headersObj;
128
+ const requestHeaders = {};
129
+ Object.keys(headers).forEach((header) => {
130
+ const headerLower = header.toLowerCase();
131
+ if (headerLower in authSources.header) {
132
+ const authScheme = authSources.header[headerLower];
133
+ if (authScheme === "*") {
134
+ authData.push(buildAuthSnippet(headers[header]));
135
+ } else {
136
+ let authKey = headers[header].replace(`${authSources.header[headerLower]} `, "");
137
+ if (authScheme.toLowerCase() === "basic") {
138
+ authKey = Buffer.from(authKey, "base64").toString("ascii");
139
+ authKey = authKey.split(":");
232
140
  }
141
+ authData.push(buildAuthSnippet(authKey));
142
+ }
143
+ delete headers[header];
144
+ return;
145
+ } else if (headerLower === "content-type") {
146
+ const parsedContentType = contentType.parse(headers[header]);
147
+ if (!Object.keys(parsedContentType.parameters).length) {
148
+ delete headers[header];
149
+ return;
150
+ }
151
+ } else if (headerLower === "accept") {
152
+ if (matchesMimeType.json(headers[header])) {
153
+ delete headers[header];
154
+ return;
155
+ }
233
156
  }
234
- var body;
235
- switch (postData.mimeType) {
236
- case 'application/x-www-form-urlencoded':
237
- body = postData.paramsObj;
238
- break;
239
- case 'application/json':
240
- if (postData.jsonObj) {
241
- body = postData.jsonObj;
242
- }
243
- break;
244
- case 'multipart/form-data':
245
- if (postData.params) {
246
- body = {};
247
- // If there's a `Content-Type` header present in the metadata, but it's for the
248
- // `multipart/form-data` request then dump it off the snippet. We shouldn't offload that
249
- // unnecessary bloat of multipart boundaries to the user, instead letting the SDK handle it
250
- // automatically.
251
- if ('content-type' in metadata && metadata['content-type'].indexOf('multipart/form-data') === 0) {
252
- delete metadata['content-type'];
253
- }
254
- postData.params.forEach(function (param) {
255
- if (param.fileName) {
256
- body[param.name] = param.fileName;
257
- }
258
- else {
259
- body[param.name] = param.value;
260
- }
261
- });
262
- }
263
- break;
264
- default:
265
- if (postData.text) {
266
- body = postData.text;
267
- }
268
- }
269
- var args = [];
270
- var accessor = operation.getOperationId({ camelCase: true });
271
- // If we're going to be rendering out body params and metadata we should cut their character
272
- // limit in half because we'll be rendering them in their own lines.
273
- var inlineCharacterLimit = typeof body !== 'undefined' && Object.keys(metadata).length > 0 ? 40 : 80;
274
- if (typeof body !== 'undefined') {
275
- args.push(stringify(body, { inlineCharacterLimit: inlineCharacterLimit }));
276
- }
277
- if (Object.keys(metadata).length > 0) {
278
- args.push(stringify(metadata, { inlineCharacterLimit: inlineCharacterLimit }));
157
+ requestHeaders[headerLower] = headers[header];
158
+ });
159
+ if (Object.keys(requestHeaders).length > 0) {
160
+ metadata = Object.assign(metadata, requestHeaders);
161
+ }
162
+ }
163
+ let body;
164
+ switch (postData.mimeType) {
165
+ case "application/x-www-form-urlencoded":
166
+ body = postData.paramsObj;
167
+ break;
168
+ case "application/json":
169
+ if (postData.jsonObj) {
170
+ body = postData.jsonObj;
279
171
  }
280
- if (authData.length) {
281
- push(authData.join('\n'));
172
+ break;
173
+ case "multipart/form-data":
174
+ if (postData.params) {
175
+ body = {};
176
+ if ("content-type" in metadata && metadata["content-type"].indexOf("multipart/form-data") === 0) {
177
+ delete metadata["content-type"];
178
+ }
179
+ postData.params.forEach((param) => {
180
+ if (param.fileName) {
181
+ body[param.name] = param.fileName;
182
+ } else {
183
+ body[param.name] = param.value;
184
+ }
185
+ });
282
186
  }
283
- if (configData.length) {
284
- push(configData.join('\n'));
187
+ break;
188
+ default:
189
+ if (postData.text) {
190
+ body = postData.text;
285
191
  }
286
- push("sdk.".concat(accessor, "(").concat(args.join(', '), ")"));
287
- push('.then(({ data }) => console.log(data))', 1);
288
- push('.catch(err => console.error(err));', 1);
289
- return join();
290
192
  }
193
+ const args = [];
194
+ const accessor = operation.getOperationId({ camelCase: true });
195
+ const inlineCharacterLimit = typeof body !== "undefined" && Object.keys(metadata).length > 0 ? 40 : 80;
196
+ if (typeof body !== "undefined") {
197
+ args.push(stringify(body, { inlineCharacterLimit }));
198
+ }
199
+ if (Object.keys(metadata).length > 0) {
200
+ args.push(stringify(metadata, { inlineCharacterLimit }));
201
+ }
202
+ if (authData.length) {
203
+ push(authData.join("\n"));
204
+ }
205
+ if (configData.length) {
206
+ push(configData.join("\n"));
207
+ }
208
+ push(`sdk.${accessor}(${args.join(", ")})`);
209
+ push(".then(({ data }) => console.log(data))", 1);
210
+ push(".catch(err => console.error(err));", 1);
211
+ return join();
212
+ }
213
+ };
214
+ var src_default = client;
215
+ export {
216
+ src_default as default
291
217
  };
292
- exports["default"] = client;
218
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { ReducedHelperObject } from '@readme/httpsnippet/helpers/reducer';\nimport type { Client } from '@readme/httpsnippet/targets';\nimport type Operation from 'oas/operation';\nimport type { HttpMethods, OASDocument } from 'oas/rmoas.types';\n\nimport { CodeBuilder } from '@readme/httpsnippet/helpers/code-builder';\nimport contentType from 'content-type';\nimport Oas from 'oas';\nimport { matchesMimeType } from 'oas/utils';\nimport stringifyObject from 'stringify-object';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction stringify(obj: any, opts = {}) {\n return stringifyObject(obj, { indent: ' ', ...opts });\n}\n\nfunction buildAuthSnippet(authKey: string | string[]) {\n // Auth key will be an array for Basic auth cases.\n if (Array.isArray(authKey)) {\n const auth: string[] = [];\n authKey.forEach((token, i) => {\n // If the token part is the last part of the key and it's empty, don't add it to the snippet.\n if (token.length === 0 && authKey.length > 1 && i === authKey.length - 1) {\n return;\n }\n\n auth.push(`'${token.replace(/'/g, \"\\\\'\")}'`);\n });\n\n return `sdk.auth(${auth.join(', ')});`;\n }\n\n return `sdk.auth('${authKey.replace(/'/g, \"\\\\'\")}');`;\n}\n\nfunction getAuthSources(operation: Operation) {\n const matchers: { cookie: string[]; header: Record<string, string>; query: string[] } = {\n header: {},\n query: [],\n cookie: [],\n };\n\n if (operation.getSecurity().length === 0) {\n return matchers;\n }\n\n Object.entries(operation.prepareSecurity()).forEach(([, schemes]) => {\n schemes.forEach(scheme => {\n if (scheme.type === 'http') {\n if (scheme.scheme === 'basic') {\n matchers.header.authorization = 'Basic';\n } else if (scheme.scheme === 'bearer') {\n matchers.header.authorization = 'Bearer';\n }\n } else if (scheme.type === 'oauth2') {\n matchers.header.authorization = 'Bearer';\n } else if (scheme.type === 'apiKey') {\n if (scheme.in === 'query') {\n matchers.query.push(scheme.name);\n } else if (scheme.in === 'header') {\n // The way that this asterisk header matcher works is that since this `apiKey` goes in a\n // named header (`scheme.name`) because the header is the key, we're matching against the\n // entire header -- counter to the way that the HTTP basic matcher above works where we\n // match and extract the API key from everything after `Basic ` in the `Authorization`\n // header.\n matchers.header[scheme.name.toLowerCase()] = '*';\n } else if (scheme.in === 'cookie') {\n matchers.cookie.push(scheme.name);\n }\n }\n });\n });\n\n return matchers;\n}\n\ninterface APIOptions {\n apiDefinition: OASDocument;\n apiDefinitionUri: string;\n escapeBrackets?: boolean;\n indent?: string | false;\n}\n\nconst client: Client<APIOptions> = {\n info: {\n key: 'api',\n title: 'API',\n link: 'https://npm.im/api',\n description: 'Automatic SDK generation from an OpenAPI definition.',\n extname: '.js',\n },\n convert: ({ cookiesObj, headersObj, postData, queryObj, url, ...source }, options) => {\n const opts = {\n ...options,\n } as APIOptions;\n\n if (!('apiDefinitionUri' in opts)) {\n throw new Error('This HTTP Snippet client must have an `apiDefinitionUri` option supplied to it.');\n } else if (!('apiDefinition' in opts)) {\n throw new Error('This HTTP Snippet client must have an `apiDefinition` option supplied to it.');\n }\n\n const method = source.method.toLowerCase() as HttpMethods;\n const oas = new Oas(opts.apiDefinition);\n const apiDefinition = oas.getDefinition();\n const foundOperation = oas.findOperation(url, method);\n if (!foundOperation) {\n throw new Error(\n `Unable to locate a matching operation in the supplied \\`apiDefinition\\` for: ${source.method} ${url}`,\n );\n }\n\n const operationSlugs = foundOperation.url.slugs;\n const operation = oas.operation(foundOperation.url.nonNormalizedPath, method);\n const operationPathParameters = operation.getParameters().filter(param => param.in === 'path');\n const path = operation.path;\n const authData: string[] = [];\n const authSources = getAuthSources(operation);\n\n const { blank, push, join } = new CodeBuilder({ indent: opts.indent || ' ' });\n\n push(`const sdk = require('api')('${opts.apiDefinitionUri}');`);\n blank();\n\n // If we have multiple servers configured and our source URL differs from the stock URL that we\n // receive from our `oas` library then the URL either has server variables contained in it (that\n // don't match the defaults), or the OAS offers alternate server URLs and we should expose that\n // in the generated snippet.\n const configData = [];\n if ((apiDefinition.servers || []).length > 1) {\n const stockUrl = oas.url();\n const baseUrl = url.replace(path, '');\n if (baseUrl !== stockUrl) {\n const serverVars = oas.splitVariables(baseUrl);\n const serverUrl = serverVars ? oas.url(serverVars.selected, serverVars.variables) : baseUrl;\n\n configData.push(`sdk.server('${serverUrl}');`);\n }\n }\n\n let metadata: Record<string, string | string[]> = {};\n Object.keys(queryObj).forEach(param => {\n if (authSources.query.includes(param)) {\n authData.push(buildAuthSnippet(queryObj[param]));\n\n // If this query param is part of an auth source then we don't want it doubled up in the\n // snippet.\n return;\n }\n\n metadata[param] = queryObj[param];\n });\n\n Object.keys(cookiesObj).forEach(cookie => {\n if (authSources.cookie.includes(cookie)) {\n authData.push(buildAuthSnippet(cookiesObj[cookie]));\n\n // If this cookie is part of an auth source then we don't want it doubled up.\n return;\n }\n\n // Note that we may have the potential to overlap any cookie that also shares the name as\n // another metadata parameter. This problem is currently inherent to `api` and not this\n // snippet generator.\n metadata[cookie] = cookiesObj[cookie];\n });\n\n // If we have path parameters present we should add them into the metadata object.\n Array.from(Object.entries(operationSlugs)).forEach(([param, value]) => {\n // The keys in `operationSlugs` will always be prefixed with a `:` in the `oas` library so\n // we can safely do this substring here without asserting this context.\n const cleanedParam = param.substring(1);\n\n // If our incoming path slug out of `oas.findOperation()` has been sanitized and is missing\n // a hyphen, but there is a parameter in the OpenAPI definition that matches what our\n // hyphen-less slug is then we should use that for the snippet.\n const unsanitizedParam = operationPathParameters.find(p => {\n return p.name.includes('-') && p.name.replace(/-/g, '') === cleanedParam ? p.name : false;\n });\n\n if (unsanitizedParam) {\n metadata[unsanitizedParam.name] = value;\n } else {\n metadata[cleanedParam] = value;\n }\n });\n\n if (Object.keys(headersObj).length) {\n const headers = headersObj;\n const requestHeaders: ReducedHelperObject = {};\n\n Object.keys(headers).forEach(header => {\n // Headers in HTTPSnippet are case-insensitive so we need to add in some special handling to\n // make sure we're able to match them properly.\n const headerLower = header.toLowerCase();\n\n if (headerLower in authSources.header) {\n // If this header has been set up as an authentication header, let's remove it and add it\n // into our auth data so we can build up an `.auth()` snippet for the SDK.\n const authScheme = authSources.header[headerLower];\n if (authScheme === '*') {\n authData.push(buildAuthSnippet(headers[header]));\n } else {\n // @ts-expect-error `headers[header]` is typed improperly in HTTPSnippet.\n let authKey = headers[header].replace(`${authSources.header[headerLower]} `, '');\n if (authScheme.toLowerCase() === 'basic') {\n authKey = Buffer.from(authKey, 'base64').toString('ascii');\n authKey = authKey.split(':');\n }\n\n authData.push(buildAuthSnippet(authKey));\n }\n\n delete headers[header];\n return;\n } else if (headerLower === 'content-type') {\n // `Content-Type` headers are automatically added within the SDK so we can filter them out\n // if they don't have parameters attached to them.\n // @ts-expect-error `headers[header]` is typed improperly in HTTPSnippet.\n const parsedContentType = contentType.parse(headers[header]);\n if (!Object.keys(parsedContentType.parameters).length) {\n delete headers[header];\n return;\n }\n } else if (headerLower === 'accept') {\n // If the `Accept` header here is JSON-like header then we can remove it from the code\n // snippet because `api` natively supports and prioritizes JSON over any other mime type.\n if (matchesMimeType.json(headers[header] as string)) {\n delete headers[header];\n return;\n }\n }\n\n // If we haven't used our header anywhere else, or we've deleted it from the payload\n // because it'll be handled internally by `api` then we should add the lowercased version\n // of our header into the generated code snippet.\n requestHeaders[headerLower] = headers[header];\n });\n\n if (Object.keys(requestHeaders).length > 0) {\n metadata = Object.assign(metadata, requestHeaders);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let body: any;\n switch (postData.mimeType) {\n case 'application/x-www-form-urlencoded':\n body = postData.paramsObj;\n break;\n\n case 'application/json':\n if (postData.jsonObj) {\n body = postData.jsonObj;\n }\n break;\n\n case 'multipart/form-data':\n if (postData.params) {\n body = {};\n\n // If there's a `Content-Type` header present in the metadata, but it's for the\n // `multipart/form-data` request then dump it off the snippet. We shouldn't offload that\n // unnecessary bloat of multipart boundaries to the user, instead letting the SDK handle it\n // automatically.\n if ('content-type' in metadata && metadata['content-type'].indexOf('multipart/form-data') === 0) {\n delete metadata['content-type'];\n }\n\n postData.params.forEach(param => {\n if (param.fileName) {\n body[param.name] = param.fileName;\n } else {\n body[param.name] = param.value;\n }\n });\n }\n break;\n\n default:\n if (postData.text) {\n body = postData.text;\n }\n }\n\n const args = [];\n\n const accessor = operation.getOperationId({ camelCase: true });\n\n // If we're going to be rendering out body params and metadata we should cut their character\n // limit in half because we'll be rendering them in their own lines.\n const inlineCharacterLimit = typeof body !== 'undefined' && Object.keys(metadata).length > 0 ? 40 : 80;\n if (typeof body !== 'undefined') {\n args.push(stringify(body, { inlineCharacterLimit }));\n }\n\n if (Object.keys(metadata).length > 0) {\n args.push(stringify(metadata, { inlineCharacterLimit }));\n }\n\n if (authData.length) {\n push(authData.join('\\n'));\n }\n\n if (configData.length) {\n push(configData.join('\\n'));\n }\n\n push(`sdk.${accessor}(${args.join(', ')})`);\n push('.then(({ data }) => console.log(data))', 1);\n push('.catch(err => console.error(err));', 1);\n\n return join();\n },\n};\n\nexport default client;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,14 +1,26 @@
1
1
  {
2
2
  "name": "httpsnippet-client-api",
3
- "version": "6.1.0",
3
+ "version": "7.0.0-alpha.4",
4
4
  "description": "An HTTPSnippet client for generating snippets for the `api` module.",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
5
+ "sideEffects": false,
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/index.cjs",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "main": "dist/index.cjs",
15
+ "module": "dist/index.js",
16
+ "types": "dist/index.d.cts",
7
17
  "scripts": {
8
- "build": "tsc",
18
+ "attw": "attw --pack --format table-flipped",
19
+ "build": "tsup",
20
+ "lint:types": "tsc --noEmit",
9
21
  "prebuild": "rm -rf dist/",
10
22
  "prepack": "npm run build",
11
- "test": "jest --coverage"
23
+ "test": "vitest run --coverage"
12
24
  },
13
25
  "repository": {
14
26
  "type": "git",
@@ -21,29 +33,25 @@
21
33
  "author": "Jon Ursenbach <jon@readme.io>",
22
34
  "license": "MIT",
23
35
  "engines": {
24
- "node": ">=16"
36
+ "node": ">=18"
25
37
  },
26
38
  "dependencies": {
27
39
  "content-type": "^1.0.5",
28
40
  "stringify-object": "^3.3.0"
29
41
  },
30
42
  "peerDependencies": {
31
- "@readme/httpsnippet": ">=4",
32
- "oas": "^20.0.0"
43
+ "@readme/httpsnippet": ">=8",
44
+ "oas": "^23.0.0"
33
45
  },
34
46
  "devDependencies": {
35
- "@readme/oas-examples": "^5.9.0",
36
- "@readme/openapi-parser": "^2.4.0",
37
- "@types/content-type": "^1.1.5",
38
- "@types/jest": "^29.5.2",
39
- "@types/stringify-object": "^4.0.2",
40
- "api": "^6.1.0",
41
- "fetch-mock": "^9.11.0",
42
- "isomorphic-fetch": "^3.0.0",
43
- "jest": "^29.6.1",
44
- "ts-jest": "^29.1.1",
45
- "typescript": "^4.9.5"
47
+ "@readme/oas-examples": "^5.12.0",
48
+ "@readme/openapi-parser": "^2.5.0",
49
+ "@types/content-type": "^1.1.6",
50
+ "@types/stringify-object": "^3.3.1",
51
+ "@vitest/coverage-v8": "^0.34.4",
52
+ "typescript": "^5.2.2",
53
+ "vitest": "^0.34.5"
46
54
  },
47
55
  "prettier": "@readme/eslint-config/prettier",
48
- "gitHead": "244093f2ad00e5ea527420f49bdb541bc2095806"
56
+ "gitHead": "9f45fd21218af94f457250ac67aad71ad06942bf"
49
57
  }
package/src/index.ts CHANGED
@@ -1,16 +1,15 @@
1
- import type { ReducedHelperObject } from '@readme/httpsnippet/dist/helpers/reducer';
2
- import type { Client } from '@readme/httpsnippet/dist/targets/targets';
3
- import type { Operation } from 'oas';
4
- import type { HttpMethods, OASDocument } from 'oas/dist/rmoas.types';
1
+ import type { ReducedHelperObject } from '@readme/httpsnippet/helpers/reducer';
2
+ import type { Client } from '@readme/httpsnippet/targets';
3
+ import type Operation from 'oas/operation';
4
+ import type { HttpMethods, OASDocument } from 'oas/rmoas.types';
5
5
 
6
- import { CodeBuilder } from '@readme/httpsnippet/dist/helpers/code-builder';
6
+ import { CodeBuilder } from '@readme/httpsnippet/helpers/code-builder';
7
7
  import contentType from 'content-type';
8
8
  import Oas from 'oas';
9
+ import { matchesMimeType } from 'oas/utils';
9
10
  import stringifyObject from 'stringify-object';
10
11
 
11
- // This should really be an exported type in `oas`.
12
- type SecurityType = 'Basic' | 'Bearer' | 'Query' | 'Header' | 'Cookie' | 'OAuth2' | 'http' | 'apiKey';
13
-
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
13
  function stringify(obj: any, opts = {}) {
15
14
  return stringifyObject(obj, { indent: ' ', ...opts });
16
15
  }
@@ -45,9 +44,8 @@ function getAuthSources(operation: Operation) {
45
44
  return matchers;
46
45
  }
47
46
 
48
- const security = operation.prepareSecurity();
49
- Object.keys(security).forEach((id: SecurityType) => {
50
- security[id].forEach(scheme => {
47
+ Object.entries(operation.prepareSecurity()).forEach(([, schemes]) => {
48
+ schemes.forEach(scheme => {
51
49
  if (scheme.type === 'http') {
52
50
  if (scheme.scheme === 'basic') {
53
51
  matchers.header.authorization = 'Basic';
@@ -76,7 +74,7 @@ function getAuthSources(operation: Operation) {
76
74
  return matchers;
77
75
  }
78
76
 
79
- export interface APIOptions {
77
+ interface APIOptions {
80
78
  apiDefinition: OASDocument;
81
79
  apiDefinitionUri: string;
82
80
  escapeBrackets?: boolean;
@@ -89,11 +87,12 @@ const client: Client<APIOptions> = {
89
87
  title: 'API',
90
88
  link: 'https://npm.im/api',
91
89
  description: 'Automatic SDK generation from an OpenAPI definition.',
90
+ extname: '.js',
92
91
  },
93
92
  convert: ({ cookiesObj, headersObj, postData, queryObj, url, ...source }, options) => {
94
93
  const opts = {
95
94
  ...options,
96
- };
95
+ } as APIOptions;
97
96
 
98
97
  if (!('apiDefinitionUri' in opts)) {
99
98
  throw new Error('This HTTP Snippet client must have an `apiDefinitionUri` option supplied to it.');
@@ -107,7 +106,7 @@ const client: Client<APIOptions> = {
107
106
  const foundOperation = oas.findOperation(url, method);
108
107
  if (!foundOperation) {
109
108
  throw new Error(
110
- `Unable to locate a matching operation in the supplied \`apiDefinition\` for: ${source.method} ${url}`
109
+ `Unable to locate a matching operation in the supplied \`apiDefinition\` for: ${source.method} ${url}`,
111
110
  );
112
111
  }
113
112
 
@@ -224,10 +223,9 @@ const client: Client<APIOptions> = {
224
223
  return;
225
224
  }
226
225
  } else if (headerLower === 'accept') {
227
- // If the `Accept` header here is not the default or first `Accept` header for the
228
- // operations' request body then we should add it otherwise we can let the SDK handle it
229
- // itself.
230
- if (headers[header] === operation.getContentType()) {
226
+ // If the `Accept` header here is JSON-like header then we can remove it from the code
227
+ // snippet because `api` natively supports and prioritizes JSON over any other mime type.
228
+ if (matchesMimeType.json(headers[header] as string)) {
231
229
  delete headers[header];
232
230
  return;
233
231
  }
@@ -244,6 +242,7 @@ const client: Client<APIOptions> = {
244
242
  }
245
243
  }
246
244
 
245
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
247
246
  let body: any;
248
247
  switch (postData.mimeType) {
249
248
  case 'application/x-www-form-urlencoded':
package/tsconfig.json CHANGED
@@ -1,11 +1,7 @@
1
1
  {
2
+ "extends": "../../tsconfig.base.json",
2
3
  "compilerOptions": {
3
- "allowJs": true,
4
- "baseUrl": "./src",
5
- "declaration": true,
6
- "esModuleInterop": true,
7
- "lib": ["dom", "es2020"],
8
- "noImplicitAny": true,
4
+ "lib": ["DOM", "ES2023"],
9
5
  "outDir": "dist/"
10
6
  },
11
7
  "include": ["./src/**/*"]
package/tsup.config.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { Options } from 'tsup';
2
+
3
+ // eslint-disable-next-line import/no-extraneous-dependencies
4
+ import { defineConfig } from 'tsup';
5
+
6
+ // eslint-disable-next-line import/no-relative-packages
7
+ import config from '../../tsup.config.js';
8
+
9
+ export default defineConfig((options: Options) => ({
10
+ ...options,
11
+ ...config,
12
+
13
+ entry: ['src/index.ts'],
14
+ silent: !options.watch,
15
+ }));