@backstage/repo-tools 0.3.5 → 0.4.0

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,5 +1,39 @@
1
1
  # @backstage/repo-tools
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4e36abef14: Remove support for the deprecated `--experimental-type-build` option for `package build`.
8
+ - 6694b369a3: Adds a new command `schema openapi test` that performs runtime validation of your OpenAPI specs using your test data. Under the hood, we're using Optic to perform this check, really cool work by them!
9
+
10
+ To use this new command, you will have to run `yarn add @useoptic/optic` in the root of your repo.
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+ - @backstage/cli-node@0.2.0
16
+ - @backstage/catalog-model@1.4.3
17
+ - @backstage/cli-common@0.1.13
18
+ - @backstage/errors@1.2.3
19
+
20
+ ## 0.4.0-next.0
21
+
22
+ ### Minor Changes
23
+
24
+ - 4e36abef14: Remove support for the deprecated `--experimental-type-build` option for `package build`.
25
+ - 6694b369a3: Adds a new command `schema openapi test` that performs runtime validation of your OpenAPI specs using your test data. Under the hood, we're using Optic to perform this check, really cool work by them!
26
+
27
+ To use this new command, you will have to run `yarn add @useoptic/optic` in the root of your repo.
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies
32
+ - @backstage/cli-node@0.2.0-next.0
33
+ - @backstage/catalog-model@1.4.3
34
+ - @backstage/cli-common@0.1.13
35
+ - @backstage/errors@1.2.3
36
+
3
37
  ## 0.3.5
4
38
 
5
39
  ### Patch Changes
@@ -219,16 +219,13 @@ function logApiReportInstructions() {
219
219
  async function findPackageEntryPoints(packageDirs) {
220
220
  return Promise.all(
221
221
  packageDirs.map(async (packageDir) => {
222
- var _a, _b, _c, _d;
222
+ var _a, _b;
223
223
  const pkg = await fs__default["default"].readJson(
224
224
  paths.paths.resolveTargetRoot(packageDir, "package.json")
225
225
  );
226
- return (_d = (_a = entryPoints.getPackageExportNames(pkg)) == null ? void 0 : _a.map((name) => ({ packageDir, name }))) != null ? _d : {
226
+ return (_b = (_a = entryPoints.getPackageExportNames(pkg)) == null ? void 0 : _a.map((name) => ({ packageDir, name }))) != null ? _b : {
227
227
  packageDir,
228
- name: "index",
229
- usesExperimentalTypeBuild: (_c = (_b = pkg.scripts) == null ? void 0 : _b.build) == null ? void 0 : _c.includes(
230
- "--experimental-type-build"
231
- )
228
+ name: "index"
232
229
  };
233
230
  })
234
231
  ).then((results) => results.flat());
@@ -258,11 +255,7 @@ async function runApiExtraction({
258
255
  };
259
256
  }
260
257
  const warnings = new Array();
261
- for (const {
262
- packageDir,
263
- name,
264
- usesExperimentalTypeBuild
265
- } of packageEntryPoints) {
258
+ for (const { packageDir, name } of packageEntryPoints) {
266
259
  console.log(`## Processing ${packageDir}`);
267
260
  const noBail = Array.isArray(allowWarnings) ? allowWarnings.some((aw) => aw === packageDir || minimatch__default["default"](packageDir, aw)) : allowWarnings;
268
261
  const projectFolder = paths.paths.resolveTargetRoot(packageDir);
@@ -368,7 +361,7 @@ async function runApiExtraction({
368
361
  },
369
362
  compilerState
370
363
  });
371
- if (validateReleaseTags && !usesExperimentalTypeBuild && fs__default["default"].pathExistsSync(extractorConfig.reportFilePath)) {
364
+ if (validateReleaseTags && fs__default["default"].pathExistsSync(extractorConfig.reportFilePath)) {
372
365
  if (["index", "alpha", "beta"].includes(name)) {
373
366
  const report = await fs__default["default"].readFile(
374
367
  extractorConfig.reportFilePath,
@@ -1157,4 +1150,4 @@ function parseArrayOption(value) {
1157
1150
  }
1158
1151
 
1159
1152
  exports.buildApiReports = buildApiReports;
1160
- //# sourceMappingURL=api-reports-2bcd4682.cjs.js.map
1153
+ //# sourceMappingURL=api-reports-2b0ee2bf.cjs.js.map
@@ -3,21 +3,35 @@
3
3
  var paths = require('./paths-9ab9b8a8.cjs.js');
4
4
  var pLimit = require('p-limit');
5
5
  var path = require('path');
6
+ var portFinder = require('portfinder');
6
7
 
7
8
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
9
 
9
10
  var pLimit__default = /*#__PURE__*/_interopDefaultLegacy(pLimit);
11
+ var portFinder__default = /*#__PURE__*/_interopDefaultLegacy(portFinder);
10
12
 
11
- async function runner(paths$1, command) {
13
+ async function runner(paths$1, command, options) {
14
+ var _a;
12
15
  const packages = await paths.resolvePackagePaths({ paths: paths$1 });
13
- const limit = pLimit__default["default"](5);
16
+ const limit = pLimit__default["default"]((_a = options == null ? void 0 : options.concurrencyLimit) != null ? _a : 5);
17
+ let port = (options == null ? void 0 : options.startingPort) && await portFinder__default["default"].getPortPromise({
18
+ // Prevent collisions with optic which runs 8000->8999
19
+ port: options.startingPort,
20
+ stopPort: options.startingPort + 1e3
21
+ });
14
22
  const resultsList = await Promise.all(
15
23
  packages.map(
16
24
  (pkg) => limit(async () => {
17
25
  let resultText = "";
18
26
  try {
27
+ if (port)
28
+ port = (options == null ? void 0 : options.startingPort) && await portFinder__default["default"].getPortPromise({
29
+ // Prevent collisions with optic which runs 8000->8999
30
+ port: port + 1,
31
+ stopPort: options.startingPort + 1e3
32
+ });
19
33
  console.log(`## Processing ${pkg}`);
20
- await command(pkg);
34
+ await command(pkg, port ? { port } : void 0);
21
35
  } catch (err) {
22
36
  resultText = err.message;
23
37
  }
@@ -39,4 +53,4 @@ exports.TS_MODULE = TS_MODULE;
39
53
  exports.TS_SCHEMA_PATH = TS_SCHEMA_PATH;
40
54
  exports.YAML_SCHEMA_PATH = YAML_SCHEMA_PATH;
41
55
  exports.runner = runner;
42
- //# sourceMappingURL=constants-2eebdd79.cjs.js.map
56
+ //# sourceMappingURL=constants-f7b16ffc.cjs.js.map
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var util = require('util');
4
+ var child_process = require('child_process');
5
+
6
+ const execPromise = util.promisify(child_process.exec);
7
+ const exec = (command, options = [], execOptions) => {
8
+ return execPromise(
9
+ [
10
+ command,
11
+ ...options.filter((e) => e).map((e) => e.startsWith("-") ? e : `"${e}"`)
12
+ ].join(" "),
13
+ execOptions
14
+ );
15
+ };
16
+
17
+ exports.exec = exec;
18
+ //# sourceMappingURL=exec-7bf444eb.cjs.js.map
@@ -5,13 +5,14 @@ var YAML = require('js-yaml');
5
5
  var chalk = require('chalk');
6
6
  var path = require('path');
7
7
  var paths = require('./paths-9ab9b8a8.cjs.js');
8
- var constants = require('./constants-2eebdd79.cjs.js');
8
+ var constants = require('./constants-f7b16ffc.cjs.js');
9
9
  var util = require('util');
10
10
  var child_process = require('child_process');
11
11
  require('@backstage/cli-common');
12
12
  require('@backstage/cli-node');
13
13
  require('minimatch');
14
14
  require('p-limit');
15
+ require('portfinder');
15
16
 
16
17
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
17
18
 
@@ -76,4 +77,4 @@ async function bulkCommand(paths = []) {
76
77
  }
77
78
 
78
79
  exports.bulkCommand = bulkCommand;
79
- //# sourceMappingURL=generate-242d2277.cjs.js.map
80
+ //# sourceMappingURL=generate-f300e17f.cjs.js.map
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs-extra');
4
+ var path = require('path');
5
+ var chalk = require('chalk');
6
+ var constants = require('./constants-f7b16ffc.cjs.js');
7
+ var paths = require('./paths-9ab9b8a8.cjs.js');
8
+ var exec = require('./exec-7bf444eb.cjs.js');
9
+ require('p-limit');
10
+ require('portfinder');
11
+ require('@backstage/cli-common');
12
+ require('@backstage/cli-node');
13
+ require('minimatch');
14
+ require('util');
15
+ require('child_process');
16
+
17
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
18
+
19
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
20
+ var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
21
+
22
+ async function test(directoryPath, { port }, options) {
23
+ const openapiPath = path.join(directoryPath, constants.YAML_SCHEMA_PATH);
24
+ if (!await fs__default["default"].pathExists(openapiPath)) {
25
+ return;
26
+ }
27
+ const opticConfigFilePath = path.join(directoryPath, "optic.yml");
28
+ if (!await fs__default["default"].pathExists(opticConfigFilePath)) {
29
+ return;
30
+ }
31
+ let opticLocation = "";
32
+ try {
33
+ opticLocation = (await exec.exec(`yarn bin optic`, [], { cwd: paths.paths.ownRoot })).stdout;
34
+ } catch (err) {
35
+ throw new Error(
36
+ `Failed to find an Optic CLI installation, ensure that you have @useoptic/optic installed in the root of your repo. If not, run yarn add @useoptic/optic from the root of your repo.`
37
+ );
38
+ }
39
+ try {
40
+ await exec.exec(
41
+ `${opticLocation.trim()} capture`,
42
+ [
43
+ constants.YAML_SCHEMA_PATH,
44
+ "--server-override",
45
+ `http://localhost:${port}`,
46
+ (options == null ? void 0 : options.update) ? "--update" : ""
47
+ ],
48
+ {
49
+ cwd: directoryPath,
50
+ env: {
51
+ ...process.env,
52
+ PORT: `${port}`
53
+ }
54
+ }
55
+ );
56
+ } catch (err) {
57
+ err.message = err.stderr + err.stdout;
58
+ err.message = err.message.split("\n").map((e) => e.replace(/.{1} Sending requests to server/, "")).filter((e) => !e.includes("PASS")).filter((e) => e.trim()).join("\n");
59
+ throw err;
60
+ }
61
+ if (await paths.paths.resolveTargetRoot("node_modules/.bin/prettier") && (options == null ? void 0 : options.update)) {
62
+ await exec.exec(`yarn prettier`, ["--write", openapiPath]);
63
+ }
64
+ }
65
+ async function bulkCommand(paths = [], options) {
66
+ const resultsList = await constants.runner(
67
+ paths,
68
+ (dir, runnerOptions) => test(dir, runnerOptions, options),
69
+ {
70
+ concurrencyLimit: 1,
71
+ startingPort: 9e3
72
+ }
73
+ );
74
+ let failed = false;
75
+ for (const { relativeDir, resultText } of resultsList) {
76
+ if (resultText) {
77
+ console.log();
78
+ console.log(
79
+ chalk__default["default"].red(
80
+ `OpenAPI runtime validation against tests failed in ${relativeDir}:`
81
+ )
82
+ );
83
+ console.log(resultText.trimStart());
84
+ failed = true;
85
+ }
86
+ }
87
+ if (failed) {
88
+ process.exit(1);
89
+ } else {
90
+ console.log(chalk__default["default"].green("Verified all specifications against test data."));
91
+ }
92
+ }
93
+
94
+ exports.bulkCommand = bulkCommand;
95
+ //# sourceMappingURL=index-80206881.cjs.js.map
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs-extra');
4
+ var path = require('path');
5
+ var constants = require('./constants-f7b16ffc.cjs.js');
6
+ var paths = require('./paths-9ab9b8a8.cjs.js');
7
+ var chalk = require('chalk');
8
+ var exec = require('./exec-7bf444eb.cjs.js');
9
+ require('p-limit');
10
+ require('portfinder');
11
+ require('@backstage/cli-common');
12
+ require('@backstage/cli-node');
13
+ require('minimatch');
14
+ require('util');
15
+ require('child_process');
16
+
17
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
18
+
19
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
20
+ var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
21
+
22
+ const ROUTER_TEST_PATHS = [
23
+ "src/service/router.test.ts",
24
+ "src/service/createRouter.test.ts"
25
+ ];
26
+ async function init(directoryPath) {
27
+ const openapiPath = path.join(directoryPath, constants.YAML_SCHEMA_PATH);
28
+ if (!await fs__default["default"].pathExists(openapiPath)) {
29
+ throw new Error(
30
+ `You do not have an OpenAPI YAML file at ${openapiPath}. Please create one and retry this command. If you already have existing test cases for your router, see 'backstage-repo-tools schema openapi test --update'`
31
+ );
32
+ }
33
+ const opticConfigFilePath = path.join(directoryPath, "optic.yml");
34
+ if (!await fs__default["default"].pathExists(opticConfigFilePath)) {
35
+ throw new Error(`This directory already has an optic.yml file. Exiting.`);
36
+ }
37
+ await fs__default["default"].writeFile(
38
+ opticConfigFilePath,
39
+ `ruleset:
40
+ - breaking-changes
41
+ capture:
42
+ ${constants.YAML_SCHEMA_PATH}:
43
+ # \u{1F527} Runnable example with simple get requests.
44
+ # Run with "PORT=3000 optic capture ${constants.YAML_SCHEMA_PATH} --update interactive" in '${directoryPath}'
45
+ # You can change the server and the 'requests' section to experiment
46
+ server:
47
+ # This will not be used by 'backstage-repo-tools schema openapi test', but may be useful for interactive updates.
48
+ url: http://localhost:3000
49
+ requests:
50
+ # \u2139\uFE0F Requests should be sent to the Optic proxy, the address of which is injected into 'run.command's env as OPTIC_PROXY (or the value of 'run.proxy_variable').
51
+ run:
52
+ # \u{1F527} Specify a command that will generate traffic
53
+ command: yarn backstage-cli package test --no-watch ${ROUTER_TEST_PATHS.map(
54
+ (e) => `"${e}"`
55
+ ).join(",")}
56
+ `
57
+ );
58
+ if (await paths.paths.resolveTargetRoot("node_modules/.bin/prettier")) {
59
+ await exec.exec(`yarn prettier`, ["--write", opticConfigFilePath]);
60
+ }
61
+ }
62
+ async function initCommand(paths = []) {
63
+ const resultsList = await constants.runner(paths, (dir) => init(dir), {
64
+ concurrencyLimit: 5
65
+ });
66
+ let failed = false;
67
+ for (const { relativeDir, resultText } of resultsList) {
68
+ if (resultText) {
69
+ console.log();
70
+ console.log(
71
+ chalk__default["default"].red(`Failed to initialize ${relativeDir} for OpenAPI commands.`)
72
+ );
73
+ console.log(resultText.trimStart());
74
+ failed = true;
75
+ }
76
+ }
77
+ if (failed) {
78
+ process.exit(1);
79
+ } else {
80
+ console.log(chalk__default["default"].green(`All directories have already been configured.`));
81
+ }
82
+ }
83
+
84
+ exports["default"] = initCommand;
85
+ //# sourceMappingURL=init-1a47015e.cjs.js.map
@@ -6,7 +6,7 @@ var ruleset = require('@apisyouwonthate/style-guide');
6
6
  var fs = require('fs-extra');
7
7
  var chalk = require('chalk');
8
8
  var path = require('path');
9
- var constants = require('./constants-2eebdd79.cjs.js');
9
+ var constants = require('./constants-f7b16ffc.cjs.js');
10
10
  var spectralRulesets = require('@stoplight/spectral-rulesets');
11
11
  var types = require('@stoplight/types');
12
12
  var spectralFormatters = require('@stoplight/spectral-formatters');
@@ -15,6 +15,7 @@ require('@backstage/cli-common');
15
15
  require('@backstage/cli-node');
16
16
  require('minimatch');
17
17
  require('p-limit');
18
+ require('portfinder');
18
19
 
19
20
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
21
 
@@ -91,4 +92,4 @@ async function bulkCommand(paths = [], options) {
91
92
  }
92
93
 
93
94
  exports.bulkCommand = bulkCommand;
94
- //# sourceMappingURL=lint-d126add4.cjs.js.map
95
+ //# sourceMappingURL=lint-fa452310.cjs.js.map
@@ -6,9 +6,10 @@ var lodash = require('lodash');
6
6
  var path = require('path');
7
7
  var chalk = require('chalk');
8
8
  var Parser = require('@apidevtools/swagger-parser');
9
- var constants = require('./constants-2eebdd79.cjs.js');
9
+ var constants = require('./constants-f7b16ffc.cjs.js');
10
10
  var paths = require('./paths-9ab9b8a8.cjs.js');
11
11
  require('p-limit');
12
+ require('portfinder');
12
13
  require('@backstage/cli-common');
13
14
  require('@backstage/cli-node');
14
15
  require('minimatch');
@@ -79,4 +80,4 @@ async function bulkCommand(paths = []) {
79
80
  }
80
81
 
81
82
  exports.bulkCommand = bulkCommand;
82
- //# sourceMappingURL=verify-8fe63985.cjs.js.map
83
+ //# sourceMappingURL=verify-7bbbd05f.cjs.js.map
package/dist/index.cjs.js CHANGED
@@ -49,14 +49,16 @@ function registerSchemaCommand(program) {
49
49
  const openApiCommand = command.command("openapi [command]").description("Tooling for OpenApi schema");
50
50
  openApiCommand.command("verify [paths...]").description(
51
51
  "Verify that all OpenAPI schemas are valid and have a matching `schemas/openapi.generated.ts` file."
52
- ).action(lazy(() => Promise.resolve().then(function () { return require('./cjs/verify-8fe63985.cjs.js'); }).then((m) => m.bulkCommand)));
52
+ ).action(lazy(() => Promise.resolve().then(function () { return require('./cjs/verify-7bbbd05f.cjs.js'); }).then((m) => m.bulkCommand)));
53
53
  openApiCommand.command("generate [paths...]").description(
54
54
  "Generates a Typescript file from an OpenAPI yaml spec. For use with the `@backstage/backend-openapi-utils` ApiRouter type."
55
- ).action(lazy(() => Promise.resolve().then(function () { return require('./cjs/generate-242d2277.cjs.js'); }).then((m) => m.bulkCommand)));
55
+ ).action(lazy(() => Promise.resolve().then(function () { return require('./cjs/generate-f300e17f.cjs.js'); }).then((m) => m.bulkCommand)));
56
56
  openApiCommand.command("lint [paths...]").description("Lint OpenAPI schemas.").option(
57
57
  "--strict",
58
58
  "Fail on any linting severity messages, not just errors."
59
- ).action(lazy(() => Promise.resolve().then(function () { return require('./cjs/lint-d126add4.cjs.js'); }).then((m) => m.bulkCommand)));
59
+ ).action(lazy(() => Promise.resolve().then(function () { return require('./cjs/lint-fa452310.cjs.js'); }).then((m) => m.bulkCommand)));
60
+ openApiCommand.command("test [paths...]").description("Test OpenAPI schemas against written tests").option("--update", "Update the spec on failure.").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/index-80206881.cjs.js'); }).then((m) => m.bulkCommand)));
61
+ openApiCommand.command("init <paths...>").description("Creates any config needed for the test command.").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/init-1a47015e.cjs.js'); }).then((m) => m.default)));
60
62
  }
61
63
  function registerCommands(program) {
62
64
  program.command("api-reports [paths...]").option("--ci", "CI run checks that there is no changes on API reports").option("--tsc", "executes the tsc compilation before extracting the APIs").option("--docs", "generates the api documentation").option(
@@ -82,7 +84,7 @@ function registerCommands(program) {
82
84
  "Turn on release tag validation for the public, beta, and alpha APIs"
83
85
  ).description("Generate an API report for selected packages").action(
84
86
  lazy(
85
- () => Promise.resolve().then(function () { return require('./cjs/api-reports-2bcd4682.cjs.js'); }).then((m) => m.buildApiReports)
87
+ () => Promise.resolve().then(function () { return require('./cjs/api-reports-2b0ee2bf.cjs.js'); }).then((m) => m.buildApiReports)
86
88
  )
87
89
  );
88
90
  program.command("type-deps").description("Find inconsistencies in types of all packages and plugins").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/type-deps-5eacd931.cjs.js'); }).then((m) => m.default)));
@@ -114,7 +116,7 @@ function lazy(getActionFunc) {
114
116
  };
115
117
  }
116
118
 
117
- var version = "0.3.5";
119
+ var version = "0.4.0";
118
120
 
119
121
  const main = (argv) => {
120
122
  commander.program.name("backstage-repo-tools").version(version);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/repo-tools",
3
3
  "description": "CLI for Backstage repo tooling ",
4
- "version": "0.3.5",
4
+ "version": "0.4.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -34,7 +34,7 @@
34
34
  "@apisyouwonthate/style-guide": "^1.4.0",
35
35
  "@backstage/catalog-model": "^1.4.3",
36
36
  "@backstage/cli-common": "^0.1.13",
37
- "@backstage/cli-node": "^0.1.5",
37
+ "@backstage/cli-node": "^0.2.0",
38
38
  "@backstage/errors": "^1.2.3",
39
39
  "@manypkg/get-packages": "^1.1.3",
40
40
  "@microsoft/api-documenter": "^7.22.33",
@@ -43,7 +43,7 @@
43
43
  "@stoplight/spectral-formatters": "^1.1.0",
44
44
  "@stoplight/spectral-functions": "^1.7.2",
45
45
  "@stoplight/spectral-parsers": "^1.0.2",
46
- "@stoplight/spectral-rulesets": "^1.16.0",
46
+ "@stoplight/spectral-rulesets": "^1.18.0",
47
47
  "@stoplight/spectral-runtime": "^1.1.2",
48
48
  "@stoplight/types": "^13.14.0",
49
49
  "chalk": "^4.0.0",
@@ -56,21 +56,24 @@
56
56
  "lodash": "^4.17.21",
57
57
  "minimatch": "^5.1.1",
58
58
  "p-limit": "^3.0.2",
59
+ "portfinder": "^1.0.32",
59
60
  "ts-node": "^10.0.0",
60
61
  "yaml-diff-patch": "^2.0.0"
61
62
  },
62
63
  "devDependencies": {
63
- "@backstage/cli": "^0.23.0",
64
+ "@backstage/cli": "^0.24.0",
64
65
  "@backstage/types": "^1.1.1",
65
66
  "@types/is-glob": "^4.0.2",
66
67
  "@types/mock-fs": "^4.13.0",
67
68
  "@types/node": "^18.17.8",
69
+ "@types/prettier": "^2.0.0",
68
70
  "mock-fs": "^5.2.0"
69
71
  },
70
72
  "peerDependencies": {
71
73
  "@microsoft/api-extractor-model": "*",
72
74
  "@microsoft/tsdoc": "*",
73
75
  "@microsoft/tsdoc-config": "*",
76
+ "@useoptic/optic": "^0.50.7",
74
77
  "prettier": "^2.8.1",
75
78
  "typescript": "> 3.0.0"
76
79
  },