neonctl 1.29.1 → 1.29.4
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/analytics.js +78 -0
- package/api.js +35 -0
- package/auth.js +101 -0
- package/{src/cli.ts → cli.js} +0 -1
- package/commands/auth.js +102 -0
- package/commands/auth.test.js +42 -0
- package/commands/branches.js +303 -0
- package/commands/branches.test.js +321 -0
- package/commands/connection_string.js +158 -0
- package/commands/connection_string.test.js +253 -0
- package/commands/databases.js +79 -0
- package/commands/databases.test.js +51 -0
- package/commands/help.test.js +11 -0
- package/{src/commands/index.ts → commands/index.js} +10 -11
- package/commands/ip_allow.js +135 -0
- package/commands/ip_allow.test.js +78 -0
- package/commands/operations.js +28 -0
- package/commands/operations.test.js +11 -0
- package/commands/projects.js +186 -0
- package/commands/projects.test.js +132 -0
- package/commands/roles.js +57 -0
- package/commands/roles.test.js +42 -0
- package/commands/set_context.js +22 -0
- package/commands/set_context.test.js +53 -0
- package/commands/user.js +15 -0
- package/config.js +11 -0
- package/context.js +48 -0
- package/env.js +6 -0
- package/errors.js +16 -0
- package/help.js +146 -0
- package/index.js +168 -0
- package/log.js +15 -0
- package/package.json +1 -1
- package/parameters.gen.js +322 -0
- package/pkg.js +3 -45
- package/test_utils/mock_server.js +16 -0
- package/test_utils/oauth_server.js +9 -0
- package/test_utils/test_cli_command.js +80 -0
- package/types.js +1 -0
- package/utils/enrichers.js +49 -0
- package/utils/formats.js +5 -0
- package/utils/formats.test.js +32 -0
- package/utils/middlewares.js +20 -0
- package/utils/point_in_time.js +50 -0
- package/utils/psql.js +24 -0
- package/utils/string.js +5 -0
- package/utils/ui.js +59 -0
- package/writer.js +87 -0
- package/writer.test.js +86 -0
- package/.bump +0 -1
- package/.editorconfig +0 -7
- package/.eslintrc.cjs +0 -15
- package/.github/workflows/commitlint.yml +0 -46
- package/.github/workflows/pr.yml +0 -25
- package/.github/workflows/release.yml +0 -30
- package/.husky/commit-msg +0 -4
- package/.husky/pre-commit +0 -4
- package/.nvmrc +0 -1
- package/.prettierignore +0 -3
- package/.prettierrc.json +0 -3
- package/.releaserc.json +0 -47
- package/LICENSE +0 -202
- package/commitlint.config.cjs +0 -7
- package/generateOptionsFromSpec.ts +0 -68
- package/jest/setup.js +0 -5
- package/jest.config.ts +0 -199
- package/mocks/bin/psql.cjs +0 -9
- package/mocks/main/projects/GET.js +0 -27
- package/mocks/main/projects/POST.js +0 -22
- package/mocks/main/projects/shared/GET.js +0 -16
- package/mocks/main/projects/test/DELETE.json +0 -7
- package/mocks/main/projects/test/GET.json +0 -13
- package/mocks/main/projects/test/PATCH.js +0 -18
- package/mocks/main/projects/test/branches/GET.json +0 -25
- package/mocks/main/projects/test/branches/POST.js +0 -83
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/DELETE.json +0 -7
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/GET.json +0 -9
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/PATCH.js +0 -14
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/databases/GET.json +0 -6
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/databases/POST.js +0 -13
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/databases/test_db/DELETE.json +0 -6
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/endpoints/GET.json +0 -26
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/endpoints/POST.json +0 -6
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/roles/GET.json +0 -3
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/roles/POST.js +0 -14
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/roles/test_role/DELETE.json +0 -6
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/roles/test_role/reveal_password/GET.json +0 -3
- package/mocks/main/projects/test/branches/br-cloudy-branch-12345678/set_as_primary/POST.json +0 -9
- package/mocks/main/projects/test/branches/br-numbered-branch-123456/GET.json +0 -10
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/DELETE.json +0 -7
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/GET.json +0 -10
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/PATCH.js +0 -14
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/databases/GET.json +0 -6
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/databases/POST.js +0 -13
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/databases/test_db/DELETE.json +0 -6
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/endpoints/GET.json +0 -26
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/endpoints/POST.json +0 -6
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/restore/POST.js +0 -16
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/roles/GET.json +0 -3
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/roles/POST.js +0 -14
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/roles/test_role/DELETE.json +0 -6
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/roles/test_role/reveal_password/GET.json +0 -3
- package/mocks/main/projects/test/branches/br-sunny-branch-123456/set_as_primary/POST.json +0 -9
- package/mocks/main/projects/test/endpoints/GET.json +0 -9
- package/mocks/main/projects/test/endpoints/POST.js +0 -32
- package/mocks/main/projects/test/endpoints/test_endpoint_id/DELETE.json +0 -7
- package/mocks/main/projects/test/endpoints/test_endpoint_id/GET.json +0 -9
- package/mocks/main/projects/test/endpoints/test_endpoint_id/PATCH.js +0 -17
- package/mocks/main/projects/test/operations/GET.json +0 -22
- package/mocks/main/users/me/GET.json +0 -5
- package/mocks/restore/projects/test/branches/GET.json +0 -21
- package/mocks/restore/projects/test/branches/br-another-branch-123456/GET.json +0 -6
- package/mocks/restore/projects/test/branches/br-another-branch-123456/restore/POST.js +0 -13
- package/mocks/restore/projects/test/branches/br-any-branch-123456/GET.json +0 -6
- package/mocks/restore/projects/test/branches/br-parent-tots-123456/GET.json +0 -7
- package/mocks/restore/projects/test/branches/br-parent-tots-123456/restore/POST.js +0 -14
- package/mocks/restore/projects/test/branches/br-self-tolsn-123456/GET.json +0 -6
- package/mocks/restore/projects/test/branches/br-self-tolsn-123456/restore/POST.js +0 -15
- package/mocks/single_project/projects/GET.json +0 -10
- package/mocks/single_project/projects/test-project-123456/GET.json +0 -14
- package/mocks/single_project/projects/test-project-123456/branches/GET.json +0 -11
- package/mocks/single_project/projects/test-project-123456/branches/br-main-branch-123456/databases/GET.json +0 -3
- package/mocks/single_project/projects/test-project-123456/branches/br-main-branch-123456/endpoints/GET.json +0 -10
- package/mocks/single_project/projects/test-project-123456/branches/br-main-branch-123456/roles/GET.json +0 -3
- package/mocks/single_project/projects/test-project-123456/branches/br-main-branch-123456/roles/test_role/reveal_password/GET.json +0 -3
- package/rollup.config.js +0 -20
- package/snapshots/commands/branches.test.snap +0 -221
- package/snapshots/commands/connection_string.test.snap +0 -70
- package/snapshots/commands/databases.test.snap +0 -20
- package/snapshots/commands/ip_allow.test.snap +0 -55
- package/snapshots/commands/operations.test.snap +0 -17
- package/snapshots/commands/projects.test.snap +0 -141
- package/snapshots/commands/roles.test.snap +0 -19
- package/snapshots/commands/set_context.test.snap +0 -30
- package/snapshots/writer.test.snap +0 -60
- package/snapshotsResolver.cjs +0 -32
- package/src/analytics.ts +0 -95
- package/src/api.ts +0 -44
- package/src/auth.ts +0 -137
- package/src/commands/auth.test.ts +0 -62
- package/src/commands/auth.ts +0 -148
- package/src/commands/branches.test.ts +0 -354
- package/src/commands/branches.ts +0 -451
- package/src/commands/connection_string.test.ts +0 -250
- package/src/commands/connection_string.ts +0 -210
- package/src/commands/databases.test.ts +0 -55
- package/src/commands/databases.ts +0 -129
- package/src/commands/help.test.ts +0 -13
- package/src/commands/ip_allow.test.ts +0 -86
- package/src/commands/ip_allow.ts +0 -202
- package/src/commands/operations.test.ts +0 -13
- package/src/commands/operations.ts +0 -41
- package/src/commands/projects.test.ts +0 -147
- package/src/commands/projects.ts +0 -275
- package/src/commands/roles.test.ts +0 -46
- package/src/commands/roles.ts +0 -100
- package/src/commands/set_context.test.ts +0 -64
- package/src/commands/set_context.ts +0 -27
- package/src/commands/user.ts +0 -21
- package/src/config.ts +0 -22
- package/src/context.ts +0 -61
- package/src/env.ts +0 -7
- package/src/errors.ts +0 -24
- package/src/help.ts +0 -185
- package/src/index.ts +0 -180
- package/src/log.ts +0 -16
- package/src/parameters.gen.ts +0 -332
- package/src/pkg.ts +0 -9
- package/src/test_utils/mock_server.ts +0 -27
- package/src/test_utils/oauth_server.ts +0 -10
- package/src/test_utils/test_cli_command.ts +0 -117
- package/src/types.ts +0 -25
- package/src/utils/enrichers.ts +0 -73
- package/src/utils/formats.test.ts +0 -41
- package/src/utils/formats.ts +0 -11
- package/src/utils/middlewares.ts +0 -23
- package/src/utils/point_in_time.ts +0 -86
- package/src/utils/psql.ts +0 -29
- package/src/utils/string.ts +0 -8
- package/src/utils/ui.ts +0 -64
- package/src/writer.test.ts +0 -98
- package/src/writer.ts +0 -131
- package/tsconfig.json +0 -17
- /package/{src/callback.html → callback.html} +0 -0
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { Api } from '@neondatabase/api-client';
|
|
2
|
-
import { looksLikeLSN, looksLikeTimestamp } from './formats.js';
|
|
3
|
-
import { branchIdResolve } from './enrichers.js';
|
|
4
|
-
|
|
5
|
-
export type PointInTime =
|
|
6
|
-
| {
|
|
7
|
-
tag: 'head';
|
|
8
|
-
}
|
|
9
|
-
| {
|
|
10
|
-
tag: 'lsn';
|
|
11
|
-
lsn: string;
|
|
12
|
-
}
|
|
13
|
-
| {
|
|
14
|
-
tag: 'timestamp';
|
|
15
|
-
timestamp: string;
|
|
16
|
-
};
|
|
17
|
-
export type PointInTimeBranchId = {
|
|
18
|
-
branchId: string;
|
|
19
|
-
} & PointInTime;
|
|
20
|
-
|
|
21
|
-
export type PointInTimeBranch = {
|
|
22
|
-
branch: string;
|
|
23
|
-
} & PointInTime;
|
|
24
|
-
|
|
25
|
-
export interface PointInTimeProps {
|
|
26
|
-
targetBranchId: string;
|
|
27
|
-
pointInTime: string;
|
|
28
|
-
projectId: string;
|
|
29
|
-
api: Api<unknown>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class PointInTimeParseError extends Error {
|
|
33
|
-
constructor(message: string) {
|
|
34
|
-
super(message);
|
|
35
|
-
this.name = 'PointInTimeParseError';
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const parsePITBranch = (input: string) => {
|
|
40
|
-
const splitIndex = input.lastIndexOf('@');
|
|
41
|
-
const sourceBranch = splitIndex === -1 ? input : input.slice(0, splitIndex);
|
|
42
|
-
const exactPIT = splitIndex === -1 ? null : input.slice(splitIndex + 1);
|
|
43
|
-
const result = {
|
|
44
|
-
branch: sourceBranch,
|
|
45
|
-
...(exactPIT === null
|
|
46
|
-
? { tag: 'head' }
|
|
47
|
-
: looksLikeLSN(exactPIT)
|
|
48
|
-
? { tag: 'lsn', lsn: exactPIT }
|
|
49
|
-
: { tag: 'timestamp', timestamp: exactPIT }),
|
|
50
|
-
} satisfies PointInTimeBranch;
|
|
51
|
-
if (result.tag === 'timestamp' && !looksLikeTimestamp(result.timestamp)) {
|
|
52
|
-
throw new PointInTimeParseError('Invalid source branch format');
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export const parsePointInTime = async ({
|
|
58
|
-
pointInTime,
|
|
59
|
-
targetBranchId,
|
|
60
|
-
projectId,
|
|
61
|
-
api,
|
|
62
|
-
}: PointInTimeProps) => {
|
|
63
|
-
const parsedPIT = parsePITBranch(pointInTime);
|
|
64
|
-
|
|
65
|
-
let branchId = '';
|
|
66
|
-
if (parsedPIT.branch === '^self') {
|
|
67
|
-
branchId = targetBranchId;
|
|
68
|
-
} else if (parsedPIT.branch === '^parent') {
|
|
69
|
-
const { data } = await api.getProjectBranch(projectId, targetBranchId);
|
|
70
|
-
const { parent_id: parentId } = data.branch;
|
|
71
|
-
if (parentId == null) {
|
|
72
|
-
throw new PointInTimeParseError('Branch has no parent');
|
|
73
|
-
}
|
|
74
|
-
branchId = parentId;
|
|
75
|
-
} else {
|
|
76
|
-
branchId = await branchIdResolve({
|
|
77
|
-
branch: parsedPIT.branch,
|
|
78
|
-
projectId,
|
|
79
|
-
apiClient: api,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// @ts-expect-error extracting pit from parsedPIT
|
|
84
|
-
delete parsedPIT.branch;
|
|
85
|
-
return { ...parsedPIT, branchId };
|
|
86
|
-
};
|
package/src/utils/psql.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { log } from '../log.js';
|
|
2
|
-
import { spawn } from 'child_process';
|
|
3
|
-
import which from 'which';
|
|
4
|
-
|
|
5
|
-
export const psql = async (connection_uri: string, args: string[] = []) => {
|
|
6
|
-
const psqlPathOrNull = await which('psql', { nothrow: true });
|
|
7
|
-
|
|
8
|
-
if (psqlPathOrNull === null) {
|
|
9
|
-
log.error(`psql is not available in the PATH`);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
log.info('Connecting to the database using psql...');
|
|
14
|
-
const psql = spawn(psqlPathOrNull, [connection_uri, ...args], {
|
|
15
|
-
stdio: 'inherit',
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
for (const signame of ['SIGINT', 'SIGTERM']) {
|
|
19
|
-
process.on(signame, (code) => {
|
|
20
|
-
if (!psql.killed && code !== null) {
|
|
21
|
-
psql.kill(code);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
psql.on('exit', (code: number | null) => {
|
|
27
|
-
process.exit(code === null ? 1 : code);
|
|
28
|
-
});
|
|
29
|
-
};
|
package/src/utils/string.ts
DELETED
package/src/utils/ui.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// returns the next string if matches the given matcher,
|
|
2
|
-
// otherwise returns null
|
|
3
|
-
// consumes the line from the lines array
|
|
4
|
-
export const consumeNextMatching = (lines: string[], matcher: RegExp) => {
|
|
5
|
-
while (lines.length > 0) {
|
|
6
|
-
const line = (lines.shift() as string).trim();
|
|
7
|
-
if (line === '') {
|
|
8
|
-
continue;
|
|
9
|
-
}
|
|
10
|
-
if (matcher.test(line)) {
|
|
11
|
-
return line;
|
|
12
|
-
}
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
return null;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// returns strings if next non-empty line matches the given matcher,
|
|
19
|
-
// otherwise returns empty array
|
|
20
|
-
// consumes the lines from the lines array
|
|
21
|
-
export const consumeBlockIfMatches = (lines: string[], matcher: RegExp) => {
|
|
22
|
-
const result = [] as string[];
|
|
23
|
-
if (lines.length === 0) {
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let line = lines.shift() as string;
|
|
28
|
-
|
|
29
|
-
while (line.trim() === '') {
|
|
30
|
-
line = lines.shift() as string;
|
|
31
|
-
}
|
|
32
|
-
if (!matcher.test(line)) {
|
|
33
|
-
lines.unshift(line);
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
result.push(line);
|
|
37
|
-
while (lines.length > 0) {
|
|
38
|
-
line = lines.shift() as string;
|
|
39
|
-
if (line.trim() === '') {
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
result.push(line);
|
|
43
|
-
}
|
|
44
|
-
return result;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const splitColumns = (line: string) => {
|
|
48
|
-
const result = line.trim().split(/\s{2,}/);
|
|
49
|
-
result[1] = result[1] ?? '';
|
|
50
|
-
if (result.length > 2) {
|
|
51
|
-
result[1] = result.slice(1).join(' ');
|
|
52
|
-
}
|
|
53
|
-
return result;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const drawPointer = (width: number) => {
|
|
57
|
-
const result = [] as string[];
|
|
58
|
-
result.push('└');
|
|
59
|
-
for (let i = 0; i < width - 4; i++) {
|
|
60
|
-
result.push('─');
|
|
61
|
-
}
|
|
62
|
-
result.push('>');
|
|
63
|
-
return result.join('');
|
|
64
|
-
};
|
package/src/writer.test.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { Writable } from 'node:stream';
|
|
2
|
-
import { describe, it, expect } from '@jest/globals';
|
|
3
|
-
import { writer } from './writer.js';
|
|
4
|
-
|
|
5
|
-
class MockWritable extends Writable {
|
|
6
|
-
_data: Buffer[] = [];
|
|
7
|
-
|
|
8
|
-
get data() {
|
|
9
|
-
return this._data.map((chunk) => chunk.toString()).join('');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
_write(chunk: Buffer) {
|
|
13
|
-
this._data.push(chunk);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
describe('writer', () => {
|
|
18
|
-
describe('outputs yaml', () => {
|
|
19
|
-
it('outputs single data', () => {
|
|
20
|
-
const stream = new MockWritable();
|
|
21
|
-
const out = writer({ output: 'yaml', out: stream });
|
|
22
|
-
out.end({ foo: 'bar' }, { fields: ['foo'] });
|
|
23
|
-
expect(stream.data).toMatchSnapshot();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('outputs single data with title', () => {
|
|
27
|
-
const stream = new MockWritable();
|
|
28
|
-
const out = writer({ output: 'yaml', out: stream });
|
|
29
|
-
out.end({ foo: 'bar' }, { fields: ['foo'], title: 'baz' });
|
|
30
|
-
expect(stream.data).toMatchSnapshot();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('outputs multiple data', () => {
|
|
34
|
-
const stream = new MockWritable();
|
|
35
|
-
const out = writer({ output: 'yaml', out: stream });
|
|
36
|
-
out
|
|
37
|
-
.write({ foo: 'bar' }, { fields: ['foo'], title: 'T1' })
|
|
38
|
-
.write({ baz: 'xyz' }, { fields: ['baz'], title: 'T2' })
|
|
39
|
-
.end();
|
|
40
|
-
expect(stream.data).toMatchSnapshot();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('outputs json', () => {
|
|
45
|
-
it('outputs single data', () => {
|
|
46
|
-
const stream = new MockWritable();
|
|
47
|
-
const out = writer({ output: 'json', out: stream });
|
|
48
|
-
out.end({ foo: 'bar' }, { fields: ['foo'] });
|
|
49
|
-
expect(stream.data).toMatchSnapshot();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('outputs single data with title', () => {
|
|
53
|
-
const stream = new MockWritable();
|
|
54
|
-
const out = writer({ output: 'json', out: stream });
|
|
55
|
-
out.end({ foo: 'bar' }, { fields: ['foo'], title: 'baz' });
|
|
56
|
-
expect(stream.data).toMatchSnapshot();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('outputs multiple data', () => {
|
|
60
|
-
const stream = new MockWritable();
|
|
61
|
-
const out = writer({ output: 'json', out: stream });
|
|
62
|
-
out
|
|
63
|
-
.write({ foo: 'bar' }, { fields: ['foo'], title: 'T1' })
|
|
64
|
-
.write({ baz: 'xyz' }, { fields: ['baz'], title: 'T2' })
|
|
65
|
-
.end();
|
|
66
|
-
expect(stream.data).toMatchSnapshot();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('outputs table', () => {
|
|
71
|
-
it('outputs single data', () => {
|
|
72
|
-
const stream = new MockWritable();
|
|
73
|
-
const out = writer({ output: 'table', out: stream });
|
|
74
|
-
out.end({ foo: 'bar', extra: 'extra' }, { fields: ['foo'] });
|
|
75
|
-
expect(stream.data).toMatchSnapshot();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('outputs single data with title', () => {
|
|
79
|
-
const stream = new MockWritable();
|
|
80
|
-
const out = writer({ output: 'table', out: stream });
|
|
81
|
-
out.end(
|
|
82
|
-
{ foo: 'bar', extra: 'extra' },
|
|
83
|
-
{ fields: ['foo'], title: 'baz' },
|
|
84
|
-
);
|
|
85
|
-
expect(stream.data).toMatchSnapshot();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('outputs multiple data', () => {
|
|
89
|
-
const stream = new MockWritable();
|
|
90
|
-
const out = writer({ output: 'table', out: stream });
|
|
91
|
-
out
|
|
92
|
-
.write({ foo: 'bar', extra: 'extra' }, { fields: ['foo'], title: 'T1' })
|
|
93
|
-
.write({ baz: 'xyz', extra: 'extra' }, { fields: ['baz'], title: 'T2' })
|
|
94
|
-
.end();
|
|
95
|
-
expect(stream.data).toMatchSnapshot();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|
package/src/writer.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import YAML from 'yaml';
|
|
2
|
-
import Table from 'cli-table';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
|
|
5
|
-
import { CommonProps } from './types.js';
|
|
6
|
-
import { isCi } from './env.js';
|
|
7
|
-
import { isObject, toSnakeCase } from './utils/string.js';
|
|
8
|
-
|
|
9
|
-
type ExtractFromArray<T> = T extends (infer R)[] ? R : T;
|
|
10
|
-
type OnlyStrings<T> = T extends string ? T : never;
|
|
11
|
-
|
|
12
|
-
type WriteOutConfig<T> = {
|
|
13
|
-
// Fields to output in human-readable format
|
|
14
|
-
fields: Readonly<OnlyStrings<keyof ExtractFromArray<T>>[]>;
|
|
15
|
-
// Title of the output
|
|
16
|
-
title?: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const writeYaml = (chunks: { data: any; config: WriteOutConfig<any> }[]) => {
|
|
20
|
-
return YAML.stringify(
|
|
21
|
-
chunks.length === 1
|
|
22
|
-
? chunks[0].data
|
|
23
|
-
: Object.fromEntries(
|
|
24
|
-
chunks.map(({ config, data }, idx) => [
|
|
25
|
-
config.title ? toSnakeCase(config.title) : idx,
|
|
26
|
-
data,
|
|
27
|
-
]),
|
|
28
|
-
),
|
|
29
|
-
null,
|
|
30
|
-
2,
|
|
31
|
-
);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const writeJson = (chunks: { data: any; config: WriteOutConfig<any> }[]) => {
|
|
35
|
-
return JSON.stringify(
|
|
36
|
-
chunks.length === 1
|
|
37
|
-
? chunks[0].data
|
|
38
|
-
: Object.fromEntries(
|
|
39
|
-
chunks.map(({ config, data }, idx) => [
|
|
40
|
-
config.title ? toSnakeCase(config.title) : idx,
|
|
41
|
-
data,
|
|
42
|
-
]),
|
|
43
|
-
),
|
|
44
|
-
null,
|
|
45
|
-
2,
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const writeTable = (
|
|
50
|
-
chunks: { data: any; config: WriteOutConfig<any> }[],
|
|
51
|
-
out: NodeJS.WritableStream,
|
|
52
|
-
) => {
|
|
53
|
-
chunks.forEach(({ data, config }) => {
|
|
54
|
-
const arrayData = Array.isArray(data) ? data : [data];
|
|
55
|
-
const fields = config.fields.filter((field) =>
|
|
56
|
-
arrayData.some((item) => item[field] !== undefined && item[field] !== ''),
|
|
57
|
-
);
|
|
58
|
-
const table = new Table({
|
|
59
|
-
style: {
|
|
60
|
-
head: ['green'],
|
|
61
|
-
},
|
|
62
|
-
head: fields.map((field: string) =>
|
|
63
|
-
field
|
|
64
|
-
.split('_')
|
|
65
|
-
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
66
|
-
.join(' '),
|
|
67
|
-
),
|
|
68
|
-
});
|
|
69
|
-
arrayData.forEach((item) => {
|
|
70
|
-
table.push(
|
|
71
|
-
fields.map((field: string | number) => {
|
|
72
|
-
const value = item[field];
|
|
73
|
-
return Array.isArray(value)
|
|
74
|
-
? value.join('\n')
|
|
75
|
-
: isObject(value)
|
|
76
|
-
? JSON.stringify(value, null, 2)
|
|
77
|
-
: value;
|
|
78
|
-
}),
|
|
79
|
-
);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
if (config.title) {
|
|
83
|
-
out.write((isCi() ? config.title : chalk.bold(config.title)) + '\n');
|
|
84
|
-
}
|
|
85
|
-
out.write(table.toString());
|
|
86
|
-
out.write('\n');
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
*
|
|
92
|
-
* Parses the output format, takes data and writes the output to stdout.
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* const { data } = await props.apiClient.listProjectBranches(props.project.id);
|
|
96
|
-
* // to output single data
|
|
97
|
-
* writer(props).end(data, { fields: ['id', 'name', 'created_at'] })
|
|
98
|
-
* // to output multiple data
|
|
99
|
-
* writer(props)
|
|
100
|
-
* .write(data, { fields: ['id', 'name', 'created_at'], title: 'branches' })
|
|
101
|
-
* .write(data, { fields: ['id', 'created_at'], title: 'endpoints' })
|
|
102
|
-
* .end()
|
|
103
|
-
*/
|
|
104
|
-
export const writer = (
|
|
105
|
-
props: Pick<CommonProps, 'output'> & { out?: NodeJS.WritableStream },
|
|
106
|
-
) => {
|
|
107
|
-
const out = props.out ?? process.stdout;
|
|
108
|
-
const chunks: { data: any; config: WriteOutConfig<any> }[] = [];
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
write<T>(data: T, config: WriteOutConfig<T>) {
|
|
112
|
-
chunks.push({ data, config });
|
|
113
|
-
return this;
|
|
114
|
-
},
|
|
115
|
-
end: <T>(...args: [T, WriteOutConfig<T>] | []) => {
|
|
116
|
-
if (args.length === 2) {
|
|
117
|
-
chunks.push({ data: args[0], config: args[1] });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (props.output == 'yaml') {
|
|
121
|
-
return out.write(writeYaml(chunks));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (props.output == 'json') {
|
|
125
|
-
return out.write(writeJson(chunks));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return writeTable(chunks, out);
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
};
|
package/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ES2022",
|
|
5
|
-
"strict": true,
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"allowJs": true,
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"allowSyntheticDefaultImports": true,
|
|
10
|
-
"moduleResolution": "node",
|
|
11
|
-
"resolveJsonModule": true,
|
|
12
|
-
"noImplicitAny": true,
|
|
13
|
-
"strictNullChecks": true,
|
|
14
|
-
"skipLibCheck": true
|
|
15
|
-
},
|
|
16
|
-
"include": ["src/**/*.ts"]
|
|
17
|
-
}
|
|
File without changes
|