@redocly/cli 1.29.0 → 1.31.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +36 -16
  3. package/lib/__tests__/commands/push-region.test.js +3 -3
  4. package/lib/auth/__tests__/device-flow.test.js +62 -0
  5. package/lib/auth/__tests__/oauth-client.test.js +93 -0
  6. package/lib/auth/device-flow.d.ts +26 -0
  7. package/lib/auth/device-flow.js +133 -0
  8. package/lib/auth/oauth-client.d.ts +14 -0
  9. package/lib/auth/oauth-client.js +93 -0
  10. package/lib/commands/auth.d.ts +13 -0
  11. package/lib/commands/auth.js +51 -0
  12. package/lib/commands/push.d.ts +1 -1
  13. package/lib/commands/push.js +4 -4
  14. package/lib/index.js +103 -15
  15. package/lib/otel.d.ts +10 -0
  16. package/lib/otel.js +47 -0
  17. package/lib/reunite/api/__tests__/domains.test.js +32 -0
  18. package/lib/{cms → reunite}/api/api-client.d.ts +9 -0
  19. package/lib/{cms → reunite}/api/api-client.js +2 -1
  20. package/lib/reunite/api/domains.d.ts +4 -0
  21. package/lib/reunite/api/domains.js +22 -0
  22. package/lib/reunite/commands/__tests__/push.test.d.ts +1 -0
  23. package/lib/reunite/commands/__tests__/utils.test.d.ts +1 -0
  24. package/lib/types.d.ts +5 -4
  25. package/lib/utils/miscellaneous.d.ts +5 -4
  26. package/lib/utils/miscellaneous.js +14 -14
  27. package/package.json +11 -4
  28. package/src/__tests__/commands/push-region.test.ts +2 -2
  29. package/src/auth/__tests__/device-flow.test.ts +73 -0
  30. package/src/auth/__tests__/oauth-client.test.ts +117 -0
  31. package/src/auth/device-flow.ts +175 -0
  32. package/src/auth/oauth-client.ts +111 -0
  33. package/src/commands/auth.ts +66 -0
  34. package/src/commands/push.ts +3 -3
  35. package/src/index.ts +115 -16
  36. package/src/otel.ts +59 -0
  37. package/src/reunite/api/__tests__/domains.test.ts +41 -0
  38. package/src/{cms → reunite}/api/api-client.ts +1 -1
  39. package/src/reunite/api/domains.ts +23 -0
  40. package/src/types.ts +8 -4
  41. package/src/utils/miscellaneous.ts +19 -18
  42. package/tsconfig.json +1 -1
  43. package/tsconfig.tsbuildinfo +1 -1
  44. package/lib/cms/api/__tests__/domains.test.js +0 -13
  45. package/lib/cms/api/domains.d.ts +0 -1
  46. package/lib/cms/api/domains.js +0 -11
  47. package/lib/commands/login.d.ts +0 -9
  48. package/lib/commands/login.js +0 -23
  49. package/src/cms/api/__tests__/domains.test.ts +0 -15
  50. package/src/cms/api/domains.ts +0 -11
  51. package/src/commands/login.ts +0 -34
  52. /package/lib/{cms/api/__tests__/api-keys.test.d.ts → auth/__tests__/device-flow.test.d.ts} +0 -0
  53. /package/lib/{cms/api/__tests__/api.client.test.d.ts → auth/__tests__/oauth-client.test.d.ts} +0 -0
  54. /package/lib/{cms/api/__tests__/domains.test.d.ts → reunite/api/__tests__/api-keys.test.d.ts} +0 -0
  55. /package/lib/{cms → reunite}/api/__tests__/api-keys.test.js +0 -0
  56. /package/lib/{cms/commands/__tests__/push-status.test.d.ts → reunite/api/__tests__/api.client.test.d.ts} +0 -0
  57. /package/lib/{cms → reunite}/api/__tests__/api.client.test.js +0 -0
  58. /package/lib/{cms/commands/__tests__/push.test.d.ts → reunite/api/__tests__/domains.test.d.ts} +0 -0
  59. /package/lib/{cms → reunite}/api/api-keys.d.ts +0 -0
  60. /package/lib/{cms → reunite}/api/api-keys.js +0 -0
  61. /package/lib/{cms → reunite}/api/index.d.ts +0 -0
  62. /package/lib/{cms → reunite}/api/index.js +0 -0
  63. /package/lib/{cms → reunite}/api/types.d.ts +0 -0
  64. /package/lib/{cms → reunite}/api/types.js +0 -0
  65. /package/lib/{cms/commands/__tests__/utils.test.d.ts → reunite/commands/__tests__/push-status.test.d.ts} +0 -0
  66. /package/lib/{cms → reunite}/commands/__tests__/push-status.test.js +0 -0
  67. /package/lib/{cms → reunite}/commands/__tests__/push.test.js +0 -0
  68. /package/lib/{cms → reunite}/commands/__tests__/utils.test.js +0 -0
  69. /package/lib/{cms → reunite}/commands/push-status.d.ts +0 -0
  70. /package/lib/{cms → reunite}/commands/push-status.js +0 -0
  71. /package/lib/{cms → reunite}/commands/push.d.ts +0 -0
  72. /package/lib/{cms → reunite}/commands/push.js +0 -0
  73. /package/lib/{cms → reunite}/commands/utils.d.ts +0 -0
  74. /package/lib/{cms → reunite}/commands/utils.js +0 -0
  75. /package/lib/{cms → reunite}/utils.d.ts +0 -0
  76. /package/lib/{cms → reunite}/utils.js +0 -0
  77. /package/src/{cms → reunite}/api/__tests__/api-keys.test.ts +0 -0
  78. /package/src/{cms → reunite}/api/__tests__/api.client.test.ts +0 -0
  79. /package/src/{cms → reunite}/api/api-keys.ts +0 -0
  80. /package/src/{cms → reunite}/api/index.ts +0 -0
  81. /package/src/{cms → reunite}/api/types.ts +0 -0
  82. /package/src/{cms → reunite}/commands/__tests__/push-status.test.ts +0 -0
  83. /package/src/{cms → reunite}/commands/__tests__/push.test.ts +0 -0
  84. /package/src/{cms → reunite}/commands/__tests__/utils.test.ts +0 -0
  85. /package/src/{cms → reunite}/commands/push-status.ts +0 -0
  86. /package/src/{cms → reunite}/commands/push.ts +0 -0
  87. /package/src/{cms → reunite}/commands/utils.ts +0 -0
  88. /package/src/{cms → reunite}/utils.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { handlePush as handleCMSPush } from '../cms/commands/push';
1
+ import { handlePush as handleCMSPush } from '../reunite/commands/push';
2
2
  import type { Config, Region } from '@redocly/openapi-core';
3
3
  import type { CommandArgs } from '../wrapper';
4
4
  import type { VerifyConfigOptions } from '../types';
@@ -13,9 +13,9 @@ const crypto_1 = require("crypto");
13
13
  const openapi_core_1 = require("@redocly/openapi-core");
14
14
  const utils_1 = require("@redocly/openapi-core/lib/utils");
15
15
  const miscellaneous_1 = require("../utils/miscellaneous");
16
- const login_1 = require("./login");
17
- const push_1 = require("../cms/commands/push");
18
- const api_client_1 = require("../cms/api/api-client");
16
+ const auth_1 = require("./auth");
17
+ const push_1 = require("../reunite/commands/push");
18
+ const api_client_1 = require("../reunite/api/api-client");
19
19
  const DEFAULT_VERSION = 'latest';
20
20
  exports.DESTINATION_REGEX =
21
21
  // eslint-disable-next-line no-useless-escape
@@ -31,7 +31,7 @@ async function handlePush({ argv, config }) {
31
31
  const isAuthorized = await client.isAuthorizedWithRedoclyByRegion();
32
32
  if (!isAuthorized) {
33
33
  try {
34
- const clientToken = await (0, login_1.promptClientToken)(client.domain);
34
+ const clientToken = await (0, auth_1.promptClientToken)(client.domain);
35
35
  await client.login(clientToken);
36
36
  }
37
37
  catch (e) {
package/lib/index.js CHANGED
@@ -1,19 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ const path = require("path");
5
+ const dotenv = require("dotenv");
4
6
  require("./utils/assert-node-version");
5
7
  const yargs = require("yargs");
6
8
  const colors = require("colorette");
7
- const openapi_core_1 = require("@redocly/openapi-core");
8
9
  const types_1 = require("./types");
9
10
  const preview_docs_1 = require("./commands/preview-docs");
10
11
  const stats_1 = require("./commands/stats");
11
12
  const split_1 = require("./commands/split");
12
13
  const join_1 = require("./commands/join");
13
- const push_status_1 = require("./cms/commands/push-status");
14
+ const push_status_1 = require("./reunite/commands/push-status");
14
15
  const lint_1 = require("./commands/lint");
15
16
  const bundle_1 = require("./commands/bundle");
16
- const login_1 = require("./commands/login");
17
+ const auth_1 = require("./commands/auth");
17
18
  const build_docs_1 = require("./commands/build-docs");
18
19
  const update_version_notifier_1 = require("./utils/update-version-notifier");
19
20
  const wrapper_1 = require("./wrapper");
@@ -22,6 +23,7 @@ const translations_1 = require("./commands/translations");
22
23
  const eject_1 = require("./commands/eject");
23
24
  const constants_1 = require("./commands/preview-project/constants");
24
25
  const push_1 = require("./commands/push");
26
+ dotenv.config({ path: path.resolve(process.cwd(), './.env') });
25
27
  if (!('replaceAll' in String.prototype)) {
26
28
  require('core-js/actual/string/replace-all');
27
29
  }
@@ -521,32 +523,32 @@ yargs
521
523
  process.env.REDOCLY_CLI_COMMAND = 'check-config';
522
524
  (0, wrapper_1.commandWrapper)()(argv);
523
525
  })
524
- .command('login', 'Login to the Redocly API registry with an access token.', async (yargs) => yargs.options({
526
+ .command('login', 'Log in to Redocly.', async (yargs) => yargs.options({
525
527
  verbose: {
526
528
  description: 'Include additional output.',
527
529
  type: 'boolean',
528
530
  },
529
- region: {
530
- description: 'Specify a region.',
531
- alias: 'r',
532
- choices: types_1.regionChoices,
531
+ residency: {
532
+ description: 'Residency of the application. Defaults to `us`.',
533
+ alias: ['r', 'region'],
534
+ type: 'string',
533
535
  },
534
536
  config: {
535
537
  description: 'Path to the config file.',
536
538
  requiresArg: true,
537
539
  type: 'string',
538
540
  },
541
+ next: {
542
+ description: 'Use Reunite application to login.',
543
+ type: 'boolean',
544
+ },
539
545
  }), (argv) => {
540
546
  process.env.REDOCLY_CLI_COMMAND = 'login';
541
- (0, wrapper_1.commandWrapper)(login_1.handleLogin)(argv);
547
+ (0, wrapper_1.commandWrapper)(auth_1.handleLogin)(argv);
542
548
  })
543
- .command('logout', 'Clear your stored credentials for the Redocly API registry.', (yargs) => yargs, async (argv) => {
549
+ .command('logout', 'Clear your stored credentials for the Redocly API registry.', (yargs) => yargs, (argv) => {
544
550
  process.env.REDOCLY_CLI_COMMAND = 'logout';
545
- await (0, wrapper_1.commandWrapper)(async () => {
546
- const client = new openapi_core_1.RedoclyClient();
547
- client.logout();
548
- process.stdout.write('Logged out from the Redocly account. ✋\n');
549
- })(argv);
551
+ (0, wrapper_1.commandWrapper)(auth_1.handleLogout)(argv);
550
552
  })
551
553
  .command('preview', 'Preview Redocly project using one of the product NPM packages.', (yargs) => yargs.options({
552
554
  product: {
@@ -726,6 +728,92 @@ yargs
726
728
  }), (argv) => {
727
729
  process.env.REDOCLY_CLI_COMMAND = 'eject';
728
730
  (0, wrapper_1.commandWrapper)(eject_1.handleEject)(argv);
731
+ })
732
+ .command('respect [files...]', 'Run Arazzo tests.', (yargs) => {
733
+ return yargs
734
+ .positional('files', {
735
+ describe: 'Test files or glob pattern.',
736
+ type: 'string',
737
+ array: true,
738
+ default: [],
739
+ })
740
+ .env('REDOCLY_CLI_RESPECT')
741
+ .options({
742
+ input: {
743
+ alias: 'i',
744
+ describe: 'Input parameters.',
745
+ type: 'string',
746
+ },
747
+ server: {
748
+ alias: 'S',
749
+ describe: 'Server parameters.',
750
+ type: 'string',
751
+ },
752
+ workflow: {
753
+ alias: 'w',
754
+ describe: 'Workflow name.',
755
+ type: 'string',
756
+ array: true,
757
+ },
758
+ skip: {
759
+ alias: 's',
760
+ describe: 'Workflow to skip.',
761
+ type: 'string',
762
+ array: true,
763
+ },
764
+ verbose: {
765
+ alias: 'v',
766
+ describe: 'Apply verbose mode.',
767
+ type: 'boolean',
768
+ },
769
+ 'har-output': {
770
+ describe: 'Har file output name.',
771
+ type: 'string',
772
+ },
773
+ 'json-output': {
774
+ describe: 'JSON file output name.',
775
+ type: 'string',
776
+ },
777
+ 'client-cert': {
778
+ describe: 'Mutual TLS client certificate.',
779
+ type: 'string',
780
+ },
781
+ 'client-key': {
782
+ describe: 'Mutual TLS client key.',
783
+ type: 'string',
784
+ },
785
+ 'ca-cert': {
786
+ describe: 'Mutual TLS CA certificate.',
787
+ type: 'string',
788
+ },
789
+ severity: {
790
+ describe: 'Severity of the check.',
791
+ type: 'string',
792
+ },
793
+ });
794
+ }, async (argv) => {
795
+ process.env.REDOCLY_CLI_COMMAND = 'respect';
796
+ const { handleRun } = await Promise.resolve().then(() => require('@redocly/respect-core'));
797
+ (0, wrapper_1.commandWrapper)(handleRun)(argv);
798
+ })
799
+ .command('generate-arazzo <descriptionPath>', 'Auto-generate arazzo description file from an API description.', (yargs) => {
800
+ return yargs
801
+ .positional('descriptionPath', {
802
+ describe: 'Description file path.',
803
+ type: 'string',
804
+ })
805
+ .env('REDOCLY_CLI_RESPECT')
806
+ .options({
807
+ 'output-file': {
808
+ alias: 'o',
809
+ describe: 'Output File name.',
810
+ type: 'string',
811
+ },
812
+ });
813
+ }, async (argv) => {
814
+ process.env.REDOCLY_CLI_COMMAND = 'generate-arazzo';
815
+ const { handleGenerate } = await Promise.resolve().then(() => require('@redocly/respect-core'));
816
+ (0, wrapper_1.commandWrapper)(handleGenerate)(argv);
729
817
  })
730
818
  .completion('completion', 'Generate autocomplete script for `redocly` command.')
731
819
  .demandCommand(1)
package/lib/otel.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { Analytics } from './utils/miscellaneous';
2
+ type Events = {
3
+ [key: string]: Analytics;
4
+ };
5
+ export declare class OtelServerTelemetry {
6
+ init(): void;
7
+ send<K extends keyof Events>(event: K, data: Events[K]): void;
8
+ }
9
+ export declare const otelTelemetry: OtelServerTelemetry;
10
+ export {};
package/lib/otel.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.otelTelemetry = exports.OtelServerTelemetry = void 0;
4
+ const api_1 = require("@opentelemetry/api");
5
+ const resources_1 = require("@opentelemetry/resources");
6
+ const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
7
+ const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
8
+ const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
9
+ const update_version_notifier_1 = require("./utils/update-version-notifier");
10
+ const fetch_with_timeout_1 = require("./utils/fetch-with-timeout");
11
+ const OTEL_TRACES_URL = process.env.OTEL_TRACES_URL || 'https://otel.cloud.redocly.com/v1/traces';
12
+ class OtelServerTelemetry {
13
+ init() {
14
+ const nodeTracerProvider = new sdk_trace_node_1.NodeTracerProvider({
15
+ resource: new resources_1.Resource({
16
+ [semantic_conventions_1.ATTR_SERVICE_NAME]: `redocly-cli`,
17
+ [semantic_conventions_1.ATTR_SERVICE_VERSION]: `@redocly/cli@${update_version_notifier_1.version}`,
18
+ }),
19
+ });
20
+ nodeTracerProvider.addSpanProcessor(new sdk_trace_node_1.SimpleSpanProcessor(new exporter_trace_otlp_http_1.OTLPTraceExporter({
21
+ url: OTEL_TRACES_URL,
22
+ headers: {},
23
+ timeoutMillis: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
24
+ })));
25
+ nodeTracerProvider.register();
26
+ }
27
+ send(event, data) {
28
+ const time = new Date();
29
+ const eventId = crypto.randomUUID();
30
+ const span = api_1.trace.getTracer('CliTelemetry').startSpan(`event.${event}`, {
31
+ attributes: {
32
+ 'cloudevents.event_client.id': eventId,
33
+ 'cloudevents.event_client.type': event,
34
+ },
35
+ startTime: time,
36
+ });
37
+ for (const [key, value] of Object.entries(data)) {
38
+ const keySnakeCase = key.replace(/([A-Z])/g, '_$1').toLowerCase();
39
+ if (value !== undefined) {
40
+ span.setAttribute(`cloudevents.event_data.${keySnakeCase}`, value);
41
+ }
42
+ }
43
+ span.end(time);
44
+ }
45
+ }
46
+ exports.OtelServerTelemetry = OtelServerTelemetry;
47
+ exports.otelTelemetry = new OtelServerTelemetry();
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const domains_1 = require("../domains");
4
+ const domains_2 = require("../domains");
5
+ describe('getDomain()', () => {
6
+ afterEach(() => {
7
+ delete process.env.REDOCLY_DOMAIN;
8
+ });
9
+ it('should return the domain from environment variable', () => {
10
+ process.env.REDOCLY_DOMAIN = 'test-domain';
11
+ expect((0, domains_1.getDomain)()).toBe('test-domain');
12
+ });
13
+ it('should return the default domain if no domain provided', () => {
14
+ process.env.REDOCLY_DOMAIN = '';
15
+ expect((0, domains_1.getDomain)()).toBe('https://app.cloud.redocly.com');
16
+ });
17
+ });
18
+ describe('getReuniteUrl()', () => {
19
+ it('should return US API URL when US region specified', () => {
20
+ expect((0, domains_2.getReuniteUrl)('us')).toBe('https://app.cloud.redocly.com/api');
21
+ });
22
+ it('should return EU API URL when EU region specified', () => {
23
+ expect((0, domains_2.getReuniteUrl)('eu')).toBe('https://app.cloud.eu.redocly.com/api');
24
+ });
25
+ it('should return custom domain API URL when custom domain specified', () => {
26
+ const customDomain = 'https://custom.domain.com';
27
+ expect((0, domains_2.getReuniteUrl)(customDomain)).toBe('https://custom.domain.com/api');
28
+ });
29
+ it('should return US API URL when no region specified', () => {
30
+ expect((0, domains_2.getReuniteUrl)()).toBe('https://app.cloud.redocly.com/api');
31
+ });
32
+ });
@@ -15,6 +15,15 @@ export declare class ReuniteApiError extends Error {
15
15
  status: number;
16
16
  constructor(message: string, status: number);
17
17
  }
18
+ export declare class ReuniteApiClient implements BaseApiClient {
19
+ protected version: string;
20
+ protected command: string;
21
+ sunsetWarnings: SunsetWarningsBuffer;
22
+ constructor(version: string, command: string);
23
+ request(url: string, options: FetchWithTimeoutOptions): Promise<Response>;
24
+ private collectSunsetWarning;
25
+ private getSunsetDate;
26
+ }
18
27
  declare class RemotesApi {
19
28
  private client;
20
29
  private readonly domain;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ReuniteApi = exports.ReuniteApiError = void 0;
3
+ exports.ReuniteApi = exports.ReuniteApiClient = exports.ReuniteApiError = void 0;
4
4
  exports.streamToBuffer = streamToBuffer;
5
5
  const colorette_1 = require("colorette");
6
6
  const fetch_with_timeout_1 = require("../../utils/fetch-with-timeout");
@@ -59,6 +59,7 @@ class ReuniteApiClient {
59
59
  return Date.parse(sunsetDate);
60
60
  }
61
61
  }
62
+ exports.ReuniteApiClient = ReuniteApiClient;
62
63
  class RemotesApi {
63
64
  constructor(client, domain, apiKey) {
64
65
  this.client = client;
@@ -0,0 +1,4 @@
1
+ import type { Region } from '@redocly/openapi-core';
2
+ export declare const REUNITE_URLS: Record<Region, string>;
3
+ export declare function getDomain(): string;
4
+ export declare function getReuniteUrl(residency?: string): string;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REUNITE_URLS = void 0;
4
+ exports.getDomain = getDomain;
5
+ exports.getReuniteUrl = getReuniteUrl;
6
+ exports.REUNITE_URLS = {
7
+ us: 'https://app.cloud.redocly.com',
8
+ eu: 'https://app.cloud.eu.redocly.com',
9
+ };
10
+ function getDomain() {
11
+ return process.env.REDOCLY_DOMAIN || exports.REUNITE_URLS.us;
12
+ }
13
+ function getReuniteUrl(residency) {
14
+ if (!residency)
15
+ residency = 'us';
16
+ let reuniteUrl = exports.REUNITE_URLS[residency];
17
+ if (!reuniteUrl) {
18
+ reuniteUrl = residency;
19
+ }
20
+ const url = new URL('/api', reuniteUrl).toString();
21
+ return url;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/lib/types.d.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import type { BundleOutputFormat, Region, Config, RuleSeverity } from '@redocly/openapi-core';
2
+ import type { RespectOptions, GenerateArazzoFileOptions } from '@redocly/respect-core';
2
3
  import type { ArgumentsCamelCase } from 'yargs';
3
4
  import type { LintOptions } from './commands/lint';
4
5
  import type { BundleOptions } from './commands/bundle';
5
6
  import type { JoinOptions } from './commands/join';
6
- import type { LoginOptions } from './commands/login';
7
+ import type { LoginOptions, LogoutOptions } from './commands/auth';
7
8
  import type { PushOptions } from './commands/push';
8
9
  import type { StatsOptions } from './commands/stats';
9
10
  import type { SplitOptions } from './commands/split';
10
11
  import type { PreviewDocsOptions } from './commands/preview-docs';
11
12
  import type { BuildDocsArgv } from './commands/build-docs/types';
12
- import type { PushOptions as CMSPushOptions } from './cms/commands/push';
13
- import type { PushStatusOptions } from './cms/commands/push-status';
13
+ import type { PushOptions as CMSPushOptions } from './reunite/commands/push';
14
+ import type { PushStatusOptions } from './reunite/commands/push-status';
14
15
  import type { PreviewProjectOptions } from './commands/preview-project/types';
15
16
  import type { TranslationsOptions } from './commands/translations';
16
17
  import type { EjectOptions } from './commands/eject';
@@ -27,7 +28,7 @@ export type Entrypoint = {
27
28
  export declare const outputExtensions: ReadonlyArray<BundleOutputFormat>;
28
29
  export type OutputExtensions = 'json' | 'yaml' | 'yml' | undefined;
29
30
  export declare const regionChoices: ReadonlyArray<Region>;
30
- export type CommandOptions = StatsOptions | SplitOptions | JoinOptions | PushOptions | CMSPushOptions | LintOptions | BundleOptions | LoginOptions | PreviewDocsOptions | BuildDocsArgv | PushStatusOptions | PreviewProjectOptions | TranslationsOptions | EjectOptions;
31
+ export type CommandOptions = StatsOptions | SplitOptions | JoinOptions | PushOptions | CMSPushOptions | LintOptions | BundleOptions | LoginOptions | LogoutOptions | PreviewDocsOptions | BuildDocsArgv | PushStatusOptions | PreviewProjectOptions | TranslationsOptions | EjectOptions | RespectOptions | GenerateArazzoFileOptions;
31
32
  export type VerifyConfigOptions = {
32
33
  config?: string;
33
34
  'lint-config'?: RuleSeverity;
@@ -59,17 +59,18 @@ export type ExitCode = 0 | 1 | 2;
59
59
  export type Analytics = {
60
60
  event: string;
61
61
  event_time: string;
62
- logged_in: boolean;
63
- command: string | number;
64
- arguments: Record<string, unknown>;
62
+ logged_in: 'yes' | 'no';
63
+ command: string;
64
+ arguments: string;
65
65
  node_version: string;
66
66
  npm_version: string;
67
+ os_platform: string;
67
68
  version: string;
68
69
  exit_code: ExitCode;
69
70
  environment?: string;
70
71
  environment_ci?: string;
71
72
  raw_input: string;
72
- has_config?: boolean;
73
+ has_config?: 'yes' | 'no';
73
74
  spec_version?: string;
74
75
  spec_keyword?: string;
75
76
  spec_full_version?: string;
@@ -37,6 +37,7 @@ const colorette_1 = require("colorette");
37
37
  const perf_hooks_1 = require("perf_hooks");
38
38
  const glob = require("glob");
39
39
  const fs = require("fs");
40
+ const os = require("os");
40
41
  const readline = require("readline");
41
42
  const stream_1 = require("stream");
42
43
  const child_process_1 = require("child_process");
@@ -48,7 +49,7 @@ const reference_docs_config_schema_1 = require("@redocly/config/lib/reference-do
48
49
  const types_1 = require("../types");
49
50
  const update_version_notifier_1 = require("./update-version-notifier");
50
51
  const push_1 = require("../commands/push");
51
- const fetch_with_timeout_1 = require("./fetch-with-timeout");
52
+ const api_1 = require("../reunite/api");
52
53
  async function getFallbackApisOrExit(argsApis, config) {
53
54
  const { apis } = config;
54
55
  const shouldFallbackToAllDefinitions = !(0, utils_1.isNotEmptyArray)(argsApis) && (0, utils_1.isNotEmptyObject)(apis);
@@ -448,33 +449,32 @@ async function sendTelemetry(argv, exit_code, has_config, spec_version, spec_key
448
449
  const { _: [command], $0: _, ...args } = argv;
449
450
  const event_time = new Date().toISOString();
450
451
  const redoclyClient = new openapi_core_1.RedoclyClient();
451
- const logged_in = redoclyClient.hasTokens();
452
+ const { RedoclyOAuthClient } = await Promise.resolve().then(() => require('../auth/oauth-client'));
453
+ const oauthClient = new RedoclyOAuthClient('redocly-cli', update_version_notifier_1.version);
454
+ const reuniteUrl = (0, api_1.getReuniteUrl)(argv.residency);
455
+ const logged_in = redoclyClient.hasTokens() || (await oauthClient.isAuthorized(reuniteUrl));
452
456
  const data = {
453
457
  event: 'cli_command',
454
458
  event_time,
455
- logged_in,
456
- command,
457
- arguments: cleanArgs(args),
459
+ logged_in: logged_in ? 'yes' : 'no',
460
+ command: `${command}`,
461
+ arguments: JSON.stringify(cleanArgs(args)),
458
462
  node_version: process.version,
459
463
  npm_version: (0, child_process_1.execSync)('npm -v').toString().replace('\n', ''),
464
+ os_platform: os.platform(),
460
465
  version: update_version_notifier_1.version,
461
466
  exit_code,
462
467
  environment: process.env.REDOCLY_ENVIRONMENT,
463
468
  environment_ci: process.env.CI,
464
469
  raw_input: cleanRawInput(process.argv.slice(2)),
465
- has_config,
470
+ has_config: has_config ? 'yes' : 'no',
466
471
  spec_version,
467
472
  spec_keyword,
468
473
  spec_full_version,
469
474
  };
470
- await (0, fetch_with_timeout_1.default)(`https://api.redocly.com/registry/telemetry/cli`, {
471
- timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
472
- method: 'POST',
473
- headers: {
474
- 'content-type': 'application/json',
475
- },
476
- body: JSON.stringify(data),
477
- });
475
+ const { otelTelemetry } = await Promise.resolve().then(() => require('../otel'));
476
+ otelTelemetry.init();
477
+ otelTelemetry.send(data.command, data);
478
478
  }
479
479
  catch (err) {
480
480
  // Do nothing.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@redocly/cli",
3
- "version": "1.29.0",
3
+ "version": "1.31.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "bin": {
7
- "openapi": "bin/cli.js",
8
- "redocly": "bin/cli.js"
7
+ "redocly": "bin/cli.js",
8
+ "openapi": "bin/cli.js"
9
9
  },
10
10
  "engines": {
11
11
  "node": ">=18.17.0",
@@ -30,22 +30,29 @@
30
30
  "OpenAPI linter",
31
31
  "Swagger linter",
32
32
  "AsyncAPI linter",
33
+ "Arazzo linter",
33
34
  "oas"
34
35
  ],
35
36
  "contributors": [
36
37
  "Roman Hotsiy <roman@redocly.com> (https://redocly.com/)"
37
38
  ],
38
39
  "dependencies": {
39
- "@redocly/openapi-core": "1.29.0",
40
+ "@redocly/openapi-core": "1.31.0",
40
41
  "abort-controller": "^3.0.0",
41
42
  "chokidar": "^3.5.1",
42
43
  "colorette": "^1.2.0",
43
44
  "core-js": "^3.32.1",
45
+ "dotenv": "^16.4.7",
44
46
  "form-data": "^4.0.0",
45
47
  "get-port-please": "^3.0.1",
46
48
  "glob": "^7.1.6",
47
49
  "handlebars": "^4.7.6",
48
50
  "mobx": "^6.0.4",
51
+ "@opentelemetry/api": "1.9.0",
52
+ "@opentelemetry/exporter-trace-otlp-http": "0.53.0",
53
+ "@opentelemetry/resources": "1.26.0",
54
+ "@opentelemetry/sdk-trace-node": "1.26.0",
55
+ "@opentelemetry/semantic-conventions": "1.27.0",
49
56
  "pluralize": "^8.0.0",
50
57
  "react": "^17.0.0 || ^18.2.0 || ^19.0.0",
51
58
  "react-dom": "^17.0.0 || ^18.2.0 || ^19.0.0",
@@ -1,6 +1,6 @@
1
1
  import { getMergedConfig } from '@redocly/openapi-core';
2
2
  import { handlePush } from '../../commands/push';
3
- import { promptClientToken } from '../../commands/login';
3
+ import { promptClientToken } from '../../commands/auth';
4
4
  import { ConfigFixture } from '../fixtures/config';
5
5
  import { Readable } from 'node:stream';
6
6
 
@@ -23,7 +23,7 @@ jest.mock('fs', () => ({
23
23
 
24
24
  // Mock OpenAPI core
25
25
  jest.mock('@redocly/openapi-core');
26
- jest.mock('../../commands/login');
26
+ jest.mock('../../commands/auth');
27
27
  jest.mock('../../utils/miscellaneous');
28
28
 
29
29
  const mockPromptClientToken = promptClientToken as jest.MockedFunction<typeof promptClientToken>;
@@ -0,0 +1,73 @@
1
+ import { RedoclyOAuthDeviceFlow } from '../device-flow';
2
+
3
+ jest.mock('child_process');
4
+
5
+ describe('RedoclyOAuthDeviceFlow', () => {
6
+ const mockBaseUrl = 'https://test.redocly.com';
7
+ const mockClientName = 'test-client';
8
+ const mockVersion = '1.0.0';
9
+ let flow: RedoclyOAuthDeviceFlow;
10
+
11
+ beforeEach(() => {
12
+ flow = new RedoclyOAuthDeviceFlow(mockBaseUrl, mockClientName, mockVersion);
13
+ jest.resetAllMocks();
14
+ });
15
+
16
+ describe('verifyToken', () => {
17
+ it('returns true for valid token', async () => {
18
+ jest.spyOn(flow['apiClient'], 'request').mockResolvedValue({
19
+ json: () => Promise.resolve({ user: { id: '123' } }),
20
+ } as Response);
21
+
22
+ const result = await flow.verifyToken('valid-token');
23
+ expect(result).toBe(true);
24
+ });
25
+
26
+ it('returns false for invalid token', async () => {
27
+ jest.spyOn(flow['apiClient'], 'request').mockRejectedValue(new Error('Invalid token'));
28
+ const result = await flow.verifyToken('invalid-token');
29
+ expect(result).toBe(false);
30
+ });
31
+ });
32
+
33
+ describe('verifyApiKey', () => {
34
+ it('returns true for valid API key', async () => {
35
+ jest.spyOn(flow['apiClient'], 'request').mockResolvedValue({
36
+ json: () => Promise.resolve({ success: true }),
37
+ } as Response);
38
+
39
+ const result = await flow.verifyApiKey('valid-key');
40
+ expect(result).toBe(true);
41
+ });
42
+
43
+ it('returns false for invalid API key', async () => {
44
+ jest.spyOn(flow['apiClient'], 'request').mockRejectedValue(new Error('Invalid API key'));
45
+ const result = await flow.verifyApiKey('invalid-key');
46
+ expect(result).toBe(false);
47
+ });
48
+ });
49
+
50
+ describe('refreshToken', () => {
51
+ it('successfully refreshes token', async () => {
52
+ const mockResponse = {
53
+ access_token: 'new-token',
54
+ refresh_token: 'new-refresh',
55
+ expires_in: 3600,
56
+ };
57
+ jest.spyOn(flow['apiClient'], 'request').mockResolvedValue({
58
+ json: () => Promise.resolve(mockResponse),
59
+ } as Response);
60
+
61
+ const result = await flow.refreshToken('old-refresh-token');
62
+ expect(result).toEqual(mockResponse);
63
+ });
64
+
65
+ it('throws error when refresh fails', async () => {
66
+ jest.spyOn(flow['apiClient'], 'request').mockResolvedValue({
67
+ json: () => Promise.resolve({}),
68
+ } as Response);
69
+
70
+ await expect(flow.refreshToken('invalid-refresh')).rejects.toThrow('Failed to refresh token');
71
+ });
72
+ });
73
+ });