@readme/oas-to-har 16.1.0 → 17.0.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,35 @@
1
+ ## <small>17.0.2 (2022-04-25)</small>
2
+
3
+ * test: adding some clearer test coverage for raw string payloads ([a47ebc1](https://github.com/readmeio/oas-to-har/commit/a47ebc1))
4
+ * feat: adding support for RAW_BODY payloads when not on JSON-like content types (#81) ([ca2d395](https://github.com/readmeio/oas-to-har/commit/ca2d395)), closes [#81](https://github.com/readmeio/oas-to-har/issues/81)
5
+
6
+
7
+
8
+ ## <small>17.0.1 (2022-04-22)</small>
9
+
10
+ * fix: issue where a lowercase `Accept` header may be duplicated (#80) ([cb9e1b4](https://github.com/readmeio/oas-to-har/commit/cb9e1b4)), closes [#80](https://github.com/readmeio/oas-to-har/issues/80)
11
+
12
+
13
+
14
+ ## 17.0.0 (2022-04-22)
15
+
16
+ > **BREAKING RELEASE**
17
+ >
18
+ > Node 12 is no longer supported.
19
+
20
+ * chore: adding some more exclusions into the npmignore list ([25b3121](https://github.com/readmeio/oas-to-har/commit/25b3121))
21
+ * chore(deps-dev): bump @readme/oas-examples from 4.5.0 to 5.0.0 (#75) ([845535b](https://github.com/readmeio/oas-to-har/commit/845535b)), closes [#75](https://github.com/readmeio/oas-to-har/issues/75)
22
+ * chore(deps-dev): bump eslint from 8.11.0 to 8.12.0 (#73) ([c26d37c](https://github.com/readmeio/oas-to-har/commit/c26d37c)), closes [#73](https://github.com/readmeio/oas-to-har/issues/73)
23
+ * chore(deps-dev): bump prettier from 2.6.0 to 2.6.1 (#72) ([24613fc](https://github.com/readmeio/oas-to-har/commit/24613fc)), closes [#72](https://github.com/readmeio/oas-to-har/issues/72)
24
+ * chore(deps): bump actions/checkout from 2.4.0 to 3 (#71) ([38c66a7](https://github.com/readmeio/oas-to-har/commit/38c66a7)), closes [#71](https://github.com/readmeio/oas-to-har/issues/71)
25
+ * chore(deps): bump oas from 18.0.6 to 18.1.0 (#74) ([93c2bc4](https://github.com/readmeio/oas-to-har/commit/93c2bc4)), closes [#74](https://github.com/readmeio/oas-to-har/issues/74)
26
+ * fix: code cleanup (#79) ([245dcbb](https://github.com/readmeio/oas-to-har/commit/245dcbb)), closes [#79](https://github.com/readmeio/oas-to-har/issues/79)
27
+ * fix: support for allowReserved (#76) ([f609297](https://github.com/readmeio/oas-to-har/commit/f609297)), closes [#76](https://github.com/readmeio/oas-to-har/issues/76)
28
+ * fix: support for multi-level deepObjects (#77) ([b287f68](https://github.com/readmeio/oas-to-har/commit/b287f68)), closes [#77](https://github.com/readmeio/oas-to-har/issues/77)
29
+ * feat: browser testing! (#78) ([5ebe4d4](https://github.com/readmeio/oas-to-har/commit/5ebe4d4)), closes [#78](https://github.com/readmeio/oas-to-har/issues/78)
30
+
31
+
32
+
1
33
  ## 16.1.0 (2022-03-23)
2
34
 
3
35
  * chore: removing some docs from the repo as they're in our .github/ repo now ([f3d81c3](https://github.com/readmeio/oas-to-har/commit/f3d81c3))
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2021 ReadMe
1
+ Copyright © 2022 ReadMe
2
2
 
3
3
  Permission to use, copy, modify, and/or distribute this software for any purpose
4
4
  with or without fee is hereby granted, provided that the above copyright notice
package/README.md CHANGED
@@ -15,14 +15,37 @@ npm install --save @readme/oas-to-har
15
15
  ## Usage
16
16
 
17
17
  ```js
18
- const Oas = require('oas');
18
+ const Oas = require('oas').default;
19
19
  const oasToHar = require('@readme/oas-to-har');
20
20
 
21
- const spec = new Oas('petstore.json');
22
- console.log(oasToHar(spec, { path: '/pets', method: 'post'}));
21
+ const petstore = require('./petstore.json');
22
+
23
+ const spec = new Oas(petstore);
24
+ console.log(oasToHar(spec, spec.operation('/pets', 'post')));
25
+ ```
26
+
27
+ ```json
28
+ {
29
+ log: {
30
+ entries: [
31
+ {
32
+ request: {
33
+ cookies: [],
34
+ headers: [],
35
+ headersSize: 0,
36
+ queryString: [],
37
+ bodySize: 0,
38
+ method: 'POST',
39
+ url: 'http://petstore.swagger.io/v2/pets',
40
+ httpVersion: 'HTTP/1.1'
41
+ }
42
+ }
43
+ ]
44
+ }
45
+ }
23
46
  ```
24
47
 
25
- ### `oasToHar(har, operationSchema, values, auth, opts) => Object`
48
+ ### `oasToHar(oas, operationSchema, values, auth, opts) => Object`
26
49
 
27
50
  - `oas` *{Oas}*: Instance of our [oas/tooling](https://npm.im/oas) class.
28
51
  - `operationSchema` *{Object\|Operation}*: Can either be an object with `path` and `method` properties (that exist in the supplied OAS), or an instance of our `Operation` class from [oas/tooling](https://npm.im/oas) - accessed through `new Oas(spec).operation(path, method)`.
@@ -36,10 +59,3 @@ console.log(oasToHar(spec, { path: '/pets', method: 'post'}));
36
59
  - `server` &mdash; If the supplied OAS has multiple severs or server variables you can use this to set which server and variables to use. Shape of it should be: `{ selected: Integer, variables: { ...key-values }}`. `selected` should coorespond to index of the `servers` array in your OAS.
37
60
  - `auth` *{Object}*: Authentication information for the request.
38
61
  - `opts.proxyUrl` *{Boolean}*: Boolean to toggle if composed HAR objects should have their `url` be sent through our CORS-friendly proxy. Defaults to `false`.
39
-
40
- ## Credits
41
- [Jon Ursenbach](https://github.com/erunion)
42
-
43
- ## License
44
-
45
- ISC
package/karma.conf.js ADDED
@@ -0,0 +1,14 @@
1
+ const { karmaConfig } = require('@jsdevtools/karma-config');
2
+ const { host } = require('@jsdevtools/host-environment');
3
+
4
+ module.exports = karmaConfig({
5
+ sourceDir: '.',
6
+ fixtures: ['test/__datasets__/*.json'],
7
+ browsers: {
8
+ chrome: true,
9
+ firefox: true,
10
+ safari: host.os.mac,
11
+ edge: false,
12
+ ie: false,
13
+ },
14
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@readme/oas-to-har",
3
3
  "description": "Utility to transform an OAS operation into a HAR representation",
4
- "version": "16.1.0",
4
+ "version": "17.0.2",
5
5
  "main": "src/index.js",
6
6
  "author": "Jon Ursenbach <jon@ursenba.ch>",
7
7
  "license": "ISC",
@@ -10,34 +10,39 @@
10
10
  "url": "git://github.com/readmeio/oas-to-har.git"
11
11
  },
12
12
  "engines": {
13
- "node": "^12 || ^14 || ^16"
13
+ "node": ">=14"
14
14
  },
15
15
  "scripts": {
16
16
  "lint": "eslint .",
17
- "lint:docs": "alex . .github/",
18
17
  "prepare": "husky install",
19
- "pretest": "npm run lint && npm run lint:docs",
18
+ "pretest": "npm run lint",
20
19
  "prettier": "prettier --list-different --write \"./**/**.{js}\"",
21
20
  "release": "npx conventional-changelog-cli -i CHANGELOG.md -s && git add CHANGELOG.md",
22
- "test": "jest --coverage"
21
+ "test:browser": "karma start --single-run",
22
+ "test:browser:chrome": "karma start --browsers=Chrome --single-run=false",
23
+ "test:browser:debug": "karma start --single-run=false",
24
+ "test": "nyc mocha"
23
25
  },
24
26
  "dependencies": {
25
27
  "@readme/oas-extensions": "^14.2.0",
26
28
  "oas": "^18.0.6",
27
29
  "parse-data-url": "^4.0.1",
30
+ "qs": "^6.10.3",
28
31
  "remove-undefined-objects": "^1.1.0"
29
32
  },
30
33
  "devDependencies": {
31
34
  "@commitlint/cli": "^16.2.3",
32
35
  "@commitlint/config-conventional": "^16.0.0",
36
+ "@jsdevtools/host-environment": "^2.1.2",
37
+ "@jsdevtools/karma-config": "^3.1.7",
33
38
  "@readme/eslint-config": "^8.5.1",
34
- "@readme/oas-examples": "^4.5.0",
35
- "alex": "^10.0.0",
36
- "datauri": "^4.1.0",
39
+ "@readme/oas-examples": "^5.1.1",
40
+ "chai": "^4.3.6",
37
41
  "eslint": "^8.11.0",
42
+ "har-validator": "^5.1.5",
38
43
  "husky": "^7.0.4",
39
- "jest": "^27.4.7",
40
- "jest-expect-har": "^3.0.1",
44
+ "mocha": "^9.2.2",
45
+ "nyc": "^15.1.0",
41
46
  "prettier": "^2.6.0"
42
47
  },
43
48
  "prettier": "@readme/eslint-config/prettier",
package/src/index.js CHANGED
@@ -11,8 +11,8 @@ const { jsonSchemaTypes } = utils;
11
11
  function formatter(values, param, type, onlyIfExists) {
12
12
  if (param.style) {
13
13
  const value = values[type][param.name];
14
- // Note: Technically we could send everything through the format style and choose the proper default for each
15
- // `in` type (e.g. query defaults to form).
14
+ // Note: Technically we could send everything through the format style and choose the proper
15
+ // default for each `in` type (e.g. query defaults to form).
16
16
  return formatStyle(value, param);
17
17
  }
18
18
 
@@ -26,18 +26,20 @@ function formatter(values, param, type, onlyIfExists) {
26
26
  } else if (param.required && param.schema && param.schema.default) {
27
27
  value = param.schema.default;
28
28
  } else if (type === 'path') {
29
- // If we don't have any values for the path parameter, just use the name of the parameter as the value so we don't
30
- // try try to build a URL to something like `https://example.com/undefined`.
29
+ // If we don't have any values for the path parameter, just use the name of the parameter as the
30
+ // value so we don't try try to build a URL to something like `https://example.com/undefined`.
31
31
  return param.name;
32
32
  }
33
33
 
34
- // Handle file uploads. Specifically arrays of file uploads which need to be formatted very specifically.
34
+ // Handle file uploads. Specifically arrays of file uploads which need to be formatted very
35
+ // specifically.
35
36
  if (param.schema && param.schema.type === 'array' && param.schema.items && param.schema.items.format === 'binary') {
36
37
  return JSON.stringify(value);
37
38
  }
38
39
 
39
40
  if (value !== undefined) {
40
- // Query params should always be formatted, even if they don't have a `style` serialization configured.
41
+ // Query params should always be formatted, even if they don't have a `style` serialization
42
+ // configured.
41
43
  if (type === 'query') {
42
44
  return formatStyle(value, param);
43
45
  }
@@ -77,7 +79,8 @@ function multipartBodyToFormatterParams(multipartBody, oasMediaTypeObject) {
77
79
  .filter(Boolean);
78
80
  }
79
81
 
80
- // Pretty sure that we'll never have anything but an object for multipart bodies, so returning empty array if we get anything else.
82
+ // Pretty sure that we'll never have anything but an object for multipart bodies, so returning
83
+ // empty array if we get anything else.
81
84
  return [];
82
85
  }
83
86
 
@@ -118,12 +121,14 @@ function appendHarValue(harParam, name, value, addtlData = {}) {
118
121
  if (typeof value === 'undefined') return;
119
122
 
120
123
  if (Array.isArray(value)) {
121
- // If the formatter gives us an array, we're expected to add each array value as a new parameter item with the same parameter name
124
+ // If the formatter gives us an array, we're expected to add each array value as a new
125
+ // parameter item with the same parameter name
122
126
  value.forEach(singleValue => {
123
127
  appendHarValue(harParam, name, singleValue);
124
128
  });
125
129
  } else if (typeof value === 'object' && value !== null) {
126
- // If the formatter gives us an object, we're expected to add each property value as a new parameter item, each with the name of the property
130
+ // If the formatter gives us an object, we're expected to add each property value as a new
131
+ // parameter item, each with the name of the property
127
132
  Object.keys(value).forEach(key => {
128
133
  appendHarValue(harParam, key, value[key]);
129
134
  });
@@ -143,8 +148,8 @@ module.exports = (
143
148
  values = {},
144
149
  auth = {},
145
150
  opts = {
146
- // If true, the operation URL will be rewritten and prefixed with https://try.readme.io/ in order to funnel requests
147
- // through our CORS-friendly proxy.
151
+ // If true, the operation URL will be rewritten and prefixed with https://try.readme.io/ in
152
+ // order to funnel requests through our CORS-friendly proxy.
148
153
  proxyUrl: false,
149
154
  }
150
155
  ) => {
@@ -196,7 +201,8 @@ module.exports = (
196
201
  // Find the path parameter or set a default value if it does not exist
197
202
  const parameter = parameters.find(param => param.name === key) || { name: key };
198
203
 
199
- // The library that handles our style processing already encodes uri elements. For everything else we need to handle it here.
204
+ // The library that handles our style processing already encodes uri elements. For everything
205
+ // else we need to handle it here.
200
206
  if (!parameter.style) {
201
207
  return encodeURIComponent(formatter(formData, parameter, 'path'));
202
208
  }
@@ -226,8 +232,9 @@ module.exports = (
226
232
  Object.keys(operation.schema.responses).some(response => {
227
233
  if (!operation.schema.responses[response].content) return false;
228
234
 
229
- // if there is an Accept header specified in the form, we'll use that instead.
230
- if (formData.header.Accept) return true;
235
+ // If there's no `Accept` header present we should add one so their eventual code snippet
236
+ // follows best practices.
237
+ if (Object.keys(formData.header).find(h => h.toLowerCase() === 'accept')) return true;
231
238
 
232
239
  har.headers.push({
233
240
  name: 'Accept',
@@ -272,11 +279,14 @@ module.exports = (
272
279
  }
273
280
 
274
281
  // Do we have an `Accept` header set up in the form, but it hasn't been added yet?
275
- if (formData.header && formData.header.Accept && har.headers.find(hdr => hdr.name === 'Accept') === undefined) {
276
- har.headers.push({
277
- name: 'Accept',
278
- value: String(formData.header.Accept),
279
- });
282
+ if (formData.header) {
283
+ const acceptHeader = Object.keys(formData.header).find(h => h.toLowerCase() === 'accept');
284
+ if (acceptHeader && !har.headers.find(hdr => hdr.name.toLowerCase() === 'accept')) {
285
+ har.headers.push({
286
+ name: 'Accept',
287
+ value: String(formData.header[acceptHeader]),
288
+ });
289
+ }
280
290
  }
281
291
 
282
292
  let requestBody = false;
@@ -316,8 +326,9 @@ module.exports = (
316
326
  har.postData.mimeType = 'multipart/form-data';
317
327
  har.postData.params = [];
318
328
 
319
- // Discover all `{ type: string, format: binary }` properties the schema. If there are any, then that means
320
- // that we're dealing with a `multipart/form-data` request and need to treat the payload as `postData.params`.
329
+ // Discover all `{ type: string, format: binary }` properties the schema. If there are
330
+ // any, then that means that we're dealing with a `multipart/form-data` request and
331
+ // need to treat the payload as `postData.params`.
321
332
  const binaryTypes = Object.keys(requestBody.schema.properties).filter(
322
333
  key => requestBody.schema.properties[key].format === 'binary'
323
334
  );
@@ -333,9 +344,10 @@ module.exports = (
333
344
 
334
345
  const value = formatter(formData, param, 'body', true);
335
346
 
336
- // If we're dealing with a binary type, and the value is a valid data URL we should parse out any
337
- // available filename and content type to send along with the parameter to interpreters like `fetch-har`
338
- // can make sense of it and send a usable payload.
347
+ // If we're dealing with a binary type, and the value is a valid data URL we should
348
+ // parse out any available filename and content type to send along with the
349
+ // parameter to interpreters like `fetch-har` can make sense of it and send a usable
350
+ // payload.
339
351
  const addtlData = {};
340
352
 
341
353
  if (binaryTypes.includes(name)) {
@@ -355,17 +367,21 @@ module.exports = (
355
367
  har.postData.mimeType = contentType;
356
368
 
357
369
  // Handle arbitrary JSON input via a string.
358
- // In OAS you usually find this in an application/json content type.
359
- // with a schema type=string, format=json.
360
- // In the UI this is represented by an arbitrary text input
361
- // This ensures we remove any newlines or tabs and use a clean json block in the example
370
+ //
371
+ // In OAS you usually find this in an `application/json` content type with a schema
372
+ // `type=string, format=json`. In the UI this is represented by an arbitrary text input.
373
+ //
374
+ // This ensures we remove any newlines or tabs and use a clean JSON block in the
375
+ // example.
362
376
  if (requestBody.schema.type === 'string') {
363
377
  har.postData.text = JSON.stringify(JSON.parse(cleanBody));
364
378
  } else {
365
- // Handle formatted JSON objects that have properties that accept arbitrary JSON
366
- // Find all `{ type: string, format: json }` properties in the schema because we need to manually JSON.parse
367
- // them before submit, otherwise they'll be escaped instead of actual objects.
368
- // We also only want values that the user has entered, so we drop any undefined cleanBody keys
379
+ // Handle formatted JSON objects that have properties that accept arbitrary JSON.
380
+ //
381
+ // Find all `{ type: string, format: json }` properties in the schema because we need
382
+ // to manually `JSON.parse` them before submit, otherwise they'll be escaped instead
383
+ // of actual objects. We also only want values that the user has entered, so we drop
384
+ // any `undefined` `cleanBody` keys
369
385
  const jsonTypes = Object.keys(requestBody.schema.properties).filter(
370
386
  key => requestBody.schema.properties[key].format === 'json' && cleanBody[key] !== undefined
371
387
  );
@@ -380,7 +396,8 @@ module.exports = (
380
396
  }
381
397
  });
382
398
 
383
- // `RAW_BODY` is a ReadMe-specific thing where we'll interpret its contents as raw JSON.
399
+ // `RAW_BODY` is a ReadMe-specific thing where we'll interpret the entire payload
400
+ // as a raw string. https://docs.readme.com/docs/raw-body-content
384
401
  if (typeof cleanBody.RAW_BODY !== 'undefined') {
385
402
  cleanBody = cleanBody.RAW_BODY;
386
403
  }
@@ -395,16 +412,23 @@ module.exports = (
395
412
  }
396
413
  }
397
414
  } catch (e) {
398
- // you should log this error if you're debugging why data is showing up in text, when it should show up in params
399
- // console.log('catching ', e);
400
- // If anything above fails for whatever reason, assume that whatever we had is invalid JSON and just treat it
401
- // as raw text.
415
+ // If anything above fails for whatever reason, assume that whatever we had is invalid
416
+ // JSON and just treat it as raw text.
402
417
  har.postData.text = stringify(formData.body);
403
418
  }
404
419
  } else {
405
420
  har.postData.mimeType = contentType;
406
421
  if (isPrimitive(formData.body)) {
407
422
  har.postData.text = formData.body;
423
+ } else if (
424
+ typeof formData.body === 'object' &&
425
+ formData.body !== null &&
426
+ !Array.isArray(formData.body) &&
427
+ typeof formData.body.RAW_BODY !== 'undefined'
428
+ ) {
429
+ // `RAW_BODY` is a ReadMe-specific thing where we'll interpret the entire payload as a
430
+ // raw string. https://docs.readme.com/docs/raw-body-content
431
+ har.postData.text = formData.body.RAW_BODY;
408
432
  } else {
409
433
  har.postData.text = stringify(formData.body);
410
434
  }
@@ -412,8 +436,9 @@ module.exports = (
412
436
  }
413
437
  }
414
438
 
415
- // Add a `Content-Type` header if there are any body values setup above or if there is a schema defined, but only do
416
- // so if we don't already have a `Content-Type` present as it's impossible for a request to have multiple.
439
+ // Add a `Content-Type` header if there are any body values setup above or if there is a schema
440
+ // defined, but only do so if we don't already have a `Content-Type` present as it's impossible
441
+ // for a request to have multiple.
417
442
  if ((har.postData.text || (requestBody && Object.keys(requestBody.schema).length)) && !hasContentType) {
418
443
  har.headers.push({
419
444
  name: 'Content-Type',
@@ -1,6 +1,7 @@
1
+ const qs = require('qs');
1
2
  const stylize = require('./style-serializer');
2
3
 
3
- // Certain styles don't support empty values, This function tracks that list
4
+ // Certain styles don't support empty values.
4
5
  function shouldNotStyleEmptyValues(parameter) {
5
6
  return ['simple', 'spaceDelimited', 'pipeDelimited', 'deepObject'].includes(parameter.style);
6
7
  }
@@ -9,9 +10,12 @@ function shouldNotStyleReservedHeader(parameter) {
9
10
  return ['accept', 'authorization', 'content-type'].includes(parameter.name.toLowerCase());
10
11
  }
11
12
 
12
- // Note: This isn't necessarily part of the spec. Behavior for the value 'undefined' is, well, undefined.
13
- // This code makes our system look better. If we wanted to be more accurate, we might want to remove this,
14
- // restore the un-fixed behavior for undefined and have our UI pass in empty string instead of undefined.
13
+ /**
14
+ * Note: This isn't necessarily part of the spec. Behavior for the value 'undefined' is, well,
15
+ * undefined. This code makes our system look better. If we wanted to be more accurate, we might
16
+ * want to remove this, restore the un-fixed behavior for undefined and have our UI pass in empty
17
+ * string instead of undefined.
18
+ */
15
19
  function removeUndefinedForPath(value) {
16
20
  let finalValue = value;
17
21
 
@@ -41,35 +45,43 @@ function stylizeValue(value, parameter) {
41
45
 
42
46
  // Some styles don't work with empty values. We catch those there
43
47
  if (shouldNotStyleEmptyValues(parameter) && (typeof finalValue === 'undefined' || finalValue === '')) {
44
- // Paths need return an unstyled empty string instead of undefined so it's ignored in the final path string
48
+ // Paths need return an unstyled empty string instead of undefined so it's ignored in the final
49
+ // path string.
45
50
  if (parameter.in === 'path') {
46
51
  return '';
47
52
  }
48
- // Everything but path should return undefined when unstyled so it's ignored in the final parameter array
53
+
54
+ // Everything but path should return undefined when unstyled so it's ignored in the final
55
+ // parameter array.
49
56
  return undefined;
50
57
  }
51
58
 
52
- // Every style that adds their style to empty values should use emptystring for path parameters instead of undefined to avoid the string 'undefined'
59
+ // Every style that adds their style to empty values should use emptystring for path parameters
60
+ // instead of undefined to avoid the string `undefined`.
53
61
  if (parameter.in === 'path') {
54
62
  finalValue = removeUndefinedForPath(finalValue);
55
63
  }
56
64
 
57
- // Eventhough `Accept`, `Authorization`, and `Content-Type` headers can be defined as parameters, they should be
58
- // completely ignored when it comes to serialization.
59
- //
60
- // > If in is "header" and the name field is "Accept", "Content-Type" or "Authorization", the parameter definition
61
- // > SHALL be ignored.
62
- //
63
- // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-10
65
+ /**
66
+ * Eventhough `Accept`, `Authorization`, and `Content-Type` headers can be defined as parameters,
67
+ * they should be completely ignored when it comes to serialization.
68
+ *
69
+ * > If in is "header" and the name field is "Accept", "Content-Type" or "Authorization", the
70
+ * > parameter definition SHALL be ignored.
71
+ *
72
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-10}
73
+ */
64
74
  if (parameter.in === 'header' && shouldNotStyleReservedHeader(parameter)) {
65
75
  return value;
66
76
  }
67
77
 
68
- // All parameter types have a default `style` format so if they don't have one prescribed we should still conform to
69
- // what the spec defines.
70
- //
71
- // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#user-content-parameterstyle
72
- // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#user-content-parameterstyle
78
+ /**
79
+ * All parameter types have a default `style` format so if they don't have one prescribed we
80
+ * should still conform to what the spec defines.
81
+ *
82
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#user-content-parameterstyle}
83
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#user-content-parameterstyle}
84
+ */
73
85
  let style = parameter.style;
74
86
  if (!style) {
75
87
  if (parameter.in === 'query') {
@@ -85,10 +97,12 @@ function stylizeValue(value, parameter) {
85
97
 
86
98
  let explode = parameter.explode;
87
99
  if (explode === undefined && style === 'form') {
88
- // Per the spec if no `explode` is present but `style` is `form` then `explode` should default to `true`.
89
- //
90
- // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#user-content-parameterexplode
91
- // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#user-content-parameterexplode
100
+ /**
101
+ * Per the spec if no `explode` is present but `style` is `form` then `explode` should default to `true`.
102
+ *
103
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#user-content-parameterexplode}
104
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#user-content-parameterexplode}
105
+ */
92
106
  explode = true;
93
107
  }
94
108
 
@@ -98,15 +112,49 @@ function stylizeValue(value, parameter) {
98
112
  key: parameter.name,
99
113
  style,
100
114
  explode,
101
- /*
102
- TODO: this parameter is optional to stylize. It defaults to false, and can accept falsy, truthy, or "unsafe".
103
- I do not know if it is correct for query to use this. See style-serializer for more info
104
- */
115
+ /**
116
+ * @todo this parameter is optional to stylize. It defaults to false, and can accept falsy, truthy, or "unsafe".
117
+ * I do not know if it is correct for query to use this. See style-serializer for more info
118
+ */
105
119
  escape: true,
120
+ ...(parameter.in === 'query' ? { isAllowedReserved: parameter.allowReserved || false } : {}),
106
121
  });
107
122
  }
108
123
 
109
- // Explode is handled on its own, because style-serializer doesn't return what we expect for proper HAR output
124
+ function handleDeepObject(value, parameter) {
125
+ return qs
126
+ .stringify(value, {
127
+ // eslint-disable-next-line consistent-return
128
+ encoder(str, defaultEncoder, charset, type) {
129
+ if (type === 'key') {
130
+ // `str` will be here as `dog[treats][0]` but because the `qs` library doesn't have any
131
+ // awareness of our OpenAPI parameters we need to rewrite it to slap the `parameter.name`
132
+ // to the top, like `pets[dog][treats][0]`.
133
+ const prefixedKey = str
134
+ .split(/[[\]]/g)
135
+ .filter(Boolean)
136
+ .map(k => `[${k}]`)
137
+ .join('');
138
+
139
+ return `${parameter.name}${prefixedKey}`;
140
+ } else if (type === 'value') {
141
+ return stylizeValue(str, parameter);
142
+ }
143
+ },
144
+ })
145
+ .split('&')
146
+ .map(item => {
147
+ const split = item.split('=');
148
+ return {
149
+ label: split[0],
150
+ // `qs` will coerce null values into being `undefined` string but we want to preserve them.
151
+ value: split[1] === 'undefined' ? null : split[1],
152
+ };
153
+ });
154
+ }
155
+
156
+ // Explode is handled on its own, because style-serializer doesn't return what we expect for proper
157
+ // HAR output.
110
158
  function handleExplode(value, parameter) {
111
159
  if (Array.isArray(value)) {
112
160
  return value.map(val => {
@@ -118,12 +166,13 @@ function handleExplode(value, parameter) {
118
166
  const newObj = {};
119
167
 
120
168
  Object.keys(value).forEach(key => {
121
- const stylizedValue = stylizeValue(value[key], parameter);
122
-
123
169
  if (parameter.style === 'deepObject') {
124
- newObj[`${parameter.name}[${key}]`] = stylizedValue;
170
+ const deepObjs = handleDeepObject(value, parameter);
171
+ deepObjs.forEach(obj => {
172
+ newObj[obj.label] = obj.value;
173
+ });
125
174
  } else {
126
- newObj[key] = stylizedValue;
175
+ newObj[key] = stylizeValue(value[key], parameter);
127
176
  }
128
177
  });
129
178
 
@@ -148,10 +197,15 @@ module.exports = function formatStyle(value, parameter) {
148
197
  return undefined;
149
198
  }
150
199
 
151
- // This custom explode logic allows us to bubble up arrays and objects to be handled differently by our HAR transformer
152
- // We need this because the stylizeValue function assumes we're building strings, not richer data types
153
- // The first part of this conditional checks if explode is enabled. Explode is disabled for everything by default except for forms.
154
- // The second part of this conditional bypasses the custom explode logic for headers, because they work differently, and stylizeValue is accurate
200
+ // This custom explode logic allows us to bubble up arrays and objects to be handled differently
201
+ // by our HAR transformer. We need this because the `stylizeValue` function assumes we're building
202
+ // strings, not richer data types.
203
+ //
204
+ // The first part of this conditional checks if `explode` is enabled. Explode is disabled for
205
+ // everything by default except for forms.
206
+ //
207
+ // The second part of this conditional bypasses the custom explode logic for headers, because they
208
+ // work differently, and `stylizeValue` is accurate.
155
209
  if (shouldExplode(parameter)) {
156
210
  return handleExplode(value, parameter);
157
211
  }
@@ -16,8 +16,8 @@ function isURIEncoded(value) {
16
16
  try {
17
17
  return decodeURIComponent(value) !== value;
18
18
  } catch (err) {
19
- // `decodeURIComponent` will throw an exception if a string that has an un-encoded percent sign in it (like 20%),
20
- // so if it's throwing we can just assume that the value hasn't been encoded.
19
+ // `decodeURIComponent` will throw an exception if a string that has an un-encoded percent sign
20
+ // in it (like 20%), o if it's throwing we can just assume that the value hasn't been encoded.
21
21
  return false;
22
22
  }
23
23
  }
@@ -36,7 +36,7 @@ module.exports = function stylize(config) {
36
36
 
37
37
  module.exports.encodeDisallowedCharacters = function encodeDisallowedCharacters(
38
38
  str,
39
- { escape, returnIfEncoded = false } = {}, // eslint-disable-line default-param-last
39
+ { escape, returnIfEncoded = false, isAllowedReserved } = {}, // eslint-disable-line default-param-last
40
40
  parse
41
41
  ) {
42
42
  if (typeof str === 'number') {
@@ -61,17 +61,16 @@ module.exports.encodeDisallowedCharacters = function encodeDisallowedCharacters(
61
61
  return JSON.parse(str);
62
62
  }
63
63
 
64
- // In ES6 you can do this quite easily by using the new ... spread operator.
65
- // This causes the string iterator (another new ES6 feature) to be used internally,
66
- // and because that iterator is designed to deal with
67
- // code points rather than UCS-2/UTF-16 code units.
64
+ // In ES6 you can do this quite easily by using the new ... spread operator. This causes the
65
+ // string iterator (another new ES6 feature) to be used internally, and because that iterator is
66
+ // designed to deal with code points rather than UCS-2/UTF-16 code units.
68
67
  return [...str]
69
68
  .map(char => {
70
69
  if (isRfc3986Unreserved(char)) {
71
70
  return char;
72
71
  }
73
72
 
74
- if (isRfc3986Reserved(char) && escape === 'unsafe') {
73
+ if (isRfc3986Reserved(char) && (escape === 'unsafe' || isAllowedReserved)) {
75
74
  return char;
76
75
  }
77
76
 
@@ -86,162 +85,255 @@ module.exports.encodeDisallowedCharacters = function encodeDisallowedCharacters(
86
85
  .join('');
87
86
  };
88
87
 
89
- function encodeArray({ location, key, value, style, explode, escape }) {
88
+ /**
89
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-examples}
90
+ */
91
+ function encodeArray({ location, key, value, style, explode, escape, isAllowedReserved = false }) {
90
92
  const valueEncoder = str =>
91
93
  module.exports.encodeDisallowedCharacters(str, {
92
94
  escape,
93
95
  returnIfEncoded: location === 'query',
96
+ isAllowedReserved,
94
97
  });
95
98
 
96
- if (style === 'simple') {
97
- return value.map(val => valueEncoder(val)).join(',');
98
- }
99
-
100
- if (style === 'label') {
101
- return `.${value.map(val => valueEncoder(val)).join('.')}`;
102
- }
103
-
104
- if (style === 'matrix') {
105
- return value
106
- .map(val => valueEncoder(val))
107
- .reduce((prev, curr) => {
108
- if (!prev || explode) {
109
- return `${prev || ''};${key}=${curr}`;
110
- }
111
- return `${prev},${curr}`;
112
- }, '');
113
- }
114
-
115
- if (style === 'form') {
116
- const after = explode ? `&${key}=` : ',';
117
- return value.map(val => valueEncoder(val)).join(after);
118
- }
119
-
120
- if (style === 'spaceDelimited') {
121
- const after = explode ? `${key}=` : '';
122
- return value.map(val => valueEncoder(val)).join(` ${after}`);
99
+ switch (style) {
100
+ /**
101
+ * @example <caption>`style: simple`</caption>
102
+ * `["blue","black","brown"]` → `blue,black,brown`
103
+ */
104
+ case 'simple':
105
+ return value.map(val => valueEncoder(val)).join(',');
106
+
107
+ /**
108
+ * @example <caption>`style: label`</caption>
109
+ * `["blue","black","brown"]` → `.blue.black.brown`
110
+ */
111
+ case 'label':
112
+ return `.${value.map(val => valueEncoder(val)).join('.')}`;
113
+
114
+ /**
115
+ * @example <caption>`style: matrix` + `explode: true`</caption>
116
+ * `["blue","black","brown"]` → `;color=blue;color=black;color=brown`
117
+ *
118
+ * @example <caption>`style: matrix` + `explode: false` (the default behavior)</caption>
119
+ * `["blue","black","brown"]` → `;color=blue,black,brown `
120
+ */
121
+ case 'matrix':
122
+ return value
123
+ .map(val => valueEncoder(val))
124
+ .reduce((prev, curr) => {
125
+ if (!prev || explode) {
126
+ return `${prev || ''};${key}=${curr}`;
127
+ }
128
+ return `${prev},${curr}`;
129
+ }, '');
130
+
131
+ /**
132
+ * @example <caption>`style: form` + `explode: true`</caption>
133
+ * `["blue","black","brown"]` → `color=blue&color=black&color=brown`
134
+ *
135
+ * @example <caption>`style: form` + `explode: false` (the default behavior)</caption>
136
+ * `["blue","black","brown"]` → `ccolor=blue,black,brown`
137
+ */
138
+ case 'form':
139
+ return value.map(val => valueEncoder(val)).join(explode ? `&${key}=` : ',');
140
+
141
+ /**
142
+ * @example <caption>`style: spaceDelimited`</caption>
143
+ * `["blue","black","brown"]` → `blue%20black%20brown`
144
+ */
145
+ case 'spaceDelimited':
146
+ return value.map(val => valueEncoder(val)).join(` ${explode ? `${key}=` : ''}`);
147
+
148
+ /**
149
+ * @example <caption>`style: pipeDelimited`</caption>
150
+ * `["blue","black","brown"]` → `blue|black|brown`
151
+ */
152
+ case 'pipeDelimited':
153
+ return value.map(val => valueEncoder(val)).join(`|${explode ? `${key}=` : ''}`);
154
+
155
+ default:
156
+ return undefined;
123
157
  }
124
-
125
- if (style === 'pipeDelimited') {
126
- const after = explode ? `${key}=` : '';
127
- return value.map(val => valueEncoder(val)).join(`|${after}`);
128
- }
129
-
130
- return undefined;
131
158
  }
132
159
 
133
- function encodeObject({ location, key, value, style, explode, escape }) {
160
+ /**
161
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-examples}
162
+ */
163
+ function encodeObject({ location, key, value, style, explode, escape, isAllowedReserved = false }) {
134
164
  const valueEncoder = str =>
135
165
  module.exports.encodeDisallowedCharacters(str, {
136
166
  escape,
137
167
  returnIfEncoded: location === 'query',
168
+ isAllowedReserved,
138
169
  });
139
170
 
140
171
  const valueKeys = Object.keys(value);
141
172
 
142
- if (style === 'simple') {
143
- return valueKeys.reduce((prev, curr) => {
144
- const val = valueEncoder(value[curr]);
145
- const middleChar = explode ? '=' : ',';
146
- const prefix = prev ? `${prev},` : '';
147
-
148
- return `${prefix}${curr}${middleChar}${val}`;
149
- }, '');
150
- }
151
-
152
- if (style === 'label') {
153
- return valueKeys.reduce((prev, curr) => {
154
- const val = valueEncoder(value[curr]);
155
- const middleChar = explode ? '=' : '.';
156
- const prefix = prev ? `${prev}.` : '.';
173
+ switch (style) {
174
+ /**
175
+ * @example <caption>`style: simple` + `explode: true`</caption>
176
+ * `{ "R": 100, "G": 200, "B": 150 }` → `R=100,G=200,B=150`
177
+ *
178
+ * @example <caption>`style: simple` + `explode: false` (the default behavior)</caption>
179
+ * `{ "R": 100, "G": 200, "B": 150 }` → `R,100,G,200,B,150`
180
+ */
181
+ case 'simple':
182
+ return valueKeys.reduce((prev, curr) => {
183
+ const val = valueEncoder(value[curr]);
184
+ const middleChar = explode ? '=' : ',';
185
+ const prefix = prev ? `${prev},` : '';
186
+
187
+ return `${prefix}${curr}${middleChar}${val}`;
188
+ }, '');
157
189
 
158
- return `${prefix}${curr}${middleChar}${val}`;
159
- }, '');
160
- }
190
+ /**
191
+ * @example <caption>`style: label` + `explode: true`</caption>
192
+ * `{ "R": 100, "G": 200, "B": 150 }` → `.R=100.G=200.B=150`
193
+ *
194
+ * @example <caption>`style: label` + `explode: false` (the default behavior)</caption>
195
+ * `{ "R": 100, "G": 200, "B": 150 }` → `.R.100.G.200.B.150`
196
+ */
197
+ case 'label':
198
+ return valueKeys.reduce((prev, curr) => {
199
+ const val = valueEncoder(value[curr]);
200
+ const middleChar = explode ? '=' : '.';
201
+ const prefix = prev ? `${prev}.` : '.';
202
+
203
+ return `${prefix}${curr}${middleChar}${val}`;
204
+ }, '');
161
205
 
162
- if (style === 'matrix' && explode) {
163
- return valueKeys.reduce((prev, curr) => {
164
- const val = valueEncoder(value[curr]);
165
- const prefix = prev ? `${prev};` : ';';
206
+ /**
207
+ * @example <caption>`style: matrix` + `explode: true`</caption>
208
+ * `{ "R": 100, "G": 200, "B": 150 }` → `;R=100;G=200;B=150`
209
+ *
210
+ * @example <caption>`style: matrix` + `explode: false` (the default behavior)</caption>
211
+ * `{ "R": 100, "G": 200, "B": 150 }` → `;color=R,100,G,200,B,150`
212
+ */
213
+ case 'matrix':
214
+ if (explode) {
215
+ return valueKeys.reduce((prev, curr) => {
216
+ const val = valueEncoder(value[curr]);
217
+ const prefix = prev ? `${prev};` : ';';
218
+
219
+ return `${prefix}${curr}=${val}`;
220
+ }, '');
221
+ }
166
222
 
167
- return `${prefix}${curr}=${val}`;
168
- }, '');
169
- }
223
+ return valueKeys.reduce((prev, curr) => {
224
+ const val = valueEncoder(value[curr]);
225
+ const prefix = prev ? `${prev},` : `;${key}=`;
170
226
 
171
- if (style === 'matrix') {
172
- // no explode
173
- return valueKeys.reduce((prev, curr) => {
174
- const val = valueEncoder(value[curr]);
175
- const prefix = prev ? `${prev},` : `;${key}=`;
227
+ return `${prefix}${curr},${val}`;
228
+ }, '');
176
229
 
177
- return `${prefix}${curr},${val}`;
178
- }, '');
179
- }
230
+ /**
231
+ * @example <caption>`style: form` + `explode: true`</caption>
232
+ * `{ "R": 100, "G": 200, "B": 150 }` → `R=100&G=200&B=150`
233
+ *
234
+ * @example <caption>`style: form` + `explode: false` (the default behavior)</caption>
235
+ * `{ "R": 100, "G": 200, "B": 150 }` → `color=R,100,G,200,B,150`
236
+ */
237
+ case 'form':
238
+ return valueKeys.reduce((prev, curr) => {
239
+ const val = valueEncoder(value[curr]);
240
+ const prefix = prev ? `${prev}${explode ? '&' : ','}` : '';
241
+ const separator = explode ? '=' : ',';
242
+
243
+ return `${prefix}${curr}${separator}${val}`;
244
+ }, '');
180
245
 
181
- if (style === 'form') {
182
- return valueKeys.reduce((prev, curr) => {
183
- const val = valueEncoder(value[curr]);
184
- const prefix = prev ? `${prev}${explode ? '&' : ','}` : '';
185
- const separator = explode ? '=' : ',';
246
+ /**
247
+ * @example <caption>`style: spaceDelimited`</caption>
248
+ * `{ "R": 100, "G": 200, "B": 150 }` → `R%20100%20G%20200%20B%20150`
249
+ */
250
+ case 'spaceDelimited':
251
+ return valueKeys.reduce((prev, curr) => {
252
+ const val = valueEncoder(value[curr]);
253
+ const prefix = prev ? `${prev} ` : '';
186
254
 
187
- return `${prefix}${curr}${separator}${val}`;
188
- }, '');
189
- }
255
+ return `${prefix}${curr} ${val}`;
256
+ }, '');
190
257
 
191
- // Supported in 3.1, added by Readme
192
- if (style === 'spaceDelimited') {
193
- return valueKeys.reduce((prev, curr) => {
194
- const val = valueEncoder(value[curr]);
195
- const prefix = prev ? `${prev} ` : '';
258
+ /**
259
+ * @example <caption>`style: pipeDelimited`</caption>
260
+ * `{ "R": 100, "G": 200, "B": 150 }` → `R|100|G|200|B|150`
261
+ */
262
+ case 'pipeDelimited':
263
+ return valueKeys.reduce((prev, curr) => {
264
+ const val = valueEncoder(value[curr]);
265
+ const prefix = prev ? `${prev}|` : '';
196
266
 
197
- return `${prefix}${curr} ${val}`;
198
- }, '');
199
- }
267
+ return `${prefix}${curr}|${val}`;
268
+ }, '');
200
269
 
201
- // Supported in 3.1, added by Readme
202
- if (style === 'pipeDelimited') {
203
- return valueKeys.reduce((prev, curr) => {
204
- const val = valueEncoder(value[curr]);
205
- const prefix = prev ? `${prev}|` : '';
270
+ /**
271
+ * @example <caption>`style: deepObject`</caption>
272
+ * `{ "R": 100, "G": 200, "B": 150 }` → `color[R]=100&color[G]=200&color[B]=150`
273
+ */
274
+ case 'deepObject':
275
+ return valueKeys.reduce(curr => {
276
+ const val = valueEncoder(value[curr], {}, true);
277
+ return `${val}`;
278
+ }, '');
206
279
 
207
- return `${prefix}${curr}|${val}`;
208
- }, '');
280
+ default:
281
+ return undefined;
209
282
  }
210
-
211
- return undefined;
212
283
  }
213
284
 
214
- function encodePrimitive({ location, key, value, style, escape }) {
285
+ /**
286
+ * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-examples}
287
+ */
288
+ function encodePrimitive({ location, key, value, style, escape, isAllowedReserved = false }) {
215
289
  const valueEncoder = str =>
216
290
  module.exports.encodeDisallowedCharacters(str, {
217
291
  escape,
218
292
  returnIfEncoded: location === 'query' || location === 'body',
293
+ isAllowedReserved,
219
294
  });
220
295
 
221
- if (style === 'simple') {
222
- return valueEncoder(value);
223
- }
224
-
225
- if (style === 'label') {
226
- return `.${valueEncoder(value)}`;
227
- }
296
+ switch (style) {
297
+ /**
298
+ * @example <caption>`style: simple`</caption>
299
+ * `blue` → `blue`
300
+ */
301
+ case 'simple':
302
+ return valueEncoder(value);
303
+
304
+ /**
305
+ * @example <caption>`style: label`</caption>
306
+ * `blue` → `.blue`
307
+ */
308
+ case 'label':
309
+ return `.${valueEncoder(value)}`;
310
+
311
+ /**
312
+ * @example <caption>`style: matrix`</caption>
313
+ * `blue` → `;color=blue`
314
+ */
315
+ case 'matrix':
316
+ if (value === '') {
317
+ return `;${key}`;
318
+ }
228
319
 
229
- if (style === 'matrix') {
230
- // This conditional added by Aaron to be more accurate to the spec
231
- if (value === '') {
232
- return `;${key}`;
233
- }
320
+ return `;${key}=${valueEncoder(value)}`;
234
321
 
235
- return `;${key}=${valueEncoder(value)}`;
236
- }
322
+ /**
323
+ * @example <caption>`style: form`</caption>
324
+ * `blue` → `color=blue`
325
+ */
326
+ case 'form':
327
+ return valueEncoder(value);
237
328
 
238
- if (style === 'form') {
239
- return valueEncoder(value);
240
- }
329
+ /**
330
+ * @example <caption>`style: deepObject`</caption>
331
+ * `blue` → n/a
332
+ */
333
+ case 'deepObject':
334
+ return valueEncoder(value, {}, true);
241
335
 
242
- if (style === 'deepObject') {
243
- return valueEncoder(value, {}, true);
336
+ default:
337
+ return undefined;
244
338
  }
245
-
246
- return undefined;
247
339
  }
@@ -1,26 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: github-actions
4
- directory: "/"
5
- schedule:
6
- interval: monthly
7
- reviewers:
8
- - erunion
9
- labels:
10
- - dependencies
11
- commit-message:
12
- prefix: chore(deps)
13
- prefix-development: chore(deps-dev)
14
-
15
- - package-ecosystem: npm
16
- directory: "/"
17
- schedule:
18
- interval: monthly
19
- open-pull-requests-limit: 10
20
- reviewers:
21
- - erunion
22
- labels:
23
- - dependencies
24
- commit-message:
25
- prefix: chore(deps)
26
- prefix-development: chore(deps-dev)
@@ -1,24 +0,0 @@
1
- name: CI
2
-
3
- on: [push]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
- strategy:
9
- matrix:
10
- node-version: [12.x, 14.x, 16.x]
11
-
12
- steps:
13
- - uses: actions/checkout@v2.4.0
14
-
15
- - name: Use Node.js ${{ matrix.node-version }}
16
- uses: actions/setup-node@v3
17
- with:
18
- node-version: ${{ matrix.node-version }}
19
-
20
- - name: Install deps
21
- run: npm ci
22
-
23
- - name: Run tests
24
- run: npm test
@@ -1,35 +0,0 @@
1
- name: "CodeQL"
2
-
3
- on:
4
- push:
5
- branches: [ main ]
6
- pull_request:
7
- branches: [ main ]
8
- schedule:
9
- - cron: '0 0 1 * *'
10
-
11
- jobs:
12
- analyze:
13
- name: Analyze
14
- runs-on: ubuntu-latest
15
- permissions:
16
- actions: read
17
- contents: read
18
- security-events: write
19
-
20
- strategy:
21
- fail-fast: false
22
- matrix:
23
- language: [ 'javascript' ]
24
-
25
- steps:
26
- - name: Checkout repository
27
- uses: actions/checkout@v2.4.0
28
-
29
- - name: Initialize CodeQL
30
- uses: github/codeql-action/init@v1
31
- with:
32
- languages: ${{ matrix.language }}
33
-
34
- - name: Perform CodeQL Analysis
35
- uses: github/codeql-action/analyze@v1
package/.husky/commit-msg DELETED
@@ -1,4 +0,0 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
- npx commitlint --edit $1