@redocly/cli 1.22.1 → 1.23.1

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 (51) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/lib/__tests__/commands/bundle.test.js +146 -1
  3. package/lib/__tests__/fetch-with-timeout.test.js +29 -5
  4. package/lib/__tests__/utils.test.js +54 -32
  5. package/lib/cms/api/__tests__/api.client.test.js +17 -9
  6. package/lib/cms/api/api-client.d.ts +26 -7
  7. package/lib/cms/api/api-client.js +103 -72
  8. package/lib/cms/commands/__tests__/push-status.test.js +1 -1
  9. package/lib/cms/commands/__tests__/push.test.js +41 -1
  10. package/lib/cms/commands/__tests__/utils.test.js +1 -1
  11. package/lib/cms/commands/push-status.d.ts +1 -1
  12. package/lib/cms/commands/push-status.js +3 -7
  13. package/lib/cms/commands/push.js +4 -4
  14. package/lib/cms/commands/utils.d.ts +3 -0
  15. package/lib/cms/commands/utils.js +8 -1
  16. package/lib/commands/bundle.d.ts +1 -1
  17. package/lib/commands/bundle.js +15 -9
  18. package/lib/commands/eject.d.ts +1 -1
  19. package/lib/commands/eject.js +1 -1
  20. package/lib/commands/preview-project/index.js +1 -1
  21. package/lib/index.js +1 -2
  22. package/lib/types.d.ts +1 -0
  23. package/lib/utils/__mocks__/miscellaneous.d.ts +1 -0
  24. package/lib/utils/__mocks__/miscellaneous.js +2 -1
  25. package/lib/utils/fetch-with-timeout.d.ts +6 -1
  26. package/lib/utils/fetch-with-timeout.js +16 -14
  27. package/lib/utils/miscellaneous.d.ts +10 -1
  28. package/lib/utils/miscellaneous.js +24 -20
  29. package/lib/utils/update-version-notifier.js +8 -4
  30. package/package.json +2 -2
  31. package/src/__tests__/commands/bundle.test.ts +172 -4
  32. package/src/__tests__/fetch-with-timeout.test.ts +36 -6
  33. package/src/__tests__/utils.test.ts +58 -33
  34. package/src/cms/api/__tests__/api.client.test.ts +20 -11
  35. package/src/cms/api/api-client.ts +158 -91
  36. package/src/cms/commands/__tests__/push-status.test.ts +1 -1
  37. package/src/cms/commands/__tests__/push.test.ts +49 -2
  38. package/src/cms/commands/__tests__/utils.test.ts +1 -1
  39. package/src/cms/commands/push-status.ts +5 -9
  40. package/src/cms/commands/push.ts +5 -6
  41. package/src/cms/commands/utils.ts +15 -1
  42. package/src/commands/bundle.ts +20 -12
  43. package/src/commands/eject.ts +2 -2
  44. package/src/commands/preview-project/index.ts +1 -1
  45. package/src/index.ts +1 -2
  46. package/src/types.ts +1 -0
  47. package/src/utils/__mocks__/miscellaneous.ts +1 -0
  48. package/src/utils/fetch-with-timeout.ts +23 -14
  49. package/src/utils/miscellaneous.ts +45 -30
  50. package/src/utils/update-version-notifier.ts +11 -5
  51. package/tsconfig.tsbuildinfo +1 -1
@@ -1,63 +1,89 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ReuniteApiClient = void 0;
4
- const node_fetch_1 = require("node-fetch");
3
+ exports.ReuniteApiClient = exports.ReuniteApiError = void 0;
5
4
  const FormData = require("form-data");
6
- const openapi_core_1 = require("@redocly/openapi-core");
7
5
  const fetch_with_timeout_1 = require("../../utils/fetch-with-timeout");
8
- class RemotesApiClient {
9
- constructor(domain, apiKey) {
10
- this.domain = domain;
11
- this.apiKey = apiKey;
6
+ class ReuniteApiError extends Error {
7
+ constructor(message, status) {
8
+ super(message);
9
+ this.status = status;
10
+ }
11
+ }
12
+ exports.ReuniteApiError = ReuniteApiError;
13
+ class ReuniteBaseApiClient {
14
+ constructor(version, command) {
15
+ this.version = version;
16
+ this.command = command;
12
17
  }
13
18
  async getParsedResponse(response) {
14
19
  const responseBody = await response.json();
15
20
  if (response.ok) {
16
21
  return responseBody;
17
22
  }
18
- throw new Error(responseBody.title || response.statusText);
23
+ throw new ReuniteApiError(`${responseBody.title || response.statusText || 'Unknown error'}.`, response.status);
19
24
  }
20
- async getDefaultBranch(organizationId, projectId) {
21
- const response = await (0, fetch_with_timeout_1.default)(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`, {
22
- method: 'GET',
23
- headers: {
24
- 'Content-Type': 'application/json',
25
- Authorization: `Bearer ${this.apiKey}`,
26
- },
25
+ request(url, options) {
26
+ const headers = {
27
+ ...options.headers,
28
+ 'user-agent': `redocly-cli/${this.version.trim()} ${this.command}`,
29
+ };
30
+ return (0, fetch_with_timeout_1.default)(url, {
31
+ ...options,
32
+ headers,
27
33
  });
28
- if (!response) {
29
- throw new Error(`Failed to get default branch.`);
30
- }
34
+ }
35
+ }
36
+ class RemotesApiClient extends ReuniteBaseApiClient {
37
+ constructor(domain, apiKey, version, command) {
38
+ super(version, command);
39
+ this.domain = domain;
40
+ this.apiKey = apiKey;
41
+ }
42
+ async getDefaultBranch(organizationId, projectId) {
31
43
  try {
44
+ const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`, {
45
+ timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
46
+ method: 'GET',
47
+ headers: {
48
+ 'Content-Type': 'application/json',
49
+ Authorization: `Bearer ${this.apiKey}`,
50
+ },
51
+ });
32
52
  const source = await this.getParsedResponse(response);
33
53
  return source.branchName;
34
54
  }
35
55
  catch (err) {
36
- throw new Error(`Failed to fetch default branch: ${err.message || 'Unknown error'}`);
56
+ const message = `Failed to fetch default branch. ${err.message}`;
57
+ if (err instanceof ReuniteApiError) {
58
+ throw new ReuniteApiError(message, err.status);
59
+ }
60
+ throw new Error(message);
37
61
  }
38
62
  }
39
63
  async upsert(organizationId, projectId, remote) {
40
- const response = await (0, fetch_with_timeout_1.default)(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`, {
41
- method: 'POST',
42
- headers: {
43
- 'Content-Type': 'application/json',
44
- Authorization: `Bearer ${this.apiKey}`,
45
- },
46
- body: JSON.stringify({
47
- mountPath: remote.mountPath,
48
- mountBranchName: remote.mountBranchName,
49
- type: 'CICD',
50
- autoMerge: true,
51
- }),
52
- });
53
- if (!response) {
54
- throw new Error(`Failed to upsert.`);
55
- }
56
64
  try {
65
+ const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`, {
66
+ timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ Authorization: `Bearer ${this.apiKey}`,
71
+ },
72
+ body: JSON.stringify({
73
+ mountPath: remote.mountPath,
74
+ mountBranchName: remote.mountBranchName,
75
+ type: 'CICD',
76
+ autoMerge: true,
77
+ }),
78
+ });
57
79
  return await this.getParsedResponse(response);
58
80
  }
59
81
  catch (err) {
60
- throw new Error(`Failed to upsert remote: ${err.message || 'Unknown error'}`);
82
+ const message = `Failed to upsert remote. ${err.message}`;
83
+ if (err instanceof ReuniteApiError) {
84
+ throw new ReuniteApiError(message, err.status);
85
+ }
86
+ throw new Error(message);
61
87
  }
62
88
  }
63
89
  async push(organizationId, projectId, payload, files) {
@@ -76,63 +102,68 @@ class RemotesApiClient {
76
102
  formData.append(`files[${file.path}]`, file.stream);
77
103
  }
78
104
  payload.isMainBranch && formData.append('isMainBranch', 'true');
79
- const response = await (0, node_fetch_1.default)(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes`, {
80
- method: 'POST',
81
- headers: {
82
- Authorization: `Bearer ${this.apiKey}`,
83
- },
84
- body: formData,
85
- agent: (0, openapi_core_1.getProxyAgent)(),
86
- });
87
105
  try {
106
+ const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes`, {
107
+ method: 'POST',
108
+ headers: {
109
+ Authorization: `Bearer ${this.apiKey}`,
110
+ },
111
+ body: formData,
112
+ });
88
113
  return await this.getParsedResponse(response);
89
114
  }
90
115
  catch (err) {
91
- throw new Error(`Failed to push: ${err.message || 'Unknown error'}`);
116
+ const message = `Failed to push. ${err.message}`;
117
+ if (err instanceof ReuniteApiError) {
118
+ throw new ReuniteApiError(message, err.status);
119
+ }
120
+ throw new Error(message);
92
121
  }
93
122
  }
94
- async getRemotesList(organizationId, projectId, mountPath) {
95
- const response = await (0, fetch_with_timeout_1.default)(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`, {
96
- method: 'GET',
97
- headers: {
98
- 'Content-Type': 'application/json',
99
- Authorization: `Bearer ${this.apiKey}`,
100
- },
101
- });
102
- if (!response) {
103
- throw new Error(`Failed to get remotes list.`);
104
- }
123
+ async getRemotesList({ organizationId, projectId, mountPath, }) {
105
124
  try {
125
+ const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`, {
126
+ timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
127
+ method: 'GET',
128
+ headers: {
129
+ 'Content-Type': 'application/json',
130
+ Authorization: `Bearer ${this.apiKey}`,
131
+ },
132
+ });
106
133
  return await this.getParsedResponse(response);
107
134
  }
108
135
  catch (err) {
109
- throw new Error(`Failed to get remote list: ${err.message || 'Unknown error'}`);
136
+ const message = `Failed to get remote list. ${err.message}`;
137
+ if (err instanceof ReuniteApiError) {
138
+ throw new ReuniteApiError(message, err.status);
139
+ }
140
+ throw new Error(message);
110
141
  }
111
142
  }
112
143
  async getPush({ organizationId, projectId, pushId, }) {
113
- const response = await (0, fetch_with_timeout_1.default)(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes/${pushId}`, {
114
- method: 'GET',
115
- headers: {
116
- 'Content-Type': 'application/json',
117
- Authorization: `Bearer ${this.apiKey}`,
118
- },
119
- });
120
- if (!response) {
121
- throw new Error(`Failed to get push status.`);
122
- }
123
144
  try {
145
+ const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes/${pushId}`, {
146
+ timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
147
+ method: 'GET',
148
+ headers: {
149
+ 'Content-Type': 'application/json',
150
+ Authorization: `Bearer ${this.apiKey}`,
151
+ },
152
+ });
124
153
  return await this.getParsedResponse(response);
125
154
  }
126
155
  catch (err) {
127
- throw new Error(`Failed to get push status: ${err.message || 'Unknown error'}`);
156
+ const message = `Failed to get push status. ${err.message}`;
157
+ if (err instanceof ReuniteApiError) {
158
+ throw new ReuniteApiError(message, err.status);
159
+ }
160
+ throw new Error(message);
128
161
  }
129
162
  }
130
163
  }
131
164
  class ReuniteApiClient {
132
- constructor(domain, apiKey) {
133
- this.domain = domain;
134
- this.apiKey = apiKey;
135
- this.remotes = new RemotesApiClient(this.domain, this.apiKey);
165
+ constructor({ domain, apiKey, version, command, }) {
166
+ this.remotes = new RemotesApiClient(domain, apiKey, version, command);
136
167
  }
137
168
  }
138
169
  exports.ReuniteApiClient = ReuniteApiClient;
@@ -554,7 +554,7 @@ describe('handlePushStatus()', () => {
554
554
  config: mockConfig,
555
555
  version: 'cli-version',
556
556
  })).rejects.toThrowErrorMatchingInlineSnapshot(`
557
- "✗ Failed to get push status. Reason: Timeout exceeded
557
+ "✗ Failed to get push status. Reason: Timeout exceeded.
558
558
  "
559
559
  `);
560
560
  });
@@ -269,6 +269,46 @@ describe('handlePush()', () => {
269
269
  config: mockConfig,
270
270
  version: 'cli-version',
271
271
  });
272
- expect(api_1.ReuniteApiClient).toBeCalledWith('test-domain-from-env', 'test-api-key');
272
+ expect(api_1.ReuniteApiClient).toBeCalledWith({
273
+ domain: 'test-domain-from-env',
274
+ apiKey: 'test-api-key',
275
+ version: 'cli-version',
276
+ command: 'push',
277
+ });
278
+ });
279
+ it('should print error message', async () => {
280
+ const mockConfig = { apis: {} };
281
+ process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
282
+ remotes.push.mockRestore();
283
+ remotes.push.mockRejectedValueOnce(new api_1.ReuniteApiError('Deprecated.', 412));
284
+ fsStatSyncSpy.mockReturnValueOnce({
285
+ isDirectory() {
286
+ return false;
287
+ },
288
+ });
289
+ pathResolveSpy.mockImplementationOnce((p) => p);
290
+ pathRelativeSpy.mockImplementationOnce((_, p) => p);
291
+ pathDirnameSpy.mockImplementation((_) => '.');
292
+ expect((0, push_1.handlePush)({
293
+ argv: {
294
+ domain: 'test-domain',
295
+ 'mount-path': 'test-mount-path',
296
+ organization: 'test-org',
297
+ project: 'test-project',
298
+ branch: 'test-branch',
299
+ namespace: 'test-namespace',
300
+ repository: 'test-repository',
301
+ 'commit-sha': 'test-commit-sha',
302
+ 'commit-url': 'test-commit-url',
303
+ 'default-branch': 'test-branch',
304
+ 'created-at': 'test-created-at',
305
+ author: 'TestAuthor <test-author@mail.com>',
306
+ message: 'Test message',
307
+ files: ['test-file'],
308
+ 'max-execution-time': 10,
309
+ },
310
+ config: mockConfig,
311
+ version: 'cli-version',
312
+ })).rejects.toThrow('✗ File upload failed. Reason: Deprecated.');
273
313
  });
274
314
  });
@@ -26,7 +26,7 @@ describe('retryUntilConditionMet()', () => {
26
26
  condition: (result) => result?.status === 'done',
27
27
  retryIntervalMs: 100,
28
28
  retryTimeoutMs: 1000,
29
- })).rejects.toThrow('Timeout exceeded');
29
+ })).rejects.toThrow('Timeout exceeded.');
30
30
  });
31
31
  it('should call "onConditionNotMet" and "onRetry" callbacks', async () => {
32
32
  const operation = jest
@@ -20,4 +20,4 @@ export interface PushStatusSummary {
20
20
  production: DeploymentStatusResponse | null;
21
21
  commit: PushResponse['commit'];
22
22
  }
23
- export declare function handlePushStatus({ argv, config, }: CommandArgs<PushStatusOptions>): Promise<PushStatusSummary | undefined>;
23
+ export declare function handlePushStatus({ argv, config, version, }: CommandArgs<PushStatusOptions>): Promise<PushStatusSummary | void>;
@@ -9,7 +9,7 @@ const api_1 = require("../api");
9
9
  const js_utils_1 = require("../../utils/js-utils");
10
10
  const utils_2 = require("./utils");
11
11
  const RETRY_INTERVAL_MS = 5000; // 5 sec
12
- async function handlePushStatus({ argv, config, }) {
12
+ async function handlePushStatus({ argv, config, version, }) {
13
13
  const startedAt = performance.now();
14
14
  const spinner = new spinner_1.Spinner();
15
15
  const { organization, project: projectId, pushId, wait } = argv;
@@ -28,7 +28,7 @@ async function handlePushStatus({ argv, config, }) {
28
28
  const continueOnDeployFailures = argv['continue-on-deploy-failures'] || false;
29
29
  try {
30
30
  const apiKey = (0, api_1.getApiKeys)(domain);
31
- const client = new api_1.ReuniteApiClient(domain, apiKey);
31
+ const client = new api_1.ReuniteApiClient({ domain, apiKey, version, command: 'push-status' });
32
32
  let pushResponse;
33
33
  pushResponse = await (0, utils_2.retryUntilConditionMet)({
34
34
  operation: () => client.remotes.getPush({
@@ -128,11 +128,7 @@ async function handlePushStatus({ argv, config, }) {
128
128
  }
129
129
  catch (err) {
130
130
  spinner.stop(); // Spinner can block process exit, so we need to stop it explicitly.
131
- const message = err instanceof utils_1.DeploymentError
132
- ? err.message
133
- : `✗ Failed to get push status. Reason: ${err.message}\n`;
134
- (0, miscellaneous_1.exitWithError)(message);
135
- return;
131
+ (0, utils_2.handleReuniteError)('✗ Failed to get push status.', err);
136
132
  }
137
133
  finally {
138
134
  spinner.stop(); // Spinner can block process exit, so we need to stop it explicitly.
@@ -9,13 +9,14 @@ const colorette_1 = require("colorette");
9
9
  const miscellaneous_1 = require("../../utils/miscellaneous");
10
10
  const push_status_1 = require("./push-status");
11
11
  const api_1 = require("../api");
12
+ const utils_2 = require("./utils");
12
13
  async function handlePush({ argv, config, version, }) {
13
14
  const startedAt = performance.now(); // for printing execution time
14
15
  const startTime = Date.now(); // for push-status command
15
16
  const { organization, project: projectId, 'mount-path': mountPath, verbose } = argv;
16
17
  const orgId = organization || config.organization;
17
18
  if (!argv.message || !argv.author || !argv.branch) {
18
- (0, miscellaneous_1.exitWithError)('Error: message, author and branch are required for push to the CMS.');
19
+ (0, miscellaneous_1.exitWithError)('Error: message, author and branch are required for push to the Reunite.');
19
20
  }
20
21
  if (!orgId) {
21
22
  return (0, miscellaneous_1.exitWithError)(`No organization provided, please use --organization option or specify the 'organization' field in the config file.`);
@@ -32,7 +33,7 @@ async function handlePush({ argv, config, version, }) {
32
33
  if (!filesToUpload.length) {
33
34
  return (0, miscellaneous_1.printExecutionTime)('push', startedAt, `No files to upload`);
34
35
  }
35
- const client = new api_1.ReuniteApiClient(domain, apiKey);
36
+ const client = new api_1.ReuniteApiClient({ domain, apiKey, version, command: 'push' });
36
37
  const projectDefaultBranch = await client.remotes.getDefaultBranch(orgId, projectId);
37
38
  const remote = await client.remotes.upsert(orgId, projectId, {
38
39
  mountBranchName: projectDefaultBranch,
@@ -82,8 +83,7 @@ async function handlePush({ argv, config, version, }) {
82
83
  };
83
84
  }
84
85
  catch (err) {
85
- const message = err instanceof miscellaneous_1.HandledError ? '' : `✗ File upload failed. Reason: ${err.message}`;
86
- (0, miscellaneous_1.exitWithError)(message);
86
+ (0, utils_2.handleReuniteError)(' File upload failed.', err);
87
87
  }
88
88
  }
89
89
  function parseCommitAuthor(author) {
@@ -1,3 +1,5 @@
1
+ import { DeploymentError } from '../utils';
2
+ import type { ReuniteApiError } from '../api';
1
3
  /**
2
4
  * This function retries an operation until a condition is met or a timeout is exceeded.
3
5
  * If the condition is not met within the timeout, an error is thrown.
@@ -20,3 +22,4 @@ retryIntervalMs, }: {
20
22
  retryTimeoutMs?: number;
21
23
  retryIntervalMs?: number;
22
24
  }): Promise<T>;
25
+ export declare function handleReuniteError(message: string, error: ReuniteApiError | DeploymentError | Error): void;
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.retryUntilConditionMet = retryUntilConditionMet;
4
+ exports.handleReuniteError = handleReuniteError;
4
5
  const openapi_core_1 = require("@redocly/openapi-core");
6
+ const utils_1 = require("../utils");
7
+ const miscellaneous_1 = require("../../utils/miscellaneous");
5
8
  /**
6
9
  * This function retries an operation until a condition is met or a timeout is exceeded.
7
10
  * If the condition is not met within the timeout, an error is thrown.
@@ -26,7 +29,7 @@ retryIntervalMs = 5000, // 5 sec
26
29
  return result;
27
30
  }
28
31
  else if (Date.now() - startTime > retryTimeoutMs) {
29
- throw new Error('Timeout exceeded');
32
+ throw new Error('Timeout exceeded.');
30
33
  }
31
34
  else {
32
35
  onConditionNotMet?.(result);
@@ -37,3 +40,7 @@ retryIntervalMs = 5000, // 5 sec
37
40
  }
38
41
  return attempt();
39
42
  }
43
+ function handleReuniteError(message, error) {
44
+ const errorMessage = error instanceof utils_1.DeploymentError ? error.message : `${message} Reason: ${error.message}\n`;
45
+ return (0, miscellaneous_1.exitWithError)(errorMessage);
46
+ }
@@ -4,7 +4,7 @@ export type BundleOptions = {
4
4
  apis?: string[];
5
5
  extends?: string[];
6
6
  output?: string;
7
- ext: OutputExtensions;
7
+ ext?: OutputExtensions;
8
8
  dereferenced?: boolean;
9
9
  force?: boolean;
10
10
  metafile?: string;
@@ -13,7 +13,7 @@ async function handleBundle({ argv, config, version, collectSpecData, }) {
13
13
  const totals = { errors: 0, warnings: 0, ignored: 0 };
14
14
  const deprecatedOptions = [];
15
15
  (0, miscellaneous_1.checkForDeprecatedOptions)(argv, deprecatedOptions);
16
- for (const { path, alias } of apis) {
16
+ for (const { path, alias, output } of apis) {
17
17
  try {
18
18
  const startedAt = perf_hooks_1.performance.now();
19
19
  const resolvedConfig = (0, openapi_core_1.getMergedConfig)(config, alias);
@@ -30,15 +30,21 @@ async function handleBundle({ argv, config, version, collectSpecData, }) {
30
30
  collectSpecData,
31
31
  });
32
32
  const fileTotals = (0, openapi_core_1.getTotals)(problems);
33
- const { outputFile, ext } = (0, miscellaneous_1.getOutputFileName)(path, apis.length, argv.output, argv.ext);
33
+ const { outputFile, ext } = (0, miscellaneous_1.getOutputFileName)({
34
+ entrypoint: path,
35
+ output,
36
+ argvOutput: argv.output,
37
+ ext: argv.ext,
38
+ entries: argv?.apis?.length || 0,
39
+ });
34
40
  if (fileTotals.errors === 0 || argv.force) {
35
- if (!argv.output) {
36
- const output = (0, miscellaneous_1.dumpBundle)((0, miscellaneous_1.sortTopLevelKeysForOas)(result.parsed), argv.ext || 'yaml', argv.dereferenced);
37
- process.stdout.write(output);
41
+ if (!outputFile) {
42
+ const bundled = (0, miscellaneous_1.dumpBundle)((0, miscellaneous_1.sortTopLevelKeysForOas)(result.parsed), argv.ext || 'yaml', argv.dereferenced);
43
+ process.stdout.write(bundled);
38
44
  }
39
45
  else {
40
- const output = (0, miscellaneous_1.dumpBundle)((0, miscellaneous_1.sortTopLevelKeysForOas)(result.parsed), ext, argv.dereferenced);
41
- (0, miscellaneous_1.saveBundle)(outputFile, output);
46
+ const bundled = (0, miscellaneous_1.dumpBundle)((0, miscellaneous_1.sortTopLevelKeysForOas)(result.parsed), ext, argv.dereferenced);
47
+ (0, miscellaneous_1.saveBundle)(outputFile, bundled);
42
48
  }
43
49
  }
44
50
  totals.errors += fileTotals.errors;
@@ -60,14 +66,14 @@ async function handleBundle({ argv, config, version, collectSpecData, }) {
60
66
  const elapsed = (0, miscellaneous_1.getExecutionTime)(startedAt);
61
67
  if (fileTotals.errors > 0) {
62
68
  if (argv.force) {
63
- process.stderr.write(`❓ Created a bundle for ${(0, colorette_1.blue)(path)} at ${(0, colorette_1.blue)(outputFile)} with errors ${(0, colorette_1.green)(elapsed)}.\n${(0, colorette_1.yellow)('Errors ignored because of --force')}.\n`);
69
+ process.stderr.write(`❓ Created a bundle for ${(0, colorette_1.blue)(path)} at ${(0, colorette_1.blue)(outputFile || 'stdout')} with errors ${(0, colorette_1.green)(elapsed)}.\n${(0, colorette_1.yellow)('Errors ignored because of --force')}.\n`);
64
70
  }
65
71
  else {
66
72
  process.stderr.write(`❌ Errors encountered while bundling ${(0, colorette_1.blue)(path)}: bundle not created (use --force to ignore errors).\n`);
67
73
  }
68
74
  }
69
75
  else {
70
- process.stderr.write(`📦 Created a bundle for ${(0, colorette_1.blue)(path)} at ${(0, colorette_1.blue)(outputFile)} ${(0, colorette_1.green)(elapsed)}.\n`);
76
+ process.stderr.write(`📦 Created a bundle for ${(0, colorette_1.blue)(path)} at ${(0, colorette_1.blue)(outputFile || 'stdout')} ${(0, colorette_1.green)(elapsed)}.\n`);
71
77
  }
72
78
  const removedCount = meta.visitorsData?.['remove-unused-components']?.removedCount;
73
79
  if (removedCount) {
@@ -2,7 +2,7 @@ import type { CommandArgs } from '../wrapper';
2
2
  import type { VerifyConfigOptions } from '../types';
3
3
  export type EjectOptions = {
4
4
  type: 'component';
5
- path: string;
5
+ path?: string;
6
6
  'project-dir'?: string;
7
7
  force: boolean;
8
8
  } & VerifyConfigOptions;
@@ -10,7 +10,7 @@ const handleEject = async ({ argv }) => {
10
10
  '@redocly/realm',
11
11
  'eject',
12
12
  `${argv.type}`,
13
- `${argv.path}`,
13
+ `${argv.path ?? ''}`,
14
14
  `-d=${argv['project-dir']}`,
15
15
  argv.force ? `--force=${argv.force}` : '',
16
16
  ], { stdio: 'inherit' });
@@ -17,7 +17,7 @@ const previewProject = async ({ argv }) => {
17
17
  const packageName = constants_1.PRODUCT_PACKAGES[product];
18
18
  process.stdout.write(`\nLaunching preview of ${productName} ${plan} using NPX.\n\n`);
19
19
  const npxExecutableName = process.platform === 'win32' ? 'npx.cmd' : 'npx';
20
- (0, child_process_1.spawn)(npxExecutableName, ['-y', packageName, 'develop', `--plan=${plan}`, `--port=${port || 4000}`], {
20
+ (0, child_process_1.spawn)(npxExecutableName, ['-y', packageName, 'preview', `--plan=${plan}`, `--port=${port || 4000}`], {
21
21
  stdio: 'inherit',
22
22
  cwd: projectDir,
23
23
  });
package/lib/index.js CHANGED
@@ -688,7 +688,7 @@ yargs
688
688
  process.env.REDOCLY_CLI_COMMAND = 'translate';
689
689
  (0, wrapper_1.commandWrapper)(translations_1.handleTranslations)(argv);
690
690
  })
691
- .command('eject <type> <path>', 'Helper function to eject project elements for customization.', (yargs) => yargs
691
+ .command('eject <type> [path]', 'Helper function to eject project elements for customization.', (yargs) => yargs
692
692
  .positional('type', {
693
693
  description: 'Specifies what type of project element to eject. Currently, it could be only `component`.',
694
694
  demandOption: true,
@@ -697,7 +697,6 @@ yargs
697
697
  .positional('path', {
698
698
  description: 'Filepath to a component or filepath with glob pattern.',
699
699
  type: 'string',
700
- demandOption: true,
701
700
  })
702
701
  .options({
703
702
  'project-dir': {
package/lib/types.d.ts CHANGED
@@ -22,6 +22,7 @@ export type Totals = {
22
22
  export type Entrypoint = {
23
23
  path: string;
24
24
  alias?: string;
25
+ output?: string;
25
26
  };
26
27
  export declare const outputExtensions: ReadonlyArray<BundleOutputFormat>;
27
28
  export type OutputExtensions = 'json' | 'yaml' | 'yml' | undefined;
@@ -39,3 +39,4 @@ export declare const sortTopLevelKeysForOas: jest.Mock<any, [document: any]>;
39
39
  export declare const getAndValidateFileExtension: jest.Mock<string | undefined, [fileName: string]>;
40
40
  export declare const writeToFileByExtension: jest.Mock<any, any>;
41
41
  export declare const checkForDeprecatedOptions: jest.Mock<any, any>;
42
+ export declare const saveBundle: jest.Mock<any, any>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkForDeprecatedOptions = exports.writeToFileByExtension = exports.getAndValidateFileExtension = exports.sortTopLevelKeysForOas = exports.checkIfRulesetExist = exports.loadConfigAndHandleErrors = exports.writeYaml = exports.exitWithError = exports.handleError = exports.getOutputFileName = exports.printLintTotals = exports.printUnusedWarnings = exports.printExecutionTime = exports.getExecutionTime = exports.pluralize = exports.slash = exports.dumpBundle = exports.getFallbackApisOrExit = void 0;
3
+ exports.saveBundle = exports.checkForDeprecatedOptions = exports.writeToFileByExtension = exports.getAndValidateFileExtension = exports.sortTopLevelKeysForOas = exports.checkIfRulesetExist = exports.loadConfigAndHandleErrors = exports.writeYaml = exports.exitWithError = exports.handleError = exports.getOutputFileName = exports.printLintTotals = exports.printUnusedWarnings = exports.printExecutionTime = exports.getExecutionTime = exports.pluralize = exports.slash = exports.dumpBundle = exports.getFallbackApisOrExit = void 0;
4
4
  const config_1 = require("../../__tests__/fixtures/config");
5
5
  exports.getFallbackApisOrExit = jest.fn((entrypoints) => entrypoints.map((path) => ({ path })));
6
6
  exports.dumpBundle = jest.fn(() => '');
@@ -20,3 +20,4 @@ exports.sortTopLevelKeysForOas = jest.fn((document) => document);
20
20
  exports.getAndValidateFileExtension = jest.fn((fileName) => fileName.split('.').pop());
21
21
  exports.writeToFileByExtension = jest.fn();
22
22
  exports.checkForDeprecatedOptions = jest.fn();
23
+ exports.saveBundle = jest.fn();
@@ -1,2 +1,7 @@
1
- declare const _default: (url: string, options?: {}) => Promise<import("node-fetch").Response | undefined>;
1
+ import { type RequestInit } from 'node-fetch';
2
+ export declare const DEFAULT_FETCH_TIMEOUT = 3000;
3
+ export type FetchWithTimeoutOptions = RequestInit & {
4
+ timeout?: number;
5
+ };
6
+ declare const _default: (url: string, { timeout, ...options }?: FetchWithTimeoutOptions) => Promise<import("node-fetch").Response>;
2
7
  export default _default;
@@ -1,24 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_FETCH_TIMEOUT = void 0;
3
4
  const node_fetch_1 = require("node-fetch");
4
5
  const abort_controller_1 = require("abort-controller");
5
6
  const openapi_core_1 = require("@redocly/openapi-core");
6
- const TIMEOUT = 3000;
7
- exports.default = async (url, options = {}) => {
8
- try {
9
- const controller = new abort_controller_1.default();
10
- const timeout = setTimeout(() => {
11
- controller.abort();
12
- }, TIMEOUT);
13
- const res = await (0, node_fetch_1.default)(url, {
14
- signal: controller.signal,
7
+ exports.DEFAULT_FETCH_TIMEOUT = 3000;
8
+ exports.default = async (url, { timeout, ...options } = {}) => {
9
+ if (!timeout) {
10
+ return (0, node_fetch_1.default)(url, {
15
11
  ...options,
16
12
  agent: (0, openapi_core_1.getProxyAgent)(),
17
13
  });
18
- clearTimeout(timeout);
19
- return res;
20
- }
21
- catch (e) {
22
- return;
23
14
  }
15
+ const controller = new abort_controller_1.default();
16
+ const timeoutId = setTimeout(() => {
17
+ controller.abort();
18
+ }, timeout);
19
+ const res = await (0, node_fetch_1.default)(url, {
20
+ signal: controller.signal,
21
+ ...options,
22
+ agent: (0, openapi_core_1.getProxyAgent)(),
23
+ });
24
+ clearTimeout(timeoutId);
25
+ return res;
24
26
  };
@@ -25,7 +25,16 @@ export declare class HandledError extends Error {
25
25
  }
26
26
  export declare function printLintTotals(totals: Totals, definitionsCount: number): void;
27
27
  export declare function printConfigLintTotals(totals: Totals, command?: string | number): void;
28
- export declare function getOutputFileName(entrypoint: string, entries: number, output?: string, ext?: BundleOutputFormat): {
28
+ export declare function getOutputFileName({ entrypoint, output, argvOutput, ext, entries, }: {
29
+ entrypoint: string;
30
+ output?: string;
31
+ argvOutput?: string;
32
+ ext?: BundleOutputFormat;
33
+ entries: number;
34
+ }): {
35
+ ext: BundleOutputFormat;
36
+ outputFile?: undefined;
37
+ } | {
29
38
  outputFile: string;
30
39
  ext: BundleOutputFormat;
31
40
  };