@regressionproof/cli 0.3.6 → 0.3.8

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 (59) hide show
  1. package/build/cli.d.ts +2 -0
  2. package/build/cli.js +52 -0
  3. package/build/commands/invite/AcceptInvite.d.ts +1 -0
  4. package/build/commands/invite/AcceptInvite.js +17 -0
  5. package/build/commands/invite/CreateInvite.d.ts +1 -0
  6. package/build/commands/invite/CreateInvite.js +42 -0
  7. package/build/commands/invite/ListInvites.d.ts +1 -0
  8. package/build/commands/invite/ListInvites.js +18 -0
  9. package/build/commands/invite/RevokeInvite.d.ts +1 -0
  10. package/build/commands/invite/RevokeInvite.js +11 -0
  11. package/build/components/Init.d.ts +4 -0
  12. package/build/components/Init.js +286 -0
  13. package/{src → build}/components/Init.tsx +1 -2
  14. package/build/config/ConfigManager.d.ts +17 -0
  15. package/build/config/ConfigManager.js +35 -0
  16. package/build/esm/cli.d.ts +2 -0
  17. package/build/esm/cli.js +52 -0
  18. package/build/esm/commands/invite/AcceptInvite.d.ts +1 -0
  19. package/build/esm/commands/invite/AcceptInvite.js +29 -0
  20. package/build/esm/commands/invite/CreateInvite.d.ts +1 -0
  21. package/build/esm/commands/invite/CreateInvite.js +55 -0
  22. package/build/esm/commands/invite/ListInvites.d.ts +1 -0
  23. package/build/esm/commands/invite/ListInvites.js +30 -0
  24. package/build/esm/commands/invite/RevokeInvite.d.ts +1 -0
  25. package/build/esm/commands/invite/RevokeInvite.js +23 -0
  26. package/build/esm/components/Init.d.ts +4 -0
  27. package/build/esm/components/Init.js +301 -0
  28. package/build/esm/config/ConfigManager.d.ts +17 -0
  29. package/build/esm/config/ConfigManager.js +34 -0
  30. package/build/esm/index.d.ts +1 -0
  31. package/{src/index.ts → build/esm/index.js} +1 -0
  32. package/build/esm/jest/JestConfigurator.d.ts +12 -0
  33. package/build/esm/jest/JestConfigurator.js +57 -0
  34. package/build/esm/utilities/slug.d.ts +2 -0
  35. package/build/esm/utilities/slug.js +22 -0
  36. package/build/index.d.ts +1 -0
  37. package/build/index.js +2 -0
  38. package/build/jest/JestConfigurator.d.ts +12 -0
  39. package/build/jest/JestConfigurator.js +59 -0
  40. package/build/utilities/slug.d.ts +2 -0
  41. package/build/utilities/slug.js +21 -0
  42. package/package.json +8 -3
  43. package/.nvmrc +0 -1
  44. package/.vscode/launch.json +0 -58
  45. package/.vscode/settings.json +0 -67
  46. package/.vscode/tasks.json +0 -112
  47. package/CHANGELOG.md +0 -322
  48. package/eslint.config.mjs +0 -3
  49. package/src/cli.ts +0 -53
  50. package/src/commands/invite/AcceptInvite.ts +0 -21
  51. package/src/commands/invite/CreateInvite.ts +0 -60
  52. package/src/commands/invite/ListInvites.ts +0 -33
  53. package/src/commands/invite/RevokeInvite.ts +0 -18
  54. package/src/config/ConfigManager.ts +0 -64
  55. package/src/jest/JestConfigurator.ts +0 -92
  56. package/src/utilities/slug.ts +0 -22
  57. package/terms.md +0 -3
  58. package/tsconfig.json +0 -42
  59. /package/{src → build}/.spruce/settings.json +0 -0
@@ -0,0 +1,55 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var _a;
11
+ import ConfigManager from '../../config/ConfigManager.js.js';
12
+ import { getRepoNameFromGit, toSlug } from '../../utilities/slug.js.js';
13
+ const API_URL = (_a = process.env.REGRESSIONPROOF_API_URL) !== null && _a !== void 0 ? _a : 'https://api.regressionproof.ai';
14
+ class InviteCreator {
15
+ constructor(configManager = new ConfigManager()) {
16
+ this.configManager = configManager;
17
+ }
18
+ run(projectNameArg, note) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const projectName = this.resolveProjectName(projectNameArg);
21
+ const creds = this.configManager.loadCredentials(projectName);
22
+ if (!creds) {
23
+ throw new Error(`No credentials found for ${projectName}. Run regressionproof init first.`);
24
+ }
25
+ const response = yield fetch(`${API_URL}/invites`, {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Content-Type': 'application/json',
29
+ Authorization: `Bearer ${creds.token}`,
30
+ },
31
+ body: JSON.stringify({ name: projectName, note }),
32
+ });
33
+ if (!response.ok) {
34
+ const text = yield response.text();
35
+ throw new Error(`Invite create failed: ${response.status} ${text}`);
36
+ }
37
+ const data = (yield response.json());
38
+ console.log('Invite token:', data.token);
39
+ });
40
+ }
41
+ resolveProjectName(projectNameArg) {
42
+ const provided = projectNameArg ? toSlug(projectNameArg) : '';
43
+ const name = provided || getRepoNameFromGit();
44
+ if (!name) {
45
+ throw new Error('Project name is required. Provide it explicitly or ensure git origin is set.');
46
+ }
47
+ return name;
48
+ }
49
+ }
50
+ export default function createInvite(projectName, note) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ const creator = new InviteCreator();
53
+ yield creator.run(projectName, note);
54
+ });
55
+ }
@@ -0,0 +1 @@
1
+ export default function listInvites(projectName?: string): Promise<void>;
@@ -0,0 +1,30 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var _a;
11
+ const API_URL = (_a = process.env.REGRESSIONPROOF_API_URL) !== null && _a !== void 0 ? _a : 'https://api.regressionproof.ai';
12
+ export default function listInvites(projectName) {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ const query = projectName ? `?name=${encodeURIComponent(projectName)}` : '';
15
+ const response = yield fetch(`${API_URL}/invites${query}`);
16
+ if (!response.ok) {
17
+ const text = yield response.text();
18
+ throw new Error(`Invite list failed: ${response.status} ${text}`);
19
+ }
20
+ const data = (yield response.json());
21
+ if (data.length === 0) {
22
+ console.log('No invites found.');
23
+ return;
24
+ }
25
+ for (const invite of data) {
26
+ console.log(`${invite.projectName} | ${invite.status} | created ${invite.createdAt}` +
27
+ (invite.note ? ` | ${invite.note}` : ''));
28
+ }
29
+ });
30
+ }
@@ -0,0 +1 @@
1
+ export default function revokeInvite(token: string): Promise<void>;
@@ -0,0 +1,23 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var _a;
11
+ const API_URL = (_a = process.env.REGRESSIONPROOF_API_URL) !== null && _a !== void 0 ? _a : 'https://api.regressionproof.ai';
12
+ export default function revokeInvite(token) {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ const response = yield fetch(`${API_URL}/invites/${encodeURIComponent(token)}`, {
15
+ method: 'DELETE',
16
+ });
17
+ if (!response.ok) {
18
+ const text = yield response.text();
19
+ throw new Error(`Invite revoke failed: ${response.status} ${text}`);
20
+ }
21
+ console.log('Invite revoked.');
22
+ });
23
+ }
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export default function Init(props: {
3
+ projectName?: string;
4
+ }): React.ReactElement;
@@ -0,0 +1,301 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var _a;
11
+ import { spawnSync } from 'node:child_process';
12
+ import { existsSync, readFileSync } from 'node:fs';
13
+ import { RegressionProofClient } from '@regressionproof/client';
14
+ import { Box, Text, useApp } from 'ink';
15
+ import BigText from 'ink-big-text';
16
+ import TextInput from 'ink-text-input';
17
+ import React from 'react';
18
+ import ConfigManager from '../config/ConfigManager.js.js';
19
+ import JestConfigurator from '../jest/JestConfigurator.js.js';
20
+ import { getRepoNameFromGit, toSlug } from '../utilities/slug.js.js';
21
+ const API_URL = (_a = process.env.REGRESSIONPROOF_API_URL) !== null && _a !== void 0 ? _a : 'https://api.regressionproof.ai';
22
+ class InitComponent extends React.Component {
23
+ constructor(props) {
24
+ super(props);
25
+ this.checkTimeout = null;
26
+ this.handleNameChange = (value) => {
27
+ this.setState({ name: toSlug(value) });
28
+ };
29
+ this.handleSubmit = () => __awaiter(this, void 0, void 0, function* () {
30
+ const { availability, name } = this.state;
31
+ if (availability !== 'available' || name.length < 3) {
32
+ return;
33
+ }
34
+ this.setState({ step: 'registering' });
35
+ yield this.register();
36
+ });
37
+ const providedName = props.projectName;
38
+ const defaultName = providedName !== null && providedName !== void 0 ? providedName : getRepoNameFromGit();
39
+ this.configManager = new ConfigManager();
40
+ this.apiClient = new RegressionProofClient(API_URL);
41
+ // Check if already registered (idempotent)
42
+ const existingCreds = this.configManager.loadCredentials(defaultName);
43
+ if (existingCreds) {
44
+ this.state = {
45
+ name: defaultName,
46
+ step: 'installing',
47
+ availability: 'available',
48
+ errorMessage: '',
49
+ credentials: existingCreds,
50
+ jestConfig: null,
51
+ };
52
+ }
53
+ else {
54
+ this.state = {
55
+ name: defaultName,
56
+ step: providedName ? 'registering' : 'input',
57
+ availability: 'idle',
58
+ errorMessage: '',
59
+ credentials: null,
60
+ jestConfig: null,
61
+ };
62
+ }
63
+ }
64
+ componentDidMount() {
65
+ // If we have a provided name and not already registered, start registration
66
+ if (this.props.projectName && this.state.step === 'registering') {
67
+ void this.register();
68
+ }
69
+ else if (this.state.step === 'installing') {
70
+ void this.installAndConfigure();
71
+ }
72
+ else {
73
+ void this.checkAvailability();
74
+ }
75
+ }
76
+ componentDidUpdate(_, prevState) {
77
+ if (prevState.name !== this.state.name && this.state.step === 'input') {
78
+ void this.checkAvailability();
79
+ }
80
+ }
81
+ componentWillUnmount() {
82
+ this.clearCheckTimeout();
83
+ }
84
+ clearCheckTimeout() {
85
+ if (this.checkTimeout) {
86
+ clearTimeout(this.checkTimeout);
87
+ this.checkTimeout = null;
88
+ }
89
+ }
90
+ checkAvailability() {
91
+ return __awaiter(this, void 0, void 0, function* () {
92
+ this.clearCheckTimeout();
93
+ const { name } = this.state;
94
+ if (name.length < 3) {
95
+ this.setState({ availability: 'idle', errorMessage: '' });
96
+ return;
97
+ }
98
+ this.setState({ availability: 'checking' });
99
+ this.checkTimeout = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
100
+ try {
101
+ const isAvailable = yield this.apiClient.checkNameAvailability(name);
102
+ this.setState({
103
+ availability: isAvailable ? 'available' : 'taken',
104
+ errorMessage: '',
105
+ });
106
+ }
107
+ catch (err) {
108
+ this.setState({
109
+ availability: 'error',
110
+ errorMessage: this.formatError(err),
111
+ });
112
+ }
113
+ }), 300);
114
+ });
115
+ }
116
+ formatError(err) {
117
+ const message = err instanceof Error ? err.message : String(err);
118
+ const cause = err instanceof Error && 'cause' in err ? ` (${err.cause})` : '';
119
+ return `${message}${cause} - ${API_URL}`;
120
+ }
121
+ register() {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ const { name } = this.state;
124
+ try {
125
+ const credentials = yield this.apiClient.registerProject({ name });
126
+ this.setState({ credentials });
127
+ this.configManager.saveCredentials(name, credentials);
128
+ yield this.installAndConfigure();
129
+ }
130
+ catch (err) {
131
+ this.setState({
132
+ step: 'error',
133
+ errorMessage: err instanceof Error ? err.message : String(err),
134
+ });
135
+ }
136
+ });
137
+ }
138
+ installAndConfigure() {
139
+ return __awaiter(this, void 0, void 0, function* () {
140
+ var _a;
141
+ this.setState({ step: 'installing' });
142
+ const installResult = this.installDependencies();
143
+ if (!installResult.success) {
144
+ this.setState({
145
+ step: 'error',
146
+ errorMessage: (_a = installResult.message) !== null && _a !== void 0 ? _a : 'Failed to install dependencies.',
147
+ });
148
+ return;
149
+ }
150
+ this.setState({ step: 'configuring' });
151
+ const jestConfigurator = new JestConfigurator();
152
+ const jestConfig = jestConfigurator.configure();
153
+ this.setState({ jestConfig, step: 'success' });
154
+ setTimeout(() => this.props.exit(), 3000);
155
+ });
156
+ }
157
+ installDependencies() {
158
+ var _a, _b, _c, _d, _e, _f;
159
+ if (!existsSync('package.json')) {
160
+ return {
161
+ success: false,
162
+ message: 'No package.json found. regressionproof currently supports Node.js + Jest projects.',
163
+ };
164
+ }
165
+ let packageJson;
166
+ try {
167
+ packageJson = JSON.parse(readFileSync('package.json', 'utf8'));
168
+ }
169
+ catch (err) {
170
+ const message = err instanceof Error ? err.message : String(err);
171
+ return {
172
+ success: false,
173
+ message: `Failed to read package.json: ${message}`,
174
+ };
175
+ }
176
+ const hasReporter = Boolean((_b = (_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a['@regressionproof/jest-reporter']) !== null && _b !== void 0 ? _b : (_c = packageJson.devDependencies) === null || _c === void 0 ? void 0 : _c['@regressionproof/jest-reporter']);
177
+ if (hasReporter) {
178
+ return { success: true };
179
+ }
180
+ const packageManager = this.getPackageManager();
181
+ const result = spawnSync(packageManager.command, [...packageManager.args, '@regressionproof/jest-reporter'], {
182
+ encoding: 'utf8',
183
+ shell: true,
184
+ });
185
+ if (result.error || result.status !== 0) {
186
+ const details = ((_d = result.stderr) === null || _d === void 0 ? void 0 : _d.trim()) ||
187
+ ((_e = result.stdout) === null || _e === void 0 ? void 0 : _e.trim()) ||
188
+ ((_f = result.error) === null || _f === void 0 ? void 0 : _f.message);
189
+ return {
190
+ success: false,
191
+ message: `Failed to install dependencies${details ? `: ${details}` : ''}`,
192
+ };
193
+ }
194
+ return { success: true };
195
+ }
196
+ getPackageManager() {
197
+ if (existsSync('pnpm-lock.yaml')) {
198
+ return { command: 'pnpm', args: ['add', '-D'] };
199
+ }
200
+ if (existsSync('yarn.lock')) {
201
+ return { command: 'yarn', args: ['add', '-D'] };
202
+ }
203
+ if (existsSync('package-lock.json')) {
204
+ return { command: 'npm', args: ['install', '-D'] };
205
+ }
206
+ return { command: 'npm', args: ['install', '-D'] };
207
+ }
208
+ renderStatusIndicator() {
209
+ const { availability, errorMessage } = this.state;
210
+ switch (availability) {
211
+ case 'idle':
212
+ return null;
213
+ case 'checking':
214
+ return React.createElement(Text, { color: "yellow" }, "checking...");
215
+ case 'available':
216
+ return React.createElement(Text, { color: "green" }, "available (press Enter)");
217
+ case 'taken':
218
+ return React.createElement(Text, { color: "red" }, "already taken");
219
+ case 'error':
220
+ return React.createElement(Text, { color: "red" }, errorMessage);
221
+ }
222
+ }
223
+ renderRegistering() {
224
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
225
+ React.createElement(Text, { color: "yellow" },
226
+ "Registering project \"",
227
+ this.state.name,
228
+ "\"...")));
229
+ }
230
+ renderConfiguring() {
231
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
232
+ React.createElement(Text, { color: "yellow" }, "Configuring Jest reporter...")));
233
+ }
234
+ renderInstalling() {
235
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
236
+ React.createElement(Text, { color: "yellow" }, "Installing dependencies...")));
237
+ }
238
+ renderSuccess() {
239
+ const { name, credentials, jestConfig } = this.state;
240
+ const configDir = this.configManager.getConfigDir(name);
241
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
242
+ React.createElement(Text, { color: "green", bold: true }, "Project registered successfully!"),
243
+ React.createElement(Box, { marginTop: 1, flexDirection: "column" },
244
+ React.createElement(Text, null,
245
+ "Config saved to:",
246
+ ' ',
247
+ React.createElement(Text, { color: "cyan" },
248
+ configDir,
249
+ "/config.json")),
250
+ React.createElement(Text, null,
251
+ "Git remote: ",
252
+ React.createElement(Text, { color: "cyan" }, credentials === null || credentials === void 0 ? void 0 : credentials.url)),
253
+ (jestConfig === null || jestConfig === void 0 ? void 0 : jestConfig.configured) ? (React.createElement(Text, null,
254
+ "Jest reporter added to:",
255
+ ' ',
256
+ React.createElement(Text, { color: "cyan" }, jestConfig.location))) : (React.createElement(Text, { color: "yellow" }, "Could not auto-configure Jest. Add manually:"))),
257
+ !(jestConfig === null || jestConfig === void 0 ? void 0 : jestConfig.configured) && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
258
+ React.createElement(Text, { color: "gray" }, "// jest.config.js"),
259
+ React.createElement(Text, { color: "gray" }, "reporters: ['default', '@regressionproof/jest-reporter']"))),
260
+ React.createElement(Box, { marginTop: 1 },
261
+ React.createElement(Text, { color: "green" }, "Run your tests and snapshots will be captured automatically!"))));
262
+ }
263
+ renderError() {
264
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
265
+ React.createElement(Text, { color: "red", bold: true }, "Registration failed"),
266
+ React.createElement(Text, { color: "red" }, this.state.errorMessage)));
267
+ }
268
+ renderInput() {
269
+ const { name } = this.state;
270
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
271
+ React.createElement(BigText, { text: "regressionproof.ai", font: "tiny", colors: ['magenta', 'cyan'] }),
272
+ React.createElement(Text, { color: "gray" }, "Teaching LLMs to write better code."),
273
+ React.createElement(Box, { marginTop: 1, flexDirection: "column" },
274
+ React.createElement(Text, { bold: true }, "Project name:"),
275
+ React.createElement(Box, null,
276
+ React.createElement(TextInput, { value: name, onChange: this.handleNameChange, onSubmit: this.handleSubmit, placeholder: "my-awesome-project" }),
277
+ React.createElement(Box, { marginLeft: 2 }, this.renderStatusIndicator())),
278
+ name.length > 0 && name.length < 3 && (React.createElement(Text, { color: "gray" }, "Name must be at least 3 characters")))));
279
+ }
280
+ render() {
281
+ const { step } = this.state;
282
+ switch (step) {
283
+ case 'registering':
284
+ return this.renderRegistering();
285
+ case 'installing':
286
+ return this.renderInstalling();
287
+ case 'configuring':
288
+ return this.renderConfiguring();
289
+ case 'success':
290
+ return this.renderSuccess();
291
+ case 'error':
292
+ return this.renderError();
293
+ default:
294
+ return this.renderInput();
295
+ }
296
+ }
297
+ }
298
+ export default function Init(props) {
299
+ const { exit } = useApp();
300
+ return React.createElement(InitComponent, { exit: exit, projectName: props.projectName });
301
+ }
@@ -0,0 +1,17 @@
1
+ export default class ConfigManager {
2
+ private baseDir;
3
+ constructor(baseDir?: string);
4
+ getConfigDir(projectName: string): string;
5
+ saveCredentials(projectName: string, credentials: Credentials): void;
6
+ loadCredentials(projectName: string): Credentials | null;
7
+ }
8
+ export interface Credentials {
9
+ url: string;
10
+ token: string;
11
+ }
12
+ export interface ProjectConfig {
13
+ remote: {
14
+ url: string;
15
+ token: string;
16
+ };
17
+ }
@@ -0,0 +1,34 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ export default class ConfigManager {
5
+ constructor(baseDir = path.join(os.homedir(), '.regressionproof')) {
6
+ this.baseDir = baseDir;
7
+ }
8
+ getConfigDir(projectName) {
9
+ return path.join(this.baseDir, projectName);
10
+ }
11
+ saveCredentials(projectName, credentials) {
12
+ const configDir = this.getConfigDir(projectName);
13
+ fs.mkdirSync(configDir, { recursive: true });
14
+ const configPath = path.join(configDir, 'config.json');
15
+ const config = {
16
+ remote: {
17
+ url: credentials.url,
18
+ token: credentials.token,
19
+ },
20
+ };
21
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
22
+ }
23
+ loadCredentials(projectName) {
24
+ const configPath = path.join(this.getConfigDir(projectName), 'config.json');
25
+ if (!fs.existsSync(configPath)) {
26
+ return null;
27
+ }
28
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
29
+ return {
30
+ url: config.remote.url,
31
+ token: config.remote.token,
32
+ };
33
+ }
34
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1,2 @@
1
+ export {};
1
2
  //exports go here
@@ -0,0 +1,12 @@
1
+ export default class JestConfigurator {
2
+ private cwd;
3
+ private reporterPackage;
4
+ constructor(cwd?: string);
5
+ configure(): JestConfigResult;
6
+ private tryConfigurePackageJson;
7
+ private tryConfigureJestConfig;
8
+ }
9
+ export interface JestConfigResult {
10
+ configured: boolean;
11
+ location: string;
12
+ }
@@ -0,0 +1,57 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ export default class JestConfigurator {
4
+ constructor(cwd = process.cwd()) {
5
+ this.reporterPackage = '@regressionproof/jest-reporter';
6
+ this.cwd = cwd;
7
+ }
8
+ configure() {
9
+ var _a, _b, _c;
10
+ return ((_c = (_b = (_a = this.tryConfigurePackageJson()) !== null && _a !== void 0 ? _a : this.tryConfigureJestConfig('jest.config.ts')) !== null && _b !== void 0 ? _b : this.tryConfigureJestConfig('jest.config.js')) !== null && _c !== void 0 ? _c : {
11
+ configured: false,
12
+ location: '',
13
+ });
14
+ }
15
+ tryConfigurePackageJson() {
16
+ const packageJsonPath = path.join(this.cwd, 'package.json');
17
+ if (!fs.existsSync(packageJsonPath)) {
18
+ return null;
19
+ }
20
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
21
+ if (!packageJson.jest) {
22
+ return null;
23
+ }
24
+ if (!packageJson.jest.reporters) {
25
+ packageJson.jest.reporters = ['default'];
26
+ }
27
+ if (!packageJson.jest.reporters.includes(this.reporterPackage)) {
28
+ packageJson.jest.reporters.push(this.reporterPackage);
29
+ }
30
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
31
+ return { configured: true, location: 'package.json' };
32
+ }
33
+ tryConfigureJestConfig(filename) {
34
+ const configPath = path.join(this.cwd, filename);
35
+ if (!fs.existsSync(configPath)) {
36
+ return null;
37
+ }
38
+ let content = fs.readFileSync(configPath, 'utf-8');
39
+ if (content.includes(this.reporterPackage)) {
40
+ return {
41
+ configured: true,
42
+ location: `${filename} (already configured)`,
43
+ };
44
+ }
45
+ if (content.includes('reporters:')) {
46
+ content = content.replace(/(reporters:\s*\[)/, `$1'${this.reporterPackage}', `);
47
+ }
48
+ else {
49
+ const exportPattern = filename.endsWith('.ts')
50
+ ? /(export\s+default\s*\{)/
51
+ : /(module\.exports\s*=\s*\{)/;
52
+ content = content.replace(exportPattern, `$1\n reporters: ['default', '${this.reporterPackage}'],`);
53
+ }
54
+ fs.writeFileSync(configPath, content);
55
+ return { configured: true, location: filename };
56
+ }
57
+ }
@@ -0,0 +1,2 @@
1
+ export declare function toSlug(input: string): string;
2
+ export declare function getRepoNameFromGit(): string;
@@ -0,0 +1,22 @@
1
+ import { execSync } from 'child_process';
2
+ export function toSlug(input) {
3
+ return input
4
+ .toLowerCase()
5
+ .replace(/\s+/g, '-')
6
+ .replace(/[^a-z0-9-_]/g, '')
7
+ .replace(/-+/g, '-')
8
+ .replace(/^-|-$/g, '');
9
+ }
10
+ export function getRepoNameFromGit() {
11
+ var _a;
12
+ try {
13
+ const remoteUrl = execSync('git remote get-url origin', {
14
+ encoding: 'utf-8',
15
+ }).trim();
16
+ const match = remoteUrl.match(/[/:]([^/:]+?)(\.git)?$/);
17
+ return toSlug((_a = match === null || match === void 0 ? void 0 : match[1]) !== null && _a !== void 0 ? _a : '');
18
+ }
19
+ catch (_b) {
20
+ return '';
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //exports go here
@@ -0,0 +1,12 @@
1
+ export default class JestConfigurator {
2
+ private cwd;
3
+ private reporterPackage;
4
+ constructor(cwd?: string);
5
+ configure(): JestConfigResult;
6
+ private tryConfigurePackageJson;
7
+ private tryConfigureJestConfig;
8
+ }
9
+ export interface JestConfigResult {
10
+ configured: boolean;
11
+ location: string;
12
+ }
@@ -0,0 +1,59 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ export default class JestConfigurator {
4
+ cwd;
5
+ reporterPackage = '@regressionproof/jest-reporter';
6
+ constructor(cwd = process.cwd()) {
7
+ this.cwd = cwd;
8
+ }
9
+ configure() {
10
+ return (this.tryConfigurePackageJson() ??
11
+ this.tryConfigureJestConfig('jest.config.ts') ??
12
+ this.tryConfigureJestConfig('jest.config.js') ?? {
13
+ configured: false,
14
+ location: '',
15
+ });
16
+ }
17
+ tryConfigurePackageJson() {
18
+ const packageJsonPath = path.join(this.cwd, 'package.json');
19
+ if (!fs.existsSync(packageJsonPath)) {
20
+ return null;
21
+ }
22
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
23
+ if (!packageJson.jest) {
24
+ return null;
25
+ }
26
+ if (!packageJson.jest.reporters) {
27
+ packageJson.jest.reporters = ['default'];
28
+ }
29
+ if (!packageJson.jest.reporters.includes(this.reporterPackage)) {
30
+ packageJson.jest.reporters.push(this.reporterPackage);
31
+ }
32
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
33
+ return { configured: true, location: 'package.json' };
34
+ }
35
+ tryConfigureJestConfig(filename) {
36
+ const configPath = path.join(this.cwd, filename);
37
+ if (!fs.existsSync(configPath)) {
38
+ return null;
39
+ }
40
+ let content = fs.readFileSync(configPath, 'utf-8');
41
+ if (content.includes(this.reporterPackage)) {
42
+ return {
43
+ configured: true,
44
+ location: `${filename} (already configured)`,
45
+ };
46
+ }
47
+ if (content.includes('reporters:')) {
48
+ content = content.replace(/(reporters:\s*\[)/, `$1'${this.reporterPackage}', `);
49
+ }
50
+ else {
51
+ const exportPattern = filename.endsWith('.ts')
52
+ ? /(export\s+default\s*\{)/
53
+ : /(module\.exports\s*=\s*\{)/;
54
+ content = content.replace(exportPattern, `$1\n reporters: ['default', '${this.reporterPackage}'],`);
55
+ }
56
+ fs.writeFileSync(configPath, content);
57
+ return { configured: true, location: filename };
58
+ }
59
+ }
@@ -0,0 +1,2 @@
1
+ export declare function toSlug(input: string): string;
2
+ export declare function getRepoNameFromGit(): string;