@curl-runner/cli 1.16.0 → 1.16.2
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/package.json +2 -2
- package/src/ci-exit.test.ts +0 -216
- package/src/cli.ts +0 -1351
- package/src/commands/upgrade.ts +0 -262
- package/src/diff/baseline-manager.test.ts +0 -181
- package/src/diff/baseline-manager.ts +0 -266
- package/src/diff/diff-formatter.ts +0 -316
- package/src/diff/index.ts +0 -3
- package/src/diff/response-differ.test.ts +0 -330
- package/src/diff/response-differ.ts +0 -489
- package/src/executor/max-concurrency.test.ts +0 -139
- package/src/executor/profile-executor.test.ts +0 -132
- package/src/executor/profile-executor.ts +0 -167
- package/src/executor/request-executor.ts +0 -663
- package/src/parser/yaml.test.ts +0 -480
- package/src/parser/yaml.ts +0 -271
- package/src/snapshot/index.ts +0 -3
- package/src/snapshot/snapshot-differ.test.ts +0 -358
- package/src/snapshot/snapshot-differ.ts +0 -296
- package/src/snapshot/snapshot-formatter.ts +0 -170
- package/src/snapshot/snapshot-manager.test.ts +0 -204
- package/src/snapshot/snapshot-manager.ts +0 -342
- package/src/types/bun-yaml.d.ts +0 -11
- package/src/types/config.ts +0 -638
- package/src/utils/colors.ts +0 -30
- package/src/utils/condition-evaluator.test.ts +0 -415
- package/src/utils/condition-evaluator.ts +0 -327
- package/src/utils/curl-builder.test.ts +0 -165
- package/src/utils/curl-builder.ts +0 -209
- package/src/utils/installation-detector.test.ts +0 -52
- package/src/utils/installation-detector.ts +0 -123
- package/src/utils/logger.ts +0 -856
- package/src/utils/response-store.test.ts +0 -213
- package/src/utils/response-store.ts +0 -108
- package/src/utils/stats.test.ts +0 -161
- package/src/utils/stats.ts +0 -151
- package/src/utils/version-checker.ts +0 -158
- package/src/version.ts +0 -43
- package/src/watcher/file-watcher.test.ts +0 -186
- package/src/watcher/file-watcher.ts +0 -140
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { getVersion } from '../version';
|
|
2
|
-
import { color } from './colors';
|
|
3
|
-
|
|
4
|
-
interface VersionCheckCache {
|
|
5
|
-
lastCheck: number;
|
|
6
|
-
latestVersion: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const CACHE_FILE = `${process.env.HOME}/.curl-runner-version-cache.json`;
|
|
10
|
-
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
|
|
11
|
-
const NPM_REGISTRY_URL = 'https://registry.npmjs.org/@curl-runner/cli/latest';
|
|
12
|
-
|
|
13
|
-
export class VersionChecker {
|
|
14
|
-
async checkForUpdates(skipCache = false): Promise<void> {
|
|
15
|
-
try {
|
|
16
|
-
// Don't check in CI environments
|
|
17
|
-
if (process.env.CI) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const currentVersion = getVersion();
|
|
22
|
-
|
|
23
|
-
// Don't check for development versions
|
|
24
|
-
if (currentVersion === '0.0.0') {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Check cache first
|
|
29
|
-
if (!skipCache) {
|
|
30
|
-
const cached = await this.getCachedVersion();
|
|
31
|
-
if (cached && Date.now() - cached.lastCheck < CACHE_DURATION) {
|
|
32
|
-
this.compareVersions(currentVersion, cached.latestVersion);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Fetch latest version from npm registry
|
|
38
|
-
const latestVersion = await this.fetchLatestVersion();
|
|
39
|
-
if (latestVersion) {
|
|
40
|
-
// Update cache
|
|
41
|
-
await this.setCachedVersion(latestVersion);
|
|
42
|
-
|
|
43
|
-
// Compare versions
|
|
44
|
-
this.compareVersions(currentVersion, latestVersion);
|
|
45
|
-
}
|
|
46
|
-
} catch {
|
|
47
|
-
// Silently fail - we don't want to interrupt the CLI usage
|
|
48
|
-
// due to version check failures
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private async fetchLatestVersion(): Promise<string | null> {
|
|
53
|
-
try {
|
|
54
|
-
const response = await fetch(NPM_REGISTRY_URL, {
|
|
55
|
-
signal: AbortSignal.timeout(3000), // 3 second timeout
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
if (!response.ok) {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const data = (await response.json()) as { version: string };
|
|
63
|
-
return data.version;
|
|
64
|
-
} catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private compareVersions(current: string, latest: string): void {
|
|
70
|
-
if (this.isNewerVersion(current, latest)) {
|
|
71
|
-
console.log();
|
|
72
|
-
console.log(color('╭───────────────────────────────────────────────╮', 'yellow'));
|
|
73
|
-
console.log(
|
|
74
|
-
color('│', 'yellow') +
|
|
75
|
-
' ' +
|
|
76
|
-
color('│', 'yellow'),
|
|
77
|
-
);
|
|
78
|
-
console.log(
|
|
79
|
-
color('│', 'yellow') +
|
|
80
|
-
' ' +
|
|
81
|
-
color('Update available!', 'bright') +
|
|
82
|
-
` ${color(current, 'red')} → ${color(latest, 'green')}` +
|
|
83
|
-
' ' +
|
|
84
|
-
color('│', 'yellow'),
|
|
85
|
-
);
|
|
86
|
-
console.log(
|
|
87
|
-
color('│', 'yellow') +
|
|
88
|
-
' ' +
|
|
89
|
-
color('│', 'yellow'),
|
|
90
|
-
);
|
|
91
|
-
console.log(
|
|
92
|
-
color('│', 'yellow') +
|
|
93
|
-
' Run ' +
|
|
94
|
-
color('curl-runner upgrade', 'cyan') +
|
|
95
|
-
' to update ' +
|
|
96
|
-
color('│', 'yellow'),
|
|
97
|
-
);
|
|
98
|
-
console.log(
|
|
99
|
-
color('│', 'yellow') +
|
|
100
|
-
' ' +
|
|
101
|
-
color('│', 'yellow'),
|
|
102
|
-
);
|
|
103
|
-
console.log(color('╰───────────────────────────────────────────────╯', 'yellow'));
|
|
104
|
-
console.log();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private isNewerVersion(current: string, latest: string): boolean {
|
|
109
|
-
try {
|
|
110
|
-
// Remove 'v' prefix if present
|
|
111
|
-
const currentVersion = current.replace(/^v/, '');
|
|
112
|
-
const latestVersion = latest.replace(/^v/, '');
|
|
113
|
-
|
|
114
|
-
const currentParts = currentVersion.split('.').map(Number);
|
|
115
|
-
const latestParts = latestVersion.split('.').map(Number);
|
|
116
|
-
|
|
117
|
-
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
118
|
-
const currentPart = currentParts[i] || 0;
|
|
119
|
-
const latestPart = latestParts[i] || 0;
|
|
120
|
-
|
|
121
|
-
if (latestPart > currentPart) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
if (latestPart < currentPart) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return false;
|
|
130
|
-
} catch {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private async getCachedVersion(): Promise<VersionCheckCache | null> {
|
|
136
|
-
try {
|
|
137
|
-
const file = Bun.file(CACHE_FILE);
|
|
138
|
-
if (await file.exists()) {
|
|
139
|
-
return JSON.parse(await file.text());
|
|
140
|
-
}
|
|
141
|
-
} catch {
|
|
142
|
-
// Ignore cache read errors
|
|
143
|
-
}
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private async setCachedVersion(latestVersion: string): Promise<void> {
|
|
148
|
-
try {
|
|
149
|
-
const cache: VersionCheckCache = {
|
|
150
|
-
lastCheck: Date.now(),
|
|
151
|
-
latestVersion,
|
|
152
|
-
};
|
|
153
|
-
await Bun.write(CACHE_FILE, JSON.stringify(cache));
|
|
154
|
-
} catch {
|
|
155
|
-
// Ignore cache write errors
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
package/src/version.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// Version management for curl-runner CLI
|
|
2
|
-
// This file handles version detection for both development and compiled binaries
|
|
3
|
-
|
|
4
|
-
declare const BUILD_VERSION: string | undefined;
|
|
5
|
-
|
|
6
|
-
export function getVersion(): string {
|
|
7
|
-
// Check compile-time constant first (set by --define flag during build)
|
|
8
|
-
if (typeof BUILD_VERSION !== 'undefined') {
|
|
9
|
-
return BUILD_VERSION;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Check environment variable (for local builds with our script)
|
|
13
|
-
if (process.env.CURL_RUNNER_VERSION) {
|
|
14
|
-
return process.env.CURL_RUNNER_VERSION;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// In development or npm installation, try to read from package.json
|
|
18
|
-
try {
|
|
19
|
-
// Try multiple paths to find package.json
|
|
20
|
-
const possiblePaths = [
|
|
21
|
-
'../package.json', // Development or npm installation
|
|
22
|
-
'./package.json', // In case we're at root
|
|
23
|
-
'../../package.json', // In case of different directory structure
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
for (const path of possiblePaths) {
|
|
27
|
-
try {
|
|
28
|
-
const packageJson = require(path);
|
|
29
|
-
if (packageJson.name === '@curl-runner/cli' && packageJson.version) {
|
|
30
|
-
return packageJson.version;
|
|
31
|
-
}
|
|
32
|
-
} catch {
|
|
33
|
-
// Try next path
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// If no package.json found, return default
|
|
38
|
-
return '0.0.0';
|
|
39
|
-
} catch {
|
|
40
|
-
// If all else fails, return a default version
|
|
41
|
-
return '0.0.0';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
2
|
-
import { FileWatcher, type WatcherOptions } from './file-watcher';
|
|
3
|
-
|
|
4
|
-
// Mock logger
|
|
5
|
-
const createMockLogger = () => ({
|
|
6
|
-
logWatch: mock(() => {}),
|
|
7
|
-
logWatchReady: mock(() => {}),
|
|
8
|
-
logFileChanged: mock(() => {}),
|
|
9
|
-
logError: mock(() => {}),
|
|
10
|
-
logWarning: mock(() => {}),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// Helper to access private method for testing
|
|
14
|
-
const triggerFileChange = (watcher: FileWatcher, filename: string) => {
|
|
15
|
-
// biome-ignore lint/complexity/useLiteralKeys: accessing private method for testing
|
|
16
|
-
watcher['handleFileChange'](filename);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
describe('FileWatcher', () => {
|
|
20
|
-
let watcher: FileWatcher;
|
|
21
|
-
let mockLogger: ReturnType<typeof createMockLogger>;
|
|
22
|
-
let onRunMock: ReturnType<typeof mock>;
|
|
23
|
-
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
mockLogger = createMockLogger();
|
|
26
|
-
onRunMock = mock(() => Promise.resolve());
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
afterEach(() => {
|
|
30
|
-
if (watcher) {
|
|
31
|
-
watcher.stop();
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('should debounce rapid file changes', async () => {
|
|
36
|
-
const options: WatcherOptions = {
|
|
37
|
-
files: [],
|
|
38
|
-
config: { debounce: 50 },
|
|
39
|
-
onRun: onRunMock,
|
|
40
|
-
logger: mockLogger as unknown as WatcherOptions['logger'],
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
watcher = new FileWatcher(options);
|
|
44
|
-
|
|
45
|
-
// Simulate rapid file changes
|
|
46
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
47
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
48
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
49
|
-
|
|
50
|
-
// Wait for debounce to settle
|
|
51
|
-
await Bun.sleep(100);
|
|
52
|
-
|
|
53
|
-
// Should only have been called once due to debouncing
|
|
54
|
-
// Initial run doesn't happen because we didn't call start()
|
|
55
|
-
expect(onRunMock).toHaveBeenCalledTimes(1);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('should queue runs during execution', async () => {
|
|
59
|
-
let runCount = 0;
|
|
60
|
-
const slowOnRun = mock(async () => {
|
|
61
|
-
runCount++;
|
|
62
|
-
await Bun.sleep(100);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const options: WatcherOptions = {
|
|
66
|
-
files: [],
|
|
67
|
-
config: { debounce: 10, clear: false },
|
|
68
|
-
onRun: slowOnRun,
|
|
69
|
-
logger: mockLogger as unknown as WatcherOptions['logger'],
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
watcher = new FileWatcher(options);
|
|
73
|
-
|
|
74
|
-
// Start first run
|
|
75
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
76
|
-
await Bun.sleep(20); // Let debounce fire
|
|
77
|
-
|
|
78
|
-
// Change during run (should be queued)
|
|
79
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
80
|
-
|
|
81
|
-
// Wait for both runs to complete
|
|
82
|
-
await Bun.sleep(300);
|
|
83
|
-
|
|
84
|
-
expect(runCount).toBe(2);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('should clear debounce timer on stop', () => {
|
|
88
|
-
const options: WatcherOptions = {
|
|
89
|
-
files: [],
|
|
90
|
-
config: { debounce: 1000 },
|
|
91
|
-
onRun: onRunMock,
|
|
92
|
-
logger: mockLogger as unknown as WatcherOptions['logger'],
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
watcher = new FileWatcher(options);
|
|
96
|
-
|
|
97
|
-
// Trigger a change to start debounce timer
|
|
98
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
99
|
-
|
|
100
|
-
// Stop should clear the timer
|
|
101
|
-
watcher.stop();
|
|
102
|
-
|
|
103
|
-
// biome-ignore lint/complexity/useLiteralKeys: accessing private field for testing
|
|
104
|
-
expect(watcher['debounceTimer']).toBeNull();
|
|
105
|
-
// biome-ignore lint/complexity/useLiteralKeys: accessing private field for testing
|
|
106
|
-
expect(watcher['watchers']).toHaveLength(0);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('should use default debounce of 300ms', async () => {
|
|
110
|
-
const options: WatcherOptions = {
|
|
111
|
-
files: [],
|
|
112
|
-
config: {}, // No debounce specified
|
|
113
|
-
onRun: onRunMock,
|
|
114
|
-
logger: mockLogger as unknown as WatcherOptions['logger'],
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
watcher = new FileWatcher(options);
|
|
118
|
-
|
|
119
|
-
const startTime = performance.now();
|
|
120
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
121
|
-
|
|
122
|
-
// Wait less than default debounce
|
|
123
|
-
await Bun.sleep(100);
|
|
124
|
-
expect(onRunMock).not.toHaveBeenCalled();
|
|
125
|
-
|
|
126
|
-
// Wait for full debounce
|
|
127
|
-
await Bun.sleep(250);
|
|
128
|
-
expect(onRunMock).toHaveBeenCalledTimes(1);
|
|
129
|
-
|
|
130
|
-
const elapsed = performance.now() - startTime;
|
|
131
|
-
expect(elapsed).toBeGreaterThanOrEqual(300);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test('should handle onRun errors gracefully', async () => {
|
|
135
|
-
const errorOnRun = mock(async () => {
|
|
136
|
-
throw new Error('Test error');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const options: WatcherOptions = {
|
|
140
|
-
files: [],
|
|
141
|
-
config: { debounce: 10 },
|
|
142
|
-
onRun: errorOnRun,
|
|
143
|
-
logger: mockLogger as unknown as WatcherOptions['logger'],
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
watcher = new FileWatcher(options);
|
|
147
|
-
|
|
148
|
-
// Should not throw
|
|
149
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
150
|
-
await Bun.sleep(50);
|
|
151
|
-
|
|
152
|
-
expect(mockLogger.logError).toHaveBeenCalledWith('Test error');
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('should log file changed with filename', async () => {
|
|
156
|
-
const options: WatcherOptions = {
|
|
157
|
-
files: [],
|
|
158
|
-
config: { debounce: 10, clear: false },
|
|
159
|
-
onRun: onRunMock,
|
|
160
|
-
logger: mockLogger as unknown as WatcherOptions['logger'],
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
watcher = new FileWatcher(options);
|
|
164
|
-
|
|
165
|
-
triggerFileChange(watcher, 'my-api.yaml');
|
|
166
|
-
await Bun.sleep(50);
|
|
167
|
-
|
|
168
|
-
expect(mockLogger.logFileChanged).toHaveBeenCalledWith('my-api.yaml');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test('should call logWatchReady after run completes', async () => {
|
|
172
|
-
const options: WatcherOptions = {
|
|
173
|
-
files: [],
|
|
174
|
-
config: { debounce: 10, clear: false },
|
|
175
|
-
onRun: onRunMock,
|
|
176
|
-
logger: mockLogger as unknown as WatcherOptions['logger'],
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
watcher = new FileWatcher(options);
|
|
180
|
-
|
|
181
|
-
triggerFileChange(watcher, 'test.yaml');
|
|
182
|
-
await Bun.sleep(50);
|
|
183
|
-
|
|
184
|
-
expect(mockLogger.logWatchReady).toHaveBeenCalled();
|
|
185
|
-
});
|
|
186
|
-
});
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { type FSWatcher, watch } from 'node:fs';
|
|
2
|
-
import type { WatchConfig } from '../types/config';
|
|
3
|
-
import type { Logger } from '../utils/logger';
|
|
4
|
-
|
|
5
|
-
export interface WatcherOptions {
|
|
6
|
-
files: string[];
|
|
7
|
-
config: WatchConfig;
|
|
8
|
-
onRun: () => Promise<void>;
|
|
9
|
-
logger: Logger;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class FileWatcher {
|
|
13
|
-
private watchers: FSWatcher[] = [];
|
|
14
|
-
private debounceTimer: Timer | null = null;
|
|
15
|
-
private isRunning = false;
|
|
16
|
-
private pendingRun = false;
|
|
17
|
-
private options: WatcherOptions;
|
|
18
|
-
|
|
19
|
-
constructor(options: WatcherOptions) {
|
|
20
|
-
this.options = options;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async start(): Promise<void> {
|
|
24
|
-
const { files, logger } = this.options;
|
|
25
|
-
|
|
26
|
-
// Initial run
|
|
27
|
-
await this.runRequests();
|
|
28
|
-
|
|
29
|
-
// Setup watchers for each file
|
|
30
|
-
for (const file of files) {
|
|
31
|
-
try {
|
|
32
|
-
const watcher = watch(file, (event) => {
|
|
33
|
-
if (event === 'change') {
|
|
34
|
-
this.handleFileChange(file);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
watcher.on('error', (error) => {
|
|
39
|
-
logger.logWarning(`Watch error on ${file}: ${error.message}`);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
this.watchers.push(watcher);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
logger.logWarning(
|
|
45
|
-
`Failed to watch ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Log watching status
|
|
51
|
-
logger.logWatch(files);
|
|
52
|
-
|
|
53
|
-
// Handle graceful shutdown
|
|
54
|
-
this.setupSignalHandlers();
|
|
55
|
-
|
|
56
|
-
// Keep process alive
|
|
57
|
-
await this.keepAlive();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private handleFileChange(filename: string): void {
|
|
61
|
-
const { config, logger } = this.options;
|
|
62
|
-
const debounce = config.debounce ?? 300;
|
|
63
|
-
|
|
64
|
-
// Clear existing debounce timer
|
|
65
|
-
if (this.debounceTimer) {
|
|
66
|
-
clearTimeout(this.debounceTimer);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Debounce the run
|
|
70
|
-
this.debounceTimer = setTimeout(async () => {
|
|
71
|
-
// If already running, queue for after completion
|
|
72
|
-
if (this.isRunning) {
|
|
73
|
-
this.pendingRun = true;
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (config.clear !== false) {
|
|
78
|
-
console.clear();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
logger.logFileChanged(filename);
|
|
82
|
-
await this.runRequests();
|
|
83
|
-
}, debounce);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private async runRequests(): Promise<void> {
|
|
87
|
-
const { onRun, logger } = this.options;
|
|
88
|
-
|
|
89
|
-
this.isRunning = true;
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
await onRun();
|
|
93
|
-
} catch (error) {
|
|
94
|
-
logger.logError(error instanceof Error ? error.message : String(error));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this.isRunning = false;
|
|
98
|
-
|
|
99
|
-
// Check for pending runs
|
|
100
|
-
if (this.pendingRun) {
|
|
101
|
-
this.pendingRun = false;
|
|
102
|
-
const { config } = this.options;
|
|
103
|
-
if (config.clear !== false) {
|
|
104
|
-
console.clear();
|
|
105
|
-
}
|
|
106
|
-
logger.logFileChanged('(queued change)');
|
|
107
|
-
await this.runRequests();
|
|
108
|
-
} else {
|
|
109
|
-
logger.logWatchReady();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private setupSignalHandlers(): void {
|
|
114
|
-
const cleanup = () => {
|
|
115
|
-
this.stop();
|
|
116
|
-
process.exit(0);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
process.on('SIGINT', cleanup);
|
|
120
|
-
process.on('SIGTERM', cleanup);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
private async keepAlive(): Promise<never> {
|
|
124
|
-
while (true) {
|
|
125
|
-
await Bun.sleep(1000);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
stop(): void {
|
|
130
|
-
for (const watcher of this.watchers) {
|
|
131
|
-
watcher.close();
|
|
132
|
-
}
|
|
133
|
-
this.watchers = [];
|
|
134
|
-
|
|
135
|
-
if (this.debounceTimer) {
|
|
136
|
-
clearTimeout(this.debounceTimer);
|
|
137
|
-
this.debounceTimer = null;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|