Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions packages/agents/.config/eslint/deferred-lint-rules.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
export const deferredLintRules = {
'unicorn/no-computed-property-existence-check': 'warn',
'unicorn/no-declarations-before-early-exit': 'warn',
'unicorn/no-duplicate-loops': 'warn',
'unicorn/no-incorrect-template-string-interpolation': 'warn',
'unicorn/no-return-array-push': 'warn',
'unicorn/no-top-level-assignment-in-function': 'warn',
'unicorn/no-unreadable-for-of-expression': 'warn',
'unicorn/no-unsafe-string-replacement': 'warn',
'unicorn/prefer-await': 'warn',
'unicorn/prefer-includes-over-repeated-comparisons': 'warn',
'unicorn/prefer-iterator-to-array': 'warn',
'unicorn/prefer-minimal-ternary': 'warn',
'unicorn/prefer-object-iterable-methods': 'warn',
'unicorn/prefer-set-methods': 'warn',
'unicorn/prefer-simple-condition-first': 'warn',
'preserve-caught-error': 'warn',
} as const;
5 changes: 0 additions & 5 deletions packages/foreman/.config/eslint/deferred-lint-rules.ts

This file was deleted.

10 changes: 0 additions & 10 deletions packages/foreman/.config/strict-lint.config.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/foreman/eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { defineConfig } from 'eslint/config';
import reactPlugin from 'eslint-plugin-react';

import baseConfig from '../../eslint.config.ts';
import { deferredLintRules } from './.config/eslint/deferred-lint-rules.ts';

const config = defineConfig([
...baseConfig,
Expand Down Expand Up @@ -46,10 +45,6 @@ const config = defineConfig([
},
},
},
{
files: ['**/*.ts', '**/*.tsx', '**/*.md/*.ts'],
rules: deferredLintRules,
},
]);

export default config;
14 changes: 4 additions & 10 deletions packages/foreman/src/hooks/__tests__/useFleetSnapshot.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class FakeEventSource {
}

private dispatch(type: string, event: MessageEvent<string>): void {
for (const listener of this.listeners.get(type) ?? []) {
const listeners = this.listeners.get(type) ?? [];
for (const listener of listeners) {
listener(event);
}
}
Expand Down Expand Up @@ -105,15 +106,8 @@ describe('useFleetSnapshot', () => {
});

it('discards a fetch result that resolves after a stream frame', async () => {
let resolveFetch: (response: Response) => void = () => {};
vi.stubGlobal(
'fetch',
vi.fn().mockReturnValue(
new Promise<Response>((resolve) => {
resolveFetch = resolve;
}),
),
);
const { promise: fetchResponse, resolve: resolveFetch } = Promise.withResolvers<Response>();
vi.stubGlobal('fetch', vi.fn().mockReturnValue(fetchResponse));
const { result } = renderHook(() => useFleetSnapshot());

act(() => {
Expand Down
36 changes: 20 additions & 16 deletions packages/foreman/vitest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,28 @@ afterEach(() => {
cleanup();
});

// jsdom provides neither matchMedia nor ResizeObserver; Mantine components consult both when rendering.
globalThis.matchMedia = function matchMedia(query: string): MediaQueryList {
return {
matches: false,
media: query,
onchange: null,
addEventListener(): void {},
removeEventListener(): void {},
addListener(): void {},
removeListener(): void {},
dispatchEvent(): boolean {
return false;
},
};
};
const matchMediaStub: typeof globalThis.matchMedia = (query) => ({
matches: false,
media: query,
onchange: null,
addEventListener(): void {},
removeEventListener(): void {},
addListener(): void {},
removeListener(): void {},
dispatchEvent(): boolean {
return false;
},
});

globalThis.ResizeObserver = class ResizeObserver {
const resizeObserverStub: typeof globalThis.ResizeObserver = class {
observe(): void {}
unobserve(): void {}
disconnect(): void {}
};

// jsdom provides neither matchMedia nor ResizeObserver; Mantine components consult both when rendering.
// `defineProperties` survives the `vi.unstubAllGlobals()` that two suites run in `afterEach`; a `vi.stubGlobal` stub would not.
Object.defineProperties(globalThis, {
matchMedia: { configurable: true, value: matchMediaStub, writable: true },
ResizeObserver: { configurable: true, value: resizeObserverStub, writable: true },
});
1 change: 0 additions & 1 deletion packages/kb/.config/eslint/deferred-lint-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ export const deferredLintRules = {
'unicorn/prefer-includes-over-repeated-comparisons': 'warn',
'unicorn/prefer-iterator-to-array': 'warn',
'unicorn/prefer-number-is-safe-integer': 'warn',
'unicorn/prefer-simple-condition-first': 'warn',
'preserve-caught-error': 'warn',
} as const;
12 changes: 0 additions & 12 deletions packages/lifecycle/.config/eslint/deferred-lint-rules.ts

This file was deleted.

10 changes: 0 additions & 10 deletions packages/lifecycle/.config/strict-lint.config.ts

This file was deleted.

18 changes: 1 addition & 17 deletions packages/lifecycle/eslint.config.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
import baseConfig from '@williamthorsen/eslint-config-typescript';
import { defineConfig, globalIgnores } from 'eslint/config';

import { deferredLintRules } from './.config/eslint/deferred-lint-rules.ts';

const config = defineConfig([
...baseConfig,
globalIgnores([
'**/coverage/**',
'**/dist/**',
'**/local/**',
// Ignore test fixtures that are intentionally syntactically broken.
// '**/__tests__/**/fixtures/**/*malformed*/**',
// '**/__tests__/**/fixtures/**/*malformed*',
]),
{
files: ['**/*.ts', '**/*.mts', '**/*.tsx', '**/*.md/*.ts', '**/*.js'],
rules: deferredLintRules,
},
]);
const config = defineConfig([...baseConfig, globalIgnores(['**/coverage/**', '**/dist/**', '**/local/**'])]);

export default config;
5 changes: 0 additions & 5 deletions packages/run-core/.config/eslint/deferred-lint-rules.ts

This file was deleted.

10 changes: 0 additions & 10 deletions packages/run-core/.config/strict-lint.config.ts

This file was deleted.

18 changes: 1 addition & 17 deletions packages/run-core/eslint.config.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
import baseConfig from '@williamthorsen/eslint-config-typescript';
import { defineConfig, globalIgnores } from 'eslint/config';

import { deferredLintRules } from './.config/eslint/deferred-lint-rules.ts';

const config = defineConfig([
...baseConfig,
globalIgnores([
'**/coverage/**',
'**/dist/**',
'**/local/**',
// Ignore test fixtures that are intentionally syntactically broken.
// '**/__tests__/**/fixtures/**/*malformed*/**',
// '**/__tests__/**/fixtures/**/*malformed*',
]),
{
files: ['**/*.ts', '**/*.mts', '**/*.tsx', '**/*.md/*.ts', '**/*.js'],
rules: deferredLintRules,
},
]);
const config = defineConfig([...baseConfig, globalIgnores(['**/coverage/**', '**/dist/**', '**/local/**'])]);

export default config;
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ const v2Fixture = {
],
};

/** Resolves with the value the operation rejects with. */
async function captureRejection(operation: () => Promise<unknown>): Promise<unknown> {
try {
await operation();
} catch (error) {
return error;
}
throw new Error('Expected the operation to reject, but it resolved.');
}

function mockJson(data: Record<string, unknown>): void {
mockedReadFile.mockResolvedValue(JSON.stringify(data));
}
Expand Down Expand Up @@ -593,7 +603,7 @@ describe('parseStatusFile', () => {
it('throws RunDataParseError with corrupt_json category for invalid JSON', async () => {
mockedReadFile.mockResolvedValue('not json');

const error = await parseStatusFile('/path/to/status.json').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseStatusFile('/path/to/status.json'));

expect(error).toBeInstanceOf(RunDataParseError);
expect(error).toBeInstanceOf(Error);
Expand All @@ -606,7 +616,7 @@ describe('parseStatusFile', () => {
it('throws RunDataParseError with invalid_schema category and zodIssues for schema failures', async () => {
mockedReadFile.mockResolvedValue('{}');

const error = await parseStatusFile('/path/to/status.json').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseStatusFile('/path/to/status.json'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand Down Expand Up @@ -1142,7 +1152,7 @@ describe('parseRunData', () => {
'/runs/test-run/run-index.json': '{ not valid json !!!',
});

const error = await parseRunData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand All @@ -1156,7 +1166,7 @@ describe('parseRunData', () => {
'/runs/test-run/run-index.json': JSON.stringify(invalid),
});

const error = await parseRunData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand Down Expand Up @@ -1186,7 +1196,7 @@ describe('parseRunData', () => {
'/runs/test-run/run-index.json': JSON.stringify(v3Header),
});

const error = await parseRunData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand All @@ -1199,7 +1209,7 @@ describe('parseRunData', () => {
'/runs/test-run/status.json': 'not json at all',
});

const error = await parseRunData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand All @@ -1212,7 +1222,7 @@ describe('parseRunData', () => {
'/runs/test-run/status.json': JSON.stringify({ runId: 'only-one-field' }),
});

const error = await parseRunData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand Down Expand Up @@ -1272,7 +1282,7 @@ describe(parseRunRawData, () => {
'/runs/test-run/status.json': JSON.stringify(currentFormatFixture),
});

const error = await parseRunRawData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunRawData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand All @@ -1284,7 +1294,7 @@ describe(parseRunRawData, () => {
'/runs/test-run/run-index.json': JSON.stringify(v2Fixture),
});

const error = await parseRunRawData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunRawData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand All @@ -1296,7 +1306,7 @@ describe(parseRunRawData, () => {
'/runs/test-run/run-index.json': '{ not valid json !!!',
});

const error = await parseRunRawData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunRawData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand All @@ -1308,7 +1318,7 @@ describe(parseRunRawData, () => {
'/runs/test-run/run-index.json': JSON.stringify(minimalV3Header()),
});

const error = await parseRunRawData('/runs/test-run').catch((error_: unknown) => error_);
const error = await captureRejection(() => parseRunRawData('/runs/test-run'));

expect(error).toBeInstanceOf(RunDataParseError);
if (!(error instanceof RunDataParseError)) return;
Expand Down
15 changes: 6 additions & 9 deletions packages/run-core/src/scanners/run-directory-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,12 @@ export async function discoverRunDirectories(basePath: string): Promise<RunDirec
if (!(await isDirectory(projectPath))) continue;

const projectEntries = await readdirSafe(projectPath);
const ticketsDir = projectEntries.includes('tickets');

if (ticketsDir) {
const ticketEntries = await scanTicketsDir(join(projectPath, 'tickets'), slug);
entries.push(...ticketEntries);
} else {
const ticketEntries = await scanDirectEntries(projectEntries, projectPath, slug);
entries.push(...ticketEntries);
}
const hasTicketsDir = projectEntries.includes('tickets');

const ticketEntries = hasTicketsDir
? await scanTicketsDir(join(projectPath, 'tickets'), slug)
: await scanDirectEntries(projectEntries, projectPath, slug);
entries.push(...ticketEntries);
}

return entries;
Expand Down
Loading