@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
package/src/otel.ts ADDED
@@ -0,0 +1,59 @@
1
+ import { trace } from '@opentelemetry/api';
2
+ import { Resource as OtelResource } from '@opentelemetry/resources';
3
+ import { NodeTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
4
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
5
+ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
6
+ import { version } from './utils/update-version-notifier';
7
+ import { DEFAULT_FETCH_TIMEOUT } from './utils/fetch-with-timeout';
8
+
9
+ import type { Analytics } from './utils/miscellaneous';
10
+
11
+ type Events = {
12
+ [key: string]: Analytics;
13
+ };
14
+
15
+ const OTEL_TRACES_URL = process.env.OTEL_TRACES_URL || 'https://otel.cloud.redocly.com/v1/traces';
16
+
17
+ export class OtelServerTelemetry {
18
+ init() {
19
+ const nodeTracerProvider = new NodeTracerProvider({
20
+ resource: new OtelResource({
21
+ [ATTR_SERVICE_NAME]: `redocly-cli`,
22
+ [ATTR_SERVICE_VERSION]: `@redocly/cli@${version}`,
23
+ }),
24
+ });
25
+
26
+ nodeTracerProvider.addSpanProcessor(
27
+ new SimpleSpanProcessor(
28
+ new OTLPTraceExporter({
29
+ url: OTEL_TRACES_URL,
30
+ headers: {},
31
+ timeoutMillis: DEFAULT_FETCH_TIMEOUT,
32
+ })
33
+ )
34
+ );
35
+
36
+ nodeTracerProvider.register();
37
+ }
38
+
39
+ send<K extends keyof Events>(event: K, data: Events[K]): void {
40
+ const time = new Date();
41
+ const eventId = crypto.randomUUID();
42
+ const span = trace.getTracer('CliTelemetry').startSpan(`event.${event}`, {
43
+ attributes: {
44
+ 'cloudevents.event_client.id': eventId,
45
+ 'cloudevents.event_client.type': event,
46
+ },
47
+ startTime: time,
48
+ });
49
+ for (const [key, value] of Object.entries(data)) {
50
+ const keySnakeCase = key.replace(/([A-Z])/g, '_$1').toLowerCase();
51
+ if (value !== undefined) {
52
+ span.setAttribute(`cloudevents.event_data.${keySnakeCase}`, value);
53
+ }
54
+ }
55
+ span.end(time);
56
+ }
57
+ }
58
+
59
+ export const otelTelemetry = new OtelServerTelemetry();
@@ -0,0 +1,41 @@
1
+ import { getDomain } from '../domains';
2
+ import { getReuniteUrl } from '../domains';
3
+
4
+ import type { Region } from '@redocly/openapi-core';
5
+
6
+ describe('getDomain()', () => {
7
+ afterEach(() => {
8
+ delete process.env.REDOCLY_DOMAIN;
9
+ });
10
+
11
+ it('should return the domain from environment variable', () => {
12
+ process.env.REDOCLY_DOMAIN = 'test-domain';
13
+
14
+ expect(getDomain()).toBe('test-domain');
15
+ });
16
+
17
+ it('should return the default domain if no domain provided', () => {
18
+ process.env.REDOCLY_DOMAIN = '';
19
+
20
+ expect(getDomain()).toBe('https://app.cloud.redocly.com');
21
+ });
22
+ });
23
+
24
+ describe('getReuniteUrl()', () => {
25
+ it('should return US API URL when US region specified', () => {
26
+ expect(getReuniteUrl('us')).toBe('https://app.cloud.redocly.com/api');
27
+ });
28
+
29
+ it('should return EU API URL when EU region specified', () => {
30
+ expect(getReuniteUrl('eu')).toBe('https://app.cloud.eu.redocly.com/api');
31
+ });
32
+
33
+ it('should return custom domain API URL when custom domain specified', () => {
34
+ const customDomain = 'https://custom.domain.com';
35
+ expect(getReuniteUrl(customDomain as Region)).toBe('https://custom.domain.com/api');
36
+ });
37
+
38
+ it('should return US API URL when no region specified', () => {
39
+ expect(getReuniteUrl()).toBe('https://app.cloud.redocly.com/api');
40
+ });
41
+ });
@@ -26,7 +26,7 @@ export class ReuniteApiError extends Error {
26
26
  }
27
27
  }
28
28
 
29
- class ReuniteApiClient implements BaseApiClient {
29
+ export class ReuniteApiClient implements BaseApiClient {
30
30
  public sunsetWarnings: SunsetWarningsBuffer = [];
31
31
 
32
32
  constructor(protected version: string, protected command: string) {}
@@ -0,0 +1,23 @@
1
+ import type { Region } from '@redocly/openapi-core';
2
+
3
+ export const REUNITE_URLS: Record<Region, string> = {
4
+ us: 'https://app.cloud.redocly.com',
5
+ eu: 'https://app.cloud.eu.redocly.com',
6
+ } as const;
7
+
8
+ export function getDomain(): string {
9
+ return process.env.REDOCLY_DOMAIN || REUNITE_URLS.us;
10
+ }
11
+
12
+ export function getReuniteUrl(residency?: string) {
13
+ if (!residency) residency = 'us';
14
+
15
+ let reuniteUrl: string = REUNITE_URLS[residency as Region];
16
+
17
+ if (!reuniteUrl) {
18
+ reuniteUrl = residency;
19
+ }
20
+
21
+ const url = new URL('/api', reuniteUrl).toString();
22
+ return url;
23
+ }
package/src/types.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';
@@ -37,12 +38,15 @@ export type CommandOptions =
37
38
  | LintOptions
38
39
  | BundleOptions
39
40
  | LoginOptions
41
+ | LogoutOptions
40
42
  | PreviewDocsOptions
41
43
  | BuildDocsArgv
42
44
  | PushStatusOptions
43
45
  | PreviewProjectOptions
44
46
  | TranslationsOptions
45
- | EjectOptions;
47
+ | EjectOptions
48
+ | RespectOptions
49
+ | GenerateArazzoFileOptions;
46
50
 
47
51
  export type VerifyConfigOptions = {
48
52
  config?: string;
@@ -3,6 +3,7 @@ import { blue, gray, green, red, yellow } from 'colorette';
3
3
  import { performance } from 'perf_hooks';
4
4
  import * as glob from 'glob';
5
5
  import * as fs from 'fs';
6
+ import * as os from 'os';
6
7
  import * as readline from 'readline';
7
8
  import { Writable } from 'stream';
8
9
  import { execSync } from 'child_process';
@@ -28,7 +29,7 @@ import { deprecatedRefDocsSchema } from '@redocly/config/lib/reference-docs-conf
28
29
  import { outputExtensions } from '../types';
29
30
  import { version } from './update-version-notifier';
30
31
  import { DESTINATION_REGEX } from '../commands/push';
31
- import fetch, { DEFAULT_FETCH_TIMEOUT } from './fetch-with-timeout';
32
+ import { getReuniteUrl } from '../reunite/api';
32
33
 
33
34
  import type { Arguments } from 'yargs';
34
35
  import type {
@@ -565,33 +566,32 @@ export async function sendTelemetry(
565
566
  } = argv;
566
567
  const event_time = new Date().toISOString();
567
568
  const redoclyClient = new RedoclyClient();
568
- const logged_in = redoclyClient.hasTokens();
569
+ const { RedoclyOAuthClient } = await import('../auth/oauth-client');
570
+ const oauthClient = new RedoclyOAuthClient('redocly-cli', version);
571
+ const reuniteUrl = getReuniteUrl(argv.residency as string | undefined);
572
+ const logged_in = redoclyClient.hasTokens() || (await oauthClient.isAuthorized(reuniteUrl));
569
573
  const data: Analytics = {
570
574
  event: 'cli_command',
571
575
  event_time,
572
- logged_in,
573
- command,
574
- arguments: cleanArgs(args),
576
+ logged_in: logged_in ? 'yes' : 'no',
577
+ command: `${command}`,
578
+ arguments: JSON.stringify(cleanArgs(args)),
575
579
  node_version: process.version,
576
580
  npm_version: execSync('npm -v').toString().replace('\n', ''),
581
+ os_platform: os.platform(),
577
582
  version,
578
583
  exit_code,
579
584
  environment: process.env.REDOCLY_ENVIRONMENT,
580
585
  environment_ci: process.env.CI,
581
586
  raw_input: cleanRawInput(process.argv.slice(2)),
582
- has_config,
587
+ has_config: has_config ? 'yes' : 'no',
583
588
  spec_version,
584
589
  spec_keyword,
585
590
  spec_full_version,
586
591
  };
587
- await fetch(`https://api.redocly.com/registry/telemetry/cli`, {
588
- timeout: DEFAULT_FETCH_TIMEOUT,
589
- method: 'POST',
590
- headers: {
591
- 'content-type': 'application/json',
592
- },
593
- body: JSON.stringify(data),
594
- });
592
+ const { otelTelemetry } = await import('../otel');
593
+ otelTelemetry.init();
594
+ otelTelemetry.send(data.command, data);
595
595
  } catch (err) {
596
596
  // Do nothing.
597
597
  }
@@ -602,17 +602,18 @@ export type ExitCode = 0 | 1 | 2;
602
602
  export type Analytics = {
603
603
  event: string;
604
604
  event_time: string;
605
- logged_in: boolean;
606
- command: string | number;
607
- arguments: Record<string, unknown>;
605
+ logged_in: 'yes' | 'no';
606
+ command: string;
607
+ arguments: string;
608
608
  node_version: string;
609
609
  npm_version: string;
610
+ os_platform: string;
610
611
  version: string;
611
612
  exit_code: ExitCode;
612
613
  environment?: string;
613
614
  environment_ci?: string;
614
615
  raw_input: string;
615
- has_config?: boolean;
616
+ has_config?: 'yes' | 'no';
616
617
  spec_version?: string;
617
618
  spec_keyword?: string;
618
619
  spec_full_version?: string;
package/tsconfig.json CHANGED
@@ -4,6 +4,6 @@
4
4
  "rootDir": "src",
5
5
  "outDir": "lib"
6
6
  },
7
- "references": [{ "path": "../core" }],
7
+ "references": [{ "path": "../core" }, { "path": "../respect-core" }],
8
8
  "exclude": ["lib"]
9
9
  }