httpsnippet-client-api 6.1.1 → 7.0.0-alpha.5

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