@auto-engineer/job-graph-processor 1.12.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.
- package/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-test.log +14 -0
- package/.turbo/turbo-type-check.log +4 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +10 -0
- package/README.md +408 -0
- package/dist/src/apply-policy.d.ts +3 -0
- package/dist/src/apply-policy.d.ts.map +1 -0
- package/dist/src/apply-policy.js +24 -0
- package/dist/src/apply-policy.js.map +1 -0
- package/dist/src/apply-policy.specs.d.ts +2 -0
- package/dist/src/apply-policy.specs.d.ts.map +1 -0
- package/dist/src/apply-policy.specs.js +75 -0
- package/dist/src/apply-policy.specs.js.map +1 -0
- package/dist/src/commands/process-job-graph.d.ts +31 -0
- package/dist/src/commands/process-job-graph.d.ts.map +1 -0
- package/dist/src/commands/process-job-graph.js +64 -0
- package/dist/src/commands/process-job-graph.js.map +1 -0
- package/dist/src/commands/process-job-graph.specs.d.ts +2 -0
- package/dist/src/commands/process-job-graph.specs.d.ts.map +1 -0
- package/dist/src/commands/process-job-graph.specs.js +73 -0
- package/dist/src/commands/process-job-graph.specs.js.map +1 -0
- package/dist/src/evolve.d.ts +70 -0
- package/dist/src/evolve.d.ts.map +1 -0
- package/dist/src/evolve.js +82 -0
- package/dist/src/evolve.js.map +1 -0
- package/dist/src/evolve.specs.d.ts +2 -0
- package/dist/src/evolve.specs.d.ts.map +1 -0
- package/dist/src/evolve.specs.js +209 -0
- package/dist/src/evolve.specs.js.map +1 -0
- package/dist/src/graph-processor.d.ts +22 -0
- package/dist/src/graph-processor.d.ts.map +1 -0
- package/dist/src/graph-processor.js +82 -0
- package/dist/src/graph-processor.js.map +1 -0
- package/dist/src/graph-processor.specs.d.ts +2 -0
- package/dist/src/graph-processor.specs.d.ts.map +1 -0
- package/dist/src/graph-processor.specs.js +286 -0
- package/dist/src/graph-processor.specs.js.map +1 -0
- package/dist/src/graph-validator.d.ts +19 -0
- package/dist/src/graph-validator.d.ts.map +1 -0
- package/dist/src/graph-validator.js +65 -0
- package/dist/src/graph-validator.js.map +1 -0
- package/dist/src/graph-validator.specs.d.ts +2 -0
- package/dist/src/graph-validator.specs.d.ts.map +1 -0
- package/dist/src/graph-validator.specs.js +86 -0
- package/dist/src/graph-validator.specs.js.map +1 -0
- package/dist/src/handle-job-event.d.ts +16 -0
- package/dist/src/handle-job-event.d.ts.map +1 -0
- package/dist/src/handle-job-event.js +46 -0
- package/dist/src/handle-job-event.js.map +1 -0
- package/dist/src/handle-job-event.specs.d.ts +2 -0
- package/dist/src/handle-job-event.specs.d.ts.map +1 -0
- package/dist/src/handle-job-event.specs.js +136 -0
- package/dist/src/handle-job-event.specs.js.map +1 -0
- package/dist/src/index.d.ts +41 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +11 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/integration.specs.d.ts +2 -0
- package/dist/src/integration.specs.d.ts.map +1 -0
- package/dist/src/integration.specs.js +225 -0
- package/dist/src/integration.specs.js.map +1 -0
- package/dist/src/process-graph.d.ts +14 -0
- package/dist/src/process-graph.d.ts.map +1 -0
- package/dist/src/process-graph.js +26 -0
- package/dist/src/process-graph.js.map +1 -0
- package/dist/src/process-graph.specs.d.ts +2 -0
- package/dist/src/process-graph.specs.d.ts.map +1 -0
- package/dist/src/process-graph.specs.js +41 -0
- package/dist/src/process-graph.specs.js.map +1 -0
- package/dist/src/process-job-graph.e2e.specs.d.ts +2 -0
- package/dist/src/process-job-graph.e2e.specs.d.ts.map +1 -0
- package/dist/src/process-job-graph.e2e.specs.js +81 -0
- package/dist/src/process-job-graph.e2e.specs.js.map +1 -0
- package/dist/src/retry-manager.d.ts +10 -0
- package/dist/src/retry-manager.d.ts.map +1 -0
- package/dist/src/retry-manager.js +17 -0
- package/dist/src/retry-manager.js.map +1 -0
- package/dist/src/retry-manager.specs.d.ts +2 -0
- package/dist/src/retry-manager.specs.d.ts.map +1 -0
- package/dist/src/retry-manager.specs.js +55 -0
- package/dist/src/retry-manager.specs.js.map +1 -0
- package/dist/src/timeout-manager.d.ts +7 -0
- package/dist/src/timeout-manager.d.ts.map +1 -0
- package/dist/src/timeout-manager.js +25 -0
- package/dist/src/timeout-manager.js.map +1 -0
- package/dist/src/timeout-manager.specs.d.ts +2 -0
- package/dist/src/timeout-manager.specs.d.ts.map +1 -0
- package/dist/src/timeout-manager.specs.js +44 -0
- package/dist/src/timeout-manager.specs.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/ketchup-plan.md +31 -0
- package/package.json +25 -0
- package/src/apply-policy.specs.ts +85 -0
- package/src/apply-policy.ts +27 -0
- package/src/commands/process-job-graph.specs.ts +93 -0
- package/src/commands/process-job-graph.ts +80 -0
- package/src/evolve.specs.ts +235 -0
- package/src/evolve.ts +121 -0
- package/src/graph-processor.specs.ts +331 -0
- package/src/graph-processor.ts +121 -0
- package/src/graph-validator.specs.ts +105 -0
- package/src/graph-validator.ts +94 -0
- package/src/handle-job-event.specs.ts +154 -0
- package/src/handle-job-event.ts +59 -0
- package/src/index.ts +17 -0
- package/src/integration.specs.ts +249 -0
- package/src/process-graph.specs.ts +44 -0
- package/src/process-graph.ts +42 -0
- package/src/process-job-graph.e2e.specs.ts +121 -0
- package/src/retry-manager.specs.ts +66 -0
- package/src/retry-manager.ts +29 -0
- package/src/timeout-manager.specs.ts +55 -0
- package/src/timeout-manager.ts +34 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createRetryManager } from './retry-manager';
|
|
3
|
+
|
|
4
|
+
describe('createRetryManager', () => {
|
|
5
|
+
it('schedules retry with initial backoff on first failure', () => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
const onRetry = vi.fn();
|
|
8
|
+
const manager = createRetryManager(onRetry);
|
|
9
|
+
|
|
10
|
+
manager.recordFailure('job-a', { maxRetries: 3, backoffMs: 100, maxBackoffMs: 5000 });
|
|
11
|
+
vi.advanceTimersByTime(100);
|
|
12
|
+
|
|
13
|
+
expect(onRetry).toHaveBeenCalledWith('job-a', 1);
|
|
14
|
+
vi.useRealTimers();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('uses exponential backoff on subsequent failures', () => {
|
|
18
|
+
vi.useFakeTimers();
|
|
19
|
+
const onRetry = vi.fn();
|
|
20
|
+
const manager = createRetryManager(onRetry);
|
|
21
|
+
const config = { maxRetries: 3, backoffMs: 100, maxBackoffMs: 5000 };
|
|
22
|
+
|
|
23
|
+
manager.recordFailure('job-a', config);
|
|
24
|
+
vi.advanceTimersByTime(100);
|
|
25
|
+
manager.recordFailure('job-a', config);
|
|
26
|
+
vi.advanceTimersByTime(199);
|
|
27
|
+
expect(onRetry).toHaveBeenCalledTimes(1);
|
|
28
|
+
vi.advanceTimersByTime(1);
|
|
29
|
+
|
|
30
|
+
expect(onRetry).toHaveBeenCalledTimes(2);
|
|
31
|
+
expect(onRetry).toHaveBeenLastCalledWith('job-a', 2);
|
|
32
|
+
vi.useRealTimers();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns true when max retries exhausted', () => {
|
|
36
|
+
vi.useFakeTimers();
|
|
37
|
+
const onRetry = vi.fn();
|
|
38
|
+
const manager = createRetryManager(onRetry);
|
|
39
|
+
const config = { maxRetries: 1, backoffMs: 100, maxBackoffMs: 5000 };
|
|
40
|
+
|
|
41
|
+
manager.recordFailure('job-a', config);
|
|
42
|
+
vi.advanceTimersByTime(100);
|
|
43
|
+
const exhausted = manager.recordFailure('job-a', config);
|
|
44
|
+
|
|
45
|
+
expect(exhausted).toBe(true);
|
|
46
|
+
expect(onRetry).toHaveBeenCalledTimes(1);
|
|
47
|
+
vi.useRealTimers();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('caps backoff at maxBackoffMs', () => {
|
|
51
|
+
vi.useFakeTimers();
|
|
52
|
+
const onRetry = vi.fn();
|
|
53
|
+
const manager = createRetryManager(onRetry);
|
|
54
|
+
const config = { maxRetries: 10, backoffMs: 1000, maxBackoffMs: 2000 };
|
|
55
|
+
|
|
56
|
+
manager.recordFailure('job-a', config);
|
|
57
|
+
vi.advanceTimersByTime(1000);
|
|
58
|
+
manager.recordFailure('job-a', config);
|
|
59
|
+
vi.advanceTimersByTime(2000);
|
|
60
|
+
manager.recordFailure('job-a', config);
|
|
61
|
+
vi.advanceTimersByTime(2000);
|
|
62
|
+
|
|
63
|
+
expect(onRetry).toHaveBeenCalledTimes(3);
|
|
64
|
+
vi.useRealTimers();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface RetryConfig {
|
|
2
|
+
maxRetries: number;
|
|
3
|
+
backoffMs: number;
|
|
4
|
+
maxBackoffMs: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RetryManager {
|
|
8
|
+
recordFailure(jobId: string, config: RetryConfig): boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createRetryManager(onRetry: (jobId: string, attempt: number) => void): RetryManager {
|
|
12
|
+
const attempts = new Map<string, number>();
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
recordFailure(jobId: string, config: RetryConfig): boolean {
|
|
16
|
+
const current = attempts.get(jobId) ?? 0;
|
|
17
|
+
const next = current + 1;
|
|
18
|
+
|
|
19
|
+
if (next > config.maxRetries) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
attempts.set(jobId, next);
|
|
24
|
+
const delay = Math.min(config.backoffMs * 2 ** (next - 1), config.maxBackoffMs);
|
|
25
|
+
setTimeout(() => onRetry(jobId, next), delay);
|
|
26
|
+
return false;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createTimeoutManager } from './timeout-manager';
|
|
3
|
+
|
|
4
|
+
describe('createTimeoutManager', () => {
|
|
5
|
+
it('fires callback when timer expires', () => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
const onTimeout = vi.fn();
|
|
8
|
+
const manager = createTimeoutManager(onTimeout);
|
|
9
|
+
|
|
10
|
+
manager.start('job-a', 1000);
|
|
11
|
+
vi.advanceTimersByTime(1000);
|
|
12
|
+
|
|
13
|
+
expect(onTimeout).toHaveBeenCalledWith('job-a');
|
|
14
|
+
vi.useRealTimers();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('does not fire callback before timeout', () => {
|
|
18
|
+
vi.useFakeTimers();
|
|
19
|
+
const onTimeout = vi.fn();
|
|
20
|
+
const manager = createTimeoutManager(onTimeout);
|
|
21
|
+
|
|
22
|
+
manager.start('job-a', 1000);
|
|
23
|
+
vi.advanceTimersByTime(999);
|
|
24
|
+
|
|
25
|
+
expect(onTimeout).not.toHaveBeenCalled();
|
|
26
|
+
vi.useRealTimers();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('cancels timer and prevents callback', () => {
|
|
30
|
+
vi.useFakeTimers();
|
|
31
|
+
const onTimeout = vi.fn();
|
|
32
|
+
const manager = createTimeoutManager(onTimeout);
|
|
33
|
+
|
|
34
|
+
manager.start('job-a', 1000);
|
|
35
|
+
manager.clear('job-a');
|
|
36
|
+
vi.advanceTimersByTime(1000);
|
|
37
|
+
|
|
38
|
+
expect(onTimeout).not.toHaveBeenCalled();
|
|
39
|
+
vi.useRealTimers();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('clearAll cancels all active timers', () => {
|
|
43
|
+
vi.useFakeTimers();
|
|
44
|
+
const onTimeout = vi.fn();
|
|
45
|
+
const manager = createTimeoutManager(onTimeout);
|
|
46
|
+
|
|
47
|
+
manager.start('job-a', 1000);
|
|
48
|
+
manager.start('job-b', 2000);
|
|
49
|
+
manager.clearAll();
|
|
50
|
+
vi.advanceTimersByTime(2000);
|
|
51
|
+
|
|
52
|
+
expect(onTimeout).not.toHaveBeenCalled();
|
|
53
|
+
vi.useRealTimers();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface TimeoutManager {
|
|
2
|
+
start(jobId: string, timeoutMs: number): void;
|
|
3
|
+
clear(jobId: string): void;
|
|
4
|
+
clearAll(): void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createTimeoutManager(onTimeout: (jobId: string) => void): TimeoutManager {
|
|
8
|
+
const timers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
start(jobId: string, timeoutMs: number) {
|
|
12
|
+
timers.set(
|
|
13
|
+
jobId,
|
|
14
|
+
setTimeout(() => {
|
|
15
|
+
timers.delete(jobId);
|
|
16
|
+
onTimeout(jobId);
|
|
17
|
+
}, timeoutMs),
|
|
18
|
+
);
|
|
19
|
+
},
|
|
20
|
+
clear(jobId: string) {
|
|
21
|
+
const timer = timers.get(jobId);
|
|
22
|
+
if (timer !== undefined) {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
timers.delete(jobId);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
clearAll() {
|
|
28
|
+
for (const timer of timers.values()) {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
timers.clear();
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
package/tsconfig.json
ADDED
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: 'node',
|
|
7
|
+
include: ['src/**/*.specs.ts'],
|
|
8
|
+
passWithNoTests: true,
|
|
9
|
+
coverage: {
|
|
10
|
+
provider: 'v8',
|
|
11
|
+
reporter: ['text', 'json', 'html'],
|
|
12
|
+
thresholds: {
|
|
13
|
+
lines: 100,
|
|
14
|
+
functions: 100,
|
|
15
|
+
branches: 100,
|
|
16
|
+
statements: 100,
|
|
17
|
+
},
|
|
18
|
+
include: ['src/**/*.ts'],
|
|
19
|
+
exclude: ['src/**/*.specs.ts', 'src/**/index.ts'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|